aboutsummaryrefslogtreecommitdiffstats
path: root/programs
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2006-04-28 07:14:48 +0000
committerMartin Willi <martin@strongswan.org>2006-04-28 07:14:48 +0000
commit997358a6c475c8886cce388ab325184a1ff733c9 (patch)
tree27a15790e030fc186d00cd710d2a3540f4defe69 /programs
parent52923c9acb349adec3d1cc039e7a74c2e822da6e (diff)
downloadstrongswan-997358a6c475c8886cce388ab325184a1ff733c9.tar.bz2
strongswan-997358a6c475c8886cce388ab325184a1ff733c9.tar.xz
- import of strongswan-2.7.0
- applied patch for charon
Diffstat (limited to 'programs')
-rw-r--r--programs/Makefile46
-rw-r--r--programs/Makefile.program150
-rw-r--r--programs/_confread/.cvsignore7
-rw-r--r--programs/_confread/Makefile27
-rw-r--r--programs/_confread/README.conf.V2103
-rw-r--r--programs/_confread/_confread.828
-rwxr-xr-xprograms/_confread/_confread.in520
-rw-r--r--programs/_confread/block.in8
-rw-r--r--programs/_confread/clear-or-private.in8
-rw-r--r--programs/_confread/clear.in7
-rw-r--r--programs/_confread/ipsec.conf.51286
-rw-r--r--programs/_confread/ipsec.conf.in44
-rw-r--r--programs/_confread/private-or-clear.in14
-rw-r--r--programs/_confread/private.in6
-rwxr-xr-xprograms/_confread/randomize28
-rw-r--r--programs/_copyright/.cvsignore1
-rw-r--r--programs/_copyright/Makefile44
-rw-r--r--programs/_copyright/_copyright.832
-rw-r--r--programs/_copyright/_copyright.c69
-rw-r--r--programs/_include/.cvsignore1
-rw-r--r--programs/_include/Makefile43
-rw-r--r--programs/_include/_include.835
-rwxr-xr-xprograms/_include/_include.in102
-rw-r--r--programs/_keycensor/.cvsignore1
-rw-r--r--programs/_keycensor/Makefile43
-rw-r--r--programs/_keycensor/_keycensor.833
-rwxr-xr-xprograms/_keycensor/_keycensor.in52
-rw-r--r--programs/_plutoload/.cvsignore1
-rw-r--r--programs/_plutoload/Makefile43
-rw-r--r--programs/_plutoload/_plutoload.833
-rwxr-xr-xprograms/_plutoload/_plutoload.in164
-rw-r--r--programs/_plutorun/.cvsignore1
-rw-r--r--programs/_plutorun/Makefile43
-rw-r--r--programs/_plutorun/_plutorun.837
-rwxr-xr-xprograms/_plutorun/_plutorun.in281
-rw-r--r--programs/_realsetup/.cvsignore1
-rw-r--r--programs/_realsetup/Makefile43
-rw-r--r--programs/_realsetup/_realsetup.836
-rwxr-xr-xprograms/_realsetup/_realsetup.in456
-rw-r--r--programs/_secretcensor/.cvsignore1
-rw-r--r--programs/_secretcensor/Makefile43
-rw-r--r--programs/_secretcensor/_secretcensor.834
-rwxr-xr-xprograms/_secretcensor/_secretcensor.in75
-rw-r--r--programs/_startklips/.cvsignore1
-rw-r--r--programs/_startklips/Makefile43
-rw-r--r--programs/_startklips/_startklips.833
-rwxr-xr-xprograms/_startklips/_startklips.in367
-rw-r--r--programs/_updown/.cvsignore2
-rw-r--r--programs/_updown/Makefile22
-rw-r--r--programs/_updown/_updown.819
-rwxr-xr-xprograms/_updown/_updown.in503
-rw-r--r--programs/_updown_espmark/Makefile22
-rw-r--r--programs/_updown_espmark/_updown_espmark.818
-rw-r--r--programs/_updown_espmark/_updown_espmark.in452
-rw-r--r--programs/auto/.cvsignore1
-rw-r--r--programs/auto/Makefile21
-rw-r--r--programs/auto/auto.8481
-rwxr-xr-xprograms/auto/auto.in660
-rw-r--r--programs/barf/.cvsignore1
-rw-r--r--programs/barf/Makefile38
-rw-r--r--programs/barf/barf.884
-rwxr-xr-xprograms/barf/barf.in296
-rw-r--r--programs/calcgoo/.cvsignore1
-rw-r--r--programs/calcgoo/Makefile41
-rw-r--r--programs/calcgoo/calcgoo.831
-rw-r--r--programs/calcgoo/calcgoo.in43
-rw-r--r--programs/charon/Doxyfile220
-rw-r--r--programs/charon/Makefile99
-rw-r--r--programs/charon/charon.kdevelop105
-rw-r--r--programs/charon/charon/Makefile.charon25
-rw-r--r--programs/charon/charon/config/Makefile.config32
-rwxr-xr-xprograms/charon/charon/config/configuration.c112
-rwxr-xr-xprograms/charon/charon/config/configuration.h89
-rw-r--r--programs/charon/charon/config/connections/Makefile.connections24
-rw-r--r--programs/charon/charon/config/connections/connection.c367
-rw-r--r--programs/charon/charon/config/connections/connection.h283
-rwxr-xr-xprograms/charon/charon/config/connections/connection_store.h112
-rw-r--r--programs/charon/charon/config/connections/local_connection_store.c228
-rw-r--r--programs/charon/charon/config/connections/local_connection_store.h63
-rw-r--r--programs/charon/charon/config/credentials/Makefile.credentials20
-rwxr-xr-xprograms/charon/charon/config/credentials/credential_store.h91
-rw-r--r--programs/charon/charon/config/credentials/local_credential_store.c315
-rw-r--r--programs/charon/charon/config/credentials/local_credential_store.h84
-rw-r--r--programs/charon/charon/config/policies/Makefile.policies24
-rw-r--r--programs/charon/charon/config/policies/local_policy_store.c136
-rw-r--r--programs/charon/charon/config/policies/local_policy_store.h60
-rw-r--r--programs/charon/charon/config/policies/policy.c397
-rw-r--r--programs/charon/charon/config/policies/policy.h249
-rwxr-xr-xprograms/charon/charon/config/policies/policy_store.h76
-rw-r--r--programs/charon/charon/config/proposal.c642
-rw-r--r--programs/charon/charon/config/proposal.h269
-rw-r--r--programs/charon/charon/config/traffic_selector.c425
-rw-r--r--programs/charon/charon/config/traffic_selector.h258
-rw-r--r--programs/charon/charon/daemon.c390
-rw-r--r--programs/charon/charon/daemon.h324
-rw-r--r--programs/charon/charon/encoding/Makefile.encoding30
-rw-r--r--programs/charon/charon/encoding/generator.c1077
-rw-r--r--programs/charon/charon/encoding/generator.h101
-rw-r--r--programs/charon/charon/encoding/message.c1251
-rw-r--r--programs/charon/charon/encoding/message.h367
-rw-r--r--programs/charon/charon/encoding/parser.c1065
-rw-r--r--programs/charon/charon/encoding/parser.h95
-rw-r--r--programs/charon/charon/encoding/payloads/Makefile.payloads108
-rw-r--r--programs/charon/charon/encoding/payloads/auth_payload.c265
-rw-r--r--programs/charon/charon/encoding/payloads/auth_payload.h122
-rw-r--r--programs/charon/charon/encoding/payloads/cert_payload.c279
-rw-r--r--programs/charon/charon/encoding/payloads/cert_payload.h155
-rw-r--r--programs/charon/charon/encoding/payloads/certreq_payload.c259
-rw-r--r--programs/charon/charon/encoding/payloads/certreq_payload.h125
-rw-r--r--programs/charon/charon/encoding/payloads/configuration_attribute.c282
-rw-r--r--programs/charon/charon/encoding/payloads/configuration_attribute.h149
-rw-r--r--programs/charon/charon/encoding/payloads/cp_payload.c305
-rw-r--r--programs/charon/charon/encoding/payloads/cp_payload.h138
-rw-r--r--programs/charon/charon/encoding/payloads/delete_payload.c322
-rw-r--r--programs/charon/charon/encoding/payloads/delete_payload.h156
-rw-r--r--programs/charon/charon/encoding/payloads/eap_payload.c227
-rw-r--r--programs/charon/charon/encoding/payloads/eap_payload.h105
-rw-r--r--programs/charon/charon/encoding/payloads/encodings.c68
-rw-r--r--programs/charon/charon/encoding/payloads/encodings.h540
-rw-r--r--programs/charon/charon/encoding/payloads/encryption_payload.c702
-rw-r--r--programs/charon/charon/encoding/payloads/encryption_payload.h196
-rw-r--r--programs/charon/charon/encoding/payloads/id_payload.c320
-rw-r--r--programs/charon/charon/encoding/payloads/id_payload.h172
-rw-r--r--programs/charon/charon/encoding/payloads/ike_header.c408
-rw-r--r--programs/charon/charon/encoding/payloads/ike_header.h261
-rw-r--r--programs/charon/charon/encoding/payloads/ke_payload.c276
-rw-r--r--programs/charon/charon/encoding/payloads/ke_payload.h110
-rw-r--r--programs/charon/charon/encoding/payloads/nonce_payload.c241
-rw-r--r--programs/charon/charon/encoding/payloads/nonce_payload.h89
-rw-r--r--programs/charon/charon/encoding/payloads/notify_payload.c441
-rw-r--r--programs/charon/charon/encoding/payloads/notify_payload.h200
-rw-r--r--programs/charon/charon/encoding/payloads/payload.c131
-rw-r--r--programs/charon/charon/encoding/payloads/payload.h279
-rw-r--r--programs/charon/charon/encoding/payloads/proposal_substructure.c629
-rw-r--r--programs/charon/charon/encoding/payloads/proposal_substructure.h231
-rw-r--r--programs/charon/charon/encoding/payloads/sa_payload.c390
-rw-r--r--programs/charon/charon/encoding/payloads/sa_payload.h140
-rw-r--r--programs/charon/charon/encoding/payloads/traffic_selector_substructure.c374
-rw-r--r--programs/charon/charon/encoding/payloads/traffic_selector_substructure.h171
-rw-r--r--programs/charon/charon/encoding/payloads/transform_attribute.c333
-rw-r--r--programs/charon/charon/encoding/payloads/transform_attribute.h154
-rw-r--r--programs/charon/charon/encoding/payloads/transform_substructure.c485
-rw-r--r--programs/charon/charon/encoding/payloads/transform_substructure.h198
-rw-r--r--programs/charon/charon/encoding/payloads/ts_payload.c365
-rw-r--r--programs/charon/charon/encoding/payloads/ts_payload.h152
-rw-r--r--programs/charon/charon/encoding/payloads/unknown_payload.c207
-rw-r--r--programs/charon/charon/encoding/payloads/unknown_payload.h95
-rw-r--r--programs/charon/charon/encoding/payloads/vendor_id_payload.c227
-rw-r--r--programs/charon/charon/encoding/payloads/vendor_id_payload.h103
-rw-r--r--programs/charon/charon/network/Makefile.network24
-rw-r--r--programs/charon/charon/network/packet.c189
-rw-r--r--programs/charon/charon/network/packet.h135
-rw-r--r--programs/charon/charon/network/socket.c457
-rw-r--r--programs/charon/charon/network/socket.h128
-rw-r--r--programs/charon/charon/queues/Makefile.queues30
-rw-r--r--programs/charon/charon/queues/event_queue.c349
-rw-r--r--programs/charon/charon/queues/event_queue.h117
-rw-r--r--programs/charon/charon/queues/job_queue.c153
-rw-r--r--programs/charon/charon/queues/job_queue.h99
-rw-r--r--programs/charon/charon/queues/jobs/Makefile.jobs40
-rw-r--r--programs/charon/charon/queues/jobs/delete_established_ike_sa_job.c90
-rw-r--r--programs/charon/charon/queues/jobs/delete_established_ike_sa_job.h78
-rw-r--r--programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c90
-rw-r--r--programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h79
-rw-r--r--programs/charon/charon/queues/jobs/incoming_packet_job.c102
-rw-r--r--programs/charon/charon/queues/jobs/incoming_packet_job.h78
-rw-r--r--programs/charon/charon/queues/jobs/initiate_ike_sa_job.c101
-rw-r--r--programs/charon/charon/queues/jobs/initiate_ike_sa_job.h75
-rw-r--r--programs/charon/charon/queues/jobs/job.c34
-rw-r--r--programs/charon/charon/queues/jobs/job.h120
-rw-r--r--programs/charon/charon/queues/jobs/retransmit_request_job.c132
-rw-r--r--programs/charon/charon/queues/jobs/retransmit_request_job.h105
-rw-r--r--programs/charon/charon/queues/send_queue.c153
-rw-r--r--programs/charon/charon/queues/send_queue.h100
-rw-r--r--programs/charon/charon/sa/Makefile.sa37
-rw-r--r--programs/charon/charon/sa/authenticator.c405
-rw-r--r--programs/charon/charon/sa/authenticator.h138
-rw-r--r--programs/charon/charon/sa/child_sa.c590
-rw-r--r--programs/charon/charon/sa/child_sa.h149
-rw-r--r--programs/charon/charon/sa/ike_sa.c1199
-rw-r--r--programs/charon/charon/sa/ike_sa.h462
-rw-r--r--programs/charon/charon/sa/ike_sa_id.c185
-rw-r--r--programs/charon/charon/sa/ike_sa_id.h146
-rw-r--r--programs/charon/charon/sa/ike_sa_manager.c812
-rw-r--r--programs/charon/charon/sa/ike_sa_manager.h185
-rw-r--r--programs/charon/charon/sa/states/Makefile.states43
-rw-r--r--programs/charon/charon/sa/states/ike_auth_requested.c671
-rw-r--r--programs/charon/charon/sa/states/ike_auth_requested.h72
-rw-r--r--programs/charon/charon/sa/states/ike_sa_established.c239
-rw-r--r--programs/charon/charon/sa/states/ike_sa_established.h64
-rw-r--r--programs/charon/charon/sa/states/ike_sa_init_requested.c798
-rw-r--r--programs/charon/charon/sa/states/ike_sa_init_requested.h68
-rw-r--r--programs/charon/charon/sa/states/ike_sa_init_responded.c695
-rw-r--r--programs/charon/charon/sa/states/ike_sa_init_responded.h73
-rw-r--r--programs/charon/charon/sa/states/initiator_init.c360
-rw-r--r--programs/charon/charon/sa/states/initiator_init.h84
-rw-r--r--programs/charon/charon/sa/states/responder_init.c568
-rw-r--r--programs/charon/charon/sa/states/responder_init.h68
-rw-r--r--programs/charon/charon/sa/states/state.c37
-rw-r--r--programs/charon/charon/sa/states/state.h166
-rw-r--r--programs/charon/charon/threads/Makefile.threads39
-rw-r--r--programs/charon/charon/threads/kernel_interface.c729
-rw-r--r--programs/charon/charon/threads/kernel_interface.h185
-rw-r--r--programs/charon/charon/threads/receiver.c128
-rw-r--r--programs/charon/charon/threads/receiver.h67
-rw-r--r--programs/charon/charon/threads/scheduler.c124
-rw-r--r--programs/charon/charon/threads/scheduler.h67
-rw-r--r--programs/charon/charon/threads/sender.c126
-rw-r--r--programs/charon/charon/threads/sender.h63
-rwxr-xr-xprograms/charon/charon/threads/stroke_interface.c661
-rw-r--r--programs/charon/charon/threads/stroke_interface.h86
-rw-r--r--programs/charon/charon/threads/thread_pool.c623
-rw-r--r--programs/charon/charon/threads/thread_pool.h78
-rw-r--r--programs/charon/doc/Architecture.txt56
-rw-r--r--programs/charon/doc/Known-bugs.txt6
-rw-r--r--programs/charon/doc/Todo-list.txt49
-rw-r--r--programs/charon/lib/Makefile.lib31
-rw-r--r--programs/charon/lib/asn1/Makefile.asn129
-rw-r--r--programs/charon/lib/asn1/asn1.c738
-rw-r--r--programs/charon/lib/asn1/asn1.h137
-rw-r--r--programs/charon/lib/asn1/oid.c197
-rw-r--r--programs/charon/lib/asn1/oid.h80
-rw-r--r--programs/charon/lib/asn1/oid.pl127
-rw-r--r--programs/charon/lib/asn1/oid.txt184
-rwxr-xr-xprograms/charon/lib/asn1/pem.c343
-rwxr-xr-xprograms/charon/lib/asn1/pem.h25
-rw-r--r--programs/charon/lib/asn1/ttodata.c374
-rw-r--r--programs/charon/lib/asn1/ttodata.h30
-rw-r--r--programs/charon/lib/crypto/Makefile.transforms37
-rw-r--r--programs/charon/lib/crypto/crypters/Makefile.crypters23
-rw-r--r--programs/charon/lib/crypto/crypters/aes_cbc_crypter.c1627
-rw-r--r--programs/charon/lib/crypto/crypters/aes_cbc_crypter.h61
-rw-r--r--programs/charon/lib/crypto/crypters/crypter.c63
-rw-r--r--programs/charon/lib/crypto/crypters/crypter.h153
-rw-r--r--programs/charon/lib/crypto/diffie_hellman.c615
-rw-r--r--programs/charon/lib/crypto/diffie_hellman.h149
-rw-r--r--programs/charon/lib/crypto/hashers/Makefile.hashers27
-rw-r--r--programs/charon/lib/crypto/hashers/hasher.c60
-rw-r--r--programs/charon/lib/crypto/hashers/hasher.h147
-rw-r--r--programs/charon/lib/crypto/hashers/md5_hasher.c394
-rw-r--r--programs/charon/lib/crypto/hashers/md5_hasher.h60
-rw-r--r--programs/charon/lib/crypto/hashers/sha1_hasher.c269
-rw-r--r--programs/charon/lib/crypto/hashers/sha1_hasher.h60
-rw-r--r--programs/charon/lib/crypto/hmac.c209
-rw-r--r--programs/charon/lib/crypto/hmac.h118
-rw-r--r--programs/charon/lib/crypto/prf_plus.c157
-rw-r--r--programs/charon/lib/crypto/prf_plus.h93
-rw-r--r--programs/charon/lib/crypto/prfs/Makefile.prfs23
-rw-r--r--programs/charon/lib/crypto/prfs/hmac_prf.c117
-rw-r--r--programs/charon/lib/crypto/prfs/hmac_prf.h64
-rw-r--r--programs/charon/lib/crypto/prfs/prf.c67
-rw-r--r--programs/charon/lib/crypto/prfs/prf.h136
-rw-r--r--programs/charon/lib/crypto/rsa/Makefile.rsa23
-rw-r--r--programs/charon/lib/crypto/rsa/rsa_private_key.c772
-rw-r--r--programs/charon/lib/crypto/rsa/rsa_private_key.h185
-rw-r--r--programs/charon/lib/crypto/rsa/rsa_public_key.c458
-rw-r--r--programs/charon/lib/crypto/rsa/rsa_public_key.h153
-rw-r--r--programs/charon/lib/crypto/signers/Makefile.signers23
-rw-r--r--programs/charon/lib/crypto/signers/hmac_signer.c169
-rw-r--r--programs/charon/lib/crypto/signers/hmac_signer.h58
-rw-r--r--programs/charon/lib/crypto/signers/signer.c59
-rw-r--r--programs/charon/lib/crypto/signers/signer.h147
-rwxr-xr-xprograms/charon/lib/crypto/x509.c937
-rwxr-xr-xprograms/charon/lib/crypto/x509.h136
-rw-r--r--programs/charon/lib/definitions.c40
-rw-r--r--programs/charon/lib/definitions.h120
-rw-r--r--programs/charon/lib/library.c42
-rw-r--r--programs/charon/lib/library.h100
-rw-r--r--programs/charon/lib/types.c115
-rw-r--r--programs/charon/lib/types.h186
-rw-r--r--programs/charon/lib/utils/Makefile.utils47
-rw-r--r--programs/charon/lib/utils/host.c365
-rw-r--r--programs/charon/lib/utils/host.h225
-rw-r--r--programs/charon/lib/utils/identification.c1167
-rw-r--r--programs/charon/lib/utils/identification.h245
-rw-r--r--programs/charon/lib/utils/iterator.h153
-rw-r--r--programs/charon/lib/utils/leak_detective.c540
-rw-r--r--programs/charon/lib/utils/leak_detective.h50
-rw-r--r--programs/charon/lib/utils/linked_list.c727
-rw-r--r--programs/charon/lib/utils/linked_list.h203
-rw-r--r--programs/charon/lib/utils/logger.c342
-rw-r--r--programs/charon/lib/utils/logger.h198
-rw-r--r--programs/charon/lib/utils/logger_manager.c220
-rw-r--r--programs/charon/lib/utils/logger_manager.h160
-rw-r--r--programs/charon/lib/utils/randomizer.c164
-rw-r--r--programs/charon/lib/utils/randomizer.h110
-rw-r--r--programs/charon/lib/utils/tester.c256
-rw-r--r--programs/charon/lib/utils/tester.h148
-rw-r--r--programs/charon/patches/strongswan-2.7.0.patch874
-rw-r--r--programs/charon/scripts/alice-key.derbin0 -> 1190 bytes
-rw-r--r--programs/charon/scripts/alice.derbin0 -> 764 bytes
-rw-r--r--programs/charon/scripts/bob-key.derbin0 -> 1187 bytes
-rw-r--r--programs/charon/scripts/bob.derbin0 -> 759 bytes
-rw-r--r--programs/charon/scripts/complex1.derbin0 -> 934 bytes
-rw-r--r--programs/charon/scripts/complex2.derbin0 -> 956 bytes
-rwxr-xr-xprograms/charon/scripts/daemon-loop.sh13
-rwxr-xr-xprograms/charon/scripts/deleteline9
-rwxr-xr-xprograms/charon/scripts/replace9
-rwxr-xr-xprograms/charon/scripts/to-alice.sh27
-rwxr-xr-xprograms/charon/scripts/to-bob.sh27
-rw-r--r--programs/charon/stroke/Makefile.stroke17
-rw-r--r--programs/charon/stroke/stroke.c304
-rw-r--r--programs/charon/stroke/stroke.h91
-rw-r--r--programs/charon/testing/Makefile.testcases139
-rw-r--r--programs/charon/testing/aes_cbc_crypter_test.c201
-rw-r--r--programs/charon/testing/aes_cbc_crypter_test.h38
-rw-r--r--programs/charon/testing/certificate_test.c112
-rw-r--r--programs/charon/testing/certificate_test.h42
-rw-r--r--programs/charon/testing/child_sa_test.c101
-rw-r--r--programs/charon/testing/child_sa_test.h42
-rw-r--r--programs/charon/testing/connection_test.c82
-rw-r--r--programs/charon/testing/connection_test.h38
-rw-r--r--programs/charon/testing/diffie_hellman_test.c76
-rw-r--r--programs/charon/testing/diffie_hellman_test.h37
-rw-r--r--programs/charon/testing/encryption_payload_test.c139
-rw-r--r--programs/charon/testing/encryption_payload_test.h37
-rw-r--r--programs/charon/testing/event_queue_test.c143
-rw-r--r--programs/charon/testing/event_queue_test.h39
-rw-r--r--programs/charon/testing/generator_test.c1410
-rw-r--r--programs/charon/testing/generator_test.h183
-rw-r--r--programs/charon/testing/hasher_test.c170
-rw-r--r--programs/charon/testing/hasher_test.h49
-rw-r--r--programs/charon/testing/hmac_signer_test.c203
-rw-r--r--programs/charon/testing/hmac_signer_test.h46
-rw-r--r--programs/charon/testing/hmac_test.c408
-rw-r--r--programs/charon/testing/hmac_test.h49
-rw-r--r--programs/charon/testing/identification_test.c166
-rw-r--r--programs/charon/testing/identification_test.h37
-rw-r--r--programs/charon/testing/ike_sa_id_test.c84
-rw-r--r--programs/charon/testing/ike_sa_id_test.h40
-rw-r--r--programs/charon/testing/ike_sa_manager_test.c185
-rw-r--r--programs/charon/testing/ike_sa_manager_test.h39
-rw-r--r--programs/charon/testing/ike_sa_test.c56
-rw-r--r--programs/charon/testing/ike_sa_test.h37
-rw-r--r--programs/charon/testing/job_queue_test.c132
-rw-r--r--programs/charon/testing/job_queue_test.h40
-rw-r--r--programs/charon/testing/kernel_interface_test.c84
-rw-r--r--programs/charon/testing/kernel_interface_test.h38
-rw-r--r--programs/charon/testing/leak_detective_test.c79
-rw-r--r--programs/charon/testing/leak_detective_test.h38
-rw-r--r--programs/charon/testing/linked_list_test.c241
-rw-r--r--programs/charon/testing/linked_list_test.h74
-rw-r--r--programs/charon/testing/packet_test.c55
-rw-r--r--programs/charon/testing/packet_test.h37
-rw-r--r--programs/charon/testing/parser_test.c963
-rw-r--r--programs/charon/testing/parser_test.h170
-rw-r--r--programs/charon/testing/policy_test.c246
-rw-r--r--programs/charon/testing/policy_test.h42
-rw-r--r--programs/charon/testing/prf_plus_test.c145
-rw-r--r--programs/charon/testing/prf_plus_test.h38
-rw-r--r--programs/charon/testing/proposal_test.c98
-rw-r--r--programs/charon/testing/proposal_test.h42
-rw-r--r--programs/charon/testing/rsa_test.c226
-rw-r--r--programs/charon/testing/rsa_test.h41
-rw-r--r--programs/charon/testing/scheduler_test.c92
-rw-r--r--programs/charon/testing/scheduler_test.h37
-rw-r--r--programs/charon/testing/send_queue_test.c142
-rw-r--r--programs/charon/testing/send_queue_test.h40
-rw-r--r--programs/charon/testing/sender_test.c88
-rw-r--r--programs/charon/testing/sender_test.h37
-rw-r--r--programs/charon/testing/socket_test.c82
-rw-r--r--programs/charon/testing/socket_test.h38
-rw-r--r--programs/charon/testing/testcases.c263
-rw-r--r--programs/charon/testing/thread_pool_test.c41
-rw-r--r--programs/charon/testing/thread_pool_test.h37
-rw-r--r--programs/eroute/.cvsignore1
-rw-r--r--programs/eroute/Makefile52
-rw-r--r--programs/eroute/eroute.5272
-rw-r--r--programs/eroute/eroute.8354
-rw-r--r--programs/eroute/eroute.c1044
-rw-r--r--programs/examples/Makefile22
-rw-r--r--programs/examples/oe.conf.in68
-rw-r--r--programs/ikeping/.cvsignore1
-rw-r--r--programs/ikeping/Makefile57
-rw-r--r--programs/ikeping/ikeping.871
-rw-r--r--programs/ikeping/ikeping.c483
-rw-r--r--programs/ipsec/.cvsignore1
-rw-r--r--programs/ipsec/Makefile28
-rw-r--r--programs/ipsec/distro.txt1
-rw-r--r--programs/ipsec/ipsec.8336
-rwxr-xr-xprograms/ipsec/ipsec.in244
-rw-r--r--programs/klipsdebug/.cvsignore1
-rw-r--r--programs/klipsdebug/Makefile80
-rw-r--r--programs/klipsdebug/klipsdebug.5138
-rw-r--r--programs/klipsdebug/klipsdebug.8164
-rw-r--r--programs/klipsdebug/klipsdebug.c436
-rw-r--r--programs/look/.cvsignore1
-rw-r--r--programs/look/Makefile38
-rw-r--r--programs/look/look.845
-rwxr-xr-xprograms/look/look.in87
-rw-r--r--programs/lwdnsq/.cvsignore4
-rw-r--r--programs/lwdnsq/CONTRACT.txt106
-rw-r--r--programs/lwdnsq/Makefile96
-rw-r--r--programs/lwdnsq/cmds.c351
-rw-r--r--programs/lwdnsq/lookup.c632
-rw-r--r--programs/lwdnsq/lwdnsq.8250
-rw-r--r--programs/lwdnsq/lwdnsq.c506
-rw-r--r--programs/lwdnsq/lwdnsq.h121
-rw-r--r--programs/lwdnsq/lwdnsq.xml.in446
-rw-r--r--programs/lwdnsq/states.fig66
-rw-r--r--programs/lwdnsq/states.pngbin0 -> 6756 bytes
-rw-r--r--programs/mailkey/.cvsignore1
-rw-r--r--programs/mailkey/Makefile41
-rw-r--r--programs/mailkey/mailkey.847
-rwxr-xr-xprograms/mailkey/mailkey.in241
-rw-r--r--programs/manual/.cvsignore1
-rw-r--r--programs/manual/Makefile38
-rw-r--r--programs/manual/manual.8267
-rwxr-xr-xprograms/manual/manual.in637
-rw-r--r--programs/openac/Makefile154
-rw-r--r--programs/openac/build.c242
-rw-r--r--programs/openac/build.h47
-rw-r--r--programs/openac/loglite.c295
-rw-r--r--programs/openac/openac.8180
-rwxr-xr-xprograms/openac/openac.c438
-rw-r--r--programs/pf_key/.cvsignore1
-rw-r--r--programs/pf_key/Makefile49
-rw-r--r--programs/pf_key/pf_key.5122
-rw-r--r--programs/pf_key/pf_key.873
-rw-r--r--programs/pf_key/pf_key.c353
-rw-r--r--programs/pluto/.cvsignore3
-rw-r--r--programs/pluto/Makefile1090
-rw-r--r--programs/pluto/PLUTO-CONVENTIONS127
-rw-r--r--programs/pluto/TODO129
-rw-r--r--programs/pluto/ac.c1018
-rw-r--r--programs/pluto/ac.h103
-rw-r--r--programs/pluto/adns.c615
-rw-r--r--programs/pluto/adns.h75
-rw-r--r--programs/pluto/alg/Config.ike_alg9
-rw-r--r--programs/pluto/alg/Makefile93
-rw-r--r--programs/pluto/alg/Makefile.ike_alg_aes14
-rw-r--r--programs/pluto/alg/Makefile.ike_alg_blowfish13
-rw-r--r--programs/pluto/alg/Makefile.ike_alg_serpent13
-rw-r--r--programs/pluto/alg/Makefile.ike_alg_sha213
-rw-r--r--programs/pluto/alg/Makefile.ike_alg_twofish13
-rw-r--r--programs/pluto/alg/ike_alg_aes.c68
-rw-r--r--programs/pluto/alg/ike_alg_blowfish.c52
-rw-r--r--programs/pluto/alg/ike_alg_serpent.c70
-rw-r--r--programs/pluto/alg/ike_alg_sha2.c61
-rw-r--r--programs/pluto/alg/ike_alg_twofish.c85
-rw-r--r--programs/pluto/alg_info.c1197
-rw-r--r--programs/pluto/alg_info.h85
-rw-r--r--programs/pluto/asn1.c770
-rw-r--r--programs/pluto/asn1.h141
-rw-r--r--programs/pluto/ca.c694
-rw-r--r--programs/pluto/ca.h70
-rw-r--r--programs/pluto/certs.c287
-rw-r--r--programs/pluto/certs.h80
-rw-r--r--programs/pluto/connections.c4431
-rw-r--r--programs/pluto/connections.h375
-rw-r--r--programs/pluto/constants.c1271
-rw-r--r--programs/pluto/constants.h1184
-rw-r--r--programs/pluto/cookie.c67
-rw-r--r--programs/pluto/cookie.h24
-rw-r--r--programs/pluto/crl.c763
-rw-r--r--programs/pluto/crl.h87
-rw-r--r--programs/pluto/crypto.c261
-rw-r--r--programs/pluto/crypto.h107
-rw-r--r--programs/pluto/db_ops.c439
-rw-r--r--programs/pluto/db_ops.h56
-rw-r--r--programs/pluto/defs.c374
-rw-r--r--programs/pluto/defs.h145
-rw-r--r--programs/pluto/demux.c2411
-rw-r--r--programs/pluto/demux.h100
-rw-r--r--programs/pluto/dnskey.c1962
-rw-r--r--programs/pluto/dnskey.h84
-rw-r--r--programs/pluto/dsa.c476
-rw-r--r--programs/pluto/dsa.h32
-rw-r--r--programs/pluto/elgamal.c613
-rw-r--r--programs/pluto/elgamal.h35
-rw-r--r--programs/pluto/fetch.c1081
-rw-r--r--programs/pluto/fetch.h79
-rw-r--r--programs/pluto/foodgroups.c462
-rw-r--r--programs/pluto/foodgroups.h24
-rw-r--r--programs/pluto/gcryptfix.c283
-rw-r--r--programs/pluto/gcryptfix.h111
-rw-r--r--programs/pluto/id.c509
-rw-r--r--programs/pluto/id.h67
-rw-r--r--programs/pluto/ike_alg.c459
-rw-r--r--programs/pluto/ike_alg.h73
-rw-r--r--programs/pluto/ipsec.secrets.5175
-rw-r--r--programs/pluto/ipsec_doi.c5649
-rw-r--r--programs/pluto/ipsec_doi.h104
-rw-r--r--programs/pluto/kameipsec.h47
-rw-r--r--programs/pluto/kernel.c2997
-rw-r--r--programs/pluto/kernel.h200
-rw-r--r--programs/pluto/kernel_alg.c775
-rw-r--r--programs/pluto/kernel_alg.h46
-rw-r--r--programs/pluto/kernel_netlink.c1221
-rw-r--r--programs/pluto/kernel_netlink.h20
-rw-r--r--programs/pluto/kernel_noklips.c126
-rw-r--r--programs/pluto/kernel_noklips.h19
-rw-r--r--programs/pluto/kernel_pfkey.c938
-rw-r--r--programs/pluto/kernel_pfkey.h23
-rw-r--r--programs/pluto/keys.c1404
-rw-r--r--programs/pluto/keys.h110
-rw-r--r--programs/pluto/lex.c213
-rw-r--r--programs/pluto/lex.h52
-rw-r--r--programs/pluto/linux26/netlink.h90
-rw-r--r--programs/pluto/linux26/rtnetlink.h562
-rw-r--r--programs/pluto/linux26/xfrm.h233
-rw-r--r--programs/pluto/log.c843
-rw-r--r--programs/pluto/log.h236
-rw-r--r--programs/pluto/md2.c237
-rw-r--r--programs/pluto/md2.h72
-rw-r--r--programs/pluto/md5.c385
-rw-r--r--programs/pluto/md5.h75
-rw-r--r--programs/pluto/modecfg.c798
-rw-r--r--programs/pluto/modecfg.h33
-rw-r--r--programs/pluto/mp_defs.c70
-rw-r--r--programs/pluto/mp_defs.h36
-rw-r--r--programs/pluto/nat_traversal.c869
-rw-r--r--programs/pluto/nat_traversal.h154
-rw-r--r--programs/pluto/ocsp.c1568
-rw-r--r--programs/pluto/ocsp.h85
-rw-r--r--programs/pluto/oid.c197
-rw-r--r--programs/pluto/oid.h75
-rw-r--r--programs/pluto/oid.pl123
-rw-r--r--programs/pluto/oid.txt184
-rw-r--r--programs/pluto/packet.c1244
-rw-r--r--programs/pluto/packet.h655
-rw-r--r--programs/pluto/pem.c463
-rw-r--r--programs/pluto/pem.h18
-rw-r--r--programs/pluto/pgp.c647
-rw-r--r--programs/pluto/pgp.h54
-rw-r--r--programs/pluto/pkcs1.c635
-rw-r--r--programs/pluto/pkcs1.h88
-rw-r--r--programs/pluto/pkcs7.c862
-rw-r--r--programs/pluto/pkcs7.h51
-rw-r--r--programs/pluto/pluto-style.el4
-rw-r--r--programs/pluto/pluto.81649
-rw-r--r--programs/pluto/plutomain.c696
-rw-r--r--programs/pluto/primegen.c593
-rw-r--r--programs/pluto/rcv_info.c308
-rw-r--r--programs/pluto/rcv_info.h18
-rw-r--r--programs/pluto/rcv_whack.c655
-rw-r--r--programs/pluto/rcv_whack.h17
-rw-r--r--programs/pluto/rnd.c250
-rw-r--r--programs/pluto/rnd.h21
-rw-r--r--programs/pluto/routing.txt331
-rw-r--r--programs/pluto/rsaref/pkcs11.h299
-rw-r--r--programs/pluto/rsaref/pkcs11f.h912
-rw-r--r--programs/pluto/rsaref/pkcs11t.h1685
-rw-r--r--programs/pluto/rsaref/unix.h24
-rw-r--r--programs/pluto/server.c1064
-rw-r--r--programs/pluto/server.h60
-rw-r--r--programs/pluto/sha1.c193
-rw-r--r--programs/pluto/sha1.h16
-rw-r--r--programs/pluto/smallprime.c122
-rw-r--r--programs/pluto/smartcard.c1956
-rw-r--r--programs/pluto/smartcard.h100
-rw-r--r--programs/pluto/spdb.c2402
-rw-r--r--programs/pluto/spdb.h113
-rw-r--r--programs/pluto/state.c1007
-rw-r--r--programs/pluto/state.h269
-rw-r--r--programs/pluto/timer.c537
-rw-r--r--programs/pluto/timer.h34
-rw-r--r--programs/pluto/vendor.c493
-rw-r--r--programs/pluto/vendor.h107
-rw-r--r--programs/pluto/virtual.c338
-rw-r--r--programs/pluto/virtual.h31
-rw-r--r--programs/pluto/whack.c1911
-rw-r--r--programs/pluto/whack.h318
-rw-r--r--programs/pluto/x509.c2241
-rw-r--r--programs/pluto/x509.h138
-rw-r--r--programs/proc/Makefile51
-rw-r--r--programs/proc/trap_count.535
-rw-r--r--programs/proc/trap_sendcount.533
-rw-r--r--programs/proc/version.554
-rw-r--r--programs/ranbits/.cvsignore1
-rw-r--r--programs/ranbits/Makefile39
-rw-r--r--programs/ranbits/ranbits.877
-rw-r--r--programs/ranbits/ranbits.c146
-rw-r--r--programs/rsasigkey/.cvsignore1
-rw-r--r--programs/rsasigkey/Makefile39
-rw-r--r--programs/rsasigkey/rsasigkey.8259
-rw-r--r--programs/rsasigkey/rsasigkey.c573
-rw-r--r--programs/scepclient/Makefile184
-rw-r--r--programs/scepclient/pkcs10.c220
-rw-r--r--programs/scepclient/pkcs10.h57
-rw-r--r--programs/scepclient/rsakey.c349
-rw-r--r--programs/scepclient/rsakey.h31
-rw-r--r--programs/scepclient/scep.c598
-rw-r--r--programs/scepclient/scep.h93
-rw-r--r--programs/scepclient/scepclient.8288
-rw-r--r--programs/scepclient/scepclient.c1036
-rw-r--r--programs/secrets/Makefile38
-rw-r--r--programs/secrets/secrets.820
-rw-r--r--programs/secrets/secrets.in18
-rw-r--r--programs/send-pr/.cvsignore1
-rw-r--r--programs/send-pr/Makefile39
-rw-r--r--programs/send-pr/ipsec_pr.template54
-rw-r--r--programs/send-pr/send-pr.8291
-rwxr-xr-xprograms/send-pr/send-pr.in643
-rw-r--r--programs/setup/.cvsignore1
-rw-r--r--programs/setup/Makefile22
-rw-r--r--programs/setup/setup.8142
-rwxr-xr-xprograms/setup/setup.in162
-rw-r--r--programs/showdefaults/.cvsignore1
-rw-r--r--programs/showdefaults/Makefile38
-rw-r--r--programs/showdefaults/showdefaults.834
-rwxr-xr-xprograms/showdefaults/showdefaults.in33
-rw-r--r--programs/showhostkey/.cvsignore1
-rw-r--r--programs/showhostkey/Makefile38
-rw-r--r--programs/showhostkey/showhostkey.8168
-rwxr-xr-xprograms/showhostkey/showhostkey.in180
-rw-r--r--programs/showpolicy/.cvsignore1
-rw-r--r--programs/showpolicy/Makefile38
-rw-r--r--programs/showpolicy/showpolicy.841
-rw-r--r--programs/showpolicy/showpolicy.c251
-rw-r--r--programs/spi/.cvsignore1
-rw-r--r--programs/spi/Makefile69
-rw-r--r--programs/spi/spi.5213
-rw-r--r--programs/spi/spi.8525
-rw-r--r--programs/spi/spi.c1689
-rw-r--r--programs/spigrp/.cvsignore1
-rw-r--r--programs/spigrp/Makefile52
-rw-r--r--programs/spigrp/spigrp.5116
-rw-r--r--programs/spigrp/spigrp.8174
-rw-r--r--programs/spigrp/spigrp.c491
-rw-r--r--programs/starter/Makefile182
-rw-r--r--programs/starter/README104
-rw-r--r--programs/starter/args.c620
-rw-r--r--programs/starter/args.h34
-rw-r--r--programs/starter/cmp.c105
-rw-r--r--programs/starter/cmp.h29
-rw-r--r--programs/starter/confread.c861
-rw-r--r--programs/starter/confread.h199
-rw-r--r--programs/starter/exec.c54
-rw-r--r--programs/starter/exec.h23
-rw-r--r--programs/starter/files.h47
-rw-r--r--programs/starter/interfaces.c595
-rw-r--r--programs/starter/interfaces.h41
-rw-r--r--programs/starter/invokepluto.c286
-rw-r--r--programs/starter/invokepluto.h28
-rw-r--r--programs/starter/keywords.c235
-rw-r--r--programs/starter/keywords.h164
-rw-r--r--programs/starter/keywords.txt105
-rw-r--r--programs/starter/klips.c134
-rw-r--r--programs/starter/klips.h26
-rw-r--r--programs/starter/lex.yy.c1966
-rw-r--r--programs/starter/netkey.c85
-rw-r--r--programs/starter/netkey.h24
-rw-r--r--programs/starter/parser.h57
-rw-r--r--programs/starter/parser.l190
-rw-r--r--programs/starter/parser.output351
-rw-r--r--programs/starter/parser.tab.c1666
-rw-r--r--programs/starter/parser.tab.h72
-rw-r--r--programs/starter/parser.y283
-rw-r--r--programs/starter/starter.80
-rw-r--r--programs/starter/starter.c571
-rw-r--r--programs/starter/starterwhack.c371
-rw-r--r--programs/starter/starterwhack.h32
-rw-r--r--programs/tncfg/.cvsignore1
-rw-r--r--programs/tncfg/Makefile52
-rw-r--r--programs/tncfg/tncfg.5109
-rw-r--r--programs/tncfg/tncfg.8113
-rw-r--r--programs/tncfg/tncfg.c393
658 files changed, 170345 insertions, 0 deletions
diff --git a/programs/Makefile b/programs/Makefile
new file mode 100644
index 000000000..03c9d582a
--- /dev/null
+++ b/programs/Makefile
@@ -0,0 +1,46 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=spi eroute spigrp tncfg klipsdebug pf_key proc pluto
+SUBDIRS+=_confread _copyright _include _keycensor _plutoload _plutorun
+SUBDIRS+=_realsetup _secretcensor _startklips _updown _updown_espmark
+SUBDIRS+=auto barf ipsec look manual ranbits secrets starter
+SUBDIRS+=rsasigkey send-pr setup showdefaults showhostkey calcgoo mailkey
+SUBDIRS+=ikeping examples openac scepclient
+
+ifeq ($(USE_LWRES),true)
+SUBDIRS+=lwdnsq
+endif
+
+ifeq ($(USE_IPSECPOLICY),true)
+SUBDIRS+=showpolicy
+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/programs/Makefile.program b/programs/Makefile.program
new file mode 100644
index 000000000..6868c258a
--- /dev/null
+++ b/programs/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/programs/_confread/.cvsignore b/programs/_confread/.cvsignore
new file mode 100644
index 000000000..405492384
--- /dev/null
+++ b/programs/_confread/.cvsignore
@@ -0,0 +1,7 @@
+_confread
+ipsec.conf
+block
+clear
+private
+clear-or-private
+private-or-clear
diff --git a/programs/_confread/Makefile b/programs/_confread/Makefile
new file mode 100644
index 000000000..1bdc9a3f0
--- /dev/null
+++ b/programs/_confread/Makefile
@@ -0,0 +1,27 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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 2004/03/31 19:23:00 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=_confread
+PROGRAMDIR=${LIBDIR}
+EXTRA5MAN=ipsec.conf.5
+CONFFILES=ipsec.conf
+
+CONFDSUBDIR=policies
+CONFDFILES=clear clear-or-private private-or-clear private block
+
+include ../Makefile.program
diff --git a/programs/_confread/README.conf.V2 b/programs/_confread/README.conf.V2
new file mode 100644
index 000000000..244e245c5
--- /dev/null
+++ b/programs/_confread/README.conf.V2
@@ -0,0 +1,103 @@
+Subject: [Design] changes to ipsec.conf
+# RCSID $Id: README.conf.V2,v 1.1 2004/03/15 20:35:27 as Exp $
+
+We are changing ipsec.conf for the 2.0 series of FreeS/WAN.
+
+OE is enabled by default. This is accomplished by automatically
+defining a conn "OEself" UNLESS the sysadmin defines one with the same
+name:
+
+conn OEself
+ # authby=rsasig # default
+ left=%defaultroute
+ leftrsasigkey=%dnsondemand # default
+ right=%opportunistic
+ rightrsasigkey=%dnsondemand # default
+ keyingtries=3
+ ikelifetime=1h
+ keylife=1h # default
+ rekey=no
+ # disablearrivalcheck=no # default
+ auto=route
+
+This will only work if %defaultroute works.
+The leftid will be the resulting IP address (won't work if
+you haven't filled in the reverse DNS entry).
+Unlike other conns, nothing in this implicit conn is changed by conn %default.
+
+We'd like a better name. A conn name starting with % cannot be
+defined by the sysadmin, so that is out. Names that haven't grabbed
+us: OEhost, OElocalhost, OEthishost, OEforself, OE4self.
+
+There is no requirement to have /etc/ipsec.conf. If you do, the first
+significant line (non-blank, non-comment) must be (not indented):
+version 2.0
+This signifies that the file was intended for FreeS/WAN version 2.0.
+
+
+The following table shows most changes. "-" means that the option
+doesn't exist. "Recent Boilerplate" shows the effect of the "conn
+%default" in the automatically installed /etc/ipsec.conf (not
+installed if you already had one).
+
+Option Old Default Recent Boilerplate New Default
+====== =========== ================== ===========
+
+config setup:
+interfaces "" %defaultroute %defaultroute
+plutoload "" %search - [same as %search]
+plutostart "" %search - [same as %search]
+uniqueids no yes yes
+rp_filter - - 0
+plutowait yes yes no
+dump no no - [use dumpdir]
+plutobackgroundload ignored ignored -
+no_eroute_pass no no - [use packetdefault]
+
+conn %default:
+keyingtries 3 0 %forever [0 means this]
+disablearrivalcheck yes no no
+authby secret rsasig rsasig
+leftrsasigkey "" %dnsondemand %dnsondemand
+rightrsasigkey "" %dnsondemand %dnsondemand
+lifetime ==keylife ==keylife - [use keylife]
+rekeystart ==rekeymargin ==rekeymargin - [use rekeymargin]
+rekeytries ==keyingtries ==keyingtries - [use keyingtries]
+
+====== =========== ================== ===========
+Option Old Default Recent Boilerplate New Default
+
+
+The auto= mechanism has been extended to support manual conns. If you
+specify auto=manual in a conn, an "ipsec manual" will be performed on
+it at startup (ipsec setup start).
+
+
+There is a new config setup option "rp_filter". It controls
+ /proc/sys/net/ipv4/conf/PHYS/rp_filter
+for each PHYSical IP interface used by FreeS/WAN. Settings are:
+ %unchanged do not touch (but warn if wrong)
+ 0 set to 0; default; means: no filtering
+ 1 set to 1; means: loose filter
+ 2 set to 1; means: strict filter
+0 is often necessary for FreeS/WAN to function. Some folks
+want other settings. Shutting down FreeS/WAN does not restore
+the original value.
+
+Currently ikelife defaults to 1 hour and keylife defaults to 8 hours.
+There have been some rumblings that these are the wrong defaults, but
+it isn't clear what would be best. Perhaps both should be closer.
+Any thoughts of what these should be? Any Road Warrior or OE conn
+should probably have carefully thought-out values explicitly
+specified. The settings don't matter much for VPN connections.
+
+keyingtries=%forever is the new improved notation for keyingtries=0.
+Eventually the 0 notation will be eliminated.
+
+Some options can now be set to %none to signify no setting. Otherwise
+there would be no way for the user to override a default setting:
+ leftrsasigkey, rightrsasigkey [added in 1.98]
+ interfaces
+
+Hugh Redelmeier
+hugh@mimosa.com voice: +1 416 482-8253
diff --git a/programs/_confread/_confread.8 b/programs/_confread/_confread.8
new file mode 100644
index 000000000..20d92a002
--- /dev/null
+++ b/programs/_confread/_confread.8
@@ -0,0 +1,28 @@
+.TH _CONFREAD 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _confread.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _confread \- internal routing to parse config file
+.SH DESCRIPTION
+.I _confread
+is an internal script used for parsing /etc/ipsec.conf into a canonical format.
+.SH "SEE ALSO"
+ipsec(8), ipsec_conf(8)
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Program written by Henry Spencer.
+.\"
+.\" $Log: _confread.8,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/09/16 01:28:43 dhr
+.\"
+.\" typo
+.\"
+.\" Revision 1.2 2002/04/29 22:39:31 mcr
+.\" added basic man page for all internal commands.
+.\"
+.\"
+.\"
diff --git a/programs/_confread/_confread.in b/programs/_confread/_confread.in
new file mode 100755
index 000000000..4561af9fe
--- /dev/null
+++ b/programs/_confread/_confread.in
@@ -0,0 +1,520 @@
+#!/bin/sh
+# configuration-file reader utility
+# Copyright (C) 1999-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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _confread.in,v 1.15 2006/04/20 04:42:12 as Exp $
+#
+# Extract configuration info from /etc/ipsec.conf, repackage as assignments
+# to shell variables or tab-delimited fields. Success or failure is reported
+# inline, as extra data, due to the vagaries of shell backquote handling.
+# In the absence of --varprefix, output is tab-separated fields, like:
+# = sectionname
+# : parameter value
+# ! status (empty for success, else complaint)
+# In the presence of (say) "--varprefix IPSEC", output is like:
+# IPSEC_confreadsection="sectionname"
+# IPSECparameter="value"
+# IPSEC_confreadstatus="status" (same empty/complaint convention)
+#
+# The "--search parametername" option inverts the search: instead of
+# yielding the parameters of the specified name(s), it yields the names
+# of sections with parameter <parametername> having (one of) the
+# specified value(s). In this case, --varprefix output is a list of
+# names in the <prefix>_confreadnames variable. Search values with
+# white space in them are currently not handled properly.
+#
+# Typical usage:
+# eval `ipsec _confread --varprefix IPSEC --type config setup`
+# if test " $IPSEC_confreadstatus" != " "
+# then
+# echo "$0: $IPSEC_confreadstatus -- aborting" 2>&1
+# exit 1
+# fi
+
+# absent default config file treated as empty
+config=${IPSEC_CONFS-@FINALCONFDIR@}/ipsec.conf
+if test ! -f "$config" ; then config=/dev/null ; fi
+
+include=yes
+type=conn
+fieldfmt=yes
+prefix=
+search=
+export=0
+version=
+optional=0
+me="ipsec _confread"
+
+for dummy
+do
+ case "$1" in
+ --config) config="$2" ; shift ;;
+ --noinclude) include= ;;
+ --type) type="$2" ; shift ;;
+ --varprefix) fieldfmt=
+ prefix="$2"
+ shift ;;
+ --export) export=1 ;;
+ --search) search="$2" ; shift ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --optional) optional=1 ;;
+ --) shift ; break ;;
+ -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+if test "$include"
+then
+ ipsec _include --inband $config
+else
+ cat $config
+fi |
+awk 'BEGIN {
+ type = "'"$type"'"
+ names = "'"$*"'"
+ prefix = "'"$prefix"'"
+ export = "'"$export"'"
+ optional = 0 + '"$optional"'
+ myid = "'"$IPSECmyid"'"
+ search = "'"$search"'"
+ searching = 0
+ if (search != "") {
+ searching = 1
+ searchpat = search "[ \t]*=[ \t]*"
+ }
+ fieldfmt = 0
+ if ("'"$fieldfmt"'" == "yes")
+ fieldfmt = 1
+ including = 0
+ if ("'"$include"'" == "yes")
+ including = 1
+ filename = "'"$config"'"
+ lineno = 0
+ originalfilename = filename
+ if (fieldfmt)
+ bq = eq = "\""
+ else
+ bq = eq = "\\\""
+ failed = 0
+ insection = 0
+ wrongtype = 0
+ indefault = 0
+ outputting = 0
+ sawnondefault = 0
+ OFS = "\t"
+ o_status = "!"
+ o_parm = ":"
+ o_section = "="
+ o_names = "%"
+ o_end = "."
+ n = split(names, na, " ")
+ if (n == 0)
+ fail("no section names supplied")
+ for (i = 1; i <= n; i++) {
+ if (na[i] in wanted)
+ fail("section " bq na[i] eq " requested more than once")
+ wanted[na[i]] = 1
+ pending[na[i]] = 1
+ if (!searching && na[i] !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/)
+ fail("invalid section name " bq na[i] eq)
+ }
+
+ good = "also alsoflip type auto authby _plutodevel"
+ left = " left leftsubnet leftnexthop leftfirewall lefthostaccess leftupdown"
+ akey = " keyexchange auth pfs keylife rekey rekeymargin rekeyfuzz"
+ akey = akey " dpdaction dpddelay dpdtimeout"
+ akey = akey " pfsgroup compress"
+ akey = akey " keyingtries ikelifetime disablearrivalcheck failureshunt ike"
+ mkey = " spibase spi esp espenckey espauthkey espreplay_window"
+ left = left " leftespenckey leftespauthkey leftahkey"
+ left = left " leftespspi leftahspi leftid leftrsasigkey leftrsasigkey2"
+ left = left " leftsendcert leftcert leftca leftsubnetwithin leftprotoport"
+ left = left " leftgroups leftsourceip"
+ mkey = mkey " ah ahkey ahreplay_window"
+ right = left
+ gsub(/left/, "right", right)
+ n = split(good left right akey mkey, g)
+ for (i = 1; i <= n; i++)
+ goodnames["conn:" g[i]] = 1
+
+ good = "also interfaces forwardcontrol myid"
+ good = good " syslog klipsdebug plutodebug plutoopts plutostderrlog"
+ good = good " plutorestartoncrash"
+ good = good " dumpdir manualstart pluto"
+ good = good " plutowait prepluto postpluto"
+ good = good " fragicmp hidetos rp_filter uniqueids"
+ good = good " overridemtu pkcs11module pkcs11keepstate pkcs11proxy"
+ good = good " nocrsend strictcrlpolicy crlcheckinterval cachecrls"
+ good = good " nat_traversal keep_alive force_keepalive"
+ good = good " disable_port_floating virtual_private"
+
+ n = split(good, g)
+ for (i = 1; i <= n; i++)
+ goodnames["config:" g[i]] = 1
+
+ good = "auto cacert ldaphost ldapbase crluri crluri2 ocspuri"
+ good = good " strictcrlpolicy"
+
+ n = split(good, g)
+ for (i = 1; i <= n; i++)
+ goodnames["ca:" g[i]] = 1
+
+ goodtypes["conn"] = 1
+ goodtypes["config"] = 1
+ goodtypes["ca"] = 1
+
+ badchars = ""
+ for (i = 1; i < 32; i++)
+ badchars = badchars sprintf("%c", i)
+ for (i = 127; i < 128+32; i++)
+ badchars = badchars sprintf("%c", i)
+ badchar = "[" badchars "]"
+
+ # if searching, seen is set of sectionnames which match
+ # if not searching, seen is set of parameter names found
+ seen[""] = ""
+ defaults[""] = ""
+ usesdefault[""] = ""
+ orientation = 1
+}
+
+
+
+function output(code, v1, v2) {
+ if (code == o_parm) {
+ if (v2 == "") # suppress empty parameters
+ return
+ if (privatename(v1)) # and private ones
+ return
+ if (v2 ~ badchar)
+ fail("parameter value " bq v2 eq " contains unprintable character")
+ }
+
+ if (fieldfmt) {
+ print code, v1, v2
+ return
+ }
+
+ if (code == o_status) {
+ v2 = v1
+ v1 = "_confreadstatus"
+ } else if (code == o_section) {
+ v2 = v1
+ v1 = "_confreadsection"
+ } else if (code == o_names) {
+ v2 = v1
+ v1 = "_confreadnames"
+ } else if (code != o_parm)
+ return # currently no variable version of o_end
+
+ print prefix v1 "=\"" v2 "\""
+ if (export)
+ print "export " prefix v1
+}
+function searchfound(sectionname, n, i, reflist) {
+ # a hit in x is a hit in everybody who refers to x too
+ n = split(refsto[sectionname], reflist, ";")
+ for (i = 1; i <= n; i++)
+ if (reflist[i] in seen)
+ fail("duplicated parameter " bq search eq)
+ else
+ seen[reflist[i]] = 1
+ seen[sectionname] = 1
+}
+function fail(msg) {
+ output(o_status, ("(" filename ", line " lineno ") " msg))
+ failed = 1
+ while ((getline junk) > 0)
+ continue
+ exit
+}
+function badname(n) {
+ if ((type ":" n) in goodnames)
+ return 0
+ if (privatename(n))
+ return 0
+ return 1
+}
+function privatename(n) {
+ if (n ~ /^[xX][-_]/)
+ return 1
+ return 0
+}
+function orient(n) {
+ if (orientation == -1) {
+ if (n ~ /left/)
+ gsub(/left/, "right", n)
+ else if (n ~ /right/)
+ gsub(/right/, "left", n)
+ }
+ return n
+}
+# in searching, referencing is transitive: xyz->from->to
+function chainref(from, to, i, reflist, listnum) {
+ if (from in refsto) {
+ listnum = split(refsto[from], reflist, ";")
+ for (i = 1; i <= listnum; i++)
+ chainref(reflist[i], to)
+ }
+ if (to in refsto)
+ refsto[to] = refsto[to] ";" from
+ else
+ refsto[to] = from
+}
+
+# start of rules
+
+{
+ lineno++
+ # lineno is now the number of this line
+
+ # we must remember indentation because comment stripping loses it
+ exdented = $0 !~ /^[ \t]/
+ sub(/^[ \t]+/, "") # get rid of leading white space
+ sub(/[ \t]+$/, "") # get rid of trailing white space
+}
+including && $0 ~ /^#[<>:]/ {
+ # _include control line
+ if ($1 ~ /^#[<>]$/) {
+ filename = $2
+ lineno = $3 - 1
+ } else if ($0 ~ /^#:/) {
+ msg = substr($0, 3)
+ gsub(/"/, "\\\"", msg)
+ fail(msg)
+ }
+ next
+}
+exdented {
+ # any non-leading-white-space line is a section end
+ ### but not the end of relevant stuff, might be also= sections later
+ ###if (insection && !indefault && !searching && outputting)
+ ### output(o_end)
+ insection = 0
+ wrongtype = 0
+ indefault = 0
+ outputting = 0
+}
+/[ \t]#/ {
+ # strip trailing comments including the leading whitespace
+ # tricky because we must respect quotes
+ q = 0
+ for (i = 1; i <= NF; i++) {
+ if ($i ~ /^#/ && q % 2 == 0) {
+ NF = i - 1;
+ break
+ }
+ # using $i in gsub loses whitespace?!?
+ junk = $i
+ q += gsub(/"/, "&", junk)
+ }
+}
+$0 == "" || $0 ~ /^#/ {
+ # empty lines and comments are ignored
+ next
+}
+exdented && NF != 2 {
+ # bad section header
+ fail("section header " bq $0 eq " has wrong number of fields (" NF ")")
+}
+exdented && $1 == "version" {
+ version = $2 + 0
+ if (version < 2.0 || 2.0 < version)
+ fail("we only support version 2.0 ipsec.conf files, not " bq version eq)
+ next
+}
+version == "" {
+ fail("we only support version 2 ipsec.conf files")
+}
+exdented && !($1 in goodtypes) {
+ # unknown section type
+ fail("section type " bq $1 eq " not recognized")
+}
+exdented && $1 != type {
+ # section header, but not of the type we want
+ insection = 1
+ wrongtype = 1
+ next
+}
+extented {
+ # type fits
+ wrongtype = 0
+}
+exdented && $1 == "config" && $2 != "setup" {
+ fail("unknown config section " bq $2 eq)
+}
+exdented && $2 != "%default" {
+ # non-default section header of our type
+ sawnondefault = 1
+}
+exdented && searching && $2 != "%default" {
+ # section header, during search
+ insection = 1
+ sectionname = $2
+ usesdefault[sectionname] = 1 # tentatively
+ next
+}
+exdented && !searching && $2 in wanted {
+ # one of our wanted section headers
+ if (!($2 in pending))
+ fail("duplicate " type " section " bq $2 eq)
+ delete pending[$2]
+ tag = bq type " " $2 eq
+ outputting = 1
+ insection = 1
+ orientation = wanted[$2]
+ output(o_section, $2)
+ next
+}
+exdented && $2 == "%default" {
+ # relevant default section header
+ if (sawnondefault)
+ fail(bq $1 " %default" eq " sections must precede non-default ones")
+ tag = bq type " " $2 eq
+ indefault = 1
+ next
+}
+exdented {
+ # section header, but not one we want
+ insection = 1
+ next
+}
+!insection && !indefault {
+ # starts with white space but not in a section... oops
+ fail("parameter is not within a section")
+}
+!wrongtype && searching && $0 ~ searchpat {
+ # search found the right parameter name
+ match($0, searchpat)
+ rest = substr($0, RLENGTH+1)
+ if (rest ~ /^".*"$/)
+ rest = substr(rest, 2, length(rest)-2)
+ if (!indefault) {
+ if (!usesdefault[sectionname])
+ fail("duplicated parameter " bq search eq)
+ usesdefault[sectionname] = 0
+ } else if (search in defaults)
+ fail("duplicated parameter " bq search eq)
+ if (rest in wanted) { # a hit
+ if (indefault)
+ defaults[search] = rest
+ else
+ searchfound(sectionname)
+ } else {
+ # rather a kludge, but must check this somewhere
+ if (search == "auto" && rest !~ /^(add|route|start|ignore|manual)$/)
+ fail("illegal auto value " bq rest eq)
+ }
+ next
+}
+!searching && !outputting && !indefault {
+ # uninteresting line
+ next
+}
+$0 ~ /"/ && $0 !~ /^[^=]+=[ \t]*"[^"]*"$/ {
+ if (!searching)
+ fail("mismatched quotes in parameter value")
+ else
+ gsub(/"/, "", $0)
+}
+$0 !~ /^[a-zA-Z_][a-zA-Z0-9_-]*[ \t]*=/ {
+ if (searching)
+ next # just ignore it
+ fail("syntax error or illegal parameter name")
+}
+{
+ sub(/[ \t]*=[ \t]*/, "=") # get rid of white space around =
+}
+$0 ~ /^(also|alsoflip)=/ {
+ v = orientation
+ if ($0 ~ /^alsoflip/)
+ v = -v;
+ if (indefault)
+ fail("%default section may not contain " bq "also" eq " or " bq "alsoflip" eq " parameter")
+ sub(/^(also|alsoflip)=/, "")
+ if ($0 !~ /^[a-zA-Z][a-zA-Z0-9._-]*$/)
+ fail("invalid section name " bq $0 eq)
+ if (!searching) {
+ if ($0 in wanted)
+ fail("section " bq $0 eq " requested more than once")
+ wanted[$0] = v
+ pending[$0] = 1
+ } else
+ chainref(sectionname, $0)
+ next
+}
+!outputting && !indefault {
+ # uninteresting line even for a search
+ next
+}
+{
+ equal = match($0, /[=]/)
+ name = substr($0, 1, equal-1)
+ if (badname(name))
+ fail("unknown parameter name " bq name eq)
+ value = substr($0, equal+1)
+ if (value ~ /^"/)
+ value = substr(value, 2, length(value)-2)
+ else if (value ~ /[ \t]/)
+ fail("white space within non-quoted parameter " bq name eq)
+}
+indefault {
+ if (name in defaults)
+ fail("duplicated default parameter " bq name eq)
+ defaults[name] = value
+ next
+}
+{
+ name = orient(name)
+ if (name in seen)
+ fail("duplicated parameter " bq name eq)
+ seen[name] = 1
+ output(o_parm, name, value)
+}
+END {
+ if (failed)
+ exit 1
+
+ filename = originalfilename
+ unseen = ""
+ for (i in pending)
+ unseen = unseen " " i
+ if (!optional && !searching && unseen != "")
+ fail("did not find " type " section(s) " bq substr(unseen, 2) eq)
+ if (!searching) {
+ for (name in defaults)
+ if (!(name in seen))
+ output(o_parm, name, defaults[name])
+ } else {
+ if (defaults[search] in wanted)
+ for (name in usesdefault)
+ if (usesdefault[name])
+ seen[name] = 1
+ delete seen[""]
+ if (fieldfmt)
+ for (name in seen)
+ output(o_section, name)
+ else {
+ outlist = ""
+ for (name in seen)
+ if (outlist == "")
+ outlist = name
+ else
+ outlist = outlist " " name
+ output(o_names, outlist)
+ }
+ }
+ output(o_status, "")
+}'
diff --git a/programs/_confread/block.in b/programs/_confread/block.in
new file mode 100644
index 000000000..e3a4b2dd5
--- /dev/null
+++ b/programs/_confread/block.in
@@ -0,0 +1,8 @@
+# This file defines the set of CIDRs (network/mask-length) to which
+# communication should never be allowed.
+#
+# See @FINALDOCDIR@/policygroups.html for details.
+#
+# $Id: block.in,v 1.1 2004/03/15 20:35:27 as Exp $
+#
+
diff --git a/programs/_confread/clear-or-private.in b/programs/_confread/clear-or-private.in
new file mode 100644
index 000000000..800093d94
--- /dev/null
+++ b/programs/_confread/clear-or-private.in
@@ -0,0 +1,8 @@
+# This file defines the set of CIDRs (network/mask-length) to which
+# we will communicate in the clear, or, if the other side initiates IPSEC,
+# using encryption. This behaviour is also called "Opportunistic Responder".
+#
+# See @FINALDOCDIR@/policygroups.html for details.
+#
+# $Id: clear-or-private.in,v 1.1 2004/03/15 20:35:27 as Exp $
+#
diff --git a/programs/_confread/clear.in b/programs/_confread/clear.in
new file mode 100644
index 000000000..46e63388e
--- /dev/null
+++ b/programs/_confread/clear.in
@@ -0,0 +1,7 @@
+# This file defines the set of CIDRs (network/mask-length) to which
+# communication should always be in the clear.
+#
+# See @FINALDOCDIR@/policygroups.html for details.
+#
+# $Id: clear.in,v 1.1 2004/03/15 20:35:27 as Exp $
+#
diff --git a/programs/_confread/ipsec.conf.5 b/programs/_confread/ipsec.conf.5
new file mode 100644
index 000000000..af6fae6bd
--- /dev/null
+++ b/programs/_confread/ipsec.conf.5
@@ -0,0 +1,1286 @@
+.TH IPSEC.CONF 5 "20 Jan 2006"
+.\" RCSID $Id: ipsec.conf.5,v 1.2 2006/01/22 15:33:46 as Exp $
+.SH NAME
+ipsec.conf \- IPsec configuration and connections
+.SH DESCRIPTION
+The optional
+.I ipsec.conf
+file
+specifies most configuration and control information for the
+strongSwan IPsec subsystem.
+(The major exception is secrets for authentication;
+see
+.IR ipsec.secrets (5).)
+Its contents are not security-sensitive
+.I unless
+manual keying is being done for more than just testing,
+in which case the encryption/authentication keys in the
+descriptions for the manually-keyed connections are very sensitive
+(and those connection descriptions
+are probably best kept in a separate file,
+via the include facility described below).
+.PP
+The file is a text file, consisting of one or more
+.IR sections .
+White space followed by
+.B #
+followed by anything to the end of the line
+is a comment and is ignored,
+as are empty lines which are not within a section.
+.PP
+A line which contains
+.B include
+and a file name, separated by white space,
+is replaced by the contents of that file,
+preceded and followed by empty lines.
+If the file name is not a full pathname,
+it is considered to be relative to the directory containing the
+including file.
+Such inclusions can be nested.
+Only a single filename may be supplied, and it may not contain white space,
+but it may include shell wildcards (see
+.IR sh (1));
+for example:
+.PP
+.B include
+.B "ipsec.*.conf"
+.PP
+The intention of the include facility is mostly to permit keeping
+information on connections, or sets of connections,
+separate from the main configuration file.
+This permits such connection descriptions to be changed,
+copied to the other security gateways involved, etc.,
+without having to constantly extract them from the configuration
+file and then insert them back into it.
+Note also the
+.B also
+parameter (described below) which permits splitting a single logical
+section (e.g. a connection description) into several actual sections.
+.PP
+The first significant line of the file must specify the version
+of this specification that it conforms to:
+.PP
+\fBversion 2\fP
+.PP
+A section
+begins with a line of the form:
+.PP
+.I type
+.I name
+.PP
+where
+.I type
+indicates what type of section follows, and
+.I name
+is an arbitrary name which distinguishes the section from others
+of the same type.
+(Names must start with a letter and may contain only
+letters, digits, periods, underscores, and hyphens.)
+All subsequent non-empty lines
+which begin with white space are part of the section;
+comments within a section must begin with white space too.
+There may be only one section of a given type with a given name.
+.PP
+Lines within the section are generally of the form
+.PP
+\ \ \ \ \ \fIparameter\fB=\fIvalue\fR
+.PP
+(note the mandatory preceding white space).
+There can be white space on either side of the
+.BR = .
+Parameter names follow the same syntax as section names,
+and are specific to a section type.
+Unless otherwise explicitly specified,
+no parameter name may appear more than once in a section.
+.PP
+An empty
+.I value
+stands for the system default value (if any) of the parameter,
+i.e. it is roughly equivalent to omitting the parameter line entirely.
+A
+.I value
+may contain white space only if the entire
+.I value
+is enclosed in double quotes (\fB"\fR);
+a
+.I value
+cannot itself contain a double quote,
+nor may it be continued across more than one line.
+.PP
+Numeric values are specified to be either an ``integer''
+(a sequence of digits) or a ``decimal number''
+(sequence of digits optionally followed by `.' and another sequence of digits).
+.PP
+There is currently one parameter which is available in any type of
+section:
+.TP
+.B also
+the value is a section name;
+the parameters of that section are appended to this section,
+as if they had been written as part of it.
+The specified section must exist, must follow the current one,
+and must have the same section type.
+(Nesting is permitted,
+and there may be more than one
+.B also
+in a single section,
+although it is forbidden to append the same section more than once.)
+This allows, for example, keeping the encryption keys
+for a connection in a separate file
+from the rest of the description, by using both an
+.B also
+parameter and an
+.B include
+line.
+.PP
+Parameter names beginning with
+.B x-
+(or
+.BR X- ,
+or
+.BR x_ ,
+or
+.BR X_ )
+are reserved for user extensions and will never be assigned meanings
+by IPsec.
+Parameters with such names must still observe the syntax rules
+(limits on characters used in the name;
+no white space in a non-quoted value;
+no newlines or double quotes within the value).
+All other as-yet-unused parameter names are reserved for future IPsec
+improvements.
+.PP
+A section with name
+.B %default
+specifies defaults for sections of the same type.
+For each parameter in it,
+any section of that type which does not have a parameter of the same name
+gets a copy of the one from the
+.B %default
+section.
+There may be multiple
+.B %default
+sections of a given type,
+but only one default may be supplied for any specific parameter name,
+and all
+.B %default
+sections of a given type must precede all non-\c
+.B %default
+sections of that type.
+.B %default
+sections may not contain the
+.B also
+parameter.
+.PP
+Currently there are three types of sections:
+a
+.B config
+section specifies general configuration information for IPsec, a
+.B conn
+section specifies an IPsec connection, while a
+.B ca
+section specifies special properties a certification authority.
+.SH "CONN SECTIONS"
+A
+.B conn
+section contains a
+.IR "connection specification" ,
+defining a network connection to be made using IPsec.
+The name given is arbitrary, and is used to identify the connection to
+.IR ipsec_auto (8)
+and
+.IR ipsec_manual (8).
+Here's a simple example:
+.PP
+.ne 10
+.nf
+.ft B
+.ta 1c
+conn snt
+ left=10.11.11.1
+ leftsubnet=10.0.1.0/24
+ leftnexthop=172.16.55.66
+ right=192.168.22.1
+ rightsubnet=10.0.2.0/24
+ rightnexthop=172.16.88.99
+ keyingtries=%forever
+.ft
+.fi
+.PP
+A note on terminology...
+In automatic keying, there are two kinds of communications going on:
+transmission of user IP packets, and gateway-to-gateway negotiations for
+keying, rekeying, and general control.
+The data path (a set of ``IPsec SAs'') used for user packets is herein
+referred to as the ``connection'';
+the path used for negotiations (built with ``ISAKMP SAs'') is referred to as
+the ``keying channel''.
+.PP
+To avoid trivial editing of the configuration file to suit it to each system
+involved in a connection,
+connection specifications are written in terms of
+.I left
+and
+.I right
+participants,
+rather than in terms of local and remote.
+Which participant is considered
+.I left
+or
+.I right
+is arbitrary;
+IPsec figures out which one it is being run on based on internal information.
+This permits using identical connection specifications on both ends.
+There are cases where there is no symmetry; a good convention is to
+use
+.I left
+for the local side and
+.I right
+for the remote side (the first letters are a good mnemonic).
+.PP
+Many of the parameters relate to one participant or the other;
+only the ones for
+.I left
+are listed here, but every parameter whose name begins with
+.B left
+has a
+.B right
+counterpart,
+whose description is the same but with
+.B left
+and
+.B right
+reversed.
+.PP
+Parameters are optional unless marked ``(required)'';
+a parameter required for manual keying need not be included for
+a connection which will use only automatic keying, and vice versa.
+.SS "CONN PARAMETERS: GENERAL"
+The following parameters are relevant to both automatic and manual keying.
+Unless otherwise noted,
+for a connection to work,
+in general it is necessary for the two ends to agree exactly
+on the values of these parameters.
+.TP 14
+.B type
+the type of the connection; currently the accepted values
+are
+.B tunnel
+(the default)
+signifying a host-to-host, host-to-subnet, or subnet-to-subnet tunnel;
+.BR transport ,
+signifying host-to-host transport mode;
+.BR passthrough ,
+signifying that no IPsec processing should be done at all;
+.BR drop ,
+signifying that packets should be discarded; and
+.BR reject ,
+signifying that packets should be discarded and a diagnostic ICMP returned.
+.TP
+.B left
+(required)
+the IP address of the left participant's public-network interface,
+in any form accepted by
+.IR ipsec_ttoaddr (3)
+or one of several magic values.
+If it is
+.BR %defaultroute ,
+and
+the
+.B config
+.B setup
+section's,
+.B interfaces
+specification contains
+.BR %defaultroute,
+.B left
+will be filled in automatically with the local address
+of the default-route interface (as determined at IPsec startup time);
+this also overrides any value supplied for
+.BR leftnexthop .
+(Either
+.B left
+or
+.B right
+may be
+.BR %defaultroute ,
+but not both.)
+The value
+.B %any
+signifies an address to be filled in (by automatic keying) during
+negotiation.
+The value
+.B %opportunistic
+signifies that both
+.B left
+and
+.B leftnexthop
+are to be filled in (by automatic keying) from DNS data for
+.BR left 's
+client.
+The values
+.B %group
+and
+.B %opportunisticgroup
+makes this a policy group conn: one that will be instantiated
+into a regular or opportunistic conn for each CIDR block listed in the
+policy group file with the same name as the conn.
+.TP
+.B leftsubnet
+private subnet behind the left participant, expressed as
+\fInetwork\fB/\fInetmask\fR
+(actually, any form acceptable to
+.IR ipsec_ttosubnet (3));
+if omitted, essentially assumed to be \fIleft\fB/32\fR,
+signifying that the left end of the connection goes to the left participant only
+.TP
+.B leftnexthop
+next-hop gateway IP address for the left participant's connection
+to the public network;
+defaults to
+.B %direct
+(meaning
+.IR right ).
+If the value is to be overridden by the
+.B left=%defaultroute
+method (see above),
+an explicit value must
+.I not
+be given.
+If that method is not being used,
+but
+.B leftnexthop
+is
+.BR %defaultroute ,
+and
+.B interfaces=%defaultroute
+is used in the
+.B config
+.B setup
+section,
+the next-hop gateway address of the default-route interface
+will be used.
+The magic value
+.B %direct
+signifies a value to be filled in (by automatic keying)
+with the peer's address.
+Relevant only locally, other end need not agree on it.
+.TP
+.B leftupdown
+what ``updown'' script to run to adjust routing and/or firewalling
+when the status of the connection
+changes (default
+.BR "ipsec _updown" ).
+May include positional parameters separated by white space
+(although this requires enclosing the whole string in quotes);
+including shell metacharacters is unwise.
+See
+.IR ipsec_pluto (8)
+for details.
+Relevant only locally, other end need not agree on it.
+.TP
+.B leftfirewall
+whether the left participant is doing forwarding-firewalling
+(including masquerading) for traffic from \fIleftsubnet\fR,
+which should be turned off (for traffic to the other subnet)
+once the connection is established;
+acceptable values are
+.B yes
+and (the default)
+.BR no .
+May not be used in the same connection description with
+.BR leftupdown .
+Implemented as a parameter to the default
+.I updown
+script.
+See notes below.
+Relevant only locally, other end need not agree on it.
+.PP
+If one or both security gateways are doing forwarding firewalling
+(possibly including masquerading),
+and this is specified using the firewall parameters,
+tunnels established with IPsec are exempted from it
+so that packets can flow unchanged through the tunnels.
+(This means that all subnets connected in this manner must have
+distinct, non-overlapping subnet address blocks.)
+This is done by the default
+.I updown
+script (see
+.IR ipsec_pluto (8)).
+.PP
+The implementation of this makes certain assumptions about firewall setup,
+notably the use of the old
+.I ipfwadm
+interface to the firewall.
+In situations calling for more control,
+it may be preferable for the user to supply his own
+.I updown
+script,
+which makes the appropriate adjustments for his system.
+.SS "CONN PARAMETERS: AUTOMATIC KEYING"
+The following parameters are relevant only to automatic keying,
+and are ignored in manual keying.
+Unless otherwise noted,
+for a connection to work,
+in general it is necessary for the two ends to agree exactly
+on the values of these parameters.
+.TP 14
+.B auto
+what operation, if any, should be done automatically at IPsec startup;
+currently-accepted values are
+.B add
+(signifying an
+.B ipsec auto
+.BR \-\-add ),
+.B route
+(signifying that plus an
+.B ipsec auto
+.BR \-\-route ),
+.B start
+(signifying that plus an
+.B ipsec auto
+.BR \-\-up ),
+.B manual
+(signifying an
+.B ipsec
+.B manual
+.BR \-\-up ),
+and
+.B ignore
+(also the default) (signifying no automatic startup operation).
+See the
+.B config
+.B setup
+discussion below.
+Relevant only locally, other end need not agree on it
+(but in general, for an intended-to-be-permanent connection,
+both ends should use
+.B auto=start
+to ensure that any reboot causes immediate renegotiation).
+.TP
+.B auth
+whether authentication should be done as part of
+ESP encryption, or separately using the AH protocol;
+acceptable values are
+.B esp
+(the default) and
+.BR ah .
+.TP
+.B authby
+how the two security gateways should authenticate each other;
+acceptable values are
+.B secret
+for shared secrets,
+.B rsasig
+for RSA digital signatures (the default),
+.B secret|rsasig
+for either, and
+.B never
+if negotiation is never to be attempted or accepted (useful for shunt-only conns).
+Digital signatures are superior in every way to shared secrets.
+.TP
+.B compress
+whether IPComp compression of content is proposed on the connection
+(link-level compression does not work on encrypted data,
+so to be effective, compression must be done \fIbefore\fR encryption);
+acceptable values are
+.B yes
+and
+.B no
+(the default).
+The two ends need not agree.
+A value of
+.B yes
+causes IPsec to propose both compressed and uncompressed,
+and prefer compressed.
+A value of
+.B no
+prevents IPsec from proposing compression;
+a proposal to compress will still be accepted.
+.TP
+.B disablearrivalcheck
+whether KLIPS's normal tunnel-exit check
+(that a packet emerging from a tunnel has plausible addresses in its header)
+should be disabled;
+acceptable values are
+.B yes
+and
+.B no
+(the default).
+Tunnel-exit checks improve security and do not break any normal configuration.
+Relevant only locally, other end need not agree on it.
+.TP
+.B dpdaction
+controls the use of the Dead Peer Detection protocol (DPD, RFC 3706) where
+R_U_THERE IKE notification messages are periodically sent in order to check the
+liveliness of the IPsec peer. The default is..
+.B none
+which disables the active sending of R_U_THERE notifications.
+Nevertheless pluto will always send the DPD Vendor ID during connection set up
+in order to signal the readiness to act passively as a responder if the peer
+wants to use DPD. The values
+.B clear
+and
+.B hold
+both activate DPD. If no activity is detected, all connections with a dead peer
+are stopped and unrouted (
+.B clear
+) or put in the hold state (
+.B hold
+).
+.TP
+.B dpddelay
+defines the period time interval with which R_U_THERE messages are sent to the peer.
+.TP
+.B dpdtimeout
+defines the timeout interval, after which all connections to a peer are deleted
+in case of inactivity.
+.TP
+.B failureshunt
+what to do with packets when negotiation fails.
+The default is
+.BR none :
+no shunt;
+.BR passthrough ,
+.BR drop ,
+and
+.B reject
+have the obvious meanings.
+.TP
+.B ikelifetime
+how long the keying channel of a connection (buzzphrase: ``ISAKMP SA'')
+should last before being renegotiated;
+acceptable values as for
+.B keyexchange
+method of key exchange;
+the default and currently the only accepted value is
+.B ike
+.TP
+.B keylife
+(default set by
+.IR ipsec_pluto (8),
+currently
+.BR 3h ,
+maximum
+.BR 24h ).
+The two-ends-disagree case is similar to that of
+.BR keylife .
+.TP
+.B keyingtries
+how many attempts (a whole number or \fB%forever\fP) should be made to
+negotiate a connection, or a replacement for one, before giving up
+(default
+.BR %forever ).
+The value \fB%forever\fP
+means ``never give up'' (obsolete: this can be written \fB0\fP).
+Relevant only locally, other end need not agree on it.
+.TP
+.B keylife
+how long a particular instance of a connection
+(a set of encryption/authentication keys for user packets) should last,
+from successful negotiation to expiry;
+acceptable values are an integer optionally followed by
+.BR s
+(a time in seconds)
+or a decimal number followed by
+.BR m ,
+.BR h ,
+or
+.B d
+(a time
+in minutes, hours, or days respectively)
+(default
+.BR 1h ,
+maximum
+.BR 24h ).
+Normally, the connection is renegotiated (via the keying channel)
+before it expires.
+The two ends need not exactly agree on
+.BR keylife ,
+although if they do not,
+there will be some clutter of superseded connections on the end
+which thinks the lifetime is longer.
+.TP
+.B leftca
+the distinguished name of a certificate authority which is required to
+lie in the trust path going from the left participant's certificate up
+to the root certification authority.
+.TP
+.B leftcert
+the path to the left participant's X.509 certificate. The file can be coded either in
+PEM or DER format. OpenPGP certificates are supported as well.
+Both absolute paths or paths relative to
+.B /etc/ipsec.d/certs
+are accepted. By default
+.B leftcert
+sets
+.B leftid
+to the distinguished name of the certificate's subject and
+.B leftca
+to the distinguished name of the certificate's issuer.
+The left participant's ID can be overriden by specifying a
+.B leftid
+value which must be certified by the certificate, though.
+.TP
+.B leftgroups
+a comma separated list of group names. If the
+.B leftgroups
+parameter is present then the peer must be a member of at least one
+of the groups defined by the parameter. Group membership must be certified
+by a valid attribute certificate stored in \fI/etc/ipsec.d/acerts\fP thas has been
+issued to the peer by a trusted Authorization Authority stored in
+\fI/etc/ipsec.d/aacerts\fP.
+.TP
+.B leftid
+how
+the left participant
+should be identified for authentication;
+defaults to
+.BR left .
+Can be an IP address (in any
+.IR ipsec_ttoaddr (3)
+syntax)
+or a fully-qualified domain name preceded by
+.B @
+(which is used as a literal string and not resolved).
+The magic value
+.B %myid
+stands for the current setting of \fImyid\fP.
+This is set in \fBconfig setup\fP or by \fIipsec_whack\fP(8)), or, if not set,
+it is the IP address in \fB%defaultroute\fP (if that is supported by a TXT record in its reverse domain), or otherwise
+it is the system's hostname (if that is supported by a TXT record in its forward domain), or otherwise it is undefined.
+.TP
+.B leftrsasigkey
+the left participant's
+public key for RSA signature authentication,
+in RFC 2537 format using
+.IR ipsec_ttodata (3)
+encoding.
+The magic value
+.B %none
+means the same as not specifying a value (useful to override a default).
+The value
+.B %cert
+(the default)
+means that the key is extracted from a certificate.
+The value
+.B %dnsondemand
+means the key is to be fetched from DNS at the time it is needed.
+The value
+.B %dnsonload
+means the key is to be fetched from DNS at the time
+the connection description is read from
+.IR ipsec.conf ;
+currently this will be treated as
+.B %none
+if
+.B right=%any
+or
+.BR right=%opportunistic .
+The value
+.B %dns
+is currently treated as
+.B %dnsonload
+but will change to
+.B %dnsondemand
+in the future.
+The identity used for the left participant
+must be a specific host, not
+.B %any
+or another magic value.
+.B Caution:
+if two connection descriptions
+specify different public keys for the same
+.BR leftid ,
+confusion and madness will ensue.
+.TP
+.B leftrsasigkey2
+if present, a second public key.
+Either key can authenticate the signature, allowing for key rollover.
+.TP
+.B leftsourceip
+.TP
+.B leftsubnetwithin
+.TP
+.B pfs
+whether Perfect Forward Secrecy of keys is desired on the connection's
+keying channel
+(with PFS, penetration of the key-exchange protocol
+does not compromise keys negotiated earlier);
+acceptable values are
+.B yes
+(the default)
+and
+.BR no .
+.TP
+.B rekey
+whether a connection should be renegotiated when it is about to expire;
+acceptable values are
+.B yes
+(the default)
+and
+.BR no .
+The two ends need not agree,
+but while a value of
+.B no
+prevents Pluto from requesting renegotiation,
+it does not prevent responding to renegotiation requested from the other end,
+so
+.B no
+will be largely ineffective unless both ends agree on it.
+.TP
+.B rekeyfuzz
+maximum percentage by which
+.B rekeymargin
+should be randomly increased to randomize rekeying intervals
+(important for hosts with many connections);
+acceptable values are an integer,
+which may exceed 100,
+followed by a `%'
+(default set by
+.IR ipsec_pluto (8),
+currently
+.BR 100% ).
+The value of
+.BR rekeymargin ,
+after this random increase,
+must not exceed
+.BR keylife .
+The value
+.B 0%
+will suppress time randomization.
+Relevant only locally, other end need not agree on it.
+.TP
+.B rekeymargin
+how long before connection expiry or keying-channel expiry
+should attempts to
+negotiate a replacement
+begin; acceptable values as for
+.B keylife
+(default
+.BR 9m ).
+Relevant only locally, other end need not agree on it.
+.SS "CONN PARAMETERS: MANUAL KEYING"
+The following parameters are relevant only to manual keying,
+and are ignored in automatic keying.
+Unless otherwise noted,
+for a connection to work,
+in general it is necessary for the two ends to agree exactly
+on the values of these parameters.
+A manually-keyed
+connection must specify at least one of AH or ESP.
+.TP 14
+.B spi
+(this or
+.B spibase
+required for manual keying)
+the SPI number to be used for the connection (see
+.IR ipsec_manual (8));
+must be of the form \fB0x\fIhex\fB\fR,
+where
+.I hex
+is one or more hexadecimal digits
+(note, it will generally be necessary to make
+.I spi
+at least
+.B 0x100
+to be acceptable to KLIPS,
+and use of SPIs in the range
+.BR 0x100 - 0xfff
+is recommended)
+.TP 14
+.B spibase
+(this or
+.B spi
+required for manual keying)
+the base number for the SPIs to be used for the connection (see
+.IR ipsec_manual (8));
+must be of the form \fB0x\fIhex\fB0\fR,
+where
+.I hex
+is one or more hexadecimal digits
+(note, it will generally be necessary to make
+.I spibase
+at least
+.B 0x100
+for the resulting SPIs
+to be acceptable to KLIPS,
+and use of numbers in the range
+.BR 0x100 - 0xff0
+is recommended)
+.TP
+.B esp
+ESP encryption/authentication algorithm to be used
+for the connection, e.g.
+.B 3des-md5-96
+(must be suitable as a value of
+.IR ipsec_spi (8)'s
+.B \-\-esp
+option);
+default is not to use ESP
+.TP
+.B espenckey
+ESP encryption key
+(must be suitable as a value of
+.IR ipsec_spi (8)'s
+.B \-\-enckey
+option)
+(may be specified separately for each direction using
+.B leftespenckey
+(leftward SA)
+and
+.B rightespenckey
+parameters)
+.TP
+.B espauthkey
+ESP authentication key
+(must be suitable as a value of
+.IR ipsec_spi (8)'s
+.B \-\-authkey
+option)
+(may be specified separately for each direction using
+.B leftespauthkey
+(leftward SA)
+and
+.B rightespauthkey
+parameters)
+.TP
+.B espreplay_window
+ESP replay-window setting,
+an integer from
+.B 0
+(the
+.IR ipsec_manual
+default, which turns off replay protection) to
+.BR 64 ;
+relevant only if ESP authentication is being used
+.TP
+.B leftespspi
+SPI to be used for the leftward ESP SA, overriding
+automatic assignment using
+.B spi
+or
+.BR spibase ;
+typically a hexadecimal number beginning with
+.B 0x
+.TP
+.B ah
+AH authentication algorithm to be used
+for the connection, e.g.
+.B hmac-md5-96
+(must be suitable as a value of
+.IR ipsec_spi (8)'s
+.B \-\-ah
+option);
+default is not to use AH
+.TP
+.B ahkey
+(required if
+.B ah
+is present) AH authentication key
+(must be suitable as a value of
+.IR ipsec_spi (8)'s
+.B \-\-authkey
+option)
+(may be specified separately for each direction using
+.B leftahkey
+(leftward SA)
+and
+.B rightahkey
+parameters)
+.TP
+.B ahreplay_window
+AH replay-window setting,
+an integer from
+.B 0
+(the
+.I ipsec_manual
+default, which turns off replay protection) to
+.B 64
+.TP
+.B leftahspi
+SPI to be used for the leftward AH SA, overriding
+automatic assignment using
+.B spi
+or
+.BR spibase ;
+typically a hexadecimal number beginning with
+.B 0x
+.SH "CA SECTIONS"
+This are optional sections that can be used to assign special
+parameters to a Certification Authority (CA).
+.TP 10
+.B auto
+currently can have either the value
+.B ignore
+or
+.B add
+.
+.TP
+.B cacert
+defines a path to the CA certificate either relative to
+\fI/etc/ipsec.d/cacerts\fP or as an absolute path.
+.TP
+.B crluri
+defines a CRL distribution point (ldap, http, or file URI)
+.TP
+.B crluri2
+defines an alternative CRL distribution point (ldap, http, or file URI)
+.TP
+.B ldaphost
+defines an ldap host.
+.TP
+.B ocspuri
+defines an OCSP URI.
+.SH "CONFIG SECTIONS"
+At present, the only
+.B config
+section known to the IPsec software is the one named
+.BR setup ,
+which contains information used when the software is being started
+(see
+.IR ipsec_setup (8)).
+Here's an example:
+.PP
+.ne 8
+.nf
+.ft B
+.ta 1c
+config setup
+ interfaces="ipsec0=eth1 ipsec1=ppp0"
+ klipsdebug=none
+ plutodebug=all
+ manualstart=
+.ft
+.fi
+.PP
+Parameters are optional unless marked ``(required)''.
+The currently-accepted
+.I parameter
+names in a
+.B config
+.B setup
+section are:
+.TP 14
+.B myid
+the identity to be used for
+.BR %myid .
+.B %myid
+is used in the implicit policy group conns and can be used as
+an identity in explicit conns.
+If unspecified,
+.B %myid
+is set to the IP address in \fB%defaultroute\fP (if that is supported by a TXT record in its reverse domain), or otherwise
+the system's hostname (if that is supported by a TXT record in its forward domain), or otherwise it is undefined.
+An explicit value generally starts with ``\fB@\fP''.
+.TP
+.B interfaces
+virtual and physical interfaces for IPsec to use:
+a single
+\fIvirtual\fB=\fIphysical\fR pair, a (quoted!) list of pairs separated
+by white space, or
+.BR %none .
+One of the pairs may be written as
+.BR %defaultroute ,
+which means: find the interface \fId\fR that the default route points to,
+and then act as if the value was ``\fBipsec0=\fId\fR''.
+.B %defaultroute
+is the default;
+.B %none
+must be used to denote no interfaces.
+If
+.B %defaultroute
+is used (implicitly or explicitly)
+information about the default route and its interface is noted for
+use by
+.IR ipsec_manual (8)
+and
+.IR ipsec_auto (8).)
+.TP
+.B forwardcontrol
+whether
+.I setup
+should turn IP forwarding on
+(if it's not already on) as IPsec is started,
+and turn it off again (if it was off) as IPsec is stopped;
+acceptable values are
+.B yes
+and (the default)
+.BR no .
+For this to have full effect, forwarding must be
+disabled before the hardware interfaces are brought
+up (e.g.,
+.B "net.ipv4.ip_forward\ =\ 0"
+in Red Hat 6.x
+.IR /etc/sysctl.conf ),
+because IPsec doesn't get control early enough to do that.
+.TP
+.B rp_filter
+whether and how
+.I setup
+should adjust the reverse path filtering mechanism for the
+physical devices to be used.
+Values are \fB%unchanged\fP (to leave it alone)
+or \fB0\fP, \fB1\fP, \fB2\fP (values to set it to).
+\fI/proc/sys/net/ipv4/conf/PHYS/rp_filter\fP
+is badly documented; it must be \fB0\fP in many cases
+for ipsec to function.
+The default value for the parameter is \fB0\fP.
+.TP
+.B syslog
+the
+.IR syslog (2)
+``facility'' name and priority to use for
+startup/shutdown log messages,
+default
+.BR daemon.error .
+.TP
+.B klipsdebug
+how much KLIPS debugging output should be logged.
+An empty value,
+or the magic value
+.BR none ,
+means no debugging output (the default).
+The magic value
+.B all
+means full output.
+Otherwise only the specified types of output
+(a quoted list, names separated by white space) are enabled;
+for details on available debugging types, see
+.IR ipsec_klipsdebug (8).
+.TP
+.B plutodebug
+how much Pluto debugging output should be logged.
+An empty value,
+or the magic value
+.BR none ,
+means no debugging output (the default).
+The magic value
+.B all
+means full output.
+Otherwise only the specified types of output
+(a quoted list, names without the
+.B \-\-debug\-
+prefix,
+separated by white space) are enabled;
+for details on available debugging types, see
+.IR ipsec_pluto (8).
+.TP
+.B plutoopts
+additional options to pass to pluto upon startup. See
+.IR ipsec_pluto (8).
+.TP
+.B plutostderrlog
+do not use syslog, but rather log to stderr, and direct stderr to the
+argument file.
+.TP
+.B dumpdir
+in what directory should things started by
+.I setup
+(notably the Pluto daemon) be allowed to
+dump core?
+The empty value (the default) means they are not
+allowed to.
+.TP
+.B manualstart
+which manually-keyed connections to set up at startup
+(empty, a name, or a quoted list of names separated by white space);
+see
+.IR ipsec_manual (8).
+Default is none.
+.TP
+.B pluto
+whether to start Pluto or not;
+Values are
+.B yes
+(the default)
+or
+.B no
+(useful only in special circumstances).
+.TP
+.B plutowait
+should Pluto wait for each
+negotiation attempt that is part of startup to
+finish before proceeding with the next?
+Values are
+.B yes
+or
+.BR no
+(the default).
+.TP
+.B prepluto
+shell command to run before starting Pluto
+(e.g., to decrypt an encrypted copy of the
+.I ipsec.secrets
+file).
+It's run in a very simple way;
+complexities like I/O redirection are best hidden within a script.
+Any output is redirected for logging,
+so running interactive commands is difficult unless they use
+.I /dev/tty
+or equivalent for their interaction.
+Default is none.
+.TP
+.B postpluto
+shell command to run after starting Pluto
+(e.g., to remove a decrypted copy of the
+.I ipsec.secrets
+file).
+It's run in a very simple way;
+complexities like I/O redirection are best hidden within a script.
+Any output is redirected for logging,
+so running interactive commands is difficult unless they use
+.I /dev/tty
+or equivalent for their interaction.
+Default is none.
+.TP
+.B fragicmp
+whether a tunnel's need to fragment a packet should be reported
+back with an ICMP message,
+in an attempt to make the sender lower his PMTU estimate;
+acceptable values are
+.B yes
+(the default)
+and
+.BR no .
+.TP
+.B hidetos
+whether a tunnel packet's TOS field should be set to
+.B 0
+rather than copied from the user packet inside;
+acceptable values are
+.B yes
+(the default)
+and
+.BR no .
+.TP
+.B uniqueids
+whether a particular participant ID should be kept unique,
+with any new (automatically keyed)
+connection using an ID from a different IP address
+deemed to replace all old ones using that ID;
+acceptable values are
+.B yes
+(the default)
+and
+.BR no .
+Participant IDs normally \fIare\fR unique,
+so a new (automatically-keyed) connection using the same ID is
+almost invariably intended to replace an old one.
+.TP
+.B overridemtu
+value that the MTU of the ipsec\fIn\fR interface(s) should be set to,
+overriding IPsec's (large) default.
+This parameter is needed only in special situations.
+.TP
+.B nat_traversal
+.TP
+.B crlcheckinterval
+.TP
+.B strictcrlpolicy
+.TP
+.B pkcs11module
+.TP
+.B pkcs11keepstate
+
+.SH CHOOSING A CONNECTION
+.PP
+When choosing a connection to apply to an outbound packet caught with a
+.BR %trap,
+the system prefers the one with the most specific eroute that
+includes the packet's source and destination IP addresses.
+Source subnets are examined before destination subnets.
+For initiating, only routed connections are considered. For responding,
+unrouted but added connections are considered.
+.PP
+When choosing a connection to use to respond to a negotiation which
+doesn't match an ordinary conn, an opportunistic connection
+may be instantiated. Eventually, its instance will be /32 -> /32, but
+for earlier stages of the negotiation, there will not be enough
+information about the client subnets to complete the instantiation.
+.SH FILES
+.nf
+/etc/ipsec.conf
+/etc/ipsec.d/cacerts
+/etc/ipsec.d/certs
+/etc/ipsec.d/crls
+/etc/ipsec.d/aacerts
+/etc/ipsec.d/acerts
+
+.SH SEE ALSO
+ipsec(8), ipsec_ttoaddr(8), ipsec_auto(8), ipsec_manual(8), ipsec_rsasigkey(8)
+.SH HISTORY
+Written for the FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer. Extended for the strongSwan project
+<http://www.strongswan.org>
+by Andreas Steffen.
+.SH BUGS
+.PP
+When
+.B type
+or
+.B failureshunt
+is set to
+.B drop
+or
+.BR reject,
+strongSwan blocks outbound packets using eroutes, but assumes inbound
+blocking is handled by the firewall. strongSwan offers firewall hooks
+via an ``updown'' script. However, the default
+.B ipsec _updown
+provides no help in controlling a modern firewall.
+.PP
+Including attributes of the keying channel
+(authentication methods,
+.BR ikelifetime ,
+etc.)
+as an attribute of a connection,
+rather than of a participant pair, is dubious and incurs limitations.
+.PP
+.IR Ipsec_manual
+is not nearly as generous about the syntax of subnets,
+addresses, etc. as the usual strongSwan user interfaces.
+Four-component dotted-decimal must be used for all addresses.
+It
+.I is
+smart enough to translate bit-count netmasks to dotted-decimal form.
+.PP
+It would be good to have a line-continuation syntax,
+especially for the very long lines involved in
+RSA signature keys.
+.PP
+The ability to specify different identities,
+.BR authby ,
+and public keys for different automatic-keyed connections
+between the same participants is misleading;
+this doesn't work dependably because the identity of the participants
+is not known early enough.
+This is especially awkward for the ``Road Warrior'' case,
+where the remote IP address is specified as
+.BR 0.0.0.0 ,
+and that is considered to be the ``participant'' for such connections.
+.PP
+In principle it might be necessary to control MTU on an
+interface-by-interface basis,
+rather than with the single global override that
+.B overridemtu
+provides.
+.PP
+A number of features which \fIcould\fR be implemented in
+both manual and automatic keying
+actually are not yet implemented for manual keying.
+This is unlikely to be fixed any time soon.
+.PP
+If conns are to be added before DNS is available,
+\fBleft=\fP\fIFQDN\fP,
+\fBleftnextop=\fP\fIFQDN\fP,
+and
+.B leftrsasigkey=%dnsonload
+will fail.
+.IR ipsec_pluto (8)
+does not actually use the public key for our side of a conn but it
+isn't generally known at a add-time which side is ours (Road Warrior
+and Opportunistic conns are currently exceptions).
+.PP
+The \fBmyid\fP option does not affect explicit \fB ipsec auto \-\-add\fP or \fBipsec auto \-\-replace\fP commands for implicit conns.
diff --git a/programs/_confread/ipsec.conf.in b/programs/_confread/ipsec.conf.in
new file mode 100644
index 000000000..296986459
--- /dev/null
+++ b/programs/_confread/ipsec.conf.in
@@ -0,0 +1,44 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+# RCSID $Id: ipsec.conf.in,v 1.7 2006/01/31 13:09:10 as Exp $
+
+# Manual: ipsec.conf.5
+# Help: http://www.strongswan.org/docs/readme.htm
+
+version 2.0 # conforms to second version of ipsec.conf specification
+
+# basic configuration
+
+config setup
+ # Debug-logging controls: "none" for (almost) none, "all" for lots.
+ # plutodebug=all
+ # crlcheckinterval=600
+ # strictcrlpolicy=yes
+ # cachecrls=yes
+ # nat_traversal=yes
+
+# Uncomment to activate Opportunistic Encryption (OE)
+# include /etc/ipsec.d/examples/oe.conf
+
+# Add connections here.
+
+# Sample VPN connections
+
+#conn sample-self-signed
+# left=%defaultroute
+# leftsubnet=10.1.0.0/16
+# leftcert=selfCert.der
+# leftsendcert=never
+# right=192.168.0.2
+# rightsubnet=10.2.0.0/16
+# rightcert=peerCert.der
+# auto=start
+
+#conn sample-with-ca-cert
+# left=%defaultroute
+# leftsubnet=10.1.0.0/16
+# leftcert=myCert.pem
+# right=192.168.0.2
+# rightsubnet=10.2.0.0/16
+# rightid="C=CH, O=Linux strongSwan CN=peer name"
+# auto=start
diff --git a/programs/_confread/private-or-clear.in b/programs/_confread/private-or-clear.in
new file mode 100644
index 000000000..c66b1d29f
--- /dev/null
+++ b/programs/_confread/private-or-clear.in
@@ -0,0 +1,14 @@
+# This file defines the set of CIDRs (network/mask-length) to which
+# communication should be private, if possible, but in the clear otherwise.
+#
+# If the target has a TXT (later IPSECKEY) record that specifies
+# authentication material, we will require private (i.e. encrypted)
+# communications. If no such record is found, communications will be
+# in the clear.
+#
+# See @FINALDOCDIR@/policygroups.html for details.
+#
+# $Id: private-or-clear.in,v 1.1 2004/03/15 20:35:27 as Exp $
+#
+
+0.0.0.0/0
diff --git a/programs/_confread/private.in b/programs/_confread/private.in
new file mode 100644
index 000000000..9d4bd6c67
--- /dev/null
+++ b/programs/_confread/private.in
@@ -0,0 +1,6 @@
+# This file defines the set of CIDRs (network/mask-length) to which
+# communication should always be private (i.e. encrypted).
+# See @FINALDOCDIR@/policygroups.html for details.
+#
+# $Id: private.in,v 1.1 2004/03/15 20:35:27 as Exp $
+#
diff --git a/programs/_confread/randomize b/programs/_confread/randomize
new file mode 100755
index 000000000..26d80a8f3
--- /dev/null
+++ b/programs/_confread/randomize
@@ -0,0 +1,28 @@
+#! /bin/sh
+# internal utility for putting random keys into sample configuration file
+# Copyright (C) 1998, 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: randomize,v 1.1 2004/03/15 20:35:27 as Exp $
+
+awk '/`[0-9]+`/ {
+ match($0, /`[0-9]+`/)
+ n = substr($0, RSTART+1, RLENGTH-2)
+ cmd = "./ranbits --quick " n
+ cmd | getline key
+ cmd | getline eof
+ close(cmd)
+ sub(/`[0-9]+`/, key, $0)
+ print
+ next
+}
+{ print }' $*
diff --git a/programs/_copyright/.cvsignore b/programs/_copyright/.cvsignore
new file mode 100644
index 000000000..23ebcb381
--- /dev/null
+++ b/programs/_copyright/.cvsignore
@@ -0,0 +1 @@
+_copyright
diff --git a/programs/_copyright/Makefile b/programs/_copyright/Makefile
new file mode 100644
index 000000000..52c594b68
--- /dev/null
+++ b/programs/_copyright/Makefile
@@ -0,0 +1,44 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/_copyright/_copyright.8 b/programs/_copyright/_copyright.8
new file mode 100644
index 000000000..87e4adc98
--- /dev/null
+++ b/programs/_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
+<http://www.freeswan.org/>
+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/programs/_copyright/_copyright.c b/programs/_copyright/_copyright.c
new file mode 100644
index 000000000..0fb360f40
--- /dev/null
+++ b/programs/_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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <freeswan.h>
+
+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/programs/_include/.cvsignore b/programs/_include/.cvsignore
new file mode 100644
index 000000000..ab6204115
--- /dev/null
+++ b/programs/_include/.cvsignore
@@ -0,0 +1 @@
+_include
diff --git a/programs/_include/Makefile b/programs/_include/Makefile
new file mode 100644
index 000000000..6b5f11682
--- /dev/null
+++ b/programs/_include/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_include
+PROGRAMDIR=${LIBDIR}
+
+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:11 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/programs/_include/_include.8 b/programs/_include/_include.8
new file mode 100644
index 000000000..56ffa0723
--- /dev/null
+++ b/programs/_include/_include.8
@@ -0,0 +1,35 @@
+.TH _INCLUDE 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _include.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _include \- internal script to process config files
+.SH DESCRIPTION
+.I _include
+is used by
+.I _confread
+to process
+.B include
+directives in /etc/ipsec.conf.
+.SH "SEE ALSO"
+ipsec(8), ipsec__confread(8)
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Program written by Henry Spencer.
+.\"
+.\" $Log: _include.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/programs/_include/_include.in b/programs/_include/_include.in
new file mode 100755
index 000000000..10a8a49e4
--- /dev/null
+++ b/programs/_include/_include.in
@@ -0,0 +1,102 @@
+#! /bin/sh
+# implements nested file inclusion for control files, including wildcarding
+# Copyright (C) 1998, 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _include.in,v 1.2 2004/03/15 21:03:06 as Exp $
+#
+# Output includes marker lines for file changes:
+# "#< filename lineno" signals entry into that file
+# "#> filename lineno" signals return to that file
+# The lineno is the line number of the *next* line.
+#
+# Errors are reported with a "#:message" line rather than on stderr.
+#
+# Lines which look like marker and report lines are never passed through.
+
+IPSEC_NAME="strongSwan"
+
+usage="Usage: $0 file ..."
+me="ipsec _include"
+
+for dummy
+do
+ case "$1" in
+ --inband) ;; # back compatibility
+ --help) echo "$usage" ; exit 0 ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --) shift ; break ;;
+ -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+case $# in
+0) echo "$usage" >&2 ; exit 2 ;;
+esac
+
+for f
+do
+ if test ! -r "$f"
+ then
+ if test ! "$f" = "/etc/ipsec.conf"
+ then
+ echo "#:cannot open configuration file \'$f\'"
+ if test "$f" = "/etc/ipsec.secrets"
+ then
+ echo "#:Your secrets file will be created when you start $IPSEC_NAME for the first time."
+ fi
+ exit 1
+ else
+ exit 1
+ fi
+ fi
+done
+
+awk 'BEGIN {
+ wasfile = ""
+}
+FNR == 1 {
+ print ""
+ print "#<", FILENAME, 1
+ lineno = 0
+ wasfile = FILENAME
+}
+{
+ lineno++
+ # lineno is now the number of this line
+}
+/^#[<>:]/ {
+ next
+}
+/^include[ \t]+/ {
+ orig = $0
+ sub(/[ \t]+#.*$/, "")
+ if (NF != 2) {
+ msg = "(" FILENAME ", line " lineno ")"
+ msg = msg " include syntax error in \"" orig "\""
+ print "#:" msg
+ exit 1
+ }
+ newfile = $2
+ if (newfile !~ /^\// && FILENAME ~ /\//) {
+ prefix = FILENAME
+ sub("[^/]+$", "", prefix)
+ newfile = prefix newfile
+ }
+ system("ipsec _include " newfile)
+ print ""
+ print "#>", FILENAME, lineno + 1
+ next
+}
+{ print }' $*
diff --git a/programs/_keycensor/.cvsignore b/programs/_keycensor/.cvsignore
new file mode 100644
index 000000000..97d0bb2bf
--- /dev/null
+++ b/programs/_keycensor/.cvsignore
@@ -0,0 +1 @@
+_keycensor
diff --git a/programs/_keycensor/Makefile b/programs/_keycensor/Makefile
new file mode 100644
index 000000000..bc495328f
--- /dev/null
+++ b/programs/_keycensor/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_keycensor
+PROGRAMDIR=${LIBDIR}
+
+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:15 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/programs/_keycensor/_keycensor.8 b/programs/_keycensor/_keycensor.8
new file mode 100644
index 000000000..89a97a9f9
--- /dev/null
+++ b/programs/_keycensor/_keycensor.8
@@ -0,0 +1,33 @@
+.TH _KEYCENSOR 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _keycensor.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _keycensor \- internal routine to remove sensitive information
+.SH DESCRIPTION
+.I _keycensor
+is used by
+.B ipsec barf
+to process the /etc/ipsec.secrets file, removing private key info.
+.SH "SEE ALSO"
+ipsec(8), ipsec_barf(8)
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program by Henry Spencer.
+.\"
+.\" $Log: _keycensor.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/programs/_keycensor/_keycensor.in b/programs/_keycensor/_keycensor.in
new file mode 100755
index 000000000..7d6f257e5
--- /dev/null
+++ b/programs/_keycensor/_keycensor.in
@@ -0,0 +1,52 @@
+#! /bin/sh
+# implements key censoring for barf
+# Copyright (C) 1999, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _keycensor.in,v 1.1 2004/03/15 20:35:27 as Exp $
+
+usage="Usage: $0 [file ...]"
+me="ipsec _keycensor"
+
+for dummy
+do
+ case "$1" in
+ --help) echo "$usage" ; exit 0 ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --) shift ; break ;;
+ -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+awk ' /(sig|enc|auth)key[ \t]*=[ \t]*[^%]/ {
+ i = match($0, /key[ \t]*=[ \t]*/)
+ i += RLENGTH
+ cold = substr($0, 1, i-1)
+ hot = substr($0, i)
+ sub(/[ \t]+(#.*)?$/, "", hot)
+ q = "'"'"'" # single quote
+ if (hot ~ q)
+ cooled = "[cannot be condensed]"
+ else if (hot ~ /^0s/)
+ cooled = "[keyid " substr(hot, 3, 9) "]"
+ else {
+ run = "echo " q hot q " | md5sum"
+ run | getline
+ close(run)
+ cooled = "[sums to " substr($1, 1, 4) "...]"
+ }
+ print cold cooled
+ next
+ }
+ { print }' $*
diff --git a/programs/_plutoload/.cvsignore b/programs/_plutoload/.cvsignore
new file mode 100644
index 000000000..cbcf7e699
--- /dev/null
+++ b/programs/_plutoload/.cvsignore
@@ -0,0 +1 @@
+_plutoload
diff --git a/programs/_plutoload/Makefile b/programs/_plutoload/Makefile
new file mode 100644
index 000000000..af9ffee18
--- /dev/null
+++ b/programs/_plutoload/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_plutoload
+PROGRAMDIR=${LIBDIR}
+
+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:19 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/programs/_plutoload/_plutoload.8 b/programs/_plutoload/_plutoload.8
new file mode 100644
index 000000000..ba421b6c3
--- /dev/null
+++ b/programs/_plutoload/_plutoload.8
@@ -0,0 +1,33 @@
+.TH _PLUTOLOAD 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _plutoload.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _plutoload \- internal script to start pluto
+.SH DESCRIPTION
+.I _plutoload
+is called by
+.B _plutorun
+to actually start the pluto executable.
+.SH "SEE ALSO"
+ipsec(8), ipsec_setup(8), ipsec__realsetup(8), ipsec__plutorun(8)
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program by Henry Spencer.
+.\"
+.\" $Log: _plutoload.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/programs/_plutoload/_plutoload.in b/programs/_plutoload/_plutoload.in
new file mode 100755
index 000000000..73841197d
--- /dev/null
+++ b/programs/_plutoload/_plutoload.in
@@ -0,0 +1,164 @@
+#!/bin/sh
+# Pluto database-loading script
+# Copyright (C) 1998, 1999, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _plutoload.in,v 1.2 2004/03/31 16:15:10 as Exp $
+#
+# exit status is 13 for protocol violation, that of Pluto otherwise
+
+me='ipsec _plutoload' # for messages
+
+for dummy
+do
+ case "$1" in
+ --load) plutoload="$2" ; shift ;;
+ --start) plutostart="$2" ; shift ;;
+ --wait) plutowait="$2" ; shift ;;
+ --post) postpluto="$2" ; shift ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+# load ca information
+eval `ipsec _confread --varprefix PLUTO --type ca --search auto add start`
+if test " $PLUTO_confreadstatus" != " "
+then
+ echo "auto=add/start search: $PLUTO_confreadstatus"
+ echo "unable to determine what ca information to add -- adding none"
+ caload=
+else
+ caload="$PLUTO_confreadnames"
+fi
+
+# searches, if needed
+# the way the searches were done ensures plutoload >= plutoroute >= plutostart
+
+# search for things to "ipsec auto --add": auto in "add" "route" "start"
+eval `ipsec _confread --varprefix PLUTO --search auto add route start`
+if test " $PLUTO_confreadstatus" != " "
+then
+ echo "auto=add/route/start search: $PLUTO_confreadstatus"
+ echo "unable to determine what conns to add -- adding none"
+ plutoload=
+else
+ plutoload="$PLUTO_confreadnames"
+fi
+
+# search for things to "ipsec auto --route": auto in "route" "start"
+eval `ipsec _confread --varprefix PLUTO --search auto route start`
+if test " $PLUTO_confreadstatus" != " "
+then
+ echo "auto=route/start search: $PLUTO_confreadstatus"
+ echo "unable to determine what conns to route -- routing none"
+ plutoroute=
+else
+ plutoroute="$PLUTO_confreadnames"
+fi
+
+# search for things to "ipsec auto --up": auto in "start"
+eval `ipsec _confread --varprefix PLUTO --search auto start`
+if test " $PLUTO_confreadstatus" != " "
+then
+ echo "auto=start search: $PLUTO_confreadstatus"
+ echo "unable to determine what conns to start -- starting none"
+ plutostart=
+else
+ plutostart="$PLUTO_confreadnames"
+fi
+
+# await Pluto's readiness (not likely to be an issue, but...)
+eofed=y
+while read saying
+do
+ case "$saying" in
+ 'Pluto initialized') eofed= ; break ;; # NOTE BREAK OUT
+ *) echo "pluto unexpectedly said \`$saying'" ;;
+ esac
+done
+if test "$eofed"
+then
+ echo "pluto died unexpectedly!?!"
+ exit 13
+fi
+
+# ca database load
+for tu in $caload
+do
+ ipsec auto --type ca --add $tu ||
+ echo "...could not add ca \"$tu\""
+done
+
+# conn database load
+for tu in $plutoload
+do
+ ipsec auto --add $tu ||
+ echo "...could not add conn \"$tu\""
+done
+
+# enable listening
+ipsec auto --ready
+
+# execute any post-startup cleanup
+if test " $postpluto" != " "
+then
+ $postpluto
+ st=$?
+ if test " $st" -ne 0
+ then
+ echo "...postpluto command exited with status $st"
+ fi
+fi
+
+# quickly establish routing
+for tu in $plutoroute
+do
+ ipsec auto --route $tu ||
+ echo "...could not route conn \"$tu\""
+done
+
+# tunnel initiation, which may take a while
+async=
+if test " $plutowait" = " no"
+then
+ async="--asynchronous"
+fi
+for tu in $plutostart
+do
+ ipsec auto --up $async $tu ||
+ echo "...could not start conn \"$tu\""
+done
+
+# report any further utterances, and watch for exit status
+eofed=y
+while read saying
+do
+ case "$saying" in
+ exit) eofed= ; break ;; # NOTE BREAK OUT
+ *) echo "pluto unexpectedly says \`$saying'" ;;
+ esac
+done
+if test "$eofed"
+then
+ echo "pluto died without exit status!?!"
+ exit 13
+fi
+if read status
+then
+ exit $status
+else
+ echo "pluto yielded no exit status!?!"
+ exit 13
+fi
diff --git a/programs/_plutorun/.cvsignore b/programs/_plutorun/.cvsignore
new file mode 100644
index 000000000..13e0ae1a1
--- /dev/null
+++ b/programs/_plutorun/.cvsignore
@@ -0,0 +1 @@
+_plutorun
diff --git a/programs/_plutorun/Makefile b/programs/_plutorun/Makefile
new file mode 100644
index 000000000..b0928797c
--- /dev/null
+++ b/programs/_plutorun/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_plutorun
+PROGRAMDIR=${LIBDIR}
+
+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:26 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/programs/_plutorun/_plutorun.8 b/programs/_plutorun/_plutorun.8
new file mode 100644
index 000000000..9de6927dc
--- /dev/null
+++ b/programs/_plutorun/_plutorun.8
@@ -0,0 +1,37 @@
+.TH _PLUTORUN 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _plutorun.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _plutorun \- internal script to start pluto
+.SH DESCRIPTION
+.I _plutorun
+is called by
+.B _realsetup
+to configure and bring up
+.B ipsec_pluto(8).
+It calls
+.B _plutoload
+to invoke pluto, and watches to makes sure that pluto is restarted if it fails.
+.SH "SEE ALSO"
+ipsec(8), ipsec_setup(8), ipsec__realsetup(8), ipsec__plutoload(8), ipsec_pluto(8).
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program written by Henry Spencer.
+.\"
+.\" $Log: _plutorun.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/programs/_plutorun/_plutorun.in b/programs/_plutorun/_plutorun.in
new file mode 100755
index 000000000..b02afeefb
--- /dev/null
+++ b/programs/_plutorun/_plutorun.in
@@ -0,0 +1,281 @@
+#!/bin/sh
+# Pluto control daemon
+# Copyright (C) 1998, 1999, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _plutorun.in,v 1.9 2005/10/16 13:28:15 as Exp $
+
+me='ipsec _plutorun' # for messages
+
+info=/var/run/ipsec.info
+
+popts=
+stderrlog=
+plutorestartoncrash=true
+
+wherelog=daemon.error
+pidfile=/var/run/pluto.pid
+verb="Starting"
+for dummy
+do
+ case "$1" in
+ --re) verb="Restarting" ;;
+ --plutorestartoncrash) plutorestartoncrash="$2"; shift ;;
+ --debug) plutodebug="$2" ; shift ;;
+ --uniqueids) uniqueids="$2" ; shift ;;
+ --nat_traversal) nat_traversal="$2" ; shift ;;
+ --keep_alive) keep_alive="$2" ; shift ;;
+ --force_keepalive) force_keepalive="$2" ; shift ;;
+ --disable_port_floating) disable_port_floating="$2" ; shift ;;
+ --virtual_private) virtual_private="$2" ; shift ;;
+ --nocrsend) nocrsend="$2" ; shift ;;
+ --strictcrlpolicy) strictcrlpolicy="$2" ; shift ;;
+ --crlcheckinterval) crlcheckinterval="$2"; shift ;;
+ --cachecrls) cachecrls="$2" ; shift ;;
+ --pkcs11module) pkcs11module="$2"; shift ;;
+ --pkcs11keepstate) pkcs11keepstate="$2"; shift ;;
+ --pkcs11proxy) pkcs11proxy="$2"; shift ;;
+ --dump) dumpdir="$2" ; shift ;;
+ --opts) popts="$2" ; shift ;;
+ --stderrlog) stderrlog="$2" ; shift ;;
+ --wait) plutowait="$2" ; shift ;;
+ --pre) prepluto="$2" ; shift ;;
+ --post) postpluto="$2" ; shift ;;
+ --log) wherelog="$2" ; shift ;;
+ --pid) pidfile="$2" ; shift ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+# initially we are in the foreground, with parent looking after logging
+
+# precautions
+if test -f $pidfile
+then
+ echo "pluto appears to be running already (\`$pidfile' exists), will not start another"
+ exit 1
+fi
+if test ! -e /dev/urandom
+then
+ echo "cannot start Pluto, system lacks \`/dev/urandom'!?!"
+ exit 1
+fi
+
+# sort out options
+for d in $plutodebug
+do
+ popts="$popts --debug-$d"
+done
+case "$uniqueids" in
+yes) popts="$popts --uniqueids" ;;
+no|'') ;;
+*) echo "unknown uniqueids value (not yes/no) \`$IPSECuniqueids'" ;;
+esac
+case "$nocrsend" in
+yes) popts="$popts --nocrsend" ;;
+no|'') ;;
+*) echo "unknown nocrsend value (not yes/no) \`$IPSECnocrsend'" ;;
+esac
+case "$strictcrlpolicy" in
+yes) popts="$popts --strictcrlpolicy" ;;
+no|'') ;;
+*) echo "unknown strictcrlpolicy value (not yes/no) \`$IPSECstrictcrlpolicy'" ;;
+esac
+case "$cachecrls" in
+yes) popts="$popts --cachecrls" ;;
+no|'') ;;
+*) echo "unknown cachecrls value (not yes/no) \`$IPSECcachecrls'" ;;
+esac
+case "$nat_traversal" in
+yes) popts="$popts --nat_traversal" ;;
+no|'') ;;
+*) echo "unknown nat_traversal value (not yes/no) \`$IPSECnat_traversal'" ;;
+esac
+[ -n "$keep_alive" ] && popts="$popts --keep_alive $keep_alive"
+case "$force_keepalive" in
+yes) popts="$popts --force_keepalive" ;;
+no|'') ;;
+*) echo "unknown force_keepalive value (not yes/no) \`$IPSECforce_keepalive'" ;;
+esac
+case "$disable_port_floating" in
+yes) popts="$popts --disable_port_floating" ;;
+no|'') ;;
+*) echo "unknown disable_port_floating (not yes/no) \`$disable_port_floating'" ;;
+esac
+case "$pkcs11keepstate" in
+yes) popts="$popts --pkcs11keepstate" ;;
+no|'') ;;
+*) echo "unknown pkcs11keepstate value (not yes/no) \`$IPSECpkcs11keepstate'" ;;
+esac
+case "$pkcs11proxy" in
+yes) popts="$popts --pkcs11proxy" ;;
+no|'') ;;
+*) echo "unknown pkcs11proxy value (not yes/no) \`$IPSECpkcs11proxy'" ;;
+esac
+
+[ -n "$virtual_private" ] && popts="$popts --virtual_private $virtual_private"
+
+# add crl check interval
+if test ${crlcheckinterval:-0} -gt 0
+then
+ popts="$popts --crlcheckinterval $crlcheckinterval"
+fi
+
+if test -n "$pkcs11module"
+then
+ popts="$popts --pkcs11module $pkcs11module"
+fi
+
+if test -n "$stderrlog"
+then
+ popts="$popts --stderrlog 2>>$stderrlog"
+
+ if test -f $stderrlog
+ then
+ if test ! -w $stderrlog
+ then
+ echo Cannot write to \"$stderrlog\".
+ exit 1
+ fi
+ else
+ if test ! -w "`dirname $stderrlog`"
+ then
+ echo Cannot write to directory to create \"$stderrlog\".
+ exit 1
+ fi
+ fi
+
+ echo "Plutorun started on "`date` >$stderrlog
+fi
+
+# set up dump directory
+if test " $dumpdir" = " "
+then
+ ulimit -c 0 # preclude core dumps
+elif test ! -d "$dumpdir"
+then
+ echo "dumpdir \`$dumpdir' does not exist, ignored"
+ ulimit -c 0 # preclude core dumps
+elif cd $dumpdir # put them where desired
+then
+ ulimit -c unlimited # permit them
+else
+ echo "cannot cd to dumpdir \`$dumpdir', ignored"
+ ulimit -c 0 # preclude them
+fi
+
+# execute any preliminaries
+if test " $prepluto" != " "
+then
+ $prepluto
+ st=$?
+ if test " $st" -ne 0
+ then
+ echo "...prepluto command exited with status $st"
+ fi
+fi
+
+IPSEC_SECRETS=${IPSEC_CONFS}/ipsec.secrets
+if test ! -f "${IPSEC_SECRETS}"
+then
+ ( logger -p authpriv.info -t ipsec__plutorun No file ${IPSEC_SECRETS}, generating key.
+ ipsec scepclient --out pkcs1 --out cert-self --quiet
+ echo -e "# /etc/ipsec.secrets - strongSwan IPsec secrets file\n" > ${IPSEC_SECRETS}
+ chmod 600 ${IPSEC_SECRETS}
+ echo ": RSA myKey.der" >> ${IPSEC_SECRETS}
+
+ # tell pluto to go re-read the file
+ ipsec auto --rereadsecrets
+ ) &
+fi
+
+#
+# make sure that the isakmp port is open!
+#
+if test -f /etc/sysconfig/ipchains
+then
+ if egrep -q 500:500 /etc/sysconfig/ipchains
+ then
+ :
+ else
+ ipchains -I input 1 -p udp -s 0.0.0.0/0.0.0.0 -d 0.0.0.0/0.0.0.0 500:500 -j ACCEPT
+ # if it redhat, then save the rules again.
+ if [ -f /etc/redhat-release ]
+ then
+ sh /etc/rc.d/init.d/ipchains save
+ fi
+ fi
+fi
+
+# spin off into the background, with our own logging
+echo "$verb Pluto subsystem..." | logger -p authpriv.error -t ipsec__plutorun
+execdir=${IPSEC_EXECDIR-@IPSEC_EXECDIR@}
+libdir=${IPSEC_LIBDIR-@IPSEC_LIBDIR@}
+until (
+ if test -s $info
+ then
+ . $info
+ export defaultroutephys defaultroutevirt defaultrouteaddr defaultroutenexthop
+ fi
+ # eval allows $popts to contain redirection and other magic
+ eval $execdir/pluto --nofork --secretsfile "$IPSEC_SECRETS" --policygroupsdir "${IPSEC_CONFS}/ipsec.d/policies" $popts
+ status=$?
+ echo "exit"
+ echo $status
+ ) | $libdir/_plutoload --wait "$plutowait" --post "$postpluto"
+do
+ status=$?
+ case "$status" in
+ 13) echo "internal failure in pluto scripts, impossible to carry on"
+ exit 1
+ ;;
+ 10) echo "pluto apparently already running (?!?), giving up"
+ exit 1
+ ;;
+ 137) echo "pluto killed by SIGKILL, terminating without restart or unlock"
+ exit 0
+ ;;
+ 143) echo "pluto killed by SIGTERM, terminating without restart"
+ # pluto now does its own unlock for this
+ exit 0
+ ;;
+ *) st=$status
+ if $plutorestartoncrash
+ then
+ :
+ else
+ exit 0
+ fi
+
+ if test $st -gt 128
+ then
+ st="$st (signal `expr $st - 128`)"
+ fi
+ echo "!pluto failure!: exited with error status $st"
+ echo "restarting IPsec after pause..."
+ (
+ sleep 10
+ ipsec setup _autorestart
+ ) </dev/null >/dev/null 2>&1 &
+ exit 1
+ ###sleep 10
+ ###rm -rf $pidfile
+ #### and go around the loop again
+ ;;
+ esac
+done </dev/null 2>&1 |
+ logger -s -p $wherelog -t ipsec__plutorun >/dev/null 2>/dev/null &
+
+exit 0
diff --git a/programs/_realsetup/.cvsignore b/programs/_realsetup/.cvsignore
new file mode 100644
index 000000000..54941b8a3
--- /dev/null
+++ b/programs/_realsetup/.cvsignore
@@ -0,0 +1 @@
+_realsetup
diff --git a/programs/_realsetup/Makefile b/programs/_realsetup/Makefile
new file mode 100644
index 000000000..c339007e0
--- /dev/null
+++ b/programs/_realsetup/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_realsetup
+PROGRAMDIR=${LIBDIR}
+
+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:34 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/programs/_realsetup/_realsetup.8 b/programs/_realsetup/_realsetup.8
new file mode 100644
index 000000000..51b647115
--- /dev/null
+++ b/programs/_realsetup/_realsetup.8
@@ -0,0 +1,36 @@
+.TH _REALSETUP 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _realsetup.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _realsetup \- internal routine to start FreeS/WAN.
+.SH DESCRIPTION
+.I _realsetup
+is called by the system init scripts to start the FreeS/WAN
+system. It starts
+.B KLIPS
+(the kernel component) and
+.B pluto
+(the userspace keying component).
+.SH "SEE ALSO"
+ipsec(8), ipsec__klipsstart(8), ipsec__plutorun(8).
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program by Henry Spencer.
+.\"
+.\" $Log: _realsetup.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/programs/_realsetup/_realsetup.in b/programs/_realsetup/_realsetup.in
new file mode 100755
index 000000000..91b6e98d3
--- /dev/null
+++ b/programs/_realsetup/_realsetup.in
@@ -0,0 +1,456 @@
+#!/bin/sh
+# IPsec startup and shutdown command
+# Copyright (C) 1998, 1999, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _realsetup.in,v 1.10 2005/09/25 21:30:52 as Exp $
+
+IPSEC_NAME=strongSwan
+
+me='ipsec setup' # for messages
+
+# Misc. paths (some of this should perhaps be overrideable from ipsec.conf).
+plutopid=/var/run/pluto.pid
+subsyslock=/var/lock/subsys/ipsec
+lock=/var/run/ipsec_setup.pid
+info=/var/run/ipsec.info
+sysflags=/proc/sys/net/ipsec
+modules=/proc/modules
+ipforward=/proc/sys/net/ipv4/ip_forward
+ipsecversion=/proc/net/ipsec_version
+kamepfkey=/proc/net/pfkey
+
+# make sure output of (e.g.) ifconfig is in English
+unset LANG LANGUAGE LC_ALL LC_MESSAGES
+
+# check we were called properly
+if test " $IPSEC_confreadsection" != " setup"
+then
+ echo "$me: $0 must be called by ipsec_setup" >&2
+ exit 1
+fi
+# defaults for "config setup" items
+
+IPSECinterfaces=${IPSECinterfaces:-%defaultroute}
+ if test " $IPSECinterfaces" = " %none" ; then IPSECinterfaces= ; fi
+# IPSECforwardcontrol "no"
+# IPSECsyslog "daemon.error"
+# IPSECklipsdebug "none"
+# IPSECplutodebug "none"
+# IPSECdumpdir "" (no dump)
+# IPSECmanualstart ""
+# IPSECpluto "yes"
+IPSECplutowait=${IPSECplutowait:-no}
+# IPSECprepluto ""
+# IPSECpostpluto ""
+# IPSECfragicmp "yes"
+# IPSEChidetos "yes"
+IPSECrp_filter=${IPSECrp_filter:-0}
+IPSECuniqueids=${IPSECuniqueids:-yes}
+IPSECcrlcheckinterval=${IPSECcrlcheckinterval:-0}
+# IPSECpkcs11module ""
+# IPSECoverridemtu ""
+
+# Shall we trace?
+execute="true"
+display="false"
+for i in $IPSEC_setupflags
+do
+ case "$i" in
+ "--showonly") execute="false" ; display=true ;;
+ "--show") display=true ;;
+ esac
+done
+
+if $display
+then
+ echo " " PATH="$PATH"
+fi
+
+perform() {
+ if $display
+ then
+ echo " " "$*"
+ fi
+
+ if $execute
+ then
+ eval "$*"
+ fi
+}
+
+# function to set up manually-keyed connections
+manualconns() {
+ if test " $IPSECmanualstart" != " "
+ then
+ for tu in $IPSECmanualstart
+ do
+ perform ipsec manual --up $tu
+ done
+ fi
+
+ # search for things to "ipsec manual --up": auto == "manual"
+ eval `ipsec _confread --varprefix MANUALSTART --search auto manual`
+ if test " $MANUALSTART_confreadstatus" != " "
+ then
+ echo "auto=manual search: $MANUALSTART_confreadstatus"
+ echo "unable to determine what conns to manual --up; none done"
+ elif test " $MANUALSTART_confreadnames" != " "
+ then
+ for tu in $MANUALSTART_confreadnames
+ do
+ perform ipsec manual --up $tu
+ done
+ fi
+}
+
+# for no-stdout logging:
+LOGONLY="logger -p $IPSECsyslog -t ipsec_setup"
+
+# What an ugly string.
+# Must be a string, not a function, because it is nested
+# within another sequence (for plutorun).
+# Luckily there are NO substitutions in it.
+KILLKLIPS='ifl=` ifconfig | sed -n -e "/^ipsec/s/ .*//p" ` ;
+ test "X$ifl" != "X" &&
+ for i in $ifl ;
+ do
+ ifconfig $i down ;
+ ipsec tncfg --detach --virtual $i ;
+ done ;
+ test -r /proc/net/ipsec_klipsdebug && ipsec klipsdebug --none ;
+ ipsec eroute --clear ;
+ ipsec spi --clear ;
+ for alg in aes serpent twofish blowfish sha2 ;
+ do
+ lsmod 2>&1 | grep "^ipsec_$alg" > /dev/null && rmmod ipsec_$alg ;
+ done ;
+ lsmod 2>&1 | grep "^ipsec" > /dev/null && rmmod ipsec'
+
+if test -f $kamepfkey
+then
+ KILLKLIPS='
+ if ip xfrm state > /dev/null 2>&1 ;
+ then
+ ip xfrm state flush ;
+ ip xfrm policy flush ;
+ elif type setkey > /dev/null 2>&1 ;
+ then
+ setkey -F ;
+ setkey -FP ;
+ fi'
+fi
+
+
+
+# do it
+case "$1" in
+ start|--start|_autostart)
+ # First, does it seem to be going already?
+ perform test ! -f $lock "||" "{" \
+ echo "\"$IPSEC_NAME IPsec apparently already running, start aborted\"" ";" \
+ exit 1 ";" \
+ "}"
+
+ # announcement
+ # (Warning, changes to this log message may affect barf.)
+ version="`ipsec --version | awk 'NR == 1 { print $(3) }' | sed -e 's/^U\(.*\)\/K(.*/\1/'`"
+ case "$1" in
+ start|--start) perform echo "\"Starting $IPSEC_NAME IPsec $version...\"" ;;
+ _autostart) perform echo "\"Restarting $IPSEC_NAME IPsec $version...\"" ;;
+ esac
+
+ # preliminaries
+ perform rm -f $lock
+
+ for f in /dev/random /dev/urandom
+ do
+ perform test -r $f "||" "{" \
+ echo "\"...unable to start $IPSEC_NAME IPsec, no $f!\"" ";" \
+ exit 1 ";" \
+ "}"
+ done
+
+ # the meaning of $$ at a different runtime is questionable!
+ perform echo '$$' ">" $lock
+ perform test -s $lock "||" "{" \
+ echo "\"...unable to create $lock, aborting start!\"" ";" \
+ rm -f $lock ";" \
+ exit 1 ";" \
+ "}"
+
+ perform ">" $info
+
+ # here we go
+ perform ipsec _startklips \
+ --info $info \
+ --debug "\"$IPSECklipsdebug\"" \
+ --omtu "\"$IPSECoverridemtu\"" \
+ --fragicmp "\"$IPSECfragicmp\"" \
+ --hidetos "\"$IPSEChidetos\"" \
+ --rpfilter "\"$IPSECrp_filter\"" \
+ --log "\"$IPSECsyslog\"" \
+ $IPSECinterfaces "||" \
+ "{" rm -f $lock ";" exit 1 ";" "}"
+
+ perform test -f $ipsecversion "||" \
+ test -f $kamepfkey "||" "{" \
+ echo "\"OOPS, should have aborted! Broken shell!\"" ";" \
+ exit 1 ";" \
+ "}"
+
+ # misc pre-Pluto setup
+
+ perform test -d `dirname $subsyslock` "&&" touch $subsyslock
+
+ if test " $IPSECforwardcontrol" = " yes"
+ then
+ perform grep '"^0"' $ipforward ">" /dev/null "&&" "{" \
+ echo "\"enabling IP forwarding:\"" "|" $LOGONLY ";" \
+ echo "\"ipforwardingwas=$fw\"" ">>" $info ";" \
+ echo 1 ">" $ipforward ";" \
+ "}"
+ fi
+ manualconns
+
+ plutorestartoncrash=""
+ case "$IPSECplutorestartoncrash" in
+ true|[yY]|yes|restart) plutorestartoncrash="--plutorestartoncrash true";;
+ false|[nN]|no|die) plutorestartoncrash="--plutorestartoncrash false" ;;
+ esac
+
+ # Pluto
+ case "$1" in
+ start|--start) re= ;;
+ _autostart) re=--re ;;
+ esac
+ if test " $IPSECpluto" != " no"
+ then
+ perform ipsec _plutorun $re \
+ --debug "\"$IPSECplutodebug\"" \
+ --uniqueids "\"$IPSECuniqueids\"" \
+ --nocrsend "\"$IPSECnocrsend\"" \
+ --strictcrlpolicy "\"$IPSECstrictcrlpolicy\"" \
+ --cachecrls "\"$IPSECcachecrls\"" \
+ --nat_traversal "\"$IPSECnat_traversal\"" \
+ --keep_alive "\"$IPSECkeep_alive\"" \
+ --force_keepalive "\"$IPSECforce_keepalive\"" \
+ --disable_port_floating "\"$IPSECdisable_port_floating\"" \
+ --virtual_private "\"$IPSECvirtual_private\"" \
+ --crlcheckinterval "\"$IPSECcrlcheckinterval\"" \
+ --pkcs11module "\"$IPSECpkcs11module\"" \
+ --pkcs11keepstate "\"$IPSECpkcs11keepstate\"" \
+ --pkcs11proxy "\"$IPSECpkcs11proxy\"" \
+ --dump "\"$IPSECdumpdir\"" \
+ --opts "\"$IPSECplutoopts\"" \
+ --stderrlog "\"$IPSECplutostderrlog\"" \
+ --wait "\"$IPSECplutowait\"" \
+ --pre "\"$IPSECprepluto\"" \
+ --post "\"$IPSECpostpluto\"" \
+ --log "\"$IPSECsyslog\"" $plutorestartoncrash \
+ --pid "\"$plutopid\"" "||" "{" \
+ $KILLKLIPS ";" \
+ rm -f $lock ";" \
+ exit 1 ";" \
+ "}"
+ fi
+
+ # done!
+ perform echo "\"...$IPSEC_NAME IPsec started\"" "|" $LOGONLY
+ ;;
+
+ stop|--stop|_autostop) # _autostop is same as stop
+ # Shut things down.
+ perform echo "\"Stopping $IPSEC_NAME IPsec...\""
+ perform \
+ if test -r $lock ";" \
+ then \
+ status=0 ";" \
+ . $info ";" \
+ else \
+ echo "\"stop ordered, but IPsec does not appear to be running!\"" ";" \
+ echo "\"doing cleanup anyway...\"" ";" \
+ status=1 ";" \
+ fi
+ if test " $IPSECforwardcontrol" = " yes"
+ then
+ perform test "\"X\$ipforwardingwas\"" = "\"X0\"" "&&" "{" \
+ echo "\"disabling IP forwarding:\"" "|" $LOGONLY ";" \
+ echo 0 ">" $ipforward ";" \
+ "}"
+ fi
+
+ perform test -f $plutopid "&&" "{" \
+ if ps -p '`' cat $plutopid '`' ">" /dev/null ";" \
+ then \
+ ipsec whack --shutdown "|" grep -v "^002" ";" \
+ sleep 1 ";" \
+ if test -s $plutopid ";" \
+ then \
+ echo "\"Attempt to shut Pluto down failed! Trying kill:\"" ";" \
+ kill '`' cat $plutopid '`' ";" \
+ sleep 5 ";" \
+ fi ";" \
+ else \
+ echo "\"Removing orphaned $plutopid:\"" ";" \
+ fi ";" \
+ rm -f $plutopid ";" \
+ "}"
+
+ perform $KILLKLIPS
+
+ perform test -d `dirname $subsyslock` "&&" rm -f $subsyslock
+
+ perform rm -f $info $lock
+ perform echo "...$IPSEC_NAME IPsec stopped" "|" $LOGONLY
+ perform exit \$status
+ ;;
+
+ status|--status)
+ if test " $IPSEC_setupflags" != " "
+ then
+ echo "$me $1 does not support $IPSEC_setupflags"
+ exit 1
+ fi
+
+ if test -f $info
+ then
+ hasinfo=yes
+ fi
+
+ if test -f $lock
+ then
+ haslock=yes
+ fi
+
+ if test -f $subsyslock
+ then
+ hassublock=yes
+ fi
+
+ if test -s $plutopid
+ then
+ if ps -p `cat $plutopid` >/dev/null
+ then
+ plutokind=normal
+ elif ps -C pluto >/dev/null
+ then
+ plutokind=illicit
+ fi
+ elif ps -C pluto >/dev/null
+ then
+ plutokind=orphaned
+ else
+ plutokind=no
+ fi
+
+ if test -r /proc/net/ipsec_eroute
+ then
+ if test " `wc -l </proc/net/ipsec_eroute`" -gt 0
+ then
+ eroutes=yes
+ fi
+ fi
+
+ if test -r $ipsecversion
+ then
+ klips=yes
+ elif test -r $modules
+ then
+ klips=maybe
+ else
+ klips=none
+ fi
+
+ if test "$haslock"
+ then
+ echo "IPsec running"
+ # might not be a subsystem lock dir, ignore that issue
+ if test "$plutokind" = "normal" -a "$klips" = "yes" -a "$hasinfo"
+ then
+ echo "pluto pid `cat $plutopid`"
+ exit 0
+ fi
+ echo "but..."
+ if test "$plutokind" != "normal"
+ then
+ echo "$plutokind Pluto running!"
+ fi
+ if test ! "$hasinfo"
+ then
+ echo "$info file missing!"
+ fi
+ case $klips in
+ maybe) echo "KLIPS module is not loaded!" ;;
+ none) echo "no KLIPS in kernel!" ;;
+ esac
+ if test "$eroutes"
+ then
+ echo "some eroutes exist"
+ fi
+ exit 1
+ else
+ echo "IPsec stopped"
+ if test ! "$hassublock" -a ! "$hasinfo" -a "$plutokind" = "no" \
+ -a ! "$eroutes"
+ then
+ exit 0
+ fi
+ echo "but..."
+ if test "$hassublock"
+ then
+ echo "has subsystem lock ($subsyslock)!"
+ fi
+ if test "$hasinfo"
+ then
+ echo "has $info file!"
+ fi
+ if test "$plutokind" != "normal"
+ then
+ echo "$plutokind Pluto is running!"
+ fi
+ if test "$eroutes"
+ then
+ echo "some eroutes exist!"
+ fi
+ exit 1
+ fi
+ ;;
+
+ --version)
+ if test " $IPSEC_setupflags" != " "
+ then
+ echo "$me $1 does not support $IPSEC_setupflags"
+ exit 1
+ fi
+
+ echo "$me $IPSEC_VERSION"
+ exit 0
+ ;;
+
+ --help)
+ if test " $IPSEC_setupflags" != " "
+ then
+ echo "$me $1 does not support $IPSEC_setupflags"
+ exit 1
+ fi
+
+ echo "Usage: $me {--start|--stop|--restart|--status}"
+ exit 0
+ ;;
+
+ *)
+ echo "Usage: $me {--start|--stop|--restart|--status}" >&2
+ exit 2
+esac
+
+exit 0
diff --git a/programs/_secretcensor/.cvsignore b/programs/_secretcensor/.cvsignore
new file mode 100644
index 000000000..202d856fe
--- /dev/null
+++ b/programs/_secretcensor/.cvsignore
@@ -0,0 +1 @@
+_secretcensor
diff --git a/programs/_secretcensor/Makefile b/programs/_secretcensor/Makefile
new file mode 100644
index 000000000..3df15286e
--- /dev/null
+++ b/programs/_secretcensor/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_secretcensor
+PROGRAMDIR=${LIBDIR}
+
+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:38 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/programs/_secretcensor/_secretcensor.8 b/programs/_secretcensor/_secretcensor.8
new file mode 100644
index 000000000..d502bbd37
--- /dev/null
+++ b/programs/_secretcensor/_secretcensor.8
@@ -0,0 +1,34 @@
+.TH _SECRETCENSOR 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _secretcensor.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _secretcensor \- internal routing to sanitize files
+.SH DESCRIPTION
+.I _secretcensor
+is called by
+.B ipsec barf
+to process the /etc/ipsec.secrets file to remove the private key components
+from the file prior to revealing the contents.
+.SH "SEE ALSO"
+ipsec(8), ipsec_barf(8).
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program by Henry Spencer.
+.\"
+.\" $Log: _secretcensor.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/programs/_secretcensor/_secretcensor.in b/programs/_secretcensor/_secretcensor.in
new file mode 100755
index 000000000..150c13cbc
--- /dev/null
+++ b/programs/_secretcensor/_secretcensor.in
@@ -0,0 +1,75 @@
+#! /bin/sh
+# implements secret censoring for barf
+# Copyright (C) 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _secretcensor.in,v 1.1 2004/03/15 20:35:27 as Exp $
+
+usage="Usage: $0 [file ...]"
+me="ipsec _secretcensor"
+
+for dummy
+do
+ case "$1" in
+ --help) echo "$usage" ; exit 0 ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --) shift ; break ;;
+ -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+awk ' function cool(hot, q, cooled, run) {
+ # warning: may destroy input line!
+ q = "'"'"'" # single quote
+ if (hot ~ q)
+ return "[cannot be summed]"
+ if (hot ~ /^0s/)
+ return "[keyid " substr(hot, 3, 9) "]"
+ run = "echo " q hot q " | md5sum"
+ run | getline
+ close(run)
+ return "[sums to " substr($1, 1, 4) "...]"
+ }
+ /"/ {
+ i = match($0, /"[^"]+"/)
+ cold1 = substr($0, 1, i)
+ cold2 = substr($0, i+RLENGTH-1)
+ hot = substr($0, i+1, RLENGTH-2)
+ print cold1 cool(hot) cold2
+ next
+ }
+ /#pubkey=/ {
+ i = match($0, /^.*#pubkey=/)
+ i += RLENGTH-1
+ cold = substr($0, 1, i)
+ hot = substr($0, i+1)
+ print cold cool(hot)
+ next
+ }
+ /#IN KEY / {
+ i = match($0, /^.*[ \t][^ \t]/)
+ i += RLENGTH-2
+ cold = substr($0, 1, i)
+ hot = substr($0, i+1)
+ print cold cool("0s" hot)
+ next
+ }
+ /^[ \t]+(Modulus|P[a-z]+Exponent|Prime[12]|Exponent[12]|Coefficient):/ {
+ i = match($0, /^[^:]*:[ \t]*/)
+ i += RLENGTH-1
+ cold = substr($0, 1, i)
+ print cold "[...]"
+ next
+ }
+ { print }' $*
diff --git a/programs/_startklips/.cvsignore b/programs/_startklips/.cvsignore
new file mode 100644
index 000000000..a206fe65f
--- /dev/null
+++ b/programs/_startklips/.cvsignore
@@ -0,0 +1 @@
+_startklips
diff --git a/programs/_startklips/Makefile b/programs/_startklips/Makefile
new file mode 100644
index 000000000..9df701b0e
--- /dev/null
+++ b/programs/_startklips/Makefile
@@ -0,0 +1,43 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=_startklips
+PROGRAMDIR=${LIBDIR}
+
+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:42 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/programs/_startklips/_startklips.8 b/programs/_startklips/_startklips.8
new file mode 100644
index 000000000..066699085
--- /dev/null
+++ b/programs/_startklips/_startklips.8
@@ -0,0 +1,33 @@
+.TH _STARTKLIPS 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: _startklips.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec _startklips \- internal script to bring up kernel components
+.SH DESCRIPTION
+.I _startklips
+brings up the FreeS/WAN kernel component. This involves loading any
+required modules, attaching and configuring the ipsecX pseudo-devices and
+attaching the pseudo-devices to the physical devices.
+.SH "SEE ALSO"
+ipsec(8), ipsec_tncfg(8).
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program by Henry Spencer.
+.\"
+.\" $Log: _startklips.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/programs/_startklips/_startklips.in b/programs/_startklips/_startklips.in
new file mode 100755
index 000000000..7f85a94de
--- /dev/null
+++ b/programs/_startklips/_startklips.in
@@ -0,0 +1,367 @@
+#!/bin/sh
+# KLIPS startup script
+# Copyright (C) 1998, 1999, 2001, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: _startklips.in,v 1.6 2005/05/06 22:11:33 as Exp $
+
+me='ipsec _startklips' # for messages
+
+# KLIPS-related paths
+sysflags=/proc/sys/net/ipsec
+modules=/proc/modules
+# full rp_filter path is $rpfilter1/interface/$rpfilter2
+rpfilter1=/proc/sys/net/ipv4/conf
+rpfilter2=rp_filter
+# %unchanged or setting (0, 1, or 2)
+rpfiltercontrol=0
+ipsecversion=/proc/net/ipsec_version
+moduleplace=/lib/modules/`uname -r`/kernel/net/ipsec
+bareversion=`uname -r | sed -e 's/^\(2\.[0-9]\.[1-9][0-9]*-[1-9][0-9]*\(\.[0-9][0-9]*\)*\(\.x\)*\).*$/\1/'`
+moduleinstplace=/lib/modules/$bareversion/kernel/net/ipsec
+modulename=ipsec.o
+klips=true
+netkey=/proc/net/pfkey
+
+info=/dev/null
+log=daemon.error
+for dummy
+do
+ case "$1" in
+ --log) log="$2" ; shift ;;
+ --info) info="$2" ; shift ;;
+ --debug) debug="$2" ; shift ;;
+ --omtu) omtu="$2" ; shift ;;
+ --fragicmp) fragicmp="$2" ; shift ;;
+ --hidetos) hidetos="$2" ; shift ;;
+ --rpfilter) rpfiltercontrol="$2" ; shift ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+
+
+# some shell functions, to clarify the actual code
+
+# set up a system flag based on a variable
+# sysflag value shortname default flagname
+sysflag() {
+ case "$1" in
+ '') v="$3" ;;
+ *) v="$1" ;;
+ esac
+ if test ! -f $sysflags/$4
+ then
+ if test " $v" != " $3"
+ then
+ echo "cannot do $2=$v, $sysflags/$4 does not exist"
+ exit 1
+ else
+ return # can't set, but it's the default anyway
+ fi
+ fi
+ case "$v" in
+ yes|no) ;;
+ *) echo "unknown (not yes/no) $2 value \`$1'"
+ exit 1
+ ;;
+ esac
+ case "$v" in
+ yes) echo 1 >$sysflags/$4 ;;
+ no) echo 0 >$sysflags/$4 ;;
+ esac
+}
+
+# set up a Klips interface
+klipsinterface() {
+ # pull apart the interface spec
+ virt=`expr $1 : '\([^=]*\)=.*'`
+ phys=`expr $1 : '[^=]*=\(.*\)'`
+ case "$virt" in
+ ipsec[0-9]) ;;
+ *) echo "invalid interface \`$virt' in \`$1'" ; exit 1 ;;
+ esac
+
+ # figure out ifconfig for interface
+ addr=
+ eval `ifconfig $phys |
+ awk '$1 == "inet" && $2 ~ /^addr:/ && $NF ~ /^Mask:/ {
+ gsub(/:/, " ", $0)
+ print "addr=" $3
+ other = $5
+ if ($4 == "Bcast")
+ print "type=broadcast"
+ else if ($4 == "P-t-P")
+ print "type=pointopoint"
+ else if (NF == 5) {
+ print "type="
+ other = ""
+ } else
+ print "type=unknown"
+ print "otheraddr=" other
+ print "mask=" $NF
+ }'`
+ if test " $addr" = " "
+ then
+ echo "unable to determine address of \`$phys'"
+ exit 1
+ fi
+ if test " $type" = " unknown"
+ then
+ echo "\`$phys' is of an unknown type"
+ exit 1
+ fi
+ if test " $omtu" != " "
+ then
+ mtu="mtu $omtu"
+ else
+ mtu=
+ fi
+ echo "KLIPS $virt on $phys $addr/$mask $type $otheraddr $mtu" | logonly
+
+ if $klips
+ then
+ # attach the interface and bring it up
+ ipsec tncfg --attach --virtual $virt --physical $phys
+ ifconfig $virt inet $addr $type $otheraddr netmask $mask $mtu
+ fi
+
+ # if %defaultroute, note the facts
+ if test " $2" != " "
+ then
+ (
+ echo "defaultroutephys=$phys"
+ echo "defaultroutevirt=$virt"
+ echo "defaultrouteaddr=$addr"
+ if test " $2" != " 0.0.0.0"
+ then
+ echo "defaultroutenexthop=$2"
+ fi
+ ) >>$info
+ else
+ echo '#dr: no default route' >>$info
+ fi
+
+ # check for rp_filter trouble
+ checkif $phys # thought to be a problem only on phys
+}
+
+# check an interface for problems
+checkif() {
+ $klips || return 0
+ rpf=$rpfilter1/$1/$rpfilter2
+ if test -f $rpf
+ then
+ r="`cat $rpf`"
+ if test " $r" != " 0"
+ then
+ case "$r-$rpfiltercontrol" in
+ 0-%unchanged|0-0|1-1|2-2)
+ # happy state
+ ;;
+ *-%unchanged)
+ echo "WARNING: $1 has route filtering turned on; KLIPS may not work ($rpf is $r)"
+ ;;
+ [012]-[012])
+ echo "WARNING: changing route filtering on $1 (changing $rpf from $r to $rpfiltercontrol)"
+ echo "$rpfiltercontrol" >$rpf
+ ;;
+ [012]-*)
+ echo "ERROR: unknown rpfilter setting: $rpfiltercontrol"
+ ;;
+ *)
+ echo "ERROR: unknown $rpf value $r"
+ ;;
+ esac
+ fi
+ fi
+}
+
+# interfaces=%defaultroute: put ipsec0 on top of default route's interface
+defaultinterface() {
+ phys=`netstat -nr |
+ awk '$1 == "0.0.0.0" && $3 == "0.0.0.0" { print $NF }'`
+ if test " $phys" = " "
+ then
+ echo "no default route, %defaultroute cannot cope!!!"
+ exit 1
+ fi
+ if test `echo " $phys" | wc -l` -gt 1
+ then
+ echo "multiple default routes, %defaultroute cannot cope!!!"
+ exit 1
+ fi
+ next=`netstat -nr |
+ awk '$1 == "0.0.0.0" && $3 == "0.0.0.0" { print $2 }'`
+ klipsinterface "ipsec0=$phys" $next
+}
+
+# log only to syslog, not to stdout/stderr
+logonly() {
+ logger -p $log -t ipsec_setup
+}
+
+# sort out which module is appropriate, changing it if necessary
+setmodule() {
+ wantgoo="`ipsec calcgoo /proc/ksyms`"
+ module=$moduleplace/$modulename
+ if test -f $module
+ then
+ goo="`nm -ao $module | ipsec calcgoo`"
+ if test " $wantgoo" = " $goo"
+ then
+ return # looks right
+ fi
+ fi
+ if test -f $moduleinstplace/$wantgoo
+ then
+ echo "insmod failed, but found matching template module $wantgoo."
+ echo "Copying $moduleinstplace/$wantgoo to $module."
+ rm -f $module
+ mkdir -p $moduleplace
+ cp -p $moduleinstplace/$wantgoo $module
+ # "depmod -a" gets done by caller
+ fi
+}
+
+
+
+# main line
+
+# load module if possible
+if test ! -f $ipsecversion && test ! -f $netkey
+then
+ # statically compiled KLIPS not found; try to load the module
+ insmod ipsec
+fi
+
+if test ! -f $ipsecversion && test ! -f $netkey
+then
+ modprobe -v af_key
+fi
+
+if test -f $netkey
+then
+ klips=false
+ if test -f $modules
+ then
+ modprobe -qv ah4
+ modprobe -qv esp4
+ modprobe -qv ipcomp
+ modprobe -qv xfrm4_tunnel
+ modprobe -qv xfrm_user
+ fi
+fi
+
+if test ! -f $ipsecversion && $klips
+then
+ if test -r $modules # kernel does have modules
+ then
+ setmodule
+ unset MODPATH MODULECONF # no user overrides!
+ depmod -a >/dev/null 2>&1
+ modprobe -v ipsec
+ fi
+ if test ! -f $ipsecversion
+ then
+ echo "kernel appears to lack KLIPS"
+ exit 1
+ fi
+fi
+
+# load all compiled algo modules
+if $klips
+then
+ for alg in aes serpent twofish blowfish sha2
+ do
+ if test -f $moduleinstplace/alg/ipsec_$alg.o
+ then
+ modprobe ipsec_$alg
+ fi
+ done
+fi
+
+# figure out debugging flags
+case "$debug" in
+'') debug=none ;;
+esac
+if test -r /proc/net/ipsec_klipsdebug
+then
+ echo "KLIPS debug \`$debug'" | logonly
+ case "$debug" in
+ none) ipsec klipsdebug --none ;;
+ all) ipsec klipsdebug --all ;;
+ *) ipsec klipsdebug --none
+ for d in $debug
+ do
+ ipsec klipsdebug --set $d
+ done
+ ;;
+ esac
+elif $klips
+then
+ if test " $debug" != " none"
+ then
+ echo "klipsdebug=\`$debug' ignored, KLIPS lacks debug facilities"
+ fi
+fi
+
+# figure out misc. kernel config
+if test -d $sysflags
+then
+ sysflag "$fragicmp" "fragicmp" yes icmp
+ echo 1 >$sysflags/inbound_policy_check # no debate
+ sysflag no "no_eroute_pass" no no_eroute_pass # obsolete parm
+ sysflag no "opportunistic" no opportunistic # obsolete parm
+ sysflag "$hidetos" "hidetos" yes tos
+elif $klips
+then
+ echo "WARNING: cannot adjust KLIPS flags, no $sysflags directory!"
+ # carry on
+fi
+
+if $klips; then
+ # clear tables out in case dregs have been left over
+ ipsec eroute --clear
+ ipsec spi --clear
+elif test $netkey
+then
+ if ip xfrm state > /dev/null 2>&1
+ then
+ ip xfrm state flush
+ ip xfrm policy flush
+ elif type setkey > /dev/null 2>&1
+ then
+ setkey -F
+ setkey -FP
+ else
+ echo "WARNING: cannot flush state/policy database -- \`$1'" |
+ logger -s -p $log -t ipsec_setup
+ fi
+fi
+
+# figure out interfaces
+for i
+do
+ case "$i" in
+ ipsec*=?*) klipsinterface "$i" ;;
+ %defaultroute) defaultinterface ;;
+ *) echo "interface \`$i' not understood"
+ exit 1
+ ;;
+ esac
+done
+
+exit 0
diff --git a/programs/_updown/.cvsignore b/programs/_updown/.cvsignore
new file mode 100644
index 000000000..81e2e4f86
--- /dev/null
+++ b/programs/_updown/.cvsignore
@@ -0,0 +1,2 @@
+_updown
+_updown.in
diff --git a/programs/_updown/Makefile b/programs/_updown/Makefile
new file mode 100644
index 000000000..e0aaab488
--- /dev/null
+++ b/programs/_updown/Makefile
@@ -0,0 +1,22 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/_updown/_updown.8 b/programs/_updown/_updown.8
new file mode 100644
index 000000000..5107d3694
--- /dev/null
+++ b/programs/_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 <http://www.freeswan.org/>
+by Michael Richardson. Original program written by Henry Spencer. Extended
+for the Linux strongSwan project <http://www.strongswan.org/> by Andreas
+Steffen.
diff --git a/programs/_updown/_updown.in b/programs/_updown/_updown.in
new file mode 100755
index 000000000..8db74f737
--- /dev/null
+++ b/programs/_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 <andreas.steffen@strongswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/_updown_espmark/Makefile b/programs/_updown_espmark/Makefile
new file mode 100644
index 000000000..bd9cd38cb
--- /dev/null
+++ b/programs/_updown_espmark/Makefile
@@ -0,0 +1,22 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/_updown_espmark/_updown_espmark.8 b/programs/_updown_espmark/_updown_espmark.8
new file mode 100644
index 000000000..91eaa5cb7
--- /dev/null
+++ b/programs/_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 <http://www.strongswan.org/>
+by Andreas Steffen. Original program written by Henry Spencer.
diff --git a/programs/_updown_espmark/_updown_espmark.in b/programs/_updown_espmark/_updown_espmark.in
new file mode 100644
index 000000000..3627d470d
--- /dev/null
+++ b/programs/_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 <andreas.steffen@strongsec.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/auto/.cvsignore b/programs/auto/.cvsignore
new file mode 100644
index 000000000..865faf10c
--- /dev/null
+++ b/programs/auto/.cvsignore
@@ -0,0 +1 @@
+auto
diff --git a/programs/auto/Makefile b/programs/auto/Makefile
new file mode 100644
index 000000000..035dbf708
--- /dev/null
+++ b/programs/auto/Makefile
@@ -0,0 +1,21 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28:38 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=auto
+
+include ../Makefile.program
diff --git a/programs/auto/auto.8 b/programs/auto/auto.8
new file mode 100644
index 000000000..21b5fd11b
--- /dev/null
+++ b/programs/auto/auto.8
@@ -0,0 +1,481 @@
+.TH IPSEC_AUTO 8 "17 December 2004"
+.\" RCSID $Id: auto.8,v 1.6 2004/12/17 22:34:38 as Exp $
+.SH NAME
+ipsec auto \- control automatically-keyed IPsec connections
+.SH SYNOPSIS
+.B ipsec
+.B auto
+[
+.B \-\-show
+] [
+.B \-\-showonly
+] [
+.B \-\-asynchronous
+]
+.br
+\ \ \ [
+.B \-\-config
+configfile
+] [
+.B \-\-verbose
+] [
+.B \-\-type conn
+]
+.br
+\ \ \ operation
+connection
+.sp
+.B ipsec
+.B auto
+[
+.B \-\-show
+] [
+.B \-\-showonly
+]
+.br
+\ \ \ [
+.B \-\-config
+configfile
+] [
+.B \-\-verbose
+]
+.B \-\-type ca
+.br
+\ \ \ operation
+ca
+.sp
+.B ipsec
+.B auto
+[
+.B \-\-show
+] [
+.B \-\-showonly
+] operation
+.SH DESCRIPTION
+.I Auto
+manipulates automatically-keyed strongSwan IPsec connections,
+setting them up and shutting them down
+based on the information in the IPsec configuration file.
+In the normal usage,
+.I connection
+is the name of a connection specification in the configuration file;
+.I ca
+is the name of a Certification Authority (CA) specification in the configuration file;
+.I operation
+is
+.BR \-\-add ,
+.BR \-\-delete ,
+.BR \-\-replace ,
+.BR \-\-up ,
+.BR \-\-down ,
+.BR \-\-route ,
+or
+.BR \-\-unroute .
+The
+.BR \-\-status
+and
+.BR \-\-statusall
+.I operations
+may take a
+.I connection
+name.
+The
+.BR \-\-ready ,
+.BR \-\-rereadsecrets ,
+.BR \-\-rereadgroups ,
+.BR \-\-rereadcacerts ,
+.BR \-\-rereadaacerts ,
+.BR \-\-rereadocspcerts ,
+.BR \-\-rereadacerts ,
+.BR \-\-rereadcrls ,
+.BR \-\-rereadall ,
+.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 ,
+and
+.BR \-\-purgeocsp
+.I operations
+do not take a connection name.
+.I Auto
+generates suitable
+commands and feeds them to a shell for execution.
+.PP
+The
+.B \-\-add
+operation adds a connection or ca specification to the internal database
+within
+.IR pluto ;
+it will fail if
+.I pluto
+already has a specification by that name.
+The
+.B \-\-delete
+operation deletes a connection or ca specification from
+.IR pluto 's
+internal database (also tearing down any connections based on it);
+it will fail if the specification does not exist.
+The
+.B \-\-replace
+operation is equivalent to
+.B \-\-delete
+(if there is already a specification by the given name)
+followed by
+.BR \-\-add ,
+and is a convenience for updating
+.IR pluto 's
+internal specification to match an external one.
+(Note that a
+.B \-\-rereadsecrets
+may also be needed.)
+The
+.B \-\-rereadgroups
+operation causes any changes to the policy group files to take effect
+(this is currently a synonym for
+.BR \-\-ready ,
+but that may change).
+None of the other operations alters the internal database.
+.PP
+The
+.B \-\-up
+operation asks
+.I pluto
+to establish a connection based on an entry in its internal database.
+The
+.B \-\-down
+operation tells
+.I pluto
+to tear down such a connection.
+.PP
+Normally,
+.I pluto
+establishes a route to the destination specified for a connection as
+part of the
+.B \-\-up
+operation.
+However, the route and only the route can be established with the
+.B \-\-route
+operation.
+Until and unless an actual connection is established,
+this discards any packets sent there,
+which may be preferable to having them sent elsewhere based on a more
+general route (e.g., a default route).
+.PP
+Normally,
+.IR pluto 's
+route to a destination remains in place when a
+.B \-\-down
+operation is used to take the connection down
+(or if connection setup, or later automatic rekeying, fails).
+This permits establishing a new connection (perhaps using a
+different specification; the route is altered as necessary)
+without having a ``window'' in which packets might go elsewhere
+based on a more general route.
+Such a route can be removed using the
+.B \-\-unroute
+operation
+(and is implicitly removed by
+.BR \-\-delete ).
+.PP
+The
+.B \-\-ready
+operation tells
+.I pluto
+to listen for connection-setup requests from other hosts.
+Doing an
+.B \-\-up
+operation before doing
+.B \-\-ready
+on both ends is futile and will not work,
+although this is now automated as part of IPsec startup and
+should not normally be an issue.
+.PP
+The
+.B \-\-status
+operation asks
+.I pluto
+for current connection status either for all connections
+(no connection argument) or a for specified
+.I connection
+name. For more detailed information use
+.B \-\-statusall
+\. The output format is ad-hoc and likely to change.
+.PP
+The
+.B \-\-rereadsecrets
+operation tells
+.I pluto
+to re-read the
+.I /etc/ipsec.secrets
+secret-keys file,
+which it normally reads only at startup time.
+(This is currently a synonym for
+.BR \-\-ready ,
+but that may change.)
+.PP
+The
+.B \-\-rereadcacerts
+operation reads all certificate files contained in the
+.IR /etc/ipsec.d/cacerts
+directory and adds them to
+.IR pluto 's
+list of Certification Authority (CA) certificates.
+.PP
+The
+.B \-\-rereadaacerts
+operation reads all certificate files contained in the
+.IR /etc/ipsec.d/aacerts
+directory and adds them to
+.IR pluto 's
+list of Authorization Authority (AA) certificates.
+.PP
+The
+.B \-\-rereadocspcerts
+operation reads all certificate files contained in the
+.IR /etc/ipsec.d/ocspcerts
+directory and adds them to
+.IR pluto 's
+list of OCSP signer certificates.
+.PP
+The
+.B \-\-rereadacerts
+operation reads all certificate files contained in the
+.IR /etc/ipsec.d/acerts
+directory and adds them to
+.IR pluto 's
+list of attribute certificates.
+.PP
+The
+.B \-\-rereadcrls
+operation reads all certificate revocation list (CRL) files
+contained in the
+.IR /etc/ipsec.d/crls
+directory and adds them to
+.IR pluto 's
+list of CRLs.
+.PP
+The
+.B \-\-rereadall
+operation is equivalent to the execution of
+.BR \-\-rereadsecrets ,
+.BR \-\-rereadcacerts ,
+.BR \-\-rereadaacerts ,
+.BR \-\-rereadocspcerts ,
+.BR \-\-rereadacerts ,
+and
+.BR \-\-rereadcrls .
+.PP
+The
+.B \-\-listalgs
+operation lists all registed IKE encryption and hash algorithms,
+that are available to
+.IR pluto ,
+as well as the Diffie-Hellman (DH) groups.
+.PP
+The
+.B \-\-listpubkeys
+operation lists all RSA public keys either received from peers
+via the IKE protocol embedded in authenticated certificate payloads
+or loaded locally using the
+.BR rightcert \ /
+.BR leftcert
+or
+.BR rightrsasigkey \ /
+.BR leftrsasigkey
+parameters in
+.IR ipsec.conf (5).
+.PP
+The
+.B \-\-listcerts
+operation lists all X.509 and OpenPGP certificates loaded locally using the
+.BR rightcert
+and
+.BR leftcert
+parameters in
+.IR ipsec.conf (5).
+.PP
+The
+.B \-\-listcacerts
+operation lists all X.509 CA certificates either loaded locally from the
+.IR /etc/ipsec.d/cacerts
+directory or received in PKCS#7-wrapped certificate payloads via
+the IKE protocol.
+.PP
+The
+.B \-\-listaacerts
+operation lists all X.509 AA certificates loaded locally from the
+.IR /etc/ipsec.d/aacerts
+directory.
+.PP
+The
+.B \-\-listocspcerts
+operation lists all OCSP signer certificates either loaded locally from the
+.IR /etc/ipsec.d/ocspcerts
+directory or received via the Online Certificate Status Protocol
+from an OCSP server.
+.PP
+The
+.B \-\-listacerts
+operation lists all X.509 attribute certificates loaded locally from the
+.IR /etc/ipsec.d/acerts
+directory.
+.PP
+The
+.B \-\-listgropus
+operation lists all groups that are either used in connection definitions in
+.IR ipsec.conf (5)
+or are embedded in loaded X.509 attributes certificates.
+.PP
+The
+.B \-\-listcainfos
+operation lists the certification authority information specified in the ca
+sections of
+.IR ipsec.conf (5).
+.PP
+The
+.B \-\-listcrls
+operation lists all Certificate Revocation Lists (CRLs) either loaded
+locally from the
+.IR /etc/ipsec.d/crls
+directory or fetched dynamically from an HTTP or LDAP server.
+.PP
+The
+.B \-\-listocsp
+operation lists the certicates status information fetched from
+OCSP servers.
+.PP
+The
+.B \-\-purgeocsp
+operation deletes any cached certificate status information and pending
+OCSP fetch requests.
+.PP
+The
+.B \-\-listcards
+operation lists information about attached smartcards or crypto tokens.
+.PP
+The
+.B \-\-listall
+operation is equivalent to the execution of
+.BR \-\-listalgs ,
+.BR \-\-listpubkeys ,
+.BR \-\-listcerts ,
+.BR \-\-listcacerts ,
+.BR \-\-listaacerts ,
+.BR \-\-listocspcerts ,
+.BR \-\-listacerts ,
+.BR \-\-listgroups ,
+.BR \-\-listcainfos ,
+.BR \-\-listcrls ,
+.BR \-\-listocsp ,
+and
+.BR \-\-listcards .
+.PP
+The
+.B \-\-show
+option turns on the
+.B \-x
+option of the shell used to execute the commands,
+so each command is shown as it is executed.
+.PP
+The
+.B \-\-showonly
+option causes
+.I auto
+to show the commands it would run, on standard output,
+and not run them.
+.PP
+The
+.B \-\-asynchronous
+option, applicable only to the
+.B up
+operation,
+tells
+.I pluto
+to attempt to establish the connection,
+but does not delay to report results.
+This is especially useful to start multiple connections in parallel
+when network links are slow.
+.PP
+The
+.B \-\-verbose
+option instructs
+.I auto
+to pass through all output from
+.IR ipsec_whack (8),
+including log output that is normally filtered out as uninteresting.
+.PP
+The
+.B \-\-config
+option specifies a non-standard location for the IPsec
+configuration file (default
+.IR /etc/ipsec.conf ).
+.PP
+See
+.IR ipsec.conf (5)
+for details of the configuration file.
+Apart from the basic parameters which specify the endpoints and routing
+of a connection (\fBleft\fR
+and
+.BR right ,
+plus possibly
+.BR leftsubnet ,
+.BR leftnexthop ,
+.BR leftfirewall ,
+their
+.B right
+equivalents,
+and perhaps
+.BR type ),
+an
+.I auto
+connection almost certainly needs a
+.B keyingtries
+parameter (since the
+.B keyingtries
+default is poorly chosen).
+.SH FILES
+.ta \w'/var/run/ipsec.info'u+4n
+/etc/ipsec.conf default IPSEC configuration file
+.br
+/var/run/ipsec.info \fB%defaultroute\fR information
+.SH SEE ALSO
+ipsec.conf(5), ipsec(8), ipsec_pluto(8), ipsec_whack(8), ipsec_manual(8)
+.SH HISTORY
+Written for the FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+Extended for the strongSwan project
+<http://www.strongswan.org>
+by Andreas Steffen.
+.SH BUGS
+Although an
+.B \-\-up
+operation does connection setup on both ends,
+.B \-\-down
+tears only one end of the connection down
+(although the orphaned end will eventually time out).
+.PP
+There is no support for
+.B passthrough
+connections.
+.PP
+A connection description which uses
+.B %defaultroute
+for one of its
+.B nexthop
+parameters but not the other may be falsely
+rejected as erroneous in some circumstances.
+.PP
+The exit status of
+.B \-\-showonly
+does not always reflect errors discovered during processing of the request.
+(This is fine for human inspection, but not so good for use in scripts.)
diff --git a/programs/auto/auto.in b/programs/auto/auto.in
new file mode 100755
index 000000000..05568f9b5
--- /dev/null
+++ b/programs/auto/auto.in
@@ -0,0 +1,660 @@
+#! /bin/sh
+# user interface to automatic keying and Pluto in general
+# Copyright (C) 1998, 1999, 2000 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: auto.in,v 1.17 2006/04/20 04:42:12 as Exp $
+
+me='ipsec auto'
+usage="Usage:
+ $me [--showonly] [--asynchronous] --up connectionname
+ $me [--showonly] [-- type conn|ca] --{add|delete|replace|down} name
+ $me [--showonly] --{route|unroute} connectionname
+ $me [--showonly] --ready
+ $me [--showonly] --{status|statusall} [connectionname]
+ $me [--showonly] --{rereadsecrets|rereadgroups}
+ $me [--showonly] --{rereadcacerts|rereadaacerts|rereadocspcerts}
+ $me [--showonly] --{rereadacerts|rereadcrls|rereadall}
+ $me [--showonly] [--utc] --{listalgs|listpubkeys|listcerts}
+ $me [--showonly] [--utc] --{listcacerts|listaacerts|listocspcerts}
+ $me [--showonly] [--utc] --{listacerts|listgroups|listcainfos}
+ $me [--showonly] [--utc] --{listcrls|listocsp|listcards|listall}
+ $me [--showonly] --purgeocsp
+
+ other options: [--config ipsecconfigfile] [--verbose] [--show]"
+
+showonly=
+config=
+info=/var/run/ipsec.info
+shopts=
+noinclude=
+async=
+logfilter='$1 != "002"'
+op=
+argc=
+utc=
+type="conn"
+name="--name"
+
+for dummy
+do
+ case "$1" in
+ --help) echo "$usage" ; exit 0 ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --show) shopts=-x ;;
+ --showonly) showonly=yes ;;
+ --utc) utc="$1" ;;
+ --config) config="--config $2" ; shift ;;
+ --noinclude) noinclude=--noinclude ;;
+ --asynchronous) async="--asynchronous" ;;
+ --verbose) logfilter='1' ;;
+ --type) type="$2" ; shift ;;
+ --up|--down|--add|--delete|--replace|--route|--unroute)
+ if test " $op" != " "
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ op="$1"
+ argc=1
+ if test "$type" = "ca"
+ then
+ name="--caname"
+ case "$op" in
+ --add|--delete|--replace) ;;
+ --*) echo "$op option not supported for --type ca";
+ exit 3 ;;
+ esac
+ fi
+ ;;
+ --status|--statusall)
+ if test " $op" != " "
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ op="$1"
+ argc=1
+ if test $# -eq 1
+ then
+ argc=0; name=
+ fi
+ ;;
+ --ready|--rereadsecrets|--rereadgroups|\
+ --rereadcacerts|--rereadaacerts|--rereadocspcerts|\
+ --rereadacerts|--rereadcrls|--rereadall|\
+ --listalgs|--listpubkeys|--listcerts|\
+ --listcacerts|--listaacerts|--listocspcerts|\
+ --listacerts|--listgroups|--listcainfos|\
+ --listcrls|--listocsp|--listcards|--listall|\
+ --purgeocsp)
+ if test " $op" != " "
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ op="$1"
+ argc=0
+ ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+names=
+case "$op" in
+--*) if test " $argc" -ne $#
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ names="$*"
+ ;;
+*) echo "$usage" >&2 ; exit 2 ;;
+esac
+
+
+runit() {
+ if test "$showonly"
+ then
+ cat
+ else
+ (
+ echo '('
+ cat
+ echo ')'
+ echo 'echo = $?'
+ ) | sh $shopts |
+ awk "/^= / { exit \$2 } $logfilter { print }"
+ fi
+}
+
+case "$op" in
+--ready) echo "ipsec whack --listen" | runit ; exit ;;
+--rereadsecrets) echo "ipsec whack --rereadsecrets" | runit ; exit ;;
+--rereadgroups) echo "ipsec whack --listen" | runit ; exit ;;
+--rereadcacerts) echo "ipsec whack --rereadcacerts" | runit ; exit ;;
+--rereadaacerts) echo "ipsec whack --rereadaacerts" | runit ; exit ;;
+--rereadocspcerts) echo "ipsec whack --rereadocspcerts" | runit ; exit ;;
+--rereadacerts) echo "ipsec whack --rereadacerts" | runit ; exit ;;
+--rereadcrls) echo "ipsec whack --rereadcrls" | runit ; exit ;;
+--rereadall) echo "ipsec whack --rereadall" | runit ; exit ;;
+--listalgs) echo "ipsec whack --listalgs" | runit ; exit ;;
+--listpubkeys) echo "ipsec whack $utc --listpubkeys" | runit ; exit ;;
+--listcerts) echo "ipsec whack $utc --listcerts" | runit ; exit ;;
+--listcacerts) echo "ipsec whack $utc --listcacerts" | runit ; exit ;;
+--listaacerts) echo "ipsec whack $utc --listaacerts" | runit ; exit ;;
+--listocspcerts) echo "ipsec whack $utc --listocspcerts" | runit ; exit ;;
+--listacerts) echo "ipsec whack $utc --listacerts" | runit ; exit ;;
+--listgroups) echo "ipsec whack $utc --listgroups" | runit ; exit ;;
+--listcainfos) echo "ipsec whack $utc --listcainfos" | runit ; exit ;;
+--listcrls) echo "ipsec whack $utc --listcrls" | runit ; exit ;;
+--listocsp) echo "ipsec whack $utc --listocsp" | runit ; exit ;;
+--listcards) echo "ipsec whack $utc --listcards" | runit ; exit ;;
+--listall) echo "ipsec whack $utc --listall" | runit ; exit ;;
+--purgeocsp) echo "ipsec whack $utc --purgeocsp" | runit ; exit ;;
+--up) echo "ipsec whack $async --name $names --initiate" | runit ; exit ;;
+--down) echo "ipsec whack --name $names --terminate" | runit ; exit ;;
+--delete) echo "ipsec whack $name $names --delete" | runit ; exit ;;
+--route) echo "ipsec whack --name $names --route" | runit ; exit ;;
+--unroute) echo "ipsec whack --name $names --unroute" | runit ; exit ;;
+--status) echo "ipsec whack $name $names --status" | runit ; exit ;;
+--statusall) echo "ipsec whack $name $names --statusall" | runit ; exit ;;
+esac
+
+if test -s $info
+then
+ . $info
+fi
+
+ipsec _confread $config $noinclude --type $type $names |
+awk -v section="$type" ' BEGIN {
+ FS = "\t"
+ op = "'"$op"'"
+ err = "cat >&2"
+ draddr = "'"$defaultrouteaddr"'"
+ drnexthop = "'"$defaultroutenexthop"'"
+ failed = 0
+ s[""] = ""
+ init()
+ print "PATH=\"'"$PATH"'\""
+ print "export PATH"
+ flip["left"] = "right"
+ flip["right"] = "left"
+ }
+ function init(n) {
+ for (n in s)
+ delete s[n]
+ name = ""
+ seensome = 0
+ }
+ $1 == ":" {
+ s[$2] = $3
+ seensome = 1
+ next
+ }
+ $1 == "!" {
+ if ($2 != "")
+ fail($2)
+ next
+ }
+ $1 == "=" {
+ if (name == "")
+ name = $2
+ next
+ }
+ $1 == "." {
+ if (section == "ca")
+ output_ca()
+ else
+ output()
+ init()
+ next
+ }
+ {
+ fail("internal error, unknown type code " v($1))
+ }
+ function fail(m) {
+ print "ipsec_auto: fatal error in " v(name) ": " m |err
+ failed = 1
+ exit
+ }
+ function yesno(k) {
+ if ((k in s) && s[k] != "yes" && s[k] != "no")
+ fail("parameter " v(k) " must be \"yes\" or \"no\"")
+ }
+ function setdefault(k, val) {
+ if (!(k in s))
+ s[k] = val
+ }
+ function was(new, old) {
+ if (!(new in s) && (old in s))
+ s[new] = s[old]
+ }
+ function need(k) {
+ if (!(k in s))
+ fail("connection has no " v(k) " parameter specified")
+ if (s[k] == "")
+ fail("parameter " v(k) " value must be non-empty")
+ }
+ function integer(k) {
+ if (!(k in s))
+ return
+ if (s[k] !~ /^[0-9]+$/)
+ fail("parameter " v(k) " value must be integer")
+ }
+ function duration(k, n, t) {
+ if (!(k in s))
+ return
+ t = s[k]
+ n = substr(t, 1, length(t)-1)
+ if (t ~ /^[0-9]+$/)
+ s[k] = t
+ else if (t ~ /^[0-9]+s$/)
+ s[k] = n
+ else if (t ~ /^[0-9]+(\.[0-9]+)?m$/)
+ s[k] = int(n*60)
+ else if (t ~ /^[0-9]+(\.[0-9]+)?h$/)
+ s[k] = int(n*3600)
+ else if (t ~ /^[0-9]+(\.[0-9]+)?d$/)
+ s[k] = int(n*3600*24)
+ else
+ fail("parameter " v(k) " not valid time, must be nnn[smhd]")
+ }
+ function nexthopset(dir, val, k) {
+ k = dir "nexthop"
+ if (k in s)
+ fail("non-default value of " k " is being overridden")
+ if (val != "")
+ s[k] = val
+ else if (k in s)
+ delete s[k]
+ }
+ function id(dir, k) {
+ k = dir "id"
+ if (!(k in s))
+ k = dir
+ return s[k]
+ }
+ function whackkey(dir, which, flag, rk, n) {
+ if (id(dir) == "%opportunistic")
+ return
+ rk = s[dir which]
+ if (rk == "%dnsondemand")
+ {
+ kod="--dnskeyondemand"
+ return
+ }
+ if (rk == "" || rk == "%none" || rk == "%cert" || rk == "0x00")
+ return
+ n = "\"\\\"" name "\\\" " dir which"\""
+ if (rk == "%dns" || rk == "%dnsonload")
+ {
+ if (id(flip[dir]) == "%opportunistic" || s[flip[dir]] == "%any")
+ return
+ print "ipsec whack --label", n, flag,
+ "--keyid", q(id(dir)), "\\"
+ }
+ else
+ {
+ print "ipsec whack --label", n, flag,
+ "--keyid", q(id(dir)),
+ "--pubkeyrsa", q(rk), "\\"
+ }
+ print "\t|| exit $?"
+ }
+ function q(str) { # quoting for shell
+ return "\"" str "\""
+ }
+ function qs(k) { # utility abbreviation for q(s[k])
+ return q(s[k])
+ }
+ function v(str) { # quoting for human viewing
+ return "\"" str "\""
+ }
+ function output() {
+ if (!seensome)
+ fail("internal error, output called inappropriately")
+
+ setdefault("type", "tunnel")
+ type_flags = ""
+ t = s["type"]
+ if (t == "tunnel") {
+ # do NOT default subnets to side/32, despite what
+ # the docs say...
+ type_flags = "--tunnel"
+ } else if (t == "transport") {
+ if ("leftsubnet" in s)
+ fail("type=transport incompatible with leftsubnet")
+ if ("rightsubnet" in s)
+ fail("type=transport incompatible with rightsubnet")
+ type_flags = ""
+ } else if (t == "passthrough") {
+ type_flags = "--pass"
+ } else if (t == "drop") {
+ type_flags = "--drop"
+ } else if (t == "reject") {
+ type_flags = "--reject"
+ } else
+ fail("unknown type " v(t))
+
+ setdefault("failureshunt", "none")
+ t = s["failureshunt"]
+ if (t == "passthrough")
+ type_flags = type_flags " --failpass";
+ else if (t == "drop")
+ type_flags = type_flags " --faildrop";
+ else if (t == "reject")
+ type_flags = type_flags " --failreject";
+ else if (t != "none")
+ fail("unknown failureshunt value " v(t))
+
+ need("left")
+ need("right")
+ if (s["left"] == "%defaultroute") {
+ if (s["right"] == "%defaultroute")
+ fail("left and right cannot both be %defaultroute")
+ if (draddr == "")
+ fail("%defaultroute requested but not known")
+ s["left"] = draddr
+ nexthopset("left", drnexthop)
+ } else if (s["right"] == "%defaultroute") {
+ if (draddr == "")
+ fail("%defaultroute requested but not known")
+ s["right"] = draddr
+ nexthopset("right", drnexthop)
+ }
+
+ setdefault("keyexchange", "ike")
+ if (s["keyexchange"] != "ike")
+ fail("only know how to do keyexchange=ike")
+ setdefault("auth", "esp")
+ if (("auth" in s) && s["auth"] != "esp" && s["auth"] != "ah")
+ fail("only know how to do auth=esp or auth=ah")
+ yesno("pfs")
+
+ setdefault("pfs", "yes")
+ duration("dpddelay")
+ duration("dpdtimeout")
+ if ("dpdaction" in s)
+ {
+ setdefault("dpddelay",30)
+ setdefault("dpdtimeout",120)
+ }
+ yesno("compress")
+ setdefault("compress", "no")
+ setdefault("keylife", "1h")
+ duration("keylife")
+ yesno("rekey")
+ setdefault("rekey", "yes")
+ setdefault("rekeymargin", "9m")
+ duration("rekeymargin")
+ setdefault("keyingtries", "%forever")
+ if (s["keyingtries"] == "%forever")
+ s["keyingtries"] = 0
+ integer("keyingtries")
+ if ("rekeyfuzz" in s) {
+ if (s["rekeyfuzz"] !~ /%$/)
+ fail("rekeyfuzz must be nnn%")
+ r = s["rekeyfuzz"]
+ s["rekeyfuzz"] = substr(r, 1, length(r)-1)
+ integer("rekeyfuzz")
+ }
+ duration("ikelifetime")
+ setdefault("disablearrivalcheck", "no")
+
+ setdefault("leftsendcert", "always")
+ setdefault("rightsendcert", "always")
+
+ setdefault("leftnexthop", "%direct")
+ setdefault("rightnexthop", "%direct")
+ if (s["leftnexthop"] == s["left"])
+ fail("left and leftnexthop must not be the same")
+ if (s["rightnexthop"] == s["right"])
+ fail("right and rightnexthop must not be the same")
+ if (s["leftnexthop"] == "%defaultroute") {
+ if (drnexthop == "")
+ fail("%defaultroute requested but not known")
+ s["leftnexthop"] = drnexthop
+ }
+ if (s["rightnexthop"] == "%defaultroute") {
+ if (drnexthop == "")
+ fail("%defaultroute requested but not known")
+ s["rightnexthop"] = drnexthop
+ }
+
+ if ("leftfirewall" in s && "leftupdown" in s)
+ fail("cannot have both leftfirewall and leftupdown")
+ if ("rightfirewall" in s && "rightupdown" in s)
+ fail("cannot have both rightfirewall and rightupdown")
+ setdefault("leftupdown", "ipsec _updown")
+ setdefault("rightupdown", "ipsec _updown")
+ setdefault("lefthostaccess", "no")
+ setdefault("righthostaccess", "no")
+ yesno("lefthostaccess")
+ yesno("righthostaccess")
+ lha = ""
+ if (s["lefthostaccess"] == "yes")
+ lha = "--hostaccess"
+ rha = ""
+ if (s["righthostaccess"] == "yes")
+ rha = "--hostaccess"
+ setdefault("leftfirewall", "no")
+ setdefault("rightfirewall", "no")
+ yesno("leftfirewall")
+ yesno("rightfirewall")
+ if (s["leftfirewall"] == "yes")
+ s["leftupdown"] = s["leftupdown"] " iptables"
+ if (s["rightfirewall"] == "yes")
+ s["rightupdown"] = s["rightupdown"] " iptables"
+
+ setdefault("authby", "rsasig")
+ t = s["authby"]
+ if (t == "rsasig" || t == "secret|rsasig" || t == "rsasig|secret") {
+ authtype = "--rsasig"
+ type_flags = "--encrypt " type_flags
+ if (!("leftcert" in s)) {
+ setdefault("leftrsasigkey", "%cert")
+ if (id("left") == "%any" &&
+ !(s["leftrsasigkey"] == "%cert" ||
+ s["leftrsasigkey"] == "0x00") )
+ fail("ID " v(id("left")) " cannot have RSA key")
+ }
+ if (!("rightcert" in s)) {
+ setdefault("rightrsasigkey", "%cert")
+ if (id("right") == "%any" &&
+ !(s["rightrsasigkey"] == "%cert" ||
+ s["rightrsasigkey"] == "0x00") )
+ fail("ID " v(id("right")) " cannot have RSA key")
+ }
+ if (t != "rsasig")
+ authtype = authtype " --psk"
+ } else if (t == "secret") {
+ authtype = "--psk"
+ type_flags = "--encrypt " type_flags
+ } else if (t == "never") {
+ authtype = ""
+ } else {
+ fail("unknown authby value " v(t))
+ }
+
+ settings = type_flags
+ setdefault("ike", "3des-sha,3des-md5")
+ if (s["ike"] != "")
+ settings = settings " --ike " qs("ike")
+ setdefault("esp", "3des")
+ if (s["esp"] != "")
+ settings = settings " --esp " qs("esp")
+ if (s["auth"] == "ah")
+ settings = settings " --authenticate"
+ if (s["pfs"] == "yes") {
+ settings = settings " --pfs"
+ if (s["pfsgroup"] != "")
+ settings = settings " --pfsgroup " qs("pfsgroup")
+ }
+
+ if (s["dpdaction"])
+ settings = settings " --dpdaction " qs("dpdaction")
+ if (s["dpddelay"])
+ settings = settings " --dpddelay " qs("dpddelay")
+ if (s["dpdtimeout"])
+ settings = settings " --dpdtimeout " qs("dpdtimeout")
+
+ if (s["compress"] == "yes")
+ settings = settings " --compress"
+ if (op == "--replace")
+ settings = settings " --delete"
+ if ("ikelifetime" in s)
+ settings = settings " --ikelifetime " qs("ikelifetime")
+ if (s["disablearrivalcheck"] == "yes")
+ settings = settings " --disablearrivalcheck"
+ settings = settings " " authtype
+
+ lc = ""
+ rc = ""
+ if ("leftsubnet" in s)
+ lc = "--client " qs("leftsubnet")
+ if ("rightsubnet" in s)
+ rc = "--client " qs("rightsubnet")
+ if ("leftsubnetwithin" in s)
+ lc = lc " --clientwithin " qs("leftsubnetwithin")
+ if ("rightsubnetwithin" in s)
+ rc = rc " --clientwithin " qs("rightsubnetwithin")
+ lp = ""
+ rp = ""
+ if ("leftprotoport" in s)
+ lp = "--clientprotoport " qs("leftprotoport")
+ if ("rightprotoport" in s)
+ rp = "--clientprotoport " qs("rightprotoport")
+ lud = "--updown " qs("leftupdown")
+ rud = "--updown " qs("rightupdown")
+
+ lid = ""
+ if ("leftid" in s)
+ lid = "--id " qs("leftid")
+ rid = ""
+ if ("rightid" in s)
+ rid = "--id " qs("rightid")
+ lsip = ""
+ if ("leftsourceip" in s)
+ lsip = "--srcip " qs("leftsourceip")
+ rsip = ""
+ if ("rightsourceip" in s)
+ rsip = "--srcip " qs("rightsourceip")
+ lscert = ""
+ if ("leftsendcert" in s)
+ lscert = "--sendcert " qs("leftsendcert")
+ rscert = ""
+ if ("rightsendcert" in s)
+ rscert = "--sendcert " qs("rightsendcert")
+ lcert = ""
+ if ("leftcert" in s)
+ lcert = "--cert " qs("leftcert")
+ rcert = ""
+ if ("rightcert" in s)
+ rcert = "--cert " qs("rightcert")
+ lca = ""
+ if ("leftca" in s)
+ lca = "--ca " qs("leftca")
+ rca = ""
+ if ("rightca" in s)
+ rca = "--ca " qs("rightca")
+ lgr = ""
+ if ("leftgroups" in s)
+ lgr = "--groups " qs("leftgroups")
+ rgr = ""
+ if ("rightgroups" in s)
+ rgr = "--groups " qs("rightgroups")
+ fuzz = ""
+ if ("rekeyfuzz" in s)
+ fuzz = "--rekeyfuzz " qs("rekeyfuzz")
+ rk = ""
+ if (s["rekey"] == "no")
+ rk = "--dontrekey"
+ pd = ""
+ if ("_plutodevel" in s)
+ pd = "--plutodevel " s["_plutodevel"] # not qs()
+
+ lkod = ""
+ rkod = ""
+ if (authtype != "--psk") {
+ kod = ""
+ whackkey("left", "rsasigkey", "")
+ whackkey("left", "rsasigkey2", "--addkey")
+ lkod = kod
+ kod = ""
+ whackkey("right", "rsasigkey", "")
+ whackkey("right", "rsasigkey2", "--addkey")
+ rkod = kod
+ }
+ print "ipsec whack --name", name, settings, "\\"
+ print "\t--host", qs("left"), lc, lp, "--nexthop",
+ qs("leftnexthop"), lud, lha, lid, lkod, lscert, lcert, lca, lsip, lgr, "\\"
+ print "\t--to", "--host", qs("right"), rc, rp, "--nexthop",
+ qs("rightnexthop"), rud, rha, rid, rkod, rscert, rcert, rca, rsip, rgr, "\\"
+ print "\t--ipseclifetime", qs("keylife"),
+ "--rekeymargin", qs("rekeymargin"), "\\"
+ print "\t--keyingtries", qs("keyingtries"), fuzz, rk, pd, "\\"
+ print "\t|| exit $?"
+ }
+ function output_ca() {
+ if (!seensome)
+ fail("internal error, output called inappropriately")
+ settings = ""
+ if (op == "--replace")
+ settings = "--delete"
+ cacert = ""
+ if ("cacert" in s)
+ cacert = "--cacert " qs("cacert")
+ ldaphost = ""
+ if ("ldaphost" in s)
+ ldaphost = "--ldaphost " qs("ldaphost")
+ ldapbase = ""
+ if ("ldapbase" in s)
+ ldapbase = "--ldapbase " qs("ldapbase")
+ crluri = ""
+ if ("crluri" in s)
+ crluri = "--crluri " qs("crluri")
+ crluri2 = ""
+ if ("crluri2" in s)
+ crluri2 = "--crluri2 " qs("crluri2")
+ ocspuri = ""
+ if ("ocspuri" in s)
+ ocspuri = "--ocspuri " qs("ocspuri")
+ yesno("strictcrlpolicy")
+ setdefault("strictcrlpolicy", "no")
+ if (s["strictcrlpolicy"] == "yes")
+ settings = settings " --strictcrlpolicy"
+ yesno("cachecrls")
+ setdefault("cachecrls", "no")
+ if (s["cachecrls"] == "yes")
+ settings = settings " --cachecrls"
+
+ print "ipsec whack --caname", name, settings, cacert, ldaphost, ldapbase,
+ crluri, crluri2, ocspuri, "\\"
+ print "\t|| exit $?"
+ }
+ END {
+ if (failed) {
+ print "# fatal error discovered, force failure using \"false\" command"
+ print "false"
+ exit 1 # just on general principles
+ }
+ if (seensome) {
+ if (section == "ca")
+ output_ca()
+ else
+ output()
+ }
+ }' | runit
diff --git a/programs/barf/.cvsignore b/programs/barf/.cvsignore
new file mode 100644
index 000000000..bca77a6ee
--- /dev/null
+++ b/programs/barf/.cvsignore
@@ -0,0 +1 @@
+barf
diff --git a/programs/barf/Makefile b/programs/barf/Makefile
new file mode 100644
index 000000000..6a20d4ee2
--- /dev/null
+++ b/programs/barf/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=barf
+
+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.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/programs/barf/barf.8 b/programs/barf/barf.8
new file mode 100644
index 000000000..e692a4e5f
--- /dev/null
+++ b/programs/barf/barf.8
@@ -0,0 +1,84 @@
+.TH IPSEC_BARF 8 "17 March 2002"
+.\" RCSID $Id: barf.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.SH NAME
+ipsec barf \- spew out collected IPsec debugging information
+.SH SYNOPSIS
+.B ipsec
+.B barf
+[
+.B \-\-short
+]
+.sp
+.SH DESCRIPTION
+.I Barf
+outputs (on standard output) a collection of debugging information
+(contents of files, selections from logs, etc.)
+related to the IPsec encryption/authentication system.
+It is primarily a convenience for remote debugging,
+a single command which packages up (and labels) all information
+that might be relevant to diagnosing a problem in IPsec.
+.PP
+.PP
+The
+.B \-\-short
+option limits the length of
+the log portion of
+.IR barf 's
+output, which can otherwise be extremely voluminous
+if debug logging is turned on.
+.PP
+.I Barf
+censors its output,
+replacing keys
+and secrets with brief checksums to avoid revealing sensitive information.
+.PP
+Beware that the output of both commands is aimed at humans,
+not programs,
+and the output format is subject to change without warning.
+.PP
+.I Barf
+has to figure out which files in
+.I /var/log
+contain the IPsec log messages.
+It looks for KLIPS and general log messages first in
+.IR messages
+and
+.IR syslog ,
+and for Pluto messages first in
+.IR secure ,
+.IR auth.log ,
+and
+.IR debug .
+In both cases,
+if it does not find what it is looking for in one of those ``likely'' places,
+it will resort to a brute-force search of most (non-compressed) files in
+.IR /var/log .
+.SH FILES
+.nf
+/proc/net/*
+/var/log/*
+/etc/ipsec.conf
+/etc/ipsec.secrets
+.fi
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+.I Barf
+uses heuristics to try to pick relevant material out of the logs,
+and relevant messages
+which are not labelled with any of the tags that
+.I barf
+looks for will be lost.
+We think we've eliminated the last such case, but one never knows...
+.PP
+Finding
+.I updown
+scripts (so they can be included in output) is, in general, difficult.
+.I Barf
+uses a very simple heuristic that is easily fooled.
+.PP
+The brute-force search for the right log files can get expensive on
+systems with a lot of clutter in
+.IR /var/log .
diff --git a/programs/barf/barf.in b/programs/barf/barf.in
new file mode 100755
index 000000000..99cc3546c
--- /dev/null
+++ b/programs/barf/barf.in
@@ -0,0 +1,296 @@
+#! /bin/sh
+# dump assorted information of use in debugging
+# Copyright (C) 1998, 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: barf.in,v 1.4 2004/09/23 21:08:23 as Exp $
+
+IPSEC_NAME="strongSwan"
+
+KERNSRC=${KERNSRC-/usr/src/linux}
+LOGS=${LOGS-/var/log}
+CONFS=${IPSEC_CONFS-/etc}
+CONFDDIR=${IPSEC_CONFDDIR-/etc/ipsec.d}
+me="ipsec barf"
+
+# kludge to produce no barf output mentioning policygroups if none are present.
+# This will not catch ".file" policygroups.
+PREPOLICIES=${CONFDDIR}/policies
+if [ `ls $PREPOLICIES 2> /dev/null | wc -l` -ne 0 ]
+then
+ POLICIES=$PREPOLICIES
+fi
+
+# message patterns that start relevant parts of logs
+fstart="Starting $IPSEC_NAME"
+pstart='Starting Pluto subsystem'
+
+case "$1" in
+--help) echo "Usage: ipsec barf" ; exit 0 ;;
+--version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+esac
+
+# make sure output is in English
+unset LANG LANGUAGE LC_ALL LC_MESSAGES
+
+# log-location guesser, results in $findlog_file and $findlog_startline
+# Fine point: startline is the *last* line containing "string", or
+# failing that, the *first* line containing "fallbackstring".
+findlog() { # findlog string fallbackstring possiblefile ...
+ s="$1"
+ shift
+ t="$1"
+ shift
+ # try the suggested files first
+ for f in $*
+ do
+ if test -r $LOGS/$f -a -f $LOGS/$f && egrep -q "$s" $LOGS/$f
+ then
+ # aha, this one has it
+ findlog_file=$LOGS/$f
+ findlog_startline=`egrep -n "$s" $LOGS/$f |
+ sed -n '$s/:.*//p'`
+ return 0
+ fi
+ done
+ for f in $*
+ do
+ if test -r $LOGS/$f -a -f $LOGS/$f && egrep -q "$t" $LOGS/$f
+ then
+ # aha, this one has it
+ findlog_file=$LOGS/$f
+ findlog_startline=`egrep -n "$t" $LOGS/$f |
+ sed -n '1s/:.*//p'`
+ return 0
+ fi
+ done
+ # nope, resort to a search, newest first, of uncompressed logs
+ for f in `ls -t $LOGS | egrep -v '^mail' | egrep -v '\.(gz|Z)$'`
+ do
+ if test -r $LOGS/$f -a ! -d $LOGS/$f && egrep -q "$s" $LOGS/$f
+ then
+ # found it
+ findlog_file=$LOGS/$f
+ findlog_startline=`egrep -n "$s" $LOGS/$f |
+ sed -n '$s/:.*//p'`
+ return 0
+ fi
+ done
+ for f in `ls -t $LOGS | egrep -v '^mail' | egrep -v '\.(gz|Z)$'`
+ do
+ if test -r $LOGS/$f -a -f $LOGS/$f && egrep -q "$t" $LOGS/$f
+ then
+ # found it
+ findlog_file=$LOGS/$f
+ findlog_startline=`egrep -n "$t" $LOGS/$f |
+ sed -n '1s/:.*//p'`
+ return 0
+ fi
+ done
+# echo "$0: unable to find $LOGS/$1 or local equivalent" >&2
+ findlog_file=/dev/null
+ findlog_startline=1 # arbitrary
+}
+
+# try to guess where logs are
+findlog "$fstart" "klips" messages syslog
+if test " $findlog_file" = " /dev/null"
+then
+echo "Unable to find KLIPS messages, typically found in /var/log/messages or equivalent. You may need to run $IPSEC_NAME for the first time; alternatively, your log files have been emptied (ie, logwatch) or we do not understand your logging configuration."
+fi
+klog=$findlog_file
+kline=$findlog_startline
+
+findlog "$pstart" "Pluto" secure auth.log debug
+if test " $findlog_file" = " /dev/null"
+then
+echo "Unable to find Pluto messages, typically found in /var/log/secure or equivalent. You may need to run $IPSEC_NAME for the first time; alternatively, your log files have been emptied (ie, logwatch) or we do not understand your logging configuration."
+fi
+plog=$findlog_file
+pline=$findlog_startline
+
+# /lib/modules examiner
+modulegoo() {
+ set +x
+ for d in `ls /lib/modules`
+ do
+ if test -d /lib/modules/$d
+ then
+ f=/lib/modules/$d/$1
+ if test -f $f
+ then
+ nm -g $f | egrep "$2"
+ else
+ echo
+ fi | sed "s;^;$d: ;"
+ fi
+ done
+ set -x
+}
+
+# advanced shell deviousness to get dividers into output
+_________________________() {
+ $2 # something to do nothing and not echo anything
+}
+
+exec 2>&1 # stderr on stdout, so errors go into main output
+
+hostname ; date
+set -x
+_________________________ version
+ipsec --version
+_________________________ proc/version
+cat /proc/version
+_________________________ proc/net/ipsec_eroute
+sort -sg +3 /proc/net/ipsec_eroute || cat /proc/net/ipsec_eroute
+_________________________ netstat-rn
+netstat -nr
+_________________________ proc/net/ipsec_spi
+cat /proc/net/ipsec_spi
+_________________________ proc/net/ipsec_spigrp
+cat /proc/net/ipsec_spigrp
+_________________________ proc/net/ipsec_tncfg
+cat /proc/net/ipsec_tncfg
+_________________________ proc/net/pf_key
+cat /proc/net/pf_key
+_________________________ proc/net/pf_key-star
+( cd /proc/net && egrep '^' pf_key_* )
+_________________________ proc/sys/net/ipsec-star
+( cd /proc/sys/net/ipsec && egrep '^' * )
+_________________________ ipsec/statusall
+ipsec auto --statusall
+_________________________ ifconfig-a
+ifconfig -a
+_________________________ mii-tool
+if [ -x /sbin/mii-tool ]
+then
+ /sbin/mii-tool -v
+elif [ -x /usr/sbin/mii-tool ]
+then
+ /usr/sbin/mii-tool -v
+else
+ mii-tool -v
+fi
+_________________________ ipsec/directory
+ipsec --directory
+_________________________ hostname/fqdn
+hostname --fqdn
+_________________________ hostname/ipaddress
+hostname --ip-address
+_________________________ uptime
+uptime
+_________________________ ps
+# -i ppid picks up the header
+ps alxwf | egrep -i 'ppid|pluto|ipsec|klips'
+_________________________ ipsec/showdefaults
+ipsec showdefaults
+_________________________ ipsec/conf
+ipsec _include $CONFS/ipsec.conf | ipsec _keycensor
+_________________________ ipsec/secrets
+ipsec _include $CONFS/ipsec.secrets | ipsec _secretcensor
+_________________________ ipsec/listall
+ipsec auto --listall
+if [ $POLICIES ]
+then
+ for policy in $POLICIES/*; do base=`basename $policy`;
+ _________________________ ipsec/policies/$base
+ cat $policy
+ done
+fi
+_________________________ ipsec/ls-libdir
+ls -l ${IPSEC_LIBDIR-/usr/local/lib/ipsec}
+_________________________ ipsec/ls-execdir
+ls -l ${IPSEC_EXECDIR-/usr/local/libexec/ipsec}
+_________________________ ipsec/updowns
+for f in `ls ${IPSEC_EXECDIR-/usr/local/libexec/ipsec} | egrep updown`
+do
+ cat ${IPSEC_EXECDIR-/usr/local/libexec/ipsec}/$f
+done
+_________________________ proc/net/dev
+cat /proc/net/dev
+_________________________ proc/net/route
+cat /proc/net/route
+_________________________ proc/sys/net/ipv4/ip_forward
+cat /proc/sys/net/ipv4/ip_forward
+_________________________ proc/sys/net/ipv4/conf/star-rp_filter
+( cd /proc/sys/net/ipv4/conf && egrep '^' */rp_filter )
+_________________________ uname-a
+uname -a
+_________________________ redhat-release
+if test -r /etc/redhat-release
+then
+ cat /etc/redhat-release
+fi
+_________________________ proc/net/ipsec_version
+cat /proc/net/ipsec_version
+_________________________ iptables/list
+iptables -L -v -n
+_________________________ ipchains/list
+ipchains -L -v -n
+_________________________ ipfwadm/forward
+ipfwadm -F -l -n -e
+_________________________ ipfwadm/input
+ipfwadm -I -l -n -e
+_________________________ ipfwadm/output
+ipfwadm -O -l -n -e
+_________________________ iptables/nat
+iptables -t nat -L -v -n
+_________________________ ipchains/masq
+ipchains -M -L -v -n
+_________________________ ipfwadm/masq
+ipfwadm -M -l -n -e
+_________________________ iptables/mangle
+iptables -t mangle -L -v -n
+_________________________ proc/modules
+cat /proc/modules
+_________________________ proc/meminfo
+cat /proc/meminfo
+_________________________ dev/ipsec-ls
+ls -l /dev/ipsec*
+_________________________ proc/net/ipsec-ls
+ls -l /proc/net/ipsec_*
+_________________________ usr/src/linux/.config
+if test -f $KERNSRC/.config
+then
+ egrep 'IP|NETLINK' $KERNSRC/.config
+fi
+_________________________ etc/syslog.conf
+cat /etc/syslog.conf
+_________________________ etc/resolv.conf
+cat /etc/resolv.conf
+_________________________ lib/modules-ls
+ls -ltr /lib/modules
+_________________________ proc/ksyms-netif_rx
+egrep netif_rx /proc/ksyms
+_________________________ lib/modules-netif_rx
+modulegoo kernel/net/ipv4/ipip.o netif_rx
+_________________________ kern.debug
+if test -f $LOGS/kern.debug
+then
+ tail -100 $LOGS/kern.debug
+fi
+_________________________ klog
+sed -n $kline,'$'p $klog |
+ egrep -i 'ipsec|klips|pluto' |
+ case "$1" in
+ --short) tail -500 ;;
+ *) cat ;;
+ esac
+_________________________ plog
+sed -n $pline,'$'p $plog |
+ egrep -i 'pluto' |
+ case "$1" in
+ --short) tail -500 ;;
+ *) cat ;;
+ esac
+_________________________ date
+date
diff --git a/programs/calcgoo/.cvsignore b/programs/calcgoo/.cvsignore
new file mode 100644
index 000000000..b4aa748b7
--- /dev/null
+++ b/programs/calcgoo/.cvsignore
@@ -0,0 +1 @@
+calcgoo
diff --git a/programs/calcgoo/Makefile b/programs/calcgoo/Makefile
new file mode 100644
index 000000000..8e3cae9ea
--- /dev/null
+++ b/programs/calcgoo/Makefile
@@ -0,0 +1,41 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=calcgoo
+
+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.1 2002/06/10 04:27:25 mcr
+# calcgoo program processes kernel symbol list and generates a
+# composite value by xor'ing the programmed symbol.
+#
+# Revision 1.1 2002/06/10 00:19:44 mcr
+# rename "ipsec check" to "ipsec verify"
+#
+# Revision 1.1 2002/06/08 17:01:25 mcr
+# added new program "ipsec check" to do rudamentary testing
+# on a newly installed system to see if it is OE ready.
+#
+#
+#
+
diff --git a/programs/calcgoo/calcgoo.8 b/programs/calcgoo/calcgoo.8
new file mode 100644
index 000000000..ceb576e41
--- /dev/null
+++ b/programs/calcgoo/calcgoo.8
@@ -0,0 +1,31 @@
+.TH IPSEC_CALCGOO 8 "8 June 2002"
+.\" RCSID $Id: calcgoo.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.SH NAME
+ipsec calcgoo \- calculate hex value for matching modules and kernels
+.SH SYNOPSIS
+.B ipsec
+.B calcgoo
+.SH DESCRIPTION
+.I calcgoo
+accepts the output of
+.B nm -ao
+or
+.B /proc/ksyms
+and extracts a release dependant list of symbols from it. The symbols
+are processed to extract the values assigned during the MODVERSIONS
+process. This process makes sure that Linux modules are only loaded
+on matching kernels.
+.P
+This routine is used to find an appropriate module to match the currently
+running kernel by _startklips.
+.SH FILES
+.nf
+/proc/ksyms
+.fi
+.SH "SEE ALSO"
+ipsec__startklips(8), genksyms(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Michael Richardson.
+.SH BUGS
diff --git a/programs/calcgoo/calcgoo.in b/programs/calcgoo/calcgoo.in
new file mode 100644
index 000000000..0d383d173
--- /dev/null
+++ b/programs/calcgoo/calcgoo.in
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+$MODULE_GOO_LIST="@MODULE_GOO_LIST@";
+
+@goo = split(/\s+/,$MODULE_GOO_LIST);
+
+$sep="(";
+$goore=" ";
+
+#print "GOO: ",join('|',@goo),"\n";
+
+foreach $sym (@goo) {
+ $goore=${goore}.${sep}.${sym};
+ $sep="|";
+}
+$goore=${goore}.")_R(smp_){0,1}([0-9A-F]{8})";
+
+#print "GOORE: $goore\n";
+
+while(<>) {
+ chomp;
+ if(/$goore/io) {
+ $sym=$1;
+ $goosym=$3;
+ $bingoo=hex($goosym);
+ if($2 eq "smp_") {
+ $bingoo++;
+ }
+ #print STDERR "Processing $goosym (from $_)\n";
+ $bingoo{$sym}=$bingoo;
+ }
+}
+$wholegoo=0;
+foreach $sym (keys %bingoo) {
+ $wholegoo=$wholegoo ^ $bingoo{$sym};
+}
+print sprintf("%08x", $wholegoo)."\n";
+
+# Local variables::
+# mode: perl
+# End variables::
+
+
diff --git a/programs/charon/Doxyfile b/programs/charon/Doxyfile
new file mode 100644
index 000000000..5ee25a839
--- /dev/null
+++ b/programs/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/programs/charon/Makefile b/programs/charon/Makefile
new file mode 100644
index 000000000..b69438b84
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon.kdevelop b/programs/charon/charon.kdevelop
new file mode 100644
index 000000000..270e815c4
--- /dev/null
+++ b/programs/charon/charon.kdevelop
@@ -0,0 +1,105 @@
+<?xml version = '1.0'?>
+<kdevelop>
+ <general>
+ <author>Martin Willi</author>
+ <email>martin@strongswan.org</email>
+ <version>$VERSION$</version>
+ <projectmanagement>KDevCustomProject</projectmanagement>
+ <primarylanguage>C</primarylanguage>
+ <ignoreparts/>
+ </general>
+ <kdevcustomproject>
+ <run>
+ <mainprogram>Source</mainprogram>
+ <directoryradio>executable</directoryradio>
+ </run>
+ <general>
+ <activedir/>
+ </general>
+ </kdevcustomproject>
+ <kdevdebugger>
+ <general>
+ <dbgshell/>
+ </general>
+ </kdevdebugger>
+ <kdevdoctreeview>
+ <ignoretocs>
+ <toc>ada</toc>
+ <toc>ada_bugs_gcc</toc>
+ <toc>bash</toc>
+ <toc>bash_bugs</toc>
+ <toc>clanlib</toc>
+ <toc>fortran_bugs_gcc</toc>
+ <toc>gnome1</toc>
+ <toc>gnustep</toc>
+ <toc>gtk</toc>
+ <toc>gtk_bugs</toc>
+ <toc>haskell</toc>
+ <toc>haskell_bugs_ghc</toc>
+ <toc>java_bugs_gcc</toc>
+ <toc>java_bugs_sun</toc>
+ <toc>kde2book</toc>
+ <toc>libstdc++</toc>
+ <toc>opengl</toc>
+ <toc>pascal_bugs_fp</toc>
+ <toc>php</toc>
+ <toc>php_bugs</toc>
+ <toc>perl</toc>
+ <toc>perl_bugs</toc>
+ <toc>python</toc>
+ <toc>python_bugs</toc>
+ <toc>qt-kdev3</toc>
+ <toc>ruby</toc>
+ <toc>ruby_bugs</toc>
+ <toc>sdl</toc>
+ <toc>stl</toc>
+ <toc>sw</toc>
+ <toc>w3c-dom-level2-html</toc>
+ <toc>w3c-svg</toc>
+ <toc>w3c-uaag10</toc>
+ <toc>wxwidgets_bugs</toc>
+ </ignoretocs>
+ <ignoreqt_xml>
+ <toc>Guide to the Qt Translation Tools</toc>
+ <toc>Qt Assistant Manual</toc>
+ <toc>Qt Designer Manual</toc>
+ <toc>Qt Reference Documentation</toc>
+ <toc>qmake User Guide</toc>
+ </ignoreqt_xml>
+ <ignoredoxygen>
+ <toc>KDE Libraries (Doxygen)</toc>
+ </ignoredoxygen>
+ </kdevdoctreeview>
+ <kdevfilecreate>
+ <filetypes/>
+ <useglobaltypes>
+ <type ext="c" />
+ <type ext="h" />
+ </useglobaltypes>
+ </kdevfilecreate>
+ <kdevcppsupport>
+ <references/>
+ <codecompletion>
+ <includeGlobalFunctions>true</includeGlobalFunctions>
+ <includeTypes>true</includeTypes>
+ <includeEnums>true</includeEnums>
+ <includeTypedefs>false</includeTypedefs>
+ <automaticCodeCompletion>true</automaticCodeCompletion>
+ <automaticArgumentsHint>true</automaticArgumentsHint>
+ <automaticHeaderCompletion>true</automaticHeaderCompletion>
+ <codeCompletionDelay>250</codeCompletionDelay>
+ <argumentsHintDelay>400</argumentsHintDelay>
+ <headerCompletionDelay>250</headerCompletionDelay>
+ </codecompletion>
+ </kdevcppsupport>
+ <kdevfileview>
+ <groups>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ <hidenonlocation>false</hidenonlocation>
+ </groups>
+ <tree>
+ <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+ <hidenonprojectfiles>false</hidenonprojectfiles>
+ </tree>
+ </kdevfileview>
+</kdevelop>
diff --git a/programs/charon/charon/Makefile.charon b/programs/charon/charon/Makefile.charon
new file mode 100644
index 000000000..336495db9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/config/Makefile.config b/programs/charon/charon/config/Makefile.config
new file mode 100644
index 000000000..d4638b318
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/config/configuration.c b/programs/charon/charon/config/configuration.c
new file mode 100755
index 000000000..eac1bd43a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#include "configuration.h"
+
+#include <types.h>
+
+/**
+ * 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/programs/charon/charon/config/configuration.h b/programs/charon/charon/config/configuration.h
new file mode 100755
index 000000000..6b741f9fb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+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/programs/charon/charon/config/connections/Makefile.connections b/programs/charon/charon/config/connections/Makefile.connections
new file mode 100644
index 000000000..8fbc983f6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/config/connections/connection.c b/programs/charon/charon/config/connections/connection.c
new file mode 100644
index 000000000..74e6762b4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "connection.h"
+
+#include <utils/linked_list.h>
+#include <utils/logger.h>
+
+/**
+ * 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/programs/charon/charon/config/connections/connection.h b/programs/charon/charon/config/connections/connection.h
new file mode 100644
index 000000000..2cb3c20b8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/host.h>
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+#include <config/proposal.h>
+#include <crypto/diffie_hellman.h>
+
+
+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/programs/charon/charon/config/connections/connection_store.h b/programs/charon/charon/config/connections/connection_store.h
new file mode 100755
index 000000000..41fd58e42
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <config/connections/connection.h>
+
+
+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/programs/charon/charon/config/connections/local_connection_store.c b/programs/charon/charon/config/connections/local_connection_store.c
new file mode 100644
index 000000000..3f07f0d21
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "local_connection_store.h"
+
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+
+
+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**)&current);
+
+ 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**)&current);
+
+ 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**)&current);
+ 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/programs/charon/charon/config/connections/local_connection_store.h b/programs/charon/charon/config/connections/local_connection_store.h
new file mode 100644
index 000000000..14a0a24ae
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <config/connections/connection_store.h>
+
+
+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/programs/charon/charon/config/credentials/Makefile.credentials b/programs/charon/charon/config/credentials/Makefile.credentials
new file mode 100644
index 000000000..720d56656
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/config/credentials/credential_store.h b/programs/charon/charon/config/credentials/credential_store.h
new file mode 100755
index 000000000..2339469c0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <crypto/rsa/rsa_private_key.h>
+#include <crypto/rsa/rsa_public_key.h>
+#include <utils/identification.h>
+
+
+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/programs/charon/charon/config/credentials/local_credential_store.c b/programs/charon/charon/config/credentials/local_credential_store.c
new file mode 100644
index 000000000..dc6cb6c50
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/stat.h>
+#include <dirent.h>
+
+#include "local_credential_store.h"
+
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+#include <crypto/x509.h>
+
+
+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**)&current);
+ 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**)&current);
+ 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/programs/charon/charon/config/credentials/local_credential_store.h b/programs/charon/charon/config/credentials/local_credential_store.h
new file mode 100644
index 000000000..ab9ef88d7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <config/credentials/credential_store.h>
+
+
+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/programs/charon/charon/config/policies/Makefile.policies b/programs/charon/charon/config/policies/Makefile.policies
new file mode 100644
index 000000000..e7ed8ab13
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/config/policies/local_policy_store.c b/programs/charon/charon/config/policies/local_policy_store.c
new file mode 100644
index 000000000..ae02357ea
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <utils/linked_list.h>
+#include <utils/logger_manager.h>
+
+
+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 **)&current);
+ 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/programs/charon/charon/config/policies/local_policy_store.h b/programs/charon/charon/config/policies/local_policy_store.h
new file mode 100644
index 000000000..7ab9e0efd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <config/policies/policy_store.h>
+
+
+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/programs/charon/charon/config/policies/policy.c b/programs/charon/charon/config/policies/policy.c
new file mode 100644
index 000000000..cff87fc6b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <utils/linked_list.h>
+#include <utils/identification.h>
+#include <utils/logger.h>
+
+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/programs/charon/charon/config/policies/policy.h b/programs/charon/charon/config/policies/policy.h
new file mode 100644
index 000000000..78cda1e8b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/identification.h>
+#include <config/traffic_selector.h>
+#include <config/proposal.h>
+#include <encoding/payloads/auth_payload.h>
+
+
+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/programs/charon/charon/config/policies/policy_store.h b/programs/charon/charon/config/policies/policy_store.h
new file mode 100755
index 000000000..651dea634
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <config/policies/policy.h>
+
+
+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/programs/charon/charon/config/proposal.c b/programs/charon/charon/config/proposal.c
new file mode 100644
index 000000000..cb71a756a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "proposal.h"
+
+#include <utils/linked_list.h>
+#include <utils/identification.h>
+#include <utils/logger.h>
+
+
+/**
+ * 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**)&current_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/programs/charon/charon/config/proposal.h b/programs/charon/charon/config/proposal.h
new file mode 100644
index 000000000..48e3ad8d5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/identification.h>
+#include <utils/linked_list.h>
+#include <utils/host.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <crypto/diffie_hellman.h>
+#include <config/traffic_selector.h>
+
+
+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/programs/charon/charon/config/traffic_selector.c b/programs/charon/charon/config/traffic_selector.c
new file mode 100644
index 000000000..81272659a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <utils/linked_list.h>
+#include <utils/identification.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+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<<bit & from) != (1<<bit & to))
+ {
+ return bit;
+ }
+ }
+ return 32;
+ }
+ case TS_IPV6_ADDR_RANGE:
+ default:
+ {
+ return 0;
+ }
+ }
+}
+
+/**
+ * Implements traffic_selector_t.update_address_range.
+ */
+static void update_address_range(private_traffic_selector_t *this, host_t *host)
+{
+ if (host->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/programs/charon/charon/config/traffic_selector.h b/programs/charon/charon/config/traffic_selector.h
new file mode 100644
index 000000000..5ac5bdeb1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/host.h>
+
+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/programs/charon/charon/daemon.c b/programs/charon/charon/daemon.c
new file mode 100644
index 000000000..4b0ea54e8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <execinfo.h>
+#include <string.h>
+
+#include "daemon.h"
+
+#include <types.h>
+#include <config/connections/local_connection_store.h>
+#include <config/credentials/local_credential_store.h>
+#include <config/policies/local_policy_store.h>
+
+
+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/programs/charon/charon/daemon.h b/programs/charon/charon/daemon.h
new file mode 100644
index 000000000..5aee21fdb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <threads/sender.h>
+#include <threads/receiver.h>
+#include <threads/scheduler.h>
+#include <threads/kernel_interface.h>
+#include <threads/thread_pool.h>
+#include <threads/stroke_interface.h>
+#include <network/socket.h>
+#include <sa/ike_sa_manager.h>
+#include <queues/send_queue.h>
+#include <queues/job_queue.h>
+#include <queues/event_queue.h>
+#include <utils/logger_manager.h>
+#include <config/configuration.h>
+#include <config/connections/connection_store.h>
+#include <config/policies/policy_store.h>
+#include <config/credentials/credential_store.h>
+
+/**
+ * @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/programs/charon/charon/encoding/Makefile.encoding b/programs/charon/charon/encoding/Makefile.encoding
new file mode 100644
index 000000000..ccdb42f79
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/encoding/generator.c b/programs/charon/charon/encoding/generator.c
new file mode 100644
index 000000000..ba12190dd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+
+
+#include "generator.h"
+
+#include <types.h>
+#include <daemon.h>
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/vendor_id_payload.h>
+#include <encoding/payloads/cp_payload.h>
+#include <encoding/payloads/configuration_attribute.h>
+#include <encoding/payloads/eap_payload.h>
+
+
+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 **)&current_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 **)&current_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 **)&current_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 **)&current_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 **)&current_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/programs/charon/charon/encoding/generator.h b/programs/charon/charon/encoding/generator.h
new file mode 100644
index 000000000..717d32b73
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/message.c b/programs/charon/charon/encoding/message.c
new file mode 100644
index 000000000..a57315272
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#include "message.h"
+
+#include <types.h>
+#include <daemon.h>
+#include <sa/ike_sa_id.h>
+#include <encoding/generator.h>
+#include <encoding/parser.h>
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/encryption_payload.h>
+#include <encoding/payloads/unknown_payload.h>
+
+/**
+ * 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 **) &current_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 **)&current_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 **)&current_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, &current_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, &current_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,&current_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 **)&current_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/programs/charon/charon/encoding/message.h b/programs/charon/charon/encoding/message.h
new file mode 100644
index 000000000..e3a72f439
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa_id.h>
+#include <network/packet.h>
+#include <encoding/payloads/ike_header.h>
+#include <encoding/payloads/notify_payload.h>
+#include <utils/linked_list.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+
+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/programs/charon/charon/encoding/parser.c b/programs/charon/charon/encoding/parser.c
new file mode 100644
index 000000000..a589e9bde
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "parser.h"
+
+#include <types.h>
+#include <definitions.h>
+#include <daemon.h>
+#include <utils/logger.h>
+#include <utils/linked_list.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <encoding/payloads/transform_attribute.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/encryption_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/vendor_id_payload.h>
+#include <encoding/payloads/cp_payload.h>
+#include <encoding/payloads/configuration_attribute.h>
+#include <encoding/payloads/eap_payload.h>
+#include <encoding/payloads/unknown_payload.h>
+
+
+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/programs/charon/charon/encoding/parser.h b/programs/charon/charon/encoding/parser.h
new file mode 100644
index 000000000..216fac9b7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/payload.h>
+
+
+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/programs/charon/charon/encoding/payloads/Makefile.payloads b/programs/charon/charon/encoding/payloads/Makefile.payloads
new file mode 100644
index 000000000..61d920907
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/encoding/payloads/auth_payload.c b/programs/charon/charon/encoding/payloads/auth_payload.c
new file mode 100644
index 000000000..cc7c4bfb1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <encoding/payloads/encodings.h>
+
+
+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/programs/charon/charon/encoding/payloads/auth_payload.h b/programs/charon/charon/encoding/payloads/auth_payload.h
new file mode 100644
index 000000000..e099cdfef
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <config/connections/connection.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/cert_payload.c b/programs/charon/charon/encoding/payloads/cert_payload.c
new file mode 100644
index 000000000..146d42eda
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/cert_payload.h b/programs/charon/charon/encoding/payloads/cert_payload.h
new file mode 100644
index 000000000..9148cfd31
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/certreq_payload.c b/programs/charon/charon/encoding/payloads/certreq_payload.c
new file mode 100644
index 000000000..cdab82be4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/certreq_payload.h b/programs/charon/charon/encoding/payloads/certreq_payload.h
new file mode 100644
index 000000000..3e88e7ffe
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/cert_payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/configuration_attribute.c b/programs/charon/charon/encoding/payloads/configuration_attribute.c
new file mode 100644
index 000000000..489d7f372
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "configuration_attribute.h"
+
+#include <encoding/payloads/encodings.h>
+#include <types.h>
+
+
+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/programs/charon/charon/encoding/payloads/configuration_attribute.h b/programs/charon/charon/encoding/payloads/configuration_attribute.h
new file mode 100644
index 000000000..5b6b4f473
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/cp_payload.c b/programs/charon/charon/encoding/payloads/cp_payload.c
new file mode 100644
index 000000000..583488382
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "cp_payload.h"
+
+#include <encoding/payloads/encodings.h>
+#include <utils/linked_list.h>
+
+
+/**
+ * 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 **) &current_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 **)&current_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/programs/charon/charon/encoding/payloads/cp_payload.h b/programs/charon/charon/encoding/payloads/cp_payload.h
new file mode 100644
index 000000000..eb8076446
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/configuration_attribute.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/delete_payload.c b/programs/charon/charon/encoding/payloads/delete_payload.c
new file mode 100644
index 000000000..28e78800f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/delete_payload.h b/programs/charon/charon/encoding/payloads/delete_payload.h
new file mode 100644
index 000000000..71a6317d4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/proposal_substructure.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/eap_payload.c b/programs/charon/charon/encoding/payloads/eap_payload.c
new file mode 100644
index 000000000..2a0e17679
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/eap_payload.h b/programs/charon/charon/encoding/payloads/eap_payload.h
new file mode 100644
index 000000000..5e5a0c6d8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/encodings.c b/programs/charon/charon/encoding/payloads/encodings.c
new file mode 100644
index 000000000..da39467a9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/encoding/payloads/encodings.h b/programs/charon/charon/encoding/payloads/encodings.h
new file mode 100644
index 000000000..e30e1c215
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+
+
+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/programs/charon/charon/encoding/payloads/encryption_payload.c b/programs/charon/charon/encoding/payloads/encryption_payload.c
new file mode 100644
index 000000000..e0ca74ff4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <string.h>
+
+#include "encryption_payload.h"
+
+#include <daemon.h>
+#include <encoding/payloads/encodings.h>
+#include <utils/linked_list.h>
+#include <utils/logger.h>
+#include <encoding/generator.h>
+#include <encoding/parser.h>
+#include <utils/iterator.h>
+#include <utils/randomizer.h>
+#include <crypto/signers/signer.h>
+
+
+
+
+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**)&current_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**)&current_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 **) &current_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 **)&current_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/programs/charon/charon/encoding/payloads/encryption_payload.h b/programs/charon/charon/encoding/payloads/encryption_payload.h
new file mode 100644
index 000000000..77be246c5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <encoding/payloads/payload.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/id_payload.c b/programs/charon/charon/encoding/payloads/id_payload.c
new file mode 100644
index 000000000..6a8d7738d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "id_payload.h"
+
+#include <encoding/payloads/encodings.h>
+
+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/programs/charon/charon/encoding/payloads/id_payload.h b/programs/charon/charon/encoding/payloads/id_payload.h
new file mode 100644
index 000000000..c35b44d59
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/identification.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/ike_header.c b/programs/charon/charon/encoding/payloads/ike_header.c
new file mode 100644
index 000000000..ad46d3d29
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "ike_header.h"
+
+#include <encoding/payloads/encodings.h>
+
+
+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/programs/charon/charon/encoding/payloads/ike_header.h b/programs/charon/charon/encoding/payloads/ike_header.h
new file mode 100644
index 000000000..ec55f0e18
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/ke_payload.c b/programs/charon/charon/encoding/payloads/ke_payload.c
new file mode 100644
index 000000000..0c92e033d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "ke_payload.h"
+
+#include <encoding/payloads/encodings.h>
+
+
+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/programs/charon/charon/encoding/payloads/ke_payload.h b/programs/charon/charon/encoding/payloads/ke_payload.h
new file mode 100644
index 000000000..982d29754
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <utils/linked_list.h>
+/**
+ * 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/programs/charon/charon/encoding/payloads/nonce_payload.c b/programs/charon/charon/encoding/payloads/nonce_payload.c
new file mode 100644
index 000000000..a7528fbfb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "nonce_payload.h"
+
+#include <encoding/payloads/encodings.h>
+
+
+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/programs/charon/charon/encoding/payloads/nonce_payload.h b/programs/charon/charon/encoding/payloads/nonce_payload.h
new file mode 100644
index 000000000..366dfec15
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/notify_payload.c b/programs/charon/charon/encoding/payloads/notify_payload.c
new file mode 100644
index 000000000..43d0c5322
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "notify_payload.h"
+
+#include <daemon.h>
+#include <encoding/payloads/encodings.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/notify_payload.h b/programs/charon/charon/encoding/payloads/notify_payload.h
new file mode 100644
index 000000000..093f99144
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/payload.c b/programs/charon/charon/encoding/payloads/payload.c
new file mode 100644
index 000000000..b89e80a53
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <encoding/payloads/ike_header.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/encryption_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/vendor_id_payload.h>
+#include <encoding/payloads/cp_payload.h>
+#include <encoding/payloads/configuration_attribute.h>
+#include <encoding/payloads/eap_payload.h>
+#include <encoding/payloads/unknown_payload.h>
+
+/*
+ * 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/programs/charon/charon/encoding/payloads/payload.h b/programs/charon/charon/encoding/payloads/payload.h
new file mode 100644
index 000000000..fc3457832
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+#include <encoding/payloads/encodings.h>
+
+
+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/programs/charon/charon/encoding/payloads/proposal_substructure.c b/programs/charon/charon/encoding/payloads/proposal_substructure.c
new file mode 100644
index 000000000..cb3c695b2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "proposal_substructure.h"
+
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <types.h>
+#include <utils/linked_list.h>
+
+
+/**
+ * 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) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! !
+ ~ <Transforms> ~
+ ! !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/**
+ * 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 **)&current_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 **) &current_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 **) &current_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 **) &current_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 **)&current_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/programs/charon/charon/encoding/payloads/proposal_substructure.h b/programs/charon/charon/encoding/payloads/proposal_substructure.h
new file mode 100644
index 000000000..506d25800
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <config/proposal.h>
+#include <utils/linked_list.h>
+
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/sa_payload.c b/programs/charon/charon/encoding/payloads/sa_payload.c
new file mode 100644
index 000000000..81b4e6709
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "sa_payload.h"
+
+#include <encoding/payloads/encodings.h>
+#include <utils/linked_list.h>
+
+
+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 !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! !
+ ~ <Proposals> ~
+ ! !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/**
+ * 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 **)&current_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 **)&current_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 **) &current_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/programs/charon/charon/encoding/payloads/sa_payload.h b/programs/charon/charon/encoding/payloads/sa_payload.h
new file mode 100644
index 000000000..45095c030
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/traffic_selector_substructure.c b/programs/charon/charon/encoding/payloads/traffic_selector_substructure.c
new file mode 100644
index 000000000..c1a461e8a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <encoding/payloads/encodings.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/traffic_selector_substructure.h b/programs/charon/charon/encoding/payloads/traffic_selector_substructure.h
new file mode 100644
index 000000000..755917055
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+#include <utils/host.h>
+#include <config/traffic_selector.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/transform_attribute.c b/programs/charon/charon/encoding/payloads/transform_attribute.c
new file mode 100644
index 000000000..71cdd59e2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <stddef.h>
+
+#include "transform_attribute.h"
+
+#include <encoding/payloads/encodings.h>
+#include <types.h>
+
+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/programs/charon/charon/encoding/payloads/transform_attribute.h b/programs/charon/charon/encoding/payloads/transform_attribute.h
new file mode 100644
index 000000000..547699915
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+
+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/programs/charon/charon/encoding/payloads/transform_substructure.c b/programs/charon/charon/encoding/payloads/transform_substructure.c
new file mode 100644
index 000000000..350ad63e4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "transform_substructure.h"
+
+#include <encoding/payloads/transform_attribute.h>
+#include <encoding/payloads/encodings.h>
+#include <types.h>
+#include <utils/linked_list.h>
+
+
+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 **)&current_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 **) &current_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 **) &current_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 **) &current_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 **)&current_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/programs/charon/charon/encoding/payloads/transform_substructure.h b/programs/charon/charon/encoding/payloads/transform_substructure.h
new file mode 100644
index 000000000..f6af3ee59
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/transform_attribute.h>
+#include <utils/linked_list.h>
+#include <crypto/diffie_hellman.h>
+#include <crypto/signers/signer.h>
+#include <crypto/prfs/prf.h>
+#include <crypto/crypters/crypter.h>
+#include <config/proposal.h>
+
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/ts_payload.c b/programs/charon/charon/encoding/payloads/ts_payload.c
new file mode 100644
index 000000000..58772e666
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#include "ts_payload.h"
+
+#include <encoding/payloads/encodings.h>
+#include <utils/linked_list.h>
+
+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 !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! !
+ ~ <Traffic Selectors> ~
+ ! !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/**
+ * 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 **)&current_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 **) &current_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 **) &current_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/programs/charon/charon/encoding/payloads/ts_payload.h b/programs/charon/charon/encoding/payloads/ts_payload.h
new file mode 100644
index 000000000..775ff6134
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/linked_list.h>
+#include <config/traffic_selector.h>
+#include <encoding/payloads/payload.h>
+#include <encoding/payloads/traffic_selector_substructure.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/unknown_payload.c b/programs/charon/charon/encoding/payloads/unknown_payload.c
new file mode 100644
index 000000000..25bb37d59
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/unknown_payload.h b/programs/charon/charon/encoding/payloads/unknown_payload.h
new file mode 100644
index 000000000..9c4926ea7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/encoding/payloads/vendor_id_payload.c b/programs/charon/charon/encoding/payloads/vendor_id_payload.c
new file mode 100644
index 000000000..436b82d79
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+
+#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/programs/charon/charon/encoding/payloads/vendor_id_payload.h b/programs/charon/charon/encoding/payloads/vendor_id_payload.h
new file mode 100644
index 000000000..c9ead4337
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/payloads/payload.h>
+
+/**
+ * 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/programs/charon/charon/network/Makefile.network b/programs/charon/charon/network/Makefile.network
new file mode 100644
index 000000000..fd99bd085
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/network/packet.c b/programs/charon/charon/network/packet.c
new file mode 100644
index 000000000..6cded72a3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/network/packet.h b/programs/charon/charon/network/packet.h
new file mode 100644
index 000000000..a2620d391
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/host.h>
+
+
+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/programs/charon/charon/network/socket.c b/programs/charon/charon/network/socket.c
new file mode 100644
index 000000000..32ff84538
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <linux/filter.h>
+
+#include "socket.h"
+
+#include <daemon.h>
+#include <utils/logger_manager.h>
+
+
+#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/programs/charon/charon/network/socket.h b/programs/charon/charon/network/socket.h
new file mode 100644
index 000000000..498e7700a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <network/packet.h>
+
+
+/**
+ * @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/programs/charon/charon/queues/Makefile.queues b/programs/charon/charon/queues/Makefile.queues
new file mode 100644
index 000000000..eeb012d2b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/queues/event_queue.c b/programs/charon/charon/queues/event_queue.c
new file mode 100644
index 000000000..ece9d1513
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <pthread.h>
+#include <stdlib.h>
+
+#include "event_queue.h"
+
+#include <types.h>
+#include <utils/linked_list.h>
+
+
+
+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(&current_time,NULL);
+ long difference = time_difference(&current_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 **) &current_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 **) &current_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 **) &current_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(&current_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/programs/charon/charon/queues/event_queue.h b/programs/charon/charon/queues/event_queue.h
new file mode 100644
index 000000000..a60424100
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sys/time.h>
+
+#include <types.h>
+#include <queues/jobs/job.h>
+
+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/programs/charon/charon/queues/job_queue.c b/programs/charon/charon/queues/job_queue.c
new file mode 100644
index 000000000..3640395ab
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+
+#include "job_queue.h"
+
+#include <utils/linked_list.h>
+
+
+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/programs/charon/charon/queues/job_queue.h b/programs/charon/charon/queues/job_queue.h
new file mode 100644
index 000000000..9fcf08001
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <queues/jobs/job.h>
+
+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/programs/charon/charon/queues/jobs/Makefile.jobs b/programs/charon/charon/queues/jobs/Makefile.jobs
new file mode 100644
index 000000000..db89987bc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/queues/jobs/delete_established_ike_sa_job.c b/programs/charon/charon/queues/jobs/delete_established_ike_sa_job.c
new file mode 100644
index 000000000..7251e2ca4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/queues/jobs/delete_established_ike_sa_job.h b/programs/charon/charon/queues/jobs/delete_established_ike_sa_job.h
new file mode 100644
index 000000000..762dceae6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa_id.h>
+#include <queues/jobs/job.h>
+
+
+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/programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c b/programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.c
new file mode 100644
index 000000000..610285e20
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h b/programs/charon/charon/queues/jobs/delete_half_open_ike_sa_job.h
new file mode 100644
index 000000000..ea42be8f2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa_id.h>
+#include <queues/jobs/job.h>
+
+
+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/programs/charon/charon/queues/jobs/incoming_packet_job.c b/programs/charon/charon/queues/jobs/incoming_packet_job.c
new file mode 100644
index 000000000..fc71f63ea
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/queues/jobs/incoming_packet_job.h b/programs/charon/charon/queues/jobs/incoming_packet_job.h
new file mode 100644
index 000000000..e3fb5797e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <network/packet.h>
+#include <queues/jobs/job.h>
+
+
+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/programs/charon/charon/queues/jobs/initiate_ike_sa_job.c b/programs/charon/charon/queues/jobs/initiate_ike_sa_job.c
new file mode 100644
index 000000000..ac9ace36c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#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/programs/charon/charon/queues/jobs/initiate_ike_sa_job.h b/programs/charon/charon/queues/jobs/initiate_ike_sa_job.h
new file mode 100644
index 000000000..cee31f07b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <queues/jobs/job.h>
+#include <config/connections/connection.h>
+
+
+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/programs/charon/charon/queues/jobs/job.c b/programs/charon/charon/queues/jobs/job.c
new file mode 100644
index 000000000..df739f9e5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/queues/jobs/job.h b/programs/charon/charon/queues/jobs/job.h
new file mode 100644
index 000000000..eea4da09e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+
+
+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/programs/charon/charon/queues/jobs/retransmit_request_job.c b/programs/charon/charon/queues/jobs/retransmit_request_job.c
new file mode 100644
index 000000000..e171df5bd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/queues/jobs/retransmit_request_job.h b/programs/charon/charon/queues/jobs/retransmit_request_job.h
new file mode 100644
index 000000000..2349d3f5e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <queues/jobs/job.h>
+#include <sa/ike_sa_id.h>
+
+
+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/programs/charon/charon/queues/send_queue.c b/programs/charon/charon/queues/send_queue.c
new file mode 100644
index 000000000..0852e5303
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <pthread.h>
+
+#include "send_queue.h"
+
+#include <utils/linked_list.h>
+
+
+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/programs/charon/charon/queues/send_queue.h b/programs/charon/charon/queues/send_queue.h
new file mode 100644
index 000000000..6dc5867eb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <network/packet.h>
+
+
+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/programs/charon/charon/sa/Makefile.sa b/programs/charon/charon/sa/Makefile.sa
new file mode 100644
index 000000000..825c19959
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/sa/authenticator.c b/programs/charon/charon/sa/authenticator.c
new file mode 100644
index 000000000..3aeb8795f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "authenticator.h"
+
+#include <daemon.h>
+
+/**
+ * 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"), <msg octets>) */
+ 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/programs/charon/charon/sa/authenticator.h b/programs/charon/charon/sa/authenticator.h
new file mode 100644
index 000000000..b6bc317ac
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa.h>
+#include <network/packet.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/id_payload.h>
+
+
+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/programs/charon/charon/sa/child_sa.c b/programs/charon/charon/sa/child_sa.c
new file mode 100644
index 000000000..a678ea9b8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <netdb.h>
+
+#include "child_sa.h"
+
+#include <daemon.h>
+
+
+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/programs/charon/charon/sa/child_sa.h b/programs/charon/charon/sa/child_sa.h
new file mode 100644
index 000000000..6ccbff13f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <crypto/prf_plus.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <utils/logger.h>
+
+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/programs/charon/charon/sa/ike_sa.c b/programs/charon/charon/sa/ike_sa.c
new file mode 100644
index 000000000..6322eb8e9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "ike_sa.h"
+
+#include <types.h>
+#include <daemon.h>
+#include <definitions.h>
+#include <utils/linked_list.h>
+#include <utils/logger_manager.h>
+#include <utils/randomizer.h>
+#include <crypto/diffie_hellman.h>
+#include <crypto/prf_plus.h>
+#include <crypto/crypters/crypter.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <encoding/payloads/transform_attribute.h>
+#include <sa/states/initiator_init.h>
+#include <sa/states/responder_init.h>
+#include <queues/jobs/retransmit_request_job.h>
+#include <queues/jobs/delete_established_ike_sa_job.h>
+
+
+
+
+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/programs/charon/charon/sa/ike_sa.h b/programs/charon/charon/sa/ike_sa.h
new file mode 100644
index 000000000..c526c6347
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <encoding/message.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <sa/ike_sa_id.h>
+#include <sa/child_sa.h>
+#include <sa/states/state.h>
+#include <config/configuration.h>
+#include <utils/logger.h>
+#include <utils/randomizer.h>
+#include <crypto/prfs/prf.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <config/connections/connection.h>
+#include <config/policies/policy.h>
+#include <utils/logger.h>
+
+/**
+ * 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/programs/charon/charon/sa/ike_sa_id.c b/programs/charon/charon/sa/ike_sa_id.c
new file mode 100644
index 000000000..bf3a05d11
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/sa/ike_sa_id.h b/programs/charon/charon/sa/ike_sa_id.h
new file mode 100644
index 000000000..0f16f7637
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+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/programs/charon/charon/sa/ike_sa_manager.c b/programs/charon/charon/sa/ike_sa_manager.c
new file mode 100644
index 000000000..01f3f5ad2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <pthread.h>
+#include <string.h>
+
+#include "ike_sa_manager.h"
+
+#include <daemon.h>
+#include <sa/ike_sa_id.h>
+#include <utils/logger.h>
+#include <utils/logger_manager.h>
+#include <utils/linked_list.h>
+
+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**)&current);
+ 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**)&current);
+ /* 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**)&current);
+ 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**)&current);
+ 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/programs/charon/charon/sa/ike_sa_manager.h b/programs/charon/charon/sa/ike_sa_manager.h
new file mode 100644
index 000000000..e2235b4b6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa.h>
+#include <utils/logger.h>
+
+
+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/programs/charon/charon/sa/states/Makefile.states b/programs/charon/charon/sa/states/Makefile.states
new file mode 100644
index 000000000..a258ebef0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/sa/states/ike_auth_requested.c b/programs/charon/charon/sa/states/ike_auth_requested.c
new file mode 100644
index 000000000..3d49f440f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "ike_auth_requested.h"
+
+#include <daemon.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/signers/signer.h>
+#include <crypto/crypters/crypter.h>
+#include <sa/states/ike_sa_established.h>
+#include <sa/authenticator.h>
+#include <sa/child_sa.h>
+
+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/programs/charon/charon/sa/states/ike_auth_requested.h b/programs/charon/charon/sa/states/ike_auth_requested.h
new file mode 100644
index 000000000..a8eef014c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sa/states/state.h>
+#include <sa/ike_sa.h>
+
+
+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/programs/charon/charon/sa/states/ike_sa_established.c b/programs/charon/charon/sa/states/ike_sa_established.c
new file mode 100644
index 000000000..e91409f6a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+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/programs/charon/charon/sa/states/ike_sa_established.h b/programs/charon/charon/sa/states/ike_sa_established.h
new file mode 100644
index 000000000..8477ad5bc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sa/states/state.h>
+#include <sa/ike_sa.h>
+
+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/programs/charon/charon/sa/states/ike_sa_init_requested.c b/programs/charon/charon/sa/states/ike_sa_init_requested.c
new file mode 100644
index 000000000..311cdf0a0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <crypto/diffie_hellman.h>
+#include <sa/states/ike_auth_requested.h>
+#include <sa/states/initiator_init.h>
+#include <sa/authenticator.h>
+
+
+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/programs/charon/charon/sa/states/ike_sa_init_requested.h b/programs/charon/charon/sa/states/ike_sa_init_requested.h
new file mode 100644
index 000000000..0a43afad1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <sa/ike_sa.h>
+#include <sa/states/state.h>
+#include <crypto/diffie_hellman.h>
+
+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/programs/charon/charon/sa/states/ike_sa_init_responded.c b/programs/charon/charon/sa/states/ike_sa_init_responded.c
new file mode 100644
index 000000000..e40b0cf22
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "ike_sa_init_responded.h"
+
+#include <daemon.h>
+#include <sa/authenticator.h>
+#include <sa/child_sa.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/signers/signer.h>
+#include <crypto/crypters/crypter.h>
+#include <sa/states/ike_sa_established.h>
+
+
+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/programs/charon/charon/sa/states/ike_sa_init_responded.h b/programs/charon/charon/sa/states/ike_sa_init_responded.h
new file mode 100644
index 000000000..43aecf26f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+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/programs/charon/charon/sa/states/initiator_init.c b/programs/charon/charon/sa/states/initiator_init.c
new file mode 100644
index 000000000..35d15235d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <sa/states/state.h>
+#include <sa/states/ike_sa_init_requested.h>
+#include <queues/jobs/retransmit_request_job.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+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/programs/charon/charon/sa/states/initiator_init.h b/programs/charon/charon/sa/states/initiator_init.h
new file mode 100644
index 000000000..6b4940a73
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+
+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/programs/charon/charon/sa/states/responder_init.c b/programs/charon/charon/sa/states/responder_init.c
new file mode 100644
index 000000000..10acf645c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <sa/states/state.h>
+#include <sa/states/ike_sa_init_responded.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <crypto/diffie_hellman.h>
+
+
+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/programs/charon/charon/sa/states/responder_init.h b/programs/charon/charon/sa/states/responder_init.h
new file mode 100644
index 000000000..c8ba73ea3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <sa/ike_sa.h>
+#include <sa/states/state.h>
+
+
+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/programs/charon/charon/sa/states/state.c b/programs/charon/charon/sa/states/state.c
new file mode 100644
index 000000000..595f5abbb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/charon/sa/states/state.h b/programs/charon/charon/sa/states/state.h
new file mode 100644
index 000000000..c93068d35
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <definitions.h>
+#include <types.h>
+#include <encoding/message.h>
+
+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/programs/charon/charon/threads/Makefile.threads b/programs/charon/charon/threads/Makefile.threads
new file mode 100644
index 000000000..949c1ad24
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/charon/threads/kernel_interface.c b/programs/charon/charon/threads/kernel_interface.c
new file mode 100644
index 000000000..679cf69ee
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include "kernel_interface.h"
+
+#include <daemon.h>
+#include <utils/linked_list.h>
+
+
+#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/programs/charon/charon/threads/kernel_interface.h b/programs/charon/charon/threads/kernel_interface.h
new file mode 100644
index 000000000..ceafa6468
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <linux/xfrm.h>
+
+#include <utils/host.h>
+#include <encoding/payloads/proposal_substructure.h>
+
+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/programs/charon/charon/threads/receiver.c b/programs/charon/charon/threads/receiver.c
new file mode 100644
index 000000000..0cf8b7bde
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+
+#include "receiver.h"
+
+#include <daemon.h>
+#include <network/socket.h>
+#include <network/packet.h>
+#include <queues/job_queue.h>
+#include <queues/jobs/job.h>
+#include <queues/jobs/incoming_packet_job.h>
+#include <utils/logger_manager.h>
+
+
+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,&current_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/programs/charon/charon/threads/receiver.h b/programs/charon/charon/threads/receiver.h
new file mode 100644
index 000000000..932774f5f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+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/programs/charon/charon/threads/scheduler.c b/programs/charon/charon/threads/scheduler.c
new file mode 100644
index 000000000..47c5d6fb9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+
+#include "scheduler.h"
+
+#include <daemon.h>
+#include <definitions.h>
+#include <utils/logger_manager.h>
+#include <queues/job_queue.h>
+
+
+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/programs/charon/charon/threads/scheduler.h b/programs/charon/charon/threads/scheduler.h
new file mode 100644
index 000000000..0165a718b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+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/programs/charon/charon/threads/sender.c b/programs/charon/charon/threads/sender.c
new file mode 100644
index 000000000..42d11beb9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+
+#include "sender.h"
+
+#include <daemon.h>
+#include <network/socket.h>
+#include <network/packet.h>
+#include <queues/send_queue.h>
+#include <utils/logger_manager.h>
+
+
+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/programs/charon/charon/threads/sender.h b/programs/charon/charon/threads/sender.h
new file mode 100644
index 000000000..ea8124147
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+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/programs/charon/charon/threads/stroke_interface.c b/programs/charon/charon/threads/stroke_interface.c
new file mode 100755
index 000000000..ef5d5f1f6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "stroke_interface.h"
+
+#include <stroke.h>
+#include <types.h>
+#include <daemon.h>
+#include <crypto/x509.h>
+#include <queues/jobs/initiate_ike_sa_job.h>
+
+
+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/programs/charon/charon/threads/stroke_interface.h b/programs/charon/charon/threads/stroke_interface.h
new file mode 100644
index 000000000..f8efc9c67
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <config/policies/policy_store.h>
+#include <config/connections/connection_store.h>
+#include <config/credentials/credential_store.h>
+
+
+typedef struct stroke_t stroke_t;
+
+/**
+ * @brief Stroke is a configuration and control interface which
+ * allows other processes to modify charons behavior.
+ *
+ * stroke_t allows config manipulation (as whack in pluto).
+ * Messages of type stroke_msg_t's are sent over a unix socket
+ * (/var/run/charon.ctl). 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/programs/charon/charon/threads/thread_pool.c b/programs/charon/charon/threads/thread_pool.c
new file mode 100644
index 000000000..4482e795f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+
+#include "thread_pool.h"
+
+#include <daemon.h>
+#include <queues/job_queue.h>
+#include <queues/jobs/delete_half_open_ike_sa_job.h>
+#include <queues/jobs/delete_established_ike_sa_job.h>
+#include <queues/jobs/incoming_packet_job.h>
+#include <queues/jobs/initiate_ike_sa_job.h>
+#include <queues/jobs/retransmit_request_job.h>
+#include <encoding/payloads/notify_payload.h>
+#include <utils/logger.h>
+
+
+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/programs/charon/charon/threads/thread_pool.h b/programs/charon/charon/threads/thread_pool.h
new file mode 100644
index 000000000..b33be08e3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdlib.h>
+
+#include <types.h>
+
+
+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/programs/charon/doc/Architecture.txt b/programs/charon/doc/Architecture.txt
new file mode 100644
index 000000000..14b99274c
--- /dev/null
+++ b/programs/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/programs/charon/doc/Known-bugs.txt b/programs/charon/doc/Known-bugs.txt
new file mode 100644
index 000000000..3f594ad79
--- /dev/null
+++ b/programs/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/programs/charon/doc/Todo-list.txt b/programs/charon/doc/Todo-list.txt
new file mode 100644
index 000000000..11b30fb7d
--- /dev/null
+++ b/programs/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/programs/charon/lib/Makefile.lib b/programs/charon/lib/Makefile.lib
new file mode 100644
index 000000000..80a44ff69
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/asn1/Makefile.asn1 b/programs/charon/lib/asn1/Makefile.asn1
new file mode 100644
index 000000000..3a5450d50
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/asn1/asn1.c b/programs/charon/lib/asn1/asn1.c
new file mode 100644
index 000000000..c847461b6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "asn1.h"
+
+#include <utils/logger_manager.h>
+
+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/programs/charon/lib/asn1/asn1.h b/programs/charon/lib/asn1/asn1.h
new file mode 100644
index 000000000..556bb2b05
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdarg.h>
+#include <gmp.h>
+
+#include <types.h>
+#include <asn1/oid.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 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/programs/charon/lib/asn1/oid.c b/programs/charon/lib/asn1/oid.c
new file mode 100644
index 000000000..4b0632de2
--- /dev/null
+++ b/programs/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 <stdlib.h>
+
+#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/programs/charon/lib/asn1/oid.h b/programs/charon/lib/asn1/oid.h
new file mode 100644
index 000000000..a9265d43f
--- /dev/null
+++ b/programs/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/programs/charon/lib/asn1/oid.pl b/programs/charon/lib/asn1/oid.pl
new file mode 100644
index 000000000..a3725e57d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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, "<oid.txt")
+ or die "could not open 'oid.txt': $!";
+
+$counter = 0;
+$max_name = 0;
+$max_order = 0;
+
+while ($line = <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 <stdlib.h>\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/programs/charon/lib/asn1/oid.txt b/programs/charon/lib/asn1/oid.txt
new file mode 100644
index 000000000..eed46d59d
--- /dev/null
+++ b/programs/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/programs/charon/lib/asn1/pem.c b/programs/charon/lib/asn1/pem.c
new file mode 100755
index 000000000..b02268dd9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include "pem.h"
+#include "ttodata.h"
+
+#include <crypto/hashers/hasher.h>
+#include <crypto/crypters/crypter.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 = 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/programs/charon/lib/asn1/pem.h b/programs/charon/lib/asn1/pem.h
new file mode 100755
index 000000000..a4332fd34
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdio.h>
+
+#include <types.h>
+
+status_t pemtobin(chunk_t *blob, char *pass);
+
+#endif /*PEM_H_*/
diff --git a/programs/charon/lib/asn1/ttodata.c b/programs/charon/lib/asn1/ttodata.c
new file mode 100644
index 000000000..5e8149955
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/lgpl.txt>.
+ *
+ * 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 <string.h>
+#include <ctype.h>
+
+/* converters and misc */
+static int unhex(const char *, char *, size_t);
+static int unb64(const char *, char *, size_t);
+static int untext(const char *, char *, size_t);
+static const char *badch(const char *, int, char *, size_t);
+
+/* internal error codes for converters */
+#define SHORT (-2) /* internal buffer too short */
+#define BADPAD (-3) /* bad base64 padding */
+#define BADCH0 (-4) /* invalid character 0 */
+#define BADCH1 (-5) /* invalid character 1 */
+#define BADCH2 (-6) /* invalid character 2 */
+#define BADCH3 (-7) /* invalid character 3 */
+#define BADOFF(code) (BADCH0-(code))
+
+/*
+ - ttodatav - convert text to data, with verbose error reports
+ * If some of this looks slightly odd, it's because it has changed
+ * repeatedly (from the original atodata()) without a major rewrite.
+ */
+const char * /* NULL on success, else literal or errp */
+ttodatav(src, srclen, base, dst, dstlen, lenp, errp, errlen, flags)
+const char *src;
+size_t srclen; /* 0 means apply strlen() */
+int base; /* 0 means figure it out */
+char *dst; /* need not be valid if dstlen is 0 */
+size_t dstlen;
+size_t *lenp; /* where to record length (NULL is nowhere) */
+char *errp; /* error buffer */
+size_t errlen;
+unsigned int flags;
+{
+ size_t ingroup; /* number of input bytes converted at once */
+ char buf[4]; /* output from conversion */
+ int nbytes; /* size of output */
+ int (*decode)(const char *, char *, size_t);
+ char *stop;
+ int ndone;
+ int i;
+ int underscoreok;
+ int skipSpace = 0;
+
+ if (srclen == 0)
+ srclen = strlen(src);
+ if (dstlen == 0)
+ dst = buf; /* point it somewhere valid */
+ stop = dst + dstlen;
+
+ if (base == 0) {
+ if (srclen < 2)
+ return "input too short to be valid";
+ if (*src++ != '0')
+ return "input does not begin with format prefix";
+ switch (*src++) {
+ case 'x':
+ case 'X':
+ base = 16;
+ break;
+ case 's':
+ case 'S':
+ base = 64;
+ break;
+ case 't':
+ case 'T':
+ base = 256;
+ break;
+ default:
+ return "unknown format prefix";
+ }
+ srclen -= 2;
+ }
+ switch (base) {
+ case 16:
+ decode = unhex;
+ underscoreok = 1;
+ ingroup = 2;
+ break;
+ case 64:
+ decode = unb64;
+ underscoreok = 0;
+ ingroup = 4;
+ if(flags & TTODATAV_IGNORESPACE) {
+ skipSpace = 1;
+ }
+ break;
+
+ case 256:
+ decode = untext;
+ ingroup = 1;
+ underscoreok = 0;
+ break;
+ default:
+ return "unknown base";
+ }
+
+ /* proceed */
+ ndone = 0;
+ while (srclen > 0) {
+ char stage[4]; /* staging area for group */
+ size_t sl = 0;
+
+ /* Grab ingroup characters into stage,
+ * squeezing out blanks if we are supposed to ignore them.
+ */
+ for (sl = 0; sl < ingroup; src++, srclen--) {
+ if (srclen == 0)
+ return "input ends in mid-byte, perhaps truncated";
+ else if (!(skipSpace && (*src == ' ' || *src == '\t')))
+ stage[sl++] = *src;
+ }
+
+ nbytes = (*decode)(stage, buf, sizeof(buf));
+ switch (nbytes) {
+ case BADCH0:
+ case BADCH1:
+ case BADCH2:
+ case BADCH3:
+ return badch(stage, nbytes, errp, errlen);
+ case SHORT:
+ return "internal buffer too short (\"can't happen\")";
+ case BADPAD:
+ return "bad (non-zero) padding at end of base64 input";
+ }
+ if (nbytes <= 0)
+ return "unknown internal error";
+ for (i = 0; i < nbytes; i++) {
+ if (dst < stop)
+ *dst++ = buf[i];
+ ndone++;
+ }
+ while (srclen >= 1 && skipSpace && (*src == ' ' || *src == '\t')){
+ src++;
+ srclen--;
+ }
+ if (underscoreok && srclen > 1 && *src == '_') {
+ /* srclen > 1 means not last character */
+ src++;
+ srclen--;
+ }
+ }
+
+ if (ndone == 0)
+ return "no data bytes specified by input";
+ if (lenp != NULL)
+ *lenp = ndone;
+ return NULL;
+}
+
+/*
+ - ttodata - convert text to data
+ */
+const char * /* NULL on success, else literal */
+ttodata(src, srclen, base, dst, dstlen, lenp)
+const char *src;
+size_t srclen; /* 0 means apply strlen() */
+int base; /* 0 means figure it out */
+char *dst; /* need not be valid if dstlen is 0 */
+size_t dstlen;
+size_t *lenp; /* where to record length (NULL is nowhere) */
+{
+ return ttodatav(src, srclen, base, dst, dstlen, lenp, (char *)NULL,
+ (size_t)0, TTODATAV_SPACECOUNTS);
+}
+
+/*
+ - atodata - convert ASCII to data
+ * backward-compatibility interface
+ */
+size_t /* 0 for failure, true length for success */
+atodata(src, srclen, dst, dstlen)
+const char *src;
+size_t srclen;
+char *dst;
+size_t dstlen;
+{
+ size_t len;
+ const char *err;
+
+ err = ttodata(src, srclen, 0, dst, dstlen, &len);
+ if (err != NULL)
+ return 0;
+ return len;
+}
+
+/*
+ - atobytes - convert ASCII to data bytes
+ * another backward-compatibility interface
+ */
+const char *
+atobytes(src, srclen, dst, dstlen, lenp)
+const char *src;
+size_t srclen;
+char *dst;
+size_t dstlen;
+size_t *lenp;
+{
+ return ttodata(src, srclen, 0, dst, dstlen, lenp);
+}
+
+/*
+ - unhex - convert two ASCII hex digits to byte
+ */
+static int /* number of result bytes, or error code */
+unhex(src, dst, dstlen)
+const char *src; /* known to be full length */
+char *dst;
+size_t dstlen; /* not large enough is a failure */
+{
+ char *p;
+ unsigned byte;
+ static char hex[] = "0123456789abcdef";
+
+ if (dstlen < 1)
+ return SHORT;
+
+ p = strchr(hex, *src);
+ if (p == NULL)
+ p = strchr(hex, tolower(*src));
+ if (p == NULL)
+ return BADCH0;
+ byte = (p - hex) << 4;
+ src++;
+
+ p = strchr(hex, *src);
+ if (p == NULL)
+ p = strchr(hex, tolower(*src));
+ if (p == NULL)
+ return BADCH1;
+ byte |= (p - hex);
+
+ *dst = byte;
+ return 1;
+}
+
+/*
+ - unb64 - convert four ASCII base64 digits to three bytes
+ * Note that a base64 digit group is padded out with '=' if it represents
+ * less than three bytes: one byte is dd==, two is ddd=, three is dddd.
+ */
+static int /* number of result bytes, or error code */
+unb64(src, dst, dstlen)
+const char *src; /* known to be full length */
+char *dst;
+size_t dstlen;
+{
+ char *p;
+ unsigned byte1;
+ unsigned byte2;
+ static char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ if (dstlen < 3)
+ return SHORT;
+
+ p = strchr(base64, *src++);
+
+ if (p == NULL)
+ return BADCH0;
+ byte1 = (p - base64) << 2; /* first six bits */
+
+ p = strchr(base64, *src++);
+ if (p == NULL) {
+ return BADCH1;
+ }
+
+ byte2 = p - base64; /* next six: two plus four */
+ *dst++ = byte1 | (byte2 >> 4);
+ byte1 = (byte2 & 0xf) << 4;
+
+ p = strchr(base64, *src++);
+ if (p == NULL) {
+ if (*(src-1) == '=' && *src == '=') {
+ if (byte1 != 0) /* bad padding */
+ return BADPAD;
+ return 1;
+ }
+ return BADCH2;
+ }
+
+ byte2 = p - base64; /* next six: four plus two */
+ *dst++ = byte1 | (byte2 >> 2);
+ byte1 = (byte2 & 0x3) << 6;
+
+ p = strchr(base64, *src++);
+ if (p == NULL) {
+ if (*(src-1) == '=') {
+ if (byte1 != 0) /* bad padding */
+ return BADPAD;
+ return 2;
+ }
+ return BADCH3;
+ }
+ byte2 = p - base64; /* last six */
+ *dst++ = byte1 | byte2;
+
+ return 3;
+}
+
+/*
+ - untext - convert one ASCII character to byte
+ */
+static int /* number of result bytes, or error code */
+untext(src, dst, dstlen)
+const char *src; /* known to be full length */
+char *dst;
+size_t dstlen; /* not large enough is a failure */
+{
+ if (dstlen < 1)
+ return SHORT;
+
+ *dst = *src;
+ return 1;
+}
+
+/*
+ - badch - produce a nice complaint about an unknown character
+ *
+ * If the compiler complains that the array bigenough[] has a negative
+ * size, that means the TTODATAV_BUF constant has been set too small.
+ */
+static const char * /* literal or errp */
+badch(src, errcode, errp, errlen)
+const char *src;
+int errcode;
+char *errp; /* might be NULL */
+size_t errlen;
+{
+ static const char pre[] = "unknown character (`";
+ static const char suf[] = "') in input";
+ char buf[5];
+# define REQD (sizeof(pre) - 1 + sizeof(buf) - 1 + sizeof(suf))
+ struct sizecheck {
+ char bigenough[TTODATAV_BUF - REQD]; /* see above */
+ };
+ char ch;
+
+ if (errp == NULL || errlen < REQD)
+ return "unknown character in input";
+ strcpy(errp, pre);
+ ch = *(src + BADOFF(errcode));
+ if (isprint(ch)) {
+ buf[0] = ch;
+ buf[1] = '\0';
+ } else {
+ buf[0] = '\\';
+ buf[1] = ((ch & 0700) >> 6) + '0';
+ buf[2] = ((ch & 0070) >> 3) + '0';
+ buf[3] = ((ch & 0007) >> 0) + '0';
+ buf[4] = '\0';
+ }
+ strcat(errp, buf);
+ strcat(errp, suf);
+ return (const char *)errp;
+}
diff --git a/programs/charon/lib/asn1/ttodata.h b/programs/charon/lib/asn1/ttodata.h
new file mode 100644
index 000000000..d57244ef5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/lgpl.txt>.
+ *
+ * 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 <types.h>
+
+#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/programs/charon/lib/crypto/Makefile.transforms b/programs/charon/lib/crypto/Makefile.transforms
new file mode 100644
index 000000000..af0b147da
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/crypters/Makefile.crypters b/programs/charon/lib/crypto/crypters/Makefile.crypters
new file mode 100644
index 000000000..612477de8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/crypters/aes_cbc_crypter.c b/programs/charon/lib/crypto/crypters/aes_cbc_crypter.c
new file mode 100644
index 000000000..9b7b07c62
--- /dev/null
+++ b/programs/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 <brg@gladman.uk.net>
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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(pos<data.len)
+ {
+ if (pos==0)
+ iv_i=(const u_int32_t*) iv.ptr;
+ else
+ iv_i=(const u_int32_t*) (out-16);
+ *((u_int32_t *)(&out[ 0])) = iv_i[0]^*((const u_int32_t *)(&in[ 0]));
+ *((u_int32_t *)(&out[ 4])) = iv_i[1]^*((const u_int32_t *)(&in[ 4]));
+ *((u_int32_t *)(&out[ 8])) = iv_i[2]^*((const u_int32_t *)(&in[ 8]));
+ *((u_int32_t *)(&out[12])) = iv_i[3]^*((const u_int32_t *)(&in[12]));
+ this->encrypt_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/programs/charon/lib/crypto/crypters/aes_cbc_crypter.h b/programs/charon/lib/crypto/crypters/aes_cbc_crypter.h
new file mode 100644
index 000000000..d7a3c0f5b
--- /dev/null
+++ b/programs/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 <brg@gladman.uk.net>
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/crypters/crypter.h>
+
+
+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/programs/charon/lib/crypto/crypters/crypter.c b/programs/charon/lib/crypto/crypters/crypter.c
new file mode 100644
index 000000000..827d10228
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <crypto/crypters/aes_cbc_crypter.h>
+
+
+/**
+ * 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/programs/charon/lib/crypto/crypters/crypter.h b/programs/charon/lib/crypto/crypters/crypter.h
new file mode 100644
index 000000000..9c219f5cc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+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/programs/charon/lib/crypto/diffie_hellman.c b/programs/charon/lib/crypto/diffie_hellman.c
new file mode 100644
index 000000000..e458fb80f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h>
+#include <stdio.h>
+
+#include "diffie_hellman.h"
+
+#include <daemon.h>
+#include <utils/randomizer.h>
+
+
+/**
+ * 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/programs/charon/lib/crypto/diffie_hellman.h b/programs/charon/lib/crypto/diffie_hellman.h
new file mode 100644
index 000000000..48a165557
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+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/programs/charon/lib/crypto/hashers/Makefile.hashers b/programs/charon/lib/crypto/hashers/Makefile.hashers
new file mode 100644
index 000000000..e05d41af3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/hashers/hasher.c b/programs/charon/lib/crypto/hashers/hasher.c
new file mode 100644
index 000000000..c15f41804
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <crypto/hashers/sha1_hasher.h>
+#include <crypto/hashers/md5_hasher.h>
+
+/**
+ * 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/programs/charon/lib/crypto/hashers/hasher.h b/programs/charon/lib/crypto/hashers/hasher.h
new file mode 100644
index 000000000..24683c01b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+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/programs/charon/lib/crypto/hashers/md5_hasher.c b/programs/charon/lib/crypto/hashers/md5_hasher.c
new file mode 100644
index 000000000..bd3ab0c62
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "md5_hasher.h"
+
+#include <definitions.h>
+
+#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/programs/charon/lib/crypto/hashers/md5_hasher.h b/programs/charon/lib/crypto/hashers/md5_hasher.h
new file mode 100644
index 000000000..1e6d95d19
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/hashers/hasher.h>
+
+
+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/programs/charon/lib/crypto/hashers/sha1_hasher.c b/programs/charon/lib/crypto/hashers/sha1_hasher.c
new file mode 100644
index 000000000..2b82ef4ba
--- /dev/null
+++ b/programs/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 <steve@edmweb.com> 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "sha1_hasher.h"
+
+#include <definitions.h>
+
+#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/programs/charon/lib/crypto/hashers/sha1_hasher.h b/programs/charon/lib/crypto/hashers/sha1_hasher.h
new file mode 100644
index 000000000..5124ea1a8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/hashers/hasher.h>
+
+
+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/programs/charon/lib/crypto/hmac.c b/programs/charon/lib/crypto/hmac.c
new file mode 100644
index 000000000..bb8880770
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it 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 <string.h>
+
+#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/programs/charon/lib/crypto/hmac.h b/programs/charon/lib/crypto/hmac.h
new file mode 100644
index 000000000..8945fc1fc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/hashers/hasher.h>
+#include <definitions.h>
+
+
+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/programs/charon/lib/crypto/prf_plus.c b/programs/charon/lib/crypto/prf_plus.c
new file mode 100644
index 000000000..d408d0517
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "prf_plus.h"
+
+#include <definitions.h>
+
+typedef struct private_prf_plus_t private_prf_plus_t;
+
+/**
+ * Private data of an prf_plus_t object.
+ *
+ */
+struct private_prf_plus_t {
+ /**
+ * Public interface of prf_plus_t.
+ */
+ prf_plus_t public;
+
+ /**
+ * PRF to use.
+ */
+ prf_t *prf;
+
+ /**
+ * Initial seed.
+ */
+ chunk_t seed;
+
+ /**
+ * Buffer to store current PRF result.
+ */
+ chunk_t buffer;
+
+ /**
+ * Already given out bytes in current buffer.
+ */
+ size_t given_out;
+
+ /**
+ * Octet which will be appended to the seed.
+ */
+ u_int8_t appending_octet;
+};
+
+/**
+ * Implementation of prf_plus_t.get_bytes.
+ */
+static void get_bytes(private_prf_plus_t *this, size_t length, u_int8_t *buffer)
+{
+ chunk_t appending_chunk;
+ size_t bytes_in_round;
+ size_t total_bytes_written = 0;
+
+ appending_chunk.ptr = &(this->appending_octet);
+ appending_chunk.len = 1;
+
+ while (length > 0)
+ { /* still more to do... */
+ if (this->buffer.len == this->given_out)
+ { /* no bytes left in buffer, get next*/
+ this->prf->get_bytes(this->prf, this->buffer, NULL);
+ this->prf->get_bytes(this->prf, this->seed, NULL);
+ this->prf->get_bytes(this->prf, appending_chunk, this->buffer.ptr);
+ this->given_out = 0;
+ this->appending_octet++;
+ }
+ /* how many bytes can we write in this round ? */
+ bytes_in_round = min(length, this->buffer.len - this->given_out);
+ /* copy bytes from buffer with offset */
+ memcpy(buffer + total_bytes_written, this->buffer.ptr + this->given_out, bytes_in_round);
+
+ length -= bytes_in_round;
+ this->given_out += bytes_in_round;
+ total_bytes_written += bytes_in_round;
+ }
+}
+
+/**
+ * Implementation of prf_plus_t.allocate_bytes.
+ */
+static void allocate_bytes(private_prf_plus_t *this, size_t length, chunk_t *chunk)
+{
+ chunk->ptr = malloc(length);
+ chunk->len = length;
+ this->public.get_bytes(&(this->public), length, chunk->ptr);
+}
+
+/**
+ * Implementation of prf_plus_t.destroy.
+ */
+static void destroy(private_prf_plus_t *this)
+{
+ free(this->buffer.ptr);
+ free(this->seed.ptr);
+ free(this);
+}
+
+/*
+ * Description in header.
+ */
+prf_plus_t *prf_plus_create(prf_t *prf, chunk_t seed)
+{
+ private_prf_plus_t *this;
+ chunk_t appending_chunk;
+
+ this = malloc_thing(private_prf_plus_t);
+
+ /* set public methods */
+ this->public.get_bytes = (void (*)(prf_plus_t *,size_t,u_int8_t*))get_bytes;
+ this->public.allocate_bytes = (void (*)(prf_plus_t *,size_t,chunk_t*))allocate_bytes;
+ this->public.destroy = (void (*)(prf_plus_t *))destroy;
+
+ /* take over prf */
+ this->prf = prf;
+
+ /* allocate buffer for prf output */
+ this->buffer.len = prf->get_block_size(prf);
+ this->buffer.ptr = malloc(this->buffer.len);
+
+ this->appending_octet = 0x01;
+
+ /* clone seed */
+ this->seed.ptr = clalloc(seed.ptr, seed.len);
+ this->seed.len = seed.len;
+
+ /* do the first run */
+ appending_chunk.ptr = &(this->appending_octet);
+ appending_chunk.len = 1;
+ this->prf->get_bytes(this->prf, this->seed, NULL);
+ this->prf->get_bytes(this->prf, appending_chunk, this->buffer.ptr);
+ this->given_out = 0;
+ this->appending_octet++;
+
+ return &(this->public);
+}
diff --git a/programs/charon/lib/crypto/prf_plus.h b/programs/charon/lib/crypto/prf_plus.h
new file mode 100644
index 000000000..bdcd01966
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/prfs/prf.h>
+
+
+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/programs/charon/lib/crypto/prfs/Makefile.prfs b/programs/charon/lib/crypto/prfs/Makefile.prfs
new file mode 100644
index 000000000..a98894346
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/prfs/hmac_prf.c b/programs/charon/lib/crypto/prfs/hmac_prf.c
new file mode 100644
index 000000000..2a7d34a3a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <crypto/hmac.h>
+
+
+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/programs/charon/lib/crypto/prfs/hmac_prf.h b/programs/charon/lib/crypto/prfs/hmac_prf.h
new file mode 100644
index 000000000..3a68960f7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <crypto/prfs/prf.h>
+#include <crypto/hashers/hasher.h>
+
+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/programs/charon/lib/crypto/prfs/prf.c b/programs/charon/lib/crypto/prfs/prf.c
new file mode 100644
index 000000000..bb7015e64
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <crypto/hashers/hasher.h>
+#include <crypto/prfs/hmac_prf.h>
+
+
+/**
+ * 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/programs/charon/lib/crypto/prfs/prf.h b/programs/charon/lib/crypto/prfs/prf.h
new file mode 100644
index 000000000..b1c1e6a66
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+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/programs/charon/lib/crypto/rsa/Makefile.rsa b/programs/charon/lib/crypto/rsa/Makefile.rsa
new file mode 100644
index 000000000..1a0204c83
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/rsa/rsa_private_key.c b/programs/charon/lib/crypto/rsa/rsa_private_key.c
new file mode 100644
index 000000000..358653f0e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "rsa_private_key.h"
+
+#include <daemon.h>
+#include <asn1/asn1.h>
+
+/*
+ * 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/programs/charon/lib/crypto/rsa/rsa_private_key.h b/programs/charon/lib/crypto/rsa/rsa_private_key.h
new file mode 100644
index 000000000..b3b8ae87f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+#include <crypto/rsa/rsa_public_key.h>
+#include <crypto/hashers/hasher.h>
+
+
+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/programs/charon/lib/crypto/rsa/rsa_public_key.c b/programs/charon/lib/crypto/rsa/rsa_public_key.c
new file mode 100644
index 000000000..6601b6cda
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "rsa_public_key.h"
+
+#include <daemon.h>
+#include <crypto/hashers/hasher.h>
+#include <asn1/asn1.h>
+
+/*
+ * 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/programs/charon/lib/crypto/rsa/rsa_public_key.h b/programs/charon/lib/crypto/rsa/rsa_public_key.h
new file mode 100644
index 000000000..ef79153d6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <gmp.h>
+
+#include <types.h>
+#include <definitions.h>
+
+
+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/programs/charon/lib/crypto/signers/Makefile.signers b/programs/charon/lib/crypto/signers/Makefile.signers
new file mode 100644
index 000000000..8f161a09d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/crypto/signers/hmac_signer.c b/programs/charon/lib/crypto/signers/hmac_signer.c
new file mode 100644
index 000000000..cb7d08244
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "hmac_signer.h"
+
+#include <crypto/prfs/hmac_prf.h>
+
+/**
+ * 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/programs/charon/lib/crypto/signers/hmac_signer.h b/programs/charon/lib/crypto/signers/hmac_signer.h
new file mode 100644
index 000000000..62427167e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/signers/signer.h>
+#include <crypto/hashers/hasher.h>
+
+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/programs/charon/lib/crypto/signers/signer.c b/programs/charon/lib/crypto/signers/signer.c
new file mode 100644
index 000000000..3e6378957
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <crypto/signers/hmac_signer.h>
+
+/**
+ * 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/programs/charon/lib/crypto/signers/signer.h b/programs/charon/lib/crypto/signers/signer.h
new file mode 100644
index 000000000..9625af813
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+
+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/programs/charon/lib/crypto/x509.c b/programs/charon/lib/crypto/x509.c
new file mode 100755
index 000000000..86a595618
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "x509.h"
+
+#include <daemon.h>
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+#include <utils/logger_manager.h>
+
+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(&current_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/programs/charon/lib/crypto/x509.h b/programs/charon/lib/crypto/x509.h
new file mode 100755
index 000000000..077238eab
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <definitions.h>
+#include <crypto/rsa/rsa_public_key.h>
+#include <utils/identification.h>
+#include <utils/iterator.h>
+
+
+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/programs/charon/lib/definitions.c b/programs/charon/lib/definitions.c
new file mode 100644
index 000000000..59c97a29b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/lib/definitions.h b/programs/charon/lib/definitions.h
new file mode 100644
index 000000000..c9ef066c1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <endian.h>
+# 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 <sys/endian.h>
+#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/programs/charon/lib/library.c b/programs/charon/lib/library.c
new file mode 100644
index 000000000..fa9c732bf
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <utils/logger_manager.h>
+#include <utils/leak_detective.h>
+
+/**
+ * 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/programs/charon/lib/library.h b/programs/charon/lib/library.h
new file mode 100644
index 000000000..da96befe1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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/programs/charon/lib/types.c b/programs/charon/lib/types.c
new file mode 100644
index 000000000..09ebf7310
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#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/programs/charon/lib/types.h b/programs/charon/lib/types.h
new file mode 100644
index 000000000..0e0782b31
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <gmp.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include <definitions.h>
+
+/**
+ * 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/programs/charon/lib/utils/Makefile.utils b/programs/charon/lib/utils/Makefile.utils
new file mode 100644
index 000000000..1c82283d7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/lib/utils/host.c b/programs/charon/lib/utils/host.c
new file mode 100644
index 000000000..020ed27f3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#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/programs/charon/lib/utils/host.h b/programs/charon/lib/utils/host.h
new file mode 100644
index 000000000..d81efffa6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/xfrm.h>
+
+#include <types.h>
+
+
+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/programs/charon/lib/utils/identification.c b/programs/charon/lib/utils/identification.c
new file mode 100644
index 000000000..33f3d92cd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "identification.h"
+
+#include <asn1/asn1.h>
+
+/**
+ * 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/programs/charon/lib/utils/identification.h b/programs/charon/lib/utils/identification.h
new file mode 100644
index 000000000..309b6858c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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/programs/charon/lib/utils/iterator.h b/programs/charon/lib/utils/iterator.h
new file mode 100644
index 000000000..de81db8e9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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/programs/charon/lib/utils/leak_detective.c b/programs/charon/lib/utils/leak_detective.c
new file mode 100644
index 000000000..780ba4c05
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <execinfo.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <pthread.h>
+
+#include "leak_detective.h"
+
+#include <types.h>
+#include <utils/logger_manager.h>
+
+#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/programs/charon/lib/utils/leak_detective.h b/programs/charon/lib/utils/leak_detective.h
new file mode 100644
index 000000000..13c0d01ab
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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/programs/charon/lib/utils/linked_list.c b/programs/charon/lib/utils/linked_list.c
new file mode 100644
index 000000000..64443434b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#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/programs/charon/lib/utils/linked_list.h b/programs/charon/lib/utils/linked_list.h
new file mode 100644
index 000000000..8647f064d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+#include <utils/iterator.h>
+
+
+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/programs/charon/lib/utils/logger.c b/programs/charon/lib/utils/logger.c
new file mode 100644
index 000000000..fdaeddff0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <syslog.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <pthread.h>
+
+#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/programs/charon/lib/utils/logger.h b/programs/charon/lib/utils/logger.h
new file mode 100644
index 000000000..dec73078e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdio.h>
+
+#include <types.h>
+
+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/programs/charon/lib/utils/logger_manager.c b/programs/charon/lib/utils/logger_manager.c
new file mode 100644
index 000000000..ecbe1a6c1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <definitions.h>
+#include <utils/linked_list.h>
+
+/**
+ * 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/programs/charon/lib/utils/logger_manager.h b/programs/charon/lib/utils/logger_manager.h
new file mode 100644
index 000000000..a3ff5a37e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <pthread.h>
+
+#include <utils/logger.h>
+
+#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/programs/charon/lib/utils/randomizer.c b/programs/charon/lib/utils/randomizer.c
new file mode 100644
index 000000000..09e81894e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#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/programs/charon/lib/utils/randomizer.h b/programs/charon/lib/utils/randomizer.h
new file mode 100644
index 000000000..55519550e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <types.h>
+
+
+/**
+ * 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/programs/charon/lib/utils/tester.c b/programs/charon/lib/utils/tester.c
new file mode 100644
index 000000000..a7599dd82
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+#include "tester.h"
+
+#include <utils/linked_list.h>
+#include <queues/job_queue.h>
+
+
+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/programs/charon/lib/utils/tester.h b/programs/charon/lib/utils/tester.h
new file mode 100644
index 000000000..3decb2039
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <stdio.h>
+
+#include <types.h>
+
+
+/* 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/programs/charon/patches/strongswan-2.7.0.patch b/programs/charon/patches/strongswan-2.7.0.patch
new file mode 100644
index 000000000..b21e1013b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT 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 <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <signal.h>
++#include <string.h>
++#include <stdlib.h>
++#include <errno.h>
++
++#include <freeswan.h>
++
++#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 <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT 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 <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT 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 <sys/types.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <linux/stddef.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <netinet/in.h>
++#include <arpa/inet.h>
++
++#include <freeswan.h>
++
++#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 <http://www.fsf.org/copyleft/gpl.txt>.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT 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/programs/charon/scripts/alice-key.der b/programs/charon/scripts/alice-key.der
new file mode 100644
index 000000000..5a8aef6cb
--- /dev/null
+++ b/programs/charon/scripts/alice-key.der
Binary files differ
diff --git a/programs/charon/scripts/alice.der b/programs/charon/scripts/alice.der
new file mode 100644
index 000000000..8154defd9
--- /dev/null
+++ b/programs/charon/scripts/alice.der
Binary files differ
diff --git a/programs/charon/scripts/bob-key.der b/programs/charon/scripts/bob-key.der
new file mode 100644
index 000000000..f944dec9f
--- /dev/null
+++ b/programs/charon/scripts/bob-key.der
Binary files differ
diff --git a/programs/charon/scripts/bob.der b/programs/charon/scripts/bob.der
new file mode 100644
index 000000000..401611888
--- /dev/null
+++ b/programs/charon/scripts/bob.der
Binary files differ
diff --git a/programs/charon/scripts/complex1.der b/programs/charon/scripts/complex1.der
new file mode 100644
index 000000000..ba460cbee
--- /dev/null
+++ b/programs/charon/scripts/complex1.der
Binary files differ
diff --git a/programs/charon/scripts/complex2.der b/programs/charon/scripts/complex2.der
new file mode 100644
index 000000000..160b21f47
--- /dev/null
+++ b/programs/charon/scripts/complex2.der
Binary files differ
diff --git a/programs/charon/scripts/daemon-loop.sh b/programs/charon/scripts/daemon-loop.sh
new file mode 100755
index 000000000..9a361e012
--- /dev/null
+++ b/programs/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/programs/charon/scripts/deleteline b/programs/charon/scripts/deleteline
new file mode 100755
index 000000000..9f529dccc
--- /dev/null
+++ b/programs/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/programs/charon/scripts/replace b/programs/charon/scripts/replace
new file mode 100755
index 000000000..adfc8e09a
--- /dev/null
+++ b/programs/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/programs/charon/scripts/to-alice.sh b/programs/charon/scripts/to-alice.sh
new file mode 100755
index 000000000..01ba27f5b
--- /dev/null
+++ b/programs/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/programs/charon/scripts/to-bob.sh b/programs/charon/scripts/to-bob.sh
new file mode 100755
index 000000000..df30bd893
--- /dev/null
+++ b/programs/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/programs/charon/stroke/Makefile.stroke b/programs/charon/stroke/Makefile.stroke
new file mode 100644
index 000000000..c87445095
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/stroke/stroke.c b/programs/charon/stroke/stroke.c
new file mode 100644
index 000000000..9ecda0413
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <linux/stddef.h>
+
+#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/programs/charon/stroke/stroke.h b/programs/charon/stroke/stroke.h
new file mode 100644
index 000000000..cb40cf843
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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/programs/charon/testing/Makefile.testcases b/programs/charon/testing/Makefile.testcases
new file mode 100644
index 000000000..5a261a799
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/charon/testing/aes_cbc_crypter_test.c b/programs/charon/testing/aes_cbc_crypter_test.c
new file mode 100644
index 000000000..30dae3926
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "aes_cbc_crypter_test.h"
+
+#include <daemon.h>
+
+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/programs/charon/testing/aes_cbc_crypter_test.h b/programs/charon/testing/aes_cbc_crypter_test.h
new file mode 100644
index 000000000..c3897a4d6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/crypters/aes_cbc_crypter.h>
+#include <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/certificate_test.c b/programs/charon/testing/certificate_test.c
new file mode 100644
index 000000000..be8a8f7cf
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "certificate_test.h"
+
+#include <daemon.h>
+#include <crypto/x509.h>
+#include <utils/logger.h>
+
+
+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/programs/charon/testing/certificate_test.h b/programs/charon/testing/certificate_test.h
new file mode 100644
index 000000000..8dcbd0f93
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/child_sa_test.c b/programs/charon/testing/child_sa_test.c
new file mode 100644
index 000000000..0cf354c26
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <sa/child_sa.h>
+#include <utils/logger.h>
+
+
+/**
+ * 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/programs/charon/testing/child_sa_test.h b/programs/charon/testing/child_sa_test.h
new file mode 100644
index 000000000..ef92499fe
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/connection_test.c b/programs/charon/testing/connection_test.c
new file mode 100644
index 000000000..6b12afc1d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <config/connections/connection.h>
+#include <crypto/prfs/prf.h>
+
+
+/**
+ * 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/programs/charon/testing/connection_test.h b/programs/charon/testing/connection_test.h
new file mode 100644
index 000000000..4d2a1d89e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/diffie_hellman_test.c b/programs/charon/testing/diffie_hellman_test.c
new file mode 100644
index 000000000..0a44a022a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "diffie_hellman_test.h"
+
+#include <daemon.h>
+#include <utils/logger_manager.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <crypto/diffie_hellman.h>
+
+/*
+ * 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/programs/charon/testing/diffie_hellman_test.h b/programs/charon/testing/diffie_hellman_test.h
new file mode 100644
index 000000000..e6e3ff608
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/encryption_payload_test.c b/programs/charon/testing/encryption_payload_test.c
new file mode 100644
index 000000000..9e4108b9e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "encryption_payload_test.h"
+
+#include <daemon.h>
+#include <utils/logger_manager.h>
+#include <encoding/generator.h>
+#include <encoding/parser.h>
+#include <encoding/payloads/encryption_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+/*
+ * 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/programs/charon/testing/encryption_payload_test.h b/programs/charon/testing/encryption_payload_test.h
new file mode 100644
index 000000000..5e6353bfd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/event_queue_test.c b/programs/charon/testing/event_queue_test.c
new file mode 100644
index 000000000..58a214051
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+
+#include "event_queue_test.h"
+
+#include <queues/event_queue.h>
+#include <queues/jobs/initiate_ike_sa_job.h>
+
+/**
+ * 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(&current_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(&current_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/programs/charon/testing/event_queue_test.h b/programs/charon/testing/event_queue_test.h
new file mode 100644
index 000000000..5f8c47fad
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/generator_test.c b/programs/charon/testing/generator_test.c
new file mode 100644
index 000000000..004c700e6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "generator_test.h"
+
+#include <daemon.h>
+#include <encoding/generator.h>
+#include <utils/logger_manager.h>
+#include <utils/logger.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/ike_header.h>
+#include <encoding/payloads/transform_attribute.h>
+#include <encoding/payloads/transform_substructure.h>
+#include <encoding/payloads/proposal_substructure.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/vendor_id_payload.h>
+#include <encoding/payloads/cp_payload.h>
+#include <encoding/payloads/eap_payload.h>
+
+/*
+ * 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/programs/charon/testing/generator_test.h b/programs/charon/testing/generator_test.h
new file mode 100644
index 000000000..204255fb7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/hasher_test.c b/programs/charon/testing/hasher_test.c
new file mode 100644
index 000000000..9130a2092
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#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/programs/charon/testing/hasher_test.h b/programs/charon/testing/hasher_test.h
new file mode 100644
index 000000000..cc6fe52c8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/hashers/hasher.h>
+#include <crypto/hashers/md5_hasher.h>
+#include <crypto/hashers/sha1_hasher.h>
+#include <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/hmac_signer_test.c b/programs/charon/testing/hmac_signer_test.c
new file mode 100644
index 000000000..a1ac8ea43
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "hmac_signer_test.h"
+
+#include <crypto/signers/signer.h>
+#include <daemon.h>
+
+
+/*
+ * 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/programs/charon/testing/hmac_signer_test.h b/programs/charon/testing/hmac_signer_test.h
new file mode 100644
index 000000000..4a2459a8e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/hmac_test.c b/programs/charon/testing/hmac_test.c
new file mode 100644
index 000000000..c1341257c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "hmac_test.h"
+
+#include <crypto/hmac.h>
+
+
+/*
+ * 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/programs/charon/testing/hmac_test.h b/programs/charon/testing/hmac_test.h
new file mode 100644
index 000000000..1eef93cd3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/hmac.h>
+#include <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/identification_test.c b/programs/charon/testing/identification_test.c
new file mode 100644
index 000000000..b148b53e0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "identification_test.h"
+
+#include <utils/identification.h>
+#include <utils/logger.h>
+
+/*
+ * 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/programs/charon/testing/identification_test.h b/programs/charon/testing/identification_test.h
new file mode 100644
index 000000000..b1078c52f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/ike_sa_id_test.c b/programs/charon/testing/ike_sa_id_test.c
new file mode 100644
index 000000000..ba44363fb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sa/ike_sa_id.h>
+
+/*
+ * 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/programs/charon/testing/ike_sa_id_test.h b/programs/charon/testing/ike_sa_id_test.h
new file mode 100644
index 000000000..75429e4fb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/ike_sa_manager_test.c b/programs/charon/testing/ike_sa_manager_test.c
new file mode 100644
index 000000000..5247be7f0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "ike_sa_manager_test.h"
+
+#include <types.h>
+#include <sa/ike_sa_manager.h>
+
+
+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/programs/charon/testing/ike_sa_manager_test.h b/programs/charon/testing/ike_sa_manager_test.h
new file mode 100644
index 000000000..c3e9f99f1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/ike_sa_test.c b/programs/charon/testing/ike_sa_test.c
new file mode 100644
index 000000000..798b5edc9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <types.h>
+#include <encoding/message.h>
+#include <sa/ike_sa.h>
+
+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/programs/charon/testing/ike_sa_test.h b/programs/charon/testing/ike_sa_test.h
new file mode 100644
index 000000000..e93bc34fd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/job_queue_test.c b/programs/charon/testing/job_queue_test.c
new file mode 100644
index 000000000..336a9a188
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "job_queue_test.h"
+
+#include <queues/job_queue.h>
+#include <queues/jobs/initiate_ike_sa_job.h>
+
+
+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/programs/charon/testing/job_queue_test.h b/programs/charon/testing/job_queue_test.h
new file mode 100644
index 000000000..f2d3edc4c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/kernel_interface_test.c b/programs/charon/testing/kernel_interface_test.c
new file mode 100644
index 000000000..86553e15e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <threads/kernel_interface.h>
+#include <utils/logger.h>
+#include <utils/host.h>
+
+
+/*
+ * 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/programs/charon/testing/kernel_interface_test.h b/programs/charon/testing/kernel_interface_test.h
new file mode 100644
index 000000000..fc8dab4b6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/leak_detective_test.c b/programs/charon/testing/leak_detective_test.c
new file mode 100644
index 000000000..8d71d9f0f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/charon/testing/leak_detective_test.h b/programs/charon/testing/leak_detective_test.h
new file mode 100644
index 000000000..e64266bd5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/linked_list_test.c b/programs/charon/testing/linked_list_test.c
new file mode 100644
index 000000000..3d5666f64
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "linked_list_test.h"
+
+#include <utils/linked_list.h>
+
+ /*
+ * 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/programs/charon/testing/linked_list_test.h b/programs/charon/testing/linked_list_test.h
new file mode 100644
index 000000000..a9773f8f0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/packet_test.c b/programs/charon/testing/packet_test.c
new file mode 100644
index 000000000..fdb195ec1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "packet_test.h"
+
+#include <daemon.h>
+#include <network/packet.h>
+#include <utils/logger_manager.h>
+
+
+/*
+ * 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/programs/charon/testing/packet_test.h b/programs/charon/testing/packet_test.h
new file mode 100644
index 000000000..8bc297e1b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/parser_test.c b/programs/charon/testing/parser_test.c
new file mode 100644
index 000000000..263c6eb70
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "parser_test.h"
+
+#include <utils/logger_manager.h>
+#include <encoding/generator.h>
+#include <encoding/parser.h>
+#include <encoding/payloads/encodings.h>
+#include <encoding/payloads/ike_header.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/delete_payload.h>
+#include <encoding/payloads/vendor_id_payload.h>
+#include <encoding/payloads/cp_payload.h>
+#include <encoding/payloads/eap_payload.h>
+
+
+/*
+ * 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**)&notify_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/programs/charon/testing/parser_test.h b/programs/charon/testing/parser_test.h
new file mode 100644
index 000000000..4956df13e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/policy_test.c b/programs/charon/testing/policy_test.c
new file mode 100644
index 000000000..9003eeff0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <config/policies/policy.h>
+#include <config/traffic_selector.h>
+#include <utils/logger.h>
+#include <encoding/payloads/ts_payload.h>
+
+
+/**
+ * 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; i<count; i++)
+// {
+// chunk_t fa_res = ts_result[i]->get_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/programs/charon/testing/policy_test.h b/programs/charon/testing/policy_test.h
new file mode 100644
index 000000000..6c8072a9c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/prf_plus_test.c b/programs/charon/testing/prf_plus_test.c
new file mode 100644
index 000000000..818c5c17e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "prf_plus_test.h"
+
+#include <crypto/prf_plus.h>
+
+
+/*
+ * 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/programs/charon/testing/prf_plus_test.h b/programs/charon/testing/prf_plus_test.h
new file mode 100644
index 000000000..2ad8ce0c1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/prf_plus.h>
+#include <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/proposal_test.c b/programs/charon/testing/proposal_test.c
new file mode 100644
index 000000000..1b16390d3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <daemon.h>
+#include <config/proposal.h>
+#include <utils/logger.h>
+
+
+/**
+ * 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/programs/charon/testing/proposal_test.h b/programs/charon/testing/proposal_test.h
new file mode 100644
index 000000000..059af11cc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/rsa_test.c b/programs/charon/testing/rsa_test.c
new file mode 100644
index 000000000..696901531
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "rsa_test.h"
+
+#include <daemon.h>
+#include <utils/logger.h>
+#include <crypto/x509.h>
+
+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/programs/charon/testing/rsa_test.h b/programs/charon/testing/rsa_test.h
new file mode 100644
index 000000000..baeccf402
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <crypto/rsa/rsa_public_key.h>
+#include <crypto/rsa/rsa_private_key.h>
+
+#include <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/scheduler_test.c b/programs/charon/testing/scheduler_test.c
new file mode 100644
index 000000000..de7346d83
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <unistd.h>
+
+#include "scheduler_test.h"
+
+#include <daemon.h>
+#include <threads/scheduler.h>
+#include <queues/event_queue.h>
+#include <queues/job_queue.h>
+#include <queues/jobs/incoming_packet_job.h>
+
+
+/**
+ * @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/programs/charon/testing/scheduler_test.h b/programs/charon/testing/scheduler_test.h
new file mode 100644
index 000000000..746848e49
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/send_queue_test.c b/programs/charon/testing/send_queue_test.c
new file mode 100644
index 000000000..a56f8e5a2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <pthread.h>
+
+#include "send_queue_test.h"
+
+#include <queues/send_queue.h>
+
+
+/**
+ * @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/programs/charon/testing/send_queue_test.h b/programs/charon/testing/send_queue_test.h
new file mode 100644
index 000000000..138657e10
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/sender_test.c b/programs/charon/testing/sender_test.c
new file mode 100644
index 000000000..391d71fbc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+
+#include "sender_test.h"
+
+#include <daemon.h>
+#include <threads/sender.h>
+#include <network/packet.h>
+#include <network/socket.h>
+#include <queues/send_queue.h>
+#include <queues/job_queue.h>
+#include <queues/jobs/incoming_packet_job.h>
+
+/**
+ * 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/programs/charon/testing/sender_test.h b/programs/charon/testing/sender_test.h
new file mode 100644
index 000000000..1fdfed69d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/socket_test.c b/programs/charon/testing/socket_test.c
new file mode 100644
index 000000000..9ae1b0fbc
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+
+#include "socket_test.h"
+
+#include <network/socket.h>
+#include <utils/logger.h>
+
+/*
+ * 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/programs/charon/testing/socket_test.h b/programs/charon/testing/socket_test.h
new file mode 100644
index 000000000..a59995297
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/charon/testing/testcases.c b/programs/charon/testing/testcases.c
new file mode 100644
index 000000000..e4d92becf
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+
+#include <daemon.h>
+
+#include <queues/job_queue.h>
+#include <queues/event_queue.h>
+#include <queues/send_queue.h>
+#include <config/configuration.h>
+#include <sa/ike_sa_manager.h>
+#include <network/socket.h>
+#include <utils/logger_manager.h>
+#include <utils/tester.h>
+#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/programs/charon/testing/thread_pool_test.c b/programs/charon/testing/thread_pool_test.c
new file mode 100644
index 000000000..ee7a5101f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#include "thread_pool_test.h"
+
+#include <threads/thread_pool.h>
+
+/*
+ * 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/programs/charon/testing/thread_pool_test.h b/programs/charon/testing/thread_pool_test.h
new file mode 100644
index 000000000..bdae797b7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY 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 <utils/tester.h>
+
+/**
+ * @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/programs/eroute/.cvsignore b/programs/eroute/.cvsignore
new file mode 100644
index 000000000..133c4b456
--- /dev/null
+++ b/programs/eroute/.cvsignore
@@ -0,0 +1 @@
+eroute
diff --git a/programs/eroute/Makefile b/programs/eroute/Makefile
new file mode 100644
index 000000000..6d8f68033
--- /dev/null
+++ b/programs/eroute/Makefile
@@ -0,0 +1,52 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:=eroute
+EXTRA5PROC=eroute.5
+
+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.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 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.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
diff --git a/programs/eroute/eroute.5 b/programs/eroute/eroute.5
new file mode 100644
index 000000000..52b3f4d25
--- /dev/null
+++ b/programs/eroute/eroute.5
@@ -0,0 +1,272 @@
+.TH IPSEC_EROUTE 5 "20 Sep 2001"
+.\"
+.\" RCSID $Id: eroute.5,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec_eroute \- list of existing eroutes
+.SH SYNOPSIS
+.B ipsec
+.B eroute
+.PP
+.B cat
+.B /proc/net/ipsec_eroute
+.SH DESCRIPTION
+.I /proc/net/ipsec_eroute
+lists the IPSEC extended routing tables,
+which control what (if any) processing is applied
+to non-encrypted packets arriving for IPSEC processing and forwarding.
+At this point it is a read-only file.
+.PP
+A table entry consists of:
+.IP + 3
+packet count,
+.IP +
+source address with mask and source port (0 if all ports or not applicable)
+.IP +
+a '->' separator for visual and automated parsing between src and dst
+.IP +
+destination address with mask and destination port (0 if all ports or
+not applicable)
+.IP +
+a '=>' separator for visual and automated parsing between selection
+criteria and SAID to use
+.IP +
+SAID (Security Association IDentifier), comprised of:
+.IP + 6
+protocol
+(\fIproto\fR),
+.IP +
+address family
+(\fIaf\fR),
+where '.' stands for IPv4 and ':' for IPv6
+.IP +
+Security Parameters Index
+(\fISPI\fR),
+.IP +
+effective destination
+(\fIedst\fR),
+where the packet should be forwarded after processing
+(normally the other security gateway)
+together indicate which Security Association should be used to process
+the packet,
+.IP + 3
+a ':' separating the SAID from the transport protocol (0 if all protocols)
+.IP +
+source identity text string with no whitespace, in parens,
+.IP +
+destination identity text string with no whitespace, in parens
+.PP
+Addresses are written as IPv4 dotted quads or IPv6 coloned hex,
+protocol is one of "ah", "esp", "comp" or "tun"
+and
+SPIs are prefixed hexadecimal numbers where the prefix '.' is for IPv4 and the prefix ':' is for IPv6
+.
+.PP
+SAIDs are written as "protoafSPI@edst". There are also 5
+"magic" SAIDs which have special meaning:
+.IP + 3
+.B %drop
+means that matches are to be dropped
+.IP +
+.B %reject
+means that matches are to be dropped and an ICMP returned, if
+possible to inform
+.IP +
+.B %trap
+means that matches are to trigger an ACQUIRE message to the Key
+Management daemon(s) and a hold eroute will be put in place to
+prevent subsequent packets also triggering ACQUIRE messages.
+.IP +
+.B %hold
+means that matches are to stored until the eroute is replaced or
+until that eroute gets reaped
+.IP +
+.B %pass
+means that matches are to allowed to pass without IPSEC processing
+.br
+.ne 5
+.SH EXAMPLES
+.LP
+.B "1867 172.31.252.0/24:0 -> 0.0.0.0/0:0 => tun0x130@192.168.43.1:0 "
+.br
+.B " () ()"
+.LP
+means that 1,867 packets have been sent to an
+.BR eroute
+that has been set up to protect traffic between the subnet
+.BR 172.31.252.0
+with a subnet mask of
+.BR 24
+bits and the default address/mask represented by an address of
+.BR 0.0.0.0
+with a subnet mask of
+.BR 0
+bits using the local machine as a security gateway on this end of the
+tunnel and the machine
+.BR 192.168.43.1
+on the other end of the tunnel with a Security Association IDentifier of
+.BR tun0x130@192.168.43.1
+which means that it is a tunnel mode connection (4, IPPROTO_IPIP) with a
+Security Parameters Index of
+.BR 130
+in hexadecimal with no identies defined for either end.
+.LP
+.B "746 192.168.2.110/32:0 -> 192.168.2.120/32:25 => esp0x130@192.168.2.120:6 "
+.br
+.B " () ()"
+.LP
+means that 746 packets have been sent to an
+.BR eroute
+that has been set up to protect traffic sent from any port on the host
+.BR 192.168.2.110
+to the SMTP (TCP, port 25) port on the host
+.BR 192.168.2.120
+with a Security Association IDentifier of
+.BR tun0x130@192.168.2.120
+which means that it is a transport mode connection with a
+Security Parameters Index of
+.BR 130
+in hexadecimal with no identies defined for either end.
+.LP
+.B 125 3049:1::/64 -> 0:0/0 => tun:130@3058:4::5 () ()
+.LP
+means that 125 packets have been sent to an
+.BR eroute
+that has been set up to protect traffic between the subnet
+.BR 3049:1::
+with a subnet mask of
+.BR 64
+bits and the default address/mask represented by an address of
+.BR 0:0
+with a subnet mask of
+.BR 0
+bits using the local machine as a security gateway on this end of the
+tunnel and the machine
+.BR 3058:4::5
+on the other end of the tunnel with a Security Association IDentifier of
+.BR tun:130@3058:4::5
+which means that it is a tunnel mode connection with a
+Security Parameters Index of
+.BR 130
+in hexadecimal with no identies defined for either end.
+.LP
+.B 42 192.168.6.0/24:0 -> 192.168.7.0/24:0 => %passthrough
+.LP
+means that 42 packets have been sent to an
+.BR eroute
+that has been set up to pass the traffic from the subnet
+.BR 192.168.6.0
+with a subnet mask of
+.BR 24
+bits and to subnet
+.BR 192.168.7.0
+with a subnet mask of
+.BR 24
+bits without any IPSEC processing with no identies defined for either end.
+.LP
+.B 2112 192.168.8.55/32:0 -> 192.168.9.47/24:0 => %hold (east) ()
+.LP
+means that 2112 packets have been sent to an
+.BR eroute
+that has been set up to hold the traffic from the host
+.BR 192.168.8.55
+and to host
+.BR 192.168.9.47
+until a key exchange from a Key Management daemon
+succeeds and puts in an SA or fails and puts in a pass
+or drop eroute depending on the default configuration with the local client
+defined as "east" and no identy defined for the remote end.
+.LP
+.B "2001 192.168.2.110/32:0 -> 192.168.2.120/32:0 => "
+.br
+.B " esp0xe6de@192.168.2.120:0 () ()"
+.LP
+means that 2001 packets have been sent to an
+.BR eroute
+that has been set up to protect traffic between the host
+.BR 192.168.2.110
+and the host
+.BR 192.168.2.120
+using
+.BR 192.168.2.110
+as a security gateway on this end of the
+connection and the machine
+.BR 192.168.2.120
+on the other end of the connection with a Security Association IDentifier of
+.BR esp0xe6de@192.168.2.120
+which means that it is a transport mode connection with a Security
+Parameters Index of
+.BR e6de
+in hexadecimal using Encapsuation Security Payload protocol (50,
+IPPROTO_ESP) with no identies defined for either end.
+.LP
+.B "1984 3049:1::110/128 -> 3049:1::120/128 => "
+.br
+.B " ah:f5ed@3049:1::120 () ()"
+.LP
+means that 1984 packets have been sent to an
+.BR eroute
+that has been set up to authenticate traffic between the host
+.BR 3049:1::110
+and the host
+.BR 3049:1::120
+using
+.BR 3049:1::110
+as a security gateway on this end of the
+connection and the machine
+.BR 3049:1::120
+on the other end of the connection with a Security Association IDentifier of
+.BR ah:f5ed@3049:1::120
+which means that it is a transport mode connection with a Security
+Parameters Index of
+.BR f5ed
+in hexadecimal using Authentication Header protocol (51,
+IPPROTO_AH) with no identies defined for either end.
+.SH FILES
+/proc/net/ipsec_eroute, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(5), ipsec_spi(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_eroute(8), ipsec_version(5),
+ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: eroute.5,v $
+.\" Revision 1.1 2004/03/15 20:35:27 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.9 2002/04/24 07:35:38 mcr
+.\" Moved from ./klips/utils/eroute.5,v
+.\"
+.\" Revision 1.8 2001/09/20 15:33:13 rgb
+.\" PF_KEYv2 ident extension output documentation.
+.\"
+.\" Revision 1.7 2001/05/29 05:15:31 rgb
+.\" Added packet count field at beginning of line.
+.\"
+.\" Revision 1.6 2001/02/26 19:58:32 rgb
+.\" Put SAID elements in order they appear in SAID.
+.\" Implement magic SAs %drop, %reject, %trap, %hold, %pass as part
+.\" of the new SPD and to support opportunistic.
+.\"
+.\" Revision 1.5 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.4 2000/09/13 15:54:31 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/28 12:44:11 henry
+.\" format touchup
+.\"
+.\" Revision 1.1 2000/06/28 05:43:00 rgb
+.\" Added manpages for all 5 klips utils.
+.\"
+.\"
+.\"
diff --git a/programs/eroute/eroute.8 b/programs/eroute/eroute.8
new file mode 100644
index 000000000..d9449632b
--- /dev/null
+++ b/programs/eroute/eroute.8
@@ -0,0 +1,354 @@
+.TH IPSEC_EROUTE 8 "21 Jun 2000"
+.\"
+.\" RCSID $Id: eroute.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.\"
+.SH NAME
+ipsec eroute \- manipulate IPSEC extended routing tables
+.SH SYNOPSIS
+.B ipsec
+.B eroute
+.PP
+.B ipsec
+.B eroute
+.B \-\-add
+.B \-\-eraf (inet | inet6)
+.B \-\-src
+src/srcmaskbits|srcmask
+.B \-\-dst
+dst/dstmaskbits|dstmask
+[
+.B \-\-transport\-proto
+transport-protocol
+]
+[
+.B \-\-src\-port
+source-port
+]
+[
+.B \-\-dst\-port
+dest-port
+]
+<SAID>
+.PP
+.B ipsec
+.B eroute
+.B \-\-replace
+.B \-\-eraf (inet | inet6)
+.B \-\-src
+src/srcmaskbits|srcmask
+.B \-\-dst
+dst/dstmaskbits|dstmask
+[
+.B \-\-transport\-proto
+transport-protocol
+]
+[
+.B \-\-src\-port
+source-port
+]
+[
+.B \-\-dst\-port
+dest-port
+]
+<SAID>
+.PP
+.B ipsec
+.B eroute
+.B \-\-del
+.B \-\-eraf (inet | inet6)
+.B \-\-src
+src/srcmaskbits|srcmask
+.B \-\-dst
+dst/dstmaskbits|dstmask
+[
+.B \-\-transport\-proto
+transport-protocol
+]
+[
+.B \-\-src\-port
+source-port
+]
+[
+.B \-\-dst\-port
+dest-port
+]
+.PP
+.B ipsec
+.B eroute
+.B \-\-clear
+.PP
+.B ipsec
+.B eroute
+.B \-\-help
+.PP
+.B ipsec
+.B eroute
+.B \-\-version
+.PP
+Where <SAID> is
+.B \-\-af
+(inet | inet6)
+.B \-\-edst
+edst
+.B \-\-spi
+spi
+.B \-\-proto
+proto
+OR
+.B \-\-said
+said
+OR
+.B \-\-said
+.B (%passthrough | %passthrough4 | %passthrough6 | %drop | %reject | %trap | %hold | %pass )
+.SH DESCRIPTION
+.I Eroute
+manages the IPSEC extended routing tables,
+which control what (if any) processing is applied
+to non-encrypted packets arriving for IPSEC processing and forwarding.
+The form with no additional arguments lists the contents of
+/proc/net/ipsec_eroute.
+The
+.B \-\-add
+form adds a table entry, the
+.B \-\-replace
+form replaces a table entry, while the
+.B \-\-del
+form deletes one. The
+.B \-\-clear
+form deletes the entire table.
+.PP
+A table entry consists of:
+.IP + 3
+source and destination addresses,
+with masks, source and destination ports and protocol
+for selection of packets. The source and destination ports are only
+legal if the transport protocol is
+.BR TCP
+or
+.BR UDP.
+A port can be specified as either decimal, hexadecimal (leading 0x),
+octal (leading 0) or a name listed in the first column of /etc/services.
+A transport protocol can be specified as either decimal, hexadecimal
+(leading 0x), octal (leading 0) or a name listed in the first column
+of /etc/protocols. If a transport protocol or port is not specified
+then it defaults to 0 which means all protocols or all ports
+respectively.
+.IP +
+Security Association IDentifier, comprised of:
+.IP + 6
+protocol
+(\fIproto\fR), indicating (together with the
+effective destination and the security parameters index)
+which Security Association should be used to process the packet
+.IP +
+address family
+(\fIaf\fR),
+.IP +
+Security Parameters Index
+(\fIspi\fR), indicating (together with the
+effective destination and protocol)
+which Security Association should be used to process the packet
+(must be larger than or equal to 0x100)
+.IP +
+effective destination
+(\fIedst\fR),
+where the packet should be forwarded after processing
+(normally the other security gateway)
+.IP + 3
+OR
+.IP + 6
+SAID
+(\fIsaid\fR), indicating
+which Security Association should be used to process the packet
+.PP
+Addresses are written as IPv4 dotted quads or IPv6 coloned hex,
+protocol is one of "ah", "esp", "comp" or "tun" and SPIs are
+prefixed hexadecimal numbers where '.' represents IPv4 and ':'
+stands for IPv6.
+.PP
+SAIDs are written as "protoafSPI@address". There are also 5
+"magic" SAIDs which have special meaning:
+.IP + 3
+.B %drop
+means that matches are to be dropped
+.IP +
+.B %reject
+means that matches are to be dropped and an ICMP returned, if
+possible to inform
+.IP +
+.B %trap
+means that matches are to trigger an ACQUIRE message to the Key
+Management daemon(s) and a hold eroute will be put in place to
+prevent subsequent packets also triggering ACQUIRE messages.
+.IP +
+.B %hold
+means that matches are to stored until the eroute is replaced or
+until that eroute gets reaped
+.IP +
+.B %pass
+means that matches are to allowed to pass without IPSEC processing
+.PP
+The format of /proc/net/ipsec_eroute is listed in ipsec_eroute(5).
+.br
+.ne 5
+.SH EXAMPLES
+.LP
+.B "ipsec eroute \-\-add \-\-eraf inet \-\-src 192.168.0.1/32 \e"
+.br
+.B " \-\-dst 192.168.2.0/24 \-\-af inet \-\-edst 192.168.0.2 \e"
+.br
+.B " \-\-spi 0x135 \-\-proto tun"
+.LP
+sets up an
+.BR eroute
+on a Security Gateway to protect traffic between the host
+.BR 192.168.0.1
+and the subnet
+.BR 192.168.2.0
+with
+.BR 24
+bits of subnet mask via Security Gateway
+.BR 192.168.0.2
+using the Security Association with address
+.BR 192.168.0.2 ,
+Security Parameters Index
+.BR 0x135
+and protocol
+.BR tun
+(50, IPPROTO_ESP).
+.LP
+.B "ipsec eroute \-\-add \-\-eraf inet6 \-\-src 3049:1::1/128 \e"
+.br
+.B " \-\-dst 3049:2::/64 \-\-af inet6 \-\-edst 3049:1::2 \e"
+.br
+.B " \-\-spi 0x145 \-\-proto tun"
+.LP
+sets up an
+.BR eroute
+on a Security Gateway to protect traffic between the host
+.BR 3049:1::1
+and the subnet
+.BR 3049:2::
+with
+.BR 64
+bits of subnet mask via Security Gateway
+.BR 3049:1::2
+using the Security Association with address
+.BR 3049:1::2 ,
+Security Parameters Index
+.BR 0x145
+and protocol
+.BR tun
+(50, IPPROTO_ESP).
+.LP
+.B "ipsec eroute \-\-replace \-\-eraf inet \-\-src company.com/24 \e"
+.br
+.B " \-\-dst ftp.ngo.org/32 \-\-said tun.135@gw.ngo.org"
+.LP
+replaces an
+.BR eroute
+on a Security Gateway to protect traffic between the subnet
+.BR company.com
+with
+.BR 24
+bits of subnet mask and the host
+.BR ftp.ngo.org
+via Security Gateway
+.BR gw.ngo.org
+using the Security Association with Security Association ID
+.BR tun0x135@gw.ngo.org
+.LP
+.B "ipsec eroute \-\-del \-\-eraf inet \-\-src company.com/24 \e"
+.br
+.B " \-\-dst www.ietf.org/32 \-\-said %passthrough4"
+.LP
+deletes an
+.BR eroute
+on a Security Gateway that allowed traffic between the subnet
+.BR company.com
+with
+.BR 24
+bits of subnet mask and the host
+.BR www.ietf.org
+to pass in the clear, unprocessed.
+.LP
+.B "ipsec eroute \-\-add \-\-eraf inet \-\-src company.com/24 \e"
+.br
+.B " \-\-dst mail.ngo.org/32 \-\-transport-proto 6 \e"
+.br
+.B " \-\-dst\-port 110 \-\-said tun.135@mail.ngo.org"
+.LP
+sets up an
+.BR eroute
+on on a Security Gateway to protect only TCP traffic on port 110
+(pop3) between the subnet
+.BR company.com
+with
+.BR 24
+bits of subnet mask and the host
+.BR ftp.ngo.org
+via Security Gateway
+.BR mail.ngo.org
+using the Security Association with Security Association ID
+.BR tun0x135@mail.ngo.org.
+Note that any other traffic bound for
+.BR mail.ngo.org
+that is routed via the ipsec device will be dropped. If you wish to
+allow other traffic to pass through then you must add a %pass rule.
+For example the following rule when combined with the above will
+ensure that POP3 messages read from
+.BR mail.ngo.org
+will be encrypted but all other traffic to/from
+.BR mail.ngo.org
+will be in clear text.
+.LP
+.B "ipsec eroute \-\-add \-\-eraf inet \-\-src company.com/24 \e"
+.br
+.B " \-\-dst mail.ngo.org/32 \-\-said %pass"
+.br
+.LP
+.SH FILES
+/proc/net/ipsec_eroute, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(8), ipsec_spi(8),
+ipsec_spigrp(8), ipsec_klipsdebug(8), ipsec_eroute(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: eroute.8,v $
+.\" Revision 1.1 2004/03/15 20:35:27 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.25 2002/04/24 07:35:38 mcr
+.\" Moved from ./klips/utils/eroute.8,v
+.\"
+.\" Revision 1.24 2001/02/26 19:58:49 rgb
+.\" Added a comment on the restriction of spi > 0x100.
+.\" Implement magic SAs %drop, %reject, %trap, %hold, %pass as part
+.\" of the new SPD and to support opportunistic.
+.\"
+.\" Revision 1.23 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.22 2000/09/13 15:54:31 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.21 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.20 2000/06/21 16:54:57 rgb
+.\" Added 'no additional args' text for listing contents of
+.\" /proc/net/ipsec_* files.
+.\"
+.\" Revision 1.19 1999/07/19 18:47:24 henry
+.\" fix slightly-misformed comments
+.\"
+.\" Revision 1.18 1999/04/06 04:54:37 rgb
+.\" Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+.\" patch shell fixes.
+.\"
+.\"
diff --git a/programs/eroute/eroute.c b/programs/eroute/eroute.c
new file mode 100644
index 000000000..d1b2bff0a
--- /dev/null
+++ b/programs/eroute/eroute.c
@@ -0,0 +1,1044 @@
+/*
+ * manipulate eroutes
+ * Copyright (C) 1996 John Ioannidis.
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char eroute_c_version[] = "RCSID $Id: eroute.c,v 1.3 2005/02/24 20:03:46 as Exp $";
+
+
+#include <sys/types.h>
+#include <linux/types.h> /* new */
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h> /* system(), strtoul() */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netdb.h>
+
+
+#include <unistd.h>
+#include <freeswan.h>
+#if 0
+#include <linux/autoconf.h> /* CONFIG_IPSEC_PFKEYv2 */
+#endif
+/* permanently turn it on since netlink support has been disabled */
+
+#include <signal.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#include "freeswan/radij.h"
+#include "freeswan/ipsec_encap.h"
+
+#include <stdio.h>
+#include <getopt.h>
+
+char *program_name;
+char me[] = "ipsec eroute";
+extern char *optarg;
+extern int optind, opterr, optopt;
+char *eroute_af_opt, *said_af_opt, *edst_opt, *spi_opt, *proto_opt, *said_opt, *dst_opt, *src_opt;
+char *transport_proto_opt, *src_port_opt, *dst_port_opt;
+int action_type = 0;
+
+int pfkey_sock;
+fd_set pfkey_socks;
+uint32_t pfkey_seq = 0;
+
+#define EMT_IFADDR 1 /* set enc if addr */
+#define EMT_SETSPI 2 /* Set SPI properties */
+#define EMT_DELSPI 3 /* Delete an SPI */
+#define EMT_GRPSPIS 4 /* Group SPIs (output order) */
+#define EMT_SETEROUTE 5 /* set an extended route */
+#define EMT_DELEROUTE 6 /* del an extended route */
+#define EMT_TESTROUTE 7 /* try to find route, print to console */
+#define EMT_SETDEBUG 8 /* set debug level if active */
+#define EMT_UNGRPSPIS 9 /* UnGroup SPIs (output order) */
+#define EMT_CLREROUTE 10 /* clear the extended route table */
+#define EMT_CLRSPIS 11 /* clear the spi table */
+#define EMT_REPLACEROUTE 12 /* set an extended route */
+#define EMT_GETDEBUG 13 /* get debug level if active */
+#define EMT_INEROUTE 14 /* set incoming policy for IPIP on a chain */
+
+static void
+add_port(int af, ip_address * addr, short port)
+{
+ switch (af)
+ {
+ case AF_INET:
+ addr->u.v4.sin_port = port;
+ break;
+ case AF_INET6:
+ addr->u.v6.sin6_port = port;
+ break;
+ }
+}
+
+static void
+usage(char* arg)
+{
+ fprintf(stdout, "usage: %s --{add,addin,replace} --eraf <inet | inet6> --src <src>/<srcmaskbits>|<srcmask> --dst <dst>/<dstmaskbits>|<dstmask> [ --transport-proto <protocol> ] [ --src-port <source-port> ] [ --dst-port <dest-port> ] <SA>\n", arg);
+ fprintf(stdout, " where <SA> is '--af <inet | inet6> --edst <edst> --spi <spi> --proto <proto>'\n");
+ fprintf(stdout, " OR '--said <said>'\n");
+ fprintf(stdout, " OR '--said <%%passthrough | %%passthrough4 | %%passthrough6 | %%drop | %%reject | %%trap | %%hold | %%pass>'.\n");
+ fprintf(stdout, " %s --del --eraf <inet | inet6>--src <src>/<srcmaskbits>|<srcmask> --dst <dst>/<dstmaskbits>|<dstmask> [ --transport-proto <protocol> ] [ --src-port <source-port> ] [ --dst-port <dest-port> ]\n", arg);
+ fprintf(stdout, " %s --clear\n", arg);
+ fprintf(stdout, " %s --help\n", arg);
+ fprintf(stdout, " %s --version\n", arg);
+ fprintf(stdout, " %s\n", arg);
+ fprintf(stdout, " [ --debug ] is optional to any %s command.\n", arg);
+ fprintf(stdout, " [ --label <label> ] is optional to any %s command.\n", arg);
+ exit(1);
+}
+
+static struct option const longopts[] =
+{
+ {"dst", 1, 0, 'D'},
+ {"src", 1, 0, 'S'},
+ {"eraf", 1, 0, 'f'},
+ {"add", 0, 0, 'a'},
+ {"addin", 0, 0, 'A'},
+ {"replace", 0, 0, 'r'},
+ {"clear", 0, 0, 'c'},
+ {"del", 0, 0, 'd'},
+ {"af", 1, 0, 'i'},
+ {"edst", 1, 0, 'e'},
+ {"proto", 1, 0, 'p'},
+ {"transport-proto", 1, 0, 'P'},
+ {"src-port", 1, 0, 'Q'},
+ {"dst-port", 1, 0, 'R'},
+ {"help", 0, 0, 'h'},
+ {"spi", 1, 0, 's'},
+ {"said", 1, 0, 'I'},
+ {"version", 0, 0, 'v'},
+ {"label", 1, 0, 'l'},
+ {"optionsfrom", 1, 0, '+'},
+ {"debug", 0, 0, 'g'},
+ {0, 0, 0, 0}
+};
+
+int
+main(int argc, char **argv)
+{
+ /* int fd; */
+ char *endptr;
+ /* int ret; */
+ int c, previous = -1;
+ const char* error_s;
+ int debug = 0;
+
+ int error = 0;
+
+ char ipaddr_txt[ADDRTOT_BUF];
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+ ip_address pfkey_address_s_ska;
+ /*struct sockaddr_in pfkey_address_d_ska;*/
+ ip_address pfkey_address_sflow_ska;
+ ip_address pfkey_address_dflow_ska;
+ ip_address pfkey_address_smask_ska;
+ ip_address pfkey_address_dmask_ska;
+
+ int transport_proto = 0;
+ int src_port = 0;
+ int dst_port = 0;
+ ip_said said;
+ ip_subnet s_subnet, d_subnet;
+ int eroute_af = 0;
+ int said_af = 0;
+
+ int argcount = argc;
+
+ const char permitted_options[] =
+ "%s: Only one of '--add', '--addin', '--replace', '--clear', or '--del' options permitted.\n";
+
+ program_name = argv[0];
+ eroute_af_opt = said_af_opt = edst_opt = spi_opt = proto_opt = said_opt = dst_opt = src_opt = NULL;
+
+ while((c = getopt_long(argc, argv, ""/*"acdD:e:i:hprs:S:f:vl:+:g"*/, longopts, 0)) != EOF)
+ {
+ switch(c)
+ {
+ case 'g':
+ debug = 1;
+ pfkey_lib_debug = PF_KEY_DEBUG_PARSE_MAX;
+ argcount--;
+ break;
+ case 'a':
+ if (action_type)
+ {
+ fprintf(stderr, permitted_options, program_name);
+ exit(1);
+ }
+ action_type = EMT_SETEROUTE;
+ break;
+ case 'A':
+ if (action_type)
+ {
+ fprintf(stderr, permitted_options, program_name);
+ exit(1);
+ }
+ action_type = EMT_INEROUTE;
+ break;
+ case 'r':
+ if (action_type)
+ {
+ fprintf(stderr, permitted_options, program_name);
+ exit(1);
+ }
+ action_type = EMT_REPLACEROUTE;
+ break;
+ case 'c':
+ if (action_type)
+ {
+ fprintf(stderr, permitted_options, program_name);
+ exit(1);
+ }
+ action_type = EMT_CLREROUTE;
+ break;
+ case 'd':
+ if (action_type)
+ {
+ fprintf(stderr, permitted_options, program_name);
+ exit(1);
+ }
+ action_type = EMT_DELEROUTE;
+ break;
+ case 'e':
+ if (said_opt)
+ {
+ fprintf(stderr, "%s: Error, EDST parameter redefined:%s, already defined in SA:%s\n"
+ , program_name, optarg, said_opt);
+ exit (1);
+ }
+ if (edst_opt)
+ {
+ fprintf(stderr, "%s: Error, EDST parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, edst_opt);
+ exit (1);
+ }
+ error_s = ttoaddr(optarg, 0, said_af, &said.dst);
+ if (error_s != NULL)
+ {
+ fprintf(stderr, "%s: Error, %s converting --edst argument:%s\n"
+ , program_name, error_s, optarg);
+ exit (1);
+ }
+ edst_opt = optarg;
+ break;
+ case 'h':
+ case '?':
+ usage(program_name);
+ exit(1);
+ case 's':
+ if (said_opt)
+ {
+ fprintf(stderr, "%s: Error, SPI parameter redefined:%s, already defined in SA:%s\n"
+ , program_name, optarg, said_opt);
+ exit (1);
+ }
+ if (spi_opt)
+ {
+ fprintf(stderr, "%s: Error, SPI parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, spi_opt);
+ exit (1);
+ }
+ said.spi = htonl(strtoul(optarg, &endptr, 0));
+ if (!(endptr == optarg + strlen(optarg)))
+ {
+ fprintf(stderr, "%s: Invalid character in SPI parameter: %s\n"
+ , program_name, optarg);
+ exit (1);
+ }
+ if (ntohl(said.spi) < 0x100)
+ {
+ fprintf(stderr, "%s: Illegal reserved spi: %s => 0x%x Must be larger than 0x100.\n"
+ , program_name, optarg, ntohl(said.spi));
+ exit(1);
+ }
+ spi_opt = optarg;
+ break;
+ case 'p':
+ if (said_opt)
+ {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined:%s, already defined in SA:%s\n"
+ , program_name, optarg, said_opt);
+ exit (1);
+ }
+ if (proto_opt)
+ {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, proto_opt);
+ exit (1);
+ }
+#if 0
+ if (said.proto)
+ {
+ fprintf(stderr, "%s: Warning, PROTO parameter redefined:%s\n"
+ , program_name, optarg);
+ exit (1);
+ }
+#endif
+ if (!strcmp(optarg, "ah"))
+ said.proto = SA_AH;
+ if (!strcmp(optarg, "esp"))
+ said.proto = SA_ESP;
+ if (!strcmp(optarg, "tun"))
+ said.proto = SA_IPIP;
+ if (!strcmp(optarg, "comp"))
+ said.proto = SA_COMP;
+ if (said.proto == 0)
+ {
+ fprintf(stderr, "%s: Invalid PROTO parameter: %s\n"
+ , program_name, optarg);
+ exit (1);
+ }
+ proto_opt = optarg;
+ break;
+ case 'I':
+ if (said_opt)
+ {
+ fprintf(stderr, "%s: Error, SAID parameter redefined:%s, already defined in SA:%s\n"
+ , program_name, optarg, said_opt);
+ exit (1);
+ }
+ if (proto_opt)
+ {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined in SA:%s, already defined as:%s\n"
+ , program_name, optarg, proto_opt);
+ exit (1);
+ }
+ if (edst_opt)
+ {
+ fprintf(stderr, "%s: Error, EDST parameter redefined in SA:%s, already defined as:%s\n"
+ , program_name, optarg, edst_opt);
+ exit (1);
+ }
+ if (spi_opt)
+ {
+ fprintf(stderr, "%s: Error, SPI parameter redefined in SA:%s, already defined as:%s\n"
+ , program_name, optarg, spi_opt);
+ exit (1);
+ }
+ if (said_af_opt)
+ {
+ fprintf(stderr, "%s: Error, address family parameter redefined in SA:%s, already defined as:%s\n"
+ , program_name, optarg, said_af_opt);
+ exit (1);
+ }
+ error_s = ttosa(optarg, 0, &said);
+ if (error_s != NULL)
+ {
+ fprintf(stderr, "%s: Error, %s converting --sa argument:%s\n"
+ , program_name, error_s, optarg);
+ exit (1);
+ }
+ else if (ntohl(said.spi) < 0x100)
+ {
+ fprintf(stderr, "%s: Illegal reserved spi: %s => 0x%x Must be larger than or equal to 0x100.\n"
+ , program_name, optarg, said.spi);
+ exit(1);
+ }
+ said_af = addrtypeof(&said.dst);
+ said_opt = optarg;
+ break;
+ case 'v':
+ fprintf(stdout, "%s %s\n", me, ipsec_version_code());
+ fprintf(stdout, "See `ipsec --copyright' for copyright information.\n");
+ exit(1);
+ case 'D':
+ if (dst_opt)
+ {
+ fprintf(stderr, "%s: Error, --dst parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, dst_opt);
+ exit (1);
+ }
+ error_s = ttosubnet(optarg, 0, eroute_af, &d_subnet);
+ if (error_s != NULL)
+ {
+ fprintf(stderr, "%s: Error, %s converting --dst argument: %s\n"
+ , program_name, error_s, optarg);
+ exit (1);
+ }
+ dst_opt = optarg;
+ break;
+ case 'S':
+ if (src_opt)
+ {
+ fprintf(stderr, "%s: Error, --src parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, src_opt);
+ exit (1);
+ }
+ error_s = ttosubnet(optarg, 0, eroute_af, &s_subnet);
+ if (error_s != NULL)
+ {
+ fprintf(stderr, "%s: Error, %s converting --src argument: %s\n"
+ , program_name, error_s, optarg);
+ exit (1);
+ }
+ src_opt = optarg;
+ break;
+ case 'P':
+ if (transport_proto_opt)
+ {
+ fprintf(stderr, "%s: Error, --transport-proto parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, transport_proto_opt);
+ exit(1);
+ }
+ transport_proto_opt = optarg;
+ break;
+ case 'Q':
+ if (src_port_opt)
+ {
+ fprintf(stderr, "%s: Error, --src-port parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, src_port_opt);
+ exit(1);
+ }
+ src_port_opt = optarg;
+ break;
+ case 'R':
+ if (dst_port_opt)
+ {
+ fprintf(stderr, "%s: Error, --dst-port parameter redefined:%s, already defined as:%s\n"
+ , program_name, optarg, dst_port_opt);
+ exit(1);
+ }
+ dst_port_opt = optarg;
+ break;
+ case 'l':
+ program_name = malloc(strlen(argv[0])
+ + 10 /* update this when changing the sprintf() */
+ + strlen(optarg));
+ sprintf(program_name, "%s --label %s", argv[0], optarg);
+ argcount -= 2;
+ break;
+ case 'i': /* specifies the address family of the SAID, stored in said_af */
+ if (said_af_opt)
+ {
+ fprintf(stderr, "%s: Error, address family of SAID redefined:%s, already defined as:%s\n"
+ , program_name, optarg, said_af_opt);
+ exit (1);
+ }
+ if (!strcmp(optarg, "inet"))
+ said_af = AF_INET;
+ if (!strcmp(optarg, "inet6"))
+ said_af = AF_INET6;
+ if (said_af == 0)
+ {
+ fprintf(stderr, "%s: Invalid address family parameter for SAID: %s\n"
+ , program_name, optarg);
+ exit (1);
+ }
+ said_af_opt = optarg;
+ break;
+ case 'f': /* specifies the address family of the eroute, stored in eroute_af */
+ if (eroute_af_opt)
+ {
+ fprintf(stderr, "%s: Error, address family of eroute redefined:%s, already defined as:%s\n"
+ , program_name, optarg, eroute_af_opt);
+ exit (1);
+ }
+ if (!strcmp(optarg, "inet"))
+ eroute_af = AF_INET;
+ if (!strcmp(optarg, "inet6"))
+ eroute_af = AF_INET6;
+ if (eroute_af == 0)
+ {
+ fprintf(stderr, "%s: Invalid address family parameter for eroute: %s\n"
+ , program_name, optarg);
+ exit (1);
+ }
+ eroute_af_opt = optarg;
+ break;
+ case '+': /* optionsfrom */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* no return on error */
+ break;
+ default:
+ break;
+ }
+ previous = c;
+ }
+
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: argc=%d\n", program_name, argc);
+ }
+
+ if (argcount == 1)
+ {
+ system("cat /proc/net/ipsec_eroute");
+ exit(0);
+ }
+
+ /* Sanity checks */
+
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: action_type=%d\n", program_name, action_type);
+ }
+
+ if (transport_proto_opt != 0)
+ {
+ struct protoent * proto = getprotobyname(transport_proto_opt);
+
+ if (proto != 0)
+ {
+ transport_proto = proto->p_proto;
+ }
+ else
+ {
+ transport_proto = strtoul(transport_proto_opt, &endptr, 0);
+
+ if ((*endptr != '\0')
+ || (transport_proto == 0 && endptr == transport_proto_opt))
+ {
+ fprintf(stderr, "%s: Invalid character in --transport-proto parameter: %s\n"
+ , program_name, transport_proto_opt);
+ exit (1);
+ }
+ if (transport_proto > 255)
+ {
+ fprintf(stderr, "%s: --transport-proto parameter: %s must be in the range 0 to 255 inclusive\n"
+ , program_name, transport_proto_opt);
+ exit (1);
+ }
+ }
+ }
+
+ if (src_port_opt != 0 || dst_port_opt != 0)
+ {
+ switch (transport_proto)
+ {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ break;
+ default:
+ fprintf(stderr, "%s: --transport-proto with either UDP or TCP must be specified if --src-port or --dst-port is used\n"
+ , program_name);
+ exit(1);
+ }
+ }
+
+ if (src_port_opt)
+ {
+ struct servent * ent = getservbyname(src_port_opt, 0);
+
+ if (ent != 0)
+ {
+ src_port = ent->s_port;
+ }
+ else
+ {
+ src_port = strtoul(src_port_opt, &endptr, 0);
+
+ if ((*endptr != '\0')
+ || (src_port == 0 && endptr == src_port_opt))
+ {
+ fprintf(stderr, "%s: Invalid character in --src-port parameter: %s\n"
+ , program_name, src_port_opt);
+ exit (1);
+ }
+ if (src_port > 65535)
+ {
+ fprintf(stderr, "%s: --src-port parameter: %s must be in the range 0 to 65535 inclusive\n"
+ , program_name, src_port_opt);
+ }
+ src_port = htons(src_port);
+ }
+ }
+
+ if (dst_port_opt)
+ {
+ struct servent * ent = getservbyname(dst_port_opt, 0);
+
+ if (ent != 0)
+ {
+ dst_port = ent->s_port;
+ }
+ else
+ {
+ dst_port = strtoul(dst_port_opt, &endptr, 0);
+
+ if ((*endptr != '\0')
+ || (dst_port == 0 && endptr == dst_port_opt))
+ {
+ fprintf(stderr, "%s: Invalid character in --dst-port parameter: %s\n"
+ , program_name, dst_port_opt);
+ exit (1);
+ }
+ if (dst_port > 65535)
+ {
+ fprintf(stderr, "%s: --dst-port parameter: %s must be in the range 0 to 65535 inclusive\n"
+ , program_name, dst_port_opt);
+ }
+ dst_port = htons(dst_port);
+ }
+ }
+
+ switch(action_type)
+ {
+ case EMT_SETEROUTE:
+ case EMT_REPLACEROUTE:
+ case EMT_INEROUTE:
+ if (!(said_af_opt && edst_opt && spi_opt && proto_opt) && !(said_opt))
+ {
+ fprintf(stderr, "%s: add and addin options must have SA specified.\n"
+ , program_name);
+ exit(1);
+ }
+ case EMT_DELEROUTE:
+ if (!src_opt)
+ {
+ fprintf(stderr, "%s: Error -- %s option '--src' is required.\n"
+ , program_name, (action_type == EMT_SETEROUTE) ? "add" : "del");
+ exit(1);
+ }
+ if (!dst_opt)
+ {
+ fprintf(stderr, "%s: Error -- %s option '--dst' is required.\n"
+ , program_name, (action_type == EMT_SETEROUTE) ? "add" : "del");
+ exit(1);
+ }
+ case EMT_CLREROUTE:
+ break;
+ default:
+ fprintf(stderr, "%s: exactly one of '--add', '--addin', '--replace', '--del' or '--clear' options must be specified.\n"
+ "Try %s --help' for usage information.\n"
+ , program_name, program_name);
+ exit(1);
+ }
+
+ if ((pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0)
+ {
+ fprintf(stderr, "%s: Trouble opening PF_KEY family socket with error: "
+ , program_name);
+ switch(errno)
+ {
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if (getuid() == 0)
+ {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ }
+ else
+ {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "KLIPS not loaded.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. "
+ "Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, KLIPS not loaded or check kernel log messages for specifics.\n");
+ break;
+ case ENOBUFS:
+ case ENOMEM:
+ case ENFILE:
+ fprintf(stderr, "No kernel memory to allocate socket.\n");
+ break;
+ case EMFILE:
+ fprintf(stderr, "Process file table overflow.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Socket type not supported.\n");
+ break;
+ case EPROTONOSUPPORT:
+ fprintf(stderr, "Protocol version not supported.\n");
+ break;
+ case EAFNOSUPPORT:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown file open error %d. Please report as much detail as possible to development team.\n"
+ , errno);
+ }
+ exit(1);
+ }
+
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: PFKEYv2 socket successfully openned=%d.\n"
+ , program_name, pfkey_sock);
+ }
+
+ /* Build an SADB_X_ADDFLOW or SADB_X_DELFLOW message to send down. */
+ /* It needs <base, SA, address(SD), flow(SD), mask(SD)> minimum. */
+ pfkey_extensions_init(extensions);
+
+ error = pfkey_msg_hdr_build(&extensions[0]
+ , (action_type == EMT_SETEROUTE || action_type == EMT_REPLACEROUTE
+ || action_type == EMT_INEROUTE)? SADB_X_ADDFLOW : SADB_X_DELFLOW
+ , proto2satype(said.proto)
+ , 0
+ , ++pfkey_seq
+ , getpid()
+ );
+
+ if (error)
+ {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n"
+ , program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_msg_hdr_build successfull.\n"
+ , program_name);
+ }
+
+ switch (action_type)
+ {
+ case EMT_SETEROUTE:
+ case EMT_REPLACEROUTE:
+ case EMT_INEROUTE:
+ case EMT_CLREROUTE:
+ error = pfkey_sa_build(&extensions[SADB_EXT_SA]
+ , SADB_EXT_SA
+ , said.spi /* in network order */
+ , 0
+ , 0
+ , 0
+ , 0
+ , (action_type == EMT_CLREROUTE) ? SADB_X_SAFLAGS_CLEARFLOW : 0
+ );
+
+ if (error)
+ {
+ fprintf(stderr, "%s: Trouble building sa extension, error=%d.\n"
+ , program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_sa_build successful.\n"
+ , program_name);
+ }
+ default:
+ break;
+ }
+
+ switch (action_type)
+ {
+ case EMT_SETEROUTE:
+ case EMT_REPLACEROUTE:
+ case EMT_INEROUTE:
+ anyaddr(said_af, &pfkey_address_s_ska);
+ error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC]
+ , SADB_EXT_ADDRESS_SRC
+ , 0
+ , 0
+ , sockaddrof(&pfkey_address_s_ska)
+ );
+ if (error)
+ {
+ addrtot(&pfkey_address_s_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_s extension (%s), error=%d.\n"
+ , program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for src.\n"
+ , program_name);
+ }
+
+ error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST]
+ , SADB_EXT_ADDRESS_DST
+ , 0
+ , 0
+ , sockaddrof(&said.dst)
+ );
+
+ if (error)
+ {
+ addrtot(&said.dst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_d extension (%s), error=%d.\n"
+ , program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for dst.\n"
+ , program_name);
+ }
+ default:
+ break;
+ }
+
+ switch (action_type)
+ {
+ case EMT_SETEROUTE:
+ case EMT_REPLACEROUTE:
+ case EMT_INEROUTE:
+ case EMT_DELEROUTE:
+ networkof(&s_subnet, &pfkey_address_sflow_ska); /* src flow */
+ add_port(eroute_af, &pfkey_address_sflow_ska, src_port);
+
+ error = pfkey_address_build(&extensions[SADB_X_EXT_ADDRESS_SRC_FLOW]
+ , SADB_X_EXT_ADDRESS_SRC_FLOW
+ , 0
+ , 0
+ , sockaddrof(&pfkey_address_sflow_ska)
+ );
+
+ if (error)
+ {
+ addrtot(&pfkey_address_sflow_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_sflow extension (%s), error=%d.\n",
+ program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for src flow.\n"
+ , program_name);
+ }
+
+ networkof(&d_subnet, &pfkey_address_dflow_ska); /* dst flow */
+ add_port(eroute_af, &pfkey_address_dflow_ska, dst_port);
+
+ error = pfkey_address_build(&extensions[SADB_X_EXT_ADDRESS_DST_FLOW]
+ , SADB_X_EXT_ADDRESS_DST_FLOW
+ , 0
+ , 0
+ , sockaddrof(&pfkey_address_dflow_ska)
+ );
+
+ if (error)
+ {
+ addrtot(&pfkey_address_dflow_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_dflow extension (%s), error=%d.\n"
+ , program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for dst flow.\n"
+ , program_name);
+ }
+
+ maskof(&s_subnet, &pfkey_address_smask_ska); /* src mask */
+ add_port(eroute_af, &pfkey_address_smask_ska, src_port ? ~0:0);
+
+ error = pfkey_address_build(&extensions[SADB_X_EXT_ADDRESS_SRC_MASK]
+ , SADB_X_EXT_ADDRESS_SRC_MASK
+ , 0
+ , 0
+ , sockaddrof(&pfkey_address_smask_ska)
+ );
+
+ if (error)
+ {
+ addrtot(&pfkey_address_smask_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_smask extension (%s), error=%d.\n"
+ , program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for src mask.\n"
+ , program_name);
+ }
+
+ maskof(&d_subnet, &pfkey_address_dmask_ska); /* dst mask */
+ add_port(eroute_af, &pfkey_address_dmask_ska, dst_port ? ~0:0);
+
+ error = pfkey_address_build(&extensions[SADB_X_EXT_ADDRESS_DST_MASK]
+ , SADB_X_EXT_ADDRESS_DST_MASK
+ , 0
+ , 0
+ , sockaddrof(&pfkey_address_dmask_ska)
+ );
+
+ if (error)
+ {
+ addrtot(&pfkey_address_dmask_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_dmask extension (%s), error=%d.\n"
+ , program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_address_build successful for dst mask.\n"
+ , program_name);
+ }
+ }
+
+ if (transport_proto != 0)
+ {
+ error = pfkey_x_protocol_build(&extensions[SADB_X_EXT_PROTOCOL]
+ , transport_proto);
+
+ if (error)
+ {
+ fprintf(stderr, "%s: Trouble building transport protocol extension, error=%d.\n"
+ , program_name, error);
+ exit(1);
+ }
+ }
+
+ error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN);
+
+ if (error)
+ {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n"
+ , program_name, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey_msg_build successful.\n"
+ , program_name);
+ }
+
+ error = write(pfkey_sock
+ , pfkey_msg
+ , pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN
+ )
+ != (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+
+ if (error)
+ {
+ fprintf(stderr, "%s: pfkey write failed, returning %d with errno=%d.\n"
+ , program_name, error, errno);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+
+ switch (errno)
+ {
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENXIO:
+ if (action_type == EMT_SETEROUTE || action_type == EMT_REPLACEROUTE)
+ {
+ fprintf(stderr, "Invalid mask.\n");
+ break;
+ }
+ if (action_type == EMT_DELEROUTE)
+ {
+ fprintf(stderr, "Mask not found.\n");
+ break;
+ }
+ case EFAULT:
+ if (action_type == EMT_SETEROUTE || action_type == EMT_REPLACEROUTE)
+ {
+ fprintf(stderr, "Invalid address.\n");
+ break;
+ }
+ if (action_type == EMT_DELEROUTE)
+ {
+ fprintf(stderr, "Address not found.\n");
+ break;
+ }
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if (getuid() == 0)
+ {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ }
+ else
+ {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "KLIPS not loaded.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. "
+ "Please report as much detail as possible to development team.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ fprintf(stderr, "No device?!?\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "eroute already in use. Delete old one first.\n");
+ break;
+ case ENOENT:
+ if (action_type == EMT_INEROUTE)
+ {
+ fprintf(stderr, "non-existant IPIP SA.\n");
+ break;
+ }
+ fprintf(stderr, "eroute doesn't exist. Can't delete.\n");
+ break;
+ case ENOSPC:
+ fprintf(stderr, "no room in kernel SAref table. Cannot process request.\n");
+ break;
+ case ESPIPE:
+ fprintf(stderr, "kernel SAref table internal error. Cannot process request.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket write error %d. Please report as much detail as possible to development team.\n"
+ , errno);
+ }
+/* fprintf(stderr, "%s: socket write returned errno %d\n",
+ program_name, errno);*/
+ exit(1);
+ }
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: pfkey write successful.\n"
+ , program_name);
+ }
+
+ if (pfkey_msg)
+ {
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ }
+
+ (void) close(pfkey_sock); /* close the socket */
+
+ if (debug)
+ {
+ fprintf(stdout, "%s: DEBUG: write ok\n", program_name);
+ }
+
+ exit(0);
+}
diff --git a/programs/examples/Makefile b/programs/examples/Makefile
new file mode 100644
index 000000000..114008a73
--- /dev/null
+++ b/programs/examples/Makefile
@@ -0,0 +1,22 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/08/28 11:25:09 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+CONFDSUBDIR=examples
+CONFDFILES=oe.conf
+
+include ../Makefile.program
diff --git a/programs/examples/oe.conf.in b/programs/examples/oe.conf.in
new file mode 100644
index 000000000..4eff4d0dd
--- /dev/null
+++ b/programs/examples/oe.conf.in
@@ -0,0 +1,68 @@
+# defines default policy groups for Opportunistic Encryption (OE)
+#
+# RCSID $Id: oe.conf.in,v 1.1 2004/08/28 11:25:09 as Exp $
+
+conn packetdefault
+ type=tunnel
+ leftsubnet=0.0.0.0/0
+ right=%opportunistic
+ failureshunt=passthrough
+ keyingtries=3
+ ikelifetime=1h
+ keylife=1h
+ rekey=no
+ also=oe_defaults
+ auto=route
+
+conn clear
+ type=passthrough
+ authby=never
+ right=%group
+ also=oe_defaults
+ auto=route
+
+conn clear-or-private
+ type=passthrough
+ right=%opportunisticgroup
+ failureshunt=passthrough
+ keyingtries=3
+ ikelifetime=1h
+ keylife=1h
+ rekey=no
+ also=oe_defaults
+ auto=route
+
+conn private-or-clear
+ type=tunnel
+ right=%opportunisticgroup
+ failureshunt=passthrough
+ keyingtries=3
+ ikelifetime=1h
+ keylife=1h
+ rekey=no
+ also=oe_defaults
+ auto=route
+
+conn private
+ type=tunnel
+ right=%opportunisticgroup
+ failureshunt=drop
+ keyingtries=3
+ ikelifetime=1h
+ keylife=1h
+ rekey=no
+ also=oe_defaults
+ auto=route
+
+conn block
+ type=reject
+ authby=never
+ right=%group
+ also=oe_defaults
+ auto=route
+
+conn oe_defaults
+ left=%defaultroute
+ leftid=%myid
+ leftrsasigkey=%dnsondemand
+ rightrsasigkey=%dnsondemand
diff --git a/programs/ikeping/.cvsignore b/programs/ikeping/.cvsignore
new file mode 100644
index 000000000..755295a5f
--- /dev/null
+++ b/programs/ikeping/.cvsignore
@@ -0,0 +1 @@
+ikeping
diff --git a/programs/ikeping/Makefile b/programs/ikeping/Makefile
new file mode 100644
index 000000000..6c7b31d59
--- /dev/null
+++ b/programs/ikeping/Makefile
@@ -0,0 +1,57 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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=ikeping
+LIBS=${FREESWANLIB}
+
+ifeq ($(USE_IKEPING),false)
+NOINSTALL=true
+install:
+ # do nothing
+
+install_file_list:
+ # do nothing
+
+endif
+
+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.4 2003/06/29 21:34:49 mcr
+# added "NOINSTALL" to omit install: target from common
+# Makefile so that it can be overridden
+#
+# Revision 1.3 2003/06/25 03:57:45 mcr
+# build, but do not install "ikeping" even when we do not
+# want it as part of the system.
+#
+# 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/programs/ikeping/ikeping.8 b/programs/ikeping/ikeping.8
new file mode 100644
index 000000000..a9b80b46d
--- /dev/null
+++ b/programs/ikeping/ikeping.8
@@ -0,0 +1,71 @@
+.TH IPSEC_IKEPING 8 "23 Feb 2002"
+.\" RCSID $Id: ikeping.8,v 1.1 2004/03/15 20:35:27 as Exp $
+.SH NAME
+ipsec ikeping \- send/receive ISAKMP/IKE echo requests/replies
+.SH SYNOPSIS
+.B ipsec
+.B ikeping
+[
+.B \-\-listen
+] [
+.B \-\-verbose
+] [
+.B \-\-wait
+time ] [
+.B \-\-exchangenum
+num ] [
+.B \-\-ikeport
+localport ] [
+.B \-\-ikeaddress
+address ] [
+.B \-\-inet
+] [
+.B \-\-inet6
+] destaddr[/dstport] ...
+.SH DESCRIPTION
+.I Ikeping
+sends and receives ISAKMP/IKE echo request and echo reply packets. These
+packets are intended for diagnostics purposes, in a manner similar to
+.IR ping (8)
+does for ICMP echo request/reply packets.
+.PP
+At the time of this writing, the ISAKMP echo request/reply exchange is still
+an internet-draft, and is therefore completely non-standard.
+.PP
+.I Ikeping
+will bind to the local address given by
+.B \-\-ikeaddress
+and the port number given by
+.B \-\-ikeport
+defaulting to the wildcard address and the ISAKMP port 500. An ISAKMP
+exchange of type 244 (a private use number) is sent to each of the
+address/ports listed on the command line. The exchange number may be
+overridden by the
+.B \-\-exchangenum
+option.
+.PP
+.I Ikeping
+then listens for replies, printing them as they are received. Replies
+are of exchange type 245 or the specified exchange number plus 1.
+.I Ikeping
+will keep listening until it either receives as many echo responses as it sent,
+or until the timeout period (10 seconds) has been reached. Receipt of a
+packet will reset the timer. The
+.B \-\-wait
+option can be used to specify a different timeout period.
+.PP
+If the
+.B \-\-listen
+option is given, then
+.I ikeping
+will not send any packets. Instead, it will listen for them and reply to
+each request received.
+.SH FILES
+no external files
+.SH SEE ALSO
+ping(8), ipsec_pluto(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Michael Richardson.
+.SH BUGS
diff --git a/programs/ikeping/ikeping.c b/programs/ikeping/ikeping.c
new file mode 100644
index 000000000..7efb26ad7
--- /dev/null
+++ b/programs/ikeping/ikeping.c
@@ -0,0 +1,483 @@
+/* send out an IKE "ping" packet.
+ * Copyright (C) 2002 Michael Richardson
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: ikeping.c,v 1.1 2004/03/15 20:35:27 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <assert.h>
+#include <poll.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/packet.h"
+
+#ifndef ISAKMP_XCHG_ECHOREQUEST
+#define ISAKMP_XCHG_ECHOREQUEST 30 /* Echo Request */
+#define ISAKMP_XCHG_ECHOREPLY 31 /* Echo Reply */
+#endif
+
+#ifndef ISAKMP_XCGH_ECHOREQUEST_PRIV
+#define ISAKMP_XCHG_ECHOREQUEST_PRIV 244 /* Private Echo Request */
+#define ISAKMP_XCHG_ECHOREPLY_PRIV 245 /* Private Echo Reply */
+#endif
+
+
+/* what exchange number to use for outgoing requests */
+static int exchange_number;
+
+static void
+help(void)
+{
+ fprintf(stderr,
+ "Usage:\n\n"
+ "ikeping"
+ " [--listen] causes IKEping to open a socket and reply to requests.\n"
+ " [--verbose] causes IKEping to hexdump all packets sent/received.\n"
+ " [--ikeport <port-number>] port to listen on/send from\n"
+ " [--ikeaddress <address>] address to listen on/send from\n"
+ " [--inet] just send/listen on IPv4 socket\n"
+ " [--inet6] just send/listen on IPv6 socket\n"
+ " [--version] just dump version number and exit\n"
+ " [--exchangenum num] use num instead of 244 for the exchange type.\n"
+ " [--wait seconds] time to wait for replies, defaults to 10 seconds.\n"
+ " host/port ...\n\n"
+ "FreeS/WAN %s\n",
+ ipsec_version_code());
+}
+
+static void
+hton_ping(struct isakmp_hdr *ih)
+{
+ u_int32_t *ihp;
+
+ ihp=(u_int32_t *)ih;
+
+ /* put it in network byte order. */
+ /* cookies are byte viewed anyway */
+ ihp[4]=htonl(ihp[4]);
+ ih->isa_msgid = htonl(ih->isa_msgid);
+ ih->isa_length = htonl(ih->isa_length);
+}
+
+static void
+ntoh_ping(struct isakmp_hdr *ih)
+{
+ u_int32_t *ihp;
+
+ ihp=(u_int32_t *)ih;
+
+ /* put it in network byte order. */
+ /* cookies are byte viewed anyway */
+ ihp[4]=ntohl(ihp[4]);
+ ih->isa_msgid = ntohl(ih->isa_msgid);
+ ih->isa_length = ntohl(ih->isa_length);
+}
+
+
+/*
+ * send an IKE ping
+ *
+ */
+static void
+send_ping(int afamily,
+ int s,
+ ip_address *raddr,
+ int rport)
+{
+ struct isakmp_hdr ih;
+ int i, raddrlen;
+
+ raddrlen=0;
+
+ for(i=0; i<COOKIE_SIZE; i++) {
+ ih.isa_icookie[i]=rand()&0xff;
+ }
+
+ for(i=0; i<COOKIE_SIZE; i++) {
+ ih.isa_rcookie[i]=rand()&0xff;
+ }
+
+ ih.isa_np = NOTHING_WRONG;
+ ih.isa_version = (1 << ISA_MAJ_SHIFT) | 0;
+ ih.isa_xchg = (exchange_number ?
+ exchange_number : ISAKMP_XCHG_ECHOREQUEST_PRIV);
+ ih.isa_flags =0;
+ ih.isa_msgid =rand();
+ ih.isa_length=0;
+
+ switch(afamily) {
+ case AF_INET:
+ raddr->u.v4.sin_port = htons(rport);
+ raddrlen=sizeof(raddr->u.v4);
+ break;
+
+ case AF_INET6:
+ raddr->u.v6.sin6_port = htons(rport);
+ raddrlen=sizeof(raddr->u.v6);
+ break;
+ }
+
+ hton_ping(&ih);
+
+ if(sendto(s, &ih, sizeof(ih), 0, (struct sockaddr *)raddr, raddrlen) < 0) {
+ perror("sendto");
+ exit(5);
+ }
+}
+
+/*
+ * send an IKE ping
+ *
+ */
+static void
+reply_packet(int afamily,
+ int s,
+ ip_address *dst_addr,
+ int dst_len,
+ struct isakmp_hdr *op)
+{
+ int i, tmp;
+
+ tmp=afamily; /* shut up compiler */
+
+ for(i=0; i<COOKIE_SIZE; i++) {
+ tmp=op->isa_icookie[i];
+ op->isa_icookie[i]=op->isa_rcookie[i];
+ op->isa_rcookie[i]=tmp;
+ }
+
+ op->isa_np = NOTHING_WRONG;
+ op->isa_version = (1 << ISA_MAJ_SHIFT) | 0;
+ op->isa_xchg = ISAKMP_XCHG_ECHOREPLY;
+ op->isa_flags =0;
+ op->isa_msgid =rand();
+ op->isa_length=0;
+
+ hton_ping(op);
+
+ if(sendto(s, op, sizeof(*op), 0, (struct sockaddr *)dst_addr, dst_len) < 0) {
+ perror("sendto");
+ exit(5);
+ }
+}
+
+/*
+ * receive and decode packet.
+ *
+ */
+static void
+receive_ping(int afamily, int s, int reply)
+{
+ ip_address sender;
+ struct isakmp_hdr ih;
+ char buf[64];
+ int n, rport, sendlen;
+ const char *xchg_name;
+ int xchg;
+
+ rport = 500;
+ xchg = 0;
+ sendlen=sizeof(sender);
+ n = recvfrom(s, &ih, sizeof(ih), 0, (struct sockaddr *)&sender, &sendlen);
+
+ addrtot(&sender, 0, buf, sizeof(buf));
+ switch(afamily) {
+ case AF_INET:
+ rport = sender.u.v4.sin_port;
+ break;
+
+ case AF_INET6:
+ rport = sender.u.v6.sin6_port;
+ break;
+ }
+
+ if((unsigned int)n < sizeof(ih)) {
+ fprintf(stderr, "read short packet (%d) from %s/%d\n",
+ n, buf, rport);
+ return;
+ }
+
+ /* translate from network byte order */
+ ntoh_ping(&ih);
+
+
+ if(ih.isa_xchg == ISAKMP_XCHG_ECHOREQUEST ||
+ ih.isa_xchg == ISAKMP_XCHG_ECHOREQUEST_PRIV ||
+ (exchange_number!=0 && ih.isa_xchg == exchange_number)) {
+ xchg_name="echo-request";
+ xchg=ISAKMP_XCHG_ECHOREQUEST;
+ } else if(ih.isa_xchg == ISAKMP_XCHG_ECHOREPLY ||
+ ih.isa_xchg == ISAKMP_XCHG_ECHOREPLY_PRIV ||
+ (exchange_number!=0 && ih.isa_xchg == exchange_number+1)) {
+ xchg_name="echo-reply";
+ } else {
+ xchg_name="";
+ }
+
+ printf("received %d(%s) packet from %s/%d of len: %d\n",
+ ih.isa_xchg, xchg_name, buf, ntohs(rport), n);
+ printf("\trcookie=%08x_%08x icookie=%08x_%08x msgid=%08x\n",
+ *(u_int32_t *)(ih.isa_icookie),
+ *(u_int32_t *)(ih.isa_icookie+4),
+ *(u_int32_t *)(ih.isa_rcookie),
+ *(u_int32_t *)(ih.isa_rcookie+4),
+ ih.isa_msgid);
+ printf("\tnp=%03d version=%d.%d xchg=%s(%d)\n",
+ ih.isa_np,
+ ih.isa_version >> ISA_MAJ_SHIFT,
+ ih.isa_version & ISA_MIN_MASK,
+ xchg_name,
+ ih.isa_xchg);
+
+ if(reply && xchg==ISAKMP_XCHG_ECHOREQUEST) {
+ reply_packet(afamily, s, &sender, sendlen, &ih);
+ }
+}
+
+static const struct option long_opts[] = {
+ /* name, has_arg, flag, val */
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "listen", no_argument, NULL, 's' },
+ { "ikeport", required_argument, NULL, 'p' },
+ { "ikeaddress", required_argument, NULL, 'b' },
+ { "inet", no_argument, NULL, '4' },
+ { "inet6", no_argument, NULL, '6' },
+ { "exchangenum", required_argument, NULL, 'n' },
+ { "wait", required_argument, NULL, 'w' },
+ { 0,0,0,0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ char *foo;
+ const char *errstr;
+ int s;
+ int listen_only;
+ int lport,dport;
+ int afamily;
+ int pfamily;
+ int c;
+ int numSenders, numReceived, noDNS;
+ int waitTime;
+ int verbose, timedOut;
+ ip_address laddr, raddr;
+
+ afamily=AF_INET;
+ pfamily=PF_INET;
+ lport=500;
+ dport=500;
+ waitTime=10;
+ verbose=0;
+ listen_only=0;
+ noDNS=0;
+ bzero(&laddr, sizeof(laddr));
+
+ while((c = getopt_long(argc, argv, "hVnvsp:b:46E:w:", long_opts, 0))!=EOF) {
+ switch (c) {
+ case 'h': /* --help */
+ help();
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'V': /* --version */
+ fprintf(stderr, "FreeS/WAN %s\n", ipsec_version_code());
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'v': /* --label <string> */
+ verbose++;
+ continue;
+
+ case 'n':
+ noDNS=1;
+ break;
+
+ case 'E':
+ exchange_number=strtol(optarg, &foo, 0);
+ if(optarg==foo || exchange_number < 1 || exchange_number>255) {
+ fprintf(stderr, "Invalid exchange number '%s' (should be 1<=x<255)\n",
+ optarg);
+ exit(1);
+ }
+ continue;
+
+
+ case 's':
+ listen_only++;
+ continue;
+
+ case 'p':
+ lport=strtol(optarg, &foo, 0);
+ if(optarg==foo || lport <0 || lport>65535) {
+ fprintf(stderr, "Invalid port number '%s' (should be 0<=x<65536)\n",
+ optarg);
+ exit(1);
+ }
+ continue;
+
+ case 'w':
+ waitTime=strtol(optarg, &foo, 0);
+ if(optarg==foo || waitTime < 0) {
+ fprintf(stderr, "Invalid waittime number '%s' (should be 0<=x)\n",
+ optarg);
+ exit(1);
+ }
+ continue;
+
+ case 'b':
+ errstr = ttoaddr(optarg, strlen(optarg), afamily, &laddr);
+ if(errstr!=NULL) {
+ fprintf(stderr, "Invalid local address '%s': %s\n",
+ optarg, errstr);
+ exit(1);
+ }
+ continue;
+
+ case '4':
+ afamily=AF_INET;
+ pfamily=PF_INET;
+ continue;
+
+ case '6':
+ afamily=AF_INET6;
+ pfamily=PF_INET6;
+ continue;
+
+ default:
+ assert(FALSE); /* unknown return value */
+ }
+ }
+
+ s=socket(pfamily, SOCK_DGRAM, IPPROTO_UDP);
+ if(s < 0) {
+ perror("socket");
+ exit(3);
+ }
+
+ switch(afamily) {
+ case AF_INET:
+ laddr.u.v4.sin_port = htons(lport);
+ if(bind(s, (struct sockaddr *)&laddr.u.v4, sizeof(laddr.u.v4)) < 0) {
+ perror("v4 bind");
+ exit(5);
+ }
+ break;
+
+ case AF_INET6:
+ laddr.u.v6.sin6_port = htons(lport);
+ if(bind(s, (struct sockaddr *)&laddr.u.v6, sizeof(laddr.u.v6)) < 0) {
+ perror("v6 bind");
+ exit(5);
+ }
+ break;
+ }
+
+ numSenders = 0;
+
+ if(!listen_only) {
+ while(optind < argc) {
+ char *port;
+ char *host;
+ char namebuf[128];
+
+ host = argv[optind];
+
+ port = strchr(host, '/');
+ dport=500;
+ if(port) {
+ *port='\0';
+ port++;
+ dport= strtol(port, &foo, 0);
+ if(port==foo || dport < 0 || dport > 65535) {
+ fprintf(stderr, "Invalid port number '%s' "
+ "(should be 0<=x<65536)\n",
+ port);
+ exit(1);
+ }
+ }
+
+ errstr = ttoaddr(host, strlen(host),
+ afamily, &raddr);
+ if(errstr!=NULL) {
+ fprintf(stderr, "Invalid remote address '%s': %s\n",
+ host, errstr);
+ exit(1);
+ }
+
+ addrtot(&raddr, 0, namebuf, sizeof(namebuf));
+
+ printf("Sending packet to %s/%d\n", namebuf, dport);
+
+ send_ping(afamily, s, &raddr, dport);
+ numSenders++;
+ optind++;
+ }
+ }
+
+ timedOut = 0;
+ numReceived=0;
+
+ /* really should catch ^C and print stats on exit */
+ while(numSenders > 0 || listen_only) {
+ struct pollfd ready;
+ int n;
+
+ ready.fd = s;
+ ready.events = POLLIN;
+
+ n = poll(&ready, 1, waitTime);
+ if(n < 0) {
+ perror("poll");
+ exit(1);
+ }
+
+ if(n == 0 && !listen_only) {
+ break;
+ }
+
+ if(n == 1) {
+ numReceived++;
+ receive_ping(afamily, s, listen_only);
+ }
+ }
+
+ if(numReceived > 0) {
+ printf("%d packets sent, %d packets received. %d packet loss\n",
+ numSenders, numReceived, numSenders*100/numReceived);
+ }
+ exit(0);
+}
+
+/*
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 4
+ * End:
+ *
+ */
diff --git a/programs/ipsec/.cvsignore b/programs/ipsec/.cvsignore
new file mode 100644
index 000000000..70025a7f8
--- /dev/null
+++ b/programs/ipsec/.cvsignore
@@ -0,0 +1 @@
+ipsec
diff --git a/programs/ipsec/Makefile b/programs/ipsec/Makefile
new file mode 100644
index 000000000..fdff3728a
--- /dev/null
+++ b/programs/ipsec/Makefile
@@ -0,0 +1,28 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/ipsec/distro.txt b/programs/ipsec/distro.txt
new file mode 100644
index 000000000..80f4192a4
--- /dev/null
+++ b/programs/ipsec/distro.txt
@@ -0,0 +1 @@
+distributed by Andreas Steffen <andreas.steffen@strongswan.org>
diff --git a/programs/ipsec/ipsec.8 b/programs/ipsec/ipsec.8
new file mode 100644
index 000000000..823289372
--- /dev/null
+++ b/programs/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
+<http://www.freeswan.org>
+by Henry Spencer.
+Updated and extended for Linux strongSwan
+<http://www.strongswan.org>
+by Andreas Steffen.
diff --git a/programs/ipsec/ipsec.in b/programs/ipsec/ipsec.in
new file mode 100755
index 000000000..0616561d8
--- /dev/null
+++ b/programs/ipsec/ipsec.in
@@ -0,0 +1,244 @@
+#! /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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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"
+
+# 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 <http://www.strongswan.org> 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 <connectionname>"
+ echo " status|statusall [<connectionname>]"
+ 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 <value> [--inbase <base>] [--outbase <base>] [--keyid <id>]"
+ 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
+ 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"
+ 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
+ ;;
+start)
+ shift
+ exec $IPSEC_EXECDIR/starter "$@"
+ ;;
+status|statusall)
+ op="$1"
+ shift
+ if test $# -eq 0
+ then
+ $IPSEC_EXECDIR/whack "--$op"
+ else
+ $IPSEC_EXECDIR/whack --name "$1" "--$op"
+ 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
+ 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/programs/klipsdebug/.cvsignore b/programs/klipsdebug/.cvsignore
new file mode 100644
index 000000000..03c1d474c
--- /dev/null
+++ b/programs/klipsdebug/.cvsignore
@@ -0,0 +1 @@
+klipsdebug
diff --git a/programs/klipsdebug/Makefile b/programs/klipsdebug/Makefile
new file mode 100644
index 000000000..6c98e7592
--- /dev/null
+++ b/programs/klipsdebug/Makefile
@@ -0,0 +1,80 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM:=klipsdebug
+EXTRA5PROC=${PROGRAM}.5
+
+LIBS:=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 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.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
+
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 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.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
diff --git a/programs/klipsdebug/klipsdebug.5 b/programs/klipsdebug/klipsdebug.5
new file mode 100644
index 000000000..8e5f985f0
--- /dev/null
+++ b/programs/klipsdebug/klipsdebug.5
@@ -0,0 +1,138 @@
+.TH IPSEC_KLIPSDEBUG 5 "26 Jun 2000"
+.\"
+.\" RCSID $Id: klipsdebug.5,v 1.1 2004/03/15 20:35:28 as Exp $
+.\"
+.SH NAME
+ipsec_klipsdebug \- list KLIPS (kernel IPSEC support) debug features and level
+.SH SYNOPSIS
+.B ipsec
+.B klipsdebug
+.PP
+.B cat
+.B /proc/net/ipsec_klipsdebug
+.SH DESCRIPTION
+.I /proc/net/ipsec_klipsdebug
+lists flags that control various parts of the debugging output of Klips
+(the kernel portion of FreeS/WAN IPSEC).
+At this point it is a read-only file.
+.PP
+A table entry consists of:
+.IP + 3
+a KLIPS debug variable
+.IP +
+a '=' separator for visual and automated parsing between the variable
+name and its current value
+.IP +
+hexadecimal bitmap of variable's flags.
+.PP
+The variable names roughly describe the scope of the debugging variable.
+Currently, no flags are documented or individually accessible yet except
+tunnel-xmit.
+.ne 5
+.PP
+The variable names are:
+.TP 8
+.B tunnel
+tunnelling code
+.TP
+.B netlink
+userspace communication code (obsolete)
+.TP
+.B xform
+transform selection and manipulation code
+.TP
+.B eroute
+eroute table manipulation code
+.TP
+.B spi
+SA table manipulation code
+.TP
+.B radij
+radij tree manipulation code
+.TP
+.B esp
+encryptions transforms code
+.TP
+.B ah
+authentication transforms code
+.TP
+.B rcv
+receive code
+.TP
+.B ipcomp
+ip compression transforms code
+.TP
+.B verbose
+give even more information, beware this will probably trample the 4k kernel printk buffer giving inaccurate output
+.PP
+All KLIPS debug output appears as
+.B kernel.info
+messages to
+.IR syslogd (8).
+Most systems are set up
+to log these messages to
+.IR /var/log/messages .
+.PP
+.SH EXAMPLES
+.LP
+.B debug_tunnel=00000010.
+.br
+.B debug_netlink=00000000.
+.br
+.B debug_xform=00000000.
+.br
+.B debug_eroute=00000000.
+.br
+.B debug_spi=00000000.
+.br
+.B debug_radij=00000000.
+.br
+.B debug_esp=00000000.
+.br
+.B debug_ah=00000000.
+.br
+.B debug_rcv=00000000.
+.br
+.B debug_pfkey=ffffffff.
+.LP
+means that one
+.B tunnel
+flag has been set (tunnel-xmit),
+full
+.B pfkey
+sockets debugging has been set and everything else is not set.
+.LP
+.SH FILES
+/proc/net/ipsec_klipsdebug, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(8), ipsec_eroute(8),
+ipsec_spi(8), ipsec_spigrp(8), ipsec_klipsdebug(5), ipsec_version(5),
+ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: klipsdebug.5,v $
+.\" Revision 1.1 2004/03/15 20:35:28 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.5 2002/04/24 07:35:38 mcr
+.\" Moved from ./klips/utils/klipsdebug.5,v
+.\"
+.\" Revision 1.4 2000/10/10 20:10:19 rgb
+.\" Added support for debug_ipcomp and debug_verbose to klipsdebug.
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/28 12:44:12 henry
+.\" format touchup
+.\"
+.\" Revision 1.1 2000/06/28 05:43:00 rgb
+.\" Added manpages for all 5 klips utils.
+.\"
+.\"
+.\"
diff --git a/programs/klipsdebug/klipsdebug.8 b/programs/klipsdebug/klipsdebug.8
new file mode 100644
index 000000000..60d018eec
--- /dev/null
+++ b/programs/klipsdebug/klipsdebug.8
@@ -0,0 +1,164 @@
+.TH IPSEC_KLIPSDEBUG 8 "21 Jun 2000"
+.\"
+.\" RCSID $Id: klipsdebug.8,v 1.1 2004/03/15 20:35:28 as Exp $
+.\"
+.SH NAME
+ipsec klipsdebug \- set KLIPS (kernel IPSEC support) debug features and level
+.SH SYNOPSIS
+.B ipsec
+.B klipsdebug
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-set
+flagname
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-clear
+flagname
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-all
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-none
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-help
+.PP
+.B ipsec
+.B klipsdebug
+.B \-\-version
+.SH DESCRIPTION
+.I Klipsdebug
+sets and clears flags that control
+various parts of the debugging output of Klips
+(the kernel portion of FreeS/WAN IPSEC).
+The form with no additional arguments lists the present contents of
+/proc/net/ipsec_klipsdebug.
+The
+.B \-\-set
+form turns the specified flag on,
+while the
+.B \-\-clear
+form turns the specified flag off.
+The
+.B \-\-all
+form
+turns all flags on except verbose, while the
+.B \-\-none
+form turns all flags off.
+.PP
+The current flag names are:
+.TP 8
+.B tunnel
+tunnelling code
+.TP
+.B tunnel-xmit
+tunnelling transmit only code
+.TP
+.B pfkey
+userspace communication code
+.TP
+.B xform
+transform selection and manipulation code
+.TP
+.B eroute
+eroute table manipulation code
+.TP
+.B spi
+SA table manipulation code
+.TP
+.B radij
+radij tree manipulation code
+.TP
+.B esp
+encryptions transforms code
+.TP
+.B ah
+authentication transforms code
+.B rcv
+receive code
+.TP
+.B ipcomp
+ip compression transforms code
+.TP
+.B verbose
+give even more information, BEWARE:
+a)this will print authentication and encryption keys in the logs
+b)this will probably trample the 4k kernel printk buffer giving inaccurate output
+.PP
+All Klips debug output appears as
+.B kernel.info
+messages to
+.IR syslogd (8).
+Most systems are set up
+to log these messages to
+.IR /var/log/messages .
+Beware that
+.B klipsdebug
+.B \-\-all
+produces a lot of output and the log file will grow quickly.
+.PP
+The file format for /proc/net/ipsec_klipsdebug is discussed in
+ipsec_klipsdebug(5).
+.SH EXAMPLES
+.TP
+.B klipsdebug \-\-all
+turns on all KLIPS debugging except verbose.
+.TP
+.B klipsdebug \-\-clear tunnel
+turns off only the
+.B tunnel
+debugging messages.
+.LP
+.SH FILES
+/proc/net/ipsec_klipsdebug, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(8), ipsec_eroute(8),
+ipsec_spi(8), ipsec_spigrp(8), ipsec_klipsdebug(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.SH BUGS
+It really ought to be possible to set or unset selective combinations
+of flags.
+.\"
+.\" $Log: klipsdebug.8,v $
+.\" Revision 1.1 2004/03/15 20:35:28 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.18 2002/04/24 07:35:39 mcr
+.\" Moved from ./klips/utils/klipsdebug.8,v
+.\"
+.\" Revision 1.17 2000/10/10 20:10:19 rgb
+.\" Added support for debug_ipcomp and debug_verbose to klipsdebug.
+.\"
+.\" Revision 1.16 2000/08/18 17:33:11 rgb
+.\" Updated obsolete netlink reference and added pfkey and tunnel-xmit.
+.\"
+.\" Revision 1.15 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.14 2000/06/28 05:53:09 rgb
+.\" Mention that netlink is obsolete.
+.\"
+.\" Revision 1.13 2000/06/21 16:54:58 rgb
+.\" Added 'no additional args' text for listing contents of
+.\" /proc/net/ipsec_* files.
+.\"
+.\" Revision 1.12 1999/07/19 18:47:24 henry
+.\" fix slightly-misformed comments
+.\"
+.\" Revision 1.11 1999/04/06 04:54:37 rgb
+.\" Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+.\" patch shell fixes.
+.\"
+.\"
diff --git a/programs/klipsdebug/klipsdebug.c b/programs/klipsdebug/klipsdebug.c
new file mode 100644
index 000000000..c205038a1
--- /dev/null
+++ b/programs/klipsdebug/klipsdebug.c
@@ -0,0 +1,436 @@
+/*
+ * control KLIPS debugging options
+ * Copyright (C) 1996 John Ioannidis.
+ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs <rgb@freeswan.org>
+ * 2001 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char klipsdebug_c_version[] = "RCSID $Id: klipsdebug.c,v 1.2 2004/06/07 15:16:34 as Exp $";
+
+
+#include <sys/types.h>
+#include <linux/types.h> /* new */
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h> /* system(), strtoul() */
+#include <sys/stat.h> /* open() */
+#include <fcntl.h> /* open() */
+
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+
+#include <unistd.h>
+#include <freeswan.h>
+#if 0
+#include <linux/autoconf.h> /* CONFIG_IPSEC_PFKEYv2 */
+#endif
+
+/* permanently turn it on since netlink support has been disabled */
+#include <signal.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#include "freeswan/radij.h"
+#include "freeswan/ipsec_encap.h"
+#ifndef CONFIG_IPSEC_DEBUG
+#define CONFIG_IPSEC_DEBUG
+#endif /* CONFIG_IPSEC_DEBUG */
+#include "freeswan/ipsec_tunnel.h"
+
+#include <stdio.h>
+#include <getopt.h>
+
+__u32 bigbuf[1024];
+char *program_name;
+
+int pfkey_sock;
+fd_set pfkey_socks;
+uint32_t pfkey_seq = 0;
+
+char copyright[] =
+"Copyright (C) 1999 Henry Spencer, Richard Guy Briggs, D. Hugh Redelmeier,\n\
+ Sandy Harris, Angelos D. Keromytis, John Ioannidis.\n\
+\n\
+ This program is free software; you can redistribute it and/or modify it\n\
+ under the terms of the GNU General Public License as published by the\n\
+ Free Software Foundation; either version 2 of the License, or (at your\n\
+ option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.\n\
+\n\
+ This program is distributed in the hope that it will be useful, but\n\
+ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\n\
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License\n\
+ (file COPYING in the distribution) for more details.\n";
+
+static void
+usage(char * arg)
+{
+ fprintf(stdout, "usage: %s {--set|--clear} {tunnel|tunnel-xmit|netlink|xform|eroute|spi|radij|esp|ah|rcv|pfkey|ipcomp|verbose}\n", arg);
+ fprintf(stdout, " %s {--all|--none}\n", arg);
+ fprintf(stdout, " %s --help\n", arg);
+ fprintf(stdout, " %s --version\n", arg);
+ fprintf(stdout, " %s\n", arg);
+ fprintf(stdout, " [ --debug ] is optional to any %s command\n", arg);
+ fprintf(stdout, " [ --label <label> ] is optional to any %s command.\n", arg);
+ exit(1);
+}
+
+static struct option const longopts[] =
+{
+ {"set", 1, 0, 's'},
+ {"clear", 1, 0, 'c'},
+ {"all", 0, 0, 'a'},
+ {"none", 0, 0, 'n'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {"label", 1, 0, 'l'},
+ {"optionsfrom", 1, 0, '+'},
+ {"debug", 0, 0, 'd'},
+ {0, 0, 0, 0}
+};
+
+int
+main(int argc, char **argv)
+{
+/* int fd; */
+ unsigned char action = 0;
+ int c, previous = -1;
+
+ int debug = 0;
+ int error = 0;
+ int argcount = argc;
+ int em_db_tn, em_db_nl, em_db_xf, em_db_er, em_db_sp;
+ int em_db_rj, em_db_es, em_db_ah, em_db_rx, em_db_ky;
+ int em_db_gz, em_db_vb;
+
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+
+ em_db_tn=em_db_nl=em_db_xf=em_db_er=em_db_sp=0;
+ em_db_rj=em_db_es=em_db_ah=em_db_rx=em_db_ky=0;
+ em_db_gz=em_db_vb=0;
+
+
+ program_name = argv[0];
+
+ while((c = getopt_long(argc, argv, ""/*"s:c:anhvl:+:d"*/, longopts, 0)) != EOF) {
+ switch(c) {
+ case 'd':
+ debug = 1;
+ pfkey_lib_debug = PF_KEY_DEBUG_PARSE_MAX;
+ argcount--;
+ break;
+ case 's':
+ if(action) {
+ fprintf(stderr, "%s: Only one of '--set', '--clear', '--all' or '--none' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ action = 's';
+ em_db_tn=em_db_nl=em_db_xf=em_db_er=em_db_sp=0;
+ em_db_rj=em_db_es=em_db_ah=em_db_rx=em_db_ky=0;
+ em_db_gz=em_db_vb=0;
+ if(strcmp(optarg, "tunnel") == 0) {
+ em_db_tn = -1L;
+ } else if(strcmp(optarg, "tunnel-xmit") == 0) {
+ em_db_tn = DB_TN_XMIT;
+ } else if(strcmp(optarg, "netlink") == 0) {
+ em_db_nl = -1L;
+ } else if(strcmp(optarg, "xform") == 0) {
+ em_db_xf = -1L;
+ } else if(strcmp(optarg, "eroute") == 0) {
+ em_db_er = -1L;
+ } else if(strcmp(optarg, "spi") == 0) {
+ em_db_sp = -1L;
+ } else if(strcmp(optarg, "radij") == 0) {
+ em_db_rj = -1L;
+ } else if(strcmp(optarg, "esp") == 0) {
+ em_db_es = -1L;
+ } else if(strcmp(optarg, "ah") == 0) {
+ em_db_ah = -1L;
+ } else if(strcmp(optarg, "rcv") == 0) {
+ em_db_rx = -1L;
+ } else if(strcmp(optarg, "pfkey") == 0) {
+ em_db_ky = -1L;
+ } else if(strcmp(optarg, "comp") == 0) {
+ em_db_gz = -1L;
+ } else if(strcmp(optarg, "verbose") == 0) {
+ em_db_vb = -1L;
+ } else {
+ usage(program_name);
+ }
+ em_db_nl |= 1 << (sizeof(em_db_nl) * 8 -1);
+ break;
+ case 'c':
+ if(action) {
+ fprintf(stderr, "%s: Only one of '--set', '--clear', '--all' or '--none' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ em_db_tn=em_db_nl=em_db_xf=em_db_er=em_db_sp=-1;
+ em_db_rj=em_db_es=em_db_ah=em_db_rx=em_db_ky=-1;
+ em_db_gz=em_db_vb=-1;
+
+ action = 'c';
+ if(strcmp(optarg, "tunnel") == 0) {
+ em_db_tn = 0;
+ } else if(strcmp(optarg, "tunnel-xmit") == 0) {
+ em_db_tn = ~DB_TN_XMIT;
+ } else if(strcmp(optarg, "netlink") == 0) {
+ em_db_nl = 0;
+ } else if(strcmp(optarg, "xform") == 0) {
+ em_db_xf = 0;
+ } else if(strcmp(optarg, "eroute") == 0) {
+ em_db_er = 0;
+ } else if(strcmp(optarg, "spi") == 0) {
+ em_db_sp = 0;
+ } else if(strcmp(optarg, "radij") == 0) {
+ em_db_rj = 0;
+ } else if(strcmp(optarg, "esp") == 0) {
+ em_db_es = 0;
+ } else if(strcmp(optarg, "ah") == 0) {
+ em_db_ah = 0;
+ } else if(strcmp(optarg, "rcv") == 0) {
+ em_db_rx = 0;
+ } else if(strcmp(optarg, "pfkey") == 0) {
+ em_db_ky = 0;
+ } else if(strcmp(optarg, "comp") == 0) {
+ em_db_gz = 0;
+ } else if(strcmp(optarg, "verbose") == 0) {
+ em_db_vb = 0;
+ } else {
+ usage(program_name);
+ }
+ em_db_nl &= ~(1 << (sizeof(em_db_nl) * 8 -1));
+ break;
+ case 'a':
+ if(action) {
+ fprintf(stderr, "%s: Only one of '--set', '--clear', '--all' or '--none' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ action = 'a';
+ em_db_tn=em_db_nl=em_db_xf=em_db_er=em_db_sp=-1;
+ em_db_rj=em_db_es=em_db_ah=em_db_rx=em_db_ky=-1;
+ em_db_gz=-1;
+ em_db_vb= 0;
+ break;
+ case 'n':
+ if(action) {
+ fprintf(stderr, "%s: Only one of '--set', '--clear', '--all' or '--none' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ action = 'n';
+ em_db_tn=em_db_nl=em_db_xf=em_db_er=em_db_sp=0;
+ em_db_rj=em_db_es=em_db_ah=em_db_rx=em_db_ky=0;
+ em_db_gz=em_db_vb=0;
+ break;
+ case 'h':
+ case '?':
+ usage(program_name);
+ exit(1);
+ case 'v':
+ fprintf(stdout, "klipsdebug (Linux FreeS/WAN %s) %s\n",
+ ipsec_version_code(), klipsdebug_c_version);
+ fputs(copyright, stdout);
+ exit(0);
+ case 'l':
+ program_name = malloc(strlen(argv[0])
+ + 10 /* update this when changing the sprintf() */
+ + strlen(optarg));
+ sprintf(program_name, "%s --label %s",
+ argv[0],
+ optarg);
+ argcount -= 2;
+ break;
+ case '+': /* optionsfrom */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* no return on error */
+ break;
+ default:
+ break;
+ }
+ previous = c;
+ }
+
+ if(argcount == 1) {
+ system("cat /proc/net/ipsec_klipsdebug");
+ exit(0);
+ }
+
+ if(!action) {
+ usage(program_name);
+ }
+
+ if((pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0) {
+ fprintf(stderr, "%s: Trouble opening PF_KEY family socket with error: ",
+ program_name);
+ switch(errno) {
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, KLIPS not loaded or check kernel log messages for specifics.\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case EAFNOSUPPORT:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown file open error %d. Please report as much detail as possible to development team.\n", errno);
+ }
+ exit(1);
+ }
+
+ pfkey_extensions_init(extensions);
+
+ if((error = pfkey_msg_hdr_build(&extensions[0],
+ SADB_X_DEBUG,
+ 0,
+ 0,
+ ++pfkey_seq,
+ getpid()))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+
+ if((error = pfkey_x_debug_build(&extensions[SADB_X_EXT_DEBUG],
+ em_db_tn,
+ em_db_nl,
+ em_db_xf,
+ em_db_er,
+ em_db_sp,
+ em_db_rj,
+ em_db_es,
+ em_db_ah,
+ em_db_rx,
+ em_db_ky,
+ em_db_gz,
+ em_db_vb))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+
+ if((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN))) {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+
+ if((error = write(pfkey_sock,
+ pfkey_msg,
+ pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) !=
+ (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
+ fprintf(stderr,
+ "%s: pfkey write failed, tried to write %u octets, returning %d with errno=%d.\n",
+ program_name,
+ (unsigned)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN),
+ error,
+ errno);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ fprintf(stderr, "No device?!?\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case ENOSPC:
+ fprintf(stderr, "no room in kernel SAref table. Cannot process request.\n");
+ break;
+ case ESPIPE:
+ fprintf(stderr, "kernel SAref table internal error. Cannot process request.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket write error %d. Please report as much detail as possible to development team.\n", errno);
+ }
+ exit(1);
+ }
+
+ if(pfkey_msg) {
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ }
+
+ (void) close(pfkey_sock); /* close the socket */
+ exit(0);
+}
diff --git a/programs/look/.cvsignore b/programs/look/.cvsignore
new file mode 100644
index 000000000..6f094f8d7
--- /dev/null
+++ b/programs/look/.cvsignore
@@ -0,0 +1 @@
+look
diff --git a/programs/look/Makefile b/programs/look/Makefile
new file mode 100644
index 000000000..e66ca60c1
--- /dev/null
+++ b/programs/look/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=look
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# 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/programs/look/look.8 b/programs/look/look.8
new file mode 100644
index 000000000..fc2d53eca
--- /dev/null
+++ b/programs/look/look.8
@@ -0,0 +1,45 @@
+.TH look 8 "25 Apr 2002"
+.\"
+.\" RCSID $Id: look.8,v 1.1 2004/03/15 20:35:28 as Exp $
+.\"
+.SH NAME
+ipsec look \- get a quick summary of FreeS/WAN status
+.SH SYNOPSIS
+.I look
+is used to get a quick overview of what the status of FreeSWAN is.
+It is equivalent to:
+\ \ \ ipsec eroute
+
+\ \ \ ipsec spigrp
+
+\ \ \ ipsec tncfg
+
+\ \ \ ipsec spi
+
+\ \ \ netstat -rn
+
+.LP
+However a bit of processing is done to combine the outputs.
+.SH "SEE ALSO"
+ipsec(8), ipsec_tncfg(8), ipsec_spi(8), ipsec_spigrp(8), ipsec_eroute(5),
+netstat(8).
+.SH HISTORY
+Man page written for the Linux FreeS/WAN project <http://www.freeswan.org/>
+by Michael Richardson. Original program written by Henry Spencer.
+.\"
+.\" $Log: look.8,v $
+.\" Revision 1.1 2004/03/15 20:35:28 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/programs/look/look.in b/programs/look/look.in
new file mode 100755
index 000000000..a5331c03b
--- /dev/null
+++ b/programs/look/look.in
@@ -0,0 +1,87 @@
+#! /bin/sh
+# quick look at current connections and related information
+# Copyright (C) 1998, 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: look.in,v 1.1 2004/03/15 20:35:28 as Exp $
+
+info=/var/run/ipsec.info
+me="ipsec look"
+
+case "$1" in
+--help) echo "Usage: ipsec look" ; exit 0 ;;
+--version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+esac
+
+# clear out variables that have strange effects on sort etc.
+unset LANG LANGUAGE LC_ALL LC_MESSAGES
+
+# Pick up IPsec configuration etc.
+eval `ipsec _confread --varprefix IPSEC --optional --type config setup`
+if test " $IPSEC_confreadstatus" != " "
+then
+ echo "$IPSEC_confreadstatus -- aborting" |
+ logger -s -p daemon.error -t ipsec_look
+ exit 1
+fi
+if test -s $info
+then
+ . $info
+fi
+
+# label it just to be sure
+echo "`hostname` `date`"
+
+# combine spigrp and eroute
+cat /proc/net/ipsec_spigrp /proc/net/ipsec_eroute |
+ awk '
+ function pad(subnet) {
+ sub("/", ".", subnet)
+ split(subnet, d, ".")
+ return sprintf("%03s%03s%03s%03s%03s", d[1], d[2],
+ d[3], d[4], d[5])
+ }
+ $2 == "->" {
+ printf "%s:%-18s -> %-18s => %s\n",
+ (pad($1) pad($3)),
+ $1, $3, (($5 in tun) ? tun[$5] : $5)
+ next
+ }
+ $3 == "->" {
+ printf "%s:%-18s -> %-18s => %s (%s)\n",
+ (pad($2) pad($4)),
+ $2, $4, (($6 in tun) ? tun[$6] : $6), $1
+ next
+ }
+ { tun[$1] = $0 }
+ ' | sort | sed 's/^[^:]*://'
+
+# tncfg (mostly as a divider line)
+egrep -v 'NULL[ \t]+mtu=0\(0\)[ \t]+->[ \t]+0' /proc/net/ipsec_tncfg |
+ paste -d % | sed 's/%/ /g' | sed 's/ -> /->/g'
+
+# SAs
+sort /proc/net/ipsec_spi
+
+# relevant routing information, including header line (which is good
+# enough as a separator, no need for another bar)
+pat="^Dest"
+if test " $defaultroutephys" != " "
+then
+ pat="$pat|$defaultroutephys\$|$defaultroutevirt\$"
+else
+ for i in `echo "$IPSECinterfaces" | tr '=' ' '`
+ do
+ pat="$pat|$i\$"
+ done
+fi
+netstat -nr | egrep "$pat" | sed '/^Dest/s/^/ /' | sort | sed '/^ Dest/s/ //'
diff --git a/programs/lwdnsq/.cvsignore b/programs/lwdnsq/.cvsignore
new file mode 100644
index 000000000..b1ff942bf
--- /dev/null
+++ b/programs/lwdnsq/.cvsignore
@@ -0,0 +1,4 @@
+dnskey
+dnskey.cat8
+lwdnsq
+lwdnsq.xml
diff --git a/programs/lwdnsq/CONTRACT.txt b/programs/lwdnsq/CONTRACT.txt
new file mode 100644
index 000000000..77335e8cf
--- /dev/null
+++ b/programs/lwdnsq/CONTRACT.txt
@@ -0,0 +1,106 @@
+The only delays are after START, and after CNAME.
+
+add the time to each line.
+
+put DNSSEC status on each line.
+
+The format of the replies is:
+
+ <ID> <TIME> <TTL> <TYPE> <TYPE-SPECIFIC> \n
+ ^- whitespace.
+
+ID is a unique number that identifies the transaction. It is determined
+by the caller. lwdnsq treats this ID as a string, and does nothing
+with it other than repeat it on each line.
+There is no predetermined bound on the length, but the total line
+length of input to lwdnsq must not exceed LWDNSQ_CMDBUF_LEN (1024).
+LWDNSQ_CMDBUF_LEN is defined in <freeswan.h>
+
+The output of lwdnsq is currently limited to LWDNSQ_RESULT_LEN_MAX (4096) byte
+lines. LWDNSQ_RESULT_LEN_MAX is defined in <freeswan.h>
+
+Time is a decimal encoded integer, currently 32-bit time (time_t) since Unix
+epoch. On systems with a 64-bit time_t, it would be 64-bit in range.
+
+The TTL field gives the number of seconds that the result is valid for.
+(starting at the time given). If there is no useful TTL value for the
+record, it will be either "0".
+
+Type is a case-insensitive, one of:
+structure
+ START (optional comments) acknowledges start of transaction
+ DONE (optional comments) signals the end of data for a transaction
+
+errors
+ RETRY same as for FATAL, but this implies that the data
+ was not found, but could be found later.
+
+ FATAL Following this, is text detailing the fault,
+ in a human readable form.
+ "FATAL" results likely mean that the lwdnsq should
+ be restarted.
+
+ WARNING Log this result, but do not cancel transaction.
+
+ Errors are still followed by "DONE".
+
+
+
+data answers
+ DNSSEC followed by "OKAY" or "not present"
+ NAME followed by canonical name for requested RR,
+ i.e. the result of any CNAMEs/DNAMEs that were chased
+ by the recursive resolving server.
+ CNAME followed by the name which has been followed.
+ CNAMEFROM the thing that was mapped
+ TXT followed by RR-specific Presentation Format
+ SIG "
+ A "
+ AAAA "
+ PTR "
+ KEY "
+ AD-TXT followed by RR-specific Presentation Format - DNSSEC
+ TXT followed by RR-specific Presentation Format - DNSSEC
+ AD-KEY followed by RR-specific Presentation Format - DNSSEC
+ KEY followed by RR-specific Presentation Format - DNSSEC
+
+If there is no data of the type requested, even after lwdnsq
+has attempted to follow CNAMEs, then there will be no resource
+records returned. This is the formal indication of the lack of
+the records, however, in addition, an error will be returned, of the type:
+ RETRY the record "foobar" does not have a RR resource record.
+
+The -ldns library from bind9 will deal with the presentation format,
+producing a structure breakout from it. The functions are:
+
+dns_rdata_fromtext(3)
+ Presentation Format -> Wire Format
+dns_rdata_tostruct(3)
+ Wire Format -> C-structure
+
+dns_rdata_totext(3)
+ Wire Format -> Presentation Format
+
+(Above from .../src/bind-9.3.0s20020722/lib/dns/include/dns/rdata.h)
+
+The lwdnsq program uses dns_rdata_totext(3) to format the resource record
+(received from lwres in wire format) into its presentation format.
+
+The documentation is in the bind-9.3 source tree, in the header files.
+(They are likely all installed into the include directories).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/programs/lwdnsq/Makefile b/programs/lwdnsq/Makefile
new file mode 100644
index 000000000..2fca5e249
--- /dev/null
+++ b/programs/lwdnsq/Makefile
@@ -0,0 +1,96 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM:=lwdnsq
+
+OBJS:=cmds.o lookup.o
+
+LWRESINCL=${LWRESDIR}/include
+
+LIBS:=${FREESWANLIB} ${LWRESLIB} ${BIND9STATICLIBDIR}/libdns.a ${BIND9STATICLIBDIR}/libisc.a
+CFLAGS+=-I${LWRESINCL}
+#USERCOMPILE=-g
+
+
+include ../Makefile.program
+
+lwdnsq.8: lwdnsq.xml
+ xmlto man lwdnsq.xml
+
+lwdnsq.xml: lwdnsq.xml.in
+
+TAGS:
+ etags *.[ch] ../../lib/liblwres/*.[ch] ../../lib/liblwres/include/lwres/*.h
+
+# manually maintained dependancies
+lwdnsq.o: lwdnsq.c lwdnsq.h
+cmds.o: cmds.c lwdnsq.h
+lookup.o: lookup.c lwdnsq.h
+
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.9 2003/09/03 01:13:24 mcr
+# first attempt at async capable lwdnsq.
+#
+# Revision 1.8 2003/02/27 09:29:02 mcr
+# moved targets to after include file so that XML-conversion
+# does not occur by default.
+#
+# Revision 1.7 2003/02/01 01:36:53 mcr
+# updates to lwdnsq man page to reflect CONTRACT
+#
+# Revision 1.6 2003/01/14 03:01:14 dhr
+#
+# improve diagnostics; tidy
+#
+# Revision 1.5 2003/01/10 23:20:40 dhr
+#
+# remove reference to /sandel
+#
+# Revision 1.4 2002/12/19 05:45:47 mcr
+# use BIND9STATICLIBDIR to find -lisc/-ldns.
+#
+# Revision 1.3 2002/12/12 06:03:41 mcr
+# added --regress option to force times to be regular
+#
+# Revision 1.2 2002/12/04 03:21:06 mcr
+# DNS zone files (with signed versions) for DNSSEC enabled testing root.
+#
+# Revision 1.1 2002/10/30 02:25:31 mcr
+# renamed version of files from dnskey/
+#
+# Revision 1.4 2002/10/18 04:08:02 mcr
+# added -ldns and -lisc to libraries, but it isn't clear
+# where we will find these only-slightly standard libraries yet.
+#
+# Revision 1.3 2002/10/09 20:13:10 mcr
+# get appropriate LWRES include directory.
+#
+# Revision 1.2 2002/09/30 18:55:54 mcr
+# skeleton for dnskey helper program.
+#
+# Revision 1.1 2002/09/30 16:50:23 mcr
+# documentation for "dnskey" helper
+#
+#
+#
diff --git a/programs/lwdnsq/cmds.c b/programs/lwdnsq/cmds.c
new file mode 100644
index 000000000..1b15202ff
--- /dev/null
+++ b/programs/lwdnsq/cmds.c
@@ -0,0 +1,351 @@
+/*
+ * DNS KEY lookup helper - command implementation
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <arpa/nameser.h>
+#include <lwres/netdb.h>
+#include <time.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+#include <isc/result.h>
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+
+
+#include "lwdnsq.h"
+
+static void cmd_not_implemented(dnskey_glob *gs, const char *what)
+{
+ fprintf(gs->cmdproto_out, "0 FATAL unimplemented command \"%s\"\n", what);
+}
+
+void output_transaction_line(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ char *data)
+{
+ time_t t;
+
+ t=time(NULL);
+
+ /* regularlize time for regression testing */
+ if(gs->regress) {
+ t=3145915;
+ }
+
+ if(data) {
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s %s\n",
+ id, t, ttl, cmd, data);
+ } else {
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s\n",
+ id, t, ttl, cmd);
+ }
+
+}
+
+void output_transaction_line_limited(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ int max,
+ char *data)
+{
+ time_t t;
+
+ t=time(NULL);
+
+ /* regularlize time for regression testing */
+ if(gs->regress) {
+ t=3145915;
+ }
+
+ fprintf(gs->cmdproto_out,
+ "%s %ld %d %s %.*s\n",
+ id, t, ttl, cmd, max, data);
+}
+
+
+#if 0
+again:
+
+ lwres_getrrsetbyname_xmit(ctx, &las);
+ timeout.tv_sec = lwres_async_timeout(ctx);
+ sock = lwres_async_fd(ctx);
+
+ FD_ZERO(&readfds);
+ FD_SET(sock, &readfds);
+ ret2 = select(sock + 1, &readfds, NULL, NULL, &timeout);
+
+ /*
+ * What happened with select?
+ */
+ if (ret2 < 0) {
+ success = LWRES_R_IOERROR;
+ goto out3;
+ }
+ if (ret2 == 0) {
+ success = LWRES_R_TIMEOUT;
+ goto out3;
+ }
+
+ out:
+ if (ctx != NULL)
+ lwres_context_destroy(&ctx);
+
+ out2:
+
+#endif
+
+
+void lookup_key(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *fqdn;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc!=3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ fqdn=argv[2];
+
+ lookup_thing(gs, dns_rdatatype_key, "KEY", id, fqdn);
+}
+
+void lookup_key4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "key4");
+}
+
+void lookup_key6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "key6");
+}
+
+
+void lookup_txt(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *fqdn;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc != 3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments to TXT: %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ fqdn=argv[2];
+
+ lookup_thing(gs, dns_rdatatype_txt, "TXT", id, fqdn);
+}
+
+void lookup_txt4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ char *id;
+ char *ipv4;
+ struct in_addr in4;
+ char simplebuf[80];
+
+ /* process arguments */
+ /* KEY 31459 east.uml.freeswan.org */
+ if(argc != 3) {
+ snprintf(simplebuf, sizeof(simplebuf), "wrong number of arguments to TXT: %d", argc);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ id=argv[1];
+ ipv4=argv[2];
+
+ if(inet_pton(AF_INET, ipv4, &in4) <= 0) {
+ snprintf(simplebuf, sizeof(simplebuf), "invalid IPv4 address: %s", ipv4);
+ output_transaction_line(gs, "0", 0, "FATAL", simplebuf);
+ return;
+ }
+
+ snprintf(simplebuf, 80, "%d.%d.%d.%d.in-addr.arpa",
+ in4.s_addr & 0xff,
+ (in4.s_addr & 0xff00) >> 8,
+ (in4.s_addr & 0xff0000) >> 16,
+ (in4.s_addr & 0xff000000) >> 24);
+
+ lookup_thing(gs, dns_rdatatype_txt, "TXT4", id, simplebuf);
+}
+
+void lookup_txt6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "txt6");
+}
+
+void lookup_ipseckey(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey");
+}
+
+void lookup_ipseckey4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey4");
+}
+
+void lookup_ipseckey6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "ipseckey6");
+}
+
+void lookup_oe4(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "oe4");
+}
+
+void lookup_oe6(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "oe6");
+}
+
+void lookup_a(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "a");
+}
+
+void lookup_aaaa(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ cmd_not_implemented(gs, "aaaa");
+}
+
+
+
+
+
+
+/*
+ * $Log: cmds.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.11 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ * Revision 1.10 2003/05/22 16:33:51 mcr
+ * added trailing . to CNAME return and cleaned up "CNAMEFROM" output.
+ *
+ * Revision 1.9 2003/05/14 15:47:39 mcr
+ * processing of IP address into pieces was not done with
+ * the right order of operations.
+ *
+ * Revision 1.8 2003/02/27 09:27:17 mcr
+ * adjusted lwdnsq so that it adheres to contract - TXT records
+ * are returned in a single piece. Requires custom decoding.
+ * implemented "txt4" lookup type.
+ *
+ * Revision 1.7 2003/01/14 07:53:29 dhr
+ *
+ * - attempt to diagnose lack of lwdnsq
+ * - increase too-small buffer size
+ *
+ * Revision 1.6 2003/01/14 03:01:14 dhr
+ *
+ * improve diagnostics; tidy
+ *
+ * Revision 1.5 2002/12/12 06:03:41 mcr
+ * added --regress option to force times to be regular
+ *
+ * Revision 1.4 2002/11/25 18:37:28 mcr
+ * added AD- marking of each record that was DNSSEC verified.
+ *
+ * Revision 1.3 2002/11/16 02:53:53 mcr
+ * lwdnsq - with new contract added.
+ *
+ * Revision 1.2 2002/11/12 04:33:44 mcr
+ * print DNSSEC status as we process CNAMEs.
+ *
+ * Revision 1.1 2002/10/30 02:25:31 mcr
+ * renamed version of files from dnskey/
+ *
+ * Revision 1.4 2002/10/18 23:11:02 mcr
+ * if we get ENOENT, then see if we can get a CNAME. If so, then
+ * follow it.
+ * Be careful when following them to avoid recursion.
+ *
+ * Revision 1.3 2002/10/18 04:08:47 mcr
+ * use -ldns routines to decode lwres results and format them nicely.
+ *
+ * Revision 1.2 2002/10/09 20:13:34 mcr
+ * first set of real code - lookup KEY records in forward.
+ *
+ * Revision 1.1 2002/09/30 18:55:54 mcr
+ * skeleton for dnskey helper program.
+ *
+ * Revision 1.1 2002/09/30 16:50:23 mcr
+ * documentation for "dnskey" helper
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lookup.c b/programs/lwdnsq/lookup.c
new file mode 100644
index 000000000..700c4adbe
--- /dev/null
+++ b/programs/lwdnsq/lookup.c
@@ -0,0 +1,632 @@
+/*
+ * DNS KEY lookup helper
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char lookup_c_version[] = "@(#) RCSID $Id: lookup.c,v 1.1 2004/03/15 20:35:28 as Exp $";
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <dns/name.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+#include "lwdnsq.h"
+
+static int lwresd_has_spoken = 0;
+
+char *xstrdup(const char *s)
+{
+ char *n;
+
+ n = strdup(s);
+ if(n == NULL) {
+ abort();
+ }
+ return n;
+}
+
+void free_dl(dnskey_glob *gs, dnskey_lookup *dl)
+{
+ dnskey_lookup **walk;
+
+ walk = &gs->dns_outstanding;
+ while(*walk!=NULL && *walk != dl)
+ {
+ walk = &((*walk)->next);
+ }
+ if(*walk != NULL)
+ {
+ /* if we exit with it non-null, then we
+ * found a matching location, remove
+ * it.
+ */
+ *walk = dl->next;
+ dl->next = NULL;
+ }
+ gs->dns_inflight--;
+
+ if(dl->tracking_id) {
+ free(dl->tracking_id);
+ dl->tracking_id = NULL;
+ }
+ if(dl->wantedtype_name) {
+ free(dl->wantedtype_name);
+ dl->wantedtype_name = NULL;
+ }
+ if(dl->fqdn) {
+ free(dl->fqdn);
+ dl->fqdn = NULL;
+ }
+#if 0
+ if(dl->last_cname_used) {
+ dns_name_free(&dl->last_cname, gs->iscmem);
+ }
+#endif
+
+ free(dl);
+}
+
+void lookup_thing(dnskey_glob *gs,
+ dns_rdatatype_t wantedtype,
+ char *wantedtype_name,
+ char *id,
+ char *fqdn)
+{
+ isc_mem_t *iscmem;
+ isc_buffer_t *iscbuf;
+ int success;
+ dnskey_lookup *dl;
+
+ iscmem=NULL;
+ iscbuf=NULL;
+ dl = malloc(sizeof(*dl));
+ memset(dl, 0, sizeof(*dl));
+
+ dl->tracking_id = strdup(id);
+ dl->step = dkl_start;
+
+ output_transaction_line(gs, id, 0, "START", NULL);
+
+ success = lwres_getrrsetbyname_init(fqdn, dns_rdataclass_in,
+ wantedtype, 0 /*flags*/,
+ gs->lwctx, &dl->las);
+
+ if(success != ERRSET_SUCCESS) {
+ /* screwed: */
+ output_transaction_line(gs, id, 0, "FATAL", "isc buffer error");
+ return;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ dl->step = dkl_first;
+ dl->wantedtype = wantedtype;
+ dl->wantedtype_name = xstrdup(wantedtype_name);
+ dl->fqdn = xstrdup(fqdn);
+ dl->tracking_id = xstrdup(id);
+
+ /* link it in */
+ dl->next = gs->dns_outstanding;
+ gs->dns_outstanding = dl;
+
+ gs->dns_inflight++;
+
+ return;
+}
+
+
+int setup_follow_possible_cname(dnskey_glob *gs,
+ dnskey_lookup *dl)
+{
+ int ret;
+
+ dl->cname_count++;
+
+ /*
+ * If we are on an odd cycle (starting with 1),
+ * then convert to dns_name_t so that we can compare later.
+ *
+ * This detects loops in the CNAME processing, while still
+ * allowing an arbitrary number of CNAMEs to be followed.
+ */
+ if(dl->cname_count & 1)
+ {
+ isc_buffer_t fqdn_src;
+ isc_buffer_t *fqdn_dst;
+
+ if(dl->cname_count == 1)
+ {
+ memset(&dl->last_cname, 0, sizeof(dl->last_cname));
+ dns_name_init(&dl->last_cname, NULL);
+ }
+ else
+ {
+ dns_name_reset(&dl->last_cname);
+ }
+
+ fqdn_dst=NULL;
+
+ isc_buffer_init(&fqdn_src, dl->fqdn, strlen(dl->fqdn));
+ isc_buffer_add(&fqdn_src, strlen(dl->fqdn));
+
+ isc_buffer_allocate(gs->iscmem, &fqdn_dst, strlen(dl->fqdn)+1);
+
+#if 0
+ if(dl->last_cname_used) {
+ dns_name_free(&dl->last_cname, gs->iscmem);
+ }
+#endif
+ dl->last_cname_used = 1;
+ if(dns_name_fromtext(&dl->last_cname,
+ &fqdn_src,
+ NULL,
+ 1,
+ fqdn_dst) != ISC_R_SUCCESS) {
+ return 0;
+ }
+
+ /* something else here ? */
+ }
+
+ ret = lwres_getrrsetbyname_init(dl->fqdn, dns_rdataclass_in,
+ dns_rdatatype_cname, 0 /*flags*/,
+ gs->lwctx,
+ &dl->las);
+
+ if(ret != ERRSET_SUCCESS) {
+ return 0;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ return 1;
+}
+
+
+/*
+ * we asked for, and got a CNAME of some kind.
+ */
+void process_step_cname(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success)
+{
+ struct rdatainfo *ri;
+ isc_region_t region;
+ dns_rdata_t rd;
+ dns_rdata_cname_t cn;
+ char simplebuf[80];
+ isc_buffer_t *cname_text;
+ char cname_buf[DNS_NAME_MAXTEXT];
+ /* char cname_buf2[DNS_NAME_MAXTEXT]; */
+
+ switch(success) {
+ case ERRSET_NONAME:
+ case ERRSET_NODATA:
+ /* no, no CNAME found, thing isn't there */
+ snprintf(simplebuf, sizeof(simplebuf),
+ "RR of type %s for %s was not found (tried CNAMEs)",
+ dl->wantedtype_name,
+ dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
+ simplebuf);
+ dl->step = dkl_done;
+ return;
+
+ case 0:
+ /* aha! found a CNAME */
+ break;
+
+ default:
+ fatal:
+ /* some other error */
+ snprintf(simplebuf, sizeof(simplebuf), "err=%d", success);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ return;
+ }
+
+ /*
+ * now process out the CNAMEs, and look them up, one by one...
+ * there should be only one... We just use the first one that works.
+ */
+
+ if(ans->rri_flags & RRSET_VALIDATED) {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "OKAY");
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
+ }
+
+ if(ans->rri_nrdatas != 1) {
+ /* we got a number of CNAMEs different from 1! */
+ success=0;
+ snprintf(simplebuf, sizeof(simplebuf), "illegal number of CNAMES: %d", ans->rri_nrdatas);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ return;
+ }
+
+ /* process first CNAME record */
+ ri= &ans->rri_rdatas[0];
+
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dns_rdatatype_cname, &region);
+
+ /* we set mctx to NULL, which means that the tenure for
+ * the stuff pointed to by cn will persist only as long
+ * as rd persists.
+ */
+ if(dns_rdata_tostruct(&rd, &cn, NULL) != ISC_R_SUCCESS) {
+ /* failed, try next return error */
+ success=0;
+ goto fatal;
+ }
+
+ cname_text=NULL;
+ if(isc_buffer_allocate(gs->iscmem, &cname_text, DNS_NAME_MAXTEXT)) {
+ success=0;
+ goto fatal;
+ }
+
+ if(dns_name_totext(&cn.cname, ISC_TRUE, cname_text) !=
+ ISC_R_SUCCESS) {
+ success=0;
+ goto fatal;
+ }
+
+ cname_buf[0]='\0';
+ strncat(cname_buf,
+ isc_buffer_base(cname_text),
+ isc_buffer_usedlength(cname_text));
+
+ /* free up buffer */
+ isc_buffer_free(&cname_text);
+
+ {
+ /* add a trailing . */
+ char *end;
+ end = &cname_buf[strlen(cname_buf)];
+ if(*end != '.') {
+ strncat(cname_buf, ".", sizeof(cname_buf));
+ }
+ }
+
+ /* format out a text version */
+ output_transaction_line(gs, dl->tracking_id, 0, "CNAME", cname_buf);
+ output_transaction_line(gs, dl->tracking_id, 0, "CNAMEFROM", dl->fqdn);
+
+ /* check for loops in the CNAMEs! */
+ if(dns_name_equal(&dl->last_cname, &cn.cname) == ISC_TRUE) {
+ /* damn, we found a loop! */
+ dl->step = dkl_done;
+ return;
+ }
+
+ /* send new request. */
+ /* okay, so look this new thing up */
+ success = lwres_getrrsetbyname_init(cname_buf, dns_rdataclass_in,
+ dl->wantedtype, 0 /*flags*/,
+ gs->lwctx, &dl->las);
+
+ if(success != ERRSET_SUCCESS) {
+ return;
+ }
+
+ lwres_getrrsetbyname_xmit(gs->lwctx, &dl->las);
+
+ dl->step = dkl_second;
+}
+
+void process_step_first(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success,
+ int attempt) /* attempt = 0 first time, 1 after cname */
+{
+ char simplebuf[132], typebuf[16];
+ char txtbuf[1024];
+ int i;
+
+ switch(success) {
+ case ERRSET_NODATA:
+ if(attempt == 0) {
+ lwresd_has_spoken = 1;
+ setup_follow_possible_cname(gs, dl);
+ dl->step = dkl_cname;
+ return;
+ }
+ /* FALLTHROUGH */
+ case ERRSET_NONAME:
+ lwresd_has_spoken = 1;
+ snprintf(simplebuf, sizeof(simplebuf),
+ "RR of type %s for %s was not found",
+ dl->wantedtype_name,
+ dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY",
+ simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_NOMEMORY:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "ran out of memory while looking up RR of type %s for %s",
+ dl->wantedtype_name, dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_FAIL:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "unspecified failure while looking up RR of type %s for %s%s",
+ dl->wantedtype_name, dl->fqdn,
+ lwresd_has_spoken ? "" : " (is lwresd running?)");
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ case ERRSET_INVAL:
+ snprintf(simplebuf, sizeof(simplebuf),
+ "invalid input while looking up RR of type %s for %s",
+ dl->wantedtype_name, dl->fqdn);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY", simplebuf);
+ dl->step = dkl_done;
+ goto done;
+
+ default:
+ snprintf(simplebuf, sizeof(simplebuf), " unknown error %d", success);
+ output_transaction_line(gs, dl->tracking_id, 0, "RETRY", simplebuf);
+ dl->step = dkl_done;
+ done:
+ return;
+
+ case 0:
+ /* everything okay */
+ lwresd_has_spoken = 1;
+ dl->step = dkl_done;
+ break;
+ }
+
+ /* output the rest of the data */
+
+ if(ans->rri_flags & RRSET_VALIDATED) {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "OKAY");
+ snprintf(typebuf, sizeof(typebuf), "AD-%s", dl->wantedtype_name);
+ if(dl->wantedtype_name) free(dl->wantedtype_name);
+ dl->wantedtype_name=xstrdup(typebuf);
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "DNSSEC", "not present");
+ }
+
+ output_transaction_line(gs, dl->tracking_id, 0, "NAME", ans->rri_name);
+
+ for(i=0; i<ans->rri_nrdatas; i++) {
+ struct rdatainfo *ri = &ans->rri_rdatas[i];
+ isc_region_t region;
+ dns_rdata_t rd;
+
+ isc_buffer_clear(gs->iscbuf);
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ if(dl->wantedtype == dns_rdatatype_txt) {
+ /* special treatment for TXT records */
+ unsigned int len, rdatalen, totlen;
+ unsigned char *txtp, *rdata;
+
+ txtp = txtbuf;
+ totlen = 0;
+ rdatalen = ri->rdi_length;
+ rdata = ri->rdi_data;
+
+ while(rdatalen > 0) {
+ len= (unsigned)rdata[0];
+ memcpy(txtp, rdata+1, len);
+ totlen += len;
+ txtp += len;
+ rdata += len+1;
+ rdatalen -= len+1;
+ }
+ *txtp = '\0';
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0,
+ dl->wantedtype_name,
+ totlen, txtbuf);
+
+ } else {
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dl->wantedtype, &region);
+
+ if(dns_rdata_totext(&rd, NULL, gs->iscbuf) != ISC_R_SUCCESS) {
+
+ }
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0,
+ dl->wantedtype_name,
+ (int)isc_buffer_usedlength(gs->iscbuf),
+ (char *)isc_buffer_base(gs->iscbuf));
+ }
+ }
+
+ for(i=0; i<ans->rri_nsigs; i++) {
+ struct rdatainfo *ri = &ans->rri_sigs[i];
+ isc_region_t region;
+ dns_rdata_t rd;
+
+ isc_buffer_clear(gs->iscbuf);
+ memset(&region, 0, sizeof(region));
+ memset(&rd, 0, sizeof(rd));
+
+ region.base = ri->rdi_data;
+ region.length = ri->rdi_length;
+
+ dns_rdata_fromregion(&rd, dns_rdataclass_in,
+ dns_rdatatype_sig, &region);
+ if(dns_rdata_totext(&rd, NULL, gs->iscbuf) != ISC_R_SUCCESS) {
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", "isc totext error");
+ return;
+ }
+
+ output_transaction_line_limited(gs, dl->tracking_id, 0, "SIG",
+ (int)isc_buffer_usedlength(gs->iscbuf),
+ (char *)isc_buffer_base(gs->iscbuf));
+ }
+}
+
+
+
+void lookup_step(dnskey_glob *gs,
+ dnskey_lookup *dl,
+ struct rrsetinfo *ans,
+ int success)
+{
+ /* char simplebuf[80]; */
+ int nextstate;
+
+ nextstate = dkl_done;
+
+ if(dl == NULL)
+ {
+ return;
+ }
+
+ switch(dl->step)
+ {
+ case dkl_start:
+ /* first request done, why are still in this state? */
+ break;
+
+ case dkl_first:
+ /* okay, got the reply from the first step! */
+ process_step_first(gs, dl, ans, success, 0);
+ nextstate = dl->step;
+ break;
+
+ case dkl_cname:
+ /*
+ * we asked for a cname, and we have some result to deal
+ * with here.
+ */
+ process_step_cname(gs, dl, ans, success);
+ nextstate = dl->step;
+ break;
+
+ case dkl_second:
+ /*
+ * we had asked for something, for a cname, and we followed
+ * it, and we'll see what we got back.
+ */
+ process_step_first(gs, dl, ans, success, 1);
+ nextstate = dl->step;
+ break;
+
+ case dkl_done:
+ /* this should not happen, really, just book keeping, so,
+ * just free up the structure, and return.
+ */
+ nextstate = dl->step;
+ return;
+ }
+
+
+ /* we have been through, made a state transition, if we are
+ * done, then do that.
+ */
+ if(nextstate == dkl_done)
+ {
+ output_transaction_line(gs, dl->tracking_id, 0, "DONE", NULL);
+ free_dl(gs, dl);
+ dl=NULL;
+ }
+ return;
+}
+
+void process_dns_reply(dnskey_glob *gs)
+{
+ dnskey_lookup *dl;
+ struct lwres_async_state *plas;
+ struct rrsetinfo *res;
+ int success;
+
+ plas = NULL;
+
+ success = lwres_getrrsetbyname_read(&plas, gs->lwctx, &res);
+
+ /* cast answer back to dnskey_lookup structure */
+ dl = (dnskey_lookup *)plas;
+
+ if(success == LWRES_R_RETRY) {
+ /* XXX we got something from some other weird place!
+ * transmit again, in the hope of getting the right answer
+ */
+ dl->retry_count--;
+ if(dl->retry_count > 0) {
+ lwres_getrrsetbyname_xmit(gs->lwctx, plas);
+ } else {
+ output_transaction_line(gs, dl->tracking_id, 0, "FATAL", "too many retries");
+ free_dl(gs, dl);
+ }
+ return;
+ }
+
+ /* perform next step for this one */
+ lookup_step(gs, dl, res, success);
+}
+
+/*
+ * $Log: lookup.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.3 2003/09/18 02:17:39 mcr
+ * if we have tried a CNAME lookup, then take a NODATA
+ * reply as a no-name.
+ *
+ * Revision 1.2 2003/09/10 17:55:14 mcr
+ * the CNAME message had the s removed, which changes test
+ * results gratuitously.
+ *
+ * Revision 1.1 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.8 b/programs/lwdnsq/lwdnsq.8
new file mode 100644
index 000000000..bb07985f2
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.8
@@ -0,0 +1,250 @@
+.\"Generated by db2man.xsl. Don't modify this, modify the source.
+.de Sh \" Subsection
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp \" Vertical space (when we can't use .PP)
+.if t .sp .5v
+.if n .sp
+..
+.de Ip \" List item
+.br
+.ie \\n(.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+.TH "IPSEC LWDNSQ" 8 "" "" ""
+.SH NAME
+lwdnsq \- lookup items in DNS to help pluto (and others)
+.SH "SYNOPSIS"
+
+.nf
+\fBipsec lwdnsq\fR lwdnsq\fR [\fB\-\-prompt\fR] [\fB\-\-serial\fR]
+.fi
+
+.nf
+\fBipsec lwdnsq\fR lwdnsq\fR [\fB\-\-help\fR]
+.fi
+
+.SH "DESCRIPTION"
+
+.PP
+The \fBipsec lwdnsq\fR is a helper program that does DNS lookups for other programs. It implements an asynchronous interface on stdin/stdout, with an ASCII driven command language.
+
+.PP
+If stdin is a tty or if the \fB\-\-prompt\fR option is given, then it issues a prompt to the user. Otherwise, it is silent, except for results.
+
+.PP
+The program will accept multiple queries concurrently, with each result being marked with the ID provided on the output. The IDs are strings.
+
+.PP
+If the \fB\-\-serial\fR option is given, then the program will not attempt to execute concurrent queries, but will serialize all input and output.
+
+.SH "QUERY LANGUAGE"
+
+.PP
+There are eleven command that the program understands. This is to lookup different types of records in both the forward and reverse maps. Every query includes a queryid, which is returned in the output, on every single line to identify the transaction.
+
+.SS "KEY queryid FQDN"
+
+.PP
+This request looks up the KEY resource record for the given \fBFQDN.\fR.
+
+.SS "KEY4 queryid A.B.C.D"
+
+.PP
+This request looks up the KEY resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa.
+
+.SS "KEY6 queryid A:B::C:D"
+
+.PP
+This request looks up the KEY resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "TXT4 queryid A.B.C.D"
+
+.PP
+This request looks up the TXT resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa.
+
+.SS "TXT6 queryid A:B::C:D"
+
+.PP
+This request looks up the TXT resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "KEY queryid FQDN"
+
+.PP
+This request looks up the IPSECKEY resource record for the given \fBFQDN.\fR. See note about IPSECKEY processing, below.
+
+.SS "IPSECKEY4 queryid A.B.C.D"
+
+.PP
+This request looks up the IPSECKEY resource record found in the reverse map for the IP version 4 address \fBA.B.C.D\fR, i.e. it looks up D.C.B.A.in\-addr.arpa. See special note about IPSECKEY processing, below.
+
+.SS "IPSECKEY6 queryid A:B::C:D"
+
+.PP
+This request looks up the IPSECKEY resource record found in the reverse map for the IPv6 address \fBA:B::C:D\fR, i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int). See special note about IPSECKEY processing, below.
+
+.SS "OE4 queryid A.B.C.D"
+
+.PP
+This request looks an appropriate record for Opportunistic Encryption for the given IP address. This attempts to look for the delegation record. This may be one of IPSECKEY, KEY, or TXT record. Unless configured otherwise, (see OE4 Directives, below), then a query type of ANY will be used to retrieve all relevant records, and all will be returned.
+
+.SS "OE6 queryid A:B::C:D"
+
+.PP
+This request looks an appropriate record for Opportunistic Encryption for the given IPv6 address. This attempts to look for the delegation record. This may be one of IPSECKEY, KEY, or TXT record. Unless configured otherwise, (see OE Directives, below), then a query type of ALL will be used to retrieve all relevant records, and all will be returned. i.e. it looks the 32\-nibble long entry in ip6.arpa (and ip6.int).
+
+.SS "A queryid FQDN"
+
+.PP
+This request looks up the A (IPv4) resource record for the given \fBFQDN.\fR.
+
+.SS "AAAA queryid FQDN"
+
+.PP
+This request looks up the AAAA (IPv6) resource record for the given \fBFQDN.\fR.
+
+.SH "REPLIES TO QUERIES"
+
+.PP
+All replies from the queries are in the following format:
+
+.nf
+
+<ID> <TIME> <TTL> <TYPE> <TYPE\-SPECIFIC> \\n
+
+.fi
+
+
+.TP
+\fIID\fR
+this is the \fBqueryid\fR value that was provided in the query. It is repeated on every line to permit the replies to be properly associated with the query. When the response is not ascribable to particular query (such as for a mis\-formed query), then the query ID "0" will be used.
+
+.TP
+\fITIME\fR
+this is the current time in seconds since epoch.
+
+.TP
+\fITTL\fR
+for answers which have a time to live, this is the current value. The answer is valid for this number of seconds. If there is no useful value here, then the number 0 is used.
+
+.TP
+\fITYPE\fR
+This is the type of the record that is being returned. The types are described in the next section. The TYPE specific data that follows is specific to the type.
+
+
+.PP
+The replies are limited to 4096 bytes, a value defined as \fBLWDNSQ_RESULT_LEN_MAX\fR. This is defined in \fIfreeswan.h\fR.
+
+.PP
+All of the replies which include resource records use the standard presentation format (with no line feeds or carriage returns) in their answer.
+
+.SS "START"
+
+.PP
+This reply indicates that a query has been received and has been started. It serves as an anchor point for timing, as well as an acknowledgement.
+
+.SS "DONE"
+
+.PP
+This reply indicates that a query is entirely over, and no further information from this query will be sent.
+
+.SS "RETRY"
+
+.PP
+This reply indicates that a query is entirely over, but that no data was found. The records may exist, but appropriate servers could not be reached.
+
+.SS "FATAL"
+
+.PP
+This reply indicates that a query is entirely over, and that no data of the type requested could be found. There were no timeouts, and all servers were available and confirmed non\-existances. There may be NXT records returned prior to this.
+
+.SS "CNAME"
+
+.PP
+This is an interim reply, and indicates that a CNAME was found (and followed) while performing the query. The value of the CNAME is present in the type specific section.
+
+.SS "CNAMEFROM"
+
+.PP
+This is an interim reply, and indicates that a CNAME was found. The original name that was queries for was not the canonical name, and this reply indicates the name that was actually followed.
+
+.SS "NAME"
+
+.PP
+This is an interim reply. The original name that was queries for was not the canonical name. This reply indicates the canonical name.
+
+.SS "DNSSEC"
+
+.PP
+This is an interim reply. It is followed either by "OKAY" or "not present. It indicates if DNSSEC was available on the reply.
+
+.SS "TXT and AD-TXT"
+
+.PP
+This is an interim reply. If there are TXT resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "A and AD-A"
+
+.PP
+This is an interim reply. If there are A resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "AAAA and AD-AAAA"
+
+.PP
+This is an interim reply. If there are AAAA resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "PTR and AD-PTR"
+
+.PP
+This is an interim reply. If there are PTR resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "KEY and AD-KEY"
+
+.PP
+This is an interim reply. If there are KEY resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SS "IPSECKEY and AD-IPSECKEY"
+
+.PP
+This is an interim reply. If there are IPSEC resource records in the reply, then each one is presented using this type. If preceeded by AD\-, then this record was signed with DNSSEC.
+
+.SH "SPECIAL IPSECKEY PROCESSING"
+
+.PP
+At the time of this writing, the IPSECKEY resource record is not entirely specified. In particular no resource record number has been assigned. This program assumes that it is resource record number 45. If the file /etc/ipsec.d/lwdnsq.conf exists, and contains a line like
+
+.nf
+
+ipseckey_rr=\fBnumber\fR
+
+.fi
+ then this number will be used instead. The file is read only once at startup.
+
+.SH "OE DIRECTIVES"
+
+.PP
+If the file /etc/ipsec.d/lwdnsq.conf exists, and contains a line like
+
+.nf
+
+queryany=false
+
+.fi
+ then instead of doing an ALL query when looking for OE delegation records, lwdnsq will do a series of queries. It will first look for IPSECKEY, and then TXT record. If it finds neither, it will then look for KEY records of all kinds, although they do not contain delegation information.
+
+.SH "SPECIAL IPSECKEY PROCESSING"
+
+.nf
+
+/etc/ipsec.d/lwdnsq.conf
+
+.fi
+
+.SH AUTHOR
+Michael Richardson <mcr@sandelman.ottawa.on.ca>.
diff --git a/programs/lwdnsq/lwdnsq.c b/programs/lwdnsq/lwdnsq.c
new file mode 100644
index 000000000..2684a7d45
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.c
@@ -0,0 +1,506 @@
+/*
+ * DNS KEY lookup helper
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char tncfg_c_version[] = "RCSID $Id: lwdnsq.c,v 1.1 2004/03/15 20:35:28 as Exp $";
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <freeswan.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <signal.h>
+
+#include <isc/lang.h>
+#include <isc/magic.h>
+#include <isc/types.h>
+#include <isc/result.h>
+#include <isc/mem.h>
+#include <isc/buffer.h>
+#include <isc/region.h>
+#include <dns/name.h>
+#include <dns/rdata.h>
+#include <dns/rdatastruct.h>
+#include <lwres/netdb.h>
+#include <lwres/async.h>
+
+#include "lwdnsq.h"
+
+static void
+usage(char *name)
+{
+ fprintf(stdout,"%s --attach --virtual <virtual-device> --physical <physical-device>\n",
+ name);
+ exit(1);
+}
+
+static struct option const longopts[] =
+{
+ {"prompt", 0, 0, 'i'},
+ {"serial", 0, 0, 's'},
+ {"debug", 0, 0, 'g'},
+ {"regress",0, 0, 'X'},
+ {"ignoreeof",0, 0, 'Z'},
+ {0, 0, 0, 0}
+};
+
+/* globals */
+jmp_buf getMeOut;
+
+void sig_handler(int sig)
+{
+ fprintf(stderr, "Caught signal %d, cleaning up and exiting\n", sig);
+ longjmp(getMeOut, 1);
+}
+
+void cmdprompt(dnskey_glob *gs)
+{
+ if(gs->prompt) {
+ printf("lwdnsq> ");
+ }
+ fflush(gs->cmdproto_out);
+}
+
+void quitprog(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ gs->done=1;
+}
+
+void setdebug(dnskey_glob *gs,
+ int argc,
+ char **argv)
+{
+ if(argc > 1) {
+ gs->debug=strtoul(argv[1],NULL,0);
+ }
+ printf("0 DEBUG is %d\n",gs->debug);
+}
+
+
+int cmdparse(dnskey_glob *gs,
+ char *cmdline)
+{
+ char *argv[256];
+ int argc;
+ char *arg;
+ static const struct cmd_entry {
+ const char *cmdname;
+ void (*cmdfunc)(dnskey_glob *, int, char **);
+ } cmds[]={
+ {"key", lookup_key},
+ {"key4", lookup_key4},
+ {"key6", lookup_key6},
+ {"txt", lookup_txt},
+ {"txt4", lookup_txt4},
+ {"txt6", lookup_txt6},
+ {"ipseckey", lookup_ipseckey},
+ {"ipseckey4", lookup_ipseckey4},
+ {"ipseckey6", lookup_ipseckey6},
+ {"oe4", lookup_oe4},
+ {"oe6", lookup_oe6},
+ {"vpn4", lookup_key4},
+ {"vpn6", lookup_key6},
+ {"quit", quitprog},
+ {"a", lookup_a},
+ {"aaaa", lookup_aaaa},
+ {"debug", setdebug},
+ {NULL, NULL}};
+ const struct cmd_entry *ce = cmds;
+
+ argc=0;
+
+ /* skip initial spaces */
+ while(cmdline && isspace(*cmdline)) {
+ cmdline++;
+ }
+
+ while(cmdline && *cmdline!='\0' &&
+ (arg=strsep(&cmdline, " \t\n"))!=NULL) {
+ if (argc < sizeof(argv)/sizeof(*argv - 1)) {
+ /* ignore arguments that would overflow.
+ * XXX should generate a diagnostic.
+ */
+ argv[argc++]=arg;
+ }
+ while(cmdline && isspace(*cmdline)) {
+ cmdline++;
+ }
+ }
+ argv[argc]=NULL;
+
+ if(argc==0 || argv[0][0]=='\0') {
+ /* ignore empty line */
+ } else if(strcasecmp("help", argv[0]) == 0) {
+ fprintf(gs->cmdproto_out, "0 HELP\n");
+ for (; ce->cmdname != NULL; ce++)
+ fprintf(gs->cmdproto_out, "0 HELP %s\n", ce->cmdname);
+ } else {
+ for (;; ce++) {
+ if (ce->cmdname == NULL) {
+ fprintf(gs->cmdproto_out, "0 FATAL unknown command \"%s\"\n", argv[0]);
+ break;
+ }
+ if(strcasecmp(ce->cmdname, argv[0])==0) {
+ (*ce->cmdfunc)(gs, argc, argv);
+ break;
+ }
+ }
+ }
+
+ if (!gs->done)
+ cmdprompt(gs);
+ return 0;
+}
+
+int cmdread(dnskey_glob *gs,
+ char *buf,
+ int len)
+{
+ unsigned char *nl;
+ int cmdlen;
+
+ cmdlen=0;
+
+ /*
+ * have to handle partial reads and multiple commands
+ * per read, since this may in fact be a file or a pipe.
+ */
+ if((gs->cmdloc + len + 1) > sizeof(gs->cmdbuf)) {
+ fprintf(stderr, "command '%.*s...' is too long, discarding!\n",
+ 40, buf);
+ fflush(stdout);
+
+ gs->cmdloc=0;
+ return 0;
+ }
+ memcpy(gs->cmdbuf+gs->cmdloc, buf, len);
+ gs->cmdloc+=len;
+ gs->cmdbuf[gs->cmdloc]='\0';
+
+ while((nl = strchr(gs->cmdbuf, '\n')) != NULL) {
+ /* found a newline, so turn it into a \0, and process the
+ * command, and then we will pull the rest of the buffer
+ * up.
+ */
+ *nl='\0';
+ cmdlen= nl - gs->cmdbuf +1;
+
+ cmdparse(gs, gs->cmdbuf);
+
+ gs->cmdloc -= cmdlen;
+ memmove(gs->cmdbuf, gs->cmdbuf+cmdlen, gs->cmdloc);
+ }
+ return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *program_name;
+ dnskey_glob gs;
+ int c;
+ static int ignoreeof=0; /* static to avoid longjmp clobber */
+ int ineof;
+
+ memset(&gs, 0, sizeof(dnskey_glob));
+
+#if 0
+ printf("PID: %d\n", getpid());
+ sleep(60);
+#endif
+
+ program_name = argv[0];
+ gs.concurrent = 1;
+
+ if(lwres_async_init(&gs.lwctx) != ERRSET_SUCCESS) {
+ fprintf(stderr, "Can not initialize async context\n");
+ exit(3);
+ }
+
+ if(isc_mem_create(0,0,&gs.iscmem) != ISC_R_SUCCESS) {
+ fprintf(stderr, "Can not initialize isc memory allocator\n");
+ exit(4);
+ }
+
+ if(isc_buffer_allocate(gs.iscmem, &gs.iscbuf, LWDNSQ_RESULT_LEN_MAX)) {
+ fprintf(stderr, "Can not allocate a result buffer\n");
+ exit(5);
+ }
+
+ while((c = getopt_long_only(argc, argv, "dgsiXZ", longopts, 0)) != EOF) {
+ switch(c) {
+ case 'd':
+ gs.debug+=2;
+ break;
+
+ case 'g':
+ gs.debug++;
+ break;
+ case 's':
+ gs.concurrent=0;
+ break;
+ case 'i':
+ gs.prompt=1;
+ break;
+ case 'X':
+ gs.regress++;
+ break;
+
+ case 'Z':
+ ignoreeof=1;
+ break;
+
+ default:
+ usage(program_name);
+ break;
+ }
+ }
+
+ if(gs.debug && ignoreeof) {
+ fprintf(stderr, "Ignoring end of file\n");
+ }
+
+ if(isatty(0)) {
+ gs.prompt=1;
+ }
+
+ /* do various bits of setup */
+ if(setjmp(getMeOut)!=0) {
+ signal(SIGINT, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* cleanup_crap(); */
+
+ exit(1);
+ }
+
+ if(signal(SIGINT, sig_handler) < 0)
+ perror("Setting handler for SIGINT");
+
+ if(signal(SIGPIPE, sig_handler) < 0)
+ perror("Setting handler for SIGINT");
+
+ cmdprompt(&gs);
+
+ ineof = 0;
+ gs.done = 0;
+ gs.cmdproto_out = stdout;
+ gs.l_fds[0].events = POLLIN|POLLHUP;
+ gs.l_fds[0].fd=0;
+
+ gs.l_fds[1].events = POLLIN|POLLHUP|POLLERR;
+ gs.l_fds[1].fd = lwres_async_fd(gs.lwctx);
+
+ gs.l_nfds= 2;
+
+ while(!gs.done)
+ {
+ int timeout;
+ char buf[128];
+ int n;
+ int rlen;
+
+ timeout=-1;
+
+ gs.l_fds[0].revents = 0;
+
+ gs.l_fds[1].events = POLLIN|POLLHUP|POLLERR;
+ gs.l_fds[1].revents = 0;
+ gs.l_fds[1].fd = lwres_async_fd(gs.lwctx);
+
+ if(gs.debug > 1) {
+ fprintf(stderr, "=== invoking poll(,%d,) with %s\n",
+ gs.l_nfds,
+ timeout>0 ? "waittime" : "no wait");
+ for(n = 0; n < gs.l_nfds; n++) {
+ fprintf(stderr, "=== waiting on fd#%d\n",
+ gs.l_fds[n].fd);
+ }
+ fprintf(stderr, "=== inflight: %d\n", gs.dns_inflight);
+ }
+
+ n = poll(gs.l_fds, gs.l_nfds, timeout);
+
+ if(n == 0) {
+ /* timeout! */
+ }
+
+ if(n < 0) {
+ perror("poll");
+ }
+
+ if(gs.debug > 1) {
+ fprintf(stderr, "=== poll returned with %d\n", n);
+ }
+
+ while(n>0) {
+ if((gs.l_fds[0].revents & POLLERR) == POLLERR ||
+ (gs.l_fds[1].revents & POLLERR) == POLLERR)
+ {
+ break;
+ }
+
+ /* see if there are DNS events coming back */
+ if((gs.l_fds[1].revents & POLLIN) == POLLIN) {
+ if(gs.debug > 1) {
+ fprintf(stderr,
+ "=== new responses from lwdnsd\n");
+ }
+
+ process_dns_reply(&gs);
+ fflush(stdout);
+ n--;
+ }
+
+ if(!ignoreeof &&
+ (gs.l_fds[0].revents & POLLHUP) == POLLHUP)
+ {
+ break;
+ }
+
+ if((gs.l_fds[0].revents & POLLIN) == POLLIN) {
+
+ rlen=read(0, buf, sizeof(buf));
+
+ if(gs.debug > 1) {
+ if(rlen > 0) {
+ buf[rlen]='\0';
+ }
+ fprintf(stderr,
+ "=== new commands on fd 0: %d: %s\n",
+ rlen, buf);
+ }
+
+ if(rlen > 0) {
+ cmdread(&gs, buf, rlen);
+ } else if(rlen == 0) {
+ ineof = 1;
+ if(!ignoreeof) {
+ /* EOF, die */
+ gs.done=1;
+ }
+ }
+ n--;
+ }
+
+ }
+
+ if((gs.l_fds[0].revents & POLLHUP) == POLLHUP)
+ {
+ ineof = 1;
+ if(!ignoreeof)
+ {
+ gs.done=1;
+ }
+ }
+
+ if(ignoreeof) {
+ /* if we have exhausted the input,
+ * and there are none in flight,
+ * then exit, finally.
+ */
+ if(ineof) {
+ if(gs.dns_inflight == 0) {
+ gs.done=1;
+ }
+ }
+ }
+
+ if(gs.debug) {
+ fprintf(stderr, "=== ineof: %d inflight: %d\n",
+ ineof, gs.dns_inflight);
+ }
+
+ }
+
+ signal(SIGINT, SIG_DFL);
+ signal(SIGPIPE, SIG_IGN);
+
+ exit(0);
+}
+
+/*
+ * $Log: lwdnsq.c,v $
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.12 2003/09/16 05:01:14 mcr
+ * prefix all debugging with === so that it can be easily removed.
+ *
+ * Revision 1.11 2003/09/10 04:43:52 mcr
+ * final fixes to lwdnsq to exit only when all requests are done,
+ * and we have been told to wait, *OR* if there is an EOF in stdin.
+ *
+ * Revision 1.10 2003/09/03 01:13:24 mcr
+ * first attempt at async capable lwdnsq.
+ *
+ * Revision 1.9 2003/04/02 07:37:57 dhr
+ *
+ * lwdnsq: fix non-deterministic bug in handling batched input
+ *
+ * Revision 1.8 2003/02/08 04:03:06 mcr
+ * renamed --single to --serial.
+ *
+ * Revision 1.7 2003/01/14 03:01:14 dhr
+ *
+ * improve diagnostics; tidy
+ *
+ * Revision 1.6 2002/12/19 07:29:47 dhr
+ *
+ * - avoid (improbable) buffer overflow
+ * - suppress prompt after "quit" command
+ * - add space to prompt to match aesthetics and man page
+ * - elminate a magic number
+ *
+ * Revision 1.5 2002/12/19 07:08:42 dhr
+ *
+ * continue renaming dnskey => lwdnsq
+ *
+ * Revision 1.4 2002/12/12 06:03:41 mcr
+ * added --regress option to force times to be regular
+ *
+ * Revision 1.3 2002/11/25 18:37:48 mcr
+ * make sure that we exit cleanly upon EOF.
+ *
+ * Revision 1.2 2002/11/16 02:53:53 mcr
+ * lwdnsq - with new contract added.
+ *
+ * Revision 1.1 2002/10/30 02:25:31 mcr
+ * renamed version of files from dnskey/
+ *
+ * Revision 1.3 2002/10/09 20:14:16 mcr
+ * make sure to flush stdout at the right time - do it regardless
+ * of whether or not we are printing prompts.
+ *
+ * Revision 1.2 2002/09/30 18:55:54 mcr
+ * skeleton for dnskey helper program.
+ *
+ * Revision 1.1 2002/09/30 16:50:23 mcr
+ * documentation for "dnskey" helper
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.h b/programs/lwdnsq/lwdnsq.h
new file mode 100644
index 000000000..109b39507
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.h
@@ -0,0 +1,121 @@
+/*
+ * DNS KEY lookup global definitions
+ * Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef POLLIN
+#include <poll.h>
+#endif
+
+#include "freeswan.h"
+
+/*
+ * a base-64 encoded 2192 bit key takes:
+ * 2192/8 * 4/3 = 365 bytes.
+ *
+ * a base-64 encoded 16384 bit key takes:
+ * 16384/8*4/3 = 2730 bytes.
+ *
+ * so, we pick 4096 bytes as the maximum.
+ *
+ * Note that TXT records may have an introducer (X-IPsec) and an ID which
+ * is either an IP address or @FQDN that preceeds the base64 encoded key.
+ *
+ */
+
+enum dkl_state {
+ dkl_start, /* no work yet none - initial state */
+ dkl_first, /* sent first DNS request. */
+ dkl_cname, /* sent request for CNAME record */
+ dkl_second, /* sent request for thing CNAME pointed to */
+ dkl_done /* done */
+};
+
+typedef struct dnskey_lookup dnskey_lookup;
+
+struct dnskey_lookup {
+ struct lwres_async_state las;
+ dnskey_lookup *next;
+ char *tracking_id;
+ enum dkl_state step;
+ /* lwres_context_t *ctx; */
+ char *wantedtype_name;
+ dns_rdatatype_t wantedtype;
+ char *fqdn;
+ int cname_count;
+ int last_cname_used;
+ dns_name_t last_cname;
+ int retry_count;
+};
+
+typedef struct dnskey_glob {
+ int debug;
+ int prompt;
+ int concurrent;
+ int done;
+ int regress; /* if 1, then we are doing regression testing */
+ struct pollfd l_fds[5]; /* array of input sources */
+ int l_nfds; /* number of relevant entries */
+ int cmdloc;
+ unsigned char cmdbuf[LWDNSQ_CMDBUF_LEN];
+ FILE *cmdproto_out;
+ dnskey_lookup *dns_outstanding;
+ int dns_inflight;
+ lwres_context_t *lwctx;
+ isc_mem_t *iscmem;
+ isc_buffer_t *iscbuf;
+} dnskey_glob;
+
+/* in cmds.c */
+extern void lookup_key(dnskey_glob *gs,int, char **);
+extern void lookup_key4(dnskey_glob *gs,int, char **);
+extern void lookup_key6(dnskey_glob *gs,int, char **);
+extern void lookup_txt(dnskey_glob *gs,int, char **);
+extern void lookup_txt4(dnskey_glob *gs,int, char **);
+extern void lookup_txt6(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey4(dnskey_glob *gs,int, char **);
+extern void lookup_ipseckey6(dnskey_glob *gs,int, char **);
+extern void lookup_oe4(dnskey_glob *gs,int, char **);
+extern void lookup_oe6(dnskey_glob *gs,int, char **);
+extern void lookup_a(dnskey_glob *gs,int, char **);
+extern void lookup_aaaa(dnskey_glob *gs,int, char **);
+extern void output_transaction_line(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ char *data);
+extern void output_transaction_line_limited(dnskey_glob *gs,
+ char *id,
+ int ttl,
+ char *cmd,
+ int max,
+ char *data);
+
+
+/* lookup code */
+extern void process_dns_reply(dnskey_glob *gs);
+extern void lookup_thing(dnskey_glob *gs,
+ dns_rdatatype_t wantedtype,
+ char *wantedtype_name,
+ char *id,
+ char *fqdn);
+
+/*
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * c-basic-offset: 2
+ * End:
+ *
+ */
diff --git a/programs/lwdnsq/lwdnsq.xml.in b/programs/lwdnsq/lwdnsq.xml.in
new file mode 100644
index 000000000..4c4039120
--- /dev/null
+++ b/programs/lwdnsq/lwdnsq.xml.in
@@ -0,0 +1,446 @@
+<?xml version='1.0'?> <!-- -*- docbook -*- -->
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<article>
+ <articleinfo>
+ <title>lwdnsq</title>
+
+ <author>
+ <firstname>Michael</firstname>
+ <surname>Richardson</surname>
+ <affiliation>
+ <address><email>mcr@sandelman.ottawa.on.ca</email></address>
+ </affiliation>
+ </author>
+
+ <copyright>
+ <year>2003</year>
+ <holder>Michael Richardson</holder>
+ </copyright>
+ </articleinfo>
+
+ <section>
+ <title>Reference</title>
+
+<refentry id="ipsec_lwdnsq">
+
+<refmeta>
+<refentrytitle>ipsec lwdnsq</refentrytitle>
+<manvolnum>8</manvolnum>
+</refmeta>
+
+<refnamediv>
+<refname>lwdnsq</refname>
+<refpurpose>lookup items in DNS to help pluto (and others)</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+
+<cmdsynopsis>
+ <command>ipsec lwdnsq</command>
+ <arg choice="opt"><option>--prompt</option></arg>
+ <arg choice="opt"><option>--serial</option></arg>
+</cmdsynopsis>
+
+<cmdsynopsis>
+ <command>ipsec lwdnsq</command>
+ <arg choice="opt"><option>--help</option></arg>
+</cmdsynopsis>
+
+</refsynopsisdiv>
+
+<refsect1><title>Description</title>
+<para>
+The
+<command>ipsec lwdnsq</command>
+is a helper program that does DNS lookups for other programs. It implements
+an asynchronous interface on stdin/stdout, with an ASCII driven command
+language.
+</para>
+
+<para>
+If stdin is a tty or if the
+<option>--prompt</option>
+option is given, then it issues a prompt to the user. Otherwise, it is
+silent, except for results.
+</para>
+
+<para>
+The program will accept multiple queries concurrently, with each result
+being marked with the ID provided on the output. The IDs are strings.
+</para>
+
+<para>
+If the
+<option>--serial</option>
+option is given, then the program will not attempt to execute concurrent
+queries, but will serialize all input and output.
+</para>
+
+</refsect1>
+
+<refsect1><title>QUERY LANGUAGE</title>
+
+<para>
+There are eleven command that the program understands. This is to lookup
+different types of records in both the forward and reverse maps. Every query
+includes a queryid, which is returned in the output, on every single line to
+identify the transaction.
+</para>
+
+<refsect2><title>KEY <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the KEY resource record for the given <option>FQDN.</option>.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the KEY resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the KEY resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the TXT resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa.
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the TXT resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the IPSECKEY resource record for the given
+<option>FQDN.</option>. See note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>IPSECKEY4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks up the IPSECKEY resource record found in the reverse map for
+the IP version 4 address <option>A.B.C.D</option>, i.e. it looks
+up D.C.B.A.in-addr.arpa. See special note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>IPSECKEY6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks up the IPSECKEY resource record found in the reverse map
+for the IPv6 address <option>A:B::C:D</option>, i.e.
+it looks the 32-nibble long entry in ip6.arpa (and ip6.int). See
+special note about IPSECKEY processing, below.
+</para>
+</refsect2>
+
+<refsect2>
+<title>OE4 <option>queryid</option> <option>A.B.C.D</option></title>
+<para>
+This request looks an appropriate record for Opportunistic
+Encryption for the given IP address. This attempts to look for the
+delegation record. This may be one of IPSECKEY, KEY, or TXT
+record. Unless configured otherwise, (see OE4 Directives, below), then
+a query type of ANY will be used to retrieve all relevant records, and
+all will be returned.
+</para>
+</refsect2>
+
+<refsect2>
+<title>OE6 <option>queryid</option> <option>A:B::C:D</option></title>
+<para>
+This request looks an appropriate record for Opportunistic
+Encryption for the given IPv6 address. This attempts to look for the
+delegation record. This may be one of IPSECKEY, KEY, or TXT
+record. Unless configured otherwise, (see OE Directives, below), then
+a query type of ALL will be used to retrieve all relevant records, and
+all will be returned.
+i.e. it looks the 32-nibble long entry in ip6.arpa (and ip6.int).
+</para>
+</refsect2>
+
+<refsect2>
+<title>A <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the A (IPv4) resource record for the given
+<option>FQDN.</option>.
+</para>
+</refsect2>
+
+<refsect2>
+<title>AAAA <option>queryid</option> <option>FQDN</option></title>
+<para>
+This request looks up the AAAA (IPv6) resource record for the given
+<option>FQDN.</option>.
+</para>
+</refsect2>
+
+</refsect1>
+
+<refsect1><title>Replies to queries</title>
+
+<para>
+All replies from the queries are in the following format:
+<programlisting>
+&lt;ID&gt; &lt;TIME&gt; &lt;TTL&gt; &lt;TYPE&gt; &lt;TYPE-SPECIFIC&gt; \n
+</programlisting>
+
+<variablelist>
+
+<varlistentry><term><parameter>ID</parameter></term>
+<listitem>
+<para>
+this is the <option>queryid</option> value that was provided in
+the query. It is repeated on every line to permit the replies to be
+properly associated with the query. When the response is not ascribable to
+particular query (such as for a mis-formed query), then the query ID "0" will
+be used.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><parameter>TIME</parameter></term>
+<listitem>
+<para>
+this is the current time in seconds since epoch.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><parameter>TTL</parameter></term>
+<listitem>
+<para>
+for answers which have a time to live, this is the current value. The
+answer is valid for this number of seconds. If there is no useful
+value here, then the number 0 is used.
+</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry><term><parameter>TYPE</parameter></term>
+<listitem>
+<para>
+This is the type of the record that is being returned. The types are
+described in the next section. The TYPE specific data that follows is
+specific to the type.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</para>
+
+<para>
+The replies are limited to 4096 bytes, a value defined as
+<constant>LWDNSQ_RESULT_LEN_MAX</constant>. This is defined in
+<filename>freeswan.h</filename>.
+</para>
+
+<para>All of the replies which include resource records use the
+standard presentation format (with no line feeds or carriage returns)
+in their answer.</para>
+
+<refsect2>
+<title>START</title>
+<para>
+This reply indicates that a query has been received and has been
+started. It serves as an anchor point for timing, as well as an acknowledgement.
+</para>
+</refsect2>
+
+<refsect2>
+<title>DONE</title>
+<para>
+This reply indicates that a query is entirely over, and no further
+information from this query will be sent.
+</para>
+</refsect2>
+
+<refsect2>
+<title>RETRY</title>
+<para>
+This reply indicates that a query is entirely over, but that no
+data was found. The records may exist, but appropriate servers could
+not be reached.
+</para>
+</refsect2>
+
+<refsect2>
+<title>FATAL</title>
+<para>
+This reply indicates that a query is entirely over, and that no
+data of the type requested could be found. There were no timeouts, and
+all servers were available and confirmed non-existances. There may be
+NXT records returned prior to this.
+</para>
+</refsect2>
+
+<refsect2>
+<title>CNAME</title>
+<para>
+This is an interim reply, and indicates that a CNAME was found (and
+followed) while performing the query. The value of the CNAME is
+present in the type specific section.
+</para>
+</refsect2>
+
+<refsect2>
+<title>CNAMEFROM</title>
+<para>
+This is an interim reply, and indicates that a CNAME was found. The
+original name that was queries for was not the canonical name, and
+this reply indicates the name that was actually followed.
+</para>
+</refsect2>
+
+<refsect2>
+<title>NAME</title>
+<para>
+This is an interim reply. The original name that was queries for was
+not the canonical name. This reply indicates the canonical name.
+</para>
+</refsect2>
+
+<refsect2>
+<title>DNSSEC</title>
+<para>
+This is an interim reply. It is followed either by "OKAY" or "not
+present.
+It indicates if DNSSEC was available on the reply.
+</para>
+</refsect2>
+
+<refsect2>
+<title>TXT and AD-TXT</title>
+<para>
+This is an interim reply. If there are TXT resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>A and AD-A</title>
+<para>
+This is an interim reply. If there are A resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>AAAA and AD-AAAA</title>
+<para>
+This is an interim reply. If there are AAAA resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>PTR and AD-PTR</title>
+<para>
+This is an interim reply. If there are PTR resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+<refsect2>
+<title>KEY and AD-KEY</title>
+<para>
+This is an interim reply. If there are KEY resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+
+<refsect2>
+<title>IPSECKEY and AD-IPSECKEY</title>
+<para>
+This is an interim reply. If there are IPSEC resource records in the
+reply, then each one is presented using this type. If preceeded by
+AD-, then this record was signed with DNSSEC.
+</para>
+</refsect2>
+
+
+</refsect1>
+
+<refsect1><title>Special IPSECKEY processing</title>
+
+<para>
+At the time of this writing, the IPSECKEY resource record is not
+entirely specified. In particular no resource record number has been
+assigned. This program assumes that it is resource record number
+45. If the file
+@IPSEC_CONFDDIR@/lwdnsq.conf
+exists, and contains a line like
+<programlisting>
+ipseckey_rr=<option>number</option>
+</programlisting>
+then this number will be used instead. The file is read only once at
+startup.
+</para>
+</refsect1>
+
+<refsect1><title>OE Directives</title>
+
+<para>
+If the file
+@IPSEC_CONFDDIR@/lwdnsq.conf
+exists, and contains a line like
+<programlisting>
+queryany=false
+</programlisting>
+then instead of doing an ALL query when looking for OE delegation
+records, lwdnsq will do a series of queries. It will first look for
+IPSECKEY, and then TXT record. If it finds neither, it will then look
+for KEY records of all kinds, although they do not contain delegation
+information.
+</para>
+</refsect1>
+
+<refsect1><title>Special IPSECKEY processing</title>
+
+<programlisting>
+/etc/ipsec.d/lwdnsq.conf
+</programlisting>
+
+</refsect1>
+
+</refentry>
+</section>
+</article>
+
+
+
+
+
+
diff --git a/programs/lwdnsq/states.fig b/programs/lwdnsq/states.fig
new file mode 100644
index 000000000..6a28249ee
--- /dev/null
+++ b/programs/lwdnsq/states.fig
@@ -0,0 +1,66 @@
+#FIG 3.2
+Landscape
+Center
+Metric
+A4
+100.00
+Single
+-2
+1200 2
+6 1305 1530 3330 2205
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3330 2205 3330 1530 1305 1530 1305 2205 3330 2205
+4 1 0 50 0 0 20 0.0000 4 255 1575 2385 1935 initial request\001
+-6
+6 1350 5850 3375 6525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 3375 6525 3375 5850 1350 5850 1350 6525 3375 6525
+4 1 0 50 0 0 20 0.0000 4 135 825 2430 6255 success\001
+-6
+6 4275 2700 6525 3375
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 6525 3375 6525 2700 4275 2700 4275 3375 6525 3375
+4 1 0 50 0 0 20 0.0000 4 195 2115 5400 3150 ASK for CNAME\001
+-6
+6 225 3825 2250 4500
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 2250 4500 2250 3825 225 3825 225 4500 2250 4500
+4 1 0 50 0 0 20 0.0000 4 195 750 1305 4230 failure\001
+-6
+6 5625 4545 7875 5220
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+ 7875 5220 7875 4545 5625 4545 5625 5220 7875 5220
+4 1 0 50 0 0 20 0.0000 4 255 1740 6750 4995 ASK for target\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 3330 1935 4275 2745
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 2250 2205 1305 3825
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 4275 3330 2250 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 2880 2250 2880 5850
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 5895 5220 3375 6120
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 5625 4950 2250 4275
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 6030 3375 6570 4545
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 2 0 1.00 60.00 120.00
+ 7695 4545 6525 3150
+4 0 0 50 0 0 14 0.0000 4 195 1830 3825 2295 ERRSET_NODATA\001
+4 2 0 50 0 0 14 0.0000 4 195 1875 1575 3150 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 150 300 2970 5175 OK\001
+4 0 0 50 0 0 14 0.0000 4 150 300 4500 5895 OK\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 3420 3825 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 195 1875 3420 4500 ERRSET_NONAME\001
+4 0 0 50 0 0 14 0.0000 4 150 300 6390 3960 OK\001
+4 0 0 50 0 0 14 0.0000 4 195 1830 7110 3825 ERRSET_NODATA\001
diff --git a/programs/lwdnsq/states.png b/programs/lwdnsq/states.png
new file mode 100644
index 000000000..ceb5b3c45
--- /dev/null
+++ b/programs/lwdnsq/states.png
Binary files differ
diff --git a/programs/mailkey/.cvsignore b/programs/mailkey/.cvsignore
new file mode 100644
index 000000000..5af485234
--- /dev/null
+++ b/programs/mailkey/.cvsignore
@@ -0,0 +1 @@
+mailkey
diff --git a/programs/mailkey/Makefile b/programs/mailkey/Makefile
new file mode 100644
index 000000000..4b0385823
--- /dev/null
+++ b/programs/mailkey/Makefile
@@ -0,0 +1,41 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=mailkey
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.1 2003/02/22 03:26:55 sam
+# remaining pieces of mailkey
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/mailkey/mailkey.8 b/programs/mailkey/mailkey.8
new file mode 100644
index 000000000..be6b4ff93
--- /dev/null
+++ b/programs/mailkey/mailkey.8
@@ -0,0 +1,47 @@
+.TH IPSEC_MAILKEY 8 "21 Feb 2002"
+.\" RCSID $Id: mailkey.8,v 1.1 2004/03/15 20:35:28 as Exp $
+.SH NAME
+ipsec mailkey \- mail DNS records for Opportunistic Encryption
+.SH SYNOPSIS
+.B ipsec
+.B mailkey
+\-\-me
+my@address.tld
+[
+.B \-\-reverse
+1.2.3.4
+] [
+.B \-\-forward
+hostname.domain.tld
+]
+.SH DESCRIPTION
+.I mailkey
+is a meta-program. It generates a script which will attempt to mail the TXT
+records required to enable Opportunistic Encryption (OE).
+.PP
+An e-mail address for the domain's DNS administrator is derived from SOA records.
+The mail body and destination address are freely editable in the script.
+.PP
+If no administrator can be located, the output file will not be executable.
+.PP
+.TP
+\fB\-\-me\fP\ \fImy@address.tld\fP
+set the Reply-To: address of the mail to be sent.
+.TP
+\fB\-\-forward\fP\ \fIhostname.domain.tld\fP
+the domain name to be used for initator-only OE.
+.TP
+\fB\-\-reverse\fP\ \fI1.2.3.4\fP
+the IP address to be used for full Opportunistic Encryption.
+.PP
+Only one of --forward or --reverse may be specified.
+.SH FILES
+.nf
+/etc/ipsec.secrets
+.fi
+.SH SEE ALSO
+ipsec_showhostkey(8), host(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project <http://www.freeswan.org> by Sam Sgro.
+.SH BUGS
+May produce indeterminate results when processing non-routable IPs.
diff --git a/programs/mailkey/mailkey.in b/programs/mailkey/mailkey.in
new file mode 100755
index 000000000..fecdcf62c
--- /dev/null
+++ b/programs/mailkey/mailkey.in
@@ -0,0 +1,241 @@
+#! /bin/sh
+# mail OE DNS RR info to relevent administrator
+#
+# Copyright (C) 2003 Sam Sgro <sam@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: mailkey.in,v 1.1 2004/03/15 20:35:28 as Exp $
+
+me="ipsec mailkey"
+
+PATH=/sbin:/usr/bin:/usr/local/sbin:@IPSEC_SBINDIR@:$PATH export PATH
+
+reverse=0
+forward=0
+mymail=""
+usage="Usage:
+ $me --me my@address.tld --forward hostname.domain.tld
+ $me --me my@address.tld --reverse 1.2.3.4"
+
+for dummy
+do
+ case "$1" in
+ --help) echo "$usage" ; exit 0 ;;
+ --forward) forward=1 ; reverse=0 ; hostname="$2" ; shift ;;
+ --reverse) reverse=1 ; forward=0 ; reverseip="$2" ; shift ;;
+ --me) mymail="$2" ; shift ;;
+ --) shift ; break ;;
+ -*) echo "$0: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+# only do one of iOE || (pOE/rOE/fOE/insert acronym here) at a time
+# but you have to choose one. Plus, if ya ain't specified your mail address...
+if [ "$forward" -eq "$reverse" ] || [ ! "$mymail" ]
+then
+{
+echo "$usage"; exit 0;
+}
+fi
+
+# Test to see if there is a key to process in the first place.
+test1st=`ipsec showhostkey --txt 1.2.3.4 2>&1`
+test2nd=`echo $test1st | grep TXT`
+if [ ! "$test2nd" ]
+then
+{
+echo "Our attempt to retrieve your RSA key using 'ipsec showhostkey' failed
+with the following error:
+
+"$test1st"
+
+Common concerns: This account must be able to read /etc/ipsec.secrets.
+If you haven't generated your key yet, please run 'ipsec newhostkey'."
+exit 0
+}
+fi
+
+
+# This is where we will save the script.
+save_mail_file=~/"OE_mail_""$reverseip$hostname"
+
+# RSA/SOA processing functions.
+# takes two arguments - the IP address/hostname to be used, and an attempt to guess the
+# beginning of the DNS record for the administrator
+txtprocess(){
+ipsec showhostkey --txt $1 | sed "s/^.* IN TXT/$2. IN TXT/" | grep TXT
+}
+
+# Find the hostmaster part of the SOA.
+# This only works with the "net" portion of in-addr.arpa. commands - 20.168.192.in-addr.arpa. -
+# or the domain portion of FQDNs. The data is prepped using host_data in the individual sections
+# for $forward and $reverse.
+# Note: I've experienced it returning SOAs for non-routeable IP addresses! This needs to be
+# addressed.
+hostprocess(){
+host -t soa $1 | grep SOA | while read a b c d e
+do
+echo $d | sed -e "s/\(^[a-zA-Z0-9-]*\)\.\([a-zA-Z0-9-\.]*\).$/\1@\2/"
+done
+}
+
+# generate the pieces that go into the template, which are dependent on the type of OE.
+if [ "$reverse" -eq 1 ]; then
+{
+# convert the reverse ip to something appropriate for a DNS record.
+arpaip=`echo $reverseip | sed -e "s/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\4.\3.\2.\1.in-addr.arpa/"`
+# prepare data for hostprocess()
+host_data=`echo $arpaip | sed -e "s/^[0-9]*\.\(.*\)/\1/"`
+
+firstsub=" I'm contacting you in your role as the administrator of the domain
+\"$arpaip\" as listed in its SOA record.
+
+ My network security software, which employs IPSec, requires the
+below keying information to be published as a RR in the DNS domain
+which you are responsible for.
+
+"
+
+txt=`txtprocess $reverseip $arpaip`
+secondsub=" To this end, I need you to publish the following TXT record:
+
+--DNS_RESOURCE_RECORDS--
+
+"$txt"
+
+--DNS_RESOURCE_RECORDS--"
+
+thirdsub="to enable full Opportunistic Encryption using the IP address:
+
+"$reverseip
+
+fourthsub="and TXT records are"
+
+proposed_email=`hostprocess $host_data`
+}
+elif [ "$forward" -eq 1 ]; then
+{
+# prepare data for hostprocess()
+# leave only the domain name
+domain_data=`echo $hostname | sed -e "s/.*\.\([a-zA-Z0-9-]*\.[a-zA-Z0-9-]*$\)/\1/"`
+# leave only the host name
+host_data=`echo $hostname | sed -e "s/\(.*\)\.[a-zA-Z0-9-]*\.[a-zA-Z0-9-]*$/\1/"`
+
+firstsub=" I'm contacting you in your role as the administrator of the domain
+\"$hostname\" as listed in its SOA record.
+
+ My network security software, which employs IPSec, requires the
+below keying information to be published as a RR in the DNS domain
+which you are responsible for.
+
+"
+
+txt=`txtprocess @$hostname $host_data`
+secondsub=" To this end, please publish the following TXT record for the hostname
+$hostname:
+
+
+--DNS_RESOURCE_RECORDS--
+
+$txt
+
+--DNS_RESOURCE_RECORDS--"
+thirdsub="to allow me to use the hostname:
+
+"$hostname"
+
+for initiator-only Opportunistic Encryption."
+fourthsub="record is"
+
+proposed_email=`hostprocess $domain_data`
+}
+fi
+
+# Create the template used for the body of the e-mail.
+
+mailbody=$firstsub$secondsub"
+
+
+ Please be careful to preserve the spaces and/or quotation marks as written.
+These are important for the RSA key to survive DNS processing.
+
+ Thanks for your help in securing the 'net!
+
+ $mymail
+ (Generated by '$me' for $mymail)
+
+
+
+Opportunistic Encryption (OE) is the result of ongoing effort by the FreeS/WAN
+project (www.freeswan.org). It allows for the creation of dynamic IPSec
+connections between hosts without pre-arrangement, authenticated via RSA keys
+stored in DNS records.
+
+Technical information on OE can be found in this RFC draft:
+
+http://www.freeswan.org/freeswan_snaps/CURRENT-SNAP/doc/draft-richardson-ipsec-opportunistic.txt
+
+If you have any questions about these TXT records, or about OE in general,
+please direct them to the FreeS/WAN support lists:
+
+users@lists.freeswan.org
+"
+
+# If we managed to find a hostmaster, make the appropriate modifications to the mail's body and
+# our instructions to the user.
+if [ "$proposed_email" ]; then
+{
+
+# This is now converting the mail test into an executable script.
+# Most users will have reached this stage; they can edit the contact_email
+# if they know better than us.
+# -s - Subject line. By extending it, we can "hack" the mail program to
+# include a customized Reply-To header.
+
+mailbody="#!/bin/sh
+#
+# Edit this variable to send this message to an alternate destination
+contact_email=$proposed_email
+
+mail \$contact_email -s 'DNS records for Opportunistic Encryption ($hostname$reverseip)
+Reply-To: $mymail' <<EOF
+
+"$mailbody"
+
+EOF
+"
+
+screenoutput="Executable mail file saved to: "$save_mail_file
+}
+else
+{
+# Slightly different instructions if we have nothing to tell the user.
+
+screenoutput="$me: error: Unable to locate SOA record for this domain. Not generating executable file.
+Sample mail file saved to: "$save_mail_file
+}
+fi
+
+# Create the output that has been prepared.
+echo "$mailbody" > $save_mail_file
+
+# Only make it executable if we've guessed a destination e-mail address.
+if [ "$proposed_email" ]; then
+{
+chmod u+x $save_mail_file
+}
+fi
+
+# Tell the user what'sgoing on.
+echo "$screenoutput"
diff --git a/programs/manual/.cvsignore b/programs/manual/.cvsignore
new file mode 100644
index 000000000..2905494b6
--- /dev/null
+++ b/programs/manual/.cvsignore
@@ -0,0 +1 @@
+manual
diff --git a/programs/manual/Makefile b/programs/manual/Makefile
new file mode 100644
index 000000000..68cfb9110
--- /dev/null
+++ b/programs/manual/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=manual
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/manual/manual.8 b/programs/manual/manual.8
new file mode 100644
index 000000000..a439544da
--- /dev/null
+++ b/programs/manual/manual.8
@@ -0,0 +1,267 @@
+.TH IPSEC_MANUAL 8 "17 July 2001"
+.\" RCSID $Id: manual.8,v 1.1 2004/03/15 20:35:28 as Exp $
+.SH NAME
+ipsec manual \- take manually-keyed IPsec connections up and down
+.SH SYNOPSIS
+.B ipsec
+.B manual
+[
+.B \-\-show
+] [
+.B \-\-showonly
+] [
+.B \-\-other
+]
+.br
+\ \ \ [
+.B \-\-iam
+.RB address "@" interface
+] [
+.B \-\-config
+configfile
+]
+.br
+\ \ \ operation connection
+.sp 0.5
+.B ipsec
+.B manual
+[
+.I options
+]
+.B \-\-union
+operation part ...
+.SH DESCRIPTION
+.I Manual
+manipulates manually-keyed FreeS/WAN IPsec connections,
+setting them up and shutting them down,
+based on the information in the IPsec configuration file.
+In the normal usage,
+.I connection
+is the name of a connection specification in the configuration file;
+.I operation
+is
+.BR \-\-up ,
+.BR \-\-down ,
+.BR \-\-route ,
+or
+.BR \-\-unroute .
+.I Manual
+generates setup (\c
+.BR \-\-route
+or
+.BR \-\-up )
+or
+teardown (\c
+.BR \-\-down
+or
+.BR \-\-unroute )
+commands for the connection and feeds them to a shell for execution.
+.PP
+The
+.B \-\-up
+operation brings the specified connection up, including establishing a
+suitable route for it if necessary.
+.PP
+The
+.B \-\-route
+operation just establishes the route for a connection.
+Unless and until an
+.B \-\-up
+operation is done, packets routed by that route will simply be discarded.
+.PP
+The
+.B \-\-down
+operation tears the specified connection down,
+.I except
+that it leaves the route in place.
+Unless and until an
+.B \-\-unroute
+operation is done, packets routed by that route will simply be discarded.
+This permits establishing another connection to the same destination
+without any ``window'' in which packets can pass without encryption.
+.PP
+The
+.B \-\-unroute
+operation (and only the
+.B \-\-unroute
+operation) deletes any route established for a connection.
+.PP
+In the
+.B \-\-union
+usage, each
+.I part
+is the name of a partial connection specification in the configuration file,
+and the union of all the partial specifications is the
+connection specification used.
+The effect is as if the contents of the partial specifications were
+concatenated together;
+restrictions on duplicate parameters, etc., do apply to the result.
+(The same effect can now be had, more gracefully, using the
+.B also
+parameter in connection descriptions;
+see
+.IR ipsec.conf (5)
+for details.)
+.PP
+The
+.B \-\-show
+option turns on the
+.B \-x
+option of the shell used to execute the commands,
+so each command is shown as it is executed.
+.PP
+The
+.B \-\-showonly
+option causes
+.I manual
+to show the commands it would run, on standard output,
+and not run them.
+.PP
+The
+.B \-\-other
+option causes
+.I manual
+to pretend it is the other end of the connection.
+This is probably not useful except in combination with
+.BR \-\-showonly .
+.PP
+The
+.B \-\-iam
+option causes
+.I manual
+to believe it is running on the host with the specified IP
+.IR address ,
+and that it should use the specified
+.I interface
+(normally it determines all this automatically,
+based on what IPsec interfaces are up and how they are configured).
+.PP
+The
+.B \-\-config
+option specifies a non-standard location for the FreeS/WAN IPsec
+configuration file (default
+.IR /etc/ipsec.conf ).
+.PP
+See
+.IR ipsec.conf (5)
+for details of the configuration file.
+Apart from the basic parameters which specify the endpoints and routing
+of a connection (\fBleft\fR
+and
+.BR right ,
+plus possibly
+.BR leftsubnet ,
+.BR leftnexthop ,
+.BR leftfirewall ,
+their
+.B right
+equivalents,
+and perhaps
+.BR type ),
+a non-\fBpassthrough\fR
+.I manual
+connection needs an
+.B spi
+or
+.B spibase
+parameter and some parameters specifying encryption, authentication, or
+both, most simply
+.BR esp ,
+.BR espenckey ,
+and
+.BR espauthkey .
+Moderately-secure keys can be obtained from
+.IR ipsec_ranbits (8).
+For production use of manually-keyed connections,
+it is strongly recommended that the keys be kept in a separate file
+(with permissions
+.BR rw\-\-\-\-\-\-\- )
+using the
+.B include
+and
+.B also
+facilities of the configuration file (see
+.IR ipsec.conf (5)).
+.PP
+If an
+.B spi
+parameter is given,
+.I manual
+uses that value as the SPI number for all the SAs
+(which are in separate number spaces anyway).
+If an
+.B spibase
+parameter is given instead,
+.I manual
+assigns SPI values by altering the bottom digit
+of that value;
+SAs going from left to right get even digits starting at 0,
+SAs going from right to left get odd digits starting at 1.
+Either way, it is suggested that manually-keyed connections use
+three-digit SPIs with the first digit non-zero,
+i.e. in the range
+.B 0x100
+through
+.BR 0xfff ;
+FreeS/WAN reserves those for manual keying and will not
+attempt to use them for automatic keying (unless requested to,
+presumably by a non-FreeS/WAN other end).
+.SH FILES
+.ta \w'/var/run/ipsec.nexthop'u+4n
+/etc/ipsec.conf default IPsec configuration file
+.br
+/var/run/ipsec.info \fB%defaultroute\fR information
+.SH SEE ALSO
+ipsec(8), ipsec.conf(5), ipsec_spi(8), ipsec_eroute(8), ipsec_spigrp(8),
+route(8)
+.SH HISTORY
+Written for the FreeS/WAN project
+<http://www.freeswan.org/>
+by Henry Spencer.
+.SH BUGS
+It's not nearly as generous about the syntax of subnets,
+addresses, etc. as the usual FreeS/WAN user interfaces.
+Four-component dotted-decimal must be used for all addresses.
+It
+.I is
+smart enough to translate bit-count netmasks to dotted-decimal form.
+.PP
+If the connection specification for a connection is changed between an
+.B \-\-up
+and the ensuing
+.BR \-\-down ,
+chaos may ensue.
+.PP
+The
+.B \-\-up
+operation is not smart enough to notice whether the connection is already up.
+.PP
+.I Manual
+is not smart enough to reject insecure combinations of algorithms,
+e.g. encryption with no authentication at all.
+.PP
+Any non-IPsec route to the other end which is replaced by the
+.B \-\-up
+or
+.B \-\-route
+operation will not be re-established by
+.BR \-\-unroute .
+Whether this is a feature or a bug depends on your viewpoint.
+.PP
+The optional parameters which
+override the automatic
+.BR spibase -based
+SPI assignment are a messy area of the code and bugs are likely.
+.PP
+``Road warrior'' handling,
+and other special forms of setup which
+require negotiation between the two security gateways,
+inherently cannot be done with
+.IR manual .
+.PP
+.I Manual
+generally lags behind
+.I auto
+in support of various features,
+even when implementation \fIwould\fR be possible.
+For example, currently it does not do IPComp content compression.
diff --git a/programs/manual/manual.in b/programs/manual/manual.in
new file mode 100755
index 000000000..bda4bafa0
--- /dev/null
+++ b/programs/manual/manual.in
@@ -0,0 +1,637 @@
+#! /bin/sh
+# user interface to manual keying
+# Copyright (C) 1998, 1999 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: manual.in,v 1.1 2004/03/15 20:35:28 as Exp $
+
+me='ipsec manual'
+usage="Usage:
+ $me [--showonly] --{up|down|route|unroute} name
+ $me [--showonly] --{up|down|route|unroute} --union partname ...
+
+ other options: [--config ipsecconfigfile] [--other] [--show]
+ [--iam ipaddress@interface]"
+
+# make sure outputs of (e.g.) ifconfig are in English
+unset LANG LANGUAGE LC_ALL LC_MESSAGES
+
+showonly=
+config=
+info=/var/run/ipsec.info
+shopts=
+other=0
+union=0
+noinclude=
+interfs=
+op=
+
+for dummy
+do
+ case "$1" in
+ --help) echo "$usage" ; exit 0 ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --show) shopts=-x ;;
+ --showonly) showonly=yes ;;
+ --other) other=1 ;;
+ --union) union=1 ;;
+ --config) config="--config $2" ; shift ;;
+ --noinclude) noinclude=--noinclude ;;
+ --iam) interfs="$2" ; shift ;;
+ --up|--down|--route|--unroute)
+ if test " $op" != " "
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ op="$1"
+ ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+
+case "$op$#:$union" in
+[01]:*) echo "$usage" >&2 ; exit 2 ;;
+2:0) echo "$me: warning: obsolete command syntax used" >&2
+ op="--$2"
+ names="$1"
+ ;;
+[0-9]*:1) ;;
+--*) if test $# -eq 0
+ then
+ echo "$usage" >&2
+ exit 2
+ fi
+ names="$*"
+ ;;
+*) echo "$usage" >&2 ; exit 2 ;;
+esac
+if test " $op" = " "
+then
+ # --union obsolete-syntax case, op is last argument
+ echo "$me: warning: obsolete command syntax used" >&2
+ names=
+ prev=
+ for arg
+ do
+ names="$names $prev"
+ prev="$arg"
+ done
+ op="--$prev"
+fi
+case "$op" in
+--up|--down|--route|--unroute) ;;
+*) echo "$usage" >&2 ; exit 2 ;;
+esac
+
+case "$interfs" in
+'') interfs="`ifconfig |
+ awk ' /^ipsec/ { interf = $1 ; next }
+ /^[^ \t]/ { interf = "" ; next }
+ /^[ \t]*inet addr/ {
+ sub(/:/, " ", $0)
+ if (interf != "")
+ print $3 "@" interf
+ }' | tr '\n' ' '`"
+ ;;
+esac
+
+if test -s $info
+then
+ . $info
+fi
+
+ipsec _confread $config $noinclude $names |
+awk ' BEGIN {
+ FS = "\t"
+ myname = "'"$me"'"
+ err = "cat >&2"
+ op = "'"$op"'"
+ other = '"$other"'
+ names = "'"$names"'"
+ interfs = "'"$interfs"'"
+ ni = split(interfs, terfs, " ")
+ if (ni == 0)
+ fail("no IPsec-enabled interfaces found")
+ for (i = 1; i <= ni; i++) {
+ nc = split(terfs[i], cpts, "@")
+ if (nc != 2)
+ fail("internal error on " terfs[i])
+ interface[cpts[1]] = cpts[2]
+ }
+ draddr = "'"$defaultrouteaddr"'"
+ drnexthop = "'"$defaultroutenexthop"'"
+ s[""] = ""
+ nlspi = 0
+ nrspi = 0
+ failed = 0
+ maskbits[0] = "0.0.0.0"
+ maskbits[1] = "128.0.0.0"
+ maskbits[2] = "192.0.0.0"
+ maskbits[3] = "224.0.0.0"
+ maskbits[4] = "240.0.0.0"
+ maskbits[5] = "248.0.0.0"
+ maskbits[6] = "252.0.0.0"
+ maskbits[7] = "254.0.0.0"
+ maskbits[8] = "255.0.0.0"
+ maskbits[9] = "255.128.0.0"
+ maskbits[10] = "255.192.0.0"
+ maskbits[11] = "255.224.0.0"
+ maskbits[12] = "255.240.0.0"
+ maskbits[13] = "255.248.0.0"
+ maskbits[14] = "255.252.0.0"
+ maskbits[15] = "255.254.0.0"
+ maskbits[16] = "255.255.0.0"
+ maskbits[17] = "255.255.128.0"
+ maskbits[18] = "255.255.192.0"
+ maskbits[19] = "255.255.224.0"
+ maskbits[20] = "255.255.240.0"
+ maskbits[21] = "255.255.248.0"
+ maskbits[22] = "255.255.252.0"
+ maskbits[23] = "255.255.254.0"
+ maskbits[24] = "255.255.255.0"
+ maskbits[25] = "255.255.255.128"
+ maskbits[26] = "255.255.255.192"
+ maskbits[27] = "255.255.255.224"
+ maskbits[28] = "255.255.255.240"
+ maskbits[29] = "255.255.255.248"
+ maskbits[30] = "255.255.255.252"
+ maskbits[31] = "255.255.255.254"
+ maskbits[32] = "255.255.255.255"
+ }
+ $1 == "=" {
+ next
+ }
+ $1 == "!" {
+ if ($2 != "")
+ fail($2)
+ next
+ }
+ $1 != ":" {
+ fail("internal error, unknown type code \"" $1 "\"")
+ }
+ { s[$2] = $3 }
+ function q(s) {
+ return "\"" s "\""
+ }
+ function fail(m) {
+ print myname ": fatal error in " q(names) ": " m |err
+ failed = 1
+ exit
+ }
+ function swap(k, t, l, r) {
+ l = "left" k
+ r = "right" k
+ if ((l in s) && (r in s)) {
+ t = s[l]
+ s[l] = s[r]
+ s[r] = t
+ } else if (l in s) { # but not r
+ s[r] = s[l]
+ delete s[l]
+ } else if (r in s) { # but not l
+ s[l] = s[r]
+ delete s[r]
+ }
+ }
+ function yesno(k) {
+ if ((k in s) && s[k] != "yes" && s[k] != "no")
+ fail("parameter \"" k "\" must be \"yes\" or \"no\"")
+ }
+ function default(k, v) {
+ if (!(k in s))
+ s[k] = v
+ }
+ function need(k) {
+ if (!(k in s))
+ fail("connection has no \"" k "\" parameter specified")
+ if (s[k] == "")
+ fail("parameter \"" k "\" value must be non-empty")
+ }
+ function integer(k) {
+ if (!(k in s))
+ return
+ if (s[k] !~ /^[0-9]+$/)
+ fail("parameter \"" k "\" value must be integer")
+ }
+ function nexthopset(dir, val, k) {
+ k = dir "nexthop"
+ if (k in s)
+ fail("non-default value of " k " is being overridden")
+ if (val != "")
+ s[k] = val
+ else if (k in s)
+ delete s[k]
+ }
+ function leftward( t) {
+ nlspi++
+ if ("spi" in s)
+ return s["spi"]
+ t = spibase spil
+ spil += 2
+ return t
+ }
+ function rightward( t) {
+ nrspi++
+ if ("spi" in s)
+ return s["spi"]
+ t = spibase spir
+ spir += 2
+ return t
+ }
+ function netfix(dir, n, t) {
+ n = s[dir "subnet"]
+ if (n == "%default")
+ n = "0.0.0.0/0"
+ if (n !~ /\//)
+ fail(dir "subnet=" n " has no mask specified")
+ t = split(n, netfixarray, "/")
+ if (t != 2)
+ fail("bad syntax in " dir "subnet=" n)
+ s[dir "net"] = netfixarray[1]
+ s[dir "mask"] = mask(netfixarray[2])
+ }
+ function mask(m) {
+ if (m ~ /\./)
+ return m
+ if (!(m in maskbits))
+ fail("unknown mask syntax \"" m "\"")
+ return maskbits[m]
+ }
+ function bidir(name, l, r) {
+ l = "left" name
+ r = "right" name
+ if (!(l in s) && (name in s))
+ s[l] = s[name]
+ if (!(r in s) && (name in s))
+ s[r] = s[name]
+ if ((l in s) != (r in s))
+ fail("must give both or neither \"" l "\" and \"" \
+ r "\"")
+ }
+ function espspi(src, dest, spi, dir) {
+ if (!("esp" in s))
+ return
+ dir = (dest == me) ? "left" : "right"
+ print "ipsec spi --label", q(names), "--af inet",
+ "--said", ("esp" spi "@" dest), "\\"
+ print "\t--esp", s["esp"], "--src", src, "\\"
+ if ((dir "espauthkey") in s)
+ print "\t--authkey", s[dir "espauthkey"], "\\"
+ if ("espreplay_window" in s)
+ print "\t--replay_window", s["espreplay_window"], "\\"
+ if ((dir "espenckey") in s)
+ print "\t--enckey", s[dir "espenckey"], "&&"
+ else
+ print "\t&&"
+ }
+ function ahspi(src, dest, spi, dir) {
+ if (!("ah" in s))
+ return
+ dir = (dest == me) ? "left" : "right"
+ if (!((dir "ahkey") in s))
+ fail("AH specified but no ahkey= given")
+ print "ipsec spi --label", q(names), "--af inet",
+ "--said", ("ah" spi "@" dest), "\\"
+ print "\t--ah", s["ah"], "--src", src, "\\"
+ if ("ahreplay_window" in s)
+ print "\t--replay_window", s["ahreplay_window"], "\\"
+ print "\t--authkey", s[dir "ahkey"], "&&"
+ }
+ # issue a suitable invocation of updown command
+ function updown(verb, suffix, cmd) {
+ if ("leftupdown" in s) {
+ cmd = s["leftupdown"]
+ if (s["leftfirewall"] == "yes")
+ fail("cannot specify both updown and firewall")
+ } else {
+ cmd = "ipsec _updown"
+ if (s["leftfirewall"] == "yes")
+ cmd = cmd " ipfwadm"
+ }
+ print "PLUTO_VERB=" verb verbsuf " " cmd " " suffix
+ }
+ END {
+ #########
+ if (failed)
+ exit 1
+ default("type", "tunnel")
+ type = s["type"]
+ shunt = 0
+ if (type == "transport") {
+ if ("leftsubnet" in s)
+ fail("type=transport incompatible with leftsubnet")
+ if ("rightsubnet" in s)
+ fail("type=transport incompatible with rightsubnet")
+ } else if (type == "passthrough") {
+ shunt = 1;
+ p = "%pass"
+ } else if (type == "drop" || type == "reject") {
+ shunt = 1;
+ p = "%" type
+ } else if (type != "tunnel")
+ fail("only know how to do types tunnel/transport/passthrough")
+ if (shunt) {
+ if (("ah" in s) || ("esp" in s))
+ fail(type " connection may not specify AH or ESP")
+ } else {
+ if (!("ah" in s) && !("esp" in s))
+ fail("neither AH nor ESP specified for connection")
+ }
+
+ need("left")
+ need("right")
+ if (s["left"] == "%defaultroute") {
+ if (s["right"] == "%defaultroute")
+ fail("left and right cannot both be %defaultroute")
+ if (draddr == "")
+ fail("%defaultroute requested but not known")
+ s["left"] = draddr
+ nexthopset("left", drnexthop)
+ } else if (s["right"] == "%defaultroute") {
+ if (draddr == "")
+ fail("%defaultroute requested but not known")
+ s["right"] = draddr
+ nexthopset("right", drnexthop)
+ }
+
+ leftsub = ("leftsubnet" in s) ? 1 : 0
+ default("leftsubnet", s["left"] "/32")
+ rightsub = ("rightsubnet" in s) ? 1 : 0
+ default("rightsubnet", s["right"] "/32")
+ default("leftfirewall", "no")
+ default("rightfirewall", "no")
+ yesno("leftfirewall")
+ yesno("rightfirewall")
+ integer("espreplay_window")
+ if (("espreplay_window" in s) && s["espreplay_window"] == 0)
+ delete s["espreplay_window"]
+ integer("ahreplay_window")
+ if (("ahreplay_window" in s) && s["ahreplay_window"] == 0)
+ delete s["ahreplay_window"]
+ netfix("left")
+ netfix("right")
+
+ default("leftnexthop", s["right"])
+ default("rightnexthop", s["left"])
+ if (s["leftnexthop"] == s["left"])
+ fail("left and leftnexthop must not be the same")
+ if (s["rightnexthop"] == s["right"])
+ fail("right and rightnexthop must not be the same")
+
+ bidir("espenckey")
+ bidir("espauthkey")
+ bidir("ahkey")
+ if ("spi" in s && "spibase" in s)
+ fail("cannot specify both spi and spibase")
+ if (!shunt) {
+ if ("spibase" in s) {
+ b = s["spibase"]
+ if (b !~ /^0x[0-9a-fA-F]+0$/)
+ fail("bad syntax in spibase -- must be 0x...0")
+ spibase = substr(b, 1, length(b)-1)
+ } else {
+ need("spi")
+ if (s["spi"] !~ /^0x[0-9a-fA-F]+$/)
+ fail("bad syntax in spi -- must be 0x...")
+ }
+ }
+ spir = 0
+ spil = 1
+
+ # who am I?
+ me = ""
+ for (addr in interface) {
+ if (addr == s["left"] || addr == s["right"]) {
+ if (me != "")
+ fail("ambiguous: could be on \"" iface \
+ "\" or \"" interface[addr] "\"")
+ me = addr
+ iface = interface[addr]
+ }
+ }
+ if (me == "")
+ fail("cannot find interface for " s["left"] " or " s["right"])
+ if (other) {
+ if (s["left"] == me)
+ me = s["right"]
+ else if (s["right"] == me)
+ me = s["left"]
+ }
+ havesubnet = leftsubnet
+ if (s["right"] == me) {
+ swap("") # swaps "left" and "right"
+ swap("subnet")
+ swap("nexthop")
+ swap("net")
+ swap("mask")
+ swap("firewall")
+ swap("espspi")
+ swap("ahspi")
+ swap("espenckey")
+ swap("espauthkey")
+ swap("ahkey")
+ swap("updown")
+ t = spil
+ spil = spir
+ spir = t
+ havesubnet = rightsubnet
+ }
+ him = s["right"]
+
+ if (s["leftnexthop"] == "%defaultroute") {
+ if (drnexthop == "")
+ fail("%defaultroute requested but not known")
+ s["leftnexthop"] = drnexthop
+ }
+
+ tspi = rightward()
+ if (type == "tunnel") {
+ espi = rightward()
+ intspi = leftward()
+ } else
+ espi = tspi
+ if (s["rightespspi"] != "")
+ espi = s["rightespspi"]
+ respi = leftward()
+ if (s["leftespspi"] != "")
+ respi = s["leftespspi"]
+ if ("ah" in s) {
+ if ("esp" in s) {
+ aspi = rightward()
+ raspi = leftward()
+ } else {
+ aspi = espi
+ raspi = respi
+ }
+ if (s["rightahspi"] != "")
+ aspi = s["rightahspi"]
+ if (s["leftahspi"] != "")
+ raspi = s["leftahspi"]
+ }
+ routeid = "-net " s["rightnet"] " netmask " s["rightmask"]
+ if (s["rightmask"] == "255.255.255.255")
+ routeid = "-host " s["rightnet"]
+
+ print "PATH=\"'"$PATH"'\""
+ print "export PATH"
+ print "PLUTO_VERSION=1.1"
+ verbsuf = (havesubnet) ? "-client" : "-host"
+ print "PLUTO_CONNECTION=" q(names)
+ print "PLUTO_NEXT_HOP=" s["leftnexthop"]
+ print "PLUTO_INTERFACE=" iface
+ print "PLUTO_ME=" me
+ print "PLUTO_MY_CLIENT=" s["leftsubnet"]
+ print "PLUTO_MY_CLIENT_NET=" s["leftnet"]
+ print "PLUTO_MY_CLIENT_MASK=" s["leftmask"]
+ print "PLUTO_PEER=" him
+ print "PLUTO_PEER_CLIENT=" s["rightsubnet"]
+ print "PLUTO_PEER_CLIENT_NET=" s["rightnet"]
+ print "PLUTO_PEER_CLIENT_MASK=" s["rightmask"]
+ print "export PLUTO_VERSION PLUTO_CONNECTION PLUTO_NEXT_HOP"
+ print "export PLUTO_INTERFACE PLUTO_ME PLUTO_MY_CLIENT"
+ print "export PLUTO_MY_CLIENT_NET PLUTO_MY_CLIENT_MASK PLUTO_PEER"
+ print "export PLUTO_PEER_CLIENT PLUTO_PEER_CLIENT_NET"
+ print "export PLUTO_PEER_CLIENT_MASK"
+
+ if (op == "--up") {
+ print "{"
+ # first, the outbound SAs
+ if (type == "tunnel") {
+ print "ipsec spi --label", q(names), "--af inet",
+ "--said", ("tun" tspi "@" him), "\\"
+ print "\t--ip4", "--src", me, "--dst", him, "&&"
+ }
+ espspi(me, him, espi)
+ ahspi(me, him, aspi)
+ if (nrspi > 1) {
+ # group them
+ printf "ipsec spigrp --label %s --said ", q(names)
+ if (type == "tunnel")
+ printf "tun%s@%s ", tspi, him
+ if (("esp" in s))
+ printf "esp%s@%s ", espi, him
+ if ("ah" in s)
+ printf "ah%s@%s ", aspi, him
+ printf " &&\n"
+ }
+ # inbound SAs
+ if (type == "tunnel") {
+ print "ipsec spi --label", q(names), "--af inet",
+ "--said", ("tun" intspi "@" me), "\\"
+ print "\t--ip4", "--src", him, "--dst", me, "&&"
+ }
+ espspi(him, me, respi)
+ ahspi(him, me, raspi)
+ if (nlspi > 1) {
+ # group them
+ printf "ipsec spigrp --label %s --said ", q(names)
+ if (type == "tunnel")
+ printf "tun%s@%s ", intspi, me
+ if (("esp" in s))
+ printf "esp%s@%s ", respi, me
+ if ("ah" in s)
+ printf "ah%s@%s ", raspi, me
+ printf " &&\n"
+ }
+ # with the SAs in place, eroute to them
+ print "ipsec eroute --label", q(names),
+ "--eraf inet --replace", "\\"
+ if (!shunt) {
+ if (type == "tunnel")
+ p = "tun"
+ else if (("esp" in s))
+ p = "esp"
+ else
+ p = "ah"
+ p = p tspi "@" him
+ }
+ print "\t--src", s["leftsubnet"], "--dst", s["rightsubnet"],
+ "--said", p, "&&"
+ # with the eroute in place, NOW we can route to it
+ #print "{ route del", routeid, "2>/dev/null ; true ; } &&"
+ updown("prepare", "&&")
+ #print "route add", routeid, "dev", iface, "gw",
+ # s["leftnexthop"], "&&"
+ updown("route", "&&")
+ # and with all processing in place, we can penetrate firewall
+ #if (s["leftfirewall"] == "yes") {
+ # print "ipfwadm -F -i accept -b -S", s["leftsubnet"],
+ # "-D", s["rightsubnet"], "&&"
+ #}
+ updown("up", "&&")
+ print "true"
+ print "} || {"
+ } else if (op == "--route") {
+ #print "{ route del", routeid, "2>/dev/null ; true ; } &&"
+ updown("prepare", "&&")
+ #print "route add", routeid, "dev", iface, "gw",
+ # s["leftnexthop"]
+ updown("route")
+ exit 0
+ } else if (op == "--unroute") {
+ #print "route del", routeid, "dev", iface, "gw",
+ # s["leftnexthop"]
+ updown("unroute")
+ exit 0
+ } else # down
+ print "{"
+
+ # now do "down", unconditionally, since the desired output for "up"
+ # is { up && up && up && true } || { down ; down ; down }
+ # tear things down in fairly strict reverse order
+ #if (s["leftfirewall"] == "yes")
+ # print "ipfwadm -F -d accept -b -S", s["leftsubnet"],
+ # "-D", s["rightsubnet"]
+ updown("down")
+ #print "route del", routeid, "dev", iface, "gw", s["leftnexthop"]
+ print "# do not delete route"
+ print "ipsec eroute --label", q(names), "--eraf inet --del", "\\"
+ print "\t--src", s["leftsubnet"], "--dst", s["rightsubnet"]
+ #if ("ah" in s) {
+ # print "ipsec spi --label", q(names), "--af inet", "--del",
+ # "--said", ("ah" raspi "@" me)
+ #}
+ #if ("esp" in s) {
+ # print "ipsec spi --label", q(names), "--af inet", "--del",
+ # "--said", ("esp" respi "@" me)
+ #}
+ if (!shunt) {
+ if (type == "tunnel")
+ p = "tun"
+ else if (("esp" in s))
+ p = "esp"
+ else
+ p = "ah"
+ print "ipsec spi --label", q(names), "--af inet", "--del",
+ "--said", (p tspi "@" him),
+ " # outbound"
+ print "ipsec spi --label", q(names), "--af inet", "--del",
+ "--said", (p intspi "@" me),
+ " # inbound"
+ }
+
+ if (op == "--up")
+ print "} 2>/dev/null"
+ else
+ print "}"
+ #########
+ }' |
+if test $showonly
+then
+ cat
+else
+ sh $shopts
+fi
diff --git a/programs/openac/Makefile b/programs/openac/Makefile
new file mode 100644
index 000000000..7aeacee0a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/openac/build.c b/programs/openac/build.c
new file mode 100644
index 000000000..bd3df6fee
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#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(&notBefore, ASN1_GENERALIZEDTIME)
+ , timetoasn1(&notAfter, 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/programs/openac/build.h b/programs/openac/build.h
new file mode 100644
index 000000000..deeddda04
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <time.h>
+
+#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/programs/openac/loglite.c b/programs/openac/loglite.c
new file mode 100644
index 000000000..b1763cc9f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h> /* used only if MSG_NOSIGNAL not defined */
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+
+#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/programs/openac/openac.8 b/programs/openac/openac.8
new file mode 100644
index 000000000..8e609a1b1
--- /dev/null
+++ b/programs/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 <users@lists.strongswan.org> mailing list.
diff --git a/programs/openac/openac.c b/programs/openac/openac.c
new file mode 100755
index 000000000..524a302d7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <time.h>
+#include <gmp.h>
+
+#include <freeswan.h>
+
+#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 <filename>]"
+ " [--quiet]"
+#ifdef DEBUG
+ " \\\n\t"
+ " [--debug-all]"
+ " [--debug-parsing]"
+ " [--debug-raw]"
+ " [--debug-private]"
+#endif
+ " \\\n\t"
+ " [--days <days>]"
+ " [--hours <hours>]"
+ " \\\n\t"
+ " [--startdate <YYYYMMDDHHMMSSZ>]"
+ " [--enddate <YYYYMMDDHHMMSSZ>]"
+ " \\\n\t"
+ " --cert <certfile>"
+ " --key <keyfile>"
+ " [--password <password>]"
+ " \\\n\t"
+ " --usercert <certfile>"
+ " --groups <attr1,attr2,..>"
+ " --out <filename>"
+ "\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 <filename> */
+ {
+ 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("<days> 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("<hours> 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/programs/pf_key/.cvsignore b/programs/pf_key/.cvsignore
new file mode 100644
index 000000000..323068235
--- /dev/null
+++ b/programs/pf_key/.cvsignore
@@ -0,0 +1 @@
+pf_key
diff --git a/programs/pf_key/Makefile b/programs/pf_key/Makefile
new file mode 100644
index 000000000..6af45c8d1
--- /dev/null
+++ b/programs/pf_key/Makefile
@@ -0,0 +1,49 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:28 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM:=pf_key
+EXTRA5MAN=${PROGRAM}.5
+
+LIBS:=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:28 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.3 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.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
+
diff --git a/programs/pf_key/pf_key.5 b/programs/pf_key/pf_key.5
new file mode 100644
index 000000000..f5eab9a96
--- /dev/null
+++ b/programs/pf_key/pf_key.5
@@ -0,0 +1,122 @@
+.TH IPSEC_PF_KEY 5 "29 Jun 2000"
+.\"
+.\" RCSID $Id: pf_key.5,v 1.1 2004/03/15 20:35:28 as Exp $
+.\"
+.SH NAME
+ipsec_pf_key \- lists PF_KEY sockets registered with KLIPS
+.SH SYNOPSIS
+.B cat
+.B /proc/net/pf_key
+.SH DESCRIPTION
+.I /proc/net/pf_key
+is a read-only file which lists the presently open PF_KEY sockets on the
+local system and their parameters.
+.PP
+Each line lists one PF_KEY socket.
+A table entry consists of:
+.IP + 3
+sock pointer (sock)
+.IP +
+PID of the socket owner (pid)
+.IP +
+flag to indicate if the socket is dead (d)
+.IP +
+socket wait queue (sleep)
+.IP +
+socket pointer (socket)
+.IP +
+next socket in chain (next)
+.IP +
+previous socket in chain (prev)
+.IP +
+last socket error (e)
+.IP +
+pointer to destruct routine (destruct)
+.IP +
+is this a reused socket (r)
+.IP +
+has this socket been zapped (z)
+.IP +
+socket family to which this socket belongs (fa)
+.IP +
+local port number (n)
+.IP +
+protocol version number (p)
+.IP +
+Receive queue bytes committed (r)
+.IP +
+Transmit queue bytes committed (w)
+.IP +
+option memory allocations (o)
+.IP +
+size of send buffer in bytes (sndbf)
+.IP +
+timestamp in seconds (stamp)
+.IP +
+socket flags (Flags)
+.IP +
+socket type (Type)
+.IP +
+connection state (St)
+.BR
+.SH EXAMPLES
+.TP
+.\".B "sock pid d sleep socket next prev e destruct r z fa n p r w o sndbf stamp Flags Type St"
+.TP
+.B c3b8c140 3553 0 c0599818 c05997fc 0 0 0 0 1 0 15 0 2 0 0 0 65535 0.103232 00000000 00000003 01
+.LP
+shows that there is one pf_key socket set up that starts at
+.BR c3b8c140 ,
+whose owning process has PID
+.BR 3553 ,
+the socket is not dead, its wait queue is at
+.BR c0599818 ,
+whose owning socket is at
+.BR c05997fc ,
+with no other sockets in the chain, no errors, no destructor, it is a
+reused socket which has not been zapped, from protocol family
+.BR 15
+(PF_KEY), local port number
+.BR 0 ,
+protocol socket version
+.BR 2 ,
+no memory allocated to transmit, receive or option queues, a send buffer
+of almost
+.BR 64kB ,
+a timestamp of
+.BR 0.103232 ,
+no flags set, type
+.BR 3 ,
+in state
+.BR 1 .
+.SH "FILES"
+/proc/net/pf_key
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_eroute(5), ipsec_spi(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_tncfg(8), ipsec_version(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: pf_key.5,v $
+.\" Revision 1.1 2004/03/15 20:35:28 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.4 2002/04/24 07:35:39 mcr
+.\" Moved from ./klips/utils/pf_key.5,v
+.\"
+.\" Revision 1.3 2001/01/23 23:51:49 rgb
+.\" Fix outdated references to /proc/net/ipsec_pf_key.
+.\"
+.\" Revision 1.2 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.1 2000/06/30 06:19:27 rgb
+.\" manpages for the last two /proc/net/ipsec* files that don't have a
+.\" corresponding utility.
+.\"
+.\"
+.\"
diff --git a/programs/pf_key/pf_key.8 b/programs/pf_key/pf_key.8
new file mode 100644
index 000000000..dd42bf541
--- /dev/null
+++ b/programs/pf_key/pf_key.8
@@ -0,0 +1,73 @@
+.TH IPSEC_PF_KEY 8 "17 Oct 2001"
+.\"
+.\" RCSID $Id: pf_key.8,v 1.2 2005/07/07 19:07:43 as Exp $
+.\"
+.SH NAME
+pf_key \- shows pfkey messages emitted by the kernel
+.SH SYNOPSIS
+.B pf_key
+.B \-\-ah
+.B \-\-esp
+.B \-\-ipip
+.B \-\-ipcomp
+.B \-\-daemon
+.I file
+.BR hmac-md5-96 | hmac-sha1-96
+.SH DESCRIPTION
+.B pf_key
+is a program to open a PF_KEY socket and print all messages that are received
+from it. With no options, it will register itself to receive key requests for
+AH, ESP, IPIP and IPCOMP security associations. If given more specific
+options, then it will listen only to those protocols which are listed.
+.PP
+If the messages are recognized, the messages will be decoded.
+.PP
+If the option
+.B \-\-daemon
+is provided, then after doing the registrations, the program will fork
+into the background. The provided file will be opened and the process ID of
+the background process will be written to it. This option is present to
+present race conditions in regression testing.
+.SH EXAMPLES
+.TP
+.\".B "pfkey v.2 msg. type 3 seq=20 len=2 errno=22 satype=3"
+.SH "FILES"
+/proc/net/pf_key
+.SH "SEE ALSO"
+pf_key(5), ipsec(8), ipsec_manual(8), ipsec_eroute(5), ipsec_spi(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_tncfg(8), ipsec_version(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Michael Richardson <mcr@freeswan.org>
+.\"
+.\" $Log: pf_key.8,v $
+.\" Revision 1.2 2005/07/07 19:07:43 as
+.\" fixed man page type
+.\"
+.\" Revision 1.1 2004/03/15 20:35:28 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.4 2002/07/16 02:53:42 mcr
+.\" added --daemon <pidfile> to "ipsec pf_key" command.
+.\" this is used in *-trap-* tests to avoid race conditions between
+.\" registration of PF_KEY listeners and arrival of first test packet.
+.\"
+.\" Revision 1.3 2002/04/24 07:35:39 mcr
+.\" Moved from ./klips/utils/pf_key.8,v
+.\"
+.\" Revision 1.2 2001/11/23 07:23:14 mcr
+.\" pulled up klips2 Makefile and pf_key code.
+.\"
+.\" Revision 1.1.2.1 2001/10/23 18:49:12 mcr
+.\" renamed man page to section 8.
+.\" added --ah, --esp, --ipcomp and --ipip to control which
+.\" protocols are printed.
+.\" incomplete messages which include at least an sadb header are printed.
+.\"
+.\" Revision 1.1.2.1 2001/10/17 23:25:37 mcr
+.\" added "pk_key" program to dump raw kernel pf messages.
+.\" (program is still skeletal)
+.\"
+.\"
+.\"
diff --git a/programs/pf_key/pf_key.c b/programs/pf_key/pf_key.c
new file mode 100644
index 000000000..af7365d65
--- /dev/null
+++ b/programs/pf_key/pf_key.c
@@ -0,0 +1,353 @@
+/*
+ * @(#) pfkey socket manipulator/observer
+ *
+ * Copyright (C) 2001 Richard Guy Briggs <rgb@freeswan.org>
+ * and Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: pf_key.c,v 1.2 2004/04/20 21:23:25 as Exp $
+ *
+ */
+
+/*
+ * This program opens a pfkey socket and prints all messages that it sees.
+ *
+ * This can be used to diagnose problems.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+
+#include <sys/socket.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <freeswan.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+char *progname;
+uint32_t pfkey_seq = 0;
+int pfkey_sock;
+
+static void
+Usage(char *progname)
+{
+ fprintf(stderr, "%s: Usage: %s [--help]\n"
+ "\tby default listens for AH, ESP, IPIP and IPCOMP\n"
+ "\t--daemon <file> fork before printing, stuffing the PID in the file\n"
+ "\t--ah listen for AH messages\n"
+ "\t--esp listen for ESP messages\n"
+ "\t--ipip listen for IPIP messages\n"
+ "\t--ipcomp listen for IPCOMP messages\n",
+ progname, progname);
+ exit(1);
+}
+
+void
+pfkey_register(uint8_t satype) {
+ /* for registering SA types that can be negotiated */
+ int error = 0;
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+
+ pfkey_extensions_init(extensions);
+ if((error = pfkey_msg_hdr_build(&extensions[0],
+ SADB_REGISTER,
+ satype,
+ 0,
+ ++pfkey_seq,
+ getpid()))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ progname, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN))) {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n",
+ progname, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+ if(write(pfkey_sock, pfkey_msg,
+ pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) !=
+ (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
+ /* cleanup code here */
+ fprintf(stderr, "%s: Trouble writing to channel PF_KEY.\n", progname);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+}
+
+int dienow;
+
+void controlC(int foo)
+{
+ fflush(stdout);
+ printf("%s: Exiting on signal 15\n", progname);
+ fflush(stderr);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt;
+ ssize_t readlen;
+ unsigned char pfkey_buf[256];
+ struct sadb_msg *msg;
+ int fork_after_register;
+ char *pidfilename;
+
+ static int ah_register;
+ static int esp_register;
+ static int ipip_register;
+ static int ipcomp_register;
+
+ static struct option long_options[] =
+ {
+ {"help", no_argument, 0, 'h'},
+ {"daemon", required_argument, 0, 'f'},
+ {"ah", no_argument, &ah_register, 1},
+ {"esp", no_argument, &esp_register, 1},
+ {"ipip", no_argument, &ipip_register, 1},
+ {"ipcomp", no_argument, &ipcomp_register, 1},
+ };
+
+ ah_register = 0;
+ esp_register = 0;
+ ipip_register = 0;
+ ipcomp_register=0;
+ dienow = 0;
+ fork_after_register=0;
+ pidfilename=NULL;
+
+ progname = argv[0];
+ if(strrchr(progname, '/')) {
+ progname=strrchr(progname, '/')+1;
+ }
+
+ while((opt = getopt_long(argc, argv, "hf:",
+ long_options, NULL)) != EOF) {
+ switch(opt) {
+ case 'f':
+ pidfilename=optarg;
+ fork_after_register=1;
+ break;
+ case 'h':
+ Usage(progname);
+ break;
+ case '0':
+ /* it was a long option with a flag */
+ break;
+ }
+ }
+
+ if((pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0) {
+ fprintf(stderr, "%s: failed to open PF_KEY family socket: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+
+ if(ah_register == 0 &&
+ esp_register== 0 &&
+ ipip_register==0 &&
+ ipcomp_register==0) {
+ ah_register=1;
+ esp_register=1;
+ ipip_register=1;
+ ipcomp_register=1;
+ }
+
+ if(ah_register) {
+ pfkey_register(SADB_SATYPE_AH);
+ }
+ if(esp_register) {
+ pfkey_register(SADB_SATYPE_ESP);
+ }
+ if(ipip_register) {
+ pfkey_register(SADB_X_SATYPE_IPIP);
+ }
+ if(ipcomp_register) {
+ pfkey_register(SADB_X_SATYPE_COMP);
+ }
+
+ if(fork_after_register) {
+ /*
+ * to aid in regression testing, we offer to register
+ * everything first, and then we fork. As part of this
+ * we write the PID of the new process to a file
+ * provided.
+ */
+ int pid;
+ FILE *pidfile;
+
+ fflush(stdout);
+ fflush(stderr);
+
+ pid=fork();
+ if(pid!=0) {
+ /* in parent! */
+ exit(0);
+ }
+
+ if((pidfile=fopen(pidfilename, "w"))==NULL) {
+ perror(pidfilename);
+ } else {
+ fprintf(pidfile, "%d", getpid());
+ fclose(pidfile);
+ }
+ }
+
+ signal(SIGINT, controlC);
+ signal(SIGTERM, controlC);
+
+ while((readlen = read(pfkey_sock, pfkey_buf, sizeof(pfkey_buf))) > 0) {
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ msg = (struct sadb_msg *)pfkey_buf;
+
+ /* first, see if we got enough for an sadb_msg */
+ if((size_t)readlen < sizeof(struct sadb_msg)) {
+ printf("%s: runt packet of size: %d (<%lu)\n",
+ progname, (int)readlen, (unsigned long)sizeof(struct sadb_msg));
+ continue;
+ }
+
+ /* okay, we got enough for a message, print it out */
+ printf("\npfkey v%d msg. type=%d(%s) seq=%d len=%d pid=%d errno=%d satype=%d(%s)\n",
+ msg->sadb_msg_version,
+ msg->sadb_msg_type,
+ pfkey_v2_sadb_type_string(msg->sadb_msg_type),
+ msg->sadb_msg_seq,
+ msg->sadb_msg_len,
+ msg->sadb_msg_pid,
+ msg->sadb_msg_errno,
+ msg->sadb_msg_satype,
+ satype2name(msg->sadb_msg_satype));
+
+ if((size_t)readlen != msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)
+ {
+ printf("%s: packet size read from socket=%d doesn't equal sadb_msg_len %d * %u; message not decoded\n",
+ progname,
+ (int)readlen,
+ msg->sadb_msg_len,
+ (int) IPSEC_PFKEYv2_ALIGN);
+ continue;
+ }
+
+ pfkey_lib_debug = PF_KEY_DEBUG_PARSE_STRUCT;
+ if (pfkey_msg_parse(msg, NULL, extensions, EXT_BITS_OUT)) {
+ printf("%s: unparseable PF_KEY message.\n",
+ progname);
+ } else {
+ printf("%s: parseable PF_KEY message.\n",
+ progname);
+ }
+ }
+ printf("%s: exited normally\n", progname);
+ exit(0);
+}
+
+/*
+ * $Log: pf_key.c,v $
+ * Revision 1.2 2004/04/20 21:23:25 as
+ * int cast fix for 64 bit platforms
+ *
+ * Revision 1.1 2004/03/15 20:35:28 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.15 2003/09/10 00:01:30 mcr
+ * fixes for gcc 3.3 from Matthias Bethke <Matthias.Bethke@gmx.net>
+ *
+ * Revision 1.14 2002/10/09 03:12:05 dhr
+ *
+ * [kenb+dhr] 64-bit fixes
+ *
+ * Revision 1.13 2002/09/20 05:02:15 rgb
+ * Cleaned up pfkey_lib_debug usage.
+ *
+ * Revision 1.12 2002/09/13 23:02:23 rgb
+ * Type fiddling to tame ia64 compiler.
+ * Added text labels to elucidate numeric values presented.
+ *
+ * Revision 1.11 2002/08/26 03:05:25 mcr
+ * duh, pf_key much catch SIGTERM as well as SIGINT...
+ *
+ * Revision 1.10 2002/08/13 19:01:27 mcr
+ * patches from kenb to permit compilation of FreeSWAN on ia64.
+ * des library patched to use proper DES_LONG type for ia64.
+ *
+ * Revision 1.9 2002/07/16 02:53:42 mcr
+ * added --daemon <pidfile> to "ipsec pf_key" command.
+ * this is used in *-trap-* tests to avoid race conditions between
+ * registration of PF_KEY listeners and arrival of first test packet.
+ *
+ * Revision 1.8 2002/06/17 04:32:55 mcr
+ * exit nicely from pf_key when SIGINT (^C) is sent.
+ * This is needed so that the stdout will flush properly.
+ *
+ * Revision 1.7 2002/04/24 07:55:32 mcr
+ * #include patches and Makefiles for post-reorg compilation.
+ *
+ * Revision 1.6 2002/04/24 07:35:39 mcr
+ * Moved from ./klips/utils/pf_key.c,v
+ *
+ * Revision 1.5 2002/03/08 21:44:04 rgb
+ * Update for all GNU-compliant --version strings.
+ *
+ * Revision 1.4 2001/11/27 05:19:06 mcr
+ * added extra newline between packets.
+ * set pfkey_lib_debug to enum rather than just to "1".
+ *
+ * Revision 1.3 2001/11/27 03:35:29 rgb
+ * Added stdlib *again*.
+ *
+ * Revision 1.2 2001/11/23 07:23:14 mcr
+ * pulled up klips2 Makefile and pf_key code.
+ *
+ * Revision 1.1.2.5 2001/10/23 18:49:12 mcr
+ * renamed man page to section 8.
+ * added --ah, --esp, --ipcomp and --ipip to control which
+ * protocols are printed.
+ * incomplete messages which include at least an sadb header are printed.
+ *
+ * Revision 1.1.2.4 2001/10/22 21:50:51 rgb
+ * Added pfkey register for AH, ESP, IPIP and COMP.
+ *
+ * Revision 1.1.2.3 2001/10/21 21:51:06 rgb
+ * Bug fixes to get working.
+ *
+ * Revision 1.1.2.2 2001/10/20 22:45:31 rgb
+ * Added check for exact length and a call to message parser to get some
+ * idea of the contents of each extension.
+ *
+ * Revision 1.1.2.1 2001/10/17 23:25:37 mcr
+ * added "pk_key" program to dump raw kernel pf messages.
+ * (program is still skeletal)
+ *
+ *
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ *
+ */
diff --git a/programs/pluto/.cvsignore b/programs/pluto/.cvsignore
new file mode 100644
index 000000000..fb96dae41
--- /dev/null
+++ b/programs/pluto/.cvsignore
@@ -0,0 +1,3 @@
+_pluto_adns
+pluto
+whack
diff --git a/programs/pluto/Makefile b/programs/pluto/Makefile
new file mode 100644
index 000000000..515b3fac0
--- /dev/null
+++ b/programs/pluto/Makefile
@@ -0,0 +1,1090 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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 <resolver.h>
+# 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
+
+# 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}/packaging/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}/packaging/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}/packaging/utils/manlink pluto.8 | \
+ while read from to; \
+ do\
+ echo $(PMANDIR)/$$to; \
+ done
+ @echo $(FMANDIR)/ipsec.secrets.5
+ @sh ${FREESWANSRCDIR}/packaging/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/programs/pluto/PLUTO-CONVENTIONS b/programs/pluto/PLUTO-CONVENTIONS
new file mode 100644
index 000000000..5288dd2bb
--- /dev/null
+++ b/programs/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/programs/pluto/TODO b/programs/pluto/TODO
new file mode 100644
index 000000000..7db4a9ebc
--- /dev/null
+++ b/programs/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/programs/pluto/ac.c b/programs/pluto/ac.c
new file mode 100644
index 000000000..bcf5f80d1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+
+#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(&current_time);
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log(" not before : %s", timetoa(&ac->notBefore, TRUE));
+ DBG_log(" current time: %s", timetoa(&current_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/programs/pluto/ac.h b/programs/pluto/ac.h
new file mode 100644
index 000000000..3913d745d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/adns.c b/programs/pluto/adns.c
new file mode 100644
index 000000000..c5977d23c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h> /* ??? for h_errno */
+
+#include <freeswan.h>
+
+/* GCC magic! */
+#ifdef GCC_LINT
+# define UNUSED __attribute__ ((unused))
+#else
+# define UNUSED /* ignore */
+#endif
+
+#include "constants.h"
+#include "adns.h" /* needs <resolv.h> */
+
+/* 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/programs/pluto/adns.h b/programs/pluto/adns.h
new file mode 100644
index 000000000..00fc4ad07
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/alg/Config.ike_alg b/programs/pluto/alg/Config.ike_alg
new file mode 100644
index 000000000..0fcda4cad
--- /dev/null
+++ b/programs/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/programs/pluto/alg/Makefile b/programs/pluto/alg/Makefile
new file mode 100644
index 000000000..9732cc80e
--- /dev/null
+++ b/programs/pluto/alg/Makefile
@@ -0,0 +1,93 @@
+# pluto/alg Makefile
+# Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/programs/pluto/alg/Makefile.ike_alg_aes b/programs/pluto/alg/Makefile.ike_alg_aes
new file mode 100644
index 000000000..12009ba5c
--- /dev/null
+++ b/programs/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/programs/pluto/alg/Makefile.ike_alg_blowfish b/programs/pluto/alg/Makefile.ike_alg_blowfish
new file mode 100644
index 000000000..c3af6199b
--- /dev/null
+++ b/programs/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/programs/pluto/alg/Makefile.ike_alg_serpent b/programs/pluto/alg/Makefile.ike_alg_serpent
new file mode 100644
index 000000000..3395ac0ea
--- /dev/null
+++ b/programs/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/programs/pluto/alg/Makefile.ike_alg_sha2 b/programs/pluto/alg/Makefile.ike_alg_sha2
new file mode 100644
index 000000000..67e68a667
--- /dev/null
+++ b/programs/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/programs/pluto/alg/Makefile.ike_alg_twofish b/programs/pluto/alg/Makefile.ike_alg_twofish
new file mode 100644
index 000000000..dcd30dd3e
--- /dev/null
+++ b/programs/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/programs/pluto/alg/ike_alg_aes.c b/programs/pluto/alg/ike_alg_aes.c
new file mode 100644
index 000000000..44de09b4c
--- /dev/null
+++ b/programs/pluto/alg/ike_alg_aes.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <freeswan.h>
+
+#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/programs/pluto/alg/ike_alg_blowfish.c b/programs/pluto/alg/ike_alg_blowfish.c
new file mode 100644
index 000000000..2bbef051b
--- /dev/null
+++ b/programs/pluto/alg/ike_alg_blowfish.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <freeswan.h>
+
+#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/programs/pluto/alg/ike_alg_serpent.c b/programs/pluto/alg/ike_alg_serpent.c
new file mode 100644
index 000000000..fb01caa41
--- /dev/null
+++ b/programs/pluto/alg/ike_alg_serpent.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <freeswan.h>
+
+#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/programs/pluto/alg/ike_alg_sha2.c b/programs/pluto/alg/ike_alg_sha2.c
new file mode 100644
index 000000000..ad24f7cf0
--- /dev/null
+++ b/programs/pluto/alg/ike_alg_sha2.c
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <freeswan.h>
+
+#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/programs/pluto/alg/ike_alg_twofish.c b/programs/pluto/alg/ike_alg_twofish.c
new file mode 100644
index 000000000..1788bc394
--- /dev/null
+++ b/programs/pluto/alg/ike_alg_twofish.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <freeswan.h>
+
+#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/programs/pluto/alg_info.c b/programs/pluto/alg_info.c
new file mode 100644
index 000000000..4ac7f2ca9
--- /dev/null
+++ b/programs/pluto/alg_info.c
@@ -0,0 +1,1197 @@
+/*
+ * Algorithm info parsing and creation functions
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * $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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+#include <pfkeyv2.h>
+
+#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 <assert.h>
+#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/programs/pluto/alg_info.h b/programs/pluto/alg_info.h
new file mode 100644
index 000000000..cd2011dcc
--- /dev/null
+++ b/programs/pluto/alg_info.h
@@ -0,0 +1,85 @@
+/* Algorithm info parsing and creation functions
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/asn1.c b/programs/pluto/asn1.c
new file mode 100644
index 000000000..0663bc490
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/asn1.h b/programs/pluto/asn1.h
new file mode 100644
index 000000000..2a3fb3e9e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdarg.h>
+#include <gmp.h>
+
+#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/programs/pluto/ca.c b/programs/pluto/ca.c
new file mode 100644
index 000000000..c1e0261d8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/ca.h b/programs/pluto/ca.h
new file mode 100644
index 000000000..8d4602dc6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/certs.c b/programs/pluto/certs.c
new file mode 100644
index 000000000..92b40605f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/certs.h b/programs/pluto/certs.h
new file mode 100644
index 000000000..cca128965
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/connections.c b/programs/pluto/connections.c
new file mode 100644
index 000000000..263bdbd1e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+#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 <resolv.h> */
+#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]="<NULL>";
+
+ 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]="<NULL>";
+
+ 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; i<count; i++)
+ {
+ const char *ifn;
+ char instance[1 + 10 + 1];
+ char prio[POLICY_PRIO_BUF];
+
+ c = array[i];
+
+ ifn = oriented(*c)? c->interface->rname : "";
+
+ instance[0] = '\0';
+ if (c->kind == CK_INSTANCE && c->instance_serial != 0)
+ snprintf(instance, sizeof(instance), "[%lu]", c->instance_serial);
+
+ /* show topology */
+ {
+ char topo[CONNECTION_BUF];
+ struct spd_route *sr = &c->spd;
+ int num=0;
+
+ while (sr != NULL)
+ {
+ (void) format_connection(topo, sizeof(topo), c, sr);
+ whack_log(RC_COMMENT, "\"%s\"%s: %s; %s; eroute owner: #%lu"
+ , c->name, instance, topo
+ , enum_name(&routing_story, sr->routing)
+ , sr->eroute_owner);
+ sr = sr->next;
+ num++;
+ }
+ }
+
+ if (all)
+ {
+ /* show CAs if defined */
+ if (c->spd.this.ca.ptr != NULL || c->spd.that.ca.ptr != NULL)
+ {
+ char this_ca[BUF_LEN], that_ca[BUF_LEN];
+
+ dntoa_or_null(this_ca, BUF_LEN, c->spd.this.ca, "%any");
+ dntoa_or_null(that_ca, BUF_LEN, c->spd.that.ca, "%any");
+
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: CAs: '%s'...'%s'"
+ , c->name
+ , instance
+ , this_ca
+ , that_ca);
+ }
+
+ /* show group attributes if defined */
+ if (c->spd.that.groups != NULL)
+ {
+ char buf[BUF_LEN];
+
+ format_groups(c->spd.that.groups, buf, BUF_LEN);
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: groups: %s"
+ , c->name
+ , instance
+ , buf);
+ }
+
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: ike_life: %lus; ipsec_life: %lus;"
+ " rekey_margin: %lus; rekey_fuzz: %lu%%; keyingtries: %lu"
+ , c->name
+ , instance
+ , (unsigned long) c->sa_ike_life_seconds
+ , (unsigned long) c->sa_ipsec_life_seconds
+ , (unsigned long) c->sa_rekey_margin
+ , (unsigned long) c->sa_rekey_fuzz
+ , (unsigned long) c->sa_keying_tries);
+
+ /* show DPD parameters if defined */
+
+ if (c->dpd_action != DPD_ACTION_NONE)
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: dpd_action: %s;"
+ " dpd_delay: %lus; dpd_timeout: %lus;"
+ , c->name
+ , instance
+ , enum_show(&dpd_action_names, c->dpd_action)
+ , (unsigned long) c->dpd_delay
+ , (unsigned long) c->dpd_timeout);
+
+ if (c->policy_next)
+ {
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: policy_next: %s"
+ , c->name, instance, c->policy_next->name);
+ }
+
+ /* Note: we display key_from_DNS_on_demand as if policy [lr]KOD */
+ fmt_policy_prio(c->prio, prio);
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: policy: %s%s%s; prio: %s; interface: %s; "
+ , c->name
+ , instance
+ , prettypolicy(c->policy)
+ , c->spd.this.key_from_DNS_on_demand? "+lKOD" : ""
+ , c->spd.that.key_from_DNS_on_demand? "+rKOD" : ""
+ , prio
+ , ifn);
+ }
+
+ whack_log(RC_COMMENT
+ , "\"%s\"%s: newest ISAKMP SA: #%ld; newest IPsec SA: #%ld; "
+ , c->name
+ , instance
+ , c->newest_isakmp_sa
+ , c->newest_ipsec_sa);
+
+ if (all)
+ {
+ ike_alg_show_connection(c, instance);
+ kernel_alg_show_connection(c, instance);
+ }
+ }
+ 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/programs/pluto/connections.h b/programs/pluto/connections.h
new file mode 100644
index 000000000..6dfddbe22
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/queue.h>
+
+#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/programs/pluto/constants.c b/programs/pluto/constants.c
new file mode 100644
index 000000000..27e4db1e0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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<<ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION,
+ ISAKMP_MAJOR_VERSION<<ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION,
+ version_name, NULL };
+
+/* RFC 2459 CRL reason codes */
+
+static const char *const crl_reason_name[] = {
+ "unspecified",
+ "key compromise",
+ "ca compromise",
+ "affiliation changed",
+ "superseded",
+ "cessation of operation",
+ "certificate hold",
+ "reason #7",
+ "remove from crl"
+ };
+
+enum_names crl_reason_names =
+ { REASON_UNSPECIFIED, REASON_REMOVE_FROM_CRL, crl_reason_name, NULL};
+
+/* RFC 3706 Dead Peer Detection */
+
+static const char *const dpd_action_name[] = {
+ "none",
+ "clear",
+ "hold",
+ "restart"
+ };
+
+enum_names dpd_action_names =
+ { DPD_ACTION_NONE, DPD_ACTION_RESTART, dpd_action_name, NULL};
+
+/* Timer events */
+
+static const char *const timer_event_name[] = {
+ "EVENT_NULL",
+ "EVENT_REINIT_SECRET",
+ "EVENT_SHUNT_SCAN",
+ "EVENT_SO_DISCARD",
+ "EVENT_RETRANSMIT",
+ "EVENT_SA_REPLACE",
+ "EVENT_SA_REPLACE_IF_USED",
+ "EVENT_SA_EXPIRE",
+ "EVENT_NAT_T_KEEPALIVE",
+ "EVENT_DPD",
+ "EVENT_DPD_TIMEOUT",
+ "EVENT_LOG_DAILY"
+ };
+
+enum_names timer_event_names =
+ { EVENT_NULL, EVENT_LOG_DAILY, timer_event_name, NULL };
+
+/* Domain of Interpretation */
+
+static const char *const doi_name[] = {
+ "ISAKMP_DOI_ISAKMP",
+ "ISAKMP_DOI_IPSEC",
+};
+
+enum_names doi_names = { ISAKMP_DOI_ISAKMP, ISAKMP_DOI_IPSEC, doi_name, NULL };
+
+/* debugging settings: a set of selections for reporting
+ * These would be more naturally situated in log.h,
+ * but they are shared with whack.
+ * It turns out that "debug-" is clutter in all contexts this is used,
+ * so we leave it off.
+ */
+#ifdef DEBUG
+const char *const debug_bit_names[] = {
+ "raw",
+ "crypt",
+ "parsing",
+ "emitting",
+ "control",
+ "lifecycle",
+ "klips",
+ "dns",
+ "natt",
+ "oppo",
+ "controlmore",
+
+ "private",
+
+ "impair-delay-adns-key-answer",
+ "impair-delay-adns-txt-answer",
+ "impair-bust-mi2",
+ "impair-bust-mr2",
+
+ NULL
+ };
+#endif
+
+/* State of exchanges */
+
+static const char *const state_name[] = {
+ "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",
+ "STATE_MODE_CFG_R1",
+ "STATE_MODE_CFG_R2",
+ "STATE_MODE_CFG_I1",
+ "STATE_MODE_CFG_I2",
+
+ "STATE_IKE_ROOF"
+ };
+
+enum_names state_names =
+ { STATE_MAIN_R0, STATE_IKE_ROOF-1, state_name, NULL };
+
+/* story for state */
+
+const char *const state_story[] = {
+ "expecting MI1", /* STATE_MAIN_R0 */
+ "sent MI1, expecting MR1", /* STATE_MAIN_I1 */
+ "sent MR1, expecting MI2", /* STATE_MAIN_R1 */
+ "sent MI2, expecting MR2", /* STATE_MAIN_I2 */
+ "sent MR2, expecting MI3", /* STATE_MAIN_R2 */
+ "sent MI3, expecting MR3", /* STATE_MAIN_I3 */
+ "sent MR3, ISAKMP SA established", /* STATE_MAIN_R3 */
+ "ISAKMP SA established", /* STATE_MAIN_I4 */
+
+ "expecting QI1", /* STATE_QUICK_R0 */
+ "sent QI1, expecting QR1", /* STATE_QUICK_I1 */
+ "sent QR1, inbound IPsec SA installed, expecting QI2", /* STATE_QUICK_R1 */
+ "sent QI2, IPsec SA established", /* STATE_QUICK_I2 */
+ "IPsec SA established", /* STATE_QUICK_R2 */
+
+ "got Informational Message in clear", /* STATE_INFO */
+ "got encrypted Informational Message", /* STATE_INFO_PROTECTED */
+
+ "sent ModeCfg reply", /* STATE_MODE_CFG_R0 */
+ "sent ModeCfg reply", /* STATE_MODE_CFG_R1 */
+ "ModeCfg R2", /* STATE_MODE_CFG_R2 */
+ "sent ModeCfg request, expecting reply", /* STATE_MODE_CFG_I1 */
+ "received ModeCfg reply", /* STATE_MODE_CFG_I2 */
+ };
+
+/* kind of struct connection */
+
+static const char *const connection_kind_name[] = {
+ "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 */
+};
+
+enum_names connection_kind_names =
+ { CK_GROUP, CK_GOING_AWAY, connection_kind_name, NULL };
+
+/* routing status names */
+
+static const char *const routing_story_strings[] = {
+ "unrouted", /* RT_UNROUTED: unrouted */
+ "unrouted HOLD", /* RT_UNROUTED_HOLD: unrouted, but HOLD shunt installed */
+ "eroute eclipsed", /* RT_ROUTED_ECLIPSED: RT_ROUTED_PROSPECTIVE except bare HOLD or instance has eroute */
+ "prospective erouted", /* RT_ROUTED_PROSPECTIVE: routed, and prospective shunt installed */
+ "erouted HOLD", /* RT_ROUTED_HOLD: routed, and HOLD shunt installed */
+ "fail erouted", /* RT_ROUTED_FAILURE: routed, and failure-context shunt eroute installed */
+ "erouted", /* RT_ROUTED_TUNNEL: routed, and erouted to an IPSEC SA group */
+ "keyed, unrouted", /* RT_UNROUTED_KEYED: was routed+keyed, but it got turned into an outer policy */
+ };
+
+enum_names routing_story =
+ { RT_UNROUTED, RT_ROUTED_TUNNEL, routing_story_strings, NULL};
+
+/* Payload types (RFC 2408 "ISAKMP" section 3.1) */
+
+const char *const payload_name[] = {
+ "ISAKMP_NEXT_NONE",
+ "ISAKMP_NEXT_SA",
+ "ISAKMP_NEXT_P",
+ "ISAKMP_NEXT_T",
+ "ISAKMP_NEXT_KE",
+ "ISAKMP_NEXT_ID",
+ "ISAKMP_NEXT_CERT",
+ "ISAKMP_NEXT_CR",
+ "ISAKMP_NEXT_HASH",
+ "ISAKMP_NEXT_SIG",
+ "ISAKMP_NEXT_NONCE",
+ "ISAKMP_NEXT_N",
+ "ISAKMP_NEXT_D",
+ "ISAKMP_NEXT_VID",
+ "ISAKMP_NEXT_MODECFG",
+ "ISAKMP_NEXT_15",
+ "ISAKMP_NEXT_16",
+ "ISAKMP_NEXT_17",
+ "ISAKMP_NEXT_18",
+ "ISAKMP_NEXT_19",
+ "ISAKMP_NEXT_NAT-D",
+ "ISAKMP_NEXT_NAT-OA",
+ NULL
+ };
+
+const char *const payload_name_nat_d[] = { "ISAKMP_NEXT_NAT-D",
+ "ISAKMP_NEXT_NAT-OA", NULL };
+
+static enum_names payload_names_nat_d =
+ { ISAKMP_NEXT_NATD_DRAFTS, ISAKMP_NEXT_NATOA_DRAFTS, payload_name_nat_d, NULL };
+
+enum_names payload_names =
+ { ISAKMP_NEXT_NONE, ISAKMP_NEXT_NATOA_RFC, payload_name, &payload_names_nat_d };
+
+/* Exchange types (note: two discontinuous ranges) */
+
+static const char *const exchange_name[] = {
+ "ISAKMP_XCHG_NONE",
+ "ISAKMP_XCHG_BASE",
+ "ISAKMP_XCHG_IDPROT",
+ "ISAKMP_XCHG_AO",
+ "ISAKMP_XCHG_AGGR",
+ "ISAKMP_XCHG_INFO",
+ "ISAKMP_XCHG_MODE_CFG",
+ };
+
+static const char *const exchange_name2[] = {
+ "ISAKMP_XCHG_QUICK",
+ "ISAKMP_XCHG_NGRP",
+ "ISAKMP_XCHG_ACK_INFO",
+ };
+
+static enum_names exchange_desc2 =
+ { ISAKMP_XCHG_QUICK, ISAKMP_XCHG_ACK_INFO, exchange_name2, NULL };
+
+enum_names exchange_names =
+ { ISAKMP_XCHG_NONE, ISAKMP_XCHG_MODE_CFG, exchange_name, &exchange_desc2 };
+
+/* Flag BITS */
+const char *const flag_bit_names[] = {
+ "ISAKMP_FLAG_ENCRYPTION",
+ "ISAKMP_FLAG_COMMIT",
+ NULL
+ };
+
+/* Situation BITS definition for IPsec DOI */
+
+const char *const sit_bit_names[] = {
+ "SIT_IDENTITY_ONLY",
+ "SIT_SECRECY",
+ "SIT_INTEGRITY",
+ NULL
+ };
+
+/* Protocol IDs (RFC 2407 "IPsec DOI" section 4.4.1) */
+
+static const char *const protocol_name[] = {
+ "PROTO_ISAKMP",
+ "PROTO_IPSEC_AH",
+ "PROTO_IPSEC_ESP",
+ "PROTO_IPCOMP",
+ };
+
+enum_names protocol_names =
+ { PROTO_ISAKMP, PROTO_IPCOMP, protocol_name, NULL };
+
+/* IPsec ISAKMP transform values */
+
+static const char *const isakmp_transform_name[] = {
+ "KEY_IKE",
+ };
+
+enum_names isakmp_transformid_names =
+ { KEY_IKE, KEY_IKE, isakmp_transform_name, NULL };
+
+/* IPsec AH transform values */
+
+static const char *const ah_transform_name[] = {
+ "AH_MD5",
+ "AH_SHA",
+ "AH_DES",
+ "AH_SHA2_256",
+ "AH_SHA2_384",
+ "AH_SHA2_512",
+ "AH_RIPEMD"
+ };
+
+enum_names ah_transformid_names =
+ { AH_MD5, AH_RIPEMD, ah_transform_name, NULL };
+
+/* IPsec ESP transform values */
+
+static const char *const esp_transform_name[] = {
+ "ESP_DES_IV64",
+ "ESP_DES",
+ "ESP_3DES",
+ "ESP_RC5",
+ "ESP_IDEA",
+ "ESP_CAST",
+ "ESP_BLOWFISH",
+ "ESP_3IDEA",
+ "ESP_DES_IV32",
+ "ESP_RC4",
+ "ESP_NULL",
+ "ESP_AES",
+ "ESP_AES-CTR",
+ "ESP_AES-CCM_8",
+ "ESP_AES-CCM_12",
+ "ESP_AES-CCM_16"
+ };
+
+/*
+ * ipsec drafts suggest "high" ESP ids values for testing,
+ * assign generic ESP_ID<num> 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, &notification_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, &notification_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 <arpa/nameser.h>
+
+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/programs/pluto/constants.h b/programs/pluto/constants.h
new file mode 100644
index 000000000..b66d002ee
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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).
+ * <freeswan.h> 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/programs/pluto/cookie.c b/programs/pluto/cookie.c
new file mode 100644
index 000000000..458120e46
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/cookie.h b/programs/pluto/cookie.h
new file mode 100644
index 000000000..f5b0e64d1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <freeswan.h>
+
+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/programs/pluto/crl.c b/programs/pluto/crl.c
new file mode 100644
index 000000000..8d4b3bd7b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/crl.h b/programs/pluto/crl.h
new file mode 100644
index 000000000..9f985b6cd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/crypto.c b/programs/pluto/crypto.c
new file mode 100644
index 000000000..24939bd04
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+#define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in <des.h> */
+#include <crypto/des.h>
+
+#include <errno.h>
+
+#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/programs/pluto/crypto.h b/programs/pluto/crypto.h
new file mode 100644
index 000000000..d29475af2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h> /* 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/programs/pluto/db_ops.c b/programs/pluto/db_ops.c
new file mode 100644
index 000000000..bbcd7918f
--- /dev/null
+++ b/programs/pluto/db_ops.c
@@ -0,0 +1,439 @@
+/* Dynamic db (proposal, transforms, attributes) handling.
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+
+#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 <assert.h>
+
+#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
+ * <current_size>/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
+ * <current_size>/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/programs/pluto/db_ops.h b/programs/pluto/db_ops.h
new file mode 100644
index 000000000..433e75280
--- /dev/null
+++ b/programs/pluto/db_ops.h
@@ -0,0 +1,56 @@
+/* Dynamic db (proposal, transforms, attributes) handling.
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/defs.c b/programs/pluto/defs.c
new file mode 100644
index 000000000..16f6a3949
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/defs.h b/programs/pluto/defs.h
new file mode 100644
index 000000000..7e92ea540
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <sys/types.h>
+
+#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/programs/pluto/demux.c b/programs/pluto/demux.c
new file mode 100644
index 000000000..2f8fb9a8f
--- /dev/null
+++ b/programs/pluto/demux.c
@@ -0,0 +1,2411 @@
+/* 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h> /* only used for belt-and-suspenders select call */
+#include <sys/poll.h> /* only used for forensic poll call */
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+
+#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE)
+# include <asm/types.h> /* for __u8, __u32 */
+# include <linux/errqueue.h>
+# include <sys/uio.h> /* struct iovec */
+#endif
+
+#include <freeswan.h>
+
+#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), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * SMF_RPKE_AUTH:
+ * --> HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>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), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * --> HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * SMF_RPKE_AUTH:
+ * HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
+ * --> HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>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, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * --> HDR*, HASH_I
+ * SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>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 <ak@suse.de> 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
+
+ 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(&notification_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(&notification_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(&notification_names, md->note)));
+ break;
+ }
+}
diff --git a/programs/pluto/demux.h b/programs/pluto/demux.h
new file mode 100644
index 000000000..7adac44f3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/dnskey.c b/programs/pluto/dnskey.c
new file mode 100644
index 000000000..9aca1938d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h> /* ??? for h_errno */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#include "constants.h"
+#include "adns.h" /* needs <resolv.h> */
+#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 <character-string>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 <character-string>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/programs/pluto/dnskey.h b/programs/pluto/dnskey.h
new file mode 100644
index 000000000..0b9f0ee33
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/dsa.c b/programs/pluto/dsa.c
new file mode 100644
index 000000000..c5982fbf4
--- /dev/null
+++ b/programs/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 <gmp.h>
+#include <freeswan.h>
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "rnd.h"
+#include "gcryptfix.h"
+#else /*! PLUTO */
+/* #include <config.h> */
+#endif /* !PLUTO */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef PLUTO
+/* #include <assert.h> */
+/* #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/programs/pluto/dsa.h b/programs/pluto/dsa.h
new file mode 100644
index 000000000..1456d65b6
--- /dev/null
+++ b/programs/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/programs/pluto/elgamal.c b/programs/pluto/elgamal.c
new file mode 100644
index 000000000..0c099bb90
--- /dev/null
+++ b/programs/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 <gmp.h>
+#include <freeswan.h>
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "rnd.h"
+#include "gcryptfix.h"
+#else /*! PLUTO */
+/* #include <config.h> */
+#endif /* !PLUTO */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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/programs/pluto/elgamal.h b/programs/pluto/elgamal.h
new file mode 100644
index 000000000..f104c2a52
--- /dev/null
+++ b/programs/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/programs/pluto/fetch.c b/programs/pluto/fetch.c
new file mode 100644
index 000000000..075b88fd2
--- /dev/null
+++ b/programs/pluto/fetch.c
@@ -0,0 +1,1081 @@
+/* Dynamic fetching of X.509 CRLs
+ * Copyright (C) 2002 Stephane Laroche <stephane.laroche@colubris.com>
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+
+#ifdef THREADS
+#include <pthread.h>
+#endif
+
+#ifdef LIBCURL
+#include <curl/curl.h>
+#endif
+
+#include <freeswan.h>
+
+#ifdef LDAP_VER
+#include <ldap.h>
+#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/programs/pluto/fetch.h b/programs/pluto/fetch.h
new file mode 100644
index 000000000..6303f37e4
--- /dev/null
+++ b/programs/pluto/fetch.h
@@ -0,0 +1,79 @@
+/* Dynamic fetching of X.509 CRLs
+ * Copyright (C) 2002 Stephane Laroche <stephane.laroche@colubris.com>
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/foodgroups.c b/programs/pluto/foodgroups.c
new file mode 100644
index 000000000..52e32f0fb
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/foodgroups.h b/programs/pluto/foodgroups.h
new file mode 100644
index 000000000..7cbbccc44
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/gcryptfix.c b/programs/pluto/gcryptfix.c
new file mode 100644
index 000000000..1ebacdcf6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+
+#include <gmp.h>
+#include <freeswan.h>
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "rnd.h"
+#include "gcryptfix.h" /* includes <gmp.h> "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<<k);
+#else
+ G = m_alloc_clear( (1<<k) * sizeof *G );
+#endif
+
+ #ifdef USE_BARRETT
+ barrett_y = init_barrett( m, &barrett_k, &barrett_r1, &barrett_r2 );
+ #endif
+ /* and calculate */
+ tmp = mpi_alloc( mpi_get_nlimbs(m)+1 );
+ mpi_set_ui( res, 1 );
+ for(i = 1; i <= t; i++ ) {
+ barrett_mulm(tmp, res, res, m, barrett_y, barrett_k,
+ barrett_r1, barrett_r2 );
+ idx = build_index( exparray, k, i, t );
+ passert( idx >= 0 && idx < (1<<k) );
+ if( !G[idx] ) {
+ if( !idx )
+ G[0] = mpi_alloc_set_ui( 1 );
+ else {
+ for(j=0; j < k; j++ ) {
+ if( (idx & (1<<j) ) ) {
+ if( !G[idx] )
+ G[idx] = mpi_copy( basearray[j] );
+ else
+ barrett_mulm( G[idx], G[idx], basearray[j],
+ m, barrett_y, barrett_k, barrett_r1, barrett_r2 );
+ }
+ }
+ if( !G[idx] )
+ G[idx] = mpi_alloc(0);
+ }
+ }
+ barrett_mulm(res, tmp, G[idx], m, barrett_y, barrett_k, barrett_r1, barrett_r2 );
+ }
+
+ /* cleanup */
+ mpi_free(tmp);
+ #ifdef USE_BARRETT
+ mpi_free(barrett_y);
+ mpi_free(barrett_r1);
+ mpi_free(barrett_r2);
+ #endif
+ for(i=0; i < (1<<k); i++ )
+ mpi_free(G[i]);
+ m_free(G);
+}
+
+void
+log_mpidump( const char *text UNUSED, MPI a )
+{
+ /* Print number in hex -- helpful to see if they match bytes.
+ * Humans are not going to do arithmetic with the large numbers!
+ * Much code adapted from mpz_to_n.
+ */
+ u_char buf[8048]; /* this ought to be big enough */
+ size_t len = (mpz_sizeinbase(a, 16) + 1) / 2; /* bytes */
+ MP_INT temp1, temp2;
+ int i;
+
+ passert(len <= sizeof(buf));
+
+ mpz_init(&temp1);
+ mpz_init(&temp2);
+
+ mpz_set(&temp1, a);
+
+ for (i = len-1; i >= 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/programs/pluto/gcryptfix.h b/programs/pluto/gcryptfix.h
new file mode 100644
index 000000000..637ecbc8d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/id.c b/programs/pluto/id.c
new file mode 100644
index 000000000..4e306d3a7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#ifndef HOST_NAME_MAX /* POSIX 1003.1-2001 says <unistd.h> defines this */
+# define HOST_NAME_MAX 255 /* upper bound, according to SUSv2 */
+#endif
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/id.h b/programs/pluto/id.h
new file mode 100644
index 000000000..4fe9ef227
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/ike_alg.c b/programs/pluto/ike_alg.c
new file mode 100644
index 000000000..47393079a
--- /dev/null
+++ b/programs/pluto/ike_alg.c
@@ -0,0 +1,459 @@
+/* IKE modular algorithm handling interface
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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 = "<NULL>";
+ }
+
+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/programs/pluto/ike_alg.h b/programs/pluto/ike_alg.h
new file mode 100644
index 000000000..a41718c04
--- /dev/null
+++ b/programs/pluto/ike_alg.h
@@ -0,0 +1,73 @@
+/* IKE modular algorithm handling interface
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/ipsec.secrets.5 b/programs/pluto/ipsec.secrets.5
new file mode 100644
index 000000000..3cce4d3f8
--- /dev/null
+++ b/programs/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
+<http://www.freeswan.org>
+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/programs/pluto/ipsec_doi.c b/programs/pluto/ipsec_doi.c
new file mode 100644
index 000000000..fe5c846a7
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+#include <sys/time.h> /* for gettimeofday */
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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 <resolv.h> */
+#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(&notification_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, &not_pbs)
+ || !out_raw(spi, spisize, &not_pbs, "spi"))
+ impossible();
+ close_output_pbs(&not_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), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * RPKE_AUTH: --> HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i,
+ * <IDi1_b>Ke_i [,<<Cert-I_b>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), ] <IDi1_b>PubKey_r, <Ni_b>PubKey_r
+ * --> HDR, KE, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * RPKE_AUTH:
+ * HDR, [ HASH(1), ] <Ni_b>Pubkey_r, <KE_b>Ke_i, <IDi1_b>Ke_i [,<<Cert-I_b>Ke_i]
+ * --> HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>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, <IDr1_b>PubKey_i, <Nr_b>PubKey_i
+ * --> HDR*, HASH_I
+ * SMF_RPKE_AUTH: HDR, <Nr_b>PubKey_i, <KE_b>Ke_r, <IDr1_b>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, &notify_pbs))
+ return STF_INTERNAL_ERROR;
+ if (!out_raw(st->st_icookie, COOKIE_SIZE, &notify_pbs, "notify icookie"))
+ return STF_INTERNAL_ERROR;
+ if (!out_raw(st->st_rcookie, COOKIE_SIZE, &notify_pbs, "notify rcookie"))
+ return STF_INTERNAL_ERROR;
+ if (data != NULL && len > 0)
+ if (!out_raw(data, len, &notify_pbs, "notify data"))
+ return STF_INTERNAL_ERROR;
+ close_output_pbs(&notify_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/programs/pluto/ipsec_doi.h b/programs/pluto/ipsec_doi.h
new file mode 100644
index 000000000..80b12c31d
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/kameipsec.h b/programs/pluto/kameipsec.h
new file mode 100644
index 000000000..5f08c7d38
--- /dev/null
+++ b/programs/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/programs/pluto/kernel.c b/programs/pluto/kernel.c
new file mode 100644
index 000000000..5d7c5f78a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/queue.h>
+
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#ifdef KLIPS
+#include <signal.h>
+#include <sys/time.h> /* for select(2) */
+#include <sys/types.h> /* for select(2) */
+#include <pfkeyv2.h>
+#include <pfkey.h>
+#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 :-
+ *
+ * %<keyword>:p
+ * <ip-proto><spi>@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/programs/pluto/kernel.h b/programs/pluto/kernel.h
new file mode 100644
index 000000000..c01ff31f9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/kernel_alg.c b/programs/pluto/kernel_alg.c
new file mode 100644
index 000000000..920a879d7
--- /dev/null
+++ b/programs/pluto/kernel_alg.c
@@ -0,0 +1,775 @@
+/* Kernel runtime algorithm handling interface
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/queue.h>
+
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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 <assert.h>
+#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_")*/
+ : "<Phase1>"
+ : "<N/A>"
+ );
+}
+#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/programs/pluto/kernel_alg.h b/programs/pluto/kernel_alg.h
new file mode 100644
index 000000000..483e97da1
--- /dev/null
+++ b/programs/pluto/kernel_alg.h
@@ -0,0 +1,46 @@
+/* Kernel runtime algorithm handling interface definitions
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/kernel_netlink.c b/programs/pluto/kernel_netlink.c
new file mode 100644
index 000000000..fd43c4653
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <unistd.h>
+
+#include "kameipsec.h"
+#include "linux26/rtnetlink.h"
+#include "linux26/xfrm.h"
+
+#include <freeswan.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#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/programs/pluto/kernel_netlink.h b/programs/pluto/kernel_netlink.h
new file mode 100644
index 000000000..1b5f42e48
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/kernel_noklips.c b/programs/pluto/kernel_noklips.c
new file mode 100644
index 000000000..570bb0470
--- /dev/null
+++ b/programs/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 <mcr@freeswan.org>
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#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/programs/pluto/kernel_noklips.h b/programs/pluto/kernel_noklips.h
new file mode 100644
index 000000000..fe4e77ec4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/kernel_pfkey.c b/programs/pluto/kernel_pfkey.c
new file mode 100644
index 000000000..76bfbaf9a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <errno.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#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
+ * <base, address(SD), (address(P)), (identity(SD),) (sensitivity,) proposal>
+ * - 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/programs/pluto/kernel_pfkey.h b/programs/pluto/kernel_pfkey.h
new file mode 100644
index 000000000..9dbcdd341
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/keys.c b/programs/pluto/keys.c
new file mode 100644
index 000000000..21092383a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+
+#include <glob.h>
+#ifndef GLOB_ABORTED
+# define GLOB_ABORTED GLOB_ABEND /* fix for old versions */
+#endif
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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 <resolv.h> */
+#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<reader>:<id>";
+
+ 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 <pin code>, %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/programs/pluto/keys.h b/programs/pluto/keys.h
new file mode 100644
index 000000000..d47d8b0a2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h> /* 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/programs/pluto/lex.c b/programs/pluto/lex.c
new file mode 100644
index 000000000..5c811725a
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/lex.h b/programs/pluto/lex.h
new file mode 100644
index 000000000..fb6c15236
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/linux26/netlink.h b/programs/pluto/linux26/netlink.h
new file mode 100644
index 000000000..6b0896da6
--- /dev/null
+++ b/programs/pluto/linux26/netlink.h
@@ -0,0 +1,90 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <stdint.h>
+#include <sys/socket.h> /* 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/programs/pluto/linux26/rtnetlink.h b/programs/pluto/linux26/rtnetlink.h
new file mode 100644
index 000000000..341bc1f86
--- /dev/null
+++ b/programs/pluto/linux26/rtnetlink.h
@@ -0,0 +1,562 @@
+#ifndef __LINUX_RTNETLINK_H
+#define __LINUX_RTNETLINK_H
+
+#include "netlink.h"
+#include <stdint.h>
+
+#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/programs/pluto/linux26/xfrm.h b/programs/pluto/linux26/xfrm.h
new file mode 100644
index 000000000..4269ae29b
--- /dev/null
+++ b/programs/pluto/linux26/xfrm.h
@@ -0,0 +1,233 @@
+#ifndef _LINUX_XFRM_H
+#define _LINUX_XFRM_H
+
+#include <stdint.h>
+
+/* 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/programs/pluto/log.c b/programs/pluto/log.c
new file mode 100644
index 000000000..137e92980
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h> /* used only if MSG_NOSIGNAL not defined */
+#include <sys/queue.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/log.h b/programs/pluto/log.h
new file mode 100644
index 000000000..0bf8219aa
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <freeswan.h>
+
+#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/programs/pluto/md2.c b/programs/pluto/md2.c
new file mode 100644
index 000000000..d6465477d
--- /dev/null
+++ b/programs/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 <memory.h>
+#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/programs/pluto/md2.h b/programs/pluto/md2.h
new file mode 100644
index 000000000..b3b48dd92
--- /dev/null
+++ b/programs/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/programs/pluto/md5.c b/programs/pluto/md5.c
new file mode 100644
index 000000000..5d75e38a4
--- /dev/null
+++ b/programs/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 <stddef.h>
+#include <string.h>
+#include <sys/types.h> /* for u_int*_t */
+#include <endian.h> /* 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 <memory.h>
+#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/programs/pluto/md5.h b/programs/pluto/md5.h
new file mode 100644
index 000000000..9b29bc46e
--- /dev/null
+++ b/programs/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/programs/pluto/modecfg.c b/programs/pluto/modecfg.c
new file mode 100644
index 000000000..1c22845a5
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/modecfg.h b/programs/pluto/modecfg.h
new file mode 100644
index 000000000..f6856d263
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/mp_defs.c b/programs/pluto/mp_defs.c
new file mode 100644
index 000000000..7ad896751
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <freeswan.h>
+
+#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/programs/pluto/mp_defs.h b/programs/pluto/mp_defs.h
new file mode 100644
index 000000000..744a028d1
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h>
+
+#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/programs/pluto/nat_traversal.c b/programs/pluto/nat_traversal.c
new file mode 100644
index 000000000..2f5ba3cb4
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h> /* used only if MSG_NOSIGNAL not defined */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+#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/programs/pluto/nat_traversal.h b/programs/pluto/nat_traversal.h
new file mode 100644
index 000000000..71222c54c
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/ocsp.c b/programs/pluto/ocsp.c
new file mode 100644
index 000000000..f31b96c7f
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/ocsp.h b/programs/pluto/ocsp.h
new file mode 100644
index 000000000..49e1026ec
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/oid.c b/programs/pluto/oid.c
new file mode 100644
index 000000000..4b0632de2
--- /dev/null
+++ b/programs/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 <stdlib.h>
+
+#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/programs/pluto/oid.h b/programs/pluto/oid.h
new file mode 100644
index 000000000..71f8101cd
--- /dev/null
+++ b/programs/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/programs/pluto/oid.pl b/programs/pluto/oid.pl
new file mode 100644
index 000000000..52ac8eae0
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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, "<oid.txt")
+ or die "could not open 'oid.txt': $!";
+
+$counter = 0;
+$max_name = 0;
+$max_order = 0;
+
+while ($line = <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 <stdlib.h>\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/programs/pluto/oid.txt b/programs/pluto/oid.txt
new file mode 100644
index 000000000..eed46d59d
--- /dev/null
+++ b/programs/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/programs/pluto/packet.c b/programs/pluto/packet.c
new file mode 100644
index 000000000..9f04c8bb2
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#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", &notification_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/programs/pluto/packet.h b/programs/pluto/packet.h
new file mode 100644
index 000000000..676a5e6cd
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/pem.c b/programs/pluto/pem.c
new file mode 100644
index 000000000..e8d381741
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+#define HEADER_DES_LOCL_H /* stupid trick to force prototype decl in <des.h> */
+#include <crypto/des.h>
+
+#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/programs/pluto/pem.h b/programs/pluto/pem.h
new file mode 100644
index 000000000..815b5d85b
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/pgp.c b/programs/pluto/pgp.c
new file mode 100644
index 000000000..015319aaf
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#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/programs/pluto/pgp.h b/programs/pluto/pgp.h
new file mode 100644
index 000000000..4f34debc9
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/pkcs1.c b/programs/pluto/pkcs1.c
new file mode 100644
index 000000000..413938976
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/pkcs1.h b/programs/pluto/pkcs1.h
new file mode 100644
index 000000000..c927db0f8
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <gmp.h> /* 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/programs/pluto/pkcs7.c b/programs/pluto/pkcs7.c
new file mode 100644
index 000000000..0691a80d6
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <crypto/des.h>
+
+#include <freeswan.h>
+
+#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/programs/pluto/pkcs7.h b/programs/pluto/pkcs7.h
new file mode 100644
index 000000000..38c633f4e
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/programs/pluto/pluto-style.el b/programs/pluto/pluto-style.el
new file mode 100644
index 000000000..0de474e44
--- /dev/null
+++ b/programs/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/programs/pluto/pluto.8 b/programs/pluto/pluto.8
new file mode 100644
index 000000000..b80d13772
--- /dev/null
+++ b/programs/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 <htp://www.freeswan.org>
+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
+<http://www.freeswan.org>
+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 <rgb@conscoop.ottawa.on.ca> and Peter Onion
+<ponion@srd.bt.co.uk> 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 <users@lists.freeswan.org> 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/programs/pluto/plutomain.c b/programs/pluto/plutomain.c
new file mode 100644
index 000000000..f9badbae3
--- /dev/null
+++ b/programs/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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#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 <resolv.h> */
+#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 <filename>]"
+ " \\\n\t"
+ "[--nofork]"
+ " [--stderrlog]"
+ " [--noklips]"
+ " [--nocrsend]"
+ " \\\n\t"
+ "[--strictcrlpolicy]"
+ " [--crlcheckinterval]"
+ " [--cachecrls]"
+ " [--uniqueids]"
+ " \\\n\t"
+ "[--interface <ifname>]"
+ " [--ikeport <port-number>]"
+ " \\\n\t"
+ "[--ctlbase <path>]"
+ " \\\n\t"
+ "[--perpeerlogbase <path>] [--perpeerlog]"
+ " \\\n\t"
+ "[--secretsfile <secrets-file>]"
+ " [--policygroupsdir <policygroups-dir>]"
+ " \\\n\t"
+ "[--adns <pathname>]"
+ "[--pkcs11module <path>]"
+ "[--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 <delay_sec>]"
+ " \\\n\t"
+ "[--force_keepalive] [--disable_port_floating]"
+#endif
+#ifdef VIRTUAL_IP
+ " \\\n\t"
+ "[--virtual_private <network_list>]"
+#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 "<pid>\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 <filename> */
+ 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 <time>*/
+ if (optarg == NULL || !isdigit(optarg[0]))
+ usage("missing interval time");
+
+ {
+ char *endptr;
+ long interval = strtol(optarg, &endptr, 0);
+
+ if (*endptr != '\0' || endptr == optarg
+ || interval <= 0)
+ usage("<interval-time> must be a positive number");
+ crl_check_interval = interval;
+ }
+ continue;
+
+ case 'C': /* --cachecrls */
+ cache_crls = TRUE;
+ continue;
+
+ case 'u': /* --uniqueids */
+ uniqueIDs = TRUE;
+ continue;
+
+ case 'i': /* --interface <ifname> */
+ if (!use_interface(optarg))
+ usage("too many --interface specifications");
+ continue;
+
+ case 'p': /* --port <portnumber> */
+ if (optarg == NULL || !isdigit(optarg[0]))
+ usage("missing port number");
+
+ {
+ char *endptr;
+ long port = strtol(optarg, &endptr, 0);
+
+ if (*endptr != '\0' || endptr == optarg
+ || port <= 0 || port > 0x10000)
+ usage("<port-number> must be a number between 1 and 65535");
+ pluto_port = port;
+ }
+ continue;
+
+ case 'b': /* --ctlbase <path> */
+ if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
+ , "%s%s", optarg, CTL_SUFFIX) == -1)
+ usage("<path>" CTL_SUFFIX " too long for sun_path");
+ if (snprintf(info_addr.sun_path, sizeof(info_addr.sun_path)
+ , "%s%s", optarg, INFO_SUFFIX) == -1)
+ usage("<path>" INFO_SUFFIX " too long for sun_path");
+ if (snprintf(pluto_lock, sizeof(pluto_lock)
+ , "%s%s", optarg, LOCK_SUFFIX) == -1)
+ usage("<path>" LOCK_SUFFIX " must fit");
+ continue;
+
+ case 's': /* --secretsfile <secrets-file> */
+ shared_secrets_file = optarg;
+ continue;
+
+ case 'f': /* --policygroupsdir <policygroups-dir> */
+ policygroups_dir = optarg;
+ continue;
+
+ case 'a': /* --adns <pathname> */
+ pluto_adns_option = optarg;
+ continue;
+
+ case 'm': /* --pkcs11module <pathname> */
+ pkcs11_module_path = optarg;
+ continue;
+
+ case 'k': /* --pkcs11keepstate */
+ pkcs11_keep_state = TRUE;
+ continue;
+
+ case 'y': /* --pkcs11proxy */
+ pkcs11_proxy = TRUE;
+ continue;
+
+#ifdef DEBUG
+ case 'N': /* --debug-none */
+ base_debugging = DBG_NONE;
+ continue;
+
+ case 'A': /* --debug-all */
+ base_debugging = DBG_ALL;
+ continue;
+#endif
+
+ case 'P': /* --perpeerlogbase */
+ base_perpeer_logdir = optarg;
+ continue;
+
+ case 'l':
+ log_to_perpeer = TRUE;
+ continue;
+
+#ifdef NAT_TRAVERSAL
+ case '1': /* --nat_traversal */
+ nat_traversal = TRUE;
+ continue;
+ case '2': /* --keep_alive */
+ keep_alive = atoi(optarg);
+ continue;
+ case '3': /* --force_keepalive */
+ force_keepalive = TRUE;
+ continue;
+ case '4': /* --disable_port_floating */
+ nat_t_spf = FALSE;
+ continue;
+ case '5': /* --debug-nat_t */
+ base_debugging |= DBG_NATT;
+ continue;
+#endif
+#ifdef VIRTUAL_IP
+ case '6': /* --virtual_private */
+ virtual_private = optarg;
+ continue;
+#endif
+
+ default:
+#ifdef DEBUG
+ if (c >= DBG_OFFSET)
+ {
+ base_debugging |= c - DBG_OFFSET;
+ continue;
+ }
+# undef DBG_OFFSET
+#endif
+ bad_case(c);
+ }
+ break;
+ }
+ if (optind != argc)
+ usage("unexpected argument");
+ reset_debugging();
+ lockfd = create_lock();
+
+ /* select between logging methods */
+
+ if (log_to_stderr_desired)
+ log_to_syslog = FALSE;
+ else
+ log_to_stderr = FALSE;
+
+ /* set the logging function of pfkey debugging */
+#ifdef DEBUG
+ pfkey_debug_func = DBG_log;
+#else
+ pfkey_debug_func = NULL;
+#endif
+
+ /* create control socket.
+ * We must create it before the parent process returns so that
+ * there will be no race condition in using it. The easiest
+ * place to do this is before the daemon fork.
+ */
+ {
+ err_t ugh = init_ctl_socket();
+
+ if (ugh != NULL)
+ {
+ fprintf(stderr, "pluto: %s", ugh);
+ exit_pluto(1);
+ }
+ }
+
+#ifdef IPSECPOLICY
+ /* create info socket. */
+ {
+ err_t ugh = init_info_socket();
+
+ if (ugh != NULL)
+ {
+ fprintf(stderr, "pluto: %s", ugh);
+ exit_pluto(1);
+ }
+ }
+#endif
+
+ /* If not suppressed, do daemon fork */
+
+ if (fork_desired)
+ {
+ {
+ pid_t pid = fork();
+
+ if (pid < 0)
+ {
+ int e = errno;
+
+ fprintf(stderr, "pluto: fork failed (%d %s)\n",
+ errno, strerror(e));
+ exit_pluto(1);
+ }
+
+ if (pid != 0)
+ {
+ /* parent: die, after filling PID into lock file.
+ * must not use exit_pluto: lock would be removed!
+ */
+ exit(fill_lock(lockfd, pid)? 0 : 1);
+ }
+ }
+
+ if (setsid() < 0)
+ {
+ int e = errno;
+
+ fprintf(stderr, "setsid() failed in main(). Errno %d: %s\n",
+ errno, strerror(e));
+ exit_pluto(1);
+ }
+ }
+ else
+ {
+ /* no daemon fork: we have to fill in lock file */
+ (void) fill_lock(lockfd, getpid());
+ fprintf(stdout, "Pluto initialized\n");
+ fflush(stdout);
+ }
+
+ /* Close everything but ctl_fd and (if needed) stderr.
+ * There is some danger that a library that we don't know
+ * about is using some fd that we don't know about.
+ * I guess we'll soon find out.
+ */
+ {
+ int i;
+
+ for (i = getdtablesize() - 1; i >= 0; i--) /* Bad hack */
+ if ((!log_to_stderr || i != 2)
+#ifdef IPSECPOLICY
+ && i != info_fd
+#endif
+ && i != ctl_fd)
+ close(i);
+
+ /* make sure that stdin, stdout, stderr are reserved */
+ if (open("/dev/null", O_RDONLY) != 0)
+ abort();
+ if (dup2(0, 1) != 1)
+ abort();
+ if (!log_to_stderr && dup2(0, 2) != 2)
+ abort();
+ }
+
+ init_constants();
+ init_log("pluto");
+
+ /* Note: some scripts may look for this exact message -- don't change
+ * ipsec barf was one, but it no longer does.
+ */
+ plog("Starting Pluto (strongSwan Version %s%s)"
+ , ipsec_version_code()
+ , compile_time_interop_options);
+
+#ifdef NAT_TRAVERSAL
+ init_nat_traversal(nat_traversal, keep_alive, force_keepalive, nat_t_spf);
+#endif
+
+#ifdef VIRTUAL_IP
+ init_virtual_ip(virtual_private);
+#endif
+ scx_init(pkcs11_module_path); /* load and initialize PKCS #11 module */
+ init_rnd_pool();
+ init_secret();
+ init_states();
+ init_crypto();
+ init_demux();
+ init_kernel();
+ init_adns();
+ init_id();
+ init_fetch();
+
+ /* loading X.509 CA certificates */
+ load_authcerts("CA cert", CA_CERT_PATH, AUTH_CA);
+ /* loading X.509 AA certificates */
+ load_authcerts("AA cert", AA_CERT_PATH, AUTH_AA);
+ /* loading X.509 OCSP certificates */
+ load_authcerts("OCSP cert", OCSP_CERT_PATH, AUTH_OCSP);
+ /* loading X.509 CRLs */
+ load_crls();
+ /* loading attribute certificates (experimental) */
+ load_acerts();
+
+ daily_log_event();
+ call_server();
+ return -1; /* Shouldn't ever reach this */
+}
+
+/* leave pluto, with status.
+ * Once child is launched, parent must not exit this way because
+ * the lock would be released.
+ *
+ * 0 OK
+ * 1 general discomfort
+ * 10 lock file exists
+ */
+void
+exit_pluto(int status)
+{
+ reset_globals(); /* needed because we may be called in odd state */
+ free_preshared_secrets();
+ free_remembered_public_keys();
+ delete_every_connection();
+ free_crl_fetch(); /* free chain of crl fetch requests */
+ free_ocsp_fetch(); /* free chain of ocsp fetch requests */
+ free_authcerts(); /* free chain of X.509 authority certificates */
+ free_crls(); /* free chain of X.509 CRLs */
+ free_acerts(); /* free chain of X.509 attribute certificates */
+ free_ca_infos(); /* free chain of X.509 CA information records */
+ free_ocsp(); /* free ocsp cache */
+ free_ifaces();
+ scx_finalize(); /* finalize and unload PKCS #11 module */
+ stop_adns();
+ free_md_pool();
+ delete_lock();
+#ifdef LEAK_DETECTIVE
+ report_leaks();
+#endif /* LEAK_DETECTIVE */
+ close_log();
+ exit(status);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:4
+ * c-style: pluto
+ * End:
+ */
diff --git a/programs/pluto/primegen.c b/programs/pluto/primegen.c
new file mode 100644
index 000000000..159490345
--- /dev/null
+++ b/programs/pluto/primegen.c
@@ -0,0 +1,593 @@
+/* primegen.c - prime number generator
+ * 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
+ *
+ * ***********************************************************************
+ * The algorithm used to generate practically save primes is due to
+ * Lim and Lee as described in the CRYPTO '97 proceedings (ISBN3540633847)
+ * page 260.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef PLUTO
+#include <gmp.h>
+#include <freeswan.h>
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "rnd.h"
+#include "gcryptfix.h"
+#else /*! PLUTO */
+/* #include <assert.h> */
+/* #include <config.h> */
+/* #include "util.h" */
+/* #include "mpi.h" */
+/* #include "cipher.h" */
+#endif /* !PLUTO */
+
+static int no_of_small_prime_numbers;
+static MPI gen_prime( unsigned nbits, int mode, int randomlevel );
+static int check_prime( MPI prime, MPI val_2 );
+static int is_prime( MPI n, unsigned steps, int *count );
+static void m_out_of_n( char *array, int m, int n );
+
+
+static void
+progress( int c )
+{
+ fputc( c, stderr );
+}
+
+
+/****************
+ * Generate a prime number (stored in secure memory)
+ */
+MPI
+generate_secret_prime( unsigned nbits )
+{
+ MPI prime;
+
+ prime = gen_prime( nbits, 1, 2 );
+ progress('\n');
+ return prime;
+}
+
+MPI
+generate_public_prime( unsigned nbits )
+{
+ MPI prime;
+
+ prime = gen_prime( nbits, 0, 2 );
+ progress('\n');
+ return prime;
+}
+
+
+/****************
+ * We do not need to use the strongest RNG because we gain no extra
+ * security from it - The prime number is public and we could also
+ * offer the factors for those who are willing to check that it is
+ * indeed a strong prime.
+ *
+ * mode 0: Standard
+ * 1: Make sure that at least one factor is of size qbits.
+ */
+MPI
+generate_elg_prime( int mode, unsigned pbits, unsigned qbits,
+ MPI g, MPI **ret_factors )
+{
+ int n; /* number of factors */
+ int m; /* number of primes in pool */
+ unsigned fbits; /* length of prime factors */
+ MPI *factors; /* current factors */
+ MPI *pool; /* pool of primes */
+ MPI q; /* first prime factor (variable)*/
+ MPI prime; /* prime test value */
+ MPI q_factor; /* used for mode 1 */
+ byte *perms = NULL;
+ int i, j;
+ int count1, count2;
+ unsigned nprime;
+ unsigned req_qbits = qbits; /* the requested q bits size */
+ MPI val_2 = mpi_alloc_set_ui( 2 );
+
+ /* find number of needed prime factors */
+ for(n=1; (pbits - qbits - 1) / n >= qbits; n++ )
+ ;
+ n--;
+ if( !n || (mode==1 && n < 2) )
+ log_fatal("can't gen prime with pbits=%u qbits=%u\n", pbits, qbits );
+ if( mode == 1 ) {
+ n--;
+ fbits = (pbits - 2*req_qbits -1) / n;
+ qbits = pbits - req_qbits - n*fbits;
+ }
+ else {
+ fbits = (pbits - req_qbits -1) / n;
+ qbits = pbits - n*fbits;
+ }
+ if( DBG_CIPHER )
+ log_debug("gen prime: pbits=%u qbits=%u fbits=%u/%u n=%d\n",
+ pbits, req_qbits, qbits, fbits, n );
+ prime = mpi_alloc( (pbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB );
+ q = gen_prime( qbits, 0, 1 );
+ q_factor = mode==1? gen_prime( req_qbits, 0, 1 ) : NULL;
+
+ /* allocate an array to hold the factors + 2 for later usage */
+#ifdef PLUTO
+ m_alloc_ptrs_clear(factors, n+2);
+#else
+ factors = m_alloc_clear( (n+2) * sizeof *factors );
+#endif
+
+ /* make a pool of 3n+5 primes (this is an arbitrary value) */
+ m = n*3+5;
+ if( mode == 1 )
+ m += 5; /* need some more for DSA */
+ if( m < 25 )
+ m = 25;
+#ifdef PLUTO
+ m_alloc_ptrs_clear(pool, m);
+#else
+ pool = m_alloc_clear( m * sizeof *pool );
+#endif
+
+ /* permutate over the pool of primes */
+ count1=count2=0;
+ do {
+ next_try:
+ if( !perms ) {
+ /* allocate new primes */
+ for(i=0; i < m; i++ ) {
+ mpi_free(pool[i]);
+ pool[i] = NULL;
+ }
+ /* init m_out_of_n() */
+#ifdef PLUTO
+ perms = alloc_bytes( m, "perms" );
+#else
+ perms = m_alloc_clear( m );
+#endif
+ for(i=0; i < n; i++ ) {
+ perms[i] = 1;
+ pool[i] = gen_prime( fbits, 0, 1 );
+ factors[i] = pool[i];
+ }
+ }
+ else {
+ m_out_of_n( perms, n, m );
+ for(i=j=0; i < m && j < n ; i++ )
+ if( perms[i] ) {
+ if( !pool[i] )
+ pool[i] = gen_prime( fbits, 0, 1 );
+ factors[j++] = pool[i];
+ }
+ if( i == n ) {
+ m_free(perms); perms = NULL;
+ progress('!');
+ goto next_try; /* allocate new primes */
+ }
+ }
+
+ mpi_set( prime, q );
+ mpi_mul_ui( prime, prime, 2 );
+ if( mode == 1 )
+ mpi_mul( prime, prime, q_factor );
+ for(i=0; i < n; i++ )
+ mpi_mul( prime, prime, factors[i] );
+ mpi_add_ui( prime, prime, 1 );
+ nprime = mpi_get_nbits(prime);
+ if( nprime < pbits ) {
+ if( ++count1 > 20 ) {
+ count1 = 0;
+ qbits++;
+ progress('>');
+ q = gen_prime( qbits, 0, 1 );
+ goto next_try;
+ }
+ }
+ else
+ count1 = 0;
+ if( nprime > pbits ) {
+ if( ++count2 > 20 ) {
+ count2 = 0;
+ qbits--;
+ progress('<');
+ q = gen_prime( qbits, 0, 1 );
+ goto next_try;
+ }
+ }
+ else
+ count2 = 0;
+ } while( !(nprime == pbits && check_prime( prime, val_2 )) );
+
+ if( DBG_CIPHER ) {
+ progress('\n');
+ log_mpidump( "prime : ", prime );
+ log_mpidump( "factor q: ", q );
+ if( mode == 1 )
+ log_mpidump( "factor q0: ", q_factor );
+ for(i=0; i < n; i++ )
+ log_mpidump( "factor pi: ", factors[i] );
+ log_debug("bit sizes: prime=%u, q=%u", mpi_get_nbits(prime), mpi_get_nbits(q) );
+ if( mode == 1 )
+ fprintf(stderr, ", q0=%u", mpi_get_nbits(q_factor) );
+ for(i=0; i < n; i++ )
+ fprintf(stderr, ", p%d=%u", i, mpi_get_nbits(factors[i]) );
+ progress('\n');
+ }
+
+ if( ret_factors ) { /* caller wants the factors */
+#ifdef PLUTO
+ m_alloc_ptrs_clear(*ret_factors, n+2);
+#else
+ *ret_factors = m_alloc_clear( (n+2) * sizeof **ret_factors);
+#endif
+ if( mode == 1 ) {
+ i = 0;
+ (*ret_factors)[i++] = mpi_copy( q_factor );
+ for(; i <= n; i++ )
+ (*ret_factors)[i] = mpi_copy( factors[i] );
+ }
+ else {
+ for(; i < n; i++ )
+ (*ret_factors)[i] = mpi_copy( factors[i] );
+ }
+ }
+
+ if( g ) { /* create a generator (start with 3)*/
+ MPI tmp = mpi_alloc( mpi_get_nlimbs(prime) );
+ MPI b = mpi_alloc( mpi_get_nlimbs(prime) );
+ MPI pmin1 = mpi_alloc( mpi_get_nlimbs(prime) );
+
+ if( mode == 1 )
+ BUG(); /* not yet implemented */
+ factors[n] = q;
+ factors[n+1] = mpi_alloc_set_ui(2);
+ mpi_sub_ui( pmin1, prime, 1 );
+ mpi_set_ui(g,2);
+ do {
+ mpi_add_ui(g, g, 1);
+ if( DBG_CIPHER ) {
+#ifdef PLUTO
+ log_mpidump("checking g: ", g);
+#else
+ log_debug("checking g: ");
+ mpi_print( stderr, g, 1 );
+#endif
+ }
+ else
+ progress('^');
+ for(i=0; i < n+2; i++ ) {
+ /*fputc('~', stderr);*/
+ mpi_fdiv_q(tmp, pmin1, factors[i] );
+ /* (no mpi_pow(), but it is okay to use this with mod prime) */
+ mpi_powm(b, g, tmp, prime );
+ if( !mpi_cmp_ui(b, 1) )
+ break;
+ }
+ if( DBG_CIPHER )
+ progress('\n');
+ } while( i < n+2 );
+ mpi_free(factors[n+1]);
+ mpi_free(tmp);
+ mpi_free(b);
+ mpi_free(pmin1);
+ }
+ if( !DBG_CIPHER )
+ progress('\n');
+
+ m_free( factors ); /* (factors are shallow copies) */
+ for(i=0; i < m; i++ )
+ mpi_free( pool[i] );
+ m_free( pool );
+ m_free(perms);
+ mpi_free(val_2);
+ return prime;
+}
+
+
+
+static MPI
+gen_prime( unsigned nbits, int secret, int randomlevel )
+{
+ unsigned nlimbs;
+ MPI prime, ptest, pminus1, val_2, val_3, result;
+ int i;
+ unsigned x, step;
+ unsigned count1, count2;
+ int *mods;
+
+ if( 0 && DBG_CIPHER )
+ log_debug("generate a prime of %u bits ", nbits );
+
+ if( !no_of_small_prime_numbers ) {
+ for(i=0; small_prime_numbers[i]; i++ )
+ no_of_small_prime_numbers++;
+ }
+ mods = m_alloc( no_of_small_prime_numbers * sizeof *mods );
+ /* make nbits fit into MPI implementation */
+ nlimbs = (nbits + BITS_PER_MPI_LIMB - 1) / BITS_PER_MPI_LIMB;
+ val_2 = mpi_alloc_set_ui( 2 );
+ val_3 = mpi_alloc_set_ui( 3);
+ prime = secret? mpi_alloc_secure( nlimbs ): mpi_alloc( nlimbs );
+ result = mpi_alloc_like( prime );
+ pminus1= mpi_alloc_like( prime );
+ ptest = mpi_alloc_like( prime );
+ count1 = count2 = 0;
+ for(;;) { /* try forvever */
+ int dotcount=0;
+
+ /* generate a random number */
+ { char *p = get_random_bits( nbits, randomlevel, secret );
+ mpi_set_buffer( prime, p, (nbits+7)/8, 0 );
+ m_free(p);
+ }
+
+ /* set high order bit to 1, set low order bit to 1 */
+ mpi_set_highbit( prime, nbits-1 );
+ mpi_set_bit( prime, 0 );
+
+ /* calculate all remainders */
+ for(i=0; (x = small_prime_numbers[i]); i++ )
+ mods[i] = mpi_fdiv_r_ui(NULL, prime, x);
+
+ /* now try some primes starting with prime */
+ for(step=0; step < 20000; step += 2 ) {
+ /* check against all the small primes we have in mods */
+ count1++;
+ for(i=0; (x = small_prime_numbers[i]); i++ ) {
+ while( mods[i] + step >= x )
+ mods[i] -= x;
+ if( !(mods[i] + step) )
+ break;
+ }
+ if( x )
+ continue; /* found a multiple of an already known prime */
+
+ mpi_add_ui( ptest, prime, step );
+
+ /* do a faster Fermat test */
+ count2++;
+ mpi_sub_ui( pminus1, ptest, 1);
+ mpi_powm( result, val_2, pminus1, ptest );
+ if( !mpi_cmp_ui( result, 1 ) ) { /* not composite */
+ /* perform stronger tests */
+ if( is_prime(ptest, 5, &count2 ) ) {
+ if( !mpi_test_bit( ptest, nbits-1 ) ) {
+ progress('\n');
+ log_debug("overflow in prime generation\n");
+ break; /* step loop, continue with a new prime */
+ }
+
+ mpi_free(val_2);
+ mpi_free(val_3);
+ mpi_free(result);
+ mpi_free(pminus1);
+ mpi_free(prime);
+ m_free(mods);
+ return ptest;
+ }
+ }
+ if( ++dotcount == 10 ) {
+ progress('.');
+ dotcount = 0;
+ }
+ }
+ progress(':'); /* restart with a new random value */
+ }
+}
+
+/****************
+ * Returns: true if this may be a prime
+ */
+static int
+check_prime( MPI prime, MPI val_2 )
+{
+ int i;
+ unsigned x;
+ int count=0;
+
+ /* check against small primes */
+ for(i=0; (x = small_prime_numbers[i]); i++ ) {
+ if( mpi_divisible_ui( prime, x ) )
+ return 0;
+ }
+
+ /* a quick fermat test */
+ {
+ MPI result = mpi_alloc_like( prime );
+ MPI pminus1 = mpi_alloc_like( prime );
+ mpi_sub_ui( pminus1, prime, 1);
+ mpi_powm( result, val_2, pminus1, prime );
+ mpi_free( pminus1 );
+ if( mpi_cmp_ui( result, 1 ) ) { /* if composite */
+ mpi_free( result );
+ progress('.');
+ return 0;
+ }
+ mpi_free( result );
+ }
+
+ /* perform stronger tests */
+ if( is_prime(prime, 5, &count ) )
+ return 1; /* is probably a prime */
+ progress('.');
+ return 0;
+}
+
+
+/****************
+ * Return true if n is probably a prime
+ */
+static int
+is_prime( MPI n, unsigned steps, int *count )
+{
+ MPI x = mpi_alloc( mpi_get_nlimbs( n ) );
+ MPI y = mpi_alloc( mpi_get_nlimbs( n ) );
+ MPI z = mpi_alloc( mpi_get_nlimbs( n ) );
+ MPI nminus1 = mpi_alloc( mpi_get_nlimbs( n ) );
+ MPI a2 = mpi_alloc_set_ui( 2 );
+ MPI q;
+ unsigned i, j, k;
+ int rc = 0;
+ unsigned nbits = mpi_get_nbits( n );
+
+ mpi_sub_ui( nminus1, n, 1 );
+
+ /* find q and k, so that n = 1 + 2^k * q */
+ q = mpi_copy( nminus1 );
+ k = mpi_trailing_zeros( q );
+ mpi_tdiv_q_2exp(q, q, k);
+
+ for(i=0 ; i < steps; i++ ) {
+ ++*count;
+ if( !i ) {
+ mpi_set_ui( x, 2 );
+ }
+ else {
+ /*mpi_set_bytes( x, nbits-1, get_random_byte, 0 );*/
+ { char *p = get_random_bits( nbits, 0, 0 );
+ mpi_set_buffer( x, p, (nbits+7)/8, 0 );
+ m_free(p);
+ }
+ /* make sure that the number is smaller than the prime
+ * and keep the randomness of the high bit */
+ if( mpi_test_bit( x, nbits-2 ) ) {
+ mpi_set_highbit( x, nbits-2 ); /* clear all higher bits */
+ }
+ else {
+ mpi_set_highbit( x, nbits-2 );
+ mpi_clear_bit( x, nbits-2 );
+ }
+ assert( mpi_cmp( x, nminus1 ) < 0 && mpi_cmp_ui( x, 1 ) > 0 );
+ }
+ mpi_powm( y, x, q, n);
+ if( mpi_cmp_ui(y, 1) && mpi_cmp( y, nminus1 ) ) {
+ for( j=1; j < k && mpi_cmp( y, nminus1 ); j++ ) {
+ mpi_powm(y, y, a2, n);
+ if( !mpi_cmp_ui( y, 1 ) )
+ goto leave; /* not a prime */
+ }
+ if( mpi_cmp( y, nminus1 ) )
+ goto leave; /* not a prime */
+ }
+ progress('+');
+ }
+ rc = 1; /* may be a prime */
+
+ leave:
+ mpi_free( x );
+ mpi_free( y );
+ mpi_free( z );
+ mpi_free( nminus1 );
+ mpi_free( q );
+
+ return rc;
+}
+
+
+static void
+m_out_of_n( char *array, int m, int n )
+{
+ int i=0, i1=0, j=0, jp=0, j1=0, k1=0, k2=0;
+
+ if( !m || m >= n )
+ return;
+
+ if( m == 1 ) { /* special case */
+ for(i=0; i < n; i++ )
+ if( array[i] ) {
+ array[i++] = 0;
+ if( i >= n )
+ i = 0;
+ array[i] = 1;
+ return;
+ }
+ BUG();
+ }
+
+ for(j=1; j < n; j++ ) {
+ if( array[n-1] == array[n-j-1] )
+ continue;
+ j1 = j;
+ break;
+ }
+
+ if( m & 1 ) { /* m is odd */
+ if( array[n-1] ) {
+ if( j1 & 1 ) {
+ k1 = n - j1;
+ k2 = k1+2;
+ if( k2 > n )
+ k2 = n;
+ goto leave;
+ }
+ goto scan;
+ }
+ k2 = n - j1 - 1;
+ if( k2 == 0 ) {
+ k1 = i;
+ k2 = n - j1;
+ }
+ else if( array[k2] && array[k2-1] )
+ k1 = n;
+ else
+ k1 = k2 + 1;
+ }
+ else { /* m is even */
+ if( !array[n-1] ) {
+ k1 = n - j1;
+ k2 = k1 + 1;
+ goto leave;
+ }
+
+ if( !(j1 & 1) ) {
+ k1 = n - j1;
+ k2 = k1+2;
+ if( k2 > n )
+ k2 = n;
+ goto leave;
+ }
+ scan:
+ jp = n - j1 - 1;
+ for(i=1; i <= jp; i++ ) {
+ i1 = jp + 2 - i;
+ if( array[i1-1] ) {
+ if( array[i1-2] ) {
+ k1 = i1 - 1;
+ k2 = n - j1;
+ }
+ else {
+ k1 = i1 - 1;
+ k2 = n + 1 - j1;
+ }
+ goto leave;
+ }
+ }
+ k1 = 1;
+ k2 = n + 1 - m;
+ }
+ leave:
+ array[k1-1] = !array[k1-1];
+ array[k2-1] = !array[k2-1];
+}
+
diff --git a/programs/pluto/rcv_info.c b/programs/pluto/rcv_info.c
new file mode 100644
index 000000000..1f6127830
--- /dev/null
+++ b/programs/pluto/rcv_info.c
@@ -0,0 +1,308 @@
+/* info/policy communicating routines
+ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rcv_info.c,v 1.2 2004/04/01 18:44:38 as Exp $
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "id.h"
+#include "connections.h"
+#include "foodgroups.h"
+#include "whack.h" /* needs connections.h */
+#include "packet.h"
+#include "demux.h" /* needs packet.h */
+#include "state.h"
+#include "ipsec_doi.h" /* needs demux.h and state.h */
+#include "kernel.h"
+#include "rcv_whack.h"
+#include "log.h"
+#include "keys.h"
+#include "adns.h" /* needs <resolv.h> */
+#include "dnskey.h" /* needs keys.h and adns.h */
+#include "server.h"
+
+#include "freeswan/ipsec_policy.h"
+#include "rcv_info.h"
+
+/* global */
+int info_fd = -1;
+
+static void
+info_lookuphostpair(struct ipsec_policy_cmd_query *ipcq)
+{
+ struct connection *c;
+ struct state *p1st, *p2st;
+
+
+ /* default result: no crypto */
+ ipcq->strength = IPSEC_PRIVACY_NONE;
+ ipcq->bandwidth = IPSEC_QOS_WIRESPEED;
+ ipcq->credential_count = 0;
+
+#ifdef DEBUG
+ {
+ char sstr[ADDRTOT_BUF], dstr[ADDRTOT_BUF];
+
+ addrtot(&ipcq->query_local, 0, sstr, sizeof(sstr));
+ addrtot(&ipcq->query_remote, 0, dstr, sizeof(dstr));
+ DBG_log("info request for %s -> %s", sstr, dstr);
+ }
+#endif
+
+ /* okay, look up what connection handles this ip pair */
+
+ c = find_connection_for_clients(NULL,
+ &ipcq->query_local,
+ &ipcq->query_remote);
+ if (c == NULL)
+ {
+ /* try reversing it */
+ c = find_connection_for_clients(NULL,
+ &ipcq->query_remote,
+ &ipcq->query_local);
+ if (c != NULL)
+ {
+ ip_address tmp;
+ tmp = ipcq->query_local;
+ ipcq->query_local = ipcq->query_remote;
+ ipcq->query_remote = tmp;
+ }
+ }
+
+ if (c == NULL)
+ {
+#ifdef DEBUG
+ DBG_log("no connection found");
+#endif
+ return; /* no crypto */
+ }
+
+ if (c->newest_ipsec_sa == SOS_NOBODY)
+ {
+ ip_subnet us, them;
+
+ DBG_log("connection %s found, no ipsec state, looking again", c->name);
+ addrtosubnet(&ipcq->query_local, &us);
+ addrtosubnet(&ipcq->query_remote, &them);
+ c = find_client_connection(c, &us, &them);
+
+ if (c == NULL)
+ return; /* no crypto */
+ }
+
+ DBG_log("connection %s[%ld] with state %u"
+ , c->name, c->instance_serial
+ , (unsigned int)c->newest_ipsec_sa);
+
+ if (c->newest_ipsec_sa == SOS_NOBODY)
+ return; /* no crypto */
+
+ /* we found a connection, try to lookup the state */
+ p2st = state_with_serialno(c->newest_ipsec_sa);
+
+ p1st = find_phase1_state(c, ISAKMP_SA_ESTABLISHED_STATES);
+
+ if (p1st == NULL || p2st == NULL)
+ {
+ DBG_log("connection %s[%ld] has missing states %s %s"
+ , c->name, c->instance_serial
+ , (p1st ? "phase1" : "")
+ , (p2st ? "phase1" : ""));
+ return; /* no crypto */
+ }
+
+ /* if we have AH present, then record minimal info */
+ if (p2st->st_ah.present)
+ {
+ ipcq->strength = IPSEC_PRIVACY_INTEGRAL;
+ ipcq->auth_detail = p2st->st_esp.attrs.auth;
+ }
+
+ if (p2st->st_esp.present)
+ {
+ /*
+ * XXX-mcr Please do not shout at me about relative strengths
+ * here. I'm not a cryptographer. I just diddle bits.
+ */
+ switch (p2st->st_esp.attrs.transid)
+ {
+ case ESP_NULL:
+ /* actually, do not change it if we set it from AH */
+ break;
+
+ case ESP_DES:
+ case ESP_DES_IV64:
+ case ESP_DES_IV32:
+ case ESP_RC4:
+ ipcq->strength = IPSEC_PRIVACY_ROT13;
+ break;
+
+ case ESP_RC5:
+ case ESP_IDEA:
+ case ESP_CAST:
+ case ESP_BLOWFISH:
+ case ESP_3DES:
+ ipcq->strength = IPSEC_PRIVACY_PRIVATE;
+ ipcq->bandwidth = IPSEC_QOS_VOIP;
+ break;
+
+ case ESP_3IDEA:
+ ipcq->strength = IPSEC_PRIVACY_STRONG;
+ ipcq->bandwidth = IPSEC_QOS_INTERACTIVE;
+ break;
+
+ case ESP_AES:
+ ipcq->strength = IPSEC_PRIVACY_STRONG;
+ ipcq->bandwidth = IPSEC_QOS_FTP;
+ break;
+ }
+ ipcq->esp_detail = p2st->st_esp.attrs.transid;
+ }
+
+ if (p2st->st_ipcomp.present)
+ ipcq->comp_detail = p2st->st_esp.attrs.transid;
+
+ /* now! the credentails that were used */
+ /* for the moment we only have 1 credential, the DNS name,
+ * because the DNS servers do not return the chain of SIGs yet
+ */
+
+ if(!c->spd.this.key_from_DNS_on_demand)
+ {
+ /* the key didn't come from the DNS in some way,
+ * so it must have been loaded locally.
+ */
+ ipcq->credential_count = 1;
+ ipcq->credentials[0].ii_type = c->spd.this.id.kind;
+ ipcq->credentials[0].ii_format = CERT_RAW_RSA;
+ }
+
+#if 0
+ switch (c->spd.id.kind)
+ {
+ case ID_IPV4_ADDR:
+ }
+ if (c->gw_info == NULL)
+ {
+ plog("rcv_info: connection %s had NULL gw_info.", c->name);
+ return
+ }
+#endif
+
+ ipcq->credential_count = 1;
+
+ /* pull credentials out of gw_info */
+
+ switch (p1st->st_peer_pubkey->dns_auth_level)
+ {
+ case DAL_UNSIGNED:
+ case DAL_NOTSEC:
+ /* these seem to be the same for this purpose */
+ ipcq->credentials[0].ii_type = p1st->st_peer_pubkey->id.kind;
+ ipcq->credentials[0].ii_type = CERT_NONE;
+ idtoa(&p1st->st_peer_pubkey->id
+ , ipcq->credentials[0].ii_credential.ipsec_dns_signed.fqdn
+ , sizeof(ipcq->credentials[0].ii_credential.ipsec_dns_signed.fqdn));
+ break;
+
+ case DAL_SIGNED:
+ ipcq->credentials[0].ii_type = p1st->st_peer_pubkey->id.kind;
+ ipcq->credentials[0].ii_format = CERT_DNS_SIGNED_KEY;
+ idtoa(&p1st->st_peer_pubkey->id
+ , ipcq->credentials[0].ii_credential.ipsec_dns_signed.fqdn
+ , sizeof(ipcq->credentials[0].ii_credential.ipsec_dns_signed.fqdn));
+
+ if (p1st->st_peer_pubkey->dns_sig != NULL)
+ {
+ strncat(ipcq->credentials[0].ii_credential.ipsec_dns_signed.dns_sig
+ , p1st->st_peer_pubkey->dns_sig
+ , sizeof(ipcq->credentials[0].ii_credential.ipsec_dns_signed.dns_sig));
+ }
+ break;
+
+ case DAL_LOCAL:
+ ipcq->credentials[0].ii_type = p1st->st_peer_pubkey->id.kind;
+ ipcq->credentials[0].ii_format = CERT_RAW_RSA;
+ idtoa(&p1st->st_peer_pubkey->id
+ , ipcq->credentials[0].ii_credential.ipsec_raw_key.id_name
+ , sizeof(ipcq->credentials[0].ii_credential.ipsec_raw_key.id_name));
+ break;
+ }
+}
+
+/*
+ * Handle an info/policy request.
+ *
+ * For now, we close the socket after answering the request.
+ *
+ */
+void
+info_handle(int infoctlfd)
+{
+ struct sockaddr_un info_client_addr;
+ int info_addr_len = sizeof(info_client_addr);
+ /* Note: actual value in n should fit in int. To print, cast to int. */
+ int infofd;
+ err_t err;
+ struct ipsec_policy_cmd_query ipcq;
+
+ infofd = accept(infoctlfd, (struct sockaddr *)&info_client_addr
+ , &info_addr_len);
+
+ if (infofd < 0)
+ {
+ log_errno((e, "accept() failed in info_handle()"));
+ return;
+ }
+
+ err = ipsec_policy_readmsg(infofd, (unsigned char *)&ipcq, sizeof(ipcq));
+
+ if (err != NULL)
+ {
+ log_errno((e, "readmsg said: %s", err));
+ close(infofd);
+ return;
+ }
+
+ switch (ipcq.head.ipm_msg_type)
+ {
+ case IPSEC_CMD_QUERY_HOSTPAIR:
+ info_lookuphostpair(&ipcq);
+ write(infofd, &ipcq, ipcq.head.ipm_msg_len);
+ break;
+
+ default:
+ plog("got unimplemented msg type: %d", ipcq.head.ipm_msg_type);
+ break;
+ }
+
+ /* for now, close the socket */
+ close(infofd);
+}
diff --git a/programs/pluto/rcv_info.h b/programs/pluto/rcv_info.h
new file mode 100644
index 000000000..b5eaef219
--- /dev/null
+++ b/programs/pluto/rcv_info.h
@@ -0,0 +1,18 @@
+/* whack communicating routines
+ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rcv_info.h,v 1.1 2004/03/15 20:35:29 as Exp $
+ */
+
+#include "freeswan/ipsec_policy.h"
+extern void info_handle(int infoctlfd);
diff --git a/programs/pluto/rcv_whack.c b/programs/pluto/rcv_whack.c
new file mode 100644
index 000000000..164a4f249
--- /dev/null
+++ b/programs/pluto/rcv_whack.c
@@ -0,0 +1,655 @@
+/* whack communicating routines
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rcv_whack.c,v 1.17 2005/12/25 12:41:23 as Exp $
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+#include <fcntl.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "id.h"
+#include "ca.h"
+#include "certs.h"
+#include "ac.h"
+#include "smartcard.h"
+#include "connections.h"
+#include "foodgroups.h"
+#include "whack.h" /* needs connections.h */
+#include "packet.h"
+#include "demux.h" /* needs packet.h */
+#include "state.h"
+#include "ipsec_doi.h" /* needs demux.h and state.h */
+#include "kernel.h"
+#include "rcv_whack.h"
+#include "log.h"
+#include "keys.h"
+#include "adns.h" /* needs <resolv.h> */
+#include "dnskey.h" /* needs keys.h and adns.h */
+#include "server.h"
+#include "fetch.h"
+#include "ocsp.h"
+#include "crl.h"
+
+#include "kernel_alg.h"
+#include "ike_alg.h"
+/* helper variables and function to decode strings from whack message */
+
+static char *next_str
+ , *str_roof;
+
+static bool
+unpack_str(char **p)
+{
+ char *end = memchr(next_str, '\0', str_roof - next_str);
+
+ if (end == NULL)
+ {
+ return FALSE; /* fishy: no end found */
+ }
+ else
+ {
+ *p = next_str == end? NULL : next_str;
+ next_str = end + 1;
+ return TRUE;
+ }
+}
+
+/* bits loading keys from asynchronous DNS */
+
+enum key_add_attempt {
+ ka_TXT,
+#ifdef USE_KEYRR
+ ka_KEY,
+#endif
+ ka_roof /* largest value + 1 */
+};
+
+struct key_add_common {
+ int refCount;
+ char *diag[ka_roof];
+ int whack_fd;
+ bool success;
+};
+
+struct key_add_continuation {
+ struct adns_continuation ac; /* common prefix */
+ struct key_add_common *common; /* common data */
+ enum key_add_attempt lookingfor;
+};
+
+static void
+key_add_ugh(const struct id *keyid, err_t ugh)
+{
+ char name[BUF_LEN]; /* longer IDs will be truncated in message */
+
+ (void)idtoa(keyid, name, sizeof(name));
+ loglog(RC_NOKEY
+ , "failure to fetch key for %s from DNS: %s", name, ugh);
+}
+
+/* last one out: turn out the lights */
+static void
+key_add_merge(struct key_add_common *oc, const struct id *keyid)
+{
+ if (oc->refCount == 0)
+ {
+ enum key_add_attempt kaa;
+
+ /* if no success, print all diagnostics */
+ if (!oc->success)
+ for (kaa = ka_TXT; kaa != ka_roof; kaa++)
+ key_add_ugh(keyid, oc->diag[kaa]);
+
+ for (kaa = ka_TXT; kaa != ka_roof; kaa++)
+ pfreeany(oc->diag[kaa]);
+
+ close(oc->whack_fd);
+ pfree(oc);
+ }
+}
+
+static void
+key_add_continue(struct adns_continuation *ac, err_t ugh)
+{
+ struct key_add_continuation *kc = (void *) ac;
+ struct key_add_common *oc = kc->common;
+
+ passert(whack_log_fd == NULL_FD);
+ whack_log_fd = oc->whack_fd;
+
+ if (ugh != NULL)
+ {
+ oc->diag[kc->lookingfor] = clone_str(ugh, "key add error");
+ }
+ else
+ {
+ oc->success = TRUE;
+ transfer_to_public_keys(kc->ac.gateways_from_dns
+#ifdef USE_KEYRR
+ , &kc->ac.keys_from_dns
+#endif /* USE_KEYRR */
+ );
+ }
+
+ oc->refCount--;
+ key_add_merge(oc, &ac->id);
+ whack_log_fd = NULL_FD;
+}
+
+static void
+key_add_request(const whack_message_t *msg)
+{
+ struct id keyid;
+ err_t ugh = atoid(msg->keyid, &keyid, FALSE);
+
+ if (ugh != NULL)
+ {
+ loglog(RC_BADID, "bad --keyid \"%s\": %s", msg->keyid, ugh);
+ }
+ else
+ {
+ if (!msg->whack_addkey)
+ delete_public_keys(&keyid, msg->pubkey_alg
+ , empty_chunk, empty_chunk);
+
+ if (msg->keyval.len == 0)
+ {
+ struct key_add_common *oc
+ = alloc_thing(struct key_add_common
+ , "key add common things");
+ enum key_add_attempt kaa;
+
+ /* initialize state shared by queries */
+ oc->refCount = 0;
+ oc->whack_fd = dup_any(whack_log_fd);
+ oc->success = FALSE;
+
+ for (kaa = ka_TXT; kaa != ka_roof; kaa++)
+ {
+ struct key_add_continuation *kc
+ = alloc_thing(struct key_add_continuation
+ , "key add continuation");
+
+ oc->diag[kaa] = NULL;
+ oc->refCount++;
+ kc->common = oc;
+ kc->lookingfor = kaa;
+ switch (kaa)
+ {
+ case ka_TXT:
+ ugh = start_adns_query(&keyid
+ , &keyid /* same */
+ , T_TXT
+ , key_add_continue
+ , &kc->ac);
+ break;
+#ifdef USE_KEYRR
+ case ka_KEY:
+ ugh = start_adns_query(&keyid
+ , NULL
+ , T_KEY
+ , key_add_continue
+ , &kc->ac);
+ break;
+#endif /* USE_KEYRR */
+ default:
+ bad_case(kaa); /* suppress gcc warning */
+ }
+ if (ugh != NULL)
+ {
+ oc->diag[kaa] = clone_str(ugh, "early key add failure");
+ oc->refCount--;
+ }
+ }
+
+ /* Done launching queries.
+ * Handle total failure case.
+ */
+ key_add_merge(oc, &keyid);
+ }
+ else
+ {
+ ugh = add_public_key(&keyid, DAL_LOCAL, msg->pubkey_alg
+ , &msg->keyval, &pubkeys);
+ if (ugh != NULL)
+ loglog(RC_LOG_SERIOUS, "%s", ugh);
+ }
+ }
+}
+
+/* Handle a kernel request. Supposedly, there's a message in
+ * the kernelsock socket.
+ */
+void
+whack_handle(int whackctlfd)
+{
+ whack_message_t msg;
+ struct sockaddr_un whackaddr;
+ int whackaddrlen = sizeof(whackaddr);
+ int whackfd = accept(whackctlfd, (struct sockaddr *)&whackaddr, &whackaddrlen);
+ /* Note: actual value in n should fit in int. To print, cast to int. */
+ ssize_t n;
+
+ if (whackfd < 0)
+ {
+ log_errno((e, "accept() failed in whack_handle()"));
+ return;
+ }
+ if (fcntl(whackfd, F_SETFD, FD_CLOEXEC) < 0)
+ {
+ log_errno((e, "failed to set CLOEXEC in whack_handle()"));
+ close(whackfd);
+ return;
+ }
+
+ n = read(whackfd, &msg, sizeof(msg));
+
+ if (n == -1)
+ {
+ log_errno((e, "read() failed in whack_handle()"));
+ close(whackfd);
+ return;
+ }
+
+ whack_log_fd = whackfd;
+
+ /* sanity check message */
+ {
+ err_t ugh = NULL;
+
+ next_str = msg.string;
+ str_roof = (char *)&msg + n;
+
+ if ((size_t)n < offsetof(whack_message_t, whack_shutdown) + sizeof(msg.whack_shutdown))
+ {
+ ugh = builddiag("ignoring runt message from whack: got %d bytes", (int)n);
+ }
+ else if (msg.magic != WHACK_MAGIC)
+ {
+ if (msg.magic == WHACK_BASIC_MAGIC)
+ {
+ /* Only shutdown command. Simpler inter-version compatability. */
+ if (msg.whack_shutdown)
+ {
+ plog("shutting down");
+ exit_pluto(0); /* delete lock and leave, with 0 status */
+ }
+ ugh = ""; /* bail early, but without complaint */
+ }
+ else
+ {
+ ugh = builddiag("ignoring message from whack with bad magic %d; should be %d; probably wrong version"
+ , msg.magic, WHACK_MAGIC);
+ }
+ }
+ else if (next_str > str_roof)
+ {
+ ugh = builddiag("ignoring truncated message from whack: got %d bytes; expected %u"
+ , (int) n, (unsigned) sizeof(msg));
+ }
+ else if (!unpack_str(&msg.name) /* string 1 */
+ || !unpack_str(&msg.left.id) /* string 2 */
+ || !unpack_str(&msg.left.cert) /* string 3 */
+ || !unpack_str(&msg.left.ca) /* string 4 */
+ || !unpack_str(&msg.left.groups) /* string 5 */
+ || !unpack_str(&msg.left.updown) /* string 6 */
+#ifdef VIRTUAL_IP
+ || !unpack_str(&msg.left.virt)
+#endif
+ || !unpack_str(&msg.right.id) /* string 7 */
+ || !unpack_str(&msg.right.cert) /* string 8 */
+ || !unpack_str(&msg.right.ca) /* string 9 */
+ || !unpack_str(&msg.right.groups) /* string 10 */
+ || !unpack_str(&msg.right.updown) /* string 11 */
+#ifdef VIRTUAL_IP
+ || !unpack_str(&msg.right.virt)
+#endif
+ || !unpack_str(&msg.keyid) /* string 12 */
+ || !unpack_str(&msg.myid) /* string 13 */
+ || !unpack_str(&msg.cacert) /* string 14 */
+ || !unpack_str(&msg.ldaphost) /* string 15 */
+ || !unpack_str(&msg.ldapbase) /* string 16 */
+ || !unpack_str(&msg.crluri) /* string 17 */
+ || !unpack_str(&msg.crluri2) /* string 18 */
+ || !unpack_str(&msg.ocspuri) /* string 19 */
+ || !unpack_str(&msg.ike) /* string 20 */
+ || !unpack_str(&msg.esp) /* string 21 */
+ || !unpack_str(&msg.sc_data) /* string 22 */
+ || str_roof - next_str != (ptrdiff_t)msg.keyval.len) /* check chunk */
+ {
+ ugh = "message from whack contains bad string";
+ }
+ else
+ {
+ msg.keyval.ptr = next_str; /* grab chunk */
+ }
+
+ if (ugh != NULL)
+ {
+ if (*ugh != '\0')
+ loglog(RC_BADWHACKMESSAGE, "%s", ugh);
+ whack_log_fd = NULL_FD;
+ close(whackfd);
+ return;
+ }
+ }
+
+ if (msg.whack_options)
+ {
+#ifdef DEBUG
+ if (msg.name == NULL)
+ {
+ /* we do a two-step so that if either old or new would
+ * cause the message to print, it will be printed.
+ */
+ cur_debugging |= msg.debugging;
+ DBG(DBG_CONTROL
+ , DBG_log("base debugging = %s"
+ , bitnamesof(debug_bit_names, msg.debugging)));
+ cur_debugging = base_debugging = msg.debugging;
+ }
+ else if (!msg.whack_connection)
+ {
+ struct connection *c = con_by_name(msg.name, TRUE);
+
+ if (c != NULL)
+ {
+ c->extra_debugging = msg.debugging;
+ DBG(DBG_CONTROL
+ , DBG_log("\"%s\" extra_debugging = %s"
+ , c->name
+ , bitnamesof(debug_bit_names, c->extra_debugging)));
+ }
+ }
+#endif
+ }
+
+ if (msg.whack_myid)
+ set_myid(MYID_SPECIFIED, msg.myid);
+
+ /* Deleting combined with adding a connection works as replace.
+ * To make this more useful, in only this combination,
+ * delete will silently ignore the lack of the connection.
+ */
+ if (msg.whack_delete)
+ {
+ if (msg.whack_ca)
+ find_ca_info_by_name(msg.name, TRUE);
+ else
+ delete_connections_by_name(msg.name, !msg.whack_connection);
+ }
+
+ if (msg.whack_deletestate)
+ {
+ struct state *st = state_with_serialno(msg.whack_deletestateno);
+
+ if (st == NULL)
+ {
+ loglog(RC_UNKNOWN_NAME, "no state #%lu to delete"
+ , msg.whack_deletestateno);
+ }
+ else
+ {
+ delete_state(st);
+ }
+ }
+
+ if (msg.whack_crash)
+ delete_states_by_peer(&msg.whack_crash_peer);
+
+ if (msg.whack_connection)
+ add_connection(&msg);
+
+ if (msg.whack_ca && msg.cacert != NULL)
+ add_ca_info(&msg);
+
+ /* process "listen" before any operation that could require it */
+ if (msg.whack_listen)
+ {
+ close_peerlog(); /* close any open per-peer logs */
+ plog("listening for IKE messages");
+ listening = TRUE;
+ daily_log_reset();
+ reset_adns_restart_count();
+ set_myFQDN();
+ find_ifaces();
+ load_preshared_secrets(NULL_FD);
+ load_groups();
+ }
+ if (msg.whack_unlisten)
+ {
+ plog("no longer listening for IKE messages");
+ listening = FALSE;
+ }
+
+ if (msg.whack_reread & REREAD_SECRETS)
+ {
+ load_preshared_secrets(whackfd);
+ }
+
+ if (msg.whack_reread & REREAD_CACERTS)
+ {
+ load_authcerts("CA cert", CA_CERT_PATH, AUTH_CA);
+ }
+
+ if (msg.whack_reread & REREAD_AACERTS)
+ {
+ load_authcerts("AA cert", AA_CERT_PATH, AUTH_AA);
+ }
+
+ if (msg.whack_reread & REREAD_OCSPCERTS)
+ {
+ load_authcerts("OCSP cert", OCSP_CERT_PATH, AUTH_OCSP);
+ }
+
+ if (msg.whack_reread & REREAD_ACERTS)
+ {
+ load_acerts();
+ }
+
+ if (msg.whack_reread & REREAD_CRLS)
+ {
+ load_crls();
+ }
+
+ if (msg.whack_purgeocsp)
+ {
+ free_ocsp_fetch();
+ free_ocsp_cache();
+ }
+
+ if (msg.whack_list & LIST_ALGS)
+ {
+ ike_alg_list();
+ kernel_alg_list();
+ }
+ if (msg.whack_list & LIST_PUBKEYS)
+ {
+ list_public_keys(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_CERTS)
+ {
+ list_certs(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_CACERTS)
+ {
+ list_authcerts("CA", AUTH_CA, msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_AACERTS)
+ {
+ list_authcerts("AA", AUTH_AA, msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_OCSPCERTS)
+ {
+ list_authcerts("OCSP", AUTH_OCSP, msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_ACERTS)
+ {
+ list_acerts(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_GROUPS)
+ {
+ list_groups(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_CAINFOS)
+ {
+ list_ca_infos(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_CRLS)
+ {
+ list_crls(msg.whack_utc, strict_crl_policy);
+ list_crl_fetch_requests(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_OCSP)
+ {
+ list_ocsp_cache(msg.whack_utc, strict_crl_policy);
+ list_ocsp_fetch_requests(msg.whack_utc);
+ }
+
+ if (msg.whack_list & LIST_CARDS)
+ {
+ scx_list(msg.whack_utc);
+ }
+
+ if (msg.whack_key)
+ {
+ /* add a public key */
+ key_add_request(&msg);
+ }
+
+ if (msg.whack_route)
+ {
+ if (!listening)
+ whack_log(RC_DEAF, "need --listen before --route");
+ else
+ {
+ struct connection *c = con_by_name(msg.name, TRUE);
+
+ if (c != NULL)
+ {
+ set_cur_connection(c);
+ if (!oriented(*c))
+ whack_log(RC_ORIENT
+ , "we have no ipsecN interface for either end of this connection");
+ else if (c->policy & POLICY_GROUP)
+ route_group(c);
+ else if (!trap_connection(c))
+ whack_log(RC_ROUTE, "could not route");
+ reset_cur_connection();
+ }
+ }
+ }
+
+ if (msg.whack_unroute)
+ {
+ struct connection *c = con_by_name(msg.name, TRUE);
+
+ if (c != NULL)
+ {
+ struct spd_route *sr;
+ int fail = 0;
+
+ set_cur_connection(c);
+
+ for (sr = &c->spd; sr != NULL; sr = sr->next)
+ {
+ if (sr->routing >= RT_ROUTED_TUNNEL)
+ fail++;
+ }
+ if (fail > 0)
+ whack_log(RC_RTBUSY, "cannot unroute: route busy");
+ else if (c->policy & POLICY_GROUP)
+ unroute_group(c);
+ else
+ unroute_connection(c);
+ reset_cur_connection();
+ }
+ }
+
+ if (msg.whack_initiate)
+ {
+ if (!listening)
+ whack_log(RC_DEAF, "need --listen before --initiate");
+ else
+ initiate_connection(msg.name
+ , msg.whack_async? NULL_FD : dup_any(whackfd));
+ }
+
+ if (msg.whack_oppo_initiate)
+ {
+ if (!listening)
+ whack_log(RC_DEAF, "need --listen before opportunistic initiation");
+ else
+ initiate_opportunistic(&msg.oppo_my_client, &msg.oppo_peer_client, 0
+ , FALSE
+ , msg.whack_async? NULL_FD : dup_any(whackfd));
+ }
+
+ if (msg.whack_terminate)
+ terminate_connection(msg.name);
+
+ if (msg.whack_status)
+ show_status(msg.whack_statusall, msg.name);
+
+ if (msg.whack_shutdown)
+ {
+ plog("shutting down");
+ exit_pluto(0); /* delete lock and leave, with 0 status */
+ }
+
+ if (msg.whack_sc_op != SC_OP_NONE)
+ {
+ if (pkcs11_proxy)
+ scx_op_via_whack(msg.sc_data, msg.inbase, msg.outbase
+ , msg.whack_sc_op, msg.keyid, whackfd);
+ else
+ plog("pkcs11 access to smartcard not allowed (set pkcs11proxy=yes)");
+ }
+
+ whack_log_fd = NULL_FD;
+ close(whackfd);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:4
+ * c-style: pluto
+ * End:
+ */
diff --git a/programs/pluto/rcv_whack.h b/programs/pluto/rcv_whack.h
new file mode 100644
index 000000000..f42761c51
--- /dev/null
+++ b/programs/pluto/rcv_whack.h
@@ -0,0 +1,17 @@
+/* whack communicating routines
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rcv_whack.h,v 1.1 2004/03/15 20:35:29 as Exp $
+ */
+
+extern void whack_handle(int kernelfd);
diff --git a/programs/pluto/rnd.c b/programs/pluto/rnd.c
new file mode 100644
index 000000000..da72cc8ff
--- /dev/null
+++ b/programs/pluto/rnd.c
@@ -0,0 +1,250 @@
+/* randomness machinery
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rnd.c,v 1.3 2005/09/08 16:26:30 as Exp $
+ */
+
+/* A true random number generator (we hope)
+ *
+ * Under LINUX ("linux" predefined), use /dev/urandom.
+ * Under OpenBSD ("__OpenBSD__" predefined), use arc4random().
+ * Otherwise use our own random number generator based on clock skew.
+ * I (ADK) first heard of the idea from John Ioannidis, who heard it
+ * from Matt Blaze and/or Jack Lacy.
+ * ??? Why is mixing need for linux but not OpenBSD?
+ */
+
+/* Pluto's uses of randomness:
+ *
+ * - Setting up the "secret_of_the_day". This changes every hour! 20
+ * bytes a shot. It is used in building responder cookies.
+ *
+ * - generating initiator cookies (8 bytes, once per Phase 1 initiation).
+ *
+ * - 32 bytes per DH local secret. Once per Main Mode exchange and once
+ * per Quick Mode Exchange with PFS. (Size is our choice, with
+ * tradeoffs.)
+ *
+ * - 16 bytes per nonce we generate. Once per Main Mode exchange and
+ * once per Quick Mode exchange. (Again, we choose the size.)
+ *
+ * - 4 bytes per SPI number that we generate. We choose the SPIs for all
+ * inbound SPIs, one to three per IPSEC SA (one for AH (rare, probably)
+ * one for ESP (almost always), and one for tunnel (very common)).
+ * I don't actually know how the kernel would generate these numbers --
+ * currently Pluto generates them; this isn't the way things will be
+ * done in the future.
+ *
+ * - 4 bytes per Message ID we need to generate. One per Quick Mode
+ * exchange. Eventually, one per informational exchange.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+#include <freeswan.h>
+
+#include "sha1.h"
+#include "constants.h"
+#include "defs.h"
+#include "rnd.h"
+#include "log.h"
+#include "timer.h"
+
+#ifdef linux
+# define USE_DEV_RANDOM 1
+# define RANDOM_PATH "/dev/urandom"
+#else
+# ifdef __OpenBSD__
+# define USE_ARC4RANDOM
+# else
+# define USE_CLOCK_SLEW
+# endif
+#endif
+
+#ifdef USE_ARC4RANDOM
+
+#define get_rnd_byte() (arc4random() % 256)
+
+#else /**** start of large #else ****/
+
+#ifdef USE_DEV_RANDOM
+static int random_fd = NULL_FD;
+#endif
+
+#define RANDOM_POOL_SIZE SHA1_DIGEST_SIZE
+static u_char random_pool[RANDOM_POOL_SIZE];
+
+#ifdef USE_DEV_RANDOM
+
+/* Generate (what we hope is) a true random byte using /dev/urandom */
+static u_char
+generate_rnd_byte(void)
+{
+ u_char c;
+
+ if (read(random_fd, &c, sizeof(c)) == -1)
+ exit_log_errno((e, "read() failed in get_rnd_byte()"));
+
+ return c;
+}
+
+#else /* !USE_DEV_RANDOM */
+
+/* Generate (what we hope is) a true random byte using the clock skew trick.
+ * Note: this code is not maintained! In particular, LINUX signal(2)
+ * semantics changed with glibc2 (and not for the better). It isn't clear
+ * that this code will work. We keep the code because someday it might
+ * come in handy.
+ */
+# error "This code is not maintained. Please define USE_DEV_RANDOM."
+
+static volatile sig_atomic_t i, j, k;
+
+/* timer signal handler */
+static void
+rnd_handler(int ignore_me UNUSED)
+{
+ k <<= 1; /* Shift left by 1 */
+ j++;
+ k |= (i & 0x1); /* Get lsbit of counter */
+
+ if (j != 8)
+ signal(SIGVTALRM, rnd_handler);
+}
+
+static u_char
+generate_rnd_byte(void)
+{
+ struct itimerval tmval, ntmval;
+
+# ifdef NEVER /* ??? */
+# ifdef linux
+ int mask = siggetmask();
+
+ mask |= SIGVTALRM;
+ sigsetmask(mask);
+# endif
+# endif
+
+ i = 0;
+ j = 0;
+
+ ntmval.it_interval.tv_sec = 0;
+ ntmval.it_interval.tv_usec = 1;
+ ntmval.it_value.tv_sec = 0;
+ ntmval.it_value.tv_usec = 1;
+ signal(SIGVTALRM, rnd_handler);
+ setitimer(ITIMER_VIRTUAL, &ntmval, &tmval);
+
+ while (j != 8)
+ i++;
+
+ setitimer(ITIMER_VIRTUAL, &tmval, &ntmval);
+ signal(SIGVTALRM, SIG_IGN);
+
+# ifdef NEVER /* ??? */
+# ifdef linux
+ mask ^= SIGVTALRM;
+ sigsetmask(mask);
+# endif
+# endif
+
+ return k;
+}
+
+#endif /* !USE_DEV_RANDOM */
+
+static void
+mix_pool(void)
+{
+ SHA1_CTX ctx;
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, random_pool, RANDOM_POOL_SIZE);
+ SHA1Final(random_pool, &ctx);
+}
+
+/*
+ * Get a single random byte.
+ */
+static u_char
+get_rnd_byte(void)
+{
+ random_pool[RANDOM_POOL_SIZE - 1] = generate_rnd_byte();
+ random_pool[0] = generate_rnd_byte();
+ mix_pool();
+ return random_pool[0];
+}
+
+#endif /* !USE_ARC4RANDOM */ /**** end of large #else ****/
+
+void
+get_rnd_bytes(u_char *buffer, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++)
+ buffer[i] = get_rnd_byte();
+}
+
+/*
+ * Initialize the random pool.
+ */
+void
+init_rnd_pool(void)
+{
+#ifndef USE_ARC4RANDOM
+# ifdef USE_DEV_RANDOM
+ DBG(DBG_KLIPS, DBG_log("opening %s", RANDOM_PATH));
+ random_fd = open(RANDOM_PATH, O_RDONLY);
+ if (random_fd == -1)
+ exit_log_errno((e, "open of %s failed in init_rnd_pool()", RANDOM_PATH));
+ fcntl(random_fd, F_SETFD, FD_CLOEXEC);
+# endif
+
+ get_rnd_bytes(random_pool, RANDOM_POOL_SIZE);
+ mix_pool();
+#endif /* !USE_ARC4RANDOM */
+
+ /* start of rand(3) on the right foot */
+ {
+ unsigned int seed;
+
+ get_rnd_bytes((void *)&seed, sizeof(seed));
+ srand(seed);
+ }
+}
+
+u_char secret_of_the_day[SHA1_DIGEST_SIZE];
+
+#ifndef NO_PLUTO
+
+void
+init_secret(void)
+{
+ /*
+ * Generate the secret value for responder cookies, and
+ * schedule an event for refresh.
+ */
+ get_rnd_bytes(secret_of_the_day, sizeof(secret_of_the_day));
+ event_schedule(EVENT_REINIT_SECRET, EVENT_REINIT_SECRET_DELAY, NULL);
+}
+
+#endif /* NO_PLUTO */
diff --git a/programs/pluto/rnd.h b/programs/pluto/rnd.h
new file mode 100644
index 000000000..0bd168039
--- /dev/null
+++ b/programs/pluto/rnd.h
@@ -0,0 +1,21 @@
+/* randomness machinery
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rnd.h,v 1.1 2004/03/15 20:35:29 as Exp $
+ */
+
+extern u_char secret_of_the_day[SHA1_DIGEST_SIZE];
+
+extern void get_rnd_bytes(u_char *buffer, int length);
+extern void init_rnd_pool(void);
+extern void init_secret(void);
diff --git a/programs/pluto/routing.txt b/programs/pluto/routing.txt
new file mode 100644
index 000000000..a69b8a542
--- /dev/null
+++ b/programs/pluto/routing.txt
@@ -0,0 +1,331 @@
+Routing and Erouting in Pluto
+=============================
+
+RCSID $Id: routing.txt,v 1.1 2004/03/15 20:35:29 as Exp $
+
+This is meant as internal documentation for Pluto. As such, it
+presumes some understanding of Pluto's code.
+
+It also describes KLIPS 1 erouting, including details not otherwise
+documented. KLIPS 1 documentation would be better included in KLIPS.
+
+Routing and erouting are complicated enough that the Pluto code needs
+a guide. This document is meant to be that guide.
+
+
+Mechanisms available to Pluto
+-----------------------------
+
+All outbound packets that are to be processed by KLIPS 1 must be
+routed to an ipsecN network interface. Pluto only uses normal routing
+(as opposed to "Advanced Routing"), so the selection of packets is
+made solely on the basis of the destination address. (Since the
+actual routing commands are in the updown script, they could be
+changed by the administrator, but Pluto needs to understand what is
+going on, and it currently assumes normal routing is used.)
+
+When an outbound packet hits an ipsecN interface, KLIPS figures out
+how to process it by finding an eroute that applies to the source and
+destination addresses. Eroutes are global: they are not specific to a
+particular ipsecN interface (routing needs to get the packets to any
+ipsecN interface; erouting takes it from there, ignoring issues of
+source IP address and nexthop (because nobody knows!)). If multiple
+eroutes apply to the packet, among the ones with the most specific
+source subnet, the one with the most specific destination subset is
+chosen (RGB thinks). If no eroute is discovered, KLIPS acts as if it
+was covered by a DROP eroute (this is the default behaviour; it can be
+changed). At most one eroute can exist for a particular pair of
+client subnets.
+
+There are fundamentally two kinds of eroutes: "shunt" eroutes and ones
+that specify that a packet is to be processed by a group of IPSEC SAs.
+Shunt eroutes specify what is to be done with the packet. Remember
+that these only apply to outbound packets.
+
+- TRAP: notify Pluto of the packet (presumably to attempt to negotiate
+ an appropriate group of IPSEC SAs). At the same time, KLIPS
+ installs a HOLD shunt (see below) for the specific source and
+ destination addresses from the packet and retains the packet
+ for later reprocessing (KLIPS does not yet implement retention).
+ Beware: if the TRAP's subnets both contained a single IP address
+ then installing the HOLD would actually delete the TRAP.
+
+- PASS: let the packet through in the clear
+
+- DROP: discard the packet
+
+- REJECT: discard the packet and notify the sender
+
+- HOLD: (automatically created by KLIPS when a TRAP fires) block
+ the packet, but retain it. If there is already a retained
+ packet, drop the old one and retain the new. When the HOLD
+ shunt is deleted or replaced, the retained packet is reinjected --
+ there might now be a tunnel. Note that KLIPS doesn't yet
+ implement the retention part, so HOLD is really like a DROP.
+
+One consequence of there being only one eroute for a pair of clients
+is that KLIPS will only use one SA group for output for this pair,
+even though there could be several SA groups that are authorised and
+live. Pluto chooses to make this the youngest such group.
+
+
+
+KLIPS lets through in the clear outbound UDP/500 packets that would
+otherwise be processed if they originate on this host and meet certain
+other conditions. The actual test is
+ source == me
+ && (no_eroute || dest == eroute.dest || isanyaddr(eroute.dest))
+ && port == UDP/500
+The idea is that IKE packets between us and a peer should not be
+sent through an IPSEC tunnel negotiated between us. Furthermore,
+our shunt eroutes should not apply to our IKE packets (shunt eroutes
+will generally have an eroute.dest of 0.0.0.0 or its IPv6 equivalent).
+
+Inbound behaviour is controlled in a quite different way. KLIPS
+processes only those inbound packets of ESP or AH protocol, with a
+destination address for this machine's ipsecN interfaces. The
+processing is as dictated by the SAs involved. Unfortunately, the
+decapsulated packet's source and destination address are not checked
+(part of "inbound policy checking").
+
+To prevent clear packets being accepted, firewall rules must be put in
+place. This has nothing to do with KLIPS, but is nonetheless in
+important part of security. It isn't clear what firewalling makes
+sense when Opportunism is allowed.
+
+
+For routing and firewalling, Pluto invokes the updown script. Pluto
+installs eroutes via extended PF_KEY messages.
+
+
+Current Pluto Behaviour
+-----------------------
+
+Data Structures:
+
+Routes and most eroutes are associated with connections (struct
+connection, a potential connection description). The enum routing_t
+field "routing" in struct connection records the state of routing and
+erouting for that connection. The values are:
+ RT_UNROUTED, /* unrouted */
+ RT_UNROUTED_HOLD, /* unrouted, but HOLD shunt installed */
+ RT_ROUTED_PROSPECTIVE, /* routed, and TRAP 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 */
+Notice that the routing and erouting are not independent: erouting
+(except for HOLD) implies that the connection is routed.
+
+Several struct connections may have the same destination subnet. If
+they agree on what the route should be, they can share it -- any of
+them may have routing >= RT_ROUTED_PROSPECTIVE. If they disagree,
+they cannot simultaneously be routed.
+
+invariant: for all struct connections c, d:
+ (c.that.client == d.that.client
+ && c.routing >= RT_ROUTED_PROSPECTIVE
+ && d.routing >= RT_ROUTED_PROSPECTIVE)
+ => c.interface == d.interface && c.this.nexthop == d.this.nexthop
+
+There are two kinds of eroutes: shunt eroutes and ones for an IPSEC SA
+Group. Most eroutes are associated with and are represeented in a
+connection. The exception is that some HOLD and PASS shunts do not
+correspond to connections; those are represented in the bare_shunt
+table.
+
+An eroute for an IPSEC SA Group is associated with the state object
+for that Group. The existence of such an eroute is also represented
+by the "so_serial_t eroute_owner" field in the struct connection. The
+value is the serial number of the state object for the Group. The
+special value SOS_NOBODY means that there is no owner associated with
+this connection for the eroute and hence no normal eroute. At most
+one eroute owner may exist for a particular (source subnet,
+destination subnet) pair. A Pluto-managed eroute cannot be associated
+with an RT_UNROUTED connection.
+
+invariant: for all struct connection c:
+ c.routing == RT_EROUTED_TUNNEL || c.eroute_owner == SOS_NOBODY
+
+invariant: for all struct connections c, d:
+ c.this.client == d.this.client && c.that.client == d.that.client
+ && &c != &d
+ => c.routing == RT_UNROUTED || d.routing == RT_UNROUTED
+
+If no normal eroute is set for a particular (source subnet,
+destination subnet) pair for which a connection is routed, then a
+shunt eroute would have been installed. This specifies what should
+happen to packets snared by the route.
+
+When Pluto is notified by KLIPS of a packet that has been TRAPped,
+there is no connection with which to associate the HOLD. It is
+temporarily held in the "bare_shunt table". If Opportunism is
+attempted but DNS doesn't provide Security Gateway information, Pluto
+will replace the HOLD with a PASS shunt. Since this PASS isn't
+associated with a connection, it too will reside in the bare_shunt
+table. If the HOLD can be associated with a connection, it will be
+removed from the bare_shunt table and represented in the connection.
+
+There are two contexts for which shunt eroutes are installed by Pluto
+for a particular connection. The first context is with the prospect
+of dealing with packets before any negotiation has been attempted. I
+call this context "prospective". Currently is a TRAP shunt, used to
+catch packets for initiate opportunistic negotiation. In the future,
+it might also be used to implement preordained PASS, DROP, or REJECT
+rules.
+
+The second context is after a failed negotiation. I call this context
+"failure". At this point a different kind of shunt eroute is
+appropriate. Depending on policy, it could be PASS, DROP, or REJECT,
+but it is unlikely to be TRAP. The shunt eroute should have a
+lifetime (this isn't yet implemented). When the lifetime expires, the
+failure shunt eroute should be replaced by the prospective shunt
+eroute.
+
+The kind and duration of a failure shunt eroute should perhaps depend
+on the nature of the failure, at least as imperfectly detected by
+Pluto. We haven't looked at this. In particular, the mapping from
+observations to robust respose isn't obvious.
+
+The shunt eroute policies should be a function of the potential
+connection. The failure shunt eroute can be specified for a
+particular connection with the flags --pass and --drop in a connection
+definition. There are four combinations, and each has a distinct
+meaning. The failure shunt eroute is incompletely implemented and
+cannot be represented in /etc/ipsec.conf.
+
+There is as yet no control over the prospective shunt eroute: it is
+always TRAP as far as Pluto is concerned. This is probably
+reasonable: any other fate suggests that no negotiation will be done,
+and so a connection definition is inappropriate. These should be
+implemented as manual conns. There remains the issue of whether Pluto
+should be aware of them -- currently it is not.
+
+
+Routines:
+
+[in kernel.c]
+
+bool do_command(struct connection *c, const char *verb)
+ Run the updown script to perform such tasks as installing a route
+ and adjust the firewall.
+
+bool could_route(struct connection *c)
+ Check to see whether we could route and eroute the connection.
+ <- shunt_eroute_connection (to check if --route can be performed)
+ <- install_inbound_ipsec_sa (to see if it will be possible
+ to (later) install route and eroute the corresponding outbound SA)
+ <- install_ipsec_sa (to see if the outbound SA can be routed and erouted)
+
+bool trap_connection(struct connection *c)
+ Install a TRAP shunt eroute for this connection. This implements
+ "whack --route", the way an admin can specify that packets for a
+ connection should be caught without first bringing it up.
+
+void unroute_connection(struct connection *c)
+ Delete any eroute for a connection and unroute it if route isn't shared.
+ <- release_connection
+ <- whack_handle (for "whack --unroute)
+
+bool eroute_connection(struct connection *c
+, ipsec_spi_t spi, unsigned int proto, unsigned int satype
+, unsigned int op, const char *opname UNUSED)
+ Issue PF_KEY commands to KLIPS to add, replace, or delete an eroute.
+ The verb is specified by op and described (for logging) by opname.
+ <- assign_hold
+ <- sag_eroute
+ <- shunt_eroute
+
+bool assign_hold(struct connection *c
+, const ip_address *src, const ip_address *dst)
+ Take a HOLD from the bare_shunt table and assign it to a connection.
+ If the HOLD is broadened (i.e. the connection's source or destination
+ subnets contain more than one IP address), this will involve replacing
+ the HOLD with a different one.
+
+bool sag_eroute(struct state *st, unsigned op, const char *opname)
+ SA Group eroute manipulation. The SA Group concerned is
+ identified with a state object.
+ <- route_and_eroute several times
+
+bool shunt_eroute(struct connection *c, unsigned int op, const char *opname)
+ shunt eroute manipulation. Shunt eroutes are associated with
+ connections.
+ <- unroute_connection
+ <- route_and_eroute
+ <- delete_ipsec_sa
+
+bool route_and_eroute(struct connection *c, struct state *st)
+ Install a route and then a prospective shunt eroute or an SA group
+ eroute. The code assumes that could_route had previously
+ given the go-ahead. Any SA group to be erouted must already
+ exist.
+ <- shunt_eroute_connection
+ <- install_ipsec_sa
+
+void scan_proc_shunts(void)
+ Every SHUNT_SCAN_INTERVAL scan /proc/net/ipsec_eroute.
+ Delete any PASS eroute in the bare_shunt table that hasn't been used
+ within the last SHUNT_PATIENCE seconds.
+ For any HOLD for which Pluto hasn't received an ACQUIRE (possibly
+ lost due to congestion), act as if an ACQUIRE were received.
+
+[in connection.c]
+
+struct connection *route_owner(struct connection *c, struct connection **erop)
+ 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. Return NULL if no connection is routed
+ at all. If erop is non-null, sets it to a connection sharing both
+ our client subnet and peer's client subnet with the largest value
+ of .routing.
+ The return value is used to find other connections sharing
+ a route. The value of *erop is used to find other connections
+ sharing an eroute.
+ <- could_route (to find any conflicting routes or eroutes)
+ <- unroute_connection (to find out if our route is still in use
+ after this connection is finished with it)
+ <- install_inbound_ipsec_sa (to find other IPSEC SAs for the
+ same peer clients; when we find them WE KILL THEM; a
+ kludge to deal with road warriors reconnecting)
+ <- route_and_eroute (to find all the connections from which the
+ route or eroute is being stolen)
+
+Uses:
+
+- setting up route & shunt eroute to TRAP packets for opportunism
+ (whack --route). Perhaps also manually designating DROP, REJECT, or
+ PASS for certain packets.
+
+ whack_handle() responds to --route; calls route_connection()
+
+
+- removing same (whack --unroute)
+
+ whack_handle() responds to --unroute; calls unroute_connection()
+
+- installing route & normal eroute for a newly negotiated group of
+ outbound IPSEC SAs
+
+ + perhaps an (additional) route is not needed: if the negotiation
+ was initiated by a TRAPped outgoing packet, then there must
+ already have been a route that got the packet to ipsecN. Mind
+ you, it could have been the wrong N!
+
+ install_ipsec_sa()
+
+- updating a normal eroute when a new group of IPSEC SAs replaces
+ an old one due to rekeying.
+
+ install_ipsec_sa()
+
+- replacing an old eroute when a negotiation fails. But this is
+ tricky. If this was a rekeying, we should just leave the old
+ normal eroute be -- it might still work. Otherwise, this was
+ an initial negotiation: we should replace the shunt eroute
+ with one appropriate for the failure context.
+
+- when a group of IPSEC SAs dies or is killed, and it had the eroute,
+ its normal eroute should be replaced by a shunt eroute. If there
+ was an attempt to replace the group, the replacement is in the
+ failure context; otherwise the replacement is in the prospective
+ context.
diff --git a/programs/pluto/rsaref/pkcs11.h b/programs/pluto/rsaref/pkcs11.h
new file mode 100644
index 000000000..9261e1e4c
--- /dev/null
+++ b/programs/pluto/rsaref/pkcs11.h
@@ -0,0 +1,299 @@
+/* pkcs11.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA 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.
+ */
+
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 6 platform-specific macros must be defined. These
+ * macros are described below, and typical definitions for them
+ * are also given. Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a Cryptoki library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 6 macros, the packing convention
+ * for Cryptoki structures should be set. The Cryptoki
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, this might be done by using the following
+ * preprocessor directive before including pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, this might be done by using
+ * the following preprocessor directive before including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own for this. You might
+ * not need to do (or be able to do!) anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object. It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to produce
+ * Win32 stuff, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to produce Win16 stuff, it might be defined by:
+ *
+ * #define CK_PTR far *
+ *
+ * In a typical UNIX environment, it might be defined by:
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DEFINE_FUNCTION(returnType, name): A macro which makes
+ * an exportable Cryptoki library function definition out of a
+ * return type and a function name. It should be used in the
+ * following fashion to define the exposed Cryptoki functions in
+ * a Cryptoki library:
+ *
+ * CK_DEFINE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * )
+ * {
+ * ...
+ * }
+ *
+ * If you're using Microsoft Developer Studio 5.0 to define a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllexport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to define a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable Cryptoki library function declaration out of a
+ * return type and a function name. It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * );
+ *
+ * If you're using Microsoft Developer Studio 5.0 to declare a
+ * function in a Win32 Cryptoki .dll, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllimport) name
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to declare a function in a Win16 Cryptoki .dll, it
+ * might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 4. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a Cryptoki API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name. It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a Cryptoki API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // Cryptoki API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to access
+ * functions in a Win32 Cryptoki .dll, in might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __declspec(dllimport) (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to access functions in a Win16 Cryptoki .dll, it might
+ * be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 5. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV. It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * If you're using Microsoft Developer Studio 5.0 to do Win32
+ * Cryptoki development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ * If you're using an earlier version of Microsoft Developer
+ * Studio to do Win16 development, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by:
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 6. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should best be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various Cryptoki types and #define'd values are in the
+ * file pkcs11t.h. */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y) x##y
+
+
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ extern CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points. That is, for
+ * each Cryptoki function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points. A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's Cryptoki version
+ * and then a whole slew of function pointers to the routines in
+ * the library. This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ __PASTE(CK_,name) name;
+
+struct CK_FUNCTION_LIST {
+
+ CK_VERSION version; /* Cryptoki version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the Cryptoki
+ * function prototypes. */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/programs/pluto/rsaref/pkcs11f.h b/programs/pluto/rsaref/pkcs11f.h
new file mode 100644
index 000000000..dec6315dd
--- /dev/null
+++ b/programs/pluto/rsaref/pkcs11f.h
@@ -0,0 +1,912 @@
+/* pkcs11f.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA 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.
+ */
+
+/* This header file contains pretty much everything about all the */
+/* Cryptoki function prototypes. Because this information is */
+/* used for more than just declaring function prototypes, the */
+/* order of the functions appearing herein is important, and */
+/* should not be altered. */
+
+/* General-purpose */
+
+/* C_Initialize initializes the Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets
+ * cast to CK_C_INITIALIZE_ARGS_PTR
+ * and dereferenced */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * Cryptoki library. */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about Cryptoki. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_INFO_PTR pInfo /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to
+ * function list */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_BBOOL tokenPresent, /* only slots with tokens? */
+ CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */
+ CK_ULONG_PTR pulCount /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the ID of the slot */
+ CK_SLOT_INFO_PTR pInfo /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_TOKEN_INFO_PTR pInfo /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of token's slot */
+ CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */
+ CK_ULONG_PTR pulCount /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_MECHANISM_TYPE type, /* type of mechanism */
+ CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+/* pLabel changed from CK_CHAR_PTR to CK_UTF8CHAR_PTR for v2.10 */
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */
+ CK_ULONG ulPinLen, /* length in bytes of the PIN */
+ CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */
+ CK_ULONG ulPinLen /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pOldPin, /* the old PIN */
+ CK_ULONG ulOldLen, /* length of the old PIN */
+ CK_UTF8CHAR_PTR pNewPin, /* the new PIN */
+ CK_ULONG ulNewLen /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the slot's ID */
+ CK_FLAGS flags, /* from CK_SESSION_INFO */
+ CK_VOID_PTR pApplication, /* passed to callback */
+ CK_NOTIFY Notify, /* callback function */
+ CK_SESSION_HANDLE_PTR phSession /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_SESSION_INFO_PTR pInfo /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session. */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* gets state */
+ CK_ULONG_PTR pulOperationStateLen /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session. */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* holds state */
+ CK_ULONG ulOperationStateLen, /* holds state length */
+ CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */
+ CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_USER_TYPE userType, /* the user type */
+ CK_UTF8CHAR_PTR pPin, /* the user's PIN */
+ CK_ULONG ulPinLen /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy. */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new object */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ULONG_PTR pulSize /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes. */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */
+ CK_ULONG ulCount /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */
+ CK_ULONG ulMaxObjectCount, /* max handles to get */
+ CK_ULONG_PTR pulObjectCount /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pData, /* the plaintext data */
+ CK_ULONG ulDataLen, /* bytes of plaintext */
+ CK_BYTE_PTR pEncryptedData, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext data len */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_BYTE_PTR pLastEncryptedPart, /* last c-text */
+ CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedData, /* ciphertext */
+ CK_ULONG ulEncryptedDataLen, /* ciphertext length */
+ CK_BYTE_PTR pData, /* gets plaintext */
+ CK_ULONG_PTR pulDataLen /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* encrypted data */
+ CK_ULONG ulEncryptedPartLen, /* input length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pLastPart, /* gets plaintext */
+ CK_ULONG_PTR pulLastPartLen /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* data to be digested */
+ CK_ULONG ulDataLen, /* bytes of data to digest */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* data to be digested */
+ CK_ULONG ulPartLen /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested. */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hKey /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ *signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* the data to sign */
+ CK_ULONG ulPartLen /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation,
+ * returning the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ * cannot be recovered from the signature (e.g. DSA). */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* signed data */
+ CK_ULONG ulDataLen, /* length of signed data */
+ CK_BYTE_PTR pSignature, /* signature */
+ CK_ULONG ulSignatureLen /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* signed data */
+ CK_ULONG ulPartLen /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen, /* signature length */
+ CK_BYTE_PTR pData, /* gets signed data */
+ CK_ULONG_PTR pulDataLen /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key generation mech. */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new key */
+ CK_ULONG ulCount, /* # of attrs in template */
+ CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session
+ * handle */
+ CK_MECHANISM_PTR pMechanism, /* key-gen
+ * mech. */
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template
+ * for pub.
+ * key */
+ CK_ULONG ulPublicKeyAttributeCount, /* # pub.
+ * attrs. */
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template
+ * for priv.
+ * key */
+ CK_ULONG ulPrivateKeyAttributeCount, /* # priv.
+ * attrs. */
+ CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub.
+ * key
+ * handle */
+ CK_OBJECT_HANDLE_PTR phPrivateKey /* gets
+ * priv. key
+ * handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */
+ CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */
+ CK_OBJECT_HANDLE hKey, /* key to be wrapped */
+ CK_BYTE_PTR pWrappedKey, /* gets wrapped key */
+ CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object. */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */
+ CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */
+ CK_BYTE_PTR pWrappedKey, /* the wrapped key */
+ CK_ULONG ulWrappedKeyLen, /* wrapped key len */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */
+ CK_OBJECT_HANDLE hBaseKey, /* base key */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator. */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSeed, /* the seed material */
+ CK_ULONG ulSeedLen /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR RandomData, /* receives the random data */
+ CK_ULONG ulRandomLen /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel. */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Functions added in for Cryptoki Version 2.01 or later */
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur. */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FLAGS flags, /* blocking/nonblocking flag */
+ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */
+ CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */
+);
+#endif
diff --git a/programs/pluto/rsaref/pkcs11t.h b/programs/pluto/rsaref/pkcs11t.h
new file mode 100644
index 000000000..3da20b215
--- /dev/null
+++ b/programs/pluto/rsaref/pkcs11t.h
@@ -0,0 +1,1685 @@
+/* pkcs11t.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA 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.
+ */
+
+/* See top of pkcs11.h for information about the macros that
+ * must be defined and the structure-packing conventions that
+ * must be set before including this file. */
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CK_TRUE 1
+#define CK_FALSE 0
+
+#ifndef CK_DISABLE_TRUE_FALSE
+#ifndef FALSE
+#define FALSE CK_FALSE
+#endif
+
+#ifndef TRUE
+#define TRUE CK_TRUE
+#endif
+#endif
+
+/* an unsigned 8-bit value */
+typedef unsigned char CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+/* CK_LONG is new for v2.0 */
+typedef long int CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION (~0UL)
+#define CK_EFFECTIVELY_INFINITE 0
+
+
+typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+typedef CK_CHAR CK_PTR CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR;
+typedef CK_ULONG CK_PTR CK_ULONG_PTR;
+typedef void CK_PTR CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session */
+/* handle or object handle */
+#define CK_INVALID_HANDLE 0
+
+
+typedef struct CK_VERSION {
+ CK_BYTE major; /* integer portion of version number */
+ CK_BYTE minor; /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+ /* manufacturerID and libraryDecription have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_VERSION cryptokiVersion; /* Cryptoki interface ver */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags; /* must be zero */
+
+ /* libraryDescription and libraryVersion are new for v2.0 */
+ CK_UTF8CHAR libraryDescription[32]; /* blank padded */
+ CK_VERSION libraryVersion; /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * Cryptoki provides to an application */
+/* CK_NOTIFICATION has been changed from an enum to a CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER 0
+
+
+typedef CK_ULONG CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+ /* slotDescription and manufacturerID have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_UTF8CHAR slotDescription[64]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags;
+
+ /* hardwareVersion and firmwareVersion are new for v2.0 */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_TOKEN_PRESENT 0x00000001 /* a token is there */
+#define CKF_REMOVABLE_DEVICE 0x00000002 /* removable devices*/
+#define CKF_HW_SLOT 0x00000004 /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+ /* label, manufacturerID, and model have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_UTF8CHAR label[32]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_UTF8CHAR model[16]; /* blank padded */
+ CK_CHAR serialNumber[16]; /* blank padded */
+ CK_FLAGS flags; /* see below */
+
+ /* ulMaxSessionCount, ulSessionCount, ulMaxRwSessionCount,
+ * ulRwSessionCount, ulMaxPinLen, and ulMinPinLen have all been
+ * changed from CK_USHORT to CK_ULONG for v2.0 */
+ CK_ULONG ulMaxSessionCount; /* max open sessions */
+ CK_ULONG ulSessionCount; /* sess. now open */
+ CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */
+ CK_ULONG ulRwSessionCount; /* R/W sess. now open */
+ CK_ULONG ulMaxPinLen; /* in bytes */
+ CK_ULONG ulMinPinLen; /* in bytes */
+ CK_ULONG ulTotalPublicMemory; /* in bytes */
+ CK_ULONG ulFreePublicMemory; /* in bytes */
+ CK_ULONG ulTotalPrivateMemory; /* in bytes */
+ CK_ULONG ulFreePrivateMemory; /* in bytes */
+
+ /* hardwareVersion, firmwareVersion, and time are new for
+ * v2.0 */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+ CK_CHAR utcTime[16]; /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RNG 0x00000001 /* has random #
+ * generator */
+#define CKF_WRITE_PROTECTED 0x00000002 /* token is
+ * write-
+ * protected */
+#define CKF_LOGIN_REQUIRED 0x00000004 /* user must
+ * login */
+#define CKF_USER_PIN_INITIALIZED 0x00000008 /* normal user's
+ * PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED is new for v2.0. If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state */
+#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020
+
+/* CKF_CLOCK_ON_TOKEN is new for v2.0. If it is set, that means
+ * that the token has some sort of clock. The time on that
+ * clock is returned in the token info structure */
+#define CKF_CLOCK_ON_TOKEN 0x00000040
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH is new for v2.0. If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the Cryptoki library itself */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100
+
+/* CKF_DUAL_CRYPTO_OPERATIONS is new for v2.0. If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign) */
+#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200
+
+/* CKF_TOKEN_INITIALIZED if new for v2.10. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized. */
+#define CKF_TOKEN_INITIALIZED 0x00000400
+
+/* CKF_SECONDARY_AUTHENTICATION if new for v2.10. If it is
+ * true, the token supports secondary authentication for
+ * private key objects. This flag is deprecated in v2.11 and
+ onwards. */
+#define CKF_SECONDARY_AUTHENTICATION 0x00000800
+
+/* CKF_USER_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication. */
+#define CKF_USER_PIN_COUNT_LOW 0x00010000
+
+/* CKF_USER_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect user PIN will it to become locked. */
+#define CKF_USER_PIN_FINAL_TRY 0x00020000
+
+/* CKF_USER_PIN_LOCKED if new for v2.10. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible. */
+#define CKF_USER_PIN_LOCKED 0x00040000
+
+/* CKF_USER_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000
+
+/* CKF_SO_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication. */
+#define CKF_SO_PIN_COUNT_LOW 0x00100000
+
+/* CKF_SO_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect SO PIN will it to become locked. */
+#define CKF_SO_PIN_FINAL_TRY 0x00200000
+
+/* CKF_SO_PIN_LOCKED if new for v2.10. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED 0x00400000
+
+/* CKF_SO_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a Cryptoki-assigned value that
+ * identifies a session */
+typedef CK_ULONG CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of Cryptoki users */
+/* CK_USER_TYPE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO 0
+/* Normal user */
+#define CKU_USER 1
+/* Context specific (added in v2.20) */
+#define CKU_CONTEXT_SPECIFIC 2
+
+/* CK_STATE enumerates the session states */
+/* CK_STATE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_STATE;
+#define CKS_RO_PUBLIC_SESSION 0
+#define CKS_RO_USER_FUNCTIONS 1
+#define CKS_RW_PUBLIC_SESSION 2
+#define CKS_RW_USER_FUNCTIONS 3
+#define CKS_RW_SO_FUNCTIONS 4
+
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+ CK_SLOT_ID slotID;
+ CK_STATE state;
+ CK_FLAGS flags; /* see below */
+
+ /* ulDeviceError was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulDeviceError; /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RW_SESSION 0x00000002 /* session is r/w */
+#define CKF_SERIAL_SESSION 0x00000004 /* no parallel */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object */
+typedef CK_ULONG CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that Cryptoki recognizes. It is defined
+ * as follows: */
+/* CK_OBJECT_CLASS was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+/* CKO_HW_FEATURE is new for v2.10 */
+/* CKO_DOMAIN_PARAMETERS is new for v2.11 */
+/* CKO_MECHANISM is new for v2.20 */
+#define CKO_DATA 0x00000000
+#define CKO_CERTIFICATE 0x00000001
+#define CKO_PUBLIC_KEY 0x00000002
+#define CKO_PRIVATE_KEY 0x00000003
+#define CKO_SECRET_KEY 0x00000004
+#define CKO_HW_FEATURE 0x00000005
+#define CKO_DOMAIN_PARAMETERS 0x00000006
+#define CKO_MECHANISM 0x00000007
+#define CKO_VENDOR_DEFINED 0x80000000
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is new for v2.10. CK_HW_FEATURE_TYPE is a
+ * value that identifies the hardware feature type of an object
+ * with CK_OBJECT_CLASS equal to CKO_HW_FEATURE. */
+typedef CK_ULONG CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+/* CKH_USER_INTERFACE is new for v2.20 */
+#define CKH_MONOTONIC_COUNTER 0x00000001
+#define CKH_CLOCK 0x00000002
+#define CKH_USER_INTERFACE 0x00000003
+#define CKH_VENDOR_DEFINED 0x80000000
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+/* CK_KEY_TYPE was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA 0x00000000
+#define CKK_DSA 0x00000001
+#define CKK_DH 0x00000002
+
+/* CKK_ECDSA and CKK_KEA are new for v2.0 */
+/* CKK_ECDSA is deprecated in v2.11, CKK_EC is preferred. */
+#define CKK_ECDSA 0x00000003
+#define CKK_EC 0x00000003
+#define CKK_X9_42_DH 0x00000004
+#define CKK_KEA 0x00000005
+
+#define CKK_GENERIC_SECRET 0x00000010
+#define CKK_RC2 0x00000011
+#define CKK_RC4 0x00000012
+#define CKK_DES 0x00000013
+#define CKK_DES2 0x00000014
+#define CKK_DES3 0x00000015
+
+/* all these key types are new for v2.0 */
+#define CKK_CAST 0x00000016
+#define CKK_CAST3 0x00000017
+/* CKK_CAST5 is deprecated in v2.11, CKK_CAST128 is preferred. */
+#define CKK_CAST5 0x00000018
+#define CKK_CAST128 0x00000018
+#define CKK_RC5 0x00000019
+#define CKK_IDEA 0x0000001A
+#define CKK_SKIPJACK 0x0000001B
+#define CKK_BATON 0x0000001C
+#define CKK_JUNIPER 0x0000001D
+#define CKK_CDMF 0x0000001E
+#define CKK_AES 0x0000001F
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKK_BLOWFISH 0x00000020
+#define CKK_TWOFISH 0x00000021
+
+#define CKK_VENDOR_DEFINED 0x80000000
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type */
+/* CK_CERTIFICATE_TYPE was changed from CK_USHORT to CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG CK_CERTIFICATE_TYPE;
+
+/* The following certificate types are defined: */
+/* CKC_X_509_ATTR_CERT is new for v2.10 */
+/* CKC_WTLS is new for v2.20 */
+#define CKC_X_509 0x00000000
+#define CKC_X_509_ATTR_CERT 0x00000001
+#define CKC_WTLS 0x00000002
+#define CKC_VENDOR_DEFINED 0x80000000
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type */
+/* CK_ATTRIBUTE_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+ consists of an array of values. */
+#define CKF_ARRAY_ATTRIBUTE 0x40000000
+
+/* The following attribute types are defined: */
+#define CKA_CLASS 0x00000000
+#define CKA_TOKEN 0x00000001
+#define CKA_PRIVATE 0x00000002
+#define CKA_LABEL 0x00000003
+#define CKA_APPLICATION 0x00000010
+#define CKA_VALUE 0x00000011
+
+/* CKA_OBJECT_ID is new for v2.10 */
+#define CKA_OBJECT_ID 0x00000012
+
+#define CKA_CERTIFICATE_TYPE 0x00000080
+#define CKA_ISSUER 0x00000081
+#define CKA_SERIAL_NUMBER 0x00000082
+
+/* CKA_AC_ISSUER, CKA_OWNER, and CKA_ATTR_TYPES are new
+ * for v2.10 */
+#define CKA_AC_ISSUER 0x00000083
+#define CKA_OWNER 0x00000084
+#define CKA_ATTR_TYPES 0x00000085
+
+/* CKA_TRUSTED is new for v2.11 */
+#define CKA_TRUSTED 0x00000086
+
+/* CKA_CERTIFICATE_CATEGORY ...
+ * CKA_CHECK_VALUE are new for v2.20 */
+#define CKA_CERTIFICATE_CATEGORY 0x00000087
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088
+#define CKA_URL 0x00000089
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008A
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008B
+#define CKA_CHECK_VALUE 0x00000090
+
+#define CKA_KEY_TYPE 0x00000100
+#define CKA_SUBJECT 0x00000101
+#define CKA_ID 0x00000102
+#define CKA_SENSITIVE 0x00000103
+#define CKA_ENCRYPT 0x00000104
+#define CKA_DECRYPT 0x00000105
+#define CKA_WRAP 0x00000106
+#define CKA_UNWRAP 0x00000107
+#define CKA_SIGN 0x00000108
+#define CKA_SIGN_RECOVER 0x00000109
+#define CKA_VERIFY 0x0000010A
+#define CKA_VERIFY_RECOVER 0x0000010B
+#define CKA_DERIVE 0x0000010C
+#define CKA_START_DATE 0x00000110
+#define CKA_END_DATE 0x00000111
+#define CKA_MODULUS 0x00000120
+#define CKA_MODULUS_BITS 0x00000121
+#define CKA_PUBLIC_EXPONENT 0x00000122
+#define CKA_PRIVATE_EXPONENT 0x00000123
+#define CKA_PRIME_1 0x00000124
+#define CKA_PRIME_2 0x00000125
+#define CKA_EXPONENT_1 0x00000126
+#define CKA_EXPONENT_2 0x00000127
+#define CKA_COEFFICIENT 0x00000128
+#define CKA_PRIME 0x00000130
+#define CKA_SUBPRIME 0x00000131
+#define CKA_BASE 0x00000132
+
+/* CKA_PRIME_BITS and CKA_SUB_PRIME_BITS are new for v2.11 */
+#define CKA_PRIME_BITS 0x00000133
+#define CKA_SUBPRIME_BITS 0x00000134
+#define CKA_SUB_PRIME_BITS CKA_SUBPRIME_BITS
+/* (To retain backwards-compatibility) */
+
+#define CKA_VALUE_BITS 0x00000160
+#define CKA_VALUE_LEN 0x00000161
+
+/* CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE,
+ * CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS,
+ * and CKA_EC_POINT are new for v2.0 */
+#define CKA_EXTRACTABLE 0x00000162
+#define CKA_LOCAL 0x00000163
+#define CKA_NEVER_EXTRACTABLE 0x00000164
+#define CKA_ALWAYS_SENSITIVE 0x00000165
+
+/* CKA_KEY_GEN_MECHANISM is new for v2.11 */
+#define CKA_KEY_GEN_MECHANISM 0x00000166
+
+#define CKA_MODIFIABLE 0x00000170
+
+/* CKA_ECDSA_PARAMS is deprecated in v2.11,
+ * CKA_EC_PARAMS is preferred. */
+#define CKA_ECDSA_PARAMS 0x00000180
+#define CKA_EC_PARAMS 0x00000180
+
+#define CKA_EC_POINT 0x00000181
+
+/* CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+ * are new for v2.10. Deprecated in v2.11 and onwards. */
+#define CKA_SECONDARY_AUTH 0x00000200
+#define CKA_AUTH_PIN_FLAGS 0x00000201
+
+/* CKA_ALWAYS_AUTHENTICATE ...
+ * CKA_UNWRAP_TEMPLATE are new for v2.20 */
+#define CKA_ALWAYS_AUTHENTICATE 0x00000202
+
+#define CKA_WRAP_WITH_TRUSTED 0x00000210
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000211)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000212)
+
+/* CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, and CKA_HAS_RESET
+ * are new for v2.10 */
+#define CKA_HW_FEATURE_TYPE 0x00000300
+#define CKA_RESET_ON_INIT 0x00000301
+#define CKA_HAS_RESET 0x00000302
+
+/* The following attributes are new for v2.20 */
+#define CKA_PIXEL_X 0x00000400
+#define CKA_PIXEL_Y 0x00000401
+#define CKA_RESOLUTION 0x00000402
+#define CKA_CHAR_ROWS 0x00000403
+#define CKA_CHAR_COLUMNS 0x00000404
+#define CKA_COLOR 0x00000405
+#define CKA_BITS_PER_PIXEL 0x00000406
+#define CKA_CHAR_SETS 0x00000480
+#define CKA_ENCODING_METHODS 0x00000481
+#define CKA_MIME_TYPES 0x00000482
+#define CKA_MECHANISM_TYPE 0x00000500
+#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501
+#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502
+#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE|0x00000600)
+
+#define CKA_VENDOR_DEFINED 0x80000000
+
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute */
+typedef struct CK_ATTRIBUTE {
+ CK_ATTRIBUTE_TYPE type;
+ CK_VOID_PTR pValue;
+
+ /* ulValueLen went from CK_USHORT to CK_ULONG for v2.0 */
+ CK_ULONG ulValueLen; /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+ CK_CHAR year[4]; /* the year ("1900" - "9999") */
+ CK_CHAR month[2]; /* the month ("01" - "12") */
+ CK_CHAR day[2]; /* the day ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type */
+/* CK_MECHANISM_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000
+#define CKM_RSA_PKCS 0x00000001
+#define CKM_RSA_9796 0x00000002
+#define CKM_RSA_X_509 0x00000003
+
+/* CKM_MD2_RSA_PKCS, CKM_MD5_RSA_PKCS, and CKM_SHA1_RSA_PKCS
+ * are new for v2.0. They are mechanisms which hash and sign */
+#define CKM_MD2_RSA_PKCS 0x00000004
+#define CKM_MD5_RSA_PKCS 0x00000005
+#define CKM_SHA1_RSA_PKCS 0x00000006
+
+/* CKM_RIPEMD128_RSA_PKCS, CKM_RIPEMD160_RSA_PKCS, and
+ * CKM_RSA_PKCS_OAEP are new for v2.10 */
+#define CKM_RIPEMD128_RSA_PKCS 0x00000007
+#define CKM_RIPEMD160_RSA_PKCS 0x00000008
+#define CKM_RSA_PKCS_OAEP 0x00000009
+
+/* CKM_RSA_X9_31_KEY_PAIR_GEN, CKM_RSA_X9_31, CKM_SHA1_RSA_X9_31,
+ * CKM_RSA_PKCS_PSS, and CKM_SHA1_RSA_PKCS_PSS are new for v2.11 */
+#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000A
+#define CKM_RSA_X9_31 0x0000000B
+#define CKM_SHA1_RSA_X9_31 0x0000000C
+#define CKM_RSA_PKCS_PSS 0x0000000D
+#define CKM_SHA1_RSA_PKCS_PSS 0x0000000E
+
+#define CKM_DSA_KEY_PAIR_GEN 0x00000010
+#define CKM_DSA 0x00000011
+#define CKM_DSA_SHA1 0x00000012
+#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020
+#define CKM_DH_PKCS_DERIVE 0x00000021
+
+/* CKM_X9_42_DH_KEY_PAIR_GEN, CKM_X9_42_DH_DERIVE,
+ * CKM_X9_42_DH_HYBRID_DERIVE, and CKM_X9_42_MQV_DERIVE are new for
+ * v2.11 */
+#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030
+#define CKM_X9_42_DH_DERIVE 0x00000031
+#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032
+#define CKM_X9_42_MQV_DERIVE 0x00000033
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_RSA_PKCS 0x00000040
+#define CKM_SHA384_RSA_PKCS 0x00000041
+#define CKM_SHA512_RSA_PKCS 0x00000042
+#define CKM_SHA256_RSA_PKCS_PSS 0x00000043
+#define CKM_SHA384_RSA_PKCS_PSS 0x00000044
+#define CKM_SHA512_RSA_PKCS_PSS 0x00000045
+
+#define CKM_RC2_KEY_GEN 0x00000100
+#define CKM_RC2_ECB 0x00000101
+#define CKM_RC2_CBC 0x00000102
+#define CKM_RC2_MAC 0x00000103
+
+/* CKM_RC2_MAC_GENERAL and CKM_RC2_CBC_PAD are new for v2.0 */
+#define CKM_RC2_MAC_GENERAL 0x00000104
+#define CKM_RC2_CBC_PAD 0x00000105
+
+#define CKM_RC4_KEY_GEN 0x00000110
+#define CKM_RC4 0x00000111
+#define CKM_DES_KEY_GEN 0x00000120
+#define CKM_DES_ECB 0x00000121
+#define CKM_DES_CBC 0x00000122
+#define CKM_DES_MAC 0x00000123
+
+/* CKM_DES_MAC_GENERAL and CKM_DES_CBC_PAD are new for v2.0 */
+#define CKM_DES_MAC_GENERAL 0x00000124
+#define CKM_DES_CBC_PAD 0x00000125
+
+#define CKM_DES2_KEY_GEN 0x00000130
+#define CKM_DES3_KEY_GEN 0x00000131
+#define CKM_DES3_ECB 0x00000132
+#define CKM_DES3_CBC 0x00000133
+#define CKM_DES3_MAC 0x00000134
+
+/* CKM_DES3_MAC_GENERAL, CKM_DES3_CBC_PAD, CKM_CDMF_KEY_GEN,
+ * CKM_CDMF_ECB, CKM_CDMF_CBC, CKM_CDMF_MAC,
+ * CKM_CDMF_MAC_GENERAL, and CKM_CDMF_CBC_PAD are new for v2.0 */
+#define CKM_DES3_MAC_GENERAL 0x00000135
+#define CKM_DES3_CBC_PAD 0x00000136
+#define CKM_CDMF_KEY_GEN 0x00000140
+#define CKM_CDMF_ECB 0x00000141
+#define CKM_CDMF_CBC 0x00000142
+#define CKM_CDMF_MAC 0x00000143
+#define CKM_CDMF_MAC_GENERAL 0x00000144
+#define CKM_CDMF_CBC_PAD 0x00000145
+
+/* the following four DES mechanisms are new for v2.20 */
+#define CKM_DES_OFB64 0x00000150
+#define CKM_DES_OFB8 0x00000151
+#define CKM_DES_CFB64 0x00000152
+#define CKM_DES_CFB8 0x00000153
+
+#define CKM_MD2 0x00000200
+
+/* CKM_MD2_HMAC and CKM_MD2_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD2_HMAC 0x00000201
+#define CKM_MD2_HMAC_GENERAL 0x00000202
+
+#define CKM_MD5 0x00000210
+
+/* CKM_MD5_HMAC and CKM_MD5_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD5_HMAC 0x00000211
+#define CKM_MD5_HMAC_GENERAL 0x00000212
+
+#define CKM_SHA_1 0x00000220
+
+/* CKM_SHA_1_HMAC and CKM_SHA_1_HMAC_GENERAL are new for v2.0 */
+#define CKM_SHA_1_HMAC 0x00000221
+#define CKM_SHA_1_HMAC_GENERAL 0x00000222
+
+/* CKM_RIPEMD128, CKM_RIPEMD128_HMAC,
+ * CKM_RIPEMD128_HMAC_GENERAL, CKM_RIPEMD160, CKM_RIPEMD160_HMAC,
+ * and CKM_RIPEMD160_HMAC_GENERAL are new for v2.10 */
+#define CKM_RIPEMD128 0x00000230
+#define CKM_RIPEMD128_HMAC 0x00000231
+#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232
+#define CKM_RIPEMD160 0x00000240
+#define CKM_RIPEMD160_HMAC 0x00000241
+#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256 0x00000250
+#define CKM_SHA256_HMAC 0x00000251
+#define CKM_SHA256_HMAC_GENERAL 0x00000252
+#define CKM_SHA384 0x00000260
+#define CKM_SHA384_HMAC 0x00000261
+#define CKM_SHA384_HMAC_GENERAL 0x00000262
+#define CKM_SHA512 0x00000270
+#define CKM_SHA512_HMAC 0x00000271
+#define CKM_SHA512_HMAC_GENERAL 0x00000272
+
+/* All of the following mechanisms are new for v2.0 */
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST_KEY_GEN 0x00000300
+#define CKM_CAST_ECB 0x00000301
+#define CKM_CAST_CBC 0x00000302
+#define CKM_CAST_MAC 0x00000303
+#define CKM_CAST_MAC_GENERAL 0x00000304
+#define CKM_CAST_CBC_PAD 0x00000305
+#define CKM_CAST3_KEY_GEN 0x00000310
+#define CKM_CAST3_ECB 0x00000311
+#define CKM_CAST3_CBC 0x00000312
+#define CKM_CAST3_MAC 0x00000313
+#define CKM_CAST3_MAC_GENERAL 0x00000314
+#define CKM_CAST3_CBC_PAD 0x00000315
+#define CKM_CAST5_KEY_GEN 0x00000320
+#define CKM_CAST128_KEY_GEN 0x00000320
+#define CKM_CAST5_ECB 0x00000321
+#define CKM_CAST128_ECB 0x00000321
+#define CKM_CAST5_CBC 0x00000322
+#define CKM_CAST128_CBC 0x00000322
+#define CKM_CAST5_MAC 0x00000323
+#define CKM_CAST128_MAC 0x00000323
+#define CKM_CAST5_MAC_GENERAL 0x00000324
+#define CKM_CAST128_MAC_GENERAL 0x00000324
+#define CKM_CAST5_CBC_PAD 0x00000325
+#define CKM_CAST128_CBC_PAD 0x00000325
+#define CKM_RC5_KEY_GEN 0x00000330
+#define CKM_RC5_ECB 0x00000331
+#define CKM_RC5_CBC 0x00000332
+#define CKM_RC5_MAC 0x00000333
+#define CKM_RC5_MAC_GENERAL 0x00000334
+#define CKM_RC5_CBC_PAD 0x00000335
+#define CKM_IDEA_KEY_GEN 0x00000340
+#define CKM_IDEA_ECB 0x00000341
+#define CKM_IDEA_CBC 0x00000342
+#define CKM_IDEA_MAC 0x00000343
+#define CKM_IDEA_MAC_GENERAL 0x00000344
+#define CKM_IDEA_CBC_PAD 0x00000345
+#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350
+#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360
+#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362
+#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363
+#define CKM_XOR_BASE_AND_DATA 0x00000364
+#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365
+#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370
+#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371
+#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372
+
+/* CKM_SSL3_MASTER_KEY_DERIVE_DH, CKM_TLS_PRE_MASTER_KEY_GEN,
+ * CKM_TLS_MASTER_KEY_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, and
+ * CKM_TLS_MASTER_KEY_DERIVE_DH are new for v2.11 */
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373
+#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374
+#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375
+#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376
+#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377
+
+/* CKM_TLS_PRF is new for v2.20 */
+#define CKM_TLS_PRF 0x00000378
+
+#define CKM_SSL3_MD5_MAC 0x00000380
+#define CKM_SSL3_SHA1_MAC 0x00000381
+#define CKM_MD5_KEY_DERIVATION 0x00000390
+#define CKM_MD2_KEY_DERIVATION 0x00000391
+#define CKM_SHA1_KEY_DERIVATION 0x00000392
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_KEY_DERIVATION 0x00000393
+#define CKM_SHA384_KEY_DERIVATION 0x00000394
+#define CKM_SHA512_KEY_DERIVATION 0x00000395
+
+#define CKM_PBE_MD2_DES_CBC 0x000003A0
+#define CKM_PBE_MD5_DES_CBC 0x000003A1
+#define CKM_PBE_MD5_CAST_CBC 0x000003A2
+#define CKM_PBE_MD5_CAST3_CBC 0x000003A3
+#define CKM_PBE_MD5_CAST5_CBC 0x000003A4
+#define CKM_PBE_MD5_CAST128_CBC 0x000003A4
+#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5
+#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5
+#define CKM_PBE_SHA1_RC4_128 0x000003A6
+#define CKM_PBE_SHA1_RC4_40 0x000003A7
+#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8
+#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9
+#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AA
+#define CKM_PBE_SHA1_RC2_40_CBC 0x000003AB
+
+/* CKM_PKCS5_PBKD2 is new for v2.10 */
+#define CKM_PKCS5_PBKD2 0x000003B0
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0
+
+/* WTLS mechanisms are new for v2.20 */
+#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0
+#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2
+#define CKM_WTLS_PRF 0x000003D3
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5
+
+#define CKM_KEY_WRAP_LYNKS 0x00000400
+#define CKM_KEY_WRAP_SET_OAEP 0x00000401
+
+/* CKM_CMS_SIG is new for v2.20 */
+#define CKM_CMS_SIG 0x00000500
+
+/* Fortezza mechanisms */
+#define CKM_SKIPJACK_KEY_GEN 0x00001000
+#define CKM_SKIPJACK_ECB64 0x00001001
+#define CKM_SKIPJACK_CBC64 0x00001002
+#define CKM_SKIPJACK_OFB64 0x00001003
+#define CKM_SKIPJACK_CFB64 0x00001004
+#define CKM_SKIPJACK_CFB32 0x00001005
+#define CKM_SKIPJACK_CFB16 0x00001006
+#define CKM_SKIPJACK_CFB8 0x00001007
+#define CKM_SKIPJACK_WRAP 0x00001008
+#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009
+#define CKM_SKIPJACK_RELAYX 0x0000100a
+#define CKM_KEA_KEY_PAIR_GEN 0x00001010
+#define CKM_KEA_KEY_DERIVE 0x00001011
+#define CKM_FORTEZZA_TIMESTAMP 0x00001020
+#define CKM_BATON_KEY_GEN 0x00001030
+#define CKM_BATON_ECB128 0x00001031
+#define CKM_BATON_ECB96 0x00001032
+#define CKM_BATON_CBC128 0x00001033
+#define CKM_BATON_COUNTER 0x00001034
+#define CKM_BATON_SHUFFLE 0x00001035
+#define CKM_BATON_WRAP 0x00001036
+
+/* CKM_ECDSA_KEY_PAIR_GEN is deprecated in v2.11,
+ * CKM_EC_KEY_PAIR_GEN is preferred */
+#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040
+#define CKM_EC_KEY_PAIR_GEN 0x00001040
+
+#define CKM_ECDSA 0x00001041
+#define CKM_ECDSA_SHA1 0x00001042
+
+/* CKM_ECDH1_DERIVE, CKM_ECDH1_COFACTOR_DERIVE, and CKM_ECMQV_DERIVE
+ * are new for v2.11 */
+#define CKM_ECDH1_DERIVE 0x00001050
+#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051
+#define CKM_ECMQV_DERIVE 0x00001052
+
+#define CKM_JUNIPER_KEY_GEN 0x00001060
+#define CKM_JUNIPER_ECB128 0x00001061
+#define CKM_JUNIPER_CBC128 0x00001062
+#define CKM_JUNIPER_COUNTER 0x00001063
+#define CKM_JUNIPER_SHUFFLE 0x00001064
+#define CKM_JUNIPER_WRAP 0x00001065
+#define CKM_FASTHASH 0x00001070
+
+/* CKM_AES_KEY_GEN, CKM_AES_ECB, CKM_AES_CBC, CKM_AES_MAC,
+ * CKM_AES_MAC_GENERAL, CKM_AES_CBC_PAD, CKM_DSA_PARAMETER_GEN,
+ * CKM_DH_PKCS_PARAMETER_GEN, and CKM_X9_42_DH_PARAMETER_GEN are
+ * new for v2.11 */
+#define CKM_AES_KEY_GEN 0x00001080
+#define CKM_AES_ECB 0x00001081
+#define CKM_AES_CBC 0x00001082
+#define CKM_AES_MAC 0x00001083
+#define CKM_AES_MAC_GENERAL 0x00001084
+#define CKM_AES_CBC_PAD 0x00001085
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKM_BLOWFISH_KEY_GEN 0x00001090
+#define CKM_BLOWFISH_CBC 0x00001091
+#define CKM_TWOFISH_KEY_GEN 0x00001092
+#define CKM_TWOFISH_CBC 0x00001093
+
+
+/* CKM_xxx_ENCRYPT_DATA mechanisms are new for v2.20 */
+#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100
+#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101
+#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102
+#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103
+#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104
+#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105
+
+#define CKM_DSA_PARAMETER_GEN 0x00002000
+#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001
+#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002
+
+#define CKM_VENDOR_DEFINED 0x80000000
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism */
+typedef struct CK_MECHANISM {
+ CK_MECHANISM_TYPE mechanism;
+ CK_VOID_PTR pParameter;
+
+ /* ulParameterLen was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulParameterLen; /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism */
+typedef struct CK_MECHANISM_INFO {
+ CK_ULONG ulMinKeySize;
+ CK_ULONG ulMaxKeySize;
+ CK_FLAGS flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ * Bit Flag Mask Meaning */
+#define CKF_HW 0x00000001 /* performed by HW */
+
+/* The flags CKF_ENCRYPT, CKF_DECRYPT, CKF_DIGEST, CKF_SIGN,
+ * CKG_SIGN_RECOVER, CKF_VERIFY, CKF_VERIFY_RECOVER,
+ * CKF_GENERATE, CKF_GENERATE_KEY_PAIR, CKF_WRAP, CKF_UNWRAP,
+ * and CKF_DERIVE are new for v2.0. They specify whether or not
+ * a mechanism can be used for a particular task */
+#define CKF_ENCRYPT 0x00000100
+#define CKF_DECRYPT 0x00000200
+#define CKF_DIGEST 0x00000400
+#define CKF_SIGN 0x00000800
+#define CKF_SIGN_RECOVER 0x00001000
+#define CKF_VERIFY 0x00002000
+#define CKF_VERIFY_RECOVER 0x00004000
+#define CKF_GENERATE 0x00008000
+#define CKF_GENERATE_KEY_PAIR 0x00010000
+#define CKF_WRAP 0x00020000
+#define CKF_UNWRAP 0x00040000
+#define CKF_DERIVE 0x00080000
+
+/* CKF_EC_F_P, CKF_EC_F_2M, CKF_EC_ECPARAMETERS, CKF_EC_NAMEDCURVE,
+ * CKF_EC_UNCOMPRESS, and CKF_EC_COMPRESS are new for v2.11. They
+ * describe a token's EC capabilities not available in mechanism
+ * information. */
+#define CKF_EC_F_P 0x00100000
+#define CKF_EC_F_2M 0x00200000
+#define CKF_EC_ECPARAMETERS 0x00400000
+#define CKF_EC_NAMEDCURVE 0x00800000
+#define CKF_EC_UNCOMPRESS 0x01000000
+#define CKF_EC_COMPRESS 0x02000000
+
+#define CKF_EXTENSION 0x80000000 /* FALSE for this version */
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+
+/* CK_RV is a value that identifies the return value of a
+ * Cryptoki function */
+/* CK_RV was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG CK_RV;
+
+#define CKR_OK 0x00000000
+#define CKR_CANCEL 0x00000001
+#define CKR_HOST_MEMORY 0x00000002
+#define CKR_SLOT_ID_INVALID 0x00000003
+
+/* CKR_FLAGS_INVALID was removed for v2.0 */
+
+/* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */
+#define CKR_GENERAL_ERROR 0x00000005
+#define CKR_FUNCTION_FAILED 0x00000006
+
+/* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS,
+ * and CKR_CANT_LOCK are new for v2.01 */
+#define CKR_ARGUMENTS_BAD 0x00000007
+#define CKR_NO_EVENT 0x00000008
+#define CKR_NEED_TO_CREATE_THREADS 0x00000009
+#define CKR_CANT_LOCK 0x0000000A
+
+#define CKR_ATTRIBUTE_READ_ONLY 0x00000010
+#define CKR_ATTRIBUTE_SENSITIVE 0x00000011
+#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012
+#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013
+#define CKR_DATA_INVALID 0x00000020
+#define CKR_DATA_LEN_RANGE 0x00000021
+#define CKR_DEVICE_ERROR 0x00000030
+#define CKR_DEVICE_MEMORY 0x00000031
+#define CKR_DEVICE_REMOVED 0x00000032
+#define CKR_ENCRYPTED_DATA_INVALID 0x00000040
+#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041
+#define CKR_FUNCTION_CANCELED 0x00000050
+#define CKR_FUNCTION_NOT_PARALLEL 0x00000051
+
+/* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */
+#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054
+
+#define CKR_KEY_HANDLE_INVALID 0x00000060
+
+/* CKR_KEY_SENSITIVE was removed for v2.0 */
+
+#define CKR_KEY_SIZE_RANGE 0x00000062
+#define CKR_KEY_TYPE_INCONSISTENT 0x00000063
+
+/* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED,
+ * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED,
+ * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for
+ * v2.0 */
+#define CKR_KEY_NOT_NEEDED 0x00000064
+#define CKR_KEY_CHANGED 0x00000065
+#define CKR_KEY_NEEDED 0x00000066
+#define CKR_KEY_INDIGESTIBLE 0x00000067
+#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068
+#define CKR_KEY_NOT_WRAPPABLE 0x00000069
+#define CKR_KEY_UNEXTRACTABLE 0x0000006A
+
+#define CKR_MECHANISM_INVALID 0x00000070
+#define CKR_MECHANISM_PARAM_INVALID 0x00000071
+
+/* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID
+ * were removed for v2.0 */
+#define CKR_OBJECT_HANDLE_INVALID 0x00000082
+#define CKR_OPERATION_ACTIVE 0x00000090
+#define CKR_OPERATION_NOT_INITIALIZED 0x00000091
+#define CKR_PIN_INCORRECT 0x000000A0
+#define CKR_PIN_INVALID 0x000000A1
+#define CKR_PIN_LEN_RANGE 0x000000A2
+
+/* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */
+#define CKR_PIN_EXPIRED 0x000000A3
+#define CKR_PIN_LOCKED 0x000000A4
+
+#define CKR_SESSION_CLOSED 0x000000B0
+#define CKR_SESSION_COUNT 0x000000B1
+#define CKR_SESSION_HANDLE_INVALID 0x000000B3
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4
+#define CKR_SESSION_READ_ONLY 0x000000B5
+#define CKR_SESSION_EXISTS 0x000000B6
+
+/* CKR_SESSION_READ_ONLY_EXISTS and
+ * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */
+#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7
+#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8
+
+#define CKR_SIGNATURE_INVALID 0x000000C0
+#define CKR_SIGNATURE_LEN_RANGE 0x000000C1
+#define CKR_TEMPLATE_INCOMPLETE 0x000000D0
+#define CKR_TEMPLATE_INCONSISTENT 0x000000D1
+#define CKR_TOKEN_NOT_PRESENT 0x000000E0
+#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1
+#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2
+#define CKR_USER_ALREADY_LOGGED_IN 0x00000100
+#define CKR_USER_NOT_LOGGED_IN 0x00000101
+#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102
+#define CKR_USER_TYPE_INVALID 0x00000103
+
+/* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES
+ * are new to v2.01 */
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104
+#define CKR_USER_TOO_MANY_TYPES 0x00000105
+
+#define CKR_WRAPPED_KEY_INVALID 0x00000110
+#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112
+#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113
+#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115
+#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120
+
+/* These are new to v2.0 */
+#define CKR_RANDOM_NO_RNG 0x00000121
+
+/* These are new to v2.11 */
+#define CKR_DOMAIN_PARAMS_INVALID 0x00000130
+
+/* These are new to v2.0 */
+#define CKR_BUFFER_TOO_SMALL 0x00000150
+#define CKR_SAVED_STATE_INVALID 0x00000160
+#define CKR_INFORMATION_SENSITIVE 0x00000170
+#define CKR_STATE_UNSAVEABLE 0x00000180
+
+/* These are new to v2.01 */
+#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191
+#define CKR_MUTEX_BAD 0x000001A0
+#define CKR_MUTEX_NOT_LOCKED 0x000001A1
+
+/* This is new to v2.20 */
+#define CKR_FUNCTION_REJECTED 0x00000200
+
+#define CKR_VENDOR_DEFINED 0x80000000
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_NOTIFICATION event,
+ CK_VOID_PTR pApplication /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec
+ * version and pointers of appropriate types to all the
+ * Cryptoki functions */
+/* CK_FUNCTION_LIST is new for v2.0 */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+ CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize */
+typedef struct CK_C_INITIALIZE_ARGS {
+ CK_CREATEMUTEX CreateMutex;
+ CK_DESTROYMUTEX DestroyMutex;
+ CK_LOCKMUTEX LockMutex;
+ CK_UNLOCKMUTEX UnlockMutex;
+ CK_FLAGS flags;
+ CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001
+#define CKF_OS_LOCKING_OK 0x00000002
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK 1
+
+/* CK_RSA_PKCS_OAEP_MGF_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_MGF_TYPE is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme. */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+/* CKG_MGF1_SHA256, CKG_MGF1_SHA384, and CKG_MGF1_SHA512
+ * are new for v2.20 */
+#define CKG_MGF1_SHA1 0x00000001
+#define CKG_MGF1_SHA256 0x00000002
+#define CKG_MGF1_SHA384 0x00000003
+#define CKG_MGF1_SHA512 0x00000004
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme. */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED 0x00000001
+
+/* CK_RSA_PKCS_OAEP_PARAMS is new for v2.10.
+ * CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism. */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+ CK_VOID_PTR pSourceData;
+ CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS is new for v2.11.
+ * CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s). */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_ULONG sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+/* CK_EC_KDF_TYPE is new for v2.11. */
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL 0x00000001
+#define CKD_SHA1_KDF 0x00000002
+
+/* CK_ECDH1_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+
+/* CK_ECDH2_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs. */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms (new for PKCS #11 v2.11) */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* The following X9.42 DH key derivation functions are defined
+ (besides CKD_NULL already defined : */
+#define CKD_SHA1_KDF_ASN1 0x00000003
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004
+
+/* CK_X9_42_DH1_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism */
+/* CK_KEA_DERIVE_PARAMS is new for v2.0 */
+typedef struct CK_KEA_DERIVE_PARAMS {
+ CK_BBOOL isSender;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pRandomB;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just
+ * holds the effective keysize */
+typedef CK_ULONG CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism */
+typedef struct CK_RC2_CBC_PARAMS {
+ /* ulEffectiveBits was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+
+ CK_BYTE iv[8]; /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism */
+/* CK_RC2_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms */
+/* CK_RC5_PARAMS is new for v2.0 */
+typedef struct CK_RC5_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism */
+/* CK_RC5_CBC_PARAMS is new for v2.0 */
+typedef struct CK_RC5_CBC_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_BYTE_PTR pIv; /* pointer to IV */
+ CK_ULONG ulIvLen; /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism */
+/* CK_RC5_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms. Its value is the length of
+ * the MAC */
+/* CK_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef CK_ULONG CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_DES/AES_ECB/CBC_ENCRYPT_DATA_PARAMS are new for v2.20 */
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[8];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism */
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pPassword;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPAndGLen;
+ CK_ULONG ulQLen;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pPrimeP;
+ CK_BYTE_PTR pBaseG;
+ CK_BYTE_PTR pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+ CK_SKIPJACK_PRIVATE_WRAP_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism */
+/* CK_SKIPJACK_RELAYX_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+ CK_ULONG ulOldWrappedXLen;
+ CK_BYTE_PTR pOldWrappedX;
+ CK_ULONG ulOldPasswordLen;
+ CK_BYTE_PTR pOldPassword;
+ CK_ULONG ulOldPublicDataLen;
+ CK_BYTE_PTR pOldPublicData;
+ CK_ULONG ulOldRandomLen;
+ CK_BYTE_PTR pOldRandomA;
+ CK_ULONG ulNewPasswordLen;
+ CK_BYTE_PTR pNewPassword;
+ CK_ULONG ulNewPublicDataLen;
+ CK_BYTE_PTR pNewPublicData;
+ CK_ULONG ulNewRandomLen;
+ CK_BYTE_PTR pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+ CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+ CK_BYTE_PTR pInitVector;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pSalt;
+ CK_ULONG ulSaltLen;
+ CK_ULONG ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism */
+/* CK_KEY_WRAP_SET_OAEP_PARAMS is new for v2.0 */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+ CK_BYTE bBC; /* block contents byte */
+ CK_BYTE_PTR pX; /* extra data */
+ CK_ULONG ulXLen; /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR \
+ CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hClientMacSecret;
+ CK_OBJECT_HANDLE hServerMacSecret;
+ CK_OBJECT_HANDLE hClientKey;
+ CK_OBJECT_HANDLE hServerKey;
+ CK_BYTE_PTR pIVClient;
+ CK_BYTE_PTR pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+/* CK_TLS_PRF_PARAMS is new for version 2.20 */
+typedef struct CK_TLS_PRF_PARAMS {
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+/* WTLS is new for version 2.20 */
+typedef struct CK_WTLS_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hMacSecret;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_ULONG ulSequenceNumber;
+ CK_BBOOL bIsExport;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+/* CMS is new for version 2.20 */
+typedef struct CK_CMS_SIG_PARAMS {
+ CK_OBJECT_HANDLE certificateHandle;
+ CK_MECHANISM_PTR pSigningMechanism;
+ CK_MECHANISM_PTR pDigestMechanism;
+ CK_UTF8CHAR_PTR pContentType;
+ CK_BYTE_PTR pRequestedAttributes;
+ CK_ULONG ulRequestedAttributesLen;
+ CK_BYTE_PTR pRequiredAttributes;
+ CK_ULONG ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+ CK_BYTE_PTR pData;
+ CK_ULONG ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+ CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key */
+/* CK_EXTRACT_PARAMS is new for v2.0 */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is new for v2.10.
+ * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+/* The following PRFs are defined in PKCS #5 v2.0. */
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001
+
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is new for v2.10.
+ * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED 0x00000001
+
+/* CK_PKCS5_PBKD2_PARAMS is new for v2.10.
+ * CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism. */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG_PTR ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+#endif
diff --git a/programs/pluto/rsaref/unix.h b/programs/pluto/rsaref/unix.h
new file mode 100644
index 000000000..2e7eb6663
--- /dev/null
+++ b/programs/pluto/rsaref/unix.h
@@ -0,0 +1,24 @@
+
+
+#ifndef UNIX_H
+#define UNIX_H
+
+#define CK_PTR *
+
+#define CK_DEFINE_FUNCTION(returnType, name) \
+ returnType name
+
+#define CK_DECLARE_FUNCTION(returnType, name) \
+ returnType name
+
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ returnType (* name)
+
+#define CK_CALLBACK_FUNCTION(returnType, name) \
+ returnType (* name)
+
+#ifndef NULL_PTR
+#define NULL_PTR 0
+#endif
+
+#endif
diff --git a/programs/pluto/server.c b/programs/pluto/server.c
new file mode 100644
index 000000000..30251138e
--- /dev/null
+++ b/programs/pluto/server.c
@@ -0,0 +1,1064 @@
+/* get-next-event loop
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: server.c,v 1.9 2005/09/09 14:15:35 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#ifdef SOLARIS
+# include <sys/sockio.h> /* for Solaris 2.6: defines SIOCGIFCONF */
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <resolv.h>
+#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "state.h"
+#include "connections.h"
+#include "kernel.h"
+#include "log.h"
+#include "server.h"
+#include "timer.h"
+#include "packet.h"
+#include "demux.h" /* needs packet.h */
+#include "rcv_whack.h"
+#include "rcv_info.h"
+#include "keys.h"
+#include "adns.h" /* needs <resolv.h> */
+#include "dnskey.h" /* needs keys.h and adns.h */
+#include "whack.h" /* for RC_LOG_SERIOUS */
+
+#include <pfkeyv2.h>
+#include <pfkey.h>
+#include "kameipsec.h"
+
+#ifdef NAT_TRAVERSAL
+#include "nat_traversal.h"
+#endif
+
+/*
+ * Server main loop and socket initialization routines.
+ */
+
+static const int on = TRUE; /* by-reference parameter; constant, we hope */
+
+/* control (whack) socket */
+int ctl_fd = NULL_FD; /* file descriptor of control (whack) socket */
+struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
+
+/* info (showpolicy) socket */
+int policy_fd = NULL_FD;
+struct sockaddr_un info_addr= { AF_UNIX, DEFAULT_CTLBASE INFO_SUFFIX };
+
+/* Initialize the control socket.
+ * Note: this is called very early, so little infrastructure is available.
+ * It is important that the socket is created before the original
+ * Pluto process returns.
+ */
+err_t
+init_ctl_socket(void)
+{
+ err_t failed = NULL;
+
+ delete_ctl_socket(); /* preventative medicine */
+ ctl_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (ctl_fd == -1)
+ failed = "create";
+ else if (fcntl(ctl_fd, F_SETFD, FD_CLOEXEC) == -1)
+ failed = "fcntl FD+CLOEXEC";
+ else if (setsockopt(ctl_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on)) < 0)
+ failed = "setsockopt";
+ else
+ {
+ /* to keep control socket secure, use umask */
+ mode_t ou = umask(~S_IRWXU);
+
+ if (bind(ctl_fd, (struct sockaddr *)&ctl_addr
+ , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
+ failed = "bind";
+ umask(ou);
+ }
+
+ /* 5 is a haphazardly chosen limit for the backlog.
+ * Rumour has it that this is the max on BSD systems.
+ */
+ if (failed == NULL && listen(ctl_fd, 5) < 0)
+ failed = "listen() on";
+
+ return failed == NULL? NULL : builddiag("could not %s control socket: %d %s"
+ , failed, errno, strerror(errno));
+}
+
+void
+delete_ctl_socket(void)
+{
+ /* Is noting failure useful? Not when used as preventative medicine. */
+ unlink(ctl_addr.sun_path);
+}
+
+#ifdef IPSECPOLICY
+/* Initialize the info socket.
+ */
+err_t
+init_info_socket(void)
+{
+ err_t failed = NULL;
+
+ delete_info_socket(); /* preventative medicine */
+ info_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (info_fd == -1)
+ failed = "create";
+ else if (fcntl(info_fd, F_SETFD, FD_CLOEXEC) == -1)
+ failed = "fcntl FD+CLOEXEC";
+ else if (setsockopt(info_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&on, sizeof(on)) < 0)
+ failed = "setsockopt";
+ else
+ {
+ /* this socket should be openable by all proceses */
+ mode_t ou = umask(0);
+
+ if (bind(info_fd, (struct sockaddr *)&info_addr
+ , offsetof(struct sockaddr_un, sun_path) + strlen(info_addr.sun_path)) < 0)
+ failed = "bind";
+ umask(ou);
+ }
+
+ /* 64 might be big enough, and the system may limit us anyway.
+ */
+ if (failed == NULL && listen(info_fd, 64) < 0)
+ failed = "listen() on";
+
+ return failed == NULL? NULL : builddiag("could not %s info socket: %d %s"
+ , failed, errno, strerror(errno));
+}
+
+void
+delete_info_socket(void)
+{
+ unlink(info_addr.sun_path);
+}
+#endif /* IPSECPOLICY */
+
+
+bool listening = FALSE; /* should we pay attention to IKE messages? */
+
+struct iface *interfaces = NULL; /* public interfaces */
+
+/* Initialize the interface sockets. */
+
+static void
+mark_ifaces_dead(void)
+{
+ struct iface *p;
+
+ for (p = interfaces; p != NULL; p = p->next)
+ p->change = IFN_DELETE;
+}
+
+static void
+free_dead_ifaces(void)
+{
+ struct iface *p;
+ bool some_dead = FALSE
+ , some_new = FALSE;
+
+ for (p = interfaces; p != NULL; p = p->next)
+ {
+ if (p->change == IFN_DELETE)
+ {
+ plog("shutting down interface %s/%s %s"
+ , p->vname, p->rname, ip_str(&p->addr));
+ some_dead = TRUE;
+ }
+ else if (p->change == IFN_ADD)
+ {
+ some_new = TRUE;
+ }
+ }
+
+ if (some_dead)
+ {
+ struct iface **pp;
+
+ release_dead_interfaces();
+ for (pp = &interfaces; (p = *pp) != NULL; )
+ {
+ if (p->change == IFN_DELETE)
+ {
+ *pp = p->next; /* advance *pp */
+ pfree(p->vname);
+ pfree(p->rname);
+ close(p->fd);
+ pfree(p);
+ }
+ else
+ {
+ pp = &p->next; /* advance pp */
+ }
+ }
+ }
+
+ /* this must be done after the release_dead_interfaces
+ * in case some to the newly unoriented connections can
+ * become oriented here.
+ */
+ if (some_dead || some_new)
+ check_orientations();
+}
+
+void
+free_ifaces(void)
+{
+ mark_ifaces_dead();
+ free_dead_ifaces();
+}
+
+struct raw_iface {
+ ip_address addr;
+ char name[IFNAMSIZ + 20]; /* what would be a safe size? */
+ struct raw_iface *next;
+};
+
+/* Called to handle --interface <ifname>
+ * Semantics: if specified, only these (real) interfaces are considered.
+ */
+static const char *pluto_ifn[10];
+static int pluto_ifn_roof = 0;
+
+bool
+use_interface(const char *rifn)
+{
+ if (pluto_ifn_roof >= (int)elemsof(pluto_ifn))
+ {
+ return FALSE;
+ }
+ else
+ {
+ pluto_ifn[pluto_ifn_roof++] = rifn;
+ return TRUE;
+ }
+}
+
+#ifndef IPSECDEVPREFIX
+# define IPSECDEVPREFIX "ipsec"
+#endif
+
+static struct raw_iface *
+find_raw_ifaces4(void)
+{
+ int j; /* index into buf */
+ struct ifconf ifconf;
+ struct ifreq buf[300]; /* for list of interfaces -- arbitrary limit */
+ struct raw_iface *rifaces = NULL;
+ int master_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); /* Get a UDP socket */
+
+ /* get list of interfaces with assigned IPv4 addresses from system */
+
+ if (master_sock == -1)
+ exit_log_errno((e, "socket() failed in find_raw_ifaces4()"));
+
+ if (setsockopt(master_sock, SOL_SOCKET, SO_REUSEADDR
+ , (const void *)&on, sizeof(on)) < 0)
+ exit_log_errno((e, "setsockopt() in find_raw_ifaces4()"));
+
+ /* bind the socket */
+ {
+ ip_address any;
+
+ happy(anyaddr(AF_INET, &any));
+ setportof(htons(pluto_port), &any);
+ if (bind(master_sock, sockaddrof(&any), sockaddrlenof(&any)) < 0)
+ exit_log_errno((e, "bind() failed in find_raw_ifaces4()"));
+ }
+
+ /* Get local interfaces. See netdevice(7). */
+ ifconf.ifc_len = sizeof(buf);
+ ifconf.ifc_buf = (void *) buf;
+ zero(buf);
+
+ if (ioctl(master_sock, SIOCGIFCONF, &ifconf) == -1)
+ exit_log_errno((e, "ioctl(SIOCGIFCONF) in find_raw_ifaces4()"));
+
+ /* Add an entry to rifaces for each interesting interface. */
+ for (j = 0; (j+1) * sizeof(*buf) <= (size_t)ifconf.ifc_len; j++)
+ {
+ struct raw_iface ri;
+ const struct sockaddr_in *rs = (struct sockaddr_in *) &buf[j].ifr_addr;
+ struct ifreq auxinfo;
+
+ /* ignore all but AF_INET interfaces */
+ if (rs->sin_family != AF_INET)
+ continue; /* not interesting */
+
+ /* build a NUL-terminated copy of the rname field */
+ memcpy(ri.name, buf[j].ifr_name, IFNAMSIZ);
+ ri.name[IFNAMSIZ] = '\0';
+
+ /* ignore if our interface names were specified, and this isn't one */
+ if (pluto_ifn_roof != 0)
+ {
+ int i;
+
+ for (i = 0; i != pluto_ifn_roof; i++)
+ if (streq(ri.name, pluto_ifn[i]))
+ break;
+ if (i == pluto_ifn_roof)
+ continue; /* not found -- skip */
+ }
+
+ /* Find out stuff about this interface. See netdevice(7). */
+ zero(&auxinfo); /* paranoia */
+ memcpy(auxinfo.ifr_name, buf[j].ifr_name, IFNAMSIZ);
+ if (ioctl(master_sock, SIOCGIFFLAGS, &auxinfo) == -1)
+ exit_log_errno((e
+ , "ioctl(SIOCGIFFLAGS) for %s in find_raw_ifaces4()"
+ , ri.name));
+ if (!(auxinfo.ifr_flags & IFF_UP))
+ continue; /* ignore an interface that isn't UP */
+
+ /* ignore unconfigured interfaces */
+ if (rs->sin_addr.s_addr == 0)
+ continue;
+
+ happy(initaddr((const void *)&rs->sin_addr, sizeof(struct in_addr)
+ , AF_INET, &ri.addr));
+
+ DBG(DBG_CONTROL, DBG_log("found %s with address %s"
+ , ri.name, ip_str(&ri.addr)));
+ ri.next = rifaces;
+ rifaces = clone_thing(ri, "struct raw_iface");
+ }
+
+ close(master_sock);
+
+ return rifaces;
+}
+
+static struct raw_iface *
+find_raw_ifaces6(void)
+{
+
+ /* Get list of interfaces with IPv6 addresses from system from /proc/net/if_inet6).
+ *
+ * Documentation of format?
+ * RTFS: linux-2.2.16/net/ipv6/addrconf.c:iface_proc_info()
+ * linux-2.4.9-13/net/ipv6/addrconf.c:iface_proc_info()
+ *
+ * Sample from Gerhard's laptop:
+ * 00000000000000000000000000000001 01 80 10 80 lo
+ * 30490009000000000000000000010002 02 40 00 80 ipsec0
+ * 30490009000000000000000000010002 07 40 00 80 eth0
+ * fe80000000000000025004fffefd5484 02 0a 20 80 ipsec0
+ * fe80000000000000025004fffefd5484 07 0a 20 80 eth0
+ *
+ * Each line contains:
+ * - IPv6 address: 16 bytes, in hex, no punctuation
+ * - ifindex: 1 byte, in hex
+ * - prefix_len: 1 byte, in hex
+ * - scope (e.g. global, link local): 1 byte, in hex
+ * - flags: 1 byte, in hex
+ * - device name: string, followed by '\n'
+ */
+ struct raw_iface *rifaces = NULL;
+ static const char proc_name[] = "/proc/net/if_inet6";
+ FILE *proc_sock = fopen(proc_name, "r");
+
+ if (proc_sock == NULL)
+ {
+ DBG(DBG_CONTROL, DBG_log("could not open %s", proc_name));
+ }
+ else
+ {
+ for (;;)
+ {
+ struct raw_iface ri;
+ unsigned short xb[8]; /* IPv6 address as 8 16-bit chunks */
+ char sb[8*5]; /* IPv6 address as string-with-colons */
+ unsigned int if_idx; /* proc field, not used */
+ unsigned int plen; /* proc field, not used */
+ unsigned int scope; /* proc field, used to exclude link-local */
+ unsigned int dad_status; /* proc field, not used */
+ /* ??? I hate and distrust scanf -- DHR */
+ int r = fscanf(proc_sock
+ , "%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx"
+ " %02x %02x %02x %02x %20s\n"
+ , xb+0, xb+1, xb+2, xb+3, xb+4, xb+5, xb+6, xb+7
+ , &if_idx, &plen, &scope, &dad_status, ri.name);
+
+ /* ??? we should diagnose any problems */
+ if (r != 13)
+ break;
+
+ /* ignore addresses with link local scope.
+ * From linux-2.4.9-13/include/net/ipv6.h:
+ * IPV6_ADDR_LINKLOCAL 0x0020U
+ * IPV6_ADDR_SCOPE_MASK 0x00f0U
+ */
+ if ((scope & 0x00f0U) == 0x0020U)
+ continue;
+
+ snprintf(sb, sizeof(sb)
+ , "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x"
+ , xb[0], xb[1], xb[2], xb[3], xb[4], xb[5], xb[6], xb[7]);
+
+ happy(ttoaddr(sb, 0, AF_INET6, &ri.addr));
+
+ if (!isunspecaddr(&ri.addr))
+ {
+ DBG(DBG_CONTROL
+ , DBG_log("found %s with address %s"
+ , ri.name, sb));
+ ri.next = rifaces;
+ rifaces = clone_thing(ri, "struct raw_iface");
+ }
+ }
+ fclose(proc_sock);
+ }
+
+ return rifaces;
+}
+
+#if 1
+static int
+create_socket(struct raw_iface *ifp, const char *v_name, int port)
+{
+ int fd = socket(addrtypeof(&ifp->addr), SOCK_DGRAM, IPPROTO_UDP);
+ int fcntl_flags;
+
+ if (fd < 0)
+ {
+ log_errno((e, "socket() in process_raw_ifaces()"));
+ return -1;
+ }
+
+#if 1
+ /* Set socket Nonblocking */
+ if ((fcntl_flags=fcntl(fd, F_GETFL)) >= 0) {
+ if (!(fcntl_flags & O_NONBLOCK)) {
+ fcntl_flags |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, fcntl_flags);
+ }
+ }
+#endif
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ {
+ log_errno((e, "fcntl(,, FD_CLOEXEC) in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR
+ , (const void *)&on, sizeof(on)) < 0)
+ {
+ log_errno((e, "setsockopt SO_REUSEADDR in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+
+ /* To improve error reporting. See ip(7). */
+#if defined(IP_RECVERR) && defined(MSG_ERRQUEUE)
+ if (setsockopt(fd, SOL_IP, IP_RECVERR
+ , (const void *)&on, sizeof(on)) < 0)
+ {
+ log_errno((e, "setsockopt IP_RECVERR in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+#endif
+
+ /* With IPv6, there is no fragmentation after
+ * it leaves our interface. PMTU discovery
+ * is mandatory but doesn't work well with IKE (why?).
+ * So we must set the IPV6_USE_MIN_MTU option.
+ * See draft-ietf-ipngwg-rfc2292bis-01.txt 11.1
+ */
+#ifdef IPV6_USE_MIN_MTU /* YUCK: not always defined */
+ if (addrtypeof(&ifp->addr) == AF_INET6
+ && setsockopt(fd, SOL_SOCKET, IPV6_USE_MIN_MTU
+ , (const void *)&on, sizeof(on)) < 0)
+ {
+ log_errno((e, "setsockopt IPV6_USE_MIN_MTU in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+#endif
+
+#if defined(linux) && defined(KERNEL26_SUPPORT)
+ if (!no_klips && kernel_ops->type == KERNEL_TYPE_LINUX)
+ {
+ struct sadb_x_policy policy;
+ int level, opt;
+
+ policy.sadb_x_policy_len = sizeof(policy) / IPSEC_PFKEYv2_ALIGN;
+ policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+ policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS;
+ policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND;
+ policy.sadb_x_policy_reserved = 0;
+ policy.sadb_x_policy_id = 0;
+ policy.sadb_x_policy_reserved2 = 0;
+
+ if (addrtypeof(&ifp->addr) == AF_INET6)
+ {
+ level = IPPROTO_IPV6;
+ opt = IPV6_IPSEC_POLICY;
+ }
+ else
+ {
+ level = IPPROTO_IP;
+ opt = IP_IPSEC_POLICY;
+ }
+
+ if (setsockopt(fd, level, opt
+ , &policy, sizeof(policy)) < 0)
+ {
+ log_errno((e, "setsockopt IPSEC_POLICY in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+
+ policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND;
+
+ if (setsockopt(fd, level, opt
+ , &policy, sizeof(policy)) < 0)
+ {
+ log_errno((e, "setsockopt IPSEC_POLICY in process_raw_ifaces()"));
+ close(fd);
+ return -1;
+ }
+ }
+#endif
+
+ setportof(htons(port), &ifp->addr);
+ if (bind(fd, sockaddrof(&ifp->addr), sockaddrlenof(&ifp->addr)) < 0)
+ {
+ log_errno((e, "bind() for %s/%s %s:%u in process_raw_ifaces()"
+ , ifp->name, v_name
+ , ip_str(&ifp->addr), (unsigned) port));
+ close(fd);
+ return -1;
+ }
+ setportof(htons(pluto_port), &ifp->addr);
+ return fd;
+}
+#endif
+
+static void
+process_raw_ifaces(struct raw_iface *rifaces)
+{
+ struct raw_iface *ifp;
+
+ /* Find all virtual/real interface pairs.
+ * For each real interface...
+ */
+ for (ifp = rifaces; ifp != NULL; ifp = ifp->next)
+ {
+ struct raw_iface *v = NULL; /* matching ipsecX interface */
+ struct raw_iface fake_v;
+ bool after = FALSE; /* has vfp passed ifp on the list? */
+ bool bad = FALSE;
+ struct raw_iface *vfp;
+
+ /* ignore if virtual (ipsec*) interface */
+ if (strncmp(ifp->name, IPSECDEVPREFIX, sizeof(IPSECDEVPREFIX)-1) == 0)
+ continue;
+
+ for (vfp = rifaces; vfp != NULL; vfp = vfp->next)
+ {
+ if (vfp == ifp)
+ {
+ after = TRUE;
+ }
+ else if (sameaddr(&ifp->addr, &vfp->addr))
+ {
+ /* Different entries with matching IP addresses.
+ * Many interesting cases.
+ */
+ if (strncmp(vfp->name, IPSECDEVPREFIX, sizeof(IPSECDEVPREFIX)-1) == 0)
+ {
+ if (v != NULL && !streq(v->name, vfp->name))
+ {
+ loglog(RC_LOG_SERIOUS
+ , "ipsec interfaces %s and %s share same address %s"
+ , v->name, vfp->name, ip_str(&ifp->addr));
+ bad = TRUE;
+ }
+ else
+ {
+ v = vfp; /* current winner */
+ }
+ }
+ else
+ {
+ /* ugh: a second real interface with the same IP address
+ * "after" allows us to avoid double reporting.
+ */
+#if defined(linux) && defined(KERNEL26_SUPPORT)
+ if (!no_klips && kernel_ops->type == KERNEL_TYPE_LINUX)
+ {
+ if (after)
+ {
+ bad = TRUE;
+ break;
+ }
+ continue;
+ }
+#endif
+ if (after)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "IP interfaces %s and %s share address %s!"
+ , ifp->name, vfp->name, ip_str(&ifp->addr));
+ }
+ bad = TRUE;
+ }
+ }
+ }
+
+ if (bad)
+ continue;
+
+#if defined(linux) && defined(KERNEL26_SUPPORT)
+ if (!no_klips && kernel_ops->type == KERNEL_TYPE_LINUX)
+ {
+ v = ifp;
+ goto add_entry;
+ }
+#endif
+
+ /* what if we didn't find a virtual interface? */
+ if (v == NULL)
+ {
+ if (no_klips)
+ {
+ /* kludge for testing: invent a virtual device */
+ static const char fvp[] = "virtual";
+ fake_v = *ifp;
+ passert(sizeof(fake_v.name) > sizeof(fvp));
+ strcpy(fake_v.name, fvp);
+ addrtot(&ifp->addr, 0, fake_v.name + sizeof(fvp) - 1
+ , sizeof(fake_v.name) - (sizeof(fvp) - 1));
+ v = &fake_v;
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("IP interface %s %s has no matching ipsec* interface -- ignored"
+ , ifp->name, ip_str(&ifp->addr)));
+ continue;
+ }
+ }
+
+ /* We've got all we need; see if this is a new thing:
+ * search old interfaces list.
+ */
+#if defined(linux) && defined(KERNEL26_SUPPORT)
+add_entry:
+#endif
+ {
+ struct iface **p = &interfaces;
+
+ for (;;)
+ {
+ struct iface *q = *p;
+
+ /* search is over if at end of list */
+ if (q == NULL)
+ {
+ /* matches nothing -- create a new entry */
+ int fd = create_socket(ifp, v->name, pluto_port);
+
+ if (fd < 0)
+ break;
+
+#ifdef NAT_TRAVERSAL
+ if (nat_traversal_support_non_ike
+ && addrtypeof(&ifp->addr) == AF_INET)
+ {
+ nat_traversal_espinudp_socket(fd, ESPINUDP_WITH_NON_IKE);
+ }
+#endif
+
+ q = alloc_thing(struct iface, "struct iface");
+ q->rname = clone_str(ifp->name, "real device name");
+ q->vname = clone_str(v->name, "virtual device name");
+ q->addr = ifp->addr;
+ q->fd = fd;
+ q->next = interfaces;
+ q->change = IFN_ADD;
+ interfaces = q;
+ plog("adding interface %s/%s %s:%d"
+ , q->vname, q->rname, ip_str(&q->addr), pluto_port);
+#ifdef NAT_TRAVERSAL
+ if (nat_traversal_support_port_floating
+ && addrtypeof(&ifp->addr) == AF_INET)
+ {
+ fd = create_socket(ifp, v->name, NAT_T_IKE_FLOAT_PORT);
+ if (fd < 0)
+ break;
+ nat_traversal_espinudp_socket(fd,
+ ESPINUDP_WITH_NON_ESP);
+ q = alloc_thing(struct iface, "struct iface");
+ q->rname = clone_str(ifp->name, "real device name");
+ q->vname = clone_str(v->name, "virtual device name");
+ q->addr = ifp->addr;
+ setportof(htons(NAT_T_IKE_FLOAT_PORT), &q->addr);
+ q->fd = fd;
+ q->next = interfaces;
+ q->change = IFN_ADD;
+ q->ike_float = TRUE;
+ interfaces = q;
+ plog("adding interface %s/%s %s:%d",
+ q->vname, q->rname, ip_str(&q->addr), NAT_T_IKE_FLOAT_PORT);
+ }
+#endif
+ break;
+ }
+
+ /* search over if matching old entry found */
+ if (streq(q->rname, ifp->name)
+ && streq(q->vname, v->name)
+ && sameaddr(&q->addr, &ifp->addr))
+ {
+ /* matches -- rejuvinate old entry */
+ q->change = IFN_KEEP;
+#ifdef NAT_TRAVERSAL
+ /* look for other interfaces to keep (due to NAT-T) */
+ for (q = q->next ; q ; q = q->next) {
+ if (streq(q->rname, ifp->name)
+ && streq(q->vname, v->name)
+ && sameaddr(&q->addr, &ifp->addr)) {
+ q->change = IFN_KEEP;
+ }
+ }
+#endif
+ break;
+ }
+
+ /* try again */
+ p = &q->next;
+ } /* for (;;) */
+ }
+ }
+
+ /* delete the raw interfaces list */
+ while (rifaces != NULL)
+ {
+ struct raw_iface *t = rifaces;
+
+ rifaces = t->next;
+ pfree(t);
+ }
+}
+
+void
+find_ifaces(void)
+{
+ mark_ifaces_dead();
+ process_raw_ifaces(find_raw_ifaces4());
+ process_raw_ifaces(find_raw_ifaces6());
+
+ free_dead_ifaces(); /* ditch remaining old entries */
+
+ if (interfaces == NULL)
+ loglog(RC_LOG_SERIOUS, "no public interfaces found");
+}
+
+void
+show_ifaces_status(void)
+{
+ struct iface *p;
+
+ for (p = interfaces; p != NULL; p = p->next)
+ whack_log(RC_COMMENT, "interface %s/%s %s:%d"
+ , p->vname, p->rname, ip_str(&p->addr), ntohs(portof(&p->addr)));
+}
+
+void
+show_debug_status(void)
+{
+#ifdef DEBUG
+ whack_log(RC_COMMENT, "debug %s"
+ , bitnamesof(debug_bit_names, cur_debugging));
+#endif
+}
+
+static volatile sig_atomic_t sighupflag = FALSE;
+
+static void
+huphandler(int sig UNUSED)
+{
+ sighupflag = TRUE;
+}
+
+static volatile sig_atomic_t sigtermflag = FALSE;
+
+static void
+termhandler(int sig UNUSED)
+{
+ sigtermflag = TRUE;
+}
+
+/* call_server listens for incoming ISAKMP packets and Whack messages,
+ * and handles timer events.
+ */
+void
+call_server(void)
+{
+ struct iface *ifp;
+
+ /* catch SIGHUP and SIGTERM */
+ {
+ int r;
+ struct sigaction act;
+
+ act.sa_handler = &huphandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0; /* no SA_ONESHOT, no SA_RESTART, no nothing */
+ r = sigaction(SIGHUP, &act, NULL);
+ passert(r == 0);
+
+ act.sa_handler = &termhandler;
+ r = sigaction(SIGTERM, &act, NULL);
+ passert(r == 0);
+ }
+
+ for (;;)
+ {
+ fd_set readfds;
+ fd_set writefds;
+ int ndes;
+
+ /* wait for next interesting thing */
+
+ for (;;)
+ {
+ long next_time = next_event(); /* time to any pending timer event */
+ int maxfd = ctl_fd;
+
+ if (sigtermflag)
+ exit_pluto(0);
+
+ if (sighupflag)
+ {
+ /* Ignorant folks think poking any daemon with SIGHUP
+ * is polite. We catch it and tell them otherwise.
+ * There is one use: unsticking a hung recvfrom.
+ * This sticking happens sometimes -- kernel bug?
+ */
+ sighupflag = FALSE;
+ plog("Pluto ignores SIGHUP -- perhaps you want \"whack --listen\"");
+ }
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(ctl_fd, &readfds);
+#ifdef IPSECPOLICY
+ FD_SET(info_fd, &readfds);
+ if (maxfd < info_fd)
+ maxfd = info_fd;
+#endif
+
+ /* the only write file-descriptor of interest */
+ if (adns_qfd != NULL_FD && unsent_ADNS_queries)
+ {
+ if (maxfd < adns_qfd)
+ maxfd = adns_qfd;
+ FD_SET(adns_qfd, &writefds);
+ }
+
+ if (adns_afd != NULL_FD)
+ {
+ if (maxfd < adns_afd)
+ maxfd = adns_afd;
+ FD_SET(adns_afd, &readfds);
+ }
+
+#ifdef KLIPS
+ if (!no_klips)
+ {
+ int fd = *kernel_ops->async_fdp;
+
+ if (kernel_ops->process_queue)
+ kernel_ops->process_queue();
+ if (maxfd < fd)
+ maxfd = fd;
+ passert(!FD_ISSET(fd, &readfds));
+ FD_SET(fd, &readfds);
+ }
+#endif
+
+ if (listening)
+ {
+ for (ifp = interfaces; ifp != NULL; ifp = ifp->next)
+ {
+ if (maxfd < ifp->fd)
+ maxfd = ifp->fd;
+ passert(!FD_ISSET(ifp->fd, &readfds));
+ FD_SET(ifp->fd, &readfds);
+ }
+ }
+
+ if (next_time == -1)
+ {
+ /* select without timer */
+
+ ndes = select(maxfd + 1, &readfds, &writefds, NULL, NULL);
+ }
+ else if (next_time == 0)
+ {
+ /* timer without select: there is a timer event pending,
+ * and it should fire now so don't bother to do the select.
+ */
+ ndes = 0; /* signify timer expiration */
+ }
+ else
+ {
+ /* select with timer */
+
+ struct timeval tm;
+
+ tm.tv_sec = next_time;
+ tm.tv_usec = 0;
+ ndes = select(maxfd + 1, &readfds, &writefds, NULL, &tm);
+ }
+
+ if (ndes != -1)
+ break; /* success */
+
+ if (errno != EINTR)
+ exit_log_errno((e, "select() failed in call_server()"));
+
+ /* retry if terminated by signal */
+ }
+
+ /* figure out what is interesting */
+
+ if (ndes == 0)
+ {
+ /* timer event */
+
+ DBG(DBG_CONTROL,
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*time to handle event"));
+
+ handle_timer_event();
+ passert(GLOBALS_ARE_RESET());
+ }
+ else
+ {
+ /* at least one file descriptor is ready */
+
+ if (adns_qfd != NULL_FD && FD_ISSET(adns_qfd, &writefds))
+ {
+ passert(ndes > 0);
+ send_unsent_ADNS_queries();
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+
+ if (adns_afd != NULL_FD && FD_ISSET(adns_afd, &readfds))
+ {
+ passert(ndes > 0);
+ DBG(DBG_CONTROL,
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*received adns message"));
+ handle_adns_answer();
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+
+#ifdef KLIPS
+ if (!no_klips && FD_ISSET(*kernel_ops->async_fdp, &readfds))
+ {
+ passert(ndes > 0);
+ DBG(DBG_CONTROL,
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*received kernel message"));
+ kernel_ops->process_msg();
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+#endif
+
+ for (ifp = interfaces; ifp != NULL; ifp = ifp->next)
+ {
+ if (FD_ISSET(ifp->fd, &readfds))
+ {
+ /* comm_handle will print DBG_CONTROL intro,
+ * with more info than we have here.
+ */
+
+ passert(ndes > 0);
+ comm_handle(ifp);
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+ }
+
+ if (FD_ISSET(ctl_fd, &readfds))
+ {
+ passert(ndes > 0);
+ DBG(DBG_CONTROL,
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*received whack message"));
+ whack_handle(ctl_fd);
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+
+#ifdef IPSECPOLICY
+ if (FD_ISSET(info_fd, &readfds))
+ {
+ passert(ndes > 0);
+ DBG(DBG_CONTROL,
+ DBG_log(BLANK_FORMAT);
+ DBG_log("*received info message"));
+ info_handle(info_fd);
+ passert(GLOBALS_ARE_RESET());
+ ndes--;
+ }
+#endif
+
+ passert(ndes == 0);
+ }
+ }
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * End Variables:
+ */
diff --git a/programs/pluto/server.h b/programs/pluto/server.h
new file mode 100644
index 000000000..aa14d5aaa
--- /dev/null
+++ b/programs/pluto/server.h
@@ -0,0 +1,60 @@
+/* get-next-event loop
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: server.h,v 1.2 2004/03/22 21:53:20 as Exp $
+ */
+
+extern int ctl_fd; /* file descriptor of control (whack) socket */
+extern struct sockaddr_un ctl_addr; /* address of control (whack) socket */
+
+extern int info_fd; /* file descriptor of control (info) socket */
+extern struct sockaddr_un info_addr; /* address of control (info) socket */
+
+extern err_t init_ctl_socket(void);
+extern void delete_ctl_socket(void);
+
+extern bool listening; /* should we pay attention to IKE messages? */
+
+
+/* interface: a terminal point for IKE traffic, IPsec transport mode
+ * and IPsec tunnels.
+ * Essentially:
+ * - an IP device (eg. eth1), and
+ * - its partner, an ipsec device (eg. ipsec0), and
+ * - their shared IP address (eg. 10.7.3.2)
+ * Note: the port for IKE is always implicitly UDP/pluto_port.
+ */
+struct iface {
+ char *vname; /* virtual (ipsec) device name */
+ char *rname; /* real device name */
+ ip_address addr; /* interface IP address */
+ int fd; /* file descriptor of socket for IKE UDP messages */
+ struct iface *next;
+#ifdef NAT_TRAVERSAL
+ bool ike_float;
+#endif
+ enum { IFN_ADD, IFN_KEEP, IFN_DELETE } change;
+};
+
+extern struct iface *interfaces; /* public interfaces */
+
+extern bool use_interface(const char *rifn);
+extern void find_ifaces(void);
+extern void show_ifaces_status(void);
+extern void free_ifaces(void);
+extern void show_debug_status(void);
+extern void call_server(void);
+
+/* in rcv_info.c */
+extern err_t init_info_socket(void);
+extern void delete_info_socket(void);
diff --git a/programs/pluto/sha1.c b/programs/pluto/sha1.c
new file mode 100644
index 000000000..bbf062876
--- /dev/null
+++ b/programs/pluto/sha1.c
@@ -0,0 +1,193 @@
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+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
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#define SHA1HANDSOFF
+
+#include <string.h>
+#include <sys/types.h> /* for u_int*_t */
+#include <endian.h> /* sets BYTE_ORDER, LITTLE_ENDIAN, and BIG_ENDIAN */
+
+#include "sha1.h"
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#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);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
+{
+u_int32_t a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ u_int32_t l[16];
+} CHAR64LONG16;
+#ifdef SHA1HANDSOFF
+CHAR64LONG16 block[1]; /* use array to appear as a pointer */
+ memcpy(block, buffer, 64);
+#else
+ /* The following had better never be used because it causes the
+ * pointer-to-const buffer to be cast into a pointer to non-const.
+ * And the result is written through. I threw a "const" in, hoping
+ * this will cause a diagnostic.
+ */
+CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
+#endif
+ /* 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;
+#ifdef SHA1HANDSOFF
+ memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
+{
+u_int32_t i;
+u_int32_t j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1]++;
+ context->count[1] += (len>>29);
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+unsigned i;
+unsigned char finalcount[8];
+unsigned char c;
+
+#if 0 /* untested "improvement" by DHR */
+ /* Convert context->count to a sequence of bytes
+ * in finalcount. Second element first, but
+ * big-endian order within element.
+ * But we do it all backwards.
+ */
+ unsigned char *fcp = &finalcount[8];
+
+ for (i = 0; i < 2; i++)
+ {
+ u_int32_t t = context->count[i];
+ int j;
+
+ for (j = 0; j < 4; t >>= 8, j++)
+ *--fcp = (unsigned char) t
+ }
+#else
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+#endif
+ c = 0200;
+ SHA1Update(context, &c, 1);
+ while ((context->count[0] & 504) != 448) {
+ c = 0000;
+ SHA1Update(context, &c, 1);
+ }
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ memset(context, '\0', sizeof(*context));
+ memset(&finalcount, '\0', sizeof(finalcount));
+}
diff --git a/programs/pluto/sha1.h b/programs/pluto/sha1.h
new file mode 100644
index 000000000..64b3d2f5d
--- /dev/null
+++ b/programs/pluto/sha1.h
@@ -0,0 +1,16 @@
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+typedef struct {
+ u_int32_t state[5];
+ u_int32_t count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
diff --git a/programs/pluto/smallprime.c b/programs/pluto/smallprime.c
new file mode 100644
index 000000000..87497d096
--- /dev/null
+++ b/programs/pluto/smallprime.c
@@ -0,0 +1,122 @@
+/* smallprime.c - List of small primes
+ * 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 <gmp.h>
+#include <freeswan.h>
+#include "constants.h"
+#include "defs.h"
+#include "gcryptfix.h"
+#else
+/* #include <config.h> */
+/* #include <stdio.h> */
+/* #include <stdlib.h> */
+/* #include "util.h" */
+/* #include "types.h" */
+#endif
+
+/* Note: 2 is not included because it can be tested more easily
+ * by looking at bit 0. The last entry in this list is marked by a zero
+ */
+ushort
+small_prime_numbers[] = {
+ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
+ 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101,
+ 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
+ 157, 163, 167, 173, 179, 181, 191, 193, 197, 199,
+ 211, 223, 227, 229, 233, 239, 241, 251, 257, 263,
+ 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
+ 331, 337, 347, 349, 353, 359, 367, 373, 379, 383,
+ 389, 397, 401, 409, 419, 421, 431, 433, 439, 443,
+ 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
+ 509, 521, 523, 541, 547, 557, 563, 569, 571, 577,
+ 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
+ 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
+ 709, 719, 727, 733, 739, 743, 751, 757, 761, 769,
+ 773, 787, 797, 809, 811, 821, 823, 827, 829, 839,
+ 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
+ 919, 929, 937, 941, 947, 953, 967, 971, 977, 983,
+ 991, 997, 1009, 1013, 1019, 1021, 1031, 1033,
+ 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091,
+ 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
+ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213,
+ 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277,
+ 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307,
+ 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399,
+ 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
+ 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
+ 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559,
+ 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609,
+ 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667,
+ 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
+ 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789,
+ 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871,
+ 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931,
+ 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997,
+ 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053,
+ 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111,
+ 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
+ 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243,
+ 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297,
+ 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357,
+ 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411,
+ 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473,
+ 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551,
+ 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633,
+ 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687,
+ 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729,
+ 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791,
+ 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
+ 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917,
+ 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999,
+ 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061,
+ 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137,
+ 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209,
+ 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271,
+ 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331,
+ 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391,
+ 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467,
+ 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533,
+ 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
+ 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643,
+ 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709,
+ 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779,
+ 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851,
+ 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917,
+ 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989,
+ 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049,
+ 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111,
+ 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177,
+ 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243,
+ 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
+ 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391,
+ 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457,
+ 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519,
+ 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597,
+ 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657,
+ 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729,
+ 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799,
+ 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889,
+ 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951,
+ 4957, 4967, 4969, 4973, 4987, 4993, 4999,
+ 0
+};
+
+
diff --git a/programs/pluto/smartcard.c b/programs/pluto/smartcard.c
new file mode 100644
index 000000000..f1994f1cf
--- /dev/null
+++ b/programs/pluto/smartcard.c
@@ -0,0 +1,1956 @@
+/* Support of smartcards and cryptotokens
+ * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
+ * Copyright (C) 2004 David Buechi, Michael Meier
+ * Zuercher Hochschule Winterthur, Switzerland
+ *
+ * Copyright (C) 2005 Michael Joosten
+ *
+ * Copyright (C) 2005 Andreas Steffen
+ * Hochschule für 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: smartcard.c,v 1.41 2006/01/04 21:03:52 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <dlfcn.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#include "constants.h"
+
+#ifdef SMARTCARD
+#include "rsaref/unix.h"
+#include "rsaref/pkcs11.h"
+#endif
+
+#include "defs.h"
+#include "mp_defs.h"
+#include "log.h"
+#include "x509.h"
+#include "ca.h"
+#include "certs.h"
+#include "keys.h"
+#include "smartcard.h"
+#include "whack.h"
+#include "fetch.h"
+
+#define DEFAULT_BASE 16
+
+/* chained list of smartcard records */
+static smartcard_t *smartcards = NULL;
+
+/* number of generated sc objects */
+static int sc_number = 0;
+
+const smartcard_t empty_sc = {
+ NULL , /* next */
+ 0 , /* last_load */
+ { CERT_NONE, {NULL} }, /* last_cert */
+ 0 , /* count */
+ 0 , /* number */
+ 999999 , /* slot */
+ NULL , /* id */
+ NULL , /* label */
+ { NULL, 0 } , /* pin */
+ FALSE , /* pinpad */
+ FALSE , /* valid */
+ FALSE , /* session_opened */
+ FALSE , /* logged_in */
+ TRUE , /* any_slot */
+ 0L , /* session */
+};
+
+#ifdef SMARTCARD /* compile with smartcard support */
+
+#define SCX_MAGIC 0xd00bed00
+
+struct scx_pkcs11_module {
+ u_int _magic;
+ void *handle;
+};
+
+typedef struct scx_pkcs11_module scx_pkcs11_module_t;
+
+/* PKCS #11 cryptoki context */
+static bool scx_initialized = FALSE;
+static scx_pkcs11_module_t *pkcs11_module = NULL_PTR;
+static CK_FUNCTION_LIST_PTR pkcs11_functions = NULL_PTR;
+
+/* crytoki v2.11 - return values of PKCS #11 functions*/
+
+static const char *const pkcs11_return_name[] = {
+ "CKR_OK",
+ "CKR_CANCEL",
+ "CKR_HOST_MEMORY",
+ "CKR_SLOT_ID_INVALID",
+ "CKR_FLAGS_INVALID",
+ "CKR_GENERAL_ERROR",
+ "CKR_FUNCTION_FAILED",
+ "CKR_ARGUMENTS_BAD",
+ "CKR_NO_EVENT",
+ "CKR_NEED_TO_CREATE_THREADS",
+ "CKR_CANT_LOCK"
+ };
+
+static const char *const pkcs11_return_name_10[] = {
+ "CKR_ATTRIBUTE_READ_ONLY",
+ "CKR_ATTRIBUTE_SENSITIVE",
+ "CKR_ATTRIBUTE_TYPE_INVALID",
+ "CKR_ATTRIBUTE_VALUE_INVALID"
+ };
+
+static const char *const pkcs11_return_name_20[] = {
+ "CKR_DATA_INVALID",
+ "CKR_DATA_LEN_RANGE"
+ };
+
+static const char *const pkcs11_return_name_30[] = {
+ "CKR_DEVICE_ERROR",
+ "CKR_DEVICE_MEMORY",
+ "CKR_DEVICE_REMOVED"
+ };
+
+static const char *const pkcs11_return_name_40[] = {
+ "CKR_ENCRYPTED_DATA_INVALID",
+ "CKR_ENCRYPTED_DATA_LEN_RANGE"
+ };
+
+static const char *const pkcs11_return_name_50[] = {
+ "CKR_FUNCTION_CANCELED",
+ "CKR_FUNCTION_NOT_PARALLEL",
+ "CKR_0x52_UNDEFINED",
+ "CKR_0x53_UNDEFINED",
+ "CKR_FUNCTION_NOT_SUPPORTED"
+ };
+
+static const char *const pkcs11_return_name_60[] = {
+ "CKR_KEY_HANDLE_INVALID",
+ "CKR_KEY_SENSITIVE",
+ "CKR_KEY_SIZE_RANGE",
+ "CKR_KEY_TYPE_INCONSISTENT",
+ "CKR_KEY_NOT_NEEDED",
+ "CKR_KEY_CHANGED",
+ "CKR_KEY_NEEDED",
+ "CKR_KEY_INDIGESTIBLE",
+ "CKR_KEY_FUNCTION_NOT_PERMITTED",
+ "CKR_KEY_NOT_WRAPPABLE",
+ "CKR_KEY_UNEXTRACTABLE"
+ };
+
+static const char *const pkcs11_return_name_70[] = {
+ "CKR_MECHANISM_INVALID",
+ "CKR_MECHANISM_PARAM_INVALID"
+ };
+
+static const char *const pkcs11_return_name_80[] = {
+ "CKR_OBJECT_HANDLE_INVALID"
+ };
+
+static const char *const pkcs11_return_name_90[] = {
+ "CKR_OPERATION_ACTIVE",
+ "CKR_OPERATION_NOT_INITIALIZED"
+ };
+
+static const char *const pkcs11_return_name_A0[] = {
+ "CKR_PIN_INCORRECT",
+ "CKR_PIN_INVALID",
+ "CKR_PIN_LEN_RANGE",
+ "CKR_PIN_EXPIRED",
+ "CKR_PIN_LOCKED"
+ };
+
+static const char *const pkcs11_return_name_B0[] = {
+ "CKR_SESSION_CLOSED",
+ "CKR_SESSION_COUNT",
+ "CKR_0xB2_UNDEFINED",
+ "CKR_SESSION_HANDLE_INVALID",
+ "CKR_SESSION_PARALLEL_NOT_SUPPORTED",
+ "CKR_SESSION_READ_ONLY",
+ "CKR_SESSION_EXISTS",
+ "CKR_SESSION_READ_ONLY_EXISTS",
+ "CKR_SESSION_READ_WRITE_SO_EXISTS"
+ };
+
+static const char *const pkcs11_return_name_C0[] = {
+ "CKR_SIGNATURE_INVALID",
+ "CKR_SIGNATURE_LEN_RANGE"
+ };
+
+static const char *const pkcs11_return_name_D0[] = {
+ "CKR_TEMPLATE_INCOMPLETE",
+ "CKR_TEMPLATE_INCONSISTENT"
+ };
+
+static const char *const pkcs11_return_name_E0[] = {
+ "CKR_TOKEN_NOT_PRESENT",
+ "CKR_TOKEN_NOT_RECOGNIZED",
+ "CKR_TOKEN_WRITE_PROTECTED"
+ };
+
+static const char *const pkcs11_return_name_F0[] = {
+ "CKR_UNWRAPPING_KEY_HANDLE_INVALID",
+ "CKR_UNWRAPPING_KEY_SIZE_RANGE",
+ "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"
+ };
+
+static const char *const pkcs11_return_name_100[] = {
+ "CKR_USER_ALREADY_LOGGED_IN",
+ "CKR_USER_NOT_LOGGED_IN",
+ "CKR_USER_PIN_NOT_INITIALIZED",
+ "CKR_USER_TYPE_INVALID",
+ "CKR_USER_ANOTHER_ALREADY_LOGGED_IN",
+ "CKR_USER_TOO_MANY_TYPES"
+ };
+
+static const char *const pkcs11_return_name_110[] = {
+ "CKR_WRAPPED_KEY_INVALID",
+ "CKR_0x111_UNDEFINED",
+ "CKR_WRAPPED_KEY_LEN_RANGE",
+ "CKR_WRAPPING_KEY_HANDLE_INVALID",
+ "CKR_WRAPPING_KEY_SIZE_RANGE",
+ "CKR_WRAPPING_KEY_TYPE_INCONSISTENT"
+ };
+
+static const char *const pkcs11_return_name_120[] = {
+ "CKR_RANDOM_SEED_NOT_SUPPORTED",
+ "CKR_RANDOM_NO_RNG"
+ };
+
+static const char *const pkcs11_return_name_130[] = {
+ "CKR_DOMAIN_PARAMS_INVALID"
+ };
+
+static const char *const pkcs11_return_name_150[] = {
+ "CKR_BUFFER_TOO_SMALL"
+ };
+
+static const char *const pkcs11_return_name_160[] = {
+ "CKR_SAVED_STATE_INVALID"
+ };
+
+static const char *const pkcs11_return_name_170[] = {
+ "CKR_INFORMATION_SENSITIVE"
+ };
+
+static const char *const pkcs11_return_name_180[] = {
+ "CKR_STATE_UNSAVEABLE"
+ };
+
+static const char *const pkcs11_return_name_190[] = {
+ "CKR_CRYPTOKI_NOT_INITIALIZED",
+ "CKR_CRYPTOKI_ALREADY_INITIALIZED"
+ };
+
+static const char *const pkcs11_return_name_1A0[] = {
+ "CKR_MUTEX_BAD",
+ "CKR_MUTEX_NOT_LOCKED"
+ };
+
+static const char *const pkcs11_return_name_200[] = {
+ "CKR_FUNCTION_REJECTED"
+ };
+
+static const char *const pkcs11_return_name_vendor[] = {
+ "CKR_VENDOR_DEFINED"
+ };
+
+static enum_names pkcs11_return_names_vendor =
+ { CKR_VENDOR_DEFINED, CKR_VENDOR_DEFINED
+ , pkcs11_return_name_vendor, NULL };
+
+static enum_names pkcs11_return_names_200 =
+ { CKR_FUNCTION_REJECTED, CKR_FUNCTION_REJECTED
+ , pkcs11_return_name_200, &pkcs11_return_names_vendor };
+
+static enum_names pkcs11_return_names_1A0 =
+ { CKR_MUTEX_BAD, CKR_MUTEX_NOT_LOCKED
+ , pkcs11_return_name_1A0, &pkcs11_return_names_200 };
+
+static enum_names pkcs11_return_names_190 =
+ { CKR_CRYPTOKI_NOT_INITIALIZED, CKR_CRYPTOKI_ALREADY_INITIALIZED
+ , pkcs11_return_name_190, &pkcs11_return_names_1A0 };
+
+static enum_names pkcs11_return_names_180 =
+ { CKR_STATE_UNSAVEABLE, CKR_STATE_UNSAVEABLE
+ , pkcs11_return_name_180, &pkcs11_return_names_190 };
+
+static enum_names pkcs11_return_names_170 =
+ { CKR_INFORMATION_SENSITIVE, CKR_INFORMATION_SENSITIVE
+ , pkcs11_return_name_170, &pkcs11_return_names_180 };
+
+static enum_names pkcs11_return_names_160 =
+ { CKR_SAVED_STATE_INVALID, CKR_SAVED_STATE_INVALID
+ , pkcs11_return_name_160, &pkcs11_return_names_170 };
+
+static enum_names pkcs11_return_names_150 =
+ { CKR_BUFFER_TOO_SMALL, CKR_BUFFER_TOO_SMALL
+ , pkcs11_return_name_150, &pkcs11_return_names_160 };
+
+static enum_names pkcs11_return_names_130 =
+ { CKR_DOMAIN_PARAMS_INVALID, CKR_DOMAIN_PARAMS_INVALID
+ , pkcs11_return_name_130, &pkcs11_return_names_150 };
+
+static enum_names pkcs11_return_names_120 =
+ { CKR_RANDOM_SEED_NOT_SUPPORTED, CKR_RANDOM_NO_RNG
+ , pkcs11_return_name_120, &pkcs11_return_names_130 };
+
+static enum_names pkcs11_return_names_110 =
+ { CKR_WRAPPED_KEY_INVALID, CKR_WRAPPING_KEY_TYPE_INCONSISTENT
+ , pkcs11_return_name_110, &pkcs11_return_names_120 };
+
+static enum_names pkcs11_return_names_100 =
+ { CKR_USER_ALREADY_LOGGED_IN, CKR_USER_TOO_MANY_TYPES
+ , pkcs11_return_name_100, &pkcs11_return_names_110 };
+
+static enum_names pkcs11_return_names_F0 =
+ { CKR_UNWRAPPING_KEY_HANDLE_INVALID, CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT
+ , pkcs11_return_name_F0, &pkcs11_return_names_100 };
+
+static enum_names pkcs11_return_names_E0 =
+ { CKR_TOKEN_NOT_PRESENT, CKR_TOKEN_WRITE_PROTECTED
+ , pkcs11_return_name_E0, &pkcs11_return_names_F0 };
+
+static enum_names pkcs11_return_names_D0 =
+ { CKR_TEMPLATE_INCOMPLETE, CKR_TEMPLATE_INCONSISTENT
+ , pkcs11_return_name_D0,&pkcs11_return_names_E0 };
+
+static enum_names pkcs11_return_names_C0 =
+ { CKR_SIGNATURE_INVALID, CKR_SIGNATURE_LEN_RANGE
+ , pkcs11_return_name_C0, &pkcs11_return_names_D0 };
+
+static enum_names pkcs11_return_names_B0 =
+ { CKR_SESSION_CLOSED, CKR_SESSION_READ_WRITE_SO_EXISTS
+ , pkcs11_return_name_B0, &pkcs11_return_names_C0 };
+
+static enum_names pkcs11_return_names_A0 =
+ { CKR_PIN_INCORRECT, CKR_PIN_LOCKED
+ , pkcs11_return_name_A0, &pkcs11_return_names_B0 };
+
+static enum_names pkcs11_return_names_90 =
+ { CKR_OPERATION_ACTIVE, CKR_OPERATION_NOT_INITIALIZED
+ , pkcs11_return_name_90, &pkcs11_return_names_A0 };
+
+static enum_names pkcs11_return_names_80 =
+ { CKR_OBJECT_HANDLE_INVALID, CKR_OBJECT_HANDLE_INVALID
+ , pkcs11_return_name_80, &pkcs11_return_names_90 };
+
+static enum_names pkcs11_return_names_70 =
+ { CKR_MECHANISM_INVALID, CKR_MECHANISM_PARAM_INVALID
+ , pkcs11_return_name_70, &pkcs11_return_names_80 };
+
+static enum_names pkcs11_return_names_60 =
+ { CKR_KEY_HANDLE_INVALID, CKR_KEY_UNEXTRACTABLE
+ , pkcs11_return_name_60, &pkcs11_return_names_70 };
+
+static enum_names pkcs11_return_names_50 =
+ { CKR_FUNCTION_CANCELED, CKR_FUNCTION_NOT_SUPPORTED
+ , pkcs11_return_name_50, &pkcs11_return_names_60 };
+
+static enum_names pkcs11_return_names_40 =
+ { CKR_ENCRYPTED_DATA_INVALID, CKR_ENCRYPTED_DATA_LEN_RANGE
+ , pkcs11_return_name_40, &pkcs11_return_names_50 };
+
+static enum_names pkcs11_return_names_30 =
+ { CKR_DEVICE_ERROR, CKR_DEVICE_REMOVED
+ , pkcs11_return_name_30, &pkcs11_return_names_40 };
+
+static enum_names pkcs11_return_names_20 =
+ { CKR_DATA_INVALID, CKR_DATA_LEN_RANGE
+ , pkcs11_return_name_20, &pkcs11_return_names_30 };
+
+static enum_names pkcs11_return_names_10 =
+ { CKR_ATTRIBUTE_READ_ONLY, CKR_ATTRIBUTE_VALUE_INVALID
+ , pkcs11_return_name_10, &pkcs11_return_names_20};
+
+static enum_names pkcs11_return_names =
+ { CKR_OK, CKR_CANT_LOCK
+ , pkcs11_return_name, &pkcs11_return_names_10};
+
+/*
+ * Unload a PKCS#11 module.
+ * The calling application is responsible for cleaning up
+ * and calling C_Finalize()
+ */
+static CK_RV
+scx_unload_pkcs11_module(scx_pkcs11_module_t *mod)
+{
+ if (!mod || mod->_magic != SCX_MAGIC)
+ return CKR_ARGUMENTS_BAD;
+
+ if (dlclose(mod->handle) < 0)
+ return CKR_FUNCTION_FAILED;
+
+ memset(mod, 0, sizeof(*mod));
+ pfree(mod);
+ return CKR_OK;
+}
+
+static scx_pkcs11_module_t*
+scx_load_pkcs11_module(const char *name, CK_FUNCTION_LIST_PTR_PTR funcs)
+{
+ CK_RV (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR);
+ scx_pkcs11_module_t *mod;
+ void *handle;
+ int rv;
+
+ if (name == NULL || *name == '\0')
+ return NULL;
+
+ /* Try to load PKCS#11 library module*/
+ handle = dlopen(name, RTLD_NOW);
+ if (handle == NULL)
+ return NULL;
+
+ mod = alloc_thing(scx_pkcs11_module_t, "scx_pkcs11_module");
+ mod->_magic = SCX_MAGIC;
+ mod->handle = handle;
+
+ /* Get the list of function pointers */
+ c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))
+ dlsym(mod->handle, "C_GetFunctionList");
+ if (!c_get_function_list)
+ goto failed;
+
+ rv = c_get_function_list(funcs);
+ if (rv == CKR_OK)
+ return mod;
+
+failed: scx_unload_pkcs11_module(mod);
+ return NULL;
+}
+
+/*
+ * retrieve a certificate object
+ */
+static bool
+scx_find_cert_object(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object
+, smartcard_t *sc, cert_t *cert)
+{
+ size_t hex_len, label_len;
+ u_char *hex_id = NULL;
+ chunk_t blob;
+ x509cert_t *x509cert;
+
+ CK_ATTRIBUTE attr[] = {
+ { CKA_ID, NULL_PTR, 0L },
+ { CKA_LABEL, NULL_PTR, 0L },
+ { CKA_VALUE, NULL_PTR, 0L }
+ };
+
+ /* initialize the return argument */
+ *cert = empty_cert;
+
+ /* get the length of the attributes first */
+ CK_RV rv = pkcs11_functions->C_GetAttributeValue(session, object, attr, 3);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read the attribute sizes: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ pfreeany(sc->label);
+
+ hex_id = alloc_bytes(attr[0].ulValueLen, "hex id");
+ hex_len = attr[0].ulValueLen;
+ sc->label = alloc_bytes(attr[1].ulValueLen + 1, "sc label");
+ label_len = attr[1].ulValueLen;
+ blob.ptr = alloc_bytes(attr[2].ulValueLen, "x509cert blob");
+ blob.len = attr[2].ulValueLen;
+
+ attr[0].pValue = hex_id;
+ attr[1].pValue = sc->label;
+ attr[2].pValue = blob.ptr;
+
+ /* now get the attributes */
+ rv = pkcs11_functions->C_GetAttributeValue(session, object, attr, 3);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read the attributes: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ pfree(hex_id);
+ pfreeany(sc->label);
+ pfree(blob.ptr);
+ return FALSE;
+ }
+
+ pfreeany(sc->id);
+
+ /* convert id from hex to ASCII */
+ sc->id = alloc_bytes(2*hex_len + 1, " sc id");
+ datatot(hex_id, hex_len, 16, sc->id, 2*hex_len + 1);
+ pfree(hex_id);
+
+ /* safeguard in case the label is not null terminated */
+ sc->label[label_len] = '\0';
+
+ /* parse the retrieved cert */
+ x509cert = alloc_thing(x509cert_t, "x509cert");
+ *x509cert = empty_x509cert;
+ x509cert->smartcard = TRUE;
+
+ if (!parse_x509cert(blob, 0, x509cert))
+ {
+ plog("failed to load cert from smartcard, error in X.509 certificate");
+ free_x509cert(x509cert);
+ return FALSE;
+ }
+ cert->type = CERT_X509_SIGNATURE;
+ cert->u.x509 = x509cert;
+ return TRUE;
+}
+
+/*
+ * search a given slot for PKCS#11 certificate objects
+ */
+static void
+scx_find_cert_objects(CK_SLOT_ID slot, CK_SESSION_HANDLE session)
+{
+ CK_RV rv;
+ CK_OBJECT_CLASS class = CKO_CERTIFICATE;
+ CK_ATTRIBUTE attr[] = {{ CKA_CLASS, &class, sizeof(class) }};
+
+ rv = pkcs11_functions->C_FindObjectsInit(session, attr, 1);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjectsInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return;
+ }
+
+ for (;;)
+ {
+ CK_OBJECT_HANDLE object;
+ CK_ULONG obj_count = 0;
+ err_t ugh;
+ time_t valid_until;
+ smartcard_t *sc;
+ x509cert_t *cert;
+
+ rv = pkcs11_functions->C_FindObjects(session, &object, 1, &obj_count);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjects: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ break;
+ }
+
+ /* no objects left */
+ if (obj_count == 0)
+ break;
+
+ /* create and initialize a new smartcard object */
+ sc = alloc_thing(smartcard_t, "smartcard");
+ *sc = empty_sc;
+ sc->any_slot = FALSE;
+ sc->slot = slot;
+
+ if (!scx_find_cert_object(session, object, sc, &sc->last_cert))
+ {
+ scx_free(sc);
+ continue;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("found cert in %s with id: %s, label: '%s'"
+ , scx_print_slot(sc, ""), sc->id, sc->label)
+ )
+
+ /* check validity of certificate */
+ cert = sc->last_cert.u.x509;
+ valid_until = cert->notAfter;
+ ugh = check_validity(cert, &valid_until);
+ if (ugh != NULL)
+ {
+ plog(" %s", ugh);
+ free_x509cert(cert);
+ scx_free(sc);
+ continue;
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log(" certificate is valid")
+ )
+ }
+
+ sc = scx_add(sc);
+
+ /* put end entity and ca certificates into different chains */
+ if (cert->isCA)
+ add_authcert(cert, AUTH_CA);
+ else
+ {
+ add_x509_public_key(cert, valid_until, DAL_LOCAL);
+ sc->last_cert.u.x509 = add_x509cert(cert);
+ }
+
+ share_cert(sc->last_cert);
+ time(&sc->last_load);
+ }
+
+ rv = pkcs11_functions->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjectsFinal: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ }
+}
+
+/*
+ * search all slots for PKCS#11 certificate objects
+ */
+static void
+scx_find_all_cert_objects(void)
+{
+ CK_RV rv;
+ CK_SLOT_ID_PTR slots = NULL_PTR;
+ CK_ULONG slot_count = 0;
+ CK_ULONG i;
+
+ if (!scx_initialized)
+ {
+ plog("pkcs11 module not initialized");
+ return;
+ }
+
+ /* read size, always returns CKR_OK ! */
+ rv = pkcs11_functions->C_GetSlotList(FALSE, NULL_PTR, &slot_count);
+
+ /* allocate memory for the slots */
+ slots = (CK_SLOT_ID *)alloc_bytes(slot_count * sizeof(CK_SLOT_ID), "slots");
+
+ rv = pkcs11_functions->C_GetSlotList(FALSE, slots, &slot_count);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_GetSlotList: %s", enum_show(&pkcs11_return_names, rv));
+ pfreeany(slots);
+ return;
+ }
+
+ /* look in every slot for certificate objects */
+ for (i = 0; i < slot_count; i++)
+ {
+ CK_SLOT_ID slot = slots[i];
+ CK_SLOT_INFO info;
+ CK_SESSION_HANDLE session;
+
+ rv = pkcs11_functions->C_GetSlotInfo(slot, &info);
+
+ if (rv != CKR_OK)
+ {
+ plog("error in C_GetSlotInfo: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ continue;
+ }
+
+ if (!(info.flags & CKF_TOKEN_PRESENT))
+ {
+ plog("no token present in slot %lu", slot);
+ continue;
+ }
+
+ rv = pkcs11_functions->C_OpenSession(slot
+ , CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session);
+ if (rv != CKR_OK)
+ {
+ plog("failed to open a session on slot %lu: %s"
+ , slot, enum_show(&pkcs11_return_names, rv));
+ continue;
+ }
+ DBG(DBG_CONTROLMORE,
+ DBG_log("pkcs11 session #%ld for searching slot %lu", session, slot)
+ )
+ scx_find_cert_objects(slot, session);
+
+ rv = pkcs11_functions->C_CloseSession(session);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_CloseSession: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ }
+ }
+ pfreeany(slots);
+}
+#endif
+
+/*
+ * load and initialize PKCS#11 cryptoki module
+ */
+void
+scx_init(const char* module)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+
+ if (scx_initialized)
+ {
+ plog("weird - pkcs11 module seems already to be initialized");
+ return;
+ }
+
+ if (module == NULL)
+#ifdef PKCS11_DEFAULT_LIB
+ module = PKCS11_DEFAULT_LIB;
+#else
+ {
+ plog("no pkcs11 module defined");
+ return;
+ }
+#endif
+
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 module '%s' loading...", module)
+ )
+ pkcs11_module = scx_load_pkcs11_module(module, &pkcs11_functions);
+ if (pkcs11_module == NULL)
+ {
+ plog("failed to load pkcs11 module '%s'", module);
+ return;
+ }
+
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 module initializing...")
+ )
+ rv = pkcs11_functions->C_Initialize(NULL);
+ if (rv != CKR_OK)
+ {
+ plog("failed to initialize pkcs11 module: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return;
+ }
+
+ scx_initialized = TRUE;
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 module loaded and initialized")
+ )
+
+ scx_find_all_cert_objects();
+#endif
+}
+
+/*
+ * finalize and unload PKCS#11 cryptoki module
+ */
+void
+scx_finalize(void)
+{
+#ifdef SMARTCARD
+ while (smartcards != NULL)
+ {
+ scx_release(smartcards);
+ }
+
+ if (pkcs11_functions != NULL_PTR)
+ {
+ pkcs11_functions->C_Finalize(NULL_PTR);
+ pkcs11_functions = NULL_PTR;
+ }
+
+ if (pkcs11_module != NULL)
+ {
+ scx_unload_pkcs11_module(pkcs11_module);
+ pkcs11_module = NULL;
+ }
+
+ scx_initialized = FALSE;
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 module finalized and unloaded")
+ )
+#endif
+}
+
+/*
+ * does a filename contain the token %smartcard?
+ */
+bool
+scx_on_smartcard(const char *filename)
+{
+ return strncmp(filename, SCX_TOKEN, strlen(SCX_TOKEN)) == 0;
+}
+
+#ifdef SMARTCARD
+/*
+ * find a specific object on the smartcard
+ */
+static bool
+scx_pkcs11_find_object( CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE_PTR object,
+ CK_OBJECT_CLASS class,
+ const char* id)
+{
+ size_t len;
+ char buf[BUF_LEN];
+ CK_RV rv;
+ CK_ULONG obj_count = 0;
+ CK_ULONG attr_count = 1;
+
+ CK_ATTRIBUTE attr[] = {
+ { CKA_CLASS, &class, sizeof(class) },
+ { CKA_ID, &buf, 0L }
+ };
+
+ if (id != NULL)
+ {
+ ttodata(id, strlen(id), 16, buf, BUF_LEN, &len);
+ attr[1].ulValueLen = len;
+ attr_count = 2;
+ }
+
+ /* get info for certificate with id */
+ rv = pkcs11_functions->C_FindObjectsInit(session, attr, attr_count);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjectsInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_FindObjects(session, object, 1, &obj_count);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjects: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_FindObjectsFinal(session);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_FindObjectsFinal: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ return (obj_count != 0);
+}
+
+/*
+ * check if a given certificate object id is found in a slot
+ */
+static bool
+scx_find_cert_id_in_slot(smartcard_t *sc, CK_SLOT_ID slot)
+{
+ CK_SESSION_HANDLE session;
+ CK_OBJECT_HANDLE object;
+ CK_SLOT_INFO info;
+
+ CK_RV rv = pkcs11_functions->C_GetSlotInfo(slot, &info);
+
+ if (rv != CKR_OK)
+ {
+ plog("error in C_GetSlotInfo: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ if (!(info.flags & CKF_TOKEN_PRESENT))
+ {
+ plog("no token present in slot %lu", slot);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_OpenSession(slot
+ , CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session);
+ if (rv != CKR_OK)
+ {
+ plog("failed to open a session on slot %lu: %s"
+ , slot, enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+ DBG(DBG_CONTROLMORE,
+ DBG_log("pkcs11 session #%ld for searching slot %lu", session, slot)
+ )
+
+ /* check if there is a certificate on the card in the specified slot */
+ if (scx_pkcs11_find_object(session, &object, CKO_CERTIFICATE, sc->id))
+ {
+ sc->slot = slot;
+ sc->any_slot = FALSE;
+ sc->session = session;
+ sc->session_opened = TRUE;
+ return TRUE;
+ }
+
+ rv = pkcs11_functions->C_CloseSession(session);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_CloseSession: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ }
+ return FALSE;
+}
+#endif
+
+/*
+ * Connect to the smart card in the reader and select the correct slot
+ */
+bool
+scx_establish_context(smartcard_t *sc)
+{
+#ifdef SMARTCARD
+ bool id_found = FALSE;
+
+ if (!scx_initialized)
+ {
+ plog("pkcs11 module not initialized");
+ return FALSE;
+ }
+
+ if (sc->session_opened)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 session #%ld already open", sc->session)
+ )
+ return TRUE;
+ }
+
+ if (!sc->any_slot)
+ id_found = scx_find_cert_id_in_slot(sc, sc->slot);
+
+ if (!id_found)
+ {
+ CK_RV rv;
+ CK_SLOT_ID slot;
+ CK_SLOT_ID_PTR slots = NULL_PTR;
+ CK_ULONG slot_count = 0;
+ CK_ULONG i;
+
+ /* read size, always returns CKR_OK ! */
+ rv = pkcs11_functions->C_GetSlotList(FALSE, NULL_PTR, &slot_count);
+
+ /* allocate memory for the slots */
+ slots = (CK_SLOT_ID *)alloc_bytes(slot_count * sizeof(CK_SLOT_ID), "slots");
+
+ rv = pkcs11_functions->C_GetSlotList(FALSE, slots, &slot_count);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_GetSlotList: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ pfreeany(slots);
+ return FALSE;
+ }
+
+ /* look in every slot for a certificate with a given object ID */
+ for (i = 0; i < slot_count; i++)
+ {
+ slot = slots[i];
+ id_found = scx_find_cert_id_in_slot(sc, slot);
+ if (id_found)
+ break;
+ }
+ pfreeany(slots)
+ }
+
+ if (id_found)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("found token with id %s in slot %lu", sc->id, sc->slot);
+ DBG_log("pkcs11 session #%ld opened", sc->session)
+ )
+ }
+ else
+ {
+ plog(" no certificate with id %s found on smartcard", sc->id);
+ }
+ return id_found;
+#else
+ plog("warning: SMARTCARD support is deactivated in pluto/Makefile!");
+ return FALSE;
+#endif
+}
+
+/*
+ * log in to a session
+ */
+bool
+scx_login(smartcard_t *sc)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+
+ if (sc->logged_in)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 session #%ld login already done", sc->session)
+ )
+ return TRUE;
+ }
+
+ if (sc->pin.ptr == NULL)
+ {
+ plog("unable to log in without PIN!");
+ return FALSE;
+ }
+
+ if (!sc->session_opened)
+ {
+ plog("session not opened");
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_Login(sc->session, CKU_USER
+ , (CK_UTF8CHAR *) sc->pin.ptr, sc->pin.len);
+ if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN)
+ {
+ plog("unable to login: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 session #%ld login successful", sc->session)
+ )
+ sc->logged_in = TRUE;
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+#ifdef SMARTCARD
+/*
+ * logout from a session
+ */
+static void
+scx_logout(smartcard_t *sc)
+{
+ CK_RV rv;
+
+ rv = pkcs11_functions->C_Logout(sc->session);
+ if (rv != CKR_OK)
+ plog("error in C_Logout: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ else
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 session #%ld logout", sc->session)
+ )
+ sc->logged_in = FALSE;
+}
+#endif
+
+
+/*
+ * Release context and disconnect from card
+ */
+void
+scx_release_context(smartcard_t *sc)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+
+ if (!scx_initialized)
+ return;
+
+ if (sc->session_opened)
+ {
+ if (sc->logged_in)
+ scx_logout(sc);
+
+ sc->session_opened = FALSE;
+
+ rv = pkcs11_functions->C_CloseSession(sc->session);
+ if (rv != CKR_OK)
+ plog("error in C_CloseSession: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ else
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("pkcs11 session #%ld closed", sc->session)
+ )
+ }
+#endif
+}
+
+/*
+ * Load host certificate from smartcard
+ */
+bool
+scx_load_cert(const char *filename, smartcard_t **scp, cert_t *cert
+, bool *cached)
+{
+#ifdef SMARTCARD /* compile with smartcard support */
+ CK_OBJECT_HANDLE object;
+
+ const char *number_slot_id = filename + strlen(SCX_TOKEN);
+
+ smartcard_t *sc = scx_add(scx_parse_number_slot_id(number_slot_id));
+
+ /* return the smartcard object */
+ *scp = sc;
+
+ /* is there a cached smartcard certificate? */
+ *cached = sc->last_cert.type != CERT_NONE
+ && (time(NULL) - sc->last_load) < SCX_CERT_CACHE_INTERVAL;
+
+ if (*cached)
+ {
+ *cert = sc->last_cert;
+ plog(" using cached cert from smartcard #%d (%s, id: %s, label: '%s')"
+ , sc->number
+ , scx_print_slot(sc, "")
+ , sc->id
+ , sc->label);
+ return TRUE;
+ }
+
+ if (!scx_establish_context(sc))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ /* find the certificate object */
+ if (!scx_pkcs11_find_object(sc->session, &object, CKO_CERTIFICATE, sc->id))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ /* retrieve the certificate object */
+ if (!scx_find_cert_object(sc->session, object, sc, cert))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+
+ plog(" loaded cert from smartcard #%d (%s, id: %s, label: '%s')"
+ , sc->number
+ , scx_print_slot(sc, "")
+ , sc->id
+ , sc->label);
+
+ return TRUE;
+#else
+ plog(" warning: SMARTCARD support is deactivated in pluto/Makefile!");
+ return FALSE;
+#endif
+}
+
+/*
+ * parse slot number and key id
+ * the following syntax is allowed
+ * number slot id
+ * %smartcard 1 - -
+ * %smartcard#2 2 - -
+ * %smartcard0 - 0 -
+ * %smartcard:45 - - 45
+ * %smartcard0:45 - 0 45
+ */
+smartcard_t*
+scx_parse_number_slot_id(const char *number_slot_id)
+{
+ int len = strlen(number_slot_id);
+ smartcard_t *sc = alloc_thing(smartcard_t, "smartcard");
+
+ /* assign default values */
+ *sc = empty_sc;
+
+ if (len == 0) /* default: use certificate #1 */
+ {
+ sc->number = 1;
+ }
+ else if (*number_slot_id == '#') /* #number scheme */
+ {
+ err_t ugh;
+ unsigned long ul;
+
+ ugh = atoul(number_slot_id+1, len-1 , 10, &ul);
+ if (ugh == NULL)
+ sc->number = (int)ul;
+ else
+ plog("error parsing smartcard number: %s", ugh);
+ }
+ else /* slot:id scheme */
+ {
+ int slot_len = len;
+ char *p = strchr(number_slot_id, ':');
+
+ if (p != NULL)
+ {
+ int id_len = len - (p + 1 - number_slot_id);
+ slot_len -= (1 + id_len);
+
+ if (id_len > 0) /* we have an id */
+ sc->id = p + 1;
+ }
+ if (slot_len > 0) /* we have a slot */
+ {
+ err_t ugh = NULL;
+ unsigned long ul;
+
+ ugh = atoul(number_slot_id, slot_len, 10, &ul);
+ if (ugh == NULL)
+ {
+ sc->slot = ul;
+ sc->any_slot = FALSE;
+ }
+ else
+ plog("error parsing smartcard slot number: %s", ugh);
+ }
+ }
+ /* unshare the id string */
+ sc->id = clone_str(sc->id, "key id");
+ return sc;
+}
+
+/*
+ * Verify pin on card
+ */
+bool
+scx_verify_pin(smartcard_t *sc)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+
+ if (!sc->pinpad)
+ sc->valid = FALSE;
+
+ if (sc->pin.ptr == NULL)
+ {
+ plog("unable to verify without PIN");
+ return FALSE;
+ }
+
+ /* establish context */
+ if (!scx_establish_context(sc))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_Login(sc->session, CKU_USER,
+ (CK_UTF8CHAR *) sc->pin.ptr, sc->pin.len);
+ if (rv == CKR_OK || rv == CKR_USER_ALREADY_LOGGED_IN)
+ {
+ sc->valid = TRUE;
+ sc->logged_in = TRUE;
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log((rv == CKR_OK)
+ ? "PIN code correct"
+ : "already logged in, no PIN entry required");
+ DBG_log("pkcs11 session #%ld login successful", sc->session)
+ )
+ }
+ else
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("PIN code incorrect")
+ )
+ }
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+#else
+ sc->valid = FALSE;
+#endif
+ return sc->valid;
+}
+
+/*
+ * Sign hash on smartcard
+ */
+bool
+scx_sign_hash(smartcard_t *sc, const u_char *in, size_t inlen
+, u_char *out, size_t outlen)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+ CK_OBJECT_HANDLE object;
+ CK_ULONG siglen = (CK_ULONG)outlen;
+ CK_BBOOL sign_flag, decrypt_flag;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_SIGN, &sign_flag, sizeof(sign_flag) },
+ { CKA_DECRYPT, &decrypt_flag, sizeof(decrypt_flag) }
+ };
+
+ if (!sc->logged_in)
+ return FALSE;
+
+ if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id))
+ {
+ plog("unable to find private key with id '%s'", sc->id);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 2);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read the private key attributes: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("RSA key flags: sign = %s, decrypt = %s"
+ , (sign_flag)? "true":"false"
+ , (decrypt_flag)? "true":"false")
+ )
+
+ if (sign_flag)
+ {
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 };
+
+ rv = pkcs11_functions->C_SignInit(sc->session, &mech, object);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_SignInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_Sign(sc->session, (CK_BYTE_PTR)in, inlen
+ , out, &siglen);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_Sign: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+ }
+ else if (decrypt_flag)
+ {
+ CK_MECHANISM mech = { CKM_RSA_X_509, NULL_PTR, 0 };
+ size_t padlen;
+ u_char *p = out ;
+
+ /* PKCS#1 v1.5 8.1 encryption-block formatting */
+ *p++ = 0x00;
+ *p++ = 0x01; /* BT (block type) 01 */
+ padlen = outlen - 3 - inlen;
+ memset(p, 0xFF, padlen);
+ p += padlen;
+ *p++ = 0x00;
+ memcpy(p, in, inlen);
+
+ rv = pkcs11_functions->C_DecryptInit(sc->session, &mech, object);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_DecryptInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_Decrypt(sc->session, out, outlen
+ , out, &siglen);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_Decrypt: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+ }
+ else
+ {
+ plog("private key has neither sign nor decrypt flag set");
+ return FALSE;
+ }
+
+ if (siglen > (CK_ULONG)outlen)
+ {
+ plog("signature length (%lu) larger than allocated buffer (%d)"
+ , siglen, (int)outlen);
+ return FALSE;
+ }
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*
+ * encrypt data block with an RSA public key
+ */
+bool
+scx_encrypt(smartcard_t *sc, const u_char *in, size_t inlen
+, u_char *out, size_t *outlen)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+ CK_OBJECT_HANDLE object;
+ CK_ULONG len = (CK_ULONG)(*outlen);
+ CK_BBOOL encrypt_flag;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_MODULUS, NULL_PTR, 0L },
+ { CKA_PUBLIC_EXPONENT, NULL_PTR, 0L },
+ { CKA_ENCRYPT, &encrypt_flag, sizeof(encrypt_flag) }
+ };
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 };
+
+ if (!scx_establish_context(sc))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ if (!scx_pkcs11_find_object(sc->session, &object, CKO_PUBLIC_KEY, sc->id))
+ {
+ plog("unable to find public key with id '%s'", sc->id);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 3);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read the public key attributes: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ if (!encrypt_flag)
+ {
+ plog("public key cannot be used for encryption");
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ /* there must be enough space left for the PKCS#1 v1.5 padding */
+ if (inlen > attr[0].ulValueLen - 11)
+ {
+ plog("smartcard input data length (%d) exceeds maximum of %lu bytes"
+ , (int)inlen, attr[0].ulValueLen - 11);
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_EncryptInit(sc->session, &mech, object);
+
+ if (rv != CKR_OK)
+ {
+ if (rv == CKR_FUNCTION_NOT_SUPPORTED)
+ {
+ RSA_public_key_t rsa;
+ chunk_t plain_text = {in, inlen};
+ chunk_t cipher_text;
+
+ DBG(DBG_CONTROL,
+ DBG_log("doing RSA encryption in software")
+ )
+ attr[0].pValue = alloc_bytes(attr[0].ulValueLen, "modulus");
+ attr[1].pValue = alloc_bytes(attr[1].ulValueLen, "exponent");
+
+ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 2);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read modulus and public exponent: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ pfree(attr[0].pValue);
+ pfree(attr[1].pValue);
+ scx_release_context(sc);
+ return FALSE;
+ }
+ rsa.k = attr[0].ulValueLen;
+ n_to_mpz(&rsa.n, attr[0].pValue, attr[0].ulValueLen);
+ n_to_mpz(&rsa.e, attr[1].pValue, attr[1].ulValueLen);
+ pfree(attr[0].pValue);
+ pfree(attr[1].pValue);
+
+ cipher_text = RSA_encrypt(&rsa, plain_text);
+ free_RSA_public_content(&rsa);
+ if (cipher_text.ptr == NULL)
+ {
+ plog("smartcard input data length is too large");
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ memcpy(out, cipher_text.ptr, cipher_text.len);
+ *outlen = cipher_text.len;
+ freeanychunk(cipher_text);
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+ return TRUE;
+ }
+ else
+ {
+ plog("error in C_EncryptInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ scx_release_context(sc);
+ return FALSE;
+ }
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("doing RSA encryption on smartcard")
+ )
+ rv = pkcs11_functions->C_Encrypt(sc->session, in, inlen
+ , out, &len);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_Encrypt: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ scx_release_context(sc);
+ return FALSE;
+ }
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+
+ *outlen = (size_t)len;
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+/*
+ * decrypt a data block with an RSA private key
+ */
+bool
+scx_decrypt(smartcard_t *sc, const u_char *in, size_t inlen
+, u_char *out, size_t *outlen)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+ CK_OBJECT_HANDLE object;
+ CK_ULONG len = (CK_ULONG)(*outlen);
+ CK_BBOOL decrypt_flag;
+ CK_ATTRIBUTE attr[] = {
+ { CKA_DECRYPT, &decrypt_flag, sizeof(decrypt_flag) }
+ };
+ CK_MECHANISM mech = { CKM_RSA_PKCS, NULL_PTR, 0 };
+
+ if (!scx_establish_context(sc) || !scx_login(sc))
+ {
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id))
+ {
+ plog("unable to find private key with id '%s'", sc->id);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object, attr, 1);
+ if (rv != CKR_OK)
+ {
+ plog("couldn't read the private key attributes: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ if (!decrypt_flag)
+ {
+ plog("private key cannot be used for decryption");
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("doing RSA decryption on smartcard")
+ )
+ rv = pkcs11_functions->C_DecryptInit(sc->session, &mech, object);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_DecryptInit: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ scx_release_context(sc);
+ return FALSE;
+ }
+
+ rv = pkcs11_functions->C_Decrypt(sc->session, in, inlen
+ , out, &len);
+ if (rv != CKR_OK)
+ {
+ plog("error in C_Decrypt: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ scx_release_context(sc);
+ return FALSE;
+ }
+ if (!pkcs11_keep_state)
+ scx_release_context(sc);
+
+ *outlen = (size_t)len;
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/* receive an encrypted data block via whack,
+ * decrypt it using a private RSA key and
+ * return the decrypted data block via whack
+ */
+bool
+scx_op_via_whack(const char* msg, int inbase, int outbase, sc_op_t op
+, const char* keyid, int whackfd)
+{
+ char inbuf[RSA_MAX_OCTETS];
+ char outbuf[2*RSA_MAX_OCTETS + 1];
+ size_t outlen = sizeof(inbuf);
+ size_t inlen;
+ smartcard_t *sc,*sc_new;
+
+ const char *number_slot_id = "";
+
+ err_t ugh = ttodata(msg, 0, inbase, inbuf, sizeof(inbuf), &inlen);
+
+ /* no prefix - use default base */
+ if (ugh != NULL && inbase == 0)
+ ugh = ttodata(msg, 0, DEFAULT_BASE, inbuf, sizeof(inbuf), &inlen);
+
+ if (ugh != NULL)
+ {
+ plog("format error in smartcard input data: %s", ugh);
+ return FALSE;
+ }
+
+ if (keyid != NULL)
+ {
+ number_slot_id = (strncmp(keyid, SCX_TOKEN, strlen(SCX_TOKEN)) == 0)
+ ? keyid + strlen(SCX_TOKEN) : keyid;
+ }
+
+ sc_new = scx_parse_number_slot_id(number_slot_id);
+ sc = scx_add(sc_new);
+ if (sc == sc_new)
+ scx_share(sc);
+
+ DBG((op == SC_OP_ENCRYPT)? DBG_PRIVATE:DBG_RAW,
+ DBG_dump("smartcard input data:\n", inbuf, inlen)
+ )
+
+ if (op == SC_OP_DECRYPT)
+ {
+ if (!sc->valid && whackfd != NULL_FD)
+ scx_get_pin(sc, whackfd);
+
+ if (!sc->valid)
+ {
+ loglog(RC_NOVALIDPIN, "cannot decrypt without valid PIN");
+ return FALSE;
+ }
+ }
+
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ DBG_log("using RSA key from smartcard (slot: %d, id: %s)"
+ , (int)sc->slot, sc->id)
+ )
+
+ switch (op)
+ {
+ case SC_OP_ENCRYPT:
+ if (!scx_encrypt(sc, inbuf, inlen, inbuf, &outlen))
+ return FALSE;
+ break;
+ case SC_OP_DECRYPT:
+ if (!scx_decrypt(sc, inbuf, inlen, inbuf, &outlen))
+ return FALSE;
+ break;
+ default:
+ break;
+ }
+
+ DBG((op == SC_OP_DECRYPT)? DBG_PRIVATE:DBG_RAW,
+ DBG_dump("smartcard output data:\n", inbuf, outlen)
+ )
+
+ if (outbase == 0) /* use default base */
+ outbase = DEFAULT_BASE;
+
+ if (outbase == 256) /* ascii plain text */
+ whack_log(RC_COMMENT, "%.*s", (int)outlen, inbuf);
+ else
+ {
+ outlen = datatot(inbuf, outlen, outbase, outbuf, sizeof(outbuf));
+ if (outlen == 0)
+ {
+ plog("error in output format conversion");
+ return FALSE;
+ }
+ whack_log(RC_COMMENT, "%s", outbuf);
+ }
+ return TRUE;
+}
+
+ /*
+ * get length of RSA key in bytes
+ */
+size_t
+scx_get_keylength(smartcard_t *sc)
+{
+#ifdef SMARTCARD
+ CK_RV rv;
+ CK_OBJECT_HANDLE object;
+ CK_ATTRIBUTE attr[] = {{ CKA_MODULUS, NULL_PTR, 0}};
+
+ if (!sc->logged_in)
+ return FALSE;
+
+ if (!scx_pkcs11_find_object(sc->session, &object, CKO_PRIVATE_KEY, sc->id))
+ {
+ plog("unable to find private key with id '%s'", sc->id);
+ return FALSE;
+ }
+
+ /* get the length of the private key */
+ rv = pkcs11_functions->C_GetAttributeValue(sc->session, object
+ , (CK_ATTRIBUTE_PTR)&attr, 1);
+ if (rv != CKR_OK)
+ {
+ plog("failed to get key length: %s"
+ , enum_show(&pkcs11_return_names, rv));
+ return FALSE;
+ }
+
+ return attr[0].ulValueLen; /*Return key length in bytes */
+#else
+ return 0;
+#endif
+}
+
+/*
+ * prompt for pin and verify it
+ */
+bool
+scx_get_pin(smartcard_t *sc, int whackfd)
+{
+#ifdef SMARTCARD
+ char pin[BUF_LEN];
+ int i, n;
+
+ whack_log(RC_ENTERSECRET, "need PIN for #%d (%s, id: %s, label: '%s')"
+ , sc->number, scx_print_slot(sc, ""), sc->id, sc->label);
+
+ for (i = 0; i < SCX_MAX_PIN_TRIALS; i++)
+ {
+ if (i > 0)
+ whack_log(RC_ENTERSECRET, "invalid PIN, please try again");
+
+ n = read(whackfd, pin, BUF_LEN);
+
+ if (n == -1)
+ {
+ whack_log(RC_LOG_SERIOUS, "read(whackfd) failed");
+ return FALSE;
+ }
+
+ if (strlen(pin) == 0)
+ {
+ whack_log(RC_LOG_SERIOUS, "no PIN entered, aborted");
+ return FALSE;
+ }
+
+ sc->pin.ptr = pin;
+ sc->pin.len = strlen(pin);
+
+ /* verify the pin */
+ if (scx_verify_pin(sc))
+ {
+ clonetochunk(sc->pin, pin, strlen(pin), "pin");
+ break;
+ }
+
+ /* wrong pin - we try another round */
+ sc->pin = empty_chunk;
+ }
+
+ if (sc->valid)
+ whack_log(RC_SUCCESS, "valid PIN");
+ else
+ whack_log(RC_LOG_SERIOUS, "invalid PIN, too many trials");
+#else
+ sc->valid = FALSE;
+ whack_log(RC_LOG_SERIOUS, "SMARTCARD support is deactivated in pluto/Makefile!");
+#endif
+ return sc->valid;
+}
+
+
+/*
+ * free the pin code
+ */
+void
+scx_free_pin(chunk_t *pin)
+{
+ if (pin->ptr != NULL)
+ {
+ /* clear pin field in memory */
+ memset(pin->ptr, '\0', pin->len);
+ pfree(pin->ptr);
+ *pin = empty_chunk;
+ }
+}
+
+/*
+ * frees a smartcard record
+ */
+void
+scx_free(smartcard_t *sc)
+{
+ if (sc != NULL)
+ {
+ scx_release_context(sc);
+ pfreeany(sc->id);
+ pfreeany(sc->label);
+ scx_free_pin(&sc->pin);
+ pfree(sc);
+ }
+}
+
+/* release of a smartcard record decreases the count by one
+ " the record is freed when the counter reaches zero
+ */
+void
+scx_release(smartcard_t *sc)
+{
+ if (sc != NULL && --sc->count == 0)
+ {
+ smartcard_t **pp = &smartcards;
+ while (*pp != sc)
+ pp = &(*pp)->next;
+ *pp = sc->next;
+ release_cert(sc->last_cert);
+ scx_free(sc);
+ }
+}
+
+/*
+ * compare two smartcard records by comparing their slots and ids
+ */
+static bool
+scx_same(smartcard_t *a, smartcard_t *b)
+{
+ if (a->number && b->number)
+ {
+ /* same number */
+ return a->number == b->number;
+ }
+ else
+ {
+ /* same id and/or same slot */
+ return (!a->id || (b->id && streq(a->id, b->id)))
+ && (a->any_slot || b->any_slot || a->slot == b->slot);
+ }
+}
+
+/* for each link pointing to the smartcard record
+ " increase the count by one
+ */
+void
+scx_share(smartcard_t *sc)
+{
+ if (sc != NULL)
+ sc->count++;
+}
+
+/*
+ * adds a smartcard record to the chained list
+ */
+smartcard_t*
+scx_add(smartcard_t *smartcard)
+{
+ smartcard_t *sc = smartcards;
+ smartcard_t **psc = &smartcards;
+
+ while (sc != NULL)
+ {
+ if (scx_same(smartcard, sc)) /* already in chain, free smartcard record */
+ {
+ scx_free(smartcard);
+ return sc;
+ }
+ psc = &sc->next;
+ sc = sc->next;
+ }
+
+ /* insert new smartcard record at the end of the chain */
+ *psc = smartcard;
+ smartcard->number = ++sc_number;
+ smartcard->count = 1;
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log(" smartcard #%d added", sc_number)
+ )
+ return smartcard;
+}
+
+/*
+ * get the smartcard that belongs to an X.509 certificate
+ */
+smartcard_t*
+scx_get(x509cert_t *cert)
+{
+ smartcard_t *sc = smartcards;
+
+ while (sc != NULL)
+ {
+ if (sc->last_cert.u.x509 == cert)
+ return sc;
+ sc = sc->next;
+ }
+ return NULL;
+}
+
+/*
+ * prints either the slot number or 'any slot'
+ */
+char *
+scx_print_slot(smartcard_t *sc, const char *whitespace)
+{
+ char *buf = temporary_cyclic_buffer();
+
+ if (sc->any_slot)
+ snprintf(buf, BUF_LEN, "any slot");
+ else
+ snprintf(buf, BUF_LEN, "slot: %s%lu", whitespace, sc->slot);
+ return buf;
+}
+
+/*
+ * list all smartcard info records in a chained list
+ */
+void
+scx_list(bool utc)
+{
+ smartcard_t *sc = smartcards;
+
+ if (sc != NULL)
+ {
+ whack_log(RC_COMMENT, " ");
+ whack_log(RC_COMMENT, "List of Smartcard Objects:");
+ whack_log(RC_COMMENT, " ");
+ }
+
+ while (sc != NULL)
+ {
+ whack_log(RC_COMMENT, "%s, #%d, count: %d"
+ , timetoa(&sc->last_load, utc)
+ , sc->number
+ , sc->count);
+ whack_log(RC_COMMENT, " %s, session %s, logged %s, has %s"
+ , scx_print_slot(sc, " ")
+ , sc->session_opened? "opened" : "closed"
+ , sc->logged_in? "in" : "out"
+ , sc->pinpad? "pin pad"
+ : ((sc->pin.ptr == NULL)? "no pin"
+ : sc->valid? "valid pin" : "invalid pin"));
+ if (sc->id != NULL)
+ whack_log(RC_COMMENT, " id: %s", sc->id);
+ if (sc->label != NULL)
+ whack_log(RC_COMMENT, " label: '%s'", sc->label);
+ if (sc->last_cert.type == CERT_X509_SIGNATURE)
+ {
+ char buf[BUF_LEN];
+
+ dntoa(buf, BUF_LEN, sc->last_cert.u.x509->subject);
+ whack_log(RC_COMMENT, " subject: '%s'", buf);
+ }
+ sc = sc->next;
+ }
+}
diff --git a/programs/pluto/smartcard.h b/programs/pluto/smartcard.h
new file mode 100644
index 000000000..c004ca7dd
--- /dev/null
+++ b/programs/pluto/smartcard.h
@@ -0,0 +1,100 @@
+/* Support of smartcards and cryptotokens
+ * Copyright (C) 2003 Christoph Gysin, Simon Zwahlen
+ * Copyright (C) 2004 David Buechi, Michael Meier
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: smartcard.h,v 1.14 2005/11/06 22:55:41 as Exp $
+ */
+
+#ifndef _SMARTCARD_H
+#define _SMARTCARD_H
+
+#include "certs.h"
+
+#define SCX_TOKEN "%smartcard"
+#define SCX_CERT_CACHE_INTERVAL 60 /* seconds */
+#define SCX_MAX_PIN_TRIALS 3
+
+/* smartcard operations */
+
+typedef enum {
+ SC_OP_NONE = 0,
+ SC_OP_ENCRYPT = 1,
+ SC_OP_DECRYPT = 2,
+ SC_OP_SIGN = 3,
+} sc_op_t;
+
+/* smartcard record */
+
+typedef struct smartcard smartcard_t;
+
+struct smartcard {
+ smartcard_t *next;
+ time_t last_load;
+ cert_t last_cert;
+ int count;
+ int number;
+ unsigned long slot;
+ char *id;
+ char *label;
+ chunk_t pin;
+ bool pinpad;
+ bool valid;
+ bool session_opened;
+ bool logged_in;
+ bool any_slot;
+ long session;
+};
+
+extern const smartcard_t empty_sc;
+
+/* keep a PKCS#11 login during the lifetime of pluto
+ * flag set in plutomain.c and used in ipsec_doi.c and ocsp.c
+ */
+extern bool pkcs11_keep_state;
+
+/* allow other applications access to pluto's PKCS#11 interface
+ * via whack. Could be used e.g. for disk encryption
+ */
+extern bool pkcs11_proxy;
+
+extern smartcard_t* scx_parse_number_slot_id(const char *number_slot_id);
+extern void scx_init(const char *module);
+extern void scx_finalize(void);
+extern bool scx_establish_context(smartcard_t *sc);
+extern bool scx_login(smartcard_t *sc);
+extern bool scx_on_smartcard(const char *filename);
+extern bool scx_load_cert(const char *filename, smartcard_t **scp
+ , cert_t *cert, bool *cached);
+extern bool scx_verify_pin(smartcard_t *sc);
+extern void scx_share(smartcard_t *sc);
+extern bool scx_sign_hash(smartcard_t *sc, const u_char *in, size_t inlen
+ , u_char *out, size_t outlen);
+extern bool scx_encrypt(smartcard_t *sc, const u_char *in, size_t inlen
+ , u_char *out, size_t *outlen);
+extern bool scx_decrypt(smartcard_t *sc, const u_char *in, size_t inlen
+ , u_char *out, size_t *outlen);
+extern bool scx_op_via_whack(const char* msg, int inbase, int outbase
+ , sc_op_t op, const char *keyid, int whackfd);
+extern bool scx_get_pin(smartcard_t *sc, int whackfd);
+extern size_t scx_get_keylength(smartcard_t *sc);
+extern smartcard_t* scx_add(smartcard_t *sc);
+extern smartcard_t* scx_get(x509cert_t *cert);
+extern void scx_release(smartcard_t *sc);
+extern void scx_release_context(smartcard_t *sc);
+extern void scx_free_pin(chunk_t *pin);
+extern void scx_free(smartcard_t *sc);
+extern void scx_list(bool utc);
+extern char *scx_print_slot(smartcard_t *sc, const char *whitespace);
+
+#endif /* _SMARTCARD_H */
diff --git a/programs/pluto/spdb.c b/programs/pluto/spdb.c
new file mode 100644
index 000000000..0544a1da2
--- /dev/null
+++ b/programs/pluto/spdb.c
@@ -0,0 +1,2402 @@
+/* Security Policy Data Base (such as it is)
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: spdb.c,v 1.9 2006/04/22 21:59:20 as Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "id.h"
+#include "connections.h"
+#include "state.h"
+#include "packet.h"
+#include "keys.h"
+#include "kernel.h"
+#include "log.h"
+#include "spdb.h"
+#include "whack.h" /* for RC_LOG_SERIOUS */
+
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h" /* requires sha1.h and md5.h */
+
+#include "alg_info.h"
+#include "kernel_alg.h"
+#include "ike_alg.h"
+#include "db_ops.h"
+#define AD(x) x, elemsof(x) /* Array Description */
+#define AD_NULL NULL, 0
+
+#ifdef NAT_TRAVERSAL
+#include "nat_traversal.h"
+#endif
+
+/**************** Oakely (main mode) SA database ****************/
+
+/* arrays of attributes for transforms, preshared key */
+
+static struct db_attr otpsk1024des3md5[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
+ };
+
+static struct db_attr otpsk1536des3md5[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
+ };
+
+static struct db_attr otpsk1024des3sha[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
+ };
+
+static struct db_attr otpsk1536des3sha[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
+ };
+
+/* arrays of attributes for transforms, RSA signatures */
+
+static struct db_attr otrsasig1024des3md5[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
+ };
+
+static struct db_attr otrsasig1536des3md5[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
+ };
+
+static struct db_attr otrsasig1024des3sha[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
+ };
+
+static struct db_attr otrsasig1536des3sha[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_SIG },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1536 },
+ };
+
+/* We won't accept this, but by proposing it, we get to test
+ * our rejection. We better not propose it to an IKE daemon
+ * that will accept it!
+ */
+#ifdef TEST_INDECENT_PROPOSAL
+static struct db_attr otpsk1024des3tiger[] = {
+ { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC },
+ { OAKLEY_HASH_ALGORITHM, OAKLEY_TIGER },
+ { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_PRESHARED_KEY },
+ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 },
+ };
+#endif /* TEST_INDECENT_PROPOSAL */
+
+/* tables of transforms, in preference order (select based on AUTH) */
+
+static struct db_trans oakley_trans_psk[] = {
+#ifdef TEST_INDECENT_PROPOSAL
+ { KEY_IKE, AD(otpsk1024des3tiger) },
+#endif
+ { KEY_IKE, AD(otpsk1536des3md5) },
+ { KEY_IKE, AD(otpsk1536des3sha) },
+ { KEY_IKE, AD(otpsk1024des3sha) },
+ { KEY_IKE, AD(otpsk1024des3md5) },
+ };
+
+static struct db_trans oakley_trans_rsasig[] = {
+ { KEY_IKE, AD(otrsasig1536des3md5) },
+ { KEY_IKE, AD(otrsasig1536des3sha) },
+ { KEY_IKE, AD(otrsasig1024des3sha) },
+ { KEY_IKE, AD(otrsasig1024des3md5) },
+ };
+
+/* In this table, either PSK or RSA sig is accepted.
+ * The order matters, but I don't know what would be best.
+ */
+static struct db_trans oakley_trans_pskrsasig[] = {
+#ifdef TEST_INDECENT_PROPOSAL
+ { KEY_IKE, AD(otpsk1024des3tiger) },
+#endif
+ { KEY_IKE, AD(otrsasig1536des3md5) },
+ { KEY_IKE, AD(otpsk1536des3md5) },
+ { KEY_IKE, AD(otrsasig1536des3sha) },
+ { KEY_IKE, AD(otpsk1536des3sha) },
+ { KEY_IKE, AD(otrsasig1024des3sha) },
+ { KEY_IKE, AD(otpsk1024des3sha) },
+ { KEY_IKE, AD(otrsasig1024des3md5) },
+ { KEY_IKE, AD(otpsk1024des3md5) },
+ };
+
+/* array of proposals to be conjoined (can only be one for Oakley) */
+
+static struct db_prop oakley_pc_psk[] =
+ { { PROTO_ISAKMP, AD(oakley_trans_psk) } };
+
+static struct db_prop oakley_pc_rsasig[] =
+ { { PROTO_ISAKMP, AD(oakley_trans_rsasig) } };
+
+static struct db_prop oakley_pc_pskrsasig[] =
+ { { PROTO_ISAKMP, AD(oakley_trans_pskrsasig) } };
+
+/* array of proposal conjuncts (can only be one) */
+
+static struct db_prop_conj oakley_props_psk[] = { { AD(oakley_pc_psk) } };
+
+static struct db_prop_conj oakley_props_rsasig[] = { { AD(oakley_pc_rsasig) } };
+
+static struct db_prop_conj oakley_props_pskrsasig[] = { { AD(oakley_pc_pskrsasig) } };
+
+/* the sadb entry, subscripted by POLICY_PSK and POLICY_RSASIG bits */
+struct db_sa oakley_sadb[] = {
+ { AD_NULL }, /* none */
+ { AD(oakley_props_psk) }, /* POLICY_PSK */
+ { AD(oakley_props_rsasig) }, /* POLICY_RSASIG */
+ { AD(oakley_props_pskrsasig) }, /* POLICY_PSK + POLICY_RSASIG */
+ };
+
+/**************** IPsec (quick mode) SA database ****************/
+
+/* arrays of attributes for transforms */
+
+static struct db_attr espmd5_attr[] = {
+ { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_MD5 },
+ };
+
+static struct db_attr espsha1_attr[] = {
+ { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_SHA1 },
+ };
+
+static struct db_attr ah_HMAC_MD5_attr[] = {
+ { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_MD5 },
+ };
+
+static struct db_attr ah_HMAC_SHA1_attr[] = {
+ { AUTH_ALGORITHM, AUTH_ALGORITHM_HMAC_SHA1 },
+ };
+
+/* arrays of transforms, each in in preference order */
+
+static struct db_trans espa_trans[] = {
+ { ESP_3DES, AD(espmd5_attr) },
+ { ESP_3DES, AD(espsha1_attr) },
+ };
+
+static struct db_trans esp_trans[] = {
+ { ESP_3DES, AD_NULL },
+ };
+
+#ifdef SUPPORT_ESP_NULL
+static struct db_trans espnull_trans[] = {
+ { ESP_NULL, AD(espmd5_attr) },
+ { ESP_NULL, AD(espsha1_attr) },
+ };
+#endif /* SUPPORT_ESP_NULL */
+
+static struct db_trans ah_trans[] = {
+ { AH_MD5, AD(ah_HMAC_MD5_attr) },
+ { AH_SHA, AD(ah_HMAC_SHA1_attr) },
+ };
+
+static struct db_trans ipcomp_trans[] = {
+ { IPCOMP_DEFLATE, AD_NULL },
+ };
+
+/* arrays of proposals to be conjoined */
+
+static struct db_prop ah_pc[] = {
+ { PROTO_IPSEC_AH, AD(ah_trans) },
+ };
+
+#ifdef SUPPORT_ESP_NULL
+static struct db_prop espnull_pc[] = {
+ { PROTO_IPSEC_ESP, AD(espnull_trans) },
+ };
+#endif /* SUPPORT_ESP_NULL */
+
+static struct db_prop esp_pc[] = {
+ { PROTO_IPSEC_ESP, AD(espa_trans) },
+ };
+
+static struct db_prop ah_esp_pc[] = {
+ { PROTO_IPSEC_AH, AD(ah_trans) },
+ { PROTO_IPSEC_ESP, AD(esp_trans) },
+ };
+
+static struct db_prop compress_pc[] = {
+ { PROTO_IPCOMP, AD(ipcomp_trans) },
+ };
+
+static struct db_prop ah_compress_pc[] = {
+ { PROTO_IPSEC_AH, AD(ah_trans) },
+ { PROTO_IPCOMP, AD(ipcomp_trans) },
+ };
+
+#ifdef SUPPORT_ESP_NULL
+static struct db_prop espnull_compress_pc[] = {
+ { PROTO_IPSEC_ESP, AD(espnull_trans) },
+ { PROTO_IPCOMP, AD(ipcomp_trans) },
+ };
+#endif /* SUPPORT_ESP_NULL */
+
+static struct db_prop esp_compress_pc[] = {
+ { PROTO_IPSEC_ESP, AD(espa_trans) },
+ { PROTO_IPCOMP, AD(ipcomp_trans) },
+ };
+
+static struct db_prop ah_esp_compress_pc[] = {
+ { PROTO_IPSEC_AH, AD(ah_trans) },
+ { PROTO_IPSEC_ESP, AD(esp_trans) },
+ { PROTO_IPCOMP, AD(ipcomp_trans) },
+ };
+
+/* arrays of proposal alternatives (each element is a conjunction) */
+
+static struct db_prop_conj ah_props[] = {
+ { AD(ah_pc) },
+#ifdef SUPPORT_ESP_NULL
+ { AD(espnull_pc) }
+#endif
+ };
+
+static struct db_prop_conj esp_props[] =
+ { { AD(esp_pc) } };
+
+static struct db_prop_conj ah_esp_props[] =
+ { { AD(ah_esp_pc) } };
+
+static struct db_prop_conj compress_props[] = {
+ { AD(compress_pc) },
+ };
+
+static struct db_prop_conj ah_compress_props[] = {
+ { AD(ah_compress_pc) },
+#ifdef SUPPORT_ESP_NULL
+ { AD(espnull_compress_pc) }
+#endif
+ };
+
+static struct db_prop_conj esp_compress_props[] =
+ { { AD(esp_compress_pc) } };
+
+static struct db_prop_conj ah_esp_compress_props[] =
+ { { AD(ah_esp_compress_pc) } };
+
+/* The IPsec sadb is subscripted by a bitset (subset of policy)
+ * with members from { POLICY_ENCRYPT, POLICY_AUTHENTICATE, POLICY_COMPRESS }
+ * shifted right by POLICY_IPSEC_SHIFT.
+ */
+struct db_sa ipsec_sadb[1 << 3] = {
+ { AD_NULL }, /* none */
+ { AD(esp_props) }, /* POLICY_ENCRYPT */
+ { AD(ah_props) }, /* POLICY_AUTHENTICATE */
+ { AD(ah_esp_props) }, /* POLICY_ENCRYPT+POLICY_AUTHENTICATE */
+ { AD(compress_props) }, /* POLICY_COMPRESS */
+ { AD(esp_compress_props) }, /* POLICY_ENCRYPT+POLICY_COMPRESS */
+ { AD(ah_compress_props) }, /* POLICY_AUTHENTICATE+POLICY_COMPRESS */
+ { AD(ah_esp_compress_props) }, /* POLICY_ENCRYPT+POLICY_AUTHENTICATE+POLICY_COMPRESS */
+ };
+
+#undef AD
+#undef AD_NULL
+
+/* output an attribute (within an SA) */
+static bool
+out_attr(int type
+, unsigned long val
+, struct_desc *attr_desc
+, enum_names **attr_val_descs USED_BY_DEBUG
+, pb_stream *pbs)
+{
+ struct isakmp_attribute attr;
+
+ if (val >> 16 == 0)
+ {
+ /* short value: use TV form */
+ attr.isaat_af_type = type | ISAKMP_ATTR_AF_TV;
+ attr.isaat_lv = val;
+ if (!out_struct(&attr, attr_desc, pbs, NULL))
+ return FALSE;
+ }
+ else
+ {
+ /* This is a real fudge! Since we rarely use long attributes
+ * and since this is the only place where we can cause an
+ * ISAKMP message length to be other than a multiple of 4 octets,
+ * we force the length of the value to be a multiple of 4 octets.
+ * Furthermore, we only handle values up to 4 octets in length.
+ * Voila: a fixed format!
+ */
+ pb_stream val_pbs;
+ u_int32_t nval = htonl(val);
+
+ attr.isaat_af_type = type | ISAKMP_ATTR_AF_TLV;
+ if (!out_struct(&attr, attr_desc, pbs, &val_pbs)
+ || !out_raw(&nval, sizeof(nval), &val_pbs, "long attribute value"))
+ return FALSE;
+ close_output_pbs(&val_pbs);
+ }
+ DBG(DBG_EMITTING,
+ enum_names *d = attr_val_descs[type];
+
+ if (d != NULL)
+ DBG_log(" [%lu is %s]"
+ , val, enum_show(d, val)));
+ return TRUE;
+}
+#define return_on(var, val) do { var=val;goto return_out; } while(0);
+/* Output an SA, as described by a db_sa.
+ * This has the side-effect of allocating SPIs for us.
+ */
+bool
+out_sa(pb_stream *outs
+, struct db_sa *sadb
+, struct state *st
+, bool oakley_mode
+, u_int8_t np)
+{
+ pb_stream sa_pbs;
+ int pcn;
+ bool ret = FALSE;
+ bool ah_spi_generated = FALSE
+ , esp_spi_generated = FALSE
+ , ipcomp_cpi_generated = FALSE;
+#if !defined NO_KERNEL_ALG || !defined NO_IKE_ALG
+ struct db_context *db_ctx = NULL;
+#endif
+
+ /* SA header out */
+ {
+ struct isakmp_sa sa;
+
+ sa.isasa_np = np;
+ st->st_doi = sa.isasa_doi = ISAKMP_DOI_IPSEC; /* all we know */
+ if (!out_struct(&sa, &isakmp_sa_desc, outs, &sa_pbs))
+ return_on(ret, FALSE);
+ }
+
+ /* within SA: situation out */
+ st->st_situation = SIT_IDENTITY_ONLY;
+ if (!out_struct(&st->st_situation, &ipsec_sit_desc, &sa_pbs, NULL))
+ return_on(ret, FALSE);
+
+ /* within SA: Proposal Payloads
+ *
+ * Multiple Proposals with the same number are simultaneous
+ * (conjuncts) and must deal with different protocols (AH or ESP).
+ * Proposals with different numbers are alternatives (disjuncts),
+ * in preference order.
+ * Proposal numbers must be monotonic.
+ * See RFC 2408 "ISAKMP" 4.2
+ */
+
+ for (pcn = 0; pcn != sadb->prop_conj_cnt; pcn++)
+ {
+ struct db_prop_conj *pc = &sadb->prop_conjs[pcn];
+ int pn;
+
+ for (pn = 0; pn != pc->prop_cnt; pn++)
+ {
+ struct db_prop *p = &pc->props[pn];
+ pb_stream proposal_pbs;
+ struct isakmp_proposal proposal;
+ struct_desc *trans_desc;
+ struct_desc *attr_desc;
+ enum_names **attr_val_descs;
+ int tn;
+ bool tunnel_mode;
+
+ tunnel_mode = (pn == pc->prop_cnt-1)
+ && (st->st_policy & POLICY_TUNNEL);
+
+ /* Proposal header */
+ proposal.isap_np = pcn == sadb->prop_conj_cnt-1 && pn == pc->prop_cnt-1
+ ? ISAKMP_NEXT_NONE : ISAKMP_NEXT_P;
+ proposal.isap_proposal = pcn;
+ proposal.isap_protoid = p->protoid;
+ proposal.isap_spisize = oakley_mode ? 0
+ : p->protoid == PROTO_IPCOMP ? IPCOMP_CPI_SIZE
+ : IPSEC_DOI_SPI_SIZE;
+
+ /* In quick mode ONLY, create proposal for runtime kernel algos.
+ * Replace ESP proposals with runtime created one
+ */
+ if (!oakley_mode && p->protoid == PROTO_IPSEC_ESP)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ if (st->st_connection->alg_info_esp)
+ {
+ static char buf[256]="";
+
+ alg_info_snprint(buf, sizeof (buf),
+ (struct alg_info *)st->st_connection->alg_info_esp);
+ DBG_log(buf);
+ }
+ )
+ db_ctx = kernel_alg_db_new(st->st_connection->alg_info_esp, st->st_policy);
+ p = db_prop_get(db_ctx);
+
+ if (!p || p->trans_cnt == 0)
+ {
+ loglog(RC_LOG_SERIOUS,
+ "empty IPSEC SA proposal to send "
+ "(no kernel algorithms for esp selection)");
+ return_on(ret, FALSE);
+ }
+ }
+
+ if (oakley_mode && p->protoid == PROTO_ISAKMP)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT,
+ if (st->st_connection->alg_info_ike)
+ {
+ static char buf[256]="";
+
+ alg_info_snprint(buf, sizeof (buf),
+ (struct alg_info *)st->st_connection->alg_info_ike);
+ DBG_log(buf);
+ }
+ )
+ db_ctx = ike_alg_db_new(st->st_connection->alg_info_ike, st->st_policy);
+ p = db_prop_get(db_ctx);
+
+ if (!p || p->trans_cnt == 0)
+ {
+ loglog(RC_LOG_SERIOUS,
+ "empty ISAKMP SA proposal to send "
+ "(no algorithms for ike selection?)");
+ return_on(ret, FALSE);
+ }
+ }
+
+ proposal.isap_notrans = p->trans_cnt;
+ if (!out_struct(&proposal, &isakmp_proposal_desc, &sa_pbs, &proposal_pbs))
+ return_on(ret, FALSE);
+
+ /* Per-protocols stuff:
+ * Set trans_desc.
+ * Set attr_desc.
+ * Set attr_val_descs.
+ * If not oakley_mode, emit SPI.
+ * We allocate SPIs on demand.
+ * All ESPs in an SA will share a single SPI.
+ * All AHs in an SAwill share a single SPI.
+ * AHs' SPI will be distinct from ESPs'.
+ * This latter is needed because KLIPS doesn't
+ * use the protocol when looking up a (dest, protocol, spi).
+ * ??? If multiple ESPs are composed, how should their SPIs
+ * be allocated?
+ */
+ {
+ ipsec_spi_t *spi_ptr = NULL;
+ int proto = 0;
+ bool *spi_generated = NULL;
+
+ switch (p->protoid)
+ {
+ case PROTO_ISAKMP:
+ passert(oakley_mode);
+ trans_desc = &isakmp_isakmp_transform_desc;
+ attr_desc = &isakmp_oakley_attribute_desc;
+ attr_val_descs = oakley_attr_val_descs;
+ /* no SPI needed */
+ break;
+ case PROTO_IPSEC_AH:
+ passert(!oakley_mode);
+ trans_desc = &isakmp_ah_transform_desc;
+ attr_desc = &isakmp_ipsec_attribute_desc;
+ attr_val_descs = ipsec_attr_val_descs;
+ spi_ptr = &st->st_ah.our_spi;
+ spi_generated = &ah_spi_generated;
+ proto = IPPROTO_AH;
+ break;
+ case PROTO_IPSEC_ESP:
+ passert(!oakley_mode);
+ trans_desc = &isakmp_esp_transform_desc;
+ attr_desc = &isakmp_ipsec_attribute_desc;
+ attr_val_descs = ipsec_attr_val_descs;
+ spi_ptr = &st->st_esp.our_spi;
+ spi_generated = &esp_spi_generated;
+ proto = IPPROTO_ESP;
+ break;
+ case PROTO_IPCOMP:
+ passert(!oakley_mode);
+ trans_desc = &isakmp_ipcomp_transform_desc;
+ attr_desc = &isakmp_ipsec_attribute_desc;
+ attr_val_descs = ipsec_attr_val_descs;
+
+ /* a CPI isn't quite the same as an SPI
+ * so we use specialized code to emit it.
+ */
+ if (!ipcomp_cpi_generated)
+ {
+ st->st_ipcomp.our_spi = get_my_cpi(
+ &st->st_connection->spd, tunnel_mode);
+ if (st->st_ipcomp.our_spi == 0)
+ return_on(ret, FALSE); /* problem generating CPI */
+
+ ipcomp_cpi_generated = TRUE;
+ }
+ /* CPI is stored in network low order end of an
+ * ipsec_spi_t. So we start a couple of bytes in.
+ */
+ if (!out_raw((u_char *)&st->st_ipcomp.our_spi
+ + IPSEC_DOI_SPI_SIZE - IPCOMP_CPI_SIZE
+ , IPCOMP_CPI_SIZE
+ , &proposal_pbs, "CPI"))
+ return_on(ret, FALSE);
+ break;
+ default:
+ bad_case(p->protoid);
+ }
+ if (spi_ptr != NULL)
+ {
+ if (!*spi_generated)
+ {
+ *spi_ptr = get_ipsec_spi(0
+ , proto
+ , &st->st_connection->spd
+ , tunnel_mode);
+ if (*spi_ptr == 0)
+ return FALSE;
+ *spi_generated = TRUE;
+ }
+ if (!out_raw((u_char *)spi_ptr, IPSEC_DOI_SPI_SIZE
+ , &proposal_pbs, "SPI"))
+ return_on(ret, FALSE);
+ }
+ }
+
+ /* within proposal: Transform Payloads */
+ for (tn = 0; tn != p->trans_cnt; tn++)
+ {
+ struct db_trans *t = &p->trans[tn];
+ pb_stream trans_pbs;
+ struct isakmp_transform trans;
+ int an;
+
+ trans.isat_np = (tn == p->trans_cnt - 1)
+ ? ISAKMP_NEXT_NONE : ISAKMP_NEXT_T;
+ trans.isat_transnum = tn;
+ trans.isat_transid = t->transid;
+ if (!out_struct(&trans, trans_desc, &proposal_pbs, &trans_pbs))
+ return_on(ret, FALSE);
+
+ /* Within tranform: Attributes. */
+
+ /* For Phase 2 / Quick Mode, GROUP_DESCRIPTION is
+ * automatically generated because it must be the same
+ * in every transform. Except IPCOMP.
+ */
+ if (p->protoid != PROTO_IPCOMP
+ && st->st_pfs_group != NULL)
+ {
+ passert(!oakley_mode);
+ passert(st->st_pfs_group != &unset_group);
+ out_attr(GROUP_DESCRIPTION, st->st_pfs_group->group
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ }
+
+ /* automatically generate duration
+ * and, for Phase 2 / Quick Mode, encapsulation.
+ */
+ if (oakley_mode)
+ {
+ out_attr(OAKLEY_LIFE_TYPE, OAKLEY_LIFE_SECONDS
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ out_attr(OAKLEY_LIFE_DURATION
+ , st->st_connection->sa_ike_life_seconds
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ }
+ else
+ {
+ /* RFC 2407 (IPSEC DOI) 4.5 specifies that
+ * the default is "unspecified (host-dependent)".
+ * This makes little sense, so we always specify it.
+ *
+ * Unlike other IPSEC transforms, IPCOMP defaults
+ * to Transport Mode, so we can exploit the default
+ * (draft-shacham-ippcp-rfc2393bis-05.txt 4.1).
+ */
+ if (p->protoid != PROTO_IPCOMP
+ || st->st_policy & POLICY_TUNNEL)
+ {
+#ifdef NAT_TRAVERSAL
+#ifndef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT
+ if ((st->nat_traversal & NAT_T_DETECTED)
+ && !(st->st_policy & POLICY_TUNNEL))
+ {
+ /* Inform user that we will not respect policy and only
+ * propose Tunnel Mode
+ */
+ loglog(RC_LOG_SERIOUS, "NAT-Traversal: "
+ "Transport Mode not allowed due to security concerns -- "
+ "using Tunnel mode");
+ }
+#endif
+#endif
+ out_attr(ENCAPSULATION_MODE
+#ifdef NAT_TRAVERSAL
+#ifdef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT
+ , NAT_T_ENCAPSULATION_MODE(st,st->st_policy)
+#else
+ /* If NAT-T is detected, use UDP_TUNNEL as long as Transport
+ * Mode has security concerns.
+ *
+ * User has been informed of that
+ */
+ , NAT_T_ENCAPSULATION_MODE(st,POLICY_TUNNEL)
+#endif
+#else /* ! NAT_TRAVERSAL */
+ , st->st_policy & POLICY_TUNNEL
+ ? ENCAPSULATION_MODE_TUNNEL : ENCAPSULATION_MODE_TRANSPORT
+#endif
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ }
+ out_attr(SA_LIFE_TYPE, SA_LIFE_TYPE_SECONDS
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ out_attr(SA_LIFE_DURATION
+ , st->st_connection->sa_ipsec_life_seconds
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ }
+
+ /* spit out attributes from table */
+ for (an = 0; an != t->attr_cnt; an++)
+ {
+ struct db_attr *a = &t->attrs[an];
+
+ out_attr(a->type, a->val
+ , attr_desc, attr_val_descs
+ , &trans_pbs);
+ }
+
+ close_output_pbs(&trans_pbs);
+ }
+ close_output_pbs(&proposal_pbs);
+ }
+ /* end of a conjunction of proposals */
+ }
+ close_output_pbs(&sa_pbs);
+ ret = TRUE;
+
+return_out:
+
+#if !defined NO_KERNEL_ALG || !defined NO_IKE_ALG
+ if (db_ctx)
+ db_destroy(db_ctx);
+#endif
+ return ret;
+}
+
+/* Handle long form of duration attribute.
+ * The code is can only handle values that can fit in unsigned long.
+ * "Clamping" is probably an acceptable way to impose this limitation.
+ */
+static u_int32_t
+decode_long_duration(pb_stream *pbs)
+{
+ u_int32_t val = 0;
+
+ /* ignore leading zeros */
+ while (pbs_left(pbs) != 0 && *pbs->cur == '\0')
+ pbs->cur++;
+
+ if (pbs_left(pbs) > sizeof(val))
+ {
+ /* "clamp" too large value to max representable value */
+ val -= 1; /* portable way to get to maximum value */
+ DBG(DBG_PARSING, DBG_log(" too large duration clamped to: %lu"
+ , (unsigned long)val));
+ }
+ else
+ {
+ /* decode number */
+ while (pbs_left(pbs) != 0)
+ val = (val << BITS_PER_BYTE) | *pbs->cur++;
+ DBG(DBG_PARSING, DBG_log(" long duration: %lu", (unsigned long)val));
+ }
+ return val;
+}
+
+/* Preparse the body of an ISAKMP SA Payload and
+ * return body of ISAKMP Proposal Payload
+ *
+ * Only IPsec DOI is accepted (what is the ISAKMP DOI?).
+ * Error response is rudimentary.
+ */
+notification_t
+preparse_isakmp_sa_body(const struct isakmp_sa *sa
+ , pb_stream *sa_pbs
+ , u_int32_t *ipsecdoisit
+ , pb_stream *proposal_pbs
+ , struct isakmp_proposal *proposal)
+{
+ /* DOI */
+ if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
+ {
+ loglog(RC_LOG_SERIOUS, "Unknown/unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
+ /* XXX Could send notification back */
+ return DOI_NOT_SUPPORTED;
+ }
+
+ /* Situation */
+ if (!in_struct(ipsecdoisit, &ipsec_sit_desc, sa_pbs, NULL))
+ return SITUATION_NOT_SUPPORTED;
+
+ if (*ipsecdoisit != SIT_IDENTITY_ONLY)
+ {
+ loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
+ , bitnamesof(sit_bit_names, *ipsecdoisit));
+ /* XXX Could send notification back */
+ return SITUATION_NOT_SUPPORTED;
+ }
+
+ /* The rules for ISAKMP SAs are scattered.
+ * RFC 2409 "IKE" section 5 says that there
+ * can only be one SA, and it can have only one proposal in it.
+ * There may well be multiple transforms.
+ */
+ if (!in_struct(proposal, &isakmp_proposal_desc, sa_pbs, proposal_pbs))
+ return PAYLOAD_MALFORMED;
+
+ if (proposal->isap_np != ISAKMP_NEXT_NONE)
+ {
+ loglog(RC_LOG_SERIOUS, "Proposal Payload must be alone in Oakley SA; found %s following Proposal"
+ , enum_show(&payload_names, proposal->isap_np));
+ return PAYLOAD_MALFORMED;
+ }
+
+ if (proposal->isap_protoid != PROTO_ISAKMP)
+ {
+ loglog(RC_LOG_SERIOUS, "unexpected Protocol ID (%s) found in Oakley Proposal"
+ , enum_show(&protocol_names, proposal->isap_protoid));
+ return INVALID_PROTOCOL_ID;
+ }
+
+ /* Just what should we accept for the SPI field?
+ * The RFC is sort of contradictory. We will ignore the SPI
+ * as long as it is of the proper size.
+ *
+ * From RFC2408 2.4 Identifying Security Associations:
+ * During phase 1 negotiations, the initiator and responder cookies
+ * determine the ISAKMP SA. Therefore, the SPI field in the Proposal
+ * payload is redundant and MAY be set to 0 or it MAY contain the
+ * transmitting entity's cookie.
+ *
+ * From RFC2408 3.5 Proposal Payload:
+ * o SPI Size (1 octet) - Length in octets of the SPI as defined by
+ * the Protocol-Id. In the case of ISAKMP, the Initiator and
+ * Responder cookie pair from the ISAKMP Header is the ISAKMP SPI,
+ * therefore, the SPI Size is irrelevant and MAY be from zero (0) to
+ * sixteen (16). If the SPI Size is non-zero, the content of the
+ * SPI field MUST be ignored. If the SPI Size is not a multiple of
+ * 4 octets it will have some impact on the SPI field and the
+ * alignment of all payloads in the message. The Domain of
+ * Interpretation (DOI) will dictate the SPI Size for other
+ * protocols.
+ */
+ if (proposal->isap_spisize == 0)
+ {
+ /* empty (0) SPI -- fine */
+ }
+ else if (proposal->isap_spisize <= MAX_ISAKMP_SPI_SIZE)
+ {
+ u_char junk_spi[MAX_ISAKMP_SPI_SIZE];
+
+ if (!in_raw(junk_spi, proposal->isap_spisize, proposal_pbs, "Oakley SPI"))
+ return PAYLOAD_MALFORMED;
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS, "invalid SPI size (%u) in Oakley Proposal"
+ , (unsigned)proposal->isap_spisize);
+ return INVALID_SPI;
+ }
+ return NOTHING_WRONG;
+}
+
+static struct {
+ u_int8_t *start;
+ u_int8_t *cur;
+ u_int8_t *roof;
+} backup;
+
+/*
+ * backup the pointer into a pb_stream
+ */
+void
+backup_pbs(pb_stream *pbs)
+{
+ backup.start = pbs->start;
+ backup.cur = pbs->cur;
+ backup.roof = pbs->roof;
+}
+
+/*
+ * restore the pointer into a pb_stream
+ */
+void
+restore_pbs(pb_stream *pbs)
+{
+ pbs->start = backup.start;
+ pbs->cur = backup.cur;
+ pbs->roof = backup.roof;
+}
+
+/*
+ * Parse an ISAKMP Proposal Payload for RSA and PSK authentication policies
+ */
+notification_t
+parse_isakmp_policy(pb_stream *proposal_pbs, u_int notrans, lset_t *policy)
+{
+ int last_transnum = -1;
+
+ *policy = LEMPTY;
+
+ while (notrans--)
+ {
+ pb_stream trans_pbs;
+ u_char *attr_start;
+ size_t attr_len;
+ struct isakmp_transform trans;
+
+ if (!in_struct(&trans, &isakmp_isakmp_transform_desc, proposal_pbs, &trans_pbs))
+ return BAD_PROPOSAL_SYNTAX;
+
+ if (trans.isat_transnum <= last_transnum)
+ {
+ /* picky, picky, picky */
+ loglog(RC_LOG_SERIOUS, "Transform Numbers are not monotonically increasing"
+ " in Oakley Proposal");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ last_transnum = trans.isat_transnum;
+
+ if (trans.isat_transid != KEY_IKE)
+ {
+ loglog(RC_LOG_SERIOUS, "expected KEY_IKE but found %s in Oakley Transform"
+ , enum_show(&isakmp_transformid_names, trans.isat_transid));
+ return INVALID_TRANSFORM_ID;
+ }
+
+ attr_start = trans_pbs.cur;
+ attr_len = pbs_left(&trans_pbs);
+
+ /* preprocess authentication attributes only */
+ while (pbs_left(&trans_pbs) != 0)
+ {
+ struct isakmp_attribute a;
+ pb_stream attr_pbs;
+
+ if (!in_struct(&a, &isakmp_oakley_attribute_desc, &trans_pbs, &attr_pbs))
+ return BAD_PROPOSAL_SYNTAX;
+
+ passert((a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK) < 32);
+
+ switch (a.isaat_af_type)
+ {
+ case OAKLEY_AUTHENTICATION_METHOD | ISAKMP_ATTR_AF_TV:
+ switch (a.isaat_lv)
+ {
+ case OAKLEY_PRESHARED_KEY:
+ *policy |= POLICY_PSK;
+ break;
+ case OAKLEY_RSA_SIG:
+ *policy |= POLICY_RSASIG;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if ((*policy & POLICY_PSK) && (*policy & POLICY_RSASIG))
+ {
+ DBG(DBG_CONTROL|DBG_PARSING,
+ DBG_log("preparse_isakmp_policy: "
+ "peer supports both PSK and RSASIG authentication")
+ )
+ *policy = LEMPTY;
+ }
+ else
+ {
+ DBG(DBG_CONTROL|DBG_PARSING,
+ DBG_log("preparse_isakmp_policy: peer requests %s authentication"
+ , (*policy & POLICY_PSK) ? "PSK":"RSASIG")
+ )
+ }
+ return NOTHING_WRONG;
+}
+
+/* Parse the body of an ISAKMP SA Payload (i.e. Phase 1 / Main Mode).
+ * Various shortcuts are taken. In particular, the policy, such as
+ * it is, is hardwired.
+ *
+ * If r_sa is non-NULL, the body of an SA representing the selected
+ * proposal is emitted.
+ *
+ * This routine is used by main_inI1_outR1() and main_inR1_outI2().
+ */
+notification_t
+parse_isakmp_sa_body(u_int32_t ipsecdoisit
+ , pb_stream *proposal_pbs
+ , struct isakmp_proposal *proposal
+ , pb_stream *r_sa_pbs
+ , struct state *st)
+{
+ struct connection *c = st->st_connection;
+ unsigned no_trans_left;
+
+ /* for each transform payload... */
+ no_trans_left = proposal->isap_notrans;
+
+ for (;;)
+ {
+ pb_stream trans_pbs;
+ u_char *attr_start;
+ size_t attr_len;
+ struct isakmp_transform trans;
+ lset_t seen_attrs = 0;
+ lset_t seen_durations = 0;
+ u_int16_t life_type = 0;
+ struct oakley_trans_attrs ta;
+ err_t ugh = NULL; /* set to diagnostic when problem detected */
+
+ /* initialize only optional field in ta */
+ ta.life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT; /* When this SA expires (seconds) */
+
+ if (no_trans_left == 0)
+ {
+ loglog(RC_LOG_SERIOUS, "number of Transform Payloads disagrees with Oakley Proposal Payload");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ in_struct(&trans, &isakmp_isakmp_transform_desc, proposal_pbs, &trans_pbs);
+ attr_start = trans_pbs.cur;
+ attr_len = pbs_left(&trans_pbs);
+
+ /* process all the attributes that make up the transform */
+
+ while (pbs_left(&trans_pbs) != 0)
+ {
+ struct isakmp_attribute a;
+ pb_stream attr_pbs;
+ u_int32_t val; /* room for larger values */
+
+ if (!in_struct(&a, &isakmp_oakley_attribute_desc, &trans_pbs, &attr_pbs))
+ return BAD_PROPOSAL_SYNTAX;
+
+ passert((a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK) < 32);
+
+ if (LHAS(seen_attrs, a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK))
+ {
+ loglog(RC_LOG_SERIOUS, "repeated %s attribute in Oakley Transform %u"
+ , enum_show(&oakley_attr_names, a.isaat_af_type)
+ , trans.isat_transnum);
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ seen_attrs |= LELEM(a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK);
+
+ val = a.isaat_lv;
+
+ DBG(DBG_PARSING,
+ {
+ enum_names *vdesc = oakley_attr_val_descs
+ [a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK];
+
+ if (vdesc != NULL)
+ {
+ const char *nm = enum_name(vdesc, val);
+
+ if (nm != NULL)
+ DBG_log(" [%u is %s]", (unsigned)val, nm);
+ }
+ });
+
+ switch (a.isaat_af_type)
+ {
+ case OAKLEY_ENCRYPTION_ALGORITHM | ISAKMP_ATTR_AF_TV:
+ if (ike_alg_enc_present(val))
+ {
+ ta.encrypt = val;
+ ta.encrypter = ike_alg_get_encrypter(val);
+ ta.enckeylen = ta.encrypter->keydeflen;
+ }
+ else
+ {
+ ugh = builddiag("%s is not supported"
+ , enum_show(&oakley_enc_names, val));
+ }
+ break;
+
+ case OAKLEY_HASH_ALGORITHM | ISAKMP_ATTR_AF_TV:
+ if (ike_alg_hash_present(val))
+ {
+ ta.hash = val;
+ ta.hasher = ike_alg_get_hasher(val);
+ }
+ else
+ {
+ ugh = builddiag("%s is not supported"
+ , enum_show(&oakley_hash_names, val));
+ }
+ break;
+
+ case OAKLEY_AUTHENTICATION_METHOD | ISAKMP_ATTR_AF_TV:
+ {
+ /* check that authentication method is acceptable */
+ lset_t iap = st->st_policy & POLICY_ID_AUTH_MASK;
+
+ switch (val)
+ {
+ case OAKLEY_PRESHARED_KEY:
+ if ((iap & POLICY_PSK) == LEMPTY)
+ {
+ ugh = "policy does not allow OAKLEY_PRESHARED_KEY authentication";
+ }
+ else
+ {
+ /* check that we can find a preshared secret */
+ struct connection *c = st->st_connection;
+
+ if (get_preshared_secret(c) == NULL)
+ {
+ char mid[BUF_LEN]
+ , hid[BUF_LEN];
+
+ idtoa(&c->spd.this.id, mid, sizeof(mid));
+ if (his_id_was_instantiated(c))
+ strcpy(hid, "%any");
+ else
+ idtoa(&c->spd.that.id, hid, sizeof(hid));
+ ugh = builddiag("Can't authenticate: no preshared key found for `%s' and `%s'"
+ , mid, hid);
+ }
+ ta.auth = val;
+ }
+ break;
+ case OAKLEY_RSA_SIG:
+ /* Accept if policy specifies RSASIG or is default */
+ if ((iap & POLICY_RSASIG) == LEMPTY)
+ {
+ ugh = "policy does not allow OAKLEY_RSA_SIG authentication";
+ }
+ else
+ {
+ /* We'd like to check that we can find a public
+ * key for him and a private key for us that is
+ * suitable, but we don't yet have his
+ * Id Payload, so it seems futile to try.
+ * We can assume that if he proposes it, he
+ * thinks we've got it. If we proposed it,
+ * perhaps we know what we're doing.
+ */
+ ta.auth = val;
+ }
+ break;
+
+ default:
+ ugh = builddiag("Pluto does not support %s authentication"
+ , enum_show(&oakley_auth_names, val));
+ break;
+ }
+ }
+ break;
+
+ case OAKLEY_GROUP_DESCRIPTION | ISAKMP_ATTR_AF_TV:
+ ta.group = lookup_group(val);
+ if (ta.group == NULL)
+ {
+ ugh = "only OAKLEY_GROUP_MODP1024 and OAKLEY_GROUP_MODP1536 supported";
+ }
+ break;
+
+ case OAKLEY_LIFE_TYPE | ISAKMP_ATTR_AF_TV:
+ switch (val)
+ {
+ case OAKLEY_LIFE_SECONDS:
+ case OAKLEY_LIFE_KILOBYTES:
+ if (LHAS(seen_durations, val))
+ {
+ loglog(RC_LOG_SERIOUS
+ , "attribute OAKLEY_LIFE_TYPE value %s repeated"
+ , enum_show(&oakley_lifetime_names, val));
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ seen_durations |= LELEM(val);
+ life_type = val;
+ break;
+ default:
+ ugh = builddiag("unknown value %s"
+ , enum_show(&oakley_lifetime_names, val));
+ break;
+ }
+ break;
+
+ case OAKLEY_LIFE_DURATION | ISAKMP_ATTR_AF_TLV:
+ val = decode_long_duration(&attr_pbs);
+ /* fall through */
+ case OAKLEY_LIFE_DURATION | ISAKMP_ATTR_AF_TV:
+ if (!LHAS(seen_attrs, OAKLEY_LIFE_TYPE))
+ {
+ ugh = "OAKLEY_LIFE_DURATION attribute not preceded by OAKLEY_LIFE_TYPE attribute";
+ break;
+ }
+ seen_attrs &= ~(LELEM(OAKLEY_LIFE_DURATION) | LELEM(OAKLEY_LIFE_TYPE));
+
+ switch (life_type)
+ {
+ case OAKLEY_LIFE_SECONDS:
+ if (val > OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM)
+ ugh = builddiag("peer requested %lu seconds"
+ " which exceeds our limit %d seconds"
+ , (long) val
+ , OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM);
+ ta.life_seconds = val;
+ break;
+ case OAKLEY_LIFE_KILOBYTES:
+ ta.life_kilobytes = val;
+ break;
+ default:
+ bad_case(life_type);
+ }
+ break;
+
+ case OAKLEY_KEY_LENGTH | ISAKMP_ATTR_AF_TV:
+ if ((seen_attrs & LELEM(OAKLEY_ENCRYPTION_ALGORITHM)) == 0)
+ {
+ ugh = "OAKLEY_KEY_LENGTH attribute not preceded by "
+ "OAKLEY_ENCRYPTION_ALGORITHM attribute";
+ break;
+ }
+ if (ta.encrypter == NULL)
+ {
+ ugh = "NULL encrypter with seen OAKLEY_ENCRYPTION_ALGORITHM";
+ break;
+ }
+ /*
+ * check if this keylen is compatible with specified algorithm
+ */
+ if (val
+ && (val < ta.encrypter->keyminlen || val > ta.encrypter->keymaxlen))
+ {
+ ugh = "peer proposed key length not valid for "
+ "encryption algorithm specified";
+ }
+ ta.enckeylen = val;
+ break;
+#if 0 /* not yet supported */
+ case OAKLEY_GROUP_TYPE | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_PRF | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_FIELD_SIZE | ISAKMP_ATTR_AF_TV:
+
+ case OAKLEY_GROUP_PRIME | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_PRIME | ISAKMP_ATTR_AF_TLV:
+ case OAKLEY_GROUP_GENERATOR_ONE | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_GENERATOR_ONE | ISAKMP_ATTR_AF_TLV:
+ case OAKLEY_GROUP_GENERATOR_TWO | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_GENERATOR_TWO | ISAKMP_ATTR_AF_TLV:
+ case OAKLEY_GROUP_CURVE_A | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_CURVE_A | ISAKMP_ATTR_AF_TLV:
+ case OAKLEY_GROUP_CURVE_B | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_CURVE_B | ISAKMP_ATTR_AF_TLV:
+ case OAKLEY_GROUP_ORDER | ISAKMP_ATTR_AF_TV:
+ case OAKLEY_GROUP_ORDER | ISAKMP_ATTR_AF_TLV:
+#endif
+ default:
+ ugh = "unsupported OAKLEY attribute";
+ break;
+ }
+
+ if (ugh != NULL)
+ {
+ loglog(RC_LOG_SERIOUS, "%s. Attribute %s"
+ , ugh, enum_show(&oakley_attr_names, a.isaat_af_type));
+ break;
+ }
+ }
+
+ /*
+ * ML: at last check for allowed transforms in alg_info_ike
+ * (ALG_INFO_F_STRICT flag)
+ */
+ if (ugh == NULL)
+ {
+ if (!ike_alg_ok_final(ta.encrypt, ta.enckeylen, ta.hash,
+ ta.group ? ta.group->group : -1, c->alg_info_ike))
+ {
+ ugh = "OAKLEY proposal refused";
+ }
+ }
+
+ if (ugh == NULL)
+ {
+ /* a little more checking is in order */
+ {
+ lset_t missing
+ = ~seen_attrs
+ & (LELEM(OAKLEY_ENCRYPTION_ALGORITHM)
+ | LELEM(OAKLEY_HASH_ALGORITHM)
+ | LELEM(OAKLEY_AUTHENTICATION_METHOD)
+ | LELEM(OAKLEY_GROUP_DESCRIPTION));
+
+ if (missing)
+ {
+ loglog(RC_LOG_SERIOUS, "missing mandatory attribute(s) %s in Oakley Transform %u"
+ , bitnamesof(oakley_attr_bit_names, missing)
+ , trans.isat_transnum);
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ }
+ /* We must have liked this transform.
+ * Lets finish early and leave.
+ */
+
+ DBG(DBG_PARSING | DBG_CRYPT
+ , DBG_log("Oakley Transform %u accepted", trans.isat_transnum));
+
+ if (r_sa_pbs != NULL)
+ {
+ struct isakmp_proposal r_proposal = *proposal;
+ pb_stream r_proposal_pbs;
+ struct isakmp_transform r_trans = trans;
+ pb_stream r_trans_pbs;
+
+ /* Situation */
+ if (!out_struct(&ipsecdoisit, &ipsec_sit_desc, r_sa_pbs, NULL))
+ impossible();
+
+ /* Proposal */
+#ifdef EMIT_ISAKMP_SPI
+ r_proposal.isap_spisize = COOKIE_SIZE;
+#else
+ r_proposal.isap_spisize = 0;
+#endif
+ r_proposal.isap_notrans = 1;
+ if (!out_struct(&r_proposal, &isakmp_proposal_desc, r_sa_pbs, &r_proposal_pbs))
+ impossible();
+
+ /* SPI */
+#ifdef EMIT_ISAKMP_SPI
+ if (!out_raw(my_cookie, COOKIE_SIZE, &r_proposal_pbs, "SPI"))
+ impossible();
+ r_proposal.isap_spisize = COOKIE_SIZE;
+#else
+ /* none (0) */
+#endif
+
+ /* Transform */
+ r_trans.isat_np = ISAKMP_NEXT_NONE;
+ if (!out_struct(&r_trans, &isakmp_isakmp_transform_desc, &r_proposal_pbs, &r_trans_pbs))
+ impossible();
+
+ if (!out_raw(attr_start, attr_len, &r_trans_pbs, "attributes"))
+ impossible();
+ close_output_pbs(&r_trans_pbs);
+ close_output_pbs(&r_proposal_pbs);
+ close_output_pbs(r_sa_pbs);
+ }
+
+ /* copy over the results */
+ st->st_oakley = ta;
+ return NOTHING_WRONG;
+ }
+
+ /* on to next transform */
+ no_trans_left--;
+
+ if (trans.isat_np == ISAKMP_NEXT_NONE)
+ {
+ if (no_trans_left != 0)
+ {
+ loglog(RC_LOG_SERIOUS, "number of Transform Payloads disagrees with Oakley Proposal Payload");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ break;
+ }
+ if (trans.isat_np != ISAKMP_NEXT_T)
+ {
+ loglog(RC_LOG_SERIOUS, "unexpected %s payload in Oakley Proposal"
+ , enum_show(&payload_names, proposal->isap_np));
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ }
+ loglog(RC_LOG_SERIOUS, "no acceptable Oakley Transform");
+ return NO_PROPOSAL_CHOSEN;
+}
+
+/* Parse the body of an IPsec SA Payload (i.e. Phase 2 / Quick Mode).
+ *
+ * The main routine is parse_ipsec_sa_body; other functions defined
+ * between here and there are just helpers.
+ *
+ * Various shortcuts are taken. In particular, the policy, such as
+ * it is, is hardwired.
+ *
+ * If r_sa is non-NULL, the body of an SA representing the selected
+ * proposal is emitted into it.
+ *
+ * If "selection" is true, the SA is supposed to represent the
+ * single tranform that the peer has accepted.
+ * ??? We only check that it is acceptable, not that it is one that we offered!
+ *
+ * Only IPsec DOI is accepted (what is the ISAKMP DOI?).
+ * Error response is rudimentary.
+ *
+ * Since all ISAKMP groups in all SA Payloads must match, st->st_pfs_group
+ * holds this across multiple payloads.
+ * &unset_group signifies not yet "set"; NULL signifies NONE.
+ *
+ * This routine is used by quick_inI1_outR1() and quick_inR1_outI2().
+ */
+
+static const struct ipsec_trans_attrs null_ipsec_trans_attrs = {
+ 0, /* transid (NULL, for now) */
+ 0, /* spi */
+ SA_LIFE_DURATION_DEFAULT, /* life_seconds */
+ SA_LIFE_DURATION_K_DEFAULT, /* life_kilobytes */
+ ENCAPSULATION_MODE_UNSPECIFIED, /* encapsulation */
+ AUTH_ALGORITHM_NONE, /* auth */
+ 0, /* key_len */
+ 0, /* key_rounds */
+};
+
+static bool
+parse_ipsec_transform(struct isakmp_transform *trans
+, struct ipsec_trans_attrs *attrs
+, pb_stream *prop_pbs
+, pb_stream *trans_pbs
+, struct_desc *trans_desc
+, int previous_transnum /* or -1 if none */
+, bool selection
+, bool is_last
+, bool is_ipcomp
+, struct state *st) /* current state object */
+{
+ lset_t seen_attrs = 0;
+ lset_t seen_durations = 0;
+ u_int16_t life_type = 0;
+ const struct oakley_group_desc *pfs_group = NULL;
+
+ if (!in_struct(trans, trans_desc, prop_pbs, trans_pbs))
+ return FALSE;
+
+ if (trans->isat_transnum <= previous_transnum)
+ {
+ loglog(RC_LOG_SERIOUS, "Transform Numbers in Proposal are not monotonically increasing");
+ return FALSE;
+ }
+
+ switch (trans->isat_np)
+ {
+ case ISAKMP_NEXT_T:
+ if (is_last)
+ {
+ loglog(RC_LOG_SERIOUS, "Proposal Payload has more Transforms than specified");
+ return FALSE;
+ }
+ break;
+ case ISAKMP_NEXT_NONE:
+ if (!is_last)
+ {
+ loglog(RC_LOG_SERIOUS, "Proposal Payload has fewer Transforms than specified");
+ return FALSE;
+ }
+ break;
+ default:
+ loglog(RC_LOG_SERIOUS, "expecting Transform Payload, but found %s in Proposal"
+ , enum_show(&payload_names, trans->isat_np));
+ return FALSE;
+ }
+
+ *attrs = null_ipsec_trans_attrs;
+ attrs->transid = trans->isat_transid;
+
+ while (pbs_left(trans_pbs) != 0)
+ {
+ struct isakmp_attribute a;
+ pb_stream attr_pbs;
+ enum_names *vdesc;
+ u_int32_t val; /* room for larger value */
+ bool ipcomp_inappropriate = is_ipcomp; /* will get reset if OK */
+
+ if (!in_struct(&a, &isakmp_ipsec_attribute_desc, trans_pbs, &attr_pbs))
+ return FALSE;
+
+ passert((a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK) < 32);
+
+ if (LHAS(seen_attrs, a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK))
+ {
+ loglog(RC_LOG_SERIOUS, "repeated %s attribute in IPsec Transform %u"
+ , enum_show(&ipsec_attr_names, a.isaat_af_type)
+ , trans->isat_transnum);
+ return FALSE;
+ }
+
+ seen_attrs |= LELEM(a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK);
+
+ val = a.isaat_lv;
+
+ vdesc = ipsec_attr_val_descs[a.isaat_af_type & ISAKMP_ATTR_RTYPE_MASK];
+ if (vdesc != NULL)
+ {
+ if (enum_name(vdesc, val) == NULL)
+ {
+ loglog(RC_LOG_SERIOUS, "invalid value %u for attribute %s in IPsec Transform"
+ , (unsigned)val, enum_show(&ipsec_attr_names, a.isaat_af_type));
+ return FALSE;
+ }
+ DBG(DBG_PARSING
+ , if ((a.isaat_af_type & ISAKMP_ATTR_AF_MASK) == ISAKMP_ATTR_AF_TV)
+ DBG_log(" [%u is %s]"
+ , (unsigned)val, enum_show(vdesc, val)));
+ }
+
+ switch (a.isaat_af_type)
+ {
+ case SA_LIFE_TYPE | ISAKMP_ATTR_AF_TV:
+ ipcomp_inappropriate = FALSE;
+ if (LHAS(seen_durations, val))
+ {
+ loglog(RC_LOG_SERIOUS, "attribute SA_LIFE_TYPE value %s repeated in message"
+ , enum_show(&sa_lifetime_names, val));
+ return FALSE;
+ }
+ seen_durations |= LELEM(val);
+ life_type = val;
+ break;
+ case SA_LIFE_DURATION | ISAKMP_ATTR_AF_TLV:
+ val = decode_long_duration(&attr_pbs);
+ /* fall through */
+ case SA_LIFE_DURATION | ISAKMP_ATTR_AF_TV:
+ ipcomp_inappropriate = FALSE;
+ if (!LHAS(seen_attrs, SA_LIFE_DURATION))
+ {
+ loglog(RC_LOG_SERIOUS, "SA_LIFE_DURATION IPsec attribute not preceded by SA_LIFE_TYPE attribute");
+ return FALSE;
+ }
+ seen_attrs &= ~(LELEM(SA_LIFE_DURATION) | LELEM(SA_LIFE_TYPE));
+
+ switch (life_type)
+ {
+ case SA_LIFE_TYPE_SECONDS:
+ /* silently limit duration to our maximum */
+ attrs->life_seconds = val <= SA_LIFE_DURATION_MAXIMUM
+ ? val : SA_LIFE_DURATION_MAXIMUM;
+ break;
+ case SA_LIFE_TYPE_KBYTES:
+ attrs->life_kilobytes = val;
+ break;
+ default:
+ bad_case(life_type);
+ }
+ break;
+ case GROUP_DESCRIPTION | ISAKMP_ATTR_AF_TV:
+ if (is_ipcomp)
+ {
+ /* Accept reluctantly. Should not happen, according to
+ * draft-shacham-ippcp-rfc2393bis-05.txt 4.1.
+ */
+ ipcomp_inappropriate = FALSE;
+ loglog(RC_COMMENT
+ , "IPCA (IPcomp SA) contains GROUP_DESCRIPTION."
+ " Ignoring inapproprate attribute.");
+ }
+ pfs_group = lookup_group(val);
+ if (pfs_group == NULL)
+ {
+ loglog(RC_LOG_SERIOUS, "only OAKLEY_GROUP_MODP1024 and OAKLEY_GROUP_MODP1536 supported for PFS");
+ return FALSE;
+ }
+ break;
+ case ENCAPSULATION_MODE | ISAKMP_ATTR_AF_TV:
+ ipcomp_inappropriate = FALSE;
+#ifdef NAT_TRAVERSAL
+ switch (val)
+ {
+ case ENCAPSULATION_MODE_TUNNEL:
+ case ENCAPSULATION_MODE_TRANSPORT:
+ if (st->nat_traversal & NAT_T_DETECTED)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "%s must only be used if NAT-Traversal is not detected"
+ , enum_name(&enc_mode_names, val));
+ /*
+ * Accept it anyway because SSH-Sentinel does not
+ * use UDP_TUNNEL or UDP_TRANSPORT for the diagnostic.
+ *
+ * remove when SSH-Sentinel is fixed
+ */
+#ifdef I_DONT_CARE_OF_SSH_SENTINEL
+ return FALSE;
+#endif
+ }
+ attrs->encapsulation = val;
+ break;
+ case ENCAPSULATION_MODE_UDP_TRANSPORT_DRAFTS:
+#ifndef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT
+ loglog(RC_LOG_SERIOUS
+ , "NAT-Traversal: Transport mode disabled due to security concerns");
+ return FALSE;
+#endif
+ case ENCAPSULATION_MODE_UDP_TUNNEL_DRAFTS:
+ if (st->nat_traversal & NAT_T_WITH_RFC_VALUES)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "%s must only be used with old IETF drafts"
+ , enum_name(&enc_mode_names, val));
+ return FALSE;
+ }
+ else if (st->nat_traversal & NAT_T_DETECTED)
+ {
+ attrs->encapsulation = val
+ - ENCAPSULATION_MODE_UDP_TUNNEL_DRAFTS
+ + ENCAPSULATION_MODE_TUNNEL;
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS
+ , "%s must only be used if NAT-Traversal is detected"
+ , enum_name(&enc_mode_names, val));
+ return FALSE;
+ }
+ break;
+ case ENCAPSULATION_MODE_UDP_TRANSPORT_RFC:
+#ifndef I_KNOW_TRANSPORT_MODE_HAS_SECURITY_CONCERN_BUT_I_WANT_IT
+ loglog(RC_LOG_SERIOUS
+ , "NAT-Traversal: Transport mode disabled due "
+ "to security concerns");
+ return FALSE;
+#endif
+ case ENCAPSULATION_MODE_UDP_TUNNEL_RFC:
+ if ((st->nat_traversal & NAT_T_DETECTED)
+ && (st->nat_traversal & NAT_T_WITH_RFC_VALUES))
+ {
+ attrs->encapsulation = val
+ - ENCAPSULATION_MODE_UDP_TUNNEL_RFC
+ + ENCAPSULATION_MODE_TUNNEL;
+ }
+ else if (st->nat_traversal & NAT_T_DETECTED)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "%s must only be used with NAT-T RFC"
+ , enum_name(&enc_mode_names, val));
+ return FALSE;
+ }
+ else
+ {
+ loglog(RC_LOG_SERIOUS
+ , "%s must only be used if NAT-Traversal is detected"
+ , enum_name(&enc_mode_names, val));
+ return FALSE;
+ }
+ break;
+ default:
+ loglog(RC_LOG_SERIOUS
+ , "unknown ENCAPSULATION_MODE %d in IPSec SA", val);
+ return FALSE;
+ }
+#else
+ attrs->encapsulation = val;
+#endif
+ break;
+ case AUTH_ALGORITHM | ISAKMP_ATTR_AF_TV:
+ attrs->auth = val;
+ break;
+ case KEY_LENGTH | ISAKMP_ATTR_AF_TV:
+ attrs->key_len = val;
+ break;
+ case KEY_ROUNDS | ISAKMP_ATTR_AF_TV:
+ attrs->key_rounds = val;
+ break;
+#if 0 /* not yet implemented */
+ case COMPRESS_DICT_SIZE | ISAKMP_ATTR_AF_TV:
+ break;
+ case COMPRESS_PRIVATE_ALG | ISAKMP_ATTR_AF_TV:
+ break;
+
+ case SA_LIFE_DURATION | ISAKMP_ATTR_AF_TLV:
+ break;
+ case COMPRESS_PRIVATE_ALG | ISAKMP_ATTR_AF_TLV:
+ break;
+#endif
+ default:
+ loglog(RC_LOG_SERIOUS, "unsupported IPsec attribute %s"
+ , enum_show(&ipsec_attr_names, a.isaat_af_type));
+ return FALSE;
+ }
+ if (ipcomp_inappropriate)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec attribute %s inappropriate for IPCOMP"
+ , enum_show(&ipsec_attr_names, a.isaat_af_type));
+ return FALSE;
+ }
+ }
+
+ /* Although an IPCOMP SA (IPCA) ought not to have a pfs_group,
+ * if it does, demand that it be consistent.
+ * See draft-shacham-ippcp-rfc2393bis-05.txt 4.1.
+ */
+ if (!is_ipcomp || pfs_group != NULL)
+ {
+ if (st->st_pfs_group == &unset_group)
+ st->st_pfs_group = pfs_group;
+
+ if (st->st_pfs_group != pfs_group)
+ {
+ loglog(RC_LOG_SERIOUS, "GROUP_DESCRIPTION inconsistent with that of %s in IPsec SA"
+ , selection? "the Proposal" : "a previous Transform");
+ return FALSE;
+ }
+ }
+
+ if (LHAS(seen_attrs, SA_LIFE_DURATION))
+ {
+ loglog(RC_LOG_SERIOUS, "SA_LIFE_TYPE IPsec attribute not followed by SA_LIFE_DURATION attribute in message");
+ return FALSE;
+ }
+
+ if (!LHAS(seen_attrs, ENCAPSULATION_MODE))
+ {
+ if (is_ipcomp)
+ {
+ /* draft-shacham-ippcp-rfc2393bis-05.txt 4.1:
+ * "If the Encapsulation Mode is unspecified,
+ * the default value of Transport Mode is assumed."
+ * This contradicts/overrides the DOI (quuoted below).
+ */
+ attrs->encapsulation = ENCAPSULATION_MODE_TRANSPORT;
+ }
+ else
+ {
+ /* ??? Technically, RFC 2407 (IPSEC DOI) 4.5 specifies that
+ * the default is "unspecified (host-dependent)".
+ * This makes little sense, so we demand that it be specified.
+ */
+ loglog(RC_LOG_SERIOUS, "IPsec Transform must specify ENCAPSULATION_MODE");
+ return FALSE;
+ }
+ }
+
+ /* ??? should check for key_len and/or key_rounds if required */
+
+ return TRUE;
+}
+
+static void
+echo_proposal(
+ struct isakmp_proposal r_proposal, /* proposal to emit */
+ struct isakmp_transform r_trans, /* winning transformation within it */
+ u_int8_t np, /* Next Payload for proposal */
+ pb_stream *r_sa_pbs, /* SA PBS into which to emit */
+ struct ipsec_proto_info *pi, /* info about this protocol instance */
+ struct_desc *trans_desc, /* descriptor for this transformation */
+ pb_stream *trans_pbs, /* PBS for incoming transform */
+ struct spd_route *sr, /* host details for the association */
+ bool tunnel_mode) /* true for inner most tunnel SA */
+{
+ pb_stream r_proposal_pbs;
+ pb_stream r_trans_pbs;
+
+ /* Proposal */
+ r_proposal.isap_np = np;
+ r_proposal.isap_notrans = 1;
+ if (!out_struct(&r_proposal, &isakmp_proposal_desc, r_sa_pbs, &r_proposal_pbs))
+ impossible();
+
+ /* allocate and emit our CPI/SPI */
+ if (r_proposal.isap_protoid == PROTO_IPCOMP)
+ {
+ /* CPI is stored in network low order end of an
+ * ipsec_spi_t. So we start a couple of bytes in.
+ * Note: we may fail to generate a satisfactory CPI,
+ * but we'll ignore that.
+ */
+ pi->our_spi = get_my_cpi(sr, tunnel_mode);
+ out_raw((u_char *) &pi->our_spi
+ + IPSEC_DOI_SPI_SIZE - IPCOMP_CPI_SIZE
+ , IPCOMP_CPI_SIZE
+ , &r_proposal_pbs, "CPI");
+ }
+ else
+ {
+ pi->our_spi = get_ipsec_spi(pi->attrs.spi
+ , r_proposal.isap_protoid == PROTO_IPSEC_AH ?
+ IPPROTO_AH : IPPROTO_ESP
+ , sr
+ , tunnel_mode);
+ /* XXX should check for errors */
+ out_raw((u_char *) &pi->our_spi, IPSEC_DOI_SPI_SIZE
+ , &r_proposal_pbs, "SPI");
+ }
+
+ /* Transform */
+ r_trans.isat_np = ISAKMP_NEXT_NONE;
+ if (!out_struct(&r_trans, trans_desc, &r_proposal_pbs, &r_trans_pbs))
+ impossible();
+
+ /* Transform Attributes: pure echo */
+ trans_pbs->cur = trans_pbs->start + sizeof(struct isakmp_transform);
+ if (!out_raw(trans_pbs->cur, pbs_left(trans_pbs)
+ , &r_trans_pbs, "attributes"))
+ impossible();
+
+ close_output_pbs(&r_trans_pbs);
+ close_output_pbs(&r_proposal_pbs);
+}
+
+notification_t
+parse_ipsec_sa_body(
+ pb_stream *sa_pbs, /* body of input SA Payload */
+ const struct isakmp_sa *sa, /* header of input SA Payload */
+ pb_stream *r_sa_pbs, /* if non-NULL, where to emit body of winning SA */
+ bool selection, /* if this SA is a selection, only one transform may appear */
+ struct state *st) /* current state object */
+{
+ const struct connection *c = st->st_connection;
+ u_int32_t ipsecdoisit;
+ pb_stream next_proposal_pbs;
+
+ struct isakmp_proposal next_proposal;
+ ipsec_spi_t next_spi;
+
+ bool next_full = TRUE;
+
+ /* DOI */
+ if (sa->isasa_doi != ISAKMP_DOI_IPSEC)
+ {
+ loglog(RC_LOG_SERIOUS, "Unknown or unsupported DOI %s", enum_show(&doi_names, sa->isasa_doi));
+ /* XXX Could send notification back */
+ return DOI_NOT_SUPPORTED;
+ }
+
+ /* Situation */
+ if (!in_struct(&ipsecdoisit, &ipsec_sit_desc, sa_pbs, NULL))
+ return SITUATION_NOT_SUPPORTED;
+
+ if (ipsecdoisit != SIT_IDENTITY_ONLY)
+ {
+ loglog(RC_LOG_SERIOUS, "unsupported IPsec DOI situation (%s)"
+ , bitnamesof(sit_bit_names, ipsecdoisit));
+ /* XXX Could send notification back */
+ return SITUATION_NOT_SUPPORTED;
+ }
+
+ /* The rules for IPsec SAs are scattered.
+ * RFC 2408 "ISAKMP" section 4.2 gives some info.
+ * There may be multiple proposals. Those with identical proposal
+ * numbers must be considered as conjuncts. Those with different
+ * numbers are disjuncts.
+ * Each proposal may have several transforms, each considered
+ * an alternative.
+ * Each transform may have several attributes, all applying.
+ *
+ * To handle the way proposals are combined, we need to do a
+ * look-ahead.
+ */
+
+ if (!in_struct(&next_proposal, &isakmp_proposal_desc, sa_pbs, &next_proposal_pbs))
+ return BAD_PROPOSAL_SYNTAX;
+
+ /* for each conjunction of proposals... */
+ while (next_full)
+ {
+ int propno = next_proposal.isap_proposal;
+ pb_stream ah_prop_pbs, esp_prop_pbs, ipcomp_prop_pbs;
+ struct isakmp_proposal ah_proposal, esp_proposal, ipcomp_proposal;
+ ipsec_spi_t ah_spi = 0;
+ ipsec_spi_t esp_spi = 0;
+ ipsec_spi_t ipcomp_cpi = 0;
+ bool ah_seen = FALSE;
+ bool esp_seen = FALSE;
+ bool ipcomp_seen = FALSE;
+ bool tunnel_mode = FALSE;
+ int inner_proto = 0;
+ u_int16_t well_known_cpi = 0;
+
+ pb_stream ah_trans_pbs, esp_trans_pbs, ipcomp_trans_pbs;
+ struct isakmp_transform ah_trans, esp_trans, ipcomp_trans;
+ struct ipsec_trans_attrs ah_attrs, esp_attrs, ipcomp_attrs;
+
+ /* for each proposal in the conjunction */
+ do {
+
+ if (next_proposal.isap_protoid == PROTO_IPCOMP)
+ {
+ /* IPCOMP CPI */
+ if (next_proposal.isap_spisize == IPSEC_DOI_SPI_SIZE)
+ {
+ /* This code is to accommodate those peculiar
+ * implementations that send a CPI in the bottom of an
+ * SPI-sized field.
+ * See draft-shacham-ippcp-rfc2393bis-05.txt 4.1
+ */
+ u_int8_t filler[IPSEC_DOI_SPI_SIZE - IPCOMP_CPI_SIZE];
+
+ if (!in_raw(filler, sizeof(filler)
+ , &next_proposal_pbs, "CPI filler")
+ || !all_zero(filler, sizeof(filler)))
+ return INVALID_SPI;
+ }
+ else if (next_proposal.isap_spisize != IPCOMP_CPI_SIZE)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec Proposal with improper CPI size (%u)"
+ , next_proposal.isap_spisize);
+ return INVALID_SPI;
+ }
+
+ /* We store CPI in the low order of a network order
+ * ipsec_spi_t. So we start a couple of bytes in.
+ */
+ zero(&next_spi);
+ if (!in_raw((u_char *)&next_spi
+ + IPSEC_DOI_SPI_SIZE - IPCOMP_CPI_SIZE
+ , IPCOMP_CPI_SIZE, &next_proposal_pbs, "CPI"))
+ return INVALID_SPI;
+
+ /* If sanity ruled, CPIs would have to be such that
+ * the SAID (the triple (CPI, IPCOM, destination IP))
+ * would be unique, just like for SPIs. But there is a
+ * perversion where CPIs can be well-known and consequently
+ * the triple is not unique. We hide this fact from
+ * ourselves by fudging the top 16 bits to make
+ * the property true internally!
+ */
+ switch (ntohl(next_spi))
+ {
+ case IPCOMP_DEFLATE:
+ well_known_cpi = ntohl(next_spi);
+ next_spi = uniquify_his_cpi(next_spi, st);
+ if (next_spi == 0)
+ {
+ loglog(RC_LOG_SERIOUS
+ , "IPsec Proposal contains well-known CPI that I cannot uniquify");
+ return INVALID_SPI;
+ }
+ break;
+ default:
+ if (ntohl(next_spi) < IPCOMP_FIRST_NEGOTIATED
+ || ntohl(next_spi) > IPCOMP_LAST_NEGOTIATED)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec Proposal contains CPI from non-negotiated range (0x%lx)"
+ , (unsigned long) ntohl(next_spi));
+ return INVALID_SPI;
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* AH or ESP SPI */
+ if (next_proposal.isap_spisize != IPSEC_DOI_SPI_SIZE)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec Proposal with improper SPI size (%u)"
+ , next_proposal.isap_spisize);
+ return INVALID_SPI;
+ }
+
+ if (!in_raw((u_char *)&next_spi, sizeof(next_spi), &next_proposal_pbs, "SPI"))
+ return INVALID_SPI;
+
+ /* SPI value 0 is invalid and values 1-255 are reserved to IANA.
+ * RFC 2402 (ESP) 2.4, RFC 2406 (AH) 2.1
+ * IPCOMP???
+ */
+ if (ntohl(next_spi) < IPSEC_DOI_SPI_MIN)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec Proposal contains invalid SPI (0x%lx)"
+ , (unsigned long) ntohl(next_spi));
+ return INVALID_SPI;
+ }
+ }
+
+ if (next_proposal.isap_notrans == 0)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec Proposal contains no Transforms");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ switch (next_proposal.isap_protoid)
+ {
+ case PROTO_IPSEC_AH:
+ if (ah_seen)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec SA contains two simultaneous AH Proposals");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ ah_seen = TRUE;
+ ah_prop_pbs = next_proposal_pbs;
+ ah_proposal = next_proposal;
+ ah_spi = next_spi;
+ break;
+
+ case PROTO_IPSEC_ESP:
+ if (esp_seen)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec SA contains two simultaneous ESP Proposals");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ esp_seen = TRUE;
+ esp_prop_pbs = next_proposal_pbs;
+ esp_proposal = next_proposal;
+ esp_spi = next_spi;
+ break;
+
+ case PROTO_IPCOMP:
+ if (ipcomp_seen)
+ {
+ loglog(RC_LOG_SERIOUS, "IPsec SA contains two simultaneous IPCOMP Proposals");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ ipcomp_seen = TRUE;
+ ipcomp_prop_pbs = next_proposal_pbs;
+ ipcomp_proposal = next_proposal;
+ ipcomp_cpi = next_spi;
+ break;
+
+ default:
+ loglog(RC_LOG_SERIOUS, "unexpected Protocol ID (%s) in IPsec Proposal"
+ , enum_show(&protocol_names, next_proposal.isap_protoid));
+ return INVALID_PROTOCOL_ID;
+ }
+
+ /* refill next_proposal */
+ if (next_proposal.isap_np == ISAKMP_NEXT_NONE)
+ {
+ next_full = FALSE;
+ break;
+ }
+ else if (next_proposal.isap_np != ISAKMP_NEXT_P)
+ {
+ loglog(RC_LOG_SERIOUS, "unexpected in Proposal: %s"
+ , enum_show(&payload_names, next_proposal.isap_np));
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ if (!in_struct(&next_proposal, &isakmp_proposal_desc, sa_pbs, &next_proposal_pbs))
+ return BAD_PROPOSAL_SYNTAX;
+ } while (next_proposal.isap_proposal == propno);
+
+ /* Now that we have all conjuncts, we should try
+ * the Cartesian product of eachs tranforms!
+ * At the moment, we take short-cuts on account of
+ * our rudimentary hard-wired policy.
+ * For now, we find an acceptable AH (if any)
+ * and then an acceptable ESP. The only interaction
+ * is that the ESP acceptance can know whether there
+ * was an acceptable AH and hence not require an AUTH.
+ */
+
+ if (ah_seen)
+ {
+ int previous_transnum = -1;
+ int tn;
+
+ for (tn = 0; tn != ah_proposal.isap_notrans; tn++)
+ {
+ int ok_transid = 0;
+ bool ok_auth = FALSE;
+
+ if (!parse_ipsec_transform(&ah_trans
+ , &ah_attrs
+ , &ah_prop_pbs
+ , &ah_trans_pbs
+ , &isakmp_ah_transform_desc
+ , previous_transnum
+ , selection
+ , tn == ah_proposal.isap_notrans - 1
+ , FALSE
+ , st))
+ return BAD_PROPOSAL_SYNTAX;
+
+ previous_transnum = ah_trans.isat_transnum;
+
+ /* we must understand ah_attrs.transid
+ * COMBINED with ah_attrs.auth.
+ * See RFC 2407 "IPsec DOI" section 4.4.3
+ * The following combinations are legal,
+ * but we don't implement all of them:
+ * It seems as if each auth algorithm
+ * only applies to one ah transid.
+ * AH_MD5, AUTH_ALGORITHM_HMAC_MD5
+ * AH_MD5, AUTH_ALGORITHM_KPDK (unimplemented)
+ * AH_SHA, AUTH_ALGORITHM_HMAC_SHA1
+ * AH_DES, AUTH_ALGORITHM_DES_MAC (unimplemented)
+ */
+ switch (ah_attrs.auth)
+ {
+ case AUTH_ALGORITHM_NONE:
+ loglog(RC_LOG_SERIOUS, "AUTH_ALGORITHM attribute missing in AH Transform");
+ return BAD_PROPOSAL_SYNTAX;
+
+ case AUTH_ALGORITHM_HMAC_MD5:
+ ok_auth = TRUE;
+ /* fall through */
+ case AUTH_ALGORITHM_KPDK:
+ ok_transid = AH_MD5;
+ break;
+
+ case AUTH_ALGORITHM_HMAC_SHA1:
+ ok_auth = TRUE;
+ ok_transid = AH_SHA;
+ break;
+
+ case AUTH_ALGORITHM_DES_MAC:
+ ok_transid = AH_DES;
+ break;
+ }
+ if (ah_attrs.transid != ok_transid)
+ {
+ loglog(RC_LOG_SERIOUS, "%s attribute inappropriate in %s Transform"
+ , enum_name(&auth_alg_names, ah_attrs.auth)
+ , enum_show(&ah_transformid_names, ah_attrs.transid));
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ if (!ok_auth)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("%s attribute unsupported"
+ " in %s Transform from %s"
+ , enum_name(&auth_alg_names, ah_attrs.auth)
+ , enum_show(&ah_transformid_names, ah_attrs.transid)
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+ break; /* we seem to be happy */
+ }
+ if (tn == ah_proposal.isap_notrans)
+ continue; /* we didn't find a nice one */
+ ah_attrs.spi = ah_spi;
+ inner_proto = IPPROTO_AH;
+ if (ah_attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL)
+ tunnel_mode = TRUE;
+ }
+
+ if (esp_seen)
+ {
+ int previous_transnum = -1;
+ int tn;
+
+ for (tn = 0; tn != esp_proposal.isap_notrans; tn++)
+ {
+ if (!parse_ipsec_transform(&esp_trans
+ , &esp_attrs
+ , &esp_prop_pbs
+ , &esp_trans_pbs
+ , &isakmp_esp_transform_desc
+ , previous_transnum
+ , selection
+ , tn == esp_proposal.isap_notrans - 1
+ , FALSE
+ , st))
+ return BAD_PROPOSAL_SYNTAX;
+
+ previous_transnum = esp_trans.isat_transnum;
+
+ /* set default key length for AES encryption */
+ if (!esp_attrs.key_len && esp_attrs.transid == ESP_AES)
+ {
+ esp_attrs.key_len = 128 / BITS_PER_BYTE;
+ }
+
+ if (!kernel_alg_esp_enc_ok(esp_attrs.transid, esp_attrs.key_len
+ ,c->alg_info_esp))
+ {
+ switch (esp_attrs.transid)
+ {
+ case ESP_3DES:
+ break;
+#ifdef SUPPORT_ESP_NULL /* should be about as secure as AH-only */
+ case ESP_NULL:
+ if (esp_attrs.auth == AUTH_ALGORITHM_NONE)
+ {
+ loglog(RC_LOG_SERIOUS, "ESP_NULL requires auth algorithm");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+ if (st->st_policy & POLICY_ENCRYPT)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("ESP_NULL Transform Proposal from %s"
+ " does not satisfy POLICY_ENCRYPT"
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+ break;
+#endif
+ default:
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("unsupported ESP Transform %s from %s"
+ , enum_show(&esp_transformid_names, esp_attrs.transid)
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+ }
+
+ if (!kernel_alg_esp_auth_ok(esp_attrs.auth, c->alg_info_esp))
+ {
+ switch (esp_attrs.auth)
+ {
+ case AUTH_ALGORITHM_NONE:
+ if (!ah_seen)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("ESP from %s must either have AUTH or be combined with AH"
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+ break;
+ case AUTH_ALGORITHM_HMAC_MD5:
+ case AUTH_ALGORITHM_HMAC_SHA1:
+ break;
+ default:
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("unsupported ESP auth alg %s from %s"
+ , enum_show(&auth_alg_names, esp_attrs.auth)
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+ }
+
+ /* A last check for allowed transforms in alg_info_esp
+ * (ALG_INFO_F_STRICT flag)
+ */
+ if (!kernel_alg_esp_ok_final(esp_attrs.transid, esp_attrs.key_len
+ ,esp_attrs.auth, c->alg_info_esp))
+ {
+ continue;
+ }
+
+ if (ah_seen && ah_attrs.encapsulation != esp_attrs.encapsulation)
+ {
+ /* ??? This should be an error, but is it? */
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("AH and ESP transforms disagree about encapsulation; TUNNEL presumed"));
+ }
+
+ break; /* we seem to be happy */
+ }
+ if (tn == esp_proposal.isap_notrans)
+ continue; /* we didn't find a nice one */
+
+ esp_attrs.spi = esp_spi;
+ inner_proto = IPPROTO_ESP;
+ if (esp_attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL)
+ tunnel_mode = TRUE;
+ }
+ else if (st->st_policy & POLICY_ENCRYPT)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("policy for \"%s\" requires encryption but ESP not in Proposal from %s"
+ , c->name, ip_str(&c->spd.that.host_addr)));
+ continue; /* we needed encryption, but didn't find ESP */
+ }
+ else if ((st->st_policy & POLICY_AUTHENTICATE) && !ah_seen)
+ {
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("policy for \"%s\" requires authentication"
+ " but none in Proposal from %s"
+ , c->name, ip_str(&c->spd.that.host_addr)));
+ continue; /* we need authentication, but we found neither ESP nor AH */
+ }
+
+ if (ipcomp_seen)
+ {
+ int previous_transnum = -1;
+ int tn;
+
+#ifdef NEVER /* we think IPcomp is working now */
+ /**** FUDGE TO PREVENT UNREQUESTED IPCOMP:
+ **** NEEDED BECAUSE OUR IPCOMP IS EXPERIMENTAL (UNSTABLE).
+ ****/
+ if (!(st->st_policy & POLICY_COMPRESS))
+ {
+ plog("compression proposed by %s, but policy for \"%s\" forbids it"
+ , ip_str(&c->spd.that.host_addr), c->name);
+ continue; /* unwanted compression proposal */
+ }
+#endif
+ if (!can_do_IPcomp)
+ {
+ plog("compression proposed by %s, but KLIPS is not configured with IPCOMP"
+ , ip_str(&c->spd.that.host_addr));
+ continue;
+ }
+
+ if (well_known_cpi != 0 && !ah_seen && !esp_seen)
+ {
+ plog("illegal proposal: bare IPCOMP used with well-known CPI");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ for (tn = 0; tn != ipcomp_proposal.isap_notrans; tn++)
+ {
+ if (!parse_ipsec_transform(&ipcomp_trans
+ , &ipcomp_attrs
+ , &ipcomp_prop_pbs
+ , &ipcomp_trans_pbs
+ , &isakmp_ipcomp_transform_desc
+ , previous_transnum
+ , selection
+ , tn == ipcomp_proposal.isap_notrans - 1
+ , TRUE
+ , st))
+ return BAD_PROPOSAL_SYNTAX;
+
+ previous_transnum = ipcomp_trans.isat_transnum;
+
+ if (well_known_cpi != 0 && ipcomp_attrs.transid != well_known_cpi)
+ {
+ plog("illegal proposal: IPCOMP well-known CPI disagrees with transform");
+ return BAD_PROPOSAL_SYNTAX;
+ }
+
+ switch (ipcomp_attrs.transid)
+ {
+ case IPCOMP_DEFLATE: /* all we can handle! */
+ break;
+
+ default:
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("unsupported IPCOMP Transform %s from %s"
+ , enum_show(&ipcomp_transformid_names, ipcomp_attrs.transid)
+ , ip_str(&c->spd.that.host_addr)));
+ continue; /* try another */
+ }
+
+ if (ah_seen && ah_attrs.encapsulation != ipcomp_attrs.encapsulation)
+ {
+ /* ??? This should be an error, but is it? */
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("AH and IPCOMP transforms disagree about encapsulation; TUNNEL presumed"));
+ } else if (esp_seen && esp_attrs.encapsulation != ipcomp_attrs.encapsulation)
+ {
+ /* ??? This should be an error, but is it? */
+ DBG(DBG_CONTROL | DBG_CRYPT
+ , DBG_log("ESP and IPCOMP transforms disagree about encapsulation; TUNNEL presumed"));
+ }
+
+ break; /* we seem to be happy */
+ }
+ if (tn == ipcomp_proposal.isap_notrans)
+ continue; /* we didn't find a nice one */
+ ipcomp_attrs.spi = ipcomp_cpi;
+ inner_proto = IPPROTO_COMP;
+ if (ipcomp_attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL)
+ tunnel_mode = TRUE;
+ }
+
+ /* Eureka: we liked what we saw -- accept it. */
+
+ if (r_sa_pbs != NULL)
+ {
+ /* emit what we've accepted */
+
+ /* Situation */
+ if (!out_struct(&ipsecdoisit, &ipsec_sit_desc, r_sa_pbs, NULL))
+ impossible();
+
+ /* AH proposal */
+ if (ah_seen)
+ echo_proposal(ah_proposal
+ , ah_trans
+ , esp_seen || ipcomp_seen? ISAKMP_NEXT_P : ISAKMP_NEXT_NONE
+ , r_sa_pbs
+ , &st->st_ah
+ , &isakmp_ah_transform_desc
+ , &ah_trans_pbs
+ , &st->st_connection->spd
+ , tunnel_mode && inner_proto == IPPROTO_AH);
+
+ /* ESP proposal */
+ if (esp_seen)
+ echo_proposal(esp_proposal
+ , esp_trans
+ , ipcomp_seen? ISAKMP_NEXT_P : ISAKMP_NEXT_NONE
+ , r_sa_pbs
+ , &st->st_esp
+ , &isakmp_esp_transform_desc
+ , &esp_trans_pbs
+ , &st->st_connection->spd
+ , tunnel_mode && inner_proto == IPPROTO_ESP);
+
+ /* IPCOMP proposal */
+ if (ipcomp_seen)
+ echo_proposal(ipcomp_proposal
+ , ipcomp_trans
+ , ISAKMP_NEXT_NONE
+ , r_sa_pbs
+ , &st->st_ipcomp
+ , &isakmp_ipcomp_transform_desc
+ , &ipcomp_trans_pbs
+ , &st->st_connection->spd
+ , tunnel_mode && inner_proto == IPPROTO_COMP);
+
+ close_output_pbs(r_sa_pbs);
+ }
+
+ /* save decoded version of winning SA in state */
+
+ st->st_ah.present = ah_seen;
+ if (ah_seen)
+ st->st_ah.attrs = ah_attrs;
+
+ st->st_esp.present = esp_seen;
+ if (esp_seen)
+ st->st_esp.attrs = esp_attrs;
+
+ st->st_ipcomp.present = ipcomp_seen;
+ if (ipcomp_seen)
+ st->st_ipcomp.attrs = ipcomp_attrs;
+
+ return NOTHING_WRONG;
+ }
+
+ loglog(RC_LOG_SERIOUS, "no acceptable Proposal in IPsec SA");
+ return NO_PROPOSAL_CHOSEN;
+}
diff --git a/programs/pluto/spdb.h b/programs/pluto/spdb.h
new file mode 100644
index 000000000..5eebf86cf
--- /dev/null
+++ b/programs/pluto/spdb.h
@@ -0,0 +1,113 @@
+/* Security Policy Data Base (such as it is)
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: spdb.h,v 1.4 2006/04/22 21:59:20 as Exp $
+ */
+
+#ifndef _SPDB_H
+#define _SPDB_H
+
+#include "packet.h"
+
+/* database of SA properties */
+
+/* Attribute type and value pair.
+ * Note: only "basic" values are represented so far.
+ */
+struct db_attr {
+ u_int16_t type; /* ISAKMP_ATTR_AF_TV is implied; 0 for end */
+ u_int16_t val;
+};
+
+/* transform */
+struct db_trans {
+ u_int8_t transid; /* Transform-Id */
+ struct db_attr *attrs; /* array */
+ int attr_cnt; /* number of elements */
+};
+
+/* proposal */
+struct db_prop {
+ u_int8_t protoid; /* Protocol-Id */
+ struct db_trans *trans; /* array (disjunction) */
+ int trans_cnt; /* number of elements */
+ /* SPI size and value isn't part of DB */
+};
+
+/* conjunction of proposals */
+struct db_prop_conj {
+ struct db_prop *props; /* array */
+ int prop_cnt; /* number of elements */
+};
+
+/* security association */
+struct db_sa {
+ struct db_prop_conj *prop_conjs; /* array */
+ int prop_conj_cnt; /* number of elements */
+ /* Hardwired for now;
+ * DOI: ISAKMP_DOI_IPSEC
+ * Situation: SIT_IDENTITY_ONLY
+ */
+};
+
+/* The oakley sadb is subscripted by a bitset with members
+ * from POLICY_PSK and POLICY_RSASIG.
+ */
+extern struct db_sa oakley_sadb[1 << 2];
+
+/* The ipsec sadb is subscripted by a bitset with members
+ * from POLICY_ENCRYPT, POLICY_AUTHENTICATE, POLICY_COMPRESS
+ */
+extern struct db_sa ipsec_sadb[1 << 3];
+
+/* forward declaration */
+struct state;
+
+extern bool out_sa(
+ pb_stream *outs,
+ struct db_sa *sadb,
+ struct state *st,
+ bool oakley_mode,
+ u_int8_t np);
+
+extern notification_t preparse_isakmp_sa_body(
+ const struct isakmp_sa *sa, /* header of input SA Payload */
+ pb_stream *sa_pbs, /* body of input SA Payload */
+ u_int32_t *ipsecdoisit, /* IPsec DOI SIT bitset */
+ pb_stream *proposal_pbs, /* body of proposal Payload */
+ struct isakmp_proposal *proposal);
+
+extern notification_t parse_isakmp_policy(
+ pb_stream *proposal_pbs, /* body of proposal Payload */
+ u_int notrans, /* number of transforms */
+ lset_t *policy); /* RSA or PSK policy */
+
+extern notification_t parse_isakmp_sa_body(
+ u_int32_t ipsecdoisit, /* IPsec DOI SIT bitset */
+ pb_stream *proposal_pbs, /* body of proposal Payload */
+ struct isakmp_proposal *proposal,
+ pb_stream *r_sa_pbs, /* if non-NULL, where to emit winning SA */
+ struct state *st); /* current state object */
+
+extern notification_t parse_ipsec_sa_body(
+ pb_stream *sa_pbs, /* body of input SA Payload */
+ const struct isakmp_sa *sa, /* header of input SA Payload */
+ pb_stream *r_sa_pbs, /* if non-NULL, where to emit winning SA */
+ bool selection, /* if this SA is a selection, only one tranform can appear */
+ struct state *st); /* current state object */
+
+extern void backup_pbs(pb_stream *pbs);
+extern void restore_pbs(pb_stream *pbs);
+
+#endif /* _SPDB_H */
+
diff --git a/programs/pluto/state.c b/programs/pluto/state.c
new file mode 100644
index 000000000..5957654e3
--- /dev/null
+++ b/programs/pluto/state.c
@@ -0,0 +1,1007 @@
+/* routines for state objects
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: state.c,v 1.12 2006/04/03 15:49:36 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "connections.h"
+#include "state.h"
+#include "kernel.h"
+#include "log.h"
+#include "packet.h" /* so we can calculate sizeof(struct isakmp_hdr) */
+#include "keys.h" /* for free_public_key */
+#include "rnd.h"
+#include "timer.h"
+#include "whack.h"
+#include "demux.h" /* needs packet.h */
+#include "ipsec_doi.h" /* needs demux.h and state.h */
+
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h" /* requires sha1.h and md5.h */
+
+/*
+ * Global variables: had to go somewhere, might as well be this file.
+ */
+
+u_int16_t pluto_port = IKE_UDP_PORT; /* Pluto's port */
+
+/*
+ * This file has the functions that handle the
+ * state hash table and the Message ID list.
+ */
+
+/* Message-IDs
+ *
+ * A Message ID is contained in each IKE message header.
+ * For Phase 1 exchanges (Main and Aggressive), it will be zero.
+ * For other exchanges, which must be under the protection of an
+ * ISAKMP SA, the Message ID must be unique within that ISAKMP SA.
+ * Effectively, this labels the message as belonging to a particular
+ * exchange.
+ * BTW, we feel this uniqueness allows rekeying to be somewhat simpler
+ * than specified by draft-jenkins-ipsec-rekeying-06.txt.
+ *
+ * A MessageID is a 32 bit unsigned number. We represent the value
+ * internally in network order -- they are just blobs to us.
+ * They are unsigned numbers to make hashing and comparing easy.
+ *
+ * The following mechanism is used to allocate message IDs. This
+ * requires that we keep track of which numbers have already been used
+ * so that we don't allocate one in use.
+ */
+
+struct msgid_list
+{
+ msgid_t msgid; /* network order */
+ struct msgid_list *next;
+};
+
+bool
+reserve_msgid(struct state *isakmp_sa, msgid_t msgid)
+{
+ struct msgid_list *p;
+
+ passert(msgid != MAINMODE_MSGID);
+ passert(IS_ISAKMP_ENCRYPTED(isakmp_sa->st_state));
+
+ for (p = isakmp_sa->st_used_msgids; p != NULL; p = p->next)
+ if (p->msgid == msgid)
+ return FALSE;
+
+ p = alloc_thing(struct msgid_list, "msgid");
+ p->msgid = msgid;
+ p->next = isakmp_sa->st_used_msgids;
+ isakmp_sa->st_used_msgids = p;
+ return TRUE;
+}
+
+msgid_t
+generate_msgid(struct state *isakmp_sa)
+{
+ int timeout = 100; /* only try so hard for unique msgid */
+ msgid_t msgid;
+
+ passert(IS_ISAKMP_ENCRYPTED(isakmp_sa->st_state));
+
+ for (;;)
+ {
+ get_rnd_bytes((void *) &msgid, sizeof(msgid));
+ if (msgid != 0 && reserve_msgid(isakmp_sa, msgid))
+ break;
+
+ if (--timeout == 0)
+ {
+ plog("gave up looking for unique msgid; using 0x%08lx"
+ , (unsigned long) msgid);
+ break;
+ }
+ }
+ return msgid;
+}
+
+
+/* state table functions */
+
+#define STATE_TABLE_SIZE 32
+
+static struct state *statetable[STATE_TABLE_SIZE];
+
+static struct state **
+state_hash(const u_char *icookie, const u_char *rcookie, const ip_address *peer)
+{
+ u_int i = 0, j;
+ const unsigned char *byte_ptr;
+ size_t length = addrbytesptr(peer, &byte_ptr);
+
+ DBG(DBG_RAW | DBG_CONTROL,
+ DBG_dump("ICOOKIE:", icookie, COOKIE_SIZE);
+ DBG_dump("RCOOKIE:", rcookie, COOKIE_SIZE);
+ DBG_dump("peer:", byte_ptr, length));
+
+ /* XXX the following hash is pretty pathetic */
+
+ for (j = 0; j < COOKIE_SIZE; j++)
+ i = i * 407 + icookie[j] + rcookie[j];
+
+ for (j = 0; j < length; j++)
+ i = i * 613 + byte_ptr[j];
+
+ i = i % STATE_TABLE_SIZE;
+
+ DBG(DBG_CONTROL, DBG_log("state hash entry %d", i));
+
+ return &statetable[i];
+}
+
+/* Get a state object.
+ * Caller must schedule an event for this object so that it doesn't leak.
+ * Caller must insert_state().
+ */
+struct state *
+new_state(void)
+{
+ static const struct state blank_state; /* initialized all to zero & NULL */
+ static so_serial_t next_so = SOS_FIRST;
+ struct state *st;
+
+ st = clone_thing(blank_state, "struct state in new_state()");
+ st->st_serialno = next_so++;
+ passert(next_so > SOS_FIRST); /* overflow can't happen! */
+ st->st_whack_sock = NULL_FD;
+ DBG(DBG_CONTROL, DBG_log("creating state object #%lu at %p",
+ st->st_serialno, (void *) st));
+ return st;
+}
+
+/*
+ * Initialize the state table (and mask*).
+ */
+void
+init_states(void)
+{
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ statetable[i] = (struct state *) NULL;
+}
+
+/* Find the state object with this serial number.
+ * This allows state object references that don't turn into dangerous
+ * dangling pointers: reference a state by its serial number.
+ * Returns NULL if there is no such state.
+ * If this turns out to be a significant CPU hog, it could be
+ * improved to use a hash table rather than sequential seartch.
+ */
+struct state *
+state_with_serialno(so_serial_t sn)
+{
+ if (sn >= SOS_FIRST)
+ {
+ struct state *st;
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ if (st->st_serialno == sn)
+ return st;
+ }
+ return NULL;
+}
+
+/* Insert a state object in the hash table. The object is inserted
+ * at the begining of list.
+ * Needs cookies, connection, and msgid.
+ */
+void
+insert_state(struct state *st)
+{
+ struct state **p = state_hash(st->st_icookie, st->st_rcookie
+ , &st->st_connection->spd.that.host_addr);
+
+ passert(st->st_hashchain_prev == NULL && st->st_hashchain_next == NULL);
+
+ if (*p != NULL)
+ {
+ passert((*p)->st_hashchain_prev == NULL);
+ (*p)->st_hashchain_prev = st;
+ }
+ st->st_hashchain_next = *p;
+ *p = st;
+
+ /* Ensure that somebody is in charge of killing this state:
+ * if no event is scheduled for it, schedule one to discard the state.
+ * If nothing goes wrong, this event will be replaced by
+ * a more appropriate one.
+ */
+ if (st->st_event == NULL)
+ event_schedule(EVENT_SO_DISCARD, 0, st);
+}
+
+/* unlink a state object from the hash table, but don't free it
+ */
+void
+unhash_state(struct state *st)
+{
+ /* unlink from forward chain */
+ struct state **p = st->st_hashchain_prev == NULL
+ ? state_hash(st->st_icookie, st->st_rcookie
+ , &st->st_connection->spd.that.host_addr)
+ : &st->st_hashchain_prev->st_hashchain_next;
+
+ /* unlink from forward chain */
+ passert(*p == st);
+ *p = st->st_hashchain_next;
+
+ /* unlink from backward chain */
+ if (st->st_hashchain_next != NULL)
+ {
+ passert(st->st_hashchain_next->st_hashchain_prev == st);
+ st->st_hashchain_next->st_hashchain_prev = st->st_hashchain_prev;
+ }
+
+ st->st_hashchain_next = st->st_hashchain_prev = NULL;
+}
+
+/* Free the Whack socket file descriptor.
+ * This has the side effect of telling Whack that we're done.
+ */
+void
+release_whack(struct state *st)
+{
+ close_any(st->st_whack_sock);
+}
+
+/* delete a state object */
+void
+delete_state(struct state *st)
+{
+ struct connection *const c = st->st_connection;
+ struct state *old_cur_state = cur_state == st? NULL : cur_state;
+
+ set_cur_state(st);
+
+ /* If DPD is enabled on this state object, clear any pending events */
+ if(st->st_dpd_event != NULL)
+ delete_dpd_event(st);
+
+ /* if there is a suspended state transition, disconnect us */
+ if (st->st_suspended_md != NULL)
+ {
+ passert(st->st_suspended_md->st == st);
+ st->st_suspended_md->st = NULL;
+ }
+
+ /* tell the other side of any IPSEC SAs that are going down */
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state)
+ || IS_ISAKMP_SA_ESTABLISHED(st->st_state))
+ send_delete(st);
+
+ delete_event(st); /* delete any pending timer event */
+
+ /* Ditch anything pending on ISAKMP SA being established.
+ * Note: this must be done before the unhash_state to prevent
+ * flush_pending_by_state inadvertently and prematurely
+ * deleting our connection.
+ */
+ flush_pending_by_state(st);
+
+ /* effectively, this deletes any ISAKMP SA that this state represents */
+ unhash_state(st);
+
+ /* tell kernel to delete any IPSEC SA
+ * ??? we ought to tell peer to delete IPSEC SAs
+ */
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
+ delete_ipsec_sa(st, FALSE);
+ else if (IS_ONLY_INBOUND_IPSEC_SA_ESTABLISHED(st->st_state))
+ delete_ipsec_sa(st, TRUE);
+
+ if (c->newest_ipsec_sa == st->st_serialno)
+ c->newest_ipsec_sa = SOS_NOBODY;
+
+ if (c->newest_isakmp_sa == st->st_serialno)
+ c->newest_isakmp_sa = SOS_NOBODY;
+
+ st->st_connection = NULL; /* we might be about to free it */
+ cur_state = old_cur_state; /* without st_connection, st isn't complete */
+ connection_discard(c);
+
+ release_whack(st);
+
+ /* from here on we are just freeing RAM */
+
+ {
+ struct msgid_list *p = st->st_used_msgids;
+
+ while (p != NULL)
+ {
+ struct msgid_list *q = p;
+ p = p->next;
+ pfree(q);
+ }
+ }
+
+ unreference_key(&st->st_peer_pubkey);
+
+ if (st->st_sec_in_use)
+ mpz_clear(&(st->st_sec));
+
+ pfreeany(st->st_tpacket.ptr);
+ pfreeany(st->st_rpacket.ptr);
+ pfreeany(st->st_p1isa.ptr);
+ pfreeany(st->st_gi.ptr);
+ pfreeany(st->st_gr.ptr);
+ pfreeany(st->st_shared.ptr);
+ pfreeany(st->st_ni.ptr);
+ pfreeany(st->st_nr.ptr);
+ pfreeany(st->st_skeyid.ptr);
+ pfreeany(st->st_skeyid_d.ptr);
+ pfreeany(st->st_skeyid_a.ptr);
+ pfreeany(st->st_skeyid_e.ptr);
+ pfreeany(st->st_enc_key.ptr);
+ pfreeany(st->st_ah.our_keymat);
+ pfreeany(st->st_ah.peer_keymat);
+ pfreeany(st->st_esp.our_keymat);
+ pfreeany(st->st_esp.peer_keymat);
+
+ pfree(st);
+}
+
+/*
+ * Is a connection in use by some state?
+ */
+bool
+states_use_connection(struct connection *c)
+{
+ /* are there any states still using it? */
+ struct state *st = NULL;
+ int i;
+
+ for (i = 0; st == NULL && i < STATE_TABLE_SIZE; i++)
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ if (st->st_connection == c)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * delete all states that were created for a given connection.
+ * if relations == TRUE, then also delete states that share
+ * the same phase 1 SA.
+ */
+void
+delete_states_by_connection(struct connection *c, bool relations)
+{
+ int pass;
+ /* this kludge avoids an n^2 algorithm */
+ enum connection_kind ck = c->kind;
+ struct spd_route *sr;
+
+ /* save this connection's isakmp SA, since it will get set to later SOS_NOBODY */
+ so_serial_t parent_sa = c->newest_isakmp_sa;
+
+ if (ck == CK_INSTANCE)
+ c->kind = CK_GOING_AWAY;
+
+ /* We take two passes so that we delete any ISAKMP SAs last.
+ * This allows Delete Notifications to be sent.
+ * ?? We could probably double the performance by caching any
+ * ISAKMP SA states found in the first pass, avoiding a second.
+ */
+ for (pass = 0; pass != 2; pass++)
+ {
+ int i;
+
+ /* For each hash chain... */
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *st;
+
+ /* For each state in the hash chain... */
+ for (st = statetable[i]; st != NULL; )
+ {
+ struct state *this = st;
+
+ st = st->st_hashchain_next; /* before this is deleted */
+
+
+ if ((this->st_connection == c
+ || (relations && parent_sa != SOS_NOBODY
+ && this->st_clonedfrom == parent_sa))
+ && (pass == 1 || !IS_ISAKMP_SA_ESTABLISHED(this->st_state)))
+ {
+ struct state *old_cur_state
+ = cur_state == this? NULL : cur_state;
+#ifdef DEBUG
+ lset_t old_cur_debugging = cur_debugging;
+#endif
+
+ set_cur_state(this);
+ plog("deleting state (%s)"
+ , enum_show(&state_names, this->st_state));
+ delete_state(this);
+ cur_state = old_cur_state;
+#ifdef DEBUG
+ cur_debugging = old_cur_debugging;
+#endif
+ }
+ }
+ }
+ }
+
+ sr = &c->spd;
+ while (sr != NULL)
+ {
+ passert(sr->eroute_owner == SOS_NOBODY);
+ passert(sr->routing != RT_ROUTED_TUNNEL);
+ sr = sr->next;
+ }
+
+ if (ck == CK_INSTANCE)
+ {
+ c->kind = ck;
+ delete_connection(c, relations);
+ }
+}
+
+/* Walk through the state table, and delete each state whose phase 1 (IKE)
+ * peer is among those given.
+ */
+void
+delete_states_by_peer(ip_address *peer)
+{
+ char peerstr[ADDRTOT_BUF];
+ int i;
+
+ addrtot(peer, 0, peerstr, sizeof(peerstr));
+
+ /* For each hash chain... */
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *st;
+
+ /* For each state in the hash chain... */
+ for (st = statetable[i]; st != NULL; )
+ {
+ struct state *this = st;
+ struct spd_route *sr;
+ struct connection *c = this->st_connection;
+
+ st = st->st_hashchain_next; /* before this is deleted */
+
+ /* ??? Is it not the case that the peer is the same for all spds? */
+ for (sr = &c->spd; sr != NULL; sr = sr->next)
+ {
+ if (sameaddr(&sr->that.host_addr, peer))
+ {
+ plog("peer %s for connection %s deleting - claimed to have crashed"
+ , peerstr
+ , c->name);
+ delete_states_by_connection(c, TRUE);
+ break; /* can only delete it once */
+ }
+ }
+ }
+ }
+}
+
+/* Duplicate a Phase 1 state object, to create a Phase 2 object.
+ * Caller must schedule an event for this object so that it doesn't leak.
+ * Caller must insert_state().
+ */
+struct state *
+duplicate_state(struct state *st)
+{
+ struct state *nst;
+
+ DBG(DBG_CONTROL, DBG_log("duplicating state object #%lu",
+ st->st_serialno));
+
+ /* record use of the Phase 1 state */
+ st->st_outbound_count++;
+ st->st_outbound_time = now();
+
+ nst = new_state();
+
+ memcpy(nst->st_icookie, st->st_icookie, COOKIE_SIZE);
+ memcpy(nst->st_rcookie, st->st_rcookie, COOKIE_SIZE);
+
+ nst->st_connection = st->st_connection;
+ nst->st_doi = st->st_doi;
+ nst->st_situation = st->st_situation;
+ nst->st_clonedfrom = st->st_serialno;
+ nst->st_oakley = st->st_oakley;
+ nst->st_modecfg = st->st_modecfg;
+
+# define clone_chunk(ch, name) \
+ clonetochunk(nst->ch, st->ch.ptr, st->ch.len, name)
+
+ clone_chunk(st_skeyid_d, "st_skeyid_d in duplicate_state");
+ clone_chunk(st_skeyid_a, "st_skeyid_a in duplicate_state");
+ clone_chunk(st_skeyid_e, "st_skeyid_e in duplicate_state");
+ clone_chunk(st_enc_key, "st_enc_key in duplicate_state");
+
+# undef clone_chunk
+
+ return nst;
+}
+
+#if 1
+void for_each_state(void *(f)(struct state *, void *data), void *data)
+{
+ struct state *st, *ocs = cur_state;
+ int i;
+ for (i=0; i<STATE_TABLE_SIZE; i++) {
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next) {
+ set_cur_state(st);
+ f(st, data);
+ }
+ }
+ cur_state = ocs;
+}
+#endif
+
+/*
+ * Find a state object.
+ */
+struct state *
+find_state(const u_char *icookie
+, const u_char *rcookie
+, const ip_address *peer
+, msgid_t /*network order*/ msgid)
+{
+ struct state *st = *state_hash(icookie, rcookie, peer);
+
+ while (st != (struct state *) NULL)
+ if (sameaddr(peer, &st->st_connection->spd.that.host_addr)
+ && memcmp(icookie, st->st_icookie, COOKIE_SIZE) == 0
+ && memcmp(rcookie, st->st_rcookie, COOKIE_SIZE) == 0
+ && msgid == st->st_msgid)
+ break;
+ else
+ st = st->st_hashchain_next;
+
+ DBG(DBG_CONTROL,
+ if (st == NULL)
+ DBG_log("state object not found");
+ else
+ DBG_log("state object #%lu found, in %s"
+ , st->st_serialno
+ , enum_show(&state_names, st->st_state)));
+
+ return st;
+}
+
+/* Find the state that sent a packet
+ * ??? this could be expensive -- it should be rate-limited to avoid DoS
+ */
+struct state *
+find_sender(size_t packet_len, u_char *packet)
+{
+ int i;
+ struct state *st;
+
+ if (packet_len >= sizeof(struct isakmp_hdr))
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ if (st->st_tpacket.ptr != NULL
+ && st->st_tpacket.len == packet_len
+ && memcmp(st->st_tpacket.ptr, packet, packet_len) == 0)
+ return st;
+
+ return NULL;
+}
+
+struct state *
+find_phase2_state_to_delete(const struct state *p1st
+, u_int8_t protoid
+, ipsec_spi_t spi
+, bool *bogus)
+{
+ struct state *st;
+ int i;
+
+ *bogus = FALSE;
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ {
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state)
+ && p1st->st_connection->host_pair == st->st_connection->host_pair
+ && same_peer_ids(p1st->st_connection, st->st_connection, NULL))
+ {
+ struct ipsec_proto_info *pr = protoid == PROTO_IPSEC_AH
+ ? &st->st_ah : &st->st_esp;
+
+ if (pr->present)
+ {
+ if (pr->attrs.spi == spi)
+ return st;
+ if (pr->our_spi == spi)
+ *bogus = TRUE;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Find newest Phase 1 negotiation state object for suitable for connection c
+ */
+struct state *
+find_phase1_state(const struct connection *c, lset_t ok_states)
+{
+ struct state
+ *st,
+ *best = NULL;
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ if (LHAS(ok_states, st->st_state)
+ && c->host_pair == st->st_connection->host_pair
+ && same_peer_ids(c, st->st_connection, NULL)
+ && (best == NULL || best->st_serialno < st->st_serialno))
+ best = st;
+
+ return best;
+}
+
+void
+state_eroute_usage(ip_subnet *ours, ip_subnet *his
+, unsigned long count, time_t nw)
+{
+ struct state *st;
+ int i;
+
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ {
+ struct connection *c = st->st_connection;
+
+ /* XXX spd-enum */
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state)
+ && c->spd.eroute_owner == st->st_serialno
+ && c->spd.routing == RT_ROUTED_TUNNEL
+ && samesubnet(&c->spd.this.client, ours)
+ && samesubnet(&c->spd.that.client, his))
+ {
+ if (st->st_outbound_count != count)
+ {
+ st->st_outbound_count = count;
+ st->st_outbound_time = nw;
+ }
+ return;
+ }
+ }
+ }
+ DBG(DBG_CONTROL,
+ {
+ char ourst[SUBNETTOT_BUF];
+ char hist[SUBNETTOT_BUF];
+
+ subnettot(ours, 0, ourst, sizeof(ourst));
+ subnettot(his, 0, hist, sizeof(hist));
+ DBG_log("unknown tunnel eroute %s -> %s found in scan"
+ , ourst, hist);
+ });
+}
+
+void fmt_state(struct state *st, time_t n
+, char *state_buf, size_t state_buf_len
+, char *state_buf2, size_t state_buf2_len)
+{
+ /* what the heck is interesting about a state? */
+ const struct connection *c = st->st_connection;
+
+ long delta = st->st_event->ev_time >= n
+ ? (long)(st->st_event->ev_time - n)
+ : -(long)(n - st->st_event->ev_time);
+
+ char inst[CONN_INST_BUF];
+ const char *np1 = c->newest_isakmp_sa == st->st_serialno
+ ? "; newest ISAKMP" : "";
+ const char *np2 = c->newest_ipsec_sa == st->st_serialno
+ ? "; newest IPSEC" : "";
+ /* XXX spd-enum */
+ const char *eo = c->spd.eroute_owner == st->st_serialno
+ ? "; eroute owner" : "";
+
+ passert(st->st_event != 0);
+
+ fmt_conn_instance(c, inst);
+
+ snprintf(state_buf, state_buf_len
+ , "#%lu: \"%s\"%s %s (%s); %s in %lds%s%s%s"
+ , st->st_serialno
+ , c->name, inst
+ , enum_name(&state_names, st->st_state)
+ , state_story[st->st_state - STATE_MAIN_R0]
+ , enum_name(&timer_event_names, st->st_event->ev_type)
+ , delta
+ , np1, np2, eo);
+
+ /* print out SPIs if SAs are established */
+ if (state_buf2_len != 0)
+ state_buf2[0] = '\0'; /* default to empty */
+ if (IS_IPSEC_SA_ESTABLISHED(st->st_state))
+ {
+
+ bool tunnel;
+ char buf[SATOT_BUF*6 + 2*20 + 1];
+ const char *p_end = buf + sizeof(buf);
+ char *p = buf;
+
+# define add_said(adst, aspi, aproto) { \
+ ip_said s; \
+ \
+ initsaid(adst, aspi, aproto, &s); \
+ if (p < p_end - 1) \
+ { \
+ *p++ = ' '; \
+ p += satot(&s, 0, p, p_end - p) - 1; \
+ } \
+ }
+
+# define add_sa_info(st, inbound) { \
+ u_int bytes; \
+ time_t use_time; \
+ \
+ if (get_sa_info(st, inbound, &bytes, &use_time)) \
+ { \
+ p += snprintf(p, p_end - p, " (%'u bytes", bytes); \
+ if (bytes > 0 && use_time != UNDEFINED_TIME) \
+ p += snprintf(p, p_end - p, ", %ds ago", (int)(now - use_time)); \
+ p += snprintf(p, p_end - p, ")"); \
+ } \
+ }
+
+ *p = '\0';
+ if (st->st_ah.present)
+ {
+ add_said(&c->spd.that.host_addr, st->st_ah.attrs.spi, SA_AH);
+ add_said(&c->spd.this.host_addr, st->st_ah.our_spi, SA_AH);
+ }
+ if (st->st_esp.present)
+ {
+ time_t now = time(NULL);
+
+ add_said(&c->spd.that.host_addr, st->st_esp.attrs.spi, SA_ESP);
+ add_sa_info(st, FALSE);
+ add_said(&c->spd.this.host_addr, st->st_esp.our_spi, SA_ESP);
+ add_sa_info(st, TRUE);
+ }
+ if (st->st_ipcomp.present)
+ {
+ add_said(&c->spd.that.host_addr, st->st_ipcomp.attrs.spi, SA_COMP);
+ add_said(&c->spd.this.host_addr, st->st_ipcomp.our_spi, SA_COMP);
+ }
+#ifdef KLIPS
+ tunnel = st->st_ah.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL
+ || st->st_esp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL
+ || st->st_ipcomp.attrs.encapsulation == ENCAPSULATION_MODE_TUNNEL;
+ p += snprintf(p, p_end - p, "; %s", tunnel? "tunnel":"transport");
+#endif
+
+ snprintf(state_buf2, state_buf2_len
+ , "#%lu: \"%s\"%s%s"
+ , st->st_serialno
+ , c->name, inst
+ , buf);
+
+# undef add_said
+# undef add_sa_info
+ }
+}
+
+/*
+ * sorting logic is:
+ *
+ * name
+ * type
+ * instance#
+ * isakmp_sa (XXX probably wrong)
+ *
+ */
+static int
+state_compare(const void *a, const void *b)
+{
+ const struct state *sap = *(const struct state *const *)a;
+ struct connection *ca = sap->st_connection;
+ const struct state *sbp = *(const struct state *const *)b;
+ struct connection *cb = sbp->st_connection;
+
+ /* DBG_log("comparing %s to %s", ca->name, cb->name); */
+
+ return connection_compare(ca, cb);
+}
+
+void
+show_states_status(const char *name)
+{
+ time_t n = now();
+ int i;
+ char state_buf[LOG_WIDTH];
+ char state_buf2[LOG_WIDTH];
+ int count;
+ struct state **array;
+
+ /* make count of states */
+ count = 0;
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *st;
+
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ {
+ if (name == NULL || streq(name, st->st_connection->name))
+ count++;
+ }
+ }
+
+ /* build the array */
+ array = alloc_bytes(sizeof(struct state *)*count, "state array");
+ count = 0;
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *st;
+
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ {
+ if (name == NULL || streq(name, st->st_connection->name))
+ array[count++]=st;
+ }
+ }
+
+ /* sort it! */
+ qsort(array, count, sizeof(struct state *), state_compare);
+
+ /* now print sorted results */
+ for (i = 0; i < count; i++)
+ {
+ struct state *st;
+
+ st = array[i];
+
+ fmt_state(st, n, state_buf, sizeof(state_buf)
+ , state_buf2, sizeof(state_buf2));
+ whack_log(RC_COMMENT, state_buf);
+ if (state_buf2[0] != '\0')
+ whack_log(RC_COMMENT, state_buf2);
+
+ /* show any associated pending Phase 2s */
+ if (IS_PHASE1(st->st_state))
+ show_pending_phase2(st->st_connection->host_pair, st);
+ }
+
+ /* free the array */
+ pfree(array);
+}
+
+/* Given that we've used up a range of unused CPI's,
+ * search for a new range of currently unused ones.
+ * Note: this is very expensive when not trivial!
+ * If we can't find one easily, choose 0 (a bad SPI,
+ * no matter what order) indicating failure.
+ */
+void
+find_my_cpi_gap(cpi_t *latest_cpi, cpi_t *first_busy_cpi)
+{
+ int tries = 0;
+ cpi_t base = *latest_cpi;
+ cpi_t closest;
+ int i;
+
+startover:
+ closest = ~0; /* not close at all */
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *st;
+
+ for (st = statetable[i]; st != NULL; st = st->st_hashchain_next)
+ {
+ if (st->st_ipcomp.present)
+ {
+ cpi_t c = ntohl(st->st_ipcomp.our_spi) - base;
+
+ if (c < closest)
+ {
+ if (c == 0)
+ {
+ /* oops: next spot is occupied; start over */
+ if (++tries == 20)
+ {
+ /* FAILURE */
+ *latest_cpi = *first_busy_cpi = 0;
+ return;
+ }
+ base++;
+ if (base > IPCOMP_LAST_NEGOTIATED)
+ base = IPCOMP_FIRST_NEGOTIATED;
+ goto startover; /* really a tail call */
+ }
+ closest = c;
+ }
+ }
+ }
+ }
+ *latest_cpi = base; /* base is first in next free range */
+ *first_busy_cpi = closest + base; /* and this is the roof */
+}
+
+/* Muck with high-order 16 bits of this SPI in order to make
+ * the corresponding SAID unique.
+ * Its low-order 16 bits hold a well-known IPCOMP CPI.
+ * Oh, and remember that SPIs are stored in network order.
+ * Kludge!!! So I name it with the non-English word "uniquify".
+ * If we can't find one easily, return 0 (a bad SPI,
+ * no matter what order) indicating failure.
+ */
+ipsec_spi_t
+uniquify_his_cpi(ipsec_spi_t cpi, struct state *st)
+{
+ int tries = 0;
+ int i;
+
+startover:
+
+ /* network order makes first two bytes our target */
+ get_rnd_bytes((u_char *)&cpi, 2);
+
+ /* Make sure that the result is unique.
+ * Hard work. If there is no unique value, we'll loop forever!
+ */
+ for (i = 0; i < STATE_TABLE_SIZE; i++)
+ {
+ struct state *s;
+
+ for (s = statetable[i]; s != NULL; s = s->st_hashchain_next)
+ {
+ if (s->st_ipcomp.present
+ && sameaddr(&s->st_connection->spd.that.host_addr
+ , &st->st_connection->spd.that.host_addr)
+ && cpi == s->st_ipcomp.attrs.spi)
+ {
+ if (++tries == 20)
+ return 0; /* FAILURE */
+ goto startover;
+ }
+ }
+ }
+ return cpi;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset:4
+ * End:
+ */
diff --git a/programs/pluto/state.h b/programs/pluto/state.h
new file mode 100644
index 000000000..2f30d77f1
--- /dev/null
+++ b/programs/pluto/state.h
@@ -0,0 +1,269 @@
+/* state and event objects
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: state.h,v 1.11 2006/03/08 22:12:37 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <gmp.h> /* GNU MP library */
+
+#include "connections.h"
+
+/* Message ID mechanism.
+ *
+ * A Message ID is contained in each IKE message header.
+ * For Phase 1 exchanges (Main and Aggressive), it will be zero.
+ * For other exchanges, which must be under the protection of an
+ * ISAKMP SA, the Message ID must be unique within that ISAKMP SA.
+ * Effectively, this labels the message as belonging to a particular
+ * exchange.
+ *
+ * RFC2408 "ISAKMP" 3.1 "ISAKMP Header Format" (near end) states that
+ * the Message ID must be unique. We interpret this to be "unique within
+ * one ISAKMP SA".
+ *
+ * BTW, we feel this uniqueness allows rekeying to be somewhat simpler
+ * than specified by draft-jenkins-ipsec-rekeying-06.txt.
+ */
+
+typedef u_int32_t msgid_t; /* Network order! */
+#define MAINMODE_MSGID ((msgid_t) 0)
+
+struct state; /* forward declaration of tag */
+extern bool reserve_msgid(struct state *isakmp_sa, msgid_t msgid);
+extern msgid_t generate_msgid(struct state *isakmp_sa);
+
+
+/* Oakley (Phase 1 / Main Mode) transform and attributes
+ * This is a flattened/decoded version of what is represented
+ * in the Transaction Payload.
+ * Names are chosen to match corresponding names in state.
+ */
+struct oakley_trans_attrs {
+ u_int16_t encrypt; /* Encryption algorithm */
+ u_int16_t enckeylen; /* encryption key len (bits) */
+ const struct encrypt_desc *encrypter; /* package of encryption routines */
+ u_int16_t hash; /* Hash algorithm */
+ const struct hash_desc *hasher; /* package of hashing routines */
+ u_int16_t auth; /* Authentication method */
+ const struct oakley_group_desc *group; /* Oakley group */
+ time_t life_seconds; /* When this SA expires (seconds) */
+ u_int32_t life_kilobytes; /* When this SA is exhausted (kilobytes) */
+#if 0 /* not yet */
+ u_int16_t prf; /* Pseudo Random Function */
+#endif
+};
+
+/* IPsec (Phase 2 / Quick Mode) transform and attributes
+ * This is a flattened/decoded version of what is represented
+ * by a Transaction Payload. There may be one for AH, one
+ * for ESP, and a funny one for IPCOMP.
+ */
+struct ipsec_trans_attrs {
+ u_int8_t transid; /* transform id */
+ ipsec_spi_t spi; /* his SPI */
+ time_t life_seconds; /* When this SA expires */
+ u_int32_t life_kilobytes; /* When this SA expires */
+ u_int16_t encapsulation;
+ u_int16_t auth;
+ u_int16_t key_len;
+ u_int16_t key_rounds;
+#if 0 /* not implemented yet */
+ u_int16_t cmprs_dict_sz;
+ u_int32_t cmprs_alg;
+#endif
+};
+
+/* IPsec per protocol state information */
+struct ipsec_proto_info {
+ bool present; /* was this transform specified? */
+ struct ipsec_trans_attrs attrs;
+ ipsec_spi_t our_spi;
+ u_int16_t keymat_len; /* same for both */
+ u_char *our_keymat;
+ u_char *peer_keymat;
+};
+
+/* state object: record the state of a (possibly nascent) SA
+ *
+ * Invariants (violated only during short transitions):
+ * - each state object will be in statetable exactly once.
+ * - each state object will always have a pending event.
+ * This prevents leaks.
+ */
+struct state
+{
+ so_serial_t st_serialno; /* serial number (for seniority) */
+ so_serial_t st_clonedfrom; /* serial number of parent */
+
+ struct connection *st_connection; /* connection for this SA */
+
+ int st_whack_sock; /* fd for our Whack TCP socket.
+ * Single copy: close when freeing struct.
+ */
+
+ struct msg_digest *st_suspended_md; /* suspended state-transition */
+
+ struct oakley_trans_attrs st_oakley;
+
+ struct ipsec_proto_info st_ah;
+ struct ipsec_proto_info st_esp;
+ struct ipsec_proto_info st_ipcomp;
+#ifdef KLIPS
+ ipsec_spi_t st_tunnel_in_spi; /* KLUDGE */
+ ipsec_spi_t st_tunnel_out_spi; /* KLUDGE */
+#endif
+
+ const struct oakley_group_desc *st_pfs_group; /* group for Phase 2 PFS */
+
+ u_int32_t st_doi; /* Domain of Interpretation */
+ u_int32_t st_situation;
+
+ lset_t st_policy; /* policy for IPsec SA */
+
+ msgid_t st_msgid; /* MSG-ID from header. Network Order! */
+
+ /* only for a state representing an ISAKMP SA */
+ struct msgid_list *st_used_msgids; /* used-up msgids */
+
+/* symmetric stuff */
+
+ /* initiator stuff */
+ chunk_t st_gi; /* Initiator public value */
+ u_int8_t st_icookie[COOKIE_SIZE];/* Initiator Cookie */
+ chunk_t st_ni; /* Ni nonce */
+
+ /* responder stuff */
+ chunk_t st_gr; /* Responder public value */
+ u_int8_t st_rcookie[COOKIE_SIZE];/* Responder Cookie */
+ chunk_t st_nr; /* Nr nonce */
+
+
+ /* my stuff */
+
+ chunk_t st_tpacket; /* Transmitted packet */
+
+ /* Phase 2 ID payload info about my user */
+ u_int8_t st_myuserprotoid; /* IDcx.protoid */
+ u_int16_t st_myuserport;
+
+ /* his stuff */
+
+ chunk_t st_rpacket; /* Received packet */
+
+ /* Phase 2 ID payload info about peer's user */
+ u_int8_t st_peeruserprotoid; /* IDcx.protoid */
+ u_int16_t st_peeruserport;
+
+/* end of symmetric stuff */
+
+ u_int8_t st_sec_in_use; /* bool: does st_sec hold a value */
+ MP_INT st_sec; /* Our local secret value */
+
+ chunk_t st_shared; /* Derived shared secret
+ * Note: during Quick Mode,
+ * presence indicates PFS
+ * selected.
+ */
+
+ /* In a Phase 1 state, preserve peer's public key after authentication */
+ struct pubkey *st_peer_pubkey;
+
+ enum state_kind st_state; /* State of exchange */
+ u_int8_t st_retransmit; /* Number of retransmits */
+ unsigned long st_try; /* number of times rekeying attempted */
+ /* 0 means the only time */
+ time_t st_margin; /* life after EVENT_SA_REPLACE */
+ unsigned long st_outbound_count; /* traffic through eroute */
+ time_t st_outbound_time; /* time of last change to st_outbound_count */
+ chunk_t st_p1isa; /* Phase 1 initiator SA (Payload) for HASH */
+ chunk_t st_skeyid; /* Key material */
+ chunk_t st_skeyid_d; /* KM for non-ISAKMP key derivation */
+ chunk_t st_skeyid_a; /* KM for ISAKMP authentication */
+ chunk_t st_skeyid_e; /* KM for ISAKMP encryption */
+ u_char st_iv[MAX_DIGEST_LEN]; /* IV for encryption */
+ u_char st_new_iv[MAX_DIGEST_LEN];
+ u_char st_ph1_iv[MAX_DIGEST_LEN]; /* IV at end if phase 1 */
+ unsigned int st_iv_len;
+ unsigned int st_new_iv_len;
+ unsigned int st_ph1_iv_len;
+
+ chunk_t st_enc_key; /* Oakley Encryption key */
+
+ struct event *st_event; /* backpointer for certain events */
+ struct state *st_hashchain_next; /* Next in list */
+ struct state *st_hashchain_prev; /* Previous in list */
+
+ struct {
+ bool vars_set;
+ bool started;
+ } st_modecfg;
+
+#ifdef NAT_TRAVERSAL
+ u_int32_t nat_traversal;
+ ip_address nat_oa;
+#endif
+
+ /* RFC 3706 Dead Peer Detection */
+ bool st_dpd; /* Peer supports DPD */
+ time_t st_last_dpd; /* Time of last DPD transmit */
+ u_int32_t st_dpd_seqno; /* Next R_U_THERE to send */
+ u_int32_t st_dpd_expectseqno; /* Next R_U_THERE_ACK to receive */
+ u_int32_t st_dpd_peerseqno; /* global variables */
+ struct event *st_dpd_event; /* backpointer for DPD events */
+
+ u_int32_t st_seen_vendorid; /* Bit field about recognized Vendor ID */
+};
+
+/* global variables */
+
+extern u_int16_t pluto_port; /* Pluto's port */
+
+extern bool states_use_connection(struct connection *c);
+
+/* state functions */
+
+extern struct state *new_state(void);
+extern void init_states(void);
+extern void insert_state(struct state *st);
+extern void unhash_state(struct state *st);
+extern void release_whack(struct state *st);
+extern void state_eroute_usage(ip_subnet *ours, ip_subnet *his
+ , unsigned long count, time_t nw);
+extern void delete_state(struct state *st);
+extern void delete_states_by_connection(struct connection *c, bool relations);
+
+extern struct state
+ *duplicate_state(struct state *st),
+ *find_state(const u_char *icookie
+ , const u_char *rcookie
+ , const ip_address *peer
+ , msgid_t msgid),
+ *state_with_serialno(so_serial_t sn),
+ *find_phase2_state_to_delete(const struct state *p1st, u_int8_t protoid
+ , ipsec_spi_t spi, bool *bogus),
+ *find_phase1_state(const struct connection *c, lset_t ok_states),
+ *find_sender(size_t packet_len, u_char *packet);
+
+extern void show_states_status(const char *name);
+extern void for_each_state(void *(f)(struct state *, void *data), void *data);
+extern void find_my_cpi_gap(cpi_t *latest_cpi, cpi_t *first_busy_cpi);
+extern ipsec_spi_t uniquify_his_cpi(ipsec_spi_t cpi, struct state *st);
+extern void fmt_state(struct state *st, time_t n
+ , char *state_buf, size_t state_buf_len
+ , char *state_buf2, size_t state_buf_len2);
+extern void delete_states_by_peer(ip_address *peer);
diff --git a/programs/pluto/timer.c b/programs/pluto/timer.c
new file mode 100644
index 000000000..4d9ef8fab
--- /dev/null
+++ b/programs/pluto/timer.c
@@ -0,0 +1,537 @@
+/* timer event handling
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: timer.c,v 1.5 2004/09/17 21:36:57 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "connections.h"
+#include "state.h"
+#include "demux.h"
+#include "ipsec_doi.h" /* needs demux.h and state.h */
+#include "kernel.h"
+#include "server.h"
+#include "log.h"
+#include "rnd.h"
+#include "timer.h"
+#include "whack.h"
+
+#ifdef NAT_TRAVERSAL
+#include "nat_traversal.h"
+#endif
+
+/* monotonic version of time(3) */
+time_t
+now(void)
+{
+ static time_t delta = 0
+ , last_time = 0;
+ time_t n = time((time_t)NULL);
+
+ passert(n != (time_t)-1);
+ if (last_time > n)
+ {
+ plog("time moved backwards %ld seconds", (long)(last_time - n));
+ delta += last_time - n;
+ }
+ last_time = n;
+ return n + delta;
+}
+
+/* This file has the event handling routines. Events are
+ * kept as a linked list of event structures. These structures
+ * have information like event type, expiration time and a pointer
+ * to event specific data (for example, to a state structure).
+ */
+
+static struct event *evlist = (struct event *) NULL;
+
+/*
+ * This routine places an event in the event list.
+ */
+void
+event_schedule(enum event_type type, time_t tm, struct state *st)
+{
+ struct event *ev = alloc_thing(struct event, "struct event in event_schedule()");
+
+ ev->ev_type = type;
+ ev->ev_time = tm + now();
+ ev->ev_state = st;
+
+ /* If the event is associated with a state, put a backpointer to the
+ * event in the state object, so we can find and delete the event
+ * if we need to (for example, if we receive a reply).
+ */
+ if (st != NULL)
+ {
+ if (type == EVENT_DPD || type == EVENT_DPD_TIMEOUT)
+ {
+ passert(st->st_dpd_event == NULL);
+ st->st_dpd_event = ev;
+ }
+ else
+ {
+ passert(st->st_event == NULL);
+ st->st_event = ev;
+ }
+ }
+
+ DBG(DBG_CONTROL,
+ if (st == NULL)
+ DBG_log("inserting event %s, timeout in %lu seconds"
+ , enum_show(&timer_event_names, type), (unsigned long)tm);
+ else
+ DBG_log("inserting event %s, timeout in %lu seconds for #%lu"
+ , enum_show(&timer_event_names, type), (unsigned long)tm
+ , ev->ev_state->st_serialno));
+
+ if (evlist == (struct event *) NULL
+ || evlist->ev_time >= ev->ev_time)
+ {
+ ev->ev_next = evlist;
+ evlist = ev;
+ }
+ else
+ {
+ struct event *evt;
+
+ for (evt = evlist; evt->ev_next != NULL; evt = evt->ev_next)
+ if (evt->ev_next->ev_time >= ev->ev_time)
+ break;
+
+#ifdef NEVER /* this seems to be overkill */
+ DBG(DBG_CONTROL,
+ if (evt->ev_state == NULL)
+ DBG_log("event added after event %s"
+ , enum_show(&timer_event_names, evt->ev_type));
+ else
+ DBG_log("event added after event %s for #%lu"
+ , enum_show(&timer_event_names, evt->ev_type)
+ , evt->ev_state->st_serialno));
+#endif /* NEVER */
+
+ ev->ev_next = evt->ev_next;
+ evt->ev_next = ev;
+ }
+}
+
+/*
+ * Handle the first event on the list.
+ */
+void
+handle_timer_event(void)
+{
+ time_t tm;
+ struct event *ev = evlist;
+ int type;
+ struct state *st;
+ struct connection *c = NULL;
+ ip_address peer;
+
+ if (ev == (struct event *) NULL) /* Just paranoid */
+ {
+ DBG(DBG_CONTROL, DBG_log("empty event list, yet we're called"));
+ return;
+ }
+
+ type = ev->ev_type;
+ st = ev->ev_state;
+
+ tm = now();
+
+ if (tm < ev->ev_time)
+ {
+ DBG(DBG_CONTROL, DBG_log("called while no event expired (%lu/%lu, %s)"
+ , (unsigned long)tm, (unsigned long)ev->ev_time
+ , enum_show(&timer_event_names, type)));
+
+ /* This will happen if the most close-to-expire event was
+ * a retransmission or cleanup, and we received a packet
+ * at the same time as the event expired. Due to the processing
+ * order in call_server(), the packet processing will happen first,
+ * and the event will be removed.
+ */
+ return;
+ }
+
+ evlist = evlist->ev_next; /* Ok, we'll handle this event */
+
+ DBG(DBG_CONTROL,
+ if (evlist != (struct event *) NULL)
+ DBG_log("event after this is %s in %ld seconds"
+ , enum_show(&timer_event_names, evlist->ev_type)
+ , (long) (evlist->ev_time - tm)));
+
+ /* for state-associated events, pick up the state pointer
+ * and remove the backpointer from the state object.
+ * We'll eventually either schedule a new event, or delete the state.
+ */
+ passert(GLOBALS_ARE_RESET());
+ if (st != NULL)
+ {
+ c = st->st_connection;
+ if (type == EVENT_DPD || type == EVENT_DPD_TIMEOUT)
+ {
+ passert(st->st_dpd_event == ev);
+ st->st_dpd_event = NULL;
+ }
+ else
+ {
+ passert(st->st_event == ev);
+ st->st_event = NULL;
+ }
+ peer = c->spd.that.host_addr;
+ set_cur_state(st);
+ }
+
+ switch (type)
+ {
+ case EVENT_REINIT_SECRET:
+ passert(st == NULL);
+ DBG(DBG_CONTROL, DBG_log("event EVENT_REINIT_SECRET handled"));
+ init_secret();
+ break;
+
+#ifdef KLIPS
+ case EVENT_SHUNT_SCAN:
+ passert(st == NULL);
+ scan_proc_shunts();
+ break;
+#endif
+
+ case EVENT_LOG_DAILY:
+ daily_log_event();
+ break;
+
+ case EVENT_RETRANSMIT:
+ /* Time to retransmit, or give up.
+ *
+ * Generally, we'll only try to send the message
+ * MAXIMUM_RETRANSMISSIONS times. Each time we double
+ * our patience.
+ *
+ * As a special case, if this is the first initiating message
+ * of a Main Mode exchange, and we have been directed to try
+ * forever, we'll extend the number of retransmissions to
+ * MAXIMUM_RETRANSMISSIONS_INITIAL times, with all these
+ * extended attempts having the same patience. The intention
+ * is to reduce the bother when nobody is home.
+ */
+ {
+ time_t delay = 0;
+
+ DBG(DBG_CONTROL, DBG_log(
+ "handling event EVENT_RETRANSMIT for %s \"%s\" #%lu"
+ , ip_str(&peer), c->name, st->st_serialno));
+
+ if (st->st_retransmit < MAXIMUM_RETRANSMISSIONS)
+ delay = EVENT_RETRANSMIT_DELAY_0 << (st->st_retransmit + 1);
+ else if (st->st_state == STATE_MAIN_I1
+ && c->sa_keying_tries == 0
+ && st->st_retransmit < MAXIMUM_RETRANSMISSIONS_INITIAL)
+ delay = EVENT_RETRANSMIT_DELAY_0 << MAXIMUM_RETRANSMISSIONS;
+
+ if (delay != 0)
+ {
+ st->st_retransmit++;
+ whack_log(RC_RETRANSMISSION
+ , "%s: retransmission; will wait %lus for response"
+ , enum_name(&state_names, st->st_state)
+ , (unsigned long)delay);
+ send_packet(st, "EVENT_RETRANSMIT");
+ event_schedule(EVENT_RETRANSMIT, delay, st);
+ }
+ else
+ {
+ /* check if we've tried rekeying enough times.
+ * st->st_try == 0 means that this should be the only try.
+ * c->sa_keying_tries == 0 means that there is no limit.
+ */
+ unsigned long try = st->st_try;
+ unsigned long try_limit = c->sa_keying_tries;
+ const char *details = "";
+
+ switch (st->st_state)
+ {
+ case STATE_MAIN_I3:
+ details = ". Possible authentication failure:"
+ " no acceptable response to our"
+ " first encrypted message";
+ break;
+ case STATE_MAIN_I1:
+ details = ". No response (or no acceptable response) to our"
+ " first IKE message";
+ break;
+ case STATE_QUICK_I1:
+ if (c->newest_ipsec_sa == SOS_NOBODY)
+ details = ". No acceptable response to our"
+ " first Quick Mode message:"
+ " perhaps peer likes no proposal";
+ break;
+ default:
+ break;
+ }
+ loglog(RC_NORETRANSMISSION
+ , "max number of retransmissions (%d) reached %s%s"
+ , st->st_retransmit
+ , enum_show(&state_names, st->st_state), details);
+ if (try != 0 && try != try_limit)
+ {
+ /* A lot like EVENT_SA_REPLACE, but over again.
+ * Since we know that st cannot be in use,
+ * we can delete it right away.
+ */
+ char story[80]; /* arbitrary limit */
+
+ try++;
+ snprintf(story, sizeof(story), try_limit == 0
+ ? "starting keying attempt %ld of an unlimited number"
+ : "starting keying attempt %ld of at most %ld"
+ , try, try_limit);
+
+ if (st->st_whack_sock != NULL_FD)
+ {
+ /* Release whack because the observer will get bored. */
+ loglog(RC_COMMENT, "%s, but releasing whack"
+ , story);
+ release_pending_whacks(st, story);
+ }
+ else
+ {
+ /* no whack: just log to syslog */
+ plog("%s", story);
+ }
+ ipsecdoi_replace(st, try);
+ }
+ delete_state(st);
+ }
+ }
+ break;
+
+ case EVENT_SA_REPLACE:
+ case EVENT_SA_REPLACE_IF_USED:
+ {
+ so_serial_t newest = IS_PHASE1(st->st_state)
+ ? c->newest_isakmp_sa : c->newest_ipsec_sa;
+
+ if (newest != st->st_serialno
+ && newest != SOS_NOBODY)
+ {
+ /* not very interesting: no need to replace */
+ DBG(DBG_LIFECYCLE
+ , plog("not replacing stale %s SA: #%lu will do"
+ , IS_PHASE1(st->st_state)? "ISAKMP" : "IPsec"
+ , newest));
+ }
+ else if (type == EVENT_SA_REPLACE_IF_USED
+ && st->st_outbound_time <= tm - c->sa_rekey_margin)
+ {
+ /* we observed no recent use: no need to replace
+ *
+ * The sampling effects mean that st_outbound_time
+ * could be up to SHUNT_SCAN_INTERVAL more recent
+ * than actual traffic because the sampler looks at change
+ * over that interval.
+ * st_outbound_time could also not yet reflect traffic
+ * in the last SHUNT_SCAN_INTERVAL.
+ * We expect that SHUNT_SCAN_INTERVAL is smaller than
+ * c->sa_rekey_margin so that the effects of this will
+ * be unimportant.
+ * This is just an optimization: correctness is not
+ * at stake.
+ *
+ * Note: we are abusing the DBG mechanism to control
+ * normal log output.
+ */
+ DBG(DBG_LIFECYCLE
+ , plog("not replacing stale %s SA: inactive for %lus"
+ , IS_PHASE1(st->st_state)? "ISAKMP" : "IPsec"
+ , (unsigned long)(tm - st->st_outbound_time)));
+ }
+ else
+ {
+ DBG(DBG_LIFECYCLE
+ , plog("replacing stale %s SA"
+ , IS_PHASE1(st->st_state)? "ISAKMP" : "IPsec"));
+ ipsecdoi_replace(st, 1);
+ }
+ delete_dpd_event(st);
+ event_schedule(EVENT_SA_EXPIRE, st->st_margin, st);
+ }
+ break;
+
+ case EVENT_SA_EXPIRE:
+ {
+ const char *satype;
+ so_serial_t latest;
+
+ if (IS_PHASE1(st->st_state))
+ {
+ satype = "ISAKMP";
+ latest = c->newest_isakmp_sa;
+ }
+ else
+ {
+ satype = "IPsec";
+ latest = c->newest_ipsec_sa;
+ }
+
+ if (st->st_serialno != latest)
+ {
+ /* not very interesting: already superseded */
+ DBG(DBG_LIFECYCLE
+ , plog("%s SA expired (superseded by #%lu)"
+ , satype, latest));
+ }
+ else
+ {
+ plog("%s SA expired (%s)", satype
+ , (c->policy & POLICY_DONT_REKEY)
+ ? "--dontrekey"
+ : "LATEST!"
+ );
+ }
+ }
+ /* FALLTHROUGH */
+ case EVENT_SO_DISCARD:
+ /* Delete this state object. It must be in the hash table. */
+ delete_state(st);
+ break;
+
+ case EVENT_DPD:
+ dpd_outI(st);
+ break;
+ case EVENT_DPD_TIMEOUT:
+ dpd_timeout(st);
+ break;
+#ifdef NAT_TRAVERSAL
+ case EVENT_NAT_T_KEEPALIVE:
+ nat_traversal_ka_event();
+ break;
+#endif
+ default:
+ loglog(RC_LOG_SERIOUS, "INTERNAL ERROR: ignoring unknown expiring event %s"
+ , enum_show(&timer_event_names, type));
+ }
+
+ pfree(ev);
+ reset_cur_state();
+}
+
+/*
+ * Return the time until the next event in the queue
+ * expires (never negative), or -1 if no jobs in queue.
+ */
+long
+next_event(void)
+{
+ time_t tm;
+
+ if (evlist == (struct event *) NULL)
+ return -1;
+
+ tm = now();
+
+ DBG(DBG_CONTROL,
+ if (evlist->ev_state == NULL)
+ DBG_log("next event %s in %ld seconds"
+ , enum_show(&timer_event_names, evlist->ev_type)
+ , (long)evlist->ev_time - (long)tm);
+ else
+ DBG_log("next event %s in %ld seconds for #%lu"
+ , enum_show(&timer_event_names, evlist->ev_type)
+ , (long)evlist->ev_time - (long)tm
+ , evlist->ev_state->st_serialno));
+
+ if (evlist->ev_time - tm <= 0)
+ return 0;
+ else
+ return evlist->ev_time - tm;
+}
+
+/*
+ * Delete an event.
+ */
+void
+delete_event(struct state *st)
+{
+ if (st->st_event != (struct event *) NULL)
+ {
+ struct event **ev;
+
+ for (ev = &evlist; ; ev = &(*ev)->ev_next)
+ {
+ if (*ev == NULL)
+ {
+ DBG(DBG_CONTROL, DBG_log("event %s to be deleted not found",
+ enum_show(&timer_event_names, st->st_event->ev_type)));
+ break;
+ }
+ if ((*ev) == st->st_event)
+ {
+ *ev = (*ev)->ev_next;
+
+ if (st->st_event->ev_type == EVENT_RETRANSMIT)
+ st->st_retransmit = 0;
+ pfree(st->st_event);
+ st->st_event = (struct event *) NULL;
+
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Delete a DPD event.
+ */
+void
+delete_dpd_event(struct state *st)
+{
+ if (st->st_dpd_event != (struct event *) NULL)
+ {
+ struct event **ev;
+
+ for (ev = &evlist; ; ev = &(*ev)->ev_next)
+ {
+ if (*ev == NULL)
+ {
+ DBG(DBG_CONTROL, DBG_log("event %s to be deleted not found",
+ enum_show(&timer_event_names, st->st_dpd_event->ev_type)));
+ break;
+ }
+ if ((*ev) == st->st_dpd_event)
+ {
+ *ev = (*ev)->ev_next;
+ pfree(st->st_dpd_event);
+ st->st_dpd_event = (struct event *) NULL;
+ break;
+ }
+ }
+ }
+}
+
+
diff --git a/programs/pluto/timer.h b/programs/pluto/timer.h
new file mode 100644
index 000000000..92464192c
--- /dev/null
+++ b/programs/pluto/timer.h
@@ -0,0 +1,34 @@
+/* timing machinery
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: timer.h,v 1.2 2004/07/29 18:33:45 as Exp $
+ */
+
+extern time_t now(void); /* careful version of time(2) */
+
+struct state; /* forward declaration */
+
+struct event
+{
+ time_t ev_time;
+ int ev_type; /* Event type */
+ struct state *ev_state; /* Pointer to relevant state (if any) */
+ struct event *ev_next; /* Pointer to next event */
+};
+
+extern void event_schedule(enum event_type type, time_t tm, struct state *st);
+extern void handle_timer_event(void);
+extern long next_event(void);
+extern void delete_event(struct state *st);
+extern void delete_dpd_event(struct state *st);
+extern void daily_log_event(void);
diff --git a/programs/pluto/vendor.c b/programs/pluto/vendor.c
new file mode 100644
index 000000000..51931c239
--- /dev/null
+++ b/programs/pluto/vendor.c
@@ -0,0 +1,493 @@
+/* ISAKMP VendorID
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: vendor.c,v 1.35 2006/04/12 16:44:28 as Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/queue.h>
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "md5.h"
+#include "connections.h"
+#include "packet.h"
+#include "demux.h"
+#include "whack.h"
+#include "vendor.h"
+#include "kernel.h"
+
+#ifdef NAT_TRAVERSAL
+#include "nat_traversal.h"
+#endif
+
+/**
+ * Unknown/Special VID:
+ *
+ * SafeNet SoftRemote 8.0.0:
+ * 47bbe7c993f1fc13b4e6d0db565c68e5010201010201010310382e302e3020284275696c6420313029000000
+ * >> 382e302e3020284275696c6420313029 = '8.0.0 (Build 10)'
+ * da8e937880010000
+ *
+ * SafeNet SoftRemote 9.0.1
+ * 47bbe7c993f1fc13b4e6d0db565c68e5010201010201010310392e302e3120284275696c6420313229000000
+ * >> 392e302e3120284275696c6420313229 = '9.0.1 (Build 12)'
+ * da8e937880010000
+ *
+ * Netscreen:
+ * d6b45f82f24bacb288af59a978830ab7
+ * cf49908791073fb46439790fdeb6aeed981101ab0000000500000300
+ *
+ * Cisco:
+ * 1f07f70eaa6514d3b0fa96542a500300 (VPN 3000 version 3.0.0)
+ * 1f07f70eaa6514d3b0fa96542a500301 (VPN 3000 version 3.0.1)
+ * 1f07f70eaa6514d3b0fa96542a500305 (VPN 3000 version 3.0.5)
+ * 1f07f70eaa6514d3b0fa96542a500407 (VPN 3000 version 4.0.7)
+ * (Can you see the pattern?)
+ * afcad71368a1f1c96b8696fc77570100 (Non-RFC Dead Peer Detection ?)
+ * c32364b3b4f447eb17c488ab2a480a57
+ * 6d761ddc26aceca1b0ed11fabbb860c4
+ * 5946c258f99a1a57b03eb9d1759e0f24 (From a Cisco VPN 3k)
+ * ebbc5b00141d0c895e11bd395902d690 (From a Cisco VPN 3k)
+ *
+ * Microsoft L2TP (???):
+ * 47bbe7c993f1fc13b4e6d0db565c68e5010201010201010310382e312e3020284275696c6420313029000000
+ * >> 382e312e3020284275696c6420313029 = '8.1.0 (Build 10)'
+ * 3025dbd21062b9e53dc441c6aab5293600000000
+ * da8e937880010000
+ *
+ * 3COM-superstack
+ * da8e937880010000
+ * 404bf439522ca3f6
+ *
+
+ * If someone know what they mean, mail me.
+ */
+
+#define MAX_LOG_VID_LEN 32
+
+#define VID_KEEP 0x0000
+#define VID_MD5HASH 0x0001
+#define VID_STRING 0x0002
+#define VID_FSWAN_HASH 0x0004
+
+#define VID_SUBSTRING_DUMPHEXA 0x0100
+#define VID_SUBSTRING_DUMPASCII 0x0200
+#define VID_SUBSTRING_MATCH 0x0400
+#define VID_SUBSTRING (VID_SUBSTRING_DUMPHEXA | VID_SUBSTRING_DUMPASCII | VID_SUBSTRING_MATCH)
+
+struct vid_struct {
+ enum known_vendorid id;
+ unsigned short flags;
+ const char *data;
+ const char *descr;
+ const char *vid;
+ u_int vid_len;
+};
+
+#define DEC_MD5_VID_D(id,str,descr) \
+ { VID_##id, VID_MD5HASH, str, descr, NULL, 0 },
+#define DEC_MD5_VID(id,str) \
+ { VID_##id, VID_MD5HASH, str, NULL, NULL, 0 },
+#define DEC_FSWAN_VID(id,str,descr) \
+ { VID_##id, VID_FSWAN_HASH, str, descr, NULL, 0 },
+
+static struct vid_struct _vid_tab[] = {
+
+ /* Implementation names */
+
+ { VID_OPENPGP, VID_STRING, "OpenPGP10171", "OpenPGP", NULL, 0 },
+
+ DEC_MD5_VID(KAME_RACOON, "KAME/racoon")
+
+ { VID_MS_NT5, VID_MD5HASH | VID_SUBSTRING_DUMPHEXA,
+ "MS NT5 ISAKMPOAKLEY", NULL, NULL, 0 },
+
+ DEC_MD5_VID(SSH_SENTINEL, "SSH Sentinel")
+ DEC_MD5_VID(SSH_SENTINEL_1_1, "SSH Sentinel 1.1")
+ DEC_MD5_VID(SSH_SENTINEL_1_2, "SSH Sentinel 1.2")
+ DEC_MD5_VID(SSH_SENTINEL_1_3, "SSH Sentinel 1.3")
+ DEC_MD5_VID(SSH_SENTINEL_1_4, "SSH Sentinel 1.4")
+ DEC_MD5_VID(SSH_SENTINEL_1_4_1, "SSH Sentinel 1.4.1")
+
+ /* These ones come from SSH vendors.txt */
+ DEC_MD5_VID(SSH_IPSEC_1_1_0,
+ "Ssh Communications Security IPSEC Express version 1.1.0")
+ DEC_MD5_VID(SSH_IPSEC_1_1_1,
+ "Ssh Communications Security IPSEC Express version 1.1.1")
+ DEC_MD5_VID(SSH_IPSEC_1_1_2,
+ "Ssh Communications Security IPSEC Express version 1.1.2")
+ DEC_MD5_VID(SSH_IPSEC_1_2_1,
+ "Ssh Communications Security IPSEC Express version 1.2.1")
+ DEC_MD5_VID(SSH_IPSEC_1_2_2,
+ "Ssh Communications Security IPSEC Express version 1.2.2")
+ DEC_MD5_VID(SSH_IPSEC_2_0_0,
+ "SSH Communications Security IPSEC Express version 2.0.0")
+ DEC_MD5_VID(SSH_IPSEC_2_1_0,
+ "SSH Communications Security IPSEC Express version 2.1.0")
+ DEC_MD5_VID(SSH_IPSEC_2_1_1,
+ "SSH Communications Security IPSEC Express version 2.1.1")
+ DEC_MD5_VID(SSH_IPSEC_2_1_2,
+ "SSH Communications Security IPSEC Express version 2.1.2")
+ DEC_MD5_VID(SSH_IPSEC_3_0_0,
+ "SSH Communications Security IPSEC Express version 3.0.0")
+ DEC_MD5_VID(SSH_IPSEC_3_0_1,
+ "SSH Communications Security IPSEC Express version 3.0.1")
+ DEC_MD5_VID(SSH_IPSEC_4_0_0,
+ "SSH Communications Security IPSEC Express version 4.0.0")
+ DEC_MD5_VID(SSH_IPSEC_4_0_1,
+ "SSH Communications Security IPSEC Express version 4.0.1")
+ DEC_MD5_VID(SSH_IPSEC_4_1_0,
+ "SSH Communications Security IPSEC Express version 4.1.0")
+ DEC_MD5_VID(SSH_IPSEC_4_2_0,
+ "SSH Communications Security IPSEC Express version 4.2.0")
+
+ /* note: md5('CISCO-UNITY') = 12f5f28c457168a9702d9fe274cc02d4 */
+ { VID_CISCO_UNITY, VID_KEEP, NULL, "Cisco-Unity",
+ "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00",
+ 16 },
+
+ { VID_CISCO3K, VID_KEEP | VID_SUBSTRING_MATCH,
+ NULL, "Cisco VPN 3000 Series" , "\x1f\x07\xf7\x0e\xaa\x65\x14\xd3\xb0\xfa\x96\x54\x2a\x50", 14},
+
+ /*
+ * Timestep VID seen:
+ * - 54494d455354455020312053475720313532302033313520322e303145303133
+ * = 'TIMESTEP 1 SGW 1520 315 2.01E013'
+ */
+ { VID_TIMESTEP, VID_STRING | VID_SUBSTRING_DUMPASCII, "TIMESTEP",
+ NULL, NULL, 0 },
+
+ /*
+ * Netscreen:
+ * 4865617274426561745f4e6f74696679386b0100 (HeartBeat_Notify + 386b0100)
+ */
+ { VID_MISC_HEARTBEAT_NOTIFY, VID_STRING | VID_SUBSTRING_DUMPHEXA,
+ "HeartBeat_Notify", "HeartBeat Notify", NULL, 0 },
+
+ /*
+ * MacOS X
+ */
+ { VID_MACOSX, VID_STRING|VID_SUBSTRING_DUMPHEXA, "Mac OSX 10.x",
+ "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62", NULL, 0},
+
+ /*
+ * Openswan
+ */
+ DEC_FSWAN_VID(OPENSWAN2, "Openswan 2.2.0", "Openswan 2.2.0")
+
+ /* NCP */
+ { VID_NCP_SERVER, VID_KEEP | VID_SUBSTRING_MATCH, NULL, "NCP Server",
+ "\xc6\xf5\x7a\xc3\x98\xf4\x93\x20\x81\x45\xb7\x58", 12},
+ { VID_NCP_CLIENT, VID_KEEP | VID_SUBSTRING_MATCH, NULL, "NCP Client",
+ "\xeb\x4c\x1b\x78\x8a\xfd\x4a\x9c\xb7\x73\x0a\x68", 12},
+ /*
+ * strongSwan
+ */
+ DEC_MD5_VID(STRONGSWAN, "strongSwan 2.7.0")
+ DEC_MD5_VID(STRONGSWAN_2_6_4, "strongSwan 2.6.4")
+ DEC_MD5_VID(STRONGSWAN_2_6_3, "strongSwan 2.6.3")
+ DEC_MD5_VID(STRONGSWAN_2_6_2, "strongSwan 2.6.2")
+ DEC_MD5_VID(STRONGSWAN_2_6_1, "strongSwan 2.6.1")
+ DEC_MD5_VID(STRONGSWAN_2_6_0, "strongSwan 2.6.0")
+ DEC_MD5_VID(STRONGSWAN_2_5_7, "strongSwan 2.5.7")
+ DEC_MD5_VID(STRONGSWAN_2_5_6, "strongSwan 2.5.6")
+ DEC_MD5_VID(STRONGSWAN_2_5_5, "strongSwan 2.5.5")
+ DEC_MD5_VID(STRONGSWAN_2_5_4, "strongSwan 2.5.4")
+ DEC_MD5_VID(STRONGSWAN_2_5_3, "strongSwan 2.5.3")
+ DEC_MD5_VID(STRONGSWAN_2_5_2, "strongSwan 2.5.2")
+ DEC_MD5_VID(STRONGSWAN_2_5_1, "strongSwan 2.5.1")
+ DEC_MD5_VID(STRONGSWAN_2_5_0, "strongSwan 2.5.0")
+ DEC_MD5_VID(STRONGSWAN_2_4_4, "strongSwan 2.4.4")
+ DEC_MD5_VID(STRONGSWAN_2_4_3, "strongSwan 2.4.3")
+ DEC_MD5_VID(STRONGSWAN_2_4_2, "strongSwan 2.4.2")
+ DEC_MD5_VID(STRONGSWAN_2_4_1, "strongSwan 2.4.1")
+ DEC_MD5_VID(STRONGSWAN_2_4_0, "strongSwan 2.4.0")
+ DEC_MD5_VID(STRONGSWAN_2_3_2, "strongSwan 2.3.2")
+ DEC_MD5_VID(STRONGSWAN_2_3_1, "strongSwan 2.3.1")
+ DEC_MD5_VID(STRONGSWAN_2_3_0, "strongSwan 2.3.0")
+ DEC_MD5_VID(STRONGSWAN_2_2_2, "strongSwan 2.2.2")
+ DEC_MD5_VID(STRONGSWAN_2_2_1, "strongSwan 2.2.1")
+ DEC_MD5_VID(STRONGSWAN_2_2_0, "strongSwan 2.2.0")
+
+ /* NAT-Traversal */
+
+ DEC_MD5_VID(NATT_STENBERG_01, "draft-stenberg-ipsec-nat-traversal-01")
+ DEC_MD5_VID(NATT_STENBERG_02, "draft-stenberg-ipsec-nat-traversal-02")
+ DEC_MD5_VID(NATT_HUTTUNEN, "ESPThruNAT")
+ DEC_MD5_VID(NATT_HUTTUNEN_ESPINUDP, "draft-huttunen-ipsec-esp-in-udp-00.txt")
+ DEC_MD5_VID(NATT_IETF_00, "draft-ietf-ipsec-nat-t-ike-00")
+ DEC_MD5_VID(NATT_IETF_02, "draft-ietf-ipsec-nat-t-ike-02")
+ /* hash in draft-ietf-ipsec-nat-t-ike-02 contains '\n'... Accept both */
+ DEC_MD5_VID_D(NATT_IETF_02_N, "draft-ietf-ipsec-nat-t-ike-02\n", "draft-ietf-ipsec-nat-t-ike-02_n")
+ DEC_MD5_VID(NATT_IETF_03, "draft-ietf-ipsec-nat-t-ike-03")
+ DEC_MD5_VID(NATT_RFC, "RFC 3947")
+
+ /* misc */
+
+ { VID_MISC_XAUTH, VID_KEEP, NULL, "XAUTH",
+ "\x09\x00\x26\x89\xdf\xd6\xb7\x12", 8 },
+
+ { VID_MISC_DPD, VID_KEEP, NULL, "Dead Peer Detection",
+ "\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00", 16 },
+
+ DEC_MD5_VID(MISC_FRAGMENTATION, "FRAGMENTATION")
+
+ DEC_MD5_VID(INITIAL_CONTACT, "Vid-Initial-Contact")
+
+ /* -- */
+ { 0, 0, NULL, NULL, NULL, 0 }
+
+};
+
+static const char _hexdig[] = "0123456789abcdef";
+
+static int _vid_struct_init = 0;
+
+void
+init_vendorid(void)
+{
+ struct vid_struct *vid;
+ MD5_CTX ctx;
+ int i;
+
+ for (vid = _vid_tab; vid->id; vid++)
+ {
+ if (vid->flags & VID_STRING)
+ {
+ /** VendorID is a string **/
+ vid->vid = strdup(vid->data);
+ vid->vid_len = strlen(vid->data);
+ }
+ else if (vid->flags & VID_MD5HASH)
+ {
+ /** VendorID is a string to hash with MD5 **/
+ char *vidm = malloc(MD5_DIGEST_SIZE);
+
+ vid->vid = vidm;
+ if (vidm)
+ {
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const u_char *)vid->data, strlen(vid->data));
+ MD5Final(vidm, &ctx);
+ vid->vid_len = MD5_DIGEST_SIZE;
+ }
+ }
+ else if (vid->flags & VID_FSWAN_HASH)
+ {
+ /** FreeS/WAN 2.00+ specific hash **/
+#define FSWAN_VID_SIZE 12
+ unsigned char hash[MD5_DIGEST_SIZE];
+ char *vidm = malloc(FSWAN_VID_SIZE);
+
+ vid->vid = vidm;
+ if (vidm)
+ {
+ MD5Init(&ctx);
+ MD5Update(&ctx, (const u_char *)vid->data, strlen(vid->data));
+ MD5Final(hash, &ctx);
+ vidm[0] = 'O';
+ vidm[1] = 'E';
+#if FSWAN_VID_SIZE - 2 <= MD5_DIGEST_SIZE
+ memcpy(vidm + 2, hash, FSWAN_VID_SIZE - 2);
+#else
+ memcpy(vidm + 2, hash, MD5_DIGEST_SIZE);
+ memset(vidm + 2 + MD5_DIGEST_SIZE, '\0',
+ FSWAN_VID_SIZE - 2 - MD5_DIGEST_SIZE);
+#endif
+ for (i = 2; i < FSWAN_VID_SIZE; i++)
+ {
+ vidm[i] &= 0x7f;
+ vidm[i] |= 0x40;
+ }
+ vid->vid_len = FSWAN_VID_SIZE;
+ }
+ }
+
+ if (vid->descr == NULL)
+ {
+ /** Find something to display **/
+ vid->descr = vid->data;
+ }
+ }
+ _vid_struct_init = 1;
+}
+
+static void
+handle_known_vendorid (struct msg_digest *md
+, const char *vidstr, size_t len, struct vid_struct *vid)
+{
+ char vid_dump[128];
+ bool vid_useful = FALSE;
+ size_t i, j;
+
+ switch (vid->id) {
+ /* Remote side supports OpenPGP certificates */
+ case VID_OPENPGP:
+ md->openpgp = TRUE;
+ vid_useful = TRUE;
+ break;
+#ifdef NAT_TRAVERSAL
+ /*
+ * Use most recent supported NAT-Traversal method and ignore the
+ * other ones (implementations will send all supported methods but
+ * only one will be used)
+ *
+ * Note: most recent == higher id in vendor.h
+ */
+ case VID_NATT_IETF_00:
+ if (!nat_traversal_support_non_ike)
+ break;
+ if ((nat_traversal_enabled) && (!md->nat_traversal_vid))
+ {
+ md->nat_traversal_vid = vid->id;
+ vid_useful = TRUE;
+ }
+ break;
+ case VID_NATT_IETF_02:
+ case VID_NATT_IETF_02_N:
+ case VID_NATT_IETF_03:
+ case VID_NATT_RFC:
+ if (nat_traversal_support_port_floating
+ && md->nat_traversal_vid < vid->id)
+ {
+ md->nat_traversal_vid = vid->id;
+ vid_useful = TRUE;
+ }
+ break;
+#endif
+ /* Remote side would like to do DPD with us on this connection */
+ case VID_MISC_DPD:
+ md->dpd = TRUE;
+ vid_useful = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (vid->flags & VID_SUBSTRING_DUMPHEXA)
+ {
+ /* Dump description + Hexa */
+ memset(vid_dump, 0, sizeof(vid_dump));
+ snprintf(vid_dump, sizeof(vid_dump), "%s ",
+ vid->descr ? vid->descr : "");
+ for (i = strlen(vid_dump), j = vid->vid_len;
+ j < len && i < sizeof(vid_dump) - 2;
+ i += 2, j++)
+ {
+ vid_dump[i] = _hexdig[(vidstr[j] >> 4) & 0xF];
+ vid_dump[i+1] = _hexdig[vidstr[j] & 0xF];
+ }
+ }
+ else if (vid->flags & VID_SUBSTRING_DUMPASCII)
+ {
+ /* Dump ASCII content */
+ memset(vid_dump, 0, sizeof(vid_dump));
+ for (i = 0; i < len && i < sizeof(vid_dump) - 1; i++)
+ {
+ vid_dump[i] = (isprint(vidstr[i])) ? vidstr[i] : '.';
+ }
+ }
+ else
+ {
+ /* Dump description (descr) */
+ snprintf(vid_dump, sizeof(vid_dump), "%s",
+ vid->descr ? vid->descr : "");
+ }
+
+ loglog(RC_LOG_SERIOUS, "%s Vendor ID payload [%s]",
+ vid_useful ? "received" : "ignoring", vid_dump);
+}
+
+void
+handle_vendorid (struct msg_digest *md, const char *vid, size_t len)
+{
+ struct vid_struct *pvid;
+
+ if (!_vid_struct_init)
+ init_vendorid();
+
+ /*
+ * Find known VendorID in _vid_tab
+ */
+ for (pvid = _vid_tab; pvid->id; pvid++)
+ {
+ if (pvid->vid && vid && pvid->vid_len && len)
+ {
+ if (pvid->vid_len == len)
+ {
+ if (memcmp(pvid->vid, vid, len) == 0)
+ {
+ handle_known_vendorid(md, vid, len, pvid);
+ return;
+ }
+ }
+ else if ((pvid->vid_len < len) && (pvid->flags & VID_SUBSTRING))
+ {
+ if (memcmp(pvid->vid, vid, pvid->vid_len) == 0)
+ {
+ handle_known_vendorid(md, vid, len, pvid);
+ return;
+ }
+ }
+ }
+ }
+
+ /*
+ * Unknown VendorID. Log the beginning.
+ */
+ {
+ char log_vid[2*MAX_LOG_VID_LEN+1];
+ size_t i;
+
+ memset(log_vid, 0, sizeof(log_vid));
+
+ for (i = 0; i < len && i < MAX_LOG_VID_LEN; i++)
+ {
+ log_vid[2*i] = _hexdig[(vid[i] >> 4) & 0xF];
+ log_vid[2*i+1] = _hexdig[vid[i] & 0xF];
+ }
+ loglog(RC_LOG_SERIOUS, "ignoring Vendor ID payload [%s%s]",
+ log_vid, (len>MAX_LOG_VID_LEN) ? "..." : "");
+ }
+}
+
+/**
+ * Add a vendor id payload to the msg
+ */
+bool
+out_vendorid (u_int8_t np, pb_stream *outs, enum known_vendorid vid)
+{
+ struct vid_struct *pvid;
+
+ if (!_vid_struct_init)
+ init_vendorid();
+
+ for (pvid = _vid_tab; pvid->id && pvid->id != vid; pvid++);
+
+ if (pvid->id != vid)
+ return STF_INTERNAL_ERROR; /* not found */
+ if (!pvid->vid)
+ return STF_INTERNAL_ERROR; /* not initialized */
+
+ DBG(DBG_EMITTING,
+ DBG_log("out_vendorid(): sending [%s]", pvid->descr)
+ )
+ return out_generic_raw(np, &isakmp_vendor_id_desc, outs,
+ pvid->vid, pvid->vid_len, "V_ID");
+}
+
diff --git a/programs/pluto/vendor.h b/programs/pluto/vendor.h
new file mode 100644
index 000000000..d6b414be2
--- /dev/null
+++ b/programs/pluto/vendor.h
@@ -0,0 +1,107 @@
+/* FreeS/WAN ISAKMP VendorID
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: vendor.h,v 1.30 2006/04/12 16:44:28 as Exp $
+ */
+
+#ifndef _VENDOR_H_
+#define _VENDOR_H_
+
+enum known_vendorid {
+/* 1 - 100 : Implementation names */
+ VID_OPENPGP = 1,
+ VID_KAME_RACOON = 2,
+ VID_MS_NT5 = 3,
+ VID_SSH_SENTINEL = 4,
+ VID_SSH_SENTINEL_1_1 = 5,
+ VID_SSH_SENTINEL_1_2 = 6,
+ VID_SSH_SENTINEL_1_3 = 7,
+ VID_SSH_SENTINEL_1_4 = 8,
+ VID_SSH_SENTINEL_1_4_1 = 9,
+ VID_SSH_IPSEC_1_1_0 = 10,
+ VID_SSH_IPSEC_1_1_1 = 11,
+ VID_SSH_IPSEC_1_1_2 = 12,
+ VID_SSH_IPSEC_1_2_1 = 13,
+ VID_SSH_IPSEC_1_2_2 = 14,
+ VID_SSH_IPSEC_2_0_0 = 15,
+ VID_SSH_IPSEC_2_1_0 = 16,
+ VID_SSH_IPSEC_2_1_1 = 17,
+ VID_SSH_IPSEC_2_1_2 = 18,
+ VID_SSH_IPSEC_3_0_0 = 19,
+ VID_SSH_IPSEC_3_0_1 = 20,
+ VID_SSH_IPSEC_4_0_0 = 21,
+ VID_SSH_IPSEC_4_0_1 = 22,
+ VID_SSH_IPSEC_4_1_0 = 23,
+ VID_SSH_IPSEC_4_2_0 = 24,
+ VID_CISCO_UNITY = 25,
+ VID_CISCO3K = 26,
+ VID_TIMESTEP = 27,
+ VID_SAFENET = 28,
+ VID_MACOSX = 29,
+ VID_OPENSWAN2 = 30,
+ VID_NCP_SERVER = 31,
+ VID_NCP_CLIENT = 32,
+ VID_STRONGSWAN = 33,
+ VID_STRONGSWAN_2_2_0 = 34,
+ VID_STRONGSWAN_2_2_1 = 35,
+ VID_STRONGSWAN_2_2_2 = 36,
+ VID_STRONGSWAN_2_3_0 = 37,
+ VID_STRONGSWAN_2_3_1 = 38,
+ VID_STRONGSWAN_2_3_2 = 39,
+ VID_STRONGSWAN_2_4_0 = 40,
+ VID_STRONGSWAN_2_4_1 = 41,
+ VID_STRONGSWAN_2_4_2 = 42,
+ VID_STRONGSWAN_2_4_3 = 43,
+ VID_STRONGSWAN_2_4_4 = 44,
+ VID_STRONGSWAN_2_5_0 = 45,
+ VID_STRONGSWAN_2_5_1 = 46,
+ VID_STRONGSWAN_2_5_2 = 47,
+ VID_STRONGSWAN_2_5_3 = 48,
+ VID_STRONGSWAN_2_5_4 = 49,
+ VID_STRONGSWAN_2_5_5 = 50,
+ VID_STRONGSWAN_2_5_6 = 51,
+ VID_STRONGSWAN_2_5_7 = 52,
+ VID_STRONGSWAN_2_6_0 = 53,
+ VID_STRONGSWAN_2_6_1 = 54,
+ VID_STRONGSWAN_2_6_2 = 55,
+ VID_STRONGSWAN_2_6_3 = 56,
+ VID_STRONGSWAN_2_6_4 = 57,
+
+ /* 101 - 200 : NAT-Traversal */
+ VID_NATT_STENBERG_01 =101,
+ VID_NATT_STENBERG_02 =102,
+ VID_NATT_HUTTUNEN =103,
+ VID_NATT_HUTTUNEN_ESPINUDP =104,
+ VID_NATT_IETF_00 =105,
+ VID_NATT_IETF_02_N =106,
+ VID_NATT_IETF_02 =107,
+ VID_NATT_IETF_03 =108,
+ VID_NATT_RFC =109,
+
+ /* 201 - 300 : Misc */
+ VID_MISC_XAUTH =201,
+ VID_MISC_DPD =202,
+ VID_MISC_HEARTBEAT_NOTIFY =203,
+ VID_MISC_FRAGMENTATION =204,
+ VID_INITIAL_CONTACT =205
+};
+
+void init_vendorid(void);
+
+struct msg_digest;
+void handle_vendorid (struct msg_digest *md, const char *vid, size_t len);
+
+bool out_vendorid (u_int8_t np, pb_stream *outs, enum known_vendorid vid);
+
+#endif /* _VENDOR_H_ */
+
diff --git a/programs/pluto/virtual.c b/programs/pluto/virtual.c
new file mode 100644
index 000000000..58487c1e8
--- /dev/null
+++ b/programs/pluto/virtual.c
@@ -0,0 +1,338 @@
+/* FreeS/WAN Virtual IP Management
+ * Copyright (C) 2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: virtual.c,v 1.4 2004/04/02 10:38:52 as Exp $
+ */
+
+#ifdef VIRTUAL_IP
+
+#include <freeswan.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/queue.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "log.h"
+#include "connections.h"
+#include "whack.h"
+#include "virtual.h"
+
+#define F_VIRTUAL_NO 1
+#define F_VIRTUAL_DHCP 2
+#define F_VIRTUAL_IKE_CONFIG 4
+#define F_VIRTUAL_PRIVATE 8
+#define F_VIRTUAL_ALL 16
+#define F_VIRTUAL_HOST 32
+
+struct virtual_t {
+ unsigned short flags;
+ unsigned short n_net;
+ ip_subnet net[0];
+};
+
+static ip_subnet *private_net_ok=NULL, *private_net_ko=NULL;
+static unsigned short private_net_ok_len=0, private_net_ko_len=0;
+
+/**
+ * read %v4:x.x.x.x/y or %v6:xxxxxxxxx/yy
+ * or %v4:!x.x.x.x/y if dstko not NULL
+ */
+static bool
+_read_subnet(const char *src, size_t len, ip_subnet *dst, ip_subnet *dstko,
+ bool *isok)
+{
+ bool ok;
+ int af;
+
+ if ((len > 4) && (strncmp(src, "%v4:", 4)==0))
+ {
+ af = AF_INET;
+ }
+ else if ((len > 4) && (strncmp(src, "%v6:", 4)==0))
+ {
+ af = AF_INET6;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ ok = (src[4] != '!');
+ src += ok ? 4 : 5;
+ len -= ok ? 4 : 5;
+
+ if (!len)
+ return FALSE;
+ if (!ok && !dstko)
+ return FALSE;
+
+ passert ( ((ok)?(dst):(dstko))!=NULL );
+
+ if (ttosubnet(src, len, af, ((ok)?(dst):(dstko))))
+ {
+ return FALSE;
+ }
+ if (isok)
+ *isok = ok;
+ return TRUE;
+}
+
+void
+init_virtual_ip(const char *private_list)
+{
+ const char *next, *str=private_list;
+ unsigned short ign = 0, i_ok, i_ko;
+ ip_subnet sub;
+ bool ok;
+
+ /** Count **/
+ private_net_ok_len=0;
+ private_net_ko_len=0;
+
+ while (str)
+ {
+ next = strchr(str,',');
+ if (!next)
+ next = str + strlen(str);
+ if (_read_subnet(str, next-str, &sub, &sub, &ok))
+ if (ok)
+ private_net_ok_len++;
+ else
+ private_net_ko_len++;
+ else
+ ign++;
+ str = *next ? next+1 : NULL;
+ }
+
+ if (!ign)
+ {
+ /** Allocate **/
+ if (private_net_ok_len)
+ {
+ private_net_ok = (ip_subnet *)alloc_bytes(
+ (private_net_ok_len*sizeof(ip_subnet)),
+ "private_net_ok subnets");
+ }
+ if (private_net_ko_len)
+ {
+ private_net_ko = (ip_subnet *)alloc_bytes(
+ (private_net_ko_len*sizeof(ip_subnet)),
+ "private_net_ko subnets");
+ }
+ if ((private_net_ok_len && !private_net_ok)
+ || (private_net_ko_len && !private_net_ko))
+ {
+ loglog(RC_LOG_SERIOUS,
+ "can't alloc in init_virtual_ip");
+ pfreeany(private_net_ok);
+ private_net_ok = NULL;
+ pfreeany(private_net_ko);
+ private_net_ko = NULL;
+ }
+ else
+ {
+ /** Fill **/
+ str = private_list;
+ i_ok = 0;
+ i_ko = 0;
+
+ while (str)
+ {
+ next = strchr(str,',');
+ if (!next)
+ next = str + strlen(str);
+ if (_read_subnet(str, next-str,
+ &(private_net_ok[i_ok]), &(private_net_ko[i_ko]), &ok))
+ {
+ if (ok)
+ i_ok++;
+ else
+ i_ko++;
+ }
+ str = *next ? next+1 : NULL;
+ }
+ }
+ }
+ else
+ loglog(RC_LOG_SERIOUS,
+ "%d bad entries in virtual_private - none loaded", ign);
+}
+
+/**
+ * virtual string must be :
+ * {vhost,vnet}:[%method]*
+ *
+ * vhost = accept only a host (/32)
+ * vnet = accept any network
+ *
+ * %no = no virtual IP (accept public IP)
+ * %dhcp = accept DHCP SA (0.0.0.0/0) of affected IP [not implemented]
+ * %ike = accept affected IKE Config Mode IP [not implemented]
+ * %priv = accept system-wide private net list
+ * %v4:x = accept ipv4 in list 'x'
+ * %v6:x = accept ipv6 in list 'x'
+ * %all = accept all ips [only for testing]
+ *
+ * ex: vhost:%no,%dhcp,%priv,%v4:192.168.1.0/24
+ */
+struct virtual_t
+*create_virtual(const struct connection *c, const char *string)
+{
+ unsigned short flags=0, n_net=0, i;
+ const char *str = string, *next, *first_net=NULL;
+ ip_subnet sub;
+ struct virtual_t *v;
+
+ if (!string || string[0] == '\0')
+ return NULL;
+
+ if (strlen(string) >= 6 && strncmp(string,"vhost:",6) == 0)
+ {
+ flags |= F_VIRTUAL_HOST;
+ str += 6;
+ }
+ else if (strlen(string) >= 5 && strncmp(string,"vnet:",5) == 0)
+ str += 5;
+ else
+ goto fail;
+
+ /**
+ * Parse string : fill flags & count subnets
+ */
+ while ((str) && (*str))
+ {
+ next = strchr(str,',');
+ if (!next) next = str + strlen(str);
+ if (next-str == 3 && strncmp(str, "%no", 3) == 0)
+ flags |= F_VIRTUAL_NO;
+#if 0
+ else if (next-str == 4 && strncmp(str, "%ike", 4) == 0)
+ flags |= F_VIRTUAL_IKE_CONFIG;
+ else if (next-str == 5 && strncmp(str, "%dhcp", 5) == 0)
+ flags |= F_VIRTUAL_DHCP;
+#endif
+ else if (next-str == 5 && strncmp(str, "%priv", 5) == 0)
+ flags |= F_VIRTUAL_PRIVATE;
+ else if (next-str == 4 && strncmp(str, "%all", 4) == 0)
+ flags |= F_VIRTUAL_ALL;
+ else if (_read_subnet(str, next-str, &sub, NULL, NULL))
+ {
+ n_net++;
+ if (!first_net)
+ first_net = str;
+ }
+ else
+ goto fail;
+
+ str = *next ? next+1 : NULL;
+ }
+
+ v = (struct virtual_t *)alloc_bytes(
+ sizeof(struct virtual_t) + (n_net*sizeof(ip_subnet)),
+ "virtual description");
+ if (!v) goto fail;
+
+ v->flags = flags;
+ v->n_net = n_net;
+ if (n_net && first_net)
+ {
+ /**
+ * Save subnets in newly allocated struct
+ */
+ for (str = first_net, i = 0; str && *str; )
+ {
+ next = strchr(str,',');
+ if (!next) next = str + strlen(str);
+ if (_read_subnet(str, next-str, &(v->net[i]), NULL, NULL))
+ i++;
+ str = *next ? next+1 : NULL;
+ }
+ }
+
+ return v;
+
+fail:
+ plog("invalid virtual string [%s] - "
+ "virtual selection disabled for connection '%s'", string, c->name);
+ return NULL;
+}
+
+bool
+is_virtual_end(const struct end *that)
+{
+ return ((that->virt)?TRUE:FALSE);
+}
+
+bool
+is_virtual_connection(const struct connection *c)
+{
+ return ((c->spd.that.virt)?TRUE:FALSE);
+}
+
+static bool
+net_in_list(const ip_subnet *peer_net, const ip_subnet *list,
+ unsigned short len)
+{
+ unsigned short i;
+
+ if (!list || !len)
+ return FALSE;
+
+ for (i = 0; i < len; i++)
+ {
+ if (subnetinsubnet(peer_net, &(list[i])))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool
+is_virtual_net_allowed(const struct connection *c, const ip_subnet *peer_net,
+ const ip_address *his_addr)
+{
+ if (c->spd.that.virt == NULL)
+ return FALSE;
+
+ if ((c->spd.that.virt->flags & F_VIRTUAL_HOST)
+ && !subnetishost(peer_net))
+ return FALSE;
+
+ if ((c->spd.that.virt->flags & F_VIRTUAL_NO)
+ && subnetishost(peer_net) && addrinsubnet(his_addr, peer_net))
+ return TRUE;
+
+ if ((c->spd.that.virt->flags & F_VIRTUAL_PRIVATE)
+ && net_in_list(peer_net, private_net_ok, private_net_ok_len)
+ && !net_in_list(peer_net, private_net_ko, private_net_ko_len))
+ return TRUE;
+
+ if (c->spd.that.virt->n_net
+ && net_in_list(peer_net, c->spd.that.virt->net, c->spd.that.virt->n_net))
+ return TRUE;
+
+ if (c->spd.that.virt->flags & F_VIRTUAL_ALL)
+ {
+ /** %all must only be used for testing - log it **/
+ loglog(RC_LOG_SERIOUS, "Warning - "
+ "v%s:%%all must only be used for testing",
+ (c->spd.that.virt->flags & F_VIRTUAL_HOST) ? "host" : "net");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#endif
+
diff --git a/programs/pluto/virtual.h b/programs/pluto/virtual.h
new file mode 100644
index 000000000..2d5bf27ae
--- /dev/null
+++ b/programs/pluto/virtual.h
@@ -0,0 +1,31 @@
+/* FreeS/WAN Virtual IP Management
+ * Copyright (C) 2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: virtual.h,v 1.2 2004/03/22 21:53:20 as Exp $
+ */
+
+#ifndef _VIRTUAL_IP_H
+#define _VIRTUAL_IP_H
+
+extern void init_virtual_ip(const char *private_list);
+
+extern struct virtual_t *create_virtual(const struct connection *c,
+ const char *string);
+
+extern bool is_virtual_end(const struct end *that);
+extern bool is_virtual_connection(const struct connection *c);
+extern bool is_virtual_net_allowed(const struct connection *c,
+ const ip_subnet *peer_net, const ip_address *his_addr);
+
+#endif /* _VIRTUAL_IP_H */
+
diff --git a/programs/pluto/whack.c b/programs/pluto/whack.c
new file mode 100644
index 000000000..a3b983771
--- /dev/null
+++ b/programs/pluto/whack.c
@@ -0,0 +1,1911 @@
+/* command interface to Pluto
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: whack.c,v 1.21 2006/04/20 04:42:12 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <getopt.h>
+#include <assert.h>
+
+#include <freeswan.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "whack.h"
+
+static void
+help(void)
+{
+ fprintf(stderr
+ , "Usage:\n\n"
+ "all forms:"
+ " [--optionsfrom <filename>]"
+ " [--ctlbase <path>]"
+ " [--label <string>]"
+ "\n\n"
+ "help: whack"
+ " [--help]"
+ " [--version]"
+ "\n\n"
+ "connection: whack"
+ " --name <connection_name>"
+ " \\\n "
+ " [--ipv4 | --ipv6]"
+ " [--tunnelipv4 | --tunnelipv6]"
+ " \\\n "
+ " (--host <ip-address> | --id <identity>)"
+ " \\\n "
+ " [--cert <path>]"
+ " [--ca <distinguished name>]"
+ " [--sendcert <policy>]"
+ " \\\n "
+ " [--groups <access control groups>]"
+ " \\\n "
+ " [--ikeport <port-number>]"
+ " [--nexthop <ip-address>]"
+ " [--srcip <ip-address>]"
+ " \\\n "
+ " [--client <subnet> | --clientwithin <address range>]"
+ " [--clientprotoport <protocol>/<port>]"
+ " \\\n "
+ " [--dnskeyondemand]"
+ " [--updown <updown>]"
+ " \\\n "
+ " --to"
+ " (--host <ip-address> | --id <identity>)"
+ " \\\n "
+ " [--cert <path>]"
+ " [--ca <distinguished name>]"
+ " [--sendcert <policy>]"
+ " \\\n "
+ " [--ikeport <port-number>]"
+ " [--nexthop <ip-address>]"
+ " [--srcip <ip-address>]"
+ " \\\n "
+ " [--client <subnet> | --clientwithin <address range>]"
+ " [--clientprotoport <protocol>/<port>]"
+ " \\\n "
+ " [--dnskeyondemand]"
+ " [--updown <updown>]"
+ " [--psk]"
+ " [--rsasig]"
+ " \\\n "
+ " [--encrypt]"
+ " [--authenticate]"
+ " [--compress]"
+ " [--tunnel]"
+ " [--pfs]"
+ " \\\n "
+ " [--ikelifetime <seconds>]"
+ " [--ipseclifetime <seconds>]"
+ " \\\n "
+ " [--reykeymargin <seconds>]"
+ " [--reykeyfuzz <percentage>]"
+ " \\\n "
+ " [--keyingtries <count>]"
+ " \\\n "
+ " [--esp <esp-algos>]"
+ " \\\n "
+ " [--dontrekey]"
+
+ " [--dpdaction (none|clear|hold|restart)]"
+ " \\\n "
+ " [--dpddelay <seconds> --dpdtimeout <seconds>]"
+ " \\\n "
+ " [--initiateontraffic|--pass|--drop|--reject]"
+ " \\\n "
+ " [--failnone|--failpass|--faildrop|--failreject]"
+ "\n\n"
+ "routing: whack"
+ " (--route | --unroute)"
+ " --name <connection_name>"
+ "\n\n"
+ "initiation:"
+ "\n "
+ " whack"
+ " (--initiate | --terminate)"
+ " --name <connection_name>"
+ " [--asynchronous]"
+ "\n\n"
+ "opportunistic initiation: whack"
+ " [--tunnelipv4 | --tunnelipv6]"
+ " \\\n "
+ " --oppohere <ip-address>"
+ " --oppothere <ip-address>"
+ "\n\n"
+ "delete: whack"
+ " --delete"
+ " (--name <connection_name> | --caname <ca name>)"
+ "\n\n"
+ "deletestate: whack"
+ " --deletestate <state_object_number>"
+ " --crash <ip-address>"
+ "\n\n"
+ "pubkey: whack"
+ " --keyid <id>"
+ " [--addkey]"
+ " [--pubkeyrsa <key>]"
+ "\n\n"
+ "myid: whack"
+ " --myid <id>"
+ "\n\n"
+ "ca: whack"
+ " --caname <name>"
+ " --cacert <path>"
+ " \\\n "
+ " [--ldaphost <hostname>]"
+ " [--ldapbase <base>]"
+ " \\\n "
+ " [--crluri <uri>]"
+ " [--crluri2 <uri>]"
+ " [--ocspuri <uri>]"
+ " [--strictcrlpolicy]"
+ "\n\n"
+#ifdef DEBUG
+ "debug: whack [--name <connection_name>]"
+ " \\\n "
+ " [--debug-none]"
+ " [--debug-all]"
+ " \\\n "
+ " [--debug-raw]"
+ " [--debug-crypt]"
+ " [--debug-parsing]"
+ " [--debug-emitting]"
+ " \\\n "
+ " [--debug-control]"
+ " [--debug-lifecycle]"
+ " [--debug-klips]"
+ " [--debug-dns]"
+ " \\\n "
+ " [--debug-natt]"
+ " [--debug-oppo]"
+ " [--debug-controlmore]"
+ " [--debug-private]"
+ "\n\n"
+#endif
+ "listen: whack"
+ " (--listen | --unlisten)"
+ "\n\n"
+ "list: whack [--utc]"
+ " [--listalgs]"
+ " [--listpubkeys]"
+ " [--listcerts]"
+ " [--listcacerts]"
+ " \\\n "
+ " [--listacerts]"
+ " [--listaacerts]"
+ " [--listocspcerts]"
+ " [--listgroups]"
+ " \\\n "
+ " [--listcainfos]"
+ " [--listcrls]"
+ " [--listocsp]"
+ " [--listcards]"
+ " [--listall]"
+ "\n\n"
+ "purge: whack"
+ " [--purgeocsp]"
+ "\n\n"
+ "reread: whack"
+ " [--rereadsecrets]"
+ " [--rereadcacerts]"
+ " [--rereadaacerts]"
+ " \\\n "
+ " [--rereadocspcerts]"
+ " [--rereadacerts]"
+ " [--rereadcrls]"
+ " [--rereadall]"
+ "\n\n"
+ "status: whack"
+ " [--name <connection_name>] --status|--statusall"
+ "\n\n"
+ "scdecrypt: whack"
+ " --scencrypt|scdecrypt <value>"
+ " [--inbase <base>]"
+ " [--outbase <base>]"
+ " [--keyid <id>]"
+ "\n\n"
+ "shutdown: whack"
+ " --shutdown"
+ "\n\n"
+ "strongSwan %s\n"
+ , ipsec_version_code());
+}
+
+static const char *label = NULL; /* --label operand, saved for diagnostics */
+
+static const char *name = NULL; /* --name operand, saved for diagnostics */
+
+/* print a string as a diagnostic, then exit whack unhappily */
+static void
+diag(const char *mess)
+{
+ if (mess != NULL)
+ {
+ fprintf(stderr, "whack error: ");
+ if (label != NULL)
+ fprintf(stderr, "%s ", label);
+ if (name != NULL)
+ fprintf(stderr, "\"%s\" ", name);
+ fprintf(stderr, "%s\n", mess);
+ }
+
+ exit(RC_WHACK_PROBLEM);
+}
+
+/* conditially calls diag; prints second arg, if non-NULL, as quoted string */
+static void
+diagq(err_t ugh, const char *this)
+{
+ if (ugh != NULL)
+ {
+ if (this == NULL)
+ {
+ diag(ugh);
+ }
+ else
+ {
+ char buf[120]; /* arbitrary limit */
+
+ snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this);
+ diag(buf);
+ }
+ }
+}
+
+/* complex combined operands return one of these enumerated values
+ * Note: these become flags in an lset_t. Since there are more than
+ * 32, we partition them into:
+ * - OPT_* options (most random options)
+ * - LST_* options (list various internal data)
+ * - DBGOPT_* option (DEBUG options)
+ * - END_* options (End description options)
+ * - CD_* options (Connection Description options)
+ * - CA_* options (CA description options)
+ */
+enum {
+# define OPT_FIRST OPT_CTLBASE
+ OPT_CTLBASE,
+ OPT_NAME,
+
+ OPT_CD,
+
+ OPT_KEYID,
+ OPT_ADDKEY,
+ OPT_PUBKEYRSA,
+
+ OPT_MYID,
+
+ OPT_ROUTE,
+ OPT_UNROUTE,
+
+ OPT_INITIATE,
+ OPT_TERMINATE,
+ OPT_DELETE,
+ OPT_DELETESTATE,
+ OPT_LISTEN,
+ OPT_UNLISTEN,
+
+ OPT_PURGEOCSP,
+
+ OPT_REREADSECRETS,
+ OPT_REREADCACERTS,
+ OPT_REREADAACERTS,
+ OPT_REREADOCSPCERTS,
+ OPT_REREADACERTS,
+ OPT_REREADCRLS,
+ OPT_REREADALL,
+
+ OPT_STATUS,
+ OPT_STATUSALL,
+ OPT_SHUTDOWN,
+
+ OPT_OPPO_HERE,
+ OPT_OPPO_THERE,
+
+ OPT_ASYNC,
+ OPT_DELETECRASH,
+
+# define OPT_LAST OPT_ASYNC /* last "normal" option */
+
+/* Smartcard options */
+
+# define SC_FIRST SC_ENCRYPT /* first smartcard option */
+
+ SC_ENCRYPT,
+ SC_DECRYPT,
+ SC_INBASE,
+ SC_OUTBASE,
+
+# define SC_LAST SC_OUTBASE /* last "smartcard" option */
+
+/* List options */
+
+# define LST_FIRST LST_UTC /* first list option */
+ LST_UTC,
+ LST_ALGS,
+ LST_PUBKEYS,
+ LST_CERTS,
+ LST_CACERTS,
+ LST_ACERTS,
+ LST_AACERTS,
+ LST_OCSPCERTS,
+ LST_GROUPS,
+ LST_CAINFOS,
+ LST_CRLS,
+ LST_OCSP,
+ LST_CARDS,
+ LST_ALL,
+
+# define LST_LAST LST_ALL /* last list option */
+
+/* Connection End Description options */
+
+# define END_FIRST END_HOST /* first end description */
+ END_HOST,
+ END_ID,
+ END_CERT,
+ END_CA,
+ END_SENDCERT,
+ END_GROUPS,
+ END_IKEPORT,
+ END_NEXTHOP,
+ END_CLIENT,
+ END_CLIENTWITHIN,
+ END_CLIENTPROTOPORT,
+ END_DNSKEYONDEMAND,
+ END_SRCIP,
+ END_HOSTACCESS,
+ END_UPDOWN,
+
+#define END_LAST END_UPDOWN /* last end description*/
+
+/* Connection Description options -- segregated */
+
+# define CD_FIRST CD_TO /* first connection description */
+ CD_TO,
+
+# define CD_POLICY_FIRST CD_PSK
+ CD_PSK, /* same order as POLICY_* */
+ CD_RSASIG, /* same order as POLICY_* */
+ CD_ENCRYPT, /* same order as POLICY_* */
+ CD_AUTHENTICATE, /* same order as POLICY_* */
+ CD_COMPRESS, /* same order as POLICY_* */
+ CD_TUNNEL, /* same order as POLICY_* */
+ CD_PFS, /* same order as POLICY_* */
+ CD_DISABLEARRIVALCHECK, /* same order as POLICY_* */
+ CD_SHUNT0, /* same order as POLICY_* */
+ CD_SHUNT1, /* same order as POLICY_* */
+ CD_FAIL0, /* same order as POLICY_* */
+ CD_FAIL1, /* same order as POLICY_* */
+ CD_DONT_REKEY, /* same order as POLICY_* */
+
+ CD_TUNNELIPV4,
+ CD_TUNNELIPV6,
+ CD_CONNIPV4,
+ CD_CONNIPV6,
+
+ CD_IKELIFETIME,
+ CD_IPSECLIFETIME,
+ CD_RKMARGIN,
+ CD_RKFUZZ,
+ CD_KTRIES,
+ CD_DPDACTION,
+ CD_DPDDELAY,
+ CD_DPDTIMEOUT,
+ CD_IKE,
+ CD_PFSGROUP,
+ CD_ESP,
+
+# define CD_LAST CD_ESP /* last connection description */
+
+/* Certificate Authority (CA) description options */
+
+# define CA_FIRST CA_NAME /* first ca description */
+
+ CA_NAME,
+ CA_CERT,
+ CA_LDAPHOST,
+ CA_LDAPBASE,
+ CA_CRLURI,
+ CA_CRLURI2,
+ CA_OCSPURI,
+ CA_STRICT
+
+# define CA_LAST CA_STRICT /* last ca description */
+
+#ifdef DEBUG /* must be last so others are less than 32 to fit in lset_t */
+# define DBGOPT_FIRST DBGOPT_NONE
+ ,
+ /* NOTE: these definitions must match DBG_* and IMPAIR_* in constants.h */
+ DBGOPT_NONE,
+ DBGOPT_ALL,
+
+ DBGOPT_RAW, /* same order as DBG_* */
+ DBGOPT_CRYPT, /* same order as DBG_* */
+ DBGOPT_PARSING, /* same order as DBG_* */
+ DBGOPT_EMITTING, /* same order as DBG_* */
+ DBGOPT_CONTROL, /* same order as DBG_* */
+ DBGOPT_LIFECYCLE, /* same order as DBG_* */
+ DBGOPT_KLIPS, /* same order as DBG_* */
+ DBGOPT_DNS, /* same order as DBG_* */
+ DBGOPT_NATT, /* same order as DBG_* */
+ DBGOPT_OPPO, /* same order as DBG_* */
+ DBGOPT_CONTROLMORE, /* same order as DBG_* */
+
+ DBGOPT_PRIVATE, /* same order as DBG_* */
+
+ DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER, /* same order as IMPAIR_* */
+ DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER, /* same order as IMPAIR_* */
+ DBGOPT_IMPAIR_BUST_MI2, /* same order as IMPAIR_* */
+ DBGOPT_IMPAIR_BUST_MR2 /* same order as IMPAIR_* */
+
+# define DBGOPT_LAST DBGOPT_IMPAIR_BUST_MR2
+#endif
+
+};
+
+/* Carve up space for result from getop_long.
+ * Stupidly, the only result is an int.
+ * Numeric arg is bit immediately left of basic value.
+ *
+ */
+#define OPTION_OFFSET 256 /* to get out of the way of letter options */
+#define NUMERIC_ARG (1 << 9) /* expect a numeric argument */
+#define AUX_SHIFT 10 /* amount to shift for aux information */
+
+static const struct option long_opts[] = {
+# define OO OPTION_OFFSET
+ /* name, has_arg, flag, val */
+
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'v' },
+ { "optionsfrom", required_argument, NULL, '+' },
+ { "label", required_argument, NULL, 'l' },
+
+ { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO },
+ { "name", required_argument, NULL, OPT_NAME + OO },
+
+ { "keyid", required_argument, NULL, OPT_KEYID + OO },
+ { "addkey", no_argument, NULL, OPT_ADDKEY + OO },
+ { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO },
+
+ { "myid", required_argument, NULL, OPT_MYID + OO },
+
+ { "route", no_argument, NULL, OPT_ROUTE + OO },
+ { "unroute", no_argument, NULL, OPT_UNROUTE + OO },
+
+ { "initiate", no_argument, NULL, OPT_INITIATE + OO },
+ { "terminate", no_argument, NULL, OPT_TERMINATE + OO },
+ { "delete", no_argument, NULL, OPT_DELETE + OO },
+ { "deletestate", required_argument, NULL, OPT_DELETESTATE + OO + NUMERIC_ARG },
+ { "crash", required_argument, NULL, OPT_DELETECRASH + OO },
+ { "listen", no_argument, NULL, OPT_LISTEN + OO },
+ { "unlisten", no_argument, NULL, OPT_UNLISTEN + OO },
+
+ { "purgeocsp", no_argument, NULL, OPT_PURGEOCSP + OO },
+
+ { "rereadsecrets", no_argument, NULL, OPT_REREADSECRETS + OO },
+ { "rereadcacerts", no_argument, NULL, OPT_REREADCACERTS + OO },
+ { "rereadaacerts", no_argument, NULL, OPT_REREADAACERTS + OO },
+ { "rereadocspcerts", no_argument, NULL, OPT_REREADOCSPCERTS + OO },
+ { "rereadacerts", no_argument, NULL, OPT_REREADACERTS + OO },
+ { "rereadcrls", no_argument, NULL, OPT_REREADCRLS + OO },
+ { "rereadall", no_argument, NULL, OPT_REREADALL + OO },
+ { "status", no_argument, NULL, OPT_STATUS + OO },
+ { "statusall", no_argument, NULL, OPT_STATUSALL + OO },
+ { "shutdown", no_argument, NULL, OPT_SHUTDOWN + OO },
+
+ { "oppohere", required_argument, NULL, OPT_OPPO_HERE + OO },
+ { "oppothere", required_argument, NULL, OPT_OPPO_THERE + OO },
+
+ { "asynchronous", no_argument, NULL, OPT_ASYNC + OO },
+
+ /* smartcard options */
+
+ { "scencrypt", required_argument, NULL, SC_ENCRYPT + OO },
+ { "scdecrypt", required_argument, NULL, SC_DECRYPT + OO },
+ { "inbase", required_argument, NULL, SC_INBASE + OO },
+ { "outbase", required_argument, NULL, SC_OUTBASE + OO },
+
+ /* list options */
+
+ { "utc", no_argument, NULL, LST_UTC + OO },
+ { "listalgs", no_argument, NULL, LST_ALGS + OO },
+ { "listpubkeys", no_argument, NULL, LST_PUBKEYS + OO },
+ { "listcerts", no_argument, NULL, LST_CERTS + OO },
+ { "listcacerts", no_argument, NULL, LST_CACERTS + OO },
+ { "listacerts", no_argument, NULL, LST_ACERTS + OO },
+ { "listaacerts", no_argument, NULL, LST_AACERTS + OO },
+ { "listocspcerts", no_argument, NULL, LST_OCSPCERTS + OO },
+ { "listgroups", no_argument, NULL, LST_GROUPS + OO },
+ { "listcainfos", no_argument, NULL, LST_CAINFOS + OO },
+ { "listcrls", no_argument, NULL, LST_CRLS + OO },
+ { "listocsp", no_argument, NULL, LST_OCSP + OO },
+ { "listcards", no_argument, NULL, LST_CARDS + OO },
+ { "listall", no_argument, NULL, LST_ALL + OO },
+
+ /* options for an end description */
+
+ { "host", required_argument, NULL, END_HOST + OO },
+ { "id", required_argument, NULL, END_ID + OO },
+ { "cert", required_argument, NULL, END_CERT + OO },
+ { "ca", required_argument, NULL, END_CA + OO },
+ { "sendcert", required_argument, NULL, END_SENDCERT + OO },
+ { "groups", required_argument, NULL, END_GROUPS + OO },
+ { "ikeport", required_argument, NULL, END_IKEPORT + OO + NUMERIC_ARG },
+ { "nexthop", required_argument, NULL, END_NEXTHOP + OO },
+ { "client", required_argument, NULL, END_CLIENT + OO },
+ { "clientwithin", required_argument, NULL, END_CLIENTWITHIN + OO },
+ { "clientprotoport", required_argument, NULL, END_CLIENTPROTOPORT + OO },
+ { "dnskeyondemand", no_argument, NULL, END_DNSKEYONDEMAND + OO },
+ { "srcip", required_argument, NULL, END_SRCIP + OO },
+ { "hostaccess", no_argument, NULL, END_HOSTACCESS + OO },
+ { "updown", required_argument, NULL, END_UPDOWN + OO },
+
+ /* options for a connection description */
+
+ { "to", no_argument, NULL, CD_TO + OO },
+
+ { "psk", no_argument, NULL, CD_PSK + OO },
+ { "rsasig", no_argument, NULL, CD_RSASIG + OO },
+
+ { "encrypt", no_argument, NULL, CD_ENCRYPT + OO },
+ { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO },
+ { "compress", no_argument, NULL, CD_COMPRESS + OO },
+ { "tunnel", no_argument, NULL, CD_TUNNEL + OO },
+ { "tunnelipv4", no_argument, NULL, CD_TUNNELIPV4 + OO },
+ { "tunnelipv6", no_argument, NULL, CD_TUNNELIPV6 + OO },
+ { "pfs", no_argument, NULL, CD_PFS + OO },
+ { "disablearrivalcheck", no_argument, NULL, CD_DISABLEARRIVALCHECK + OO },
+ { "initiateontraffic", no_argument, NULL
+ , CD_SHUNT0 + (POLICY_SHUNT_TRAP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
+ { "pass", no_argument, NULL
+ , CD_SHUNT0 + (POLICY_SHUNT_PASS >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
+ { "drop", no_argument, NULL
+ , CD_SHUNT0 + (POLICY_SHUNT_DROP >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
+ { "reject", no_argument, NULL
+ , CD_SHUNT0 + (POLICY_SHUNT_REJECT >> POLICY_SHUNT_SHIFT << AUX_SHIFT) + OO },
+ { "failnone", no_argument, NULL
+ , CD_FAIL0 + (POLICY_FAIL_NONE >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
+ { "failpass", no_argument, NULL
+ , CD_FAIL0 + (POLICY_FAIL_PASS >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
+ { "faildrop", no_argument, NULL
+ , CD_FAIL0 + (POLICY_FAIL_DROP >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
+ { "failreject", no_argument, NULL
+ , CD_FAIL0 + (POLICY_FAIL_REJECT >> POLICY_FAIL_SHIFT << AUX_SHIFT) + OO },
+ { "dontrekey", no_argument, NULL, CD_DONT_REKEY + OO },
+ { "ipv4", no_argument, NULL, CD_CONNIPV4 + OO },
+ { "ipv6", no_argument, NULL, CD_CONNIPV6 + OO },
+
+ { "ikelifetime", required_argument, NULL, CD_IKELIFETIME + OO + NUMERIC_ARG },
+ { "ipseclifetime", required_argument, NULL, CD_IPSECLIFETIME + OO + NUMERIC_ARG },
+ { "rekeymargin", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG },
+ { "rekeywindow", required_argument, NULL, CD_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */
+ { "rekeyfuzz", required_argument, NULL, CD_RKFUZZ + OO + NUMERIC_ARG },
+ { "keyingtries", required_argument, NULL, CD_KTRIES + OO + NUMERIC_ARG },
+ { "dpdaction", required_argument, NULL, CD_DPDACTION + OO },
+ { "dpddelay", required_argument, NULL, CD_DPDDELAY + OO + NUMERIC_ARG },
+ { "dpdtimeout", required_argument, NULL, CD_DPDTIMEOUT + OO + NUMERIC_ARG },
+ { "ike", required_argument, NULL, CD_IKE + OO },
+ { "pfsgroup", required_argument, NULL, CD_PFSGROUP + OO },
+ { "esp", required_argument, NULL, CD_ESP + OO },
+
+ /* options for a ca description */
+
+ { "caname", required_argument, NULL, CA_NAME + OO },
+ { "cacert", required_argument, NULL, CA_CERT + OO },
+ { "ldaphost", required_argument, NULL, CA_LDAPHOST + OO },
+ { "ldapbase", required_argument, NULL, CA_LDAPBASE + OO },
+ { "crluri", required_argument, NULL, CA_CRLURI + OO },
+ { "crluri2", required_argument, NULL, CA_CRLURI2 + OO },
+ { "ocspuri", required_argument, NULL, CA_OCSPURI + OO },
+ { "strictcrlpolicy", no_argument, NULL, CA_STRICT + OO },
+
+#ifdef DEBUG
+ { "debug-none", no_argument, NULL, DBGOPT_NONE + OO },
+ { "debug-all]", no_argument, NULL, DBGOPT_ALL + OO },
+ { "debug-raw", no_argument, NULL, DBGOPT_RAW + OO },
+ { "debug-crypt", no_argument, NULL, DBGOPT_CRYPT + OO },
+ { "debug-parsing", no_argument, NULL, DBGOPT_PARSING + OO },
+ { "debug-emitting", no_argument, NULL, DBGOPT_EMITTING + OO },
+ { "debug-control", no_argument, NULL, DBGOPT_CONTROL + OO },
+ { "debug-lifecycle", no_argument, NULL, DBGOPT_LIFECYCLE + OO },
+ { "debug-klips", no_argument, NULL, DBGOPT_KLIPS + OO },
+ { "debug-dns", no_argument, NULL, DBGOPT_DNS + OO },
+ { "debug-natt", no_argument, NULL, DBGOPT_NATT + OO },
+ { "debug-oppo", no_argument, NULL, DBGOPT_OPPO + OO },
+ { "debug-controlmore", no_argument, NULL, DBGOPT_CONTROLMORE + OO },
+ { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO },
+
+ { "impair-delay-adns-key-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER + OO },
+ { "impair-delay-adns-txt-answer", no_argument, NULL, DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER + OO },
+ { "impair-bust-mi2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MI2 + OO },
+ { "impair-bust-mr2", no_argument, NULL, DBGOPT_IMPAIR_BUST_MR2 + OO },
+#endif
+# undef OO
+ { 0,0,0,0 }
+};
+
+struct sockaddr_un ctl_addr = { AF_UNIX, DEFAULT_CTLBASE CTL_SUFFIX };
+
+/* helper variables and function to encode strings from whack message */
+
+static char
+ *next_str,
+ *str_roof;
+
+static bool
+pack_str(char **p)
+{
+ const char *s = *p == NULL? "" : *p; /* note: NULL becomes ""! */
+ size_t len = strlen(s) + 1;
+
+ if (str_roof - next_str < (ptrdiff_t)len)
+ {
+ return FALSE; /* fishy: no end found */
+ }
+ else
+ {
+ strcpy(next_str, s);
+ next_str += len;
+ *p = NULL; /* don't send pointers on the wire! */
+ return TRUE;
+ }
+}
+
+static void
+check_life_time(time_t life, time_t limit, const char *which
+, const whack_message_t *msg)
+{
+ time_t mint = msg->sa_rekey_margin * (100 + msg->sa_rekey_fuzz) / 100;
+
+ if (life > limit)
+ {
+ char buf[200]; /* arbitrary limit */
+
+ snprintf(buf, sizeof(buf)
+ , "%s [%lu seconds] must be less than %lu seconds"
+ , which, (unsigned long)life, (unsigned long)limit);
+ diag(buf);
+ }
+ if ((msg->policy & POLICY_DONT_REKEY) == LEMPTY && life <= mint)
+ {
+ char buf[200]; /* arbitrary limit */
+
+ snprintf(buf, sizeof(buf)
+ , "%s [%lu] must be greater than"
+ " rekeymargin*(100+rekeyfuzz)/100 [%lu*(100+%lu)/100 = %lu]"
+ , which
+ , (unsigned long)life
+ , (unsigned long)msg->sa_rekey_margin
+ , (unsigned long)msg->sa_rekey_fuzz
+ , (unsigned long)mint);
+ diag(buf);
+ }
+}
+
+static void
+clear_end(whack_end_t *e)
+{
+ zero(e);
+ e->id = NULL;
+ e->cert = NULL;
+ e->ca = NULL;
+ e->updown = NULL;
+ e->host_port = IKE_UDP_PORT;
+}
+
+static void
+update_ports(whack_message_t *m)
+{
+ int port;
+
+ if (m->left.port != 0) {
+ port = htons(m->left.port);
+ setportof(port, &m->left.host_addr);
+ setportof(port, &m->left.client.addr);
+ }
+ if (m->right.port != 0) {
+ port = htons(m->right.port);
+ setportof(port, &m->right.host_addr);
+ setportof(port, &m->right.client.addr);
+ }
+}
+
+static void
+check_end(whack_end_t *this, whack_end_t *that
+, bool default_nexthop, sa_family_t caf, sa_family_t taf)
+{
+ if (caf != addrtypeof(&this->host_addr))
+ diag("address family of host inconsistent");
+
+ if (default_nexthop)
+ {
+ if (isanyaddr(&that->host_addr))
+ diag("our nexthop must be specified when other host is a %any or %opportunistic");
+ this->host_nexthop = that->host_addr;
+ }
+
+ if (caf != addrtypeof(&this->host_nexthop))
+ diag("address family of nexthop inconsistent");
+
+ if (this->has_client)
+ {
+ if (taf != subnettypeof(&this->client))
+ diag("address family of client subnet inconsistent");
+ }
+ else
+ {
+ /* fill in anyaddr-anyaddr as (missing) client subnet */
+ ip_address cn;
+
+ diagq(anyaddr(caf, &cn), NULL);
+ diagq(rangetosubnet(&cn, &cn, &this->client), NULL);
+ }
+
+ /* fill in anyaddr if source IP is not defined */
+ if (!this->has_srcip)
+ diagq(anyaddr(caf, &this->host_srcip), optarg);
+
+ /* check protocol */
+ if (this->protocol != that->protocol)
+ diag("the protocol for leftprotoport and rightprotoport must be the same");
+}
+
+static void
+get_secret(int sock)
+{
+ const char *buf, *secret;
+ int len;
+
+ fflush(stdout);
+ usleep(20000); /* give fflush time for flushing */
+ buf = getpass("Enter: ");
+ secret = (buf == NULL)? "" : buf;
+
+ /* send the secret to pluto */
+ len = strlen(secret) + 1;
+ if (write(sock, secret, len) != len)
+ {
+ int e = errno;
+
+ fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
+ exit(RC_WHACK_PROBLEM);
+ }
+}
+
+/* This is a hack for initiating ISAKMP exchanges. */
+
+int
+main(int argc, char **argv)
+{
+ whack_message_t msg;
+ char esp_buf[256]; /* uses snprintf */
+ lset_t
+ opts_seen = LEMPTY,
+ sc_seen = LEMPTY,
+ lst_seen = LEMPTY,
+ cd_seen = LEMPTY,
+ ca_seen = LEMPTY,
+ end_seen = LEMPTY,
+ end_seen_before_to = LEMPTY;
+ const char
+ *af_used_by = NULL,
+ *tunnel_af_used_by = NULL;
+
+ /* check division of numbering space */
+#ifdef DEBUG
+ assert(OPTION_OFFSET + DBGOPT_LAST < NUMERIC_ARG);
+#else
+ assert(OPTION_OFFSET + CA_LAST < NUMERIC_ARG);
+#endif
+ assert(OPT_LAST - OPT_FIRST < (sizeof opts_seen * BITS_PER_BYTE));
+ assert(SC_LAST - SC_FIRST < (sizeof sc_seen * BITS_PER_BYTE));
+ assert(LST_LAST - LST_FIRST < (sizeof lst_seen * BITS_PER_BYTE));
+ assert(END_LAST - END_FIRST < (sizeof end_seen * BITS_PER_BYTE));
+ assert(CD_LAST - CD_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
+ assert(CA_LAST - CA_FIRST < (sizeof ca_seen * BITS_PER_BYTE));
+#ifdef DEBUG /* must be last so others are less than (sizeof cd_seen * BITS_PER_BYTE) to fit in lset_t */
+ assert(DBGOPT_LAST - DBGOPT_FIRST < (sizeof cd_seen * BITS_PER_BYTE));
+#endif
+ /* check that POLICY bit assignment matches with CD_ */
+ assert(LELEM(CD_DONT_REKEY - CD_POLICY_FIRST) == POLICY_DONT_REKEY);
+
+ zero(&msg);
+
+ clear_end(&msg.right); /* left set from this after --to */
+
+ msg.name = NULL;
+ msg.keyid = NULL;
+ msg.keyval.ptr = NULL;
+ msg.esp = NULL;
+ msg.ike = NULL;
+ msg.pfsgroup = NULL;
+
+ msg.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
+ msg.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
+ msg.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
+ msg.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
+ msg.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
+
+ msg.addr_family = AF_INET;
+ msg.tunnel_addr_family = AF_INET;
+
+ msg.cacert = NULL;
+ msg.ldaphost = NULL;
+ msg.ldapbase = NULL;
+ msg.crluri = NULL;
+ msg.crluri2 = NULL;
+ msg.ocspuri = NULL;
+
+ for (;;)
+ {
+ int long_index;
+ unsigned long opt_whole = 0; /* numeric argument for some flags */
+
+ /* 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 "hp:d:c:o:eatfs" "NARXPECK".
+ */
+ int c = getopt_long(argc, argv, "", long_opts, &long_index) - OPTION_OFFSET;
+ int aux = 0;
+
+ /* decode a numeric argument, if expected */
+ if (0 <= c)
+ {
+ if (c & NUMERIC_ARG)
+ {
+ char *endptr;
+
+ c -= NUMERIC_ARG;
+ opt_whole = strtoul(optarg, &endptr, 0);
+
+ if (*endptr != '\0' || endptr == optarg)
+ diagq("badly formed numeric argument", optarg);
+ }
+ if (c >= (1 << AUX_SHIFT))
+ {
+ aux = c >> AUX_SHIFT;
+ c -= aux << AUX_SHIFT;
+ }
+ }
+
+ /* per-class option processing */
+ if (0 <= c && c <= OPT_LAST)
+ {
+ /* OPT_* options get added to opts_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c);
+
+ if (opts_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ opts_seen |= f;
+ }
+ else if (SC_FIRST <= c && c <= SC_LAST)
+ {
+ /* SC_* options get added to sc_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c - SC_FIRST);
+
+ if (sc_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ sc_seen |= f;
+ }
+ else if (LST_FIRST <= c && c <= LST_LAST)
+ {
+ /* LST_* options get added to lst_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c - LST_FIRST);
+
+ if (lst_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ lst_seen |= f;
+ }
+#ifdef DEBUG
+ else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST)
+ {
+ msg.whack_options = TRUE;
+ }
+#endif
+ else if (END_FIRST <= c && c <= END_LAST)
+ {
+ /* END_* options are added to end_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c - END_FIRST);
+
+ if (end_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ end_seen |= f;
+ opts_seen |= LELEM(OPT_CD);
+ }
+ else if (CD_FIRST <= c && c <= CD_LAST)
+ {
+ /* CD_* options are added to cd_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c - CD_FIRST);
+
+ if (cd_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ cd_seen |= f;
+ opts_seen |= LELEM(OPT_CD);
+ }
+ else if (CA_FIRST <= c && c <= CA_LAST)
+ {
+ /* CA_* options are added to ca_seen.
+ * Reject repeated options (unless later code intervenes).
+ */
+ lset_t f = LELEM(c - CA_FIRST);
+
+ if (ca_seen & f)
+ diagq("duplicated flag", long_opts[long_index].name);
+ ca_seen |= f;
+ }
+
+ /* Note: "break"ing from switch terminates loop.
+ * most cases should end with "continue".
+ */
+ switch (c)
+ {
+ case EOF - OPTION_OFFSET: /* end of flags */
+ break;
+
+ case 0 - OPTION_OFFSET: /* long option already handled */
+ continue;
+
+ case ':' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
+ case '?' - OPTION_OFFSET: /* diagnostic already printed by getopt_long */
+ diag(NULL); /* print no additional diagnostic, but exit sadly */
+ break; /* not actually reached */
+
+ case 'h' - OPTION_OFFSET: /* --help */
+ help();
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'v' - OPTION_OFFSET: /* --version */
+ {
+ const char **sp = ipsec_copyright_notice();
+
+ printf("%s\n", ipsec_version_string());
+ for (; *sp != NULL; sp++)
+ puts(*sp);
+ }
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'l' - OPTION_OFFSET: /* --label <string> */
+ label = optarg; /* remember for diagnostics */
+ continue;
+
+ case '+' - OPTION_OFFSET: /* --optionsfrom <filename> */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* does not return on error */
+ continue;
+
+ /* the rest of the options combine in complex ways */
+
+ case OPT_CTLBASE: /* --port <ctlbase> */
+ if (snprintf(ctl_addr.sun_path, sizeof(ctl_addr.sun_path)
+ , "%s%s", optarg, CTL_SUFFIX) == -1)
+ diag("<ctlbase>" CTL_SUFFIX " must be fit in a sun_addr");
+ continue;
+
+ case OPT_NAME: /* --name <connection-name> */
+ name = optarg;
+ msg.name = optarg;
+ continue;
+
+ case OPT_KEYID: /* --keyid <identity> */
+ msg.whack_key = !msg.whack_sc_op;
+ msg.keyid = optarg; /* decoded by Pluto */
+ continue;
+
+ case OPT_MYID: /* --myid <identity> */
+ msg.whack_myid = TRUE;
+ msg.myid = optarg; /* decoded by Pluto */
+ continue;
+
+ case OPT_ADDKEY: /* --addkey */
+ msg.whack_addkey = TRUE;
+ continue;
+
+ case OPT_PUBKEYRSA: /* --pubkeyrsa <key> */
+ {
+ static char keyspace[RSA_MAX_ENCODING_BYTES]; /* room for 8K bit key */
+ char diag_space[TTODATAV_BUF];
+ const char *ugh = ttodatav(optarg, 0, 0
+ , keyspace, sizeof(keyspace)
+ , &msg.keyval.len, diag_space, sizeof(diag_space)
+ , TTODATAV_SPACECOUNTS);
+
+ if (ugh != NULL)
+ {
+ char ugh_space[80]; /* perhaps enough space */
+
+ snprintf(ugh_space, sizeof(ugh_space)
+ , "RSA public-key data malformed (%s)", ugh);
+ diagq(ugh_space, optarg);
+ }
+ msg.pubkey_alg = PUBKEY_ALG_RSA;
+ msg.keyval.ptr = keyspace;
+ }
+ continue;
+
+ case OPT_ROUTE: /* --route */
+ msg.whack_route = TRUE;
+ continue;
+
+ case OPT_UNROUTE: /* --unroute */
+ msg.whack_unroute = TRUE;
+ continue;
+
+ case OPT_INITIATE: /* --initiate */
+ msg.whack_initiate = TRUE;
+ continue;
+
+ case OPT_TERMINATE: /* --terminate */
+ msg.whack_terminate = TRUE;
+ continue;
+
+ case OPT_DELETE: /* --delete */
+ msg.whack_delete = TRUE;
+ continue;
+
+ case OPT_DELETESTATE: /* --deletestate <state_object_number> */
+ msg.whack_deletestate = TRUE;
+ msg.whack_deletestateno = opt_whole;
+ continue;
+
+ case OPT_DELETECRASH: /* --crash <ip-address> */
+ msg.whack_crash = TRUE;
+ tunnel_af_used_by = long_opts[long_index].name;
+ diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.whack_crash_peer), optarg);
+ if (isanyaddr(&msg.whack_crash_peer))
+ diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
+ continue;
+
+ case OPT_LISTEN: /* --listen */
+ msg.whack_listen = TRUE;
+ continue;
+
+ case OPT_UNLISTEN: /* --unlisten */
+ msg.whack_unlisten = TRUE;
+ continue;
+
+ case OPT_PURGEOCSP: /* --purgeocsp */
+ msg.whack_purgeocsp = TRUE;
+ continue;
+
+ case OPT_REREADSECRETS: /* --rereadsecrets */
+ case OPT_REREADCACERTS: /* --rereadcacerts */
+ case OPT_REREADAACERTS: /* --rereadaacerts */
+ case OPT_REREADOCSPCERTS: /* --rereadocspcerts */
+ case OPT_REREADACERTS: /* --rereadacerts */
+ case OPT_REREADCRLS: /* --rereadcrls */
+ msg.whack_reread |= LELEM(c-OPT_REREADSECRETS);
+ continue;
+
+ case OPT_REREADALL: /* --rereadall */
+ msg.whack_reread = REREAD_ALL;
+ continue;
+
+ case OPT_STATUSALL: /* --statusall */
+ msg.whack_statusall = TRUE;
+
+ case OPT_STATUS: /* --status */
+ msg.whack_status = TRUE;
+ continue;
+
+ case OPT_SHUTDOWN: /* --shutdown */
+ msg.whack_shutdown = TRUE;
+ continue;
+
+ case OPT_OPPO_HERE: /* --oppohere <ip-address> */
+ tunnel_af_used_by = long_opts[long_index].name;
+ diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_my_client), optarg);
+ if (isanyaddr(&msg.oppo_my_client))
+ diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
+ continue;
+
+ case OPT_OPPO_THERE: /* --oppohere <ip-address> */
+ tunnel_af_used_by = long_opts[long_index].name;
+ diagq(ttoaddr(optarg, 0, msg.tunnel_addr_family, &msg.oppo_peer_client), optarg);
+ if (isanyaddr(&msg.oppo_peer_client))
+ diagq("0.0.0.0 or 0::0 isn't a valid client address", optarg);
+ continue;
+
+ case OPT_ASYNC:
+ msg.whack_async = TRUE;
+ continue;
+
+ /* Smartcard options */
+
+ case SC_ENCRYPT: /* --scencrypt <plaintext data> */
+ case SC_DECRYPT: /* --scdecrypt <encrypted data> */
+ msg.whack_sc_op = 1 + c - SC_ENCRYPT;
+ msg.whack_key = FALSE;
+ msg.sc_data = optarg;
+ continue;
+
+ case SC_INBASE: /* --inform <format> */
+ case SC_OUTBASE: /* --outform <format> */
+ {
+ int base = 0;
+
+ if (streq(optarg, "16") || strcaseeq(optarg, "hex"))
+ base = 16;
+ else if (streq(optarg, "64") || strcaseeq(optarg, "base64"))
+ base = 64;
+ else if (streq(optarg, "256") || strcaseeq(optarg, "text")
+ || strcaseeq(optarg, "ascii"))
+ base = 256;
+ else
+ diagq("not a valid base", optarg);
+
+ if (c == SC_INBASE)
+ msg.inbase = base;
+ else
+ msg.outbase = base;
+ }
+ continue;
+
+ /* List options */
+
+ case LST_UTC: /* --utc */
+ msg.whack_utc = TRUE;
+ continue;
+
+ case LST_ALGS: /* --listalgs */
+ case LST_PUBKEYS: /* --listpubkeys */
+ case LST_CERTS: /* --listcerts */
+ case LST_CACERTS: /* --listcacerts */
+ case LST_ACERTS: /* --listacerts */
+ case LST_AACERTS: /* --listaacerts */
+ case LST_OCSPCERTS: /* --listocspcerts */
+ case LST_GROUPS: /* --listgroups */
+ case LST_CAINFOS: /* --listcainfos */
+ case LST_CRLS: /* --listcrls */
+ case LST_OCSP: /* --listocsp */
+ case LST_CARDS: /* --listcards */
+ msg.whack_list |= LELEM(c - LST_ALGS);
+ continue;
+
+ case LST_ALL: /* --listall */
+ msg.whack_list = LIST_ALL;
+ continue;
+
+ /* Connection Description options */
+
+ case END_HOST: /* --host <ip-address> */
+ {
+ lset_t new_policy = LEMPTY;
+
+ af_used_by = long_opts[long_index].name;
+ diagq(anyaddr(msg.addr_family, &msg.right.host_addr), optarg);
+ if (streq(optarg, "%any"))
+ {
+ }
+ else if (streq(optarg, "%opportunistic"))
+ {
+ /* always use tunnel mode; mark as opportunistic */
+ new_policy |= POLICY_TUNNEL | POLICY_OPPO;
+ }
+ else if (streq(optarg, "%group"))
+ {
+ /* always use tunnel mode; mark as group */
+ new_policy |= POLICY_TUNNEL | POLICY_GROUP;
+ }
+ else if (streq(optarg, "%opportunisticgroup"))
+ {
+ /* always use tunnel mode; mark as opportunistic */
+ new_policy |= POLICY_TUNNEL | POLICY_OPPO | POLICY_GROUP;
+ }
+ else
+ {
+ diagq(ttoaddr(optarg, 0, msg.addr_family
+ , &msg.right.host_addr), optarg);
+ }
+
+ msg.policy |= new_policy;
+
+ if (new_policy & (POLICY_OPPO | POLICY_GROUP))
+ {
+ if (!LHAS(end_seen, END_CLIENT - END_FIRST))
+ {
+ /* set host to 0.0.0 and --client to 0.0.0.0/0
+ * or IPV6 equivalent
+ */
+ ip_address any;
+
+ tunnel_af_used_by = optarg;
+ diagq(anyaddr(msg.tunnel_addr_family, &any), optarg);
+ diagq(initsubnet(&any, 0, '0', &msg.right.client), optarg);
+ }
+ msg.right.has_client = TRUE;
+ }
+ if (new_policy & POLICY_GROUP)
+ {
+ /* client subnet must not be specified by user:
+ * it will come from the group's file.
+ */
+ if (LHAS(end_seen, END_CLIENT - END_FIRST))
+ diag("--host %group clashes with --client");
+
+ end_seen |= LELEM(END_CLIENT - END_FIRST);
+ }
+ if (new_policy & POLICY_OPPO)
+ msg.right.key_from_DNS_on_demand = TRUE;
+ continue;
+ }
+ case END_ID: /* --id <identity> */
+ msg.right.id = optarg; /* decoded by Pluto */
+ continue;
+
+ case END_CERT: /* --cert <path> */
+ msg.right.cert = optarg; /* decoded by Pluto */
+ continue;
+
+ case END_CA: /* --ca <distinguished name> */
+ msg.right.ca = optarg; /* decoded by Pluto */
+ continue;
+
+ case END_SENDCERT:
+ if (streq(optarg, "yes") || streq(optarg, "always"))
+ {
+ msg.right.sendcert = CERT_ALWAYS_SEND;
+ }
+ else if (streq(optarg, "no") || streq(optarg, "never"))
+ {
+ msg.right.sendcert = CERT_NEVER_SEND;
+ }
+ else if (streq(optarg, "ifasked"))
+ {
+ msg.right.sendcert = CERT_SEND_IF_ASKED;
+ }
+ else
+ {
+ diagq("whack sendcert value is not legal", optarg);
+ }
+ continue;
+
+ case END_GROUPS:/* --groups <access control groups> */
+ msg.right.groups = optarg; /* decoded by Pluto */
+ continue;
+
+ case END_IKEPORT: /* --ikeport <port-number> */
+ if (opt_whole<=0 || opt_whole >= 0x10000)
+ diagq("<port-number> must be a number between 1 and 65535", optarg);
+ msg.right.host_port = opt_whole;
+ continue;
+
+ case END_NEXTHOP: /* --nexthop <ip-address> */
+ af_used_by = long_opts[long_index].name;
+ if (streq(optarg, "%direct"))
+ diagq(anyaddr(msg.addr_family
+ , &msg.right.host_nexthop), optarg);
+ else
+ diagq(ttoaddr(optarg, 0, msg.addr_family
+ , &msg.right.host_nexthop), optarg);
+ continue;
+
+ case END_SRCIP: /* --srcip <ip-address> */
+ af_used_by = long_opts[long_index].name;
+ if (streq(optarg, "%modeconfig") || streq(optarg, "%modecfg"))
+ {
+ msg.right.modecfg = TRUE;
+ }
+ else
+ {
+ diagq(ttoaddr(optarg, 0, msg.addr_family
+ , &msg.right.host_srcip), optarg);
+ msg.right.has_srcip = TRUE;
+ }
+ msg.policy |= POLICY_TUNNEL; /* srcip => tunnel */
+ continue;
+
+ case END_CLIENT: /* --client <subnet> */
+ if (end_seen & LELEM(END_CLIENTWITHIN - END_FIRST))
+ diag("--client conflicts with --clientwithin");
+ tunnel_af_used_by = long_opts[long_index].name;
+#ifdef VIRTUAL_IP
+ if ((strlen(optarg) >= 6 && strncmp(optarg,"vhost:",6) == 0)
+ || (strlen(optarg) >= 5 && strncmp(optarg,"vnet:",5) == 0))
+ {
+ msg.right.virt = optarg;
+ }
+ else
+ {
+ diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
+ msg.right.has_client = TRUE;
+ }
+#else
+ diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
+ msg.right.has_client = TRUE;
+#endif
+ msg.policy |= POLICY_TUNNEL; /* client => tunnel */
+ continue;
+
+ case END_CLIENTWITHIN: /* --clienwithin <address range> */
+ if (end_seen & LELEM(END_CLIENT - END_FIRST))
+ diag("--clientwithin conflicts with --client");
+ tunnel_af_used_by = long_opts[long_index].name;
+ diagq(ttosubnet(optarg, 0, msg.tunnel_addr_family, &msg.right.client), optarg);
+ msg.right.has_client = TRUE;
+ msg.policy |= POLICY_TUNNEL; /* client => tunnel */
+ msg.right.has_client_wildcard = TRUE;
+ continue;
+
+ case END_CLIENTPROTOPORT: /* --clientprotoport <protocol>/<port> */
+ diagq(ttoprotoport(optarg, 0, &msg.right.protocol, &msg.right.port
+ , &msg.right.has_port_wildcard), optarg);
+ continue;
+
+ case END_DNSKEYONDEMAND: /* --dnskeyondemand */
+ msg.right.key_from_DNS_on_demand = TRUE;
+ continue;
+
+ case END_HOSTACCESS: /* --hostaccess */
+ msg.right.hostaccess = TRUE;
+ continue;
+
+ case END_UPDOWN: /* --updown <updown> */
+ msg.right.updown = optarg;
+ continue;
+
+ case CD_TO: /* --to */
+ /* process right end, move it to left, reset it */
+ if (!LHAS(end_seen, END_HOST - END_FIRST))
+ diag("connection missing --host before --to");
+ msg.left = msg.right;
+ clear_end(&msg.right);
+ end_seen_before_to = end_seen;
+ end_seen = LEMPTY;
+ continue;
+
+ case CD_PSK: /* --psk */
+ case CD_RSASIG: /* --rsasig */
+ case CD_ENCRYPT: /* --encrypt */
+ case CD_AUTHENTICATE: /* --authenticate */
+ case CD_COMPRESS: /* --compress */
+ case CD_TUNNEL: /* --tunnel */
+ case CD_PFS: /* --pfs */
+ case CD_DISABLEARRIVALCHECK: /* --disablearrivalcheck */
+ case CD_DONT_REKEY: /* --donotrekey */
+ msg.policy |= LELEM(c - CD_POLICY_FIRST);
+ continue;
+
+ /* --initiateontraffic
+ * --pass
+ * --drop
+ * --reject
+ */
+ case CD_SHUNT0:
+ msg.policy = (msg.policy & ~POLICY_SHUNT_MASK)
+ | ((lset_t)aux << POLICY_SHUNT_SHIFT);
+ continue;
+
+ /* --failnone
+ * --failpass
+ * --faildrop
+ * --failreject
+ */
+ case CD_FAIL0:
+ msg.policy = (msg.policy & ~POLICY_FAIL_MASK)
+ | ((lset_t)aux << POLICY_FAIL_SHIFT);
+ continue;
+
+ case CD_IKELIFETIME: /* --ikelifetime <seconds> */
+ msg.sa_ike_life_seconds = opt_whole;
+ continue;
+
+ case CD_IPSECLIFETIME: /* --ipseclifetime <seconds> */
+ msg.sa_ipsec_life_seconds = opt_whole;
+ continue;
+
+ case CD_RKMARGIN: /* --rekeymargin <seconds> */
+ msg.sa_rekey_margin = opt_whole;
+ continue;
+
+ case CD_RKFUZZ: /* --rekeyfuzz <percentage> */
+ msg.sa_rekey_fuzz = opt_whole;
+ continue;
+
+ case CD_KTRIES: /* --keyingtries <count> */
+ msg.sa_keying_tries = opt_whole;
+ continue;
+
+ case CD_DPDACTION:
+ if (streq(optarg, "none"))
+ msg.dpd_action = DPD_ACTION_NONE;
+ else if (streq(optarg, "clear"))
+ msg.dpd_action = DPD_ACTION_CLEAR;
+ else if (streq(optarg, "hold"))
+ msg.dpd_action = DPD_ACTION_HOLD;
+ else if (streq(optarg, "restart"))
+ msg.dpd_action = DPD_ACTION_RESTART;
+ else
+ msg.dpd_action = DPD_ACTION_UNKNOWN;
+ continue;
+
+ case CD_DPDDELAY:
+ msg.dpd_delay = opt_whole;
+ continue;
+
+ case CD_DPDTIMEOUT:
+ msg.dpd_timeout = opt_whole;
+ continue;
+
+ case CD_IKE: /* --ike <ike_alg1,ike_alg2,...> */
+ msg.ike = optarg;
+ continue;
+
+ case CD_PFSGROUP: /* --pfsgroup modpXXXX */
+ msg.pfsgroup = optarg;
+ continue;
+
+ case CD_ESP: /* --esp <esp_alg1,esp_alg2,...> */
+ msg.esp = optarg;
+ continue;
+
+ case CD_CONNIPV4:
+ if (LHAS(cd_seen, CD_CONNIPV6 - CD_FIRST))
+ diag("--ipv4 conflicts with --ipv6");
+
+ /* Since this is the default, the flag is redundant.
+ * So we don't need to set msg.addr_family
+ * and we don't need to check af_used_by
+ * and we don't have to consider defaulting tunnel_addr_family.
+ */
+ continue;
+
+ case CD_CONNIPV6:
+ if (LHAS(cd_seen, CD_CONNIPV4 - CD_FIRST))
+ diag("--ipv6 conflicts with --ipv4");
+
+ if (af_used_by != NULL)
+ diagq("--ipv6 must precede", af_used_by);
+
+ af_used_by = long_opts[long_index].name;
+ msg.addr_family = AF_INET6;
+
+ /* Consider defaulting tunnel_addr_family to AF_INET6.
+ * Do so only if it hasn't yet been specified or used.
+ */
+ if (LDISJOINT(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST))
+ && tunnel_af_used_by == NULL)
+ msg.tunnel_addr_family = AF_INET6;
+ continue;
+
+ case CD_TUNNELIPV4:
+ if (LHAS(cd_seen, CD_TUNNELIPV6 - CD_FIRST))
+ diag("--tunnelipv4 conflicts with --tunnelipv6");
+
+ if (tunnel_af_used_by != NULL)
+ diagq("--tunnelipv4 must precede", af_used_by);
+
+ msg.tunnel_addr_family = AF_INET;
+ continue;
+
+ case CD_TUNNELIPV6:
+ if (LHAS(cd_seen, CD_TUNNELIPV4 - CD_FIRST))
+ diag("--tunnelipv6 conflicts with --tunnelipv4");
+
+ if (tunnel_af_used_by != NULL)
+ diagq("--tunnelipv6 must precede", af_used_by);
+
+ msg.tunnel_addr_family = AF_INET6;
+ continue;
+
+ case CA_NAME: /* --caname <name> */
+ msg.name = optarg;
+ msg.whack_ca = TRUE;
+ continue;
+ case CA_CERT: /* --cacert <path> */
+ msg.cacert = optarg;
+ continue;
+ case CA_LDAPHOST: /* --ldaphost <hostname> */
+ msg.ldaphost = optarg;
+ continue;
+ case CA_LDAPBASE: /* --ldapbase <base> */
+ msg.ldapbase = optarg;
+ continue;
+ case CA_CRLURI: /* --crluri <uri> */
+ msg.crluri = optarg;
+ continue;
+ case CA_CRLURI2: /* --crluri2 <uri> */
+ msg.crluri2 = optarg;
+ continue;
+ case CA_OCSPURI: /* --ocspuri <uri> */
+ msg.ocspuri = optarg;
+ continue;
+ case CA_STRICT: /* --strictcrlpolicy */
+ msg.whack_strict = TRUE;
+ continue;
+
+#ifdef DEBUG
+ case DBGOPT_NONE: /* --debug-none */
+ msg.debugging = DBG_NONE;
+ continue;
+
+ case DBGOPT_ALL: /* --debug-all */
+ msg.debugging |= DBG_ALL; /* note: does not include PRIVATE */
+ continue;
+
+ case DBGOPT_RAW: /* --debug-raw */
+ case DBGOPT_CRYPT: /* --debug-crypt */
+ case DBGOPT_PARSING: /* --debug-parsing */
+ case DBGOPT_EMITTING: /* --debug-emitting */
+ case DBGOPT_CONTROL: /* --debug-control */
+ case DBGOPT_LIFECYCLE: /* --debug-lifecycle */
+ case DBGOPT_KLIPS: /* --debug-klips */
+ case DBGOPT_DNS: /* --debug-dns */
+ case DBGOPT_NATT: /* --debug-natt */
+ case DBGOPT_OPPO: /* --debug-oppo */
+ case DBGOPT_CONTROLMORE: /* --debug-controlmore */
+ case DBGOPT_PRIVATE: /* --debug-private */
+ case DBGOPT_IMPAIR_DELAY_ADNS_KEY_ANSWER: /* --impair-delay-adns-key-answer */
+ case DBGOPT_IMPAIR_DELAY_ADNS_TXT_ANSWER: /* --impair-delay-adns-txt-answer */
+ case DBGOPT_IMPAIR_BUST_MI2: /* --impair_bust_mi2 */
+ case DBGOPT_IMPAIR_BUST_MR2: /* --impair_bust_mr2 */
+ msg.debugging |= LELEM(c-DBGOPT_RAW);
+ continue;
+#endif
+ default:
+ assert(FALSE); /* unknown return value */
+ }
+ break;
+ }
+
+ if (optind != argc)
+ {
+ /* If you see this message unexpectedly, perhaps the
+ * case for the previous option ended with "break"
+ * instead of "continue"
+ */
+ diagq("unexpected argument", argv[optind]);
+ }
+
+ /* For each possible form of the command, figure out if an argument
+ * suggests whether that form was intended, and if so, whether all
+ * required information was supplied.
+ */
+
+ /* check opportunistic initiation simulation request */
+ switch (opts_seen & (LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE)))
+ {
+ case LELEM(OPT_OPPO_HERE):
+ case LELEM(OPT_OPPO_THERE):
+ diag("--oppohere and --oppothere must be used together");
+ /*NOTREACHED*/
+ case LELEM(OPT_OPPO_HERE) | LELEM(OPT_OPPO_THERE):
+ msg.whack_oppo_initiate = TRUE;
+ if (LIN(cd_seen, LELEM(CD_TUNNELIPV4 - CD_FIRST) | LELEM(CD_TUNNELIPV6 - CD_FIRST)))
+ opts_seen &= ~LELEM(OPT_CD);
+ break;
+ }
+
+ /* check connection description */
+ if (LHAS(opts_seen, OPT_CD))
+ {
+ if (!LHAS(cd_seen, CD_TO-CD_FIRST))
+ diag("connection description option, but no --to");
+
+ if (!LHAS(end_seen, END_HOST-END_FIRST))
+ diag("connection missing --host after --to");
+
+ if (isanyaddr(&msg.left.host_addr)
+ && isanyaddr(&msg.right.host_addr))
+ diag("hosts cannot both be 0.0.0.0 or 0::0");
+
+ if (msg.policy & POLICY_OPPO)
+ {
+ if ((msg.policy & (POLICY_PSK | POLICY_RSASIG)) != POLICY_RSASIG)
+ diag("only RSASIG is supported for opportunism");
+ if ((msg.policy & POLICY_PFS) == 0)
+ diag("PFS required for opportunism");
+ if ((msg.policy & POLICY_ENCRYPT) == 0)
+ diag("encryption required for opportunism");
+ }
+
+ check_end(&msg.left, &msg.right, !LHAS(end_seen_before_to, END_NEXTHOP-END_FIRST)
+ , msg.addr_family, msg.tunnel_addr_family);
+
+ check_end(&msg.right, &msg.left, !LHAS(end_seen, END_NEXTHOP-END_FIRST)
+ , msg.addr_family, msg.tunnel_addr_family);
+
+ if (subnettypeof(&msg.left.client) != subnettypeof(&msg.right.client))
+ diag("endpoints clash: one is IPv4 and the other is IPv6");
+
+ if (NEVER_NEGOTIATE(msg.policy))
+ {
+ /* we think this is just a shunt (because he didn't specify
+ * a host authentication method). If he didn't specify a
+ * shunt type, he's probably gotten it wrong.
+ */
+ if ((msg.policy & POLICY_SHUNT_MASK) == POLICY_SHUNT_TRAP)
+ diag("non-shunt connection must have --psk or --rsasig or both");
+ }
+ else
+ {
+ /* not just a shunt: a real ipsec connection */
+ if ((msg.policy & POLICY_ID_AUTH_MASK) == LEMPTY)
+ diag("must specify --rsasig or --psk for a connection");
+
+ if (!HAS_IPSEC_POLICY(msg.policy)
+ && (msg.left.has_client || msg.right.has_client))
+ diag("must not specify clients for ISAKMP-only connection");
+ }
+
+ msg.whack_connection = TRUE;
+ }
+
+ /* decide whether --name is mandatory or forbidden */
+ if (!LDISJOINT(opts_seen
+ , LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE)
+ | LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE)
+ | LELEM(OPT_DELETE) | LELEM(OPT_CD)))
+ {
+ if (!LHAS(opts_seen, OPT_NAME) && !msg.whack_ca)
+ diag("missing --name <connection_name>");
+ }
+ else if (!msg.whack_options && !msg.whack_status)
+ {
+ if (LHAS(opts_seen, OPT_NAME))
+ diag("no reason for --name");
+ }
+
+ if (!LDISJOINT(opts_seen, LELEM(OPT_PUBKEYRSA) | LELEM(OPT_ADDKEY)))
+ {
+ if (!LHAS(opts_seen, OPT_KEYID))
+ diag("--addkey and --pubkeyrsa require --keyid");
+ }
+
+ if (!(msg.whack_connection || msg.whack_key || msg.whack_myid
+ || msg.whack_delete || msg.whack_deletestate
+ || msg.whack_initiate || msg.whack_oppo_initiate || msg.whack_terminate
+ || msg.whack_route || msg.whack_unroute || msg.whack_listen
+ || msg.whack_unlisten || msg.whack_list || msg.whack_purgeocsp || msg.whack_reread
+ || msg.whack_ca || msg.whack_status || msg.whack_options || msg.whack_shutdown
+ || msg.whack_sc_op))
+ {
+ diag("no action specified; try --help for hints");
+ }
+
+ update_ports(&msg);
+
+ /* tricky quick and dirty check for wild values */
+ if (msg.sa_rekey_margin != 0
+ && msg.sa_rekey_fuzz * msg.sa_rekey_margin * 4 / msg.sa_rekey_margin / 4
+ != msg.sa_rekey_fuzz)
+ diag("rekeymargin or rekeyfuzz values are so large that they cause oveflow");
+
+ check_life_time (msg.sa_ike_life_seconds, OAKLEY_ISAKMP_SA_LIFETIME_MAXIMUM
+ , "ikelifetime", &msg);
+
+ check_life_time(msg.sa_ipsec_life_seconds, SA_LIFE_DURATION_MAXIMUM
+ , "ipseclifetime", &msg);
+
+ if (msg.dpd_action == DPD_ACTION_UNKNOWN)
+ diag("dpdaction must be \"none\", \"clear\", \"hold\" or \"restart\"");
+
+ if (msg.dpd_action != DPD_ACTION_NONE)
+ {
+ if (msg.dpd_delay <= 0)
+ diag("dpddelay must be larger than zero");
+
+ if (msg.dpd_timeout <= 0)
+ diag("dpdtimeout must be larger than zero");
+
+ if (msg.dpd_timeout <= msg.dpd_delay)
+ diag("dpdtimeout must be larger than dpddelay");
+ }
+
+ /* pack strings for inclusion in message */
+ next_str = msg.string;
+ str_roof = &msg.string[sizeof(msg.string)];
+
+ /* build esp message as esp="<esp>;<pfsgroup>" */
+ if (msg.pfsgroup) {
+ snprintf(esp_buf, sizeof (esp_buf), "%s;%s",
+ msg.esp ? msg.esp : "",
+ msg.pfsgroup ? msg.pfsgroup : "");
+ msg.esp=esp_buf;
+ }
+ if (!pack_str(&msg.name) /* string 1 */
+ || !pack_str(&msg.left.id) /* string 2 */
+ || !pack_str(&msg.left.cert) /* string 3 */
+ || !pack_str(&msg.left.ca) /* string 4 */
+ || !pack_str(&msg.left.groups) /* string 5 */
+ || !pack_str(&msg.left.updown) /* string 6 */
+#ifdef VIRTUAL_IP
+ || !pack_str(&msg.left.virt)
+#endif
+ || !pack_str(&msg.right.id) /* string 7 */
+ || !pack_str(&msg.right.cert) /* string 8 */
+ || !pack_str(&msg.right.ca) /* string 9 */
+ || !pack_str(&msg.right.groups) /* string 10 */
+ || !pack_str(&msg.right.updown) /* string 11 */
+#ifdef VIRTUAL_IP
+ || !pack_str(&msg.right.virt)
+#endif
+ || !pack_str(&msg.keyid) /* string 12 */
+ || !pack_str(&msg.myid) /* string 13 */
+ || !pack_str(&msg.cacert) /* string 14 */
+ || !pack_str(&msg.ldaphost) /* string 15 */
+ || !pack_str(&msg.ldapbase) /* string 16 */
+ || !pack_str(&msg.crluri) /* string 17 */
+ || !pack_str(&msg.crluri2) /* string 18 */
+ || !pack_str(&msg.ocspuri) /* string 19 */
+ || !pack_str(&msg.ike) /* string 20 */
+ || !pack_str(&msg.esp) /* string 21 */
+ || !pack_str(&msg.sc_data) /* string 22 */
+ || str_roof - next_str < (ptrdiff_t)msg.keyval.len) /* chunk (sort of string 5) */
+ diag("too many bytes of strings to fit in message to pluto");
+
+ memcpy(next_str, msg.keyval.ptr, msg.keyval.len);
+ msg.keyval.ptr = NULL;
+ next_str += msg.keyval.len;
+
+ msg.magic = ((opts_seen & ~LELEM(OPT_SHUTDOWN))
+ | sc_seen | lst_seen | cd_seen | ca_seen) != LEMPTY
+ || msg.whack_options
+ ? WHACK_MAGIC : WHACK_BASIC_MAGIC;
+
+ /* send message to Pluto */
+ if (access(ctl_addr.sun_path, R_OK | W_OK) < 0)
+ {
+ int e = errno;
+
+ switch (e)
+ {
+ case EACCES:
+ fprintf(stderr, "whack: no right to communicate with pluto (access(\"%s\"))\n"
+ , ctl_addr.sun_path);
+ break;
+ case ENOENT:
+ fprintf(stderr, "whack: Pluto is not running (no \"%s\")\n"
+ , ctl_addr.sun_path);
+ break;
+ default:
+ fprintf(stderr, "whack: access(\"%s\") failed with %d %s\n"
+ , ctl_addr.sun_path, errno, strerror(e));
+ break;
+ }
+ exit(RC_WHACK_PROBLEM);
+ }
+ else
+ {
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ int exit_status = 0;
+ ssize_t len = next_str - (char *)&msg;
+
+ if (sock == -1)
+ {
+ int e = errno;
+
+ fprintf(stderr, "whack: socket() failed (%d %s)\n", e, strerror(e));
+ exit(RC_WHACK_PROBLEM);
+ }
+
+ if (connect(sock, (struct sockaddr *)&ctl_addr
+ , offsetof(struct sockaddr_un, sun_path) + strlen(ctl_addr.sun_path)) < 0)
+ {
+ int e = errno;
+
+ fprintf(stderr, "whack:%s connect() for \"%s\" failed (%d %s)\n"
+ , e == ECONNREFUSED? " is Pluto running? " : ""
+ , ctl_addr.sun_path, e, strerror(e));
+ exit(RC_WHACK_PROBLEM);
+ }
+
+ if (write(sock, &msg, len) != len)
+ {
+ int e = errno;
+
+ fprintf(stderr, "whack: write() failed (%d %s)\n", e, strerror(e));
+ exit(RC_WHACK_PROBLEM);
+ }
+
+ /* for now, just copy reply back to stdout */
+
+ {
+ char buf[4097]; /* arbitrary limit on log line length */
+ char *be = buf;
+
+ for (;;)
+ {
+ char *ls = buf;
+ ssize_t rl = read(sock, be, (buf + sizeof(buf)-1) - be);
+
+ if (rl < 0)
+ {
+ int e = errno;
+
+ fprintf(stderr, "whack: read() failed (%d %s)\n", e, strerror(e));
+ exit(RC_WHACK_PROBLEM);
+ }
+ if (rl == 0)
+ {
+ if (be != buf)
+ fprintf(stderr, "whack: last line from pluto too long or unterminated\n");
+ break;
+ }
+
+ be += rl;
+ *be = '\0';
+
+ for (;;)
+ {
+ char *le = strchr(ls, '\n');
+
+ if (le == NULL)
+ {
+ /* move last, partial line to start of buffer */
+ memmove(buf, ls, be-ls);
+ be -= ls - buf;
+ break;
+ }
+
+ le++; /* include NL in line */
+ write(1, ls, le - ls);
+
+ /* figure out prefix number
+ * and how it should affect our exit status
+ */
+ {
+ unsigned long s = strtoul(ls, NULL, 10);
+
+ switch (s)
+ {
+ case RC_COMMENT:
+ case RC_LOG:
+ /* ignore */
+ break;
+ case RC_SUCCESS:
+ /* be happy */
+ exit_status = 0;
+ break;
+ case RC_ENTERSECRET:
+ get_secret(sock);
+ break;
+ /* case RC_LOG_SERIOUS: */
+ default:
+ /* pass through */
+ exit_status = s;
+ break;
+ }
+ }
+ ls = le;
+ }
+ }
+ }
+ return exit_status;
+ }
+}
diff --git a/programs/pluto/whack.h b/programs/pluto/whack.h
new file mode 100644
index 000000000..3086f1543
--- /dev/null
+++ b/programs/pluto/whack.h
@@ -0,0 +1,318 @@
+/* Structure of messages from whack to Pluto proper.
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: whack.h,v 1.16 2006/04/17 10:39:14 as Exp $
+ */
+
+#ifndef _WHACK_H
+#define _WHACK_H
+
+#include <freeswan.h>
+
+#include "smartcard.h"
+
+/* Since the message remains on one host, native representation is used.
+ * Think of this as horizontal microcode: all selected operations are
+ * to be done (in the order declared here).
+ *
+ * MAGIC is used to help detect version mismatches between whack and Pluto.
+ * Whenever the interface (i.e. this struct) changes in form or
+ * meaning, change this value (probably by changing the last number).
+ *
+ * If the command only requires basic actions (status or shutdown),
+ * it is likely that the relevant part of the message changes less frequently.
+ * Whack uses WHACK_BASIC_MAGIC in those cases.
+ *
+ * NOTE: no value of WHACK_BASIC_MAGIC may equal any value of WHACK_MAGIC.
+ * Otherwise certain version mismatches will not be detected.
+ */
+
+#define WHACK_BASIC_MAGIC (((((('w' << 8) + 'h') << 8) + 'k') << 8) + 24)
+#define WHACK_MAGIC (((((('w' << 8) + 'h') << 8) + 'k') << 8) + 26)
+
+typedef struct whack_end whack_end_t;
+
+/* struct whack_end is a lot like connection.h's struct end
+ * It differs because it is going to be shipped down a socket
+ * and because whack is a separate program from pluto.
+ */
+struct whack_end {
+ char *id; /* id string (if any) -- decoded by pluto */
+ char *cert; /* path string (if any) -- loaded by pluto */
+ char *ca; /* distinguished name string (if any) -- parsed by pluto */
+ char *groups; /* access control groups (if any) -- parsed by pluto */
+ 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_srcip;
+ bool modecfg;
+ bool hostaccess;
+ certpolicy_t sendcert;
+ char *updown; /* string */
+ u_int16_t host_port; /* host order */
+ u_int16_t port; /* host order */
+ u_int8_t protocol;
+#ifdef VIRTUAL_IP
+ char *virt;
+#endif
+ };
+
+typedef struct whack_message whack_message_t;
+
+struct whack_message {
+ unsigned int magic;
+
+ /* for WHACK_STATUS: */
+ bool whack_status;
+ bool whack_statusall;
+
+
+ /* for WHACK_SHUTDOWN */
+ bool whack_shutdown;
+
+ /* END OF BASIC COMMANDS
+ * If you change anything earlier in this struct, update WHACK_BASIC_MAGIC.
+ */
+
+ /* name is used in connection, ca and initiate */
+ size_t name_len; /* string 1 */
+ char *name;
+
+ /* for WHACK_OPTIONS: */
+
+ bool whack_options;
+
+ lset_t debugging; /* only used #ifdef DEBUG, but don't want layout to change */
+
+ /* for WHACK_CONNECTION */
+
+ bool whack_connection;
+ bool whack_async;
+
+ 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;
+
+ /* For DPD 3706 - Dead Peer Detection */
+ time_t dpd_delay;
+ time_t dpd_timeout;
+ dpd_action_t dpd_action;
+
+ /* note that each end contains string 2/5.id, string 3/6 cert,
+ * and string 4/7 updown
+ */
+ whack_end_t left;
+ whack_end_t right;
+
+ /* 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 */
+
+ char *ike; /* ike algo string (separated by commas) */
+ char *pfsgroup; /* pfsgroup will be "encapsulated" in esp string for pluto */
+ char *esp; /* esp algo string (separated by commas) */
+
+ /* for WHACK_KEY: */
+ bool whack_key;
+ bool whack_addkey;
+ char *keyid; /* string 8 */
+ enum pubkey_alg pubkey_alg;
+ chunk_t keyval; /* chunk */
+
+ /* for WHACK_MYID: */
+ bool whack_myid;
+ char *myid; /* string 7 */
+
+ /* for WHACK_ROUTE: */
+ bool whack_route;
+
+ /* for WHACK_UNROUTE: */
+ bool whack_unroute;
+
+ /* for WHACK_INITIATE: */
+ bool whack_initiate;
+
+ /* for WHACK_OPINITIATE */
+ bool whack_oppo_initiate;
+ ip_address oppo_my_client, oppo_peer_client;
+
+ /* for WHACK_TERMINATE: */
+ bool whack_terminate;
+
+ /* for WHACK_DELETE: */
+ bool whack_delete;
+
+ /* for WHACK_DELETESTATE: */
+ bool whack_deletestate;
+ so_serial_t whack_deletestateno;
+
+ /* for WHACK_LISTEN: */
+ bool whack_listen, whack_unlisten;
+
+ /* for WHACK_CRASH - note if a remote peer is known to have rebooted */
+ bool whack_crash;
+ ip_address whack_crash_peer;
+
+ /* for WHACK_LIST */
+ bool whack_utc;
+ lset_t whack_list;
+
+ /* for WHACK_PURGEOCSP */
+ bool whack_purgeocsp;
+
+ /* for WHACK_REREAD */
+ u_char whack_reread;
+
+ /* for WHACK_CA */
+ bool whack_ca;
+ bool whack_strict;
+
+ char *cacert;
+ char *ldaphost;
+ char *ldapbase;
+ char *crluri;
+ char *crluri2;
+ char *ocspuri;
+
+ /* for WHACK_SC_OP */
+ sc_op_t whack_sc_op;
+ int inbase, outbase;
+ char *sc_data;
+
+ /* space for strings (hope there is enough room):
+ * Note that pointers don't travel on wire.
+ * 1 connection name [name_len]
+ * 2 left's name [left.host.name.len]
+ * 3 left's cert
+ * 4 left's ca
+ * 5 left's groups
+ * 6 left's updown
+ * 7 right's name [left.host.name.len]
+ * 8 right's cert
+ * 9 right's ca
+ * 10 right's groups
+ * 11 right's updown
+ * 12 keyid
+ * 13 myid
+ * 14 cacert
+ * 15 ldaphost
+ * 16 ldapbase
+ * 17 crluri
+ * 18 crluri2
+ * 19 ocspuri
+ * 20 ike
+ " 21 esp
+ * 22 rsa_data
+ * plus keyval (limit: 8K bits + overhead), a chunk.
+ */
+ size_t str_size;
+ char string[2048];
+};
+
+/* Codes for status messages returned to whack.
+ * These are 3 digit decimal numerals. The structure
+ * is inspired by section 4.2 of RFC959 (FTP).
+ * Since these will end up as the exit status of whack, they
+ * must be less than 256.
+ * NOTE: ipsec_auto(8) knows about some of these numbers -- change carefully.
+ */
+enum rc_type {
+ RC_COMMENT, /* non-commital utterance (does not affect exit status) */
+ RC_WHACK_PROBLEM, /* whack-detected problem */
+ RC_LOG, /* message aimed at log (does not affect exit status) */
+ RC_LOG_SERIOUS, /* serious message aimed at log (does not affect exit status) */
+ RC_SUCCESS, /* success (exit status 0) */
+
+ /* failure, but not definitive */
+
+ RC_RETRANSMISSION = 10,
+
+ /* improper request */
+
+ RC_DUPNAME = 20, /* attempt to reuse a connection name */
+ RC_UNKNOWN_NAME, /* connection name unknown or state number */
+ RC_ORIENT, /* cannot orient connection: neither end is us */
+ RC_CLASH, /* clash between two Road Warrior connections OVERLOADED */
+ RC_DEAF, /* need --listen before --initiate */
+ RC_ROUTE, /* cannot route */
+ RC_RTBUSY, /* cannot unroute: route busy */
+ RC_BADID, /* malformed --id */
+ RC_NOKEY, /* no key found through DNS */
+ RC_NOPEERIP, /* cannot initiate when peer IP is unknown */
+ RC_INITSHUNT, /* cannot initiate a shunt-oly connection */
+ RC_WILDCARD, /* cannot initiate when ID has wildcards */
+ RC_NOVALIDPIN, /* cannot initiate without valid PIN */
+
+ /* permanent failure */
+
+ RC_BADWHACKMESSAGE = 30,
+ RC_NORETRANSMISSION,
+ RC_INTERNALERR,
+ RC_OPPOFAILURE, /* Opportunism failed */
+
+ /* entry of secrets */
+ RC_ENTERSECRET = 40,
+
+ /* progress: start of range for successful state transition.
+ * Actual value is RC_NEW_STATE plus the new state code.
+ */
+ RC_NEW_STATE = 100,
+
+ /* start of range for notification.
+ * Actual value is RC_NOTIFICATION plus code for notification
+ * that should be generated by this Pluto.
+ */
+ RC_NOTIFICATION = 200 /* as per IKE notification messages */
+};
+
+/* options of whack --list*** command */
+
+#define LIST_NONE 0x0000 /* don't list anything */
+#define LIST_ALGS 0x0001 /* list all registered IKE algorithms */
+#define LIST_PUBKEYS 0x0002 /* list all public keys */
+#define LIST_CERTS 0x0004 /* list all host/user certs */
+#define LIST_CACERTS 0x0008 /* list all ca certs */
+#define LIST_ACERTS 0x0010 /* list all attribute certs */
+#define LIST_AACERTS 0x0020 /* list all aa certs */
+#define LIST_OCSPCERTS 0x0040 /* list all ocsp certs */
+#define LIST_GROUPS 0x0080 /* list all access control groups */
+#define LIST_CAINFOS 0x0100 /* list all ca information records */
+#define LIST_CRLS 0x0200 /* list all crls */
+#define LIST_OCSP 0x0400 /* list all ocsp cache entries */
+#define LIST_CARDS 0x0800 /* list all smartcard records */
+
+#define LIST_ALL LRANGES(LIST_ALGS, LIST_CARDS) /* all list options */
+
+/* options of whack --reread*** command */
+
+#define REREAD_NONE 0x00 /* don't reread anything */
+#define REREAD_SECRETS 0x01 /* reread /etc/ipsec.secrets */
+#define REREAD_CACERTS 0x02 /* reread certs in /etc/ipsec.d/cacerts */
+#define REREAD_AACERTS 0x04 /* reread certs in /etc/ipsec.d/aacerts */
+#define REREAD_OCSPCERTS 0x08 /* reread certs in /etc/ipsec.d/ocspcerts */
+#define REREAD_ACERTS 0x10 /* reread certs in /etc/ipsec.d/acerts */
+#define REREAD_CRLS 0x20 /* reread crls in /etc/ipsec.d/crls */
+
+#define REREAD_ALL LRANGES(REREAD_SECRETS, REREAD_CRLS) /* all reread options */
+
+#endif /* _WHACK_H */
diff --git a/programs/pluto/x509.c b/programs/pluto/x509.c
new file mode 100644
index 000000000..c1b4cb6e3
--- /dev/null
+++ b/programs/pluto/x509.c
@@ -0,0 +1,2241 @@
+/* Support of X.509 certificates
+ * Copyright (C) 2000 Andreas Hess, Patric Lichtsteiner, Roger Wegmann
+ * Copyright (C) 2001 Marco Bertossa, Andreas Schleiss
+ * Copyright (C) 2002 Mario Strasser
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: x509.c,v 1.36 2006/04/10 16:08:33 as Exp $
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_policy.h>
+
+#include "constants.h"
+#include "defs.h"
+#include "mp_defs.h"
+#include "log.h"
+#include "id.h"
+#include "asn1.h"
+#include "oid.h"
+#include "pkcs1.h"
+#include "x509.h"
+#include "crl.h"
+#include "ca.h"
+#include "certs.h"
+#include "keys.h"
+#include "whack.h"
+#include "fetch.h"
+#include "ocsp.h"
+#include "sha1.h"
+
+/* chained lists of X.509 end certificates */
+
+static x509cert_t *x509certs = NULL;
+
+/* 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 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, "uniformResourceIdentifier", ASN1_CONTEXT_S_6, ASN1_OPT |
+ ASN1_BODY }, /* 12 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 13 */
+ { 0, "ipAddress", ASN1_CONTEXT_S_7, ASN1_OPT |
+ ASN1_BODY }, /* 14 */
+ { 0, "end choice", ASN1_EOC, ASN1_END }, /* 15 */
+ { 0, "registeredID", ASN1_CONTEXT_S_8, ASN1_OPT |
+ ASN1_BODY }, /* 16 */
+ { 0, "end choice", ASN1_EOC, ASN1_END } /* 17 */
+};
+
+#define GN_OBJ_OTHER_NAME 0
+#define GN_OBJ_RFC822_NAME 2
+#define GN_OBJ_DNS_NAME 4
+#define GN_OBJ_X400_ADDRESS 6
+#define GN_OBJ_DIRECTORY_NAME 8
+#define GN_OBJ_EDI_PARTY_NAME 10
+#define GN_OBJ_URI 12
+#define GN_OBJ_IP_ADDRESS 14
+#define GN_OBJ_REGISTERED_ID 16
+#define GN_OBJ_ROOF 18
+
+/* ASN.1 definition of otherName */
+
+static const asn1Object_t otherNameObjects[] = {
+ {0, "type-id", ASN1_OID, ASN1_BODY }, /* 0 */
+ {0, "value", ASN1_CONTEXT_C_0, ASN1_BODY } /* 1 */
+};
+
+#define ON_OBJ_ID_TYPE 0
+#define ON_OBJ_VALUE 1
+#define ON_OBJ_ROOF 2
+
+/* ASN.1 definition of 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, "nameRelativeToCRLIssuer", 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 certificate */
+
+static const asn1Object_t certObjects[] = {
+ { 0, "certificate", 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_OBJ }, /* 14 */
+ { 5, "modulus", ASN1_INTEGER, ASN1_BODY }, /* 15 */
+ { 5, "publicExponent", ASN1_INTEGER, ASN1_BODY }, /* 16 */
+ { 2, "issuerUniqueID", ASN1_CONTEXT_C_1, ASN1_OPT }, /* 17 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 18 */
+ { 2, "subjectUniqueID", ASN1_CONTEXT_C_2, ASN1_OPT }, /* 19 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 20 */
+ { 2, "optional extensions", ASN1_CONTEXT_C_3, ASN1_OPT }, /* 21 */
+ { 3, "extensions", ASN1_SEQUENCE, ASN1_LOOP }, /* 22 */
+ { 4, "extension", ASN1_SEQUENCE, ASN1_NONE }, /* 23 */
+ { 5, "extnID", ASN1_OID, ASN1_BODY }, /* 24 */
+ { 5, "critical", ASN1_BOOLEAN, ASN1_DEF |
+ ASN1_BODY }, /* 25 */
+ { 5, "extnValue", ASN1_OCTET_STRING, ASN1_BODY }, /* 26 */
+ { 3, "end loop", ASN1_EOC, ASN1_END }, /* 27 */
+ { 2, "end opt", ASN1_EOC, ASN1_END }, /* 28 */
+ { 1, "signatureAlgorithm", ASN1_EOC, ASN1_RAW }, /* 29 */
+ { 1, "signatureValue", ASN1_BIT_STRING, ASN1_BODY } /* 30 */
+};
+
+#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_MODULUS 15
+#define X509_OBJ_PUBLIC_EXPONENT 16
+#define X509_OBJ_EXTN_ID 24
+#define X509_OBJ_CRITICAL 25
+#define X509_OBJ_EXTN_VALUE 26
+#define X509_OBJ_ALGORITHM 29
+#define X509_OBJ_SIGNATURE 30
+#define X509_OBJ_ROOF 31
+
+
+const x509cert_t empty_x509cert = {
+ NULL , /* *next */
+ UNDEFINED_TIME, /* installed */
+ 0 , /* count */
+ FALSE , /* smartcard */
+ AUTH_NONE , /* authority_flags */
+ { NULL, 0 } , /* certificate */
+ { NULL, 0 } , /* tbsCertificate */
+ 1 , /* version */
+ { NULL, 0 } , /* serialNumber */
+ OID_UNKNOWN , /* sigAlg */
+ { NULL, 0 } , /* issuer */
+ /* validity */
+ 0 , /* notBefore */
+ 0 , /* notAfter */
+ { NULL, 0 } , /* subject */
+ /* subjectPublicKeyInfo */
+ OID_UNKNOWN , /* subjectPublicKeyAlgorithm */
+ { NULL, 0 } , /* subjectPublicKey */
+ { NULL, 0 } , /* modulus */
+ { NULL, 0 } , /* publicExponent */
+ /* issuerUniqueID */
+ /* subjectUniqueID */
+ /* extensions */
+ /* extension */
+ /* extnID */
+ /* critical */
+ /* extnValue */
+ FALSE , /* isCA */
+ FALSE , /* isOcspSigner */
+ { NULL, 0 } , /* subjectKeyID */
+ { NULL, 0 } , /* authKeyID */
+ { NULL, 0 } , /* authKeySerialNumber */
+ { NULL, 0 } , /* accessLocation */
+ NULL , /* subjectAltName */
+ NULL , /* crlDistributionPoints */
+ OID_UNKNOWN , /* algorithm */
+ { NULL, 0 } /* signature */
+};
+
+/* coding of X.501 distinguished name */
+
+typedef struct {
+ const u_char *name;
+ chunk_t oid;
+ u_char type;
+} x501rdn_t;
+
+/* 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};
+
+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
+
+static u_char ASN1_subjectAltName_oid_str[] = {
+ 0x06, 0x03, 0x55, 0x1D, 0x11
+};
+
+static const chunk_t ASN1_subjectAltName_oid = strchunk(ASN1_subjectAltName_oid_str);
+
+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;
+}
+
+
+/*
+ * Pointer is set to the first RDN in a DN
+ */
+static err_t
+init_rdn(chunk_t dn, chunk_t *rdn, chunk_t *attribute, bool *next)
+{
+ *rdn = empty_chunk;
+ *attribute = empty_chunk;
+
+ /* a DN is a SEQUENCE OF RDNs */
+
+ if (*dn.ptr != ASN1_SEQUENCE)
+ {
+ return "DN is not a SEQUENCE";
+ }
+
+ rdn->len = asn1_length(&dn);
+
+ if (rdn->len == ASN1_INVALID_LENGTH)
+ return "Invalid RDN length";
+
+ rdn->ptr = dn.ptr;
+
+ /* are there any RDNs ? */
+ *next = rdn->len > 0;
+
+ return NULL;
+}
+
+/*
+ * Fetches the next RDN in a DN
+ */
+static err_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 = empty_chunk;
+ *value = empty_chunk;
+
+ /* 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)
+ return "RDN is not a SET";
+
+ attribute->len = asn1_length(rdn);
+
+ if (attribute->len == ASN1_INVALID_LENGTH)
+ return "Invalid attribute length";
+
+ 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)
+ return "attributeTypeAndValue is not a SEQUENCE";
+
+ /* extract the attribute body */
+ body.len = asn1_length(attribute);
+
+ if (body.len == ASN1_INVALID_LENGTH)
+ return "Invalid attribute body length";
+
+ 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)
+ return "attributeType is not an OID";
+
+ /* extract OID */
+ oid->len = asn1_length(&body);
+
+ if (oid->len == ASN1_INVALID_LENGTH)
+ return "Invalid attribute OID length";
+
+ 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)
+ return "Invalid attribute string length";
+
+ value->ptr = body.ptr;
+
+ /* are there any RDNs left? */
+ *next = rdn->len > 0 || attribute->len > 0;
+
+ return NULL;
+}
+
+/*
+ * Parses an ASN.1 distinguished name int its OID/value pairs
+ */
+static err_t
+dn_parse(chunk_t dn, chunk_t *str)
+{
+ chunk_t rdn, oid, attribute, value;
+ asn1_t type;
+ int oid_code;
+ bool next;
+ bool first = TRUE;
+
+ err_t ugh = init_rdn(dn, &rdn, &attribute, &next);
+
+ if (ugh != NULL) /* a parsing error has occured */
+ return ugh;
+
+ while (next)
+ {
+ ugh = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next);
+
+ if (ugh != NULL) /* a parsing error has occured */
+ return ugh;
+
+ 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 NULL;
+}
+
+/*
+ * Count the number of wildcard RDNs in a distinguished name
+ */
+int
+dn_count_wildcards(chunk_t dn)
+{
+ chunk_t rdn, attribute, oid, value;
+ asn1_t type;
+ bool next;
+ int wildcards = 0;
+
+ err_t ugh = init_rdn(dn, &rdn, &attribute, &next);
+
+ if (ugh != NULL) /* a parsing error has occured */
+ return -1;
+
+ while (next)
+ {
+ ugh = get_next_rdn(&rdn, &attribute, &oid, &value, &type, &next);
+
+ if (ugh != NULL) /* a parsing error has occured */
+ return -1;
+ if (value.len == 1 && *value.ptr == '*')
+ wildcards++; /* we have found a wildcard RDN */
+ }
+ return wildcards;
+}
+
+/*
+ * 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++));
+}
+
+
+/* Converts a binary DER-encoded ASN.1 distinguished name
+ * into LDAP-style human-readable ASCII format
+ */
+int
+dntoa(char *dst, size_t dstlen, chunk_t dn)
+{
+ err_t ugh = NULL;
+ chunk_t str;
+
+ str.ptr = dst;
+ str.len = dstlen;
+ ugh = dn_parse(dn, &str);
+
+ if (ugh != NULL) /* error, print DN as hex string */
+ {
+ DBG(DBG_PARSING,
+ DBG_log("error in DN parsing: %s", ugh)
+ )
+ str.ptr = dst;
+ str.len = dstlen;
+ hex_str(dn, &str);
+ }
+ return (int)(dstlen - str.len);
+}
+
+/*
+ * Same as dntoa but prints a special string for a null dn
+ */
+int
+dntoa_or_null(char *dst, size_t dstlen, chunk_t dn, const char* null_dn)
+{
+ if (dn.ptr == NULL)
+ return snprintf(dst, dstlen, "%s", null_dn);
+ else
+ return dntoa(dst, dstlen, dn);
+}
+
+/* Converts an LDAP-style human-readable ASCII-encoded
+ * ASN.1 distinguished name into binary DER-encoded format
+ */
+err_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;
+
+ u_char oid_len_buf[3];
+ u_char name_len_buf[3];
+ u_char rdn_seq_len_buf[3];
+ u_char rdn_set_len_buf[3];
+ u_char dn_seq_len_buf[3];
+
+ chunk_t asn1_oid_len = { oid_len_buf, 0 };
+ chunk_t asn1_name_len = { name_len_buf, 0 };
+ chunk_t asn1_rdn_seq_len = { rdn_seq_len_buf, 0 };
+ chunk_t asn1_rdn_set_len = { rdn_set_len_buf, 0 };
+ chunk_t asn1_dn_seq_len = { dn_seq_len_buf, 0 };
+ chunk_t oid = empty_chunk;
+ chunk_t name = empty_chunk;
+
+ int whitespace = 0;
+ int rdn_seq_len = 0;
+ int rdn_set_len = 0;
+ int dn_seq_len = 0;
+ int pos = 0;
+
+ err_t ugh = NULL;
+
+ u_char *dn_ptr = dn->ptr + 4;
+
+ state_t state = SEARCH_OID;
+
+ 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)
+ {
+ ugh = "unknown OID in distinguished name";
+ state = UNKNOWN_OID;
+ break;
+ }
+ code_asn1_length(x501rdns[pos].oid.len, &asn1_oid_len);
+
+ /* reset oid and change state */
+ oid = empty_chunk;
+ 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;
+ code_asn1_length(name.len, &asn1_name_len);
+
+ /* compute the length of the relative distinguished name sequence */
+ rdn_seq_len = 1 + asn1_oid_len.len + x501rdns[pos].oid.len +
+ 1 + asn1_name_len.len + name.len;
+ code_asn1_length(rdn_seq_len, &asn1_rdn_seq_len);
+
+ /* compute the length of the relative distinguished name set */
+ rdn_set_len = 1 + asn1_rdn_seq_len.len + rdn_seq_len;
+ code_asn1_length(rdn_set_len, &asn1_rdn_set_len);
+
+ /* encode the relative distinguished name */
+ *dn_ptr++ = ASN1_SET;
+ chunkcpy(dn_ptr, asn1_rdn_set_len);
+ *dn_ptr++ = ASN1_SEQUENCE;
+ chunkcpy(dn_ptr, asn1_rdn_seq_len);
+ *dn_ptr++ = ASN1_OID;
+ chunkcpy(dn_ptr, asn1_oid_len);
+ chunkcpy(dn_ptr, x501rdns[pos].oid);
+ /* encode the ASN.1 character string type of the name */
+ *dn_ptr++ = (x501rdns[pos].type == ASN1_PRINTABLESTRING
+ && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type;
+ chunkcpy(dn_ptr, asn1_name_len);
+ chunkcpy(dn_ptr, name);
+
+ /* accumulate the length of the distinguished name sequence */
+ dn_seq_len += 1 + asn1_rdn_set_len.len + rdn_set_len;
+
+ /* reset name and change state */
+ name = empty_chunk;
+ state = SEARCH_OID;
+ }
+ break;
+ case UNKNOWN_OID:
+ break;
+ }
+ } while (*src++ != '\0');
+
+ /* complete the distinguished name sequence*/
+ code_asn1_length(dn_seq_len, &asn1_dn_seq_len);
+ dn->ptr += 3 - asn1_dn_seq_len.len;
+ dn->len = 1 + asn1_dn_seq_len.len + dn_seq_len;
+ dn_ptr = dn->ptr;
+ *dn_ptr++ = ASN1_SEQUENCE;
+ chunkcpy(dn_ptr, asn1_dn_seq_len);
+ return ugh;
+}
+
+/* compare two distinguished names by
+ * comparing the individual RDNs
+ */
+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) != NULL
+ || init_rdn(b, &rdn_b, &attribute_b, &next_b) != NULL)
+ 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) != NULL
+ || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != NULL)
+ {
+ 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.
+ */
+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) != NULL
+ || init_rdn(b, &rdn_b, &attribute_b, &next_b) != NULL)
+ 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) != NULL
+ || get_next_rdn(&rdn_b, &attribute_b, &oid_b, &value_b, &type_b, &next_b) != NULL)
+ {
+ 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;
+}
+
+/*
+ * compare two X.509 certificates by comparing their signatures
+ */
+bool
+same_x509cert(const x509cert_t *a, const x509cert_t *b)
+{
+ return same_chunk(a->signature, b->signature);
+}
+
+/* for each link pointing to the certificate
+ " increase the count by one
+ */
+void
+share_x509cert(x509cert_t *cert)
+{
+ if (cert != NULL)
+ cert->count++;
+}
+
+/*
+ * add a X.509 user/host certificate to the chained list
+ */
+x509cert_t*
+add_x509cert(x509cert_t *cert)
+{
+ x509cert_t *c = x509certs;
+
+ while (c != NULL)
+ {
+ if (same_x509cert(c, cert)) /* already in chain, free cert */
+ {
+ free_x509cert(cert);
+ return c;
+ }
+ c = c->next;
+ }
+
+ /* insert new cert at the root of the chain */
+ lock_certs_and_keys("add_x509cert");
+ cert->next = x509certs;
+ x509certs = cert;
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log(" x509 cert inserted")
+ )
+ unlock_certs_and_keys("add_x509cert");
+ return cert;
+}
+
+/*
+ * choose either subject DN or a subjectAltName as connection end ID
+ */
+void
+select_x509cert_id(x509cert_t *cert, struct id *end_id)
+{
+ bool copy_subject_dn = TRUE; /* ID is subject DN */
+
+ if (end_id->kind != ID_NONE) /* check for matching subjectAltName */
+ {
+ generalName_t *gn = cert->subjectAltName;
+
+ while (gn != NULL)
+ {
+ struct id id = empty_id;
+
+ gntoid(&id, gn);
+ if (same_id(&id, end_id))
+ {
+ copy_subject_dn = FALSE; /* take subjectAltName instead */
+ break;
+ }
+ gn = gn->next;
+ }
+ }
+
+ if (copy_subject_dn)
+ {
+ if (end_id->kind != ID_NONE && end_id->kind != ID_DER_ASN1_DN)
+ {
+ char buf[BUF_LEN];
+
+ idtoa(end_id, buf, BUF_LEN);
+ plog(" no subjectAltName matches ID '%s', replaced by subject DN", buf);
+ }
+ end_id->kind = ID_DER_ASN1_DN;
+ end_id->name.len = cert->subject.len;
+ end_id->name.ptr = temporary_cyclic_buffer();
+ memcpy(end_id->name.ptr, cert->subject.ptr, cert->subject.len);
+ }
+}
+
+/*
+ * check for equality between two key identifiers
+ */
+bool
+same_keyid(chunk_t a, chunk_t b)
+{
+ if (a.ptr == NULL || b.ptr == NULL)
+ return FALSE;
+
+ return same_chunk(a, b);
+}
+
+/*
+ * check for equality between two serial numbers
+ */
+bool
+same_serial(chunk_t a, chunk_t b)
+{
+ /* do not compare serial numbers if one of them is not defined */
+ if (a.ptr == NULL || b.ptr == NULL)
+ return TRUE;
+
+ return same_chunk(a, b);
+}
+
+/*
+ * get a X.509 certificate with a given issuer found at a certain position
+ */
+x509cert_t*
+get_x509cert(chunk_t issuer, chunk_t serial, chunk_t keyid, x509cert_t *chain)
+{
+ x509cert_t *cert = (chain != NULL)? chain->next : x509certs;
+
+ while (cert != NULL)
+ {
+ if ((keyid.ptr != NULL) ? same_keyid(keyid, cert->authKeyID)
+ : (same_dn(issuer, cert->issuer)
+ && same_serial(serial, cert->authKeySerialNumber)))
+ {
+ return cert;
+ }
+ cert = cert->next;
+ }
+ return NULL;
+}
+
+/*
+ * 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)
+ {
+ chunkcpy(pos, gn->name);
+ gn = gn->next;
+ }
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_subjectAltName_oid
+ , asn1_wrap(ASN1_OCTET_STRING, "m", names));
+}
+
+/*
+ * build a to-be-signed X.509 certificate body
+ */
+static chunk_t
+build_tbs_x509cert(x509cert_t *cert, const RSA_public_key_t *rsa)
+{
+ /* version is always X.509v3 */
+ chunk_t version = asn1_simple_object(ASN1_CONTEXT_C_0, ASN1_INTEGER_2);
+
+ chunk_t extensions = empty_chunk;
+
+ if (cert->subjectAltName != NULL)
+ {
+ extensions = asn1_wrap(ASN1_CONTEXT_C_3, "m"
+ , asn1_wrap(ASN1_SEQUENCE, "m"
+ , build_subjectAltNames(cert->subjectAltName)));
+ }
+
+ return asn1_wrap(ASN1_SEQUENCE, "mmccmcmm"
+ , version
+ , asn1_simple_object(ASN1_INTEGER, cert->serialNumber)
+ , asn1_algorithmIdentifier(cert->sigAlg)
+ , cert->issuer
+ , asn1_wrap(ASN1_SEQUENCE, "mm"
+ , timetoasn1(&cert->notBefore, ASN1_UTCTIME)
+ , timetoasn1(&cert->notAfter, ASN1_UTCTIME)
+ )
+ , cert->subject
+ , pkcs1_build_publicKeyInfo(rsa)
+ , extensions
+ );
+}
+
+/*
+ * build a DER-encoded X.509 certificate
+ */
+void
+build_x509cert(x509cert_t *cert, const RSA_public_key_t *cert_key
+, const RSA_private_key_t *signer_key)
+{
+ chunk_t tbs_cert = build_tbs_x509cert(cert, cert_key);
+
+ chunk_t signature = pkcs1_build_signature(tbs_cert, cert->sigAlg
+ , signer_key, TRUE);
+
+ cert->certificate = asn1_wrap(ASN1_SEQUENCE, "mcm"
+ , tbs_cert
+ , asn1_algorithmIdentifier(cert->sigAlg)
+ , signature);
+}
+
+/*
+ * 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)
+ {
+ pfree(gn->name.ptr);
+ }
+ gn = gn->next;
+ pfree(gn_top);
+ }
+}
+
+/*
+ * free a X.509 certificate
+ */
+void
+free_x509cert(x509cert_t *cert)
+{
+ if (cert != NULL)
+ {
+ free_generalNames(cert->subjectAltName, FALSE);
+ free_generalNames(cert->crlDistributionPoints, FALSE);
+ pfreeany(cert->certificate.ptr);
+ pfree(cert);
+ cert = NULL;
+ }
+}
+
+/* release of a certificate decreases the count by one
+ " the certificate is freed when the counter reaches zero
+ */
+void
+release_x509cert(x509cert_t *cert)
+{
+ if (cert != NULL && --cert->count == 0)
+ {
+ x509cert_t **pp = &x509certs;
+ while (*pp != cert)
+ pp = &(*pp)->next;
+ *pp = cert->next;
+ free_x509cert(cert);
+ }
+}
+
+
+/*
+ * stores a chained list of end certs and CA certs
+ */
+void
+store_x509certs(x509cert_t **firstcert, bool strict)
+{
+ x509cert_t *cacerts = NULL;
+ x509cert_t **pp = firstcert;
+
+ /* first extract CA certs, discarding root CA certs */
+
+ while (*pp != NULL)
+ {
+ x509cert_t *cert = *pp;
+
+ if (cert->isCA)
+ {
+ *pp = cert->next;
+
+ /* we don't accept self-signed CA certs */
+ if (same_dn(cert->issuer, cert->subject))
+ {
+ plog("self-signed cacert rejected");
+ free_x509cert(cert);
+ }
+ else
+ {
+ /* insertion into temporary chain of candidate CA certs */
+ cert->next = cacerts;
+ cacerts = cert;
+ }
+ }
+ else
+ pp = &cert->next;
+ }
+
+ /* now verify the candidate CA certs */
+
+ while (cacerts != NULL)
+ {
+ x509cert_t *cert = cacerts;
+
+ cacerts = cacerts->next;
+
+ if (trust_authcert_candidate(cert, cacerts))
+ {
+ add_authcert(cert, AUTH_CA);
+ }
+ else
+ {
+ plog("intermediate cacert rejected");
+ free_x509cert(cert);
+ }
+ }
+
+ /* now verify the end certificates */
+
+ pp = firstcert;
+
+ while (*pp != NULL)
+ {
+ time_t valid_until;
+ x509cert_t *cert = *pp;
+
+ if (verify_x509cert(cert, strict, &valid_until))
+ {
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log("public key validated")
+ )
+ add_x509_public_key(cert, valid_until, DAL_SIGNED);
+ }
+ else
+ {
+ plog("X.509 certificate rejected");
+ }
+ *pp = cert->next;
+ free_x509cert(cert);
+ }
+}
+
+/*
+ * decrypts an RSA signature using the issuer's certificate
+ */
+static bool
+decrypt_sig(chunk_t sig, int alg, const x509cert_t *issuer_cert,
+ chunk_t *digest)
+{
+ switch (alg)
+ {
+ chunk_t decrypted;
+
+ case OID_RSA_ENCRYPTION:
+ case OID_MD2_WITH_RSA:
+ case OID_MD5_WITH_RSA:
+ case OID_SHA1_WITH_RSA:
+ case OID_SHA1_WITH_RSA_OIW:
+ case OID_SHA256_WITH_RSA:
+ case OID_SHA384_WITH_RSA:
+ case OID_SHA512_WITH_RSA:
+ {
+ mpz_t s;
+ RSA_public_key_t rsa;
+
+ init_RSA_public_key(&rsa, issuer_cert->publicExponent
+ , issuer_cert->modulus);
+
+ /* decrypt the signature s = s^e mod n */
+ n_to_mpz(s, sig.ptr, sig.len);
+ mpz_powm(s, s, &rsa.e, &rsa.n);
+
+ /* convert back to bytes */
+ decrypted = mpz_to_n(s, rsa.k);
+ DBG(DBG_PARSING,
+ DBG_dump_chunk(" decrypted signature: ", decrypted)
+ )
+
+ /* copy the least significant bits of decrypted signature
+ * into the digest string
+ */
+ memcpy(digest->ptr, decrypted.ptr + decrypted.len - digest->len,
+ digest->len);
+
+ /* free memory */
+ free_RSA_public_content(&rsa);
+ pfree(decrypted.ptr);
+ mpz_clear(s);
+ return TRUE;
+ }
+ default:
+ digest->len = 0;
+ return FALSE;
+ }
+}
+
+/*
+ * Check if a signature over binary blob is genuine
+ */
+bool
+check_signature(chunk_t tbs, chunk_t sig, int digest_alg, int enc_alg
+, const x509cert_t *issuer_cert)
+{
+ u_char digest_buf[MAX_DIGEST_LEN];
+ u_char decrypted_buf[MAX_DIGEST_LEN];
+ chunk_t digest = {digest_buf, MAX_DIGEST_LEN};
+ chunk_t decrypted = {decrypted_buf, MAX_DIGEST_LEN};
+
+ DBG(DBG_PARSING,
+ if (digest_alg != OID_UNKNOWN)
+ DBG_log("signature digest algorithm: '%s'",oid_names[digest_alg].name);
+ else
+ DBG_log("unknown signature digest algorithm");
+ )
+
+ if (!compute_digest(tbs, digest_alg, &digest))
+ {
+ plog(" digest algorithm not supported");
+ return FALSE;
+ }
+
+ DBG(DBG_PARSING,
+ DBG_dump_chunk(" digest:", digest)
+ )
+
+ decrypted.len = digest.len; /* we want the same digest length */
+
+ DBG(DBG_PARSING,
+ if (enc_alg != OID_UNKNOWN)
+ DBG_log("signature encryption algorithm: '%s'",oid_names[enc_alg].name);
+ else
+ DBG_log("unknown signature encryption algorithm");
+ )
+
+ if (!decrypt_sig(sig, enc_alg, issuer_cert, &decrypted))
+ {
+ plog(" decryption algorithm not supported");
+ return FALSE;
+ }
+
+ /* check if digests are equal */
+ return !memcmp(decrypted.ptr, digest.ptr, digest.len);
+}
+
+/*
+ * 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, DBG_RAW);
+
+ while (objectID < BASIC_CONSTRAINTS_ROOF) {
+
+ if (!extract_object(basicConstraintsObjects, &objectID,
+ &object,&level, &ctx))
+ break;
+
+ if (objectID == BASIC_CONSTRAINTS_CA)
+ {
+ isCA = object.len && *object.ptr;
+ DBG(DBG_PARSING,
+ DBG_log(" %s",(isCA)?"TRUE":"FALSE");
+ )
+ }
+ objectID++;
+ }
+ return isCA;
+}
+
+/*
+ * Converts a X.500 generalName into an ID
+ */
+void
+gntoid(struct id *id, const generalName_t *gn)
+{
+ switch(gn->kind)
+ {
+ case GN_DNS_NAME: /* ID type: ID_FQDN */
+ id->kind = ID_FQDN;
+ id->name = gn->name;
+ break;
+ case GN_IP_ADDRESS: /* ID type: ID_IPV4_ADDR */
+ {
+ const struct af_info *afi = &af_inet4_info;
+ err_t ugh = NULL;
+
+ id->kind = afi->id_addr;
+ ugh = initaddr(gn->name.ptr, gn->name.len, afi->af, &id->ip_addr);
+ }
+ break;
+ case GN_RFC822_NAME: /* ID type: ID_USER_FQDN */
+ id->kind = ID_USER_FQDN;
+ id->name = gn->name;
+ break;
+ default:
+ id->kind = ID_NONE;
+ id->name = empty_chunk;
+ }
+}
+
+/* compute the subjectKeyIdentifier according to section 4.2.1.2 of RFC 3280
+ * as the 160 bit SHA-1 hash of the public key
+ */
+void
+compute_subjectKeyID(x509cert_t *cert, chunk_t subjectKeyID)
+{
+ SHA1_CTX context;
+
+ SHA1Init(&context);
+ SHA1Update(&context
+ , cert->subjectPublicKey.ptr
+ , cert->subjectPublicKey.len);
+ SHA1Final(subjectKeyID.ptr, &context);
+ subjectKeyID.len = SHA1_DIGEST_SIZE;
+}
+
+/*
+ * extracts an otherName
+ */
+static bool
+parse_otherName(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ int objectID = 0;
+ u_int level;
+ int oid = OID_UNKNOWN;
+
+ asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
+
+ while (objectID < ON_OBJ_ROOF)
+ {
+ if (!extract_object(otherNameObjects, &objectID, &object, &level, &ctx))
+ return FALSE;
+
+ switch (objectID)
+ {
+ case ON_OBJ_ID_TYPE:
+ oid = known_oid(object);
+ break;
+ case ON_OBJ_VALUE:
+ if (oid == OID_XMPP_ADDR)
+ {
+ if (!parse_asn1_simple_object(&object, ASN1_UTF8STRING
+ , level + 1, "xmppAddr"))
+ {
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+
+/*
+ * extracts a generalName
+ */
+static generalName_t*
+parse_generalName(chunk_t blob, int level0)
+{
+ u_char buf[BUF_LEN];
+ asn1_ctx_t ctx;
+ chunk_t object;
+ int objectID = 0;
+ u_int level;
+
+ asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
+
+ while (objectID < GN_OBJ_ROOF)
+ {
+ bool valid_gn = FALSE;
+
+ 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:
+ DBG(DBG_PARSING,
+ DBG_log(" '%.*s'", (int)object.len, object.ptr);
+ )
+ valid_gn = TRUE;
+ break;
+ case GN_OBJ_DIRECTORY_NAME:
+ DBG(DBG_PARSING,
+ dntoa(buf, BUF_LEN, object);
+ DBG_log(" '%s'", buf)
+ )
+ valid_gn = TRUE;
+ break;
+ case GN_OBJ_IP_ADDRESS:
+ DBG(DBG_PARSING,
+ DBG_log(" '%d.%d.%d.%d'", *object.ptr, *(object.ptr+1),
+ *(object.ptr+2), *(object.ptr+3));
+ )
+ valid_gn = TRUE;
+ break;
+ case GN_OBJ_OTHER_NAME:
+ if (!parse_otherName(object, level + 1))
+ return NULL;
+ break;
+ case GN_OBJ_X400_ADDRESS:
+ case GN_OBJ_EDI_PARTY_NAME:
+ case GN_OBJ_REGISTERED_ID:
+ break;
+ default:
+ break;
+ }
+
+ if (valid_gn)
+ {
+ generalName_t *gn = alloc_thing(generalName_t, "generalName");
+ gn->kind = (objectID - GN_OBJ_OTHER_NAME) / 2;
+ gn->name = object;
+ gn->next = NULL;
+ return gn;
+ }
+ objectID++;
+ }
+ return NULL;
+}
+
+
+/*
+ * extracts one or several GNs and puts them into a chained list
+ */
+static generalName_t*
+parse_generalNames(chunk_t blob, int level0, bool implicit)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ generalName_t *top_gn = NULL;
+
+ asn1_init(&ctx, blob, level0, implicit, DBG_RAW);
+
+ while (objectID < GENERAL_NAMES_ROOF)
+ {
+ if (!extract_object(generalNamesObjects, &objectID, &object, &level, &ctx))
+ return NULL;
+
+ if (objectID == GENERAL_NAMES_GN)
+ {
+ generalName_t *gn = parse_generalName(object, level+1);
+ if (gn != NULL)
+ {
+ gn->next = top_gn;
+ top_gn = gn;
+ }
+ }
+ objectID++;
+ }
+ return top_gn;
+}
+
+/*
+ * returns a directoryName
+ */
+chunk_t get_directoryName(chunk_t blob, int level, bool implicit)
+{
+ chunk_t name = empty_chunk;
+ generalName_t * gn = parse_generalNames(blob, level, implicit);
+
+ if (gn != NULL && gn->kind == GN_DIRECTORY_NAME)
+ name= gn->name;
+
+ free_generalNames(gn, FALSE);
+
+ return name;
+}
+
+/*
+ * 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, DBG_RAW);
+
+ while (objectID < TIME_ROOF)
+ {
+ if (!extract_object(timeObjects, &objectID, &object, &level, &ctx))
+ return UNDEFINED_TIME;
+
+ if (objectID == TIME_UTC || objectID == TIME_GENERALIZED)
+ {
+ return asn1totime(&object, (objectID == TIME_UTC)
+ ? ASN1_UTCTIME : ASN1_GENERALIZEDTIME);
+ }
+ objectID++;
+ }
+ return UNDEFINED_TIME;
+ }
+
+/*
+ * 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, DBG_RAW);
+
+ 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, DBG_RAW);
+
+ 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:
+ {
+ generalName_t * gn = parse_generalNames(object, level+1, TRUE);
+
+ free_generalNames(gn, FALSE);
+ }
+ 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, DBG_RAW);
+
+ 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;
+
+ DBG(DBG_PARSING,
+ DBG_log(" '%.*s'",(int)object.len, object.ptr)
+ )
+
+ /* only HTTP(S) URIs accepted */
+ if (strncasecmp(object.ptr, "http", 4) == 0)
+ {
+ *accessLocation = object;
+ return;
+ }
+ }
+ plog("warning: 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, DBG_RAW);
+
+ 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 generalName_t*
+parse_crlDistributionPoints(chunk_t blob, int level0)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int objectID = 0;
+
+ generalName_t *top_gn = NULL; /* top of the chained list */
+ generalName_t **tail_gn = &top_gn; /* tail of the chained list */
+
+ asn1_init(&ctx, blob, level0, FALSE, DBG_RAW);
+
+ while (objectID < CRL_DIST_POINTS_ROOF)
+ {
+ if (!extract_object(crlDistributionPointsObjects, &objectID,
+ &object, &level, &ctx))
+ return NULL;
+
+ if (objectID == CRL_DIST_POINTS_FULLNAME)
+ {
+ generalName_t *gn = parse_generalNames(object, level+1, TRUE);
+ /* append extracted generalNames to existing chained list */
+ *tail_gn = gn;
+ /* find new tail of the chained list */
+ while (gn != NULL)
+ {
+ tail_gn = &gn->next; gn = gn->next;
+ }
+ }
+ objectID++;
+ }
+ return top_gn;
+}
+
+
+/*
+ * Parses an X.509v3 certificate
+ */
+bool
+parse_x509cert(chunk_t blob, u_int level0, x509cert_t *cert)
+{
+ u_char buf[BUF_LEN];
+ 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, DBG_RAW);
+
+ while (objectID < X509_OBJ_ROOF)
+ {
+ if (!extract_object(certObjects, &objectID, &object, &level, &ctx))
+ return FALSE;
+
+ /* those objects which will parsed further need the next higher level */
+ level++;
+
+ switch (objectID) {
+ case X509_OBJ_CERTIFICATE:
+ cert->certificate = object;
+ break;
+ case X509_OBJ_TBS_CERTIFICATE:
+ cert->tbsCertificate = object;
+ break;
+ case X509_OBJ_VERSION:
+ cert->version = (object.len) ? (1+(u_int)*object.ptr) : 1;
+ DBG(DBG_PARSING,
+ DBG_log(" 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 = object;
+ DBG(DBG_PARSING,
+ dntoa(buf, BUF_LEN, object);
+ DBG_log(" '%s'",buf)
+ )
+ 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 = object;
+ DBG(DBG_PARSING,
+ dntoa(buf, BUF_LEN, object);
+ DBG_log(" '%s'",buf)
+ )
+ break;
+ case X509_OBJ_SUBJECT_PUBLIC_KEY_ALGORITHM:
+ if (parse_algorithmIdentifier(object, level, NULL) == OID_RSA_ENCRYPTION)
+ cert->subjectPublicKeyAlgorithm = PUBKEY_ALG_RSA;
+ else
+ {
+ plog(" 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
+ {
+ plog(" invalid RSA public key format");
+ return FALSE;
+ }
+ break;
+ case X509_OBJ_RSA_PUBLIC_KEY:
+ cert->subjectPublicKey = object;
+ break;
+ case X509_OBJ_MODULUS:
+ if (object.len < RSA_MIN_OCTETS + 1)
+ {
+ plog(" " RSA_MIN_OCTETS_UGH);
+ return FALSE;
+ }
+ if (object.len > RSA_MAX_OCTETS + (size_t)(*object.ptr == 0x00))
+ {
+ plog(" " RSA_MAX_OCTETS_UGH);
+ return FALSE;
+ }
+ cert->modulus = object;
+ break;
+ case X509_OBJ_PUBLIC_EXPONENT:
+ cert->publicExponent = object;
+ break;
+ case X509_OBJ_EXTN_ID:
+ extn_oid = known_oid(object);
+ break;
+ case X509_OBJ_CRITICAL:
+ critical = object.len && *object.ptr;
+ DBG(DBG_PARSING,
+ DBG_log(" %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:
+ cert->subjectAltName =
+ parse_generalNames(object, level, FALSE);
+ break;
+ case OID_BASIC_CONSTRAINTS:
+ cert->isCA =
+ parse_basicConstraints(object, level);
+ break;
+ case OID_CRL_DISTRIBUTION_POINTS:
+ cert->crlDistributionPoints =
+ parse_crlDistributionPoints(object, level);
+ 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 certificate by
+ * checking the notBefore and notAfter dates
+ */
+err_t
+check_validity(const x509cert_t *cert, time_t *until)
+{
+ time_t current_time;
+
+ time(&current_time);
+ DBG(DBG_CONTROL | DBG_PARSING ,
+ DBG_log(" not before : %s", timetoa(&cert->notBefore, TRUE));
+ DBG_log(" current time: %s", timetoa(&current_time, TRUE));
+ DBG_log(" not after : %s", timetoa(&cert->notAfter, TRUE));
+ )
+
+ if (cert->notAfter < *until) *until = cert->notAfter;
+
+ if (current_time < cert->notBefore)
+ return "certificate is not valid yet";
+ if (current_time > cert->notAfter)
+ return "certificate has expired";
+ else
+ return NULL;
+}
+
+/*
+ * verifies a X.509 certificate
+ */
+bool
+verify_x509cert(const x509cert_t *cert, bool strict, time_t *until)
+{
+ int pathlen;
+
+ *until = cert->notAfter;
+
+ for (pathlen = 0; pathlen < MAX_CA_PATH_LEN; pathlen++)
+ {
+ x509cert_t *issuer_cert;
+ u_char buf[BUF_LEN];
+ err_t ugh = NULL;
+
+ 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(cert, until);
+
+ if (ugh != NULL)
+ {
+ plog("%s", ugh);
+ return FALSE;
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("certificate is valid")
+ )
+
+ lock_authcert_list("verify_x509cert");
+ issuer_cert = get_authcert(cert->issuer, cert->authKeySerialNumber
+ , cert->authKeyID, AUTH_CA);
+
+ if (issuer_cert == NULL)
+ {
+ plog("issuer cacert not found");
+ unlock_authcert_list("verify_x509cert");
+ return FALSE;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("issuer cacert found")
+ )
+
+ if (!check_signature(cert->tbsCertificate, cert->signature
+ , cert->algorithm, cert->algorithm, issuer_cert))
+ {
+ plog("certificate signature is invalid");
+ unlock_authcert_list("verify_x509cert");
+ return FALSE;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("certificate signature is valid")
+ )
+ unlock_authcert_list("verify_x509cert");
+
+ /* 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")
+ )
+ return TRUE;
+ }
+ else
+ {
+ time_t nextUpdate = *until;
+ time_t revocationDate = UNDEFINED_TIME;
+ crl_reason_t revocationReason = REASON_UNSPECIFIED;
+
+ /* first check certificate revocation using ocsp */
+ cert_status_t status = verify_by_ocsp(cert, &nextUpdate
+ , &revocationDate, &revocationReason);
+
+ /* if ocsp service is not available then fall back to crl */
+ if ((status == CERT_UNDEFINED)
+ || (status == CERT_UNKNOWN && strict))
+ {
+ status = verify_by_crl(cert, &nextUpdate, &revocationDate
+ , &revocationReason);
+ }
+
+ switch (status)
+ {
+ case CERT_GOOD:
+ /* if status information is stale */
+ if (strict && nextUpdate < time(NULL))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("certificate is good but status is stale")
+ )
+ remove_x509_public_key(cert);
+ return FALSE;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("certificate is good")
+ )
+
+ /* with strict crl policy the public key must have the same
+ * lifetime as the validity of the ocsp status or crl lifetime
+ */
+ if (strict && nextUpdate < *until)
+ *until = nextUpdate;
+ break;
+ case CERT_REVOKED:
+ plog("certificate was revoked on %s, reason: %s"
+ , timetoa(&revocationDate, TRUE)
+ , enum_name(&crl_reason_names, revocationReason));
+ remove_x509_public_key(cert);
+ return FALSE;
+ case CERT_UNKNOWN:
+ case CERT_UNDEFINED:
+ default:
+ plog("certificate status unknown");
+ if (strict)
+ {
+ remove_x509_public_key(cert);
+ return FALSE;
+ }
+ break;
+ }
+ }
+
+ /* go up one step in the trust chain */
+ cert = issuer_cert;
+ }
+ plog("maximum ca path length of %d levels exceeded", MAX_CA_PATH_LEN);
+ return FALSE;
+}
+
+/*
+ * list all X.509 certs in a chained list
+ */
+void
+list_x509cert_chain(const char *caption, x509cert_t* cert, u_char auth_flags
+ , bool utc)
+{
+ bool first = TRUE;
+ time_t now;
+
+ /* determine the current time */
+ time(&now);
+
+ while (cert != NULL)
+ {
+ if (auth_flags == AUTH_NONE || (auth_flags & cert->authority_flags))
+ {
+ unsigned keysize;
+ char keyid[KEYID_BUF];
+ u_char buf[BUF_LEN];
+ cert_t c;
+
+ c.type = CERT_X509_SIGNATURE;
+ c.u.x509 = cert;
+
+ if (first)
+ {
+ whack_log(RC_COMMENT, " ");
+ whack_log(RC_COMMENT, "List of X.509 %s Certificates:", caption);
+ whack_log(RC_COMMENT, " ");
+ first = FALSE;
+ }
+
+ whack_log(RC_COMMENT, "%s, count: %d", timetoa(&cert->installed, utc),
+ cert->count);
+ dntoa(buf, BUF_LEN, cert->subject);
+ whack_log(RC_COMMENT, " subject: '%s'", buf);
+ dntoa(buf, BUF_LEN, cert->issuer);
+ whack_log(RC_COMMENT, " issuer: '%s'", buf);
+ datatot(cert->serialNumber.ptr, cert->serialNumber.len, ':'
+ , buf, BUF_LEN);
+ whack_log(RC_COMMENT, " serial: %s", buf);
+ form_keyid(cert->publicExponent, cert->modulus, keyid, &keysize);
+ whack_log(RC_COMMENT, " pubkey: %4d RSA Key %s%s"
+ , 8*keysize, keyid
+ , cert->smartcard ? ", on smartcard" :
+ (has_private_key(c)? ", has private key" : ""));
+ whack_log(RC_COMMENT, " validity: not before %s %s",
+ timetoa(&cert->notBefore, utc),
+ (cert->notBefore < now)?"ok":"fatal (not valid yet)");
+ whack_log(RC_COMMENT, " not after %s %s",
+ timetoa(&cert->notAfter, utc),
+ check_expiry(cert->notAfter, CA_CERT_WARNING_INTERVAL, TRUE));
+ if (cert->subjectKeyID.ptr != NULL)
+ {
+ datatot(cert->subjectKeyID.ptr, cert->subjectKeyID.len, ':'
+ , buf, BUF_LEN);
+ whack_log(RC_COMMENT, " subjkey: %s", buf);
+ }
+ if (cert->authKeyID.ptr != NULL)
+ {
+ datatot(cert->authKeyID.ptr, cert->authKeyID.len, ':'
+ , buf, BUF_LEN);
+ whack_log(RC_COMMENT, " authkey: %s", buf);
+ }
+ if (cert->authKeySerialNumber.ptr != NULL)
+ {
+ datatot(cert->authKeySerialNumber.ptr, cert->authKeySerialNumber.len
+ , ':', buf, BUF_LEN);
+ whack_log(RC_COMMENT, " aserial: %s", buf);
+ }
+ }
+ cert = cert->next;
+ }
+}
+
+/*
+ * list all X.509 end certificates in a chained list
+ */
+void
+list_x509_end_certs(bool utc)
+{
+ list_x509cert_chain("End", x509certs, AUTH_NONE, utc);
+}
diff --git a/programs/pluto/x509.h b/programs/pluto/x509.h
new file mode 100644
index 000000000..d15b3da53
--- /dev/null
+++ b/programs/pluto/x509.h
@@ -0,0 +1,138 @@
+/* Support of X.509 certificates
+ * Copyright (C) 2000 Andreas Hess, Patric Lichtsteiner, Roger Wegmann
+ * Copyright (C) 2001 Marco Bertossa, Andreas Schleiss
+ * Copyright (C) 2002 Mario Strasser
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: x509.h,v 1.10 2005/12/06 22:52:44 as Exp $
+ */
+
+#ifndef _X509_H
+#define _X509_H
+
+#include "pkcs1.h"
+#include "id.h"
+
+/* Definition of generalNames kinds */
+
+typedef enum {
+ 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
+} generalNames_t;
+
+/* access structure for a GeneralName */
+
+typedef struct generalName generalName_t;
+
+struct generalName {
+ generalName_t *next;
+ generalNames_t kind;
+ chunk_t name;
+};
+
+/* access structure for an X.509v3 certificate */
+
+typedef struct x509cert x509cert_t;
+
+struct x509cert {
+ x509cert_t *next;
+ time_t installed;
+ int count;
+ bool smartcard;
+ u_char authority_flags;
+ chunk_t certificate;
+ chunk_t tbsCertificate;
+ u_int version;
+ chunk_t serialNumber;
+ /* signature */
+ int sigAlg;
+ chunk_t issuer;
+ /* validity */
+ time_t notBefore;
+ time_t notAfter;
+ chunk_t subject;
+ /* subjectPublicKeyInfo */
+ enum pubkey_alg subjectPublicKeyAlgorithm;
+ chunk_t subjectPublicKey;
+ chunk_t modulus;
+ chunk_t publicExponent;
+ /* 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 */
+ generalName_t *subjectAltName;
+ generalName_t *crlDistributionPoints;
+ /* signatureAlgorithm */
+ int algorithm;
+ chunk_t signature;
+};
+
+/* used for initialization */
+extern const x509cert_t empty_x509cert;
+
+extern bool same_serial(chunk_t a, chunk_t b);
+extern bool same_keyid(chunk_t a, chunk_t b);
+extern bool same_dn(chunk_t a, chunk_t b);
+extern bool match_dn(chunk_t a, chunk_t b, int *wildcards);
+extern bool same_x509cert(const x509cert_t *a, const x509cert_t *b);
+extern void hex_str(chunk_t bin, chunk_t *str);
+extern int dn_count_wildcards(chunk_t dn);
+extern int dntoa(char *dst, size_t dstlen, chunk_t dn);
+extern int dntoa_or_null(char *dst, size_t dstlen, chunk_t dn
+ , const char* null_dn);
+extern err_t atodn(char *src, chunk_t *dn);
+extern void gntoid(struct id *id, const generalName_t *gn);
+extern void compute_subjectKeyID(x509cert_t *cert, chunk_t subjectKeyID);
+extern void select_x509cert_id(x509cert_t *cert, struct id *end_id);
+extern bool parse_x509cert(chunk_t blob, u_int level0, x509cert_t *cert);
+extern time_t parse_time(chunk_t blob, int level0);
+extern void parse_authorityKeyIdentifier(chunk_t blob, int level0
+ , chunk_t *authKeyID, chunk_t *authKeySerialNumber);
+extern chunk_t get_directoryName(chunk_t blob, int level, bool implicit);
+extern err_t check_validity(const x509cert_t *cert, time_t *until);
+extern bool check_signature(chunk_t tbs, chunk_t sig, int digest_alg
+ , int enc_alg, const x509cert_t *issuer_cert);
+extern bool verify_x509cert(const x509cert_t *cert, bool strict, time_t *until);
+extern x509cert_t* add_x509cert(x509cert_t *cert);
+extern x509cert_t* get_x509cert(chunk_t issuer, chunk_t serial, chunk_t keyid
+ , x509cert_t* chain);
+extern void build_x509cert(x509cert_t *cert, const RSA_public_key_t *cert_key
+ , const RSA_private_key_t *signer_key);
+extern chunk_t build_subjectAltNames(generalName_t *subjectAltNames);
+extern void share_x509cert(x509cert_t *cert);
+extern void release_x509cert(x509cert_t *cert);
+extern void free_x509cert(x509cert_t *cert);
+extern void store_x509certs(x509cert_t **firstcert, bool strict);
+extern void list_x509cert_chain(const char *caption, x509cert_t* cert
+ , u_char auth_flags, bool utc);
+extern void list_x509_end_certs(bool utc);
+extern void free_generalNames(generalName_t* gn, bool free_name);
+
+#endif /* _X509_H */
diff --git a/programs/proc/Makefile b/programs/proc/Makefile
new file mode 100644
index 000000000..023356440
--- /dev/null
+++ b/programs/proc/Makefile
@@ -0,0 +1,51 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:30 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+EXTRA5PROC:=version.5 trap_count.5 trap_sendcount.5
+
+LIBS:=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:30 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.5 2003/06/20 02:56:20 mcr
+# added documentation for /proc/net/ipsec/stats/trap_* and
+# amendments to test cases.
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 2002/06/02 21:51:41 mcr
+# changed TOPDIR->FREESWANSRCDIR in all Makefiles.
+# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the
+# kernel sense.)
+#
+# Revision 1.2 2002/05/05 23:09:49 mcr
+# EXTRA35MAN should have the extensions on it.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
diff --git a/programs/proc/trap_count.5 b/programs/proc/trap_count.5
new file mode 100644
index 000000000..e4cfd5871
--- /dev/null
+++ b/programs/proc/trap_count.5
@@ -0,0 +1,35 @@
+.TH IPSEC_TRAP_COUNT 5 "19 Jun 2003"
+.\"
+.\" RCSID $Id: trap_count.5,v 1.1 2004/03/15 20:35:30 as Exp $
+.\"
+.SH NAME
+trap_count \- KLIPS statistic on number of ACQUIREs
+.SH SYNOPSIS
+.B cat
+.B /proc/net/ipsec/stats/trap_count
+.SH DESCRIPTION
+.I /proc/net/ipsec/stats/trap_count
+is a read-only file. It contains a hexadecimal number which records the
+number of attempts to send PF_ACQUIRE messages. Only those recorded by
+trap_sendcount were actually successfully passed to userland. Note that the
+userland may still have lost them on its own.
+.LP
+.SH "FILES"
+/proc/net/ipsec/stats/trap_sendcount
+.SH "SEE ALSO"
+ipsec(8), ipsec_pf_key(5), trap_sendcount(5), pluto(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Michael C. Richardson <mcr@freeswan.org>
+.\"
+.\" $Log: trap_count.5,v $
+.\" Revision 1.1 2004/03/15 20:35:30 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.1 2003/06/20 02:56:20 mcr
+.\" added documentation for /proc/net/ipsec/stats/trap_* and
+.\" amendments to test cases.
+.\"
+.\"
+.\"
diff --git a/programs/proc/trap_sendcount.5 b/programs/proc/trap_sendcount.5
new file mode 100644
index 000000000..27090b52b
--- /dev/null
+++ b/programs/proc/trap_sendcount.5
@@ -0,0 +1,33 @@
+.TH IPSEC_TRAP_SENDCOUNT 5 "19 Jun 2003"
+.\"
+.\" RCSID $Id: trap_sendcount.5,v 1.1 2004/03/15 20:35:30 as Exp $
+.\"
+.SH NAME
+trap_sendcount \- KLIPS statistic on number of successful ACQUIREs
+.SH SYNOPSIS
+.B cat
+.B /proc/net/ipsec/stats/trap_sendcount
+.SH DESCRIPTION
+.I /proc/net/ipsec/stats/trap_sendcount
+is a read-only file. It contains a hexadecimal number which records the
+number of successful PF_ACQUIRE messages that were sent.
+.LP
+.SH "FILES"
+/proc/net/ipsec/stats/trap_sendcount
+.SH "SEE ALSO"
+ipsec(8), ipsec_pf_key(5), trap_count(5), pluto(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Michael C. Richardson <mcr@freeswan.org>
+.\"
+.\" $Log: trap_sendcount.5,v $
+.\" Revision 1.1 2004/03/15 20:35:30 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.1 2003/06/20 02:56:20 mcr
+.\" added documentation for /proc/net/ipsec/stats/trap_* and
+.\" amendments to test cases.
+.\"
+.\"
+.\"
diff --git a/programs/proc/version.5 b/programs/proc/version.5
new file mode 100644
index 000000000..c763d6d17
--- /dev/null
+++ b/programs/proc/version.5
@@ -0,0 +1,54 @@
+.TH IPSEC_VERSION 5 "29 Jun 2000"
+.\"
+.\" RCSID $Id: version.5,v 1.1 2004/03/15 20:35:30 as Exp $
+.\"
+.SH NAME
+ipsec_version \- lists KLIPS version information
+.SH SYNOPSIS
+.B cat
+.B /proc/net/ipsec_version
+.SH DESCRIPTION
+.I /proc/net/ipsec_version
+is a read-only file which lists the currently running KLIPS version
+information.
+.PP
+.SH EXAMPLES
+.TP
+.B FreeS/WAN version: 1.4
+.LP
+shows that the currently loaded
+.B KLIPS
+is from
+.B FreeS/WAN 1.4.
+.LP
+.SH "FILES"
+/proc/net/ipsec_version
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_eroute(5), ipsec_spi(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_tncfg(8), ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: version.5,v $
+.\" Revision 1.1 2004/03/15 20:35:30 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.4 2002/04/24 07:35:41 mcr
+.\" Moved from ./klips/utils/version.5,v
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/30 06:22:22 rgb
+.\" Fix SYNOPSIS since there is no 'ipsec version' command.
+.\"
+.\" Revision 1.1 2000/06/30 06:19:26 rgb
+.\" manpages for the last two /proc/net/ipsec* files that don't have a
+.\" corresponding utility.
+.\"
+.\"
+.\"
diff --git a/programs/ranbits/.cvsignore b/programs/ranbits/.cvsignore
new file mode 100644
index 000000000..910103faa
--- /dev/null
+++ b/programs/ranbits/.cvsignore
@@ -0,0 +1 @@
+ranbits
diff --git a/programs/ranbits/Makefile b/programs/ranbits/Makefile
new file mode 100644
index 000000000..558318e8e
--- /dev/null
+++ b/programs/ranbits/Makefile
@@ -0,0 +1,39 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:30 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=ranbits
+LIBS=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:30 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/ranbits/ranbits.8 b/programs/ranbits/ranbits.8
new file mode 100644
index 000000000..5a99a088f
--- /dev/null
+++ b/programs/ranbits/ranbits.8
@@ -0,0 +1,77 @@
+.TH IPSEC_RANBITS 8 "22 Aug 2000"
+.\" RCSID $Id: ranbits.8,v 1.1 2004/03/15 20:35:30 as Exp $
+.SH NAME
+ipsec ranbits \- generate random bits in ASCII form
+.SH SYNOPSIS
+.B ipsec
+.B ranbits
+[
+.B \-\-quick
+] [
+.B \-\-continuous
+] [
+.B \-\-bytes
+] nbits
+.SH DESCRIPTION
+.I Ranbits
+obtains
+.I nbits
+(rounded up to the nearest byte)
+high-quality random bits from
+.IR random (4),
+and emits them on standard output as an ASCII string.
+The default output format is
+.IR datatot (3)
+.B h
+format:
+lowercase hexadecimal with a
+.B 0x
+prefix and an underscore every 32 bits.
+.PP
+The
+.B \-\-quick
+option produces quick-and-dirty random bits:
+instead of using the high-quality random bits from
+.IR /dev/random ,
+which may take some time to supply the necessary bits if
+.I nbits
+is large,
+.I ranbits
+uses
+.IR /dev/urandom ,
+which yields prompt results but lower-quality randomness.
+.PP
+The
+.B \-\-continuous
+option uses
+.IR datatot (3)
+.B x
+output format, like
+.B h
+but without the underscores.
+.PP
+The
+.B \-\-bytes
+option causes
+.I nbits
+to be interpreted as a byte count rather than a bit count.
+.SH FILES
+/dev/random, /dev/urandom
+.SH SEE ALSO
+ipsec_datatot(3), random(4)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+There is an internal limit on
+.IR nbits ,
+currently 20000.
+.PP
+Without
+.BR \-\-quick ,
+.IR ranbits 's
+run time is difficult to predict.
+A request for a large number of bits,
+at a time when the system's entropy pool is low on randomness,
+may take quite a while to satisfy.
diff --git a/programs/ranbits/ranbits.c b/programs/ranbits/ranbits.c
new file mode 100644
index 000000000..7b9a0f76e
--- /dev/null
+++ b/programs/ranbits/ranbits.c
@@ -0,0 +1,146 @@
+/*
+ * random bit generation for scripts, control files, etc.
+ * Copyright (C) 1998, 1999, 2000 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: ranbits.c,v 1.1 2004/03/15 20:35:30 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <freeswan.h>
+
+#ifndef DEVICE
+#define DEVICE "/dev/random"
+#endif
+#ifndef QDEVICE
+#define QDEVICE "/dev/urandom"
+#endif
+#ifndef MAXBITS
+#define MAXBITS 20000
+#endif
+
+char usage[] = "Usage: ranbits [--quick] [--continuous] [--bytes] nbits";
+struct option opts[] = {
+ {"quick", 0, NULL, 'q',},
+ {"continuous", 0, NULL, 'c',},
+ {"bytes", 0, NULL, 'b',},
+ {"help", 0, NULL, 'h',},
+ {"version", 0, NULL, 'v',},
+ {0, 0, NULL, 0,}
+};
+int quick = 0; /* quick and dirty? */
+char format = 'h'; /* datatot() format code */
+int isbytes = 0; /* byte count rather than bits? */
+
+char me[] = "ipsec ranbits"; /* for messages */
+
+char buf[MAXBITS/CHAR_BIT];
+char outbuf[3*sizeof(buf)];
+
+int main(int argc, char *argv[])
+{
+ int opt;
+ extern int optind;
+ int errflg = 0;
+ int nbits;
+ size_t nbytes;
+ char *devname;
+ int dev;
+ size_t ndone;
+ size_t nneeded;
+ ssize_t got;
+
+ while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF)
+ switch (opt) {
+ case 'q': /* quick and dirty randomness */
+ quick = 1;
+ break;
+ case 'c': /* continuous hex, no underscores */
+ format = 'x';
+ break;
+ case 'b': /* byte count, not bit count */
+ isbytes = 1;
+ break;
+ case 'h': /* help */
+ printf("%s\n", usage);
+ exit(0);
+ break;
+ case 'v': /* version */
+ printf("%s %s\n", me, ipsec_version_code());
+ exit(0);
+ break;
+ case '?':
+ default:
+ errflg = 1;
+ break;
+ }
+ if (errflg || optind != argc-1) {
+ fprintf(stderr, "%s\n", usage);
+ exit(2);
+ }
+
+ nbits = atoi(argv[optind]);
+ if (isbytes)
+ nbits *= CHAR_BIT;
+ if (nbits <= 0) {
+ fprintf(stderr, "%s: invalid bit count (%d)\n", me, nbits);
+ exit(1);
+ }
+ if (nbits > MAXBITS) {
+ fprintf(stderr, "%s: overlarge bit count (max %d)\n", me,
+ MAXBITS);
+ exit(1);
+ }
+ nbytes = (size_t)(nbits + CHAR_BIT - 1) / CHAR_BIT;
+
+ devname = (quick) ? QDEVICE : DEVICE;
+ dev = open(devname, 0);
+ if (dev < 0) {
+ fprintf(stderr, "%s: could not open %s (%s)\n", me,
+ devname, strerror(errno));
+ exit(1);
+ }
+
+ ndone = 0;
+ while (ndone < nbytes) {
+ got = read(dev, buf + ndone, nbytes - ndone);
+ if (got < 0) {
+ fprintf(stderr, "%s: read error on %s (%s)\n", me,
+ devname, strerror(errno));
+ exit(1);
+ }
+ if (got == 0) {
+ fprintf(stderr, "%s: eof on %s!?!\n", me, devname);
+ exit(1);
+ }
+ ndone += got;
+ }
+
+ nneeded = datatot(buf, nbytes, format, outbuf, sizeof(outbuf));
+ if (nneeded > sizeof(outbuf)) {
+ fprintf(stderr, "%s: buffer overflow (need %ld bytes)?!?\n",
+ me, (long)nneeded);
+ exit(1);
+ }
+ printf("%s\n", outbuf);
+ exit(0);
+}
diff --git a/programs/rsasigkey/.cvsignore b/programs/rsasigkey/.cvsignore
new file mode 100644
index 000000000..f9e610b4d
--- /dev/null
+++ b/programs/rsasigkey/.cvsignore
@@ -0,0 +1 @@
+rsasigkey
diff --git a/programs/rsasigkey/Makefile b/programs/rsasigkey/Makefile
new file mode 100644
index 000000000..c2b82e5c8
--- /dev/null
+++ b/programs/rsasigkey/Makefile
@@ -0,0 +1,39 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:30 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=rsasigkey
+LIBS=${FREESWANLIB} -lgmp
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:30 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/rsasigkey/rsasigkey.8 b/programs/rsasigkey/rsasigkey.8
new file mode 100644
index 000000000..c64dd46bd
--- /dev/null
+++ b/programs/rsasigkey/rsasigkey.8
@@ -0,0 +1,259 @@
+.TH IPSEC_RSASIGKEY 8 "22 July 2001"
+.\" RCSID $Id: rsasigkey.8,v 1.1 2004/03/15 20:35:30 as Exp $
+.SH NAME
+ipsec rsasigkey \- generate RSA signature key
+.SH SYNOPSIS
+.B ipsec
+.B rsasigkey
+[
+.B \-\-verbose
+] [
+.B \-\-random
+filename
+]
+.B \e
+.br
+\ \ \ [
+.B \-\-rounds
+nr
+] [
+.B \-\-hostname
+host ] [
+.B \-\-noopt
+] nbits
+.br
+.B ipsec
+.B rsasigkey
+[
+.B \-\-verbose
+] [
+.B \-\-hostname
+host ]
+.B \e
+.br
+\ \ \
+[
+.B \-\-noopt
+]
+.B \-\-oldkey
+file
+.SH DESCRIPTION
+.I Rsasigkey
+generates an RSA public/private key pair,
+suitable for digital signatures,
+of (exactly)
+.I nbits
+bits (that is, two primes each of exactly
+.IR nbits /2
+bits,
+and related numbers)
+and emits it on standard output as ASCII (mostly hex) data.
+.I nbits
+must be a multiple of 16.
+.PP
+The public exponent is forced to the value
+.BR 3 ,
+which has important speed advantages for signature checking.
+Beware that the resulting keys have known weaknesses as encryption keys
+\fIand should not be used for that purpose\fR.
+.PP
+The
+.B \-\-verbose
+option makes
+.I rsasigkey
+give a running commentary on standard error.
+By default, it works in silence until it is ready to generate output.
+.PP
+The
+.B \-\-random
+option specifies a source for random bits.
+The default is
+.I /dev/random
+(see
+.IR random (4)).
+Normally,
+.I rsasigkey
+reads exactly
+.I nbits
+random bits from the source;
+in extremely-rare circumstances it may need more.
+.PP
+The
+.B \-\-rounds
+option specifies the number of rounds to be done by the
+.I mpz_probab_prime_p
+probabilistic primality checker.
+The default, 30, is fairly rigorous and should not normally
+have to be overridden.
+.PP
+The
+.B \-\-hostname
+option specifies what host name to use in
+the first line of the output (see below);
+the default is what
+.IR gethostname (2)
+returns.
+.PP
+The
+.B \-\-noopt
+option suppresses an optimization of the private key
+(to be precise, setting of the decryption exponent to
+.B lcm(p\-1,q\-1)
+rather than
+.BR (p\-1)*(q\-1) )
+which speeds up operations on it slightly
+but can cause it to flunk a validity check in old RSA implementations
+(notably, obsolete versions of
+.IR ipsec_pluto (8)).
+.PP
+The
+.B \-\-oldkey
+option specifies that rather than generate a new key,
+.I rsasigkey
+should read an old key from the
+.I file
+(the name
+.B \-
+means ``standard input'')
+and use that to generate its output.
+Input lines which do not look like
+.I rsasigkey
+output are silently ignored.
+This permits updating old keys to the current format.
+.PP
+The output format looks like this (with long numbers trimmed down
+for clarity):
+.PP
+.ne 15
+.nf
+ # RSA 2048 bits xy.example.com Sat Apr 15 13:53:22 2000
+ # for signatures only, UNSAFE FOR ENCRYPTION
+ #pubkey=0sAQOF8tZ2NZt...Y1P+buFuFn/
+ Modulus: 0xcc2a86fcf440...cf1011abb82d1
+ PublicExponent: 0x03
+ # everything after this point is secret
+ PrivateExponent: 0x881c59fdf8...ab05c8c77d23
+ Prime1: 0xf49fd1f779...46504c7bf3
+ Prime2: 0xd5a9108453...321d43cb2b
+ Exponent1: 0xa31536a4fb...536d98adda7f7
+ Exponent2: 0x8e70b5ad8d...9142168d7dcc7
+ Coefficient: 0xafb761d001...0c13e98d98
+.fi
+.PP
+The first (comment) line,
+indicating the nature and date of the key,
+and giving a host name,
+is used by
+.IR ipsec_showhostkey (8)
+when generating some forms of key output.
+.PP
+The commented-out
+.B pubkey=
+line contains the public key\(emthe public exponent and the modulus\(emcombined
+in approximately RFC 2537 format
+(the one deviation is that the combined value is given with a
+.B 0s
+prefix, rather than in unadorned base-64),
+suitable for use in the
+.I ipsec.conf
+file.
+.PP
+The
+.BR Modulus ,
+.BR PublicExponent ,
+and
+.B PrivateExponent
+lines give the basic signing and verification data.
+.PP
+The
+.B Prime1
+and
+.B Prime2
+lines give the primes themselves (aka
+.I p
+and
+.IR q ),
+largest first.
+The
+.B Exponent1
+and
+.B Exponent2
+lines give
+the private exponent mod
+.IR p\-1
+and
+.IR q\-1
+respectively.
+The
+.B Coefficient
+line gives the Chinese Remainder Theorem coefficient,
+which is the inverse of
+.IR q ,
+mod
+.IR p .
+These additional numbers (which must all be kept as secret as the
+private exponent) are precomputed aids to rapid signature generation.
+.PP
+No attempt is made to break long lines.
+.PP
+The US patent on the RSA algorithm expired 20 Sept 2000.
+.SH EXAMPLES
+.TP
+.B "ipsec rsasigkey \-\-verbose 2192 >mykey"
+generates a 2192-bit signature key and puts it in the file
+.IR mykey ,
+with running commentary on standard error.
+The file contents can be inserted verbatim into a suitable entry in the
+.I ipsec.secrets
+file (see
+.IR ipsec.secrets (5)),
+and the public key can then be extracted and edited into the
+.I ipsec.conf
+file (see
+.IR ipsec.conf (5)).
+.TP
+.B "ipsec rsasigkey \-\-verbose \-\-oldkey oldie >latest"
+takes the old signature key from file
+.I oldie
+and puts a version in the current format into the file
+.IR latest ,
+with running commentary on standard error.
+.SH FILES
+/dev/random
+.SH SEE ALSO
+random(4), ipsec_showhostkey(8)
+.br
+\fIApplied Cryptography\fR, 2nd. ed., by Bruce Schneier, Wiley 1996.
+.br
+RFCs 2537, 2313.
+.br
+\fIGNU MP, the GNU multiple precision arithmetic library, edition 2.0.2\fR,
+by Torbj Granlund.
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+There is an internal limit on
+.IR nbits ,
+currently 20000.
+.PP
+.IR Rsasigkey 's
+run time is difficult to predict,
+since
+.I /dev/random
+output can be arbitrarily delayed if
+the system's entropy pool is low on randomness,
+and the time taken by the search for primes is also somewhat unpredictable.
+A reasonably typical time for a 1024-bit key on a quiet 200MHz Pentium MMX
+with plenty of randomness available is 20 seconds,
+almost all of it in the prime searches.
+Generating a 2192-bit key on the same system usually takes several minutes.
+A 4096-bit key took an hour and a half of CPU time.
+.PP
+The
+.B \-\-oldkey
+option does not check its input format as rigorously as it might.
+Corrupted
+.I rsasigkey
+output may confuse it.
diff --git a/programs/rsasigkey/rsasigkey.c b/programs/rsasigkey/rsasigkey.c
new file mode 100644
index 000000000..b55dbb889
--- /dev/null
+++ b/programs/rsasigkey/rsasigkey.c
@@ -0,0 +1,573 @@
+/*
+ * RSA signature key generation
+ * Copyright (C) 1999, 2000, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rsasigkey.c,v 1.2 2005/08/11 10:35:58 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include <freeswan.h>
+#include "gmp.h"
+
+#ifndef DEVICE
+#define DEVICE "/dev/random"
+#endif
+#ifndef MAXBITS
+#define MAXBITS 20000
+#endif
+
+/* the code in getoldkey() knows about this */
+#define E 3 /* standard public exponent */
+
+char usage[] = "rsasigkey [--verbose] [--random device] nbits";
+char usage2[] = "rsasigkey [--verbose] --oldkey filename";
+struct option opts[] = {
+ {"verbose", 0, NULL, 'v',},
+ {"random", 1, NULL, 'r',},
+ {"rounds", 1, NULL, 'p',},
+ {"oldkey", 1, NULL, 'o',},
+ {"hostname", 1, NULL, 'H',},
+ {"noopt", 0, NULL, 'n',},
+ {"help", 0, NULL, 'h',},
+ {"version", 0, NULL, 'V',},
+ {0, 0, NULL, 0,}
+};
+int verbose = 0; /* narrate the action? */
+char *device = DEVICE; /* where to get randomness */
+int nrounds = 30; /* rounds of prime checking; 25 is good */
+mpz_t prime1; /* old key's prime1 */
+mpz_t prime2; /* old key's prime2 */
+char outputhostname[1024]; /* hostname for output */
+int do_lcm = 1; /* use lcm(p-1, q-1), not (p-1)*(q-1) */
+
+char me[] = "ipsec rsasigkey"; /* for messages */
+
+/* forwards */
+int getoldkey(char *filename);
+void rsasigkey(int nbits, int useoldkey);
+void initprime(mpz_t var, int nbits, int eval);
+void initrandom(mpz_t var, int nbits);
+void getrandom(size_t nbytes, char *buf);
+char *bundle(int e, mpz_t n, size_t *sizep);
+char *conv(char *bits, size_t nbytes, int format);
+char *hexout(mpz_t var);
+void report(char *msg);
+
+/*
+ - main - mostly argument parsing
+ */
+int main(int argc, char *argv[])
+{
+ int opt;
+ extern int optind;
+ extern char *optarg;
+ int errflg = 0;
+ int i;
+ int nbits;
+ char *oldkeyfile = NULL;
+
+ while ((opt = getopt_long(argc, argv, "", opts, NULL)) != EOF)
+ switch (opt) {
+ case 'v': /* verbose description */
+ verbose = 1;
+ break;
+ case 'r': /* nonstandard /dev/random */
+ device = optarg;
+ break;
+ case 'p': /* number of prime-check rounds */
+ nrounds = atoi(optarg);
+ if (nrounds <= 0) {
+ fprintf(stderr, "%s: rounds must be > 0\n", me);
+ exit(2);
+ }
+ break;
+ case 'o': /* reformat old key */
+ oldkeyfile = optarg;
+ break;
+ case 'H': /* set hostname for output */
+ strcpy(outputhostname, optarg);
+ break;
+ case 'n': /* don't optimize the private key */
+ do_lcm = 0;
+ break;
+ case 'h': /* help */
+ printf("Usage:\t%s\n", usage);
+ printf("\tor\n");
+ printf("\t%s\n", usage2);
+ exit(0);
+ break;
+ case 'V': /* version */
+ printf("%s %s\n", me, ipsec_version_code());
+ exit(0);
+ break;
+ case '?':
+ default:
+ errflg = 1;
+ break;
+ }
+ if (errflg || optind != ((oldkeyfile != NULL) ? argc : argc-1)) {
+ printf("Usage:\t%s\n", usage);
+ printf("\tor\n");
+ printf("\t%s\n", usage2);
+ exit(2);
+ }
+
+ if (outputhostname[0] == '\0') {
+ i = gethostname(outputhostname, sizeof(outputhostname));
+ if (i < 0) {
+ fprintf(stderr, "%s: gethostname failed (%s)\n",
+ me,
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (oldkeyfile == NULL) {
+ assert(argv[optind] != NULL);
+ nbits = atoi(argv[optind]);
+ } else
+ nbits = getoldkey(oldkeyfile);
+
+ if (nbits <= 0) {
+ fprintf(stderr, "%s: invalid bit count (%d)\n", me, nbits);
+ exit(1);
+ } else if (nbits > MAXBITS) {
+ fprintf(stderr, "%s: overlarge bit count (max %d)\n", me,
+ MAXBITS);
+ exit(1);
+ } else if (nbits % (CHAR_BIT*2) != 0) { /* *2 for nbits/2-bit primes */
+ fprintf(stderr, "%s: bit count (%d) not multiple of %d\n", me,
+ nbits, (int)CHAR_BIT*2);
+ exit(1);
+ }
+
+ rsasigkey(nbits, (oldkeyfile == NULL) ? 0 : 1);
+ exit(0);
+}
+
+/*
+ - getoldkey - fetch an old key's primes
+ */
+int /* nbits */
+getoldkey(filename)
+char *filename;
+{
+ FILE *f;
+ char line[MAXBITS/2];
+ char *p;
+ char *value;
+ static char pube[] = "PublicExponent:";
+ static char pubevalue[] = "0x03";
+ static char pr1[] = "Prime1:";
+ static char pr2[] = "Prime2:";
+# define STREQ(a, b) (strcmp(a, b) == 0)
+ int sawpube = 0;
+ int sawpr1 = 0;
+ int sawpr2 = 0;
+ int nbits;
+
+ nbits = 0;
+
+ if (STREQ(filename, "-"))
+ f = stdin;
+ else
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "%s: unable to open file `%s' (%s)\n", me,
+ filename, strerror(errno));
+ exit(1);
+ }
+ if (verbose)
+ fprintf(stderr, "getting old key from %s...\n", filename);
+
+ while (fgets(line, sizeof(line), f) != NULL) {
+ p = line + strlen(line) - 1;
+ if (*p != '\n') {
+ fprintf(stderr, "%s: over-long line in file `%s'\n",
+ me, filename);
+ exit(1);
+ }
+ *p = '\0';
+
+ p = line + strspn(line, " \t"); /* p -> first word */
+ value = strpbrk(p, " \t"); /* value -> after it */
+ if (value != NULL) {
+ *value++ = '\0';
+ value += strspn(value, " \t");
+ /* value -> second word if any */
+ }
+
+ if (value == NULL || *value == '\0') {
+ /* wrong format */
+ } else if (STREQ(p, pube)) {
+ sawpube = 1;
+ if (!STREQ(value, pubevalue)) {
+ fprintf(stderr, "%s: wrong public exponent (`%s') in old key\n",
+ me, value);
+ exit(1);
+ }
+ } else if (STREQ(p, pr1)) {
+ if (sawpr1) {
+ fprintf(stderr, "%s: duplicate `%s' lines in `%s'\n",
+ me, pr1, filename);
+ exit(1);
+ }
+ sawpr1 = 1;
+ nbits = (strlen(value) - 2) * 4 * 2;
+ if (mpz_init_set_str(prime1, value, 0) < 0) {
+ fprintf(stderr, "%s: conversion error in reading old prime1\n",
+ me);
+ exit(1);
+ }
+ } else if (STREQ(p, pr2)) {
+ if (sawpr2) {
+ fprintf(stderr, "%s: duplicate `%s' lines in `%s'\n",
+ me, pr2, filename);
+ exit(1);
+ }
+ sawpr2 = 1;
+ if (mpz_init_set_str(prime2, value, 0) < 0) {
+ fprintf(stderr, "%s: conversion error in reading old prime2\n",
+ me);
+ exit(1);
+ }
+ }
+ }
+
+ if (f != stdin)
+ fclose(f);
+
+ if (!sawpube || !sawpr1 || !sawpr2) {
+ fprintf(stderr, "%s: old key missing or incomplete\n", me);
+ exit(1);
+ }
+
+ assert(sawpr1); /* and thus nbits is known */
+ return(nbits);
+}
+
+/*
+ - rsasigkey - generate an RSA signature key
+ * e is fixed at 3, without discussion. That would not be wise if these
+ * keys were to be used for encryption, but for signatures there are some
+ * real speed advantages.
+ */
+void
+rsasigkey(nbits, useoldkey)
+int nbits;
+int useoldkey; /* take primes from old key? */
+{
+ mpz_t p;
+ mpz_t q;
+ mpz_t n;
+ mpz_t e;
+ mpz_t d;
+ mpz_t q1; /* temporary */
+ mpz_t m; /* internal modulus, (p-1)*(q-1) */
+ mpz_t t; /* temporary */
+ mpz_t exp1;
+ mpz_t exp2;
+ mpz_t coeff;
+ char *bundp;
+ size_t bs;
+ int success;
+ time_t now = time((time_t *)NULL);
+
+ /* the easy stuff */
+ if (useoldkey) {
+ mpz_init_set(p, prime1);
+ mpz_init_set(q, prime2);
+ } else {
+ initprime(p, nbits/2, E);
+ initprime(q, nbits/2, E);
+ }
+ mpz_init(t);
+ if (mpz_cmp(p, q) < 0) {
+ report("swapping primes so p is the larger...");
+ mpz_set(t, p);
+ mpz_set(p, q);
+ mpz_set(q, t);
+ }
+ report("computing modulus...");
+ mpz_init(n);
+ mpz_mul(n, p, q); /* n = p*q */
+ mpz_init_set_ui(e, E);
+
+ /* internal modulus */
+ report("computing lcm(p-1, q-1)...");
+ mpz_init_set(m, p);
+ mpz_sub_ui(m, m, 1);
+ mpz_init_set(q1, q);
+ mpz_sub_ui(q1, q1, 1);
+ mpz_gcd(t, m, q1); /* t = gcd(p-1, q-1) */
+ mpz_mul(m, m, q1); /* m = (p-1)*(q-1) */
+ if (do_lcm)
+ mpz_divexact(m, m, t); /* m = lcm(p-1, q-1) */
+ mpz_gcd(t, m, e);
+ assert(mpz_cmp_ui(t, 1) == 0); /* m and e relatively prime */
+
+ /* decryption key */
+ report("computing d...");
+ mpz_init(d);
+ success = mpz_invert(d, e, m);
+ assert(success); /* e has an inverse mod m */
+ if (mpz_cmp_ui(d, 0) < 0)
+ mpz_add(d, d, m);
+ assert(mpz_cmp(d, m) < 0);
+
+ /* the speedup hacks */
+ report("computing exp1, exp1, coeff...");
+ mpz_init(exp1);
+ mpz_sub_ui(t, p, 1);
+ mpz_mod(exp1, d, t); /* exp1 = d mod p-1 */
+ mpz_init(exp2);
+ mpz_sub_ui(t, q, 1);
+ mpz_mod(exp2, d, t); /* exp2 = d mod q-1 */
+ mpz_init(coeff);
+ mpz_invert(coeff, q, p); /* coeff = q^-1 mod p */
+ if (mpz_cmp_ui(coeff, 0) < 0)
+ mpz_add(coeff, coeff, p);
+ assert(mpz_cmp(coeff, p) < 0);
+
+ /* and the output */
+ /* note, getoldkey() knows about some of this */
+ report("output...\n"); /* deliberate extra newline */
+ printf("\t# RSA %d bits %s %s", nbits, outputhostname, ctime(&now));
+ /* ctime provides \n */
+ printf("\t# for signatures only, UNSAFE FOR ENCRYPTION\n");
+ bundp = bundle(E, n, &bs);
+ printf("\t#pubkey=%s\n", conv(bundp, bs, 's')); /* RFC2537ish format */
+ printf("\tModulus: %s\n", hexout(n));
+ printf("\tPublicExponent: %s\n", hexout(e));
+ printf("\t# everything after this point is secret\n");
+ printf("\tPrivateExponent: %s\n", hexout(d));
+ printf("\tPrime1: %s\n", hexout(p));
+ printf("\tPrime2: %s\n", hexout(q));
+ printf("\tExponent1: %s\n", hexout(exp1));
+ printf("\tExponent2: %s\n", hexout(exp2));
+ printf("\tCoefficient: %s\n", hexout(coeff));
+}
+
+/*
+ - initprime - initialize an mpz_t to a random prime of specified size
+ * Efficiency tweak: we reject candidates that are 1 higher than a multiple
+ * of e, since they will make the internal modulus not relatively prime to e.
+ */
+void
+initprime(var, nbits, eval)
+mpz_t var;
+int nbits; /* known to be a multiple of CHAR_BIT */
+int eval; /* value of e; 0 means don't bother w. tweak */
+{
+ unsigned long tries;
+ size_t len;
+# define OKAY(p) (eval == 0 || mpz_fdiv_ui(p, eval) != 1)
+
+ initrandom(var, nbits);
+ assert(mpz_fdiv_ui(var, 2) == 1); /* odd number */
+
+ report("looking for a prime starting there (can take a while)...");
+ tries = 1;
+ while (!( OKAY(var) && mpz_probab_prime_p(var, nrounds) )) {
+ mpz_add_ui(var, var, 2);
+ tries++;
+ }
+
+ len = mpz_sizeinbase(var, 2);
+ assert(len == (size_t)nbits || len == (size_t)(nbits+1));
+ if (len == (size_t)(nbits+1)) {
+ report("carry out occurred (!), retrying...");
+ mpz_clear(var);
+ initprime(var, nbits, eval);
+ return;
+ }
+ if (verbose)
+ fprintf(stderr, "found it after %lu tries.\n", tries);
+}
+
+/*
+ - initrandom - initialize an mpz_t to a random number, specified bit count
+ * Converting via hex is a bit weird, but it's the best route GMP gives us.
+ * Note that highmost and lowmost bits are forced on -- highmost to give a
+ * number of exactly the specified length, lowmost so it is an odd number.
+ */
+void
+initrandom(var, nbits)
+mpz_t var;
+int nbits; /* known to be a multiple of CHAR_BIT */
+{
+ size_t nbytes = (size_t)(nbits / CHAR_BIT);
+ static char bitbuf[MAXBITS/CHAR_BIT];
+ static char hexbuf[2 + MAXBITS/4 + 1];
+ size_t hsize = sizeof(hexbuf);
+
+ assert(nbytes <= sizeof(bitbuf));
+ getrandom(nbytes, bitbuf);
+ bitbuf[0] |= 01 << (CHAR_BIT-1); /* force high bit on */
+ bitbuf[nbytes-1] |= 01; /* force low bit on */
+ if (datatot(bitbuf, nbytes, 'x', hexbuf, hsize) > hsize) {
+ fprintf(stderr, "%s: can't-happen buffer overflow\n", me);
+ exit(1);
+ }
+ if (mpz_init_set_str(var, hexbuf, 0) < 0) {
+ fprintf(stderr, "%s: can't-happen hex conversion error\n", me);
+ exit(1);
+ }
+}
+
+/*
+ - getrandom - get some random bytes from /dev/random (or wherever)
+ */
+void
+getrandom(nbytes, buf)
+size_t nbytes;
+char *buf; /* known to be big enough */
+{
+ size_t ndone;
+ int dev;
+ size_t got;
+
+ dev = open(device, 0);
+ if (dev < 0) {
+ fprintf(stderr, "%s: could not open %s (%s)\n", me,
+ device, strerror(errno));
+ exit(1);
+ }
+
+ ndone = 0;
+ if (verbose)
+ fprintf(stderr, "getting %d random bytes from %s...\n", (int) nbytes,
+ device);
+ while (ndone < nbytes) {
+ got = read(dev, buf + ndone, nbytes - ndone);
+ if (got < 0) {
+ fprintf(stderr, "%s: read error on %s (%s)\n", me,
+ device, strerror(errno));
+ exit(1);
+ }
+ if (got == 0) {
+ fprintf(stderr, "%s: eof on %s!?!\n", me, device);
+ exit(1);
+ }
+ ndone += got;
+ }
+
+ close(dev);
+}
+
+/*
+ - hexout - prepare hex output, guaranteeing even number of digits
+ * (The current FreeS/WAN conversion routines want an even digit count,
+ * but mpz_get_str doesn't promise one.)
+ */
+char * /* pointer to static buffer (ick) */
+hexout(var)
+mpz_t var;
+{
+ static char hexbuf[3 + MAXBITS/4 + 1];
+ char *hexp;
+
+ mpz_get_str(hexbuf+3, 16, var);
+ if (strlen(hexbuf+3)%2 == 0) /* even number of hex digits */
+ hexp = hexbuf+1;
+ else { /* odd, must pad */
+ hexp = hexbuf;
+ hexp[2] = '0';
+ }
+ hexp[0] = '0';
+ hexp[1] = 'x';
+
+ return hexp;
+}
+
+/*
+ - bundle - bundle e and n into an RFC2537-format lump
+ * Note, calls hexout.
+ */
+char * /* pointer to static buffer (ick) */
+bundle(e, n, sizep)
+int e;
+mpz_t n;
+size_t *sizep;
+{
+ char *hexp = hexout(n);
+ static char bundbuf[2 + MAXBITS/8];
+ const char *er;
+ size_t size;
+
+ assert(e <= 255);
+ bundbuf[0] = 1;
+ bundbuf[1] = e;
+ er = ttodata(hexp, 0, 0, bundbuf+2, sizeof(bundbuf)-2, &size);
+ if (er != NULL) {
+ fprintf(stderr, "%s: can't-happen bundle convert error `%s'\n",
+ me, er);
+ exit(1);
+ }
+ if (size > sizeof(bundbuf)-2) {
+ fprintf(stderr, "%s: can't-happen bundle overflow (need %d)\n",
+ me, (int) size);
+ exit(1);
+ }
+ if (sizep != NULL)
+ *sizep = size + 2;
+ return bundbuf;
+}
+
+/*
+ - conv - convert bits to output in specified format
+ */
+char * /* pointer to static buffer (ick) */
+conv(bits, nbytes, format)
+char *bits;
+size_t nbytes;
+int format; /* datatot() code */
+{
+ static char convbuf[MAXBITS/4 + 50]; /* enough for hex */
+ size_t n;
+
+ n = datatot(bits, nbytes, format, convbuf, sizeof(convbuf));
+ if (n == 0) {
+ fprintf(stderr, "%s: can't-happen convert error\n", me);
+ exit(1);
+ }
+ if (n > sizeof(convbuf)) {
+ fprintf(stderr, "%s: can't-happen convert overflow (need %d)\n",
+ me, (int) n);
+ exit(1);
+ }
+ return convbuf;
+}
+
+/*
+ - report - report progress, if indicated
+ */
+void
+report(msg)
+char *msg;
+{
+ if (!verbose)
+ return;
+ fprintf(stderr, "%s\n", msg);
+}
diff --git a/programs/scepclient/Makefile b/programs/scepclient/Makefile
new file mode 100644
index 000000000..dec36c888
--- /dev/null
+++ b/programs/scepclient/Makefile
@@ -0,0 +1,184 @@
+# Makefile for the scepclient
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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 ${FREESWANSRCDIR}/Makefile.inc
+
+PLUTODIR=../pluto
+OPENACDIR=../openac
+
+PROGRAM=scepclient
+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
+
+# This compile option activates dynamic URL fetching using libcurl
+ifeq ($(USE_LIBCURL),true)
+ CFLAGS+= -DLIBCURL
+ LIBS+= -lcurl
+endif
+
+X509_OBJS= asn1.o ca.o certs.o constants.o crl.o defs.o fetch.o id.o keys.o \
+ lex.o md2.o md5.o mp_defs.o ocsp.o oid.o pem.o pgp.o pkcs1.o pkcs7.o \
+ rnd.o sha1.o smartcard.o x509.o
+
+OBJS= rsakey.o pkcs10.o loglite.o scep.o ${X509_OBJS}
+
+include ../Makefile.program
+
+loglite.o : $(OPENACDIR)/loglite.c $(PLUTODIR)/log.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+rsakey.o : rsakey.c rsakey.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+pkcs10.o : pkcs10.c pkcs10.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+scep.o : scep.c scep.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# X.509 library
+
+asn1.o : $(PLUTODIR)/asn1.c $(PLUTODIR)/asn1.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+ca.o : $(PLUTODIR)/ca.c $(PLUTODIR)/ca.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+crl.o : $(PLUTODIR)/crl.c $(PLUTODIR)/crl.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 $@ $<
+
+defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/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 $@ $<
+
+mp_defs.o : $(PLUTODIR)/mp_defs.c $(PLUTODIR)/mp_defs.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 $@ $<
+
+pkcs7.o : $(PLUTODIR)/pkcs7.c $(PLUTODIR)/pkcs7.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 $@ $<
+
+doxygen :
+ doxygen doxyconfig.DoxyFile
+
+# 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":
+
+pkcs10.o: pkcs10.c
+rsakey.o: rsakey.c
+scep.o: scep.c
+scepclient.o: scepclient.c
+
+pkcs10.o: ../pluto/constants.h
+pkcs10.o: ../pluto/defs.h
+pkcs10.o: ../pluto/oid.h
+pkcs10.o: ../pluto/asn1.h
+pkcs10.o: ../pluto/pkcs1.h
+pkcs10.o: ../pluto/log.h
+pkcs10.o: ../pluto/x509.h
+pkcs10.o: pkcs10.h
+rsakey.o: ../pluto/constants.h
+rsakey.o: ../pluto/defs.h
+rsakey.o: ../pluto/mp_defs.h
+rsakey.o: ../pluto/log.h
+rsakey.o: ../pluto/asn1.h
+rsakey.o: ../pluto/pkcs1.h
+rsakey.o: rsakey.h
+scep.o: ../pluto/constants.h
+scep.o: ../pluto/defs.h
+scep.o: ../pluto/rnd.h
+scep.o: ../pluto/oid.h
+scep.o: ../pluto/asn1.h
+scep.o: ../pluto/pkcs1.h
+scep.o: ../pluto/fetch.h
+scep.o: ../pluto/log.h
+scep.o: scep.h
+scepclient.o: ../pluto/constants.h
+scepclient.o: ../pluto/defs.h
+scepclient.o: ../pluto/log.h
+scepclient.o: ../pluto/oid.h
+scepclient.o: ../pluto/asn1.h
+scepclient.o: ../pluto/pkcs1.h
+scepclient.o: ../pluto/pkcs7.h
+scepclient.o: ../pluto/certs.h
+scepclient.o: ../pluto/fetch.h
+scepclient.o: ../pluto/rnd.h
+scepclient.o: rsakey.h
+scepclient.o: pkcs10.h
+scepclient.o: scep.h
diff --git a/programs/scepclient/pkcs10.c b/programs/scepclient/pkcs10.c
new file mode 100644
index 000000000..de3f06e18
--- /dev/null
+++ b/programs/scepclient/pkcs10.c
@@ -0,0 +1,220 @@
+/**
+ * @file pkcs10.c
+ * @brief Functions to build PKCS#10 requests
+ *
+ * Contains functions to build DER encoded pkcs#10 certificate requests
+ */
+
+/* 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/oid.h"
+#include "../pluto/asn1.h"
+#include "../pluto/pkcs1.h"
+#include "../pluto/log.h"
+#include "../pluto/x509.h"
+
+#include "pkcs10.h"
+
+/* some pre-coded OIDs */
+
+static u_char ASN1_challengePassword_oid_str[] = {
+ 0x06,0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x07
+};
+
+static const chunk_t ASN1_challengePassword_oid = strchunk(ASN1_challengePassword_oid_str);
+
+static u_char ASN1_extensionRequest_oid_str[] = {
+ 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x0E
+};
+
+static const chunk_t ASN1_extensionRequest_oid = strchunk(ASN1_extensionRequest_oid_str);
+
+/**
+ * @brief Adds a subjectAltName in DER-coded form to a linked list
+ *
+ * @param[in,out] subjectAltNames head of the linked list of subjectAltNames
+ * @param[in] kind type of the subjectAltName (which is a generalName)
+ * @param[in] value value of the subjectAltName as an ASCII string
+ */
+void
+pkcs10_add_subjectAltName(generalName_t **subjectAltNames, generalNames_t kind
+, char *value)
+{
+ generalName_t *gn;
+ asn1_t asn1_type = ASN1_EOC;
+ chunk_t name = { value, strlen(value) };
+
+ switch (kind)
+ {
+ case GN_RFC822_NAME:
+ asn1_type = ASN1_CONTEXT_S_1;
+ break;
+ case GN_DNS_NAME:
+ asn1_type = ASN1_CONTEXT_S_2;
+ break;
+ case GN_IP_ADDRESS:
+ {
+ struct in_addr addr;
+
+ /* convert an ASCII dotted IPv4 address (e.g. 123.456.78.90)
+ * to a byte representation in network order
+ */
+ if (!inet_aton(value, &addr))
+ {
+ fprintf(stderr, "error in IPv4 subjectAltName\n");
+ return;
+ }
+ asn1_type = ASN1_CONTEXT_S_7;
+ name.ptr = (u_char *) &addr.s_addr;
+ name.len = sizeof(addr.s_addr);
+ break;
+ }
+ default:
+ break;
+ }
+
+ gn = alloc_thing(generalName_t, "subjectAltName");
+ gn->kind = kind;
+ gn->name = asn1_simple_object(asn1_type, name);
+ gn->next = *subjectAltNames;
+ *subjectAltNames = gn;
+}
+
+/**
+ * @brief Builds the requestInfoAttributes of the certificationRequestInfo-field
+ *
+ * challenge password ans subjectAltNames are only included,
+ * when avaiable in given #pkcs10_t structure
+ *
+ * @param[in] pkcs10 Pointer to a #pkcs10_t structure
+ * @return 1 if succeeded, 0 otherwise
+ */
+static chunk_t
+build_req_info_attributes(pkcs10_t* pkcs10)
+{
+
+ chunk_t subjectAltNames = empty_chunk;
+ chunk_t challengePassword = empty_chunk;
+
+ if (pkcs10->subjectAltNames != NULL)
+ {
+
+ subjectAltNames = asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_extensionRequest_oid
+ , asn1_wrap(ASN1_SET, "m"
+ , asn1_wrap(ASN1_SEQUENCE, "m"
+ , build_subjectAltNames(pkcs10->subjectAltNames)
+ )
+ )
+ );
+ }
+
+ if (pkcs10->challengePassword.len > 0)
+ {
+ asn1_t type = is_printablestring(pkcs10->challengePassword)
+ ? ASN1_PRINTABLESTRING : ASN1_T61STRING;
+
+ challengePassword = asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_challengePassword_oid
+ , asn1_wrap(ASN1_SET, "m"
+ , asn1_simple_object(type, pkcs10->challengePassword)
+ )
+ );
+ }
+
+ return asn1_wrap(ASN1_CONTEXT_C_0, "mm"
+ , subjectAltNames
+ , challengePassword);
+}
+
+/**
+ * @brief Builds a DER-code pkcs#10 certificate request
+ *
+ * @param[in] pkcs10 pointer to a pkcs10_t struct
+ * @return DER-code pkcs10 request
+ */
+static chunk_t
+pkcs10_build_request(pkcs10_t *pkcs10, int signature_alg)
+{
+ RSA_public_key_t *rsak = (RSA_public_key_t *) pkcs10->private_key;
+
+ chunk_t cert_req_info = asn1_wrap(ASN1_SEQUENCE, "ccmm"
+ , ASN1_INTEGER_0
+ , pkcs10->subject
+ , pkcs1_build_publicKeyInfo(rsak)
+ , build_req_info_attributes(pkcs10));
+
+ chunk_t signature = pkcs1_build_signature(cert_req_info
+ , signature_alg, pkcs10->private_key, TRUE);
+
+ return asn1_wrap(ASN1_SEQUENCE, "mcm"
+ , cert_req_info
+ , asn1_algorithmIdentifier(signature_alg)
+ , signature);
+}
+
+/**
+ * @brief Creates a pkcs#10 certificate request object
+ *
+ * To create a certificate request, the RSA key and the
+ * names to be included as subject in the certificate request
+ * (e.g. commonName, organization) are needed. An optional challenge
+ * password or some subjectAltNames may be included.
+ *
+ * @param[in] key rsakey of type #rsakey_t
+ * @param[in] subject DER-coded subject distinguished name
+ * @param[in] challengePassword challenge password or empty_chunk
+ * @param[in] subjectAltNames linked list of subjectAltNames or NULL
+ * @return pointer to a #pkcs10_t object
+ */
+pkcs10_t*
+pkcs10_build(RSA_private_key_t *key, chunk_t subject, chunk_t challengePassword
+, generalName_t *subjectAltNames, int signature_alg)
+{
+ pkcs10_t *pkcs10 = alloc_thing(pkcs10_t, "pkcs10_t");
+
+ pkcs10->subject = subject;
+ pkcs10->private_key = key;
+ pkcs10->challengePassword = challengePassword;
+ pkcs10->subjectAltNames = subjectAltNames;
+
+ pkcs10->request = pkcs10_build_request(pkcs10, signature_alg);
+ return pkcs10;
+}
+
+/**
+ * @brief Frees the resources used by an #pkcs10_t object
+ *
+ * @param[in] pkcs10 #pkcs10_t to free
+ */
+void
+pkcs10_free(pkcs10_t *pkcs10)
+{
+ if (pkcs10 != NULL)
+ {
+ freeanychunk(pkcs10->request);
+ pfree(pkcs10);
+ }
+}
diff --git a/programs/scepclient/pkcs10.h b/programs/scepclient/pkcs10.h
new file mode 100644
index 000000000..c2a4c1b92
--- /dev/null
+++ b/programs/scepclient/pkcs10.h
@@ -0,0 +1,57 @@
+/**
+ * @file pkcs10.h
+ * @brief Functions to build PKCS#10 Request's
+ *
+ * Contains functions to build DER encoded pkcs#10 certificate requests
+ */
+
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef _PKCS10_H
+#define _PKCS10_H
+
+#include "../pluto/defs.h"
+#include "../pluto/pkcs1.h"
+#include "../pluto/x509.h"
+
+typedef struct pkcs10_struct pkcs10_t;
+
+/**
+ * @brief type representating a pkcs#10 request.
+ *
+ * A pkcs#10 request contains a distinguished name, an optional
+ * challenge password, a public key and optional subjectAltNames.
+ *
+ * The RSA private key is needed to compute the signature of the given request
+ */
+struct pkcs10_struct {
+ RSA_private_key_t *private_key;
+ chunk_t request;
+ chunk_t subject;
+ chunk_t challengePassword;
+ generalName_t *subjectAltNames;
+};
+
+extern const pkcs10_t empty_pkcs10;
+
+extern void pkcs10_add_subjectAltName(generalName_t **subjectAltNames
+ , generalNames_t kind, char *value);
+extern pkcs10_t* pkcs10_build(RSA_private_key_t *key, chunk_t subject
+ , chunk_t challengePassword, generalName_t *subjectAltNames
+ , int signature_alg);
+extern void pkcs10_free(pkcs10_t *pkcs10);
+
+#endif /* _PKCS10_H */
diff --git a/programs/scepclient/rsakey.c b/programs/scepclient/rsakey.c
new file mode 100644
index 000000000..c4f26b286
--- /dev/null
+++ b/programs/scepclient/rsakey.c
@@ -0,0 +1,349 @@
+/**
+ * @file rsakey.c
+ * @brief Functions for RSA key generation
+ */
+
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rsakey.c,v 1.5 2006/01/04 21:16:30 as Exp $
+ */
+
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <gmp.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/mp_defs.h"
+#include "../pluto/log.h"
+#include "../pluto/asn1.h"
+#include "../pluto/pkcs1.h"
+
+#include "rsakey.h"
+
+/* Number of times the probabilistic primality test is applied */
+#define PRIMECHECK_ROUNDS 30
+
+/* Public exponent used for signature key generation */
+#define PUBLIC_EXPONENT 0x10001
+
+#ifndef RANDOM_DEVICE
+#define RANDOM_DEVICE "/dev/random"
+#endif
+
+
+/**
+ * @brief Reads a specific number of bytes from a given device/file
+ *
+ * @param[in] nbytes number of bytes to read from random device
+ * @param[out] buf pointer to buffer where to write the data in.
+ * size of buffer has to be at least nbytes.
+ * @return TRUE, if succeeded, FALSE otherwise
+ */
+
+static bool
+get_true_random_bytes(size_t nbytes, char *buf)
+{
+ size_t ndone;
+ size_t got;
+ char *device = RANDOM_DEVICE;
+
+ int dev = open(RANDOM_DEVICE, 0);
+
+ if (dev < 0)
+ {
+ fprintf(stderr, "could not open random device %s", device);
+ return FALSE;
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("getting %d bytes from %s...", (int) nbytes, device)
+ )
+
+ ndone = 0;
+ while (ndone < nbytes)
+ {
+ got = read(dev, buf + ndone, nbytes - ndone);
+ if (got < 0)
+ {
+ fprintf(stderr, "read error on %s", device);
+ return FALSE;
+ }
+ if (got == 0)
+ {
+ fprintf(stderr, "eof on %s", device);
+ return FALSE;
+ }
+ ndone += got;
+ }
+ close(dev);
+ return TRUE;
+}
+
+/**
+ * @brief initialize an mpz_t to a random number, specified bit count
+ *
+ * Converting the random value in a value of type mpz_t is done
+ * by creating a hexbuffer.
+ * Converting via hex is a bit weird, but it's the best route GMP gives us.
+ * Note that highmost and lowmost bits are forced on -- highmost to give a
+ * number of exactly the specified length, lowmost so it is an odd number.
+ *
+ * @param[out] var uninitialized mpz_t to store th random number in
+ * @param[in] nbits length of var in bits (known to be a multiple of BITS_PER_BYTE)
+ * @return TRUE on success, FALSE otherwise
+ */
+static bool
+init_random(mpz_t var, int nbits)
+{
+ size_t nbytes = (size_t)(nbits/BITS_PER_BYTE);
+ char random_buf[RSA_MAX_OCTETS/2];
+
+ assert(nbytes <= sizeof(random_buf));
+
+ if (!get_true_random_bytes(nbytes, random_buf))
+ return FALSE;
+
+ random_buf[0] |= 01 << (BITS_PER_BYTE-1); /* force high bit on */
+ random_buf[nbytes-1] |= 01; /* force low bit on */
+ n_to_mpz(var, random_buf, nbytes);
+ return TRUE;
+}
+
+/**
+ * @brief initialize an mpz_t to a random prime of specified size
+ *
+ * Efficiency tweak: we reject candidates that are 1 higher than a multiple
+ * of e, since they will make the internal modulus not relatively prime to e.
+ *
+ * @param[out] var mpz_t variable to initialize
+ * @param[in] nbits length of given prime in bits (known to be a multiple of BITS_PER_BYTE)
+ * @param[in] eval E-Value, 0 means don't bother w. tweak
+ * @return 1 on success, 0 otherwise
+ */
+static bool
+init_prime(mpz_t var, int nbits, int eval)
+{
+ unsigned long tries;
+ size_t len;
+
+ /* get a random value of nbits length */
+ if (!init_random(var, nbits))
+ return FALSE;
+
+ /* check if odd number */
+ assert(mpz_fdiv_ui(var, 2) == 1);
+ DBG(DBG_CONTROLMORE,
+ DBG_log("looking for a prime starting there (can take a while)...")
+ )
+
+ tries = 1;
+ while (mpz_fdiv_ui(var, eval) == 1
+ || !mpz_probab_prime_p(var, PRIMECHECK_ROUNDS))
+ {
+ /* not a prime, increase by 2 */
+ mpz_add_ui(var, var, 2);
+ tries++;
+ }
+
+ len = mpz_sizeinbase(var, 2);
+
+ /* check bit length of primee */
+ assert(len == (size_t)nbits || len == (size_t)(nbits+1));
+
+ if (len == (size_t)(nbits+1))
+ {
+ DBG(DBG_CONTROLMORE,
+ DBG_log("carry out occurred (!), retrying...")
+ )
+ mpz_clear(var);
+ /* recursive call */
+ return init_prime(var, nbits, eval);
+ }
+ DBG(DBG_CONTROLMORE,
+ DBG_log("found it after %lu tries.",tries)
+ )
+ return TRUE;
+}
+
+/**
+ * @brief Generate a RSA key usable for encryption
+ *
+ * Generate an RSA key usable for encryption. All the
+ * values of the RSA key are filled into mpz_t parameters.
+ * These mpz_t parameters must not be initialized and have
+ * to be cleared with mpz_clear after using.
+ *
+ * @param[in] nbits size of rsa key in bits
+ * @return RSA_public_key_t containing the generated RSA key
+ */
+err_t
+generate_rsa_private_key(int nbits, RSA_private_key_t *key)
+{
+ mpz_t p, q, n, e, d, exp1, exp2, coeff;
+ mpz_t m, q1, t; /* temporary variables*/
+
+ DBG(DBG_CONTROL,
+ DBG_log("generating %d bit RSA key:", nbits)
+ )
+
+ if (nbits <= 0)
+ return "negative rsa key length!";
+
+ /* Get values of primes p and q */
+ DBG(DBG_CONTROLMORE,
+ DBG_log("initialize prime p")
+ )
+ if (!init_prime(p, nbits/2, PUBLIC_EXPONENT))
+ return "could not generate prime p";
+
+ DBG(DBG_CONTROLMORE,
+ DBG_log("initialize prime q")
+ )
+ if (!init_prime(q, nbits/2, PUBLIC_EXPONENT))
+ return "could not generate prime q";
+
+ mpz_init(t);
+
+ /* Swapping primes so p is larger then q */
+ if (mpz_cmp(p, q) < 0)
+ {
+ DBG(DBG_CONTROLMORE,
+ DBG_log("swapping primes so p is the larger...")
+ );
+ mpz_set(t, p);
+ mpz_set(p, q);
+ mpz_set(q, t);
+ }
+
+ DBG(DBG_CONTROLMORE,
+ DBG_log("computing modulus...")
+ )
+ mpz_init(n);
+ /* n = p*q */
+ mpz_mul(n, p, q);
+
+ /* Assign e the value of defined PUBLIC_EXPONENT */
+ mpz_init_set_ui(e, PUBLIC_EXPONENT);
+
+ DBG(DBG_CONTROLMORE,
+ DBG_log("computing lcm(p-1, q-1)...")
+ )
+ /* m = p */
+ mpz_init_set(m, p);
+ /* m = m-1 */
+ mpz_sub_ui(m, m, 1);
+ /* q1 = q */
+ mpz_init_set(q1, q);
+ /* q1 = q1-1 */
+ mpz_sub_ui(q1, q1, 1);
+ /* t = gcd(p-1, q-1) */
+ mpz_gcd(t, m, q1);
+ /* m = (p-1)*(q-1) */
+ mpz_mul(m, m, q1);
+ /* m = m / t */
+ mpz_divexact(m, m, t);
+ /* t = gcd(m, e) (greatest common divisor) */
+ mpz_gcd(t, m, e);
+ /* m and e relatively prime */
+ assert(mpz_cmp_ui(t, 1) == 0);
+
+ /* decryption key */
+ DBG(DBG_CONTROLMORE,
+ DBG_log("computing d...")
+ )
+ mpz_init(d);
+ /* e has an inverse mod m */
+ assert(mpz_invert(d, e, m));
+
+ /* make sure d is positive */
+ if (mpz_cmp_ui(d, 0) < 0)
+ mpz_add(d, d, m);
+
+ /* d has to be positive */
+ assert(mpz_cmp(d, m) < 0);
+
+ /* the speedup hacks */
+ DBG(DBG_CONTROLMORE,
+ DBG_log("computing exp1, exp1, coeff...")
+ )
+ mpz_init(exp1);
+ /* t = p-1 */
+ mpz_sub_ui(t, p, 1);
+ /* exp1 = d mod p-1 */
+ mpz_mod(exp1, d, t);
+
+ mpz_init(exp2);
+ /* t = q-1 */
+ mpz_sub_ui(t, q, 1);
+ /* exp2 = d mod q-1 */
+ mpz_mod(exp2, d, t);
+
+ mpz_init(coeff);
+ /* coeff = q^-1 mod p */
+ mpz_invert(coeff, q, p);
+
+ /* make sure coeff is positive */
+ if (mpz_cmp_ui(coeff, 0) < 0)
+ mpz_add(coeff, coeff, p);
+
+ /* coeff has to be positive */
+ assert(mpz_cmp(coeff, p) < 0);
+
+ /* Clear temporary variables */
+ mpz_clear(q1);
+ mpz_clear(m);
+ mpz_clear(t);
+
+ /* form FreeS/WAN keyid */
+ {
+ size_t e_len = (mpz_sizeinbase(e,2)+BITS_PER_BYTE-1)/BITS_PER_BYTE;
+ size_t n_len = (mpz_sizeinbase(n,2)+BITS_PER_BYTE-1)/BITS_PER_BYTE;
+ chunk_t e_ch = mpz_to_n(e, e_len);
+ chunk_t n_ch = mpz_to_n(n, n_len);
+ form_keyid(e_ch, n_ch, key->pub.keyid, &key->pub.k);
+ freeanychunk(e_ch);
+ freeanychunk(n_ch);
+ }
+ /* fill in the elements of the RSA private key */
+ key->p = *p;
+ key->q = *q;
+ key->pub.n = *n;
+ key->pub.e = *e;
+ key->d = *d;
+ key->dP = *exp1;
+ key->dQ = *exp2;
+ key->qInv = *coeff;
+
+ DBG(DBG_CONTROL,
+ DBG_log("RSA key *%s generated with %d bits", key->pub.keyid
+ , (int)mpz_sizeinbase(n,2))
+ )
+
+#ifdef DEBUG
+ DBG(DBG_PRIVATE,
+ RSA_show_private_key(key)
+ )
+#endif
+ return NULL;
+}
diff --git a/programs/scepclient/rsakey.h b/programs/scepclient/rsakey.h
new file mode 100644
index 000000000..3e3156d81
--- /dev/null
+++ b/programs/scepclient/rsakey.h
@@ -0,0 +1,31 @@
+/**
+ * @file rsakey.h
+ * @brief Functions for RSA key generation
+ */
+
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: rsakey.h,v 1.2 2005/08/11 21:52:56 as Exp $
+ */
+
+#ifndef RSAKEY_H_
+#define RSAKEY_H_
+
+#include "../pluto/pkcs1.h"
+
+extern err_t generate_rsa_private_key(int nbits, RSA_private_key_t *key);
+
+#endif // RSAKEY_H_
diff --git a/programs/scepclient/scep.c b/programs/scepclient/scep.c
new file mode 100644
index 000000000..577191787
--- /dev/null
+++ b/programs/scepclient/scep.c
@@ -0,0 +1,598 @@
+/**
+ * @file scep.c
+ * @brief SCEP specific functions
+ *
+ * Contains functions to build SCEP request's and to parse SCEP reply's.
+ */
+
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 <string.h>
+#include <stdlib.h>
+
+#include <freeswan.h>
+
+#ifdef LIBCURL
+#include <curl/curl.h>
+#endif
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/rnd.h"
+#include "../pluto/oid.h"
+#include "../pluto/asn1.h"
+#include "../pluto/pkcs1.h"
+#include "../pluto/fetch.h"
+#include "../pluto/log.h"
+
+#include "scep.h"
+
+static char ASN1_messageType_oid_str[] = {
+ 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x02
+};
+
+static char ASN1_senderNonce_oid_str[] = {
+ 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x05
+};
+
+static char ASN1_transId_oid_str[] = {
+ 0x06, 0x0A, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x45, 0x01, 0x09, 0x07
+};
+
+static const chunk_t ASN1_messageType_oid =
+ strchunk(ASN1_messageType_oid_str);
+static const chunk_t ASN1_senderNonce_oid =
+ strchunk(ASN1_senderNonce_oid_str);
+static const chunk_t ASN1_transId_oid =
+ strchunk(ASN1_transId_oid_str);
+
+static const char *pkiStatus_values[] = { "0", "2", "3" };
+
+static const char *pkiStatus_names[] = {
+ "SUCCESS",
+ "FAILURE",
+ "PENDING",
+ "UNKNOWN"
+};
+
+static const char *msgType_values[] = { "3", "19", "20", "21", "22" };
+
+static const char *msgType_names[] = {
+ "CertRep",
+ "PKCSReq",
+ "GetCertInitial",
+ "GetCert",
+ "GetCRL",
+ "Unknown"
+};
+
+static const char *failInfo_reasons[] = {
+ "badAlg - unrecognized or unsupported algorithm identifier",
+ "badMessageCheck - integrity check failed",
+ "badRequest - transaction not permitted or supported",
+ "badTime - Message time field was not sufficiently close to the system time",
+ "badCertId - No certificate could be identified matching the provided criteria"
+};
+
+const scep_attributes_t empty_scep_attributes = {
+ SCEP_Unknown_MSG , /* msgType */
+ SCEP_UNKNOWN , /* pkiStatus */
+ SCEP_unknown_REASON, /* failInfo */
+ { NULL, 0 } , /* transID */
+ { NULL, 0 } , /* senderNonce */
+ { NULL, 0 } , /* recipientNonce */
+};
+
+/* ASN.1 definition of the X.501 atttribute type */
+
+static const asn1Object_t attributesObjects[] = {
+ { 0, "attributes", ASN1_SET, ASN1_LOOP }, /* 0 */
+ { 1, "attribute", ASN1_SEQUENCE, ASN1_NONE }, /* 1 */
+ { 2, "type", ASN1_OID, ASN1_BODY }, /* 2 */
+ { 2, "values", ASN1_SET, ASN1_LOOP }, /* 3 */
+ { 3, "value", ASN1_EOC, ASN1_RAW }, /* 4 */
+ { 2, "end loop", ASN1_EOC, ASN1_END }, /* 5 */
+ { 0, "end loop", ASN1_EOC, ASN1_END }, /* 6 */
+};
+
+#define ATTRIBUTE_OBJ_TYPE 2
+#define ATTRIBUTE_OBJ_VALUE 4
+#define ATTRIBUTE_OBJ_ROOF 7
+
+/*
+ * extract and store an attribute
+ */
+static bool
+extract_attribute(int oid, chunk_t object, u_int level
+, scep_attributes_t *attrs)
+{
+ asn1_t type = ASN1_EOC;
+ const char *name = "none";
+
+ switch (oid)
+ {
+ case OID_PKCS9_CONTENT_TYPE:
+ type = ASN1_OID;
+ name = "contentType";
+ break;
+ case OID_PKCS9_SIGNING_TIME:
+ type = ASN1_UTCTIME;
+ name = "signingTime";
+ break;
+ case OID_PKCS9_MESSAGE_DIGEST:
+ type = ASN1_OCTET_STRING;
+ name = "messageDigest";
+ break;
+ case OID_PKI_MESSAGE_TYPE:
+ type = ASN1_PRINTABLESTRING;
+ name = "messageType";
+ break;
+ case OID_PKI_STATUS:
+ type = ASN1_PRINTABLESTRING;
+ name = "pkiStatus";
+ break;
+ case OID_PKI_FAIL_INFO:
+ type = ASN1_PRINTABLESTRING;
+ name = "failInfo";
+ break;
+ case OID_PKI_SENDER_NONCE:
+ type = ASN1_OCTET_STRING;
+ name = "senderNonce";
+ break;
+ case OID_PKI_RECIPIENT_NONCE:
+ type = ASN1_OCTET_STRING;
+ name = "recipientNonce";
+ break;
+ case OID_PKI_TRANS_ID:
+ type = ASN1_PRINTABLESTRING;
+ name = "transID";
+ break;
+ default:
+ break;
+ }
+
+ if (type == ASN1_EOC)
+ return TRUE;
+
+ if (!parse_asn1_simple_object(&object, type, level+1, name))
+ return FALSE;
+
+ switch (oid)
+ {
+ case OID_PKCS9_CONTENT_TYPE:
+ break;
+ case OID_PKCS9_SIGNING_TIME:
+ break;
+ case OID_PKCS9_MESSAGE_DIGEST:
+ break;
+ case OID_PKI_MESSAGE_TYPE:
+ {
+ scep_msg_t m;
+
+ for (m = SCEP_CertRep_MSG; m < SCEP_Unknown_MSG; m++)
+ {
+ if (strncmp(msgType_values[m], object.ptr, object.len) == 0)
+ attrs->msgType = m;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("messageType: %s", msgType_names[attrs->msgType])
+ )
+ }
+ break;
+ case OID_PKI_STATUS:
+ {
+ pkiStatus_t s;
+
+ for (s = SCEP_SUCCESS; s < SCEP_UNKNOWN; s++)
+ {
+ if (strncmp(pkiStatus_values[s], object.ptr, object.len) == 0)
+ attrs->pkiStatus = s;
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("pkiStatus: %s", pkiStatus_names[attrs->pkiStatus])
+ )
+ }
+ break;
+ case OID_PKI_FAIL_INFO:
+ if (object.len == 1
+ && *object.ptr >= '0' && *object.ptr <= '4')
+ {
+ attrs->failInfo = (failInfo_t)(*object.ptr - '0');
+ }
+ if (attrs->failInfo != SCEP_unknown_REASON)
+ plog("failInfo: %s", failInfo_reasons[attrs->failInfo]);
+ break;
+ case OID_PKI_SENDER_NONCE:
+ attrs->senderNonce = object;
+ break;
+ case OID_PKI_RECIPIENT_NONCE:
+ attrs->recipientNonce = object;
+ break;
+ case OID_PKI_TRANS_ID:
+ attrs->transID = object;
+ }
+ return TRUE;
+}
+
+/*
+ * parse X.501 attributes
+ */
+bool
+parse_attributes(chunk_t blob, scep_attributes_t *attrs)
+{
+ asn1_ctx_t ctx;
+ chunk_t object;
+ u_int level;
+ int oid = OID_UNKNOWN;
+ int objectID = 0;
+
+ asn1_init(&ctx, blob, 0, FALSE, DBG_RAW);
+
+ DBG(DBG_CONTROL | DBG_PARSING,
+ DBG_log("parsing attributes")
+ )
+ while (objectID < ATTRIBUTE_OBJ_ROOF)
+ {
+ if (!extract_object(attributesObjects, &objectID
+ , &object, &level, &ctx))
+ return FALSE;
+
+ switch (objectID)
+ {
+ case ATTRIBUTE_OBJ_TYPE:
+ oid = known_oid(object);
+ break;
+ case ATTRIBUTE_OBJ_VALUE:
+ if (!extract_attribute(oid, object, level, attrs))
+ return FALSE;
+ }
+ objectID++;
+ }
+ return TRUE;
+}
+
+/* generates a unique fingerprint of the pkcs10 request
+ * by computing an MD5 hash over it
+ */
+void
+scep_generate_pkcs10_fingerprint(chunk_t pkcs10, chunk_t *fingerprint)
+{
+ char buf[MD5_DIGEST_SIZE];
+ chunk_t digest = { buf, sizeof(buf) };
+
+ /* the fingerprint is the MD5 hash in hexadecimal format */
+ compute_digest(pkcs10, OID_MD5, &digest);
+ fingerprint->len = 2*digest.len;
+ fingerprint->ptr = alloc_bytes(fingerprint->len + 1, "fingerprint");
+ datatot(digest.ptr, digest.len, 16, fingerprint->ptr, fingerprint->len + 1);
+}
+
+/* generate a transaction id as the MD5 hash of an public key
+ * the transaction id is also used as a unique serial number
+ */
+void
+scep_generate_transaction_id(const RSA_public_key_t *rsak
+, chunk_t *transID, chunk_t *serialNumber)
+{
+ char buf[MD5_DIGEST_SIZE];
+
+ chunk_t digest = { buf, sizeof(buf) };
+ chunk_t public_key = pkcs1_build_publicKeyInfo(rsak);
+
+ bool msb_set;
+ u_char *pos;
+
+ compute_digest(public_key, OID_MD5, &digest);
+ pfree(public_key.ptr);
+
+ /* is the most significant bit of the digest set? */
+ msb_set = (*digest.ptr & 0x80) == 0x80;
+
+ /* allocate space for the serialNumber */
+ serialNumber->len = msb_set + digest.len;
+ serialNumber->ptr = alloc_bytes(serialNumber->len, "serialNumber");
+
+ /* the serial number as the two's complement of the digest */
+ pos = serialNumber->ptr;
+ if (msb_set)
+ {
+ *pos++ = 0x00;
+ }
+ memcpy(pos, digest.ptr, digest.len);
+
+ /* the transaction id is the serial number in hex format */
+ transID->len = 2*digest.len;
+ transID->ptr = alloc_bytes(transID->len + 1, "transID");
+ datatot(digest.ptr, digest.len, 16, transID->ptr, transID->len + 1);
+}
+
+/*
+ * builds a transId attribute
+ */
+chunk_t
+scep_transId_attribute(chunk_t transID)
+{
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_transId_oid
+ , asn1_wrap(ASN1_SET, "m"
+ , asn1_simple_object(ASN1_PRINTABLESTRING, transID)
+ )
+ );
+}
+
+/*
+ * builds a messageType attribute
+ */
+chunk_t
+scep_messageType_attribute(scep_msg_t m)
+{
+ chunk_t msgType = {
+ msgType_values[m],
+ strlen(msgType_values[m])
+ };
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_messageType_oid
+ , asn1_wrap(ASN1_SET, "m"
+ , asn1_simple_object(ASN1_PRINTABLESTRING, msgType)
+ )
+ );
+}
+
+/*
+ * builds a senderNonce attribute
+ */
+chunk_t
+scep_senderNonce_attribute(void)
+{
+ const size_t nonce_len = 16;
+ u_char nonce_buf[nonce_len];
+ chunk_t senderNonce = { nonce_buf, nonce_len };
+
+ get_rnd_bytes(nonce_buf, nonce_len);
+
+ return asn1_wrap(ASN1_SEQUENCE, "cm"
+ , ASN1_senderNonce_oid
+ , asn1_wrap(ASN1_SET, "m"
+ , asn1_simple_object(ASN1_OCTET_STRING, senderNonce)
+ )
+ );
+}
+
+/*
+ * builds a pkcs7 enveloped and signed scep request
+ */
+chunk_t
+scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg
+, const x509cert_t *enc_cert, int enc_alg
+, const x509cert_t *signer_cert, int digest_alg
+, const RSA_private_key_t *private_key)
+{
+ chunk_t envelopedData, attributes, request;
+
+ envelopedData = pkcs7_build_envelopedData(data, enc_cert, enc_alg);
+
+ attributes = asn1_wrap(ASN1_SET, "mmmmm"
+ , pkcs7_contentType_attribute()
+ , pkcs7_messageDigest_attribute(envelopedData
+ , digest_alg)
+ , scep_transId_attribute(transID)
+ , scep_messageType_attribute(msg)
+ , scep_senderNonce_attribute());
+
+ request = pkcs7_build_signedData(envelopedData, attributes
+ , signer_cert, digest_alg, private_key);
+ freeanychunk(envelopedData);
+ freeanychunk(attributes);
+ return request;
+}
+
+#ifdef LIBCURL
+/* converts a binary request to base64 with 64 characters per line
+ * newline and '+' characters are escaped by %0A and %2B, respectively
+ */
+static char*
+escape_http_request(chunk_t req)
+{
+ char *escaped_req = NULL;
+ char *p1, *p2;
+ int lines = 0;
+ int plus = 0;
+ int n = 0;
+
+ /* compute and allocate the size of the base64-encoded request */
+ int len = 1 + 4*((req.len + 2)/3);
+ char *encoded_req = alloc_bytes(len, "encoded request");
+
+ /* do the base64 conversion */
+ len = datatot(req.ptr, req.len, 64, encoded_req, len);
+
+ /* compute newline characters to be inserted every 64 characters */
+ lines = (len - 2) / 64;
+
+ /* count number of + characters to be escaped */
+ p1 = encoded_req;
+ while (*p1 != '\0')
+ {
+ if (*p1++ == '+')
+ plus++;
+ }
+
+ escaped_req = alloc_bytes(len + 3*(lines + plus), "escaped request");
+
+ /* escape special characters in the request */
+ p1 = encoded_req;
+ p2 = escaped_req;
+ while (*p1 != '\0')
+ {
+ if (n == 64)
+ {
+ memcpy(p2, "%0A", 3);
+ p2 += 3;
+ n = 0;
+ }
+ if (*p1 == '+')
+ {
+ memcpy(p2, "%2B", 3);
+ p2 += 3;
+ }
+ else
+ {
+ *p2++ = *p1;
+ }
+ p1++;
+ n++;
+ }
+ *p2 = '\0';
+ pfreeany(encoded_req);
+ return escaped_req;
+}
+#endif
+
+/*
+ * send a SCEP request via HTTP and wait for a response
+ */
+bool
+scep_http_request(const char *url, chunk_t pkcs7, scep_op_t op
+, fetch_request_t req_type, chunk_t *response)
+{
+#ifdef LIBCURL
+ char errorbuffer[CURL_ERROR_SIZE] = "";
+ char *complete_url = NULL;
+ struct curl_slist *headers = NULL;
+ CURL *curl;
+ CURLcode res;
+
+ /* initialize response */
+ *response = empty_chunk;
+
+ /* initialize curl context */
+ curl = curl_easy_init();
+ if (curl == NULL)
+ {
+ plog("could not initialize curl context");
+ return FALSE;
+ }
+
+ if (op == SCEP_PKI_OPERATION)
+ {
+ const char operation[] = "PKIOperation";
+
+ if (req_type == FETCH_GET)
+ {
+ char *escaped_req = escape_http_request(pkcs7);
+
+ /* form complete url */
+ int len = strlen(url) + 20 + strlen(operation) + strlen(escaped_req) + 1;
+
+ complete_url = alloc_bytes(len, "complete url");
+ snprintf(complete_url, len, "%s?operation=%s&message=%s"
+ , url, operation, escaped_req);
+ pfreeany(escaped_req);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, TRUE);
+ headers = curl_slist_append(headers, "Pragma:");
+ headers = curl_slist_append(headers, "Host:");
+ headers = curl_slist_append(headers, "Accept:");
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+ }
+ else /* HTTP_POST */
+ {
+ /* form complete url */
+ int len = strlen(url) + 11 + strlen(operation) + 1;
+
+ complete_url = alloc_bytes(len, "complete url");
+ snprintf(complete_url, len, "%s?operation=%s", url, operation);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, FALSE);
+ headers = curl_slist_append(headers, "Content-Type:");
+ headers = curl_slist_append(headers, "Expect:");
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, pkcs7.ptr);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, pkcs7.len);
+ }
+ }
+ else /* SCEP_GET_CA_CERT */
+ {
+ const char operation[] = "GetCACert";
+
+ /* form complete url */
+ int len = strlen(url) + 32 + strlen(operation) + 1;
+
+ complete_url = alloc_bytes(len, "complete url");
+ snprintf(complete_url, len, "%s?operation=%s&message=CAIdentifier"
+ , url, operation);
+
+ curl_easy_setopt(curl, CURLOPT_HTTPGET, TRUE);
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, complete_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);
+
+ DBG(DBG_CONTROL,
+ DBG_log("sending scep request to '%s'", url)
+ )
+ res = curl_easy_perform(curl);
+
+ if (res == CURLE_OK)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("received scep response")
+ )
+ DBG(DBG_RAW,
+ DBG_dump_chunk("SCEP response:\n", *response)
+ )
+ }
+ else
+ {
+ plog("failed to fetch scep response from '%s': %s", url, errorbuffer);
+ }
+ curl_slist_free_all(headers);
+ curl_easy_cleanup(curl);
+ pfreeany(complete_url);
+
+ return (res == CURLE_OK);
+#else /* !LIBCURL */
+ plog("scep error: pluto wasn't compiled with libcurl support");
+ return FALSE;
+#endif /* !LIBCURL */
+}
+
+err_t
+scep_parse_response(chunk_t response, chunk_t transID, contentInfo_t *data
+, scep_attributes_t *attrs, x509cert_t *signer_cert)
+{
+ chunk_t attributes;
+
+ if (!pkcs7_parse_signedData(response, data, NULL, &attributes, signer_cert))
+ {
+ return "error parsing the scep response";
+ }
+ if (!parse_attributes(attributes, attrs))
+ {
+ return "error parsing the scep response attributes";
+ }
+ if (!same_chunk(transID, attrs->transID))
+ {
+ return "transaction ID of scep response does not match";
+ }
+ return NULL;
+}
diff --git a/programs/scepclient/scep.h b/programs/scepclient/scep.h
new file mode 100644
index 000000000..81e5d1a4b
--- /dev/null
+++ b/programs/scepclient/scep.h
@@ -0,0 +1,93 @@
+/**
+ * @file scep.h
+ * @brief SCEP specific functions
+ *
+ * Contains functions to build and parse SCEP requests and replies
+ */
+
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef _SCEP_H
+#define _SCEP_H
+
+#include "../pluto/defs.h"
+#include "../pluto/pkcs1.h"
+#include "../pluto/pkcs7.h"
+
+/* supported SCEP operation types */
+typedef enum {
+ SCEP_PKI_OPERATION,
+ SCEP_GET_CA_CERT
+} scep_op_t;
+
+/* SCEP pkiStatus values */
+typedef enum {
+ SCEP_SUCCESS,
+ SCEP_FAILURE,
+ SCEP_PENDING,
+ SCEP_UNKNOWN
+} pkiStatus_t;
+
+/* SCEP messageType values */
+typedef enum {
+ SCEP_CertRep_MSG,
+ SCEP_PKCSReq_MSG,
+ SCEP_GetCertInitial_MSG,
+ SCEP_GetCert_MSG,
+ SCEP_GetCRL_MSG,
+ SCEP_Unknown_MSG
+} scep_msg_t;
+
+/* SCEP failure reasons */
+typedef enum {
+ SCEP_badAlg_REASON = 0,
+ SCEP_badMessageCheck_REASON = 1,
+ SCEP_badRequest_REASON = 2,
+ SCEP_badTime_REASON = 3,
+ SCEP_badCertId_REASON = 4,
+ SCEP_unknown_REASON = 5
+} failInfo_t;
+
+/* SCEP attributes */
+typedef struct {
+ scep_msg_t msgType;
+ pkiStatus_t pkiStatus;
+ failInfo_t failInfo;
+ chunk_t transID;
+ chunk_t senderNonce;
+ chunk_t recipientNonce;
+} scep_attributes_t;
+
+extern const scep_attributes_t empty_scep_attributes;
+
+extern bool parse_attributes(chunk_t blob, scep_attributes_t *attrs);
+extern void scep_generate_pkcs10_fingerprint(chunk_t pkcs10
+ , chunk_t *fingerprint);
+extern void scep_generate_transaction_id(const RSA_public_key_t *rsak
+ , chunk_t *transID, chunk_t *serialNumber);
+extern chunk_t scep_transId_attribute(chunk_t transaction_id);
+extern chunk_t scep_messageType_attribute(scep_msg_t m);
+extern chunk_t scep_senderNonce_attribute(void);
+extern chunk_t scep_build_request(chunk_t data, chunk_t transID, scep_msg_t msg
+ , const x509cert_t *enc_cert, int enc_alg
+ , const x509cert_t *signer_cert, int digest_alg
+ , const RSA_private_key_t *private_key);
+extern bool scep_http_request(const char *url, chunk_t pkcs7, scep_op_t op
+ , fetch_request_t request_type, chunk_t *response);
+extern err_t scep_parse_response(chunk_t response, chunk_t transID
+ , contentInfo_t *data, scep_attributes_t *attrs, x509cert_t *signer_cert);
+
+#endif /* _SCEP_H */
diff --git a/programs/scepclient/scepclient.8 b/programs/scepclient/scepclient.8
new file mode 100644
index 000000000..0d6364ef2
--- /dev/null
+++ b/programs/scepclient/scepclient.8
@@ -0,0 +1,288 @@
+.\"
+.TH "IPSEC_SCEPCLIENT" "8" "29 September 2005" "Jan Hutter, Martin Willi" ""
+.SH "NAME"
+ipsec scepclient \- Client for the SCEP protocol
+.SH "SYNOPSIS"
+.B ipsec scepclient [argument ...]
+.sp
+.B ipsec scepclient
+.B \-\-help
+.br
+.B ipsec scepclient
+.B \-\-version
+.SH "DESCRIPTION"
+.BR scepclient
+is a client implementation of Cisco System's Simple Certificate Enrollment Protocol (SCEP) written for Linux strongSwan <http://www.strongswan.org>.
+.BR scepclient
+is designed to be used for certificate enrollment on machines using the OpenSource IPsec solution
+.I strongSwan.
+.SH "FEATURES"
+.BR scepclient
+implements the following features of SCEP:
+.br
+.IP "\-" 4
+Automatic enrollment of client certificate using a preshared secret
+.IP "\-" 4
+Manual enrollment of client certificate. Offline fingerprint check required!
+.IP "\-" 4
+Acquisition of CA certificate(s)
+.SH "OPTIONS"
+.SS Basic Startup Options
+.B \-v, \-\-version
+.RS 4
+Display the version of ipsec scepclient.
+.PP
+.RE
+.B \-h, \-\-help
+.RS 4
+Display usage of ipsec scepclient.
+.RE
+
+.SS General Options
+.B \-u, \-\-url \fIurl\fP
+.RS 4
+Full HTTP URL of the SCEP server to be used for certificate enrollment and CA certificate acquisition.
+.RE
+.PP
+.B \-+, \-\-optionsfrom \fIfilename\fP
+.RS 4
+Reads additional options from \fIfilename\fP.
+.RE
+.PP
+.B \-f, \-\-force
+.RS 4
+Overwrite existing output file[s].
+.RE
+.PP
+.B \-q, \-\-quiet
+.RS 4
+Do not write log output to stderr.
+.RE
+
+.SS Options for CA Certificate Acquisition
+.B \-o, \-\-out cacert[=\fIfilename\fP]
+.RS 4
+Output file of acquired CA certificate. If more then one CA certificate is available, \fIfilename\fP is used as prefix for the resulting files.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
+.RE
+
+.SS Options For Certificate Enrollment
+.B \-i, \-\-in \fItype\fP[=\fIfilename\fP]
+.RS 4
+Input file for certificate enrollment. This option can be specified multiple times to specify input files for every \fItype\fP.
+Input files can bei either DER or PEM encoded.
+.PP
+Supported values for \fItype\fP:
+.IP "\fBpkcs1\fP" 12
+RSA private key in PKCS#1 file format. If no input of this type is specified, a RSA key gets generated.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/private/myKey.der.
+.IP "\fBcacert\-enc\fP" 12
+CA certificate to encrypt the SCEP request. Has to be specified for certificate enrollment.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
+.IP "\fBcacert\-sig\fP" 12
+CA certificate to check signature of SCEP reply. Has to be specified for certificate enrollment.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/cacerts/caCert.der.
+.RE
+.PP
+.B \-k, \-\-keylength \fIbits\fP
+.RS 4
+sets the key length for RSA key generation. The default length for a generated rsa key is set to 2048 bit.
+.RE
+.PP
+.B \-D, \-\-days \fIdays\fP
+.RS 4
+Validity of the self-signed X.509 certificate in days. The default is 1825 days (5 years).
+.RE
+.PP
+.B \-S, \-\-startdate \fIYYMMDDHHMMSS\fPZ
+.RS 4
+defines the \fBnotBefore\fP date when the X.509 certificate becomes valid.
+The date has the format \fIYYMMDDHHMMSS\fP and must be specified in UTC (Zulu time).
+If the \fB--startdate\fP option is not specified then the current date is taken as a default.
+.RE
+.PP
+.B \-E, \-\-enddate \fIYYMMDDHHMMSS\fPZ
+.RS 4
+defines the \fBnotAfter\fP date when the X.509 certificate will expire.
+The date has the format \fIYYMMDDHHMMSS\fP and must be specified in UTC (Zulu time).
+If the \fB--enddate\fP option is not specified then the default \fBnotAfter\fP value is computed by
+adding the validity interval specified by the \fB--days\fP option to the \fBnotBefore\fP date.
+.RE
+.PP
+.B \-d, \-\-dn \fIdn\fP
+.RS 4
+Distinguished name as comma separated list of relative distinguished names. Use quotation marks for a distinguished name containing spaces. If the \fB\-\-dn\fP parameter is missing then the default "C=CH, O=Linux strongSwan, CN=\fIhostname\fP"
+is used with \fIhostname\fP being the return value of the \fIgethostname\fP() function.
+.RE
+.PP
+.B \-s, \-\-subjectAltName \fItype\fP=\fIvalue\fP
+.RS 4
+Include subjectAltName in certificate request. This option can be specified multiple times to specify a subjectAltName
+for every \fItype\fP.
+.PP
+Supported values for \fItype\fP:
+.IP "\fBemail\fP" 12
+subjectAltName is a email address.
+.IP "\fBdns\fP" 12
+subjectAltName is a hostname.
+.IP "\fBip\fP" 12
+subjectAltName is a IP address.
+.RE
+.PP
+.B \-p, \-\-password \fIpw\fP
+.RS 4
+Password to be included as a \fIchallenge password\fP in SCEP request.
+If \fIpw\fP is \fB%prompt\fP', the password gets prompted for on the command line.
+.IP
+\- In automatic mode, this password corresponds to the preshared secret for the given enrollment.
+.IP
+\- In manual mode, this password can be used to later revoke the corresponding certificate.
+.RE
+.PP
+.B \-a, \-\-algorithm \fIalgo\fP
+.RS 4
+Change symmetric algorithm to use for encryption of certificate Request.
+The default is \fB3des\-cbc\fP.
+.PP
+Supported values for \fIalgo\fP:
+.IP "\fBdes\-cbc\fP" 12
+DES CBC encryption (key size = 56 bit).
+.IP "\fB3des\-cbc\fP" 12
+Triple DES CBC encryption (key size = 168 bit).
+.RE
+.PP
+.B \-o, \-\-out \fItype\fP[=\fIfilename\fP]
+.RS 4
+Output file for certificate enrollment. This option can be specified multiple times to specify output files for every \fItype\fP.
+.PP
+Supported values for \fItype\fP:
+.IP "\fBpkcs1\fP" 12
+RSA private key in PKCS#1 file format. If specified, the RSA key used for enrollment is stored in file \fIfilename\fP.
+If none of the \fItypes\fP listed below are specified, \fBscepclient\fP will stop after outputting this file.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/private/myKey.der.
+.IP "\fBpkcs10\fP" 12
+PKCS#10 certificate request. If specified, the PKCS#10 request used or certificate enrollment is stored in file \fIfilename\fP.
+If none of the \fItypes\fP listed below are specified, \fBscepclient\fP will stop after outputting this file.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/req/myReq.der.
+.IP "\fBpkcs7\fP" 12
+PKCS#7 SCEP request as it is sent using HTTP to the SCEP server. If specified, this SCEP request is stored in file \fIfilename\fP.
+If none of \fItypes\fP listed below is not specified, \fBscepclient\fP will stop after outputting this file.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/req/pkcs7.der.
+.IP "\fBcert-self\fP" 12
+Self-signed certificate. If specified the self-signed certificate is stored in file \fIfilename\fP.
+.br
+The default \fIfilename\fP is $CONFDIR/ipsec.d/certs/selfCert.der.
+.IP "\fBcert\fP" 12
+Enrolled certificate. This \fItype\fP must be specified for certificate enrollment.
+The enrolled certificate is stored in file \fIfilename\fP.
+.br
+The default \fIfilename\fP is set to $CONFDIR/ipsec.d/certs/myCert.der.
+.RE
+.PP
+.B \-m, \-\-method \fImethod\fP
+.RS 4
+Change HTTP request method for certificate enrollment. Default is \fBget\fP.
+.PP
+Supported values for \fImethod\fP:
+.IP "\fBpost\fP" 12
+Certificate enrollment using HTTP POST. Must be supported by the given SCEP server.
+.IP "\fBget\fP" 12
+Certificate enrollment using HTTP GET.
+.RE
+.PP
+.B \-t, \-\-interval \fIseconds\fP
+.RS 4
+Set interval time in seconds when polling in manual mode.
+The default interval is set to 5 seconds.
+.RE
+.PP
+.B \-x, \-\-maxpolltime \fIseconds\fP
+.RS 4
+Set max time in seconds to poll in manual mode.
+The default max time is set to unlimited.
+.RE
+
+.SS Debugging Output Options:
+.B \-A, \-\-debug\-all
+.RS 4
+Log everything except private data.
+.RE
+.PP
+.B \-P, \-\-debug\-parsing
+.RS 4
+Log parsing relevant stuff.
+.RE
+.PP
+.B \-R, \-\-debug\-raw
+.RS 4
+Log raw hex dumps.
+.RE
+.PP
+.B \-C, \-\-debug\-control
+.RS 4
+Log informations about control flow.
+.RE
+.PP
+.B \-M, \-\-debug\-controlmore
+.RS 4
+Log more detailed informations about control flow.
+.RE
+.PP
+.B \-X, \-\-debug\-private
+.RS 4
+Log sensitive data (e.g. private keys).
+.RE
+.SH "EXAMPLES"
+.B ipsec scepclient \-\-out caCert \-\-url http://scepserver/cgi\-bin/pkiclient.exe \-f
+.RS 4
+Acquire CA certificate from SCEP server and store it in the default file $CONFDIR/ipsec.d/cacerts/caCert.der.
+If more then one CA certificate is returned, store them in files named caCert.der\-1', caCert.der\-2', etc.
+.br
+Existing files are overwritten.
+.RE
+.PP
+.B ipsec scepclient \-\-out pkcs1=joeKey.der \-k 1024
+.RS 4
+Generate RSA private key with key length of 1024 bit and store it in file joeKey.der.
+.RE
+.PP
+.B ipsec scepclient \-\-in pkcs1=joeKey.der \-\-out pkcs10=joeReq.der \e
+.br
+.B \-\-dn \*(rqC=AT, CN=John Doe\*(rq \-s email=john@doe.com \-p mypassword
+.RS 4
+Generate a PKCS#10 request and store it in file joeReq.der. Use the RSA private key joeKey.der
+created earlier to sign the PKCS#10\-Request. In addition to the distinguished name include a
+email\-subjectAltName and a challenge password in the request.
+.RE
+.PP
+.B ipsec scepclient \-\-out pkcs1=joeKey.der \-\-out cert==joeCert.der \e
+.br
+.B \-\-dn \*(rqC=CH, CN=John Doe\*(rq \-k 512 \-p 5xH2pnT7wq \e
+.br
+.B \-\-url http://scep.hsr.ch/cgi\-bin/pkiclient.exe \e
+.br
+.B \-\-in cacert\-enc=caCert.der \-\-in cacert\-sig=caCert.der
+.RS 4
+Generate a new RSA key for the request and store it in joeKey.der. Then enroll a certificate and store as joeCert.der.
+The challenge password is '5xH2pnT7wq'. The encryption and signature check has to be made with the same CA certificate
+caCert.der.
+.RE
+
+
+.SH "BUGS"
+\fB\-\-optionsfrom\fP seems to have parsing problems reading option files containing strings in quotation marks.
+.SH "COPYRIGHT"
+Copyright (C) 2005 Jan Hutter, Martin Willi
+.br
+Hochschule fuer Technik Rapperswil
+.PP
+This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+.PP
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
diff --git a/programs/scepclient/scepclient.c b/programs/scepclient/scepclient.c
new file mode 100644
index 000000000..bde460844
--- /dev/null
+++ b/programs/scepclient/scepclient.c
@@ -0,0 +1,1036 @@
+/*
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @file main.c
+ * @brief scepclient main program
+ */
+
+/**
+ * @mainpage SCEP for Linux strongSwan
+ *
+ * Documentation of SCEP for Linux StrongSwan
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <time.h>
+#include <gmp.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+#include "../pluto/oid.h"
+#include "../pluto/asn1.h"
+#include "../pluto/pkcs1.h"
+#include "../pluto/pkcs7.h"
+#include "../pluto/certs.h"
+#include "../pluto/fetch.h"
+#include "../pluto/rnd.h"
+
+#include "rsakey.h"
+#include "pkcs10.h"
+#include "scep.h"
+
+/*
+ * definition of some defaults
+ */
+
+/* default name of DER-encoded PKCS#1 private key file */
+#define DEFAULT_FILENAME_PKCS1 "myKey.der"
+
+/* default name of DER-encoded PKCS#10 certificate request file */
+#define DEFAULT_FILENAME_PKCS10 "myReq.der"
+
+/* default name of DER-encoded PKCS#7 file */
+#define DEFAULT_FILENAME_PKCS7 "pkcs7.der"
+
+/* default name of DER-encoded self-signed X.509 certificate file */
+#define DEFAULT_FILENAME_CERT_SELF "selfCert.der"
+
+/* default name of DER-encoded X.509 certificate file */
+#define DEFAULT_FILENAME_CERT "myCert.der"
+
+/* default name of DER-encoded CA cert file used for key encipherment */
+#define DEFAULT_FILENAME_CACERT_ENC "caCert.der"
+
+/* default name of the der encoded CA cert file used for signature verification */
+#define DEFAULT_FILENAME_CACERT_SIG "caCert.der"
+
+/* default prefix of the der encoded CA certificates received from the SCEP server */
+#define DEFAULT_FILENAME_PREFIX_CACERT "caCert.der"
+
+/* default certificate validity */
+#define DEFAULT_CERT_VALIDITY 5 * 3600 * 24 * 365 /* seconds */
+
+/* default polling time interval in SCEP manual mode */
+#define DEFAULT_POLL_INTERVAL 20 /* seconds */
+
+/* default key length for self-generated RSA keys */
+#define DEFAULT_RSA_KEY_LENGTH 2048 /* bits */
+
+/* default distinguished name */
+#define DEFAULT_DN "C=CH, O=Linux strongSwan, CN="
+
+/* challenge password buffer size */
+#define MAX_PASSWORD_LENGTH 256
+
+/* Max length of filename for tempfile */
+#define MAX_TEMP_FILENAME_LENGTH 256
+
+
+/* current scepclient version */
+static const char *scepclient_version = "1.0";
+
+/* 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;
+
+
+/*
+ * Global variables
+ */
+
+RSA_private_key_t *private_key = NULL;
+
+chunk_t pkcs1;
+chunk_t pkcs7;
+chunk_t subject;
+chunk_t challengePassword;
+chunk_t serialNumber;
+chunk_t transID;
+chunk_t fingerprint;
+chunk_t issuerAndSubject;
+chunk_t getCertInitial;
+chunk_t scep_response;
+cert_t cert;
+
+x509cert_t *x509_signer = NULL;
+x509cert_t *x509_ca_enc = NULL;
+x509cert_t *x509_ca_sig = NULL;
+generalName_t *subjectAltNames = NULL;
+pkcs10_t *pkcs10 = NULL;
+
+/**
+ * @brief exit scepclient
+ *
+ * The log is closed and leaks are reported
+ * if LEAK_DETECTIVE is activated
+ *
+ * @param status 0 = OK, 1 = general discomfort
+ */
+static void
+exit_scepclient(err_t message, ...)
+{
+ if (private_key != NULL)
+ {
+ free_RSA_private_content(private_key);
+ pfree(private_key);
+ }
+ freeanychunk(pkcs1);
+ freeanychunk(pkcs7);
+ freeanychunk(subject);
+ freeanychunk(serialNumber);
+ freeanychunk(transID);
+ freeanychunk(fingerprint);
+ freeanychunk(issuerAndSubject);
+ freeanychunk(getCertInitial);
+ if (scep_response.ptr != NULL)
+ free(scep_response.ptr);
+
+ free_generalNames(subjectAltNames, TRUE);
+ if (x509_signer != NULL)
+ x509_signer->subjectAltName = NULL;
+
+ free_x509cert(x509_signer);
+ free_x509cert(x509_ca_enc);
+ free_x509cert(x509_ca_sig);
+ pkcs10_free(pkcs10);
+
+#ifdef LEAK_DETECTIVE
+ report_leaks();
+#endif /* LEAK_DETECTIVE */
+ close_log();
+
+ /* print any error message to stderr */
+ if (message != NULL && *message != '\0')
+ {
+ 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, "error: %s\n", m);
+ exit(-1);
+ }
+ exit(0);
+}
+
+/**
+ * @brief prints the program version and exits
+ *
+ */
+static void
+version(void)
+{
+ printf("scepclient %s\n", scepclient_version);
+ exit_scepclient(NULL);
+}
+
+/**
+ * @brief prints the usage of the program to the stderr output
+ *
+ * If message is set, program is exitet with 1 (error)
+ * @param message message in case of an error
+ */
+static void
+usage(const char *message)
+{
+ fprintf(stderr,
+ "Usage: scepclient\n"
+ " --help (-h) show usage and exit\n"
+ " --version (-v) show version and exit\n"
+ " --quiet (-q) do not write log output to stderr\n"
+ " --in (-i) <type>[=<filename>] use <filename> of <type> for input \n"
+ " <type> = pkcs1 | cacert-enc | cacert-sig\n"
+ " - if no pkcs1 input is defined, a \n"
+ " RSA key will be generated\n"
+ " - if no filename is given, default is used\n"
+ " --out (-o) <type>[=<filename>] write output of <type> to <filename>\n"
+ " multiple outputs are allowed\n"
+ " <type> = pkcs1 | pkcs10 | pkcs7 | cert-self | cert | cacert\n"
+ " - type cacert defines filename prefix of\n"
+ " received CA certificate(s)\n"
+ " - if no filename is given, default is used\n"
+ " --optionsfrom (-+) <filename> reads additional options from given file\n"
+ " --force (-f) force existing file(s)\n"
+ "\n"
+ "Options for key generation (pkcs1):\n"
+ " --keylength (-k) <bits> key length for RSA key generation\n"
+ "(default: 2048 bits)\n"
+ "\n"
+ "Options for validity:\n"
+ " --days (-D) <days> validity in days\n"
+ " --startdate (-S) <YYMMDDHHMMSS>Z not valid before date\n"
+ " --enddate (-E) <YYMMDDHHMMSS>Z not valid after date\n"
+ "\n"
+ "Options for request generation (pkcs10):\n"
+ " --dn (-d) <dn> comma separated list of distinguished names\n"
+ " --subjectAltName (-s) <t>=<v> include subjectAltName in certificate request\n"
+ " <t> = email | dns | ip \n"
+ " --password (-p) <pw> challenge password\n"
+ " - if pw is '%%prompt', password gets prompted for\n"
+ " --algorithm (-a) <algo> use specified algorithm for PKCS#7 encryption\n"
+ " <algo> = des-cbc | 3des-cbc (default: 3des-cbc)\n"
+ "\n"
+ "Options for enrollment (cert):\n"
+ " --url (-u) <url> url of the SCEP server\n"
+ " --method (-m) post | get http request type\n"
+ " --interval (-t) <seconds> manual mode poll interval in seconds (default 20s)\n"
+ " --maxpolltime (-x) <seconds> max poll time in seconds when in manual mode\n"
+ " (default: unlimited)\n"
+#ifdef DEBUG
+ "\n"
+ "Debugging output:\n"
+ " --debug-all (-A) show everything except private\n"
+ " --debug-parsing (-P) show parsing relevant stuff\n"
+ " --debug-raw (-R) show raw hex dumps\n"
+ " --debug-control (-C) show control flow output\n"
+ " --debug-controlmore (-M) show more control flow\n"
+ " --debug-private (-X) show sensitive data (private keys, etc.)\n"
+#endif
+ );
+ exit_scepclient(message);
+}
+
+/**
+ * @brief main of scepclient
+ *
+ * @param argc number of arguments
+ * @param argv pointer to the argument values
+ */
+int main(int argc, char **argv)
+{
+ /* external values */
+ extern char * optarg;
+ extern int optind;
+
+ /* type of input and output files */
+ typedef enum {
+ PKCS1 = 0x01,
+ PKCS10 = 0x02,
+ PKCS7 = 0x04,
+ CERT_SELF = 0x08,
+ CERT = 0x10,
+ CACERT_ENC = 0x20,
+ CACERT_SIG = 0x40
+ } scep_filetype_t;
+
+ /* filetype to read from, defaults to "generate a key" */
+ scep_filetype_t filetype_in = 0;
+
+ /* filetype to write to, no default here */
+ scep_filetype_t filetype_out = 0;
+
+ /* input files */
+ char *file_in_pkcs1 = DEFAULT_FILENAME_PKCS1;
+ char *file_in_cacert_enc = DEFAULT_FILENAME_CACERT_ENC;
+ char *file_in_cacert_sig = DEFAULT_FILENAME_CACERT_SIG;
+
+ /* output files */
+ char *file_out_pkcs1 = DEFAULT_FILENAME_PKCS1;
+ char *file_out_pkcs10 = DEFAULT_FILENAME_PKCS10;
+ char *file_out_pkcs7 = DEFAULT_FILENAME_PKCS7;
+ char *file_out_cert_self = DEFAULT_FILENAME_CERT_SELF;
+ char *file_out_cert = DEFAULT_FILENAME_CERT;
+ char *file_out_prefix_cacert = DEFAULT_FILENAME_PREFIX_CACERT;
+
+ /* by default user certificate is requested */
+ bool request_ca_certificate = FALSE;
+
+ /* by default existing files are not overwritten */
+ bool force = FALSE;
+
+ /* length of RSA key in bits */
+ u_int rsa_keylength = DEFAULT_RSA_KEY_LENGTH;
+
+ /* validity of self-signed certificate */
+ time_t validity = DEFAULT_CERT_VALIDITY;
+ time_t notBefore = 0;
+ time_t notAfter = 0;
+
+ /* distinguished name for requested certificate, ASCII format */
+ char *distinguishedName = NULL;
+
+ /* challenge password */
+ char challenge_password_buffer[MAX_PASSWORD_LENGTH];
+
+ /* symmetric encryption algorithm used by pkcs7, default is 3DES */
+ int pkcs7_symmetric_cipher = OID_3DES_EDE_CBC;
+
+ /* digest algorithm used by pkcs7, default is MD5 */
+ int pkcs7_digest_alg = OID_MD5;
+
+ /* signature algorithm used by pkcs10, default is MD5 with RSA encryption */
+ int pkcs10_signature_alg = OID_MD5;
+
+ /* URL of the SCEP-Server */
+ char *scep_url = NULL;
+
+ /* http request method, default is GET */
+ fetch_request_t request_type = FETCH_GET;
+
+ /* poll interval time in manual mode in seconds */
+ u_int poll_interval = DEFAULT_POLL_INTERVAL;
+
+ /* maximum poll time */
+ u_int max_poll_time = 0;
+
+ err_t ugh = NULL;
+
+ /* initialize global variables */
+ pkcs1 = empty_chunk;
+ pkcs7 = empty_chunk;
+ serialNumber = empty_chunk;
+ transID = empty_chunk;
+ fingerprint = empty_chunk;
+ issuerAndSubject = empty_chunk;
+ challengePassword = empty_chunk;
+ getCertInitial = empty_chunk;
+ scep_response = empty_chunk;
+ log_to_stderr = TRUE;
+
+ for (;;)
+ {
+ 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' },
+ { "in", required_argument, NULL, 'i' },
+ { "out", required_argument, NULL, 'o' },
+ { "force", no_argument, NULL, 'f' },
+ { "keylength", required_argument, NULL, 'k' },
+ { "dn", required_argument, NULL, 'd' },
+ { "days", required_argument, NULL, 'D' },
+ { "startdate", required_argument, NULL, 'S' },
+ { "enddate", required_argument, NULL, 'E' },
+ { "subjectAltName", required_argument, NULL, 's' },
+ { "password", required_argument, NULL, 'p' },
+ { "algorithm", required_argument, NULL, 'a' },
+ { "url", required_argument, NULL, 'u' },
+ { "method", required_argument, NULL, 'm' },
+ { "interval", required_argument, NULL, 't' },
+ { "maxpolltime", required_argument, NULL, 'x' },
+#ifdef DEBUG
+ { "debug-all", no_argument, NULL, 'A' },
+ { "debug-parsing", no_argument, NULL, 'P'},
+ { "debug-raw", no_argument, NULL, 'R'},
+ { "debug-control", no_argument, NULL, 'C'},
+ { "debug-controlmore", no_argument, NULL, 'M'},
+ { "debug-private", no_argument, NULL, 'X'},
+#endif
+ { 0,0,0,0 }
+ };
+
+ /* parse next option */
+ int c = getopt_long(argc, argv, "hv+:qi:o:fk:d:s:p:a:u:m:t:x:APRCMS", long_opts, NULL);
+
+ switch (c)
+ {
+ case EOF: /* end of flags */
+ break;
+
+ case 'h': /* --help */
+ usage(NULL);
+
+ case 'v': /* --version */
+ version();
+
+ case 'q': /* --quiet */
+ log_to_stderr = FALSE;
+ continue;
+
+ case 'i': /* --in <type> [= <filename>] */
+ {
+ char *filename = strstr(optarg, "=");
+
+ if (filename)
+ {
+ /* replace '=' by '\0' */
+ *filename = '\0';
+ /* set pointer to start of filename */
+ filename++;
+ }
+ if (strcasecmp("pkcs1", optarg) == 0)
+ {
+ filetype_in |= PKCS1;
+ if (filename)
+ file_in_pkcs1 = filename;
+ }
+ else if (strcasecmp("cacert-enc", optarg) == 0)
+ {
+ filetype_in |= CACERT_ENC;
+ if (filename)
+ file_in_cacert_enc = filename;
+ }
+ else if (strcasecmp("cacert-sig", optarg) == 0)
+ {
+ filetype_in |= CACERT_SIG;
+ if (filename)
+ file_in_cacert_sig = filename;
+ }
+ else
+ {
+ usage("invalid --in file type");
+ }
+ continue;
+ }
+
+ case 'o': /* --out <type> [= <filename>] */
+ {
+ char *filename = strstr(optarg, "=");
+
+ if (filename)
+ {
+ /* replace '=' by '\0' */
+ *filename = '\0';
+ /* set pointer to start of filename */
+ filename++;
+ }
+ if (strcasecmp("pkcs1", optarg) == 0)
+ {
+ filetype_out |= PKCS1;
+ if (filename)
+ file_out_pkcs1 = filename;
+ }
+ else if (strcasecmp("pkcs10", optarg) == 0)
+ {
+ filetype_out |= PKCS10;
+ if (filename)
+ file_out_pkcs10 = filename;
+ }
+ else if (strcasecmp("pkcs7", optarg) == 0)
+ {
+ filetype_out |= PKCS7;
+ if (filename)
+ file_out_pkcs7 = filename;
+ }
+ else if (strcasecmp("cert-self", optarg) == 0)
+ {
+ filetype_out |= CERT_SELF;
+ if (filename)
+ file_out_cert_self = filename;
+ }
+ else if (strcasecmp("cert", optarg) == 0)
+ {
+ filetype_out |= CERT;
+ if (filename)
+ file_out_cert = filename;
+ }
+ else if (strcasecmp("cacert", optarg) == 0)
+ {
+ request_ca_certificate = TRUE;
+ if (filename)
+ file_out_prefix_cacert = filename;
+ }
+ else
+ {
+ usage("invalid --out file type");
+ }
+ continue;
+ }
+
+ case 'f': /* --force */
+ force = TRUE;
+ continue;
+
+ case '+': /* --optionsfrom <filename> */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* does not return on error */
+ continue;
+
+ case 'k': /* --keylength <length> */
+ {
+ div_t q;
+
+ rsa_keylength = atoi(optarg);
+ if (rsa_keylength == 0)
+ usage("invalid keylength");
+
+ /* check if key length is a multiple of 8 bits */
+ q = div(rsa_keylength, 2*BITS_PER_BYTE);
+ if (q.rem != 0)
+ {
+ exit_scepclient("keylength is not a multiple of %d bits!"
+ , 2*BITS_PER_BYTE);
+ }
+ 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("<days> must be a positive number");
+ validity = 24*3600*days;
+ }
+ continue;
+
+ case 'S': /* --startdate */
+ if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z')
+ usage("date format must be YYMMDDHHMMSSZ");
+ {
+ chunk_t date = { optarg, 13 };
+ notBefore = asn1totime(&date, ASN1_UTCTIME);
+ }
+ continue;
+
+ case 'E': /* --enddate */
+ if (optarg == NULL || strlen(optarg) != 13 || optarg[12] != 'Z')
+ usage("date format must be YYMMDDHHMMSSZ");
+ {
+ chunk_t date = { optarg, 13 };
+ notAfter = asn1totime(&date, ASN1_UTCTIME);
+ }
+ continue;
+
+ case 'd': /* --dn */
+ if (distinguishedName)
+ usage("only one distinguished name allowed");
+ distinguishedName = optarg;
+ continue;
+
+ case 's': /* --subjectAltName */
+ {
+ generalNames_t kind;
+ char *value = strstr(optarg, "=");
+
+ if (value)
+ {
+ /* replace '=' by '\0' */
+ *value = '\0';
+ /* set pointer to start of value */
+ value++;
+ }
+
+ if (!strcasecmp("email", optarg))
+ kind = GN_RFC822_NAME;
+ else if (!strcasecmp("dns", optarg))
+ kind = GN_DNS_NAME;
+ else if (!strcasecmp("ip", optarg))
+ kind = GN_IP_ADDRESS;
+ else
+ {
+ usage("invalid --subjectAltName type");
+ continue;
+ }
+ pkcs10_add_subjectAltName(&subjectAltNames, kind, value);
+ continue;
+ }
+
+ case 'p': /* --password */
+ if (challengePassword.len > 0)
+ usage("only one challenge password allowed");
+
+ if (strcasecmp("%prompt", optarg) == 0)
+ {
+ printf("Challenge password: ");
+ if (fgets(challenge_password_buffer, sizeof(challenge_password_buffer)-1, stdin))
+ {
+ challengePassword.ptr = challenge_password_buffer;
+ /* discard the terminating '\n' from the input */
+ challengePassword.len = strlen(challenge_password_buffer) - 1;
+ }
+ else
+ {
+ usage("challenge password could not be read");
+ }
+ }
+ else
+ {
+ challengePassword.ptr = optarg;
+ challengePassword.len = strlen(optarg);
+ }
+ continue;
+
+ case 'u': /* -- url */
+ if (scep_url)
+ usage("only one URL argument allowed");
+ scep_url = optarg;
+ continue;
+
+ case 'm': /* --method */
+ if (strcasecmp("post", optarg) == 0)
+ request_type = FETCH_POST;
+ else if (strcasecmp("get", optarg) == 0)
+ request_type = FETCH_GET;
+ else
+ usage("invalid http request method specified");
+ continue;
+
+ case 't': /* --interval */
+ poll_interval = atoi(optarg);
+ if (poll_interval <= 0)
+ usage("invalid interval specified");
+ continue;
+
+ case 'x': /* --maxpolltime */
+ max_poll_time = atoi(optarg);
+ if (max_poll_time < 0)
+ usage("invalid maxpolltime specified");
+ continue;
+
+ case 'a': /*--algorithm */
+ if (strcasecmp("des-cbc", optarg) == 0)
+ pkcs7_symmetric_cipher = OID_DES_CBC;
+ else if (strcasecmp("3des-cbc", optarg) == 0)
+ pkcs7_symmetric_cipher = OID_3DES_EDE_CBC;
+ else
+ usage("invalid encryption algorithm specified");
+ continue;
+#ifdef DEBUG
+ case 'A': /* --debug-all */
+ base_debugging |= DBG_ALL;
+ continue;
+ case 'P': /* debug parsing */
+ base_debugging |= DBG_PARSING;
+ continue;
+ case 'R': /* debug raw */
+ base_debugging |= DBG_RAW;
+ continue;
+ case 'C': /* debug control */
+ base_debugging |= DBG_CONTROL;
+ continue;
+ case 'M': /* debug control more */
+ base_debugging |= DBG_CONTROLMORE;
+ continue;
+ case 'X': /* debug private */
+ base_debugging |= DBG_PRIVATE;
+ continue;
+#endif
+ default:
+ usage("unknown option");
+ }
+ /* break from loop */
+ break;
+ }
+
+ init_log("scepclient");
+ cur_debugging = base_debugging;
+ init_rnd_pool();
+ init_fetch();
+
+ if ((filetype_out == 0) && (!request_ca_certificate))
+ usage ("--out filetype required");
+
+ if (request_ca_certificate && (filetype_out > 0 || filetype_in > 0))
+ usage("in CA certificate request, no other --in or --out option allowed");
+
+ /* check if url is given, if cert output defined */
+ if (((filetype_out & CERT) || request_ca_certificate) && !scep_url)
+ usage("URL of SCEP server required");
+
+ /* check for sanity of --in/--out */
+ if (!filetype_in && (filetype_in > filetype_out))
+ usage("cannot generate --out of given --in!");
+
+ /*
+ * input of PKCS#1 file
+ */
+ private_key = alloc_thing(RSA_private_key_t, "RSA_private_key_t");
+
+ if (filetype_in & PKCS1) /* load an RSA key pair from file */
+ {
+ prompt_pass_t pass = { "", FALSE, STDIN_FILENO };
+ const char *path = concatenate_paths(PRIVATE_KEY_PATH, file_in_pkcs1);
+
+ ugh = load_rsa_private_key(path, &pass, private_key);
+ }
+ else /* generate an RSA key pair */
+ {
+ ugh = generate_rsa_private_key(rsa_keylength, private_key);
+ }
+ if (ugh != NULL)
+ exit_scepclient(ugh);
+
+ /* check for minimum key length */
+ if ((private_key->pub.k) < RSA_MIN_OCTETS)
+ {
+ exit_scepclient("length of RSA key has to be at least %d bits"
+ ,RSA_MIN_OCTETS * BITS_PER_BYTE);
+ }
+
+ /*
+ * input of PKCS#10 file
+ */
+ if (filetype_in & PKCS10)
+ {
+ /* user wants to load a pkcs10 request
+ * operation is not yet supported
+ * would require a PKCS#10 parsing function
+
+ pkcs10 = pkcs10_read_from_file(file_in_pkcs10);
+
+ */
+ }
+ else
+ {
+ char buf[IDTOA_BUF];
+ chunk_t dn = empty_chunk;
+
+ dn.ptr = buf;
+
+ if (distinguishedName == NULL)
+ {
+ char buf[BUF_LEN];
+ int n = sprintf(buf, DEFAULT_DN);
+
+ /* set the common name to the hostname */
+ if (gethostname(buf + n, BUF_LEN - n) || strlen(buf) == n)
+ {
+ exit_scepclient("no hostname defined, use "
+ "--dn <distinguished name> option");
+ }
+ distinguishedName = buf;
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("dn: '%s'", distinguishedName);
+ )
+ ugh = atodn(distinguishedName, &dn);
+ if (ugh != NULL)
+ exit_scepclient(ugh);
+
+ clonetochunk(subject, dn.ptr, dn.len, "subject dn");
+
+ DBG(DBG_CONTROL,
+ DBG_log("building pkcs10 object:")
+ )
+ pkcs10 = pkcs10_build(private_key, subject, challengePassword
+ , subjectAltNames, pkcs10_signature_alg);
+ scep_generate_pkcs10_fingerprint(pkcs10->request, &fingerprint);
+ plog(" fingerprint: %.*s", (int)fingerprint.len, fingerprint.ptr);
+ }
+
+ /*
+ * output of PKCS#10 file
+ */
+ if (filetype_out & PKCS10)
+ {
+ const char *path = concatenate_paths(REQ_PATH, file_out_pkcs10);
+
+ if (!write_chunk(path, "pkcs10", pkcs10->request, 0022, force))
+ exit_scepclient("could not write pkcs10 file '%s'", path);
+
+ filetype_out &= ~PKCS10; /* delete PKCS10 flag */
+ }
+
+ if (!filetype_out)
+ exit_scepclient(NULL); /* no further output required */
+
+ /*
+ * output of PKCS#1 file
+ */
+ if (filetype_out & PKCS1)
+ {
+ const char *path = concatenate_paths(PRIVATE_KEY_PATH, file_out_pkcs1);
+
+ DBG(DBG_CONTROL,
+ DBG_log("building pkcs1 object:")
+ )
+ pkcs1 = pkcs1_build_private_key(private_key);
+
+ if (!write_chunk(path, "pkcs1", pkcs1, 0066, force))
+ exit_scepclient("could not write pkcs1 file '%s'", path);
+
+ filetype_out &= ~PKCS1; /* delete PKCS1 flag */
+ }
+
+ if (!filetype_out)
+ exit_scepclient(NULL); /* no further output required */
+
+ scep_generate_transaction_id((const RSA_public_key_t *)private_key
+ , &transID, &serialNumber);
+ plog(" transaction ID: %.*s", (int)transID.len, transID.ptr);
+
+ /* generate a self-signed X.509 certificate */
+ x509_signer = alloc_thing(x509cert_t, "signer cert");
+ *x509_signer = empty_x509cert;
+ x509_signer->serialNumber = serialNumber;
+ x509_signer->sigAlg = OID_SHA1_WITH_RSA;
+ x509_signer->issuer = subject;
+ x509_signer->notBefore = (notBefore)? notBefore
+ : time(NULL);
+ x509_signer->notAfter = (notAfter)? notAfter
+ : x509_signer->notBefore + validity;
+ x509_signer->subject = subject;
+ x509_signer->subjectAltName = subjectAltNames;
+
+ build_x509cert(x509_signer, (const RSA_public_key_t *)private_key
+ , private_key);
+
+ /*
+ * output of self-signed X.509 certificate file
+ */
+ if (filetype_out & CERT_SELF)
+ {
+ const char *path = concatenate_paths(HOST_CERT_PATH, file_out_cert_self);
+
+ if (!write_chunk(path, "self-signed cert", x509_signer->certificate, 0022, force))
+ exit_scepclient("could not write self-signed cert file '%s'", path);
+;
+ filetype_out &= ~CERT_SELF; /* delete CERT_SELF flag */
+ }
+
+ if (!filetype_out)
+ exit_scepclient(NULL); /* no further output required */
+
+ /*
+ * load ca encryption certificate
+ */
+ {
+ const char *path = concatenate_paths(CA_CERT_PATH, file_in_cacert_enc);
+ cert_t cert;
+
+ if (!load_cert(path, "encryption cacert", &cert))
+ exit_scepclient("could not load encryption cacert file '%s'", path);
+ x509_ca_enc = cert.u.x509;
+ }
+
+ /*
+ * input of PKCS#7 file
+ */
+ if (filetype_in & PKCS7)
+ {
+ /* user wants to load a pkcs7 encrypted request
+ * operation is not yet supported!
+ * would require additional parsing of transaction-id
+
+ pkcs7 = pkcs7_read_from_file(file_in_pkcs7);
+
+ */
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("building pkcs7 request")
+ )
+ pkcs7 = scep_build_request(pkcs10->request
+ , transID, SCEP_PKCSReq_MSG
+ , x509_ca_enc, pkcs7_symmetric_cipher
+ , x509_signer, pkcs7_digest_alg, private_key);
+ }
+
+ /*
+ * output pkcs7 encrypted and signed certificate request
+ */
+ if (filetype_out & PKCS7)
+ {
+ const char *path = concatenate_paths(REQ_PATH, file_out_pkcs7);
+
+ if (!write_chunk(path, "pkcs7 encrypted request", pkcs7, 0022, force))
+ exit_scepclient("could not write pkcs7 file '%s'", path);
+;
+ filetype_out &= ~PKCS7; /* delete PKCS7 flag */
+ }
+
+ if (!filetype_out)
+ exit_scepclient(NULL); /* no further output required */
+
+ /*
+ * output certificate fetch from SCEP server
+ */
+ if (filetype_out & CERT)
+ {
+ const char *path = concatenate_paths(CA_CERT_PATH, file_in_cacert_sig);
+ cert_t cert;
+ time_t poll_start;
+
+ x509cert_t *certs = NULL;
+ chunk_t envelopedData = empty_chunk;
+ chunk_t certData = empty_chunk;
+ contentInfo_t data = empty_contentInfo;
+ scep_attributes_t attrs = empty_scep_attributes;
+
+ if (!load_cert(path, "signature cacert", &cert))
+ exit_scepclient("could not load signature cacert file '%s'", path);
+ x509_ca_sig = cert.u.x509;
+
+ if (!scep_http_request(scep_url, pkcs7, SCEP_PKI_OPERATION
+ , request_type, &scep_response))
+ {
+ exit_scepclient("did not receive a valid scep response");
+ }
+ ugh = scep_parse_response(scep_response, transID, &data, &attrs
+ , x509_ca_sig);
+ if (ugh != NULL)
+ exit_scepclient(ugh);
+
+ /* in case of manual mode, we are going into a polling loop */
+ if (attrs.pkiStatus == SCEP_PENDING)
+ {
+ plog(" scep request pending, polling every %d seconds"
+ , poll_interval);
+ time(&poll_start);
+ issuerAndSubject = asn1_wrap(ASN1_SEQUENCE, "cc"
+ , x509_ca_sig->subject
+ , subject);
+ }
+ while (attrs.pkiStatus == SCEP_PENDING)
+ {
+ if (max_poll_time > 0
+ && (time(NULL) - poll_start >= max_poll_time))
+ {
+ exit_scepclient("maximum poll time reached: %d seconds"
+ , max_poll_time);
+ }
+ DBG(DBG_CONTROL,
+ DBG_log("going to sleep for %d seconds", poll_interval)
+ )
+ sleep(poll_interval);
+ free(scep_response.ptr);
+
+ DBG(DBG_CONTROL,
+ DBG_log("fingerprint: %.*s", (int)fingerprint.len, fingerprint.ptr);
+ DBG_log("transaction ID: %.*s", (int)transID.len, transID.ptr)
+ )
+
+ freeanychunk(getCertInitial);
+ getCertInitial = scep_build_request(issuerAndSubject
+ , transID, SCEP_GetCertInitial_MSG
+ , x509_ca_enc, pkcs7_symmetric_cipher
+ , x509_signer, pkcs7_digest_alg, private_key);
+
+ if (!scep_http_request(scep_url, getCertInitial, SCEP_PKI_OPERATION
+ , request_type, &scep_response))
+ {
+ exit_scepclient("did not receive a valid scep response");
+ }
+ ugh = scep_parse_response(scep_response, transID, &data, &attrs
+ , x509_ca_sig);
+ if (ugh != NULL)
+ exit_scepclient(ugh);
+ }
+
+ if (attrs.pkiStatus != SCEP_SUCCESS)
+ {
+ exit_scepclient("reply status is not 'SUCCESS'");
+ }
+
+ envelopedData = data.content;
+
+ if (data.type != OID_PKCS7_DATA
+ || !parse_asn1_simple_object(&envelopedData, ASN1_OCTET_STRING, 0, "data"))
+ {
+ exit_scepclient("contentInfo is not of type 'data'");
+ }
+ if (!pkcs7_parse_envelopedData(envelopedData, &certData
+ , serialNumber, private_key))
+ {
+ exit_scepclient("could not decrypt envelopedData");
+ }
+ if (!pkcs7_parse_signedData(certData, NULL, &certs, NULL, NULL))
+ {
+ exit_scepclient("error parsing the scep response");
+ }
+ freeanychunk(certData);
+
+ /* store the end entity certificate */
+ path = concatenate_paths(HOST_CERT_PATH, file_out_cert);
+ while (certs != NULL)
+ {
+ bool stored = FALSE;
+ x509cert_t *cert = certs;
+
+ if (!cert->isCA)
+ {
+ if (stored)
+ exit_scepclient("multiple certs received, only first stored");
+ if (!write_chunk(path, "requested cert", cert->certificate, 0022, force))
+ exit_scepclient("could not write cert file '%s'", path);
+ stored = TRUE;
+ }
+ certs = certs->next;
+ free_x509cert(cert);
+ }
+ filetype_out &= ~CERT; /* delete CERT flag */
+ }
+
+ exit_scepclient(NULL);
+ return -1; /* should never be reached */
+}
+
+
diff --git a/programs/secrets/Makefile b/programs/secrets/Makefile
new file mode 100644
index 000000000..a853d22f2
--- /dev/null
+++ b/programs/secrets/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:30 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=secrets
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:30 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# 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/programs/secrets/secrets.8 b/programs/secrets/secrets.8
new file mode 100644
index 000000000..2333a0a3e
--- /dev/null
+++ b/programs/secrets/secrets.8
@@ -0,0 +1,20 @@
+.TH IPSEC_SECRETS 8 "31 Aug 2003"
+.\" RCSID $Id: secrets.8,v 1.1 2004/03/15 20:35:30 as Exp $
+.SH NAME
+ipsec secrets \- prompt for PIN codes and passphrases
+.SH SYNOPSIS
+.B ipsec
+.B secrets
+.SH DESCRIPTION
+.I Secrets
+is an alias for
+.B ipsec auto --rereadsecrets
+and prompts for PIN codes and passphrases protecting private RSA keys.
+.SH SEE ALSO
+ipsec.secrets(5)
+.SH HISTORY
+Written for the FreeS/WAN project
+<http://www.freeswan.org>
+by Andreas Steffen.
+.SH BUGS
+None
diff --git a/programs/secrets/secrets.in b/programs/secrets/secrets.in
new file mode 100644
index 000000000..b7e486098
--- /dev/null
+++ b/programs/secrets/secrets.in
@@ -0,0 +1,18 @@
+#! /bin/sh
+# program which prompts for PINs and passphrases
+# alias for ipsec auto --rereadsecrets
+# Copyright (C) 2003 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: secrets.in,v 1.1 2004/03/15 20:35:31 as Exp $
+
+ipsec auto --rereadsecrets
diff --git a/programs/send-pr/.cvsignore b/programs/send-pr/.cvsignore
new file mode 100644
index 000000000..953bfcf5a
--- /dev/null
+++ b/programs/send-pr/.cvsignore
@@ -0,0 +1 @@
+send-pr
diff --git a/programs/send-pr/Makefile b/programs/send-pr/Makefile
new file mode 100644
index 000000000..db7d51929
--- /dev/null
+++ b/programs/send-pr/Makefile
@@ -0,0 +1,39 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=send-pr
+LIBFILES=ipsec_pr.template
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/send-pr/ipsec_pr.template b/programs/send-pr/ipsec_pr.template
new file mode 100644
index 000000000..3e809a677
--- /dev/null
+++ b/programs/send-pr/ipsec_pr.template
@@ -0,0 +1,54 @@
+SEND-PR: -*- send-pr -*-
+SEND-PR: Lines starting with `SEND-PR' will be removed automatically, as
+SEND-PR: will all comments (text enclosed in `<' and `>').
+SEND-PR:
+SEND-PR: Please consult the send-pr man page `send-pr(1)' or the Texinfo
+SEND-PR: manual if you are not sure how to fill out a problem report.
+SEND-PR: Note that the Synopsis field is mandatory. The Subject (for
+SEND-PR: the mail) will be made the same as Synopsis unless explicitly
+SEND-PR: changed.
+SEND-PR:
+SEND-PR: Choose from the following categories:
+SEND-PR:
+SEND-PR: pluto - Problems with IKE daemon
+SEND-PR: klips - Problems with kernel code
+SEND-PR: startup- Problems with start/configuration code
+SEND-PR: doc - Problems with documentation
+SEND-PR: interop- Problems with interoperability
+SEND-PR: source - source code patches/contributions
+SEND-PR: admin - Problems with freeswan.org machines
+SEND-PR:
+To: gnats-bugs@freeswan.org
+Subject:
+From: <FROM>
+Reply-To: <REPLYTO>
+Cc:
+X-send-pr-version: 4.0-alpha
+X-GNATS-Notify:
+
+>Submitter-Id: <SUBMITTER>
+>Originator: <DEFAULT_ORIGINATOR>
+>Organization:
+ unknown
+>Synopsis: <One-line summary of the PR (one line)>
+>Confidential: <[ yes | no ] (one line)>
+>Severity: <[ critical | serious | non-critical ] (one line)>
+>Priority: <[ high | medium | low ] (one line)>
+>Category: <choose from a category listed above (one line)>
+>Class: <[ sw-bug | dos | interop | mtu | log | doc-bug | support | change-request | mistaken | duplicate ] (one line)>
+>Release: <DEFAULT_VERSION>
+>Environment:
+ <DEFAULT_ENVIRONMENT>
+
+>IPsec-barf-location: <DEFAULT_BARF>
+ <some URL with the output of ipsec barf.>
+
+>Description:
+ <Precise description of the problem (multiple lines)>
+>How-To-Repeat:
+ <code/input/activities to reproduce the problem (multiple lines)>
+>Fix:
+ <How to correct or work around the problem, if known (multiple lines)>
+
+>IPsec-look:
+
diff --git a/programs/send-pr/send-pr.8 b/programs/send-pr/send-pr.8
new file mode 100644
index 000000000..73a5bbf3c
--- /dev/null
+++ b/programs/send-pr/send-pr.8
@@ -0,0 +1,291 @@
+.\" -*- nroff -*-
+.\" ---------------------------------------------------------------------------
+.\" man page for send-pr (by Heinz G. Seidl, hgs@cygnus.com)
+.\" updated Feb 1993 for GNATS 3.00 by Jeffrey Osier, jeffrey@cygnus.com
+.\"
+.\" This file is part of the Problem Report Management System (GNATS)
+.\" Copyright 1992 Cygnus Support
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public
+.\" License as published by the Free Software Foundation; either
+.\" version 2 of the License, or (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU Library General Public
+.\" License along with this program; if not, write to the Free
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
+.\"
+.\" ---------------------------------------------------------------------------
+.nh
+.TH SEND-PR 8 xVERSIONx "February 1993"
+.SH NAME
+ipsec send-pr \- send problem report (PR) to a central support site
+.SH SYNOPSIS
+.B ipsec send-pr
+[
+.I site
+]
+[
+.B \-f
+.I problem-report
+]
+[
+.B \-t
+.I mail-address
+]
+.br
+.in +0.8i
+[
+.B \-P
+]
+[
+.B \-L
+]
+[
+.B \-s
+.I severity
+]
+[
+.B \-c
+.I address
+]
+.br
+[
+.B \-\-request-id
+]
+[
+.B \-V
+]
+.SH DESCRIPTION
+.B ipsec send-pr
+is a tool used to submit
+.I problem reports
+.\" SITE ADMINISTRATORS - change this if you use a local default
+(PRs) to a central support site. In most cases the correct
+.I site
+will be the default. This argument indicates the support site which
+is responsible for the category of problem involved. Some sites may
+use a local address as a default.
+.I site
+values are defined by using the
+.BR aliases (5).
+.LP
+.B ipsec send-pr
+invokes an editor on a problem report template (after trying to fill
+in some fields with reasonable default values). When you exit the
+editor,
+.B ipsec send-pr
+sends the completed form to the
+.I Problem Report Management System
+(\fBGNATS\fR) at a central support site. At the support site, the PR
+is assigned a unique number and is stored in the \fBGNATS\fR database
+according to its category and submitter-id. \fBGNATS\fR automatically
+replies with an acknowledgement, citing the category and the PR
+number.
+.LP
+To ensure that a PR is handled promptly, it should contain your (unique)
+\fIsubmitter-id\fR and one of the available \fIcategories\fR to identify the
+problem area. (Use
+.B `ipsec send-pr -L'
+to see a list of categories.)
+.LP
+The
+.B ipsec send-pr
+template at your site should already be customized with your
+submitter-id (running `\|\fBinstall-sid\fP \fIsubmitter-id\fP\|' to
+accomplish this is part of the installation procedures for
+.BR ipsec send-pr ).
+If this hasn't been done, see your system administrator for your
+submitter-id, or request one from your support site by invoking
+.B `ipsec send-pr \-\-request\-id'.
+If your site does not distinguish between different user sites, or if
+you are not affiliated with the support site, use
+.B `net'
+for this field.
+.LP
+The more precise your problem description and the more complete your
+information, the faster your support team can solve your problems.
+.SH OPTIONS
+.TP
+.BI \-f " problem-report"
+specify a file (\fIproblem-report\fR) which already contains a
+complete problem report.
+.B ipsec send-pr
+sends the contents of the file without invoking the editor. If
+the value for
+.I problem-report
+is
+.BR `\|\-\|' ,
+then
+.B ipsec send-pr
+reads from standard input.
+.TP
+.BI \-s " severity"
+Give the problem report the severity
+.IR severity .
+.TP
+.BI \-t " mail-address"
+Change mail address at the support site for problem reports. The
+default
+.I mail-address
+is the address used for the default
+.IR site .
+Use the
+.I site
+argument rather than this option in nearly all cases.
+.TP
+.BI \-c " address"
+Put
+.I address
+in the
+.B Cc:
+header of the message.
+.TP
+.B \-P
+print the form specified by the environment variable
+.B PR_FORM
+on standard output. If
+.B PR_FORM
+is not set, print the standard blank PR template. No mail is sent.
+.TP
+.B -L
+print the list of available categories. No mail is sent.
+.TP
+.B \-\-request\-id
+sends mail to the default support site, or
+.I site
+if specified, with a request for your
+.IR submitter-id .
+If you are
+not affiliated with
+.IR site ,
+use a
+.I submitter-id
+of
+.BR net \|'.
+.TP
+.B \-V
+Display the
+.B ipsec send-pr
+version number.
+.LP
+Note: use
+.B ipsec send-pr
+to submit problem reports rather than mailing them directly. Using
+both the template and
+.B ipsec send-pr
+itself will help ensure all necessary information will reach the
+support site.
+.SH ENVIRONMENT
+The environment variable
+.B EDITOR
+specifies the editor to invoke on the template.
+.br
+default:
+.B vi
+.sp
+If the environment variable
+.B PR_FORM
+is set, then its value is used as the file name of the template for
+your problem-report editing session. You can use this to start with a
+partially completed form (for example, a form with the identification
+fields already completed).
+.SH "HOW TO FILL OUT A PROBLEM REPORT"
+Problem reports have to be in a particular form so that a program can
+easily manage them. Please remember the following guidelines:
+.IP \(bu 3m
+describe only
+.B one problem
+with each problem report.
+.IP \(bu 3m
+For follow-up mail, use the same subject line as the one in the automatic
+acknowledgent. It consists of category, PR number and the original synopsis
+line. This allows the support site to relate several mail messages to a
+particular PR and to record them automatically.
+.IP \(bu 3m
+Please try to be as accurate as possible in the subject and/or synopsis line.
+.IP \(bu 3m
+The subject and the synopsis line are not confidential. This is
+because open-bugs lists are compiled from them. Avoid confidential
+information there.
+.LP
+See the GNU
+.B Info
+file
+.B send-pr.info
+or the document \fIReporting Problems With send-pr\fR\ for detailed
+information on reporting problems
+.SH "HOW TO SUBMIT TEST CASES, CODE, ETC."
+Submit small code samples with the PR. Contact the support site for
+instructions on submitting larger test cases and problematic source
+code.
+.SH FILES
+.ta \w'/tmp/pbad$$ 'u
+/tmp/p$$ copy of PR used in editing session
+.br
+/tmp/pf$$ copy of empty PR form, for testing purposes
+.br
+/tmp/pbad$$ file for rejected PRs
+.br
+@IPSEC_DIR@/send-pr.conf script to customize send-pr.
+.SH EMACS USER INTERFACE
+An Emacs user interface for
+.B send-pr
+with completion of field values is part of the
+.B send-pr
+distribution (invoked with
+.BR "M-x send-pr" ).
+See the file
+.B send-pr.info
+or the ASCII file
+.B INSTALL
+in the top level directory of the distribution for configuration and
+installation information. The Emacs LISP template file is
+.B send-pr-el.in
+and is installed as
+.BR send-pr.el .
+.SH INSTALLATION AND CONFIGURATION
+See
+.B send-pr.info
+or
+.B INSTALL
+for installation instructions.
+.SH SEE ALSO
+.I Reporting Problems Using send-pr
+(also installed as the GNU Info file
+.BR send-pr.info ).
+.LP
+.BR gnats (l),
+.BR query-pr (1),
+.BR edit-pr (1),
+.BR gnats (8),
+.BR queue-pr (8),
+.BR at-pr (8),
+.BR mkcat (8),
+.BR mkdist (8).
+.SH AUTHORS
+Jeffrey Osier, Brendan Kehoe, Jason Merrill, Heinz G. Seidl (Cygnus
+Support)
+.SH COPYING
+Copyright (c) 1992, 1993 Free Software Foundation, Inc.
+.PP
+Permission is granted to make and distribute verbatim copies of
+this manual provided the copyright notice and this permission notice
+are preserved on all copies.
+.PP
+Permission is granted to copy and distribute modified versions of this
+manual under the conditions for verbatim copying, provided that the
+entire resulting derived work is distributed under the terms of a
+permission notice identical to this one.
+.PP
+Permission is granted to copy and distribute translations of this
+manual into another language, under the above conditions for modified
+versions, except that this permission notice may be included in
+translations approved by the Free Software Foundation instead of in
+the original English.
+
diff --git a/programs/send-pr/send-pr.in b/programs/send-pr/send-pr.in
new file mode 100755
index 000000000..6cd202470
--- /dev/null
+++ b/programs/send-pr/send-pr.in
@@ -0,0 +1,643 @@
+#!/bin/sh
+# Submit a problem report to a GNATS site.
+# Copyright (C) 2001 Milan Zamazal
+# Copyright (C) 1993, 2001 Free Software Foundation, Inc.
+# Contributed by Brendan Kehoe (brendan@cygnus.com), based on a
+# version written by Heinz G. Seidl (hgs@cygnus.com).
+# Further edited by Milan Zamazal (pdm@zamazal.org).
+# mktemp support by Yngve Svendsen (yngve.svendsen@clustra.com).
+#
+# This file is part of GNU GNATS.
+#
+# GNU GNATS is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU GNATS is distributed in the hope that it will be useful,
+# but WITHOUT 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 GNU GNATS; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#
+# $Id: send-pr.in,v 1.1 2004/03/15 20:35:31 as Exp $
+#
+
+# The version of this send-pr.
+VERSION=4.0-alpha
+
+#SWAN_VERSION=
+
+# The submitter-id for your site.
+SUBMITTER=net
+
+# The place where our usual binaries live.
+BINDIR=@IPSEC_DIR@
+
+# The place where the builtin binaries are located.
+LIBDIR=@IPSEC_LIBDIR@
+LIBEXECDIR=@IPSEC_EXECDIR@
+
+# The default release for this host.
+DEFAULT_RELEASE="gnats-4.0-alpha"
+
+# The default organization.
+DEFAULT_ORGANIZATION="net"
+
+# How to read the passwd database.
+PASSWD="cat /etc/passwd"
+
+# Is the mktemp command available?
+MKTEMP="yes"
+
+ECHON=bsd
+
+# By default send-pr connects directly to the database. However, it
+# can be configured to use an existing template file by setting the
+# TEMPLATE variable below to point to a PR template generated from
+# "send-pr -P".
+TEMPLATE="$LIBDIR/ipsec_pr.template"
+
+# send-pr can use mail to submit PRs, instead of connecting to the
+# database directly. MAILPROG needs to point to a compatible mailer
+# (sendmail will work). If MAILPROG needs to have the address that
+# the mail is being sent to specified on the command line, it should
+# be specified here as well (for example, the command
+# MAILPROG="mail bugs@foo.bar.com"
+# should work). If sendmail is used, this should be set to
+# MAILPROG="/usr/lib/sendmail -oi -t"
+MAILPROG="/usr/sbin/sendmail -oi -t"
+
+# The address that PRs are sent to. Normally this can be left as "bugs";
+# however, if using mail to submit PRs, this should be set to the address
+# where PRs should be sent.
+MAILADDR="freeswan-bugs@freeswan.org"
+
+if [ $ECHON = bsd ] ; then
+ ECHON1="echo -n"
+ ECHON2=
+elif [ $ECHON = sysv ] ; then
+ ECHON1=echo
+ ECHON2='\c'
+else
+ ECHON1=echo
+ ECHON2=
+fi
+
+# Configuration file to be read. It must be a shell script that can redefine
+# the variables above to fit a local configuration.
+CONFIGFILE=@IPSEC_DIR@/send-pr.conf
+
+if [ -r $CONFIGFILE ]; then
+ . $CONFIGFILE
+fi
+
+#
+
+if [ -z "$TMPDIR" ]; then
+ TMPDIR=/tmp
+else
+ if [ "`echo $TMPDIR | grep '/$'`" != "" ]; then
+ TMPDIR="`echo $TMPDIR | sed -e 's,/$,,'`"
+ fi
+fi
+
+# TEMP: Temporary copy of the PR, to be edited by the user.
+# BAD: The PR will end up here if the user aborts.
+# REF: The 'reference' copy of the PR template, used to verify that the user
+# actually did edit the template.
+# FIXFIL: A sed script used to remove comments from the template before
+# processing.
+if [ $MKTEMP = yes ]; then
+ TEMP=`mktemp $TMPDIR/pXXXXXX` || exit 1
+ BAD=`mktemp $TMPDIR/pbadXXXXXX` || exit 1
+ REF=`mktemp $TMPDIR/pfXXXXXX` || exit 1
+ FIXFIL=`mktemp $TMPDIR/fixXXXXXX` || exit 1
+else
+ TEMP=$TMPDIR/p$$
+ BAD=$TMPDIR/pbad$$
+ REF=$TMPDIR/pf$$
+ FIXFIL=$TMPDIR/fix$$
+ bad_temp=0
+ : > $TEMP || bad_temp=1
+ : > $BAD || bad_temp=1
+ : > $REF || bad_temp=1
+ : > $FIXFIL || bad_temp=1
+ if [ $bad_temp = 1 ]; then
+ rm -f $TEMP $BAD $REF $FIXFIL
+ exit 1;
+ fi
+fi
+REMOVE_TEMP="rm -f $TEMP $BAD $REF"
+
+# find a user name
+if [ "$LOGNAME" = "" ]; then
+ if [ "$USER" != "" ]; then
+ LOGNAME="$USER"
+ else
+ LOGNAME="UNKNOWN"
+ fi
+fi
+
+FROM="$LOGNAME"
+REPLYTO="${REPLY_TO:-${REPLYTO:-$LOGNAME}}"
+if [ "x$MAILPROG" != "x" ]
+then
+ RESP_ALIAS="`query-pr --adm-field responsible --adm-key $LOGNAME --adm-subfield alias 2>/dev/null`"
+else
+ RESP_ALIAS=""
+fi
+
+# Find out the name of the originator of this PR.
+if [ -n "$NAME" ]; then
+ DEFAULT_ORIGINATOR="$NAME"
+elif [ -f $HOME/.fullname ]; then
+ DEFAULT_ORIGINATOR="`sed -e '1q' $HOME/.fullname`"
+else
+ # Must use temp file due to incompatibilities in quoting behavior
+ # and to protect shell metacharacters in the expansion of $LOGNAME
+ $PASSWD | grep "^$LOGNAME:" | awk -F: '{print $5}' | sed -e 's/,.*//' > $TEMP
+ if [ "x$RESP_ALIAS" != "x" ]
+ then
+ DEFAULT_ORIGINATOR="$RESP_ALIAS (`cat $TEMP`)"
+ else
+ DEFAULT_ORIGINATOR="$FROM (`cat $TEMP`)"
+ fi
+ rm -f $TEMP
+fi
+
+if [ -z "$ORGANIZATION" ]
+then
+ ORGANIZATION="$DEFAULT_ORGANIZATION";
+fi
+
+if [ -n "$ORGANIZATION" -a "x$ORGANIZATION" != "xunknown" ]; then
+ if [ -f "$ORGANIZATION" ]; then
+ ORGANIZATION="`cat $ORGANIZATION`"
+ fi
+ if [ -n "$ORGANIZATION" ]; then
+ ORGANIZATION="$ORGANIZATION"
+ elif [ -f $HOME/.organization ]; then
+ ORGANIZATION="`cat $HOME/.organization`"
+ fi
+fi
+
+if [ "x$ORGANIZATION" = "xunknown" ]; then
+ cat <<__EOF__
+It seems that send-pr is not installed with your organization set to a useful
+value. To fix this, you need to edit the configuration file
+$CONFIGFILE
+and fill in the organization with the correct value.
+
+__EOF__
+ ORGANIZATION="";
+fi 1>&2
+
+# If they don't have a preferred editor set, then use
+if [ -z "$VISUAL" ]; then
+ if [ -z "$EDITOR" ]; then
+ EDIT=vi
+ else
+ EDIT="$EDITOR"
+ fi
+else
+ EDIT="$VISUAL"
+fi
+
+# Find out some information.
+SYSTEM=`( [ -f /bin/uname ] && /bin/uname -a ) || \
+ ( [ -f /usr/bin/uname ] && /usr/bin/uname -a ) || echo "" | sed -e 's,|,\\|,'`
+
+# Our base command name.
+COMMAND=`echo $0 | sed -e 's,.*/,,'`
+USAGE="Usage: $COMMAND [OPTION]...
+
+ -b --batch run without printing most messages
+ --barf include a full barf inline rather than just look
+ -c --cc=LINE put LINE to the CC header
+ -d --database=DATABASE submit PR to DATABASE
+ -f --file=FILE read the PR template from FILE (\`-' for stdin)
+ -p --print just print the template and exit
+ --request-id send a request for a user id
+ -s --severity=SEVERITY PR severity
+
+ -h --help display this help and exit
+ -V --version output version information and exit
+"
+REMOVE=
+BATCH=
+CC=
+DEFAULT_SEVERITY=
+BARF=${BARF-false}
+
+if [ "$SYSTEM" != "" ]
+then
+ DEFAULT_ENVIRONMENT="System: $SYSTEM"
+fi
+
+if [ "$SWAN_VERSION" != "" ]
+then
+ DEFAULT_VERSION="$SWAN_VERSION";
+else
+ DEFAULT_VERSION=`ipsec --versioncode`
+fi
+DEFAULT_VERSION=`echo $DEFAULT_VERSION | sed -e 's,\/,\\\/,'`
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -r) ;; # Ignore for backward compat.
+ -f | --file) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift ; IN_FILE="$1"
+ if [ "$IN_FILE" != "-" -a ! -r "$IN_FILE" ]; then
+ echo "$COMMAND: cannot read $IN_FILE"
+ exit 1
+ fi
+ ;;
+ -b | --batch) BATCH=true ;;
+ --barf) BARF=true ;;
+ -c | --cc) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift ; CC="$1"
+ ;;
+ -d | --database) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift; GNATSDB="$1"; export GNATSDB
+ ;;
+ -s | --severity) if [ $# -eq 1 ]; then echo "$USAGE"; exit 1; fi
+ shift ; DEFAULT_SEVERITY="$1"
+ ;;
+ -p | -P | --print) PRINT=true ;;
+ --request-id) REQUEST_ID=true ;;
+ -h | --help) echo "$USAGE"; exit 0 ;;
+ -V | --version) echo "$VERSION"; exit 0 ;;
+ -*) echo "$USAGE" ; exit 1 ;;
+ *) echo "$USAGE" ; exit 1 ;;
+ esac
+ shift
+done
+
+if [ "x$SUBMITTER" = "x" ]
+then
+ SUBMITTER="unknown"
+fi
+
+if [ "x$SUBMITTER" = "xunknown" -a -z "$REQUEST_ID" -a -z "$IN_FILE" ]; then
+ cat << '__EOF__'
+It seems that send-pr is not installed with your unique submitter-id.
+You need to run
+
+ install-sid YOUR-SID
+
+where YOUR-SID is the identification code you received with `send-pr'.
+`send-pr' will automatically insert this value into the template field
+`>Submitter-Id'. If you've downloaded `send-pr' from the Net, use `net'
+for this value. If you do not know your id, run `send-pr --request-id' to
+get one from your support site.
+__EOF__
+ exit 1
+fi
+
+# So the template generation code finds it.
+DEFAULT_SUBMITTERID=${SUBMITTER}
+
+# Catch some signals. ($xs kludge needed by Sun /bin/sh)
+xs=0
+trap 'rm -f $REF $TEMP $FIXFIL; exit $xs' 0
+trap 'echo "$COMMAND: Aborting ..."; rm -f $REF $TEMP $FIXFIL; xs=1; exit' 1 3 13 15
+
+if [ "x$PRINT" = "xtrue" ]; then
+ FROM="<FROM>"
+ REPLYTO="<REPLYTO>"
+ DEFAULT_ORIGINATOR="<DEFAULT_ORIGINATOR>"
+ DEFAULT_SUBMITTERID="<SUBMITTER>"
+fi
+
+# If they told us to use a specific file, then do so.
+if [ -n "$IN_FILE" ]; then
+ if [ "$IN_FILE" = "-" ]; then
+ # The PR is coming from the standard input.
+ cat > $TEMP
+ else
+ # Use the file they named.
+ cat $IN_FILE > $TEMP
+ fi
+else
+ if [ -n "$TEMPLATE" -a -z "$PRINT_INTERN" ]; then
+ # If their TEMPLATE points to a bogus entry, then bail.
+ if [ ! -f "$TEMPLATE" -o ! -r "$TEMPLATE" -o ! -s "$TEMPLATE" ]; then
+ echo "$COMMAND: can't seem to read your template file (\`$TEMPLATE'), ignoring TEMPLATE"
+ sleep 1
+ PRINT_INTERN=bad_prform
+ fi
+ fi
+
+ if [ -n "$TEMPLATE" -a -z "$PRINT_INTERN" ]; then
+ sed "s/<FROM>/$FROM/;s/<REPLYTO>/$REPLYTO/;s/<DEFAULT_ORIGINATOR>/$DEFAULT_ORIGINATOR/;s/<SUBMITTER>/$DEFAULT_SUBMITTERID/;s|<DEFAULT_ENVIRONMENT>|$DEFAULT_ENVIRONMENT|;s/<DEFAULT_BARF>/$DEFAULT_BARF/;s/<DEFAULT_VERSION>/$DEFAULT_VERSION/;" < $TEMPLATE > $TEMP ||
+ ( echo "$COMMAND: could not copy $TEMPLATE" ; xs=1; exit )
+ else
+ # Which genius thought of iterating through this loop twice, when the
+ # cp command would suffice?
+ for file in $TEMP ; do
+ cat > $file << '__EOF__'
+SEND-PR: -*- send-pr -*-
+SEND-PR: Lines starting with `SEND-PR' will be removed automatically, as
+SEND-PR: will all comments (text enclosed in `<' and `>').
+SEND-PR:
+SEND-PR: Please consult the send-pr man page `send-pr(1)' or the Texinfo
+SEND-PR: manual if you are not sure how to fill out a problem report.
+SEND-PR: Note that the Synopsis field is mandatory. The Subject (for
+SEND-PR: the mail) will be made the same as Synopsis unless explicitly
+SEND-PR: changed.
+SEND-PR:
+SEND-PR: Choose from the following categories:
+SEND-PR:
+__EOF__
+
+ # Format the categories so they fit onto lines.
+ CATEGORIES=`${BINDIR}/query-pr --valid-values Category`;
+ l=`echo "$CATEGORIES" | \
+ awk 'BEGIN {max = 0; } { if (length($0) > max) { max = length($0); } }
+ END {print max + 1;}'`
+ c=`expr 61 / $l`
+ if [ $c -eq 0 ]; then c=1; fi
+ echo "$CATEGORIES" | \
+ awk 'BEGIN {printf "SEND-PR: "; i = 0 }
+ { printf ("%-'$l'.'$l's", $0);
+ if ((++i % '$c') == 0) { printf "\nSEND-PR: " } }
+ END { printf "\nSEND-PR:\n"; }' >> $file
+
+ cat >> $file << __EOF__
+To: $MAILADDR
+Subject:
+From: $FROM
+Reply-To: $REPLYTO
+Cc: $CC
+X-send-pr-version: $VERSION
+X-GNATS-Notify:
+
+
+__EOF__
+
+ #
+ # Iterate through the list of input fields. fieldname is the
+ # name of the field. fmtname is the formatted name of the field,
+ # with >, : and extra spaces to cause the field contents to be
+ # aligned.
+ #
+ ${BINDIR}/query-pr --list-input-fields | awk '{a[NR]=$1""; mnr = NR+1; len = length($1) + 2; if (mlen < len) mlen = len; } END { for (x = 1; x < mnr; x++) { b = ">"a[x]":"; printf ("%s %-"mlen"s&\n", a[x], b); } }' | while read fieldname fmtname
+ do
+ fmtname="`echo "$fmtname" | sed 's/[&]$//;'`"
+ upname="`echo $fieldname | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;s/-//g;'`"
+ # Grab the default value for this field.
+ eval 'default_val="$DEFAULT_'${upname}'"'
+ # What's stored in the field?
+ type=`${BINDIR}/query-pr --field-type $fieldname | sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ case $type in
+ enum)
+ if [ "$default_val" != "" ]
+ then
+ desc=$default_val;
+ else
+ if [ "$fieldname" != "Category" ]
+ then
+ values=`${BINDIR}/query-pr --valid-values $fieldname | tr '\n' ' ' | sed 's/ *$//g;s/ / | /g;s/^/[ /;s/$/ ]/;'`
+ valslen=`echo "$values" | wc -c`
+ else
+ values="choose from a category listed above"
+ valslen=1;
+ fi
+ if [ "$valslen" -gt 160 ]
+ then
+ desc="<`${BINDIR}/query-pr --field-description $fieldname` (one line)>";
+ else
+ desc="<${values} (one line)>";
+ fi
+ dpat=`echo "$desc" | tr '\]\[*+^$|\()&/' '............'`
+ echo "/^>${fieldname}:/ s/${dpat}//" >> $FIXFIL
+ fi
+ echo "${fmtname}${desc}" >> $file
+ ;;
+ multitext)
+ if [ "$default_val" != "" ]
+ then
+ desc=" $default_val";
+ else
+ desc=" <`${BINDIR}/query-pr --field-description $fieldname` (multiple lines)>";
+ dpat=`echo "$desc" | tr '\]\[*+^$|\()&/' '............'`
+ echo "s/^${dpat}//" >> $FIXFIL
+ fi
+ echo "${fmtname}" >> $file;
+ echo "$desc" >> $file;
+ ;;
+ *)
+ if [ "$default_val" != "" ]
+ then
+ desc="${default_val}"
+ else
+ desc="<`${BINDIR}/query-pr --field-description $fieldname` (one line)>"
+ dpat=`echo "$desc" | tr '\]\[*+^$|\()&/' '............'`
+ echo "/^>${fieldname}:/ s/${dpat}//" >> $FIXFIL
+ fi
+ echo "${fmtname}${desc}" >> $file
+ ;;
+ esac
+ done
+ done
+ fi
+
+ if [ "$PRINT" = true -o "$PRINT_INTERN" = true ]; then
+ cat $TEMP
+ xs=0; exit
+ fi
+
+ if $BARF
+ then
+ ipsec barf >>$TEMP
+ else
+ ipsec look >>$TEMP
+ fi
+
+ cp $TEMP $REF
+
+ chmod u+w $TEMP
+ if [ -z "$REQUEST_ID" ]; then
+ eval $EDIT $TEMP
+ else
+ ed -s $TEMP << '__EOF__'
+/^Subject/s/^Subject:.*/Subject: request for a customer id/
+/^>Category/s/^>Category:.*/>Category: send-pr/
+w
+q
+__EOF__
+ fi
+
+ if cmp -s $REF $TEMP ; then
+ echo "$COMMAND: problem report not filled out, therefore not sent"
+ xs=1; exit
+ fi
+fi
+
+# TEMP is the PR that we are editing. When we're done, REF will contain
+# the final PR to be sent.
+
+while [ -z "$REQUEST_ID" ]; do
+ CNT=0
+
+ #
+ # Remove comments.
+ #
+ echo '/^SEND-PR:/d' >> $FIXFIL
+ sed -f $FIXFIL $TEMP > $REF
+
+ # REF now has the actual PR that we want to send.
+
+ #
+ # Check that synopsis is not empty.
+ #
+ if grep "^>Synopsis:[ ]*$" $REF > /dev/null
+ then
+ echo "$COMMAND: Synopsis must not be empty."
+ CNT=`expr $CNT + 1`
+ fi
+
+ if [ "x$MAILPROG" = "x" ]
+ then
+ # Since we're not using mail, use pr-edit to check the PR. We can't
+ # do much checking otherwise, sorry.
+ $LIBEXECDIR/pr-edit --check-initial < $REF || CNT=`expr $CNT + 1`
+ fi
+
+ [ $CNT -gt 0 -a -z "$BATCH" ] &&
+ echo "Errors were found with the problem report."
+
+ while true; do
+ if [ -z "$BATCH" ]; then
+ $ECHON1 "a)bort, e)dit or s)end? $ECHON2"
+ read input
+ else
+ if [ $CNT -eq 0 ]; then
+ input=s
+ else
+ input=a
+ fi
+ fi
+ case "$input" in
+ a*)
+ if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $TEMP $BAD
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ xs=1; exit
+ ;;
+ e*)
+ eval $EDIT $TEMP
+ continue 2
+ ;;
+ s*)
+ break 2
+ ;;
+ esac
+ done
+done
+
+#
+# Make sure the mail has got a Subject. If not, use the same as
+# in Synopsis.
+#
+
+if grep '^Subject:[ ]*$' $REF > /dev/null
+then
+ SYNOPSIS=`grep '^>Synopsis:' $REF | sed -e 's/^>Synopsis:[ ]*//'`
+ ed -s $REF << __EOF__
+/^Subject:/s/:.*\$/: $SYNOPSIS/
+w
+q
+__EOF__
+fi
+
+while :
+do
+ if [ "x$MAILPROG" != "x" ]
+ then
+ # Use mail to send the PR.
+ if $MAILPROG < $REF
+ then
+ echo "$COMMAND: problem report mailed"
+ xs=0; exit
+ else
+ echo "$MAILPROG failed!"
+ fi
+ else
+ if $LIBEXECDIR/pr-edit --submit < $REF; then
+ echo "$COMMAND: problem report filed"
+ xs=0; exit
+ else
+ echo "$COMMAND: the problem report is not sent."
+ fi
+ fi
+ while true
+ do
+ if [ -z "$BATCH" ]; then
+ $ECHON1 "a)bort or s)end? (file=$REF) $ECHON2"
+ read input
+ case "$input" in
+ a*)
+ break 2 ;;
+ s*)
+ break ;;
+ esac
+ else
+ break 2;
+ fi
+ done
+done
+
+if [ -z "$BATCH" ]; then
+ echo "$COMMAND: the problem report remains in $BAD and is not sent."
+ mv $TEMP $BAD
+else
+ echo "$COMMAND: the problem report is not sent, is in $REF."
+fi
+
+xs=1; exit;
+
+#
+# $Log: send-pr.in,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.10 2003/07/14 12:26:17 mcr
+# use | as delimitor for $DEFAULT_ENVIRONMENT.
+# switch | to \\| when in $DEFAULT_ENVIRONMENT.
+# this is due to PR#236 where the "uname" output
+# says GNU/Linux, screwing up sed.
+#
+# Revision 1.9 2003/02/03 21:51:06 mcr
+# if MAILPROG fails, then offer to try again.
+#
+# Revision 1.8 2002/12/10 02:28:13 mcr
+# adjusted template to use gnats-bugs@freeswan.org
+# fix sed script to deal with version sanitizer.
+#
+# Revision 1.7 2002/12/10 02:17:34 mcr
+# need to init variables first
+#
+# Revision 1.6 2002/12/10 02:16:23 mcr
+# adjusted send-pr to look at LIBDIR, not LIBEXECDIR
+#
+# Revision 1.5 2002/09/30 16:04:05 mcr
+# fix for sed bug in "send-pr"
+#
+# Revision 1.4 2002/04/24 07:36:10 mcr
+# Moved from ./utils/send-pr.sh,v
+#
+# Revision 1.3 2001/11/27 15:02:55 mcr
+# added rcsids.
+# fixed submission address to be freeswan-bugs@freeswan.org
+# use new ipsec --versioncode to get version info.
+#
+#
diff --git a/programs/setup/.cvsignore b/programs/setup/.cvsignore
new file mode 100644
index 000000000..146f275e0
--- /dev/null
+++ b/programs/setup/.cvsignore
@@ -0,0 +1 @@
+setup
diff --git a/programs/setup/Makefile b/programs/setup/Makefile
new file mode 100644
index 000000000..f12d452b2
--- /dev/null
+++ b/programs/setup/Makefile
@@ -0,0 +1,22 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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/02/10 11:28:15 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=setup
+EXTRA8MAN=setup.8
+
+include ../Makefile.program
diff --git a/programs/setup/setup.8 b/programs/setup/setup.8
new file mode 100644
index 000000000..e2980ee74
--- /dev/null
+++ b/programs/setup/setup.8
@@ -0,0 +1,142 @@
+.TH IPSEC_SETUP 8 "23 July 2001"
+.\" RCSID $Id: setup.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.SH NAME
+ipsec setup \- control IPsec subsystem
+.SH SYNOPSIS
+.B ipsec
+.B setup
+[
+.B \-\-show
+|
+.B \-\-showonly
+]
+command
+.SH DESCRIPTION
+.I Setup
+controls the FreeS/WAN IPsec subsystem,
+including both the Klips kernel code and the Pluto key-negotiation daemon.
+(It is a synonym for the ``rc'' script for the subsystem;
+the system runs the equivalent of
+.B "ipsec setup start"
+at boot time,
+and
+.B "ipsec setup stop"
+at shutdown time, more or less.)
+.PP
+The action taken depends on the specific
+.IR command ,
+and on the contents of the
+.B config
+.B setup
+section of the
+IPsec configuration file (\c
+.IR /etc/ipsec.conf ,
+see
+.IR ipsec.conf (5)).
+Current
+.IR command s
+are:
+.TP 10
+.B start
+start Klips and Pluto,
+including setting up Klips to do crypto operations on the
+interface(s) specified in the configuration file,
+and (if the configuration file so specifies)
+setting up manually-keyed connections and/or
+asking Pluto to negotiate automatically-keyed connections
+to other security gateways
+.TP
+.B stop
+shut down Klips and Pluto,
+including tearing down all existing crypto connections
+.TP
+.B restart
+equivalent to
+.B stop
+followed by
+.B start
+.TP
+.B status
+report the status of the subsystem;
+normally just reports
+.B "IPsec running"
+and
+.BR "pluto pid \fInnn\fP" ,
+or
+.BR "IPsec stopped" ,
+and exits with status 0,
+but will go into more detail (and exit with status 1)
+if something strange is found.
+(An ``illicit'' Pluto is one that does not match the process ID in
+Pluto's lock file;
+an ``orphaned'' Pluto is one with no lock file.)
+.PP
+The
+.B stop
+operation tries to clean up properly even if assorted accidents
+have occurred,
+e.g. Pluto having died without removing its lock file.
+If
+.B stop
+discovers that the subsystem is (supposedly) not running,
+it will complain,
+but will do its cleanup anyway before exiting with status 1.
+.PP
+Although a number of configuration-file parameters influence
+.IR setup 's
+operations, the key one is the
+.B interfaces
+parameter, which must be right or chaos will ensue.
+.PP
+The
+.B \-\-show
+and
+.B \-\-showonly
+options cause
+.I setup
+to display the shell commands that it would execute.
+.B \-\-showonly
+suppresses their execution.
+Only
+.BR start ,
+.BR stop ,
+and
+.B restart
+commands recognize these flags.
+.SH FILES
+.ta \w'/proc/sys/net/ipv4/ip_forward'u+2n
+/etc/rc.d/init.d/ipsec the script itself
+.br
+/etc/init.d/ipsec alternate location for the script
+.br
+/etc/ipsec.conf IPsec configuration file
+.br
+/proc/sys/net/ipv4/ip_forward forwarding control
+.br
+/var/run/ipsec.info saved information
+.br
+/var/run/pluto.pid Pluto lock file
+.br
+/var/run/ipsec_setup.pid IPsec lock file
+.SH SEE ALSO
+ipsec.conf(5), ipsec(8), ipsec_manual(8), ipsec_auto(8), route(8)
+.SH DIAGNOSTICS
+All output from the commands
+.B start
+and
+.B stop
+goes both to standard
+output and to
+.IR syslogd (8),
+via
+.IR logger (1).
+Selected additional information is logged only to
+.IR syslogd (8).
+.SH HISTORY
+Written for the FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+Old versions of
+.IR logger (1)
+inject spurious extra newlines onto standard output.
diff --git a/programs/setup/setup.in b/programs/setup/setup.in
new file mode 100755
index 000000000..1e43d0d67
--- /dev/null
+++ b/programs/setup/setup.in
@@ -0,0 +1,162 @@
+#!/bin/sh
+# IPsec startup and shutdown script
+# Copyright (C) 1998, 1999, 2001 Henry Spencer.
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: setup.in,v 1.1 2004/03/15 20:35:31 as Exp $
+#
+# ipsec init.d script for starting and stopping
+# the IPsec security subsystem (KLIPS and Pluto).
+#
+# This script becomes /etc/rc.d/init.d/ipsec (or possibly /etc/init.d/ipsec)
+# and is also accessible as "ipsec setup" (the preferred route for human
+# invocation).
+#
+# The startup and shutdown times are a difficult compromise (in particular,
+# it is almost impossible to reconcile them with the insanely early/late
+# times of NFS filesystem startup/shutdown). Startup is after startup of
+# syslog and pcmcia support; shutdown is just before shutdown of syslog.
+#
+# chkconfig: 2345 47 68
+# description: IPsec provides encrypted and authenticated communications; \
+# KLIPS is the kernel half of it, Pluto is the user-level management daemon.
+
+me='ipsec setup' # for messages
+
+
+# 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@}"
+
+if test " $IPSEC_DIR" = " " # if we were not called by the ipsec command
+then
+ # we must establish a suitable PATH ourselves
+ PATH="${IPSEC_SBINDIR}":/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin
+ export PATH
+
+ IPSEC_DIR="$IPSEC_LIBDIR"
+ export IPSEC_DIR IPSEC_CONFS IPSEC_LIBDIR IPSEC_EXECDIR
+fi
+
+# Check that the ipsec command is available.
+found=
+for dir in `echo $PATH | tr ':' ' '`
+do
+ if test -f $dir/ipsec -a -x $dir/ipsec
+ then
+ found=yes
+ break # NOTE BREAK OUT
+ fi
+done
+if ! test "$found"
+then
+ echo "cannot find ipsec command -- \`$1' aborted" |
+ logger -s -p daemon.error -t ipsec_setup
+ exit 1
+fi
+
+# accept a few flags
+
+export IPSEC_setupflags
+IPSEC_setupflags=""
+
+config=""
+
+for dummy
+do
+ case "$1" in
+ --showonly|--show) IPSEC_setupflags="$1" ;;
+ --config) config="--config $2" ; shift ;;
+ *) break ;;
+ esac
+ shift
+done
+
+
+# Pick up IPsec configuration (until we have done this, successfully, we
+# do not know where errors should go, hence the explicit "daemon.error"s.)
+# Note the "--export", which exports the variables created.
+eval `ipsec _confread $config --optional --varprefix IPSEC --export --type config setup`
+if test " $IPSEC_confreadstatus" != " "
+then
+ echo "$IPSEC_confreadstatus -- \`$1' aborted" |
+ logger -s -p daemon.error -t ipsec_setup
+ exit 1
+fi
+
+IPSEC_confreadsection=${IPSEC_confreadsection:-setup}
+export IPSEC_confreadsection
+
+IPSECsyslog=${IPSECsyslog-daemon.error}
+export IPSECsyslog
+
+# misc setup
+umask 022
+
+
+# do it
+case "$1" in
+ start|--start|stop|--stop|_autostop|_autostart)
+ if test " `id -u`" != " 0"
+ then
+ echo "permission denied (must be superuser)" |
+ logger -s -p $IPSECsyslog -t ipsec_setup 2>&1
+ exit 1
+ fi
+ tmp=/var/run/ipsec_setup.st
+ (
+ ipsec _realsetup $1
+ echo "$?" >$tmp
+ ) 2>&1 | logger -s -p $IPSECsyslog -t ipsec_setup 2>&1
+ st=$?
+ if test -f $tmp
+ then
+ st=`cat $tmp`
+ rm -f $tmp
+ fi
+ exit $st
+ ;;
+
+ restart|--restart|force-reload)
+ $0 $IPSEC_setupflags stop
+ $0 $IPSEC_setupflags start
+ ;;
+
+ _autorestart) # for internal use only
+ $0 $IPSEC_setupflags _autostop
+ $0 $IPSEC_setupflags _autostart
+ ;;
+
+ status|--status)
+ ipsec _realsetup $1
+ exit
+ ;;
+
+ --version)
+ echo "$me $IPSEC_VERSION"
+ exit 0
+ ;;
+
+ --help)
+ echo "Usage: $me {--start|--stop|--restart|--status}"
+ exit 0
+ ;;
+
+ *)
+ echo "Usage: $me {--start|--stop|--restart|--status}" >&2
+ exit 2
+esac
+
+exit 0
diff --git a/programs/showdefaults/.cvsignore b/programs/showdefaults/.cvsignore
new file mode 100644
index 000000000..609b55e81
--- /dev/null
+++ b/programs/showdefaults/.cvsignore
@@ -0,0 +1 @@
+showdefaults
diff --git a/programs/showdefaults/Makefile b/programs/showdefaults/Makefile
new file mode 100644
index 000000000..d2c8f9be8
--- /dev/null
+++ b/programs/showdefaults/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=showdefaults
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/showdefaults/showdefaults.8 b/programs/showdefaults/showdefaults.8
new file mode 100644
index 000000000..4a8db9c49
--- /dev/null
+++ b/programs/showdefaults/showdefaults.8
@@ -0,0 +1,34 @@
+.TH IPSEC_SHOWDEFAULTS 8 "23 Jan 2000"
+.\" RCSID $Id: showdefaults.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.SH NAME
+ipsec showdefaults \- show %defaultroute defaults
+.SH SYNOPSIS
+.B ipsec
+.B showdefaults
+.SH DESCRIPTION
+.I Showdefaults
+outputs (on standard output) a terse description of the defaults
+used by the
+.B %defaultroute
+facilities in
+.IR ipsec_auto (8)
+and
+.IR ipsec_manual (8).
+.PP
+Beware that the exact output format is subject to change.
+.SH DIAGNOSTICS
+Normal exit status is 0.
+If no defaults are available,
+i.e. the
+.B interfaces
+parameter in
+.B "config setup"
+is not
+.BR %defaultroute ,
+produces a message on standard error and exits with status 1.
+.SH FILES
+/var/run/ipsec.info
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
diff --git a/programs/showdefaults/showdefaults.in b/programs/showdefaults/showdefaults.in
new file mode 100755
index 000000000..67daf7fd8
--- /dev/null
+++ b/programs/showdefaults/showdefaults.in
@@ -0,0 +1,33 @@
+#! /bin/sh
+# show defaults for %defaultroute
+# Copyright (C) 2000 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: showdefaults.in,v 1.1 2004/03/15 20:35:31 as Exp $
+
+info=/var/run/ipsec.info
+me="ipsec showdefaults"
+
+case "$1" in
+--help) echo "Usage: ipsec showdefaults" ; exit 0 ;;
+--version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+esac
+
+# Pick up the info.
+if test -s $info
+then
+ sed -n '/^defaultroute/s/default//p' $info
+ sed -n '/^#dr:/s/dr://p' $info
+else
+ echo "$me: cannot find defaults file \`$info'" >&2
+ exit 1
+fi
diff --git a/programs/showhostkey/.cvsignore b/programs/showhostkey/.cvsignore
new file mode 100644
index 000000000..8496cd633
--- /dev/null
+++ b/programs/showhostkey/.cvsignore
@@ -0,0 +1 @@
+showhostkey
diff --git a/programs/showhostkey/Makefile b/programs/showhostkey/Makefile
new file mode 100644
index 000000000..db819c906
--- /dev/null
+++ b/programs/showhostkey/Makefile
@@ -0,0 +1,38 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=showhostkey
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2002/06/02 21:51:41 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/programs/showhostkey/showhostkey.8 b/programs/showhostkey/showhostkey.8
new file mode 100644
index 000000000..2c0043fca
--- /dev/null
+++ b/programs/showhostkey/showhostkey.8
@@ -0,0 +1,168 @@
+.TH IPSEC_SHOWHOSTKEY 8 "5 March 2002"
+.\" RCSID $Id: showhostkey.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.SH NAME
+ipsec showhostkey \- show host's authentication key
+.SH SYNOPSIS
+.B ipsec
+.B showhostkey
+[
+.B \-\-key
+] [
+.B \-\-left
+] [
+.B \-\-right
+] [
+.B \-\-txt
+gateway
+] [
+.B \-\-dhclient
+] [
+.B \-\-file
+secretfile
+] [
+.B \-\-id
+identity
+]
+.SH DESCRIPTION
+.I Showhostkey
+outputs (on standard output) a public key suitable for this host,
+in the format specified,
+using the host key information stored in
+.IR /etc/ipsec.secrets .
+In general only the super-user can run this command,
+since only he can read
+.IR ipsec.secrets .
+.PP
+The
+.B \-\-txt
+option causes the output to be in opportunistic-encryption DNS TXT record
+format,
+with the specified
+.I gateway
+value.
+If information about how the key was generated is available,
+that is provided as a DNS-file comment.
+For example,
+.B "\-\-txt 10.11.12.13"
+might give (with the key data trimmed for clarity):
+.PP
+.nf
+ ; RSA 2048 bits xy.example.com Sat Apr 15 13:53:22 2000
+ IN TXT "X-IPsec-Server(10)=10.11.12.13 AQOF8tZ2...+buFuFn/"
+.fi
+.PP
+No name is supplied in the TXT record
+because there are too many possibilities,
+depending on how it will be used.
+If the text string is longer than 255 bytes,
+it is split up into multiple strings (matching the restrictions of
+the DNS TXT binary format).
+If any split is needed, the first split will be at the start of the key:
+this increases the chances that later hand editing will work.
+.PP
+The
+.B \-\-left
+and
+.B \-\-right
+options cause the output to be in
+.IR ipsec.conf (5)
+format, as a
+.B leftrsasigkey
+or
+.B rightrsasigkey
+parameter respectively.
+Again, generation information is included if available.
+For example,
+.B \-\-left
+might give (with the key data trimmed down for clarity):
+.PP
+.nf
+ # RSA 2048 bits xy.example.com Sat Apr 15 13:53:22 2000
+ leftrsasigkey=0sAQOF8tZ2...+buFuFn/
+.fi
+.PP
+The
+.B \-\-dhclient
+option cause the output to be suitable for inclusion in
+.IR dhclient.conf (5)
+as part of configuring WAVEsec.
+See <http://www.wavesec.org>.
+.PP
+If
+.B \-\-key
+is specified,
+the output format is the text form of a DNS KEY record;
+the host name is the one included in the key information
+(or, if that is not available,
+the output of
+.BR "hostname\ \-\-fqdn" ),
+with a
+.B \&.
+appended.
+Again, generation information is included if available.
+For example (with the key data trimmed down for clarity):
+.PP
+.nf
+ ; RSA 2048 bits xy.example.com Sat Apr 15 13:53:22 2000
+ xy.example.com. IN KEY 0x4200 4 1 AQOF8tZ2...+buFuFn/
+.fi
+.PP
+Normally, the default key for this host
+(the one with no host identities specified for it) is the one extracted.
+The
+.B \-\-id
+option overrides this,
+causing extraction of the key labeled with the specified
+.IR identity ,
+if any.
+The specified
+.I identity
+must
+.I exactly
+match the identity in the file;
+in particular, the comparison is case-sensitive.
+.PP
+The
+.B \-\-file
+option overrides the default for where the key information should be
+found, and takes it from the specified
+.IR secretfile .
+.SH DIAGNOSTICS
+A complaint about ``no pubkey line found'' indicates that the
+host has a key but it was generated with an old version of FreeS/WAN
+and does not contain the information that
+.I showhostkey
+needs.
+.SH FILES
+/etc/ipsec.secrets
+.SH SEE ALSO
+ipsec.secrets(5), ipsec.conf(5), ipsec_rsasigkey(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org>
+by Henry Spencer.
+.SH BUGS
+Arguably,
+rather than just reporting the no-IN-KEY-line-found problem,
+.I showhostkey
+should be smart enough to run the existing key through
+.I rsasigkey
+with the
+.B \-\-oldkey
+option, to generate a suitable output line.
+.PP
+The need to specify the gateway address (etc.) for
+.B \-\-txt
+is annoying, but there is no good way to determine it automatically.
+.PP
+There should be a way to specify the priority value for TXT records;
+currently it is hardwired to
+.BR 10 .
+.PP
+The
+.B \-\-id
+option assumes that the
+.I identity
+appears on the same line as the
+.B ":\ RSA\ {"
+that begins the key proper.
diff --git a/programs/showhostkey/showhostkey.in b/programs/showhostkey/showhostkey.in
new file mode 100755
index 000000000..7194363e8
--- /dev/null
+++ b/programs/showhostkey/showhostkey.in
@@ -0,0 +1,180 @@
+#! /bin/sh
+# show key for this host, in DNS (or other) format
+# Copyright (C) 2000, 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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: showhostkey.in,v 1.1 2004/03/15 20:35:31 as Exp $
+
+me="ipsec showhostkey"
+usage="Usage: $me [--file secrets] [--left] [--right] [--txt gateway] [--id id]
+ [--dhclient]"
+
+file=/etc/ipsec.secrets
+fmt=""
+gw=
+id=
+for dummy
+do
+ case "$1" in
+ --key) fmt="dns" ;;
+ --file) file="$2" ; shift ;;
+ --left) fmt="left" ;;
+ --right) fmt="right" ;;
+ --dhclient) fmt="dhclient" ;;
+ --txt) fmt="txt" ; gw="$2" ; shift ;;
+ --wavesec) fmt="wavesec" ;;
+ --id) id="$2" ; shift ;;
+ --version) echo "$me $IPSEC_VERSION" ; exit 0 ;;
+ --help) echo "$usage" ; exit 0 ;;
+ --) shift ; break ;;
+ -*) echo "$me: unknown option \`$1'" >&2 ; exit 2 ;;
+ *) break ;;
+ esac
+ shift
+done
+if test " $fmt" = " "
+then
+ echo "$me: must specify a format for the result" >&2
+ exit 2
+fi
+if test " $fmt" = " txt" -a " $gw" = " "
+then
+ echo "$me: --txt gateway value cannot be empty" >&2
+ exit 2
+fi
+
+if test ! -f $file
+then
+ echo "$me: file \`$file' does not exist" >&2
+ exit 1
+elif test ! -r $file
+then
+ echo "$me: permission denied (cannot read \`$file')" >&2
+ exit 1
+fi
+
+host="`hostname --fqdn`"
+
+awk ' BEGIN {
+ inkey = 0
+ seenkey = 0
+ nfound = 0
+ err = "cat >&2"
+ me = "'"$me"'"
+ host = "'"$host"'"
+ file = "'"$file"'"
+ fmt = "'"$fmt"'"
+ gw = "'"$gw"'"
+ id = "'"$id"'"
+ comment = ""
+ s = "[ \t]+"
+ os = "[ \t]*"
+ x = "[^ \t]+"
+ oc = "(#.*)?"
+ suffix = ":" os "[rR][sS][aA]" os "{" os oc "$"
+ if (id == "") {
+ pat = "^" suffix
+ printid = "default"
+ } else {
+ pat = "^(" x s ")*" id "(" s x ")*" os suffix
+ printid = quote(id)
+ }
+ paydirt = "^[ \t]+#pubkey=0s"
+ status = 0
+ }
+ $0 ~ pat {
+ inkey = 1
+ seenkey = 1
+ }
+ /^[ \t]+}$/ {
+ inkey = 0
+ }
+ inkey && $0 ~ /^[ \t]+# RSA [0-9]+ bits/ {
+ comment = $0
+ if (fmt == "dns" || fmt == "txt" || fmt == "dhclient")
+ sub(/^[ \t]+#/, "#", comment)
+ host = $5
+ }
+ inkey && $0 ~ /^[ \t]+#pubkey=0s/ {
+
+ }
+ inkey && fmt == "dns" && $0 ~ paydirt {
+ out = $0
+ sub(paydirt, (host ".\tIN\tKEY\t0x4200 4 1 "), out)
+ nfound++
+ }
+ inkey && fmt == "dhclient" && $0 ~ paydirt {
+ # NOT YET ADJUSTED TO KEY RR elimination
+ boilerplate = "option oe-key code 159 = string;\n" \
+ "option oe-gateway code 160 = ip-address;\n" \
+ "send oe-key = "
+ out = $0
+ sub(paydirt, "0x4200 4 1 ", out)
+ out = "option oe-key code 159 = string;\n" \
+ "option oe-gateway code 160 = ip-address;\n" \
+ "send oe-key = " quote(out) ";"
+ nfound++
+ }
+ inkey && fmt == "txt" && $0 ~ paydirt {
+ if (gw !~ /^@/ && gw !~ /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/ )
+ {
+ grump("gateway must be @FQDN or IPv4 address, not " quote(gw))
+ exit(status)
+ }
+ out = $0
+ gsub(/[ \t]+/, " ", out)
+ sub(paydirt, "", out)
+ out = " " out
+ str = "X-IPsec-Server(10)=" gw
+ if (length(str) < 255 && length(str) + length(out) > 255) {
+ str = " " quote(str)
+ } else {
+ out = str out
+ str = ""
+ }
+ while (length(out) > 255) {
+ str = str " " quote(substr(out, 1, 255))
+ out = substr(out, 256)
+ }
+ if (length(out) > 0)
+ str = str " " quote(out)
+ out = "\tIN\tTXT\t" substr(str, 2)
+ nfound++
+ }
+ inkey && (fmt == "left" || fmt == "right") && $0 ~ /^[ \t]+#pubkey=/ {
+ out = $0
+ sub(/^[ \t]+#pubkey=/, ("\t" fmt "rsasigkey="), out)
+ nfound++
+ }
+ function quote(s) {
+ return "\"" s "\""
+ }
+ function grump(s) {
+ print me ": " s |err
+ status = 1
+ }
+ END {
+ if (status != 0)
+ exit(status)
+ if (!seenkey)
+ grump("no " printid " key in " quote(file))
+ else if (nfound == 0) {
+ grump("no pubkey line found -- key information old?")
+ } else if (nfound > 1)
+ grump("multiple " printid " keys found!?!")
+ else {
+ if (comment != "")
+ print comment
+ print out
+ }
+ exit(status)
+ }' $file
diff --git a/programs/showpolicy/.cvsignore b/programs/showpolicy/.cvsignore
new file mode 100644
index 000000000..e4fad4e23
--- /dev/null
+++ b/programs/showpolicy/.cvsignore
@@ -0,0 +1 @@
+showpolicy
diff --git a/programs/showpolicy/Makefile b/programs/showpolicy/Makefile
new file mode 100644
index 000000000..b3ea5a0a8
--- /dev/null
+++ b/programs/showpolicy/Makefile
@@ -0,0 +1,38 @@
+# Makefile for the KLIPS interface utilities
+# Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=showpolicy
+EXTRA5PROC=${PROGRAM}.8
+
+LIBS=${POLICYLIB} ${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.2 2003/05/14 02:12:27 mcr
+# addition of CGI-focused interface to policy lookup interface
+#
+# Revision 1.1 2003/05/11 00:45:08 mcr
+# program to interogate ipsec policy of stdin.
+# run this from inetd.
+#
+#
diff --git a/programs/showpolicy/showpolicy.8 b/programs/showpolicy/showpolicy.8
new file mode 100644
index 000000000..4fbc2e40e
--- /dev/null
+++ b/programs/showpolicy/showpolicy.8
@@ -0,0 +1,41 @@
+.TH IPSEC_SHOWPOLICY 8 "7 May 2003"
+.\"
+.\" RCSID $Id: showpolicy.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec showpolicy \- dump policy of socket found as stdin
+.SH SYNOPSIS
+.PP
+.B ipsec
+.B showpolicy
+.PP
+.SH DESCRIPTION
+.I showpolicy
+calls the
+.IR ipsec_policy_lookup (3)
+function on the file description which is its stdin.
+.PP
+It then dumps the resulting query in a human readable form.
+.PP
+This is a test program. One might run it from inetd, via:
+.TP
+discard stream tcp nowait nobody /usr/local/libexec/ipsec/showpolicy showpolicy
+.SH FILES
+/var/run/ipsecpolicy.ctl
+.SH "SEE ALSO"
+ipsec(8), ipsec_policy_query(3), ipsec_pluto(8)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Michael Richardson
+.SH BUGS
+.\"
+.\" $Log: showpolicy.8,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.1 2003/05/11 00:45:08 mcr
+.\" program to interogate ipsec policy of stdin.
+.\" run this from inetd.
+.\"
+.\"
diff --git a/programs/showpolicy/showpolicy.c b/programs/showpolicy/showpolicy.c
new file mode 100644
index 000000000..114cc3936
--- /dev/null
+++ b/programs/showpolicy/showpolicy.c
@@ -0,0 +1,251 @@
+/*
+ * A program to dump the IPsec status of the socket found on stdin.
+ * Run me from inetd, for instance.
+ * Copyright (C) 2003 Michael Richardson <mcr@freeswan.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char showpolicy_version[] = "RCSID $Id: showpolicy.c,v 1.1 2004/03/15 20:35:31 as Exp $";
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <getopt.h>
+#include "freeswan.h"
+#include "freeswan/ipsec_policy.h"
+
+char *program_name;
+
+static void
+help(void)
+{
+ fprintf(stderr,
+ "Usage:\n\n"
+ "showpolicy"
+ " [--cgi] lookup the particulars from CGI variables.\n"
+ " [--socket] lookup the particulars from the socket on stdin.\n"
+ " [--textual] dump output in human friendly form\n"
+ " [--plaintext X] string to dump if no security\n"
+ " [--vpntext X] string to dump if VPN configured tunnel\n"
+ " [--privacytext X] string to dump if just plain DNS OE\n"
+ " [--dnssectext X] string to dump if just DNSSEC OE\n"
+ "\n\n"
+ "FreeS/WAN %s\n",
+ ipsec_version_code());
+}
+
+static const struct option long_opts[] = {
+ /* name, has_arg, flag, val */
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "socket", no_argument, NULL, 'i' },
+ { "cgi", no_argument, NULL, 'g' },
+ { "textual", no_argument, NULL, 't' },
+ { "plaintext", required_argument, NULL, 'c' },
+ { "vpntext", required_argument, NULL, 'v' },
+ { "privacytext", required_argument, NULL, 'p' },
+ { "dnssectext", required_argument, NULL, 's' },
+ { 0,0,0,0 }
+};
+
+void dump_policyreply(struct ipsec_policy_cmd_query *q)
+{
+ char src[ADDRTOT_BUF], dst[ADDRTOT_BUF];
+
+ /* now print it! */
+ addrtot(&q->query_local, 0, src, sizeof(src));
+ addrtot(&q->query_remote, 0, dst, sizeof(dst));
+
+ printf("Results of query on %s -> %s with seq %d\n",
+ src, dst, q->head.ipm_msg_seq);
+
+ printf("Received reply of %d bytes.\n", q->head.ipm_msg_len);
+
+ printf("Strength: %d\n", q->strength);
+ printf("Bandwidth: %d\n", q->bandwidth);
+ printf("authdetail: %d\n", q->auth_detail);
+ printf("esp_detail: %d\n", q->esp_detail);
+ printf("comp_detail: %d\n",q->comp_detail);
+
+ printf("credentials: %d\n", q->credential_count);
+ if(q->credential_count > 0) {
+ int c;
+
+ for(c=0; c<q->credential_count; c++) {
+ switch(q->credentials[c].ii_format) {
+ case CERT_DNS_SIGNED_KEY:
+ printf("\tDNSSEC identity: %s (SIG %s)\n",
+ q->credentials[c].ii_credential.ipsec_dns_signed.fqdn,
+ q->credentials[c].ii_credential.ipsec_dns_signed.dns_sig);
+ break;
+
+ case CERT_RAW_RSA:
+ printf("\tlocal identity: %s\n",
+ q->credentials[c].ii_credential.ipsec_raw_key.id_name);
+
+ case CERT_NONE:
+ printf("\tDNS identity: %s\n",
+ q->credentials[c].ii_credential.ipsec_dns_signed.fqdn);
+ break;
+
+ default:
+ printf("\tUnknown identity type %d", q->credentials[c].ii_format);
+ break;
+ }
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct ipsec_policy_cmd_query q;
+ err_t ret;
+ int c;
+
+ /* set the defaults */
+ char lookup_style = 'i';
+ char output_style = 's';
+
+ char *plaintext = "clear";
+ char *vpntext = "vpn";
+ char *privacytext = "private";
+ char *dnssectext = "secure";
+
+ while((c = getopt_long(argc, argv, "hVighc:v:p:s:", long_opts, 0))!=EOF) {
+ switch (c) {
+ default:
+ case 'h': /* --help */
+ help();
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'V': /* --version */
+ fprintf(stderr, "FreeS/WAN %s\n", ipsec_version_code());
+ return 0; /* GNU coding standards say to stop here */
+
+ case 'i':
+ if(isatty(0)) {
+ printf("please run this connected to a socket\n");
+ exit(1);
+ }
+
+ lookup_style = 'i';
+ break;
+
+ case 'g':
+ lookup_style = 'g';
+ break;
+
+ case 't':
+ output_style = 't';
+ break;
+
+ case 'c':
+ plaintext = optarg;
+ break;
+
+ case 'v':
+ vpntext = optarg;
+ break;
+
+ case 'p':
+ privacytext = optarg;
+ break;
+
+ case 's':
+ dnssectext = optarg;
+ break;
+ }
+ }
+
+ if((ret = ipsec_policy_init()) != NULL) {
+ perror(ret);
+ exit(2);
+ }
+
+ switch(lookup_style) {
+ case 'i':
+ if((ret = ipsec_policy_lookup(0, &q)) != NULL) {
+ perror(ret);
+ exit(3);
+ }
+ break;
+
+ case 'g':
+ if((ret = ipsec_policy_cgilookup(&q)) != NULL) {
+ perror(ret);
+ exit(3);
+ }
+ break;
+
+ default:
+ abort();
+ break;
+ }
+
+
+ if(output_style == 't') {
+ dump_policyreply(&q);
+ } else {
+ /* start by seeing if there was any crypto */
+ if(q.strength < IPSEC_PRIVACY_PRIVATE) {
+ /* no, so say clear */
+ puts(plaintext);
+ exit(0);
+ }
+
+ /* we now it is crypto, but authentic is it? */
+ if(q.credential_count == 0) {
+ puts(vpntext);
+ exit(0);
+ }
+
+ switch(q.credentials[0].ii_format) {
+ case CERT_DNS_SIGNED_KEY:
+ puts(dnssectext);
+ exit(0);
+
+ case CERT_RAW_RSA:
+ puts(vpntext);
+ exit(0);
+
+ default:
+ puts(privacytext);
+ exit(0);
+ }
+ }
+
+ exit(0);
+}
+
+/*
+ * $Log: showpolicy.c,v $
+ * Revision 1.1 2004/03/15 20:35:31 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.4 2003/05/14 15:46:44 mcr
+ * switch statement was missing break statements and was running on.
+ *
+ * Revision 1.3 2003/05/14 02:12:27 mcr
+ * addition of CGI-focused interface to policy lookup interface
+ *
+ * Revision 1.2 2003/05/13 03:25:34 mcr
+ * print credentials, if any were provided.
+ *
+ * Revision 1.1 2003/05/11 00:45:08 mcr
+ * program to interogate ipsec policy of stdin.
+ * run this from inetd.
+ *
+ *
+ *
+ */
diff --git a/programs/spi/.cvsignore b/programs/spi/.cvsignore
new file mode 100644
index 000000000..c928c4b77
--- /dev/null
+++ b/programs/spi/.cvsignore
@@ -0,0 +1 @@
+spi
diff --git a/programs/spi/Makefile b/programs/spi/Makefile
new file mode 100644
index 000000000..10a1eaa9c
--- /dev/null
+++ b/programs/spi/Makefile
@@ -0,0 +1,69 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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 2004/03/22 21:53:21 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=spi
+EXTRA5PROC=${PROGRAM}.5
+
+LIBS=${FREESWANLIB}
+
+OBJS=constants.o alg_info.o kernel_alg.o
+
+include ../Makefile.program
+
+constants.o : ../pluto/constants.c ../pluto/constants.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+alg_info.o : ../pluto/alg_info.c ../pluto/alg_info.h
+ $(CC) $(CFLAGS) -DNO_PLUTO -c -o $@ $<
+
+kernel_alg.o : ../pluto/kernel_alg.c ../pluto/kernel_alg.h
+ $(CC) $(CFLAGS) -DNO_PLUTO -c -o $@ $<
+
+#
+# $Log: Makefile,v $
+# Revision 1.2 2004/03/22 21:53:21 as
+# merged alg-0.8.1 branch with HEAD
+#
+# Revision 1.1.4.1 2004/03/16 09:48:22 as
+# alg-0.8.1rc12 patch merged
+#
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 2002/06/02 21:51:41 mcr
+# changed TOPDIR->FREESWANSRCDIR in all Makefiles.
+# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the
+# kernel sense.)
+#
+# Revision 1.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
diff --git a/programs/spi/spi.5 b/programs/spi/spi.5
new file mode 100644
index 000000000..a8faebee4
--- /dev/null
+++ b/programs/spi/spi.5
@@ -0,0 +1,213 @@
+.TH IPSEC_SPI 5 "26 Jun 2000"
+.\"
+.\" RCSID $Id: spi.5,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec_spi \- list IPSEC Security Associations
+.SH SYNOPSIS
+.B ipsec
+.B spi
+.PP
+.B cat
+.B /proc/net/ipsec_spi
+.PP
+.SH DESCRIPTION
+.I /proc/net/ipsec_spi
+is a read-only file that lists the current IPSEC Security Associations.
+A Security Association (SA) is a transform through which packet contents
+are to be processed before being forwarded. A transform can be an
+IPv4-in-IPv4 or IPv6-in-IPv6 encapsulation, an IPSEC Authentication Header (authentication
+with no encryption), or an IPSEC Encapsulation Security Payload
+(encryption, possibly including authentication).
+.PP
+When a packet is passed from a higher networking layer through an IPSEC
+virtual interface, a search in the extended routing table (see
+.IR ipsec_eroute (5))
+yields
+a IP protocol number
+,
+a Security Parameters Index (SPI)
+and
+an effective destination address
+.
+When an IPSEC packet arrives from the network,
+its ostensible destination, an SPI and an IP protocol
+specified by its outermost IPSEC header are used.
+The destination/SPI/protocol combination is used to select a relevant SA.
+(See
+.IR ipsec_spigrp (5)
+for discussion of how multiple transforms are combined.)
+.PP
+An
+.I spi ,
+.I proto,
+.I daddr
+and
+.IR address_family
+arguments specify an SAID.
+.I Proto
+is an ASCII string, "ah", "esp", "comp" or "tun", specifying the IP protocol.
+.I Spi
+is a number, preceded by '.' indicating hexadecimal and IPv4 or by ':' indicating hexadecimal and IPv6,
+where each hexadecimal digit represents 4 bits,
+between
+.B 0x100
+and
+.BR 0xffffffff ;
+values from
+.B 0x0
+to
+.B 0xff
+are reserved.
+.I Daddr
+is a dotted-decimal IPv4 destination address or a coloned hex IPv6 destination address.
+.PP
+An
+.I SAID
+combines the three parameters above, such as: "tun.101@1.2.3.4" for IPv4 or "tun:101@3049:1::1" for IPv6
+.PP
+A table entry consists of:
+.IP + 3
+.BR SAID
+.IP +
+<transform name (proto,encalg,authalg)>:
+.IP +
+direction (dir=)
+.IP +
+source address (src=)
+.IP +
+source and destination addresses and masks for inner header policy check
+addresses (policy=), as dotted-quads or coloned hex, separated by '->',
+for IPv4-in-IPv4 or IPv6-in-IPv6 SAs only
+.IP +
+initialisation vector length and value (iv_bits=, iv=) if non-zero
+.IP +
+out-of-order window size, number of out-of-order errors, sequence
+number, recently received packet bitmask, maximum difference between
+sequence numbers (ooowin=, ooo_errs=, seq=, bit=, max_seq_diff=) if SA
+is AH or ESP and if individual items are non-zero
+.IP +
+extra flags (flags=) if any are set
+.IP +
+authenticator length in bits (alen=) if non-zero
+.IP +
+authentication key length in bits (aklen=) if non-zero
+.IP +
+authentication errors (auth_errs=) if non-zero
+.IP +
+encryption key length in bits (eklen=) if non-zero
+.IP +
+encryption size errors (encr_size_errs=) if non-zero
+.IP +
+encryption padding error warnings (encr_pad_errs=) if non-zero
+.IP +
+lifetimes legend, c=Current status, s=Soft limit when exceeded will
+initiate rekeying, h=Hard limit will cause termination of SA (life(c,s,h)=)
+.IP + 6
+number of connections to which the SA is allocated (c), that will cause a
+rekey (s), that will cause an expiry (h) (alloc=), if any value is non-zero
+.IP +
+number of bytes processesd by this SA (c), that will cause a rekey (s), that
+will cause an expiry (h) (bytes=), if any value is non-zero
+.IP +
+time since the SA was added (c), until rekey (s), until expiry (h), in seconds (add=)
+.IP +
+time since the SA was first used (c), until rekey (s), until expiry (h), in seconds (used=),
+if any value is non-zero
+.IP +
+number of packets processesd by this SA (c), that will cause a rekey (s), that
+will cause an expiry (h) (packets=), if any value is non-zero
+.IP + 3
+time since the last packet was processed, in seconds (idle=), if SA has
+been used
+.IP
+average compression ratio (ratio=)
+.SH EXAMPLES
+.B "tun.12a@192.168.43.1 IPIP: dir=out src=192.168.43.2"
+.br
+.B " life(c,s,h)=bytes(14073,0,0)add(269,0,0)"
+.br
+.B " use(149,0,0)packets(14,0,0)"
+.br
+.B " idle=23
+.LP
+is an outbound IPv4-in-IPv4 (protocol 4) tunnel-mode SA set up between machines
+192.168.43.2 and 192.168.43.1 with an SPI of 12a in hexadecimal that has
+passed about 14 kilobytes of traffic in 14 packets since it was created,
+269 seconds ago, first used 149 seconds ago and has been idle for 23
+seconds.
+.LP
+.B "esp:9a35fc02@3049:1::1 ESP_3DES_HMAC_MD5:"
+.br
+.B " dir=in src=9a35fc02@3049:1::2"
+.br
+.B " ooowin=32 seq=7149 bit=0xffffffff"
+.br
+.B " alen=128 aklen=128 eklen=192"
+.br
+.B " life(c,s,h)=bytes(1222304,0,0)add(4593,0,0)"
+.br
+.B " use(3858,0,0)packets(7149,0,0)"
+.br
+.B " idle=23"
+.LP
+is an inbound Encapsulating Security Payload (protocol 50) SA on machine
+3049:1::1 with an SPI of 9a35fc02 that uses 3DES as the encryption
+cipher, HMAC MD5 as the authentication algorithm, an out-of-order
+window of 32 packets, a present sequence number of 7149, every one of
+the last 32 sequence numbers was received, the authenticator length and
+keys is 128 bits, the encryption key is 192 bits (actually 168 for 3DES
+since 1 of 8 bits is a parity bit), has passed 1.2 Mbytes of data in
+7149 packets, was added 4593 seconds ago, first used
+3858 seconds ago and has been idle for 23 seconds.
+.LP
+.SH FILES
+/proc/net/ipsec_spi, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(5), ipsec_eroute(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_spi(8), ipsec_version(5),
+ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.SH BUGS
+The add and use times are awkward, displayed in seconds since machine
+start. It would be better to display them in seconds before now for
+human readability.
+.\"
+.\" $Log: spi.5,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.9 2002/04/24 07:35:39 mcr
+.\" Moved from ./klips/utils/spi.5,v
+.\"
+.\" Revision 1.8 2001/08/01 23:22:44 rgb
+.\" Fix inconsistancies between manpage and output.
+.\"
+.\" Revision 1.7 2000/11/30 16:47:28 rgb
+.\" Added src= to /proc/net/ipsec_spi manpage.
+.\"
+.\" Revision 1.6 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.5 2000/09/13 15:54:32 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.4 2000/07/05 17:24:03 rgb
+.\" Updated for relative, rather than absolute values for addtime and
+.\" usetime.
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/28 12:44:12 henry
+.\" format touchup
+.\"
+.\" Revision 1.1 2000/06/28 05:43:00 rgb
+.\" Added manpages for all 5 klips utils.
+.\"
+.\"
diff --git a/programs/spi/spi.8 b/programs/spi/spi.8
new file mode 100644
index 000000000..fe6537c07
--- /dev/null
+++ b/programs/spi/spi.8
@@ -0,0 +1,525 @@
+.TH IPSEC_SPI 8 "23 Oct 2001"
+.\"
+.\" RCSID $Id: spi.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec spi \- manage IPSEC Security Associations
+.SH SYNOPSIS
+.br
+Note: In the following,
+.br
+.B <SA>
+means:
+.B \-\-af
+(inet | inet6)
+.B \-\-edst
+daddr
+.B \-\-spi
+spi
+.B \-\-proto
+proto OR
+.B \-\-said
+said,
+.br
+.B <life>
+means:
+.B \-\-life
+(soft | hard)\-(allocations | bytes | addtime | usetime | packets)=value[,...]
+.PP
+.B ipsec
+.B spi
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-src
+src
+.B \-\-ah
+.BR hmac-md5-96 | hmac-sha1-96
+[
+.B \-\-replay_window
+replayw ]
+[
+.B <life>
+]
+.B \-\-authkey
+akey
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-src
+src
+.B \-\-esp
+.BR 3des
+[
+.B \-\-replay_window
+replayw ]
+[
+.B <life>
+]
+.B \-\-enckey
+ekey
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-src
+src
+.B \-\-esp
+.BR 3des-md5-96 | 3des-sha1-96
+[
+.B \-\-replay_window
+replayw ]
+[
+.B <life>
+]
+.B \-\-enckey
+ekey
+.B \-\-authkey
+akey
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-src
+src
+.B \-\-comp
+.BR deflate
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-ip4
+.B \-\-src
+encap-src
+.B \-\-dst
+encap-dst
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-ip6
+.B \-\-src
+encap-src
+.B \-\-dst
+encap-dst
+.PP
+.B ipsec
+.B spi
+.B <SA>
+.B \-\-del
+.PP
+.B ipsec
+.B spi
+.B \-\-help
+.PP
+.B ipsec
+.B spi
+.B \-\-version
+.PP
+.B ipsec
+.B spi
+.B \-\-clear
+.PP
+.SH DESCRIPTION
+.I Spi
+creates and deletes IPSEC Security Associations.
+A Security Association (SA) is a transform through which packet
+contents are to be processed before being forwarded.
+A transform can be an IPv4-in-IPv4 or an IPv6-in-IPv6 encapsulation,
+an IPSEC Authentication Header (authentication with no encryption),
+or an IPSEC Encapsulation Security Payload (encryption, possibly
+including authentication).
+.PP
+When a packet is passed from a higher networking layer
+through an IPSEC virtual interface,
+a search in the extended routing table (see
+.IR ipsec_eroute (8))
+yields an effective destination address, a
+Security Parameters Index (SPI) and a IP protocol number.
+When an IPSEC packet arrives from the network,
+its ostensible destination, an SPI and an IP protocol
+specified by its outermost IPSEC header are used.
+The destination/SPI/protocol combination is used to select a relevant SA.
+(See
+.IR ipsec_spigrp (8)
+for discussion of how multiple transforms are combined.)
+.PP
+The
+.IR af ,
+.IR daddr ,
+.I spi
+and
+.I proto
+arguments specify the SA to be created or deleted.
+.I af
+is the address family (inet for IPv4, inet6 for IPv6).
+.I Daddr
+is a destination address
+in dotted-decimal notation for IPv4
+or in a coloned hex notation for IPv6.
+.I Spi
+is a number, preceded by '0x' for hexadecimal,
+between
+.B 0x100
+and
+.BR 0xffffffff ;
+values from
+.B 0x0
+to
+.B 0xff
+are reserved.
+.I Proto
+is an ASCII string, "ah", "esp", "comp" or "tun", specifying the IP protocol.
+The protocol must agree with the algorithm selected.
+.PP
+Alternatively, the
+.I said
+argument can also specify an SA to be created or deleted.
+.I Said
+combines the three parameters above, such as: "tun.101@1.2.3.4" or "tun:101@1:2::3:4",
+where the address family is specified by "." for IPv4 and ":" for IPv6. The address
+family indicators substitute the "0x" for hexadecimal.
+.PP
+The source address,
+.IR src ,
+must also be provided for the inbound policy check to
+function. The source address does not need to be included if inbound
+policy checking has been disabled.
+.PP
+Keys vectors must be entered as hexadecimal or base64 numbers.
+They should be cryptographically strong random numbers.
+.PP
+All hexadecimal numbers are entered as strings of hexadecimal digits
+(0-9 and a-f), without spaces, preceded by '0x', where each hexadecimal
+digit represents 4 bits.
+All base64 numbers are entered as strings of base64 digits
+ (0-9, A-Z, a-z, '+' and '/'), without spaces, preceded by '0s',
+where each hexadecimal digit represents 6 bits and '=' is used for padding.
+.PP
+The deletion of an SA which has been grouped will result in the entire chain
+being deleted.
+.PP
+The form with no additional arguments lists the contents of
+/proc/net/ipsec_spi. The format of /proc/net/ipsec_spi is discussed in
+ipsec_spi(5).
+.PP
+The lifetime severity of
+.B soft
+sets a limit when the key management daemons are asked to rekey the SA.
+The lifetime severity of
+.B hard
+sets a limit when the SA must expire.
+The lifetime type
+.B allocations
+tells the system when to expire the SA because it is being shared by too many
+eroutes (not currently used). The lifetime type of
+.B bytes
+tells the system to expire the SA after a certain number of bytes have been
+processed with that SA. The lifetime type of
+.B addtime
+tells the system to expire the SA a certain number of seconds after the SA was
+installed. The lifetime type of
+.B usetime
+tells the system to expire the SA a certain number of seconds after that SA has
+processed its first packet. The lifetime type of
+.B packets
+tells the system to expire the SA after a certain number of packets have been
+processed with that SA.
+.SH OPTIONS
+.TP 10
+.B \-\-af
+specifies the address family (inet for IPv4, inet6 for IPv6)
+.TP
+.B \-\-edst
+specifies the effective destination
+.I daddr
+of the Security Association
+.TP
+.B \-\-spi
+specifies the Security Parameters Index
+.I spi
+of the Security Association
+.TP
+.B \-\-proto
+specifies the IP protocol
+.I proto
+of the Security Association
+.TP
+.B \-\-said
+specifies the Security Association in monolithic format
+.TP
+.B \-\-ah
+add an SA for an IPSEC Authentication Header,
+specified by the following transform identifier
+(\c
+.BR hmac-md5-96
+or
+.BR hmac-sha1-96 )
+(RFC2402, obsoletes RFC1826)
+.TP
+.B hmac-md5-96
+transform following the HMAC and MD5 standards,
+using a 128-bit
+.I key
+to produce a 96-bit authenticator (RFC2403)
+.TP
+.B hmac-sha1-96
+transform following the HMAC and SHA1 standards,
+using a 160-bit
+.I key
+to produce a 96-bit authenticator (RFC2404)
+.TP
+.B \-\-esp
+add an SA for an IPSEC Encapsulation Security Payload,
+specified by the following
+transform identifier (\c
+.BR 3des ,
+or
+.BR 3des-md5-96 )
+(RFC2406, obsoletes RFC1827)
+.TP
+.B 3des
+encryption transform following the Triple-DES standard in
+Cipher-Block-Chaining mode using a 64-bit
+.I iv
+(internally generated) and a 192-bit 3DES
+.I ekey
+(RFC2451)
+.TP
+.B 3des-md5-96
+encryption transform following the Triple-DES standard in
+Cipher-Block-Chaining mode with authentication provided by
+HMAC and MD5
+(96-bit authenticator),
+using a 64-bit
+.IR iv
+(internally generated), a 192-bit 3DES
+.I ekey
+and a 128-bit HMAC-MD5
+.I akey
+(RFC2451, RFC2403)
+.TP
+.B 3des-sha1-96
+encryption transform following the Triple-DES standard in
+Cipher-Block-Chaining mode with authentication provided by
+HMAC and SHA1
+(96-bit authenticator),
+using a 64-bit
+.IR iv
+(internally generated), a 192-bit 3DES
+.I ekey
+and a 160-bit HMAC-SHA1
+.I akey
+(RFC2451, RFC2404)
+.TP
+.BR \-\-replay_window " replayw"
+sets the replay window size; valid values are decimal, 1 to 64
+.TP
+.BR \-\-life " life_param[,life_param]"
+sets the lifetime expiry; the format of
+.B life_param
+consists of a comma-separated list of lifetime specifications without spaces;
+a lifetime specification is comprised of a severity of
+.BR soft " or " hard
+followed by a '-', followed by a lifetime type of
+.BR allocations ", " bytes ", " addtime ", " usetime " or " packets
+followed by an '=' and finally by a value
+.TP
+.B \-\-comp
+add an SA for IPSEC IP Compression,
+specified by the following
+transform identifier (\c
+.BR deflate )
+(RFC2393)
+.TP
+.B deflate
+compression transform following the patent-free Deflate compression algorithm
+(RFC2394)
+.TP
+.B \-\-ip4
+add an SA for an IPv4-in-IPv4
+tunnel from
+.I encap-src
+to
+.I encap-dst
+.TP
+.B \-\-ip6
+add an SA for an IPv6-in-IPv6
+tunnel from
+.I encap-src
+to
+.I encap-dst
+.TP
+.B \-\-src
+specify the source end of an IP-in-IP tunnel from
+.I encap-src
+to
+.I encap-dst
+and also specifies the source address of the Security Association to be
+used in inbound policy checking and must be the same address
+family as
+.I af
+and
+.I edst
+.TP
+.B \-\-dst
+specify the destination end of an IP-in-IP tunnel from
+.I encap-src
+to
+.I encap-dst
+.TP
+.B \-\-del
+delete the specified SA
+.TP
+.BR \-\-clear
+clears the table of
+.BR SA s
+.TP
+.BR \-\-help
+display synopsis
+.TP
+.BR \-\-version
+display version information
+.SH EXAMPLES
+To keep line lengths down and reduce clutter,
+some of the long keys in these examples have been abbreviated
+by replacing part of their text with
+.RI `` ... ''.
+Keys used when the programs are actually run must,
+of course, be the full length required for the particular algorithm.
+.LP
+.B "ipsec spi \-\-af inet \-\-edst gw2 \-\-spi 0x125 \-\-proto esp \e"
+.br
+.B " \-\-src gw1 \e"
+.br
+.B " \-\-esp 3des\-md5\-96 \e"
+.br
+.BI "\ \ \ \-\-enckey\ 0x6630" "..." "97ce\ \e"
+.br
+.BI " \-\-authkey 0x9941" "..." "71df"
+.LP
+sets up an SA from
+.BR gw1
+to
+.BR gw2
+with an SPI of
+.BR 0x125
+and protocol
+.BR ESP
+(50) using
+.BR 3DES
+encryption with integral
+.BR MD5-96
+authentication transform, using an encryption key of
+.BI 0x6630 ... 97ce
+and an authentication key of
+.BI 0x9941 ... 71df
+(see note above about abbreviated keys).
+.LP
+.B "ipsec spi \-\-af inet6 \-\-edst 3049:9::9000:3100 \-\-spi 0x150 \-\-proto ah \e"
+.br
+.B " \-\-src 3049:9::9000:3101 \e"
+.br
+.B " \-\-ah hmac\-md5\-96 \e"
+.br
+.BI "\ \ \ \-\-authkey\ 0x1234" "..." "2eda\ \e"
+.LP
+sets up an SA from
+.BR 3049:9::9000:3101
+to
+.BR 3049:9::9000:3100
+with an SPI of
+.BR 0x150
+and protocol
+.BR AH
+(50) using
+.BR MD5-96
+authentication transform, using an authentication key of
+.BI 0x1234 ... 2eda
+(see note above about abbreviated keys).
+.LP
+.B "ipsec spi \-\-said tun.987@192.168.100.100 \-\-del "
+.LP
+deletes an SA to
+.BR 192.168.100.100
+with an SPI of
+.BR 0x987
+and protocol
+.BR IPv4-in-IPv4
+(4).
+.LP
+.B "ipsec spi \-\-said tun:500@3049:9::1000:1 \-\-del "
+.LP
+deletes an SA to
+.BR 3049:9::1000:1
+with an SPI of
+.BR 0x500
+and protocol
+.BR IPv6-in-IPv6
+(4).
+.LP
+.SH FILES
+/proc/net/ipsec_spi, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(8), ipsec_eroute(8),
+ipsec_spigrp(8), ipsec_klipsdebug(8), ipsec_spi(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.SH BUGS
+The syntax is messy and the transform naming needs work.
+.\"
+.\" $Log: spi.8,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.32 2002/04/24 07:35:40 mcr
+.\" Moved from ./klips/utils/spi.8,v
+.\"
+.\" Revision 1.31 2001/11/06 20:18:47 rgb
+.\" Added lifetime parameters.
+.\"
+.\" Revision 1.30 2001/10/24 03:23:32 rgb
+.\" Added lifetime option and parameters.
+.\"
+.\" Revision 1.29 2001/05/30 08:14:04 rgb
+.\" Removed vestiges of esp-null transforms.
+.\"
+.\" Revision 1.28 2000/11/29 19:15:20 rgb
+.\" Add --src requirement for inbound policy routing.
+.\"
+.\" Revision 1.27 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.26 2000/09/13 15:54:32 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.25 2000/09/12 22:36:45 rgb
+.\" Gerhard's IPv6 support.
+.\"
+.\" Revision 1.24 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.23 2000/06/21 16:54:57 rgb
+.\" Added 'no additional args' text for listing contents of
+.\" /proc/net/ipsec_* files.
+.\"
+.\" Revision 1.22 1999/08/11 08:35:16 rgb
+.\" Update, deleting references to obsolete and insecure algorithms.
+.\"
+.\" Revision 1.21 1999/07/19 18:53:55 henry
+.\" improve font usage in key abbreviations
+.\"
+.\" Revision 1.20 1999/07/19 18:50:09 henry
+.\" fix slightly-misformed comments
+.\" abbreviate long keys to avoid long-line complaints
+.\"
+.\" Revision 1.19 1999/04/06 04:54:38 rgb
+.\" Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+.\" patch shell fixes.
+.\"
diff --git a/programs/spi/spi.c b/programs/spi/spi.c
new file mode 100644
index 000000000..369d556c7
--- /dev/null
+++ b/programs/spi/spi.c
@@ -0,0 +1,1689 @@
+/*
+ * All-in-one program to set Security Association parameters
+ * Copyright (C) 1996 John Ioannidis.
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char spi_c_version[] = "RCSID $Id: spi.c,v 1.7 2004/10/14 20:03:26 as Exp $";
+
+#include <asm/types.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+/* #include <linux/netdevice.h> */
+#include <net/if.h>
+/* #include <linux/types.h> */ /* new */
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+/* #include <sys/socket.h> */
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/* #include <linux/ip.h> */
+#include <netdb.h>
+
+#include <unistd.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <freeswan.h>
+#if 0
+#include <linux/autoconf.h> /* CONFIG_IPSEC_PFKEYv2 */
+#endif
+ #include <signal.h>
+ #include <sys/socket.h>
+ #include <pfkeyv2.h>
+ #include <pfkey.h>
+
+#include "freeswan/radij.h"
+#include "freeswan/ipsec_encap.h"
+#include "freeswan/ipsec_xform.h"
+#include "freeswan/ipsec_ipe4.h"
+#include "freeswan/ipsec_ah.h"
+#include "freeswan/ipsec_esp.h"
+#include "freeswan/ipsec_sa.h" /* IPSEC_SAREF_NULL */
+
+/*
+ * Manual conn support for ipsec_alg (modular algos).
+ * Rather ugly to include from pluto dir but avoids
+ * code duplication.
+ */
+#ifndef NO_KERNEL_ALG
+#include "../pluto/alg_info.h"
+#include "../pluto/constants.h"
+struct connection;
+#include "../pluto/kernel_alg.h"
+#endif /* NO_KERNEL_ALG */
+
+char *program_name;
+int debug = 0;
+int saref = 0;
+char *command;
+extern char *optarg;
+extern int optind, opterr, optopt;
+char scratch[2];
+char *iv = NULL, *enckey = NULL, *authkey = NULL;
+size_t ivlen = 0, enckeylen = 0, authkeylen = 0;
+ip_address edst, dst, src;
+int address_family = 0;
+unsigned char proto = 0;
+int alg = 0;
+
+#ifndef NO_KERNEL_ALG
+/*
+ * Manual connection support for modular algos (ipsec_alg) --Juanjo.
+ */
+#define XF_OTHER_ALG (XF_CLR-1) /* define magic XF_ symbol for alg_info's */
+#include <assert.h>
+const char *alg_string = NULL; /* algorithm string */
+struct alg_info_esp *alg_info = NULL; /* algorithm info got from string */
+struct esp_info *esp_info = NULL; /* esp info from 1st (only) element */
+const char *alg_err; /* auxiliar for parsing errors */
+int proc_read_ok = 0; /* /proc/net/pf_key_support read ok */
+#endif /* NO_KERNEL_ALG */
+
+int replay_window = 0;
+char sa[SATOT_BUF];
+
+extern unsigned int pfkey_lib_debug; /* used by libfreeswan/pfkey_v2_build */
+int pfkey_sock;
+fd_set pfkey_socks;
+uint32_t pfkey_seq = 0;
+enum life_severity {
+ life_soft = 0,
+ life_hard = 1,
+ life_maxsever = 2
+};
+enum life_type {
+ life_alloc = 0,
+ life_bytes = 1,
+ life_addtime = 2,
+ life_usetime = 3,
+ life_packets = 4,
+ life_maxtype = 5
+};
+
+#define streql(_a,_b) (!strcmp((_a),(_b)))
+
+static const char *usage_string = "\
+Usage:\n\
+ in the following, <SA> is: --af <inet | inet6> --edst <dstaddr> --spi <spi> --proto <proto>\n\
+ OR: --said <proto><.|:><spi>@<dstaddr>\n\
+ <life> is: --life <soft|hard>-<allocations|bytes|addtime|usetime|packets>=<value>[,...]\n\
+spi --clear\n\
+spi --help\n\
+spi --version\n\
+spi\n\
+spi --del <SA>\n\
+spi --ip4 <SA> --src <encap-src> --dst <encap-dst>\n\
+spi --ip6 <SA> --src <encap-src> --dst <encap-dst>\n\
+spi --ah <algo> <SA> [<life> ][ --replay_window <replay_window> ] --authkey <key>\n\
+ where <algo> is one of: hmac-md5-96 | hmac-sha1-96\n\
+spi --esp <algo> <SA> [<life> ][ --replay_window <replay-window> ] --enckey <ekey> --authkey <akey>\n\
+ where <algo> is one of: 3des-md5-96 | 3des-sha1-96\n\
+spi --esp <algo> <SA> [<life> ][ --replay_window <replay-window> ] --enckey <ekey>\n\
+ where <algo> is: 3des\n\
+spi --comp <algo> <SA>\n\
+ where <algo> is: deflate\n\
+[ --debug ] is optional to any spi command.\n\
+[ --label <label> ] is optional to any spi command.\n\
+[ --listenreply ] is optional, and causes the command to stick\n\
+ around and listen to what the PF_KEY socket says.\n\
+";
+
+
+static void
+usage(char *s, FILE *f)
+{
+ /* s argument is actually ignored, at present */
+ fprintf(f, "%s:%s", s, usage_string);
+ exit(-1);
+}
+
+int
+parse_life_options(uint32_t life[life_maxsever][life_maxtype],
+ char *life_opt[life_maxsever][life_maxtype],
+ char *optarg)
+{
+ char *optargp = optarg;
+ char *endptr;
+
+ do {
+ int life_severity, life_type;
+ char *optargt = optargp;
+
+ if(strncmp(optargp, "soft", sizeof("soft")-1) == 0) {
+ life_severity = life_soft;
+ optargp += sizeof("soft")-1;
+ } else if(strncmp(optargp, "hard", sizeof("hard")-1) == 0) {
+ life_severity = life_hard;
+ optargp += sizeof("hard")-1;
+ } else {
+ fprintf(stderr,
+ "%s: missing lifetime severity in %s, optargt=0p%p, optargp=0p%p, sizeof(\"soft\")=%d\n",
+ program_name,
+ optargt,
+ optargt,
+ optargp,
+ (int)sizeof("soft"));
+ usage(program_name, stderr);
+ return(1);
+ }
+ if(debug) {
+ fprintf(stdout,
+ "%s: debug: life_severity=%d, optargt=0p%p=\"%s\", optargp=0p%p=\"%s\", sizeof(\"soft\")=%d\n",
+ program_name,
+ life_severity,
+ optargt,
+ optargt,
+ optargp,
+ optargp,
+ (int)sizeof("soft"));
+ }
+ if(*(optargp++) != '-') {
+ fprintf(stderr,
+ "%s: expected '-' after severity of lifetime parameter to --life option.\n",
+ program_name);
+ usage(program_name, stderr);
+ return(1);
+ }
+ if(debug) {
+ fprintf(stdout,
+ "%s: debug: optargt=0p%p=\"%s\", optargp=0p%p=\"%s\", strlen(optargt)=%d, strlen(optargp)=%d, strncmp(optargp, \"addtime\", sizeof(\"addtime\")-1)=%d\n",
+ program_name,
+ optargt,
+ optargt,
+ optargp,
+ optargp,
+ (int)strlen(optargt),
+ (int)strlen(optargp),
+ strncmp(optargp, "addtime", sizeof("addtime")-1));
+ }
+ if(strncmp(optargp, "allocations", sizeof("allocations")-1) == 0) {
+ life_type = life_alloc;
+ optargp += sizeof("allocations")-1;
+ } else if(strncmp(optargp, "bytes", sizeof("bytes")-1) == 0) {
+ life_type = life_bytes;
+ optargp += sizeof("bytes")-1;
+ } else if(strncmp(optargp, "addtime", sizeof("addtime")-1) == 0) {
+ life_type = life_addtime;
+ optargp += sizeof("addtime")-1;
+ } else if(strncmp(optargp, "usetime", sizeof("usetime")-1) == 0) {
+ life_type = life_usetime;
+ optargp += sizeof("usetime")-1;
+ } else if(strncmp(optargp, "packets", sizeof("packets")-1) == 0) {
+ life_type = life_packets;
+ optargp += sizeof("packets")-1;
+ } else {
+ fprintf(stderr,
+ "%s: missing lifetime type after '-' in %s\n",
+ program_name,
+ optargt);
+ usage(program_name, stderr);
+ return(1);
+ }
+ if(debug) {
+ fprintf(stdout,
+ "%s: debug: life_type=%d\n",
+ program_name,
+ life_type);
+ }
+ if(life_opt[life_severity][life_type] != NULL) {
+ fprintf(stderr,
+ "%s: Error, lifetime parameter redefined:%s, already defined as:0p%p\n",
+ program_name,
+ optargt,
+ life_opt[life_severity][life_type]);
+ return(1);
+ }
+ if(*(optargp++) != '=') {
+ fprintf(stderr,
+ "%s: expected '=' after type of lifetime parameter to --life option.\n",
+ program_name);
+ usage(program_name, stderr);
+ return(1);
+ }
+ if(debug) {
+ fprintf(stdout,
+ "%s: debug: optargt=0p%p, optargt+strlen(optargt)=0p%p, optargp=0p%p, strlen(optargp)=%d\n",
+ program_name,
+ optargt,
+ optargt+strlen(optargt),
+ optargp,
+ (int)strlen(optargp));
+ }
+ if(strlen(optargp) == 0) {
+ fprintf(stderr,
+ "%s: expected value after '=' in --life option. optargt=0p%p, optargt+strlen(optargt)=0p%p, optargp=0p%p\n",
+ program_name,
+ optargt,
+ optargt+strlen(optargt),
+ optargp);
+ usage(program_name, stderr);
+ return(1);
+ }
+ life[life_severity][life_type] = strtoul(optargp, &endptr, 0);
+
+ if(!((endptr == optargp + strlen(optargp)) || (endptr == optargp + strcspn(optargp, ", ")))) {
+ fprintf(stderr,
+ "%s: Invalid character='%c' at offset %d in lifetime option parameter: '%s', parameter string is %d characters long, %d valid value characters found.\n",
+ program_name,
+ *endptr,
+ (int)(endptr - optarg),
+ optarg,
+ (int)strlen(optarg),
+ (int)(strcspn(optargp, ", ") - 1));
+ return(1);
+ }
+ life_opt[life_severity][life_type] = optargt;
+ if(debug) {
+ fprintf(stdout, "%s lifetime %s set to %d.\n",
+ program_name, optargt, life[life_severity][life_type]);
+ }
+ optargp=endptr+1;
+ } while(*endptr==',' || isspace(*endptr));
+
+ return(0);
+}
+
+int
+pfkey_register(uint8_t satype) {
+ /* for registering SA types that can be negotiated */
+ int error;
+ ssize_t wlen;
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+
+ pfkey_extensions_init(extensions);
+ error = pfkey_msg_hdr_build(&extensions[0],
+ SADB_REGISTER,
+ satype,
+ 0,
+ ++pfkey_seq,
+ getpid());
+ if(error != 0) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ return(1);
+ }
+
+ error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN);
+ if(error != 0) {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ return(1);
+ }
+ wlen = write(pfkey_sock, pfkey_msg,
+ pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+ if(wlen != (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
+ /* cleanup code here */
+ if(wlen < 0)
+ fprintf(stderr, "%s: Trouble writing to channel PF_KEY: %s\n",
+ program_name,
+ strerror(errno));
+ else
+ fprintf(stderr, "%s: write to channel PF_KEY truncated.\n",
+ program_name);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ return(1);
+ }
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+
+ return(0);
+}
+
+static struct option const longopts[] =
+{
+ {"ah", 1, 0, 'H'},
+ {"esp", 1, 0, 'P'},
+ {"comp", 1, 0, 'Z'},
+ {"ip4", 0, 0, '4'},
+ {"ip6", 0, 0, '6'},
+ {"del", 0, 0, 'd'},
+
+ {"authkey", 1, 0, 'A'},
+ {"enckey", 1, 0, 'E'},
+ {"edst", 1, 0, 'e'},
+ {"spi", 1, 0, 's'},
+ {"proto", 1, 0, 'p'},
+ {"af", 1, 0, 'a'},
+ {"replay_window", 1, 0, 'w'},
+ {"iv", 1, 0, 'i'},
+ {"dst", 1, 0, 'D'},
+ {"src", 1, 0, 'S'},
+ {"said", 1, 0, 'I'},
+
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {"clear", 0, 0, 'c'},
+ {"label", 1, 0, 'l'},
+ {"debug", 0, 0, 'g'},
+ {"optionsfrom", 1, 0, '+'},
+ {"life", 1, 0, 'f'},
+ {"saref", 0, 0, 'r'},
+ {"listenreply", 0, 0, 'R'},
+ {0, 0, 0, 0}
+};
+
+int
+main(int argc, char *argv[])
+{
+ char *endptr;
+ __u32 spi = 0;
+ int c, previous = -1;
+/* int ret; */
+ ip_said said;
+ size_t sa_len;
+ const char* error_s;
+ char ipaddr_txt[ADDRTOT_BUF];
+ char ipsaid_txt[SATOT_BUF];
+
+ int error = 0;
+ ssize_t io_error;
+ int argcount = argc;
+ pid_t mypid;
+ int listenreply = 0;
+
+ unsigned char authalg, encryptalg;
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+ char *iv_opt, *akey_opt, *ekey_opt, *alg_opt, *edst_opt, *spi_opt, *proto_opt, *af_opt, *said_opt, *dst_opt, *src_opt;
+#if 0
+ ip_address pfkey_address_p_ska;
+ ip_address pfkey_ident_s_ska;
+ ip_address pfkey_ident_d_ska;
+#endif
+ uint32_t life[life_maxsever][life_maxtype];
+ char *life_opt[life_maxsever][life_maxtype];
+
+ program_name = argv[0];
+ mypid = getpid();
+
+ memset(&said, 0, sizeof(said));
+ iv_opt = akey_opt = ekey_opt = alg_opt = edst_opt = spi_opt = proto_opt = af_opt = said_opt = dst_opt = src_opt = NULL;
+ {
+ int i,j;
+ for(i = 0; i < life_maxsever; i++) {
+ for(j = 0; j < life_maxtype; j++) {
+ life_opt[i][j] = NULL;
+ life[i][j] = 0;
+ }
+ }
+ }
+
+ while((c = getopt_long(argc, argv, ""/*"H:P:Z:46dcA:E:e:s:a:w:i:D:S:hvgl:+:f:"*/, longopts, 0)) != EOF) {
+ switch(c) {
+ case 'g':
+ debug = 1;
+ pfkey_lib_debug = PF_KEY_DEBUG_PARSE_MAX;
+ argcount--;
+ break;
+
+ case 'R':
+ listenreply = 1;
+ argcount--;
+ break;
+
+ case 'r':
+ saref = 1;
+ argcount--;
+ break;
+
+ case 'l':
+ program_name = malloc(strlen(argv[0])
+ + 10 /* update this when changing the sprintf() */
+ + strlen(optarg));
+ sprintf(program_name, "%s --label %s",
+ argv[0],
+ optarg);
+ argcount -= 2;
+ break;
+ case 'H':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ if (!strcmp(optarg, "hmac-md5-96")) {
+ alg = XF_AHHMACMD5;
+ } else if(!strcmp(optarg, "hmac-sha1-96")) {
+ alg = XF_AHHMACSHA1;
+ } else {
+ fprintf(stderr, "%s: Unknown authentication algorithm '%s' follows '--ah' option.\n",
+ program_name, optarg);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case 'P':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ if (!strcmp(optarg, "3des-md5-96")) {
+ alg = XF_ESP3DESMD596;
+ } else if(!strcmp(optarg, "3des-sha1-96")) {
+ alg = XF_ESP3DESSHA196;
+ } else if(!strcmp(optarg, "3des")) {
+ alg = XF_ESP3DES;
+#ifndef NO_KERNEL_ALG
+ } else if((alg_info=alg_info_esp_create_from_str(optarg, &alg_err))) {
+ int esp_ealg_id, esp_aalg_id;
+ alg = XF_OTHER_ALG;
+ if (alg_info->alg_info_cnt>1) {
+ fprintf(stderr, "%s: Invalid encryption algorithm '%s' "
+ "follows '--esp' option: lead too many(%d) "
+ "transforms\n",
+ program_name, optarg, alg_info->alg_info_cnt);
+ exit(1);
+ }
+ alg_string=optarg;
+ esp_info=&alg_info->esp[0];
+ if (debug) {
+ fprintf(stdout, "%s: alg_info: cnt=%d ealg[0]=%d aalg[0]=%d\n",
+ program_name,
+ alg_info->alg_info_cnt,
+ esp_info->encryptalg,
+ esp_info->authalg);
+ }
+ esp_ealg_id=esp_info->esp_ealg_id;
+ esp_aalg_id=esp_info->esp_aalg_id;
+ if (kernel_alg_proc_read()==0) {
+ proc_read_ok++;
+ if (!kernel_alg_esp_enc_ok(esp_ealg_id, 0, 0))
+ {
+ fprintf(stderr, "%s: ESP encryptalg=%d (\"%s\") "
+ "not present\n",
+ program_name,
+ esp_ealg_id,
+ enum_name(&esp_transformid_names, esp_ealg_id));
+ exit(1);
+ }
+ if (!kernel_alg_esp_auth_ok(esp_aalg_id, 0))
+ {
+ fprintf(stderr, "%s: ESP authalg=%d (\"%s\")"
+ "not present\n",
+ program_name,
+ esp_aalg_id,
+ enum_name(&auth_alg_names, esp_aalg_id));
+ exit(1);
+ }
+ }
+#endif /* NO_KERNEL_ALG */
+ } else {
+ fprintf(stderr, "%s: Invalid encryption algorithm '%s' follows '--esp' option.\n",
+ program_name, optarg);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case 'Z':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ if (!strcmp(optarg, "deflate")) {
+ alg = XF_COMPDEFLATE;
+ } else {
+ fprintf(stderr, "%s: Unknown compression algorithm '%s' follows '--comp' option.\n",
+ program_name, optarg);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case '4':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ alg = XF_IP4;
+ address_family = AF_INET;
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case '6':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ alg = XF_IP6;
+ address_family = AF_INET6;
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case 'd':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ alg = XF_DEL;
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case 'c':
+ if(alg) {
+ fprintf(stderr, "%s: Only one of '--ah', '--esp', '--comp', '--ip4', '--ip6', '--del' or '--clear' options permitted.\n",
+ program_name);
+ exit(1);
+ }
+ alg = XF_CLR;
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm %d selected.\n",
+ program_name,
+ alg);
+ }
+ alg_opt = optarg;
+ break;
+ case 'e':
+ if(said_opt) {
+ fprintf(stderr, "%s: Error, EDST parameter redefined:%s, already defined in SA:%s\n",
+ program_name, optarg, said_opt);
+ exit (1);
+ }
+ if(edst_opt) {
+ fprintf(stderr, "%s: Error, EDST parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, edst_opt);
+ exit (1);
+ }
+ error_s = ttoaddr(optarg, 0, address_family, &edst);
+ if(error_s != NULL) {
+ if(error_s) {
+ fprintf(stderr, "%s: Error, %s converting --edst argument:%s\n",
+ program_name, error_s, optarg);
+ exit (1);
+ }
+ }
+ edst_opt = optarg;
+ if(debug) {
+ addrtot(&edst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: edst=%s.\n",
+ program_name,
+ ipaddr_txt);
+ }
+ break;
+ case 's':
+ if(said_opt) {
+ fprintf(stderr, "%s: Error, SPI parameter redefined:%s, already defined in SA:%s\n",
+ program_name, optarg, said_opt);
+ exit (1);
+ }
+ if(spi_opt) {
+ fprintf(stderr, "%s: Error, SPI parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, spi_opt);
+ exit (1);
+ }
+ spi = strtoul(optarg, &endptr, 0);
+ if(!(endptr == optarg + strlen(optarg))) {
+ fprintf(stderr, "%s: Invalid character in SPI parameter: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ if(spi < 0x100) {
+ fprintf(stderr, "%s: Illegal reserved spi: %s => 0x%x Must be larger than 0x100.\n",
+ program_name, optarg, spi);
+ exit(1);
+ }
+ spi_opt = optarg;
+ break;
+ case 'p':
+ if(said_opt) {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined:%s, already defined in SA:%s\n",
+ program_name, optarg, said_opt);
+ exit (1);
+ }
+ if(proto_opt) {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, proto_opt);
+ exit (1);
+ }
+ if(!strcmp(optarg, "ah"))
+ proto = SA_AH;
+ if(!strcmp(optarg, "esp"))
+ proto = SA_ESP;
+ if(!strcmp(optarg, "tun"))
+ proto = SA_IPIP;
+ if(!strcmp(optarg, "comp"))
+ proto = SA_COMP;
+ if(proto == 0) {
+ fprintf(stderr, "%s: Invalid PROTO parameter: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ proto_opt = optarg;
+ break;
+ case 'a':
+ if(said_opt) {
+ fprintf(stderr, "%s: Error, ADDRESS FAMILY parameter redefined:%s, already defined in SA:%s\n",
+ program_name, optarg, said_opt);
+ exit (1);
+ }
+ if(af_opt) {
+ fprintf(stderr, "%s: Error, ADDRESS FAMILY parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, af_opt);
+ exit (1);
+ }
+ if(strcmp(optarg, "inet") == 0) {
+ address_family = AF_INET;
+ /* currently we ensure that all addresses belong to the same address family */
+ anyaddr(address_family, &dst);
+ anyaddr(address_family, &edst);
+ anyaddr(address_family, &src);
+ }
+ if(strcmp(optarg, "inet6") == 0) {
+ address_family = AF_INET6;
+ /* currently we ensure that all addresses belong to the same address family */
+ anyaddr(address_family, &dst);
+ anyaddr(address_family, &edst);
+ anyaddr(address_family, &src);
+ }
+ if((strcmp(optarg, "inet") != 0) && (strcmp(optarg, "inet6") != 0)) {
+ fprintf(stderr, "%s: Invalid ADDRESS FAMILY parameter: %s.\n",
+ program_name, optarg);
+ exit (1);
+ }
+ af_opt = optarg;
+ break;
+ case 'I':
+ if(said_opt) {
+ fprintf(stderr, "%s: Error, SAID parameter redefined:%s, already defined in SA:%s\n",
+ program_name, optarg, said_opt);
+ exit (1);
+ }
+ if(proto_opt) {
+ fprintf(stderr, "%s: Error, PROTO parameter redefined in SA:%s, already defined as:%s\n",
+ program_name, optarg, proto_opt);
+ exit (1);
+ }
+ if(edst_opt) {
+ fprintf(stderr, "%s: Error, EDST parameter redefined in SA:%s, already defined as:%s\n",
+ program_name, optarg, edst_opt);
+ exit (1);
+ }
+ if(spi_opt) {
+ fprintf(stderr, "%s: Error, SPI parameter redefined in SA:%s, already defined as:%s\n",
+ program_name, optarg, spi_opt);
+ exit (1);
+ }
+ error_s = ttosa(optarg, 0, &said);
+ if(error_s != NULL) {
+ fprintf(stderr, "%s: Error, %s converting --sa argument:%s\n",
+ program_name, error_s, optarg);
+ exit (1);
+ }
+ if(debug) {
+ satot(&said, 0, ipsaid_txt, sizeof(ipsaid_txt));
+ fprintf(stdout, "%s: said=%s.\n",
+ program_name,
+ ipsaid_txt);
+ }
+ /* init the src and dst with the same address family */
+ if(address_family == 0) {
+ address_family = addrtypeof(&said.dst);
+ } else if(address_family != addrtypeof(&said.dst)) {
+ fprintf(stderr, "%s: Error, specified address family (%d) is different that of SAID: %s\n",
+ program_name, address_family, optarg);
+ exit (1);
+ }
+ anyaddr(address_family, &dst);
+ anyaddr(address_family, &edst);
+ anyaddr(address_family, &src);
+ said_opt = optarg;
+ break;
+ case 'A':
+ if(optarg[0] == '0') {
+ switch(optarg[1]) {
+ case 't':
+ case 'x':
+ case 's':
+ break;
+ default:
+ fprintf(stderr, "%s: Authentication key must have a '0x', '0t' or '0s' prefix to select the format: %s\n",
+ program_name, optarg);
+ exit(1);
+ }
+ }
+ authkeylen = atodata(optarg, 0, NULL, 0);
+ if(!authkeylen) {
+ fprintf(stderr, "%s: unknown format or syntax error in authentication key: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ authkey = malloc(authkeylen);
+ if(authkey == NULL) {
+ fprintf(stderr, "%s: Memory allocation error.\n", program_name);
+ exit(1);
+ }
+ memset(authkey, 0, authkeylen);
+ authkeylen = atodata(optarg, 0, authkey, authkeylen);
+ akey_opt = optarg;
+ break;
+ case 'E':
+ if(optarg[0] == '0') {
+ switch(optarg[1]) {
+ case 't':
+ case 'x':
+ case 's':
+ break;
+ default:
+ fprintf(stderr, "%s: Encryption key must have a '0x', '0t' or '0s' prefix to select the format: %s\n",
+ program_name, optarg);
+ exit(1);
+ }
+ }
+ enckeylen = atodata(optarg, 0, NULL, 0);
+ if(!enckeylen) {
+ fprintf(stderr, "%s: unknown format or syntax error in encryption key: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ enckey = malloc(enckeylen);
+ if(enckey == NULL) {
+ fprintf(stderr, "%s: Memory allocation error.\n", program_name);
+ exit(1);
+ }
+ memset(enckey, 0, enckeylen);
+ enckeylen = atodata(optarg, 0, enckey, enckeylen);
+ ekey_opt = optarg;
+ break;
+ case 'w':
+ replay_window = strtoul(optarg, &endptr, 0);
+ if(!(endptr == optarg + strlen(optarg))) {
+ fprintf(stderr, "%s: Invalid character in replay_window parameter: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ if((replay_window < 0x1) || (replay_window > 64)) {
+ fprintf(stderr, "%s: Failed -- Illegal window size: arg=%s, replay_window=%d, must be 1 <= size <= 64.\n",
+ program_name, optarg, replay_window);
+ exit(1);
+ }
+ break;
+ case 'i':
+ if(optarg[0] == '0') {
+ switch(optarg[1]) {
+ case 't':
+ case 'x':
+ case 's':
+ break;
+ default:
+ fprintf(stderr, "%s: IV must have a '0x', '0t' or '0s' prefix to select the format, found '%c'.\n",
+ program_name, optarg[1]);
+ exit(1);
+ }
+ }
+ ivlen = atodata(optarg, 0, NULL, 0);
+ if(!ivlen) {
+ fprintf(stderr, "%s: unknown format or syntax error in IV: %s\n",
+ program_name, optarg);
+ exit (1);
+ }
+ iv = malloc(ivlen);
+ if(iv == NULL) {
+ fprintf(stderr, "%s: Memory allocation error.\n", program_name);
+ exit(1);
+ }
+ memset(iv, 0, ivlen);
+ ivlen = atodata(optarg, 0, iv, ivlen);
+ iv_opt = optarg;
+ break;
+ case 'D':
+ if(dst_opt) {
+ fprintf(stderr, "%s: Error, DST parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, dst_opt);
+ exit (1);
+ }
+ error_s = ttoaddr(optarg, 0, address_family, &dst);
+ if(error_s != NULL) {
+ fprintf(stderr, "%s: Error, %s converting --dst argument:%s\n",
+ program_name, error_s, optarg);
+ exit (1);
+ }
+ dst_opt = optarg;
+ if(debug) {
+ addrtot(&dst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: dst=%s.\n",
+ program_name,
+ ipaddr_txt);
+ }
+ break;
+ case 'S':
+ if(src_opt) {
+ fprintf(stderr, "%s: Error, SRC parameter redefined:%s, already defined as:%s\n",
+ program_name, optarg, src_opt);
+ exit (1);
+ }
+ error_s = ttoaddr(optarg, 0, address_family, &src);
+ if(error_s != NULL) {
+ fprintf(stderr, "%s: Error, %s converting --src argument:%s\n",
+ program_name, error_s, optarg);
+ exit (1);
+ }
+ src_opt = optarg;
+ if(debug) {
+ addrtot(&src, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: src=%s.\n",
+ program_name,
+ ipaddr_txt);
+ }
+ break;
+ case 'h':
+ usage(program_name, stdout);
+ exit(0);
+ case '?':
+ usage(program_name, stderr);
+ exit(1);
+ case 'v':
+ fprintf(stdout, "%s, %s\n", program_name, spi_c_version);
+ exit(1);
+ case '+': /* optionsfrom */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* no return on error */
+ break;
+ case 'f':
+ if(parse_life_options(life,
+ life_opt,
+ optarg) != 0) {
+ exit(1);
+ };
+ break;
+ default:
+ fprintf(stderr, "%s: unrecognized option '%c', update option processing.\n",
+ program_name, c);
+ exit(1);
+ }
+ previous = c;
+ }
+ if(debug) {
+ fprintf(stdout, "%s: All options processed.\n",
+ program_name);
+ }
+
+ if(argcount == 1) {
+ system("cat /proc/net/ipsec_spi");
+ exit(0);
+ }
+
+ switch(alg) {
+#ifndef NO_KERNEL_ALG
+ case XF_OTHER_ALG:
+ /* validate keysizes */
+ if (proc_read_ok) {
+ const struct sadb_alg *alg_p;
+ size_t keylen, minbits, maxbits;
+
+ alg_p=kernel_alg_sadb_alg_get(SADB_SATYPE_ESP,SADB_EXT_SUPPORTED_ENCRYPT,
+ esp_info->encryptalg);
+ assert(alg_p);
+ keylen=enckeylen * 8;
+
+ if (alg_p->sadb_alg_id==ESP_3DES || alg_p->sadb_alg_id==ESP_DES) {
+ maxbits=minbits=alg_p->sadb_alg_minbits * 8 /7;
+ } else {
+ minbits=alg_p->sadb_alg_minbits;
+ maxbits=alg_p->sadb_alg_maxbits;
+ }
+ /*
+ * if explicit keylen told in encrypt algo, eg "aes128"
+ * check actual keylen "equality"
+ */
+ if (esp_info->esp_ealg_keylen &&
+ esp_info->esp_ealg_keylen!=keylen) {
+ fprintf(stderr, "%s: invalid encryption keylen=%d, "
+ "required %d by encrypt algo string=\"%s\"\n",
+ program_name,
+ (int)keylen,
+ (int)esp_info->esp_ealg_keylen,
+ alg_string);
+ exit(1);
+
+ }
+ /* thanks DES for this sh*t */
+
+ if (minbits > keylen || maxbits < keylen) {
+ fprintf(stderr, "%s: invalid encryption keylen=%d, "
+ "must be between %d and %d bits\n",
+ program_name,
+ (int)keylen, (int)minbits, (int)maxbits);
+ exit(1);
+ }
+ alg_p=kernel_alg_sadb_alg_get(SADB_SATYPE_ESP,SADB_EXT_SUPPORTED_AUTH,
+ esp_info->authalg);
+ assert(alg_p);
+ keylen=authkeylen * 8;
+ minbits=alg_p->sadb_alg_minbits;
+ maxbits=alg_p->sadb_alg_maxbits;
+ if (minbits > keylen || maxbits < keylen) {
+ fprintf(stderr, "%s: invalid auth keylen=%d, "
+ "must be between %d and %d bits\n",
+ program_name,
+ (int)keylen, (int)minbits, (int)maxbits);
+ exit(1);
+ }
+
+ }
+#endif /* NO_KERNEL_ALG */
+ case XF_IP4:
+ case XF_IP6:
+ case XF_DEL:
+ case XF_AHHMACMD5:
+ case XF_AHHMACSHA1:
+ case XF_ESP3DESMD596:
+ case XF_ESP3DESSHA196:
+ case XF_ESP3DES:
+ case XF_COMPDEFLATE:
+ if(!said_opt) {
+ if(isanyaddr(&edst)) {
+ fprintf(stderr, "%s: SA destination not specified.\n",
+ program_name);
+ exit(1);
+ }
+ if(!spi) {
+ fprintf(stderr, "%s: SA SPI not specified.\n",
+ program_name);
+ exit(1);
+ }
+ if(!proto) {
+ fprintf(stderr, "%s: SA PROTO not specified.\n",
+ program_name);
+ exit(1);
+ }
+ initsaid(&edst, htonl(spi), proto, &said);
+ } else {
+ proto = said.proto;
+ spi = ntohl(said.spi);
+ edst = said.dst;
+ }
+ if((address_family != 0) && (address_family != addrtypeof(&said.dst))) {
+ fprintf(stderr, "%s: Defined address family and address family of SA missmatch.\n",
+ program_name);
+ exit(1);
+ }
+ sa_len = satot(&said, 0, sa, sizeof(sa));
+
+ if(debug) {
+ fprintf(stdout, "%s: SA valid.\n",
+ program_name);
+ }
+ break;
+ case XF_CLR:
+ break;
+ default:
+ fprintf(stderr, "%s: No action chosen. See '%s --help' for usage.\n",
+ program_name, program_name);
+ exit(1);
+ }
+
+ switch(alg) {
+ case XF_CLR:
+ case XF_DEL:
+ case XF_IP4:
+ case XF_IP6:
+ case XF_AHHMACMD5:
+ case XF_AHHMACSHA1:
+ case XF_ESP3DESMD596:
+ case XF_ESP3DESSHA196:
+ case XF_ESP3DES:
+ case XF_COMPDEFLATE:
+#ifndef NO_KERNEL_ALG
+ case XF_OTHER_ALG:
+#endif /* NO_KERNEL_ALG */
+ break;
+ default:
+ fprintf(stderr, "%s: No action chosen. See '%s --help' for usage.\n",
+ program_name, program_name);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: Algorithm ok.\n",
+ program_name);
+ }
+
+ if((pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0) {
+ fprintf(stderr, "%s: Trouble opening PF_KEY family socket with error: ",
+ program_name);
+ switch(errno) {
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, KLIPS not loaded or check kernel log messages for specifics.\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case EAFNOSUPPORT:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown file open error %d. Please report as much detail as possible to development team.\n", errno);
+ }
+ exit(1);
+ }
+
+#ifdef MANUAL_IS_NOT_ABLE_TO_NEGOTIATE
+ /* for registering SA types that can be negotiated */
+ if(pfkey_register(SADB_SATYPE_AH) != 0) {
+ exit(1);
+ }
+ if(pfkey_register(SADB_SATYPE_ESP) != 0) {
+ exit(1);
+ }
+ if(pfkey_register(SADB_X_SATYPE_IPIP) != 0) {
+ exit(1);
+ }
+ if(pfkey_register(SADB_X_SATYPE_COMP) != 0) {
+ exit(1);
+ }
+#endif /* MANUAL_IS_NOT_ABLE_TO_NEGOTIATE */
+
+ /* Build an SADB_ADD message to send down. */
+ /* It needs <base, SA, address(SD), key(AE)> minimum. */
+ /* Lifetime(HS) could be added before addresses. */
+ pfkey_extensions_init(extensions);
+ if(debug) {
+ fprintf(stdout, "%s: extensions=0p%p &extensions=0p%p extensions[0]=0p%p &extensions[0]=0p%p cleared.\n",
+ program_name,
+ extensions,
+ &extensions,
+ extensions[0],
+ &extensions[0]);
+ }
+ if((error = pfkey_msg_hdr_build(&extensions[0],
+ (alg == XF_DEL ? SADB_DELETE : alg == XF_CLR ? SADB_FLUSH : SADB_ADD),
+ proto2satype(proto),
+ 0,
+ ++pfkey_seq,
+ mypid))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: extensions=0p%p &extensions=0p%p extensions[0]=0p%p &extensions[0]=0p%p set w/msghdr.\n",
+ program_name,
+ extensions,
+ &extensions,
+ extensions[0],
+ &extensions[0]);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: base message assembled.\n", program_name);
+ }
+
+ switch(alg) {
+ case XF_AHHMACMD5:
+ case XF_ESP3DESMD596:
+ authalg = SADB_AALG_MD5_HMAC;
+ break;
+ case XF_AHHMACSHA1:
+ case XF_ESP3DESSHA196:
+ authalg = SADB_AALG_SHA1_HMAC;
+ break;
+#ifndef NO_KERNEL_ALG
+ case XF_OTHER_ALG:
+ authalg= esp_info->authalg;
+ if(debug) {
+ fprintf(stdout, "%s: debug: authalg=%d\n",
+ program_name, authalg);
+ }
+ break;
+#endif /* NO_KERNEL_ALG */
+ case XF_ESP3DESMD5:
+ default:
+ authalg = SADB_AALG_NONE;
+ }
+ switch(alg) {
+ case XF_ESP3DES:
+ case XF_ESP3DESMD596:
+ case XF_ESP3DESSHA196:
+ encryptalg = SADB_EALG_3DES_CBC;
+ break;
+ case XF_COMPDEFLATE:
+ encryptalg = SADB_X_CALG_DEFLATE;
+ break;
+#ifndef NO_KERNEL_ALG
+ case XF_OTHER_ALG:
+ encryptalg= esp_info->encryptalg;
+ if(debug) {
+ fprintf(stdout, "%s: debug: encryptalg=%d\n",
+ program_name, encryptalg);
+ }
+ break;
+#endif /* NO_KERNEL_ALG */
+ default:
+ encryptalg = SADB_EALG_NONE;
+ }
+ if(!(alg == XF_CLR /* IE: pfkey_msg->sadb_msg_type == SADB_FLUSH */)) {
+ if((error = pfkey_sa_build(&extensions[SADB_EXT_SA],
+ SADB_EXT_SA,
+ htonl(spi), /* in network order */
+ replay_window,
+ SADB_SASTATE_MATURE,
+ authalg,
+ encryptalg,
+ 0))) {
+ fprintf(stderr, "%s: Trouble building sa extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: extensions[0]=0p%p previously set with msg_hdr.\n",
+ program_name,
+ extensions[0]);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: assembled SA extension, pfkey msg authalg=%d encalg=%d.\n",
+ program_name,
+ authalg,
+ encryptalg);
+ }
+
+ if(debug) {
+ int i,j;
+ for(i = 0; i < life_maxsever; i++) {
+ for(j = 0; j < life_maxtype; j++) {
+ fprintf(stdout, "%s: i=%d, j=%d, life_opt[%d][%d]=0p%p, life[%d][%d]=%d\n",
+ program_name,
+ i, j, i, j, life_opt[i][j], i, j, life[i][j]);
+ }
+ }
+ }
+ if(life_opt[life_soft][life_alloc] != NULL ||
+ life_opt[life_soft][life_bytes] != NULL ||
+ life_opt[life_soft][life_addtime] != NULL ||
+ life_opt[life_soft][life_usetime] != NULL ||
+ life_opt[life_soft][life_packets] != NULL) {
+ if((error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_SOFT],
+ SADB_EXT_LIFETIME_SOFT,
+ life[life_soft][life_alloc],/*-1,*/ /*allocations*/
+ life[life_soft][life_bytes],/*-1,*/ /*bytes*/
+ life[life_soft][life_addtime],/*-1,*/ /*addtime*/
+ life[life_soft][life_usetime],/*-1,*/ /*usetime*/
+ life[life_soft][life_packets]/*-1*/))) { /*packets*/
+ fprintf(stderr, "%s: Trouble building lifetime_s extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: lifetime_s extension assembled.\n",
+ program_name);
+ }
+ }
+
+ if(life_opt[life_hard][life_alloc] != NULL ||
+ life_opt[life_hard][life_bytes] != NULL ||
+ life_opt[life_hard][life_addtime] != NULL ||
+ life_opt[life_hard][life_usetime] != NULL ||
+ life_opt[life_hard][life_packets] != NULL) {
+ if((error = pfkey_lifetime_build(&extensions[SADB_EXT_LIFETIME_HARD],
+ SADB_EXT_LIFETIME_HARD,
+ life[life_hard][life_alloc],/*-1,*/ /*allocations*/
+ life[life_hard][life_bytes],/*-1,*/ /*bytes*/
+ life[life_hard][life_addtime],/*-1,*/ /*addtime*/
+ life[life_hard][life_usetime],/*-1,*/ /*usetime*/
+ life[life_hard][life_packets]/*-1*/))) { /*packets*/
+ fprintf(stderr, "%s: Trouble building lifetime_h extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: lifetime_h extension assembled.\n",
+ program_name);
+ }
+ }
+
+ if(debug) {
+ addrtot(&src, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: assembling address_s extension (%s).\n",
+ program_name, ipaddr_txt);
+ }
+
+ if((error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
+ SADB_EXT_ADDRESS_SRC,
+ 0,
+ 0,
+ sockaddrof(&src)))) {
+ addrtot(&src, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_s extension (%s), error=%d.\n",
+ program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ ip_address temp_addr;
+
+ switch(address_family) {
+ case AF_INET:
+ initaddr((const unsigned char *)&(((struct sockaddr_in*)( ((struct sadb_address*)(extensions[SADB_EXT_ADDRESS_SRC])) + 1))->sin_addr),
+ sockaddrlenof(&src), address_family, &temp_addr);
+ break;
+ case AF_INET6:
+ initaddr((const unsigned char *)&(((struct sockaddr_in6*)( ((struct sadb_address*)(extensions[SADB_EXT_ADDRESS_SRC])) + 1))->sin6_addr),
+ sockaddrlenof(&src), address_family, &temp_addr);
+ break;
+ default:
+ fprintf(stdout, "%s: unknown address family (%d).\n",
+ program_name, address_family);
+ exit(1);
+ }
+ addrtot(&temp_addr, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: address_s extension assembled (%s).\n",
+ program_name, ipaddr_txt);
+ }
+
+ if(debug) {
+ addrtot(&edst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: assembling address_d extension (%s).\n",
+ program_name, ipaddr_txt);
+ }
+
+ if((error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_DST],
+ SADB_EXT_ADDRESS_DST,
+ 0,
+ 0,
+ sockaddrof(&edst)))) {
+ addrtot(&edst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_d extension (%s), error=%d.\n",
+ program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ ip_address temp_addr;
+ switch(address_family) {
+ case AF_INET:
+ initaddr((const unsigned char *)&(((struct sockaddr_in*)( ((struct sadb_address*)(extensions[SADB_EXT_ADDRESS_DST])) + 1))->sin_addr),
+ 4, address_family, &temp_addr);
+ break;
+ case AF_INET6:
+ initaddr((const unsigned char *)&(((struct sockaddr_in6*)( ((struct sadb_address*)(extensions[SADB_EXT_ADDRESS_DST])) + 1))->sin6_addr),
+ 16, address_family, &temp_addr);
+ break;
+ default:
+ fprintf(stdout, "%s: unknown address family (%d).\n",
+ program_name, address_family);
+ exit(1);
+ }
+ addrtot(&temp_addr, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "%s: address_d extension assembled (%s).\n",
+ program_name, ipaddr_txt);
+ }
+
+#if PFKEY_PROXY
+ anyaddr(address_family, &pfkey_address_p_ska);
+ if((error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_PROXY],
+ SADB_EXT_ADDRESS_PROXY,
+ 0,
+ 0,
+ sockaddrof(&pfkey_address_p_ska)))) {
+ fprintf(stderr, "%s: Trouble building address_p extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: address_p extension assembled.\n", program_name);
+ }
+#endif /* PFKEY_PROXY */
+
+ switch(alg) {
+#ifndef NO_KERNEL_ALG
+ /* Allow no auth ... after all is local root decision 8) */
+ case XF_OTHER_ALG:
+ if (!authalg)
+ break;
+#endif /* NO_KERNEL_ALG */
+ case XF_AHHMACMD5:
+ case XF_ESP3DESMD596:
+ case XF_AHHMACSHA1:
+ case XF_ESP3DESSHA196:
+ if((error = pfkey_key_build(&extensions[SADB_EXT_KEY_AUTH],
+ SADB_EXT_KEY_AUTH,
+ authkeylen * 8,
+ authkey))) {
+ fprintf(stderr, "%s: Trouble building key_a extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: key_a extension assembled.\n",
+ program_name);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch(alg) {
+ case XF_ESP3DES:
+ case XF_ESP3DESMD596:
+ case XF_ESP3DESSHA196:
+#ifndef NO_KERNEL_ALG
+ case XF_OTHER_ALG:
+#endif /* NO_KERNEL_ALG */
+ if((error = pfkey_key_build(&extensions[SADB_EXT_KEY_ENCRYPT],
+ SADB_EXT_KEY_ENCRYPT,
+ enckeylen * 8,
+ enckey))) {
+ fprintf(stderr, "%s: Trouble building key_e extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: key_e extension assembled.\n",
+ program_name);
+ }
+ break;
+ default:
+ break;
+ }
+
+#ifdef PFKEY_IDENT /* GG: looks wierd, not touched */
+ if((pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_SRC],
+ SADB_EXT_IDENTITY_SRC,
+ SADB_IDENTTYPE_PREFIX,
+ 0,
+ strlen(pfkey_ident_s_ska),
+ pfkey_ident_s_ska))) {
+ fprintf(stderr, "%s: Trouble building ident_s extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(subnettoa(addr, mask, format, pfkey_ident_s_ska,
+ sizeof(pfkey_ident_s_ska) ) !=
+ sizeof(pfkey_ident_s_ska) ) {
+ exit (1);
+ }
+
+ if((error = pfkey_ident_build(&extensions[SADB_EXT_IDENTITY_DST],
+ SADB_EXT_IDENTITY_DST,
+ SADB_IDENTTYPE_PREFIX,
+ 0,
+ strlen(pfkey_ident_d_ska),
+ pfkey_ident_d_ska))) {
+ fprintf(stderr, "%s: Trouble building ident_d extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ if(subnettoa(addr, mask, format, pfkey_ident_d_ska,
+ sizeof(pfkey_ident_d_ska) ) !=
+ sizeof(pfkey_ident_d_ska) ) {
+ exit (1);
+ }
+
+ if(debug) {
+ fprintf(stdout, "%s: ident extensions assembled.\n",
+ program_name);
+ }
+#endif /* PFKEY_IDENT */
+ }
+
+ if(debug) {
+ fprintf(stdout, "%s: assembling pfkey msg....\n",
+ program_name);
+ }
+ if((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN))) {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: assembled.\n",
+ program_name);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: writing pfkey msg.\n",
+ program_name);
+ }
+ io_error = write(pfkey_sock,
+ pfkey_msg,
+ pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+ if(io_error < 0) {
+ fprintf(stderr, "%s: pfkey write failed (errno=%d): ",
+ program_name, errno);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ fprintf(stderr, "No device?!?\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case ENXIO:
+ case ESRCH:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case ENOSPC:
+ fprintf(stderr, "no room in kernel SAref table. Cannot process request.\n");
+ break;
+ case ESPIPE:
+ fprintf(stderr, "kernel SAref table internal error. Cannot process request.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket write error %d (%s). Please report as much detail as possible to development team.\n",
+ errno, strerror(errno));
+ }
+ exit(1);
+ } else if (io_error != (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
+ fprintf(stderr, "%s: pfkey write truncated to %d bytes\n",
+ program_name, (int)io_error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+
+ if(debug) {
+ fprintf(stdout, "%s: pfkey command written to socket.\n",
+ program_name);
+ }
+
+ if(pfkey_msg) {
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ }
+ if(debug) {
+ fprintf(stdout, "%s: pfkey message buffer freed.\n",
+ program_name);
+ }
+ if(authkey) {
+ memset((caddr_t)authkey, 0, authkeylen);
+ free(authkey);
+ }
+ if(enckey) {
+ memset((caddr_t)enckey, 0, enckeylen);
+ free(enckey);
+ }
+ if(iv) {
+ memset((caddr_t)iv, 0, ivlen);
+ free(iv);
+ }
+
+ if(listenreply || saref) {
+ ssize_t readlen;
+ unsigned char pfkey_buf[PFKEYv2_MAX_MSGSIZE];
+
+ while((readlen = read(pfkey_sock, pfkey_buf, sizeof(pfkey_buf))) > 0) {
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ pfkey_extensions_init(extensions);
+ pfkey_msg = (struct sadb_msg *)pfkey_buf;
+
+ /* first, see if we got enough for an sadb_msg */
+ if((size_t)readlen < sizeof(struct sadb_msg)) {
+ if(debug) {
+ printf("%s: runt packet of size: %ld (<%lu)\n",
+ program_name, (long)readlen, (unsigned long)sizeof(struct sadb_msg));
+ }
+ continue;
+ }
+
+ /* okay, we got enough for a message, print it out */
+ if(debug) {
+ printf("%s: pfkey v%d msg received. type=%d(%s) seq=%d len=%d pid=%d errno=%d satype=%d(%s)\n",
+ program_name,
+ pfkey_msg->sadb_msg_version,
+ pfkey_msg->sadb_msg_type,
+ pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type),
+ pfkey_msg->sadb_msg_seq,
+ pfkey_msg->sadb_msg_len,
+ pfkey_msg->sadb_msg_pid,
+ pfkey_msg->sadb_msg_errno,
+ pfkey_msg->sadb_msg_satype,
+ satype2name(pfkey_msg->sadb_msg_satype));
+ }
+
+ if(readlen != (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN))
+ {
+ if(debug) {
+ printf("%s: packet size read from socket=%d doesn't equal sadb_msg_len %u * %u; message not decoded\n",
+ program_name,
+ (int)readlen,
+ (unsigned)pfkey_msg->sadb_msg_len,
+ (unsigned)IPSEC_PFKEYv2_ALIGN);
+ }
+ continue;
+ }
+
+ if (pfkey_msg_parse(pfkey_msg, NULL, extensions, EXT_BITS_OUT)) {
+ if(debug) {
+ printf("%s: unparseable PF_KEY message.\n",
+ program_name);
+ }
+ continue;
+ } else {
+ if(debug) {
+ printf("%s: parseable PF_KEY message.\n",
+ program_name);
+ }
+ }
+ if((pid_t)pfkey_msg->sadb_msg_pid == mypid) {
+ if(saref) {
+ printf("%s: saref=%d\n",
+ program_name,
+ (extensions[SADB_EXT_SA] != NULL)
+ ? ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_x_sa_ref
+ : IPSEC_SAREF_NULL);
+ }
+ break;
+ }
+ }
+ }
+ (void) close(pfkey_sock); /* close the socket */
+ if(debug || listenreply) {
+ printf("%s: exited normally\n", program_name);
+ }
+ exit(0);
+}
diff --git a/programs/spigrp/.cvsignore b/programs/spigrp/.cvsignore
new file mode 100644
index 000000000..4fee1abcf
--- /dev/null
+++ b/programs/spigrp/.cvsignore
@@ -0,0 +1 @@
+spigrp
diff --git a/programs/spigrp/Makefile b/programs/spigrp/Makefile
new file mode 100644
index 000000000..df8899eaf
--- /dev/null
+++ b/programs/spigrp/Makefile
@@ -0,0 +1,52 @@
+# Makefile for miscelaneous programs
+# Copyright (C) 2002 Michael Richardson <mcr@freeswan.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM=spigrp
+EXTRA5PROC=${PROGRAM}.5
+
+LIBS=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 2002/06/02 21:51:41 mcr
+# changed TOPDIR->FREESWANSRCDIR in all Makefiles.
+# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the
+# kernel sense.)
+#
+# Revision 1.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
+
diff --git a/programs/spigrp/spigrp.5 b/programs/spigrp/spigrp.5
new file mode 100644
index 000000000..b00d7ae73
--- /dev/null
+++ b/programs/spigrp/spigrp.5
@@ -0,0 +1,116 @@
+.TH IPSEC_SPIGRP 5 "27 Jun 2000"
+.\"
+.\" RCSID $Id: spigrp.5,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec_spigrp \- list IPSEC Security Association groupings
+.SH SYNOPSIS
+.B ipsec
+.B spigrp
+.PP
+.B cat
+.B /proc/net/ipsec_spigrp
+.PP
+.SH DESCRIPTION
+.I /proc/net/ipsec_spigrp
+is a read-only file that lists groups of IPSEC Security Associations
+(SAs).
+.PP
+An entry in the IPSEC extended routing table can only point (via an
+SAID) to one SA. If more than one transform must be applied to a given
+type of packet, this can be accomplished by setting up several SAs with
+the same destination address but potentially different SPIs and
+protocols, and grouping them with
+.IR ipsec_spigrp(8) .
+.PP
+The SA groups are listed, one line per connection/group, as a sequence
+of SAs to be applied (or that should have been applied, in the case of
+an incoming packet) from inside to outside the packet. An SA is
+identified by its SAID, which consists of protocol ("ah", "esp", "comp" or
+"tun"), SPI (with '.' for IPv4 or ':' for IPv6 prefixed hexadecimal number ) and destination address
+(IPv4 dotted quad or IPv6 coloned hex) prefixed by '@', in the format <proto><af><spi>@<dest>.
+.SH EXAMPLES
+.TP
+.B tun.3d0@192.168.2.110
+.B comp.3d0@192.168.2.110
+.B esp.187a101b@192.168.2.110
+.B ah.187a101a@192.168.2.110
+.LP
+is a group of 3 SAs, destined for
+.BR 192.168.2.110
+with an IPv4-in-IPv4 tunnel SA applied first with an SPI of
+.BR 3d0
+in hexadecimal, followed by a Deflate compression header to compress
+the packet with CPI of
+.BR 3d0
+in hexadecimal, followed by an Encapsulating Security Payload header to
+encrypt the packet with SPI
+.BR 187a101b
+in hexadecimal, followed by an Authentication Header to authenticate the
+packet with SPI
+.BR 187a101a
+in hexadecimal, applied from inside to outside the packet. This could
+be an incoming or outgoing group, depending on the address of the local
+machine.
+.LP
+.TP
+.B tun:3d0@3049:1::2
+.B comp:3d0@3049:1::2
+.B esp:187a101b@3049:1::2
+.B ah:187a101a@3049:1::2
+.LP
+is a group of 3 SAs, destined for
+.BR 3049:1::2
+with an IPv6-in-IPv6 tunnel SA applied first with an SPI of
+.BR 3d0
+in hexadecimal, followed by a Deflate compression header to compress
+the packet with CPI of
+.BR 3d0
+in hexadecimal, followed by an Encapsulating Security Payload header to
+encrypt the packet with SPI
+.BR 187a101b
+in hexadecimal, followed by an Authentication Header to authenticate the
+packet with SPI
+.BR 187a101a
+in hexadecimal, applied from inside to outside the packet. This could
+be an incoming or outgoing group, depending on the address of the local
+machine.
+.LP
+.SH FILES
+/proc/net/ipsec_spigrp, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(5), ipsec_eroute(5),
+ipsec_spi(5), ipsec_klipsdebug(5), ipsec_spigrp(8), ipsec_version(5),
+ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.SH BUGS
+:-)
+.\"
+.\" $Log: spigrp.5,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.6 2002/04/24 07:35:40 mcr
+.\" Moved from ./klips/utils/spigrp.5,v
+.\"
+.\" Revision 1.5 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.4 2000/09/13 15:54:32 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/28 12:44:12 henry
+.\" format touchup
+.\"
+.\" Revision 1.1 2000/06/28 05:43:00 rgb
+.\" Added manpages for all 5 klips utils.
+.\"
+.\"
diff --git a/programs/spigrp/spigrp.8 b/programs/spigrp/spigrp.8
new file mode 100644
index 000000000..418ed5c3e
--- /dev/null
+++ b/programs/spigrp/spigrp.8
@@ -0,0 +1,174 @@
+.TH IPSEC_SPIGRP 8 "21 Jun 2000"
+.\"
+.\" RCSID $Id: spigrp.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec spigrp \- group/ungroup IPSEC Security Associations
+.SH SYNOPSIS
+.B ipsec
+.B spigrp
+.PP
+.B ipsec
+.B spigrp
+[
+.B \-\-label
+label ]
+af1 dst1 spi1 proto1 [ af2 dst2 spi2 proto2 [ af3 dst3 spi3 proto3 [ af4 dst4 spi4 proto4 ] ] ]
+.PP
+.B ipsec
+.B spigrp
+[
+.B \-\-label
+label ]
+.B \-\-said
+SA1 [ SA2 [ SA3 [ SA4 ] ] ]
+.PP
+.B ipsec
+.B spigrp
+.B \-\-help
+.PP
+.B ipsec
+.B spigrp
+.B \-\-version
+.PP
+.SH DESCRIPTION
+.I Spigrp
+groups IPSEC Security Associations (SAs) together or ungroups
+previously grouped SAs.
+An entry in the IPSEC extended
+routing table can only point
+(via a destination address, a Security Parameters Index (SPI) and
+a protocol identifier) to one SA.
+If more than one transform must be applied to a given type of packet,
+this can be accomplished by setting up several SAs
+with the same destination address but potentially different SPIs and protocols,
+and grouping them with
+.IR spigrp .
+.PP
+The SAs to be grouped,
+specified by destination address (DNS name lookup, IPv4 dotted quad or IPv6 coloned hex), SPI
+('0x'-prefixed hexadecimal number) and protocol ("ah", "esp", "comp" or "tun"),
+are listed from the inside transform to the
+outside;
+in other words, the transforms are applied in
+the order of the command line and removed in the reverse
+order.
+The resulting SA group is referred to by its first SA (by
+.IR af1 ,
+.IR dst1 ,
+.IR spi1
+and
+.IR proto1 ).
+.PP
+The \-\-said option indicates that the SA IDs are to be specified as
+one argument each, in the format <proto><af><spi>@<dest>. The SA IDs must
+all be specified as separate parameters without the \-\-said option or
+all as monolithic parameters after the \-\-said option.
+.PP
+The SAs must already exist and must not already
+be part of a group.
+.PP
+If
+.I spigrp
+is invoked with only one SA specification,
+it ungroups the previously-grouped set of SAs containing
+the SA specified.
+.PP
+The \-\-label option identifies all responses from that command
+invocation with a user-supplied label, provided as an argument to the
+label option. This can be helpful for debugging one invocation of the
+command out of a large number.
+.PP
+The command form with no additional arguments lists the contents of
+/proc/net/ipsec_spigrp. The format of /proc/net/ipsec_spigrp is
+discussed in ipsec_spigrp(5).
+.SH EXAMPLES
+.TP
+.B ipsec spigrp inet gw2 0x113 tun inet gw2 0x115 esp inet gw2 0x116 ah
+groups 3 SAs together, all destined for
+.BR gw2 ,
+but with an IPv4-in-IPv4 tunnel SA applied first with SPI
+.BR 0x113 ,
+then an ESP header to encrypt the packet with SPI
+.BR 0x115 ,
+and finally an AH header to authenticate the packet with SPI
+.BR 0x116 .
+.LP
+.TP
+.B ipsec spigrp --said tun.113@gw2 esp.115@gw2 ah.116@gw2
+groups 3 SAs together, all destined for
+.BR gw2 ,
+but with an IPv4-in-IPv4 tunnel SA applied first with SPI
+.BR 0x113 ,
+then an ESP header to encrypt the packet with SPI
+.BR 0x115 ,
+and finally an AH header to authenticate the packet with SPI
+.BR 0x116 .
+.LP
+.TP
+.B ipsec spigrp --said tun:233@3049:1::1 esp:235@3049:1::1 ah:236@3049:1::1
+groups 3 SAs together, all destined for
+.BR 3049:1::1,
+but with an IPv6-in-IPv6 tunnel SA applied first with SPI
+.BR 0x233 ,
+then an ESP header to encrypt the packet with SPI
+.BR 0x235 ,
+and finally an AH header to authenticate the packet with SPI
+.BR 0x236 .
+.LP
+.TP
+.B ipsec spigrp inet6 3049:1::1 0x233 tun inet6 3049:1::1 0x235 esp inet6 3049:1::1 0x236 ah
+groups 3 SAs together, all destined for
+.BR 3049:1::1,
+but with an IPv6-in-IPv6 tunnel SA applied first with SPI
+.BR 0x233 ,
+then an ESP header to encrypt the packet with SPI
+.BR 0x235 ,
+and finally an AH header to authenticate the packet with SPI
+.BR 0x236 .
+.LP
+.SH FILES
+/proc/net/ipsec_spigrp, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_tncfg(8), ipsec_eroute(8),
+ipsec_spi(8), ipsec_klipsdebug(8), ipsec_spigrp(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.SH BUGS
+Yes, it really is limited to a maximum of four SAs,
+although admittedly it's hard to see why you would need more.
+.\"
+.\" $Log: spigrp.8,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.20 2002/04/24 07:35:41 mcr
+.\" Moved from ./klips/utils/spigrp.8,v
+.\"
+.\" Revision 1.19 2000/09/17 18:56:48 rgb
+.\" Added IPCOMP support.
+.\"
+.\" Revision 1.18 2000/09/13 15:54:32 rgb
+.\" Added Gerhard's ipv6 updates.
+.\"
+.\" Revision 1.17 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.16 2000/06/21 16:54:57 rgb
+.\" Added 'no additional args' text for listing contents of
+.\" /proc/net/ipsec_* files.
+.\"
+.\" Revision 1.15 2000/02/14 21:08:30 rgb
+.\" Added description of --said option.
+.\"
+.\" Revision 1.14 1999/07/19 18:47:25 henry
+.\" fix slightly-misformed comments
+.\"
+.\" Revision 1.13 1999/04/06 04:54:39 rgb
+.\" Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+.\" patch shell fixes.
+.\"
diff --git a/programs/spigrp/spigrp.c b/programs/spigrp/spigrp.c
new file mode 100644
index 000000000..4cbac304d
--- /dev/null
+++ b/programs/spigrp/spigrp.c
@@ -0,0 +1,491 @@
+/*
+ * SA grouping
+ * Copyright (C) 1996 John Ioannidis.
+ * Copyright (C) 1997, 1998, 1999, 2000, 2001 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char spigrp_c_version[] = "RCSID $Id: spigrp.c,v 1.2 2004/06/07 15:16:34 as Exp $";
+
+
+#include <sys/types.h>
+#include <linux/types.h> /* new */
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h> /* open() */
+#include <fcntl.h> /* open() */
+#include <stdlib.h> /* system(), strtoul() */
+
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+/* #include <linux/ip.h> */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <freeswan.h>
+#if 0
+#include <linux/autoconf.h> /* CONFIG_IPSEC_PFKEYv2 */
+#endif
+
+#include <signal.h>
+#include <pfkeyv2.h>
+#include <pfkey.h>
+
+#include "freeswan/radij.h"
+#include "freeswan/ipsec_encap.h"
+#include "freeswan/ipsec_ah.h"
+
+
+char *program_name;
+
+int pfkey_sock;
+fd_set pfkey_socks;
+uint32_t pfkey_seq = 0;
+
+struct said_af {
+ int af;
+ ip_said said;
+}; /* to store the given saids and their address families in an array */
+ /* XXX: Note that we do *not* check if the address families of all SAID?s are the same.
+ * This can make it possible to group SAs for IPv4 addresses with SAs for
+ * IPv6 addresses (perhaps some kind of IPv4-over-secIPv6 or vice versa).
+ * Do not know, if this is a bug or feature */
+
+static void
+usage(char *s)
+{
+ fprintf(stdout, "usage: Note: position of options and arguments is important!\n");
+ fprintf(stdout, "usage: %s [ --debug ] [ --label <label> ] af1 dst1 spi1 proto1 [ af2 dst2 spi2 proto2 [ af3 dst3 spi3 proto3 [ af4 dst4 spi4 proto4 ] ] ]\n", s);
+ fprintf(stdout, "usage: %s [ --debug ] [ --label <label> ] --said <SA1> [ <SA2> [ <SA3> [ <SA4> ] ] ]\n", s);
+ fprintf(stdout, "usage: %s --help\n", s);
+ fprintf(stdout, "usage: %s --version\n", s);
+ fprintf(stdout, "usage: %s\n", s);
+ fprintf(stdout, " [ --debug ] is optional to any %s command.\n", s);
+ fprintf(stdout, " [ --label <label> ] is optional to any %s command.\n", s);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int i, nspis;
+ char *endptr;
+ int said_opt = 0;
+
+ const char* error_s = NULL;
+ char ipaddr_txt[ADDRTOT_BUF];
+ int debug = 0;
+ int j;
+ struct said_af said_af_array[4];
+
+ int error = 0;
+
+ struct sadb_ext *extensions[SADB_EXT_MAX + 1];
+ struct sadb_msg *pfkey_msg;
+#if 0
+ ip_address pfkey_address_s_ska;
+#endif
+
+ program_name = argv[0];
+ for(i = 0; i < 4; i++) {
+ memset(&said_af_array[i], 0, sizeof(struct said_af));
+ }
+
+ if(argc > 1 && strcmp(argv[1], "--debug") == 0) {
+ debug = 1;
+ if(debug) {
+ fprintf(stdout, "\"--debug\" option requested.\n");
+ }
+ argv += 1;
+ argc -= 1;
+ pfkey_lib_debug = PF_KEY_DEBUG_PARSE_MAX;
+ }
+
+ if(debug) {
+ fprintf(stdout, "argc=%d (%d incl. --debug option).\n",
+ argc,
+ argc + 1);
+ }
+
+ if(argc > 1 && strcmp(argv[1], "--label") == 0) {
+ if(argc > 2) {
+ program_name = malloc(strlen(argv[0])
+ + 10 /* update this when changing the sprintf() */
+ + strlen(argv[2]));
+ sprintf(program_name, "%s --label %s",
+ argv[0],
+ argv[2]);
+ if(debug) {
+ fprintf(stdout, "using \"%s\" as a label.\n", program_name);
+ }
+ argv += 2;
+ argc -= 2;
+ } else {
+ fprintf(stderr, "%s: --label option requires an argument.\n",
+ program_name);
+ exit(1);
+ }
+ }
+
+ if(debug) {
+ fprintf(stdout, "...After check for --label option.\n");
+ }
+
+ if(argc == 1) {
+ system("cat /proc/net/ipsec_spigrp");
+ exit(0);
+ }
+
+ if(debug) {
+ fprintf(stdout, "...After check for no option to print /proc/net/ipsec_spigrp.\n");
+ }
+
+ if(strcmp(argv[1], "--help") == 0) {
+ if(debug) {
+ fprintf(stdout, "\"--help\" option requested.\n");
+ }
+ usage(program_name);
+ exit(1);
+ }
+
+ if(debug) {
+ fprintf(stdout, "...After check for --help option.\n");
+ }
+
+ if(strcmp(argv[1], "--version") == 0) {
+ if(debug) {
+ fprintf(stdout, "\"--version\" option requested.\n");
+ }
+ fprintf(stderr, "%s, %s\n", program_name, spigrp_c_version);
+ exit(1);
+ }
+
+ if(debug) {
+ fprintf(stdout, "...After check for --version option.\n");
+ }
+
+ if(strcmp(argv[1], "--said") == 0) {
+ if(debug) {
+ fprintf(stdout, "processing %d args with --said flag.\n", argc);
+ }
+ said_opt = 1;
+ }
+
+ if(debug) {
+ fprintf(stdout, "...After check for --said option.\n");
+ }
+
+ if(said_opt) {
+ if (argc < 3 /*|| argc > 5*/) {
+ fprintf(stderr, "expecting 3 or more args with --said, got %d.\n", argc);
+ usage(program_name);
+ exit(1);
+ }
+ nspis = argc - 2;
+ } else {
+ if ((argc < 5) || (argc > 17) || ((argc % 4) != 1)) {
+ fprintf(stderr, "expecting 5 or more args without --said, got %d.\n", argc);
+ usage(program_name);
+ exit(1);
+ }
+ nspis = argc / 4;
+ }
+
+ if(debug) {
+ fprintf(stdout, "processing %d nspis.\n", nspis);
+ }
+
+ for(i = 0; i < nspis; i++) {
+ if(debug) {
+ fprintf(stdout, "processing spi #%d.\n", i);
+ }
+
+ if(said_opt) {
+ error_s = ttosa((const char *)argv[i+2], 0, (ip_said*)&(said_af_array[i].said));
+ if(error_s != NULL) {
+ fprintf(stderr, "%s: Error, %s converting --sa argument:%s\n",
+ program_name, error_s, argv[i+2]);
+ exit (1);
+ }
+ said_af_array[i].af = addrtypeof(&(said_af_array[i].said.dst));
+ if(debug) {
+ addrtot(&said_af_array[i].said.dst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "said[%d].dst=%s.\n", i, ipaddr_txt);
+ }
+ } else {
+ if(!strcmp(argv[i*4+4], "ah")) {
+ said_af_array[i].said.proto = SA_AH;
+ }
+ if(!strcmp(argv[i*4+4], "esp")) {
+ said_af_array[i].said.proto = SA_ESP;
+ }
+ if(!strcmp(argv[i*4+4], "tun")) {
+ said_af_array[i].said.proto = SA_IPIP;
+ }
+ if(!strcmp(argv[i*4+4], "comp")) {
+ said_af_array[i].said.proto = SA_COMP;
+ }
+ if(said_af_array[i].said.proto == 0) {
+ fprintf(stderr, "%s: Badly formed proto: %s\n",
+ program_name, argv[i*4+4]);
+ exit(1);
+ }
+ said_af_array[i].said.spi = htonl(strtoul(argv[i*4+3], &endptr, 0));
+ if(!(endptr == argv[i*4+3] + strlen(argv[i*4+3]))) {
+ fprintf(stderr, "%s: Badly formed spi: %s\n",
+ program_name, argv[i*4+3]);
+ exit(1);
+ }
+ if(!strcmp(argv[i*4+1], "inet")) {
+ said_af_array[i].af = AF_INET;
+ }
+ if(!strcmp(argv[i*4+1], "inet6")) {
+ said_af_array[i].af = AF_INET6;
+ }
+ if((said_af_array[i].af != AF_INET) && (said_af_array[i].af != AF_INET6)) {
+ fprintf(stderr, "%s: Address family %s not supported\n",
+ program_name, argv[i*4+1]);
+ exit(1);
+ }
+ error_s = ttoaddr(argv[i*4+2], 0, said_af_array[i].af, &(said_af_array[i].said.dst));
+ if(error_s != NULL) {
+ fprintf(stderr, "%s: Error, %s converting %dth address argument:%s\n",
+ program_name, error_s, i, argv[i*4+2]);
+ exit (1);
+ }
+ }
+ if(debug) {
+ fprintf(stdout, "SA %d contains: ", i+1);
+ fprintf(stdout, "\n");
+ fprintf(stdout, "proto = %d\n", said_af_array[i].said.proto);
+ fprintf(stdout, "spi = %08x\n", said_af_array[i].said.spi);
+ addrtot(&said_af_array[i].said.dst, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stdout, "edst = %s\n", ipaddr_txt);
+ }
+ }
+
+ if(debug) {
+ fprintf(stdout, "Opening pfkey socket.\n");
+ }
+
+ if((pfkey_sock = socket(PF_KEY, SOCK_RAW, PF_KEY_V2) ) < 0) {
+ fprintf(stderr, "%s: Trouble opening PF_KEY family socket with error: ",
+ program_name);
+ switch(errno) {
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, KLIPS not loaded or check kernel log messages for specifics.\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case EAFNOSUPPORT:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown file open error %d. Please report as much detail as possible to development team.\n", errno);
+ }
+ exit(1);
+ }
+
+ for(i = 0; i < (((nspis - 1) < 2) ? 1 : (nspis - 1)); i++) {
+ if(debug) {
+ fprintf(stdout, "processing %dth pfkey message.\n", i);
+ }
+
+ pfkey_extensions_init(extensions);
+ for(j = 0; j < ((nspis == 1) ? 1 : 2); j++) {
+ if(debug) {
+ fprintf(stdout, "processing %dth said of %dth pfkey message.\n", j, i);
+ }
+
+ /* Build an SADB_X_GRPSA message to send down. */
+ /* It needs <base, SA, SA2, address(D,D2) > minimum. */
+ if(!j) {
+ if((error = pfkey_msg_hdr_build(&extensions[0],
+ SADB_X_GRPSA,
+ proto2satype(said_af_array[i].said.proto),
+ 0,
+ ++pfkey_seq,
+ getpid()))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ } else {
+ if(debug) {
+ fprintf(stdout, "setting x_satype proto=%d satype=%d\n",
+ said_af_array[i+j].said.proto,
+ proto2satype(said_af_array[i+j].said.proto)
+ );
+ }
+
+ if((error = pfkey_x_satype_build(&extensions[SADB_X_EXT_SATYPE2],
+ proto2satype(said_af_array[i+j].said.proto)
+ ))) {
+ fprintf(stderr, "%s: Trouble building message header, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ }
+
+ if((error = pfkey_sa_build(&extensions[!j ? SADB_EXT_SA : SADB_X_EXT_SA2],
+ !j ? SADB_EXT_SA : SADB_X_EXT_SA2,
+ said_af_array[i+j].said.spi, /* in network order */
+ 0,
+ 0,
+ 0,
+ 0,
+ 0))) {
+ fprintf(stderr, "%s: Trouble building sa extension, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+
+#if 0
+ if(!j) {
+ anyaddr(said_af_array[i].af, &pfkey_address_s_ska); /* Is the address family correct ?? */
+ if((error = pfkey_address_build(&extensions[SADB_EXT_ADDRESS_SRC],
+ SADB_EXT_ADDRESS_SRC,
+ 0,
+ 0,
+ sockaddrof(&pfkey_address_s_ska)))) {
+ addrtot(&pfkey_address_s_ska, 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_s extension (%s), error=%d.\n",
+ program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+ }
+#endif
+ if((error = pfkey_address_build(&extensions[!j ? SADB_EXT_ADDRESS_DST : SADB_X_EXT_ADDRESS_DST2],
+ !j ? SADB_EXT_ADDRESS_DST : SADB_X_EXT_ADDRESS_DST2,
+ 0,
+ 0,
+ sockaddrof(&said_af_array[i+j].said.dst)))) {
+ addrtot(&said_af_array[i+j].said.dst,
+ 0, ipaddr_txt, sizeof(ipaddr_txt));
+ fprintf(stderr, "%s: Trouble building address_d extension (%s), error=%d.\n",
+ program_name, ipaddr_txt, error);
+ pfkey_extensions_free(extensions);
+ exit(1);
+ }
+
+ }
+
+ if((error = pfkey_msg_build(&pfkey_msg, extensions, EXT_BITS_IN))) {
+ fprintf(stderr, "%s: Trouble building pfkey message, error=%d.\n",
+ program_name, error);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ exit(1);
+ }
+
+ if((error = write(pfkey_sock,
+ pfkey_msg,
+ pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) !=
+ (ssize_t)(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN)) {
+ fprintf(stderr, "%s: pfkey write failed, returning %d with errno=%d.\n",
+ program_name, error, errno);
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ switch(errno) {
+ case EACCES:
+ fprintf(stderr, "access denied. ");
+ if(getuid() == 0) {
+ fprintf(stderr, "Check permissions. Should be 600.\n");
+ } else {
+ fprintf(stderr, "You must be root to open this file.\n");
+ }
+ break;
+ case EUNATCH:
+ fprintf(stderr, "Netlink not enabled OR KLIPS not loaded.\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "KLIPS is busy. Most likely a serious internal error occured in a previous command. Please report as much detail as possible to development team.\n");
+ break;
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "KLIPS not loaded or enabled.\n");
+ fprintf(stderr, "No device?!?\n");
+ break;
+ case ENOBUFS:
+ fprintf(stderr, "No kernel memory to allocate SA.\n");
+ break;
+ case ESOCKTNOSUPPORT:
+ fprintf(stderr, "Algorithm support not available in the kernel. Please compile in support.\n");
+ break;
+ case EEXIST:
+ fprintf(stderr, "SA already in use. Delete old one first.\n");
+ break;
+ case ENOENT:
+ fprintf(stderr, "device does not exist. See FreeS/WAN installation procedure.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "SA does not exist. Cannot delete.\n");
+ break;
+ case ENOSPC:
+ fprintf(stderr, "no room in kernel SAref table. Cannot process request.\n");
+ break;
+ case ESPIPE:
+ fprintf(stderr, "kernel SAref table internal error. Cannot process request.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket write error %d. Please report as much detail as possible to development team.\n", errno);
+ }
+ exit(1);
+ }
+ if(pfkey_msg) {
+ pfkey_extensions_free(extensions);
+ pfkey_msg_free(&pfkey_msg);
+ }
+ }
+
+ (void) close(pfkey_sock); /* close the socket */
+ exit(0);
+}
diff --git a/programs/starter/Makefile b/programs/starter/Makefile
new file mode 100644
index 000000000..60e95d360
--- /dev/null
+++ b/programs/starter/Makefile
@@ -0,0 +1,182 @@
+# ipsec starter Makefile
+# Copyright (C) 2001 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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.14 2006/02/17 19:34:02 as Exp $
+
+FREESWANSRCDIR?=$(shell cd ../..; pwd)
+include ${FREESWANSRCDIR}/Makefile.inc
+
+LD=$(CC)
+RM=rm
+LEX=flex
+BISON=bison
+GPERF=gperf
+
+FREESWANDIR=../..
+FREESWANLIB=$(FREESWANDIR)/lib/libfreeswan/libfreeswan.a
+PLUTODIR=../pluto
+OPENACDIR=../openac
+
+DEFINES+= -DVIRTUAL_IP -DDEBUG
+
+# This compile option activates the leak detective
+ifeq ($(USE_LEAK_DETECTIVE),true)
+ DEFINES+= -DLEAK_DETECTIVE
+endif
+
+INCLUDES=-I${FREESWANDIR}/linux/include
+CFLAGS=$(DEFINES) $(INCLUDES) -Wall
+CFLAGS+=-DIPSEC_EXECDIR=\"${FINALLIBEXECDIR}\" -DIPSEC_CONFDDIR=\"${FINALCONFDDIR}\"
+CFLAGS+=-DIPSEC_CONFDIR=\"${FINALCONFDIR}\"
+LDFLAGS=
+
+PLUTO_OBJS=defs.o
+
+OBJS=starter.o parser.tab.o lex.yy.o keywords.o args.o invokepluto.o \
+ starterwhack.o klips.o netkey.o interfaces.o exec.o cmp.o confread.o \
+ loglite.o ${PLUTO_OBJS}
+
+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
+
+LIBS=$(FREESWANLIB)
+
+PROGRAM=starter
+
+include ../Makefile.program
+
+all: starter
+
+starter: $(OBJS) $(FREESWANLIB)
+ $(LD) $(LDFLAGS) -o starter $(OBJS) $(LIBS)
+
+lex.yy.c: parser.tab.c parser.l parser.y parser.h
+ $(LEX) parser.l
+
+parser.tab.c: parser.l parser.y parser.h
+ $(BISON) -v -d parser.y
+
+keywords.c: keywords.txt keywords.h
+ $(GPERF) -C -G -t < keywords.txt > keywords.c
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+loglite.o : $(OPENACDIR)/loglite.c $(PLUTODIR)/log.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+# pluto library
+
+defs.o : $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean::
+ $(RM) -f starter $(OBJS) parser.tab.* lex.yy.*
+
+# 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":
+
+args.o: args.c
+cmp.o: cmp.c
+confread.o: confread.c
+exec.o: exec.c
+interfaces.o: interfaces.c
+invokepluto.o: invokepluto.c
+keywords.o: keywords.c
+klips.o: klips.c
+lex.yy.o: lex.yy.c
+netkey.o: netkey.c
+parser.tab.o: parser.tab.c
+starter.o: starter.c
+starterwhack.o: starterwhack.c
+
+args.o: ../pluto/constants.h
+args.o: ../pluto/defs.h
+args.o: ../pluto/log.h
+args.o: keywords.h
+args.o: parser.h
+args.o: confread.h
+args.o: args.h
+cmp.o: ../pluto/constants.h
+cmp.o: ../pluto/defs.h
+cmp.o: confread.h
+cmp.o: args.h
+cmp.o: interfaces.h
+cmp.o: cmp.h
+confread.o: ../pluto/constants.h
+confread.o: ../pluto/defs.h
+confread.o: ../pluto/log.h
+confread.o: keywords.h
+confread.o: parser.h
+confread.o: confread.h
+confread.o: args.h
+confread.o: interfaces.h
+exec.o: ../pluto/constants.h
+exec.o: ../pluto/defs.h
+exec.o: ../pluto/log.h
+exec.o: exec.h
+interfaces.o: ../pluto/constants.h
+interfaces.o: ../pluto/defs.h
+interfaces.o: ../pluto/log.h
+interfaces.o: interfaces.h
+interfaces.o: exec.h
+interfaces.o: files.h
+invokepluto.o: ../pluto/constants.h
+invokepluto.o: ../pluto/defs.h
+invokepluto.o: ../pluto/log.h
+invokepluto.o: confread.h
+invokepluto.o: invokepluto.h
+invokepluto.o: files.h
+invokepluto.o: starterwhack.h
+keywords.o: keywords.h
+klips.o: ../pluto/constants.h
+klips.o: ../pluto/defs.h
+klips.o: ../pluto/log.h
+klips.o: confread.h
+klips.o: klips.h
+klips.o: files.h
+klips.o: exec.h
+lex.yy.o: parser.tab.h
+netkey.o: ../pluto/constants.h
+netkey.o: ../pluto/defs.h
+netkey.o: ../pluto/log.h
+netkey.o: files.h
+parser.tab.o: ../pluto/constants.h
+parser.tab.o: ../pluto/defs.h
+parser.tab.o: parser.h
+starter.o: ../pluto/constants.h
+starter.o: ../pluto/defs.h
+starter.o: ../pluto/log.h
+starter.o: confread.h
+starter.o: files.h
+starter.o: starterwhack.h
+starter.o: invokepluto.h
+starter.o: klips.h
+starter.o: netkey.h
+starter.o: cmp.h
+starter.o: interfaces.h
+starterwhack.o: ../pluto/constants.h
+starterwhack.o: ../pluto/defs.h
+starterwhack.o: ../pluto/log.h
+starterwhack.o: ../pluto/whack.h
+starterwhack.o: starterwhack.h
+starterwhack.o: confread.h
+starterwhack.o: files.h
diff --git a/programs/starter/README b/programs/starter/README
new file mode 100644
index 000000000..12a60a11d
--- /dev/null
+++ b/programs/starter/README
@@ -0,0 +1,104 @@
+
+IPsec Starter -- Version 0.2 [Contributed by Arkoon Network Security]
+============================ [ http://www.arkoon.net/]
+
+IPsec Starter is aimed to replace all the scripts which are used to
+start and stop strongSwan and to do that in a quicker and a smarter way.
+
+IPsec Starter can also reload the configuration file (kill --HUP or periodicaly)
+and apply the changes.
+
+Usage:
+ starter [--debug] [--auto_update <x seconds>]
+ --debug: enable debugging output
+ --no_fork: all msg (including pluto) are sent to the console
+ --auto_update: reload the config file (like kill -HUP) every x seconds
+ and determine any configuration changes
+
+FEATURES
+--------
+
+o Load and unload KLIPS (ipsec.o kernel module)
+
+o Load modules of the native Linux 2.6 IPsec stack
+
+o Launch and monitor pluto
+
+o Add, initiate, route and del connections
+
+o Attach and detach interfaces according to config file
+
+o kill -HUP can be used to reload the config file. New connections will be
+ added, old ones will be removed and modified ones will be reloaded.
+ Interfaces/Klips/Pluto will be reloaded if necessary.
+
+o Full support of the %defaultroute wildcard parameter.
+
+o save own pid in /var/run/starter
+
+o Upon reloading, dynamic DNS addr will be resolved and reloaded. Use
+ --auto_update to periodicaly check dynamic DNS changes.
+
+o kill -USR1 can be used to reload all connections (delete then add and
+ route/initiate)
+
+o /var/run/dynip/xxxx can be used to use a virtual interface name in
+ ipsec.conf. By example, when adsl can be ppp0, ppp1, ... :
+ ipsec.conf: interfaces="ipsec0=adsl"
+ And use /etc/ppp/ip-up to create /var/run/dynip/adsl
+ /var/run/dynip/adsl: IP_PHYS=ppp0
+
+o %auto can be used to automaticaly name the connections
+
+o kill -TERM can be used to stop FS. pluto will be stopped and KLIPS unloaded
+ (if it has been loaded).
+
+o Can be used to start strongSwan and load lots of connections in a few
+ seconds.
+
+TODO
+----
+
+o handle wildcards in include lines -- use glob() fct
+ ex: include /etc/ipsec.*.conf
+
+o handle duplicates keywords and sections
+
+o 'also' keyword not supported
+
+o manually keyed connections
+
+o IPv6
+
+o Documentation
+
+
+CHANGES
+-------
+
+o Version 0.1 -- 2002.01.14 -- First public release
+
+o Version 0.2 -- 2002.09.04 -- Various enhancements
+ FreeS/WAN 1.98b, x509 0.9.14, algo 0.8.0
+
+o Version 0.2d -- 2004.01.13 -- Adaptions for Openswan 1.0.0
+ by Stephan Scholz <sscholz@astaro.com>
+
+o Version 0.2e -- 2004.10.14 -- Added support for change of interface address
+ by Stephan Scholz <sscholz@astaro.com>
+
+o Version 0.2s -- 2005-12-02 -- Ported to strongSwan
+ by Stephan Scholz <sscholz@astaro.com>
+
+o Version 0.2x -- 2006-01-02 -- Added missing strongSwan keywords
+ Full support of the native Linux 2.6 IPsec stack
+ Full support of %defaultroute
+ Improved parsing of keywords using perfect hash
+ function generated by gperf.
+ by Andreas Steffen <andreas.steffen@hsr.ch>
+
+THANKS
+------
+
+o Nathan Angelacos - include fix
+
diff --git a/programs/starter/args.c b/programs/starter/args.c
new file mode 100644
index 000000000..6f3da63eb
--- /dev/null
+++ b/programs/starter/args.c
@@ -0,0 +1,620 @@
+/* automatic handling of confread struct arguments
+ * Copyright (C) 2006 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: args.c,v 1.9 2006/04/17 10:32:36 as Exp $
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "keywords.h"
+#include "parser.h"
+#include "confread.h"
+#include "args.h"
+
+/* argument types */
+
+typedef enum {
+ ARG_NONE,
+ ARG_ENUM,
+ ARG_UINT,
+ ARG_TIME,
+ ARG_ULNG,
+ ARG_PCNT,
+ ARG_STR,
+ ARG_LST,
+ ARG_MISC
+} arg_t;
+
+/* various keyword lists */
+
+static const char *LST_bool[] = {
+ "no",
+ "yes",
+ NULL
+};
+
+static const char *LST_sendcert[] = {
+ "always",
+ "ifasked",
+ "never",
+ "yes",
+ "no",
+ NULL
+};
+
+static const char *LST_dpd_action[] = {
+ "none",
+ "clear",
+ "hold",
+ "restart",
+ NULL
+};
+
+static const char *LST_startup[] = {
+ "ignore",
+ "add",
+ "route",
+ "start",
+ NULL
+};
+
+static const char *LST_packetdefault[] = {
+ "drop",
+ "reject",
+ "pass",
+ NULL
+};
+
+static const char *LST_keyexchange[] = {
+ "ike",
+ NULL
+};
+
+static const char *LST_pfsgroup[] = {
+ "modp1024",
+ "modp1536",
+ "modp2048",
+ "modp3072",
+ "modp4096",
+ "modp6144",
+ "modp8192",
+ NULL
+};
+
+static const char *LST_plutodebug[] = {
+ "none",
+ "all",
+ "raw",
+ "crypt",
+ "parsing",
+ "emitting",
+ "control",
+ "lifecycle",
+ "klips",
+ "dns",
+ "natt",
+ "oppo",
+ "controlmore",
+ "private",
+ NULL
+};
+
+static const char *LST_klipsdebug[] = {
+ "tunnel",
+ "tunnel-xmit",
+ "pfkey",
+ "xform",
+ "eroute",
+ "spi",
+ "radij",
+ "esp",
+ "ah",
+ "ipcomp",
+ "verbose",
+ "all",
+ "none",
+ NULL
+};
+
+typedef struct {
+ arg_t type;
+ size_t offset;
+ const char **list;
+} token_info_t;
+
+static const token_info_t token_info[] =
+{
+ /* config setup keywords */
+ { ARG_LST, offsetof(starter_config_t, setup.interfaces), NULL },
+ { ARG_STR, offsetof(starter_config_t, setup.dumpdir), NULL },
+
+ /* pluto keywords */
+ { ARG_LST, offsetof(starter_config_t, setup.plutodebug), LST_plutodebug },
+ { ARG_STR, offsetof(starter_config_t, setup.prepluto), NULL },
+ { ARG_STR, offsetof(starter_config_t, setup.postpluto), NULL },
+ { ARG_ENUM, offsetof(starter_config_t, setup.uniqueids), LST_bool },
+ { ARG_UINT, offsetof(starter_config_t, setup.overridemtu), NULL },
+ { ARG_TIME, offsetof(starter_config_t, setup.crlcheckinterval), NULL },
+ { ARG_ENUM, offsetof(starter_config_t, setup.cachecrls), LST_bool },
+ { ARG_ENUM, offsetof(starter_config_t, setup.strictcrlpolicy), LST_bool },
+ { ARG_ENUM, offsetof(starter_config_t, setup.nocrsend), LST_bool },
+ { ARG_ENUM, offsetof(starter_config_t, setup.nat_traversal), LST_bool },
+ { ARG_TIME, offsetof(starter_config_t, setup.keep_alive), NULL },
+ { ARG_STR, offsetof(starter_config_t, setup.virtual_private), NULL },
+ { ARG_STR, offsetof(starter_config_t, setup.pkcs11module), NULL },
+ { ARG_ENUM, offsetof(starter_config_t, setup.pkcs11keepstate), LST_bool },
+ { ARG_ENUM, offsetof(starter_config_t, setup.pkcs11proxy), LST_bool },
+
+ /* KLIPS keywords */
+ { ARG_LST, offsetof(starter_config_t, setup.klipsdebug), LST_klipsdebug },
+ { ARG_ENUM, offsetof(starter_config_t, setup.fragicmp), LST_bool },
+ { ARG_STR, offsetof(starter_config_t, setup.packetdefault), LST_packetdefault },
+ { ARG_ENUM, offsetof(starter_config_t, setup.hidetos), LST_bool },
+
+ /* conn section keywords */
+ { ARG_STR, offsetof(starter_conn_t, name), NULL },
+ { ARG_ENUM, offsetof(starter_conn_t, startup), LST_startup },
+ { ARG_ENUM, offsetof(starter_conn_t, keyexchange), LST_keyexchange },
+ { ARG_MISC, 0, NULL /* KW_TYPE */ },
+ { ARG_MISC, 0, NULL /* KW_PFS */ },
+ { ARG_MISC, 0, NULL /* KW_COMPRESS */ },
+ { ARG_MISC, 0, NULL /* KW_AUTH */ },
+ { ARG_MISC, 0, NULL /* KW_AUTHBY */ },
+ { ARG_TIME, offsetof(starter_conn_t, sa_ike_life_seconds), NULL },
+ { ARG_TIME, offsetof(starter_conn_t, sa_ipsec_life_seconds), NULL },
+ { ARG_TIME, offsetof(starter_conn_t, sa_rekey_margin), NULL },
+ { ARG_ULNG, offsetof(starter_conn_t, sa_keying_tries), NULL },
+ { ARG_PCNT, offsetof(starter_conn_t, sa_rekey_fuzz), NULL },
+ { ARG_MISC, 0, NULL /* KW_REKEY */ },
+ { ARG_STR, offsetof(starter_conn_t, ike), NULL },
+ { ARG_STR, offsetof(starter_conn_t, esp), NULL },
+ { ARG_STR, offsetof(starter_conn_t, pfsgroup), LST_pfsgroup },
+ { ARG_TIME, offsetof(starter_conn_t, dpd_delay), NULL },
+ { ARG_TIME, offsetof(starter_conn_t, dpd_timeout), NULL },
+ { ARG_ENUM, offsetof(starter_conn_t, dpd_action), LST_dpd_action },
+
+ /* ca section keywords */
+ { ARG_STR, offsetof(starter_ca_t, name), NULL },
+ { ARG_ENUM, offsetof(starter_ca_t, startup), LST_startup },
+ { ARG_STR, offsetof(starter_ca_t, cacert), NULL },
+ { ARG_STR, offsetof(starter_ca_t, ldaphost), NULL },
+ { ARG_STR, offsetof(starter_ca_t, ldapbase), NULL },
+ { ARG_STR, offsetof(starter_ca_t, crluri), NULL },
+ { ARG_STR, offsetof(starter_ca_t, crluri2), NULL },
+ { ARG_STR, offsetof(starter_ca_t, ocspuri), NULL },
+
+ /* end keywords */
+ { ARG_MISC, 0, NULL /* KW_HOST */ },
+ { ARG_MISC, 0, NULL /* KW_NEXTHOP */ },
+ { ARG_MISC, 0, NULL /* KW_SUBNET */ },
+ { ARG_MISC, 0, NULL /* KW_SUBNETWITHIN */ },
+ { ARG_MISC, 0, NULL /* KW_PROTOPORT */ },
+ { ARG_MISC, 0, NULL /* KW_SOURCEIP */ },
+ { ARG_ENUM, offsetof(starter_end_t, firewall), LST_bool },
+ { ARG_ENUM, offsetof(starter_end_t, hostaccess), LST_bool },
+ { ARG_STR, offsetof(starter_end_t, updown), NULL },
+ { ARG_STR, offsetof(starter_end_t, id), NULL },
+ { ARG_STR, offsetof(starter_end_t, rsakey), NULL },
+ { ARG_STR, offsetof(starter_end_t, cert), NULL },
+ { ARG_ENUM, offsetof(starter_end_t, sendcert), LST_sendcert },
+ { ARG_STR, offsetof(starter_end_t, ca), NULL },
+ { ARG_STR, offsetof(starter_end_t, groups), NULL },
+ { ARG_STR, offsetof(starter_end_t, iface), NULL }
+};
+
+static void
+free_list(char **list)
+{
+ char **s;
+
+ for (s = list; *s; s++)
+ pfree(*s);
+ pfree(list);
+}
+
+char **
+new_list(char *value)
+{
+ char *val, *b, *e, *end, **ret;
+ int count;
+
+ val = value ? clone_str(value, "list value") : NULL;
+ if (!val)
+ return NULL;
+ end = val + strlen(val);
+ for (b = val, count = 0; b < end;)
+ {
+ for (e = b; ((*e != ' ') && (*e != '\0')); e++);
+ *e = '\0';
+ if (e != b)
+ count++;
+ b = e + 1;
+ }
+ if (count == 0)
+ {
+ pfree(val);
+ return NULL;
+ }
+ ret = (char **)alloc_bytes((count+1) * sizeof(char *), "list");
+
+ for (b = val, count = 0; b < end; )
+ {
+ for (e = b; (*e != '\0'); e++);
+ if (e != b)
+ ret[count++] = clone_str(b, "list value");
+ b = e + 1;
+ }
+ ret[count] = NULL;
+ pfree(val);
+ return ret;
+}
+
+
+/*
+ * assigns an argument value to a struct field
+ */
+bool
+assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw, char *base
+ , bool *assigned)
+{
+ char *p = base + token_info[token].offset;
+ const char **list = token_info[token].list;
+
+ int index = -1; /* used for enumeration arguments */
+
+ lset_t *seen = (lset_t *)base; /* seen flags are at the top of the struct */
+ lset_t f = LELEM(token - first); /* compute flag position of argument */
+
+ *assigned = FALSE;
+
+ DBG(DBG_CONTROLMORE,
+ DBG_log(" %s=%s", kw->entry->name, kw->value)
+ )
+
+ if (*seen & f)
+ {
+ plog("# duplicate '%s' option", kw->entry->name);
+ return FALSE;
+ }
+
+ /* set flag that this argument has been seen */
+ *seen |= f;
+
+ /* is there a keyword list? */
+ if (list != NULL && token_info[token].type != ARG_LST)
+ {
+ bool match = FALSE;
+
+ while (*list != NULL && !match)
+ {
+ index++;
+ match = streq(kw->value, *list++);
+ }
+ if (!match)
+ {
+ plog("# bad value: %s=%s", kw->entry->name, kw->value);
+ return FALSE;
+ }
+ }
+
+ switch (token_info[token].type)
+ {
+ case ARG_NONE:
+ plog("# option '%s' not supported yet", kw->entry->name);
+ return FALSE;
+ case ARG_ENUM:
+ {
+ int *i = (int *)p;
+
+ if (index < 0)
+ {
+ plog("# bad enumeration value: %s=%s (%d)"
+ , kw->entry->name, kw->value, index);
+ return FALSE;
+ }
+ *i = index;
+ }
+ break;
+
+ case ARG_UINT:
+ {
+ char *endptr;
+ u_int *u = (u_int *)p;
+
+ *u = strtoul(kw->value, &endptr, 10);
+
+ if (*endptr != '\0')
+ {
+ plog("# bad integer value: %s=%s", kw->entry->name, kw->value);
+ return FALSE;
+ }
+ }
+ break;
+ case ARG_ULNG:
+ case ARG_PCNT:
+ {
+ char *endptr;
+ unsigned long *l = (unsigned long *)p;
+
+ *l = strtoul(kw->value, &endptr, 10);
+
+ if (token_info[token].type == ARG_ULNG)
+ {
+ if (*endptr != '\0')
+ {
+ plog("# bad integer value: %s=%s", kw->entry->name, kw->value);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if ((*endptr != '%') || (endptr[1] != '\0') || endptr == kw->value)
+ {
+ plog("# bad percent value: %s=%s", kw->entry->name, kw->value);
+ return FALSE;
+ }
+ }
+
+ }
+ break;
+ case ARG_TIME:
+ {
+ char *endptr;
+ time_t *t = (time_t *)p;
+
+ *t = strtoul(kw->value, &endptr, 10);
+
+ /* time in seconds? */
+ if (*endptr == '\0' || (*endptr == 's' && endptr[1] == '\0'))
+ break;
+
+ if (endptr[1] == '\0')
+ {
+ if (*endptr == 'm') /* time in minutes? */
+ {
+ *t *= 60;
+ break;
+ }
+ if (*endptr == 'h') /* time in hours? */
+ {
+ *t *= 3600;
+ break;
+ }
+ if (*endptr == 'd') /* time in days? */
+ {
+ *t *= 3600*24;
+ break;
+ }
+ }
+ plog("# bad duration value: %s=%s", kw->entry->name, kw->value);
+ return FALSE;
+ }
+ case ARG_STR:
+ {
+ char **cp = (char **)p;
+
+ /* free any existing string */
+ pfreeany(*cp);
+
+ /* assign the new string */
+ *cp = clone_str(kw->value, "str_value");
+ }
+ break;
+ case ARG_LST:
+ {
+ char ***listp = (char ***)p;
+
+ /* free any existing list */
+ if (*listp != NULL)
+ free_list(*listp);
+
+ /* create a new list and assign values */
+ *listp = new_list(kw->value);
+
+ /* is there a keyword list? */
+ if (list != NULL)
+ {
+ char ** lst;
+
+ for (lst = *listp; lst && *lst; lst++)
+ {
+ bool match = FALSE;
+
+ list = token_info[token].list;
+
+ while (*list != NULL && !match)
+ {
+ match = streq(*lst, *list++);
+ }
+ if (!match)
+ {
+ plog("# bad value: %s=%s", kw->entry->name, *lst);
+ return FALSE;
+ }
+ }
+ }
+ }
+ default:
+ return TRUE;
+ }
+
+ *assigned = TRUE;
+ return TRUE;
+}
+
+/*
+ * frees all dynamically allocated arguments in a struct
+ */
+void
+free_args(kw_token_t first, kw_token_t last, char *base)
+{
+ kw_token_t token;
+
+ for (token = first; token <= last; token++)
+ {
+ char *p = base + token_info[token].offset;
+
+ switch (token_info[token].type)
+ {
+ case ARG_STR:
+ {
+ char **cp = (char **)p;
+
+ pfreeany(*cp);
+ *cp = NULL;
+ }
+ break;
+ case ARG_LST:
+ {
+ char ***listp = (char ***)p;
+
+ if (*listp != NULL)
+ {
+ free_list(*listp);
+ *listp = NULL;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * clone all dynamically allocated arguments in a struct
+ */
+void
+clone_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
+{
+ kw_token_t token;
+
+ for (token = first; token <= last; token++)
+ {
+ if (token_info[token].type == ARG_STR)
+ {
+ char **cp1 = (char **)(base1 + token_info[token].offset);
+ char **cp2 = (char **)(base2 + token_info[token].offset);
+
+ *cp1 = clone_str(*cp2, "cloned str");
+ }
+ }
+}
+
+static bool
+cmp_list(char **list1, char **list2)
+{
+ if ((list1 == NULL) && (list2 == NULL))
+ return TRUE;
+ if ((list1 == NULL) || (list2 == NULL))
+ return FALSE;
+
+ for ( ; *list1 && *list2; list1++, list2++)
+ {
+ if (strcmp(*list1,*list2) != 0)
+ return FALSE;
+ }
+
+ if ((*list1 != NULL) || (*list2 != NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * compare all arguments in a struct
+ */
+bool
+cmp_args(kw_token_t first, kw_token_t last, char *base1, char *base2)
+{
+ kw_token_t token;
+
+ for (token = first; token <= last; token++)
+ {
+ char *p1 = base1 + token_info[token].offset;
+ char *p2 = base2 + token_info[token].offset;
+
+ switch (token_info[token].type)
+ {
+ case ARG_ENUM:
+ {
+ int *i1 = (int *)p1;
+ int *i2 = (int *)p2;
+
+ if (*i1 != *i2)
+ return FALSE;
+ }
+ break;
+ case ARG_UINT:
+ {
+ u_int *u1 = (u_int *)p1;
+ u_int *u2 = (u_int *)p2;
+
+ if (*u1 != *u2)
+ return FALSE;
+ }
+ break;
+ case ARG_ULNG:
+ case ARG_PCNT:
+ {
+ unsigned long *l1 = (unsigned long *)p1;
+ unsigned long *l2 = (unsigned long *)p2;
+
+ if (*l1 != *l2)
+ return FALSE;
+ }
+ break;
+ case ARG_TIME:
+ {
+ time_t *t1 = (time_t *)p1;
+ time_t *t2 = (time_t *)p2;
+
+ if (*t1 != *t2)
+ return FALSE;
+ }
+ break;
+ case ARG_STR:
+ {
+ char **cp1 = (char **)p1;
+ char **cp2 = (char **)p2;
+
+ if (*cp1 == NULL && *cp2 == NULL)
+ break;
+ if (*cp1 == NULL || *cp2 == NULL || strcmp(*cp1, *cp2) != 0)
+ return FALSE;
+ }
+ break;
+ case ARG_LST:
+ {
+ char ***listp1 = (char ***)p1;
+ char ***listp2 = (char ***)p2;
+
+ if (!cmp_list(*listp1, *listp2))
+ return FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return TRUE;
+}
diff --git a/programs/starter/args.h b/programs/starter/args.h
new file mode 100644
index 000000000..302e9bb7b
--- /dev/null
+++ b/programs/starter/args.h
@@ -0,0 +1,34 @@
+/* automatic handling of confread struct arguments
+ * Copyright (C) 2006 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: args.h,v 1.3 2006/01/13 18:02:02 as Exp $
+ */
+
+#ifndef _ARGS_H_
+#define _ARGS_H_
+
+#include "keywords.h"
+#include "parser.h"
+
+extern char **new_list(char *value);
+extern bool assign_arg(kw_token_t token, kw_token_t first, kw_list_t *kw
+ , char *base, bool *assigned);
+extern void free_args(kw_token_t first, kw_token_t last, char *base);
+extern void clone_args(kw_token_t first, kw_token_t last, char *base1
+ , char *base2);
+extern bool cmp_args(kw_token_t first, kw_token_t last, char *base1
+ , char *base2);
+
+#endif /* _ARGS_H_ */
+
diff --git a/programs/starter/cmp.c b/programs/starter/cmp.c
new file mode 100644
index 000000000..9222bf58f
--- /dev/null
+++ b/programs/starter/cmp.c
@@ -0,0 +1,105 @@
+/* strongSwan IPsec starter comparison functions
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: cmp.c,v 1.12 2006/01/13 18:03:25 as Exp $
+ */
+
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+
+#include "confread.h"
+#include "args.h"
+#include "interfaces.h"
+#include "cmp.h"
+
+#define VARCMP(obj) if (c1->obj != c2->obj) return FALSE
+#define ADDCMP(obj) if (!sameaddr(&c1->obj,&c2->obj)) return FALSE
+#define SUBCMP(obj) if (!samesubnet(&c1->obj,&c2->obj)) return FALSE
+
+static bool
+starter_cmp_end(starter_end_t *c1, starter_end_t *c2)
+{
+ if ((c1 == NULL) || (c2 == NULL))
+ return FALSE;
+
+ ADDCMP(addr);
+ ADDCMP(nexthop);
+ ADDCMP(srcip);
+ SUBCMP(subnet);
+ VARCMP(has_client);
+ VARCMP(has_client_wildcard);
+ VARCMP(has_port_wildcard);
+ VARCMP(has_srcip);
+ VARCMP(modecfg);
+ VARCMP(port);
+ VARCMP(protocol);
+
+ return cmp_args(KW_END_FIRST, KW_END_LAST, (char *)c1, (char *)c2);
+ }
+
+bool
+starter_cmp_conn(starter_conn_t *c1, starter_conn_t *c2)
+{
+ if ((c1 == NULL) || (c2 == NULL))
+ return FALSE;
+
+ VARCMP(policy);
+ VARCMP(addr_family);
+ VARCMP(tunnel_addr_family);
+
+ if (!starter_cmp_end(&c1->left, &c2->left))
+ return FALSE;
+ if (!starter_cmp_end(&c1->right, &c2->right))
+ return FALSE;
+
+ return cmp_args(KW_CONN_NAME, KW_CONN_LAST, (char *)c1, (char *)c2);
+}
+
+bool
+starter_cmp_ca(starter_ca_t *c1, starter_ca_t *c2)
+{
+ if (c1 == NULL || c2 == NULL)
+ return FALSE;
+
+ return cmp_args(KW_CA_NAME, KW_CA_LAST, (char *)c1, (char *)c2);
+}
+
+bool
+starter_cmp_klips(starter_config_t *c1, starter_config_t *c2)
+{
+ if ((c1 == NULL) || (c2 == NULL))
+ return FALSE;
+
+ return cmp_args(KW_KLIPS_FIRST, KW_KLIPS_LAST, (char *)c1, (char *)c2);
+}
+
+bool
+starter_cmp_pluto(starter_config_t *c1, starter_config_t *c2)
+{
+ if ((c1 == NULL) || (c2 == NULL))
+ return FALSE;
+
+ return cmp_args(KW_PLUTO_FIRST, KW_PLUTO_LAST, (char *)c1, (char *)c2);
+}
+
+bool
+starter_cmp_defaultroute(defaultroute_t *d1, defaultroute_t *d2)
+{
+ if ((d1 == NULL) || (d2 == NULL))
+ return FALSE;
+ return memcmp(d1, d2, sizeof(defaultroute_t)) == 0;
+}
diff --git a/programs/starter/cmp.h b/programs/starter/cmp.h
new file mode 100644
index 000000000..ca355e9eb
--- /dev/null
+++ b/programs/starter/cmp.h
@@ -0,0 +1,29 @@
+/* strongSwan IPsec starter comparison functions
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: cmp.h,v 1.4 2006/01/06 20:24:41 as Exp $
+ */
+
+#ifndef _STARTER_CMP_H_
+#define _STARTER_CMP_H_
+
+#include "interfaces.h"
+
+extern bool starter_cmp_conn(starter_conn_t *c1, starter_conn_t *c2);
+extern bool starter_cmp_ca(starter_ca_t *c1, starter_ca_t *c2);
+extern bool starter_cmp_klips(starter_config_t *c1, starter_config_t *c2);
+extern bool starter_cmp_pluto(starter_config_t *c1, starter_config_t *c2);
+extern bool starter_cmp_defaultroute(defaultroute_t *d1, defaultroute_t *d2);
+
+#endif
+
diff --git a/programs/starter/confread.c b/programs/starter/confread.c
new file mode 100644
index 000000000..cf12d05ca
--- /dev/null
+++ b/programs/starter/confread.c
@@ -0,0 +1,861 @@
+/* strongSwan IPsec config file parser
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: confread.c,v 1.37 2006/04/17 19:35:07 as Exp $
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "keywords.h"
+#include "parser.h"
+#include "confread.h"
+#include "args.h"
+#include "interfaces.h"
+
+static const char ike_defaults[] = "3des-sha, 3des-md5";
+static const char esp_defaults[] = "3des-sha1, 3des-md5";
+
+static const char firewall_defaults[] = "ipsec _updown iptables";
+
+static void
+default_values(starter_config_t *cfg)
+{
+ if (cfg == NULL)
+ return;
+
+ memset(cfg, 0, sizeof(struct starter_config));
+
+ /* is there enough space for all seen flags? */
+ assert(KW_SETUP_LAST - KW_SETUP_FIRST <
+ sizeof(cfg->setup.seen) * BITS_PER_BYTE);
+ assert(KW_CONN_LAST - KW_CONN_FIRST <
+ sizeof(cfg->conn_default.seen) * BITS_PER_BYTE);
+ assert(KW_END_LAST - KW_END_FIRST <
+ sizeof(cfg->conn_default.right.seen) * BITS_PER_BYTE);
+ assert(KW_CA_LAST - KW_CA_FIRST <
+ sizeof(cfg->ca_default.seen) * BITS_PER_BYTE);
+
+ cfg->setup.seen = LEMPTY;
+ cfg->setup.fragicmp = TRUE;
+ cfg->setup.hidetos = TRUE;
+ cfg->setup.uniqueids = TRUE;
+ cfg->setup.interfaces = new_list("%defaultroute");
+
+ cfg->conn_default.seen = LEMPTY;
+ cfg->conn_default.startup = STARTUP_NO;
+ cfg->conn_default.state = STATE_IGNORE;
+ cfg->conn_default.policy = POLICY_ENCRYPT | POLICY_TUNNEL | POLICY_RSASIG
+ | POLICY_PFS;
+
+ cfg->conn_default.ike = clone_str(ike_defaults, "ike_defaults");
+ cfg->conn_default.esp = clone_str(esp_defaults, "esp_defaults");
+ cfg->conn_default.sa_ike_life_seconds = OAKLEY_ISAKMP_SA_LIFETIME_DEFAULT;
+ cfg->conn_default.sa_ipsec_life_seconds = PLUTO_SA_LIFE_DURATION_DEFAULT;
+ cfg->conn_default.sa_rekey_margin = SA_REPLACEMENT_MARGIN_DEFAULT;
+ cfg->conn_default.sa_rekey_fuzz = SA_REPLACEMENT_FUZZ_DEFAULT;
+ cfg->conn_default.sa_keying_tries = SA_REPLACEMENT_RETRIES_DEFAULT;
+ cfg->conn_default.addr_family = AF_INET;
+ cfg->conn_default.tunnel_addr_family = AF_INET;
+
+ cfg->conn_default.left.seen = LEMPTY;
+ cfg->conn_default.right.seen = LEMPTY;
+
+ anyaddr(AF_INET, &cfg->conn_default.left.addr);
+ anyaddr(AF_INET, &cfg->conn_default.left.nexthop);
+ anyaddr(AF_INET, &cfg->conn_default.left.srcip);
+ anyaddr(AF_INET, &cfg->conn_default.right.addr);
+ anyaddr(AF_INET, &cfg->conn_default.right.nexthop);
+ anyaddr(AF_INET, &cfg->conn_default.right.srcip);
+
+ cfg->ca_default.seen = LEMPTY;
+}
+
+#define KW_POLICY_FLAG(sy, sn, fl) \
+ if (streq(kw->value, sy)) { conn->policy |= fl; } \
+ else if (streq(kw->value, sn)) { conn->policy &= ~fl; } \
+ else { plog("# bad policy value: %s=%s", kw->entry->name, kw->value); cfg->err++; }
+
+static void
+load_setup(starter_config_t *cfg, config_parsed_t *cfgp)
+{
+ kw_list_t *kw;
+
+ DBG(DBG_CONTROL,
+ DBG_log("Loading config setup")
+ )
+
+ for (kw = cfgp->config_setup; kw; kw = kw->next)
+ {
+ bool assigned = FALSE;
+
+ kw_token_t token = kw->entry->token;
+
+ if (token < KW_SETUP_FIRST || token > KW_SETUP_LAST)
+ {
+ plog("# unsupported keyword '%s' in config setup", kw->entry->name);
+ cfg->err++;
+ continue;
+ }
+
+ if (!assign_arg(token, KW_SETUP_FIRST, kw, (char *)cfg, &assigned))
+ {
+ plog(" bad argument value in config setup");
+ cfg->err++;
+ continue;
+ }
+ }
+}
+
+static void
+kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token
+ , kw_list_t *kw, char *conn_name, starter_config_t *cfg)
+{
+ err_t ugh = NULL;
+ bool assigned = FALSE;
+ int has_port_wildcard; /* set if port is %any */
+
+ char *name = kw->entry->name;
+ char *value = kw->value;
+
+ if (!assign_arg(token, KW_END_FIRST, kw, (char *)end, &assigned))
+ goto err;
+
+ if (token == KW_SENDCERT)
+ {
+ if (end->sendcert == CERT_YES_SEND)
+ end->sendcert = CERT_ALWAYS_SEND;
+ else if (end->sendcert == CERT_NO_SEND)
+ end->sendcert = CERT_NEVER_SEND;
+ }
+
+ if (assigned)
+ return;
+
+ switch (token)
+ {
+ case KW_HOST:
+ if (streq(value, "%defaultroute"))
+ {
+ if (cfg->defaultroute.defined)
+ {
+ end->addr = cfg->defaultroute.addr;
+ end->nexthop = cfg->defaultroute.nexthop;
+ }
+ else
+ {
+ plog("# default route not known: %s=%s", name, value);
+ goto err;
+ }
+ }
+ else if (streq(value,"%any"))
+ {
+ anyaddr(conn->addr_family, &end->addr);
+ }
+ else if (value[0] == '%')
+ {
+ if (end->iface)
+ pfree(end->iface);
+ end->iface = clone_str(value+1, "iface");
+ if (starter_iface_find(end->iface, conn->addr_family, &end->addr,
+ &end->nexthop) == -1)
+ {
+ conn->state = STATE_INVALID;
+ }
+ }
+ else
+ {
+ ugh = ttoaddr(value, 0, conn->addr_family, &end->addr);
+ if (ugh != NULL)
+ {
+ plog("# bad addr: %s=%s [%s]", name, value, ugh);
+ goto err;
+ }
+ }
+ break;
+ case KW_NEXTHOP:
+ if (streq(value, "%defaultroute"))
+ {
+ if (cfg->defaultroute.defined)
+ end->nexthop = cfg->defaultroute.nexthop;
+ else
+ {
+ plog("# default route not known: %s=%s", name, value);
+ goto err;
+ }
+ }
+ else if (streq(value, "%direct"))
+ ugh = anyaddr(conn->addr_family, &end->nexthop);
+ else
+ ugh = ttoaddr(value, 0, conn->addr_family, &end->nexthop);
+
+ if (ugh != NULL)
+ {
+ plog("# bad addr: %s=%s [%s]", name, value, ugh);
+ goto err;
+ }
+ break;
+ case KW_SUBNET:
+ if ((strlen(value) >= 6 && strncmp(value,"vhost:",6) == 0)
+ || (strlen(value) >= 5 && strncmp(value,"vnet:",5) == 0))
+ {
+ end->virt = clone_str(value, "virt");
+ }
+ else
+ {
+ end->has_client = TRUE;
+ ugh = ttosubnet(value, 0, conn->tunnel_addr_family, &end->subnet);
+ if (ugh != NULL)
+ {
+ plog("# bad subnet: %s=%s [%s]", name, value, ugh);
+ goto err;
+ }
+ }
+ break;
+ case KW_SUBNETWITHIN:
+ end->has_client = TRUE;
+ end->has_client_wildcard = TRUE;
+ ugh = ttosubnet(value, 0, conn->tunnel_addr_family, &end->subnet);
+ break;
+ case KW_PROTOPORT:
+ ugh = ttoprotoport(value, 0, &end->protocol, &end->port, &has_port_wildcard);
+ end->has_port_wildcard = has_port_wildcard;
+ break;
+ case KW_SOURCEIP:
+ if (streq(value, "%modeconfig") || streq(value, "%modecfg"))
+ {
+ end->modecfg = TRUE;
+ }
+ else
+ {
+ ugh = ttoaddr(value, 0, conn->addr_family, &end->srcip);
+ if (ugh != NULL)
+ {
+ plog("# bad addr: %s=%s [%s]", name, value, ugh);
+ goto err;
+ }
+ end->has_srcip = TRUE;
+ }
+ conn->policy |= POLICY_TUNNEL;
+ break;
+ default:
+ break;
+ }
+ return;
+
+err:
+ plog(" bad argument value in conn '%s'", conn_name);
+ cfg->err++;
+}
+
+/*
+ * handles left|rightfirewall and left|rightupdown parameters
+ */
+static void
+handle_firewall( const char *label, starter_end_t *end, starter_config_t *cfg)
+{
+ if (end->firewall && (end->seen & LELEM(KW_FIREWALL - KW_END_FIRST)))
+ {
+ if (end->updown != NULL)
+ {
+ plog("# cannot have both %sfirewall and %supdown", label, label);
+ cfg->err++;
+ }
+ else
+ {
+ end->updown = clone_str(firewall_defaults, "firewall_defaults");
+ end->firewall = FALSE;
+ }
+ }
+}
+
+/*
+ * parse a conn section
+ */
+static void
+load_conn(starter_conn_t *conn, kw_list_t *kw, starter_config_t *cfg)
+{
+ char *conn_name = (conn->name == NULL)? "%default":conn->name;
+
+ for ( ; kw; kw = kw->next)
+ {
+ bool assigned = FALSE;
+
+ kw_token_t token = kw->entry->token;
+
+ if (token >= KW_LEFT_FIRST && token <= KW_LEFT_LAST)
+ {
+ kw_end(conn, &conn->left, token - KW_LEFT_FIRST + KW_END_FIRST
+ , kw, conn_name, cfg);
+ continue;
+ }
+ else if (token >= KW_RIGHT_FIRST && token <= KW_RIGHT_LAST)
+ {
+ kw_end(conn, &conn->right, token - KW_RIGHT_FIRST + KW_END_FIRST
+ , kw, conn_name, cfg);
+ continue;
+ }
+
+ if (token == KW_AUTO)
+ {
+ token = KW_CONN_SETUP;
+ }
+ else if (token == KW_ALSO)
+ {
+ if (cfg->parse_also)
+ {
+ also_t *also = alloc_thing(also_t, "also_t");
+
+ also->name = clone_str(kw->value, "also");
+ also->next = conn->also;
+ conn->also = also;
+
+ DBG(DBG_CONTROL,
+ DBG_log(" also=%s", kw->value)
+ )
+ }
+ continue;
+ }
+
+ if (token < KW_CONN_FIRST || token > KW_CONN_LAST)
+ {
+ plog("# unsupported keyword '%s' in conn '%s'"
+ , kw->entry->name, conn_name);
+ cfg->err++;
+ continue;
+ }
+
+ if (!assign_arg(token, KW_CONN_FIRST, kw, (char *)conn, &assigned))
+ {
+ plog(" bad argument value in conn '%s'", conn_name);
+ cfg->err++;
+ continue;
+ }
+
+ if (assigned)
+ continue;
+
+ switch (token)
+ {
+ case KW_TYPE:
+ conn->policy &= ~(POLICY_TUNNEL | POLICY_SHUNT_MASK);
+ if (streq(kw->value, "tunnel"))
+ conn->policy |= POLICY_TUNNEL;
+ else if (streq(kw->value, "passthrough") || streq(kw->value, "pass"))
+ conn->policy |= POLICY_SHUNT_PASS;
+ else if (streq(kw->value, "drop"))
+ conn->policy |= POLICY_SHUNT_DROP;
+ else if (streq(kw->value, "reject"))
+ conn->policy |= POLICY_SHUNT_REJECT;
+ else if (strcmp(kw->value, "transport") != 0)
+ {
+ plog("# bad policy value: %s=%s", kw->entry->name, kw->value);
+ cfg->err++;
+ }
+ break;
+ case KW_PFS:
+ KW_POLICY_FLAG("yes", "no", POLICY_PFS)
+ break;
+ case KW_COMPRESS:
+ KW_POLICY_FLAG("yes", "no", POLICY_COMPRESS)
+ break;
+ case KW_AUTH:
+ KW_POLICY_FLAG("ah", "esp", POLICY_AUTHENTICATE)
+ break;
+ case KW_AUTHBY:
+ conn->policy &= ~(POLICY_RSASIG | POLICY_PSK | POLICY_ENCRYPT);
+
+ if (strcmp(kw->value, "never") != 0)
+ {
+ char *value = kw->value;
+ char *second = strchr(kw->value, '|');
+
+ if (second != NULL)
+ *second = '\0';
+
+ /* also handles the cases secret|rsasig and rsasig|secret */
+ for (;;)
+ {
+ if (streq(value, "rsasig"))
+ conn->policy |= POLICY_RSASIG | POLICY_ENCRYPT;
+ else if (streq(value, "secret"))
+ conn->policy |= POLICY_PSK | POLICY_ENCRYPT;
+ else
+ {
+ plog("# bad policy value: %s=%s", kw->entry->name, kw->value);
+ cfg->err++;
+ break;
+ }
+ if (second == NULL)
+ break;
+ value = second;
+ second = NULL; /* traverse the loop no more than twice */
+ }
+ }
+ break;
+ case KW_REKEY:
+ KW_POLICY_FLAG("no", "yes", POLICY_DONT_REKEY)
+ break;
+ default:
+ break;
+ }
+ }
+ handle_firewall("left", &conn->left, cfg);
+ handle_firewall("right", &conn->right, cfg);
+}
+
+/*
+ * initialize a conn object with the default conn
+ */
+static void
+conn_default(char *name, starter_conn_t *conn, starter_conn_t *def)
+{
+ memcpy(conn, def, sizeof(starter_conn_t));
+ conn->name = clone_str(name, "conn name");
+
+ clone_args(KW_CONN_FIRST, KW_CONN_LAST
+ , (char *)conn, (char *)def);
+ clone_args(KW_END_FIRST, KW_END_LAST
+ , (char *)&conn->left, (char *)&def->left);
+ clone_args(KW_END_FIRST, KW_END_LAST
+ , (char *)&conn->right, (char *)&def->right);
+}
+
+/*
+ * parse a ca section
+ */
+static void
+load_ca(starter_ca_t *ca, kw_list_t *kw, starter_config_t *cfg)
+{
+ char *ca_name = (ca->name == NULL)? "%default":ca->name;
+
+ for ( ; kw; kw = kw->next)
+ {
+ bool assigned = FALSE;
+
+ kw_token_t token = kw->entry->token;
+
+ if (token == KW_AUTO)
+ {
+ token = KW_CA_SETUP;
+ }
+ else if (token == KW_ALSO)
+ {
+ if (cfg->parse_also)
+ {
+ also_t *also = alloc_thing(also_t, "also_t");
+
+ also->name = clone_str(kw->value, "also");
+ also->next = ca->also;
+ ca->also = also;
+
+ DBG(DBG_CONTROL,
+ DBG_log(" also=%s", kw->value)
+ )
+ }
+ continue;
+ }
+
+ if (token < KW_CA_FIRST || token > KW_CA_LAST)
+ {
+ plog("# unsupported keyword '%s' in ca '%s'"
+ , kw->entry->name, ca_name);
+ cfg->err++;
+ continue;
+ }
+
+ if (!assign_arg(token, KW_CA_FIRST, kw, (char *)ca, &assigned))
+ {
+ plog(" bad argument value in ca '%s'", ca_name);
+ cfg->err++;
+ }
+ }
+
+ /* treat 'route' and 'start' as 'add' */
+ if (ca->startup != STARTUP_NO)
+ ca->startup = STARTUP_ADD;
+}
+
+/*
+ * initialize a ca object with the default ca
+ */
+static void
+ca_default(char *name, starter_ca_t *ca, starter_ca_t *def)
+{
+ memcpy(ca, def, sizeof(starter_ca_t));
+ ca->name = clone_str(name, "ca name");
+
+ clone_args(KW_CA_FIRST, KW_CA_LAST, (char *)ca, (char *)def);
+}
+
+static kw_list_t*
+find_also_conn(const char* name, starter_conn_t *conn, starter_config_t *cfg);
+
+static void
+load_also_conns(starter_conn_t *conn, also_t *also, starter_config_t *cfg)
+{
+ while (also != NULL)
+ {
+ kw_list_t *kw = find_also_conn(also->name, conn, cfg);
+
+ if (kw == NULL)
+ {
+ plog(" conn '%s' cannot include '%s'", conn->name, also->name);
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("conn '%s' includes '%s'", conn->name, also->name)
+ )
+ /* only load if no error occurred in the first round */
+ if (cfg->err == 0)
+ load_conn(conn, kw, cfg);
+ }
+ also = also->next;
+ }
+}
+
+/*
+ * find a conn included by also
+ */
+static kw_list_t*
+find_also_conn(const char* name, starter_conn_t *conn, starter_config_t *cfg)
+{
+ starter_conn_t *c = cfg->conn_first;
+
+ while (c != NULL)
+ {
+ if (streq(name, c->name))
+ {
+ if (conn->visit == c->visit)
+ {
+ plog("# detected also loop");
+ cfg->err++;
+ return NULL;
+ }
+ c->visit = conn->visit;
+ load_also_conns(conn, c->also, cfg);
+ return c->kw;
+ }
+ c = c->next;
+ }
+
+ plog("# also '%s' not found", name);
+ cfg->err++;
+ return NULL;
+}
+
+static kw_list_t*
+find_also_ca(const char* name, starter_ca_t *ca, starter_config_t *cfg);
+
+static void
+load_also_cas(starter_ca_t *ca, also_t *also, starter_config_t *cfg)
+{
+ while (also != NULL)
+ {
+ kw_list_t *kw = find_also_ca(also->name, ca, cfg);
+
+ if (kw == NULL)
+ {
+ plog(" ca '%s' cannot include '%s'", ca->name, also->name);
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("ca '%s' includes '%s'", ca->name, also->name)
+ )
+ /* only load if no error occurred in the first round */
+ if (cfg->err == 0)
+ load_ca(ca, kw, cfg);
+ }
+ also = also->next;
+ }
+}
+
+/*
+ * find a ca included by also
+ */
+static kw_list_t*
+find_also_ca(const char* name, starter_ca_t *ca, starter_config_t *cfg)
+{
+ starter_ca_t *c = cfg->ca_first;
+
+ while (c != NULL)
+ {
+ if (streq(name, c->name))
+ {
+ if (ca->visit == c->visit)
+ {
+ plog("# detected also loop");
+ cfg->err++;
+ return NULL;
+ }
+ c->visit = ca->visit;
+ load_also_cas(ca, c->also, cfg);
+ return c->kw;
+ }
+ c = c->next;
+ }
+
+ plog("# also '%s' not found", name);
+ cfg->err++;
+ return NULL;
+}
+
+
+
+/*
+ * load and parse an IPsec configuration file
+ */
+starter_config_t *
+confread_load(const char *file)
+{
+ starter_config_t *cfg = NULL;
+ config_parsed_t *cfgp;
+ section_list_t *sconn, *sca;
+ starter_conn_t *conn;
+ starter_ca_t *ca;
+
+ u_int visit = 0;
+
+ /* load IPSec configuration file */
+ cfgp = parser_load_conf(file);
+ if (!cfgp)
+ return NULL;
+
+ cfg = (starter_config_t *)alloc_thing(starter_config_t, "starter_config_t");
+
+ /* set default values */
+ default_values(cfg);
+
+ /* determine default route */
+ get_defaultroute(&cfg->defaultroute);
+
+ /* load config setup section */
+ load_setup(cfg, cfgp);
+
+ /* in the first round parse also statements */
+ cfg->parse_also = TRUE;
+
+ /* find %default ca section */
+ for (sca = cfgp->ca_first; sca; sca = sca->next)
+ {
+ if (streq(sca->name, "%default"))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("Loading ca %%default")
+ )
+ load_ca(&cfg->ca_default, sca->kw, cfg);
+ }
+ }
+
+ /* parameters defined in ca %default sections can be overloads */
+ cfg->ca_default.seen = LEMPTY;
+
+ /* load other ca sections */
+ for (sca = cfgp->ca_first; sca; sca = sca->next)
+ {
+ /* skip %default ca section */
+ if (streq(sca->name, "%default"))
+ continue;
+
+ DBG(DBG_CONTROL,
+ DBG_log("Loading ca '%s'", sca->name)
+ )
+ ca = (starter_ca_t *)alloc_thing(starter_ca_t, "starter_ca_t");
+
+ ca_default(sca->name, ca, &cfg->ca_default);
+ ca->kw = sca->kw;
+ ca->next = NULL;
+
+ if (cfg->ca_last)
+ cfg->ca_last->next = ca;
+ cfg->ca_last = ca;
+ if (!cfg->ca_first)
+ cfg->ca_first = ca;
+
+ load_ca(ca, ca->kw, cfg);
+ }
+
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ also_t *also = ca->also;
+
+ while (also != NULL)
+ {
+ kw_list_t *kw = find_also_ca(also->name, cfg->ca_first, cfg);
+
+ load_ca(ca, kw, cfg);
+ also = also->next;
+ }
+
+ if (ca->startup != STARTUP_NO)
+ ca->state = STATE_TO_ADD;
+ }
+
+ /* find %default conn sections */
+ for (sconn = cfgp->conn_first; sconn; sconn = sconn->next)
+ {
+ if (streq(sconn->name, "%default"))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("Loading conn %%default")
+ )
+ load_conn(&cfg->conn_default, sconn->kw, cfg);
+ }
+ }
+
+ /* parameter defined in conn %default sections can be overloaded */
+ cfg->conn_default.seen = LEMPTY;
+ cfg->conn_default.right.seen = LEMPTY;
+ cfg->conn_default.left.seen = LEMPTY;
+
+ /* load other conn sections */
+ for (sconn = cfgp->conn_first; sconn; sconn = sconn->next)
+ {
+ /* skip %default conn section */
+ if (streq(sconn->name, "%default"))
+ continue;
+
+ DBG(DBG_CONTROL,
+ DBG_log("Loading conn '%s'", sconn->name)
+ )
+ conn = (starter_conn_t *)alloc_thing(starter_conn_t, "starter_conn_t");
+
+ conn_default(sconn->name, conn, &cfg->conn_default);
+ conn->kw = sconn->kw;
+ conn->next = NULL;
+
+ if (cfg->conn_last)
+ cfg->conn_last->next = conn;
+ cfg->conn_last = conn;
+ if (!cfg->conn_first)
+ cfg->conn_first = conn;
+
+ load_conn(conn, conn->kw, cfg);
+ }
+
+ /* in the second round do not parse also statements */
+ cfg->parse_also = FALSE;
+
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ ca->visit = ++visit;
+ load_also_cas(ca, ca->also, cfg);
+
+ if (ca->startup != STARTUP_NO)
+ ca->state = STATE_TO_ADD;
+ }
+
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ conn->visit = ++visit;
+ load_also_conns(conn, conn->also, cfg);
+
+ if (conn->startup != STARTUP_NO)
+ conn->state = STATE_TO_ADD;
+ }
+
+ parser_free_conf(cfgp);
+
+ if (cfg->err)
+ {
+ plog("### %d parsing error%s ###", cfg->err, (cfg->err > 1)?"s":"");
+ confread_free(cfg);
+ cfg = NULL;
+ }
+
+ return cfg;
+}
+
+/*
+ * free the memory used by also_t objects
+ */
+static void
+free_also(also_t *head)
+{
+ while (head != NULL)
+ {
+ also_t *also = head;
+
+ head = also->next;
+ pfree(also->name);
+ pfree(also);
+ }
+}
+
+/*
+ * free the memory used by a starter_conn_t object
+ */
+static void
+confread_free_conn(starter_conn_t *conn)
+{
+ free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->left);
+ free_args(KW_END_FIRST, KW_END_LAST, (char *)&conn->right);
+ free_args(KW_CONN_NAME, KW_CONN_LAST, (char *)conn);
+ free_also(conn->also);
+}
+
+/*
+ * free the memory used by a starter_ca_t object
+ */
+static void
+confread_free_ca(starter_ca_t *ca)
+{
+ free_args(KW_CA_NAME, KW_CA_LAST, (char *)ca);
+ free_also(ca->also);
+}
+
+/*
+ * free the memory used by a starter_config_t object
+ */
+void
+confread_free(starter_config_t *cfg)
+{
+ starter_conn_t *conn = cfg->conn_first;
+ starter_ca_t *ca = cfg->ca_first;
+
+ free_args(KW_SETUP_FIRST, KW_SETUP_LAST, (char *)cfg);
+
+ confread_free_conn(&cfg->conn_default);
+
+ while (conn != NULL)
+ {
+ starter_conn_t *conn_aux = conn;
+
+ conn = conn->next;
+ confread_free_conn(conn_aux);
+ pfree(conn_aux);
+ }
+
+ confread_free_ca(&cfg->ca_default);
+
+ while (ca != NULL)
+ {
+ starter_ca_t *ca_aux = ca;
+
+ ca = ca->next;
+ confread_free_ca(ca_aux);
+ pfree(ca_aux);
+ }
+
+ pfree(cfg);
+}
diff --git a/programs/starter/confread.h b/programs/starter/confread.h
new file mode 100644
index 000000000..a3b1b7379
--- /dev/null
+++ b/programs/starter/confread.h
@@ -0,0 +1,199 @@
+/* strongSwan IPsec config file parser
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: confread.h,v 1.23 2006/04/17 10:32:36 as Exp $
+ */
+
+#ifndef _IPSEC_CONFREAD_H_
+#define _IPSEC_CONFREAD_H_
+
+#ifndef _FREESWAN_H
+#include <freeswan.h>
+#include "../pluto/constants.h"
+#endif
+
+#include "parser.h"
+#include "interfaces.h"
+
+typedef enum {
+ STARTUP_NO,
+ STARTUP_ADD,
+ STARTUP_ROUTE,
+ STARTUP_START
+} startup_t;
+
+typedef enum {
+ STATE_IGNORE,
+ STATE_TO_ADD,
+ STATE_ADDED,
+ STATE_REPLACED,
+ STATE_INVALID
+} starter_state_t;
+
+typedef struct starter_end starter_end_t;
+
+struct starter_end {
+ lset_t seen;
+ char *id;
+ char *rsakey;
+ char *cert;
+ char *ca;
+ char *groups;
+ char *iface;
+ ip_address addr;
+ ip_address nexthop;
+ ip_address srcip;
+ ip_subnet subnet;
+ bool has_client;
+ bool has_client_wildcard;
+ bool has_port_wildcard;
+ bool has_srcip;
+ bool modecfg;
+ certpolicy_t sendcert;
+ bool firewall;
+ bool hostaccess;
+ char *updown;
+ u_int16_t port;
+ u_int8_t protocol;
+#ifdef VIRTUAL_IP
+ char *virt;
+#endif
+};
+
+typedef struct also also_t;
+
+struct also {
+ char *name;
+ bool included;
+ also_t *next;
+};
+
+typedef struct starter_conn starter_conn_t;
+
+struct starter_conn {
+ lset_t seen;
+ char *name;
+ also_t *also;
+ kw_list_t *kw;
+ u_int visit;
+ startup_t startup;
+ starter_state_t state;
+
+ int keyexchange;
+ lset_t policy;
+ time_t sa_ike_life_seconds;
+ time_t sa_ipsec_life_seconds;
+ time_t sa_rekey_margin;
+ unsigned long sa_keying_tries;
+ unsigned long sa_rekey_fuzz;
+ sa_family_t addr_family;
+ sa_family_t tunnel_addr_family;
+
+ starter_end_t left, right;
+
+ unsigned long id;
+
+ char *esp;
+ char *ike;
+ char *pfsgroup;
+
+ time_t dpd_delay;
+ time_t dpd_timeout;
+ dpd_action_t dpd_action;
+ int dpd_count;
+
+ starter_conn_t *next;
+};
+
+typedef struct starter_ca starter_ca_t;
+
+struct starter_ca {
+ lset_t seen;
+ char *name;
+ also_t *also;
+ kw_list_t *kw;
+ u_int visit;
+ startup_t startup;
+ starter_state_t state;
+
+ char *cacert;
+ char *ldaphost;
+ char *ldapbase;
+ char *crluri;
+ char *crluri2;
+ char *ocspuri;
+
+ bool strict;
+
+ starter_ca_t *next;
+};
+
+typedef struct starter_config starter_config_t;
+
+struct starter_config {
+ struct {
+ lset_t seen;
+ char **interfaces;
+ char *dumpdir;
+
+ /* pluto keywords */
+ char **plutodebug;
+ char *prepluto;
+ char *postpluto;
+ bool uniqueids;
+ u_int overridemtu;
+ u_int crlcheckinterval;
+ bool cachecrls;
+ bool strictcrlpolicy;
+ bool nocrsend;
+ bool nat_traversal;
+ u_int keep_alive;
+ char *virtual_private;
+ char *pkcs11module;
+ bool pkcs11keepstate;
+ bool pkcs11proxy;
+
+ /* KLIPS keywords */
+ char **klipsdebug;
+ bool fragicmp;
+ char *packetdefault;
+ bool hidetos;
+ } setup;
+
+ /* information about the default route */
+ defaultroute_t defaultroute;
+
+ /* number of encountered parsing errors */
+ u_int err;
+
+ /* do we parse also statements */
+ bool parse_also;
+
+ /* ca %default */
+ starter_ca_t ca_default;
+
+ /* connections list (without %default) */
+ starter_ca_t *ca_first, *ca_last;
+
+ /* conn %default */
+ starter_conn_t conn_default;
+
+ /* connections list (without %default) */
+ starter_conn_t *conn_first, *conn_last;
+};
+
+extern starter_config_t *confread_load(const char *file);
+extern void confread_free(starter_config_t *cfg);
+
+#endif /* _IPSEC_CONFREAD_H_ */
+
diff --git a/programs/starter/exec.c b/programs/starter/exec.c
new file mode 100644
index 000000000..98541db75
--- /dev/null
+++ b/programs/starter/exec.c
@@ -0,0 +1,54 @@
+/* strongSwan IPsec exec helper function
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: exec.c,v 1.4 2006/01/04 23:30:24 as Exp $
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "exec.h"
+
+#define BUF_SIZE 2048
+
+/**
+ * TODO:
+ * o log stdout with LOG_LEVEL_INFO and stderr with LOG_LEVEL_ERR
+ */
+
+int
+starter_exec(const char *fmt, ...)
+{
+ va_list args;
+ static char buf[BUF_SIZE];
+ int r;
+
+ va_start (args, fmt);
+ vsnprintf(buf, BUF_SIZE-1, fmt, args);
+ buf[BUF_SIZE - 1] = '\0';
+ va_end(args);
+ r = system(buf);
+ DBG(DBG_CONTROL,
+ DBG_log("starter_exec(%s) = %d", buf, r)
+ )
+ return r;
+}
+
diff --git a/programs/starter/exec.h b/programs/starter/exec.h
new file mode 100644
index 000000000..d4be931dd
--- /dev/null
+++ b/programs/starter/exec.h
@@ -0,0 +1,23 @@
+/* strongSwan IPsec starter exec helper function
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: exec.h,v 1.2 2005/12/28 10:20:32 as Exp $
+ */
+
+#ifndef _STARTER_EXEC_H_
+#define _STARTER_EXEC_H_
+
+extern int starter_exec (const char *fmt, ...);
+
+#endif /* _STARTER_EXEC_H_ */
+
diff --git a/programs/starter/files.h b/programs/starter/files.h
new file mode 100644
index 000000000..286cdf105
--- /dev/null
+++ b/programs/starter/files.h
@@ -0,0 +1,47 @@
+/* strongSwan file locations
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: files.h,v 1.5 2006/02/04 18:52:58 as Exp $
+ */
+
+#ifndef _STARTER_FILES_H_
+#define _STARTER_FILES_H_
+
+#ifndef DEFAULT_CTLBASE
+#define DEFAULT_CTLBASE "/var/run/pluto"
+#endif
+#define CTL_SUFFIX ".ctl"
+#define PID_SUFFIX ".pid"
+
+#define MY_PID_FILE "/var/run/starter.pid"
+
+#define DEV_RANDOM "/dev/random"
+#define DEV_URANDOM "/dev/urandom"
+
+#define PROC_NETKEY "/proc/net/pfkey"
+#define PROC_IPSECVERSION "/proc/net/ipsec_version"
+#define PROC_SYSFLAGS "/proc/sys/net/ipsec"
+#define PROC_MODULES "/proc/modules"
+
+#define CONFIG_FILE IPSEC_CONFDIR"/ipsec.conf"
+#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 DYNIP_DIR "/var/run/dynip"
+#define INFO_FILE "/var/run/ipsec.info"
+
+#endif /* _STARTER_FILES_H_ */
+
diff --git a/programs/starter/interfaces.c b/programs/starter/interfaces.c
new file mode 100644
index 000000000..9926ea059
--- /dev/null
+++ b/programs/starter/interfaces.c
@@ -0,0 +1,595 @@
+/* strongSwan IPsec interfaces management
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: interfaces.c,v 1.15 2006/02/05 10:51:55 as Exp $
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <freeswan.h>
+#include <freeswan/ipsec_tunnel.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "interfaces.h"
+#include "exec.h"
+#include "files.h"
+
+#define MIN(a,b) ( ((a)>(b)) ? (b) : (a) )
+
+#define N_IPSEC_IF 4
+
+struct st_ipsec_if {
+ char name[IFNAMSIZ];
+ char phys[IFNAMSIZ];
+ int up;
+};
+
+static struct st_ipsec_if _ipsec_if[N_IPSEC_IF];
+
+static char *
+_find_physical_iface(int sock, char *iface)
+{
+ static char _if[IFNAMSIZ];
+ char *b;
+ struct ifreq req;
+ FILE *fd;
+ char line[BUF_LEN];
+
+ strncpy(req.ifr_name, iface, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
+ {
+ if (req.ifr_flags & IFF_UP)
+ {
+ strncpy(_if, iface, IFNAMSIZ);
+ return _if;
+ }
+ }
+ else
+ {
+ /* If there is a file named /var/run/dynip/<iface>, look if we
+ * can get interface name from there (IP_PHYS)
+ */
+ b = (char *)alloc_bytes(strlen(DYNIP_DIR) + strlen(iface) + 10, "iface");
+ if (b)
+ {
+ sprintf(b, "%s/%s", DYNIP_DIR, iface);
+ fd = fopen(b, "r");
+ pfree(b);
+ if (fd)
+ {
+ memset(_if, 0, sizeof(_if));
+ memset(line, 0, sizeof(line));
+ while (fgets(line, sizeof(line), fd) != 0)
+ {
+ if ((strncmp(line,"IP_PHYS=\"", 9) == 0)
+ && (line[strlen(line) - 2] == '"')
+ && (line[strlen(line) - 1] == '\n'))
+ {
+ strncpy(_if, line + 9, MIN(strlen(line) - 11, IFNAMSIZ));
+ break;
+ }
+ else if ((strncmp(line,"IP_PHYS=", 8) == 0)
+ && (line[8] != '"')
+ && (line[strlen(line) - 1] == '\n'))
+ {
+ strncpy(_if, line + 8, MIN(strlen(line) - 9, IFNAMSIZ));
+ break;
+ }
+ }
+ fclose(fd);
+
+ if (*_if)
+ {
+ strncpy(req.ifr_name, _if, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+ {
+ if (req.ifr_flags & IFF_UP)
+ return _if;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+int
+starter_iface_find(char *iface, int af, ip_address *dst, ip_address *nh)
+{
+ char *phys;
+ struct ifreq req;
+ struct sockaddr_in *sa = (struct sockaddr_in *)(&req.ifr_addr);
+ int sock;
+
+ if (!iface)
+ return -1;
+
+ sock = socket(af, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return -1;
+
+ phys = _find_physical_iface(sock, iface);
+ if (!phys)
+ goto failed;
+
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
+ goto failed;
+ if (!(req.ifr_flags & IFF_UP))
+ goto failed;
+
+ if ((req.ifr_flags & IFF_POINTOPOINT)
+ && nh
+ && ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
+ {
+ if (sa->sin_family == af)
+ initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, nh);
+ }
+ if ((dst) && (ioctl(sock, SIOCGIFADDR, &req) == 0))
+ {
+ if (sa->sin_family == af)
+ initaddr((const void *)&sa->sin_addr, sizeof(struct in_addr), af, dst);
+ }
+ close(sock);
+ return 0;
+
+failed:
+ close(sock);
+ return -1;
+}
+
+static int
+valid_str(char *str, unsigned int *pn, char **pphys
+, defaultroute_t *defaultroute)
+{
+ if (streq(str, "%defaultroute"))
+ {
+ if (!defaultroute->defined)
+ {
+ return 0;
+ }
+ *pn = 0;
+ *pphys = defaultroute->iface;
+ }
+ else
+ {
+ if (strlen(str) < 8
+ || str[0] != 'i' || str[1] != 'p' || str[2] !='s' || str[3] != 'e'
+ || str[4] != 'c' || str[5] < '0' || str[5] > '9' || str[6] != '=')
+ {
+ return 0;
+ }
+ *pn = str[5] - '0';
+ *pphys = &(str[7]);
+ }
+ return 1;
+}
+
+static int
+_iface_up (int sock, struct st_ipsec_if *iface, char *phys
+, unsigned int mtu, bool nat_t)
+{
+ struct ifreq req;
+ struct ipsectunnelconf *shc=(struct ipsectunnelconf *)&req.ifr_data;
+ short phys_flags;
+ int ret = 0;
+ /* sscholz@astaro.com: for network mask 32 bit
+ struct sockaddr_in *inp;
+ */
+
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) !=0 )
+ return ret;
+ phys_flags = req.ifr_flags;
+
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) != 0)
+ return ret;
+
+ if ((!(req.ifr_flags & IFF_UP)) || (!iface->up))
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("attaching interface %s to %s", iface->name, phys)
+ )
+ ret = 1;
+ }
+
+ if ((*iface->phys) && (strcmp(iface->phys, phys) != 0 ))
+ {
+ /* tncfg --detach if phys has changed */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, IPSEC_DEL_DEV, &req);
+ ret = 1;
+ }
+
+ /* tncfg --attach */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ strncpy(shc->cf_name, phys, sizeof(shc->cf_name));
+ ioctl(sock, IPSEC_SET_DEV, &req);
+
+ /* set ipsec addr = phys addr */
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFADDR, &req);
+ }
+
+ /* set ipsec mask = phys mask */
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFNETMASK, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ /* sscholz@astaro.com: changed netmask to 32 bit
+ * in order to prevent network routes from being created
+
+ inp = (struct sockaddr_in *)&req.ifr_addr;
+ inp->sin_addr.s_addr = 0xFFFFFFFFL;
+
+ */
+ ioctl(sock, SIOCSIFNETMASK, &req);
+ }
+
+ /* set other flags & addr */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)==0)
+ {
+/* removed by sscholz@astaro.com (caused trouble with DSL/ppp0) */
+/* if (phys_flags & IFF_POINTOPOINT)
+ {
+ req.ifr_flags |= IFF_POINTOPOINT;
+ req.ifr_flags &= ~IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFDSTADDR, &req);
+ }
+ }
+ else
+ */
+ if (phys_flags & IFF_BROADCAST)
+ {
+ req.ifr_flags &= ~IFF_POINTOPOINT;
+ req.ifr_flags |= IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFBRDADDR, &req) == 0)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ ioctl(sock, SIOCSIFBRDADDR, &req);
+ }
+ }
+ else
+ {
+ req.ifr_flags &= ~IFF_POINTOPOINT;
+ req.ifr_flags &= ~IFF_BROADCAST;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ }
+ }
+
+ /*
+ * guess MTU = phys interface MTU - ESP Overhead
+ *
+ * ESP overhead : 10+16+7+2+12=57 -> 60 by security
+ * NAT-T overhead : 20
+ */
+ if (mtu == 0)
+ {
+ strncpy(req.ifr_name, phys, IFNAMSIZ);
+ ioctl(sock, SIOCGIFMTU, &req);
+ mtu = req.ifr_mtu - 60;
+ if (nat_t)
+ mtu -= 20;
+ }
+ /* set MTU */
+ if (mtu)
+ {
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ req.ifr_mtu = mtu;
+ ioctl(sock, SIOCSIFMTU, &req);
+ }
+
+ /* ipsec interface UP */
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+ {
+ req.ifr_flags |= IFF_UP;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ }
+
+ iface->up = 1;
+ strncpy(iface->phys, phys, IFNAMSIZ);
+ return ret;
+}
+
+static int
+_iface_down(int sock, struct st_ipsec_if *iface)
+{
+ struct ifreq req;
+ int ret = 0;
+
+ iface->up = 0;
+
+ strncpy(req.ifr_name, iface->name, IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &req)!=0)
+ return ret;
+
+ if (req.ifr_flags & IFF_UP)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("shutting down interface %s/%s", iface->name, iface->phys)
+ )
+ req.ifr_flags &= ~IFF_UP;
+ ioctl(sock, SIOCSIFFLAGS, &req);
+ ret = 1;
+ }
+
+ /* unset addr */
+ memset(&req.ifr_addr, 0, sizeof(req.ifr_addr));
+ req.ifr_addr.sa_family = AF_INET;
+ ioctl(sock, SIOCSIFADDR, &req);
+
+ /* tncfg --detach */
+ ioctl(sock, IPSEC_DEL_DEV, &req);
+
+ memset(iface->phys, 0, sizeof(iface->phys));
+
+ return ret;
+}
+
+void
+starter_ifaces_init(void)
+{
+ int i;
+
+ memset(_ipsec_if, 0, sizeof(_ipsec_if));
+ for (i = 0; i < N_IPSEC_IF; i++)
+ snprintf(_ipsec_if[i].name, IFNAMSIZ, "ipsec%d", i);
+}
+
+void
+starter_ifaces_clear (void)
+{
+ int sock;
+ unsigned int i;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return;
+
+ for (i = 0; i < N_IPSEC_IF; i++)
+ _iface_down (sock, &(_ipsec_if[i]));
+}
+
+int
+starter_ifaces_load(char **ifaces, unsigned int omtu, bool nat_t
+, defaultroute_t *defaultroute)
+{
+ char *tmp_phys, *phys;
+ int n;
+ char **i;
+ int sock;
+ int j, found;
+ int ret = 0;
+ struct ifreq physreq, ipsecreq; // re-attach interface
+ struct sockaddr_in *inp1, *inp2; // re-attach interface
+
+ DBG(DBG_CONTROL,
+ DBG_log("starter_ifaces_load()")
+ )
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ return -1;
+
+ for (j = 0; j < N_IPSEC_IF; j++)
+ {
+ found = 0;
+
+ for (i = ifaces; i && *i; i++)
+ {
+ if (valid_str(*i, &n, &tmp_phys, defaultroute)
+ && tmp_phys
+ && n >= 0
+ && n < N_IPSEC_IF)
+ {
+ if (n==j)
+ {
+ if (found)
+ {
+ plog( "ignoring duplicate entry for interface ipsec%d", j);
+ }
+ else
+ {
+ found++;
+ phys = _find_physical_iface(sock, tmp_phys);
+
+ /* Re-attach ipsec interface if IP address changes
+ * sscholz@astaro.com
+ */
+ if (phys)
+ {
+ memset ((void*)&physreq, 0, sizeof(physreq));
+ memset ((void*)&ipsecreq, 0, sizeof(ipsecreq));
+ strncpy(physreq.ifr_name, phys, IFNAMSIZ);
+ sprintf(ipsecreq.ifr_name, "ipsec%d", j);
+ ioctl(sock, SIOCGIFADDR, &physreq);
+ ioctl(sock, SIOCGIFADDR, &ipsecreq);
+ inp1 = (struct sockaddr_in *)&physreq.ifr_addr;
+ inp2 = (struct sockaddr_in *)&ipsecreq.ifr_addr;
+ if (inp1->sin_addr.s_addr != inp2->sin_addr.s_addr)
+ {
+ plog("IP address of physical interface changed "
+ "-> reinit of ipsec interface");
+ _iface_down (sock, &(_ipsec_if[n]));
+ }
+ ret += _iface_up (sock, &(_ipsec_if[n]), phys, omtu, nat_t);
+ }
+ else
+ {
+ ret += _iface_down (sock, &(_ipsec_if[n]));
+ }
+ }
+ }
+ }
+ else if (j == 0)
+ {
+ /* Only log in the first loop */
+ plog("ignoring invalid interface '%s'", *i);
+ }
+ }
+ if (!found)
+ ret += _iface_down (sock, &(_ipsec_if[j]));
+ }
+
+ close(sock);
+ return ret; /* = number of changes - 'whack --listen' if > 0 */
+}
+
+/*
+ * initialize a defaultroute_t struct
+ */
+static void
+init_defaultroute(defaultroute_t *defaultroute)
+{
+ memset(defaultroute, 0, sizeof(defaultroute_t));
+}
+
+/*
+ * discover the default route via /proc/net/route
+ */
+void
+get_defaultroute(defaultroute_t *defaultroute)
+{
+ FILE *fd;
+ char line[BUF_LEN];
+ bool first = TRUE;
+
+ init_defaultroute(defaultroute);
+
+ fd = fopen("/proc/net/route", "r");
+
+ if (!fd)
+ {
+ plog("could not open 'proc/net/route'");
+ return;
+ }
+
+ while (fgets(line, sizeof(line), fd) != 0)
+ {
+ char iface[11];
+ char destination[9];
+ char gateway[11];
+ char flags[5];
+ char mask[9];
+
+ int refcnt;
+ int use;
+ int metric;
+ int items;
+
+ /* proc/net/route returns IP addresses in host order */
+ strcpy(gateway, "0h");
+
+ /* skip the header line */
+ if (first)
+ {
+ first = FALSE;
+ continue;
+ }
+
+ /* parsing a single line of proc/net/route */
+ items = sscanf(line, "%10s\t%8s\t%8s\t%5s\t%d\t%d\t%d\t%8s\t"
+ , iface, destination, gateway+2, flags, &refcnt, &use, &metric, mask);
+ if (items < 8)
+ {
+ plog("parsing error while scanning /proc/net/route");
+ continue;
+ }
+
+ /* check for defaultroute (destination 0.0.0.0 and mask 0.0.0.0) */
+ if (streq(destination, "00000000") && streq(mask, "00000000"))
+ {
+ if (defaultroute->defined)
+ {
+ plog("multiple default routes - cannot cope with %%defaultroute!!!");
+ defaultroute->defined = FALSE;
+ fclose(fd);
+ return;
+ }
+ ttoaddr(gateway, strlen(gateway), AF_INET, &defaultroute->nexthop);
+ strncpy(defaultroute->iface, iface, IFNAMSIZ);
+ defaultroute->defined = TRUE;
+ }
+ }
+ fclose(fd);
+
+ if (!defaultroute->defined)
+ {
+ plog("no default route - cannot cope with %%defaultroute!!!");
+ }
+ else
+ {
+ char addr_buf[20], nexthop_buf[20];
+ struct ifreq physreq;
+
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+ /* determine IP address of iface */
+ if (sock < 0)
+ {
+ plog("could not open SOCK_DGRAM socket");
+ defaultroute->defined = FALSE;
+ return;
+ }
+ memset ((void*)&physreq, 0, sizeof(physreq));
+ strncpy(physreq.ifr_name, defaultroute->iface, IFNAMSIZ);
+ ioctl(sock, SIOCGIFADDR, &physreq);
+ close(sock);
+ defaultroute->addr.u.v4 = *((struct sockaddr_in *)&physreq.ifr_addr);
+
+ addrtot(&defaultroute->addr, 0, addr_buf, sizeof(addr_buf));
+ addrtot(&defaultroute->nexthop, 0, nexthop_buf, sizeof(nexthop_buf));
+
+ DBG(DBG_CONTROL,
+ DBG_log("Default route found: iface=%s, addr=%s, nexthop=%s"
+ , defaultroute->iface, addr_buf, nexthop_buf)
+ )
+
+ /* for backwards-compatibility with the awk shell scripts
+ * store the defaultroute in /var/run/ipsec.info
+ */
+ fd = fopen(INFO_FILE, "w");
+
+ if (fd)
+ {
+ fprintf(fd, "defaultroutephys=%s\n", defaultroute->iface );
+ fprintf(fd, "defaultroutevirt=ipsec0\n");
+ fprintf(fd, "defaultrouteaddr=%s\n", addr_buf);
+ fprintf(fd, "defaultroutenexthop=%s\n", nexthop_buf);
+ fclose(fd);
+ }
+ }
+ return;
+}
diff --git a/programs/starter/interfaces.h b/programs/starter/interfaces.h
new file mode 100644
index 000000000..9898c0516
--- /dev/null
+++ b/programs/starter/interfaces.h
@@ -0,0 +1,41 @@
+/* strongSwan IPsec interfaces management
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: interfaces.h,v 1.6 2006/01/06 20:24:07 as Exp $
+ */
+
+#ifndef _STARTER_INTERFACES_H_
+#define _STARTER_INTERFACES_H_
+
+#include <linux/if.h>
+
+#include "../pluto/constants.h"
+
+typedef struct {
+ bool defined;
+ char iface[IFNAMSIZ];
+ ip_address addr;
+ ip_address nexthop;
+} defaultroute_t;
+
+extern void starter_ifaces_init (void);
+extern int starter_iface_find(char *iface, int af, ip_address *dst
+ , ip_address *nh);
+extern int starter_ifaces_load (char **ifaces, unsigned int omtu, bool nat_t
+ , defaultroute_t *defaultroute);
+extern void starter_ifaces_clear (void);
+extern void get_defaultroute(defaultroute_t *defaultroute);
+
+
+#endif /* _STARTER_INTERFACES_H_ */
+
diff --git a/programs/starter/invokepluto.c b/programs/starter/invokepluto.c
new file mode 100644
index 000000000..70376e380
--- /dev/null
+++ b/programs/starter/invokepluto.c
@@ -0,0 +1,286 @@
+/* strongSwan Pluto launcher
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: invokepluto.c,v 1.12 2006/02/17 21:41:50 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "confread.h"
+#include "invokepluto.h"
+#include "files.h"
+#include "starterwhack.h"
+#
+static int _pluto_pid = 0;
+static int _stop_requested;
+
+pid_t
+starter_pluto_pid(void)
+{
+ return _pluto_pid;
+}
+
+void
+starter_pluto_sigchild(pid_t pid)
+{
+ if (pid == _pluto_pid)
+ {
+ _pluto_pid = 0;
+ if (!_stop_requested)
+ {
+ plog("pluto has died -- restart scheduled (%dsec)"
+ , PLUTO_RESTART_DELAY);
+ alarm(PLUTO_RESTART_DELAY); // restart in 5 sec
+ }
+ unlink(PID_FILE);
+ }
+}
+
+int
+starter_stop_pluto (void)
+{
+ pid_t pid;
+ int i;
+
+ pid = _pluto_pid;
+ if (pid)
+ {
+ _stop_requested = 1;
+ if (starter_whack_shutdown() == 0)
+ {
+ for (i = 0; i < 20; i++)
+ {
+ usleep(20000);
+ if (_pluto_pid == 0)
+ return 0;
+ }
+ }
+ /* be more and more aggressive */
+ for (i = 0; i < 20 && (pid = _pluto_pid) != 0; i++)
+ {
+ if (i < 10)
+ kill(pid, SIGTERM);
+ else
+ kill(pid, SIGKILL);
+ usleep(20000);
+ }
+ if (_pluto_pid == 0)
+ return 0;
+ plog("starter_stop_pluto(): can't stop pluto !!!");
+ return -1;
+ }
+ else
+ {
+ plog("stater_stop_pluto(): pluto is not started...");
+ }
+ return -1;
+}
+
+#define ADD_DEBUG(v) { \
+ for (l = cfg->setup.plutodebug; l && *l; l++) if (streq(*l, v)) \
+ arg[argc++] = "--debug-" v; \
+ }
+
+int
+starter_start_pluto (starter_config_t *cfg, bool debug)
+{
+ int i;
+ struct stat stb;
+ pid_t pid;
+ char **l;
+ int argc = 2;
+ char *arg[] = {
+ PLUTO_CMD, "--nofork"
+ , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+ };
+
+ printf ("starter_start_pluto entered\n");
+
+ if (debug)
+ {
+ arg[argc++] = "--stderrlog";
+ }
+ if (cfg->setup.uniqueids)
+ {
+ arg[argc++] = "--uniqueids";
+ }
+ ADD_DEBUG("none")
+ ADD_DEBUG("all")
+ ADD_DEBUG("raw")
+ ADD_DEBUG("crypt")
+ ADD_DEBUG("parsing")
+ ADD_DEBUG("emitting")
+ ADD_DEBUG("control")
+ ADD_DEBUG("lifecycle")
+ ADD_DEBUG("klips")
+ ADD_DEBUG("dns")
+ ADD_DEBUG("natt")
+ ADD_DEBUG("oppo")
+ ADD_DEBUG("controlmore")
+ ADD_DEBUG("private")
+ if (cfg->setup.crlcheckinterval > 0)
+ {
+ static char buf1[15];
+
+ arg[argc++] = "--crlcheckinterval";
+ snprintf(buf1, sizeof(buf1), "%u", cfg->setup.crlcheckinterval);
+ arg[argc++] = buf1;
+ }
+ if (cfg->setup.cachecrls)
+ {
+ arg[argc++] = "--cachecrls";
+ }
+ if (cfg->setup.strictcrlpolicy)
+ {
+ arg[argc++] = "--strictcrlpolicy";
+ }
+ if (cfg->setup.nocrsend)
+ {
+ arg[argc++] = "--nocrsend";
+ }
+ if (cfg->setup.nat_traversal)
+ {
+ arg[argc++] = "--nat_traversal";
+ }
+ if (cfg->setup.keep_alive)
+ {
+ static char buf2[15];
+
+ arg[argc++] = "--keep_alive";
+ snprintf(buf2, sizeof(buf2), "%u", cfg->setup.keep_alive);
+ arg[argc++] = buf2;
+ }
+#ifdef VIRTUAL_IP
+ if (cfg->setup.virtual_private)
+ {
+ arg[argc++] = "--virtual_private";
+ arg[argc++] = cfg->setup.virtual_private;
+ }
+#endif
+ if (cfg->setup.pkcs11module)
+ {
+ arg[argc++] = "--pkcs11module";
+ arg[argc++] = cfg->setup.pkcs11module;
+ }
+ if (cfg->setup.pkcs11keepstate)
+ {
+ arg[argc++] = "--pkcs11keepstate";
+ }
+ if (cfg->setup.pkcs11proxy)
+ {
+ arg[argc++] = "--pkcs11proxy";
+ }
+
+ if (_pluto_pid)
+ {
+ plog("starter_start_pluto(): pluto already started...");
+ return -1;
+ }
+ else
+ {
+ unlink(CTL_FILE);
+ _stop_requested = 0;
+
+ if (cfg->setup.prepluto)
+ system(cfg->setup.prepluto);
+
+ /* if ipsec.secrets file is missing then generate RSA default key pair */
+ if (stat(SECRETS_FILE, &stb) != 0)
+ {
+ mode_t oldmask;
+ FILE *f;
+
+ plog("no %s file, generating RSA key", SECRETS_FILE);
+ system("ipsec scepclient --out pkcs1 --out cert-self --quiet");
+
+ /* ipsec.secrets is root readable only */
+ oldmask = umask(0066);
+
+ f = fopen(SECRETS_FILE, "w");
+ if (f)
+ {
+ fprintf(f, "# /etc/ipsec.secrets - strongSwan IPsec secrets file\n");
+ fprintf(f, "\n");
+ fprintf(f, ": RSA myKey.der\n");
+ fclose(f);
+ }
+ umask(oldmask);
+ }
+
+ 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 */
+ _pluto_pid = pid;
+ for (i = 0; i < 50 && _pluto_pid; i++)
+ {
+ /* wait for pluto */
+ usleep(20000);
+ if (stat(CTL_FILE, &stb) == 0)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("pluto (%d) started", _pluto_pid)
+ )
+ if (cfg->setup.postpluto)
+ system(cfg->setup.postpluto);
+ return 0;
+ }
+ }
+ if (_pluto_pid)
+ {
+ /* If pluto is started but with no ctl file, stop it */
+ plog("pluto too long to start... - kill kill");
+ for (i = 0; i < 20 && (pid = _pluto_pid) != 0; i++)
+ {
+ if (i < 10)
+ kill(pid, SIGTERM);
+ else
+ kill(pid, SIGKILL);
+ usleep(20000);
+ }
+ }
+ else
+ {
+ plog("pluto refused to be started");
+ }
+ return -1;
+ }
+ }
+ return -1;
+}
diff --git a/programs/starter/invokepluto.h b/programs/starter/invokepluto.h
new file mode 100644
index 000000000..26858f9b2
--- /dev/null
+++ b/programs/starter/invokepluto.h
@@ -0,0 +1,28 @@
+/* strongSwan pluto launcher
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: invokepluto.h,v 1.3 2006/01/04 23:30:24 as Exp $
+ */
+
+#ifndef _STARTER_PLUTO_H_
+#define _STARTER_PLUTO_H_
+
+#define PLUTO_RESTART_DELAY 5
+
+extern void starter_pluto_sigchild (pid_t pid);
+extern pid_t starter_pluto_pid (void);
+extern int starter_stop_pluto (void);
+extern int starter_start_pluto (struct starter_config *cfg, bool debug);
+
+#endif /* _STARTER_PLUTO_H_ */
+
diff --git a/programs/starter/keywords.c b/programs/starter/keywords.c
new file mode 100644
index 000000000..4cc5c03e8
--- /dev/null
+++ b/programs/starter/keywords.c
@@ -0,0 +1,235 @@
+/* C code produced by gperf version 3.0.1 */
+/* Command-line: gperf -C -G -t */
+/* Computed positions: -k'3,$' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+
+/* strongSwan keywords
+ * Copyright (C) 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: keywords.c,v 1.7 2006/04/17 10:32:48 as Exp $
+ */
+
+#include <string.h>
+
+#include "keywords.h"
+
+struct kw_entry {
+ char *name;
+ kw_token_t token;
+};
+
+#define TOTAL_KEYWORDS 77
+#define MIN_WORD_LENGTH 3
+#define MAX_WORD_LENGTH 17
+#define MIN_HASH_VALUE 9
+#define MAX_HASH_VALUE 146
+/* maximum key range = 138, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash (str, len)
+ register const char *str;
+ register unsigned int len;
+{
+ static const unsigned char asso_values[] =
+ {
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 15, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 85, 147, 40,
+ 25, 25, 0, 10, 5, 80, 147, 35, 60, 35,
+ 60, 55, 10, 147, 15, 20, 5, 65, 147, 147,
+ 147, 35, 0, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147, 147, 147, 147, 147,
+ 147, 147, 147, 147, 147, 147
+ };
+ return len + asso_values[(unsigned char)str[2]] + asso_values[(unsigned char)str[len - 1]];
+}
+
+static const struct kw_entry wordlist[] =
+ {
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {"left", KW_LEFT},
+ {""}, {""}, {""},
+ {"leftcert", KW_LEFTCERT,},
+ {"auth", KW_AUTH},
+ {"leftsubnet", KW_LEFTSUBNET},
+ {""},
+ {"leftsendcert", KW_LEFTSENDCERT},
+ {"leftprotoport", KW_LEFTPROTOPORT},
+ {""},
+ {"right", KW_RIGHT},
+ {"leftnexthop", KW_LEFTNEXTHOP},
+ {"leftsourceip", KW_LEFTSOURCEIP},
+ {"esp", KW_ESP},
+ {"rightcert", KW_RIGHTCERT},
+ {""},
+ {"rightsubnet", KW_RIGHTSUBNET},
+ {""},
+ {"rightsendcert", KW_RIGHTSENDCERT},
+ {"rightprotoport", KW_RIGHTPROTOPORT},
+ {"leftgroups", KW_LEFTGROUPS},
+ {"leftid", KW_LEFTID},
+ {"rightnexthop", KW_RIGHTNEXTHOP},
+ {"rightsourceip", KW_RIGHTSOURCEIP},
+ {"lefthostaccess", KW_LEFTHOSTACCESS},
+ {"interfaces", KW_INTERFACES},
+ {""}, {""},
+ {"pfsgroup", KW_PFSGROUP},
+ {"type", KW_TYPE},
+ {"dpdtimeout", KW_DPDTIMEOUT},
+ {"rightgroups", KW_RIGHTGROUPS},
+ {"rightid", KW_RIGHTID},
+ {"pfs", KW_PFS},
+ {"rekeyfuzz", KW_REKEYFUZZ},
+ {"righthostaccess", KW_RIGHTHOSTACCESS},
+ {"authby", KW_AUTHBY},
+ {""},
+ {"leftrsasigkey", KW_LEFTRSASIGKEY},
+ {""}, {""},
+ {"cacert", KW_CACERT},
+ {"hidetos", KW_HIDETOS},
+ {"ike", KW_IKE},
+ {""},
+ {"virtual_private", KW_VIRTUAL_PRIVATE},
+ {""},
+ {"dumpdir", KW_DUMPDIR},
+ {"packetdefault", KW_PACKETDEFAULT},
+ {"rightrsasigkey", KW_RIGHTRSASIGKEY},
+ {"keep_alive", KW_KEEP_ALIVE},
+ {"ikelifetime", KW_IKELIFETIME},
+ {""},
+ {"compress", KW_COMPRESS},
+ {"auto", KW_AUTO},
+ {"strictcrlpolicy", KW_STRICTCRLPOLICY},
+ {"keyingtries", KW_KEYINGTRIES},
+ {"keylife", KW_KEYLIFE},
+ {"dpddelay", KW_DPDDELAY},
+ {"cachecrls", KW_CACHECRLS},
+ {"leftupdown", KW_LEFTUPDOWN},
+ {"keyexchange", KW_KEYEXCHANGE},
+ {"leftfirewall", KW_LEFTFIREWALL},
+ {"nocrsend", KW_NOCRSEND},
+ {""},
+ {"rekey", KW_REKEY},
+ {"leftsubnetwithin", KW_LEFTSUBNETWITHIN},
+ {"pkcs11module", KW_PKCS11MODULE},
+ {"nat_traversal", KW_NAT_TRAVERSAL},
+ {"also", KW_ALSO},
+ {"pkcs11keepstate", KW_PKCS11KEEPSTATE},
+ {"rightupdown", KW_RIGHTUPDOWN},
+ {"crluri2", KW_CRLURI2},
+ {"rightfirewall", KW_RIGHTFIREWALL},
+ {"postpluto", KW_POSTPLUTO},
+ {"plutodebug", KW_PLUTODEBUG},
+ {"pkcs11proxy", KW_PKCS11PROXY},
+ {"rightsubnetwithin", KW_RIGHTSUBNETWITHIN},
+ {"prepluto", KW_PREPLUTO},
+ {""}, {""},
+ {"leftca", KW_LEFTCA},
+ {""}, {""},
+ {"dpdaction", KW_DPDACTION},
+ {""}, {""}, {""},
+ {"ldaphost", KW_LDAPHOST},
+ {""},
+ {"klipsdebug", KW_KLIPSDEBUG},
+ {"overridemtu", KW_OVERRIDEMTU},
+ {"rightca", KW_RIGHTCA},
+ {"fragicmp", KW_FRAGICMP},
+ {""}, {""},
+ {"rekeymargin", KW_REKEYMARGIN},
+ {"ocspuri", KW_OCSPURI},
+ {""},
+ {"uniqueids", KW_UNIQUEIDS},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {"ldapbase", KW_LDAPBASE},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {"crlcheckinterval", KW_CRLCHECKINTERVAL},
+ {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
+ {"crluri", KW_CRLURI}
+ };
+
+#ifdef __GNUC__
+__inline
+#endif
+const struct kw_entry *
+in_word_set (str, len)
+ register const char *str;
+ register unsigned int len;
+{
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register int key = hash (str, len);
+
+ if (key <= MAX_HASH_VALUE && key >= 0)
+ {
+ register const char *s = wordlist[key].name;
+
+ if (*str == *s && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ return 0;
+}
diff --git a/programs/starter/keywords.h b/programs/starter/keywords.h
new file mode 100644
index 000000000..6542ae1be
--- /dev/null
+++ b/programs/starter/keywords.h
@@ -0,0 +1,164 @@
+/* strongSwan keywords
+ * Copyright (C) 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: keywords.h,v 1.8 2006/04/17 10:30:27 as Exp $
+ */
+
+#ifndef _KEYWORDS_H_
+#define _KEYWORDS_H_
+
+typedef enum {
+ /* config setup keywords */
+ KW_INTERFACES,
+ KW_DUMPDIR,
+
+ /* pluto keywords */
+ KW_PLUTODEBUG,
+ KW_PREPLUTO,
+ KW_POSTPLUTO,
+ KW_UNIQUEIDS,
+ KW_OVERRIDEMTU,
+ KW_CRLCHECKINTERVAL,
+ KW_CACHECRLS,
+ KW_STRICTCRLPOLICY,
+ KW_NOCRSEND,
+ KW_NAT_TRAVERSAL,
+ KW_KEEP_ALIVE,
+ KW_VIRTUAL_PRIVATE,
+ KW_PKCS11MODULE,
+ KW_PKCS11KEEPSTATE,
+ KW_PKCS11PROXY,
+
+#define KW_PLUTO_FIRST KW_PLUTODEBUG
+#define KW_PLUTO_LAST KW_PKCS11PROXY
+
+ /* KLIPS keywords */
+ KW_KLIPSDEBUG,
+ KW_FRAGICMP,
+ KW_PACKETDEFAULT,
+ KW_HIDETOS,
+
+#define KW_KLIPS_FIRST KW_KLIPSDEBUG
+#define KW_KLIPS_LAST KW_HIDETOS
+
+#define KW_SETUP_FIRST KW_INTERFACES
+#define KW_SETUP_LAST KW_HIDETOS
+
+ /* conn section keywords */
+ KW_CONN_NAME,
+ KW_CONN_SETUP,
+ KW_KEYEXCHANGE,
+ KW_TYPE,
+ KW_PFS,
+ KW_COMPRESS,
+ KW_AUTH,
+ KW_AUTHBY,
+ KW_IKELIFETIME,
+ KW_KEYLIFE,
+ KW_REKEYMARGIN,
+ KW_KEYINGTRIES,
+ KW_REKEYFUZZ,
+ KW_REKEY,
+ KW_IKE,
+ KW_ESP,
+ KW_PFSGROUP,
+ KW_DPDDELAY,
+ KW_DPDTIMEOUT,
+ KW_DPDACTION,
+
+#define KW_CONN_FIRST KW_CONN_SETUP
+#define KW_CONN_LAST KW_DPDACTION
+
+ /* ca section keywords */
+ KW_CA_NAME,
+ KW_CA_SETUP,
+ KW_CACERT,
+ KW_LDAPHOST,
+ KW_LDAPBASE,
+ KW_CRLURI,
+ KW_CRLURI2,
+ KW_OCSPURI,
+
+#define KW_CA_FIRST KW_CA_SETUP
+#define KW_CA_LAST KW_OCSPURI
+
+ /* end keywords */
+ KW_HOST,
+ KW_NEXTHOP,
+ KW_SUBNET,
+ KW_SUBNETWITHIN,
+ KW_PROTOPORT,
+ KW_SOURCEIP,
+ KW_FIREWALL,
+ KW_HOSTACCESS,
+ KW_UPDOWN,
+ KW_ID,
+ KW_RSASIGKEY,
+ KW_CERT,
+ KW_SENDCERT,
+ KW_CA,
+ KW_GROUPS,
+ KW_IFACE,
+
+#define KW_END_FIRST KW_HOST
+#define KW_END_LAST KW_IFACE
+
+ /* left end keywords */
+ KW_LEFT,
+ KW_LEFTNEXTHOP,
+ KW_LEFTSUBNET,
+ KW_LEFTSUBNETWITHIN,
+ KW_LEFTPROTOPORT,
+ KW_LEFTSOURCEIP,
+ KW_LEFTFIREWALL,
+ KW_LEFTHOSTACCESS,
+ KW_LEFTUPDOWN,
+ KW_LEFTID,
+ KW_LEFTRSASIGKEY,
+ KW_LEFTCERT,
+ KW_LEFTSENDCERT,
+ KW_LEFTCA,
+ KW_LEFTGROUPS,
+
+#define KW_LEFT_FIRST KW_LEFT
+#define KW_LEFT_LAST KW_LEFTGROUPS
+
+ /* right end keywords */
+ KW_RIGHT,
+ KW_RIGHTNEXTHOP,
+ KW_RIGHTSUBNET,
+ KW_RIGHTSUBNETWITHIN,
+ KW_RIGHTPROTOPORT,
+ KW_RIGHTSOURCEIP,
+ KW_RIGHTFIREWALL,
+ KW_RIGHTHOSTACCESS,
+ KW_RIGHTUPDOWN,
+ KW_RIGHTID,
+ KW_RIGHTRSASIGKEY,
+ KW_RIGHTCERT,
+ KW_RIGHTSENDCERT,
+ KW_RIGHTCA,
+ KW_RIGHTGROUPS,
+
+#define KW_RIGHT_FIRST KW_RIGHT
+#define KW_RIGHT_LAST KW_RIGHTGROUPS
+
+ /* general section keywords */
+ KW_ALSO,
+ KW_AUTO
+
+} kw_token_t;
+
+#endif /* _KEYWORDS_H_ */
+
diff --git a/programs/starter/keywords.txt b/programs/starter/keywords.txt
new file mode 100644
index 000000000..dcfdafc98
--- /dev/null
+++ b/programs/starter/keywords.txt
@@ -0,0 +1,105 @@
+%{
+/* strongSwan keywords
+ * Copyright (C) 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: keywords.txt,v 1.6 2006/04/17 10:30:27 as Exp $
+ */
+
+#include <string.h>
+
+#include "keywords.h"
+
+%}
+struct kw_entry {
+ char *name;
+ kw_token_t token;
+};
+%%
+interfaces, KW_INTERFACES
+klipsdebug, KW_KLIPSDEBUG
+plutodebug, KW_PLUTODEBUG
+dumpdir, KW_DUMPDIR
+prepluto, KW_PREPLUTO
+postpluto, KW_POSTPLUTO
+fragicmp, KW_FRAGICMP
+packetdefault, KW_PACKETDEFAULT
+hidetos, KW_HIDETOS
+uniqueids, KW_UNIQUEIDS
+overridemtu, KW_OVERRIDEMTU
+crlcheckinterval, KW_CRLCHECKINTERVAL
+cachecrls, KW_CACHECRLS
+strictcrlpolicy, KW_STRICTCRLPOLICY
+nocrsend, KW_NOCRSEND
+nat_traversal, KW_NAT_TRAVERSAL
+keep_alive, KW_KEEP_ALIVE
+virtual_private, KW_VIRTUAL_PRIVATE
+pkcs11module, KW_PKCS11MODULE
+pkcs11keepstate, KW_PKCS11KEEPSTATE
+pkcs11proxy, KW_PKCS11PROXY
+keyexchange, KW_KEYEXCHANGE
+type, KW_TYPE
+pfs, KW_PFS
+compress, KW_COMPRESS
+auth, KW_AUTH
+authby, KW_AUTHBY
+keylife, KW_KEYLIFE
+rekeymargin, KW_REKEYMARGIN
+ikelifetime, KW_IKELIFETIME
+keyingtries, KW_KEYINGTRIES
+rekeyfuzz, KW_REKEYFUZZ
+rekey, KW_REKEY
+esp, KW_ESP
+ike, KW_IKE
+pfsgroup, KW_PFSGROUP
+dpddelay, KW_DPDDELAY
+dpdtimeout, KW_DPDTIMEOUT
+dpdaction, KW_DPDACTION
+cacert, KW_CACERT
+ldaphost, KW_LDAPHOST
+ldapbase, KW_LDAPBASE
+crluri, KW_CRLURI
+crluri2, KW_CRLURI2
+ocspuri, KW_OCSPURI
+left, KW_LEFT
+leftnexthop, KW_LEFTNEXTHOP
+leftsubnet, KW_LEFTSUBNET
+leftsubnetwithin, KW_LEFTSUBNETWITHIN
+leftprotoport, KW_LEFTPROTOPORT
+leftsourceip, KW_LEFTSOURCEIP
+leftfirewall, KW_LEFTFIREWALL
+lefthostaccess, KW_LEFTHOSTACCESS
+leftupdown, KW_LEFTUPDOWN
+leftid, KW_LEFTID
+leftrsasigkey, KW_LEFTRSASIGKEY
+leftcert, KW_LEFTCERT,
+leftsendcert, KW_LEFTSENDCERT
+leftca, KW_LEFTCA
+leftgroups, KW_LEFTGROUPS
+right, KW_RIGHT
+rightnexthop, KW_RIGHTNEXTHOP
+rightsubnet, KW_RIGHTSUBNET
+rightsubnetwithin, KW_RIGHTSUBNETWITHIN
+rightprotoport, KW_RIGHTPROTOPORT
+rightsourceip, KW_RIGHTSOURCEIP
+rightfirewall, KW_RIGHTFIREWALL
+righthostaccess, KW_RIGHTHOSTACCESS
+rightupdown, KW_RIGHTUPDOWN
+rightid, KW_RIGHTID
+rightrsasigkey, KW_RIGHTRSASIGKEY
+rightcert, KW_RIGHTCERT
+rightsendcert, KW_RIGHTSENDCERT
+rightca, KW_RIGHTCA
+rightgroups, KW_RIGHTGROUPS
+also, KW_ALSO
+auto, KW_AUTO
diff --git a/programs/starter/klips.c b/programs/starter/klips.c
new file mode 100644
index 000000000..5595eb6eb
--- /dev/null
+++ b/programs/starter/klips.c
@@ -0,0 +1,134 @@
+/* strongSwan KLIPS starter
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: klips.c,v 1.8 2006/02/15 18:33:57 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "confread.h"
+#include "klips.h"
+#include "files.h"
+#include "exec.h"
+
+static int _klips_module_loaded = 0;
+
+bool
+starter_klips_init(void)
+{
+ struct stat stb;
+
+ if (stat(PROC_IPSECVERSION, &stb) != 0)
+ {
+ if (stat(PROC_MODULES, &stb) == 0)
+ {
+ unsetenv("MODPATH");
+ unsetenv("MODULECONF");
+ system("depmod -a >/dev/null 2>&1");
+ system("modprobe -qv ipsec");
+ }
+ if (stat(PROC_IPSECVERSION, &stb) == 0)
+ {
+ _klips_module_loaded = 1;
+ }
+ else
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("kernel appears to lack KLIPS")
+ )
+ return FALSE;
+ }
+ }
+
+ /* make sure that all available crypto algorithms are loaded */
+ if (stat(PROC_MODULES, &stb) == 0)
+ {
+ system("modprobe -qv ipsec_aes");
+ system("modprobe -qv ipsec_serpent");
+ system("modprobe -qv ipsec_twofish");
+ system("modprobe -qv ipsec_blowfish");
+ system("modprobe -qv ipsec_sha2");
+ }
+
+ starter_klips_clear();
+
+ DBG(DBG_CONTROL,
+ DBG_log("Found KLIPS IPsec stack")
+ )
+ return TRUE;
+}
+
+static void
+_sysflags (char *name, int value)
+{
+ int res = starter_exec("echo %d >%s/%s 2>/dev/null"
+ , value? 1 : 0, PROC_SYSFLAGS, name);
+
+ if (res)
+ plog("can't set sysflag %s to %d", name, value? 1 : 0);
+}
+
+void
+starter_klips_set_config(starter_config_t *cfg)
+{
+ char **l;
+
+ _sysflags("icmp", cfg->setup.fragicmp);
+ _sysflags("inbound_policy_check", 1);
+ /* _sysflags("no_eroute_pass", 0); */
+ /* _sysflags("opportunistic", 0); */
+ _sysflags("tos", cfg->setup.hidetos);
+
+ starter_exec("%s/klipsdebug --none", IPSEC_EXECDIR);
+ for (l = cfg->setup.klipsdebug; l && *l; l++)
+ {
+ if ((streq(*l, "none")) || (streq(*l, "all")))
+ starter_exec("%s/klipsdebug --%s", IPSEC_EXECDIR, *l);
+ else
+ starter_exec("%s/klipsdebug --set %s", IPSEC_EXECDIR, *l);
+ }
+
+ starter_exec("%s/eroute --del --eraf inet --src 0/0 --dst 0/0 2>/dev/null"
+ , IPSEC_EXECDIR);
+ starter_exec("%s/eroute --label packetdefault --replace --eraf inet "
+ "--src 0/0 --dst 0/0 --said %%%s", IPSEC_EXECDIR
+ , cfg->setup.packetdefault ? cfg->setup.packetdefault : "drop");
+}
+
+void
+starter_klips_clear(void)
+{
+ system(IPSEC_EXECDIR"/eroute --clear");
+ system(IPSEC_EXECDIR"/spi --clear");
+ system(IPSEC_EXECDIR"/klipsdebug --none");
+}
+
+void
+starter_klips_cleanup(void)
+{
+ starter_klips_clear();
+ if (_klips_module_loaded)
+ {
+ system("rmmod ipsec");
+ _klips_module_loaded = 0;
+ }
+}
diff --git a/programs/starter/klips.h b/programs/starter/klips.h
new file mode 100644
index 000000000..d07c6cca4
--- /dev/null
+++ b/programs/starter/klips.h
@@ -0,0 +1,26 @@
+/* strongSwan klips initialization and cleanup
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: klips.h,v 1.2 2005/12/30 19:03:56 as Exp $
+ */
+
+#ifndef _STARTER_KLIPS_H_
+#define _STARTER_KLIPS_H_
+
+extern bool starter_klips_init (void);
+extern void starter_klips_set_config (struct starter_config *);
+extern void starter_klips_cleanup (void);
+extern void starter_klips_clear (void);
+
+#endif /* _STARTER_KLIPS_H_ */
+
diff --git a/programs/starter/lex.yy.c b/programs/starter/lex.yy.c
new file mode 100644
index 000000000..1626f1050
--- /dev/null
+++ b/programs/starter/lex.yy.c
@@ -0,0 +1,1966 @@
+
+#line 3 "lex.yy.c"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 33
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 14
+#define YY_END_OF_BUFFER 15
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[47] =
+ { 0,
+ 0, 0, 15, 11, 2, 4, 13, 11, 3, 11,
+ 11, 11, 11, 1, 11, 2, 0, 12, 11, 0,
+ 4, 8, 11, 11, 11, 11, 1, 11, 11, 11,
+ 11, 11, 7, 11, 11, 11, 11, 11, 6, 11,
+ 5, 11, 11, 9, 10, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 4, 5, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 6, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 7, 1, 8, 9,
+
+ 10, 11, 12, 1, 13, 1, 1, 14, 1, 15,
+ 16, 17, 1, 18, 19, 20, 21, 22, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[23] =
+ { 0,
+ 1, 2, 3, 2, 1, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[51] =
+ { 0,
+ 0, 69, 70, 0, 67, 72, 64, 21, 72, 19,
+ 52, 56, 55, 62, 0, 61, 58, 72, 34, 58,
+ 72, 0, 45, 51, 38, 39, 54, 17, 41, 33,
+ 34, 39, 0, 30, 33, 36, 27, 25, 0, 17,
+ 0, 21, 15, 0, 0, 72, 28, 40, 42, 45
+ } ;
+
+static yyconst flex_int16_t yy_def[51] =
+ { 0,
+ 46, 1, 46, 47, 46, 46, 48, 49, 46, 47,
+ 47, 47, 47, 46, 47, 46, 48, 46, 49, 50,
+ 46, 47, 47, 47, 47, 47, 46, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 0, 46, 46, 46, 46
+ } ;
+
+static yyconst flex_int16_t yy_nxt[95] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 4, 10, 4, 4,
+ 4, 4, 11, 4, 4, 4, 4, 4, 12, 4,
+ 4, 13, 20, 21, 20, 22, 20, 32, 15, 45,
+ 44, 33, 43, 42, 23, 20, 21, 20, 41, 20,
+ 17, 17, 19, 19, 19, 20, 20, 20, 40, 39,
+ 38, 37, 36, 35, 34, 27, 31, 30, 29, 28,
+ 21, 18, 16, 27, 26, 25, 24, 18, 16, 46,
+ 14, 3, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46
+
+ } ;
+
+static yyconst flex_int16_t yy_chk[95] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 8, 8, 8, 10, 8, 28, 47, 43,
+ 42, 28, 40, 38, 10, 19, 19, 19, 37, 19,
+ 48, 48, 49, 49, 49, 50, 50, 50, 36, 35,
+ 34, 32, 31, 30, 29, 27, 26, 25, 24, 23,
+ 20, 17, 16, 14, 13, 12, 11, 7, 5, 3,
+ 2, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46
+
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "parser.l"
+#line 2 "parser.l"
+/* FreeS/WAN config file parser (parser.l)
+ * Copyright (C) 2001 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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.yy.c,v 1.4 2006/03/28 22:32:49 as Exp $
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <glob.h>
+
+#include "parser.tab.h"
+
+#define MAX_INCLUDE_DEPTH 20
+
+#define YY_NO_UNPUT
+extern void yyerror(const char *);
+extern int yylex (void);
+
+static struct {
+ int stack_ptr;
+ YY_BUFFER_STATE stack[MAX_INCLUDE_DEPTH];
+ FILE *file[MAX_INCLUDE_DEPTH];
+ unsigned int line[MAX_INCLUDE_DEPTH];
+ char *filename[MAX_INCLUDE_DEPTH];
+} __parser_y_private;
+
+void _parser_y_error(char *b, int size, const char *s);
+void _parser_y_init (const char *f);
+void _parser_y_fini (void);
+int _parser_y_include (const char *filename);
+
+void _parser_y_error(char *b, int size, const char *s)
+{
+ extern char *yytext; // was: char yytext[];
+
+ snprintf(b, size, "%s:%d: %s [%s]",
+ __parser_y_private.filename[__parser_y_private.stack_ptr],
+ __parser_y_private.line[__parser_y_private.stack_ptr],
+ s, yytext);
+}
+
+void _parser_y_init (const char *f)
+{
+ memset(&__parser_y_private, 0, sizeof(__parser_y_private));
+ __parser_y_private.line[0] = 1;
+ __parser_y_private.filename[0] = strdup(f);
+}
+
+void _parser_y_fini (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_INCLUDE_DEPTH; i++)
+ {
+ if (__parser_y_private.filename[i])
+ free(__parser_y_private.filename[i]);
+ if (__parser_y_private.file[i])
+ fclose(__parser_y_private.file[i]);
+ }
+ memset(&__parser_y_private, 0, sizeof(__parser_y_private));
+}
+
+int _parser_y_include (const char *filename)
+{
+ glob_t files;
+ int i, ret;
+
+ ret = glob(filename, GLOB_ERR, NULL, &files);
+ if (ret)
+ {
+ const char *err;
+
+ switch (ret)
+ {
+ case GLOB_NOSPACE:
+ err = "include files ran out of memory";
+ break;
+ case GLOB_ABORTED:
+ err = "include files aborted due to read error";
+ break;
+ case GLOB_NOMATCH:
+ err = "include files found no matches";
+ break;
+ default:
+ err = "unknown include files error";
+ }
+ yyerror(err);
+ return 1;
+ }
+
+ for (i = 0; i < files.gl_pathc; i++)
+ {
+ FILE *f;
+ unsigned int p = __parser_y_private.stack_ptr + 1;
+
+ if (p >= MAX_INCLUDE_DEPTH)
+ {
+ yyerror("max inclusion depth reached");
+ return 1;
+ }
+
+ f = fopen(files.gl_pathv[i], "r");
+ if (!f)
+ {
+ yyerror("can't open include filename");
+ continue;
+ }
+
+ __parser_y_private.stack_ptr++;
+ __parser_y_private.file[p] = f;
+ __parser_y_private.stack[p] = YY_CURRENT_BUFFER;
+ __parser_y_private.line[p] = 1;
+ __parser_y_private.filename[p] = strdup(files.gl_pathv[i]);
+
+ yy_switch_to_buffer(yy_create_buffer(f,YY_BUF_SIZE));
+ }
+ globfree(&files);
+ return 0;
+}
+
+#line 618 "lex.yy.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 134 "parser.l"
+
+
+#line 777 "lex.yy.c"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 47 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 72 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case YY_STATE_EOF(INITIAL):
+#line 136 "parser.l"
+{
+ if (__parser_y_private.filename[__parser_y_private.stack_ptr]) {
+ free(__parser_y_private.filename[__parser_y_private.stack_ptr]);
+ __parser_y_private.filename[__parser_y_private.stack_ptr] = NULL;
+ }
+ if (__parser_y_private.file[__parser_y_private.stack_ptr]) {
+ fclose(__parser_y_private.file[__parser_y_private.stack_ptr]);
+ __parser_y_private.file[__parser_y_private.stack_ptr] = NULL;
+ yy_delete_buffer (YY_CURRENT_BUFFER);
+ yy_switch_to_buffer
+ (__parser_y_private.stack[__parser_y_private.stack_ptr]);
+ }
+ if (--__parser_y_private.stack_ptr < 0) {
+ yyterminate();
+ }
+}
+ YY_BREAK
+case 1:
+YY_RULE_SETUP
+#line 153 "parser.l"
+return FIRST_SPACES;
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 155 "parser.l"
+/* ignore spaces in line */ ;
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 157 "parser.l"
+return EQUAL;
+ YY_BREAK
+case 4:
+/* rule 4 can match eol */
+YY_RULE_SETUP
+#line 159 "parser.l"
+{
+ __parser_y_private.line[__parser_y_private.stack_ptr]++;
+ return EOL;
+ }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 164 "parser.l"
+return CONFIG;
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 165 "parser.l"
+return SETUP;
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 166 "parser.l"
+return CONN;
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 167 "parser.l"
+return CA;
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 168 "parser.l"
+return INCLUDE;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 169 "parser.l"
+return VERSION;
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 171 "parser.l"
+{
+ yylval.s = strdup(yytext);
+ return STRING;
+ }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 176 "parser.l"
+{
+ yylval.s = strdup(yytext+1);
+ if (yylval.s) yylval.s[strlen(yylval.s)-1]='\0';
+ return STRING;
+ }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 182 "parser.l"
+yyerror(yytext);
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 184 "parser.l"
+ECHO;
+ YY_BREAK
+#line 961 "lex.yy.c"
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 47 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 47 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 46);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef _UNISTD_H /* assume unistd.h has isatty() for us */
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __THROW /* this is a gnuism */
+extern int isatty (int ) __THROW;
+#else
+extern int isatty (int );
+#endif
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * __yystr )
+{
+
+ return yy_scan_bytes(__yystr,strlen(__yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 184 "parser.l"
+
+
+
+int yywrap(void)
+{
+ return 1;
+}
+
+
diff --git a/programs/starter/netkey.c b/programs/starter/netkey.c
new file mode 100644
index 000000000..d0b8e0a2c
--- /dev/null
+++ b/programs/starter/netkey.c
@@ -0,0 +1,85 @@
+/* strongSwan netkey starter
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: netkey.c,v 1.4 2006/02/15 18:33:57 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "files.h"
+
+bool
+starter_netkey_init(void)
+{
+ struct stat stb;
+
+ if (stat(PROC_NETKEY, &stb) != 0)
+ {
+ /* af_key module makes the netkey proc interface visible */
+ if (stat(PROC_MODULES, &stb) == 0)
+ {
+ system("modprobe -qv af_key");
+ }
+
+ /* now test again */
+ if (stat(PROC_NETKEY, &stb) != 0)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("kernel appears to lack the native netkey IPsec stack")
+ )
+ return FALSE;
+ }
+ }
+
+ /* make sure that all required IPsec modules are loaded */
+ if (stat(PROC_MODULES, &stb) == 0)
+ {
+ system("modprobe -qv ah4");
+ system("modprobe -qv esp4");
+ system("modprobe -qv ipcomp");
+ system("modprobe -qv xfrm4_tunnel");
+ system("modprobe -qv xfrm_user");
+ }
+
+ DBG(DBG_CONTROL,
+ DBG_log("Found netkey IPsec stack")
+ )
+ return TRUE;
+}
+
+void
+starter_netkey_cleanup(void)
+{
+ if (system("ip xfrm state > /dev/null 2>&1") == 0)
+ {
+ system("ip xfrm state flush");
+ system("ip xfrm policy flush");
+ }
+ else if (system("type setkey > /dev/null 2>&1") == 0)
+ {
+ system("setkey -F");
+ system("setkey -FP");
+ }
+ else
+ {
+ plog("WARNING: cannot flush IPsec state/policy database");
+ }
+}
diff --git a/programs/starter/netkey.h b/programs/starter/netkey.h
new file mode 100644
index 000000000..ff8989d34
--- /dev/null
+++ b/programs/starter/netkey.h
@@ -0,0 +1,24 @@
+/* strongSwan netkey initialization and cleanup
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: netkey.h,v 1.1 2005/12/30 19:03:15 as Exp $
+ */
+
+#ifndef _STARTER_NETKEY_H_
+#define _STARTER_NETKEY_H_
+
+extern bool starter_netkey_init (void);
+extern void starter_netkey_cleanup (void);
+
+#endif /* _STARTER_NETKEY_H_ */
+
diff --git a/programs/starter/parser.h b/programs/starter/parser.h
new file mode 100644
index 000000000..61bdea974
--- /dev/null
+++ b/programs/starter/parser.h
@@ -0,0 +1,57 @@
+/* strongSwan config file parser
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: parser.h,v 1.5 2006/01/17 23:43:36 as Exp $
+ */
+
+#ifndef _IPSEC_PARSER_H_
+#define _IPSEC_PARSER_H_
+
+#include "keywords.h"
+
+typedef struct kw_entry kw_entry_t;
+
+struct kw_entry {
+ char *name;
+ kw_token_t token;
+};
+
+typedef struct kw_list kw_list_t;
+
+struct kw_list {
+ kw_entry_t *entry;
+ char *value;
+ kw_list_t *next;
+};
+
+typedef struct section_list section_list_t;
+
+struct section_list {
+ char *name;
+ kw_list_t *kw;
+ section_list_t *next;
+};
+
+typedef struct config_parsed config_parsed_t;
+
+struct config_parsed {
+ kw_list_t *config_setup;
+ section_list_t *conn_first, *conn_last;
+ section_list_t *ca_first, *ca_last;
+};
+
+config_parsed_t *parser_load_conf (const char *file);
+void parser_free_conf (config_parsed_t *cfg);
+
+#endif /* _IPSEC_PARSER_H_ */
+
diff --git a/programs/starter/parser.l b/programs/starter/parser.l
new file mode 100644
index 000000000..8d1cc4c31
--- /dev/null
+++ b/programs/starter/parser.l
@@ -0,0 +1,190 @@
+%{
+/* FreeS/WAN config file parser (parser.l)
+ * Copyright (C) 2001 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: parser.l,v 1.5 2006/03/28 22:32:33 as Exp $
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <glob.h>
+
+#include "parser.tab.h"
+
+#define MAX_INCLUDE_DEPTH 20
+
+#define YY_NO_UNPUT
+extern void yyerror(const char *);
+extern int yylex (void);
+
+static struct {
+ int stack_ptr;
+ YY_BUFFER_STATE stack[MAX_INCLUDE_DEPTH];
+ FILE *file[MAX_INCLUDE_DEPTH];
+ unsigned int line[MAX_INCLUDE_DEPTH];
+ char *filename[MAX_INCLUDE_DEPTH];
+} __parser_y_private;
+
+void _parser_y_error(char *b, int size, const char *s);
+void _parser_y_init (const char *f);
+void _parser_y_fini (void);
+int _parser_y_include (const char *filename);
+
+void _parser_y_error(char *b, int size, const char *s)
+{
+ extern char *yytext; // was: char yytext[];
+
+ snprintf(b, size, "%s:%d: %s [%s]",
+ __parser_y_private.filename[__parser_y_private.stack_ptr],
+ __parser_y_private.line[__parser_y_private.stack_ptr],
+ s, yytext);
+}
+
+void _parser_y_init (const char *f)
+{
+ memset(&__parser_y_private, 0, sizeof(__parser_y_private));
+ __parser_y_private.line[0] = 1;
+ __parser_y_private.filename[0] = strdup(f);
+}
+
+void _parser_y_fini (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAX_INCLUDE_DEPTH; i++)
+ {
+ if (__parser_y_private.filename[i])
+ free(__parser_y_private.filename[i]);
+ if (__parser_y_private.file[i])
+ fclose(__parser_y_private.file[i]);
+ }
+ memset(&__parser_y_private, 0, sizeof(__parser_y_private));
+}
+
+int _parser_y_include (const char *filename)
+{
+ glob_t files;
+ int i, ret;
+
+ ret = glob(filename, GLOB_ERR, NULL, &files);
+ if (ret)
+ {
+ const char *err;
+
+ switch (ret)
+ {
+ case GLOB_NOSPACE:
+ err = "include files ran out of memory";
+ break;
+ case GLOB_ABORTED:
+ err = "include files aborted due to read error";
+ break;
+ case GLOB_NOMATCH:
+ err = "include files found no matches";
+ break;
+ default:
+ err = "unknown include files error";
+ }
+ yyerror(err);
+ return 1;
+ }
+
+ for (i = 0; i < files.gl_pathc; i++)
+ {
+ FILE *f;
+ unsigned int p = __parser_y_private.stack_ptr + 1;
+
+ if (p >= MAX_INCLUDE_DEPTH)
+ {
+ yyerror("max inclusion depth reached");
+ return 1;
+ }
+
+ f = fopen(files.gl_pathv[i], "r");
+ if (!f)
+ {
+ yyerror("can't open include filename");
+ continue;
+ }
+
+ __parser_y_private.stack_ptr++;
+ __parser_y_private.file[p] = f;
+ __parser_y_private.stack[p] = YY_CURRENT_BUFFER;
+ __parser_y_private.line[p] = 1;
+ __parser_y_private.filename[p] = strdup(files.gl_pathv[i]);
+
+ yy_switch_to_buffer(yy_create_buffer(f, YY_BUF_SIZE));
+ }
+ globfree(&files);
+ return 0;
+}
+
+%}
+
+%%
+
+<<EOF>> {
+ if (__parser_y_private.filename[__parser_y_private.stack_ptr]) {
+ free(__parser_y_private.filename[__parser_y_private.stack_ptr]);
+ __parser_y_private.filename[__parser_y_private.stack_ptr] = NULL;
+ }
+ if (__parser_y_private.file[__parser_y_private.stack_ptr]) {
+ fclose(__parser_y_private.file[__parser_y_private.stack_ptr]);
+ __parser_y_private.file[__parser_y_private.stack_ptr] = NULL;
+ yy_delete_buffer (YY_CURRENT_BUFFER);
+ yy_switch_to_buffer
+ (__parser_y_private.stack[__parser_y_private.stack_ptr]);
+ }
+ if (--__parser_y_private.stack_ptr < 0) {
+ yyterminate();
+ }
+}
+
+^[\t ]+ return FIRST_SPACES;
+
+[\t ]+ /* ignore spaces in line */ ;
+
+= return EQUAL;
+
+\n|#.*\n {
+ __parser_y_private.line[__parser_y_private.stack_ptr]++;
+ return EOL;
+ }
+
+config return CONFIG;
+setup return SETUP;
+conn return CONN;
+ca return CA;
+include return INCLUDE;
+version return VERSION;
+
+[^\"= \t\n]+ {
+ yylval.s = strdup(yytext);
+ return STRING;
+ }
+
+\"[^\"\n]*\" {
+ yylval.s = strdup(yytext+1);
+ if (yylval.s) yylval.s[strlen(yylval.s)-1]='\0';
+ return STRING;
+ }
+
+. yyerror(yytext);
+
+%%
+
+int yywrap(void)
+{
+ return 1;
+}
+
diff --git a/programs/starter/parser.output b/programs/starter/parser.output
new file mode 100644
index 000000000..ddb01e89a
--- /dev/null
+++ b/programs/starter/parser.output
@@ -0,0 +1,351 @@
+Grammar
+
+ 0 $accept: config_file $end
+
+ 1 config_file: config_file section_or_include
+ 2 | /* empty */
+
+ 3 section_or_include: VERSION STRING EOL
+
+ 4 @1: /* empty */
+
+ 5 section_or_include: CONFIG SETUP EOL @1 kw_section
+
+ 6 @2: /* empty */
+
+ 7 section_or_include: CONN STRING EOL @2 kw_section
+
+ 8 @3: /* empty */
+
+ 9 section_or_include: CA STRING EOL @3 kw_section
+
+ 10 @4: /* empty */
+
+ 11 section_or_include: INCLUDE STRING @4 EOL
+ 12 | EOL
+
+ 13 kw_section: FIRST_SPACES statement_kw EOL kw_section
+ 14 | /* empty */
+
+ 15 statement_kw: STRING EQUAL STRING
+ 16 | STRING EQUAL
+ 17 | /* empty */
+
+
+Terminals, with rules where they appear
+
+$end (0) 0
+error (256)
+EQUAL (258) 15 16
+FIRST_SPACES (259) 13
+EOL (260) 3 5 7 9 11 12 13
+CONFIG (261) 5
+SETUP (262) 5
+CONN (263) 7
+CA (264) 9
+INCLUDE (265) 11
+VERSION (266) 3
+STRING (267) 3 7 9 11 15 16
+
+
+Nonterminals, with rules where they appear
+
+$accept (13)
+ on left: 0
+config_file (14)
+ on left: 1 2, on right: 0 1
+section_or_include (15)
+ on left: 3 5 7 9 11 12, on right: 1
+@1 (16)
+ on left: 4, on right: 5
+@2 (17)
+ on left: 6, on right: 7
+@3 (18)
+ on left: 8, on right: 9
+@4 (19)
+ on left: 10, on right: 11
+kw_section (20)
+ on left: 13 14, on right: 5 7 9 13
+statement_kw (21)
+ on left: 15 16 17, on right: 13
+
+
+state 0
+
+ 0 $accept: . config_file $end
+
+ $default reduce using rule 2 (config_file)
+
+ config_file go to state 1
+
+
+state 1
+
+ 0 $accept: config_file . $end
+ 1 config_file: config_file . section_or_include
+
+ $end shift, and go to state 2
+ EOL shift, and go to state 3
+ CONFIG shift, and go to state 4
+ CONN shift, and go to state 5
+ CA shift, and go to state 6
+ INCLUDE shift, and go to state 7
+ VERSION shift, and go to state 8
+
+ section_or_include go to state 9
+
+
+state 2
+
+ 0 $accept: config_file $end .
+
+ $default accept
+
+
+state 3
+
+ 12 section_or_include: EOL .
+
+ $default reduce using rule 12 (section_or_include)
+
+
+state 4
+
+ 5 section_or_include: CONFIG . SETUP EOL @1 kw_section
+
+ SETUP shift, and go to state 10
+
+
+state 5
+
+ 7 section_or_include: CONN . STRING EOL @2 kw_section
+
+ STRING shift, and go to state 11
+
+
+state 6
+
+ 9 section_or_include: CA . STRING EOL @3 kw_section
+
+ STRING shift, and go to state 12
+
+
+state 7
+
+ 11 section_or_include: INCLUDE . STRING @4 EOL
+
+ STRING shift, and go to state 13
+
+
+state 8
+
+ 3 section_or_include: VERSION . STRING EOL
+
+ STRING shift, and go to state 14
+
+
+state 9
+
+ 1 config_file: config_file section_or_include .
+
+ $default reduce using rule 1 (config_file)
+
+
+state 10
+
+ 5 section_or_include: CONFIG SETUP . EOL @1 kw_section
+
+ EOL shift, and go to state 15
+
+
+state 11
+
+ 7 section_or_include: CONN STRING . EOL @2 kw_section
+
+ EOL shift, and go to state 16
+
+
+state 12
+
+ 9 section_or_include: CA STRING . EOL @3 kw_section
+
+ EOL shift, and go to state 17
+
+
+state 13
+
+ 11 section_or_include: INCLUDE STRING . @4 EOL
+
+ $default reduce using rule 10 (@4)
+
+ @4 go to state 18
+
+
+state 14
+
+ 3 section_or_include: VERSION STRING . EOL
+
+ EOL shift, and go to state 19
+
+
+state 15
+
+ 5 section_or_include: CONFIG SETUP EOL . @1 kw_section
+
+ $default reduce using rule 4 (@1)
+
+ @1 go to state 20
+
+
+state 16
+
+ 7 section_or_include: CONN STRING EOL . @2 kw_section
+
+ $default reduce using rule 6 (@2)
+
+ @2 go to state 21
+
+
+state 17
+
+ 9 section_or_include: CA STRING EOL . @3 kw_section
+
+ $default reduce using rule 8 (@3)
+
+ @3 go to state 22
+
+
+state 18
+
+ 11 section_or_include: INCLUDE STRING @4 . EOL
+
+ EOL shift, and go to state 23
+
+
+state 19
+
+ 3 section_or_include: VERSION STRING EOL .
+
+ $default reduce using rule 3 (section_or_include)
+
+
+state 20
+
+ 5 section_or_include: CONFIG SETUP EOL @1 . kw_section
+
+ FIRST_SPACES shift, and go to state 24
+
+ $default reduce using rule 14 (kw_section)
+
+ kw_section go to state 25
+
+
+state 21
+
+ 7 section_or_include: CONN STRING EOL @2 . kw_section
+
+ FIRST_SPACES shift, and go to state 24
+
+ $default reduce using rule 14 (kw_section)
+
+ kw_section go to state 26
+
+
+state 22
+
+ 9 section_or_include: CA STRING EOL @3 . kw_section
+
+ FIRST_SPACES shift, and go to state 24
+
+ $default reduce using rule 14 (kw_section)
+
+ kw_section go to state 27
+
+
+state 23
+
+ 11 section_or_include: INCLUDE STRING @4 EOL .
+
+ $default reduce using rule 11 (section_or_include)
+
+
+state 24
+
+ 13 kw_section: FIRST_SPACES . statement_kw EOL kw_section
+
+ STRING shift, and go to state 28
+
+ $default reduce using rule 17 (statement_kw)
+
+ statement_kw go to state 29
+
+
+state 25
+
+ 5 section_or_include: CONFIG SETUP EOL @1 kw_section .
+
+ $default reduce using rule 5 (section_or_include)
+
+
+state 26
+
+ 7 section_or_include: CONN STRING EOL @2 kw_section .
+
+ $default reduce using rule 7 (section_or_include)
+
+
+state 27
+
+ 9 section_or_include: CA STRING EOL @3 kw_section .
+
+ $default reduce using rule 9 (section_or_include)
+
+
+state 28
+
+ 15 statement_kw: STRING . EQUAL STRING
+ 16 | STRING . EQUAL
+
+ EQUAL shift, and go to state 30
+
+
+state 29
+
+ 13 kw_section: FIRST_SPACES statement_kw . EOL kw_section
+
+ EOL shift, and go to state 31
+
+
+state 30
+
+ 15 statement_kw: STRING EQUAL . STRING
+ 16 | STRING EQUAL .
+
+ STRING shift, and go to state 32
+
+ $default reduce using rule 16 (statement_kw)
+
+
+state 31
+
+ 13 kw_section: FIRST_SPACES statement_kw EOL . kw_section
+
+ FIRST_SPACES shift, and go to state 24
+
+ $default reduce using rule 14 (kw_section)
+
+ kw_section go to state 33
+
+
+state 32
+
+ 15 statement_kw: STRING EQUAL STRING .
+
+ $default reduce using rule 15 (statement_kw)
+
+
+state 33
+
+ 13 kw_section: FIRST_SPACES statement_kw EOL kw_section .
+
+ $default reduce using rule 13 (kw_section)
diff --git a/programs/starter/parser.tab.c b/programs/starter/parser.tab.c
new file mode 100644
index 000000000..bc21a2fd3
--- /dev/null
+++ b/programs/starter/parser.tab.c
@@ -0,0 +1,1666 @@
+/* A Bison parser, made by GNU Bison 2.1. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.1"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ EQUAL = 258,
+ FIRST_SPACES = 259,
+ EOL = 260,
+ CONFIG = 261,
+ SETUP = 262,
+ CONN = 263,
+ CA = 264,
+ INCLUDE = 265,
+ VERSION = 266,
+ STRING = 267
+ };
+#endif
+/* Tokens. */
+#define EQUAL 258
+#define FIRST_SPACES 259
+#define EOL 260
+#define CONFIG 261
+#define SETUP 262
+#define CONN 263
+#define CA 264
+#define INCLUDE 265
+#define VERSION 266
+#define STRING 267
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "parser.y"
+
+/* strongSwan config file parser (parser.y)
+ * Copyright (C) 2001 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: parser.tab.c,v 1.5 2006/03/28 22:32:49 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+#include "parser.h"
+
+#define YYERROR_VERBOSE
+#define ERRSTRING_LEN 256
+
+/**
+ * Bison
+ */
+static char parser_errstring[ERRSTRING_LEN+1];
+
+extern void yyerror(const char *s);
+extern int yylex (void);
+extern void _parser_y_error(char *b, int size, const char *s);
+
+/**
+ * Static Globals
+ */
+static int _save_errors_;
+static config_parsed_t *_parser_cfg;
+static kw_list_t **_parser_kw, *_parser_kw_last;
+static char errbuf[ERRSTRING_LEN+1];
+
+/**
+ * Gperf
+ */
+extern kw_entry_t *in_word_set (char *str, unsigned int len);
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 56 "parser.y"
+typedef union YYSTYPE { char *s; } YYSTYPE;
+/* Line 196 of yacc.c. */
+#line 166 "parser.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 219 of yacc.c. */
+#line 178 "parser.tab.c"
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus))
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#ifndef YY_
+# if YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYINCLUDED_STDLIB_H
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1)
+# endif
+# ifdef __cplusplus
+extern "C" {
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \
+ && (defined (__STDC__) || defined (__cplusplus)))
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \
+ && (defined (__STDC__) || defined (__cplusplus)))
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifdef __cplusplus
+}
+# endif
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short int yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 2
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 27
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 13
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 9
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 18
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 34
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 267
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 6, 7, 11, 12, 18, 19, 25,
+ 26, 32, 33, 38, 40, 45, 46, 50, 53
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 14, 0, -1, 14, 15, -1, -1, 11, 12, 5,
+ -1, -1, 6, 7, 5, 16, 20, -1, -1, 8,
+ 12, 5, 17, 20, -1, -1, 9, 12, 5, 18,
+ 20, -1, -1, 10, 12, 19, 5, -1, 5, -1,
+ 4, 21, 5, 20, -1, -1, 12, 3, 12, -1,
+ 12, 3, -1, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned char yyrline[] =
+{
+ 0, 67, 67, 68, 72, 77, 76, 82, 81, 99,
+ 98, 115, 114, 120, 124, 125, 129, 154, 158
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "EQUAL", "FIRST_SPACES", "EOL", "CONFIG",
+ "SETUP", "CONN", "CA", "INCLUDE", "VERSION", "STRING", "$accept",
+ "config_file", "section_or_include", "@1", "@2", "@3", "@4",
+ "kw_section", "statement_kw", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short int yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 13, 14, 14, 15, 16, 15, 17, 15, 18,
+ 15, 19, 15, 15, 20, 20, 21, 21, 21
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 2, 0, 3, 0, 5, 0, 5, 0,
+ 5, 0, 4, 1, 4, 0, 3, 2, 0
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 3, 0, 1, 13, 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 11, 0, 5, 7, 9, 0, 4,
+ 15, 15, 15, 12, 18, 6, 8, 10, 0, 0,
+ 17, 15, 16, 14
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+ -1, 1, 9, 20, 21, 22, 18, 25, 29
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -20
+static const yysigned_char yypact[] =
+{
+ -20, 0, -20, -20, -6, -8, -5, 1, 2, -20,
+ 10, 11, 12, -20, 13, -20, -20, -20, 14, -20,
+ 16, 16, 16, -20, 9, -20, -20, -20, 19, 18,
+ 15, 16, -20, -20
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yysigned_char yypgoto[] =
+{
+ -20, -20, -20, -20, -20, -20, -20, -19, -20
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+ 2, 10, 26, 27, 11, 3, 4, 12, 5, 6,
+ 7, 8, 33, 13, 14, 15, 16, 17, 19, 23,
+ 24, 28, 30, 31, 0, 0, 0, 32
+};
+
+static const yysigned_char yycheck[] =
+{
+ 0, 7, 21, 22, 12, 5, 6, 12, 8, 9,
+ 10, 11, 31, 12, 12, 5, 5, 5, 5, 5,
+ 4, 12, 3, 5, -1, -1, -1, 12
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 14, 0, 5, 6, 8, 9, 10, 11, 15,
+ 7, 12, 12, 12, 12, 5, 5, 5, 19, 5,
+ 16, 17, 18, 5, 4, 20, 20, 20, 12, 21,
+ 3, 5, 12, 20
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short int *bottom;
+ short int *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ size_t yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+#endif /* YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+ ;
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short int yyssa[YYINITDEPTH];
+ short int *yyss = yyssa;
+ short int *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short int *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short int *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a look-ahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 73 "parser.y"
+ {
+ free((yyvsp[-1].s));
+ ;}
+ break;
+
+ case 5:
+#line 77 "parser.y"
+ {
+ _parser_kw = &(_parser_cfg->config_setup);
+ _parser_kw_last = NULL;
+ ;}
+ break;
+
+ case 7:
+#line 82 "parser.y"
+ {
+ section_list_t *section = (section_list_t *)alloc_thing(section_list_t
+ , "section_list_t");
+
+ section->name = clone_str((yyvsp[-1].s), "conn section name");
+ section->kw = NULL;
+ section->next = NULL;
+ _parser_kw = &(section->kw);
+ if (!_parser_cfg->conn_first)
+ _parser_cfg->conn_first = section;
+ if (_parser_cfg->conn_last)
+ _parser_cfg->conn_last->next = section;
+ _parser_cfg->conn_last = section;
+ _parser_kw_last = NULL;
+ free((yyvsp[-1].s));
+ ;}
+ break;
+
+ case 9:
+#line 99 "parser.y"
+ {
+ section_list_t *section = (section_list_t *)alloc_thing(section_list_t
+ , "section_list_t");
+ section->name = clone_str((yyvsp[-1].s), "ca section name");
+ section->kw = NULL;
+ section->next = NULL;
+ _parser_kw = &(section->kw);
+ if (!_parser_cfg->ca_first)
+ _parser_cfg->ca_first = section;
+ if (_parser_cfg->ca_last)
+ _parser_cfg->ca_last->next = section;
+ _parser_cfg->ca_last = section;
+ _parser_kw_last = NULL;
+ free((yyvsp[-1].s));
+ ;}
+ break;
+
+ case 11:
+#line 115 "parser.y"
+ {
+ extern void _parser_y_include (const char *f);
+ _parser_y_include((yyvsp[0].s));
+ free((yyvsp[0].s));
+ ;}
+ break;
+
+ case 16:
+#line 130 "parser.y"
+ {
+ kw_list_t *new;
+ kw_entry_t *entry = in_word_set((yyvsp[-2].s), strlen((yyvsp[-2].s)));
+
+ if (entry == NULL)
+ {
+ snprintf(errbuf, ERRSTRING_LEN, "unknown keyword '%s'", (yyvsp[-2].s));
+ yyerror(errbuf);
+ }
+ else if (_parser_kw)
+ {
+ new = (kw_list_t *)alloc_thing(kw_list_t, "kw_list_t");
+ new->entry = entry;
+ new->value = clone_str((yyvsp[0].s), "kw_list value");
+ new->next = NULL;
+ if (_parser_kw_last)
+ _parser_kw_last->next = new;
+ _parser_kw_last = new;
+ if (!*_parser_kw)
+ *_parser_kw = new;
+ }
+ free((yyvsp[-2].s));
+ free((yyvsp[0].s));
+ ;}
+ break;
+
+ case 17:
+#line 155 "parser.y"
+ {
+ free((yyvsp[-1].s));
+ ;}
+ break;
+
+
+ default: break;
+ }
+
+/* Line 1126 of yacc.c. */
+#line 1275 "parser.tab.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ char *yymsg = 0;
+# define YYERROR_VERBOSE_ARGS_MAXIMUM 5
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+#if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+#endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= yysize1 < yysize;
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= yysize1 < yysize;
+ yysize = yysize1;
+
+ if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM)
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyf))
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ goto yyexhaustedlab;
+ }
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror (YY_("syntax error"));
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding", yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (0)
+ goto yyerrorlab;
+
+yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping", yystos[yystate], yyvsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK;
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 161 "parser.y"
+
+
+void
+yyerror(const char *s)
+{
+ if (_save_errors_)
+ _parser_y_error(parser_errstring, ERRSTRING_LEN, s);
+}
+
+config_parsed_t *
+parser_load_conf(const char *file)
+{
+ config_parsed_t *cfg = NULL;
+ int err = 0;
+ FILE *f;
+
+ extern void _parser_y_init (const char *f);
+ extern FILE *yyin;
+
+ memset(parser_errstring, 0, ERRSTRING_LEN+1);
+
+ cfg = (config_parsed_t *)alloc_thing(config_parsed_t, "config_parsed_t");
+ if (cfg)
+ {
+ memset(cfg, 0, sizeof(config_parsed_t));
+ f = fopen(file, "r");
+ if (f)
+ {
+ yyin = f;
+ _parser_y_init(file);
+ _save_errors_ = 1;
+ _parser_cfg = cfg;
+
+ if (yyparse() !=0 )
+ {
+ if (parser_errstring[0] == '\0')
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "Unknown error...");
+ }
+ _save_errors_ = 0;
+ while (yyparse() != 0);
+ err++;
+ }
+ else if (parser_errstring[0] != '\0')
+ {
+ err++;
+ }
+ else
+ {
+ /**
+ * Config valid
+ */
+ }
+
+ fclose(f);
+ }
+ else
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "can't load file '%s'", file);
+ err++;
+ }
+ }
+ else
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "can't allocate memory");
+ err++;
+ }
+
+ if (err)
+ {
+ plog("%s", parser_errstring);
+
+ if (cfg)
+ parser_free_conf(cfg);
+ cfg = NULL;
+ }
+
+ return cfg;
+}
+
+static void
+parser_free_kwlist(kw_list_t *list)
+{
+ kw_list_t *elt;
+
+ while (list)
+ {
+ elt = list;
+ list = list->next;
+ if (elt->value)
+ pfree(elt->value);
+ pfree(elt);
+ }
+}
+
+void
+parser_free_conf(config_parsed_t *cfg)
+{
+ section_list_t *sec;
+ if (cfg)
+ {
+ parser_free_kwlist(cfg->config_setup);
+ while (cfg->conn_first)
+ {
+ sec = cfg->conn_first;
+ cfg->conn_first = cfg->conn_first->next;
+ if (sec->name)
+ pfree(sec->name);
+ parser_free_kwlist(sec->kw);
+ pfree(sec);
+ }
+ while (cfg->ca_first)
+ {
+ sec = cfg->ca_first;
+ cfg->ca_first = cfg->ca_first->next;
+ if (sec->name)
+ pfree(sec->name);
+ parser_free_kwlist(sec->kw);
+ pfree(sec);
+ }
+ pfree(cfg);
+ }
+}
+
diff --git a/programs/starter/parser.tab.h b/programs/starter/parser.tab.h
new file mode 100644
index 000000000..1ded28fdb
--- /dev/null
+++ b/programs/starter/parser.tab.h
@@ -0,0 +1,72 @@
+/* A Bison parser, made by GNU Bison 2.1. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ EQUAL = 258,
+ FIRST_SPACES = 259,
+ EOL = 260,
+ CONFIG = 261,
+ SETUP = 262,
+ CONN = 263,
+ CA = 264,
+ INCLUDE = 265,
+ VERSION = 266,
+ STRING = 267
+ };
+#endif
+/* Tokens. */
+#define EQUAL 258
+#define FIRST_SPACES 259
+#define EOL 260
+#define CONFIG 261
+#define SETUP 262
+#define CONN 263
+#define CA 264
+#define INCLUDE 265
+#define VERSION 266
+#define STRING 267
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 56 "parser.y"
+typedef union YYSTYPE { char *s; } YYSTYPE;
+/* Line 1447 of yacc.c. */
+#line 64 "parser.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
+
+
diff --git a/programs/starter/parser.y b/programs/starter/parser.y
new file mode 100644
index 000000000..159bbc651
--- /dev/null
+++ b/programs/starter/parser.y
@@ -0,0 +1,283 @@
+%{
+/* strongSwan config file parser (parser.y)
+ * Copyright (C) 2001 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: parser.y,v 1.6 2006/01/17 23:43:36 as Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+#include "parser.h"
+
+#define YYERROR_VERBOSE
+#define ERRSTRING_LEN 256
+
+/**
+ * Bison
+ */
+static char parser_errstring[ERRSTRING_LEN+1];
+
+extern void yyerror(const char *s);
+extern int yylex (void);
+extern void _parser_y_error(char *b, int size, const char *s);
+
+/**
+ * Static Globals
+ */
+static int _save_errors_;
+static config_parsed_t *_parser_cfg;
+static kw_list_t **_parser_kw, *_parser_kw_last;
+static char errbuf[ERRSTRING_LEN+1];
+
+/**
+ * Gperf
+ */
+extern kw_entry_t *in_word_set (char *str, unsigned int len);
+
+%}
+
+%union { char *s; };
+%token EQUAL FIRST_SPACES EOL CONFIG SETUP CONN CA INCLUDE VERSION
+%token <s> STRING
+
+%%
+
+/*
+ * Config file
+ */
+
+config_file:
+ config_file section_or_include
+ | /* NULL */
+ ;
+
+section_or_include:
+ VERSION STRING EOL
+ {
+ free($2);
+ }
+ | CONFIG SETUP EOL
+ {
+ _parser_kw = &(_parser_cfg->config_setup);
+ _parser_kw_last = NULL;
+ } kw_section
+ | CONN STRING EOL
+ {
+ section_list_t *section = (section_list_t *)alloc_thing(section_list_t
+ , "section_list_t");
+
+ section->name = clone_str($2, "conn section name");
+ section->kw = NULL;
+ section->next = NULL;
+ _parser_kw = &(section->kw);
+ if (!_parser_cfg->conn_first)
+ _parser_cfg->conn_first = section;
+ if (_parser_cfg->conn_last)
+ _parser_cfg->conn_last->next = section;
+ _parser_cfg->conn_last = section;
+ _parser_kw_last = NULL;
+ free($2);
+ } kw_section
+ | CA STRING EOL
+ {
+ section_list_t *section = (section_list_t *)alloc_thing(section_list_t
+ , "section_list_t");
+ section->name = clone_str($2, "ca section name");
+ section->kw = NULL;
+ section->next = NULL;
+ _parser_kw = &(section->kw);
+ if (!_parser_cfg->ca_first)
+ _parser_cfg->ca_first = section;
+ if (_parser_cfg->ca_last)
+ _parser_cfg->ca_last->next = section;
+ _parser_cfg->ca_last = section;
+ _parser_kw_last = NULL;
+ free($2);
+ } kw_section
+ | INCLUDE STRING
+ {
+ extern void _parser_y_include (const char *f);
+ _parser_y_include($2);
+ free($2);
+ } EOL
+ | EOL
+ ;
+
+kw_section:
+ FIRST_SPACES statement_kw EOL kw_section
+ |
+ ;
+
+statement_kw:
+ STRING EQUAL STRING
+ {
+ kw_list_t *new;
+ kw_entry_t *entry = in_word_set($1, strlen($1));
+
+ if (entry == NULL)
+ {
+ snprintf(errbuf, ERRSTRING_LEN, "unknown keyword '%s'", $1);
+ yyerror(errbuf);
+ }
+ else if (_parser_kw)
+ {
+ new = (kw_list_t *)alloc_thing(kw_list_t, "kw_list_t");
+ new->entry = entry;
+ new->value = clone_str($3, "kw_list value");
+ new->next = NULL;
+ if (_parser_kw_last)
+ _parser_kw_last->next = new;
+ _parser_kw_last = new;
+ if (!*_parser_kw)
+ *_parser_kw = new;
+ }
+ free($1);
+ free($3);
+ }
+ | STRING EQUAL
+ {
+ free($1);
+ }
+ |
+ ;
+
+%%
+
+void
+yyerror(const char *s)
+{
+ if (_save_errors_)
+ _parser_y_error(parser_errstring, ERRSTRING_LEN, s);
+}
+
+config_parsed_t *
+parser_load_conf(const char *file)
+{
+ config_parsed_t *cfg = NULL;
+ int err = 0;
+ FILE *f;
+
+ extern void _parser_y_init (const char *f);
+ extern FILE *yyin;
+
+ memset(parser_errstring, 0, ERRSTRING_LEN+1);
+
+ cfg = (config_parsed_t *)alloc_thing(config_parsed_t, "config_parsed_t");
+ if (cfg)
+ {
+ memset(cfg, 0, sizeof(config_parsed_t));
+ f = fopen(file, "r");
+ if (f)
+ {
+ yyin = f;
+ _parser_y_init(file);
+ _save_errors_ = 1;
+ _parser_cfg = cfg;
+
+ if (yyparse() !=0 )
+ {
+ if (parser_errstring[0] == '\0')
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "Unknown error...");
+ }
+ _save_errors_ = 0;
+ while (yyparse() != 0);
+ err++;
+ }
+ else if (parser_errstring[0] != '\0')
+ {
+ err++;
+ }
+ else
+ {
+ /**
+ * Config valid
+ */
+ }
+
+ fclose(f);
+ }
+ else
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "can't load file '%s'", file);
+ err++;
+ }
+ }
+ else
+ {
+ snprintf(parser_errstring, ERRSTRING_LEN, "can't allocate memory");
+ err++;
+ }
+
+ if (err)
+ {
+ plog("%s", parser_errstring);
+
+ if (cfg)
+ parser_free_conf(cfg);
+ cfg = NULL;
+ }
+
+ return cfg;
+}
+
+static void
+parser_free_kwlist(kw_list_t *list)
+{
+ kw_list_t *elt;
+
+ while (list)
+ {
+ elt = list;
+ list = list->next;
+ if (elt->value)
+ pfree(elt->value);
+ pfree(elt);
+ }
+}
+
+void
+parser_free_conf(config_parsed_t *cfg)
+{
+ section_list_t *sec;
+ if (cfg)
+ {
+ parser_free_kwlist(cfg->config_setup);
+ while (cfg->conn_first)
+ {
+ sec = cfg->conn_first;
+ cfg->conn_first = cfg->conn_first->next;
+ if (sec->name)
+ pfree(sec->name);
+ parser_free_kwlist(sec->kw);
+ pfree(sec);
+ }
+ while (cfg->ca_first)
+ {
+ sec = cfg->ca_first;
+ cfg->ca_first = cfg->ca_first->next;
+ if (sec->name)
+ pfree(sec->name);
+ parser_free_kwlist(sec->kw);
+ pfree(sec);
+ }
+ pfree(cfg);
+ }
+}
diff --git a/programs/starter/starter.8 b/programs/starter/starter.8
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/programs/starter/starter.8
diff --git a/programs/starter/starter.c b/programs/starter/starter.c
new file mode 100644
index 000000000..0b2c83369
--- /dev/null
+++ b/programs/starter/starter.c
@@ -0,0 +1,571 @@
+/* strongSwan IPsec starter
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: starter.c,v 1.23 2006/02/15 18:37:46 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "confread.h"
+#include "files.h"
+#include "starterwhack.h"
+#include "invokepluto.h"
+#include "klips.h"
+#include "netkey.h"
+#include "cmp.h"
+#include "interfaces.h"
+
+#define FLAG_ACTION_START_PLUTO 0x01
+#define FLAG_ACTION_UPDATE 0x02
+#define FLAG_ACTION_RELOAD 0x04
+#define FLAG_ACTION_QUIT 0x08
+#define FLAG_ACTION_LISTEN 0x10
+
+static unsigned int _action_ = 0;
+
+static void
+fsig(int signal)
+{
+ switch (signal)
+ {
+ case SIGCHLD:
+ {
+ int status;
+ pid_t pid;
+ char *name = NULL;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ {
+ if (pid == starter_pluto_pid())
+ name = " (Pluto)";
+ if (WIFSIGNALED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has been killed by sig %d\n",
+ pid, name?name:"", WTERMSIG(status))
+ )
+ else if (WIFSTOPPED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has been stopped by sig %d\n",
+ pid, name?name:"", WSTOPSIG(status))
+ )
+ else if (WIFEXITED(status))
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has quit (exit code %d)\n",
+ pid, name?name:"", WEXITSTATUS(status))
+ )
+ else
+ DBG(DBG_CONTROL,
+ DBG_log("child %d%s has quit", pid, name?name:"")
+ )
+
+ if (pid == starter_pluto_pid())
+ starter_pluto_sigchild(pid);
+ }
+ }
+ break;
+
+ case SIGPIPE:
+ /** ignore **/
+ break;
+
+ case SIGALRM:
+ _action_ |= FLAG_ACTION_START_PLUTO;
+ break;
+
+ case SIGHUP:
+ _action_ |= FLAG_ACTION_UPDATE;
+ break;
+
+ case SIGTERM:
+ case SIGQUIT:
+ case SIGINT:
+ _action_ |= FLAG_ACTION_QUIT;
+ break;
+
+ case SIGUSR1:
+ _action_ |= FLAG_ACTION_RELOAD;
+ _action_ |= FLAG_ACTION_UPDATE;
+ break;
+
+ default:
+ plog("fsig(): unknown signal %d -- investigate", signal);
+ break;
+ }
+}
+
+static void
+usage(char *name)
+{
+ fprintf(stderr, "Usage: starter [--nofork] [--auto-update <sec>] "
+ "[--debug|--debug-more|--debug-all]\n");
+ exit(1);
+}
+
+int main (int argc, char **argv)
+{
+ starter_config_t *cfg = NULL;
+ starter_config_t *new_cfg;
+ starter_conn_t *conn, *conn2;
+ starter_ca_t *ca, *ca2;
+
+ struct stat stb;
+
+ char *err = NULL;
+ int i;
+ int id = 1;
+ struct timeval tv;
+ unsigned long auto_update = 0;
+ time_t last_reload;
+ bool has_netkey;
+ bool no_fork = FALSE;
+
+ /* global variables defined in log.h */
+ log_to_stderr = TRUE;
+ base_debugging = DBG_NONE;
+
+ /* parse command line */
+ for (i = 1; i < argc; i++)
+ {
+ if (streq(argv[i], "--debug"))
+ {
+ base_debugging |= DBG_CONTROL;
+ }
+ else if (streq(argv[i], "--debug-more"))
+ {
+ base_debugging |= DBG_CONTROLMORE;
+ }
+ else if (streq(argv[i], "--debug-all"))
+ {
+ base_debugging |= DBG_ALL;
+ }
+ else if (streq(argv[i], "--nofork"))
+ {
+ no_fork = TRUE;
+ }
+ else if (streq(argv[i], "--auto-update") && i+1 < argc)
+ {
+ auto_update = atoi(argv[++i]);
+ if (!auto_update)
+ usage(argv[0]);
+ }
+ else
+ {
+ usage(argv[0]);
+ }
+ }
+
+ /* Init */
+ init_log("ipsec_starter");
+ cur_debugging = base_debugging;
+
+ signal(SIGHUP, fsig);
+ signal(SIGCHLD, fsig);
+ signal(SIGPIPE, fsig);
+ signal(SIGINT, fsig);
+ signal(SIGTERM, fsig);
+ signal(SIGQUIT, fsig);
+ signal(SIGALRM, fsig);
+ signal(SIGUSR1, fsig);
+
+ /* verify that we can start */
+ if (getuid() != 0)
+ {
+ plog("permission denied (must be superuser)");
+ exit(1);
+ }
+
+ if (stat(PID_FILE, &stb) == 0)
+ {
+ plog("pluto is already running (%s exists) -- aborting", PID_FILE);
+ exit(1);
+ }
+
+ if (stat(DEV_RANDOM, &stb) != 0)
+ {
+ plog("unable to start strongSwan IPsec -- no %s!", DEV_RANDOM);
+ exit(1);
+ }
+
+ if (stat(DEV_URANDOM, &stb)!= 0)
+ {
+ plog("unable to start strongSwan IPsec -- no %s!", DEV_URANDOM);
+ exit(1);
+ }
+
+ cfg = confread_load(CONFIG_FILE);
+ if (!cfg)
+ {
+ plog("unable to start strongSwan -- errors in config");
+ exit(1);
+ }
+
+ /* determine if we have a native netkey IPsec stack */
+ has_netkey = starter_netkey_init();
+
+ if (!has_netkey)
+ {
+ /* determine if we have a KLIPS IPsec stack instead */
+ if (starter_klips_init())
+ {
+ starter_klips_set_config(cfg);
+ starter_ifaces_init();
+ starter_ifaces_clear();
+ }
+ else
+ {
+ plog("neither netkey nor KLIPS IPSec stack detected");
+ exit(1);
+ }
+ }
+
+ last_reload = time(NULL);
+
+ plog("Starting strongSwan IPsec %s [starter]...", ipsec_version_code());
+
+ /* fork if we're not debugging stuff */
+ if (!no_fork)
+ {
+ log_to_stderr = FALSE;
+
+ switch (fork())
+ {
+ case 0:
+ {
+ int fnull = open("/dev/null", O_RDWR);
+
+ if (fnull >= 0)
+ {
+ dup2(fnull, STDIN_FILENO);
+ dup2(fnull, STDOUT_FILENO);
+ dup2(fnull, STDERR_FILENO);
+ close(fnull);
+ }
+ }
+ break;
+ case -1:
+ plog("can't fork: %s", strerror(errno));
+ break;
+ default:
+ exit(0);
+ }
+ }
+
+ /* save pid file in /var/run/starter.pid */
+ {
+ FILE *fd = fopen(MY_PID_FILE, "w");
+
+ if (fd)
+ {
+ fprintf(fd, "%u\n", getpid());
+ fclose(fd);
+ }
+ }
+
+ if (!has_netkey)
+ {
+ starter_ifaces_load(cfg->setup.interfaces
+ , cfg->setup.overridemtu
+ , cfg->setup.nat_traversal
+ , &cfg->defaultroute);
+ }
+
+ _action_ = FLAG_ACTION_START_PLUTO;
+
+ for (;;)
+ {
+ /*
+ * Stop pluto (if started) and exit
+ */
+ if (_action_ & FLAG_ACTION_QUIT)
+ {
+ if (starter_pluto_pid())
+ starter_stop_pluto();
+ if (has_netkey)
+ starter_netkey_cleanup();
+ else
+ {
+ starter_ifaces_clear();
+ starter_klips_cleanup();
+ }
+ confread_free(cfg);
+ unlink(MY_PID_FILE);
+ unlink(INFO_FILE);
+#ifdef LEAK_DETECTIVE
+ report_leaks();
+#endif /* LEAK_DETECTIVE */
+ close_log();
+ plog("ipsec starter stopped");
+ exit(0);
+ }
+
+ /*
+ * Delete all connections. Will be added below
+ */
+ if (_action_ & FLAG_ACTION_RELOAD)
+ {
+ if (starter_pluto_pid())
+ {
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ {
+ starter_whack_del_conn(conn);
+ conn->state = STATE_TO_ADD;
+ }
+ }
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ {
+ starter_whack_del_ca(ca);
+ ca->state = STATE_TO_ADD;
+ }
+ }
+ }
+ _action_ &= ~FLAG_ACTION_RELOAD;
+ }
+
+ /*
+ * Update configuration
+ */
+ if (_action_ & FLAG_ACTION_UPDATE)
+ {
+ err = NULL;
+ DBG(DBG_CONTROL,
+ DBG_log("Reloading config...")
+ )
+ new_cfg = confread_load(CONFIG_FILE);
+
+ if (new_cfg)
+ {
+ /* Switch to new config. New conn will be loaded below */
+ if (has_netkey)
+ {
+ if (!starter_cmp_defaultroute(&new_cfg->defaultroute
+ , &cfg->defaultroute))
+ {
+ _action_ |= FLAG_ACTION_LISTEN;
+ }
+ }
+ else
+ {
+ if (!starter_cmp_klips(cfg, new_cfg))
+ {
+ plog("KLIPS has changed");
+ starter_klips_set_config(new_cfg);
+ }
+
+ if (starter_ifaces_load(new_cfg->setup.interfaces
+ , new_cfg->setup.overridemtu
+ , new_cfg->setup.nat_traversal
+ , &new_cfg->defaultroute))
+ {
+ _action_ |= FLAG_ACTION_LISTEN;
+ }
+ }
+
+ if (!starter_cmp_pluto(cfg, new_cfg))
+ {
+ plog("Pluto has changed");
+ if (starter_pluto_pid())
+ starter_stop_pluto();
+ _action_ &= ~FLAG_ACTION_LISTEN;
+ _action_ |= FLAG_ACTION_START_PLUTO;
+ }
+ else
+ {
+ /* Only reload conn and ca sections if pluto is not killed */
+
+ /* Look for new connections that are already loaded */
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ {
+ for (conn2 = new_cfg->conn_first; conn2; conn2 = conn2->next)
+ {
+ if (conn2->state == STATE_TO_ADD
+ && starter_cmp_conn(conn, conn2))
+ {
+ conn->state = STATE_REPLACED;
+ conn2->state = STATE_ADDED;
+ conn2->id = conn->id;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Remove conn sections that have become unused */
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ starter_whack_del_conn(conn);
+ }
+
+ /* Look for new ca sections that are already loaded */
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ {
+ for (ca2 = new_cfg->ca_first; ca2; ca2 = ca2->next)
+ {
+ if (ca2->state == STATE_TO_ADD
+ && starter_cmp_ca(ca, ca2))
+ {
+ ca->state = STATE_REPLACED;
+ ca2->state = STATE_ADDED;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Remove ca sections that have become unused */
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ starter_whack_del_ca(ca);
+ }
+ }
+ confread_free(cfg);
+ cfg = new_cfg;
+ }
+ else
+ {
+ plog("can't reload config file: %s -- keeping old one");
+ }
+ _action_ &= ~FLAG_ACTION_UPDATE;
+ last_reload = time(NULL);
+ }
+
+ /*
+ * Start pluto
+ */
+ if (_action_ & FLAG_ACTION_START_PLUTO)
+ {
+ if (starter_pluto_pid() == 0)
+ {
+ DBG(DBG_CONTROL,
+ DBG_log("Attempting to start pluto...")
+ )
+ if (!has_netkey)
+ starter_klips_clear();
+
+ if (starter_start_pluto(cfg, no_fork) == 0)
+ {
+ starter_whack_listen();
+ }
+ else
+ {
+ /* schedule next try */
+ alarm(PLUTO_RESTART_DELAY);
+ }
+ }
+ _action_ &= ~FLAG_ACTION_START_PLUTO;
+
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_ADDED)
+ ca->state = STATE_TO_ADD;
+ }
+
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_ADDED)
+ conn->state = STATE_TO_ADD;
+ }
+ }
+
+ /*
+ * Tell pluto to reread its interfaces
+ */
+ if (_action_ & FLAG_ACTION_LISTEN)
+ {
+ starter_whack_listen();
+ _action_ &= ~FLAG_ACTION_LISTEN;
+ }
+
+ /*
+ * Add stale conn and ca sections
+ */
+ if (starter_pluto_pid() != 0)
+ {
+ for (ca = cfg->ca_first; ca; ca = ca->next)
+ {
+ if (ca->state == STATE_TO_ADD)
+ {
+ starter_whack_add_ca(ca);
+ ca->state = STATE_ADDED;
+ }
+ }
+
+ for (conn = cfg->conn_first; conn; conn = conn->next)
+ {
+ if (conn->state == STATE_TO_ADD)
+ {
+ if (conn->id == 0)
+ {
+ /* affect new unique id */
+ conn->id = id++;
+ }
+ starter_whack_add_conn(conn);
+ conn->state = STATE_ADDED;
+ if (conn->startup == STARTUP_START)
+ starter_whack_initiate_conn(conn);
+ else if (conn->startup == STARTUP_ROUTE)
+ starter_whack_route_conn(conn);
+ }
+ }
+ }
+
+ /*
+ * If auto_update activated, when to stop select
+ */
+ if (auto_update)
+ {
+ time_t now = time(NULL);
+ tv.tv_sec = (now < last_reload + auto_update)
+ ? (last_reload + auto_update-now) : 0;
+ tv.tv_usec = 0;
+ }
+
+ /*
+ * Wait for something to happen
+ */
+ if (select(0, NULL, NULL, NULL, auto_update ? &tv : NULL) == 0)
+ {
+ /* timeout -> auto_update */
+ _action_ |= FLAG_ACTION_UPDATE;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/programs/starter/starterwhack.c b/programs/starter/starterwhack.c
new file mode 100644
index 000000000..a671c560c
--- /dev/null
+++ b/programs/starter/starterwhack.c
@@ -0,0 +1,371 @@
+/* strongSwan whack functions to communicate with pluto (whack.c)
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: starterwhack.c,v 1.17 2006/04/17 10:32:36 as Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <linux/stddef.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+#include "../pluto/whack.h"
+
+#include "starterwhack.h"
+#include "confread.h"
+#include "files.h"
+
+static int
+pack_str (char **p, char **next, char **roof)
+{
+ const char *s = (*p==NULL) ? "" : *p; /* note: NULL becomes ""! */
+ size_t len = strlen(s) + 1;
+
+ if ((*roof - *next) < len)
+ {
+ return 0; /* not enough space */
+ }
+ else
+ {
+ strcpy(*next, s);
+ *next += len;
+ *p = NULL; /* don't send pointers on the wire! */
+ return 1;
+ }
+}
+
+static int
+send_whack_msg (whack_message_t *msg)
+{
+ struct sockaddr_un ctl_addr = { AF_UNIX, CTL_FILE };
+ int sock;
+ ssize_t len;
+ char *str_next, *str_roof;
+
+ /* pack strings */
+ str_next = (char *)msg->string;
+ str_roof = (char *)&msg->string[sizeof(msg->string)];
+
+ if (!pack_str(&msg->name, &str_next, &str_roof)
+ || !pack_str(&msg->left.id, &str_next, &str_roof)
+ || !pack_str(&msg->left.cert, &str_next, &str_roof)
+ || !pack_str(&msg->left.ca, &str_next, &str_roof)
+ || !pack_str(&msg->left.groups, &str_next, &str_roof)
+ || !pack_str(&msg->left.updown, &str_next, &str_roof)
+#ifdef VIRTUAL_IP
+ || !pack_str(&msg->left.virt, &str_next, &str_roof)
+#endif
+ || !pack_str(&msg->right.id, &str_next, &str_roof)
+ || !pack_str(&msg->right.cert, &str_next, &str_roof)
+ || !pack_str(&msg->right.ca, &str_next, &str_roof)
+ || !pack_str(&msg->right.groups, &str_next, &str_roof)
+ || !pack_str(&msg->right.updown, &str_next, &str_roof)
+#ifdef VIRTUAL_IP
+ || !pack_str(&msg->right.virt, &str_next, &str_roof)
+#endif
+ || !pack_str(&msg->keyid, &str_next, &str_roof)
+ || !pack_str(&msg->myid, &str_next, &str_roof)
+ || !pack_str(&msg->cacert, &str_next, &str_roof)
+ || !pack_str(&msg->ldaphost, &str_next, &str_roof)
+ || !pack_str(&msg->ldapbase, &str_next, &str_roof)
+ || !pack_str(&msg->crluri, &str_next, &str_roof)
+ || !pack_str(&msg->crluri2, &str_next, &str_roof)
+ || !pack_str(&msg->ocspuri, &str_next, &str_roof)
+ || !pack_str(&msg->ike, &str_next, &str_roof)
+ || !pack_str(&msg->esp, &str_next, &str_roof)
+ || !pack_str(&msg->sc_data, &str_next, &str_roof)
+ || (str_roof - str_next < msg->keyval.len))
+ {
+ plog("send_wack_msg(): can't pack strings");
+ return -1;
+ }
+ if (msg->keyval.ptr)
+ memcpy(str_next, msg->keyval.ptr, msg->keyval.len);
+ msg->keyval.ptr = NULL;
+ str_next += msg->keyval.len;
+ len = str_next - (char *)msg;
+
+ /* connect to pluto ctl */
+ 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(pluto_ctl) failed: %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /* send message */
+ if (write(sock, msg, len) != len)
+ {
+ plog("write(pluto_ctl) failed: %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ /* TODO: read reply */
+ close(sock);
+ return 0;
+}
+
+static void
+init_whack_msg(whack_message_t *msg)
+{
+ memset(msg, 0, sizeof(whack_message_t));
+ msg->magic = WHACK_MAGIC;
+}
+
+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;
+}
+
+static void
+set_whack_end(whack_end_t *w, starter_end_t *end)
+{
+ w->id = end->id;
+ w->cert = end->cert;
+ w->ca = end->ca;
+ w->groups = end->groups;
+ w->host_addr = end->addr;
+ w->host_nexthop = end->nexthop;
+ w->host_srcip = end->srcip;
+
+ if (end->has_client)
+ w->client = end->subnet;
+ else
+ w->client.addr.u.v4.sin_family = AF_INET;
+
+ w->has_client = end->has_client;
+ w->has_client_wildcard = end->has_client_wildcard;
+ w->has_port_wildcard = end->has_port_wildcard;
+ w->has_srcip = end->has_srcip;
+ w->modecfg = end->modecfg;
+ w->hostaccess = end->hostaccess;
+ w->sendcert = end->sendcert;
+ w->updown = end->updown;
+ w->host_port = IKE_UDP_PORT;
+ w->port = end->port;
+ w->protocol = end->protocol;
+ w->virt = end->virt;
+
+ if (w->port != 0)
+ {
+ int port = htons(w->port);
+
+ setportof(port, &w->host_addr);
+ setportof(port, &w->client.addr);
+ }
+}
+
+static int
+starter_whack_add_pubkey (starter_conn_t *conn, starter_end_t *end
+, const char *lr)
+{
+ const char *err;
+ static char keyspace[1024 + 4];
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+
+ msg.whack_key = TRUE;
+ msg.pubkey_alg = PUBKEY_ALG_RSA;
+ if (end->id && end->rsakey)
+ {
+ /* special values to ignore */
+ if (streq(end->rsakey, "")
+ || streq(end->rsakey, "%none")
+ || streq(end->rsakey, "%cert")
+ || streq(end->rsakey, "0x00"))
+ {
+ return 0;
+ }
+ msg.keyid = end->id;
+ err = atobytes(end->rsakey, 0, keyspace, sizeof(keyspace), &msg.keyval.len);
+ if (err)
+ {
+ plog("conn %s/%s: rsakey malformed [%s]", connection_name(conn), lr, err);
+ return 1;
+ }
+ else
+ {
+ msg.keyval.ptr = keyspace;
+ return send_whack_msg(&msg);
+ }
+ }
+ return 0;
+}
+
+int
+starter_whack_add_conn(starter_conn_t *conn)
+{
+ whack_message_t msg;
+ int r;
+
+ init_whack_msg(&msg);
+
+ msg.whack_connection = TRUE;
+ msg.name = connection_name(conn);
+
+ msg.addr_family = conn->addr_family;
+ msg.tunnel_addr_family = conn->tunnel_addr_family;
+ msg.sa_ike_life_seconds = conn->sa_ike_life_seconds;
+ msg.sa_ipsec_life_seconds = conn->sa_ipsec_life_seconds;
+ msg.sa_rekey_margin = conn->sa_rekey_margin;
+ msg.sa_rekey_fuzz = conn->sa_rekey_fuzz;
+ msg.sa_keying_tries = conn->sa_keying_tries;
+ msg.policy = conn->policy;
+
+ set_whack_end(&msg.left, &conn->left);
+ set_whack_end(&msg.right, &conn->right);
+
+ msg.esp = conn->esp;
+ msg.ike = conn->ike;
+ msg.pfsgroup = conn->pfsgroup;
+
+ /* taken from pluto/whack.c */
+ if (msg.pfsgroup)
+ {
+ char esp_buf[256];
+
+ snprintf(esp_buf, sizeof (esp_buf), "%s;%s"
+ , msg.esp ? msg.esp : ""
+ , msg.pfsgroup ? msg.pfsgroup : "");
+ msg.esp = esp_buf;
+
+ DBG(DBG_CONTROL,
+ DBG_log("Setting --esp=%s", msg.esp)
+ )
+ }
+ msg.dpd_delay = conn->dpd_delay;
+ msg.dpd_timeout = conn->dpd_timeout;
+ msg.dpd_action = conn->dpd_action;
+/* msg.dpd_count = conn->dpd_count; not supported yet by strongSwan */
+
+ r = send_whack_msg(&msg);
+
+ if (r == 0 && (conn->policy & POLICY_RSASIG))
+ {
+ r += starter_whack_add_pubkey (conn, &conn->left, "left");
+ r += starter_whack_add_pubkey (conn, &conn->right, "right");
+ }
+
+ return r;
+}
+
+int
+starter_whack_del_conn(starter_conn_t *conn)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+ msg.whack_delete = TRUE;
+ msg.name = connection_name(conn);
+ return send_whack_msg(&msg);
+}
+
+int
+starter_whack_route_conn(starter_conn_t *conn)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+ msg.whack_route = TRUE;
+ msg.name = connection_name(conn);
+ return send_whack_msg(&msg);
+}
+
+int
+starter_whack_initiate_conn(starter_conn_t *conn)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+ msg.whack_initiate = TRUE;
+ msg.whack_async = TRUE;
+ msg.name = connection_name(conn);
+ return send_whack_msg(&msg);
+}
+
+int
+starter_whack_listen(void)
+{
+ whack_message_t msg;
+ init_whack_msg(&msg);
+ msg.whack_listen = TRUE;
+ return send_whack_msg(&msg);
+}
+
+int starter_whack_shutdown(void)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+ msg.whack_shutdown = TRUE;
+ return send_whack_msg(&msg);
+}
+
+int
+starter_whack_add_ca(starter_ca_t *ca)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+
+ msg.whack_ca = TRUE;
+ msg.name = ca->name;
+ msg.cacert = ca->cacert;
+ msg.ldaphost = ca->ldaphost;
+ msg.ldapbase = ca->ldapbase;
+ msg.crluri = ca->crluri;
+ msg.crluri2 = ca->crluri2;
+ msg.ocspuri = ca->ocspuri;
+ msg.whack_strict = ca->strict;
+
+ return send_whack_msg(&msg);
+}
+
+int
+starter_whack_del_ca(starter_ca_t *ca)
+{
+ whack_message_t msg;
+
+ init_whack_msg(&msg);
+
+ msg.whack_delete = TRUE;
+ msg.whack_ca = TRUE;
+ msg.name = ca->name;
+
+ return send_whack_msg(&msg);
+}
diff --git a/programs/starter/starterwhack.h b/programs/starter/starterwhack.h
new file mode 100644
index 000000000..2e79c0715
--- /dev/null
+++ b/programs/starter/starterwhack.h
@@ -0,0 +1,32 @@
+/* FreeS/WAN whack functions to communicate with pluto (whack.h)
+ * Copyright (C) 2001-2002 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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: starterwhack.h,v 1.6 2006/01/03 18:37:03 as Exp $
+ */
+
+#ifndef _STARTER_WHACK_H_
+#define _STARTER_WHACK_H_
+
+#include "confread.h"
+
+extern int starter_whack_add_conn(starter_conn_t *conn);
+extern int starter_whack_del_conn(starter_conn_t *conn);
+extern int starter_whack_route_conn(starter_conn_t *conn);
+extern int starter_whack_initiate_conn(starter_conn_t *conn);
+extern int starter_whack_listen(void);
+extern int starter_whack_shutdown(void);
+extern int starter_whack_add_ca(starter_ca_t *ca);
+extern int starter_whack_del_ca(starter_ca_t *ca);
+
+#endif /* _STARTER_WHACK_H_ */
+
diff --git a/programs/tncfg/.cvsignore b/programs/tncfg/.cvsignore
new file mode 100644
index 000000000..c05ca8d9a
--- /dev/null
+++ b/programs/tncfg/.cvsignore
@@ -0,0 +1 @@
+tncfg
diff --git a/programs/tncfg/Makefile b/programs/tncfg/Makefile
new file mode 100644
index 000000000..ded364dbf
--- /dev/null
+++ b/programs/tncfg/Makefile
@@ -0,0 +1,52 @@
+# 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 <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT 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:31 as Exp $
+
+FREESWANSRCDIR=../..
+include ${FREESWANSRCDIR}/Makefile.inc
+
+PROGRAM:=tncfg
+EXTRA5PROC=${PROGRAM}.5
+
+LIBS:=${FREESWANLIB}
+
+include ../Makefile.program
+
+#
+# $Log: Makefile,v $
+# Revision 1.1 2004/03/15 20:35:31 as
+# added files from freeswan-2.04-x509-1.5.3
+#
+# Revision 1.4 2002/06/03 20:25:31 mcr
+# man page for files actually existant in /proc/net changed back to
+# ipsec_foo via new EXTRA5PROC process.
+#
+# Revision 1.3 2002/06/02 21:51:41 mcr
+# changed TOPDIR->FREESWANSRCDIR in all Makefiles.
+# (note that linux/net/ipsec/Makefile uses TOPDIR because this is the
+# kernel sense.)
+#
+# Revision 1.2 2002/04/26 01:21:26 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.
+#
+# Revision 1.1 2002/04/24 07:55:32 mcr
+# #include patches and Makefiles for post-reorg compilation.
+#
+#
+#
diff --git a/programs/tncfg/tncfg.5 b/programs/tncfg/tncfg.5
new file mode 100644
index 000000000..e4de862c6
--- /dev/null
+++ b/programs/tncfg/tncfg.5
@@ -0,0 +1,109 @@
+.TH IPSEC_TNCFG 5 "27 Jun 2000"
+.\"
+.\" RCSID $Id: tncfg.5,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec_tncfg \- lists IPSEC virtual interfaces attached to real interfaces
+.SH SYNOPSIS
+.B ipsec
+.B tncfg
+.PP
+.B cat
+.B /proc/net/ipsec_tncfg
+.SH DESCRIPTION
+.I /proc/net/ipsec_tncfg
+is a read-only file which lists which IPSEC virtual interfaces are
+attached to which real interfaces, through which packets will be
+forwarded once processed by IPSEC.
+.PP
+Each line lists one ipsec I/F.
+A table entry consists of:
+.IP + 3
+an ipsec virtual I/F name
+.IP +
+a visual and machine parsable separator '->', separating the virtual I/F
+and the physical I/F,
+.IP +
+a physical I/F name, to which the ipsec virtual I/F is attached or NULL
+if it is not attached,
+.IP +
+the keyword
+.BR mtu= ,
+.IP +
+the MTU of the ipsec virtual I/F,
+.IP +
+the automatically adjusted effective MTU for PMTU discovery, in brackets,
+.IP +
+a visual and machine parsable separator '->', separating the virtual I/F
+MTU and the physical I/F MTU,
+.IP +
+the MTU of the attached physical I/F.
+.BR
+.SH EXAMPLES
+.TP
+.B ipsec2 -> eth3 mtu=16260(1443) -> 1500
+.LP
+shows that virtual device
+.B ipsec2
+with an MTU of
+.B 16260
+is connected to physical device
+.B eth3
+with an MTU of
+.B 1500
+and that the effective MTU as a result of PMTU discovery has been
+automatically set to
+.BR 1443.
+.TP
+.B ipsec0 \-> wvlan0 mtu=1400(16260) \-> 1500
+.LP
+shows that virtual device
+.B ipsec0
+with an MTU of
+.B 1400
+is connected to physical device
+.B wvlan0
+with an MTU of
+.B 1500
+and no PMTU packets have gotten far enough to bump down the effective MTU
+from its default of 16260.
+.TP
+.B ipsec3 \-> NULL mtu=0(0) \-> 0
+.LP
+shows that virtual device
+.B ipsec3
+is not connected to any physical device.
+.LP
+.SH "FILES"
+/proc/net/ipsec_tncfg, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_eroute(5), ipsec_spi(5),
+ipsec_spigrp(5), ipsec_klipsdebug(5), ipsec_tncfg(8), ipsec_version(5),
+ipsec_pf_key(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: tncfg.5,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.5 2002/04/24 07:35:41 mcr
+.\" Moved from ./klips/utils/tncfg.5,v
+.\"
+.\" Revision 1.4 2001/05/29 05:15:53 rgb
+.\" Added PMTU to output format.
+.\"
+.\" Revision 1.3 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.2 2000/06/28 12:44:12 henry
+.\" format touchup
+.\"
+.\" Revision 1.1 2000/06/28 05:43:01 rgb
+.\" Added manpages for all 5 klips utils.
+.\"
+.\"
diff --git a/programs/tncfg/tncfg.8 b/programs/tncfg/tncfg.8
new file mode 100644
index 000000000..f888f2539
--- /dev/null
+++ b/programs/tncfg/tncfg.8
@@ -0,0 +1,113 @@
+.TH IPSEC_TNCFG 8 "21 Jun 2000"
+.\"
+.\" RCSID $Id: tncfg.8,v 1.1 2004/03/15 20:35:31 as Exp $
+.\"
+.SH NAME
+ipsec tncfg \- associate IPSEC virtual interface with physical interface
+.SH SYNOPSIS
+.B ipsec
+.B tncfg
+.PP
+.B ipsec
+.B tncfg
+.B \-\-attach
+.B \-\-virtual
+virtual
+.B \-\-physical
+physical
+.PP
+.B ipsec
+.B tncfg
+.B \-\-detach
+.B \-\-virtual
+virtual
+.PP
+.B ipsec
+.B tncfg
+.B \-\-clear
+.PP
+.B ipsec
+.B tncfg
+.B \-\-version
+.PP
+.B ipsec
+.B tncfg
+.B \-\-help
+.SH DESCRIPTION
+.I Tncfg
+attaches/detaches IPSEC virtual interfaces to/from
+physical interfaces,
+through which packets will be forwarded once processed by IPSEC.
+.PP
+The form with no additional arguments lists the contents of
+/proc/net/ipsec_tncfg. The format of /proc/net/ipsec_tncfg is discussed
+in ipsec_tncfg(5).
+The
+.B \-\-attach
+form attaches the
+.I virtual
+interface to the
+.I physical
+one.
+The
+.B \-\-detach
+form detaches the
+.I virtual
+interface from whichever physical interface it is attached to.
+The
+.B \-\-clear
+form clears all the
+.I virtual
+interfaces from whichever physical interfaces they were attached to.
+.PP
+Virtual interfaces typically have names like
+.BR ipsec0 ,
+while physical interfaces typically have names like
+.B eth0
+or
+.BR ppp0 .
+.SH EXAMPLES
+.TP
+.B ipsec tncfg \-\-attach \-\-virtual ipsec0 \-\-physical eth0
+attaches the
+.B ipsec0
+virtual device to the
+.B eth0
+physical device.
+.LP
+.SH "FILES"
+/proc/net/ipsec_tncfg, /usr/local/bin/ipsec
+.SH "SEE ALSO"
+ipsec(8), ipsec_manual(8), ipsec_eroute(8), ipsec_spi(8),
+ipsec_spigrp(8), ipsec_klipsdebug(8), ipsec_tncfg(5)
+.SH HISTORY
+Written for the Linux FreeS/WAN project
+<http://www.freeswan.org/>
+by Richard Guy Briggs.
+.\"
+.\" $Log: tncfg.8,v $
+.\" Revision 1.1 2004/03/15 20:35:31 as
+.\" added files from freeswan-2.04-x509-1.5.3
+.\"
+.\" Revision 1.15 2002/04/24 07:35:41 mcr
+.\" Moved from ./klips/utils/tncfg.8,v
+.\"
+.\" Revision 1.14 2000/09/12 13:09:04 rgb
+.\" Fixed real/physical discrepancy between tncfg.8 and tncfg.c.
+.\"
+.\" Revision 1.13 2000/06/30 18:21:55 rgb
+.\" Update SEE ALSO sections to include ipsec_version(5) and ipsec_pf_key(5)
+.\" and correct FILES sections to no longer refer to /dev/ipsec which has
+.\" been removed since PF_KEY does not use it.
+.\"
+.\" Revision 1.12 2000/06/21 16:54:58 rgb
+.\" Added 'no additional args' text for listing contents of
+.\" /proc/net/ipsec_* files.
+.\"
+.\" Revision 1.11 1999/07/19 18:47:25 henry
+.\" fix slightly-misformed comments
+.\"
+.\" Revision 1.10 1999/04/06 04:54:39 rgb
+.\" Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+.\" patch shell fixes.
+.\"
diff --git a/programs/tncfg/tncfg.c b/programs/tncfg/tncfg.c
new file mode 100644
index 000000000..f6aeae0e2
--- /dev/null
+++ b/programs/tncfg/tncfg.c
@@ -0,0 +1,393 @@
+/*
+ * IPSEC interface configuration
+ * Copyright (C) 1996 John Ioannidis.
+ * Copyright (C) 1998, 1999, 2000, 2001 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+char tncfg_c_version[] = "RCSID $Id: tncfg.c,v 1.1 2004/03/15 20:35:31 as Exp $";
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h> /* system(), strtoul() */
+#include <unistd.h> /* getuid() */
+#include <linux/types.h>
+#include <sys/ioctl.h> /* ioctl() */
+
+#include <freeswan.h>
+#ifdef NET_21 /* from freeswan.h */
+#include <linux/sockios.h>
+#include <sys/socket.h>
+#endif /* NET_21 */ /* from freeswan.h */
+
+#if 0
+#include <linux/if.h>
+#else
+#include <net/if.h>
+#endif
+#include <sys/types.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "freeswan/ipsec_tunnel.h"
+
+static void
+usage(char *name)
+{
+ fprintf(stdout,"%s --attach --virtual <virtual-device> --physical <physical-device>\n",
+ name);
+ fprintf(stdout,"%s --detach --virtual <virtual-device>\n",
+ name);
+ fprintf(stdout,"%s --clear\n",
+ name);
+ fprintf(stdout,"%s --help\n",
+ name);
+ fprintf(stdout,"%s --version\n",
+ name);
+ fprintf(stdout,"%s\n",
+ name);
+ fprintf(stdout, " [ --debug ] is optional to any %s command.\n", name);
+ fprintf(stdout, " [ --label <label> ] is optional to any %s command.\n", name);
+ exit(1);
+}
+
+static struct option const longopts[] =
+{
+ {"virtual", 1, 0, 'V'},
+ {"physical", 1, 0, 'P'},
+ {"attach", 0, 0, 'a'},
+ {"detach", 0, 0, 'd'},
+ {"clear", 0, 0, 'c'},
+ {"help", 0, 0, 'h'},
+ {"version", 0, 0, 'v'},
+ {"label", 1, 0, 'l'},
+ {"optionsfrom", 1, 0, '+'},
+ {"debug", 0, 0, 'g'},
+ {0, 0, 0, 0}
+};
+
+int
+main(int argc, char *argv[])
+{
+ struct ifreq ifr;
+ struct ipsectunnelconf *shc=(struct ipsectunnelconf *)&ifr.ifr_data;
+ int s;
+ int c, previous = -1;
+ char *program_name;
+ int debug = 0;
+ int argcount = argc;
+
+ memset(&ifr, 0, sizeof(ifr));
+ program_name = argv[0];
+
+ while((c = getopt_long_only(argc, argv, ""/*"adchvV:P:l:+:"*/, longopts, 0)) != EOF) {
+ switch(c) {
+ case 'g':
+ debug = 1;
+ argcount--;
+ break;
+ case 'a':
+ if(shc->cf_cmd) {
+ fprintf(stderr, "%s: exactly one of '--attach', '--detach' or '--clear' options must be specified.\n", program_name);
+ exit(1);
+ }
+ shc->cf_cmd = IPSEC_SET_DEV;
+ break;
+ case 'd':
+ if(shc->cf_cmd) {
+ fprintf(stderr, "%s: exactly one of '--attach', '--detach' or '--clear' options must be specified.\n", program_name);
+ exit(1);
+ }
+ shc->cf_cmd = IPSEC_DEL_DEV;
+ break;
+ case 'c':
+ if(shc->cf_cmd) {
+ fprintf(stderr, "%s: exactly one of '--attach', '--detach' or '--clear' options must be specified.\n", program_name);
+ exit(1);
+ }
+ shc->cf_cmd = IPSEC_CLR_DEV;
+ break;
+ case 'h':
+ usage(program_name);
+ break;
+ case 'v':
+ if(optarg) {
+ fprintf(stderr, "%s: warning; '-v' and '--version' options don't expect arguments, arg '%s' found, perhaps unintended.\n",
+ program_name, optarg);
+ }
+ fprintf(stdout, "%s, %s\n", program_name, tncfg_c_version);
+ exit(1);
+ break;
+ case 'V':
+ strcpy(ifr.ifr_name, optarg);
+ break;
+ case 'P':
+ strcpy(shc->cf_name, optarg);
+ break;
+ case 'l':
+ program_name = malloc(strlen(argv[0])
+ + 10 /* update this when changing the sprintf() */
+ + strlen(optarg));
+ sprintf(program_name, "%s --label %s",
+ argv[0],
+ optarg);
+ argcount -= 2;
+ break;
+ case '+': /* optionsfrom */
+ optionsfrom(optarg, &argc, &argv, optind, stderr);
+ /* no return on error */
+ break;
+ default:
+ usage(program_name);
+ break;
+ }
+ previous = c;
+ }
+
+ if(argcount == 1) {
+ system("cat /proc/net/ipsec_tncfg");
+ exit(0);
+ }
+
+ switch(shc->cf_cmd) {
+ case IPSEC_SET_DEV:
+ if(!shc->cf_name) {
+ fprintf(stderr, "%s: physical I/F parameter missing.\n",
+ program_name);
+ exit(1);
+ }
+ case IPSEC_DEL_DEV:
+ if(!ifr.ifr_name) {
+ fprintf(stderr, "%s: virtual I/F parameter missing.\n",
+ program_name);
+ exit(1);
+ }
+ break;
+ case IPSEC_CLR_DEV:
+ strcpy(ifr.ifr_name, "ipsec0");
+ break;
+ default:
+ fprintf(stderr, "%s: exactly one of '--attach', '--detach' or '--clear' options must be specified.\n"
+ "Try %s --help' for usage information.\n",
+ program_name, program_name);
+ exit(1);
+ }
+
+ s=socket(AF_INET, SOCK_DGRAM,0);
+ if(s==-1)
+ {
+ fprintf(stderr, "%s: Socket creation failed -- ", program_name);
+ switch(errno)
+ {
+ case EACCES:
+ if(getuid()==0)
+ fprintf(stderr, "Root denied permission!?!\n");
+ else
+ fprintf(stderr, "Run as root user.\n");
+ break;
+ case EPROTONOSUPPORT:
+ fprintf(stderr, "Internet Protocol not enabled");
+ break;
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ fprintf(stderr, "Insufficient system resources.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "No such device. Is the virtual device valid? Is the ipsec module linked into the kernel or loaded as a module?\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket error %d.\n", errno);
+ }
+ exit(1);
+ }
+ if(ioctl(s, shc->cf_cmd, &ifr)==-1)
+ {
+ if(shc->cf_cmd == IPSEC_SET_DEV) {
+ fprintf(stderr, "%s: Socket ioctl failed on attach -- ", program_name);
+ switch(errno)
+ {
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "No such device. Is the virtual device valid? Is the ipsec module linked into the kernel or loaded as a module?\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "No such device. Is the physical device valid?\n");
+ break;
+ case EBUSY:
+ fprintf(stderr, "Device busy. Virtual device %s is already attached to a physical device -- Use detach first.\n",
+ ifr.ifr_name);
+ break;
+ default:
+ fprintf(stderr, "Unknown socket error %d.\n", errno);
+ }
+ exit(1);
+ }
+ if(shc->cf_cmd == IPSEC_DEL_DEV) {
+ fprintf(stderr, "%s: Socket ioctl failed on detach -- ", program_name);
+ switch(errno)
+ {
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "No such device. Is the virtual device valid? The ipsec module may not be linked into the kernel or loaded as a module.\n");
+ break;
+ case ENXIO:
+ fprintf(stderr, "Device requested is not linked to any physical device.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket error %d.\n", errno);
+ }
+ exit(1);
+ }
+ if(shc->cf_cmd == IPSEC_CLR_DEV) {
+ fprintf(stderr, "%s: Socket ioctl failed on clear -- ", program_name);
+ switch(errno)
+ {
+ case EINVAL:
+ fprintf(stderr, "Invalid argument, check kernel log messages for specifics.\n");
+ break;
+ case ENODEV:
+ fprintf(stderr, "Failed. Is the ipsec module linked into the kernel or loaded as a module?.\n");
+ break;
+ default:
+ fprintf(stderr, "Unknown socket error %d.\n", errno);
+ }
+ exit(1);
+ }
+ }
+ exit(0);
+}
+
+/*
+ * $Log: tncfg.c,v $
+ * Revision 1.1 2004/03/15 20:35:31 as
+ * added files from freeswan-2.04-x509-1.5.3
+ *
+ * Revision 1.30 2002/04/24 07:55:32 mcr
+ * #include patches and Makefiles for post-reorg compilation.
+ *
+ * Revision 1.29 2002/04/24 07:35:41 mcr
+ * Moved from ./klips/utils/tncfg.c,v
+ *
+ * Revision 1.28 2002/03/08 21:44:05 rgb
+ * Update for all GNU-compliant --version strings.
+ *
+ * Revision 1.27 2001/06/14 19:35:15 rgb
+ * Update copyright date.
+ *
+ * Revision 1.26 2001/05/21 02:02:55 rgb
+ * Eliminate 1-letter options.
+ *
+ * Revision 1.25 2001/05/16 05:07:20 rgb
+ * Fixed --label option in KLIPS manual utils to add the label to the
+ * command name rather than replace it in error text.
+ * Fix 'print table' non-option in KLIPS manual utils to deal with --label
+ * and --debug options.
+ *
+ * Revision 1.24 2000/09/12 13:09:05 rgb
+ * Fixed real/physical discrepancy between tncfg.8 and tncfg.c.
+ *
+ * Revision 1.23 2000/08/27 01:48:30 rgb
+ * Update copyright.
+ *
+ * Revision 1.22 2000/07/26 03:41:46 rgb
+ * Changed all printf's to fprintf's. Fixed tncfg's usage to stderr.
+ *
+ * Revision 1.21 2000/06/21 16:51:27 rgb
+ * Added no additional argument option to usage text.
+ *
+ * Revision 1.20 2000/01/21 06:26:31 rgb
+ * Added --debug switch to command line.
+ *
+ * Revision 1.19 1999/12/08 20:32:41 rgb
+ * Cleaned out unused cruft.
+ * Changed include file, limiting scope, to avoid conflicts in 2.0.xx
+ * kernels.
+ *
+ * Revision 1.18 1999/12/07 18:27:10 rgb
+ * Added headers to silence fussy compilers.
+ * Converted local functions to static to limit scope.
+ *
+ * Revision 1.17 1999/11/18 04:09:21 rgb
+ * Replaced all kernel version macros to shorter, readable form.
+ *
+ * Revision 1.16 1999/05/25 01:45:36 rgb
+ * Fix version macros for 2.0.x as a module.
+ *
+ * Revision 1.15 1999/05/05 22:02:34 rgb
+ * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
+ *
+ * Revision 1.14 1999/04/15 15:37:28 rgb
+ * Forward check changes from POST1_00 branch.
+ *
+ * Revision 1.10.6.2 1999/04/13 20:58:10 rgb
+ * Add argc==1 --> /proc/net/ipsec_*.
+ *
+ * Revision 1.10.6.1 1999/03/30 17:01:36 rgb
+ * Make main() return type explicit.
+ *
+ * Revision 1.13 1999/04/11 00:12:09 henry
+ * GPL boilerplate
+ *
+ * Revision 1.12 1999/04/06 04:54:39 rgb
+ * Fix/Add RCSID Id: and Log: bits to make PHMDs happy. This includes
+ * patch shell fixes.
+ *
+ * Revision 1.11 1999/03/17 15:40:54 rgb
+ * Make explicit main() return type of int.
+ *
+ * Revision 1.10 1998/11/12 21:08:04 rgb
+ * Add --label option to identify caller from scripts.
+ *
+ * Revision 1.9 1998/10/09 18:47:30 rgb
+ * Add 'optionfrom' to get more options from a named file.
+ *
+ * Revision 1.8 1998/10/09 04:36:55 rgb
+ * Changed help output from stderr to stdout.
+ * Deleted old commented out cruft.
+ *
+ * Revision 1.7 1998/08/28 03:15:14 rgb
+ * Add some manual long options to the usage text.
+ *
+ * Revision 1.6 1998/08/05 22:29:00 rgb
+ * Change includes to accomodate RH5.x.
+ * Force long option names.
+ * Add ENXIO error return code to narrow down error reporting.
+ *
+ * Revision 1.5 1998/07/29 21:45:28 rgb
+ * Convert to long option names.
+ *
+ * Revision 1.4 1998/07/09 18:14:11 rgb
+ * Added error checking to IP's and keys.
+ * Made most error messages more specific rather than spamming usage text.
+ * Added more descriptive kernel error return codes and messages.
+ * Converted all spi translations to unsigned.
+ * Removed all invocations of perror.
+ *
+ * Revision 1.3 1998/05/27 18:48:20 rgb
+ * Adding --help and --version directives.
+ *
+ * Revision 1.2 1998/04/23 21:11:39 rgb
+ * Fixed 0 argument usage case to prevent sigsegv.
+ *
+ * Revision 1.1.1.1 1998/04/08 05:35:09 henry
+ * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
+ *
+ * Revision 0.5 1997/06/03 04:31:55 ji
+ * New file.
+ *
+ */