diff -ruN freeswan-1.5.orig/doc/pkix/README.certificates freeswan-1.5/doc/pkix/README.certificates --- freeswan-1.5.orig/doc/pkix/README.certificates Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/doc/pkix/README.certificates Thu Oct 5 07:05:59 2000 @@ -0,0 +1,452 @@ + Readme for certificate setup under FreeS/WAN + + Neil Dunbar + 5th November, 1999 + +The certificate patches are (almost) always against the most recent +snapshots available from +ftp://ftp.xs4all.nl/~freeswan/snapshot.tar.gz. The patches are +available from ftp://hplose.hpl.hp.com/pub/nd/pluto-openssl.tar.gz. + +The patches count on your having OpenSSL-0.9.3 or above installed on +your system. Check out http://www.openssl.org for details on how to +get OpenSSL. NB: the patches will *not* work on OpenSSL-0.9.2 or +below. + +1. Applying the patches + +To apply the patches, cd to the build directory for the snapshot, +which is generally freeswan-snap1999MonthDayb, and untar the +pluto-openssl.tar.gz. + +Then type 'patch -p0 < pluto.diff', followed by 'patch -p0 < +utils.diff'. + +2. Building and Installing FreeS/WAN + +Go to the pluto directory in the snapshot source directory, and edit +the Makefile. Find the line OPENSSLROOT=.... and change the value of +that variable to be the location of where you have installed the +includes and libraries for OpenSSL. By default this is set to +/usr/local/ssl. + +After that, build the system as per the FreeS/WAN installation notes. + +Installation, similarly, is no different to the normal method. + +3. Configuring FreeS/WAN + +In order to allow Pluto to use digital signatures, rather than +preshared secrets to generate key material for IPsec, there are +several options within the connection section of the file +/etc/ipsec.conf which need to be set. + +For examples, we assume the host to host connection as follows + + west === westhop ...... easthop ==== east + +3.1 certfile + +The certfile setting should be set to the full path name of the file +which contains the certificate whose private key will be used to +perform the digital signatures. This file must be set in order for +pluto to send the certificate to the other side, if requested. The +certificate can be in PEM or DER format. + +Example : /etc/ipsec/west.certificate.pem + +3.2 keyfile + +This setting should be set to the full path name to the file which +contains the key which will be used to perform the digital +signature. + +The key file can be stored in several formats, but at the moment, only +unencrypted private keys can be used. If you use an encrypted one, the +key load will fail, and signature/public key modes will cease to be +available. + +The key files can be stored in DER or PEM format, using the OpenSSL +private key format, which is specific to OpenSSL. Alternatively, the +key can be stored in PKCS-8 format, which is system independent. The +problem is that, for the moment, the OpenSSL key generation +applications only store keys in OpenSSL format. To convert a key file +into PKCS-8 form, use the following command (assuming that the file +key.pem stores the OpenSSL private key in PEM format). + +openssl -nocrypt -topk8 -in key.pem -inform PEM -outform DER \ + -out key.pk8 + +(If the original key is in DER format, change '-inform PEM' to +'-inform DER'. Similarly, if you want a PEM PKCS-8 file, change +'-outform DER' to '-outform PEM). + +The key can be either an RSA or DSA key. Note that if an RSA key is +loaded, DSS signature modes will not be offered (or accepted) from the +other side. Similarly, a DSA key will prevent RSA modes from being +selected. + +Example : /etc/ipsec/west.key.pk8 + +3.3 peerfile + +This option is obsolete. It used to specify the path to certificates +used to start public key authentication mode. This function has been +subsumed by the certpath option. + +Do not use peerfile any more. + +3.4 certpath + +This gives a comma separated list of maps (see README.xmap) which can +be used to look up certificates and/or CRLs by specifying their +subject names, issuer names and such like. + +At the moment, the XMAP types are file, dir, db, ldap. + +Example : ldap:/etc/ldap.conf:ldap_ipsec,dir:/etc/ipsec + +(The first item specifies an LDAP directory lookup, whose details are +specified in the file /etc/ldap.conf, and the identifier of which LDAP +lookup to use within that file is 'ldap_ipsec'. The second specifies a +local directory called /etc/ipsec, which should have files and +symbolic links which index the relevant certificates in the +directory). + +For the format of ldap files, and certificate directories, read the +file README.xmap. + +3.5 certopts + +This setting is a comma separated list which gives a set of switches +which control the behaviour of the public key encryption/signatures +within pluto. + +To set an option in the list, one simply includes its name. + +To clear an option in the list, one includes its name, with a '!' +symbol before the name. + +Example : !send,!pkcs7,!pk,strict,dss-sha,dss-alt + +The example means: turn ON the options 'strict', 'dss-sha' and +'dss-alt', but turn OFF the options 'send', 'pkcs7' and 'pk'. + +The options are as follows + +3.5.1 'send' + +Set this option if you want your side of the connection to send its +certificate to the other side as part of the main mode +negotiations. This certificate must be in the set of certificates +which the other side is expecting on this connection. + +3.5.2 'pkcs7' + +***NOT IMPLEMENTED YET*** + +When sending a certificate, if this option is set, a PKCS7 encoding +for the certificate will be chosen. If not set, a standard DER +encoding of the X.509 certificate will be used. + +3.5.3 'pk' + +When choosing between digital signature and public key encryption +ISAKMP methods, this option forces pluto to select public key +encryption. If not set, pluto will choose digital signature methods. + +Note that this option used to be called 'rsa', but this name makes +little sense, since both RSA and El Gamal encryption methods are +selected by this option. + +3.5.3 'rev' + +If using public key encryption for Phase 1 negotiations, this option +makes pluto prefer to use the revised mode method, rather than the +standard public key mode. Revised public key mode uses fewer public +key encryptions and transmits fewer bytes in the protocol (since +public key ciphertexts are much larger than their symmetric key +counterparts). Probably not a bad idea to use this one all the time if +you want ID protection. + +3.5.4 'strict' + +This option forces verification to be very strict on the acceptability +of certificates from the peer. If 'strict' mode is on, the following +conditions must apply to the peer's certificate -- + +1. For every signing certificate in the chain up to the root CA + certificate a valid CRL *must* be present. If a CRL is not + available, or the CRL is not valid (badly signed, expired, etc), + the verification of the certificate will fail. + +2. The name on the certificate must bear some relation to the name of + the peer, as given in the ISAKMP ID field. Assuming that the name + of the peer is an IPv4 address, which is the only supported one, + then the Common Name on the subject of the certificate must start + with the fully qualified domain name of the peer. Failing that, the + subjectAltName extension must contain an entry of type IP, which is + equal to the IP address given as the peer's name, or it must + contain an entry of type DNS, which must be equal to the name of + the resoved FQDN of the IP address given as the peer's name. If any + of these conditions are met, the certificate will be accepted. If + none of these conditions succeed, the verification will fail. + +If strict mode is not set, and these conditions fail, then debugging +warnings will be logged, but verification will succeed. Be warned that +this makes the setup less secure than would strict mode. + +3.5.5 'dss-sha' + +RFC2409 stipulates that the DSS signature method requires the output +of a SHA1 hash to sign and send to the other side. It does not say +whether this hash is a keyed one (eg HMAC) or unkeyed (ie plain SHA1). + +A subsequent document, draft-ietf-ipsec-ike-01.txt, given a formula +for the plain SHA1 output. However, as Tero Kivinen of SSH has pointed +out, most implementations of IKE use the standard HMAC method. + +This option forces pluto to use the plain SHA1 method outlined in the +draft document. Clearing this option forces the HMAC method to be +used. + +3.5.6 'dss-alt' + +RFC2409 stipulates that the encoding of a DSS signature should be 'r +followed by s'. It does not say what format this should take. ANSI +document X9.30 states that DSS output should be the DER encoding of +the ASN.1 construct - + + SEQUENCE { + r INTEGER; + s INTEGER; + }; + +This puts some wrapper bytes around the 160 bit integers r and s, so +that the signature is some 45-48 bytes long (depending on leading +zeros within the integers). + +draft-ietf-ipsec-ike-01.txt states that the signature should be a +single 320 bit string, with r occupying the first 160 bits and s +occupying the next 160, without wrappers, such that all signatures are +exactly 40 bytes long. + +By using the 'dss-alt' option, pluto is forced into the draft document +mode of operation. By clearing it, the X9.30 implementation is used. + +4. Creating the certificates + +This section summarises the far more useful guide within the OpenSSL +documentation, but should serve to generate the certificates and keys +required for a connection. The document assumes that OpenSSL has been +compiled and installed in /usr/local/ssl. Furthermore, it assumes that +that configuration file for OpenSSL is stored in +/usr/local/ssl/lib/openssl.cnf. If this is not the case, all 'openssl +' entries should be replaced by 'openssl -config +/path/to/openssl.cnf'. + +I am heavily indebted to Peter Onion of BT Labs for his efforts to +debug and clarify this documentation. Peter: Your efforts are much +appreciated. + +NB: This isn't the "correct" way to generate certs/CRLs. It's just my +own recipe (largely from memory - it's been a while!) for doing +so. It's not efficient, and you really should read the OpenSSL docs +for better guidance. + +4.1 Edit openssl.cnf + +Ensure that the following settings for the CA_default section are more +or less as follows - + +dir = /usr/local/ssl +certs = $dir/certs +crl_dir = $dir/crl +database = $dir/index.txt +new_certs_dir = $dir/newcerts + +certificate = $dir/cacert.pem +serial = $dir/serial +crl = $dir/crl.pem +private_key = $dir/private/cakey.pem +RANDFILE = $dir/private/.rand + +Fill in the default fields (country, organization, etc) as seems right +for your setup. + +Create a file ca.ext containing the following - + +----------------------------------- +# Extensions for a typical CA - PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always +basicConstraints = CA:true +keyUsage = cRLSign, keyCertSign +nsCertType = sslCA, emailCA +subjectAltName=email:copy +---------------------------------- + +4.2 Make the CA certificate + +Go to the directory /usr/local/ssl + +Generate your CA cert by the usual method + +openssl req -new -newkey rsa:1024 \ + -keyout /usr/local/ssl/private/cakey.pem \ + -out careq.pem + +Now sign the CA cert, citing the extensions for [v3_ca] + +openssl x509 -CAcreateserial -signkey private/cakey.pem -req \ + -in careq.pem -out cacert.pem -days 2000 -extfile ca.ext + +This creates the self signed certificate which can be used to sign +further certificates. Ensure that this cacert.pem file is made widely +available to all hosts which will communicate via IPsec. + +4.3 Make an empty certificate database + +Execute the following commands, whilst in the directory /usr/local/ssl + +touch index.txt + +echo "01" > serial + +This creates the database, and starts issued certificates with the +serial number starting at 1. + +4.4 Generate a CRL + +Use the command + +openssl ca -gencrl -out crl.pem + +to generate an (initially empty) CRL. You should always have a +current CRL, even if nothing has been revoked, since that proves +*actively* that no certificates have been cancelled. Without a CRL, +you are forced into the assumption that no certificates have been +revoked. + +The CRL is placed in /usr/local/ssl/crl.pem. Distribute this file to +all hosts holding the cacert.pem file. (Or, if you have an LDAP +directory handy, publish the CRL there, so that all hosts can contact +it). + +4.5 Generate the host certificate requests. + +On each host, generate a key and certificate request with the +following command (I'll assume that ipsec certificates are in the +directory /etc/ipsec, which must exist beforehand). + +I'll assume that the host is called foo, and lives in .mydomain.com, +and has IP address aa.bb.cc.dd. + +openssl req -new -newkey rsa:1024 \ + -nodes -keyout /etc/ipsec/foo.key -out foo.req.pem + +Fill in the fields for the subject name, and ensure that the common +name section is set to 'foo.mydomain.com IPsec certificate #1', or +some such thing - either way, it should start with foo.mydomain.com. + +See section 3.2 (keyfile) on converting private key files to the +standard PKCS-8 format (which pluto can also understand). If you do +this, you can delete the original private key file 'foo.key', once you +have a PKCS-8 equivalent of it. + +Change the file permissions on foo.key to be 400 - Owner read only, +and change to owner to be root. + +Transport the file foo.req.pem to the host which will sign certificate +requests. This should be done via some secure method of +transmission. In an ideal setup, your CA host shouldn't be connected +to any network at all - but that's a site specific decision) + +4.6 Sign the certificate requests. + +On the CA host, edit the openssl.cnf file such that there is a section +[svr_cert] at the bottom of the file with the following contents. + +[ svr_cert ] +basicConstraints=CA:FALSE +nsCertType = server +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +nsComment = $ENV::NSCOMMENT +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always +subjectAltName=email:copy,DNS:$ENV::HOSTFQDN,IP:$ENV::HOSTIP +issuerAltName=issuer:copy + +If this is not already done, alter the x509_extensions in the +[CA_default] section such that it looks like - + +x509_extensions = $ENV::EXTENSION + +Now sign the request with the wrapper script 'signIPSEC', which +is produced below. The script was written by Peter Onion. + +-----------------8<-------CUT HERE------8<---------------------------- +#! /bin/bash + +# Simple wrapper for "openssl ca" that sets environment to pass in +# values for the svr_cert extension +# P.J.Onion 23/9/1999 + +if test $# -ne 3 ; then + echo "Usage: signIPSEC hostname ipaddress reqfile" + exit 1 +fi + + +HOSTFQDN=$1 +HOSTIP=$2 +NSCOMMENT="IPsec Certificate for $1" +EXTENSION=svr_cert + +export HOSTFQDN HOSTIP NSCOMMENT EXTENSION +# Change /usr/local/ssl to be the directory in which OpenSSL +# is installed. + +/usr/local/ssl/bin/openssl ca -in $3 + +-----------8<----------CUT TO HERE --------8<------------------------- + + +In the case above, you would do + +signIPSEC foo.mydomain.com 192.168.1.5 foo.req + +(assuming that foo.mydomain.com has the IP address 192.168.1.5, and +that foo.req contains the PEM certificate request). + +Check the details that it prints, and commit the new certificate to +the database. The new certificate will be in the directory +/usr/local/ssl/newcerts. Tranport this file back to the requesting +host and place it in the file /etc/ipsec/foo.pem. You can delete the +file foo.req.pem now. + +Once the certificate has been delivered, move the file from newcerts +into certs and run the command + +c_rehash /usr/local/ssl/certs + +to create the hashed directory. + +4.7 Renewing a certificate + +When you want to replace a certificate stored in file foo.pem, issue +the command + +openssl ca -revoke foo.pem + +This changes the database to reflect the fact that the certificate has +been cancelled, but does not update the CRL. Do that with the +instructions in section 4.4. Make sure that the new CRL is propagated +to all participating hosts. + +Now create the key/request/certificate as per sections 4.5 onwards. + + ******************* End of document ******************** + diff -ruN freeswan-1.5.orig/doc/pkix/README.xmap freeswan-1.5/doc/pkix/README.xmap --- freeswan-1.5.orig/doc/pkix/README.xmap Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/doc/pkix/README.xmap Thu Oct 5 07:05:59 2000 @@ -0,0 +1,389 @@ + README.xmap + + Neil Dunbar + 21st October, 1999 + +1. Introduction + +As of the October release of the pluto-openssl patches, the +certificate paths parameter has been replaced by a list of +XMAPs. XMAPs are an abstraction of a dictionary lookup, which are +designed to look up public key information. This includes X.509 +certificates, X.509 certificate revocation lists, and, in theory, +simple public key values (NB: raw public keys are not supported yet). + +An XMAP is similar to the X509_LOOKUP system employed within OpenSSL +to specify file and directory lookup. Indeed, XMAP has two mechanisms +for lookup via file and directory, as well as others. + +The specification for an XMAP consists of a text string of the format +:
. The type denotes the sort of search which the XMAP +library will carry out, and the details give appropriate parameter +information so that the search can execute. As an example, a file XMAP +on the file /etc/ipsec/certs.txt would have the form +"file:/etc/ipsec/certs.txt". + +2. XMAP lookup + +(Programmer stuff - ignore if not interested) + +A lookup is a search which is identified by three parameters: the type +of the thing being looked for, the search identifier and a search +parameter (which is governed by the search identifier). + +The result of the search is a list (actually an OpenSSL STACK) of type +X509_OBJECT (all of which fit the search criteria specified, sorted to +be age order [most recent first, oldest last]. The type X509_OBJECT is +defined in the OpenSSL header file x509_vfy.h. Note: this is NOT the +same as the file X509_OBJECTS defined in x509.h. The C type +declaration is reproduced here. + +typedef struct x509_object_st { + /* one of the above types */ + int type; + union { + char *ptr; + X509 *x509; + X509_CRL *crl; + EVP_PKEY *pkey; + } data; +} X509_OBJECT; + +Thus, if an X.509 certificate is stored in such an object, the type +field will be set to X509_LU_X509, and .data.x509 holds a +pointer to the actual certificate data. + +Thus, to iterate through all certificates returned in a search, a C +program fragment would look like the following. + +XMAP *xmap; +int i; +STACK_OF(X509_OBJECT) *sk; +X509_OBJECT *obj; +X509 *x; + : + : +sk = XMAP_lookup(xmap, X509_LU_X509, , ); +for (i=0; idata.x509; + + /* Operations on the certificate 'x' */ +} + +3. XMAP Search types + +The various search types are detailed in the following sections. The +list of items are sorted (if possible) in date descending order, ie +the most recent object is first in the return list, and the oldest +object comes last. All duplicates are removed from the return list. + +3.1 "subject" (returns X.509 cert) + +The search looks for a certificate whose name is the the same as the +X.509 name given in the parameter to the search. + +3.2 "issuer" (returns X.509 CRL) + +The search looks for a certificate revocation list (CRL) whose issuer +is the same as the X.509 given as a search parameter. + +3.3 "uid" (returns X.509 cert) + +The search looks for the certificates which are listed as belonging to +the user whose ID is given by the null terminated character string +given as the search parameter. + +3.4 "dns" (returns X.509 cert) + +The search returns a list of the certificates which are listed as +belonging to the fully qualified domain name (FQDN) as given in the +search parameter, which is a null terminated character string. + +NB: Any certificate MUST have a subjectAltName which includes a DNS +entry which is the same as the search string. This requirement is in +addition to any indexing which is required for the individual XMAP +search type. + +3.5 "ip" (returns X.509 cert) + +The search returns a list of the certificates which are listed as +belonging to the IPv4 address given as a search parameter. The search +parameter is a 4 octet string, which gives the IP address in network +order; for example the ip address 15.144.59.30 would have the search +parameter (in hex) 0F903B1E. + +As with DNS indexed certificates, the certificates must contain a +subjectAltName with the correct IP entry within it. + +3.6 "ca" (returns X.509 cert) + +This search returns all CA certificates indexed in the list. A CA +certificate is one which has a basicConstraints extension with the CA +flag set to true. Also the CA has the subjectName and issuerName set +to be the same. + +There is no search parameter for this search. It should be set to NULL +in the XMAP_lookup() call. + +4. XMAP Types + +There are currently four types of XMAP: File, Directory, Berkeley DB +and LDAP. Respectively, their type specifiers are "file", "dir", "db" +and "ldap". Each subsection will detail the exact representation and +expectations of the XMAP. + +4.1 File + +Type Specifier: "file" +Syntax: file: + +This implements a simple flat file structure for lookups. The file can +contain as many certificates in PEM format as desired. In order to +index them, certain fields should be prepended to the certificates. + +For example a file might look like this. + +-----BEGIN CERTIFICATE----- +MIIEHzCCAwegAwIBAgIBADANBgkqhkiG9w0BAQQFADCBpzELMAkGA1UEBhMCR0Ix +HQ0Qb72dPlXYO20xtdMR9fX92PfSQSgAulNITYzusd8KE6yvJ8+HEbUuwXLHXZh4 + : + : + : +pk/Bv8iFUiZhH8EAr6SvF0AkpULi46bVcoQlr36Ax59uQ8OcJeNmljKYdbrN5uFL +62In +-----END CERTIFICATE----- + +uid: nd +-----BEGIN CERTIFICATE----- +MIIETDCCA7WgAwIBAgIQE9hfeRmSr51Wzgvm4B19JjANBgkqhkiG9w0BAQQFADCB +njEPMA0GA1UEChMGaHAuY29tMRowGAYDVQQLExFJVCBJbmZyYXN0cnVjdHVyZTEL + : + : + : +6pZk+pEpZ5S55lFP1QspTsBVbrBgsEuGFyGpHTo9sIEaQYxfRGNEvW+ZaEPU1HKo +YVkAahuv5T21GdvouWdelA6l6uWMK/hfsQo9PdpBLDaUrwH5lkDuXcNk+LHZvVDt +-----END CERTIFICATE----- + +-----BEGIN X509 CRL----- +MIICLTCCARUwDQYJKoZIhvcNAQEEBQAwgacxCzAJBgNVBAYTAkdCMR4wHAYDVQQI +ExVTb3V0aCBHbG91Y2VzdGVyc2hpcmUxEDAOBgNVBAcTB0JyaXN0b2wxJTAjBgNV + : + : + : +++lOGcX1WTtZ2dnV6Lb5r5xQ3IGavf2TKnu1/nCyAKFlF34+j7/+fIxT4iQiEDjk +Z8Q76UEWVRMw7Tk63aAf0Yf5qaI1Tpd+SKhSTgc4arJH +-----END X509 CRL----- + +ip: 15.144.59.30 +dns: pinky.hpl.hp.com +-----BEGIN CERTIFICATE----- +MIIHgTCCBmmgAwIBAgIBCjANBgkqhkiG9w0BAQQFADCBpzELMAkGA1UEBhMCR0Ix +HjAcBgNVBAgTFVNvdXRoIEdsb3VjZXN0ZXJzaGlyZTEQMA4GA1UEBxMHQnJpc3Rv + : + : + : +KkW/kYOxRrmg9KGnSVOolD1ueLYg9D4CtY30r1JGE/wixmIvQEP8JEv+X6Cr4Wiu +VfENrF8= +-----END CERTIFICATE----- + +This example indexes the fourth certificate as belonging to the IP +address 15.144.59.30, as well as the DNS name "pinky.hpl.hp.com". The +second certificate belongs to the user ID "nd". + +The file also contains a CRL embedded within it. + +Note: indexed searches in files are done by simple linear search. It's +not an efficient means for searching serious amounts of data - use the +other types for anything other than trivial data. + + +4.2 Directory + +Type Specifier: "dir" +Syntax: dir: + +The directory type is a derivation of the hashed directory used in the +OpenSSL applications. The certificates are stored within a single +level directory. Each certificate (or CRL) is stored in exactly one +file in the directory. These files can be in either DER or PEM format, +unlike the flat file above. To index the files for subject and issuer +searches, you need to make a symbolic link from a hashed +representation of the X.509 name of the certificate subject name (or +CRL issuer name). + +To do this, you should execute the command - + +[for certificate files -- all on one line] + +ln -s certificate.pem + `openssl x509 -noout -hash -in certificate.pem`.cert.0 + +NB: The .0 on the end is an arbitrary extension to the link name in +case there might be a hash clash in the directory. You can call it +anythin you like, but the ".cert" extension MUST be present -- unlike +standard OpenSSL hash directories. + +[ for CRL files ] +ln -s crl.pem + `openssl crl -noout -hash -in certificate.pem`.crl.0 + +Again, the .0 is a differentiating extension, but the ".crl" is +mandatory for marking out certificates. + +To index other attributes, you should make links which have the +following appearance - + +uid-.cert.0 [ denotes a user file ] +dns-.cert.0 [ a DNS indexed cert ] +ip-.cert.0 [ an IP indexed cert ] + +For example, assume that file pinky.pem held the certificate for DNS +pinky.hpl.hp.com and/or IP address 15.144.59.30, the links would look +like - + +dns-pinky.hpl.hp.com.cert.0 )__________\. pinky.pem +ip-15.144.59.30.cert.0 ) / + +CA certificates don't need to be indexed. CA searches are performed by +loading every certificate file in the directory and checking for a CA +certificate, so it's failrly slow for this type of search. + +What Directories *are* good for is converting into a Berkeley DB file +database - which is the fastest of all searches. + +3.3 - Berkeley DB searches + +Type Specifier: "db" +Syntax: db: + +Assuming you have a Berkeley DB 1.85 library available on your system, +you can index the certificates/CRLs in a DB hash file, which provides +the fastest lookup of any of the methods. + +The internal details of the DB file are maintained in a separate file +(README.XMAP.DB -- to be written -- nd). To make a DB file, you should +create a directory as in the above section, then run the utility +command "dir2hash" as follows, assuming that the directory to be +hashed is called "my_dir" and the output DB file should be called +certs.db :- + + dir2hash -o dbhash.db my_dir + +Note: You don't have to make the hashed, DNS or IP links as in the +directory above (they are automatically figured out by the dir2hash +program from the subjectNames and subjectAltNames of the +certificates/CRLs). You DO have to index the uid entries manually, +since the certificates don't necessarily have the means of +indentifying which users they belong to. + +Note that IPsec knows nothing of uid's and doesn;t use them yet, so a +perfectly valid IPsec DB file can be mafe by putting all the +certificates into a directory without any links at all, then running +dir2hash on the directory. + +However, this still creates a single file which must be copied to all +hosts participating in the IPsec process. Therefore, this mechanism +will not scale past a few, easily managed hosts. For scalability you +should use LDAP. + +3.4 - LDAP searches + +Type Specifier: "ldap" +Syntax: ldap:: + +Assuming that you have at least one LDAP server available on your +network, you can make a config file (which is NOT the same as +ipsec.conf) to specify how to search for entries in one or more LDAP +servers. A single ldap configuration file can hold search details for +many ldap servers. Each server specification must be identified by a +unique ldap ID. This ID can be any alphanumeric string. If the LDAP +identifier is omitted, then the first specification in the file will +be used by default. + +The entries in the configuration file can be of the following form: + +_host_name: + +_port: + +_base: + +_timeout: + +_bindDN: + +_bindPW: + +Note that you MUST supply a base search string: the software cannot +guess a reasonable default. Also, the bind DN and password is the only +supported authentication mechanism. PLEASE don't put the LDAP server +on a host which needs to reached via IPsec. If you're relying on it to +serve all certificates, the LDAP connection won't work (since the +IPsec connection might not be up yet). + +For each search type, you must specify the filter string and +attributes which should be searched for from the LDAP directory. + +The filter string specification has the form + +_filter::: + +where is either "uid", "subject", "issuer", "dns" or +"ip" as above, and is either "cert" (for X.509 +certificates) or "crl" for CRL searches. Either the or + can be set to "*", which is a wildcard, meaning any +index or any return type. + +Similarly, the attribute specification looks like: + +_attributes::: + +The and are as above, with the list of +attributes a comma separated list of attributes. + +For an example, let us use the following file + +ldapnd_host_name = ldap.hpl.hp.com +ldapnd_base = o=Hewlett-Packard Laboratories, c=GB +ldapnd_filter:uid = (uid=%s) + +ldapnd_attributes:*:crl = certificaterevocationlist, certificaterevocationlist;binary + +ldapnd_filter:*:crl = (&(objectclass=certificationAuthority)(cn=%s)) + +ldapnd_filter:ca = (objectclass=certificationauthority) + +ldapnd_filter:dns = (&(objectclass=hostRecord)(dNSRecord=%s)) +ldapnd_attributes:dns = servercertificate, servercertificate;binary + +ldapnd_filter:ip = (&(objectclass=hostRecord)(ipv4Address=%s)) +ldapnd_attributes:ip = servercertificate, servercertificate;binary + +This stipulates an anonymous bind search of the LDAP server on +ldap.hpl.hp.com port 389 (the default), whose base for searches is +"o=Hewlett-Packard Laboratories,c=GB". + +For DNS searches, the filter used will be + +"(&(objectClass=hostRecord)(dNSRecord=%s))" + +where "%s" will be replaced by the DNS name being searched for. In the +case of a search for pinky.hpl.hp.com, the search string will be - + +"(&(objectClass=hostRecord)(dNSRecord=pinky.hpl.hp.com))" + +For IP address searches, the filter used will be + +"(&(objectclass=hostRecord)(ipv4Address=%s))" + +Again, searching for the host with IP address 15.144.59.30, the host +search filter will be - + +"(&(objectclass=hostRecord)(ipv4Address=15.144.59.30))" + +In both DNS and IP searches, the search will return the attributes +"serverCertificate" and "serverCertificate;binary". + +Note that the return type is missing from many of the +specifications. Return types default to "cert". diff -ruN freeswan-1.5.orig/doc/pkix/ldap-ca.quickstart freeswan-1.5/doc/pkix/ldap-ca.quickstart --- freeswan-1.5.orig/doc/pkix/ldap-ca.quickstart Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/doc/pkix/ldap-ca.quickstart Thu Oct 5 07:05:59 2000 @@ -0,0 +1,169 @@ + +Quick Instructions on how I set up my own LDAP CA. +-- Luc Lanthier, 20001004 + +1- openldap server for CA use: +----------------------------------------------------------------- +cd /usr/src +tar xfvz openldap-stable-20000704.tgz +cd openldap-1.2.11 +./configure --enable-shared --with-gnu-ld --enable-ldapd +make depend +make +cd tests +make +cd .. +make install +----------------------------------------------------------------- + + +2: Notes on LDIF files: +----------------------------------------------------------------- + +Use your favorite editor and create an LDIF file that contains: + dn: dc=, dc= + objectclass: dcObject + objectclass: organization + o: + dc: + + dn: cn=Manager, dc=, dc= + objectclass: organizationalRole + cn: Manager + +Now, you may run ldapadd(1) to insert these entries into your directory. + ldapadd -D "cn=Manager, dc=, dc=" -W -f example.ldif + + +Now we're ready to verify the added entries are in your directory. You +can use any LDAP client to do this, but our example uses the +ldapsearch(1) tool. Remember to replace dc=example,dc=com with the correct +values for your site: + ldapsearch -b 'dc=example,dc=com' '(objectclass=*)' + +----------------------------------------------------------------- + +3- How I created my own LDIF file +----------------------------------------------------------------- +----------- LDIF.sh: +#!/bin/sh +parser="./parsecert.pl" + +cat < LDIF +/usr/local/libexec/slapd -s 1 +ldapadd -D "cn=Manager, dc=netwinder, dc=org" -W -f LDIF +----------------------------------------------------------------- diff -ruN freeswan-1.5.orig/doc/pkix/openssl-ca.quickstart freeswan-1.5/doc/pkix/openssl-ca.quickstart --- freeswan-1.5.orig/doc/pkix/openssl-ca.quickstart Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/doc/pkix/openssl-ca.quickstart Thu Oct 5 07:05:59 2000 @@ -0,0 +1,66 @@ +How I set up a CA on RedHat 6.2 and used it: + +1- install openssl... +cd /usr/src/redhat/SRPMS +scp root@10.8.49.101:/usr/src/redhat/SRPMS/openssl*.src.rpm . +rpm --rebuild openssl-0.9.5a-1.src.rpm +cd ../RPMS/i386/ +rpm -i openssl-* + +2- Setting up the openssl CA. +cd /var/lib/ssl +ln -s /etc etc +vi /etc/openssl.cnf; # (see README.certificates section 4.1) +vi ca.ext; # (see README.certificates section 4.2) +openssl req -new -newkey rsa:1024 -keyout \ + /var/lib/ssl/private/cakey.pem -out careq.pem +openssl x509 -CAcreateserial -signkey private/cakey.pem -req \ + -in careq.pem -out cacert.pem -days 2000 -extfile ca.ext +touch index.txt +echo "01" > serial +openssl ca -gencrl -out crl.pem; # distribute/make available to all hosts. + +3- adding to hosts (presuming flat file format) +scp crl.pem 10.1.49.233:/etc/ipsec +scp cacert.pem 10.1.49.233:/etc/ipsec +scp crl.pem 10.8.49.101:/etc/ipsec +scp cacert.pem 10.8.49.101:/etc/ipsec + +4- Each client host -- generate certificate requests. +cd /etc/ipsec +# make sure the info entered for each client is DIFFERENT. +openssl req -new -newkey rsa:1024 -nodes -keyout \ + /etc/ipsec/`hostname`.key -out /etc/ipsec/`hostname`.req.pem +ln -fs `hostname`.key privkey.pem +ln -fs `hostname`.pem pubcert.pem; # will be created soon. +chmod 400 `hostname`.key + +5- CA host -- Signing certificates +vi /etc/openssl.cnf; # (see README.certificates section 4.6) +vi /usr/local/bin/signIPSEC; # (see README.certificates section 4.6) +chmod 700 /usr/local/bin/signIPSEC +# use: signIPSEC foo.mydomain.com 192.168.1.5 foo.req +cd /var/lib/ssl/ +mkdir -p newcerts +scp root@10.1.49.233:/etc/ipsec/*.req.pem . +scp root@10.8.49.101:/etc/ipsec/*.req.pem . +signIPSEC sticky.netwinder.org 10.1.49.233 sticky.req.pem +scp newcerts/`cat serial.old`.pem root@10.1.49.233:/etc/ipsec/sticky.pem +signIPSEC x86rack1.netwinder.org 10.8.49.101 x86rack1.req.pem +scp newcerts/`cat serial.old`.pem root@10.8.49.101:/etc/ipsec/x86rack1.pem +mv /var/lib/ssl/newcerts/* /var/lib/ssl/certs +c_rehash /var/lib/ssl/certs + +6- CA host: prepping flat file for client hosts +cd /var/lib/ssl +for ii in certs/01.pem certs/02.pem cacert.pem crl.pem; +do + cat $ii | \ + perl -e '$printme = 0; while (<>) { if (/---BEGIN/) {$printme = 1;}; if ($printme) {print STDOUT $_;} }; print STDOUT "\n";' \ + >> flatfile.txt +done +# flatfile is created. Send to Client hosts. +scp flatfile.txt root@10.1.49.233:/etc/ipsec +scp flatfile.txt root@10.8.49.101:/etc/ipsec + + diff -ruN freeswan-1.5.orig/pluto/Makefile freeswan-1.5/pluto/Makefile --- freeswan-1.5.orig/pluto/Makefile Sat May 20 15:50:00 2000 +++ freeswan-1.5/pluto/Makefile Thu Oct 5 07:18:12 2000 @@ -32,16 +32,39 @@ LIBDESLITE=$(FREESWANLIBDIR)/libdes.a LIBGMP=$(FREESWANLIBDIR)/libgmp.a +#OPENSSLROOT=/opt/ssl +#OPENSSLINCLS=-I$(OPENSSLROOT)/include +#OPENSSLLIBS=-L$(OPENSSLROOT)/lib -Wl,-rpath $(OPENSSLROOT)/lib -lcrypto +#OPENSSLDEFS=-DOPENSSL + +OPENSSLROOT=/usr/lib +OPENSSLINCLS= +OPENSSLLIBS=-lcrypto +OPENSSLDEFS=-DOPENSSL + +OPENSSLOBJS=openssl.o xmap.o xmap_file.o xmap_dir.o xmap_db.o xmap_ldap.o \ + x_sobj.o + +LDAPROOT=/usr/local +LDAPINCS=-I$(LDAPROOT)/include +LDAPLIBS=-L$(LDAPROOT)/lib -lldap -llber -Wl,-rpath $(LDAPROOT)/lib +LDAPDEFS=-DHAVE_LDAP + +DBROOT=/usr +DBINCS=-I$(DBROOT)/include +DBLIBS=-L$(DBROOT)/lib -ldb +DBDEFS=-DHAVE_DB -DHAVE_DB185 + KLIPSD=../klips/src INSTALL=install # -O on Linux makes gcc coredump when compiling sha1.c -CFLAGS = -g -Wall -W -Wmissing-prototypes -Wpointer-arith -Wbad-function-cast \ - -Wcast-qual -Wmissing-declarations -Wwrite-strings -Wstrict-prototypes +CFLAGS = -g -Wall -Wmissing-prototypes -Wpointer-arith -Wbad-function-cast \ + -Wcast-qual -Wmissing-declarations -Wwrite-strings # where to find klips headers and FreeS/WAN headers -HDRDIRS = -I$(KLIPSD) $(FREESWANINCLS) +HDRDIRS = -I$(KLIPSD) $(FREESWANINCLS) $(OPENSSLINCLS) $(LDAPINCS) $(DBINCS) # 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 @@ -74,22 +97,28 @@ CPPFLAGS = $(HDRDIRS) $(BYTE_ORDER) \ -DPLUTO -DKLIPS -DDODGE_DH_MISSING_ZERO_BUG -DOLD_RESOLVER \ - -DDEBUG -DGCC_LINT + -DDEBUG -DGCC_LINT $(OPENSSLDEFS) $(LDAPDEFS) $(DBDEFS) ALLFLAGS = $(CPPFLAGS) $(CFLAGS) # libefence is a free memory allocation debugger # Solaris 2 needs -lsocket -lnsl -LIBSPLUTO = -lresolv # -lefence +LIBSPLUTO = -lresolv $(OPENSSLLIBS) $(LDAPLIBS) $(DBLIBS) # -lefence LDFLAGS = # Solaris needs -lsocket -lnsl LIBSWHACK = +LIBSD2H = $(OPENSSLLIBS) $(DBLIBS) + BINNAMEPLUTO = pluto BINNAMEWHACK = whack +# Comment this out if you don't have Berkeley DB available, which, +# if you're running Linux is a bit strange... +BINNAMED2H = dir2hash + RM = /bin/rm RMFLAGS = -f @@ -102,11 +131,11 @@ pluto.8 ipsec.secrets.5 DISTGCRYPT = \ - gcryptfix.c gcryptfix.h \ - dsa.c dsa.h \ - elgamal.c elgamal.h \ - primegen.c \ - smallprime.c + gcryptfix.c gcryptfix.h \ + g10_dsa.c g10_dsa.h \ + elgamal.c elgamal.h \ + primegen.c \ + smallprime.c DISTSRC = \ connections.c connections.h \ @@ -140,19 +169,23 @@ # start of support for DSS/DSA. Not currently used. # OBJSGCRYPT = gcryptfix.o dsa.o elgamal.o primegen.o smallprime.o -OBJSGCRYPT = +OBJSGCRYPT = gcryptfix.o g10_dsa.o elgamal.o primegen.o smallprime.o + OBJSPLUTO = connections.o constants.o cookie.o crypto.o defs.o log.o \ state.o main.o server.o timer.o id.o ipsec_doi.o kernel.o \ kernel_comm.o demux.o packet.o preshared.o dnskey.o rnd.o spdb.o \ - sha1.o md5.o $(OBJSGCRYPT) $(LIBDESLITE) $(LIBGMP) $(FREESWANLIB) + sha1.o md5.o $(OBJSGCRYPT) $(LIBDESLITE) $(LIBGMP) $(FREESWANLIB) \ + $(OPENSSLOBJS) OBJSWHACK = whack.o $(FREESWANLIB) -all: $(BINNAMEPLUTO) $(BINNAMEWHACK) +OBJSD2H = dir2hash.o + +all: $(BINNAMEPLUTO) $(BINNAMEWHACK) $(BINNAMED2H) install: all - $(INSTALL) $(BINNAMEPLUTO) $(BINNAMEWHACK) $(BINDIR) + $(INSTALL) $(BINNAMEPLUTO) $(BINNAMEWHACK) $(BINNAMED2H) $(BINDIR) $(INSTALL) pluto.8 $(PMANDIR)/ipsec_pluto.8 ../utils/manlink $(PMANDIR) ipsec_pluto.8 $(INSTALL) ipsec.secrets.5 $(FMANDIR) @@ -164,6 +197,9 @@ $(BINNAMEWHACK): $(OBJSWHACK) $(CC) -o $(BINNAMEWHACK) $(OBJSWHACK) $(LIBSWHACK) +$(BINNAMED2H): $(OBJSD2H) + $(CC) -o $(BINNAMED2H) $(OBJSD2H) $(LIBSD2H) + distlist: @echo $(DIST) @@ -183,8 +219,10 @@ realclean: clean clean: - $(RM) $(RMFLAGS) $(OBJSPLUTO) *.core core *~ a.out ktrace.out - $(RM) $(RMFLAGS) $(BINNAMEPLUTO) $(OBJSWHACK) $(BINNAMEWHACK) + $(RM) $(RMFLAGS) $(OBJSPLUTO) $(OBJSD2H) \ + *.core core *~ a.out ktrace.out + $(RM) $(RMFLAGS) $(BINNAMEPLUTO) $(OBJSWHACK) $(BINNAMEWHACK) \ + $(BINNAMED2H) .c.o: $(CC) $(COPTS) $(ALLFLAGS) -c $< @@ -222,9 +260,6 @@ defs.o: defs.c demux.o: demux.c dnskey.o: dnskey.c -dsa.o: dsa.c -elgamal.o: elgamal.c -gcryptfix.o: gcryptfix.c id.o: id.c ipsec_doi.o: ipsec_doi.c kernel.o: kernel.c @@ -234,11 +269,9 @@ md5.o: md5.c packet.o: packet.c preshared.o: preshared.c -primegen.o: primegen.c rnd.o: rnd.c server.o: server.c sha1.o: sha1.c -smallprime.o: smallprime.c spdb.o: spdb.c state.o: state.c timer.o: timer.c @@ -298,23 +331,6 @@ dnskey.o: preshared.h dnskey.o: dnskey.h dnskey.o: packet.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 -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 @@ -400,11 +416,6 @@ preshared.o: state.h preshared.o: preshared.h preshared.o: log.h -primegen.o: constants.h -primegen.o: defs.h -primegen.o: log.h -primegen.o: rnd.h -primegen.o: gcryptfix.h rnd.o: sha1.h rnd.o: constants.h rnd.o: defs.h @@ -425,9 +436,6 @@ server.o: kernel_comm.h sha1.o: sha1.h sha1.o: endian.h -smallprime.o: constants.h -smallprime.o: defs.h -smallprime.o: gcryptfix.h spdb.o: constants.h spdb.o: defs.h spdb.o: id.h diff -ruN freeswan-1.5.orig/pluto/connections.c freeswan-1.5/pluto/connections.c --- freeswan-1.5.orig/pluto/connections.c Wed Jun 21 14:24:31 2000 +++ freeswan-1.5/pluto/connections.c Thu Oct 5 07:05:59 2000 @@ -39,6 +39,11 @@ #include "preshared.h" #include "whack.h" +#ifdef OPENSSL +#include "openssl.h" +#include "xmap.h" +#endif + static struct connection *connections = NULL; /* host_pair: a nexus of information about a pair of hosts. @@ -242,6 +247,20 @@ } unorient_connection(c); /* won't delete c */ +#ifdef OPENSSL + { + int i; + + for (i=0; iother[i].cert) + X509_free((X509 *)(c->other[i].cert)); + } + + if (c->cert) X509_free((X509 *)(c->cert)); + if (c->key) EVP_PKEY_free((EVP_PKEY *)(c->key)); + if (c->lu) sk_XMAP_pop_free(c->lu, XMAP_free); +#endif + /* find and delete c from connections list */ list_rm(struct connection, next, c, connections); cur_connection = old_cur_connection; @@ -444,7 +463,11 @@ for (; c != NULL; c = c->hp_next) { +#ifndef OPENSSL if ((c->policy ^ wm->policy) & (POLICY_PSK | POLICY_RSASIG)) +#else + if ((c->policy ^ wm->policy) & (POLICY_PSK | POLICY_OPENSSL)) +#endif { loglog(RC_CLASH , "authentication method disagrees with \"%s\", which is also for an unspecified peer" @@ -499,6 +522,22 @@ c->that = t; } +#ifdef OPENSSL + { + int i; + + for(i=0; iother[i].cert = NULL; + c->other[i].type = 0; + } + } + load_cert_and_key(wm->whack_certfile, wm->whack_keyfile, + (X509 **)&(c->cert), (EVP_PKEY **)&(c->key)); + c->cert_options = parse_options(wm->whack_certopts); + make_lookups((STACK_OF(XMAP) **)(&(c->lu)), wm->whack_certpath); + strncpy(c->path, wm->whack_certpath, PATH_NAME_LIMIT); +#endif + /* set internal fields */ c->next = connections; connections = c; @@ -806,7 +845,11 @@ return NULL; /* cannot determine PSK! */ break; case OAKLEY_RSA_SIG: +#ifndef OPENSSL auth_policy = POLICY_RSASIG; +#else + auth_policy = POLICY_OPENSSL; +#endif my_RSA_pri = get_RSA_private_key(c); if (my_RSA_pri == NULL) return NULL; /* cannot determine my RSA private key! */ diff -ruN freeswan-1.5.orig/pluto/connections.h freeswan-1.5/pluto/connections.h --- freeswan-1.5.orig/pluto/connections.h Wed Jun 21 14:24:31 2000 +++ freeswan-1.5/pluto/connections.h Thu Oct 5 07:05:59 2000 @@ -14,6 +14,10 @@ * RCSID $Id: connections.h,v 1.38 2000/06/21 18:24:31 dhr Exp $ */ +#ifdef OPENSSL +#include "openssl_defs.h" +#endif + /* There are two kinds of connections: * - ISAKMP connections, between hosts (for IKE communication) * - IPsec connections, between clients (for secure IP communication) @@ -78,6 +82,13 @@ * the old one. It is deleted when no longer in use. */ +#ifdef OPENSSL +struct other_st { + u_int32_t type; /* type of certificate in 'cert': EVP_PKEY_RSA or EVP_PKEY_DSA */ + void *cert; +}; +#endif + struct end { struct id id; struct in_addr /* network order */ @@ -108,6 +119,18 @@ struct connection { char *name; +#ifdef OPENSSL + /* The declaration of these as void is truly horrific - they */ + /* should be X509 and EVP_PKEY pointers. Unfortunately, if this */ + /* is declared, the MD5 header files from OpenSSL conflict with those */ + /* in the KLIPS source. Hence we cast these pointers to the right */ + /* type at the time of use. It's disgusting, but I can't think of */ + /* another way -- ND */ + void *cert, *key, *lu; + struct other_st other[MAX_OTHER]; + u_char path[PATH_NAME_LIMIT]; + u_int32_t cert_options; +#endif lset_t policy; time_t sa_ike_life_seconds; time_t sa_ipsec_life_seconds; diff -ruN freeswan-1.5.orig/pluto/constants.c freeswan-1.5/pluto/constants.c --- freeswan-1.5.orig/pluto/constants.c Sun Jun 18 15:15:34 2000 +++ freeswan-1.5/pluto/constants.c Thu Oct 5 07:05:59 2000 @@ -277,11 +277,37 @@ enum_names ident_names = { ID_IPV4_ADDR, ID_KEY_ID, ident_name, NULL }; +#ifdef OPENSSL + +/* Certificate type values */ +static const char *const cert_name[] = { + "CERT_TYPE_NONE", + "CERT_TYPE_PKCS7", + "CERT_TYPE_PGP", + "CERT_TYPE_DNSKEY", + "CERT_TYPE_X509_SIG", + "CERT_TYPE_X509_KEX", + "CERT_TYPE_KERBEROS", + "CERT_TYPE_CRL", + "CERT_TYPE_ARL", + "CERT_TYPE_SPKI", + "CERT_TYPE_X509_ATTR", + }; + +enum_names cert_names = + { CERT_TYPE_NONE, CERT_TYPE_X509_ATTR, cert_name, NULL }; + +#endif + /* Goal BITs for establishing an SA */ const char *const sa_policy_bit_names[] = { "POLICY_PSK", +#ifndef OPENSSL "POLICY_RSASIG", +#else + "POLICY_OPENSSL", +#endif "POLICY_ENCRYPT", "POLICY_AUTHENTICATE", "POLICY_TUNNEL", diff -ruN freeswan-1.5.orig/pluto/constants.h freeswan-1.5/pluto/constants.h --- freeswan-1.5.orig/pluto/constants.h Tue Jun 20 16:09:45 2000 +++ freeswan-1.5/pluto/constants.h Thu Oct 5 07:05:59 2000 @@ -433,17 +433,45 @@ #define ID_DER_ASN1_GN 10 #define ID_KEY_ID 11 +#ifdef OPENSSL +#define CERT_OPTION_SEND 0x01 +#define CERT_OPTION_PKCS7 0x02 +#define CERT_OPTION_PK 0x04 +#define CERT_OPTION_REV 0x08 +#define CERT_OPTION_STRICT 0x10 +#define CERT_OPTION_DSS_SHA 0x20 +#define CERT_OPTION_DSS_ALT 0x40 + +extern enum_names cert_names; + +#define CERT_TYPE_NONE 0 +#define CERT_TYPE_PKCS7 1 +#define CERT_TYPE_PGP 2 +#define CERT_TYPE_DNSKEY 3 +#define CERT_TYPE_X509_SIG 4 +#define CERT_TYPE_X509_KEX 5 +#define CERT_TYPE_KERBEROS 6 +#define CERT_TYPE_CRL 7 +#define CERT_TYPE_ARL 8 +#define CERT_TYPE_SPKI 9 +#define CERT_TYPE_X509_ATTR 10 +#endif + /* Policies for establishing an SA * * These are used to specify attributes (eg. encryption) and techniques for - * (eg PFS) required of an SA. POLICY_PSK and POLICY_RSASIG are for + * (eg PFS) required of an SA. POLICY_PSK and POLICY_RSASIG/OPENSSL are for * ISAKMP SAs; the rest are about IPsec SAs */ extern const char *const sa_policy_bit_names[]; #define POLICY_PSK LELEM(0) +#ifndef OPENSSL #define POLICY_RSASIG LELEM(1) +#else +#define POLICY_OPENSSL LELEM(1) +#endif #define POLICY_ENCRYPT LELEM(2) #define POLICY_AUTHENTICATE LELEM(3) @@ -451,7 +479,11 @@ #define POLICY_PFS LELEM(5) #define POLICY_ISAKMP_SHIFT 0 /* log2(POLICY_PSK) */ +#ifndef OPENSSL #define POLICY_ISAKMP_MASK (POLICY_PSK | POLICY_RSASIG) +#else +#define POLICY_ISAKMP_MASK (POLICY_PSK | POLICY_OPENSSL) +#endif #define POLICY_IPSEC_SHIFT 2 /* log2(POLICY_ENCRYPT) */ /* Oakley transform attributes diff -ruN freeswan-1.5.orig/pluto/crypto.h freeswan-1.5/pluto/crypto.h --- freeswan-1.5.orig/pluto/crypto.h Sun Dec 12 19:40:50 1999 +++ freeswan-1.5/pluto/crypto.h Thu Oct 5 07:05:59 2000 @@ -56,7 +56,11 @@ /* unification of cryptographic hashing mechanisms */ union hash_ctx { +#ifndef OPENSSL MD5_CTX ctx_md5; +#else + PLUTO_MD5_CTX ctx_md5; +#endif SHA1_CTX ctx_sha1; }; diff -ruN freeswan-1.5.orig/pluto/defs.c freeswan-1.5/pluto/defs.c --- freeswan-1.5.orig/pluto/defs.c Wed Jun 21 14:24:31 2000 +++ freeswan-1.5/pluto/defs.c Thu Oct 5 07:05:59 2000 @@ -22,6 +22,8 @@ #include "constants.h" #include "defs.h" #include "log.h" +#include "id.h" +#include "connections.h" /* needs id.h */ #include "whack.h" /* for RC_LOG_SERIOUS */ const chunk_t empty_chunk = { NULL, 0 }; diff -ruN freeswan-1.5.orig/pluto/defs.h freeswan-1.5/pluto/defs.h --- freeswan-1.5.orig/pluto/defs.h Fri Feb 25 17:37:10 2000 +++ freeswan-1.5/pluto/defs.h Thu Oct 5 07:05:59 2000 @@ -39,6 +39,22 @@ typedef unsigned long so_serial_t; #define SOS_NOBODY 0 /* null serial number */ +#ifdef OPENSSL +#include + +typedef union _keysched_u { + struct _des3_ks { des_key_schedule ks[3]; } des3_ks; + +#if 0 /* *Please* don't define this - you *really* don't want single DES */ + des_key_schedule des_ks; +#endif + +#ifdef NOTYET + /* Other ciphers, eg CAST, RC5, etc... */ +#endif + +} keysched; +#endif /* memory allocation */ diff -ruN freeswan-1.5.orig/pluto/demux.c freeswan-1.5/pluto/demux.c --- freeswan-1.5.orig/pluto/demux.c Wed Jun 21 14:24:32 2000 +++ freeswan-1.5/pluto/demux.c Thu Oct 5 07:05:59 2000 @@ -132,6 +132,10 @@ #include "whack.h" /* requires connections.h */ #include "server.h" +#ifdef OPENSSL +#include "openssl.h" +#endif + /* This file does basic header checking and demux of * incoming packets. */ @@ -234,11 +238,19 @@ { STATE_MAIN_R1, SMF_PKE_AUTH , P(KE) | P(ID) | P(NONCE), P(VID) | P(HASH), PT(KE) +#ifndef OPENSSL , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_RETRANSMIT, main_inI2_outR2_pk }, +#endif { STATE_MAIN_R1, SMF_RPKE_AUTH , P(NONCE) | P(KE) | P(ID), P(VID) | P(HASH) | P(CERT), PT(NONCE) +#ifndef OPENSSL , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_RETRANSMIT, main_inI2_outR2_rpk }, +#endif /* for states from here on, output message must be encrypted */ @@ -256,11 +268,19 @@ { STATE_MAIN_I2, SMF_PKE_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED , P(KE) | P(ID) | P(NONCE), P(VID), PT(HASH) +#ifndef OPENSSL , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_RETRANSMIT, main_inR2_outI3_pk /* ??? not yet implemented */ }, +#endif - { STATE_MAIN_I2, SMF_ALL_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED + { STATE_MAIN_I2, SMF_RPKE_AUTH | SMF_INITIATOR | SMF_OUTPUT_ENCRYPTED , P(NONCE) | P(KE) | P(ID), P(VID), PT(HASH) +#ifndef OPENSSL , EVENT_RETRANSMIT, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_RETRANSMIT, main_inR2_outI3_rpk /* ??? not yet implemented */ }, +#endif /* for states from here on, input message must be encrypted */ @@ -275,11 +295,19 @@ { STATE_MAIN_R2, SMF_DS_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED , P(ID) | P(SIG), P(VID) | P(CERT), PT(ID) +#ifndef OPENSSL , EVENT_SA_REPLACE, main_inI3_outR3 }, +#else + , EVENT_SA_REPLACE, main_inI3_outR3_whichds}, +#endif { STATE_MAIN_R2, SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED , P(HASH), P(VID), PT(HASH) +#ifndef OPENSSL , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_SA_REPLACE, main_inI3_outR3_pk}, +#endif /* STATE_MAIN_I3: R3 --> done * SMF_PSK_AUTH: HDR*, IDr1, HASH_R --> done @@ -293,11 +321,20 @@ { STATE_MAIN_I3, SMF_DS_AUTH | SMF_INITIATOR | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED , P(ID) | P(SIG), P(VID) | P(CERT), PT(NONE) +#ifndef OPENSSL , EVENT_SA_REPLACE, main_inR3 }, +#else + , EVENT_SA_REPLACE, main_inR3_whichds }, +#endif { STATE_MAIN_I3, SMF_PKE_AUTH | SMF_RPKE_AUTH | SMF_INITIATOR | SMF_FIRST_ENCRYPTED_INPUT | SMF_ENCRYPTED , P(HASH), P(VID), PT(NONE) +#ifndef OPENSSL , EVENT_SA_REPLACE, unexpected /* ??? not yet implemented */ }, +#else + , EVENT_SA_REPLACE, main_inR3_pk }, +#endif + /* STATE_MAIN_R3: can only get here due to packet loss */ { STATE_MAIN_R3, SMF_ALL_AUTH | SMF_ENCRYPTED | SMF_RETRANSMIT_ON_DUPLICATE, LEMPTY, LEMPTY @@ -397,7 +434,9 @@ struct connection *c = st->st_connection; mksin(peer, c->that.host_addr.s_addr, c->that.host_port); - DBG_cond_dump_chunk(DBG_RAW, "sending:\n", st->st_tpacket); + DBG(DBG_RAW, + DBG_cond_dump_chunk(DBG_RAW, "sending:\n", st->st_tpacket); + ); if (sendto(c->interface->fd , st->st_tpacket.ptr, st->st_tpacket.len, 0 , (struct sockaddr *)&peer, sizeof(peer)) != (ssize_t)st->st_tpacket.len) @@ -986,6 +1025,7 @@ { lset_t s = LELEM(np); +// loglog(RC_LOG_SERIOUS, "parsing: (%s)", enum_show(&payload_names, np)); if (0 == (s & (needed | smc->opt_payloads | LELEM(ISAKMP_NEXT_N) | LELEM(ISAKMP_NEXT_D)))) { @@ -998,6 +1038,220 @@ needed &= ~s; } +#ifdef OPENSSL + { + /* This code really shouldn't be here - it should be in */ + /* the ipsec_doi code section with all the other protocol */ + /* spec. Unfortunately, the "syntax checking" of the payload */ + /* is done here, and the "semantic checking" of the payload */ + /* is over in ipsec_doi. Since the fields of the ID payload */ + /* are encrypted, and the ipsec_doi code checks those fields */ + /* before processing the ID payload, we must ensure that the */ + /* payload is decrypted and stored in the identification */ + /* structure *before* the main mode routines process it. So */ + /* the code is here. */ + + if ((IS_PHASE1(md.from_state)) && (md.st)) { + if ((md.st->st_oakley.auth == OAKLEY_RSA_ENC) || (md.st->st_oakley.auth == OAKLEY_ELGAMAL_ENC)) { + if ((sd == &isakmp_identification_desc) || + (sd == &isakmp_nonce_desc)) { + /* We need to decrypt this packet */ + char cpl[65], ppl[65]; + u_int16_t paylen; + chunk_t ch; + + + paylen = ntohs(*((u_int16_t *)(&(md.message_pbs.cur[2])))); + clonetochunk(ch, &(md.message_pbs.cur[4]), paylen-4, "Ciphertext payload"); + + /* Make a copy chunk of the payload after the generic */ + /* header, which should be the ciphertext of the ID payload +*/ + if (sd == &isakmp_identification_desc) { + snprintf(cpl, sizeof(cpl)-1, "ID payload (ciphertext): "); + snprintf(ppl, sizeof(ppl)-1, "ID payload (plaintext): "); + } else if (sd == &isakmp_nonce_desc) { + snprintf(cpl, sizeof(cpl)-1, + "Nonce payload (ciphertext): "); + snprintf(ppl, sizeof(ppl)-1, + "Nonce payload (plaintext): "); + } else { + snprintf(cpl, sizeof(cpl)-1, + "Unknown payload (ciphertext): "); + snprintf(ppl, sizeof(ppl)-1, + "Unknown payload (plaintext): "); + } + + if (!valid_ciphertext_length(paylen - 4, md.st->st_connection)) { + log("Ciphertext length not a multiple of key modulus length for %s", cpl ); + free_md(&md); + return; + } + DBG(DBG_RAW, + DBG_dump_chunk(cpl, ch); + ); + privkey_decrypt_chunk( &ch, md.st ); + if ((ch.len == 0) || (ch.len > paylen)) { + /* Error - the decryption has failed */ + log("Decryption failure - malformed payload in packet"); + free_md(&md); + return; + } + DBG(DBG_RAW, + DBG_dump_chunk(ppl, ch); + ); + /* Now restructure the message_pbs block, so that the */ + /* plaintext is now stored in the ID payload space */ + /* Adjust the length first */ + *((u_int16_t *)(&(md.message_pbs.cur[2]))) = htons(ch.len + +4); + /* Now copy the plaintext */ + memcpy(&(md.message_pbs.cur[4]), ch.ptr, ch.len); + /* Clear the source plaintext */ + memset(ch.ptr, 0, ch.len); + /* Shift down all subsequent payloads */ + memmove(&(md.message_pbs.cur[ch.len+4]), + &(md.message_pbs.cur[paylen]), + md.message_pbs.roof - &(md.message_pbs.cur[paylen])); + /* Adjust the size of root downwards */ + md.message_pbs.roof -= paylen - ch.len - 4; + freeanychunk(ch); + } + } else if ((md.st->st_oakley.auth == OAKLEY_RSA_ENC_REV) || + (md.st->st_oakley.auth == OAKLEY_ELGAMAL_ENC_REV)) { + if ((sd == &isakmp_identification_desc) || + (sd == &isakmp_nonce_desc) || + (sd == &isakmp_keyex_desc) || + (sd == &isakmp_ipsec_certificate_desc)) { + if (sd == &isakmp_nonce_desc) { + /* Nonce is public key encrypted */ + u_int16_t paylen; + chunk_t ch; + + paylen = ntohs(*((u_int16_t *)(&(md.message_pbs.cur[2])))); + clonetochunk(ch, &(md.message_pbs.cur[4]), paylen-4, "Ciphertext payload"); + + if (!valid_ciphertext_length(paylen - 4, md.st->st_connection)) { + log("Ciphertext length not a multiple of key modulus length for nonce" ); + free_md(&md); + return; + } + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Nonce payload (ciphertext)", ch); + ); + privkey_decrypt_chunk( &ch, md.st ); + if ((ch.len == 0) || (ch.len > paylen)) { + /* Error - the decryption has failed */ + log("Decryption failure - malformed payload in packet"); + free_md(&md); + return; + } + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Nonce payload (plaintext)", ch); + ); + + if ((md.st->st_state == STATE_MAIN_I1) || + (md.st->st_state == STATE_MAIN_I2) || + (md.st->st_state == STATE_MAIN_I3) || + (md.st->st_state == STATE_MAIN_I4)) { + if (derive_symmetric_key(md.st, ch, + md.st->st_rcookie, + COOKIE_SIZE, + &md.st->st_ks_r, + &md.st->st_ne_r) < 0) { + log("Unable to derive symmetric key"); + free(&md); + return; + } + memset(&md.st->st_ne_r_iv, 0, MAX_DIGEST_LEN); + DBG(DBG_PARSING, + DBG_dump_chunk("Received Ke_r:", md.st->st_ne_r); + ); + } else { + if (derive_symmetric_key(md.st, ch, + md.st->st_icookie, + COOKIE_SIZE, + &md.st->st_ks_i, + &md.st->st_ne_i) < 0) { + log("Unable to derive symmetric key"); + free(&md); + return; + } + memset(&md.st->st_ne_i_iv, 0, MAX_DIGEST_LEN); + DBG(DBG_PARSING, + DBG_dump_chunk("Received Ke_i:", md.st->st_ne_i); + ); + } + + /* Now restructure the message_pbs block, so that the */ + /* plaintext is now stored in the payload space */ + /* Adjust the length first */ + *((u_int16_t *)(&(md.message_pbs.cur[2]))) = htons(ch.len + 4); + /* Now copy the plaintext */ + memcpy(&(md.message_pbs.cur[4]), ch.ptr, ch.len); + /* Clear the source plaintext */ + memset(ch.ptr, 0, ch.len); + /* Shift down all subsequent payloads */ + memmove(&(md.message_pbs.cur[ch.len+4]), + &(md.message_pbs.cur[paylen]), + md.message_pbs.roof - &(md.message_pbs.cur[paylen])); + /* Adjust the size of roof downwards */ + md.message_pbs.roof -= paylen - ch.len - 4; + + /* Discard the plaintext chunk */ + memset(ch.ptr, 0, ch.len); + freeanychunk(ch); + } else { + u_int16_t paylen; + chunk_t ch, pch; + + paylen = ntohs(*((u_int16_t *)(&(md.message_pbs.cur[2])))); + DBG(DBG_CRYPT, + DBG_log("Encrypted payload length = %d", paylen); + ); + clonetochunk(ch, &(md.message_pbs.cur[4]), paylen-4, "Ciphertext payload"); + setchunk(pch, NULL, 0); + if ((md.st->st_state == STATE_MAIN_I1) || + (md.st->st_state == STATE_MAIN_I2) || + (md.st->st_state == STATE_MAIN_I3) || + (md.st->st_state == STATE_MAIN_I4)) { + if (! decrypt_payload(md.st, &md.st->st_ks_r, + md.st->st_ne_r_iv, + ch, &pch) ) { + log("Unable to decrypt responder payload"); + free(&md); + return; + } + } else { + if (!decrypt_payload(md.st, &md.st->st_ks_i, + md.st->st_ne_i_iv, + ch, &pch) ) { + log("Unable to decrypt initiator payload"); + free(&md); + return; + } + } + *((u_int16_t *)(&(md.message_pbs.cur[2]))) = + htons(pch.len + 4); + /* Now copy the plaintext */ + memcpy(&(md.message_pbs.cur[4]), pch.ptr, pch.len); + /* Shift down all subsequent payloads */ + memmove(&(md.message_pbs.cur[pch.len+4]), + &(md.message_pbs.cur[paylen]), + md.message_pbs.roof - &(md.message_pbs.cur[paylen])); + /* Adjust the size of roof downwards */ + md.message_pbs.roof -= paylen - pch.len - 4; + + freeanychunk(ch); + memset(pch.ptr, 0, pch.len); + freeanychunk(pch); + } + } /* Otherwise, let everything else pass */ + } /* end if (revised PK mode ) */ + } /* end if Phase 1 */ + } /* end OPENSSL handling */ +#endif /* OPENSSL */ + if (!in_struct(&pd->payload, sd, &md.message_pbs, &pd->pbs)) { loglog(RC_LOG_SERIOUS, "%smalformed payload in packet", excuse); @@ -1141,25 +1395,25 @@ { loglog(RC_LOG_SERIOUS, "ignoring informational payload, type %s" , enum_show(&ipsec_notification_names, p->payload.notification.isan_type)); - DBG_cond_dump(DBG_PARSING, "info:", p->pbs.cur, pbs_left(&p->pbs)); + DBG_cond_dump(DBG_RAW, "info:", p->pbs.cur, pbs_left(&p->pbs)); } for (p = md.chain[ISAKMP_NEXT_D]; p != NULL; p = p->next) { loglog(RC_LOG_SERIOUS, "ignoring Delete SA payload"); - DBG_cond_dump(DBG_PARSING, "del:", p->pbs.cur, pbs_left(&p->pbs)); + DBG_cond_dump(DBG_RAW, "del:", p->pbs.cur, pbs_left(&p->pbs)); } for (p = md.chain[ISAKMP_NEXT_VID]; p != NULL; p = p->next) { loglog(RC_LOG_SERIOUS, "ignoring Vendor ID payload"); - DBG_cond_dump(DBG_PARSING, "VID:", p->pbs.cur, pbs_left(&p->pbs)); + DBG_cond_dump(DBG_RAW, "VID:", p->pbs.cur, pbs_left(&p->pbs)); } for (p = md.chain[ISAKMP_NEXT_CERT]; p != NULL; p = p->next) { loglog(RC_LOG_SERIOUS, "ignoring Certificate payload"); - DBG_cond_dump(DBG_PARSING, "CERT:", p->pbs.cur, pbs_left(&p->pbs)); + DBG_cond_dump(DBG_RAW, "CERT:", p->pbs.cur, pbs_left(&p->pbs)); } } @@ -1408,3 +1662,306 @@ } free_md(&md); } + +#ifdef OPENSSL +int +derive_symmetric_key( struct state *st, const chunk_t nonce, + const u_char *seedptr, const size_t seedlen, + keysched *ks, + chunk_t *key ) +{ + struct hmac_ctx ctx; + chunk_t k, kk, kchain; + chunk_t ne; + char nestr[8]; + int needed_len; + + DBG(DBG_PARSING, + DBG_dump_chunk("Deriving symmetric key from: ", nonce); + DBG_dump("Initial Cookie:", seedptr, seedlen); + ); + switch(st->st_oakley.encrypt) { +#if 0 + /* We really shouldn't deal with DES, */ + /* uncomment this if you must have it */ + case OAKLEY_DES_CBC: + needed_len = DES_CBC_BLOCK_SIZE; + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + needed_len = IDEA_CBC_KEY_LEN; + break; + case OAKLEY_BLOWFISH_CBC: + needed_len = BLOWFISH_CBC_KEY_LEN; + break; + case OAKLEY_RC5_R16_B64_CBC: + needed_len = RC5_CBC_KEY_LEN; + break; +#endif + case OAKLEY_3DES_CBC: + needed_len = DES_CBC_BLOCK_SIZE * 3; + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + needed_len = CAST_CBC_KEY_LEN; + break; +#endif + default: + log("Unknown block cipher ID for symmetric cipher"); + return -1; + } + + /* Generate initial decryption key material */ + /* key = prf(Nonce-X, Cookie-X); X={I,R} */ + ne.ptr = NULL; + hmac_init_chunk(&ctx, st->st_oakley.hasher, nonce); + hmac_update(&ctx, seedptr, seedlen); + hmac_final_chunk(ne, nestr, &ctx); + clonetochunk(*key, ne.ptr, ne.len, "Initial key material"); + + if (key->len < needed_len) { + freeanychunk(*key); + kchain.ptr = alloc_bytes(1, "initial zero octet"); + kchain.len = 1; + memset(kchain.ptr, 0, kchain.len); + while (key->len < needed_len) { + kk.ptr = NULL; + hmac_init_chunk(&ctx, st->st_oakley.hasher, ne); + hmac_update_chunk(&ctx, kchain); + hmac_final_chunk(kk, "extra key material", &ctx); + clonereplacechunk(kchain, kk.ptr, kk.len, "chain key material"); + /* Now append kk to key */ + if (! key->ptr ) { + clonetochunk(*key, kk.ptr, kk.len, "initial extra key material"); + } else { + k.ptr = alloc_bytes(key->len + kk.len, "appended key material"); + k.len = key->len + kk.len; + memcpy(k.ptr, key->ptr, key->len); + memcpy(&(k.ptr[key->len]), kk.ptr, kk.len); + freeanychunk(*key); + key->len = k.len; + key->ptr = k.ptr; + } + freeanychunk(kk); + } + freeanychunk(kchain); + } + + switch(st->st_oakley.encrypt) { +#if 0 + /* We really shouldn't deal with DES, */ + /* uncomment this if you must have it */ + case OAKLEY_DES_CBC: + (void)des_set_key((des_cblock *)key->ptr, ks->des_ks); + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + break; + case OAKLEY_BLOWFISH_CBC: + break; + case OAKLEY_RC5_R16_B64_CBC: + break; +#endif + case OAKLEY_3DES_CBC: + (void)des_set_key((des_cblock *)key->ptr + 0, ks->des3_ks.ks[0]); + (void)des_set_key((des_cblock *)key->ptr + 1, ks->des3_ks.ks[1]); + (void)des_set_key((des_cblock *)key->ptr + 2, ks->des3_ks.ks[2]); + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + break; +#endif + } + + return 0; +} + +bool +encrypt_payload( struct state *st, keysched *ks, u_char *iv, + chunk_t inp, chunk_t *out ) +{ + chunk_t temp; + int blocklen; + u_char pv; + + switch(st->st_oakley.encrypt) { +#if 0 + case OAKLEY_DES_CBC: + blocklen = DES_CBC_BLOCK_SIZE; + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + break; + case OAKLEY_BLOWFISH_CBC: + break; + case OAKLEY_RC5_R16_B64_CBC: + break; +#endif + case OAKLEY_3DES_CBC: + blocklen = DES_CBC_BLOCK_SIZE; + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + break; +#endif + } + + /* Pad the input up to a block size multiple */ + if (inp.len % blocklen == 0) { /* Add a full block for padding */ + pv = (u_char)blocklen; + temp.len = inp.len + (size_t)pv; + } else { + pv = (u_char)(blocklen - 1 - ((inp.len + blocklen - 1) % blocklen)); + temp.len = inp.len + (size_t)pv; + } + + temp.ptr = alloc_bytes(temp.len, "padded plaintext"); + memset(temp.ptr, 0, temp.len); + clonetochunk(*out, temp.ptr, temp.len, "ciphertext"); + memcpy(temp.ptr, inp.ptr, inp.len); + temp.ptr[temp.len-1] = pv; + + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Plaintext:", temp); + DBG_dump("Old IV: ", iv, blocklen); + ); + + switch(st->st_oakley.encrypt) { +#if 0 + case OAKLEY_DES_CBC: + des_ncbc_encrypt((des_cblock *)temp.ptr, (des_cblock *)out->ptr, + temp.len, ks->des_ks, (des_cblock *)iv, TRUE); + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + break; + case OAKLEY_BLOWFISH_CBC: + break; + case OAKLEY_RC5_R16_B64_CBC: + break; +#endif + case OAKLEY_3DES_CBC: + des_ede3_cbc_encrypt((des_cblock *)temp.ptr, (des_cblock *)out->ptr, + temp.len, + ks->des3_ks.ks[0], + ks->des3_ks.ks[1], + ks->des3_ks.ks[2], + (des_cblock *)iv, TRUE); + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + break; +#endif + } + + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Ciphertext: ", *out); + DBG_dump("New IV: ", iv, blocklen); + ); + freeanychunk(temp); + return TRUE; +} + +bool +decrypt_payload( struct state *st, keysched *ks, u_char *iv, + chunk_t inp, chunk_t *out ) +{ + chunk_t temp; + int blocklen; + + switch(st->st_oakley.encrypt) { +#if 0 + case OAKLEY_DES_CBC: + blocklen = DES_CBC_BLOCK_SIZE; + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + break; + case OAKLEY_BLOWFISH_CBC: + break; + case OAKLEY_RC5_R16_B64_CBC: + break; +#endif + case OAKLEY_3DES_CBC: + blocklen = DES_CBC_BLOCK_SIZE; + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + break; +#endif + } + + if (inp.len % blocklen != 0) { + log("Ciphertext is not a multiple of block length"); + return FALSE; + } + + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Ciphertext: ", inp); + DBG_dump("Old IV: ", iv, blocklen); + ); + + temp.ptr = alloc_bytes(inp.len, "temporary plaintext"); + temp.len = inp.len; + memset(temp.ptr, 0, temp.len); + + switch(st->st_oakley.encrypt) { +#if 0 + case OAKLEY_DES_CBC: + des_ncbc_encrypt((des_cblock *)inp.ptr, (des_cblock *)temp.ptr, + inp.len, ks->des_ks, (des_cblock *)iv, FALSE); + break; +#endif +#ifdef NOTYET + case OAKLEY_IDEA_CBC: + break; + case OAKLEY_BLOWFISH_CBC: + break; + case OAKLEY_RC5_R16_B64_CBC: + break; +#endif + case OAKLEY_3DES_CBC: + des_ede3_cbc_encrypt((des_cblock *)inp.ptr, (des_cblock *)temp.ptr, + inp.len, + ks->des3_ks.ks[0], + ks->des3_ks.ks[1], + ks->des3_ks.ks[2], + (des_cblock *)iv, FALSE); + break; +#ifdef NOTYET + case OAKLEY_CAST_CBC: + break; +#endif + } + + /* Strip padding from plaintext */ + if ((temp.ptr[temp.len-1] < (u_char)1) || + (temp.ptr[temp.len-1] > (u_char)blocklen) || + (temp.ptr[temp.len-1] > temp.len)) { + /* Impossible padding value */ + log("Impossible plaintext padding value: %d", temp.ptr[temp.len-1]); + memset(temp.ptr, 0, temp.len); + freeanychunk(temp); + return FALSE; + } + + out->len = temp.len - (size_t)(temp.ptr[temp.len-1]); + out->ptr = alloc_bytes(inp.len, "plaintext"); + memcpy(out->ptr, temp.ptr, out->len); + + DBG(DBG_PARSING | DBG_CRYPT, + DBG_dump_chunk("Plaintext: ", *out); + DBG_dump("New IV: ", iv, blocklen); + ); + + memset(temp.ptr, 0, temp.len); + freeanychunk(temp); + return TRUE; +} + +#endif diff -ruN freeswan-1.5.orig/pluto/demux.h freeswan-1.5/pluto/demux.h --- freeswan-1.5.orig/pluto/demux.h Tue Dec 14 15:31:02 1999 +++ freeswan-1.5/pluto/demux.h Thu Oct 5 07:05:59 2000 @@ -76,3 +76,17 @@ } stf_status; typedef stf_status state_transition_fn(struct msg_digest *md); + +#ifdef OPENSSL + +extern int derive_symmetric_key( struct state *st, const chunk_t nonce, + const u_char *seedptr, + const size_t seedlen, + keysched *ks, + chunk_t *key ); +extern bool encrypt_payload( struct state *st, keysched *ks, u_char *iv, + chunk_t inp, chunk_t *out ); +extern bool decrypt_payload( struct state *st, keysched *ks, u_char *iv, + chunk_t inp, chunk_t *out ); + +#endif /* OPENSSL */ diff -ruN freeswan-1.5.orig/pluto/dir2hash.c freeswan-1.5/pluto/dir2hash.c --- freeswan-1.5.orig/pluto/dir2hash.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/dir2hash.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,1071 @@ +/* + * Convert an XMAP hash directory to a Berkeley DB hash file + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DB185 +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#define VERSION_SIZE 4 +static unsigned char version_data[] = { '\00', '\00', '\00', '\01' }; + +#define TAGLEN 4 + +#define UID_PREFIX "uid-" + +#define X509_TYPE 0 +#define X509_CRL_TYPE 1 + +typedef struct _cert_st { + u_int8_t type; + union _inf { + X509 *x; + X509_CRL *c; + } info; + unsigned char *d; + size_t dlen; + unsigned char digest[SHA_DIGEST_LENGTH]; + struct _cert_st *next; +} CERT; + +typedef struct _xname_list_st { + unsigned char digest[SHA_DIGEST_LENGTH]; + struct _xname_list_st *next; +} XNAME_LIST; + +typedef struct _xname_st { + unsigned char *d; + size_t dlen; + int lcount; + XNAME_LIST *list; + struct _xname_st *next; +} XNAME; + +static const char *hashout = "certhash.db"; + +void *nalloc( size_t s ); +void dump( unsigned char *p, size_t l, const char *name ); +void free_xname_list_node( XNAME_LIST *node ); +void free_xname_node( XNAME *node ); +void free_xnames( XNAME **nl ); +XNAME *alloc_xname_node( const unsigned char *n, const size_t s ); +int add_unique_xname( XNAME **nl, X509_NAME *name, + const unsigned char *digest ); +int add_unique_xname( XNAME **nl, X509_NAME *name, + const unsigned char *digest ); +void add_unique_dns(XNAME **nl, X509 *x, const unsigned char *digest ); +void add_unique_ip(XNAME **nl, X509 *x, const unsigned char *digest ); +int isCA( X509 *x ); +void free_cert_node( CERT *c ); +int add_unique_xinf( CERT **cl, XNAME **xl, + XNAME **cal, XNAME **dnl, XNAME **ipl, + unsigned int type, void *x ); +void putcerts(DB *db, int start, int end, char *argv[], + CERT **cl, XNAME **xl, XNAME **cal, XNAME **dnl, XNAME **ipl); +void free_cert_list( CERT **cl ); +void putcrls(DB *db, int start, int end, char *argv[], CERT **cl, XNAME **xl); +int add_unique_uid(XNAME **ul, X509 *x, const char *uid); +void putuids(DB *db, int start, int end, char *argv[], XNAME **ul); + +void * +nalloc( size_t s ) +{ + void *t = malloc(s); + + if (!t) { + fprintf(stderr, "cannot allocate %d bytes\n", s); + exit(-1); + } + return t; +} + +void +dump( unsigned char *p, size_t l, const char *name ) +{ + int i; + + printf("%s: (length %d)\n", name, l); + for(i=0; id) free(node->d); + for(xl=node->list; xl;) { + p = xl->next; + free_xname_list_node(xl); + xl = p; + } + free(node); +} + +void +free_xnames( XNAME **nl ) +{ + XNAME *l, *p; + + if (!nl) return; + for(l = *nl; l; ) { + p = l->next; + free_xname_node(l); + l = p; + } + *nl = NULL; +} + +XNAME * +alloc_xname_node( const unsigned char *n, const size_t s ) +{ + /* Create an empty list for name store in n */ + XNAME *ret; + + ret = nalloc(sizeof(XNAME)); + ret->lcount = 0; + ret->list = NULL; + ret->dlen = s; + ret->d = nalloc(s); + memcpy(ret->d, n, s); + ret->next = NULL; + + return ret; +} + +int +add_unique_xname( XNAME **nl, X509_NAME *name, + const unsigned char *digest ) +{ + unsigned char *d, *dum; + size_t dlen; + XNAME *p; + int found, res = 1; + + /* Create DER form of name */ + + if (name == NULL) { + dlen = 1; + d = nalloc(dlen); + d[0] = '\0'; + } else { + dlen = i2d_X509_NAME(name, NULL); + d = nalloc(dlen); dum = d; + i2d_X509_NAME(name, &dum); + } + + /* Search nl for name */ + for (p=*nl, found = 0; ((!found) && (p)); + (!found) ? p=p->next : p) + if ((dlen == p->dlen) && (memcmp(d, p->d, dlen) == 0)) found = 1; + + if (found) { + XNAME_LIST *pp; + + /* add digest to list of names if not already there */ + for(pp = p->list, found = 0; ((!found) && (pp)); + (!found) ? pp=pp->next : pp) + if (memcmp(pp->digest, digest, SHA_DIGEST_LENGTH) == 0) found = 1; + + if (!found) { + XNAME_LIST *n = nalloc(sizeof(XNAME_LIST)); + n->next = p->list; + memcpy(n->digest, digest, SHA_DIGEST_LENGTH); + p->list = n; + p->lcount++; + } else + res = 0; + } else { + p = alloc_xname_node(d, dlen); + p->list = nalloc(sizeof(XNAME_LIST)); + p->list->next = NULL; + memcpy(p->list->digest, digest, SHA_DIGEST_LENGTH); + p->lcount = 1; + p->next = *nl; + *nl = p; + } + free(d); + return res; +} + +void +add_unique_dns(XNAME **nl, X509 *x, const unsigned char *digest ) +{ + unsigned char *d; + size_t dlen; + XNAME *p; + int found, res = 1; + int i, loc; + u_char *pp; + STACK_OF(GENERAL_NAME) *nsk = NULL; + X509_EXTENSION *ext; + GENERAL_NAME *gn; + + if ((loc = X509_get_ext_by_NID(x, NID_subject_alt_name, -1)) < 0) return; + if ((ext = X509_get_ext(x, loc)) == NULL) return; + pp = ext->value->data; + d2i_GENERAL_NAMES(&nsk, &pp, ext->value->length); + if (!nsk) return; + for(i=0; itype == GEN_DNS) { + dlen = gn->d.ia5->length; + d = nalloc(dlen); + memcpy(d, gn->d.ia5->data, dlen); + + for (p=*nl, found = 0; ((!found) && (p)); + (!found) ? p=p->next : p) + if ((dlen == p->dlen) && (memcmp(d, p->d, dlen) == 0)) found = 1; + + if (found) { + XNAME_LIST *pp; + + /* add digest to list of names if not already there */ + for(pp = p->list, found = 0; ((!found) && (pp)); + (!found) ? pp=pp->next : pp) + if (memcmp(pp->digest, digest, SHA_DIGEST_LENGTH) == 0) found = 1; + + if (!found) { + XNAME_LIST *n = nalloc(sizeof(XNAME_LIST)); + n->next = p->list; + memcpy(n->digest, digest, SHA_DIGEST_LENGTH); + p->list = n; + p->lcount++; + } else + res = 0; + } else { + p = alloc_xname_node(d, dlen); + p->list = nalloc(sizeof(XNAME_LIST)); + p->list->next = NULL; + memcpy(p->list->digest, digest, SHA_DIGEST_LENGTH); + p->lcount = 1; + p->next = *nl; + *nl = p; + } + free(d); + } + } + sk_GENERAL_NAME_free(nsk); +} + +void +add_unique_ip(XNAME **nl, X509 *x, const unsigned char *digest ) +{ + unsigned char *d; + size_t dlen; + XNAME *p; + int found, res = 1; + int i, loc; + u_char *pp; + STACK_OF(GENERAL_NAME) *nsk = NULL; + X509_EXTENSION *ext; + GENERAL_NAME *gn; + + if ((loc = X509_get_ext_by_NID(x, NID_subject_alt_name, -1)) < 0) return; + if ((ext = X509_get_ext(x, loc)) == NULL) return; + pp = ext->value->data; + d2i_GENERAL_NAMES(&nsk, &pp, ext->value->length); + if (!nsk) return; + for(i=0; itype == GEN_IPADD) { + dlen = gn->d.ip->length; + d = nalloc(dlen); + memcpy(d, gn->d.ip->data, dlen); + + for (p=*nl, found = 0; ((!found) && (p)); + (!found) ? p=p->next : p) + if ((dlen == p->dlen) && (memcmp(d, p->d, dlen) == 0)) found = 1; + + if (found) { + XNAME_LIST *pp; + + /* add digest to list of names if not already there */ + for(pp = p->list, found = 0; ((!found) && (pp)); + (!found) ? pp=pp->next : pp) + if (memcmp(pp->digest, digest, SHA_DIGEST_LENGTH) == 0) found = 1; + + if (!found) { + XNAME_LIST *n = nalloc(sizeof(XNAME_LIST)); + n->next = p->list; + memcpy(n->digest, digest, SHA_DIGEST_LENGTH); + p->list = n; + p->lcount++; + } else + res = 0; + } else { + p = alloc_xname_node(d, dlen); + p->list = nalloc(sizeof(XNAME_LIST)); + p->list->next = NULL; + memcpy(p->list->digest, digest, SHA_DIGEST_LENGTH); + p->lcount = 1; + p->next = *nl; + *nl = p; + } + free(d); + } + } + sk_GENERAL_NAME_free(nsk); +} + +int +isCA( X509 *x ) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + int loc; + + loc = X509_get_ext_by_NID(x, NID_basic_constraints, -1); + if (loc >= 0) { + ext = X509_get_ext(x, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + return 0; + } else { + BASIC_CONSTRAINTS_free(p); + return 1; /* This is a CA certificate */ + } + } else + return 0; + } else + return 0; +} + +void +free_cert_node( CERT *c ) +{ + if (c->d) free(c->d); + c->d = NULL; + switch(c->type) { + case X509_TYPE: if (c->info.x) X509_free(c->info.x); break; + case X509_CRL_TYPE: if (c->info.c) X509_CRL_free(c->info.c); break; + } + c->info.x = NULL; + memset(c->digest, 0, SHA_DIGEST_LENGTH); + c->next = NULL; + free(c); +} + +int +add_unique_xinf( CERT **cl, XNAME **xl, + XNAME **cal, XNAME **dnl, XNAME **ipl, + unsigned int type, void *x ) +{ + int f; + CERT *p, *node; + unsigned char *dum; + + if (!cl) return 0; + + node = nalloc(sizeof(CERT)); + node->next = NULL; + node->type = type; + switch(type) { + case X509_TYPE: + node->info.x = (X509 *)x; + node->dlen = i2d_X509((X509 *)x, NULL); + node->d = nalloc(node->dlen); + dum = node->d; + i2d_X509( (X509 *)x, &dum ); + break; + case X509_CRL_TYPE: + node->info.c = (X509_CRL *)x; + node->dlen = i2d_X509_CRL((X509_CRL *)x, NULL); + node->d = nalloc(node->dlen); + dum = node->d; + i2d_X509_CRL( (X509_CRL *)x, &dum ); + break; + default: + fprintf(stderr, "Unknown info type: %d\n", type); + free(node); + return 0; + } + + SHA1(node->d, node->dlen, node->digest); + + for(p=*cl, f=0; (!f) && (p); p=p->next) + if (memcmp(p->digest, node->digest, SHA_DIGEST_LENGTH) == 0) f=1; + /* found a duplicate */ + + if (!f) { + /* insert at head of list */ + node->next = *cl; + *cl = node; + switch(type) { + case X509_TYPE: + add_unique_xname(xl, X509_get_subject_name((X509 *)x), node->digest); + if (isCA(x)) add_unique_xname(cal, NULL, node->digest); + add_unique_dns(dnl, (X509 *)x, node->digest); + add_unique_ip(ipl, (X509 *)x, node->digest); + break; + case X509_CRL_TYPE: + add_unique_xname(xl, X509_CRL_get_issuer((X509_CRL *)x), node->digest); + break; + } + return 1; + } else + return 0; +} + +void +free_cert_list( CERT **cl ) +{ + CERT *c; + + if (!cl) return; + + for(c=*cl; c;) { + CERT *p = c->next; + + free_cert_node(c); + c = p; + } + *cl = NULL; +} + +static char dirname[PATH_MAX+1]; +static int +select_files(const struct dirent *de) +{ + struct stat st; + char path[PATH_MAX+1]; + + snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); + if (lstat(path, &st) == 0) { + if (S_ISREG(st.st_mode)) + return 1; + else + return 0; + } else { + perror("stat"); + return 0; + } +} + +static int +select_uid_files(const struct dirent *de) +{ + struct stat st; + char path[PATH_MAX+1]; + + if (strncasecmp(UID_PREFIX, de->d_name, strlen(UID_PREFIX)) != 0) return 0; + snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); + if (lstat(path, &st) == 0) { + if ((S_ISREG(st.st_mode)) || (S_ISLNK(st.st_mode))) + return 1; + else + return 0; + } else { + perror("stat"); + return 0; + } +} + +void +putcerts(DB *db, int start, int end, char *argv[], + CERT **cl, XNAME **xl, XNAME **cal, XNAME **dnl, XNAME **ipl) +{ + int i, j, n; + struct dirent **namelist = NULL; + char path[PATH_MAX+1]; + + for(i=start; i= 0) { + for(j=0; jd_name); + snprintf(path, sizeof(path), "%s/%s", argv[i], namelist[j]->d_name); + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + if (!(x = PEM_read_bio_X509(b, NULL, NULL, NULL))) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); + } + if ((x) && (!add_unique_xinf(cl, xl, cal, dnl, ipl, X509_TYPE, x))) + X509_free(x); + BIO_free(b); + } + } + free(namelist); + } else + perror("scandir"); + } +} + +void +putcrls(DB *db, int start, int end, char *argv[], CERT **cl, XNAME **xl) +{ + int i, j, n; + struct dirent **namelist = NULL; + char path[PATH_MAX+1]; + + for(i=start; i= 0) { + for(j=0; jd_name); + snprintf(path, sizeof(path), "%s/%s", argv[i], namelist[j]->d_name); + if ((b = BIO_new_file(path, "r")) != NULL) { + X509_CRL *c; + + if (!(c = PEM_read_bio_X509_CRL(b, NULL, NULL, NULL))) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + c = d2i_X509_CRL_bio(b, NULL); + } + if ((c) && + (!add_unique_xinf(cl, xl, NULL, NULL, NULL,X509_CRL_TYPE, c))) + X509_CRL_free(c); + + BIO_free(b); + } + } + free(namelist); + } else + perror("scandir"); + } +} + +int +add_unique_uid(XNAME **ul, X509 *x, const char *uid) +{ + int found; + XNAME *p; + unsigned char digest[SHA_DIGEST_LENGTH], *d, *dum; + size_t dlen; + + if (!x || !ul) return 0; + + dlen = i2d_X509(x, NULL); + d = nalloc(dlen); + dum = d; + i2d_X509( x, &dum ); + + SHA1(d, dlen, digest); + free(d); + + printf("Adding user id \"%s\"\n", uid); + for(found=0, p=*ul; ((!found) && (p)); + (!found) ? p=p->next : p) + if ((strlen(uid) == p->dlen) && (memcmp(p->d, uid, p->dlen) == 0)) + found = 1; + + if (found) { + XNAME_LIST *pp; + + for(pp = p->list, found = 0; ((!found) && (pp)); + pp=(!found) ? pp->next : pp) + if (memcmp(pp->digest, digest, SHA_DIGEST_LENGTH) == 0) found = 1; + + if (!found) { + XNAME_LIST *n = nalloc(sizeof(XNAME_LIST)); + n->next = p->list; + memcpy(n->digest, digest, SHA_DIGEST_LENGTH); + p->list = n; + p->lcount++; + } else + return 0; + } else { + p = alloc_xname_node(uid, strlen(uid)); + p->list = nalloc(sizeof(XNAME_LIST)); + p->list->next = NULL; + memcpy(p->list->digest, digest, SHA_DIGEST_LENGTH); + p->lcount = 1; + p->next = *ul; + *ul = p; + return 1; + } + return 0; +} + +static int +isuidchar( char c ) +{ + if (isalnum(c)) return 1; + if (c == '_') return 1; + return 0; +} + +void +putuids(DB *db, int start, int end, char *argv[], XNAME **ul) +{ + int i, j, n; + struct dirent **namelist = NULL; + char path[PATH_MAX+1]; + + for(i=start; i= 0) { + for(j=0; jd_name); + snprintf(path, sizeof(path), "%s/%s", argv[i], namelist[j]->d_name); + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + if (!(x = PEM_read_bio_X509(b, NULL, NULL, NULL))) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); + } + + n = nn = strdup(&(namelist[j]->d_name[strlen(UID_PREFIX)])); + while ((*nn) && (isuidchar(*nn))) nn++; + *nn = '\0'; + if ((x) && (!add_unique_uid(ul, x, n))) X509_free(x); + + BIO_free(b); + } + } + free(namelist); + } else + perror("scandir"); + } +} + +int +main( int argc, char *argv[] ) +{ + int c; + DB *db; + CERT *clist = NULL, *crlist = NULL; + XNAME *xlist = NULL, *nlist = NULL, *xcrlist = NULL, *calist = NULL; + XNAME *dnlist = NULL, *iplist = NULL; + + while(1) { + int option_index = 0; + static struct option long_options[] = { + { "output", 1, 0, 0 }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "o:", + long_options, &option_index); + if (c == -1) break; + + switch(c) { + case 0: + if (strcasecmp(long_options[option_index].name, "output") == 0) + hashout = optarg; + break; + case 'o': + hashout = optarg; + break; + case '?': + break; + default: + printf("?? getopt returned character code 0%o ??\n", c); + } + } + + X509V3_add_standard_extensions(); + + if ((db = dbopen(hashout, O_CREAT | O_TRUNC | O_RDWR, 0644, DB_HASH, NULL)) == NULL) { + perror("dbopen"); + } else { + DBT k, v; + CERT *c; + XNAME *x; + unsigned char *kk, *vv; + + memset(&k, 0, sizeof(DBT)); + memset(&v, 0, sizeof(DBT)); + k.data = strdup("\xFF\xFF\xFF\xFFversion"); + k.size = strlen("version") + TAGLEN; + v.data = version_data; + v.size = VERSION_SIZE; + + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + db->sync(db, 0); + free(k.data); + + putcerts(db, optind, argc, argv, + &clist, &xlist, + &calist, &dnlist, &iplist); + putcrls(db, optind, argc, argv, &crlist, &xcrlist); + putuids(db, optind, argc, argv, &nlist); + + for(c=clist; c; c=c->next) { + k.size = SHA_DIGEST_LENGTH+TAGLEN; + k.data = nalloc(k.size); + v.size = c->dlen; + v.data = nalloc(v.size); + + kk = k.data; + vv = v.data; + memset(kk, 0, TAGLEN); + memcpy(&kk[TAGLEN], c->digest, SHA_DIGEST_LENGTH); + memcpy(vv, c->d, c->dlen); + + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_cert_list(&clist); + + for(x=xlist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = x->dlen + TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\01'; + memcpy(&kk[TAGLEN], x->d, x->dlen); + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) < 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&xlist); + + for(x=calist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\05'; + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&calist); + + for(x=dnlist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = x->dlen + TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\06'; + memcpy(&kk[TAGLEN], x->d, x->dlen); + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&dnlist); + + for(x=iplist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = x->dlen + TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\07'; + memcpy(&kk[TAGLEN], x->d, x->dlen); + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&iplist); + + for(c=crlist; c; c=c->next) { + k.size = SHA_DIGEST_LENGTH+TAGLEN; + k.data = nalloc(k.size); + v.size = c->dlen; + v.data = nalloc(v.size); + + kk = k.data; + vv = v.data; + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\04'; + memcpy(&kk[TAGLEN], c->digest, SHA_DIGEST_LENGTH); + memcpy(vv, c->d, c->dlen); + + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_cert_list(&crlist); + + for(x=xcrlist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = x->dlen + TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\02'; + memcpy(&kk[TAGLEN], x->d, x->dlen); + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&xcrlist); + + for(x=nlist; x; x=x->next) { + u_int32_t cnt; + int i; + XNAME_LIST *p; + + k.size = x->dlen + TAGLEN; + k.data = nalloc(k.size); + v.size = TAGLEN + x->lcount * SHA_DIGEST_LENGTH; + v.data = nalloc(v.size); + kk = k.data; + vv = v.data; + + memset(kk, 0, TAGLEN-1); kk[TAGLEN-1] = '\03'; + memcpy(&kk[TAGLEN], x->d, x->dlen); + cnt = htonl(x->lcount); + memcpy(vv, (unsigned char *)&cnt, TAGLEN); + for(i=0, p=x->list; ilcount; i++, p=p->next) + memcpy(&vv[TAGLEN + i*SHA_DIGEST_LENGTH], + p->digest, SHA_DIGEST_LENGTH); + if (db->put(db, &k, &v, 0) != 0) perror("dbput"); + free(k.data); + free(v.data); + } + free_xnames(&nlist); + db->close(db); + } + + printf("Dumping keys from DB file\n"); + if ((db = dbopen(hashout, 0, 0, DB_HASH, NULL)) == NULL) { + perror("db_open"); + } else { + DBT k, v; + int i; + + memset(&k, 0, sizeof(k)); + memset(&v, 0, sizeof(v)); + while (db->seq(db, &k, &v, R_NEXT) == 0) { + if (k.size < TAGLEN) + fprintf(stderr, "Error: key value too short\n"); + else { + unsigned char *kk = k.data, *vv = v.data, *p; + u_int32_t c1, c2; + X509_NAME *xn; + + switch(kk[TAGLEN-1]) { + case 0: + printf("X509 Certificate\n"); + dump(&kk[TAGLEN], k.size-TAGLEN, "SHA-DIGEST"); + printf("Data Length %d\n", v.size); + break; + case 1: + printf("X509 Subject Name\n"); + p = &kk[TAGLEN]; + if ((xn = d2i_X509_NAME(NULL, &p, k.size-TAGLEN)) != NULL) { + char buf[256]; + + X509_NAME_oneline(xn, buf, sizeof(buf)); + printf("%s:\n", buf); + X509_NAME_free(xn); + memcpy((unsigned char *)(&c1), v.data, TAGLEN); + c2 = ntohl(c1); + if (v.size != TAGLEN + (SHA_DIGEST_LENGTH * c2)) { + fprintf(stderr, "X509 name length error\n"); + } else { + for(i=0; iclose(db); + } + printf("Done\n"); + + X509V3_EXT_cleanup(); + return 0; +} + + diff -ruN freeswan-1.5.orig/pluto/dsa.c freeswan-1.5/pluto/dsa.c --- freeswan-1.5.orig/pluto/dsa.c Mon Nov 1 16:52:39 1999 +++ freeswan-1.5/pluto/dsa.c Wed Dec 31 19:00:00 1969 @@ -1,476 +0,0 @@ -/* dsa.c - DSA signature scheme - * Copyright (C) 1998 Free Software Foundation, Inc. - * - * This file is part of GnuPG. - * - * GnuPG is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * GnuPG is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - */ - -#ifdef PLUTO -#include -#include -#include "constants.h" -#include "defs.h" -#include "log.h" -#include "rnd.h" -#include "gcryptfix.h" -#else /*! PLUTO */ -/* #include */ -#endif /* !PLUTO */ - -#include -#include -#include - -#ifndef PLUTO -/* #include */ -/* #include "util.h" */ -/* #include "mpi.h" */ -/* #include "cipher.h" */ -#endif - -#include "dsa.h" - -typedef struct { - MPI p; /* prime */ - MPI q; /* group order */ - MPI g; /* group generator */ - MPI y; /* g^x mod p */ -} DSA_public_key; - - -typedef struct { - MPI p; /* prime */ - MPI q; /* group order */ - MPI g; /* group generator */ - MPI y; /* g^x mod p */ - MPI x; /* secret exponent */ -} DSA_secret_key; - - -static MPI gen_k( MPI q ); -static void test_keys( DSA_secret_key *sk, unsigned qbits ); -static int check_secret_key( DSA_secret_key *sk ); -static void generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ); -static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey); -static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey); - -static void -progress( int c ) -{ - fputc( c, stderr ); -} - - -/**************** - * Generate a random secret exponent k less than q - */ -static MPI -gen_k( MPI q ) -{ - MPI k = mpi_alloc_secure( mpi_get_nlimbs(q) ); - unsigned int nbits = mpi_get_nbits(q); - unsigned int nbytes = (nbits+7)/8; - char *rndbuf = NULL; - - if( DBG_CIPHER ) - log_debug("choosing a random k "); - for(;;) { - if( DBG_CIPHER ) - progress('.'); - - if( !rndbuf || nbits < 32 ) { - m_free(rndbuf); - rndbuf = get_random_bits( nbits, 1, 1 ); - } - else { /* change only some of the higher bits */ - /* we could imporove this by directly requesting more memory - * at the first call to get_random_bits() and use this the here - * maybe it is easier to do this directly in random.c */ - char *pp = get_random_bits( 32, 1, 1 ); - memcpy( rndbuf,pp, 4 ); - m_free(pp); - } - mpi_set_buffer( k, rndbuf, nbytes, 0 ); - if( mpi_test_bit( k, nbits-1 ) ) - mpi_set_highbit( k, nbits-1 ); - else { - mpi_set_highbit( k, nbits-1 ); - mpi_clear_bit( k, nbits-1 ); - } - - if( !(mpi_cmp( k, q ) < 0) ) { /* check: k < q */ - if( DBG_CIPHER ) - progress('+'); - continue; /* no */ - } - if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */ - if( DBG_CIPHER ) - progress('-'); - continue; /* no */ - } - break; /* okay */ - } - m_free(rndbuf); - if( DBG_CIPHER ) - progress('\n'); - - return k; -} - - -static void -test_keys( DSA_secret_key *sk, unsigned qbits ) -{ - DSA_public_key pk; - MPI test = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); - MPI out1_a = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); - MPI out1_b = mpi_alloc( qbits / BITS_PER_MPI_LIMB ); - - pk.p = sk->p; - pk.q = sk->q; - pk.g = sk->g; - pk.y = sk->y; - /*mpi_set_bytes( test, qbits, get_random_byte, 0 );*/ - { char *p = get_random_bits( qbits, 0, 0 ); - mpi_set_buffer( test, p, (qbits+7)/8, 0 ); - m_free(p); - } - - sign( out1_a, out1_b, test, sk ); - if( !verify( out1_a, out1_b, test, &pk ) ) - log_fatal("DSA:: sign, verify failed\n"); - - mpi_free( test ); - mpi_free( out1_a ); - mpi_free( out1_b ); -} - - - -/**************** - * Generate a DSA key pair with a key of size NBITS - * Returns: 2 structures filled with all needed values - * and an array with the n-1 factors of (p-1) - */ -static void -generate( DSA_secret_key *sk, unsigned nbits, MPI **ret_factors ) -{ - MPI p; /* the prime */ - MPI q; /* the 160 bit prime factor */ - MPI g; /* the generator */ - MPI y; /* g^x mod p */ - MPI x; /* the secret exponent */ - MPI h, e; /* helper */ - unsigned qbits; - byte *rndbuf; - - assert( nbits >= 512 && nbits <= 1024 ); - - qbits = 160; - p = generate_elg_prime( 1, nbits, qbits, NULL, ret_factors ); - /* get q out of factors */ - q = mpi_copy((*ret_factors)[0]); - if( mpi_get_nbits(q) != qbits ) - BUG(); - - /* find a generator g (h and e are helpers)*/ - /* e = (p-1)/q */ - e = mpi_alloc( mpi_get_nlimbs(p) ); - mpi_sub_ui( e, p, 1 ); - mpi_fdiv_q( e, e, q ); - g = mpi_alloc( mpi_get_nlimbs(p) ); - h = mpi_alloc_set_ui( 1 ); /* we start with 2 */ - do { - mpi_add_ui( h, h, 1 ); - /* g = h^e mod p */ - mpi_powm( g, h, e, p ); - } while( !mpi_cmp_ui( g, 1 ) ); /* continue until g != 1 */ - - /* select a random number which has these properties: - * 0 < x < q-1 - * This must be a very good random number because this - * is the secret part. */ - if( DBG_CIPHER ) - log_debug("choosing a random x "); - assert( qbits >= 160 ); - x = mpi_alloc_secure( mpi_get_nlimbs(q) ); - mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ - rndbuf = NULL; - do { - if( DBG_CIPHER ) - progress('.'); - if( !rndbuf ) - rndbuf = get_random_bits( qbits, 2, 1 ); - else { /* change only some of the higher bits (= 2 bytes)*/ - char *r = get_random_bits( 16, 2, 1 ); - memcpy(rndbuf, r, 16/8 ); - m_free(r); - } - mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); - mpi_clear_highbit( x, qbits+1 ); - } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); - m_free(rndbuf); - mpi_free( e ); - mpi_free( h ); - - /* y = g^x mod p */ - y = mpi_alloc( mpi_get_nlimbs(p) ); - mpi_powm( y, g, x, p ); - - if( DBG_CIPHER ) { - progress('\n'); - log_mpidump("dsa p= ", p ); - log_mpidump("dsa q= ", q ); - log_mpidump("dsa g= ", g ); - log_mpidump("dsa y= ", y ); - log_mpidump("dsa x= ", x ); - } - - /* copy the stuff to the key structures */ - sk->p = p; - sk->q = q; - sk->g = g; - sk->y = y; - sk->x = x; - - /* now we can test our keys (this should never fail!) */ - test_keys( sk, qbits ); -} - - - -/**************** - * Test whether the secret key is valid. - * Returns: if this is a valid key. - */ -static int -check_secret_key( DSA_secret_key *sk ) -{ - int rc; - MPI y = mpi_alloc( mpi_get_nlimbs(sk->y) ); - - mpi_powm( y, sk->g, sk->x, sk->p ); - rc = !mpi_cmp( y, sk->y ); - mpi_free( y ); - return rc; -} - - - -/**************** - * Make a DSA signature from HASH and put it into r and s. - */ - -static void -sign(MPI r, MPI s, MPI hash, DSA_secret_key *skey ) -{ - MPI k; - MPI kinv; - MPI tmp; - - /* select a random k with 0 < k < q */ - k = gen_k( skey->q ); - - /* r = (a^k mod p) mod q */ - mpi_powm( r, skey->g, k, skey->p ); - mpi_fdiv_r( r, r, skey->q ); - - /* kinv = k^(-1) mod q */ - kinv = mpi_alloc( mpi_get_nlimbs(k) ); - mpi_invm(kinv, k, skey->q ); - - /* s = (kinv * ( hash + x * r)) mod q */ - tmp = mpi_alloc( mpi_get_nlimbs(skey->p) ); - mpi_mul( tmp, skey->x, r ); - mpi_add( tmp, tmp, hash ); - mpi_mulm( s , kinv, tmp, skey->q ); - - mpi_free(k); - mpi_free(kinv); - mpi_free(tmp); -} - - -/**************** - * Returns true if the signature composed from R and S is valid. - */ -static int -verify(MPI r, MPI s, MPI hash, DSA_public_key *pkey ) -{ - int rc; - MPI w, u1, u2, v; - MPI base[3]; - MPI exp[3]; - - - if( !(mpi_cmp_ui( r, 0 ) > 0 && mpi_cmp( r, pkey->q ) < 0) ) - return 0; /* assertion 0 < r < q failed */ - if( !(mpi_cmp_ui( s, 0 ) > 0 && mpi_cmp( s, pkey->q ) < 0) ) - return 0; /* assertion 0 < s < q failed */ - - w = mpi_alloc( mpi_get_nlimbs(pkey->q) ); - u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); - u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) ); - v = mpi_alloc( mpi_get_nlimbs(pkey->p) ); - - /* w = s^(-1) mod q */ - mpi_invm( w, s, pkey->q ); - - /* u1 = (hash * w) mod q */ - mpi_mulm( u1, hash, w, pkey->q ); - - /* u2 = r * w mod q */ - mpi_mulm( u2, r, w, pkey->q ); - - /* v = g^u1 * y^u2 mod p mod q */ - base[0] = pkey->g; exp[0] = u1; - base[1] = pkey->y; exp[1] = u2; - base[2] = NULL; exp[2] = NULL; - mpi_mulpowm( v, base, exp, pkey->p ); - mpi_fdiv_r( v, v, pkey->q ); - - rc = !mpi_cmp( v, r ); - - mpi_free(w); - mpi_free(u1); - mpi_free(u2); - mpi_free(v); - return rc; -} - - -/********************************************* - ************** interface ****************** - *********************************************/ - -int -dsa_generate( int algo, unsigned nbits, MPI *skey, MPI **retfactors ) -{ - DSA_secret_key sk; - - if( algo != PUBKEY_ALGO_DSA ) - return G10ERR_PUBKEY_ALGO; - - generate( &sk, nbits, retfactors ); - skey[0] = sk.p; - skey[1] = sk.q; - skey[2] = sk.g; - skey[3] = sk.y; - skey[4] = sk.x; - return 0; -} - - -int -dsa_check_secret_key( int algo, MPI *skey ) -{ - DSA_secret_key sk; - - if( algo != PUBKEY_ALGO_DSA ) - return G10ERR_PUBKEY_ALGO; - if( !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] ) - return G10ERR_BAD_MPI; - - sk.p = skey[0]; - sk.q = skey[1]; - sk.g = skey[2]; - sk.y = skey[3]; - sk.x = skey[4]; - if( !check_secret_key( &sk ) ) - return G10ERR_BAD_SECKEY; - - return 0; -} - - - -int -dsa_sign( int algo, MPI *resarr, MPI data, MPI *skey ) -{ - DSA_secret_key sk; - - if( algo != PUBKEY_ALGO_DSA ) - return G10ERR_PUBKEY_ALGO; - if( !data || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] ) - return G10ERR_BAD_MPI; - - sk.p = skey[0]; - sk.q = skey[1]; - sk.g = skey[2]; - sk.y = skey[3]; - sk.x = skey[4]; - resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); - resarr[1] = mpi_alloc( mpi_get_nlimbs( sk.p ) ); - sign( resarr[0], resarr[1], data, &sk ); - return 0; -} - -int -dsa_verify( int algo, MPI hash, MPI *data, MPI *pkey, - int (*cmp)(void *, MPI) UNUSED, void *opaquev UNUSED) -{ - DSA_public_key pk; - - if( algo != PUBKEY_ALGO_DSA ) - return G10ERR_PUBKEY_ALGO; - if( !data[0] || !data[1] || !hash - || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] ) - return G10ERR_BAD_MPI; - - pk.p = pkey[0]; - pk.q = pkey[1]; - pk.g = pkey[2]; - pk.y = pkey[3]; - if( !verify( data[0], data[1], hash, &pk ) ) - return G10ERR_BAD_SIGN; - return 0; -} - - - -unsigned -dsa_get_nbits( int algo, MPI *pkey ) -{ - if( algo != PUBKEY_ALGO_DSA ) - return 0; - return mpi_get_nbits( pkey[0] ); -} - - -/**************** - * Return some information about the algorithm. We need algo here to - * distinguish different flavors of the algorithm. - * Returns: A pointer to string describing the algorithm or NULL if - * the ALGO is invalid. - * Usage: Bit 0 set : allows signing - * 1 set : allows encryption - */ -const char * -dsa_get_info( int algo, int *npkey, int *nskey, int *nenc, int *nsig, - int *use ) -{ - *npkey = 4; - *nskey = 5; - *nenc = 0; - *nsig = 2; - - switch( algo ) { - case PUBKEY_ALGO_DSA: *use = PUBKEY_USAGE_SIG; return "DSA"; - default: *use = 0; return NULL; - } -} - - diff -ruN freeswan-1.5.orig/pluto/dsa.h freeswan-1.5/pluto/dsa.h --- freeswan-1.5.orig/pluto/dsa.h Mon Nov 1 16:48:08 1999 +++ freeswan-1.5/pluto/dsa.h Wed Dec 31 19:00:00 1969 @@ -1,32 +0,0 @@ -/* 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 -ruN freeswan-1.5.orig/pluto/g10_dsa.c freeswan-1.5/pluto/g10_dsa.c --- freeswan-1.5.orig/pluto/g10_dsa.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/g10_dsa.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,476 @@ +/* g10_dsa.c - DSA signature scheme + * Copyright (C) 1998 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#ifdef PLUTO +#include +#include +#include "constants.h" +#include "defs.h" +#include "log.h" +#include "rnd.h" +#include "gcryptfix.h" +#else /*! PLUTO */ +/* #include */ +#endif /* !PLUTO */ + +#include +#include +#include + +#ifndef PLUTO +/* #include */ +/* #include "util.h" */ +/* #include "mpi.h" */ +/* #include "cipher.h" */ +#endif + +#include "g10_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 -ruN freeswan-1.5.orig/pluto/g10_dsa.h freeswan-1.5/pluto/g10_dsa.h --- freeswan-1.5.orig/pluto/g10_dsa.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/g10_dsa.h Thu Oct 5 07:05:59 2000 @@ -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 -ruN freeswan-1.5.orig/pluto/ipsec_doi.c freeswan-1.5/pluto/ipsec_doi.c --- freeswan-1.5.orig/pluto/ipsec_doi.c Wed Jun 21 14:24:32 2000 +++ freeswan-1.5/pluto/ipsec_doi.c Thu Oct 5 07:05:59 2000 @@ -47,6 +47,10 @@ #include "md5.h" #include "crypto.h" /* requires sha1.h and md5.h */ +#ifdef OPENSSL +#include "openssl.h" +#endif + /* MAGIC: perform f, a function that returns notification_t * and return from the ENCLOSING stf_status returning function if it fails. */ @@ -56,6 +60,65 @@ /* 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. */ +#ifdef OPENSSL +static bool +build_and_ship_KE_rpk(struct state *st, bool init, + chunk_t *g, + const struct oakley_group_desc *group, pb_stream *outs, + u_int8_t np) +{ + keysched *ks; + u_char *iv; + chunk_t op; + bool res; + + 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); + *g = mpz_to_n(&mp_g, group->bytes); + mpz_clear(&mp_g); +#ifdef DODGE_DH_MISSING_ZERO_BUG + if (g->ptr[0] == 0) + { + /* generate a new secret to avoid this situation */ + log("regenerating DH private secret to avoid Pluto 1.0 bug" + " handling public value with leading zero"); + mpz_clear(&st->st_sec); + st->st_sec_in_use = FALSE; + freeanychunk(*g); + return build_and_ship_KE_rpk(st, init, g, group, outs, np); + } +#endif + + DBG(DBG_CRYPT, + DBG_dump("Local DH secret:\n", tmp, LOCALSECRETSIZE); + DBG_dump_chunk("Public DH value sent:\n", *g)); + } + /* Encrypt the chunk */ + if (init) { + ks = &st->st_ks_i; + iv = st->st_ne_i_iv; + } else { + ks = &st->st_ks_r; + iv = st->st_ne_r_iv; + } + setchunk(op, NULL, 0); + if (!encrypt_payload( st, ks, iv, *g, &op )) return FALSE; + res = out_generic_chunk(np, &isakmp_keyex_desc, outs, op, + "encrypted keyex value"); + freeanychunk(op); + return res; +} +#endif + 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) @@ -192,6 +255,21 @@ return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name); } +#ifdef OPENSSL +static bool +build_and_ship_nonce_pk(struct state *st, chunk_t *n, pb_stream *outs, u_int8_t np, const char *name) +{ + chunk_t c; + + setchunk(*n, alloc_bytes(DEFAULT_NONCE_SIZE, name), DEFAULT_NONCE_SIZE); + get_rnd_bytes(n->ptr, DEFAULT_NONCE_SIZE); + c.len = n->len; + c.ptr = clone_bytes(n->ptr, c.len, name); + pubkey_encrypt_chunk(&c, st); + return out_generic_chunk(np, &isakmp_nonce_desc, outs, c, name); +} +#endif + /* * Send a notification to the peer. We could make a decision on * whether to send the notification, based on the type and the @@ -290,6 +368,9 @@ struct state *st; + DBG(DBG_PARSING, + DBG_log("in main_outI1"); + ); /* set up new state */ cur_state = st = new_state(); st->st_connection = c; @@ -347,7 +428,11 @@ if (get_RSA_private_key(c) != NULL && get_RSA_public_key(&c->that.id) != NULL) +#ifndef OPENSSL auth_policy |= POLICY_RSASIG; +#else + auth_policy |= POLICY_OPENSSL; +#endif /* Not clear what we should do if neither is possible. * Perhaps we should not have entered negotiations at all. */ @@ -358,6 +443,23 @@ return STF_INTERNAL_ERROR; } } + +#ifdef OPENSSL +if (use_openssl(c)) +{ + loglog(RC_LOG_SERIOUS, "Using OPENSSL sadb."); + if (!out_sa(&rbody + , &oakley_sadb2[auth_policy >> POLICY_ISAKMP_SHIFT] + , st, TRUE, ISAKMP_NEXT_NONE)) + { + cur_state = NULL; + return STF_INTERNAL_ERROR; + } +} +else +{ +#endif /* OPENSSL */ + loglog(RC_LOG_SERIOUS, "Using standard sadb."); if (!out_sa(&rbody , &oakley_sadb[auth_policy >> POLICY_ISAKMP_SHIFT] , st, TRUE, ISAKMP_NEXT_NONE)) @@ -365,6 +467,10 @@ cur_state = NULL; return STF_INTERNAL_ERROR; } +#ifdef OPENSSL +} +#endif /* ! OPENSSL */ + /* save initiator SA for later HASH */ passert(st->st_p1isa.ptr == NULL); /* no leak! (MUST be first time) */ @@ -517,6 +623,70 @@ hmac_final_chunk(st->st_skeyid, "st_skeyid in skeyid_digisig()", &ctx); return TRUE; } +// static bool +// skeyid_digisig(struct state *st) +// { +// struct hmac_ctx ctx; +// const chunk_t cat = chunk_concat( st->st_ni, st->st_nr ); +// +// hmac_init_chunk(&ctx, st->st_oakley.hasher, cat); +// hmac_update_chunk(&ctx, st->st_shared); +// hmac_final_chunk(st->st_skeyid, "st_skeyid in skeyid_digisig()", &ctx); +// memset(cat.ptr, 0, cat.len); +// free(cat.ptr); +// return TRUE; +// } + +#ifdef OPENSSL +// static const chunk_t +// chunk_concat( const chunk_t c1, const chunk_t c2 ) +// { +// chunk_t cc; +// +// cc.len = c1.len + c2.len; +// if ((cc.ptr = malloc(cc.len)) != NULL) { +// memcpy(cc.ptr, c1.ptr, c1.len); +// memcpy(&(cc.ptr[c1.len]), c2.ptr, c2.len); +// } +// return cc; +// } + + +static bool +skeyid_pke(struct state *st) +{ + /* This routine lifted straight from Kai Martius' patches */ + /* and updated to use chunks */ + struct hmac_ctx ctx; + union hash_ctx ictx; + const struct hash_desc *h = st->st_oakley.hasher; + u_char *nonce; + u_int16_t nonce_len; + chunk_t noncechunk, cky_i, cky_r; + + nonce_len = h->hash_digest_len; + nonce=alloc_bytes(nonce_len, "nonce in skeyid_pke()"); + + h->hash_init(&ictx); + h->hash_update(&ictx, st->st_ni.ptr, st->st_ni.len); + h->hash_update(&ictx, st->st_nr.ptr, st->st_nr.len); + h->hash_final(nonce, &ictx); + + setchunk(noncechunk, nonce, nonce_len); + setchunk(cky_i, st->st_icookie, COOKIE_SIZE); + setchunk(cky_r, st->st_rcookie, COOKIE_SIZE); + + hmac_init_chunk(&ctx, h, noncechunk); + hmac_update_chunk(&ctx, cky_i); + hmac_update_chunk(&ctx, cky_r); + hmac_final_chunk(st->st_skeyid, "st_skeyid in skeyid_pke()", &ctx); + + free(nonce); + + return TRUE; +} +#endif + /* Generate the SKEYID_* and new IV * See draft-ietf-ipsec-ike-01.txt 4.1 @@ -539,13 +709,21 @@ case OAKLEY_DSS_SIG: /* XXX */ - +#ifdef OPENSSL + if (!skeyid_digisig(st)) + return FALSE; + break; +#endif case OAKLEY_RSA_ENC: case OAKLEY_RSA_ENC_REV: case OAKLEY_ELGAMAL_ENC: case OAKLEY_ELGAMAL_ENC_REV: /* XXX */ - +#ifdef OPENSSL + if (!skeyid_pke(st)) + return FALSE; + break; +#endif default: exit_log("generate_skeyids_iv(): unsupported authentication method %s", enum_show(&oakley_auth_names, st->st_oakley.auth)); @@ -733,7 +911,7 @@ return ctx.hmac_digest_len; } -#if 0 /* only needed for DSS */ +#ifdef OPENSSL static void main_mode_sha1(struct state *st, u_char *hash_val, size_t *hash_len , bool hashi, bool hashus) @@ -749,6 +927,7 @@ } #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 @@ -936,6 +1115,13 @@ switch (st->st_oakley.auth) { case OAKLEY_PRESHARED_KEY: +#ifdef OPENSSL + case OAKLEY_RSA_ENC: + case OAKLEY_RSA_ENC_REV: + case OAKLEY_DSS_SIG: + case OAKLEY_ELGAMAL_ENC: + case OAKLEY_ELGAMAL_ENC_REV: +#endif { pb_stream *const hash_pbs = &md->chain[ISAKMP_NEXT_HASH]->pbs; @@ -1041,7 +1227,8 @@ } } - DBG(DBG_CRYPT, DBG_log("encrypting using %s", enum_show(&oakley_enc_names, st->st_oakley.encrypt))); + 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); @@ -1721,6 +1908,9 @@ pb_stream r_sa_pbs; + DBG(DBG_PARSING, + DBG_log("in main_inI1_outR1"); + ); if (c == NULL) { /* see if a wildcarded connection can be found */ @@ -1825,6 +2015,13 @@ main_inR1_outI2(struct msg_digest *md) { struct state *const st = md->st; +#ifdef OPENSSL + u_int8_t np; +#endif + + DBG(DBG_PARSING, + DBG_log("in main_inR1_outI2"); + ); /* verify echoed SA */ { @@ -1834,12 +2031,19 @@ , &sapd->payload.sa, NULL, TRUE, st)); } +// Here, we have to decide if the outgoing packet is going to be created +// for the classic RSASIG, or the OPENSSL functions. +#ifdef OPENSSL +if (!use_openssl(st->st_connection)) +{ +#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). */ + { struct isakmp_hdr r_hdr = md->hdr; @@ -1852,11 +2056,135 @@ if (!build_and_ship_KE(st, &st->st_gi, st->st_oakley.group , &md->rbody, ISAKMP_NEXT_NONCE)) return STF_INTERNAL_ERROR; - + /* Ni out */ if (!build_and_ship_nonce(&st->st_ni, &md->rbody, ISAKMP_NEXT_NONE, "Ni")) + return STF_INTERNAL_ERROR; + +#ifdef OPENSSL +} +else /* use_openssl == TRUE */ +{ + + DBG(DBG_PARSING, + DBG_log("main_inR1_outI2: auth chosen is %s", + enum_show(&oakley_auth_names, st->st_oakley.auth)); + ); + + if ((st->st_oakley.auth == OAKLEY_RSA_ENC_REV) || + (st->st_oakley.auth == OAKLEY_ELGAMAL_ENC_REV)) { + struct isakmp_hdr r_hdr = md->hdr; + + /* HDR out */ + r_hdr.isa_np = ISAKMP_NEXT_NONCE; + if (!out_struct(&r_hdr, &isakmp_hdr_desc, &md->reply, &md->rbody)) + return STF_INTERNAL_ERROR; + + if (!build_and_ship_nonce_pk(st, &st->st_ni, &md->rbody, + ISAKMP_NEXT_KE, "PubKey_r")) + return STF_INTERNAL_ERROR; + + if (derive_symmetric_key(st, st->st_ni, + st->st_icookie, COOKIE_SIZE, + &st->st_ks_i, + &st->st_ne_i) < 0) { + log("error while deriving symmetric key in revised mode"); + return STF_INTERNAL_ERROR; + } + memset(st->st_ne_i_iv, 0, MAX_DIGEST_LEN); + DBG(DBG_PARSING, + DBG_dump_chunk("Generated Ke_i:", st->st_ne_i); + ); + + /* KE out */ + if (!build_and_ship_KE_rpk(st, TRUE, &st->st_gi, st->st_oakley.group + , &md->rbody, ISAKMP_NEXT_ID)) + return STF_INTERNAL_ERROR; + + /* Ke_i out */ + { + chunk_t ch, out; + + // ch.len = st->st_myidentity.len + 4; + ch.len = sizeof(st->st_connection->this.id.ip_addr) + 4; + ch.ptr = alloc_bytes(ch.len, "encrypted identity"); + // ch.ptr[0] = (u_int8_t)st->st_myidentity_type; + ch.ptr[0] = (u_int8_t)st->st_connection->this.id.kind; + memset(&(ch.ptr[1]), 0, 1); + memset(&(ch.ptr[2]), 0, 2); + // memcpy(&(ch.ptr[4]), st->st_myidentity.ptr, st->st_myidentity.len); + memcpy(&(ch.ptr[4]), &(st->st_connection->this.id.ip_addr), + sizeof(st->st_connection->this.id.ip_addr)); + + if (!encrypt_payload(st, &st->st_ks_i, st->st_ne_i_iv, + ch, &out)) + return STF_INTERNAL_ERROR; + freeanychunk(ch); + if (!out_generic_chunk(ISAKMP_NEXT_NONE, &isakmp_generic_desc, + &md->rbody, out, "my identity (ciphertext)")) + { + freeanychunk(out); + return STF_INTERNAL_ERROR; + } + freeanychunk(out); + } + + } else { + struct isakmp_hdr r_hdr = md->hdr; + + r_hdr.isa_np = ISAKMP_NEXT_KE; + if (!out_struct(&r_hdr, &isakmp_hdr_desc, &md->reply, &md->rbody)) return STF_INTERNAL_ERROR; + if ((st->st_oakley.auth == OAKLEY_RSA_ENC) || + (st->st_oakley.auth == OAKLEY_ELGAMAL_ENC)) + np = ISAKMP_NEXT_ID; + else /* Digital Sig or Preshared key */ + np = ISAKMP_NEXT_NONCE; + + /* KE out */ + if (!build_and_ship_KE(st, &st->st_gi, st->st_oakley.group + , &md->rbody, np)) + return STF_INTERNAL_ERROR; + + if ((st->st_oakley.auth == OAKLEY_RSA_ENC) || + (st->st_oakley.auth == OAKLEY_ELGAMAL_ENC)) { + /* PubKey_r out */ + chunk_t ch; + + // ch.len = st->st_myidentity.len + 4; + ch.len = sizeof(st->st_connection->this.id.ip_addr) + 4; + ch.ptr = alloc_bytes(ch.len, "my identity (plaintext)"); + /* Store the identity type */ + // ch.ptr[0] = (u_int8_t)st->st_myidentity_type; + ch.ptr[0] = (u_int8_t)st->st_connection->this.id.kind; + memset(&(ch.ptr[1]), 0, 1); /* Set protoid to be 0 - perhaps it should be IPPROTO_UDP */ + memset(&(ch.ptr[2]), 0, 2); /* Set the port to be 0 - perhaps it should be our_port */ + // memcpy(&(ch.ptr[4]), st->st_myidentity.ptr, st->st_myidentity.len); + memcpy(&(ch.ptr[4]), &(st->st_connection->this.id.ip_addr), + sizeof(st->st_connection->this.id.ip_addr)); + /* PK encrypt this */ + pubkey_encrypt_chunk( &ch, st ); + if (!out_generic_chunk(ISAKMP_NEXT_NONCE, &isakmp_generic_desc, + &md->rbody, ch, "my identity (ciphertext)")) + return STF_INTERNAL_ERROR; + memset(ch.ptr, 0, ch.len); + freeanychunk(ch); + + if (!build_and_ship_nonce_pk(st, &st->st_ni, &md->rbody, + ISAKMP_NEXT_NONE, "PubKey_r")) + return STF_INTERNAL_ERROR; + } else { + DBG(DBG_PARSING, + DBG_log("Shipping nonce_i"); + ); + if (!build_and_ship_nonce(&st->st_ni, &md->rbody, ISAKMP_NEXT_NONE, "Ni")) + return STF_INTERNAL_ERROR; + } + } +} +#endif + /* finish message */ close_message(&md->rbody); @@ -1886,6 +2214,9 @@ struct state *const st = md->st; pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + DBG(DBG_PARSING, + DBG_log("in main_inI2_outR2"); + ); /* KE in */ RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi", st->st_oakley.group, keyex_pbs)); @@ -1927,6 +2258,191 @@ return STF_REPLY; } +#ifdef OPENSSL +/* Handle HDR;KE;PubKey_r;PubKey_r from Initiator. + * Send a HDR;KE;PubKey_i;PubKey_i back. + */ +stf_status +main_inI2_outR2_pk(struct msg_digest *md) +{ + struct state *const st = md->st; + + pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + + DBG(DBG_PARSING, + DBG_log("in main_inI2_outR2_pk"); + ); + /* KE in */ + // ACCEPT_KE(st->st_gi, "Gi", st->st_oakley.group, *keyex_pbs); + RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi", + st->st_oakley.group, keyex_pbs)); + + if (!decode_peer_id(md, FALSE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* Ni_b in */ + // ACCEPT_NONCE(st->st_ni, "PubKey_r"); + RETURN_STF_FAILURE(accept_nonce(md, &st->st_ni, "PubKey_r")); + + /** build output packet HDR;KE;_PubKey_i;PubKey_i*********/ + + /* HDR out done */ + + /* KE out */ + if (!build_and_ship_KE(st, &st->st_gr, st->st_oakley.group + , &md->rbody, ISAKMP_NEXT_ID)) + return STF_INTERNAL_ERROR; + + { + /* PubKey_i out */ + chunk_t ch; + + // ch.len = st->st_myidentity.len + 4; + ch.len = sizeof(st->st_connection->this.id.ip_addr) + 4; + ch.ptr = alloc_bytes(ch.len, "my identity (plaintext)"); + /* Store the identity type */ + // ch.ptr[0] = (u_int8_t)st->st_myidentity_type; + ch.ptr[0] = (u_int8_t)st->st_connection->this.id.kind; + memset(&(ch.ptr[1]), 0, 1); /* Set protoid to be 0 - perhaps it should be IPPROTO_UDP */ + memset(&(ch.ptr[2]), 0, 2); /* Set the port to be 0 - perhaps it should be our_port */ + // memcpy(&(ch.ptr[4]), st->st_myidentity.ptr, st->st_myidentity.len); + memcpy(&(ch.ptr[4]), &(st->st_connection->this.id.ip_addr), + sizeof(st->st_connection->this.id.ip_addr)); + /* PK encrypt this */ + pubkey_encrypt_chunk( &ch, st ); + if (!out_generic_chunk(ISAKMP_NEXT_NONCE, &isakmp_generic_desc, &md->rbody, ch, "my identity (ciphertext)")) + return STF_INTERNAL_ERROR; + memset(ch.ptr, 0, ch.len); + freeanychunk(ch); + } + + /* Nr out */ + if (!build_and_ship_nonce_pk(st, &st->st_nr, &md->rbody, ISAKMP_NEXT_NONE, "PubKey_i")) + return STF_INTERNAL_ERROR; + + /* finish message */ + close_message(&md->rbody); + + /* next message will be encrypted, but not this one. + * We could defer this calculation. + */ + compute_dh_shared(st, st->st_gi, st->st_oakley.group); +#ifdef DODGE_DH_MISSING_ZERO_BUG + if (st->st_shared.ptr[0] == 0) + return STF_DROP_DOOMED_EXCHANGE; +#endif + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + update_iv(st); + + /* Advance state */ + st->st_state = STATE_MAIN_R2; + + return STF_REPLY; +} +#endif /* OPENSSL */ + +#ifdef OPENSSL +/* Handle HDR;Pubkey_r;Ke_i;Ke_i from Initiator + * Send a HDR;Pubkey_i;Ke_r;Ke_r back. + */ +stf_status +main_inI2_outR2_rpk(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + + DBG(DBG_PARSING, + DBG_log("in main_inI2_outR2_rpk"); + ); + + /* Ni_b in */ + // ACCEPT_NONCE(st->st_ni, "PubKey_r"); + RETURN_STF_FAILURE(accept_nonce(md, &st->st_ni, "PubKey_r")); + + /* KE in */ + // ACCEPT_KE(st->st_gi, "Gi", st->st_oakley.group, *keyex_pbs); + RETURN_STF_FAILURE(accept_KE(&st->st_gi, "Gi", + st->st_oakley.group, keyex_pbs)); + + if (!decode_peer_id(md, FALSE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + /**************** build output packet ****************/ + + /* HDR out done */ + + /* Nr out */ + if (!build_and_ship_nonce_pk(st, &st->st_nr, &md->rbody, ISAKMP_NEXT_KE, "PubKey_i")) + return STF_INTERNAL_ERROR; + + if (derive_symmetric_key(st, st->st_nr, + st->st_rcookie, COOKIE_SIZE, + &st->st_ks_r, + &st->st_ne_r) < 0) { + log("error while deriving symmetric key in revised mode"); + return STF_INTERNAL_ERROR; + } + memset(st->st_ne_r_iv, 0, MAX_DIGEST_LEN); + + DBG(DBG_PARSING, + DBG_dump_chunk("Generated Ke_r:", st->st_ne_r); + ); + + /* KE out */ + if (!build_and_ship_KE_rpk(st, FALSE, &st->st_gr, st->st_oakley.group + , &md->rbody, ISAKMP_NEXT_ID)) + return STF_INTERNAL_ERROR; + + /* Ke_r out */ + { + chunk_t ch, out; + + // ch.len = st->st_myidentity.len + 4; + ch.len = sizeof(st->st_connection->this.id.ip_addr) + 4; + ch.ptr = alloc_bytes(ch.len, "encrypted identity"); + // ch.ptr[0] = (u_int8_t)st->st_myidentity_type; + ch.ptr[0] = (u_int8_t)st->st_connection->this.id.kind; + memset(&(ch.ptr[1]), 0, 1); + memset(&(ch.ptr[2]), 0, 2); + // memcpy(&(ch.ptr[4]), st->st_myidentity.ptr, st->st_myidentity.len); + memcpy(&(ch.ptr[4]), &(st->st_connection->this.id.ip_addr), + sizeof(st->st_connection->this.id.ip_addr)); + if (!encrypt_payload(st, &st->st_ks_r, st->st_ne_r_iv, + ch, &out)) + return STF_INTERNAL_ERROR; + freeanychunk(ch); + if (!out_generic_chunk(ISAKMP_NEXT_NONE, &isakmp_generic_desc, + &md->rbody, out, "my identity (ciphertext)")) { + freeanychunk(out); + return STF_INTERNAL_ERROR; + } + freeanychunk(out); + } + + /* 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); +#ifdef DODGE_DH_MISSING_ZERO_BUG + if (st->st_shared.ptr[0] == 0) + return STF_DROP_DOOMED_EXCHANGE; +#endif + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + update_iv(st); + + /* Advance state */ + st->st_state = STATE_MAIN_R2; + + return STF_REPLY; +} +#endif /* OPENSSL */ + + /* STATE_MAIN_I2: * SMF_PSK_AUTH: HDR, KE, Nr --> HDR*, IDi1, HASH_I * SMF_DS_AUTH: HDR, KE, Nr --> HDR*, IDi1, [ CERT, ] SIG_I @@ -1941,12 +2457,22 @@ main_inR2_outI3(struct msg_digest *md) { struct state *const st = md->st; +#ifdef OPENSSL + struct connection *c = st->st_connection; +#endif + 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; + DBG(DBG_PARSING, + DBG_log("in main_inR2_outI3"); + ); + /* KE in */ - RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr", st->st_oakley.group, keyex_pbs)); + 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")); @@ -1962,7 +2488,6 @@ return STF_FAIL + AUTHENTICATION_FAILED; /*************** build output packet HDR*;IDii;HASH/SIG_I ***************/ - /* ??? NOTE: this is almost the same as main_inI3_outR3's code */ /* HDR* out done */ @@ -1972,16 +2497,144 @@ chunk_t id_b; pb_stream id_pbs; - build_id_payload(&id_hd, &id_b, &st->st_connection->this); + build_id_payload(&id_hd, &id_b, &(st->st_connection->this)); +#ifdef OPENSSL +if (use_openssl(st->st_connection)) +{ + switch(st->st_oakley.auth) { + case OAKLEY_RSA_SIG: + case OAKLEY_DSS_SIG: + if ((c->cert_options & CERT_OPTION_SEND) == 0) + id_hd.isaiid_np = ISAKMP_NEXT_SIG; + else + id_hd.isaiid_np = ISAKMP_NEXT_CERT; + break; + case OAKLEY_RSA_ENC: + case OAKLEY_RSA_ENC_REV: + log("RSA method not supported yet"); + return STF_INTERNAL_ERROR; + break; + default: + id_hd.isaiid_np = ISAKMP_NEXT_HASH; + break; + } +} +else +{ +#endif /* OPENSSL */ id_hd.isaiid_np = auth_payload; +#ifdef OPENSSL +} +#endif /* OPENSSL */ + 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; + || !out_chunk(id_b, &id_pbs, "my identity")) + return STF_INTERNAL_ERROR; close_output_pbs(&id_pbs); + } /* IDii out done */ + +#ifdef OPENSSL +if (use_openssl(st->st_connection)) +{ + if (((c->cert_options & CERT_OPTION_SEND) != 0) && + ((st->st_oakley.auth == OAKLEY_RSA_SIG) || + (st->st_oakley.auth == OAKLEY_DSS_SIG))) { + u_char *crt, *cx; + unsigned long ulen; + /* Send the signing certificate */ + + ulen = i2d_X509(c->cert, NULL); + ulen++; + if ((cx = alloc_bytes(ulen, "ASN.1 cert")) == NULL) + { return STF_INTERNAL_ERROR; } + cx[0] = (u_char)(CERT_TYPE_X509_SIG); + crt = &(cx[1]); + i2d_X509(c->cert, &crt); + if (!out_generic_raw(ISAKMP_NEXT_SIG, &isakmp_ipsec_certificate_desc + , &md->rbody, cx, ulen, "CERT_I")) + return STF_INTERNAL_ERROR; + pfree(cx); } - /* HASH_I or SIG_I out */ - { + /* HASH_I out, OPENSSL */ + { + u_char hash_val[MAX_DIGEST_LEN]; + size_t hash_len = main_mode_hash(st, hash_val, TRUE, TRUE); + u_char *buf; + u_int elen; + bool ok; + + /* Output the signature/hash as defined by the selected Oakley transform */ + + switch (st->st_oakley.auth) { + case OAKLEY_RSA_SIG: /* output signed HASH_I */ + if ((buf = alloc_bytes(EVP_PKEY_size(c->key) + 32 /* bit of slack */, + "RSA signature")) == NULL) + return STF_INTERNAL_ERROR; + elen = RSA_private_encrypt(hash_len, hash_val, buf, + ((EVP_PKEY *)(c->key))->pkey.rsa, + RSA_PKCS1_PADDING); + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc, &md->rbody + , buf, elen, "SIG_I")) + return STF_INTERNAL_ERROR; + pfree(buf); + + DBG(DBG_PARSING, + DBG_log("Selected auth mechanism is %s", + enum_show(&oakley_auth_names, st->st_oakley.auth)); + ) + break; + + case OAKLEY_DSS_SIG: + /* We don't have to check whether the hash is a SHA1 */ + /* one, since the ciphersuite offering DSA is only */ + /* configured with SHA1. Don't change this configuration */ + if (c->cert_options & CERT_OPTION_DSS_SHA) + main_mode_sha1(st, hash_val, &hash_len, TRUE, TRUE); + + buf = alloc_bytes(EVP_PKEY_size(c->key) + 32 /* bit of slack */, + "DSA signature"); + if (buf) { + ok = (c->cert_options & CERT_OPTION_DSS_ALT) + ? DSA_sign_raw(hash_val, hash_len, + buf, &elen, ((EVP_PKEY *)(c->key))->pkey.dsa) + : DSA_sign(EVP_PKEY_DSA, hash_val, hash_len, + buf, &elen, ((EVP_PKEY *)(c->key))->pkey.dsa); + if (!ok) { + /* DSA signing failed */ + log_err(); + return STF_INTERNAL_ERROR; + } + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc, + &md->rbody, buf, elen, "SIG_I")) + return STF_INTERNAL_ERROR; + pfree(buf); + } else + return STF_INTERNAL_ERROR; + + DBG(DBG_PARSING | DBG_CONTROL, + DBG_log("Selected auth mechanism is %s", + enum_show(&oakley_auth_names, st->st_oakley.auth)); + ); + break; + /* If we get to any of these, something has gone WAY wrong */ + case OAKLEY_RSA_ENC: + return STF_INTERNAL_ERROR; + case OAKLEY_ELGAMAL_ENC: + return STF_INTERNAL_ERROR; + case OAKLEY_RSA_ENC_REV: + return STF_INTERNAL_ERROR; + case OAKLEY_ELGAMAL_ENC_REV: + return STF_INTERNAL_ERROR; + default: + } /* switch done */ + } /* HASH_I out, openssl done. */ +} +else +{ +#endif /* OPENSSL */ + /* HASH_I or SIG_I out, !OPENSSL */ + { u_char hash_val[MAX_DIGEST_LEN]; size_t hash_len = main_mode_hash(st, hash_val, TRUE, TRUE); @@ -1996,8 +2649,8 @@ { /* 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); + size_t sig_len = RSA_sign_hash(st->st_connection, + sig_val, hash_val, hash_len); if (sig_len == 0) { @@ -2009,7 +2662,10 @@ , &md->rbody, sig_val, sig_len, "SIG_I")) return STF_INTERNAL_ERROR; } - } + } +#ifdef OPENSSL +} +#endif /* OPENSSL */ /* encrypt message, except for fixed part of header */ @@ -2019,9 +2675,159 @@ /* Advance state */ st->st_state = STATE_MAIN_I3; + return STF_REPLY; +} + +#ifdef OPENSSL +/* + * Handle HDR;KE;PubKey_i;PubKey_i from responder. + * Send a HDR*;HASH_I back. + */ +stf_status main_inR2_outI3_pk(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + + DBG(DBG_PARSING, + DBG_log("in main_inR2_outI3_pk"); + ); + + /* KE in */ + // ACCEPT_KE(st->st_gr, "Gr", st->st_oakley.group, *keyex_pbs); + RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr", + st->st_oakley.group, keyex_pbs)); + + /* IDir in */ + if (!decode_peer_id(md, TRUE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* Nr in */ + // ACCEPT_NONCE(st->st_nr, "PubKey_i"); + RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "PubKey_i")); + + /* done parsing; initialize crypto */ + + compute_dh_shared(st, st->st_gr, st->st_oakley.group); +#ifdef DODGE_DH_MISSING_ZERO_BUG + if (st->st_shared.ptr[0] == 0) + return STF_REPLACE_DOOMED_EXCHANGE; +#endif + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + + /**************** build output packet HDR*;HASH_I ****************/ + + /* HDR* out done */ + + /* HASH_I out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + size_t hash_len = main_mode_hash(st, hash_val, TRUE, TRUE); + + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, TRUE, TRUE); + + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody + , hash_val, hash_len, "HASH_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 */ + + /* Advance state */ + st->st_state = STATE_MAIN_I3; return STF_REPLY; } +#endif /* OPENSSL */ + +#ifdef OPENSSL +/* Handle HDR;KE;PubKey_i;Ke_r;Ke_r from responder. + * Send a HDR*;HASH_I back. + */ +stf_status +main_inR2_outI3_rpk(struct msg_digest *md) +{ + struct state *const st = md->st; + pb_stream *const keyex_pbs = &md->chain[ISAKMP_NEXT_KE]->pbs; + + DBG(DBG_PARSING, + DBG_log("in main_inR2_outI3_rpk"); + ); + + /* Nr in */ + // ACCEPT_NONCE(st->st_nr, "PubKey_i"); + RETURN_STF_FAILURE(accept_nonce(md, &st->st_nr, "PubKey_i")); + + /* KE in */ + // ACCEPT_KE(st->st_gr, "Gr", st->st_oakley.group, *keyex_pbs); + RETURN_STF_FAILURE(accept_KE(&st->st_gr, "Gr", + st->st_oakley.group, keyex_pbs)); + + /* IDir in */ + if (!decode_peer_id(md, TRUE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + /* done parsing; initialize crypto */ + + compute_dh_shared(st, st->st_gr, st->st_oakley.group); +#ifdef DODGE_DH_MISSING_ZERO_BUG + if (st->st_shared.ptr[0] == 0) + return STF_REPLACE_DOOMED_EXCHANGE; +#endif + if (!generate_skeyids_iv(st)) + return STF_FAIL + AUTHENTICATION_FAILED; + + /**************** build output packet HDR*;HASH_I ****************/ + + /* HDR* out done */ + + /* HASH_I out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, TRUE, TRUE); + size_t hash_len = main_mode_hash(st, hash_val, TRUE, TRUE); + + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody + , hash_val, hash_len, "HASH_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 */ + + /* Advance state */ + st->st_state = STATE_MAIN_I3; + + return STF_REPLY; +} +#endif /* OPENSSL */ + + +#ifdef OPENSSL +/* STATE_MAIN_R2: + * Which type of DS are we currently handling? + * Classic? OPENSSL? + */ +stf_status +main_inI3_outR3_whichds(struct msg_digest *md) +{ + if (! use_openssl(md->st->st_connection)) { + return main_inI3_outR3(md); + } else { + return main_inI3_outR3_ds(md); + } +} +#endif /* OPENSSL */ /* STATE_MAIN_R2: * PSK_AUTH: HDR*, IDi1, HASH_I --> HDR*, IDr1, HASH_R @@ -2030,11 +2836,16 @@ */ stf_status main_inI3_outR3(struct msg_digest *md) -{ +{ struct state *const st = md->st; + int auth_payload = st->st_oakley.auth == OAKLEY_PRESHARED_KEY ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_SIG; + DBG(DBG_PARSING, + DBG_log("in main_inI3_outR3"); + ); + /* input code similar to main_inR3 -- should be factored */ /* IDii in */ @@ -2108,6 +2919,496 @@ return STF_REPLY; } +#ifdef OPENSSL +/* + * Handle HDR*;IDii;HASH_I from initiator. Send a HDR*;IDir;HASH_R back. + */ +stf_status +main_inI3_outR3_ds(struct msg_digest *md) +{ + struct state *const st = md->st; + + struct connection *c = st->st_connection; + u_char hash_val[MAX_DIGEST_LEN], *buf; + pb_stream *sig_pbs; + unsigned int dlen; + struct payload_digest *id_pld; + size_t hash_len = main_mode_hash(st, hash_val, TRUE, FALSE); + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, TRUE, FALSE); + + DBG(DBG_PARSING, + DBG_log("in main_inI3_outR3_ds"); + ); + /* input code similar to main_inR3 -- should be factored */ + + /* IDii in */ + if (!decode_peer_id(md, FALSE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + switch (st->st_oakley.auth) { + case OAKLEY_RSA_SIG: /* check SIG_I */ + { + RSA *rsa; + STACK_OF(X509) *peerlist; + X509 *recv_cert, *cert; + pb_stream *cert_pbs; + int i, success; + + recv_cert = NULL; + /* First check to see if we have a certificate sent to us */ + if ((md->chain[ISAKMP_NEXT_CERT] != NULL) && + ((cert_pbs = &md->chain[ISAKMP_NEXT_CERT]->pbs) != NULL)) { + u_char *crt; + unsigned long len; + + switch(cert_pbs->cur[0]) { + case CERT_TYPE_X509_SIG: + crt = &(cert_pbs->cur[1]); + len = pbs_left(cert_pbs)-1; + recv_cert = d2i_X509(NULL, &crt, len); + break; + default: + DBG(DBG_PARSING, + DBG_log("Unhandled certificate encoding: %s", + enum_show(&cert_names, cert_pbs->cur[0])); + ); + break; + } + } + + if (recv_cert) { + char buf[200]; + + X509_NAME_oneline(X509_get_subject_name(recv_cert) + , buf, sizeof(buf)); + DBG(DBG_PARSING, + DBG_log("Received certificate \"%s\"", buf); + ); + } + + /* We know this field is legitimate, because the */ + /* decode_peer_id has been called before */ + id_pld = md->chain[ISAKMP_NEXT_ID]; + + if ((peerlist = peer_cert_list((STACK_OF(XMAP) *)(c->lu), + EVP_PKEY_RSA, + recv_cert, + id_pld, + (c->cert_options & CERT_OPTION_STRICT) + ? TRUE : FALSE)) == NULL) { + log("Verification certificate(s) unavailable to check signature in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if (sk_X509_num(peerlist) == 0) { + log("Unable to get any certificate for peer in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if ((md->chain[ISAKMP_NEXT_SIG] == NULL) || + ((sig_pbs = &md->chain[ISAKMP_NEXT_SIG]->pbs) == NULL)) { + log("Signature not present in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + DBG(DBG_PARSING, + DBG_dump("Computed hash", hash_val, hash_len); + ); + /* Iterate through all certificates in the peer list until */ + /* we have one which verifies the signature, or we have no */ + /* more certificates to verify with */ + for (i=0, success = 0; (success == 0) && (ipkey.rsa; + if ((buf = alloc_bytes(EVP_PKEY_size(X509_get_pubkey(xx)), + "RSA decrypt sig")) != NULL) { + dlen = RSA_public_decrypt(pbs_left(sig_pbs), sig_pbs->cur, buf, + rsa, RSA_PKCS1_PADDING); + DBG(DBG_PARSING, + DBG_dump("Decrypted hash", buf, (dlen > hash_len) + ? hash_len : dlen); + ); + if ((dlen == hash_len) && (memcmp(buf, hash_val, hash_len) == 0)) { + DBG(DBG_PARSING, + DBG_log("SIG_I matches computed hash"); + ); + success = 1; + cert = X509_dup(xx); + } else { + DBG(DBG_PARSING, + DBG_log("SIG_I does not computed hash"); + ); + } + pfree(buf); + } + } + if (!success) { + log_err(); + DBG_cond_dump(DBG_CRYPT, "received SIG_I does not match computed value in Main I3:" + , sig_pbs->cur, pbs_left(sig_pbs)); + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_SIGNATURE; + } else { + /* We know that certificate 'cert' was used to */ + /* sign the accurate hash which accompanied the method */ + /* Now we have to verify that it is a valid certificate */ + /* to be signing anything */ + DBG(DBG_PARSING, + DBG_log("Verifying certificate along path %s", c->path); + ); + if (verify_certificate( cert, c->lu, c->path, (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE )) + { + DBG(DBG_PARSING, + DBG_log("Signing certificate is valid"); + ); + } + else { + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_CERTIFICATE; + } + } + sk_X509_pop_free(peerlist, X509_free); + } + break; + case OAKLEY_DSS_SIG: + { + DSA *dsa; + STACK_OF(X509) *peerlist; + X509 *recv_cert, *cert; + pb_stream *cert_pbs; + int i, success; + + if (c->cert_options & CERT_OPTION_DSS_SHA) + main_mode_sha1(st, hash_val, &hash_len, TRUE, FALSE); + recv_cert = NULL; + /* First check to see if we have a certificate sent to us */ + if ((md->chain[ISAKMP_NEXT_CERT] != NULL) && + ((cert_pbs = &md->chain[ISAKMP_NEXT_CERT]->pbs) != NULL)) { + u_char *crt; + unsigned long len; + + switch(cert_pbs->cur[0]) { + case CERT_TYPE_X509_SIG: + crt = &(cert_pbs->cur[1]); + len = pbs_left(cert_pbs)-1; + recv_cert = d2i_X509(NULL, &crt, len); + break; + default: + DBG(DBG_PARSING, + DBG_log("Unhandled certificate encoding: %s", + enum_show(&cert_names, cert_pbs->cur[0])); + ); + break; + } + } + + if (recv_cert) { + char buf[200]; + + X509_NAME_oneline(X509_get_subject_name(recv_cert) + , buf, sizeof(buf)); + DBG(DBG_PARSING, + DBG_log("Received certificate \"%s\"", buf); + ); + } + + /* We know this field is legitimate, because the */ + /* decode_peer_id has been called before */ + id_pld = md->chain[ISAKMP_NEXT_ID]; + + if ((peerlist = + peer_cert_list((STACK_OF(XMAP) *)(c->lu), EVP_PKEY_DSA, + recv_cert, id_pld, + (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE)) == NULL) { + log("Verification certificate(s) unavailable to check signature in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if (sk_X509_num(peerlist) == 0) { + log("Unable to get any certificate for peer in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if ((md->chain[ISAKMP_NEXT_SIG] == NULL) || + ((sig_pbs = &md->chain[ISAKMP_NEXT_SIG]->pbs) == NULL)) { + log("Signature not present in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + DBG(DBG_PARSING, + DBG_dump("Computed hash", hash_val, hash_len); + ); + /* Iterate through all certificates in the peer list until */ + /* we have one which verifies the signature, or we have no */ + /* more certificates to verify with */ + for(i=0, success=0, buf = NULL; + ((success == 0) && (i < sk_X509_num(peerlist))); i++) { + dsa = X509_get_pubkey(sk_X509_value(peerlist, i))->pkey.dsa; + if ((buf = alloc_bytes(SHA1_DIGEST_SIZE, "DSA sig space")) != NULL) { + success = (c->cert_options & CERT_OPTION_DSS_ALT) + ? DSA_verify_raw(hash_val, hash_len, + sig_pbs->cur, pbs_left(sig_pbs), + dsa) + : DSA_verify(EVP_PKEY_DSA, + hash_val, hash_len, + sig_pbs->cur, pbs_left(sig_pbs), + dsa) + ; + if (success == 1) { + DBG(DBG_PARSING, + DBG_log("SIG_I matches computed hash"); + ); + cert = X509_dup(sk_X509_value(peerlist, i)); + } else if (success == 0) { + DBG(DBG_PARSING, + DBG_log("SIG_I does not computed hash"); + ); + } else { + DBG(DBG_PARSING, + DBG_log("Verification error"); + ); + log_err(); + } + pfree(buf); + buf = NULL; + } + } + if (buf) pfree(buf); + if (!success) { + log_err(); + DBG_cond_dump(DBG_CRYPT, "received SIG_I does not match computed value in Main I3:" + , sig_pbs->cur, pbs_left(sig_pbs)); + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_SIGNATURE; + } else { + /* We know that certificate 'cert' was used to */ + /* sign the accurate hash which accompanied the method */ + /* Now we have to verify that it is a valid certificate */ + /* to be signing anything */ + DBG(DBG_PARSING, + DBG_log("Verifying certificate along path %s", c->path); + ); + if (verify_certificate( cert, c->lu, c->path, (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE )) + { + DBG(DBG_PARSING, + DBG_log("Signing certificate is valid"); + ); + } + else { + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_CERTIFICATE; + } + } + sk_X509_pop_free(peerlist, X509_free); + } + break; + /* Should never get here */ + default: + return STF_INTERNAL_ERROR; + } + + /************* build output packet HDR*;IDir;[CERT];SIG_R ******/ + /* ??? NOTE: this is almost the same as main_inR2_outI3's code */ + + /* IDir out */ + { + struct isakmp_ipsec_id r_id; + pb_stream r_id_pbs; + chunk_t r_id_chunk; + + // r_id.isaiid_idtype = st->st_myidentity_type; + r_id.isaiid_idtype = st->st_connection->this.id.kind; + r_id.isaiid_protoid = 0; /* ??? is this right? */ + r_id.isaiid_port = 0; /* ??? is this right? */ + + build_id_payload(&r_id, &r_id_chunk, &st->st_connection->this); + + if ((c->cert_options & CERT_OPTION_SEND) == 0) + r_id.isaiid_np = ISAKMP_NEXT_SIG; + else + r_id.isaiid_np = ISAKMP_NEXT_CERT; + + if (!out_struct(&r_id, &isakmp_ipsec_identification_desc, &md->rbody, &r_id_pbs) + || !out_chunk(r_id_chunk, &r_id_pbs, "my identity")) + return STF_INTERNAL_ERROR; + close_output_pbs(&r_id_pbs); + +// ######################## +// // if a permanent cast is needed, we'll need to rethink this. +// //// r_id_chunk.len = sizeof(st->st_connection->this.id); +// //// memcpy(&(r_id_chunk.ptr), &(st->st_connection->this.id), r_id_chunk.len); +// r_id_chunk.len = sizeof(st->st_connection->this.id.ip_addr); +// memcpy(&(r_id_chunk.ptr), &(st->st_connection->this.id.ip_addr), r_id_chunk.len); +// +// if (!out_struct(&r_id, &isakmp_ipsec_identification_desc, +// &md->rbody, &r_id_pbs) +// || !out_chunk(r_id_chunk, &r_id_pbs, "my identity")) +// // || !out_chunk(st->st_myidentity, &r_id_pbs, "my identity")) +// return STF_INTERNAL_ERROR; +// close_output_pbs(&r_id_pbs); +// ######################## + } + + if ((c->cert_options & CERT_OPTION_SEND) != 0) { + /* Send the signing certificate */ + u_char *crt, *cx; + unsigned long ulen; + /* Send the signing certificate */ + + ulen = i2d_X509(c->cert, NULL); + ulen++; + if ((cx = alloc_bytes(ulen, "ASN.1 cert")) == NULL) + { return STF_INTERNAL_ERROR; } + cx[0] = (u_char)(CERT_TYPE_X509_SIG); + crt = &(cx[1]); + i2d_X509(c->cert, &crt); + if (!out_generic_raw(ISAKMP_NEXT_SIG, &isakmp_ipsec_certificate_desc + , &md->rbody, cx, ulen, "CERT_R")) + return STF_INTERNAL_ERROR; + pfree(cx); + } + + /* SIG_R out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + u_int elen; + bool ok; + + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, FALSE, TRUE); + size_t hash_len = main_mode_hash(st, hash_val, FALSE, TRUE); + + /* Output the signature/hash as defined by the selected Oakley transform */ + switch (st->st_oakley.auth) { + case OAKLEY_RSA_SIG: /* output signed HASH_R */ + if ((buf = alloc_bytes(EVP_PKEY_size(c->key) + 32 /* bit of slack */, + "RSA sig")) == NULL) + return STF_INTERNAL_ERROR; + elen = RSA_private_encrypt(hash_len, hash_val, buf, + ((EVP_PKEY *)(c->key))->pkey.rsa, + RSA_PKCS1_PADDING); + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc, + &md->rbody, buf, elen, "SIG_R")) + return STF_INTERNAL_ERROR; + break; + case OAKLEY_DSS_SIG: + if (c->cert_options & CERT_OPTION_DSS_SHA) + main_mode_sha1(st, hash_val, &hash_len, FALSE, TRUE); + + if ((buf = alloc_bytes(EVP_PKEY_size(c->key) + 32 /* bit of slack */, + "DSA sig")) == NULL) + return STF_INTERNAL_ERROR; + ok = (c->cert_options & CERT_OPTION_DSS_ALT) + ? DSA_sign_raw(hash_val, hash_len, + buf, &elen, + ((EVP_PKEY *)(c->key))->pkey.dsa) + : DSA_sign(EVP_PKEY_DSA, hash_val, hash_len, + buf, &elen, + ((EVP_PKEY *)(c->key))->pkey.dsa); + if (!ok) { + log_err(); + return STF_INTERNAL_ERROR; + } + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_signature_desc, + &md->rbody, buf, elen, "SIG_I")) + return STF_INTERNAL_ERROR; + + DBG(DBG_PARSING, + DBG_log("Selected auth mechanism is %s", + enum_show(&oakley_auth_names, st->st_oakley.auth)); + ); + break; + default: + return STF_INTERNAL_ERROR; + } + pfree(buf); + } + + /* 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); + + /* Advance state */ + st->st_state = STATE_MAIN_R3; + st->st_connection->newest_isakmp_sa = st->st_serialno; + + return STF_REPLY; +} +#endif /* OPENSSL */ + +#ifdef OPENSSL +/* + * Handle HDR*;HASH_I from initiator. Send a HDR*;HASH_R back. + */ +stf_status +main_inI3_outR3_pk(struct msg_digest *md) +{ + struct state *const st = md->st; + + /* input code similar to main_inR3 -- should be factored */ + + DBG(DBG_PARSING, + DBG_log("in main_inI3_outR3_pk"); + ); + + /* HASH_I in */ + // CHECK_HASH(main_mode_hash(st, hash_val, &hash_len, TRUE, FALSE) + // , "HASH_I", "Main I3"); + RETURN_STF_FAILURE(check_main_authenticator(md, TRUE)); + + /**************** build output packet HDR*;HASH_R ****************/ + + /* HASH_R out */ + { + u_char hash_val[MAX_DIGEST_LEN]; + + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, FALSE, TRUE); + size_t hash_len = main_mode_hash(st, hash_val, FALSE, TRUE); + + if (!out_generic_raw(ISAKMP_NEXT_NONE, &isakmp_hash_desc, &md->rbody + , hash_val, hash_len, "HASH_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); + + /* Advance state */ + st->st_state = STATE_MAIN_R3; + st->st_connection->newest_isakmp_sa = st->st_serialno; + + return STF_REPLY; +} +#endif + +#ifdef OPENSSL +/* STATE_MAIN_I3: + * Handle HDR*;IDir;HASH/SIG_R from responder. + */ +stf_status +main_inR3_whichds(struct msg_digest *md) +{ + if (!use_openssl(md->st->st_connection)) { + return main_inR3(md); + } else { + return main_inR3_ds(md); + } +} +#endif /* OPENSSL */ + + /* STATE_MAIN_I3: * Handle HDR*;IDir;HASH/SIG_R from responder. */ @@ -2117,6 +3418,9 @@ struct state *const st = md->st; struct connection *c = st->st_connection; + DBG(DBG_PARSING, + DBG_log("in main_inR3"); + ); /* input code similar to main_inI3_outR3 -- should be factored */ /* IDir in */ @@ -2136,6 +3440,332 @@ return STF_UNPEND_QUICK; } + +#ifdef OPENSSL +/* Handle HDR*;IDir;[CERT];SIG_R from responder. + */ +stf_status +main_inR3_ds(struct msg_digest *md) +{ + struct state *const st = md->st; + struct connection *c = st->st_connection; + u_char hash_val[MAX_DIGEST_LEN], *buf; + pb_stream *sig_pbs; + unsigned int dlen; + struct payload_digest *id_pld; + size_t hash_len = main_mode_hash(st, hash_val, FALSE, FALSE); + // size_t hash_len; + // main_mode_hash(st, hash_val, &hash_len, FALSE, FALSE); + + /* input code similar to main_inI3_outR3 -- should be factored */ + + DBG(DBG_PARSING, + DBG_log("in main_inR3_ds"); + ); + /* IDir in */ + if (!decode_peer_id(md, TRUE)) + return STF_FAIL + INVALID_ID_INFORMATION; + + switch (st->st_oakley.auth) { + case OAKLEY_RSA_SIG: /* check SIG_I */ + { + RSA *rsa; + STACK_OF(X509) *peerlist; + X509 *recv_cert, *cert; + pb_stream *cert_pbs; + int i, success; + + recv_cert = NULL; + /* First check to see if we have a certificate sent to us */ + if ((md->chain[ISAKMP_NEXT_CERT] != NULL) && + ((cert_pbs = &md->chain[ISAKMP_NEXT_CERT]->pbs) != NULL)) { + u_char *crt; + unsigned long len; + + switch(cert_pbs->cur[0]) { + case CERT_TYPE_X509_SIG: + crt = &(cert_pbs->cur[1]); + len = pbs_left(cert_pbs)-1; + recv_cert = d2i_X509(NULL, &crt, len); + break; + default: + DBG(DBG_PARSING, + DBG_log("Unhandled certificate encoding: %s", + enum_show(&cert_names, cert_pbs->cur[0])); + ); + break; + } + } + + if (recv_cert) { + char buf[200]; + + X509_NAME_oneline(X509_get_subject_name(recv_cert) + , buf, sizeof(buf)); + DBG(DBG_PARSING, + DBG_log("Sent certificate \"%s\"", buf); + ); + } + + id_pld = md->chain[ISAKMP_NEXT_ID]; + if ((peerlist = + peer_cert_list((STACK_OF(XMAP) *)c->lu, + EVP_PKEY_RSA, recv_cert, id_pld, + (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE)) == NULL) { + log("Verification certificate(s) unavailable to check signature in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if (sk_X509_num(peerlist) == 0) { + log("Unable to get any certificate for peer in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if ((md->chain[ISAKMP_NEXT_SIG] == NULL) || + ((sig_pbs = &md->chain[ISAKMP_NEXT_SIG]->pbs) == NULL)) { + log("Signature not present in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + DBG(DBG_PARSING, + DBG_dump("Computed hash", hash_val, hash_len); + ); + /* Iterate through all certificates in the peer list until */ + /* we have one which verifies the signature, or we have no */ + /* more certificates to verify with */ + for(i=0, success=0, buf = NULL; ((success == 0) && (i < sk_X509_num(peerlist))); i++) { + rsa = X509_get_pubkey(sk_X509_value(peerlist, i))->pkey.rsa; + if ((buf = alloc_bytes(EVP_PKEY_size(X509_get_pubkey(sk_X509_value(peerlist, i))), + "RSA decrypt sig")) != NULL) { + dlen = RSA_public_decrypt(pbs_left(sig_pbs), sig_pbs->cur, buf, rsa + , RSA_PKCS1_PADDING); + DBG(DBG_PARSING, + DBG_dump("Decrypted hash", buf, (dlen > hash_len) + ? hash_len : dlen); + ); + if ((dlen == hash_len) && (memcmp(buf, hash_val, hash_len) == 0)) { + DBG(DBG_PARSING, + DBG_log("SIG_R matches computed hash"); + ) + success = 1; + cert = X509_dup(sk_X509_value(peerlist, i)); + } else { + DBG(DBG_PARSING, + DBG_log("SIG_R does not match computed hash"); + ); + } + pfree(buf); + buf = NULL; + } + } + if (buf) pfree(buf); + + if (!success) { + log_err(); + DBG_cond_dump(DBG_CRYPT, + "received SIG_R does not match computed value in Main R3:" + , sig_pbs->cur, pbs_left(sig_pbs)); + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_SIGNATURE; + } else { + /* We know that certificate 'cert' was used to */ + /* sign the accurate hash which accompanied the method */ + /* Now we have to verify that it is a valid certificate */ + /* to be signing anything */ + DBG(DBG_PARSING, + DBG_log("Verifying certificate along path %s", c->path); + ); + if (verify_certificate( cert, c->lu, c->path, + (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE )) + { + DBG(DBG_PARSING, + DBG_log("Signing certificate is valid"); + ); + } else { + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_CERTIFICATE; + } + } + sk_X509_pop_free(peerlist, X509_free); + } + break; + case OAKLEY_DSS_SIG: + { + DSA *dsa; + STACK_OF(X509) *peerlist; + X509 *recv_cert, *cert; + pb_stream *cert_pbs; + int i, success; + + if (c->cert_options & CERT_OPTION_DSS_SHA) + main_mode_sha1(st, hash_val, &hash_len, FALSE, FALSE); + recv_cert = NULL; + /* First check to see if we have a certificate sent to us */ + if ((md->chain[ISAKMP_NEXT_CERT] != NULL) && + ((cert_pbs = &md->chain[ISAKMP_NEXT_CERT]->pbs) != NULL)) { + u_char *crt; + unsigned long len; + + switch(cert_pbs->cur[0]) { + case CERT_TYPE_X509_SIG: + crt = &(cert_pbs->cur[1]); + len = pbs_left(cert_pbs)-1; + recv_cert = d2i_X509(NULL, &crt, len); + break; + default: + DBG(DBG_PARSING, + DBG_log("Unhandled certificate encoding: %s", + enum_show(&cert_names, cert_pbs->cur[0])); + ); + break; + } + } + + if (recv_cert) { + char buf[200]; + + X509_NAME_oneline(X509_get_subject_name(recv_cert) + , buf, sizeof(buf)); + DBG(DBG_PARSING, + DBG_log("Sent certificate \"%s\"", buf); + ); + } + + /* We know this field is legitimate, because the */ + /* decode_peer_id has been called before */ + id_pld = md->chain[ISAKMP_NEXT_ID]; + + if ((peerlist = + peer_cert_list((STACK_OF(XMAP) *)c->lu, + EVP_PKEY_DSA, recv_cert, + id_pld, + (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE )) == NULL) { + log("Verification certificate(s) unavailable to check signature in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if (sk_X509_num(peerlist) == 0) { + log("Unable to get any certificate for peer in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + + if ((md->chain[ISAKMP_NEXT_SIG] == NULL) || + ((sig_pbs = &md->chain[ISAKMP_NEXT_SIG]->pbs) == NULL)) { + log("Signature not present in Main R3"); + return STF_FAIL + INVALID_SIGNATURE; + } + DBG(DBG_PARSING, + DBG_dump("Computed hash", hash_val, hash_len); + ); + /* Iterate through all certificates in the peer list until */ + /* we have one which verifies the signature, or we have no */ + /* more certificates to verify with */ + for(i=0, success=0, buf=NULL; ((success == 0) && (i < sk_X509_num(peerlist))); i++) { + dsa = X509_get_pubkey(sk_X509_value(peerlist, i))->pkey.dsa; + if ((buf = alloc_bytes(SHA1_DIGEST_SIZE, "DSA decrypt sig")) != NULL) { + success = (c->cert_options & CERT_OPTION_DSS_ALT) + ? DSA_verify_raw(hash_val, hash_len, + sig_pbs->cur, pbs_left(sig_pbs), + dsa) + : DSA_verify(EVP_PKEY_DSA, + hash_val, hash_len, + sig_pbs->cur, pbs_left(sig_pbs), + dsa); + if (success == 1) { + DBG(DBG_PARSING, + DBG_log("SIG_I matches computed hash"); + ); + cert = X509_dup(sk_X509_value(peerlist, i));; + } else if (success == 0) { + DBG(DBG_PARSING, + DBG_log("SIG_I does not match computed hash"); + ); + } else { + DBG(DBG_PARSING, + DBG_log("Verification error"); + ); + log_err(); + } + pfree(buf); + buf = NULL; + } + } + if (buf) pfree(buf); + if (!success) { + log_err(); + DBG_cond_dump(DBG_CRYPT, "received SIG_I does not match computed value in Main I3:" + , sig_pbs->cur, pbs_left(sig_pbs)); + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_SIGNATURE; + } else { + /* We know that certificate 'cert' was used to */ + /* sign the accurate hash which accompanied the method */ + /* Now we have to verify that it is a valid certificate */ + /* to be signing anything */ + DBG(DBG_PARSING, + DBG_log("Verifying certificate along path %s", c->path); + ); + if (verify_certificate( cert, c->lu, c->path, (c->cert_options & CERT_OPTION_STRICT) ? TRUE : FALSE )) + { + DBG(DBG_PARSING, + DBG_log("Signing certificate is valid"); + ); + } + else { + sk_X509_pop_free(peerlist, X509_free); + return STF_FAIL + INVALID_CERTIFICATE; + } + } + sk_X509_pop_free(peerlist, X509_free); + } + break; + default: + return STF_INTERNAL_ERROR; + break; + } + + /**************** done input ****************/ + + /* Advance state */ + st->st_state = STATE_MAIN_I4; + c->newest_isakmp_sa = st->st_serialno; + + update_iv(st); /* finalize our Phase 1 IV */ + + return STF_UNPEND_QUICK; +} +#endif + +#ifdef OPENSSL +/* Handle HDR*;HASH_R from responder. + */ +stf_status +main_inR3_pk(struct msg_digest *md) +{ + struct state *const st = md->st; + struct connection *c = st->st_connection; + + /* input code similar to main_inI3_outR3 -- should be factored */ + + DBG(DBG_PARSING, + DBG_log("in main_inR3_pk"); + ); + + /* HASH_R in */ + // CHECK_HASH(main_mode_hash(st, hash_val, &hash_len, FALSE, FALSE) + // , "HASH_R", "Main R3"); + RETURN_STF_FAILURE(check_main_authenticator(md, FALSE)); + + /**************** done input ****************/ + + /* Advance state */ + st->st_state = STATE_MAIN_I4; + c->newest_isakmp_sa = st->st_serialno; + + update_iv(st); /* finalize our Phase 1 IV */ + + return STF_UNPEND_QUICK; +} +#endif /* Handle first message of Phase 2 -- Quick Mode. * HDR*, HASH(1), SA, Ni [, KE ] [, IDci, IDcr ] --> diff -ruN freeswan-1.5.orig/pluto/ipsec_doi.h freeswan-1.5/pluto/ipsec_doi.h --- freeswan-1.5.orig/pluto/ipsec_doi.h Tue Jun 20 22:27:28 2000 +++ freeswan-1.5/pluto/ipsec_doi.h Thu Oct 5 07:05:59 2000 @@ -28,9 +28,27 @@ main_inI1_outR1, main_inR1_outI2, main_inI2_outR2, +#ifdef OPENSSL + main_inI2_outR2_pk, + main_inI2_outR2_rpk, +#endif main_inR2_outI3, +#ifdef OPENSSL + main_inR2_outI3_pk, + main_inR2_outI3_rpk, +#endif main_inI3_outR3, +#ifdef OPENSSL + main_inI3_outR3_whichds, + main_inI3_outR3_ds, + main_inI3_outR3_pk, +#endif main_inR3, +#ifdef OPENSSL + main_inR3_whichds, + main_inR3_ds, + main_inR3_pk, +#endif quick_inI1_outR1, quick_inR1_outI2, quick_inI2; diff -ruN freeswan-1.5.orig/pluto/main.c freeswan-1.5/pluto/main.c --- freeswan-1.5.orig/pluto/main.c Fri Jan 21 21:51:17 2000 +++ freeswan-1.5/pluto/main.c Thu Oct 5 07:05:59 2000 @@ -29,6 +29,11 @@ #include +#ifdef OPENSSL +#include +#include +#endif + #include "constants.h" #include "defs.h" #include "id.h" @@ -42,6 +47,20 @@ #include "rnd.h" #include "state.h" +#ifdef OPENSSL +#include "openssl.h" +#include "xmap_dir.h" +#include "xmap_file.h" +#ifdef HAVE_DB +#include "xmap_db.h" +#endif +#ifdef HAVE_LDAP +#include "xmap_ldap.h" +#endif +#include "xmap.h" + +#endif /* OPENSSL */ + #include "sha1.h" #include "md5.h" #include "crypto.h" /* requires sha1.h and md5.h */ @@ -156,6 +175,21 @@ bool log_to_stderr_desired = FALSE; int lockfd; +#ifdef OPENSSL + SSLeay_add_all_algorithms(); + X509V3_add_standard_extensions(); + ERR_load_crypto_strings(); + ERR_load_ElGamal_strings(); + XMAP_register(XMAP_METHOD_file()); + XMAP_register(XMAP_METHOD_dir()); +#ifdef HAVE_DB + XMAP_register(XMAP_METHOD_db()); +#endif /* HAVE_DB */ +#ifdef HAVE_LDAP + XMAP_register(XMAP_METHOD_ldap()); +#endif /* HAVE_LDAP */ +#endif + /* handle arguments */ for (;;) { @@ -409,6 +443,10 @@ #ifdef LEAK_DETECTIVE report_leaks(); #endif /* LEAK_DETECTIVE */ +#ifdef OPENSSL + X509V3_EXT_cleanup(); + ERR_free_strings(); +#endif close_log(); exit(status); } diff -ruN freeswan-1.5.orig/pluto/md5.c freeswan-1.5/pluto/md5.c --- freeswan-1.5.orig/pluto/md5.c Sat Dec 11 20:31:36 1999 +++ freeswan-1.5/pluto/md5.c Thu Oct 5 07:05:59 2000 @@ -139,7 +139,11 @@ /* MD5 initialization. Begins an MD5 operation, writing a new context. */ void MD5Init (context) +#ifndef OPENSSL MD5_CTX *context; /* context */ +#else +PLUTO_MD5_CTX *context; /* context */ +#endif { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. @@ -155,7 +159,11 @@ context. */ void MD5Update (context, input, inputLen) +#ifndef OPENSSL MD5_CTX *context; /* context */ +#else +PLUTO_MD5_CTX *context; /* context */ +#endif unsigned char *input; /* input block */ UINT4 inputLen; /* length of input block */ { @@ -194,7 +202,11 @@ */ void MD5Final (digest, context) unsigned char digest[16]; /* message digest */ -MD5_CTX *context; /* context */ +#ifndef OPENSSL +MD5_CTX *context; /* context */ +#else +PLUTO_MD5_CTX *context; +#endif /* OPENSSL */ { unsigned char bits[8]; unsigned int index, padLen; diff -ruN freeswan-1.5.orig/pluto/md5.h freeswan-1.5/pluto/md5.h --- freeswan-1.5.orig/pluto/md5.h Sat Dec 11 20:31:36 1999 +++ freeswan-1.5/pluto/md5.h Thu Oct 5 07:05:59 2000 @@ -61,11 +61,22 @@ UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ +#ifndef OPENSSL } MD5_CTX; +#else +} PLUTO_MD5_CTX; +#endif /* OPENSSL */ +#ifndef OPENSSL void MD5Init PROTO_LIST ((MD5_CTX *)); void MD5Update PROTO_LIST ((MD5_CTX *, unsigned char *, UINT4)); void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); +#else +void MD5Init PROTO_LIST ((PLUTO_MD5_CTX *)); +void MD5Update PROTO_LIST + ((PLUTO_MD5_CTX *, unsigned char *, unsigned int)); +void MD5Final PROTO_LIST ((unsigned char [16], PLUTO_MD5_CTX *)); +#endif /* OPENSSL */ #define _MD5_H_ diff -ruN freeswan-1.5.orig/pluto/openssl.c freeswan-1.5/pluto/openssl.c --- freeswan-1.5.orig/pluto/openssl.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/openssl.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,1827 @@ +#ifdef OPENSSL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "constants.h" +#include "defs.h" +#include "id.h" +#include "connections.h" /* needs id.h */ +#include "state.h" +#include "kernel.h" +#include "log.h" +#include "packet.h" +#include "spdb.h" +#include "demux.h" +#include "openssl.h" +#include "xmap.h" + + +struct _tok_node { + char *s; + struct _tok_node *next; +}; + +typedef struct _tok_node *tok_list; + +enum tok_state { TOK_NORMAL, TOK_QUOTE, TOK_BACKSLASH, TOK_BACKSLASH_Q }; + +void tok_append(tok_list *t, const char *s); +tok_list tokenize( const char *s, const char sep ); +void tok_free( tok_list t ); +X509_CRL *lookup_crl(STACK_OF(XMAP) *lu, X509_NAME *name); +X509 *lookup_cert_by_subject(STACK_OF(XMAP) *lu, X509_NAME *name); +void build_cert_chain( STACK_OF(X509) **ch, STACK_OF(XMAP) *lu, X509 *x ); +static int cert_compare(X509 *a, X509 *b); +static STACK_OF(X509) *lookup_certs_by_ip(STACK_OF(XMAP) *lu, + const char *id, + const size_t len); +static int ASN1_UTCTIME_cmp(ASN1_UTCTIME *a, ASN1_UTCTIME *b); +static bool have_othercert( struct state *st, int type ); +static int cb( int ok, X509_STORE_CTX *ctx ); + +void +tok_append(tok_list *t, const char *s) { + tok_list p, pp; + + if (!t) return; + if ((p = malloc(sizeof(struct _tok_node))) == NULL) return; + if ((p->s = strdup(s)) == NULL) { free(p); return; } + p->next = NULL; + + if (*t == NULL) { *t = p; return; }; /* Empty list */ + pp = *t; + while (pp->next != NULL) pp = pp->next; + pp->next = p; +} + +tok_list +tokenize( const char *s, const char sep ) +{ + tok_list t = NULL; + int len, i, cnt, size; + char *buf; + enum tok_state st = TOK_NORMAL; + + cnt = 0; + len = strlen(s); + size = 256; /* Initial size */ + if ((buf = malloc(size)) == NULL) return t; + memset(buf, 0, size); + for(i=0; inext; + + free(p->s); + free(p); + p = pp; + } +} + +void +log_err( void ) +{ + unsigned long l; + char buf[200]; + const char *file, *data; + int line, flags; + unsigned long es; + + es=CRYPTO_thread_id(); + while ((l=ERR_get_error_line_data(&file,&line,&data,&flags)) != 0) + { + log("%lu:%s:%s:%d:%s\n",es,ERR_error_string(l,buf), + file,line,(flags&ERR_TXT_STRING)?data:""); + } +} + +void +make_lookups( STACK_OF(XMAP) **lu, const char *spec ) +{ +#define MAXSPEC 10 + const char *delim = ", "; + char *s, *p, *specs[MAXSPEC]; + int i; + XMAP *xm; + + for(i=0; i 0x00903000L + *x = PEM_read_bio_X509(b, NULL, NULL, NULL); +#else + *x = PEM_read_bio_X509(b, NULL, NULL); +#endif + if (*x == NULL) { + /* Try DER read */ + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + if ((*x = d2i_X509_bio(b, NULL)) == NULL) { + log_err(); + BIO_free(b); + return; + } + } + DBG(DBG_PARSING, + DBG_log("Read certificate from %s", cert); + ); + BIO_free(b); + + if ((b = BIO_new_file(key, "r")) == NULL) { + log_err(); + X509_free(*x); + *x = NULL; + return; + } + +#if OPENSSL_VERSION_NUMBER > 0x00903000L + *k = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL); +#else + *k = PEM_read_bio_PrivateKey(b, NULL, NULL); +#endif + if (!*k) { + RSA *r; + + /* Try DER reading of RSA Key */ + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + r = d2i_RSAPrivateKey_bio(b, NULL); + if (r) { + if ((*k = EVP_PKEY_new()) != NULL) { + (*k)->type = EVP_PKEY_RSA; + (*k)->pkey.rsa = r; + strncpy(keystr, "DER RSA private key", sizeof(keystr)-1); + } else + RSA_free(r); + } + } + + if (!*k) { + DSA *d; + + /* Try DER reading of DSA/El Gamal Key */ + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + d = d2i_DSAPrivateKey_bio(b, NULL); + if (d) { + if ((*k = EVP_PKEY_new()) != NULL) { + (*k)->type = EVP_PKEY_DSA; + (*k)->pkey.dsa = d; + strncpy(keystr, "DER DSA private key", sizeof(keystr)-1); + } else + DSA_free(d); + } + } + + if (!*k) { + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + /* try PKCS8 (unencrypted) keyform */ + + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + p8inf = PEM_read_bio_PKCS8_PRIV_KEY_INFO(b, NULL, NULL, NULL); + if (!p8inf) { + /* Try reading PKCS8 in DER format */ + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(b, NULL); + } else + strncpy(keystr, "PEM PKCS-8 private key", sizeof(keystr)-1); + if (!p8inf) { + log("Error in loading PKCS-8 private key"); + log_err(); + X509_free(*x); + *x = NULL; + BIO_free(b); + return; + } else + strncpy(keystr, "DER PKCS-8 private key", sizeof(keystr)-1); + if ((*k = EVP_PKCS82PKEY(p8inf)) == NULL) { + log("Error in converting PKCS-8 private key"); + log_err(); + X509_free(*x); + *x = NULL; + BIO_free(b); + return; + } + } + DBG(DBG_PARSING, + DBG_log("Read %s from %s", keystr, key); + ); + BIO_free(b); +} + +static int +cert_compare(X509 *a, X509 *b) +{ + /* Check for equality of 2 X509 certificates */ + /* certs are equal if their issuers, serial numbers */ + /* subject names and public keys are the same */ + /* We could probably do all of this by converting the */ + /* certs to DER format and comparing the byte streams */ + /* but that's likely to be inefficient -- nd */ + int c; + EVP_PKEY *ka, *kb; + + if ((c = X509_issuer_and_serial_cmp(a, b)) != 0) + return c; + if ((c = X509_NAME_cmp(X509_get_issuer_name(a), + X509_get_issuer_name(b))) != 0) + return c; + ka = X509_get_pubkey(a); + kb = X509_get_pubkey(b); + if (ka->type != kb->type) return 1; + switch (ka->type) { + case EVP_PKEY_RSA: + { + /* RSA compare - if public exponent and modulus are equal, return 0 */ + RSA *ra, *rb; + + ra = ka->pkey.rsa; + rb = ka->pkey.rsa; + + if (BN_cmp(ra->e, rb->e) != 0) return 1; + if (BN_cmp(ra->n, rb->n) != 0) return 1; + } + break; + case EVP_PKEY_DSA: + { + /* DSA compare, g, modulus and pubkey must be equal */ + DSA *da, *db; + + da = ka->pkey.dsa; + db = ka->pkey.dsa; + + if (BN_cmp(da->g, db->g) != 0) return 1; + if (BN_cmp(da->p, db->p) != 0) return 1; + if (BN_cmp(da->pub_key, db->pub_key) != 0) return 1; + } + break; + default: + DBG(DBG_PARSING, + DBG_log("Unknown key type presented: %d", ka->type); + ); + return 1; + break; + } + return 0; +} + +static STACK_OF(X509) * +lookup_certs_by_ip(STACK_OF(XMAP) *lu, + const char *id, + const size_t len) +{ + STACK_OF(X509) *osk = sk_X509_new_null(); + int i; + + for(i=0; idata.x509); + int k, found; + + for(k=0, found=0; (!found) && (k < sk_X509_num(osk)); k++) { + if (cert_compare(sk_X509_value(osk, k), c) == 0) + found = 1; + } + + if (!found) { + if (sk_X509_num(osk) == 0) { + sk_X509_push(osk, c); + } else { + ASN1_UTCTIME *new_notBefore = X509_get_notBefore(c); + ASN1_UTCTIME *new_notAfter = X509_get_notAfter(c); + + for(k=0; k 0) break; /* insert here if c expiry is later than tx */ + } else + if (c1 > 0) break; /* insert here is c start date is later than tx */ + /* else move on to the next certificate in the current output stack */ + } + sk_X509_insert(osk, c, k); /* Insert into the output stack */ + } + } + } + sk_X509_OBJECT_pop_free(op, X509_OBJECT_free); + pfree(cl); + } + } + if (osk) DBG(DBG_PARSING, + DBG_log("Returning %d certs in list", sk_X509_num(osk))); + return osk; +} + +/* peer_cert_list takes a supplied list of XMAPs */ +/* and adds all of the X509 certificates whose */ +/* public key matches 'type' into a safe stack certificates */ +/* If the parameter 'cert' is non-NULL, only the certificate */ +/* which matches it will be added to the list */ +STACK_OF(X509) * +peer_cert_list( STACK_OF(XMAP) *lu, int type, X509 *cert, + struct payload_digest *const id_pld, + bool strict ) +{ + STACK_OF(X509) *sk = NULL, *osk = NULL; + pb_stream *const id_pbs = &id_pld->pbs; + struct isakmp_id *const id = &id_pld->payload.id; + int i, found; + + switch(id->isaid_idtype) { + case ID_IPV4_ADDR: + if (pbs_left(id_pbs) != sizeof(struct in_addr)) { + DBG(DBG_PARSING, + DBG_log("ID size is not equal to that of an IPv4 address"); + ); + return NULL; + } + if ((sk = lookup_certs_by_ip(lu, + id_pbs->cur, + sizeof(struct in_addr))) == NULL) { + DBG(DBG_PARSING, + DBG_log("No certs for this IP address"); + ); + return NULL; + } + break; + default: + DBG(DBG_PARSING, + DBG_log("No means to check certificates for ID type = %s", + enum_show(&ident_names, id->isaid_idtype)); + ); + return NULL; + } + + /* Now filter out any certificates not of type 'type' */ + if ((osk = sk_X509_new_null()) != NULL) { + for(i=0; itype == type) { + sk_X509_push(osk, X509_dup(x)); + } else { + DBG(DBG_PARSING, + DBG_log("Rejecting certificate: wrong type: %d", pk->type); + ); + } + } + sk_X509_pop_free(sk, X509_free); + sk = osk; /* Make sk the new filtered list */ + } else { + DBG(DBG_PARSING, + DBG_log("Error in allocating new sk_X509"); + ); + sk_X509_pop_free(sk, X509_free); + sk = NULL; + } + + if ((sk) && (cert)) { + /* If a certificate was presupplied, we want to check */ + /* whether it is in the list of acceptable certificates */ + /* for this connection. If no certificate is presupplied */ + /* then it is assumed that one of the certificates in */ + /* the list will correctly decrypt the signature/nonces */ + + for(i=0, found=0; ((!found) && (i < sk_X509_num(sk))); i++) + if (cert_compare(sk_X509_value(sk, i), cert) == 0) found = 1; + + if (found) { + DBG(DBG_PARSING, + DBG_log("Certificate supplied is in list"); + ); + } else { + DBG(DBG_PARSING, + DBG_log("Certificate supplied is NOT in list"); + ); + + /* If strict is not set, this certificate will be allowed */ + /* to pass, even though the host cannot recognise it as */ + /* in the list of known certificates for this ID. Otherwise */ + /* we erase the list and return NULL -- an error condition */ + if (strict) { + sk_X509_pop_free(sk, X509_free); + sk = NULL; + } + } + } + + return sk; +} + +bool +have_rsa_key( struct state *st ) +{ + struct connection *c = st->st_connection; + + if (c->key == NULL) { + DBG(DBG_PARSING, + DBG_log("No key present"); + ); + return FALSE; + } + if ( ((EVP_PKEY *)(c->key))->type != EVP_PKEY_RSA ) { return FALSE; } + return TRUE; +} + +bool +have_dss_key( struct state *st ) +{ + struct connection *c = st->st_connection; + + if (c->key == NULL) { + DBG(DBG_PARSING, + DBG_log("No key present"); + ); + return FALSE; + } + if ( ((EVP_PKEY *)(c->key))->type != EVP_PKEY_DSA ) { return FALSE; } + return TRUE; +} + +static bool +have_othercert( struct state *st, int type ) +{ + struct connection *c = st->st_connection; + struct end *that = &st->st_connection->that; + STACK_OF(X509) *op = NULL; + X509 *x; + int i; + bool found; + + /* First check to see if we've already done */ + /* this lookup and stored the certificate in */ + /* c->other[] */ + + for(i=0; iother[i].type == type) && + (c->other[i].cert != NULL)) { + DBG(DBG_PARSING, + DBG_log("Already stored a type %d cert", type); + ); + return TRUE; + } + + op = lookup_certs_by_ip((STACK_OF(XMAP) *)c->lu, + (const char *)&that->host_addr, + sizeof(that->host_addr)); + if (!op) { + DBG(DBG_PARSING, + DBG_log("IP lookup returns NULL"); + ); + } else { + int i; + + for(i=0, found = FALSE; ((!found) && (i < sk_X509_num(op))); i++) { + x = sk_X509_value(op, i); + if (X509_get_pubkey(x)->type == type) { + found = TRUE; + for(i=0; iother[i].cert == NULL) { + DBG(DBG_PARSING, + DBG_log("Storing type %d cert at position %d", type, i); + ); + c->other[i].type = type; + c->other[i].cert = (void *)X509_dup(x); + break; + } + } + } + } + + if (found) { + DBG(DBG_PARSING, + DBG_log("Found type %d cert at position %d:", + type, i+1); + ); + } else { + DBG(DBG_PARSING, + DBG_log("No appropriate cert found"); + ); + } + sk_X509_pop_free(op, X509_free); + } + return (found); +} + +bool +have_rsa_keypair( struct state *st ) +{ + bool r = have_othercert(st, EVP_PKEY_RSA); + + if (r) r = have_rsa_key(st); + + DBG(DBG_PARSING, + DBG_log("Have RSA other cert = %s", r ? "TRUE" : "FALSE"); + ); + return r; +} + +bool +have_elgamal_keypair( struct state *st ) +{ + bool r = have_othercert(st, EVP_PKEY_DSA); + if (r) r = have_dss_key(st); + DBG(DBG_PARSING, + DBG_log("Have El Gamal other cert = %s", r ? "TRUE" : "FALSE"); + ); + return r; +} + +static int +cb( int ok, X509_STORE_CTX *ctx ) +{ + char buf[200]; + + if (!ok) { + if (ctx->error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) { + /* Check to see if it is a CA certificate, ie with */ + /* basicConstraints, and perhaps other checks */ + /* For the moment, we just let it slide */ + ok = 1; + } else { + X509_NAME_oneline(X509_get_subject_name(ctx->current_cert), buf, sizeof(buf)); + + DBG(DBG_PARSING, + DBG_log("Error for certificate: %s", buf); + DBG_log("Error %d: Depth %d: %s", + ctx->error, + ctx->error_depth, + X509_verify_cert_error_string(ctx->error)); + ); + } + } + return (ok); +} + +static int +ASN1_UTCTIME_cmp(ASN1_UTCTIME *a, ASN1_UTCTIME *b) +{ + char buff1[14],buff2[14],*p; + int i,j; + + /* Degenerate cases */ + if (!a && !b) return 0; + if (a && !b) return 1; + if (!a && b) return -1; + + memset(buff1, 0, sizeof(buff1)); + memset(buff2, 0, sizeof(buff2)); + + p=buff1; + i=a->length; + if ((i < 11) || (i > 17)) return(0); + memcpy(p,a->data,12); + + p=buff2; + j=b->length; + if ((j < 11) || (j > 17)) return(0); + memcpy(p,b->data,12); + + /* Correct for Y2K */ + i=(buff1[0]-'0')*10+(buff1[1]-'0'); + if (i < 50) i+=100; /* cf. RFC 2459 */ + j=(buff2[0]-'0')*10+(buff2[1]-'0'); + if (j < 50) j+=100; + + if (i < j) return (-1); + if (i > j) return (1); + + i=strcmp(buff1,buff2); + + if (i == 0) + return 0; + else if (i < 0) + return(-1); + else + return(1); +} + +X509_CRL * +lookup_crl(STACK_OF(XMAP) *lu, X509_NAME *name) +{ + X509_CRL *x = NULL; + STACK_OF(X509_CRL) *osk = sk_X509_CRL_new_null(); + int i, j, k, found; + + for(i=0; idata.crl); + + for(k=0, found = 0; (!found) && (k 0) + break; /* insert here if last Update is newer than current crl */ + } + + sk_X509_CRL_insert(osk, xx, k); /* Insert into the output stack */ } + } else + X509_CRL_free(xx); + } + sk_X509_OBJECT_pop_free(xo, X509_OBJECT_free); /* drop search return values */ + } + } + } + + if (sk_X509_CRL_num(osk) > 0) + x = X509_CRL_dup(sk_X509_CRL_value(osk, 0)); /* use first certificate in stack */ + sk_X509_CRL_pop_free(osk, X509_CRL_free); /* get rid of output stack */ + return x; +} + +static STACK_OF(XMAP) *lookups = NULL; +static bool verify_strict; +/* only way I can think to get the verify function (which is) */ +/* used as a callback to access the 'strict verification' flag */ +/* The problem is that there isn't any space in the parameters */ +/* to the verify_func in which to add this flag. Pity, because */ +/* a static global controlling verification policy is a fairly */ +/* stinky idea -- nd */ + +/* The following code is simply the code from x509_vfy.c */ +/* with CRL checking added, and basicConstraints checking */ +static int +verify_func(X509_STORE_CTX *ctx) +{ + int i,ok=0,n; + X509 *xs,*xi; + EVP_PKEY *pkey=NULL; + int (*cb)(); + X509_CRL *crl; + + cb=ctx->ctx->verify_cb; + + n=sk_X509_num(ctx->chain); + DBG(DBG_PARSING, + DBG_log("%d certs in chain", n); + ); + ctx->error_depth=n-1; + n--; + xi=sk_X509_value(ctx->chain,n); + DBG(DBG_PARSING, + DBG_log("xi = %08X", (u_int32_t)xi); + ); + if (X509_NAME_cmp(X509_get_subject_name(xi), + X509_get_issuer_name(xi)) == 0) + xs=xi; + else { + if (n <= 0) { + ctx->error=X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; + ctx->current_cert=xi; + ok=cb(0,ctx); + goto end; + } else { + n--; + ctx->error_depth=n; + xs=sk_X509_value(ctx->chain,n); + } + } + + while (n >= 0) { + ctx->error_depth=n; + if (!xs->valid) { + if ((pkey=X509_get_pubkey(xi)) == NULL) + { + ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; + ctx->current_cert=xi; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + if (X509_verify(xs,pkey) <= 0) { + EVP_PKEY_free(pkey); + ctx->error=X509_V_ERR_CERT_SIGNATURE_FAILURE; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + EVP_PKEY_free(pkey); + pkey=NULL; + + i=X509_cmp_current_time(X509_get_notBefore(xs)); + if (i == 0) { + ctx->error=X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + if (i > 0) { + ctx->error=X509_V_ERR_CERT_NOT_YET_VALID; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + xs->valid=1; + } + + i=X509_cmp_current_time(X509_get_notAfter(xs)); + if (i == 0) { + ctx->error=X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + if (i < 0) { + ctx->error=X509_V_ERR_CERT_HAS_EXPIRED; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + if (n == sk_X509_num(ctx->chain)-1) { + /* This is the root certificate */ + int loc = X509_get_ext_by_NID(xs, NID_basic_constraints, -1); + if (loc >= 0) { + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + + ext = X509_get_ext(xs, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + ctx->error=X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } else { + DBG(DBG_PARSING, + DBG_log("Root certificate has CA = True"); + ); + BASIC_CONSTRAINTS_free(p); + } + } else { + /* The cert has a basicConstraints extension, but */ + /* we couldn't get it */ + ctx->error=X509_V_ERR_APPLICATION_VERIFICATION; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + } else { +#ifndef DONT_BE_FASCIST + ctx->error=X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; +#endif + } + } + + /* CRL CHECK */ + if ((crl = lookup_crl(lookups, X509_get_issuer_name(xs))) != NULL) { + EVP_PKEY *pkey = X509_get_pubkey(xi); + STACK_OF(X509_REVOKED) *rev; + int nrev; + + DBG(DBG_PARSING, + DBG_log("Checking CRL for issuer"); + ); + + if (pkey == NULL) { + ctx->error=X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + if (X509_CRL_verify(crl, pkey) <= 0) { + ctx->error=X509_V_ERR_CRL_SIGNATURE_FAILURE; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + i = X509_cmp_current_time(X509_CRL_get_lastUpdate(crl)); + if (i == 0) { + ctx->error=X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + if (i > 0) { + ctx->error=X509_V_ERR_CRL_NOT_YET_VALID; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); + if (i == 0) { + ctx->error=X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + if (i < 0) { + ctx->error=X509_V_ERR_CRL_HAS_EXPIRED; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + + rev = X509_CRL_get_REVOKED(crl); + nrev = sk_X509_REVOKED_num(rev); + DBG(DBG_PARSING, + DBG_log("%d certificates revoked in CRL", nrev); + ); + for(i=0; iserialNumber) == 0) { + /* It's this certificate that's been revoked */ + int c = X509_cmp_current_time(rv->revocationDate); + if (c < 0) { + ctx->error=X509_V_ERR_CERT_REVOKED; + X509_CRL_free(crl); + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + DBG(DBG_PARSING, + DBG_log("Serial Number matches"); + ); + } + } + + X509_CRL_free(crl); + } else { + char buf[200]; + + X509_NAME_oneline(X509_get_issuer_name(xs), buf, sizeof(buf)); + DBG(DBG_PARSING, + DBG_log("Cant locate a CRL for issuer %s", buf); + ); + /* If we're being paranoid here, we really should fail */ + /* the verification. An attacker capable of mounting */ + /* a denial of service attack could cause us fail to */ + /* load the CRL, and use a revoked, but otherwise valid */ + /* certificate in the exchange to set up a valid SA */ + /* In this case, we let it go, but an idea might be */ + /* to have an ipsec.conf specified policy on how mandatory */ + /* CRLs are in the verification sequence -- nd */ + if (verify_strict) { + ctx->error=X509_V_ERR_UNABLE_TO_GET_CRL; + ctx->current_cert=xs; + ok=(*cb)(0,ctx); + if (!ok) goto end; + } + } + + /* The last error (if any) is still in the error value */ + ctx->current_cert=xs; + ok=(*cb)(1,ctx); + if (!ok) goto end; + + n--; + if (n >= 0) { + xi=xs; + xs=sk_X509_value(ctx->chain,n); + } + } + ok=1; + end: + return(ok); +} + +X509 * +lookup_cert_by_subject(STACK_OF(XMAP) *lu, X509_NAME *xn) +{ + X509 *x = NULL; /* return value */ + STACK_OF(X509) *osk = sk_X509_new_null(); /* output stack - concatenation of certificates */ + int i, j, k, found; + + DBG(DBG_PARSING, + DBG_log("Looking up certificate by subject"); + ); + for(i=0; idata.x509); + + for(k=0, found = 0; (!found) && (k 0) break; /* insert here if xx expiry is later than tx */ + } else + if (c1 > 0) break; /* insert here is xx start date is later than tx */ + /* else move on to the next certificate in the current output stack */ + } + sk_X509_insert(osk, xx, k); /* Insert into the output stack */ + } + } else + X509_free(xx); + } + sk_X509_OBJECT_pop_free(xo, X509_OBJECT_free); + } + } + } + DBG(DBG_PARSING, + DBG_log("Finished lookups"); + ); + + if (sk_X509_num(osk) > 0) + x = X509_dup(sk_X509_value(osk, 0)); + sk_X509_pop_free(osk, X509_free); /* get rid of output stack */ + return x; +} + +void +build_cert_chain( STACK_OF(X509) **ch, STACK_OF(XMAP) *lu, X509 *x ) +{ + int done = 0; + X509 *xx; + + if (!ch) return; + if ((*ch = sk_X509_new_null()) == NULL) return; + xx = x; + sk_X509_push(*ch, X509_dup(xx)); + while (!done) { + X509 *xi; + + DBG(DBG_PARSING, + DBG_log("Looking up certificate issuer"); + ); + if ((xi = lookup_cert_by_subject(lu, X509_get_issuer_name(xx))) != NULL) { + DBG(DBG_PARSING, + DBG_log("Pushing certificate onto stack"); + ); + sk_X509_push(*ch, xi); + DBG(DBG_PARSING, + DBG_log("Comparing subject and issuer"); + ); + if (X509_NAME_cmp(X509_get_subject_name(xi), + X509_get_issuer_name(xi)) == 0) { + done = 1; + } else { + xx = xi; + } + DBG(DBG_PARSING, + DBG_log("Done = %d", done); + ); + } else + done = 1; /* Cant find certificate */ + } +} + +bool +verify_certificate( X509 *x, STACK_OF(XMAP) *lu, + const char *path, bool strict ) +{ + X509_STORE *ctx = NULL; + X509_STORE_CTX csc; + int ver; + + + if ((ctx = X509_STORE_new()) == NULL) { + DBG(DBG_PARSING, + DBG_log("Cant allocate file lookup"); + ); + log_err(); + return 0; + } + + X509_STORE_set_verify_cb_func(ctx, cb); + + ERR_clear_error(); + verify_strict = strict; + lookups = lu; + X509_STORE_CTX_init(&csc, ctx, x, NULL); + build_cert_chain(&(csc.chain), lu, x); + ver = verify_func(&csc); + X509_STORE_CTX_cleanup(&csc); + + if (ctx) X509_STORE_free(ctx); + + if (ver) { + DBG(DBG_PARSING, + DBG_log("Certificate verification succeeded"); + ); + } else { + DBG(DBG_PARSING, + DBG_log("Verification failed"); + ); + log_err(); + } + + return ver; +} + +struct cert_options { + const char *opt; + u_int32_t optflag; +}; + +static struct cert_options certopts[] = { + { "send", CERT_OPTION_SEND }, + { "pkcs7", CERT_OPTION_PKCS7 }, + { "pk", CERT_OPTION_PK }, + { "rev", CERT_OPTION_REV }, + { "strict", CERT_OPTION_STRICT }, + { "dss-sha", CERT_OPTION_DSS_SHA }, + { "dss-alt", CERT_OPTION_DSS_ALT }, + { NULL, 0 } +}; + +u_int32_t +parse_options( const char *s ) +{ + tok_list tl, p; + u_int32_t i, ret = 0, b4; + + tl = tokenize(s, ','); + for(p=tl; p; p=p->next) { + int found = 0; + char *st = p->s; + + while (isspace(*st)) st++; /* skip whitespace */ + for(i=0; (!found) && (certopts[i].opt != NULL); i++) { + if ((st[0] == '!') && + (strncasecmp(&(st[1]), certopts[i].opt, + strlen(certopts[i].opt)) == 0)) { + found = 1; + b4 = ret; + ret &= ~certopts[i].optflag; + DBG(DBG_PARSING, + DBG_log("Cert option: Clearing option %s: %08x -> %08x", certopts[i].opt, b4, ret); + ); + } else if (strncasecmp(st, certopts[i].opt, + strlen(certopts[i].opt)) == 0) { + found = 1; + b4 = ret; + ret |= certopts[i].optflag; + DBG(DBG_PARSING, + DBG_log("Cert option: Setting option %s: %08x -> %08x", certopts[i].opt, b4, ret); + ); + } + } + } + tok_free(tl); + return ret; +} +int +DSA_sign_raw(unsigned char *dgst,int dlen, + unsigned char *sig, unsigned int *siglen, DSA *dsa) +{ + DSA_SIG *s; + int offset; + + DBG(DBG_PARSING, + DBG_log("DSA_sign_raw called"); + ); + s= DSA_do_sign(dgst, dlen, dsa); + if (s == NULL) { + *siglen=0; + return 0; + } + memset(sig, 0, SHA1_DIGEST_SIZE * 2); + offset = SHA1_DIGEST_SIZE - BN_num_bytes(s->r); + BN_bn2bin(s->r, &(sig[offset])); + offset = SHA1_DIGEST_SIZE - BN_num_bytes(s->s); + BN_bn2bin(s->s, &(sig[SHA1_DIGEST_SIZE + offset])); + *siglen = SHA1_DIGEST_SIZE * 2; + DSA_SIG_free(s); + return 1; +} + +int DSA_verify_raw(const unsigned char *dgst,int dgst_len, + unsigned char *sigbuf, int siglen, DSA *dsa) +{ + DSA_SIG *s; + int ret = -1; + + DBG(DBG_PARSING, + DBG_log("DSA_verify_raw called"); + ); + if (siglen != (2 * SHA1_DIGEST_SIZE)) return ret; + s = DSA_SIG_new(); + if (s == NULL) return ret; + s->r = BN_bin2bn(sigbuf, SHA1_DIGEST_SIZE, + s->r); + s->s = BN_bin2bn(&(sigbuf[SHA1_DIGEST_SIZE]), + SHA1_DIGEST_SIZE, + s->s); + ret=DSA_do_verify(dgst, dgst_len,s,dsa); + DSA_SIG_free(s); + return ret; +} + +/* + * The following code implements ElGamal encryption using DSA keys + * in a (hopefully) OpenSSL compatible fashion. It really belongs in + * OpenSSL itself, when the code approaches being both tested and + * more efficient. In order to make things a bit faster, the modular + * operations use montogomery representation. + * + * NB - this implementation is pretty naive, and contains just the + * simplest implementation of ElGamal as per HAC, Ch8, Section 8.4, + * pp 294-295. + */ + +static ERR_STRING_DATA ElGamal_str_functs[] = +{ + {ERR_PACK(0,ELGAMAL_F_ELGAMAL_PUBLIC_ENCRYPT,0), "ElGamal_public_encrypt"}, + {ERR_PACK(0,ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT,0), "ElGamal_private_decrypt"}, + { 0, NULL } +}; + +static ERR_STRING_DATA ElGamal_str_reasons[] = +{ + { ELGAMAL_R_UNKNOWN_PADDING_TYPE, "unknown padding type" }, + { ELGAMAL_R_DATA_GREATER_THAN_MOD_LEN, "data longer than modulus length" }, + { ELGAMAL_R_PADDING_CHECK_FAILED, "padding check failed" }, + { 0, NULL } +}; + +void +ERR_load_ElGamal_strings( void ) +{ + static int init = 1; + + if (init) + { + init = 0; + + ERR_load_strings(ERR_LIB_ELGAMAL, ElGamal_str_functs); + ERR_load_strings(ERR_LIB_ELGAMAL, ElGamal_str_reasons); + } +} + +int +ElGamal_public_check( DSA *dsa ) +{ + BIGNUM res; + BN_MONT_CTX *mont; + BN_CTX *ctx; + int r = 0; + + /* Check that g^q = 1 (mod p), and that y^q = 1 (mod p) */ + /* to avoid attacks involving generators which produce small */ + /* subgroups */ + + BN_init(&res); + if ((ctx = BN_CTX_new()) == NULL) goto err; + if ((mont = BN_MONT_CTX_new()) == NULL) goto err; + if (! BN_MONT_CTX_set(mont, dsa->p, ctx)) goto err; + + if (! BN_mod_exp_mont(&res, dsa->g, dsa->q, dsa->p, ctx, mont)) goto err; + if (! BN_is_one(&res)) goto err; + if (! BN_mod_exp_mont(&res, dsa->pub_key, dsa->q, dsa->p, ctx, mont)) goto err; + if (! BN_is_one(&res)) goto err; + r = 1; /* Key is OK */ + + err: + BN_clear_free(&res); + if (ctx) BN_CTX_free(ctx); + if (mont) BN_MONT_CTX_free(mont); + + return r; +} + +int +ElGamal_public_encrypt( int flen, unsigned char *from, + unsigned char *to, + DSA *dsa, int padding ) + +{ + unsigned char *buf, *p; + int num = 0, i, j, k, r = -1; + BIGNUM kappa, alpha, beta, f; + BIGNUM BETA, F; + BN_MONT_CTX *pmont; + BN_CTX *ctx = NULL; + + num = BN_num_bytes(dsa->p) ; + BN_init(&alpha); + BN_init(&beta); + BN_init(&kappa); + BN_init(&f); + BN_init(&BETA); + BN_init(&F); + + if ((ctx = BN_CTX_new()) == NULL) goto err; + if ((pmont = BN_MONT_CTX_new()) == NULL) goto err; + if (! BN_MONT_CTX_set(pmont, dsa->p, ctx)) goto err; + if ((buf = (unsigned char *)Malloc(num)) == NULL) { + ElGamalerr(ELGAMAL_F_ELGAMAL_PUBLIC_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* generate k s.t. 1 <= k <= q-1 */ + do { + if (! BN_rand(&kappa, BN_num_bits(dsa->q), 1, 1)) goto err; + } while (BN_cmp(&kappa, dsa->q) < 0); + + switch (padding) { + case ELGAMAL_PKCS1_PADDING: + i = RSA_padding_add_PKCS1_type_2(buf, num, from, flen); + break; + case ELGAMAL_PKCS1_OAEP_PADDING: + i = RSA_padding_add_PKCS1_OAEP(buf, num, from, flen, NULL, 0); + break; + case ELGAMAL_NO_PADDING: + i = RSA_padding_add_none(buf, num, from, flen); + break; + default: + ElGamalerr( ELGAMAL_F_ELGAMAL_PUBLIC_ENCRYPT, ELGAMAL_R_UNKNOWN_PADDING_TYPE ); + goto err; + } + + if (i <= 0) goto err; + + if (BN_bin2bn(buf, num, &f) == NULL) goto err; + + /* Set alpha = g^k (mod p) */ + if (!BN_mod_exp_mont(&alpha, dsa->g, &kappa, dsa->p, ctx, pmont)) goto err; + + /* Set beta = M * y^k (mod p) */ + if (!BN_mod_exp_mont(&beta, dsa->pub_key, &kappa, dsa->p, ctx, pmont)) goto err; + + BN_to_montgomery(&F, &f, pmont, ctx); + BN_to_montgomery(&BETA, &beta, pmont, ctx); + if (!BN_mod_mul_montgomery(&BETA, &BETA, &F, pmont, ctx)) goto err; + BN_from_montgomery(&beta, &BETA, pmont, ctx); + + /* Squirt out alpha, left padded to modulus size with zero bytes */ + j = BN_num_bytes(&alpha); + i = BN_bn2bin(&alpha, &(to[num-j])); + for(k=0; k<(num-i); k++) to[k]=0; + + /* Follow this with beta */ + p = &(to[num]); + j = BN_num_bytes(&beta); + i = BN_bn2bin(&beta, &(p[num-j])); + for(k=0; k<(num-i); k++) p[k]=0; + + r = num*2; + + err: + if (ctx != NULL) BN_CTX_free(ctx); + if (pmont != NULL) BN_MONT_CTX_free(pmont); + BN_clear_free(&alpha); + BN_clear_free(&beta); + BN_clear_free(&beta); + BN_clear_free(&kappa); + BN_clear_free(&f); + BN_clear_free(&BETA); + BN_clear_free(&F); + if (buf != NULL) { + memset(buf, 0, num); + Free(buf); + } + return r; +} + +int ElGamal_private_decrypt( int flen, unsigned char *from, + unsigned char *to, + DSA *dsa, int padding ) +{ + + BIGNUM alpha, beta, gamma, delta, p1a; + BIGNUM BETA, GAMMA, DELTA; + BN_CTX *ctx; + BN_MONT_CTX *pmont; + unsigned char *p, *buf = NULL; + int j, r = -1, num; + + BN_init(&alpha); + BN_init(&beta); + BN_init(&gamma); + BN_init(&delta); + BN_init(&p1a); + BN_init(&BETA); + BN_init(&GAMMA); + BN_init(&DELTA); + if ((ctx = BN_CTX_new()) == NULL) goto err; + if ((pmont = BN_MONT_CTX_new()) == NULL) goto err; + if (! BN_MONT_CTX_set(pmont, dsa->p, ctx)) goto err; + + num = BN_num_bytes(dsa->p); + + /* set p1a to be equal to p-1-x, where x is the private key */ + if (BN_copy(&p1a, dsa->p) == NULL) goto err; + if (! BN_sub_word(&p1a, 1)) goto err; + if (! BN_sub(&p1a, &p1a, dsa->priv_key)) goto err; + + if ((buf = (unsigned char *)Malloc(num)) == NULL) { + ElGamalerr(ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (flen > num*2) { + ElGamalerr(ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT, ELGAMAL_R_DATA_GREATER_THAN_MOD_LEN); + goto err; + } + + /* Copy the cipher text into the two numbers alpha and beta */ + if (BN_bin2bn(from, flen / 2, &alpha) == NULL) goto err; + if (BN_bin2bn(&(from[flen/2]), flen / 2, &beta) == NULL) goto err; + + if (! BN_mod_exp_mont(&gamma, &alpha, &p1a, dsa->p, ctx, pmont)) goto err; + BN_to_montgomery(&GAMMA, &gamma, pmont, ctx); + BN_to_montgomery(&BETA, &beta, pmont, ctx); + if (! BN_mod_mul_montgomery(&DELTA, &GAMMA, &BETA, pmont, ctx)) goto err; + BN_from_montgomery(&delta, &DELTA, pmont, ctx); + + /* Now delta should contain the (possibly padded) plaintext */ + p = buf; + j = BN_bn2bin(&delta, p); + + switch( padding ) { + case ELGAMAL_PKCS1_PADDING: + r = RSA_padding_check_PKCS1_type_2(to, num, buf, j, num); + break; + case ELGAMAL_PKCS1_OAEP_PADDING: + r = RSA_padding_check_PKCS1_OAEP(to, num, buf, j, num, NULL, 0); + break; + case ELGAMAL_NO_PADDING: + r = RSA_padding_check_none(to, num, buf, j, num); + break; + default: + ElGamalerr(ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT, ELGAMAL_R_UNKNOWN_PADDING_TYPE); + goto err; + } + + if (r < 0) + ElGamalerr(ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT, ELGAMAL_R_PADDING_CHECK_FAILED); + + err: + if (ctx != NULL) BN_CTX_free(ctx); + if (pmont != NULL) BN_MONT_CTX_free(pmont); + BN_clear_free(&alpha); + BN_clear_free(&beta); + BN_clear_free(&gamma); + BN_clear_free(&delta); + BN_clear_free(&p1a); + BN_clear_free(&BETA); + BN_clear_free(&GAMMA); + BN_clear_free(&DELTA); + if (buf != NULL) { + memset(buf, 0, num); + Free(buf); + } + return r; +} +bool +valid_ciphertext_length( const u_int32_t len, struct connection *c ) +{ + EVP_PKEY *pk = c->key; + bool ret = FALSE; + + if ((!pk) || (len == 0)) return FALSE; + switch(pk->type) { + case EVP_PKEY_RSA: + if (len % BN_num_bytes(pk->pkey.rsa->n) == 0) ret = TRUE; + break; + case EVP_PKEY_DSA: + if (len % (2 * BN_num_bytes(pk->pkey.dsa->p)) == 0) ret = TRUE; + break; + default: + return FALSE; + } + + return ret; +} + +static void +alloc_ciphertext( const u_int32_t len, + EVP_PKEY *pk, + u_char **c, + u_int32_t *ms, + u_int32_t *bsize, + u_int32_t *bl ) +{ + int modsize, chunksize, blocks; + const int padsize = 2 * SHA1_DIGEST_SIZE - 1; + u_int32_t clen; + + switch(pk->type) + { + case EVP_PKEY_RSA: + { + RSA *rsa = pk->pkey.rsa; + modsize = BN_num_bytes(rsa->n); + } + break; + case EVP_PKEY_DSA: + { + DSA *dsa = pk->pkey.dsa; + modsize = BN_num_bytes(dsa->p) * 2; + } + break; + } + chunksize = modsize - padsize; + blocks = (len + chunksize - 1) / chunksize; + clen = blocks * modsize; + *c = malloc(clen); + *bsize = chunksize; + *bl = blocks; + *ms = modsize; +} + +void +pubkey_encrypt_chunk( chunk_t *ch, struct state *st ) +{ + struct connection *c = st->st_connection; + u_char *str = ch->ptr; + u_int32_t len = ch->len; + EVP_PKEY *pk = NULL; + u_char *ciph; + u_int32_t ms, bs, bl, clen; + u_int16_t auth = st->st_oakley.auth; + int i; + + switch(auth) { + case OAKLEY_RSA_ENC: + case OAKLEY_RSA_ENC_REV: + for(i=0; iother[i].type == EVP_PKEY_RSA) && + (c->other[i].cert != NULL)) { + pk = X509_get_pubkey((X509 *)c->other[i].cert); + break; + } + } + break; + case OAKLEY_ELGAMAL_ENC: + case OAKLEY_ELGAMAL_ENC_REV: + for(i=0; iother[i].type == EVP_PKEY_DSA) && + (c->other[i].cert != NULL)) { + pk = X509_get_pubkey((X509 *)c->other[i].cert); + break; + } + } + break; + default: + DBG(DBG_PARSING, + DBG_log("Non PK authentication scheme in state"); + ); + return; + } + + if (!pk) { + DBG(DBG_PARSING, + log("Unable to select an encryption key"); + ); + return; + } + + switch(pk->type) { + case EVP_PKEY_RSA: + { + alloc_ciphertext(len, pk, &ciph, &ms, &bs, &bl); + } + break; + case EVP_PKEY_DSA: + { + alloc_ciphertext(len, pk, &ciph, &ms, &bs, &bl); + } + break; + default: + log("Unknown key type: %d", pk->type); + break; + } + + for(i=0; itype) { + case EVP_PKEY_RSA: + { + RSA *rsa = pk->pkey.rsa; + + clen = RSA_public_encrypt(plen, pst, cst, rsa, RSA_PKCS1_OAEP_PADDING); + } + break; + case EVP_PKEY_DSA: + { + DSA *dsa = pk->pkey.dsa; + + clen = ElGamal_public_encrypt(plen, pst, cst, dsa, ELGAMAL_PKCS1_OAEP_PADDING); + } + break; + default: + log("Unknown cert type: %d", pk->type); + break; + } + } + + memset(ch->ptr, 0, ch->len); + freeanychunk(*ch); + ch->len=bl*ms; + ch->ptr=ciph; +} + +void +privkey_decrypt_chunk( chunk_t *ch, struct state *st ) +{ + u_int32_t i, bl, bs, clen; + struct connection *c = st->st_connection; + EVP_PKEY *pk = c->key; + u_char *str = ch->ptr; + u_char *pst; + u_int32_t ptr, plen; + + switch(pk->type) { + case EVP_PKEY_RSA: + { + RSA *rsa = pk->pkey.rsa; + bs = BN_num_bytes(rsa->n); + } + break; + case EVP_PKEY_DSA: + { + DSA *dsa = pk->pkey.dsa; + bs = 2 * BN_num_bytes(dsa->p); + } + break; + default: + log("Unknown key type: %d", pk->type); + break; + } + clen = ch->len; + bl = clen / bs; + pst = malloc(clen); + ptr = 0; + for(i=0; itype) { + case EVP_PKEY_RSA: + { + RSA *rsa = pk->pkey.rsa; + if (clen >= bs) { + plen = RSA_private_decrypt( bs, &str[i * bs], &pst[ptr], rsa, RSA_PKCS1_OAEP_PADDING ); + if (plen == 0) { + log_err(); + } + } else /* Ciphertext is too short */ + plen = 0; + } + break; + case EVP_PKEY_DSA: + { + DSA *dsa = pk->pkey.dsa; + + if (clen >= bs) { + plen = ElGamal_private_decrypt( bs, &str[i * bs], &pst[ptr], dsa, ELGAMAL_PKCS1_OAEP_PADDING ); + if (plen == 0) { + log_err(); + } + } else /* Short ciphertext - don't try and decrypt */ + plen = 0; + } + break; + default: + log("Unknown key type: %d", pk->type); + plen = 0; + break; + } + ptr += plen; + } + freeanychunk(*ch); + ch->ptr = pst; + ch->len = ptr; +} + +bool +use_openssl( struct connection *c ) +{ +// Using data from connection *c, we can determine if we are currently using +// the openssl-related functions or not. +// ie: +// c->cert +// c->key +// c->cert_options +// +// also use c->policy to determine policy. +// +// Return 0 if openssl, otherwise 1. + + if (c->policy & POLICY_OPENSSL) { + if ((c->cert != NULL) || + (c->key != NULL) || + (c->cert_options != 0)) + { + // OPENSSL + return 1; + } + else + { + // RSASIG only + return 0; + } + } + else + { + // PSK + return 0; + } +} + +#endif /* OPENSSL */ diff -ruN freeswan-1.5.orig/pluto/openssl.h freeswan-1.5/pluto/openssl.h --- freeswan-1.5.orig/pluto/openssl.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/openssl.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,87 @@ +#ifdef OPENSSL +#ifndef OPENSSL_INC_H +#define OPENSSL_INC_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "xmap.h" + +struct _x509_cert_node { + X509 *x; + struct _x509_cert_node *next; +}; + +typedef struct _x509_cert_node *X509_cert_list; + +typedef bool discrim_fn( X509 *x, STACK_OF(XMAP) *lu, + struct payload_digest *const id_pld, bool strict); + +extern void make_lookups(STACK_OF(XMAP) **lu, const char *spec ); +extern void log_err( void ); +extern void load_cert_and_key( const char *cert, const char *key, + X509 **x, EVP_PKEY **k ); +extern bool use_openssl( struct connection *c ); +extern STACK_OF(X509) * +peer_cert_list( STACK_OF(XMAP) *lu, + int type, + X509 *cert, struct payload_digest *const id_pld, + bool strict ); + +extern bool have_rsa_key( struct state *st ); +extern bool have_dss_key( struct state *st ); +extern bool have_rsa_keypair( struct state *st ); +extern bool have_elgamal_keypair( struct state *st ); + +extern bool verify_certificate( X509 *x, STACK_OF(XMAP) *lu, + const char *path, bool strict ); + +extern u_int32_t parse_options( const char *s ); + +extern int DSA_sign_raw(unsigned char *dgst,int dlen, + unsigned char *sig, unsigned int *siglen, DSA *dsa); + +extern int DSA_verify_raw(const unsigned char *dgst,int dgst_len, + unsigned char *sigbuf, int siglen, DSA *dsa); + +#define ERR_LIB_ELGAMAL 127 + +#define ELGAMAL_F_ELGAMAL_PRIVATE_DECRYPT 100 +#define ELGAMAL_F_ELGAMAL_PUBLIC_ENCRYPT 101 + +#define ELGAMAL_R_UNKNOWN_PADDING_TYPE 100 +#define ELGAMAL_R_DATA_GREATER_THAN_MOD_LEN 101 +#define ELGAMAL_R_PADDING_CHECK_FAILED 102 + +#define ELGAMAL_PKCS1_PADDING RSA_PKCS1_PADDING +#define ELGAMAL_PKCS1_OAEP_PADDING RSA_PKCS1_OAEP_PADDING +#define ELGAMAL_NO_PADDING RSA_NO_PADDING + +#define ElGamalerr(f, r) ERR_PUT_error(ERR_LIB_ELGAMAL, (f), (r), ERR_file_name, __LINE__) + +void ERR_load_ElGamal_strings( void ); +int ElGamal_public_check( DSA *dsa ); +int ElGamal_public_encrypt( int flen, unsigned char *from, + unsigned char *to, + DSA *dsa, int padding ); +int ElGamal_private_decrypt( int flen, unsigned char *from, + unsigned char *to, + DSA *dsa, int padding ); + +/* Function pointer pointing to an encryption routine */ +typedef void encryptor_fn( chunk_t *ch, struct state *c ); + +extern bool valid_ciphertext_length( const u_int32_t len, + struct connection *c ); +extern encryptor_fn pubkey_encrypt_chunk, privkey_decrypt_chunk; + +#endif +#endif + + diff -ruN freeswan-1.5.orig/pluto/openssl_defs.h freeswan-1.5/pluto/openssl_defs.h --- freeswan-1.5.orig/pluto/openssl_defs.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/openssl_defs.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,5 @@ + +#define FILE_NAME_LIMIT 80 +#define PATH_NAME_LIMIT 256 +#define CERT_OPTIONS_LIMIT 128 +#define MAX_OTHER 2 /* only know about two type of certificate at the moment (RSA,DSA) */ diff -ruN freeswan-1.5.orig/pluto/packet.c freeswan-1.5/pluto/packet.c --- freeswan-1.5.orig/pluto/packet.c Wed Jun 21 14:24:34 2000 +++ freeswan-1.5/pluto/packet.c Thu Oct 5 07:05:59 2000 @@ -623,7 +623,7 @@ /* put at least one * out */ for (;;) { - if (pre <= space) + if (pre <= space) break; *--pre = '*'; if (p == NULL) diff -ruN freeswan-1.5.orig/pluto/spdb.c freeswan-1.5/pluto/spdb.c --- freeswan-1.5.orig/pluto/spdb.c Wed Jun 21 14:24:35 2000 +++ freeswan-1.5/pluto/spdb.c Thu Oct 5 07:05:59 2000 @@ -39,6 +39,11 @@ #include "md5.h" #include "crypto.h" /* requires sha1.h and md5.h */ +#ifdef OPENSSL +#include "demux.h" +#include "openssl.h" +#endif + #define AD(x) x, elemsof(x) /* Array Description */ /**************** Oakely (main mode) SA database ****************/ @@ -103,6 +108,86 @@ { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, }; +#ifdef OPENSSL /* !OPENSSL */ + +static struct db_attr ot1024des3md5_ds_rsa[] = { + { 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 ot1024des3sha_ds_rsa[] = { + { 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 ot1024des3sha_ds_dss[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_DSS_SIG }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3md5_pk_rsa[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_ENC }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3sha_pk_rsa[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_ENC }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3sha_pk_elgamal[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_ELGAMAL_ENC }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3md5_pk_elgamal[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_ELGAMAL_ENC }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3md5_rpk_rsa[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_ENC_REV }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3sha_rpk_rsa[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_RSA_ENC_REV }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3sha_rpk_elgamal[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_SHA }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_ELGAMAL_ENC_REV }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; + +static struct db_attr ot1024des3md5_rpk_elgamal[] = { + { OAKLEY_ENCRYPTION_ALGORITHM, OAKLEY_3DES_CBC }, + { OAKLEY_HASH_ALGORITHM, OAKLEY_MD5 }, + { OAKLEY_AUTHENTICATION_METHOD, OAKLEY_ELGAMAL_ENC_REV }, + { OAKLEY_GROUP_DESCRIPTION, OAKLEY_GROUP_MODP1024 }, + }; +#endif /* OPENSSL */ + /* 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! @@ -118,6 +203,7 @@ /* 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) }, @@ -152,6 +238,53 @@ { KEY_IKE, AD(otpsk768des3sha) }, }; +#ifdef OPENSSL + +static struct db_trans oakley_trans_psk2[] = { +#ifdef TEST_INDECENT_PROPOSAL + { KEY_IKE, AD(otpsk1024des3tiger), NULL }, +#endif + { KEY_IKE, AD(otpsk1024des3sha), NULL }, + { KEY_IKE, AD(otpsk1024des3md5), NULL }, + { KEY_IKE, AD(otpsk768des3md5), NULL }, + { KEY_IKE, AD(otpsk768des3sha), NULL }, + }; + +static struct db_trans oakley_trans_openssl[] = { + { KEY_IKE, AD(ot1024des3sha_rpk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3sha_rpk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3md5_rpk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3md5_rpk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3sha_pk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3sha_pk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3md5_pk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3md5_pk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3sha_ds_dss), have_dss_key }, + { KEY_IKE, AD(ot1024des3sha_ds_rsa), have_rsa_key }, + { KEY_IKE, AD(ot1024des3md5_ds_rsa), have_rsa_key }, + }; + +static struct db_trans oakley_trans_psk2openssl[] = { + { KEY_IKE, AD(ot1024des3sha_rpk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3sha_rpk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3md5_rpk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3md5_rpk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3sha_pk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3sha_pk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3md5_pk_elgamal), have_elgamal_keypair }, + { KEY_IKE, AD(ot1024des3md5_pk_rsa), have_rsa_keypair }, + { KEY_IKE, AD(ot1024des3sha_ds_dss), have_dss_key }, + { KEY_IKE, AD(ot1024des3sha_ds_rsa), have_rsa_key }, + { KEY_IKE, AD(ot1024des3md5_ds_rsa), have_rsa_key }, + { KEY_IKE, AD(otpsk1024des3sha), NULL }, + { KEY_IKE, AD(otpsk1024des3md5), NULL }, + { KEY_IKE, AD(otpsk768des3md5), NULL }, + { KEY_IKE, AD(otpsk768des3sha), NULL }, + }; +#endif + + + /* array of proposals to be conjoined (can only be one for Oakley) */ static struct db_prop oakley_pc_psk[] = @@ -163,6 +296,17 @@ static struct db_prop oakley_pc_pskrsasig[] = { { PROTO_ISAKMP, AD(oakley_trans_pskrsasig) } }; +#ifdef OPENSSL +static struct db_prop oakley_pc_psk2[] = + { { PROTO_ISAKMP, AD(oakley_trans_psk2) } }; + +static struct db_prop oakley_pc_openssl[] = + { { PROTO_ISAKMP, AD(oakley_trans_openssl) } }; + +static struct db_prop oakley_pc_psk2openssl[] = + { { PROTO_ISAKMP, AD(oakley_trans_psk2openssl) } }; +#endif + /* array of proposal conjuncts (can only be one) */ static struct db_prop_conj oakley_props_psk[] = { { AD(oakley_pc_psk) } }; @@ -171,6 +315,14 @@ static struct db_prop_conj oakley_props_pskrsasig[] = { { AD(oakley_pc_pskrsasig) } }; +#ifdef OPENSSL +static struct db_prop_conj oakley_props_psk2[] = { { AD(oakley_pc_psk2) } }; + +static struct db_prop_conj oakley_props_openssl[] = { { AD(oakley_pc_openssl) } }; + +static struct db_prop_conj oakley_props_psk2openssl[] = { { AD(oakley_pc_psk2openssl) } }; +#endif + /* the sadb entry, subscripted by POLICY_PSK and POLICY_RSASIG bits */ struct db_sa oakley_sadb[] = { { NULL, 0 }, /* none */ @@ -179,6 +331,16 @@ { AD(oakley_props_pskrsasig) }, /* POLICY_PSK + POLICY_RSASIG */ }; +#ifdef OPENSSL +/* the sadb entry, subscripted by POLICY_PSK and POLICY_OPENSSL bits */ +struct db_sa oakley_sadb2[] = { + { NULL, 0 }, /* none */ + { AD(oakley_props_psk2) }, /* POLICY_PSK */ + { AD(oakley_props_openssl) }, /* POLICY_OPENSSL */ + { AD(oakley_props_psk2openssl) }, /* POLICY_PSK + POLICY_OPENSSL */ + }; +#endif + /**************** IPsec (quick mode) SA database ****************/ /* arrays of attributes for transforms */ @@ -234,27 +396,51 @@ /* arrays of transforms, each in in preference order */ static struct db_trans espa_trans_tunnel[] = { +#ifndef OPENSSL { ESP_3DES, AD(espmd5_attr_tunnel) }, { ESP_3DES, AD(espsha1_attr_tunnel) }, +#else + { ESP_3DES, AD(espmd5_attr_tunnel), NULL }, + { ESP_3DES, AD(espsha1_attr_tunnel), NULL }, +#endif }; static struct db_trans espa_trans_transport[] = { +#ifndef OPENSSL { ESP_3DES, AD(espmd5_attr_transport) }, { ESP_3DES, AD(espsha1_attr_transport) }, +#else + { ESP_3DES, AD(espmd5_attr_transport), NULL }, + { ESP_3DES, AD(espsha1_attr_transport), NULL }, +#endif }; static struct db_trans esp_trans_tunnel[] = { +#ifndef OPENSSL { ESP_3DES, AD(esp_attr_tunnel) }, +#else + { ESP_3DES, AD(esp_attr_tunnel), NULL }, +#endif }; static struct db_trans esp_trans_transport[] = { +#ifndef OPENSSL { ESP_3DES, AD(esp_attr_transport) }, +#else + { ESP_3DES, AD(esp_attr_transport), NULL }, +#endif }; #ifdef SUPPORT_ESP_NULL static struct db_trans espnull_trans_tunnel[] = { +#ifndef OPENSSL { ESP_NULL, AD(espmd5_attr_tunnel) }, { ESP_NULL, AD(espsha1_attr_tunnel) }, +#else + { ESP_NULL, AD(espmd5_attr_tunnel), NULL }, + { ESP_NULL, AD(espsha1_attr_tunnel), NULL }, +#endif + }; static struct db_trans espnull_trans_transport[] = { @@ -264,13 +450,23 @@ #endif /* SUPPORT_ESP_NULL */ static struct db_trans ah_trans_tunnel[] = { +#ifndef OPENSSL { AH_MD5, AD(ah_HMAC_MD5_attr_tunnel) }, { AH_SHA, AD(ah_HMAC_SHA1_attr_tunnel) }, +#else + { AH_MD5, AD(ah_HMAC_MD5_attr_tunnel), NULL }, + { AH_MD5, AD(ah_HMAC_SHA1_attr_tunnel), NULL }, +#endif }; static struct db_trans ah_trans_transport[] = { +#ifndef OPENSSL { AH_MD5, AD(ah_HMAC_MD5_attr_transport) }, { AH_SHA, AD(ah_HMAC_SHA1_attr_transport) }, +#else + { AH_MD5, AD(ah_HMAC_MD5_attr_transport), NULL }, + { AH_MD5, AD(ah_HMAC_SHA1_attr_transport), NULL }, +#endif }; /* arrays of proposals to be conjoined */ @@ -464,7 +660,18 @@ proposal.isap_proposal = pcn; proposal.isap_protoid = p->protoid; proposal.isap_spisize = oakley_mode? 0 : IPSEC_DOI_SPI_SIZE; +#ifndef OPENSSL proposal.isap_notrans = p->trans_cnt; +#else + proposal.isap_notrans = 0; + for(tn = 0; tn < p->trans_cnt; tn++) { + struct db_trans *t = &(p->trans[tn]); + if (t->offer == NULL) + proposal.isap_notrans++; + else if ((t->offer(st))) /* only offer a propsal if we can carry it out */ + proposal.isap_notrans++; + } +#endif if (!out_struct(&proposal, &isakmp_proposal_desc, &sa_pbs, &proposal_pbs)) return FALSE; @@ -537,6 +744,14 @@ trans.isat_np = (tn == p->trans_cnt - 1) ? ISAKMP_NEXT_NONE : ISAKMP_NEXT_T; +#ifdef OPENSSL + if ((t->offer != NULL) && ((t->offer)(st) == FALSE)) { + DBG(DBG_PARSING, + DBG_log("Skipping unavailable cipher: %d", tn); + ); + continue; /* skip unavailable cipher */ + } +#endif trans.isat_transnum = tn; trans.isat_transid = t->transid; if (!out_struct(&trans, trans_desc, &proposal_pbs, &trans_pbs)) @@ -629,6 +844,180 @@ return val; } +#ifdef OPENSSL +struct _trans_node { + struct oakley_trans_attrs ta; + struct isakmp_transform trans; + u_char *attr_start; + size_t attr_len; + bool elim; /* has this node been eliminated from the selection process */ + struct _trans_node *next; +}; + +typedef struct _trans_node *trans_list; + +/* Free a list of Oakley transforms */ +static void +trans_list_free( trans_list *l ) +{ + trans_list p; + + if (!l) return; + while (*l) { + p = *l; + *l = p->next; + pfree(p->attr_start); + pfree(p); + } +} + +static void +trans_list_append(trans_list *l, + struct oakley_trans_attrs *ta, + struct isakmp_transform *trans, + u_char *attr_start, + size_t attr_len) { + trans_list p, pp; + + if (!l) return; + if ((p = alloc_bytes(sizeof(struct _trans_node), + "transformation node")) == NULL) return; + p->ta = *ta; + p->trans = *trans; + p->attr_len = attr_len; + if ((p->attr_start = alloc_bytes(attr_len, "attribute")) == NULL) { + pfree(p); + return; + } + memcpy(p->attr_start, attr_start, attr_len); + p->elim = FALSE; + p->next = NULL; + if (*l == NULL) { + *l = p; + } else { + for(pp = *l; pp->next ; pp = pp->next); + pp->next = p; + } +} + +static bool +select_trans_candidate(trans_list l, + struct oakley_trans_attrs *ta, + struct isakmp_transform *trans, + u_char **attr_start, + size_t *attr_len, + u_int32_t opts) +{ + bool ret = FALSE; + trans_list auth[OAKLEY_ELGAMAL_ENC_REV+1], p; + uint i, numprefs, pref[10]; + + if (!l) { + DBG(DBG_PARSING, + DBG_log("No candidate transforms to choose from"); + ); + return FALSE; + } + + if ((!ta) || (!attr_len) || (!trans) || (!attr_start)) { + DBG(DBG_PARSING, + DBG_log("No space to store selected transform"); + ); + return FALSE; + } + + /* Separate all elements of the list by authentication method */ + + for(i=OAKLEY_PRESHARED_KEY; i<=OAKLEY_ELGAMAL_ENC_REV; i++) + auth[i] = NULL; + for(p=l; p; p=p->next) + if ((p->ta.auth >= OAKLEY_PRESHARED_KEY) && + (p->ta.auth <= OAKLEY_ELGAMAL_ENC_REV )) { + trans_list_append(&(auth[p->ta.auth]), + &(p->ta), + &(p->trans), + p->attr_start, + p->attr_len); + } + + numprefs = 7; + pref[numprefs-1] = OAKLEY_PRESHARED_KEY; + DBG(DBG_PARSING | DBG_CRYPT, + DBG_log("select_trans_candidate: %08x", opts); + ); + if (opts & CERT_OPTION_PK) { + if (opts & CERT_OPTION_REV) { + DBG(DBG_PARSING | DBG_CRYPT, + DBG_log("Preferring revised PK encryption schemes"); + ); + pref[0] = OAKLEY_ELGAMAL_ENC_REV; + pref[1] = OAKLEY_RSA_ENC_REV; + pref[2] = OAKLEY_ELGAMAL_ENC; + pref[3] = OAKLEY_RSA_ENC; + pref[4] = OAKLEY_RSA_SIG; + pref[5] = OAKLEY_DSS_SIG; + } else { + DBG(DBG_PARSING | DBG_CRYPT, + DBG_log("Preferring standard PK encryption schemes"); + ); + pref[0] = OAKLEY_ELGAMAL_ENC; + pref[1] = OAKLEY_RSA_ENC; + pref[2] = OAKLEY_ELGAMAL_ENC_REV; + pref[3] = OAKLEY_RSA_ENC_REV; + pref[4] = OAKLEY_RSA_SIG; + pref[5] = OAKLEY_DSS_SIG; + } + } else { + if (opts & CERT_OPTION_REV) { + DBG(DBG_PARSING | DBG_CRYPT, + DBG_log("Preferring digisig schemes, then revised pk"); + ); + pref[0] = OAKLEY_RSA_SIG; + pref[1] = OAKLEY_DSS_SIG; + pref[2] = OAKLEY_ELGAMAL_ENC_REV; + pref[3] = OAKLEY_RSA_ENC_REV; + pref[4] = OAKLEY_ELGAMAL_ENC; + pref[5] = OAKLEY_RSA_ENC; + } else { + DBG(DBG_PARSING | DBG_CRYPT, + DBG_log("Preferring digisig schemes, then standard pk"); + ); + pref[0] = OAKLEY_RSA_SIG; + pref[1] = OAKLEY_DSS_SIG; + pref[2] = OAKLEY_ELGAMAL_ENC; + pref[3] = OAKLEY_RSA_ENC; + pref[4] = OAKLEY_ELGAMAL_ENC_REV; + pref[5] = OAKLEY_RSA_ENC_REV; + } + } + + for(i=0; ((ita; + *trans = auth[pref[i]]->trans; + *attr_start = alloc_bytes(auth[pref[i]]->attr_len, "attribute"); + if (*attr_start) { + *attr_len = auth[pref[i]]->attr_len; + memcpy(*attr_start, auth[pref[i]]->attr_start, *attr_len); + } + DBG(DBG_PARSING, + DBG_log("Candidate selected: %s", + enum_show(&oakley_auth_names, pref[i])); + ); + } +} + + for(i=OAKLEY_PRESHARED_KEY; i<=OAKLEY_RSA_ENC_REV; i++) { + trans_list_free(&auth[i]); + } + return ret; +} +#endif + /* 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. @@ -658,6 +1047,10 @@ struct isakmp_proposal proposal; unsigned no_trans_left; int last_transnum; +#ifdef OPENSSL + bool selected; + trans_list tl = NULL; +#endif /* DOI */ if (sa->isasa_doi != ISAKMP_DOI_IPSEC) @@ -893,10 +1286,48 @@ , "policy does not allow OAKLEY_PRESHARED_KEY authentication"); } break; +#ifdef OPENSSL + case OAKLEY_DSS_SIG: + if ((st->st_connection->key == NULL) || + (((EVP_PKEY *)(st->st_connection->key))->type != EVP_PKEY_DSA)) + snprintf(ugh, sizeof(ugh), "OAKLEY_DSS_SIG requested, but no DSA key available to sign with"); + ta.auth = val; + break; +// case OAKLEY_RSA_SIG: + case OAKLEY_RSA_ENC: + if (!have_rsa_keypair(st)) + snprintf(ugh, sizeof(ugh), "OAKLEY_RSA_ENC requested, but no cert available to encrypt with"); + ta.auth = val; + break; + case OAKLEY_ELGAMAL_ENC: + if (!have_elgamal_keypair(st)) + snprintf(ugh, sizeof(ugh), "OAKLEY_ELGAMAL_ENC requested, but no cert available to encrypt with"); + ta.auth = val; + break; + case OAKLEY_RSA_ENC_REV: + if (!have_rsa_keypair(st)) + snprintf(ugh, sizeof(ugh), "OAKLEY_RSA_ENC_REV requested, but no cert available to encrypt with"); + ta.auth = val; + break; + case OAKLEY_ELGAMAL_ENC_REV: + if (!have_elgamal_keypair(st)) + snprintf(ugh, sizeof(ugh), "OAKLEY_ELGAMAL_ENC_REV requested, but no cert available to encrypt with"); + ta.auth = val; + break; +#endif /* ! OPENSSL */ + #ifndef NO_RSA /* if this is defined, don't accept OAKLEY_RSA_SIG */ +#ifdef OPENSSL +if (!use_openssl(st->st_connection)) +{ +#endif /* OPENSSL, !use_openssl */ case OAKLEY_RSA_SIG: - /* Accept if policy specifies RSASIG or is default */ + /* Accept if policy specifies RSASIG/OPENSSL or is default */ +#ifndef OPENSSL if ((st->st_policy & POLICY_RSASIG) +#else + if ((st->st_policy & POLICY_OPENSSL) +#endif /* OPENSSL */ || (st->st_policy & POLICY_ISAKMP_MASK) == LEMPTY) { /* We'd like to check that we can find a public @@ -915,7 +1346,19 @@ , "policy does not allow OAKLEY_RSA_SIG authentication"); } break; -#endif +#ifdef OPENSSL +} +else /* use_openssl */ +{ + if ((st->st_connection->key == NULL) || + (((EVP_PKEY *)(st->st_connection->key))->type != EVP_PKEY_RSA)) + snprintf(ugh, sizeof(ugh), "OAKLEY_RSA_SIG requested, but no key available to sign with"); + ta.auth = val; + break; +} +#endif /* OPENSSL, use_openssl */ +#endif /* NO_RSA */ + default: snprintf(ugh, sizeof(ugh) , "Pluto does not support %s authentication" @@ -1030,6 +1473,15 @@ * Lets finish early and leave. */ +#ifdef OPENSSL + /* + * Alternatively, we like this transform, so push it into + * a list of potential winners, and rely on the site policy + * to pick which of the candidates should be chosen + */ +#endif + +#ifndef OPENSSL DBG(DBG_PARSING | DBG_CRYPT, DBG_log("Oakley Transform %u accepted", trans.isat_transnum)); @@ -1083,6 +1535,14 @@ /* copy over the results */ st->st_oakley = ta; return NOTHING_WRONG; +#else + /* + * We store the ta result into the list of candidates + * Afterwards, site policy dictates which of the + * candidates we should select + */ + trans_list_append(&tl, &ta, &trans, attr_start, attr_len); +#endif } /* on to next transform */ @@ -1104,8 +1564,73 @@ return BAD_PROPOSAL_SYNTAX; } } +#ifndef OPENSSL loglog(RC_LOG_SERIOUS, "no acceptable Oakley Transform"); return NO_PROPOSAL_CHOSEN; +#else + /* We should have a list of hopefuls in the list tl */ + { + u_char *attr_start; + size_t attr_len; + struct isakmp_transform trans; + + selected = select_trans_candidate(tl, &st->st_oakley, &trans, &attr_start, &attr_len, st->st_connection->cert_options); + trans_list_free(&tl); + if (! selected) { + log("no acceptable Oakley Transform"); + return NO_PROPOSAL_CHOSEN; + } else { + 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)) + passert(FALSE); + + /* 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)) + passert(FALSE); + + /* SPI */ +#ifdef EMIT_ISAKMP_SPI + if (!out_raw(my_cookie, COOKIE_SIZE, &r_proposal_pbs, "SPI")) + passert(FALSE); + 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)) + passert(FALSE); + + if (!out_raw(attr_start, attr_len, &r_trans_pbs, "attributes")) + passert(FALSE); + if (attr_start) pfree(attr_start); + + close_output_pbs(&r_trans_pbs); + close_output_pbs(&r_proposal_pbs); + close_output_pbs(r_sa_pbs); + } + return NOTHING_WRONG; + } + } +#endif + } /* Parse the body of an IPsec SA Payload (i.e. Phase 2 / Quick Mode). diff -ruN freeswan-1.5.orig/pluto/spdb.h freeswan-1.5/pluto/spdb.h --- freeswan-1.5.orig/pluto/spdb.h Sun Dec 12 19:40:57 1999 +++ freeswan-1.5/pluto/spdb.h Thu Oct 5 07:05:59 2000 @@ -24,11 +24,32 @@ u_int16_t val; }; +#ifdef OPENSSL +typedef bool offer_fn(struct state *st); +/* Addition for OpenSSL: we don't want to offer cipher suites */ +/* which we aren't capable of generating, so I added a function */ +/* parameter 'offer', which takes the state as a parameter, and */ +/* returns either TRUE or FALSE, depending on whether the cipher */ +/* suite is supportable. For instance, there's no point in */ +/* offering to sign things with a DSA signature key, if all you've */ +/* got is an RSA one. */ +/* NB: If the function pointer is set to NULL, it means offer the */ +/* cipher suite under all conditions. The downside to this is that */ +/* another parameter must be added to all the IPsec cipher transforms */ +/* which is always NULL. I suppose we could embed policy information */ +/* within that parameter, e.g. If a pair of endpoints must have */ +/* a particular cipher between them. Trouble is, that information is */ +/* present within ipsec.conf, to some extent -- nd */ +#endif + /* transform */ struct db_trans { u_int8_t transid; /* Transform-Id */ struct db_attr *attrs; /* array */ int attr_cnt; /* number of elements */ +#ifdef OPENSSL + offer_fn *offer; +#endif }; /* proposal */ @@ -56,9 +77,12 @@ }; /* The oakley sadb is subscripted by a bitset with members - * from POLICY_PSK and POLICY_RSASIG. + * from POLICY_PSK and POLICY_RSASIG/OPENSSL. */ extern struct db_sa oakley_sadb[1 << 2]; +#ifdef OPENSSL +extern struct db_sa oakley_sadb2[1 << 2]; +#endif /* The ipsec sadb is subscripted by a bitset with members * from POLICY_ENCRYPT, POLICY_AUTHENTICATE, POLICY_TUNNEL diff -ruN freeswan-1.5.orig/pluto/state.c freeswan-1.5/pluto/state.c --- freeswan-1.5.orig/pluto/state.c Wed Jun 21 14:24:35 2000 +++ freeswan-1.5/pluto/state.c Thu Oct 5 07:05:59 2000 @@ -324,6 +324,16 @@ pfreeany(st->st_esp.our_keymat); pfreeany(st->st_esp.peer_keymat); +#ifdef OPENSSL + /* Clean out the stuff for revised PK encryption mode */ + pfreeany(st->st_ne_i.ptr); + memset(&st->st_ks_i, 0, sizeof(st->st_ks_i)); + memset(st->st_ne_i_iv, 0, MAX_DIGEST_LEN); + pfreeany(st->st_ne_r.ptr); + memset(&st->st_ks_r, 0, sizeof(st->st_ks_r)); + memset(st->st_ne_r_iv, 0, MAX_DIGEST_LEN); +#endif + pfree(st); } diff -ruN freeswan-1.5.orig/pluto/state.h freeswan-1.5/pluto/state.h --- freeswan-1.5.orig/pluto/state.h Wed Jun 21 14:24:36 2000 +++ freeswan-1.5/pluto/state.h Thu Oct 5 07:05:59 2000 @@ -64,6 +64,7 @@ #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 and one @@ -144,6 +145,16 @@ chunk_t st_gr; /* Responder public value */ u_int8_t st_rcookie[COOKIE_SIZE];/* Responder Cookie */ chunk_t st_nr; /* Nr nonce */ + +#ifdef OPENSSL + /* key material for revised public key information */ + u_int8_t st_ne_i_iv[MAX_DIGEST_LEN]; + chunk_t st_ne_i; /* key for initiator payloads */ + keysched st_ks_i; + u_int8_t st_ne_r_iv[MAX_DIGEST_LEN]; + chunk_t st_ne_r; /* key for responder payloads */ + keysched st_ks_r; +#endif /* my stuff */ diff -ruN freeswan-1.5.orig/pluto/whack.c freeswan-1.5/pluto/whack.c --- freeswan-1.5.orig/pluto/whack.c Sun Jun 18 17:20:35 2000 +++ freeswan-1.5/pluto/whack.c Thu Oct 5 07:05:59 2000 @@ -1,4 +1,6 @@ /* command interface to Pluto + * + * Modified by Luc Lanthier based on work from: * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998, 1999 D. Hugh Redelmeier. * @@ -77,6 +79,10 @@ " [--authenticate]" " [--tunnel]" " [--pfs]" +#ifdef OPENSSL + " [--openssl]" +#endif + " \\\n " " [--ikelifetime ]" " [--ipseclifetime ]" @@ -84,6 +90,14 @@ " [--reykeymargin ]" " [--reykeyfuzz ]" " [--keyingtries ]" +#ifdef OPENSSL + " \\\n " + " [--certfile ]" + " [--certopts <[!]opt1,[!]opt2,...,[!]optN>]" + " [--peerfile ]" + " [--keyfile ]" + " [--certpath ]" +#endif "\n\n" "routing: whack" " (--route | --unroute)" @@ -158,47 +172,44 @@ { if (ugh != NULL) { - char buf[120]; - - snprintf(buf, sizeof(buf), "%s \"%s\"", ugh, this); - diag(buf); + 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: we are close to the limit of 32 flags in an unsigned long! + * Note: these become flags in an lset_t. Since there are more than + * 32, we partition them into: + * - OPT_* options (most random options) + * - DBGOPT_* option (DEBUG options) + * - CD_* options (Connection Description options) */ enum { OPT_CTLBASE, OPT_NAME, -# define OPT_CD_FIRST OPT_TO /* first connection description */ - OPT_TO, - OPT_HOST, /* first per-end */ - OPT_ID, - OPT_IKEPORT, - OPT_NEXTHOP, - OPT_CLIENT, - OPT_UPDOWN, /* last per-end */ - -# define OPT_POLICY_FIRST OPT_PSK - OPT_PSK, /* same order as POLICY_* */ - OPT_RSASIG, /* same order as POLICY_* */ - OPT_ENCRYPT, /* same order as POLICY_* */ - OPT_AUTHENTICATE, /* same order as POLICY_* */ - OPT_TUNNEL, /* same order as POLICY_* */ - OPT_PFS, /* same order as POLICY_* */ - - OPT_IKELIFETIME, - OPT_IPSECLIFETIME, - OPT_RKMARGIN, - OPT_RKFUZZ, - OPT_KTRIES, -# define OPT_CD_LAST OPT_KTRIES /* last connection description */ + OPT_CD, OPT_KEYID, OPT_PUBKEYRSA, +#ifdef OPENSSL + OPT_CERTFILE, + OPT_CERTOPTS, + OPT_PEERFILE, + OPT_KEYFILE, + OPT_CERTPATH, +#endif + OPT_ROUTE, OPT_UNROUTE, @@ -210,21 +221,56 @@ OPT_STATUS, OPT_SHUTDOWN, - OPT_ASYNC + OPT_ASYNC, +# define OPT_LAST OPT_ASYNC /* last "normal" option */ + +/* Connection Description options -- segregated */ + +# define CD_FIRST CD_TO /* first connection description */ + CD_TO, + CD_HOST, /* first per-end */ + CD_ID, + CD_IKEPORT, + CD_NEXTHOP, + CD_CLIENT, + CD_UPDOWN, /* last per-end */ + +# define CD_POLICY_FIRST CD_PSK + CD_PSK, /* same order as POLICY_* */ +#ifndef OPENSSL + CD_RSASIG, /* same order as POLICY_* */ +#else + CD_OPENSSL, /* same order as POLICY_* */ +#endif + CD_ENCRYPT, /* same order as POLICY_* */ + CD_AUTHENTICATE, /* same order as POLICY_* */ + CD_TUNNEL, /* same order as POLICY_* */ + CD_PFS, /* same order as POLICY_* */ + + CD_IKELIFETIME, + CD_IPSECLIFETIME, + CD_RKMARGIN, + CD_RKFUZZ, + CD_KTRIES +# define CD_LAST CD_KTRIES /* last connection description */ + #ifdef DEBUG /* must be last so others are less than 32 to fit in lset_t */ +# define DBGOPT_FIRST DBGOPT_NONE , - OPT_DEBUG_NONE, - OPT_DEBUG_ALL, + DBGOPT_NONE, + DBGOPT_ALL, - OPT_DEBUG_RAW, /* same order as DBG_* */ - OPT_DEBUG_CRYPT, /* same order as DBG_* */ - OPT_DEBUG_PARSING, /* same order as DBG_* */ - OPT_DEBUG_EMITTING, /* same order as DBG_* */ - OPT_DEBUG_CONTROL, /* same order as DBG_* */ - OPT_DEBUG_KLIPS, /* same order as DBG_* */ - OPT_DEBUG_PRIVATE /* same order as DBG_* */ + 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_KLIPS, /* same order as DBG_* */ + DBGOPT_PRIVATE /* same order as DBG_* */ +# define DBGOPT_LAST DBGOPT_PRIVATE #endif + }; #define OPTION_OFFSET 256 /* to get out of the way of letter options */ @@ -242,30 +288,6 @@ { "ctlbase", required_argument, NULL, OPT_CTLBASE + OO }, { "name", required_argument, NULL, OPT_NAME + OO }, - { "to", no_argument, NULL, OPT_TO + OO }, - - { "host", required_argument, NULL, OPT_HOST + OO }, - { "id", required_argument, NULL, OPT_ID + OO }, - { "ikeport", required_argument, NULL, OPT_IKEPORT + OO + NUMERIC_ARG }, - { "nexthop", required_argument, NULL, OPT_NEXTHOP + OO }, - { "client", required_argument, NULL, OPT_CLIENT + OO }, - { "updown", required_argument, NULL, OPT_UPDOWN + OO }, - - { "psk", no_argument, NULL, OPT_PSK + OO }, - { "rsasig", no_argument, NULL, OPT_RSASIG + OO }, - - { "encrypt", no_argument, NULL, OPT_ENCRYPT + OO }, - { "authenticate", no_argument, NULL, OPT_AUTHENTICATE + OO }, - { "tunnel", no_argument, NULL, OPT_TUNNEL + OO }, - { "pfs", no_argument, NULL, OPT_PFS + OO }, - - { "ikelifetime", required_argument, NULL, OPT_IKELIFETIME + OO + NUMERIC_ARG }, - { "ipseclifetime", required_argument, NULL, OPT_IPSECLIFETIME + OO + NUMERIC_ARG }, - { "rekeymargin", required_argument, NULL, OPT_RKMARGIN + OO + NUMERIC_ARG }, - { "rekeywindow", required_argument, NULL, OPT_RKMARGIN + OO + NUMERIC_ARG }, /* OBSOLETE */ - { "rekeyfuzz", required_argument, NULL, OPT_RKFUZZ + OO + NUMERIC_ARG }, - { "keyingtries", required_argument, NULL, OPT_KTRIES + OO + NUMERIC_ARG }, - { "keyid", required_argument, NULL, OPT_KEYID + OO }, { "pubkeyrsa", required_argument, NULL, OPT_PUBKEYRSA + OO }, @@ -282,18 +304,56 @@ { "asynchronous", no_argument, NULL, OPT_ASYNC + OO }, +/* options for a connection description */ + { "to", no_argument, NULL, CD_TO + OO }, + + { "host", required_argument, NULL, CD_HOST + OO }, + { "id", required_argument, NULL, CD_ID + OO }, + { "ikeport", required_argument, NULL, CD_IKEPORT + OO + NUMERIC_ARG }, + { "nexthop", required_argument, NULL, CD_NEXTHOP + OO }, + { "client", required_argument, NULL, CD_CLIENT + OO }, + { "updown", required_argument, NULL, CD_UPDOWN + OO }, + + { "psk", no_argument, NULL, CD_PSK + OO }, +#ifndef OPENSSL + { "rsasig", no_argument, NULL, CD_RSASIG + OO }, +#else + { "rsasig", no_argument, NULL, CD_OPENSSL + OO }, + { "openssl", no_argument, NULL, CD_OPENSSL + OO }, +#endif + + { "encrypt", no_argument, NULL, CD_ENCRYPT + OO }, + { "authenticate", no_argument, NULL, CD_AUTHENTICATE + OO }, + { "tunnel", no_argument, NULL, CD_TUNNEL + OO }, + { "pfs", no_argument, NULL, CD_PFS + 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 }, + +#ifdef OPENSSL + { "certfile", required_argument, NULL, OPT_CERTFILE + OO }, + { "certopts", required_argument, NULL, OPT_CERTOPTS + OO }, + { "peerfile", required_argument, NULL, OPT_PEERFILE + OO }, + { "keyfile", required_argument, NULL, OPT_KEYFILE + OO }, + { "certpath", required_argument, NULL, OPT_CERTPATH + OO }, +#endif + #ifdef DEBUG - { "debug-none", no_argument, NULL, OPT_DEBUG_NONE + OO }, - { "debug-all]", no_argument, NULL, OPT_DEBUG_ALL + OO }, - { "debug-raw", no_argument, NULL, OPT_DEBUG_RAW + OO }, - { "debug-crypt", no_argument, NULL, OPT_DEBUG_CRYPT + OO }, - { "debug-parsing", no_argument, NULL, OPT_DEBUG_PARSING + OO }, - { "debug-emitting", no_argument, NULL, OPT_DEBUG_EMITTING + OO }, - { "debug-control", no_argument, NULL, OPT_DEBUG_CONTROL + OO }, - { "debug-klips", no_argument, NULL, OPT_DEBUG_KLIPS + OO }, - { "debug-private", no_argument, NULL, OPT_DEBUG_PRIVATE + OO }, + { "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-klips", no_argument, NULL, DBGOPT_KLIPS + OO }, + { "debug-private", no_argument, NULL, DBGOPT_PRIVATE + OO }, #endif -# undef OO +#undef OO { 0,0,0,0 } }; @@ -332,7 +392,7 @@ if (life > limit) { - char buf[200]; + char buf[200]; /* arbitrary limit */ snprintf(buf, sizeof(buf) , "%s [%lu seconds] must be less than %lu seconds" @@ -341,7 +401,7 @@ } if (life <= mint) { - char buf[200]; + char buf[200]; /* arbitrary limit */ snprintf(buf, sizeof(buf) , "%s [%lu] must be greater than" @@ -354,6 +414,7 @@ } } + /* This is a hack for initiating ISAKMP exchanges. */ int @@ -362,7 +423,8 @@ struct whack_message msg; lset_t opts_seen = LEMPTY, - opts_seen_before_to; + cd_seen = LEMPTY, + cd_seen_before_to; memset(&msg, '\0', sizeof(msg)); msg.magic = WHACK_MAGIC; msg.right.host_port = IKE_UDP_PORT; @@ -404,24 +466,41 @@ diagq("badly formed numeric argument", optarg); } - /* all of our non-letter flags except OPT_DEBUG_* get - * added to opts_seen. The OP_DEBUG_* exception is because - * we have too many to fit in a 32-bit string! - * Unless other code intervenes, we reject repeated options. - */ + /* per-class option processing */ - if (0 <= c + if (0 <= c && c <= OPT_LAST) + { + /* OPT_* options get added 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; + } #ifdef DEBUG - && c < OPT_DEBUG_NONE + else if (DBGOPT_FIRST <= c && c <= DBGOPT_LAST) + { + /* DBGOPT_* options are treated separately to reduce + * potential members of opts_seen. + */ + msg.whack_options = TRUE; + } #endif - ) - { - lset_t f = LELEM(c); - - if (opts_seen & f) - diagq("duplicated flag", long_opts[long_index].name); - opts_seen |= f; - } + 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); + } /* Note: "break"ing from switch terminates loop. * most cases should end with "continue". @@ -469,77 +548,6 @@ msg.name = optarg; continue; - case OPT_HOST: /* --host */ - diagq(atoaddr(optarg, 0, &msg.right.host_addr), optarg); - continue; - - case OPT_ID: /* --id */ - msg.right.id = optarg; /* decoded by Pluto */ - continue; - - case OPT_IKEPORT: /* --ikeport */ - if (opt_whole<=0 || opt_whole >= 0x10000) - diagq(" must be a number between 1 and 65535", optarg); - msg.right.host_port = opt_whole; - continue; - - case OPT_NEXTHOP: /* --nexthop */ - diagq(atoaddr(optarg, 0, &msg.right.host_nexthop), optarg); - continue; - - case OPT_CLIENT: /* --client */ - diagq(atosubnet(optarg, 0, &msg.right.client_net, &msg.right.client_mask), optarg); - msg.right.has_client = TRUE; - msg.policy |= POLICY_TUNNEL; /* client => tunnel */ - continue; - - case OPT_UPDOWN: /* --updown */ - msg.right.updown = optarg; - continue; - - - case OPT_TO: /* --to */ - /* process right end, move it to left, reset it */ - if ((opts_seen & (LELEM(OPT_HOST) | LELEM(OPT_ID))) == 0) - diag("connection missing both --host and --id (before --to)"); - msg.left = msg.right; - memset(&msg.right, '\0', sizeof(msg.right)); - msg.right.id = NULL; - msg.right.updown = NULL; - msg.right.host_port = IKE_UDP_PORT; - opts_seen_before_to = opts_seen; - opts_seen = (opts_seen & ~LRANGE(OPT_HOST,OPT_UPDOWN)); - continue; - - case OPT_PSK: /* --psk */ - case OPT_RSASIG: /* --rsasig */ - case OPT_ENCRYPT: /* --encrypt */ - case OPT_AUTHENTICATE: /* --authenticate */ - case OPT_TUNNEL: /* --tunnel */ - case OPT_PFS: /* -- pfs */ - msg.policy |= LELEM(c - OPT_POLICY_FIRST); - continue; - - case OPT_IKELIFETIME: /* --ikelifetime */ - msg.sa_ike_life_seconds = opt_whole; - continue; - - case OPT_IPSECLIFETIME: /* --ipseclifetime */ - msg.sa_ipsec_life_seconds = opt_whole; - continue; - - case OPT_RKMARGIN: /* --rekeymargin */ - msg.sa_rekey_margin = opt_whole; - continue; - - case OPT_RKFUZZ: /* --rekeyfuzz */ - msg.sa_rekey_fuzz = opt_whole; - continue; - - case OPT_KTRIES: /* --keyingtries */ - msg.sa_keying_tries = opt_whole; - continue; - case OPT_KEYID: /* --keyid */ msg.whack_key = TRUE; msg.keyid = optarg; /* decoded by Pluto */ @@ -548,7 +556,7 @@ case OPT_PUBKEYRSA: { const char *ugh; - static char keyspace[1024]; + static char keyspace[1024 + 4]; /* room for 8K bit key */ msg.pubkey_alg = PUBKEY_ALG_RSA; ugh = atobytes(optarg, 0, keyspace, sizeof(keyspace), &msg.keyval.len); @@ -604,26 +612,135 @@ msg.whack_async = TRUE; continue; +#ifdef OPENSSL + case OPT_CERTFILE: + if (strlen(optarg) >= FILE_NAME_LIMIT) + diagq("certificate file name too long", optarg); + strcpy(msg.whack_certfile, optarg); + continue; + + case OPT_CERTOPTS: + if (strlen(optarg) >= CERT_OPTIONS_LIMIT) + diagq("certificate file name too long", optarg); + strcpy(msg.whack_certopts, optarg); + continue; + + case OPT_PEERFILE: + if (strlen(optarg) >= FILE_NAME_LIMIT) + diagq("peer certificate file name too long", optarg); + strcpy(msg.whack_peerfile, optarg); + continue; + + case OPT_KEYFILE: + if (strlen(optarg) >= FILE_NAME_LIMIT) + diagq("key file name too long", optarg); + strcpy(msg.whack_keyfile, optarg); + continue; + + case OPT_CERTPATH: + if (strlen(optarg) >= PATH_NAME_LIMIT) + diagq("certificate path name too long", optarg); + strcpy(msg.whack_certpath, optarg); + continue; +#endif + + /* Connection Description options */ + + case CD_HOST: /* --host */ + diagq(atoaddr(optarg, 0, &msg.right.host_addr), optarg); + continue; + + case CD_ID: /* --id */ + msg.right.id = optarg; /* decoded by Pluto */ + continue; + + case CD_IKEPORT: /* --ikeport */ + if (opt_whole<=0 || opt_whole >= 0x10000) + diagq(" must be a number between 1 and 65535", optarg); + msg.right.host_port = opt_whole; + continue; + + case CD_NEXTHOP: /* --nexthop */ + diagq(atoaddr(optarg, 0, &msg.right.host_nexthop), optarg); + continue; + + case CD_CLIENT: /* --client */ + diagq(atosubnet(optarg, 0, &msg.right.client_net, &msg.right.client_mask), optarg); + msg.right.has_client = TRUE; + msg.policy |= POLICY_TUNNEL; /* client => tunnel */ + continue; + + case CD_UPDOWN: /* --updown */ + msg.right.updown = optarg; + continue; + + + case CD_TO: /* --to */ + /* process right end, move it to left, reset it */ + if ((opts_seen & (LELEM(CD_HOST-CD_FIRST) | LELEM(CD_ID-CD_FIRST))) == 0) + diag("connection missing both --host and --id (before --to)"); + msg.left = msg.right; + memset(&msg.right, '\0', sizeof(msg.right)); + msg.right.id = NULL; + msg.right.updown = NULL; + msg.right.host_port = IKE_UDP_PORT; + cd_seen_before_to = cd_seen; + cd_seen &= ~LRANGE(CD_HOST-CD_FIRST, CD_UPDOWN-CD_FIRST); + continue; + + case CD_PSK: /* --psk */ +#ifndef OPENSSL + case CD_RSASIG: /* --rsasig */ +#else + case CD_OPENSSL: /* --rsasig */ +#endif + case CD_ENCRYPT: /* --encrypt */ + case CD_AUTHENTICATE: /* --authenticate */ + case CD_TUNNEL: /* --tunnel */ + case CD_PFS: /* -- pfs */ + msg.policy |= LELEM(c - CD_POLICY_FIRST); + continue; + + case CD_IKELIFETIME: /* --ikelifetime */ + msg.sa_ike_life_seconds = opt_whole; + continue; + + case CD_IPSECLIFETIME: /* --ipseclifetime */ + msg.sa_ipsec_life_seconds = opt_whole; + continue; + + case CD_RKMARGIN: /* --rekeymargin */ + msg.sa_rekey_margin = opt_whole; + continue; + + case CD_RKFUZZ: /* --rekeyfuzz */ + msg.sa_rekey_fuzz = opt_whole; + continue; + + case CD_KTRIES: /* --keyingtries */ + msg.sa_keying_tries = opt_whole; + continue; + #ifdef DEBUG - case OPT_DEBUG_NONE: /* --debug-none */ + case DBGOPT_NONE: /* --debug-none */ msg.whack_options = TRUE; msg.debugging = DBG_NONE; continue; - case OPT_DEBUG_ALL: /* --debug-all */ + case DBGOPT_ALL: /* --debug-all */ msg.whack_options = TRUE; msg.debugging |= DBG_ALL; /* note: does not include PRIVATE */ continue; - case OPT_DEBUG_RAW: /* --debug-raw */ - case OPT_DEBUG_CRYPT: /* --debug-crypt */ - case OPT_DEBUG_PARSING: /* --debug-parsing */ - case OPT_DEBUG_EMITTING: /* --debug-emitting */ - case OPT_DEBUG_CONTROL: /* --debug-control */ - case OPT_DEBUG_KLIPS: /* --debug-klips */ - case OPT_DEBUG_PRIVATE: /* --debug-private */ + 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_KLIPS: /* --debug-klips */ + case DBGOPT_PRIVATE: /* --debug-private */ msg.whack_options = TRUE; - msg.debugging |= LELEM(c-OPT_DEBUG_RAW); + msg.debugging |= LELEM(c-DBGOPT_RAW); continue; #endif default: @@ -640,25 +757,26 @@ * required information was supplied. */ - if (opts_seen & LRANGE(OPT_CD_FIRST, OPT_CD_LAST)) +// if (opts_seen & LRANGE(CD_FIRST, CD_LAST)) + if (opts_seen & LELEM(OPT_CD)) { - if (!LALLIN(opts_seen, LELEM(OPT_TO))) + if (!LALLIN(cd_seen, LELEM(CD_TO-CD_FIRST))) diag("connection description option, but no --to"); - if ((opts_seen & (LELEM(OPT_HOST) | LELEM(OPT_ID))) == 0) + if ((cd_seen & (LELEM(CD_HOST-CD_FIRST) | LELEM(CD_ID-CD_FIRST))) == 0) diag("connection missing both --host and --id (after --to)"); if (is_NO_IP(msg.left.host_addr) - && is_NO_IP(msg.right.host_addr)) - diag("hosts cannot both be 0.0.0.0"); + && is_NO_IP(msg.right.host_addr)) + diag("hosts cannot both be 0.0.0.0"); - if ((opts_seen_before_to & LELEM(OPT_NEXTHOP)) == 0) + if ((cd_seen_before_to & LELEM(CD_NEXTHOP-CD_FIRST)) == 0) { if (is_NO_IP(msg.right.host_addr)) diag("left nexthop must be specified when right is Road Warrior"); msg.left.host_nexthop = msg.right.host_addr; } - if ((opts_seen & LELEM(OPT_NEXTHOP)) == 0) + if ((cd_seen & LELEM(CD_NEXTHOP-CD_FIRST)) == 0) { if (is_NO_IP(msg.left.host_addr)) diag("right nexthop must be specified when left is Road Warrior"); @@ -671,7 +789,7 @@ /* decide whether --name is mandatory or forbidden */ if (opts_seen & (LELEM(OPT_ROUTE) | LELEM(OPT_UNROUTE) | LELEM(OPT_INITIATE) | LELEM(OPT_TERMINATE) - | LELEM(OPT_DELETE) | LELEM(OPT_TO))) + | LELEM(OPT_DELETE) | LELEM(OPT_CD))) { if ((opts_seen & LELEM(OPT_NAME)) == 0) diag("missing --name "); @@ -761,7 +879,7 @@ /* for now, just copy reply back to stdout */ { - char buf[4097]; + char buf[4097]; /* arbitrary limit on log line length */ char *be = buf; for (;;) diff -ruN freeswan-1.5.orig/pluto/whack.h freeswan-1.5/pluto/whack.h --- freeswan-1.5.orig/pluto/whack.h Sat Apr 15 04:56:04 2000 +++ freeswan-1.5/pluto/whack.h Thu Oct 5 07:05:59 2000 @@ -14,6 +14,11 @@ * RCSID $Id: whack.h,v 1.31 2000/04/15 08:56:04 dhr Exp $ */ +#ifdef OPENSSL +#include "openssl_defs.h" +#endif +#include + /* * Since the message remains on one host, native representation is used. * Think of this as horizontal microcode: all selected operations are @@ -24,6 +29,7 @@ * meaning, change this value (probably by changing the last number). */ #define WHACK_MAGIC (('w' << 24) + ('h' << 16) + ('k' << 8) + 10) +// #define WHACK_MAGIC (('w' << 24) + ('h' << 16) + ('k' << 8) + 12) /* struct whack_end is a lot like connection.h's struct end * It differs because it is going to be shipped down a socket @@ -77,36 +83,42 @@ enum pubkey_alg pubkey_alg; chunk_t keyval; /* chunk */ - /* for WHACK_ROUTE: */ +#ifdef OPENSSL +// hm. Are the sizes necessary? +// char *whack_certfile; +// char *whack_certopts; +// char *whack_peerfile; +// char *whack_keyfile; +// char *whack_certpath; + char whack_certfile[FILE_NAME_LIMIT]; + char whack_certopts[CERT_OPTIONS_LIMIT]; + char whack_peerfile[FILE_NAME_LIMIT]; + char whack_keyfile[FILE_NAME_LIMIT]; + char whack_certpath[PATH_NAME_LIMIT]; +#endif + /* for WHACK_ROUTE: */ bool whack_route; /* for WHACK_UNROUTE: */ - bool whack_unroute; /* for WHACK_INITIATE: */ - bool whack_initiate; /* for WHACK_TERMINATE: */ - bool whack_terminate; /* for WHACK_DELETE: */ - bool whack_delete; /* for WHACK_LISTEN: */ - bool whack_listen, whack_unlisten; /* for WHACK_STATUS: */ - bool whack_status; /* for WHACK_SHUTDOWN */ - bool whack_shutdown; /* space for strings (hope there is enough room): @@ -117,7 +129,7 @@ * 4 right's name [left.host.name.len] * 5 right's updown * 6 keyid - * plus keyval, a chunk. + * plus keyval (limit: 8K bits + overhead), a chunk. */ size_t str_size; char string[2048]; diff -ruN freeswan-1.5.orig/pluto/x_sobj.c freeswan-1.5/pluto/x_sobj.c --- freeswan-1.5.orig/pluto/x_sobj.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/x_sobj.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,34 @@ +#include "x_sobj.h" + +IMPLEMENT_STACK_OF(X509_OBJECT) + +X509_OBJECT * +X509_OBJECT_new( const int type, void *p ) +{ + X509_OBJECT *ret = Malloc(sizeof(X509_OBJECT)); + + if (ret == NULL) return ret; + switch(type) { + case X509_LU_X509: + ret->type = type; + ret->data.x509 = (X509 *)p; + break; + case X509_LU_CRL: + ret->type = type; + ret->data.crl = (X509_CRL *)p; + break; + default: /* Unknown type */ + Free((char *)ret); + ret = NULL; + break; + } + return ret; +} + +void +X509_OBJECT_free(X509_OBJECT *x) +{ + if (!x) return; + X509_OBJECT_free_contents(x); + Free((char *)x); +} diff -ruN freeswan-1.5.orig/pluto/x_sobj.h freeswan-1.5/pluto/x_sobj.h --- freeswan-1.5.orig/pluto/x_sobj.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/x_sobj.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,12 @@ +#ifndef X_SOBJ_H +#define X_SOBJ_H + +#include +#include + +DECLARE_STACK_OF(X509_OBJECT) + +X509_OBJECT *X509_OBJECT_new( const int type, void *ptr ); +void X509_OBJECT_free(X509_OBJECT *x); + +#endif diff -ruN freeswan-1.5.orig/pluto/xmap.c freeswan-1.5/pluto/xmap.c --- freeswan-1.5.orig/pluto/xmap.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,249 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "x_sobj.h" +#include "xmap_file.h" +#include "xmap_dir.h" +#include "xmap_db.h" +#include "xmap_ldap.h" +#include "xmap.h" + +#include "constants.h" +#include "defs.h" +#include "log.h" + +static XMAP_METHOD_LIST *xmth = NULL; + +XMAP *XMAP_new(const char *spec); +STACK_OF(X509_OBJECT) * +XMAP_lookup( XMAP *xm, const int type, const char *index, void *p ); +void XMAP_free(XMAP *xm); +void XMAP_dump(XMAP *xm); + +int +XMAP_register(XMAP_METHOD *meth) +{ + XMAP_METHOD_LIST *p; + int found; + + for(p=xmth,found=0; ((!found) && (p)); p=p->next) + if (strcasecmp(meth->prefix, p->meth->prefix) == 0) found=1; + + if (found) { + return 0; + } else { + p = malloc(sizeof(XMAP_METHOD_LIST)); + if (p) { + p->next = xmth; + p->meth = meth; + xmth = p; + } else + return 0; + } + return 1; +} + +void +XMAP_METHOD_LIST_free( void ) +{ + XMAP_METHOD_LIST *p, *q; + + for(p=xmth; p;) { + q = p->next; + free(p); + q = p; + } +} + +int +XMAP_unregister(const char *prefix) +{ + XMAP_METHOD_LIST *p, *q; + int found; + + for(p = xmth, found = 0; ((!found) && (p)); ) + if (strcasecmp(p->meth->prefix, prefix) == 0) + found = 1; + else + p=p->next; + + if (!found) + return 0; + else { + if ( p == xmth ) { + xmth = xmth->next; + free(p); + } else { + for(q = xmth; q->next != p; q = q->next); + q->next = p->next; + free(p); + } + } + return 1; +} + +XMAP * +XMAP_new(const char *spec) +{ + XMAP *ret; + char *s; + + if ((ret = malloc(sizeof(XMAP))) == NULL) goto end; + if ((ret->spec = strdup(spec)) == NULL) goto end; + ret->methp = NULL; + + if ((s = strchr(spec, DICT_SEPARATOR)) == NULL) { + log("No type prefix"); + } else { + int l = (int)(s - spec) + 1; + XMAP_METHOD_LIST *p; + int found; + + for(p=xmth, found = 0; ((!found) && (p));) { + if (strncasecmp(p->meth->prefix, spec, l-1) == 0) + found = 1; + else + p = p->next; + } + + if (found) { + ret->pmap = p->meth->new(&s[1]); + if (! ret->pmap) goto end; + ret->methp = p; + } else { + log("Unknown map type spec: %s", spec); + goto end; + } + } + return ret; + end: + if (ret->spec) free(ret->spec); + if (ret) free(ret); + return NULL; +} + +static int +check_methp(XMAP *xm) +{ + XMAP_METHOD_LIST *p; + + for(p = xmth; p; p = p->next) + if (xm->methp == p) + return 1; + + return 0; +} + +STACK_OF(X509_OBJECT) * +XMAP_lookup( XMAP *xm, const int type, const char *index, void *p ) +{ + if ((!xm) || (!check_methp(xm)) || (! xm->methp->meth->lookup) ) + { + DBG(DBG_PARSING, + DBG_log("XMAP_lookup returns NULL."); + ); + return NULL; + } + return xm->methp->meth->lookup(xm->pmap, type, index, p); +} + +void +XMAP_free(XMAP *xm) +{ + if (!xm) return; + if (!xm->pmap) return; + if (!check_methp(xm)) return; + if (xm->methp->meth->free) xm->methp->meth->free(xm->pmap); + if (xm->spec) free(xm->spec); + free(xm); +} + +void +XMAP_dump(XMAP *xm) +{ + if (! xm->pmap) return; + if (!check_methp(xm)) return; + if (! xm->methp->meth->dump) return; + xm->methp->meth->dump(xm->pmap); +} + +int +X509_subjAltName_check_dns(X509 *x, const char *dns) +{ + int i, loc, ret; + X509_EXTENSION *ext; + u_char *p; + STACK_OF(GENERAL_NAME) *nsk = NULL; + GENERAL_NAME *gn; + + ret = 0; + if ((loc = X509_get_ext_by_NID(x, NID_subject_alt_name, -1)) >= 0) { + if ((ext = X509_get_ext(x, loc)) != NULL) { + p = ext->value->data; + d2i_GENERAL_NAMES(&nsk, &p, ext->value->length); + if (nsk) { + for(i=0; i < sk_GENERAL_NAME_num(nsk); i++) { + gn = sk_GENERAL_NAME_value(nsk, i); + + if (gn->type == GEN_DNS) { + if (strncasecmp(dns, gn->d.ia5->data, gn->d.ia5->length) == 0) { + ret = 1; + break; + } + } + } + sk_GENERAL_NAME_free(nsk); + nsk = NULL; + } + } + } + return ret; +} + +int +X509_subjAltName_check_ip(X509 *x, const struct in_addr ip) +{ + int i, loc, ret; + X509_EXTENSION *ext; + u_char *p; + STACK_OF(GENERAL_NAME) *nsk = NULL; + GENERAL_NAME *gn; + + ret = 0; + if ((loc = X509_get_ext_by_NID(x, NID_subject_alt_name, -1)) >= 0) { + if ((ext = X509_get_ext(x, loc)) != NULL) { + p = ext->value->data; + d2i_GENERAL_NAMES(&nsk, &p, ext->value->length); + if (nsk) { + for(i=0; i < sk_GENERAL_NAME_num(nsk); i++) { + gn = sk_GENERAL_NAME_value(nsk, i); + + if (gn->type == GEN_IPADD) { + if (memcmp(&ip.s_addr, gn->d.ip->data, gn->d.ip->length) == 0) { + ret = 1; + break; + } + } + } + sk_GENERAL_NAME_free(nsk); + nsk = NULL; + } + } + } + return ret; +} + +IMPLEMENT_STACK_OF(XMAP) diff -ruN freeswan-1.5.orig/pluto/xmap.h freeswan-1.5/pluto/xmap.h --- freeswan-1.5.orig/pluto/xmap.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,56 @@ +#ifndef XMAP_H +#define XMAP_H + +#include +#include +#include + +#include +#include +#include + +#include "x_sobj.h" + +typedef struct _xmap_method_st { + char *prefix; + STACK_OF(X509_OBJECT) *(*lookup)( void *this, const int type, const char *index, void *p ); + void (*dump)(void *pmap); + void (*free)(void *pmap); + void *(*new) (const char *spec); +} XMAP_METHOD; + +typedef struct _xmap_method_list_st { + XMAP_METHOD *meth; + struct _xmap_method_list_st *next; +} XMAP_METHOD_LIST; + +typedef struct _xmap_st { + char *spec; + void *pmap; + XMAP_METHOD_LIST *methp; +} XMAP; + +DECLARE_STACK_OF(XMAP) + +#ifndef DICT_SEPARATOR +#define DICT_SEPARATOR ':' +#endif + +#define XMAP_UNKNOWN_TYPE 0 +#define XMAP_FILE_TYPE 1 +#define XMAP_DIR_TYPE 2 +#define XMAP_DB_TYPE 3 + +int XMAP_register(XMAP_METHOD *meth); +void XMAP_METHOD_LIST_free( void ); +int XMAP_unregister(const char *prefix); + +int X509_subjAltName_check_dns(X509 *x, const char *dns); +int X509_subjAltName_check_ip(X509 *x, const struct in_addr in); + +XMAP *XMAP_new(const char *spec); +STACK_OF(X509_OBJECT) *XMAP_lookup( XMAP *xm, const int type, const char *index, void *p ); +void XMAP_free(XMAP *xm); +void XMAP_dump(XMAP *xm); + +#endif /* XMAP_H */ diff -ruN freeswan-1.5.orig/pluto/xmap_db.c freeswan-1.5/pluto/xmap_db.c --- freeswan-1.5.orig/pluto/xmap_db.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_db.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,513 @@ +#if defined(HAVE_DB) || (HAVE_DB185) +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAVE_DB185 +#include +#else +#include +#endif + +#include +#include +#include + +#include "xmap_db.h" + +#include "constants.h" +#include "defs.h" +#include "log.h" + +static void XMAP_DB_dump(void *this); +static void XMAP_DB_free(void *this); +static void *XMAP_DB_new(const char *dbname); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup( void *this, const int type, const char *index, void *p ); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_subject( XMAP_DB *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_crl_by_issuer( XMAP_DB *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_uid( XMAP_DB *xm, const char *uid ); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_dns( XMAP_DB *xm, const char *dns ); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_ip( XMAP_DB *xm, const struct in_addr ip); +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_ca_certs( XMAP_DB *xm ); + +#define TAGLEN 4 + +static unsigned char tag_cert[] = { 0x00, 0x00, 0x00, 0x00 }; +static unsigned char tag_subj[] = { 0x00, 0x00, 0x00, 0x01 }; +static unsigned char tag_csub[] = { 0x00, 0x00, 0x00, 0x02 }; +static unsigned char tag_uid[] = { 0x00, 0x00, 0x00, 0x03 }; +static unsigned char tag_crl[] = { 0x00, 0x00, 0x00, 0x04 }; +static unsigned char tag_ca[] = { 0x00, 0x00, 0x00, 0x05 }; +static unsigned char tag_dns[] = { 0x00, 0x00, 0x00, 0x06 }; +static unsigned char tag_ip[] = { 0x00, 0x00, 0x00, 0x07 }; + +#define DBZ(x) memset(&(x), 0, sizeof((x))) + +static XMAP_METHOD xmap_db_methods = { + "db", + XMAP_DB_lookup, + XMAP_DB_dump, + XMAP_DB_free, + XMAP_DB_new +}; + +void * +XMAP_DB_new(const char *dbname) +{ + XMAP_DB *ret; + + if ((ret = alloc_thing(XMAP_DB, "xmap_db")) == NULL) goto end; + if ((ret->dbname = clone_bytes(dbname, strlen(dbname)+1, "dbname")) == NULL) + goto end; + if ((ret->db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL)) == NULL) + goto end; + + ret->meth = &xmap_db_methods; + + return (void *)ret; + end: + if (ret->db) ret->db->close(ret->db); + if (ret->dbname) pfree(ret->dbname); + if (ret) pfree(ret); + return NULL; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup( void *this, const int type, const char *index, void *p ) +{ + XMAP_DB *xm = (XMAP_DB *)this; + + switch(type) { + case X509_LU_X509: + if (strcasecmp(index, "subject") == 0) + return XMAP_DB_lookup_cert_by_subject( xm, (X509_NAME *)p); + else if (strcasecmp(index, "uid") == 0) + return XMAP_DB_lookup_cert_by_uid( xm, (char *)p); + else if (strcasecmp(index, "dns") == 0) + return XMAP_DB_lookup_cert_by_dns( xm, (char *)p); + else if (strcasecmp(index, "ip") == 0) + return XMAP_DB_lookup_cert_by_ip( xm, *((struct in_addr *)p)); + else if (strcasecmp(index, "ca") == 0) + return XMAP_DB_lookup_ca_certs( xm ); + else + return NULL; + break; + case X509_LU_CRL: + if (strcasecmp(index, "issuer") == 0) + return XMAP_DB_lookup_crl_by_issuer( xm, (X509_NAME *)p); + else + return NULL; + break; + default: + return NULL; + } +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_subject(XMAP_DB *xm, X509_NAME *name) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + dlen = i2d_X509_NAME(name, NULL); + if ((d = alloc_bytes(dlen + TAGLEN, "ASN.1 name")) == NULL) goto end; + dum = &d[TAGLEN]; + memcpy(d, tag_subj, TAGLEN); + i2d_X509_NAME(name, &dum); + k.data = d; + k.size = dlen + TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_cert, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509 *x; + dum = (unsigned char *)v2.data; + if ((x = d2i_X509(NULL, &dum, v2.size)) != NULL) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + if (x) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } + } + pfree(d); d = NULL; + } + end: + if (d) pfree(d); + return sk; +} + +static int +isCA( X509 *x ) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + int loc; + + loc = X509_get_ext_by_NID(x, NID_basic_constraints, -1); + if (loc >= 0) { + ext = X509_get_ext(x, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + return 0; + } else { + BASIC_CONSTRAINTS_free(p); + return 1; /* This is a CA certificate */ + } + } else + return 0; + } else + return 0; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_crl_by_issuer(XMAP_DB *xm, X509_NAME *name) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + dlen = i2d_X509_NAME(name, NULL); + if ((d = alloc_bytes(dlen + TAGLEN, "ASN.1 name")) == NULL) goto end; + dum = &d[TAGLEN]; + memcpy(d, tag_csub, TAGLEN); + i2d_X509_NAME(name, &dum); + k.data = d; + k.size = dlen + TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_crl, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509_CRL *c; + dum = (unsigned char *)v2.data; + if ((c = d2i_X509_CRL(NULL, &dum, v2.size)) != NULL) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_CRL, c); + + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_CRL_free(c); + } + } + } + pfree(d); d = NULL; + } + end: + if (d) pfree(d); + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_ca_certs( XMAP_DB *xm ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + if ((d = alloc_bytes(TAGLEN, "CA tag")) == NULL) goto end; + memcpy(d, tag_ca, TAGLEN); + k.data = d; + k.size = TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_cert, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509 *x; + dum = (unsigned char *)v2.data; + if (((x = d2i_X509(NULL, &dum, v2.size)) != NULL) && (isCA(x))) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } + } + pfree(d); d = NULL; + } + end: + if (d) pfree(d); + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_uid(XMAP_DB *xm, const char *uid) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, ulen, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + ulen = strlen(uid); + if ((d = alloc_bytes(ulen + TAGLEN, "uid tag")) == NULL) goto end; + memcpy(d, tag_uid, TAGLEN); + strncpy(&d[TAGLEN], uid, ulen); + k.data = d; + k.size = ulen + TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_cert, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509 *x; + dum = (unsigned char *)v2.data; + if ((x = d2i_X509(NULL, &dum, v2.size)) != NULL) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } + } + pfree(d); d = NULL; + } + end: + if (d) pfree(d); + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_dns(XMAP_DB *xm, const char *dns) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, ulen, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + ulen = strlen(dns); + if ((d = alloc_bytes(ulen + TAGLEN, "dns tag")) == NULL) goto end; + memcpy(d, tag_dns, TAGLEN); + strncpy(&d[TAGLEN], dns, ulen); + k.data = d; + k.size = ulen + TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_cert, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509 *x; + dum = (unsigned char *)v2.data; + if ((x = d2i_X509(NULL, &dum, v2.size)) != NULL) { + if (X509_subjAltName_check_dns(x, dns)) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } else + X509_free(x); + } + } + } + pfree(d); d = NULL; + } + end: + if (d) free(d); + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DB_lookup_cert_by_ip(XMAP_DB *xm, const struct in_addr ip) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + DBT k, v; + unsigned char *d, *dum; + unsigned int i, ulen, dlen; + + DBZ(k); + DBZ(v); + if (!xm->db) goto end; + ulen = sizeof(ip.s_addr); + if ((d = alloc_bytes(ulen + TAGLEN, "ip tag")) == NULL) goto end; + memcpy(d, tag_ip, TAGLEN); + memcpy(&d[TAGLEN], &ip.s_addr, ulen); + k.data = d; + k.size = ulen + TAGLEN; + if (xm->db->get(xm->db, &k, &v, 0) == 0) { + u_int32_t c1, c2; + + memcpy((unsigned char *)&c1, v.data, sizeof(c1)); + c2 = ntohl(c1); + pfree(d); d = NULL; + dlen = SHA_DIGEST_LENGTH + TAGLEN; + if ((d = alloc_bytes(dlen, "SHA-1 tag")) == NULL) goto end; + memcpy(d, tag_cert, TAGLEN); + for(i=0; idb->get(xm->db, &k, &v2, 0) == 0) { + X509 *x; + dum = (unsigned char *)v2.data; + + DBG(DBG_PARSING | DBG_RAW, + DBG_log("** v2 length: %d", v2.size); + ); + DBG(DBG_RAW, + DBG_dump("** v2: ", v2.data, v2.size); + ); + + if ((x = d2i_X509(NULL, &dum, v2.size)) != NULL) { + if (X509_subjAltName_check_ip(x, ip)) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } else + X509_free(x); + } + } + pfree(d); d = NULL; + } + end: + if (d) pfree(d); + return sk; +} + +static void +XMAP_DB_dump(void *this) +{ + XMAP_DB *xm = (XMAP_DB *)this; + DBG(DBG_PARSING, + DBG_log("XMAP_DB: %s", xm->dbname); + ); +} + +static void +XMAP_DB_free(void *this) +{ + XMAP_DB *xm = (XMAP_DB *)this; + + if (!xm) return; + if (xm->dbname) pfree(xm->dbname); + if (xm->db) xm->db->close(xm->db); + pfree(xm); +} + +XMAP_METHOD * +XMAP_METHOD_db( void ) +{ + return (&xmap_db_methods); +} +#endif /* HAVE_DB */ diff -ruN freeswan-1.5.orig/pluto/xmap_db.h freeswan-1.5/pluto/xmap_db.h --- freeswan-1.5.orig/pluto/xmap_db.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_db.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,27 @@ +#ifndef XMAP_DB_H +#define XMAP_DB_H + +#if defined(HAVE_DB) || defined(HAVE_DB185) + +#include +#include + +#ifdef HAVE_DB185 +#include +#else +#include +#endif + +#include "x_sobj.h" +#include "xmap.h" + +typedef struct xmap_db_st { + char *dbname; + DB *db; + XMAP_METHOD *meth; +} XMAP_DB; + +XMAP_METHOD *XMAP_METHOD_db( void ); + +#endif /* HAVE_DB */ +#endif /* XMAP_DB_H */ diff -ruN freeswan-1.5.orig/pluto/xmap_dir.c freeswan-1.5/pluto/xmap_dir.c --- freeswan-1.5.orig/pluto/xmap_dir.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_dir.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,486 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "xmap_dir.h" +#include "xmap.h" + +#include "constants.h" +#include "defs.h" +#include "log.h" + +static void XMAP_DIR_dump(void *this); +static void XMAP_DIR_free(void *this); +static void *XMAP_DIR_new(const char *dir); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup( void *this, const int type, const char *index, void *p ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_subject( XMAP_DIR *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_crl_by_issuer( XMAP_DIR *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_uid( XMAP_DIR *xm, const char *uid ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_dns( XMAP_DIR *xm, const char *dns ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_ip( XMAP_DIR *xm, const struct in_addr ip ); +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_ca_certs( XMAP_DIR *xm ); + +static XMAP_METHOD xmap_dir_methods = { + "dir", + XMAP_DIR_lookup, + XMAP_DIR_dump, + XMAP_DIR_free, + XMAP_DIR_new +}; + +static void * +XMAP_DIR_new(const char *dir) +{ + XMAP_DIR *ret; + + if ((ret = alloc_thing(XMAP_DIR, "xmap_dir")) == NULL) goto end; + if ((ret->dir = clone_bytes(dir, strlen(dir)+1, "directory name")) == NULL) + goto end; + ret->meth = &xmap_dir_methods; + + return (void *)ret; + end: + if (ret->dir) pfree(ret->dir); + if (ret) pfree(ret); + return NULL; +} + +static int +isCA( X509 *x ) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + int loc; + + loc = X509_get_ext_by_NID(x, NID_basic_constraints, -1); + if (loc >= 0) { + ext = X509_get_ext(x, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + return 0; + } else { + BASIC_CONSTRAINTS_free(p); + return 1; /* This is a CA certificate */ + } + } else + return 0; + } else + return 0; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup( void *this, const int type, const char *index, void *p ) +{ + XMAP_DIR *xm = (XMAP_DIR *)this; + + switch(type) { + case X509_LU_X509: + if (strcasecmp(index, "subject") == 0) + return XMAP_DIR_lookup_cert_by_subject( xm, (X509_NAME *)p); + else if (strcasecmp(index, "uid") == 0) + return XMAP_DIR_lookup_cert_by_uid( xm, (char *)p); + else if (strcasecmp(index, "dns") == 0) + return XMAP_DIR_lookup_cert_by_dns( xm, (char *)p); + else if (strcasecmp(index, "ip") == 0) + return XMAP_DIR_lookup_cert_by_ip( xm, *(struct in_addr *)p); + else if (strcasecmp(index, "ca") == 0) + return XMAP_DIR_lookup_ca_certs( xm ); + else + return NULL; + break; + case X509_LU_CRL: + if (strcasecmp(index, "issuer") == 0) + return XMAP_DIR_lookup_crl_by_issuer( xm, (X509_NAME *)p); + else + return NULL; + break; + default: + return NULL; + } +} + +static char hashval[PATH_MAX+1] = ""; +static char dirname[PATH_MAX+1] = ""; + +static int +hash_select(const struct dirent *de) +{ + int l = strlen(hashval); + + if (strncmp(de->d_name, hashval, l) == 0) { + return 1; + } else { + return 0; + } +} + +static int +select_files(const struct dirent *de) +{ + struct stat st; + char path[PATH_MAX+1]; + + snprintf(path, sizeof(path), "%s/%s", dirname, de->d_name); + if (lstat(path, &st) == 0) { + if (S_ISREG(st.st_mode)) + return 1; + else + return 0; + } else { + log("error in stat: %s", strerror(errno)); + return 0; + } +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_subject( XMAP_DIR *xm, X509_NAME *name ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + snprintf(hashval, sizeof(hashval)-1, "%08lx.cert", X509_NAME_hash(name)); + n = scandir(xm->dir, &flist, hash_select, alphasort); + if (n < 0) + log("error on scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + /* Try a PEM load first, then try DER if it fails */ + if ((x = PEM_read_bio_X509(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); /* Try DER loading */ + } + + if (x) { + if (X509_NAME_cmp(X509_get_subject_name(x), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + + if (x) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_free(x); + } else + X509_free(x); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_ca_certs( XMAP_DIR *xm ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + strncpy(dirname, xm->dir, sizeof(dirname)-1); + n = scandir(xm->dir, &flist, select_files, alphasort); + if (n < 0) + log("error in scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + /* Try a PEM load first, then try DER if it fails */ + if ((x = PEM_read_bio_X509(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); /* Try DER loading */ + } + + if (x) { + if (isCA(x)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + + if (xo) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_free(x); + } else + X509_free(x); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_crl_by_issuer( XMAP_DIR *xm, X509_NAME *name ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + snprintf(hashval, sizeof(hashval)-1, "%08lx.crl", X509_NAME_hash(name)); + n = scandir(xm->dir, &flist, hash_select, alphasort); + if (n < 0) + log("error in scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509_CRL *c; + + /* Try a PEM load first, then try DER if it fails */ + if ((c = PEM_read_bio_X509_CRL(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + c = d2i_X509_CRL_bio(b, NULL); /* Try DER loading */ + } + + if (c) { + if (X509_NAME_cmp(X509_CRL_get_issuer(c), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_CRL, c); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_CRL_free(c); + } else + X509_CRL_free(c); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_uid( XMAP_DIR *xm, const char *uid ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + snprintf(hashval, sizeof(hashval)-1, "uid-%s", uid); + n = scandir(xm->dir, &flist, hash_select, alphasort); + if (n < 0) + log("error in scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + /* Try a PEM load first, then try DER if it fails */ + if ((x = PEM_read_bio_X509(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); /* Try DER loading */ + } + + if (x) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_dns( XMAP_DIR *xm, const char *dns ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + snprintf(hashval, sizeof(hashval)-1, "dns-%s", dns); + n = scandir(xm->dir, &flist, hash_select, alphasort); + if (n < 0) + log("error in scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + /* Try a PEM load first, then try DER if it fails */ + if ((x = PEM_read_bio_X509(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); /* Try DER loading */ + } + + if (x) { + if (X509_subjAltName_check_dns(x, dns)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } else { + X509_free(x); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_DIR_lookup_cert_by_ip( XMAP_DIR *xm, const struct in_addr ip ) +{ + char path[PATH_MAX+1]; + int n; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + struct dirent **flist; + + if (!sk) return sk; + + snprintf(hashval, sizeof(hashval)-1, "ip-%s", inet_ntoa(ip)); + n = scandir(xm->dir, &flist, hash_select, alphasort); + if (n < 0) + log("error in scandir: %s", strerror(errno)); + else { + int i; + + for(i=0; idir, flist[i]->d_name); + + if ((b = BIO_new_file(path, "r")) != NULL) { + X509 *x; + + /* Try a PEM load first, then try DER if it fails */ + if ((x = PEM_read_bio_X509(b, NULL, NULL, NULL)) == NULL) { + BIO_ctrl(b, BIO_CTRL_RESET, 0, NULL); + x = d2i_X509_bio(b, NULL); /* Try DER loading */ + } + + if (x) { + if (X509_subjAltName_check_ip(x, ip)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } else { + X509_free(x); + } + + BIO_free(b); + } else { + ERR_print_errors_fp(stderr); + } + } + if (n > 0) free(flist); + } + return sk; +} + +static void +XMAP_DIR_dump(void *this) +{ + XMAP_DIR *xm = (XMAP_DIR *)this; + DBG_log("XMAP_DIR: %s", xm->dir); +} + +static void +XMAP_DIR_free(void *this) +{ + XMAP_DIR *xm = (XMAP_DIR *)this; + + if (!xm) return; + if (xm->dir) pfree(xm->dir); + free(xm); +} + +XMAP_METHOD * +XMAP_METHOD_dir( void ) +{ + return (&xmap_dir_methods); +} diff -ruN freeswan-1.5.orig/pluto/xmap_dir.h freeswan-1.5/pluto/xmap_dir.h --- freeswan-1.5.orig/pluto/xmap_dir.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_dir.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,17 @@ +#ifndef XMAP_DIR_H +#define XMAP_DIR_H + +#include +#include + +#include "x_sobj.h" +#include "xmap.h" + +typedef struct xmap_dir_st { + char *dir; + XMAP_METHOD *meth; +} XMAP_DIR; + +XMAP_METHOD *XMAP_METHOD_dir( void ); + +#endif /* XMAP_DIR_H */ diff -ruN freeswan-1.5.orig/pluto/xmap_file.c freeswan-1.5/pluto/xmap_file.c --- freeswan-1.5.orig/pluto/xmap_file.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_file.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "xmap.h" +#include "xmap_file.h" + +#include "constants.h" +#include "defs.h" +#include "log.h" + +static void XMAP_FILE_dump(void *this); +static void XMAP_FILE_free(void *this); +static void *XMAP_FILE_new(const char *file); +static int isCA( X509 *x ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup( void *this, const int type, const char *index, void *p ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_subject( XMAP_FILE *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_crl_by_issuer( XMAP_FILE *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_uid( XMAP_FILE *xm, const char *uid ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_dns( XMAP_FILE *xm, const char *dns ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_ip( XMAP_FILE *xm, const struct in_addr ip ); +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_ca_certs( XMAP_FILE *xm ); + +static XMAP_METHOD xmap_file_methods = { + "file", + XMAP_FILE_lookup, + XMAP_FILE_dump, + XMAP_FILE_free, + XMAP_FILE_new +}; + +/* Pattern is (ignoring lower/upper case -- + * uid: + */ +#define UID_REGEX \ +"^[[:space:]]*[Uu][Ii][Dd]:[[:space:]]*([[:alnum:]]+)[[:space:]]$" + +static int +isCA( X509 *x ) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + int loc; + + loc = X509_get_ext_by_NID(x, NID_basic_constraints, -1); + if (loc >= 0) { + ext = X509_get_ext(x, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + return 0; + } else { + BASIC_CONSTRAINTS_free(p); + return 1; /* This is a CA certificate */ + } + } else + return 0; + } else + return 0; +} + +XMAP_METHOD * +XMAP_METHOD_file( void ) +{ + return (&xmap_file_methods); +} + +static void * +XMAP_FILE_new(const char *file) +{ + XMAP_FILE *ret; + int re_code; + char errbuf[257]; + + if ((ret = alloc_thing(XMAP_FILE, "xmap_file")) == NULL) goto end; + if ((ret->file = clone_bytes(file, strlen(file), "file name")) == NULL) + goto end; + if ((ret->bio = BIO_new_file(ret->file, "r")) == NULL) goto end; + + if ((re_code = regcomp(&ret->uidreg, UID_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &ret->uidreg, errbuf, sizeof(errbuf-1)); + log("Regex Error: %s", errbuf); + goto end; + } + + ret->meth = &xmap_file_methods; + return (void *)ret; + end: + if (ret->bio) BIO_free(ret->bio); + if (ret->file) pfree(ret->file); + if (ret) pfree(ret); + return NULL; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup( void *this, const int type, const char *index, void *p ) +{ + XMAP_FILE *xm = (XMAP_FILE *)this; + + switch(type) { + case X509_LU_X509: + if (strcasecmp(index, "subject") == 0) + { + return XMAP_FILE_lookup_cert_by_subject( xm, (X509_NAME *)p); + } + else if (strcasecmp(index, "uid") == 0) + { + return XMAP_FILE_lookup_cert_by_uid( xm, (char *)p); + } + else if (strcasecmp(index, "dns") == 0) + { + return XMAP_FILE_lookup_cert_by_dns( xm, (char *)p); + } + else if (strcasecmp(index, "ip") == 0) + { + return XMAP_FILE_lookup_cert_by_ip( xm, *((struct in_addr *)p)); + } + else if (strcasecmp(index, "ca") == 0) + { + return XMAP_FILE_lookup_ca_certs( xm ); + } + else + { + return NULL; + } + break; + case X509_LU_CRL: + if (strcasecmp(index, "issuer") == 0) + return XMAP_FILE_lookup_crl_by_issuer( xm, (X509_NAME *)p); + else + return NULL; + break; + default: + return NULL; + } +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_subject( XMAP_FILE *xm, X509_NAME *name ) +{ + X509 *x; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + + if (sk == NULL) return sk; + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while ((x = PEM_read_bio_X509(xm->bio, NULL, NULL, NULL)) != NULL) { + if (X509_NAME_cmp(X509_get_subject_name(x), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_free(x); + } else + X509_free(x); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_ca_certs( XMAP_FILE *xm ) +{ + X509 *x; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + + if (sk == NULL) return sk; + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while ((x = PEM_read_bio_X509(xm->bio, NULL, NULL, NULL)) != NULL) { + if (isCA(x)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_free(x); + } else + X509_free(x); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_crl_by_issuer( XMAP_FILE *xm, X509_NAME *name ) +{ + X509_CRL *c; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + + if (sk == NULL) return sk; + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while ((c = PEM_read_bio_X509_CRL(xm->bio, NULL, NULL, NULL)) != NULL) { + if (X509_NAME_cmp(X509_CRL_get_issuer(c), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_CRL, c); + if (xo) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_CRL_free(c); + } else + X509_CRL_free(c); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_uid( XMAP_FILE *xm, const char *uid ) +{ + char line[BUFSIZ+1], user[BUFSIZ+1]; + regmatch_t pmatch[2]; + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + X509 *x; + + if (sk == NULL) return sk; + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while (BIO_gets(xm->bio, line, sizeof(line)-1)) { + if (!regexec(&xm->uidreg, line, 2, pmatch, 0)) + if ((pmatch[1].rm_so != -1) && (pmatch[1].rm_eo != -1)) { + memset(user, 0, sizeof(user)); + strncpy(user, &line[pmatch[1].rm_so], + pmatch[1].rm_eo - pmatch[1].rm_so); + if (strcmp(uid, user) == 0) { + /* Found a match for the user id */ + if ((x = PEM_read_bio_X509(xm->bio, NULL, NULL, NULL)) != NULL) { + X509_OBJECT *xo = X509_OBJECT_new( X509_LU_X509, x); + if (xo) { + sk_X509_OBJECT_push(sk, xo); + } else + X509_free(x); + } + } + } + } + + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_dns( XMAP_FILE *xm, const char *dns ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + X509 *x; + int zap; + X509_OBJECT *xo; + + if (sk == NULL) return sk; + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while ((x = PEM_read_bio_X509(xm->bio, NULL, NULL, NULL)) != NULL) { + zap = X509_subjAltName_check_dns(x, dns) ? 0 : 1; + + if (zap) + X509_free(x); + else { + xo = X509_OBJECT_new( X509_LU_X509, x); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } + + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_FILE_lookup_cert_by_ip( XMAP_FILE *xm, struct in_addr ip ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + X509 *x; + int zap; + X509_OBJECT *xo; + + if (sk == NULL) { + DBG(DBG_PARSING, + DBG_log("sk is NULL, returning NULL."); + ); + return sk; + } + BIO_ctrl(xm->bio, BIO_CTRL_RESET, 0, NULL); + while ((x = PEM_read_bio_X509(xm->bio, NULL, NULL, NULL)) != NULL) { + zap = X509_subjAltName_check_ip(x, ip) ? 0 : 1; + + if (zap) + X509_free(x); + else { + xo = X509_OBJECT_new( X509_LU_X509, x); + if (xo) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); + } + } + + return sk; +} + +static void +XMAP_FILE_dump(void *this) +{ + XMAP_FILE *xm = (XMAP_FILE *)this; + DBG(DBG_CONTROL | DBG_PARSING, + DBG_log("XMAP_FILE: %s\n", xm->file); + ); +} + +static void +XMAP_FILE_free(void *this) +{ + XMAP_FILE *xm = (XMAP_FILE *)this; + + if (!xm) return; + regfree(&xm->uidreg); + if (xm->bio) BIO_free(xm->bio); + if (xm->file) pfree(xm->file); + pfree(xm); +} diff -ruN freeswan-1.5.orig/pluto/xmap_file.h freeswan-1.5/pluto/xmap_file.h --- freeswan-1.5.orig/pluto/xmap_file.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_file.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,28 @@ +#ifndef XMAP_FILE_H +#define XMAP_FILE_H + +#include + +#include +#include +#include +#include + +#include "x_sobj.h" +#include "xmap.h" + +typedef struct xmap_file_st { + char *file; + BIO *bio; + regex_t uidreg; + regex_t ipreg; + XMAP_METHOD *meth; +} XMAP_FILE; + +XMAP_METHOD *XMAP_METHOD_file( void ); +#endif /* XMAP_FILE_H */ + + + + + diff -ruN freeswan-1.5.orig/pluto/xmap_ldap.c freeswan-1.5/pluto/xmap_ldap.c --- freeswan-1.5.orig/pluto/xmap_ldap.c Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_ldap.c Thu Oct 5 07:05:59 2000 @@ -0,0 +1,1265 @@ +#ifdef HAVE_LDAP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "x_sobj.h" +#include "xmap.h" +#include "xmap_ldap.h" + +#include + +#include "constants.h" +#include "defs.h" +#include "log.h" + +static void XMAP_LDAP_dump(void *this); +static void XMAP_LDAP_free(void *this); +static void *XMAP_LDAP_new(const char *ldap); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup( void *this, const int type, const char *index, void *p ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_subject( XMAP_LDAP *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_crl_by_issuer( XMAP_LDAP *xm, X509_NAME *name ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_uid( XMAP_LDAP *xm, const char *uid ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_dns( XMAP_LDAP *xm, const char *dns ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_ip( XMAP_LDAP *xm, const struct in_addr ip ); +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_ca_certs( XMAP_LDAP *xm ); +static char **make_attrs( const char *a ); +static void attrs_free( char **attrs ); + +char *find_filter( const char *index, const int type, flist *fl ); +char **find_attrs( const char *index, const int type, alist *al ); + +static XMAP_METHOD xmap_ldap_methods = { + "ldap", + XMAP_LDAP_lookup, + XMAP_LDAP_dump, + XMAP_LDAP_free, + XMAP_LDAP_new +}; + +#define HOST_REGEX "^([[:alnum:]]+)_host_name[[:space:]]*=[[:space:]]*([[:punct:][:alnum:]]+)" +#define PORT_REGEX "^([[:alnum:]]+)_port[[:space:]]*=[[:space:]]*([[:digit:]]+)" +#define BASE_REGEX "^([[:alnum:]]+)_base[[:space:]]*=[[:space:]]*([^[:space:]].*)" +#define TIMEOUT_REGEX "^([[:alnum:]]+)_timeout[[:space:]]*=[[:space:]]*([[:digit:]]+)" +#define BINDDN_REGEX "^([[:alnum:]]+)_bind_dn[[:space:]]*=[[:space:]]*([^[:space:]].*)" +#define BINDPW_REGEX "^([[:alnum:]]+)_password[[:space:]]*=[[:space:]]*([^[:space:]].*)" +#define FILTER_REGEX "^([[:alnum:]]+)_filter(:(([\\*[:alnum:]]+)(:([[:alnum:]]+))?))?[[:space:]]*=[[:space:]]*([^[:space:]].*)" +#define ATTRS_REGEX "^([[:alnum:]]+)_attributes(:(([\\*[:alnum:]]+)(:([[:alnum:]]+))?))?[[:space:]]*=[[:space:]]*([^[:space:]].*)" + +static void +insert_filter( const char *index, const int type, const char *filter, flist *fl ) +{ + int i; + char *f, *in; + + for(i=0; ((i < MAXFLIST) && (fl[i].index)) ;i++) + if ((strcasecmp(index, fl[i].index) == 0) && (fl[i].type == type)) break; + + if (i == MAXFLIST) return; /* No more room */ + if (fl[i].index) { + /* A replacement operation */ + if ((f = clone_bytes(filter, strlen(filter)+1, "filter")) == NULL) return; + pfree(fl[i].filter); + fl[i].filter = f; + } else { + if ((in = clone_bytes(index, strlen(index)+1, "index string")) == NULL) + return; + if ((f = clone_bytes(filter, strlen(filter)+1, "filter string")) == NULL) + { pfree(in); return; } + fl[i].index = in; + fl[i].type = type; + fl[i].filter = f; + DBG(DBG_PARSING, + DBG_log("Insert filter: %d (%s,%d,%s)", i+1, in, type, f); + ); + } +} + +static void +insert_attrs( const char *index, const int type, const char *attr, alist *al ) +{ + int i, j; + char **af, *in; + + for(i=0; ((i < MAXFLIST) && (al[i].index)) ;i++) { + if ((strcasecmp(index, al[i].index) == 0) && (al[i].type == type)) { + break; + } + } + + if (i == MAXFLIST) return; /* No more room */ + if (al[i].index) { + /* A replacement operation */ + if ((af = make_attrs(attr)) == NULL) return; + attrs_free(al[i].attrs); + al[i].attrs = af; + } else { + if ((in = clone_bytes(index, strlen(index)+1, "index string")) == NULL) + return; + if ((af = make_attrs(attr)) == NULL) { pfree(in); return; } + al[i].index = in; + al[i].type = type; + al[i].attrs = af; + DBG(DBG_PARSING, + DBG_log("Insert attribute: %d (%s,%d)", i+1, in, type); + ); + for(j=0; af[j]; j++) + DBG(DBG_PARSING, + DBG_log("Attribute %d: %s", j+1, af[j]); + ); + } +} + +char * +find_filter( const char *index, const int type, flist *fl ) +{ + int i, OKind; + unsigned char OK, indOK, typeOK; + + OK = 0x00; + for(i=0, OKind=0; ((i < MAXFLIST) && (fl[i].index)); i++) { + typeOK = (fl[i].type == X509_LU_FAIL) ? 0x01 : + ((fl[i].type == type) ? 0x04 : 0); + indOK = (strcasecmp(fl[i].index, "*") == 0) ? 0x02 : + ((strcasecmp(fl[i].index, index) == 0) ? 0x08 : 0); + if ((typeOK | indOK) > OK) { OKind = i; OK = typeOK | indOK; } + } + + return fl[OKind].filter; +} + +char ** +find_attrs( const char *index, const int type, alist *al ) +{ + int i, OKind; + unsigned char OK, indOK, typeOK; + + OK = 0x00; + for(i=0, OKind=0; ((i < MAXFLIST) && (al[i].index)); i++) { + typeOK = (al[i].type == X509_LU_FAIL) ? 0x01 : + ((al[i].type == type) ? 0x04 : 0); + indOK = (strcasecmp(al[i].index, "*") == 0) ? 0x02 : + ((strcasecmp(al[i].index, index) == 0) ? 0x08 : 0); + if ((typeOK | indOK) > OK) { OKind = i; OK = typeOK | indOK; } + } + + return al[OKind].attrs; +} + +static void +extract_re_sub( const char *buf, regmatch_t *rm, char **lv) +{ + if (!buf || !lv || !rm) return; + if (*lv) return; + if ((rm->rm_so == -1) || (rm->rm_eo == -1)) return; + if ((*lv = alloc_bytes(rm->rm_eo - rm->rm_so + 1, "subexpression")) == NULL) + return; + strncpy(*lv, &buf[rm->rm_so], rm->rm_eo - rm->rm_so); + (*lv)[rm->rm_eo - rm->rm_so] = '\0'; +} + +static char ** +make_attrs( const char *a ) +{ + char *s = clone_bytes(a, strlen(a)+1, "attribute list"); + char *at; + int i = 0, j; + char **ret = NULL; + const char *delim = ", "; + + if (!s) return NULL; + at = strtok(s, delim); + while (at != NULL) { + i++; + at = strtok(NULL, delim); + } + ret = alloc_bytes((i+1) * sizeof(char *), "attribute array"); + if (!ret) { pfree(s); return NULL; } + for(j=0; j<=i; j++) ret[j] = NULL; + pfree(s); + + if ((s = clone_bytes(a, strlen(a)+1, "attribute list")) == NULL) return NULL; + at = strtok(s, delim); + i = 0; + while (at != NULL) { + if ((ret[i++] = clone_bytes(at, strlen(at)+1, "attribute")) == NULL) { + for(i=0; ret[i]; i++) pfree(ret[i]); + pfree(ret); + pfree(s); + return NULL; + } + at = strtok(NULL, delim); + } + + pfree(s); + return ret; +} + +static void +attrs_free( char **attrs ) +{ + int i=0; + + for(i=0; attrs[i]; i++) pfree(attrs[i]); + pfree(attrs); +} + +static jmp_buf env; +static void +ldap_timeout(int unused_sig __attribute__((unused))) +{ + longjmp(env, 1); +} + +static int +open_bind( XMAP_LDAP *xm ) +{ + void (*saved_alarm)(int); + + if ((saved_alarm = signal(SIGALRM, ldap_timeout)) == SIG_ERR) { + log("signal: %s", strerror(errno)); + return 0; + } + + alarm(xm->ldap_timeout); + if (setjmp(env) == 0) { + if ((xm->ld = ldap_open(xm->ldap_host, xm->ldap_port)) == NULL) { + DBG(DBG_PARSING, + DBG_log("Open to %s:%d failed", xm->ldap_host, xm->ldap_port); + ); + log("ldap_open: %s", strerror(errno)); + return 0; + } + } + alarm(0); + + if ((ldap_bind_s(xm->ld, xm->ldap_bindDN, xm->ldap_bindPW, + LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) return 0; + + return 1; +} + +void * +XMAP_LDAP_new(const char *ldapspec) +{ + char *ls = NULL, *s, *ldapvar = NULL, *ldapfile; + FILE *fp; + XMAP_LDAP *ret = NULL; + int i, re_code; + char re_err[256]; + regmatch_t re_match[10]; + + regex_t host_re; + regex_t port_re; + regex_t timeout_re; + regex_t base_re; + regex_t binddn_re; + regex_t bindpw_re; + regex_t filter_re; + regex_t attrs_re; + + /* Default values */ + char *ldap_host = clone_bytes("localhost", strlen("localhost")+1, + "ldap_host"); + int ldap_port = LDAP_PORT; + int ldap_timeout = 10; /* seconds */ + char *ldap_bindDN = clone_bytes("", 1, "bindDN"); + char *ldap_bindPW = clone_bytes("", 1, "bindPW"); + char *ldap_base = NULL; + flist ldap_filter[MAXFLIST]; + alist ldap_attrs[MAXFLIST]; + + memset(ldap_filter, 0, sizeof(ldap_filter)); + insert_filter("*", X509_LU_FAIL, "(cn=%s)", ldap_filter); + + memset(ldap_attrs, 0, sizeof(ldap_attrs)); + insert_attrs("*", X509_LU_FAIL, "usercertificate, usercertificate;binary, cacertificate, cacertificate;binary", ldap_attrs); + + if ((re_code = regcomp(&host_re, HOST_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &host_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&port_re, PORT_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &port_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&base_re, BASE_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &base_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&timeout_re, TIMEOUT_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &timeout_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&binddn_re, BINDDN_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &binddn_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&bindpw_re, BINDPW_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &bindpw_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&filter_re, FILTER_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &filter_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((re_code = regcomp(&attrs_re, ATTRS_REGEX, REG_EXTENDED)) != 0) { + regerror(re_code, &attrs_re, re_err, sizeof(re_err)); + log("Regex error: %s", re_err); + goto end; + } + + if ((!ldap_host) || (!ldap_bindDN) || (!ldap_bindPW) || + (!ldap_filter) || (!ldap_attrs)) goto end; + + ls = clone_bytes(ldapspec, strlen(ldapspec)+1, "ldap spec"); + if (!ls) goto end; + s = strchr(ls, ':'); + ldapfile = ls; + if (s) { + ldapvar = clone_bytes(&s[1], strlen(&s[1])+1, "ldap name"); + *s = '\0'; + } else { + ldapvar = NULL; + } + + fp = fopen( ldapfile, "r" ); + if (!fp) { + perror("fopen"); + goto end; + } + + while (!feof(fp)) { + char buf[BUFSIZ], *ptr; + + if (fgets(buf, sizeof(buf), fp)) { + int l; + + for(ptr = buf; ((*ptr) && (isspace(*ptr))); ptr++); + if (! *ptr) continue; /* Blank Line */ + if (*ptr == '#') continue; /* Comment */ + l = strlen(buf)-1; + while ((l > 0) && (isspace(buf[l]))) { buf[l] = '\0'; l--; } + + /* Check for host name spec */ + if ((re_code = regexec(&host_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + if (ldap_host) { pfree(ldap_host); ldap_host = NULL; } + extract_re_sub(buf, &re_match[2], &ldap_host); + } + pfree(lv); + } + + /* Check for port spec */ + if ((re_code = regexec(&port_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + char *ps = NULL; + + extract_re_sub(buf, &re_match[2], &ps); + ldap_port = atoi(ps); + pfree(ps); + } + pfree(lv); + } + + /* Check for base spec */ + if ((re_code = regexec(&base_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + if (ldap_base) { free(ldap_base); ldap_base = NULL; } + extract_re_sub(buf, &re_match[2], &ldap_base); + } + pfree(lv); + } + + /* Check for timeout spec */ + if ((re_code = regexec(&timeout_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + char *to = NULL; + + extract_re_sub(buf, &re_match[2], &to); + ldap_timeout = atoi(to); + free(to); + } + pfree(lv); + } + + /* Check for bind DN spec */ + if ((re_code = regexec(&binddn_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + if (ldap_bindDN) { free(ldap_bindDN); ldap_bindDN = NULL; } + extract_re_sub(buf, &re_match[2], &ldap_bindDN); + } + pfree(lv); + } + + /* Check for bind PW spec */ + if ((re_code = regexec(&bindpw_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + if (ldap_bindPW) { free(ldap_bindPW); ldap_bindPW = NULL; } + extract_re_sub(buf, &re_match[2], &ldap_bindPW); + } + pfree(lv); + } + + /* Check for filter spec */ + if ((re_code = regexec(&filter_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + char *filt = NULL, *sf = NULL, *ssf = NULL; + int ssftype = X509_LU_FAIL; /* Use this as a catchall */ + + if (re_match[2].rm_so != -1) { + extract_re_sub(buf, &re_match[4], &sf); + if (re_match[5].rm_so != -1) { + extract_re_sub(buf, &re_match[6], &ssf); + if (strcasecmp(ssf, "cert") == 0) + ssftype = X509_LU_X509; + else if (strcasecmp(ssf, "crl") == 0) + ssftype = X509_LU_CRL; + else + ssftype = X509_LU_RETRY; /* Use as an "invalid" value */ + pfree(ssf); + } + } + extract_re_sub(buf, &re_match[7], &filt); + if (ssftype != X509_LU_RETRY) + insert_filter((sf == NULL) ? "*" : sf, ssftype, filt, ldap_filter); + else + if (filt) pfree(filt); + if (sf) pfree(sf); + } + pfree(lv); + } + + /* Check for attributes spec */ + if ((re_code = regexec(&attrs_re, buf, 10, re_match, 0)) == 0) { + char *lv = NULL; + + extract_re_sub(buf, &re_match[1], &lv); + if (!ldapvar) extract_re_sub(buf, &re_match[1], &ldapvar); + if (strcmp(ldapvar, lv) == 0) { + char *attr = NULL; + char *sf = NULL, *ssf = NULL; + int ssftype = X509_LU_FAIL; /* Use this as a catchall */ + + if (re_match[2].rm_so != -1) { + extract_re_sub(buf, &re_match[4], &sf); + if (re_match[5].rm_so != -1) { + extract_re_sub(buf, &re_match[6], &ssf); + if (strcasecmp(ssf, "cert") == 0) + ssftype = X509_LU_X509; + else if (strcasecmp(ssf, "crl") == 0) + ssftype = X509_LU_CRL; + else if (strcasecmp(ssf, "*") == 0) + ssftype = X509_LU_FAIL; + else + ssftype = X509_LU_RETRY; /* Use as an "invalid" value */ + pfree(ssf); + } + } + extract_re_sub(buf, &re_match[7], &attr); + if (ssftype != X509_LU_RETRY) + insert_attrs((sf == NULL) ? "*" : sf, ssftype, attr, ldap_attrs); + else + if (attr) pfree(attr); + if (sf) pfree(sf); + } + pfree(lv); + } + + } + } + + if (!ldap_base) goto end; /* No search base - and we can't default to one */ + + if ((ret = alloc_thing(XMAP_LDAP, "xmap_ldap")) == NULL) goto end; + + ret->ldapsource = ldapvar; + ret->ldap_host = ldap_host; + ret->ldap_port = ldap_port; + ret->ldap_base = ldap_base; + ret->ldap_timeout = ldap_timeout; + ret->ldap_bindDN = ldap_bindDN; + ret->ldap_bindPW = ldap_bindPW; + memcpy(ret->ldap_filter, ldap_filter, MAXFLIST * sizeof(flist)); + memcpy(ret->ldap_attrs, ldap_attrs, MAXFLIST * sizeof(alist)); + + fclose(fp); fp = NULL; + + if (!open_bind(ret)) goto end; + + if (ls) pfree(ls); + return (void *)ret; + + end: + if (ldap_host) pfree(ldap_host); + if (ldap_base) pfree(ldap_base); + if (ldap_bindDN) pfree(ldap_bindDN); + if (ldap_bindPW) pfree(ldap_bindPW); + for(i=0; ldap_filter[i].index; i++) + { pfree(ldap_filter[i].index); pfree(ldap_filter[i].filter); } + for(i=0; ldap_attrs[i].index; i++) + { pfree(ldap_attrs[i].index); attrs_free(ldap_attrs[i].attrs); } + if (ldapvar) pfree(ldapvar); + if (ls) pfree(ls); + if (fp) fclose(fp); + return NULL; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup( void *this, const int type, const char *index, void *p ) +{ + XMAP_LDAP *xm = (XMAP_LDAP *)this; + + if (type == X509_LU_X509) { + if (strcasecmp(index, "subject") == 0) + return XMAP_LDAP_lookup_cert_by_subject(xm, (X509_NAME *)p); + else if (strcasecmp(index, "uid") == 0) + return XMAP_LDAP_lookup_cert_by_uid(xm, (char *)p); + else if (strcasecmp(index, "dns") == 0) + return XMAP_LDAP_lookup_cert_by_dns(xm, (char *)p); + else if (strcasecmp(index, "ip") == 0) + return XMAP_LDAP_lookup_cert_by_ip(xm, *((struct in_addr *)p)); + else if (strcasecmp(index, "ca") == 0) + return XMAP_LDAP_lookup_ca_certs( xm ); + else + return NULL; + } else if (type == X509_LU_CRL) { + if (strcasecmp(index, "issuer") == 0) + return XMAP_LDAP_lookup_crl_by_issuer(xm, (X509_NAME *)p); + else + return NULL; + } else { + log("Unknown search type"); + return NULL; + } +} + +static int +ASN1_UTCTIME_cmp(ASN1_UTCTIME *a, ASN1_UTCTIME *b) +{ + char buff1[14],buff2[14],*p; + int i,j; + + /* Degenerate cases */ + if (!a && !b) return 0; + if (a && !b) return 1; + if (!a && b) return -1; + + memset(buff1, 0, sizeof(buff1)); + memset(buff2, 0, sizeof(buff2)); + + p=buff1; + i=a->length; + if ((i < 11) || (i > 17)) return(0); + memcpy(p,a->data,12); + + p=buff2; + j=b->length; + if ((j < 11) || (j > 17)) return(0); + memcpy(p,b->data,12); + + /* Correct for Y2K */ + i=(buff1[0]-'0')*10+(buff1[1]-'0'); + if (i < 50) i+=100; /* cf. RFC 2459 */ + j=(buff2[0]-'0')*10+(buff2[1]-'0'); + if (j < 50) j+=100; + + if (i < j) return (-1); + if (i > j) return (1); + + i=strcmp(buff1,buff2); + + if (i == 0) + return 0; + else if (i < 0) + return(-1); + else + return(1); +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_subject( XMAP_LDAP *xm, X509_NAME *name ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + STACK_OF(X509_NAME_ENTRY) *ns = name->entries; + char *str, *s1, *ff, **af; + int i, j, tlen, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + /* First find the 'common name' attribute from the subject */ + for(i=0; iobject); + + if (nid == NID_commonName) { + if (!(str = alloc_bytes(ASN1_STRING_length(ne->value)+1, "common name"))) + return sk; + memset(str, 0, ASN1_STRING_length(ne->value)+1); + memcpy(str, ASN1_STRING_data(ne->value), + ASN1_STRING_length(ne->value)); + break; + } + } + ff = find_filter("subject", X509_LU_X509, xm->ldap_filter); + af = find_attrs("subject", X509_LU_X509, xm->ldap_attrs); + tlen = strlen(ff) + strlen(str); + if ((s1 = alloc_bytes(tlen, "filter pattern")) != NULL) { + snprintf(s1, tlen, ff, str); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + s1, af, 0, + &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + unsigned char *p; + X509 *x; + + p = bv[j]->bv_val; + x = d2i_X509(NULL, &p, bv[j]->bv_len); + if (x) { + if (X509_NAME_cmp(X509_get_subject_name(x), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.x509) == 0) + found = 1; + } + if (!found) + sk_X509_OBJECT_push(sk, xo); + else { + X509_free(x); /* duplicate of existing item */ + } + } else { + X509_free(x); /* cannot allocate X509_OBJECT */ + } + } else { + X509_free(x); /* subject name doesn't match */ + } + } + } + ldap_value_free_len(bv); + } + } + } + } else { + log("ldap_search_st error: %s", ldap_err2string(rc)); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + pfree(s1); + } + pfree(str); + return sk; +} + +static int +isCA( X509 *x ) +{ + X509_EXTENSION *ext; + BASIC_CONSTRAINTS *p = NULL; + int loc; + + loc = X509_get_ext_by_NID(x, NID_basic_constraints, -1); + if (loc >= 0) { + ext = X509_get_ext(x, loc); + if (ext) { + p = X509V3_EXT_d2i(ext); + if (! p->ca) { + BASIC_CONSTRAINTS_free(p); + return 0; + } else { + BASIC_CONSTRAINTS_free(p); + return 1; /* This is a CA certificate */ + } + } else + return 0; + } else + return 0; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_ca_certs( XMAP_LDAP *xm ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + char *ff, **af; + int i, j, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + ff = find_filter("ca", X509_LU_X509, xm->ldap_filter); + af = find_attrs("ca", X509_LU_X509, xm->ldap_attrs); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + ff, af, 0, &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + unsigned char *p; + X509 *x; + + p = bv[j]->bv_val; + x = d2i_X509(NULL, &p, bv[j]->bv_len); + if ((x) && (isCA(x))) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.x509) == 0) + found = 1; + } + if (!found) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); /* duplicate of existing item */ + } else + X509_free(x); /* cannot allocate X509_OBJECT */ + } else + X509_free(x); /* subject name doesn't match */ + } + ldap_value_free_len(bv); + } + } + } + } else { + log("ldap_search_st error: %s", ldap_err2string(rc)); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_crl_by_issuer( XMAP_LDAP *xm, X509_NAME *name ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + STACK_OF(X509_NAME_ENTRY) *ns = name->entries; + char *str, *s1, *ff, **af; + int i, j, tlen, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + /* First find the 'common name' attribute from the subject */ + for(i=0; iobject); + + if (nid == NID_commonName) { + if (!(str = alloc_bytes(ASN1_STRING_length(ne->value)+1, + "common name"))) + return sk; + memset(str, 0, ASN1_STRING_length(ne->value)+1); + memcpy(str, ASN1_STRING_data(ne->value), + ASN1_STRING_length(ne->value)); + break; + } + } + ff = find_filter("issuer", X509_LU_CRL, xm->ldap_filter); + af = find_attrs("issuer", X509_LU_CRL, xm->ldap_attrs); + tlen = strlen(ff) + strlen(str); + if ((s1 = alloc_bytes(tlen, "filter pattern")) != NULL) { + snprintf(s1, tlen, ff, str); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + DBG(DBG_PARSING, + DBG_log("LDAP search: \"%s\"", s1); + ); + for(i=0; af[i]; i++) { + DBG(DBG_PARSING, + DBG_log("Attr %d: %s", i+1, af[i]); + ); + } + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + s1, af, 0, + &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + unsigned char *p; + X509_CRL *x; + + p = bv[j]->bv_val; + x = d2i_X509_CRL(NULL, &p, bv[j]->bv_len); + if (x) { + if (X509_NAME_cmp(X509_CRL_get_issuer(x), name) == 0) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_CRL, x); + + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.crl, x) == 0) && + (ASN1_UTCTIME_cmp(X509_CRL_get_lastUpdate(o->data.crl), + X509_CRL_get_lastUpdate(x)) == 0) && + (ASN1_UTCTIME_cmp(X509_CRL_get_nextUpdate(o->data.crl), + X509_CRL_get_nextUpdate(x)) == 0)) + found = 1; + } + if (!found) + sk_X509_OBJECT_push(sk, xo); + else + X509_CRL_free(x); /* duplicate of existing item */ + } else + X509_CRL_free(x); /* cannot allocate X509_OBJECT */ + } else + X509_CRL_free(x); /* subject name doesn't match */ + } + } + ldap_value_free_len(bv); + } + } + } + } else { + log("ldap_search_st error: %s", ldap_err2string(rc)); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + pfree(s1); + } + pfree(str); + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_uid( XMAP_LDAP *xm, const char *uid ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + char *s1, *ff, **af; + int i, j, tlen, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + ff = find_filter("uid", X509_LU_X509, xm->ldap_filter); + af = find_attrs("uid", X509_LU_X509, xm->ldap_attrs); + tlen = strlen(ff) + strlen(uid); + if ((s1 = alloc_bytes(tlen, "filter pattern")) != NULL) { + snprintf(s1, tlen, ff, uid); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + s1, af, 0, + &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + unsigned char *p; + X509 *x; + + p = bv[j]->bv_val; + x = d2i_X509(NULL, &p, bv[j]->bv_len); + if (x) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.x509) == 0) + found = 1; + } + if (!found) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); /* duplicate of existing item */ + } else + X509_free(x); /* cannot allocate X509_OBJECT */ + } + } + ldap_value_free_len(bv); + } + } + } + } else { + log("ldap_search_st error: %s", ldap_err2string(rc)); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + pfree(s1); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_dns( XMAP_LDAP *xm, const char *dns ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + char *s1, *ff, **af; + int i, j, tlen, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + ff = find_filter("dns", X509_LU_X509, xm->ldap_filter); + af = find_attrs("dns", X509_LU_X509, xm->ldap_attrs); + tlen = strlen(ff) + strlen(dns); + if ((s1 = alloc_bytes(tlen, "filter patten")) != NULL) { + snprintf(s1, tlen, ff, dns); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + s1, af, 0, + &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + unsigned char *p; + X509 *x; + + p = bv[j]->bv_val; + x = d2i_X509(NULL, &p, bv[j]->bv_len); + if (x) { + if (X509_subjAltName_check_dns(x, dns)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.x509) == 0) + found = 1; + } + if (!found) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); /* duplicate of existing item */ + } else + X509_free(x); /* cannot allocate X509_OBJECT */ + } else + X509_free(x); /* subjAltName mismatch */ + } + } + ldap_value_free_len(bv); + } + } + } + } else { + log("ldap_search_st error: %s", ldap_err2string(rc)); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + pfree(s1); + } + return sk; +} + +static STACK_OF(X509_OBJECT) * +XMAP_LDAP_lookup_cert_by_ip( XMAP_LDAP *xm, const struct in_addr ip ) +{ + STACK_OF(X509_OBJECT) *sk = sk_X509_OBJECT_new_null(); + char *s1, *ff, **af, *ipt; + int i, j, tlen, rc; + LDAPMessage *res = NULL, *ent = NULL; + struct timeval tv; + struct berval **bv; + + if (!sk) return NULL; + if (!xm->ld) + if (!open_bind(xm)) return NULL; + + if ((ipt = inet_ntoa(ip)) == NULL) return NULL; + ff = find_filter("ip", X509_LU_X509, xm->ldap_filter); + af = find_attrs("ip", X509_LU_X509, xm->ldap_attrs); + + tlen = strlen(ff) + strlen(ipt); + if ((s1 = alloc_bytes(tlen, "filter pattern")) != NULL) { + snprintf(s1, tlen, ff, ipt); + tv.tv_sec = xm->ldap_timeout; tv.tv_usec = 0; + + DBG(DBG_PARSING, + DBG_log("xm->ldap_base: %s -- s1: %s -- *af: %s", xm->ldap_base, s1, *af); + ); + + rc = ldap_search_st(xm->ld, xm->ldap_base, LDAP_SCOPE_SUBTREE, + s1, af, 0, + &tv, &res); + if (rc == LDAP_SUCCESS) { + for(ent = ldap_first_entry(xm->ld, res); ent ; + ent = ldap_next_entry(xm->ld, ent)) { + for(i = 0; af[i]; i++) { + + DBG(DBG_PARSING, + DBG_log("af[%d]: [%s]", i, af[i]); + ); + + bv = ldap_get_values_len(xm->ld, ent, af[i]); + if (bv) { + for(j=0; bv[j]; j++) { + signed char *p; + X509 *x; + + DBG(DBG_PARSING, + DBG_log("** We got LDAP. Trying to process. j=%d.", j); + ); + + p = bv[j]->bv_val; + + DBG(DBG_PARSING | DBG_RAW, + DBG_log("** length of X509 cert: %d", bv[j]->bv_len); + ); + DBG(DBG_RAW, + DBG_dump("** p: ", bv[j]->bv_val, bv[j]->bv_len); + ); + + x = d2i_X509(NULL, &p, bv[j]->bv_len); + if (x) { + if (X509_subjAltName_check_ip(x, ip)) { + X509_OBJECT *xo = X509_OBJECT_new(X509_LU_X509, x); + if (xo) { + int i, found = 0; + + for(i=0; (!found) && (idata.x509) == 0) + found = 1; + } /* end for */ + if (!found) + sk_X509_OBJECT_push(sk, xo); + else + X509_free(x); /* duplicate of existing item */ + } /* end if (xo) */ + else + X509_free(x); /* cannot allocate X509_OBJECT */ + } /* end if (X509_subjAltName_check_ip(x, ip)) */ + else + X509_free(x); /* SubjectAltName mismatch */ + } /* end if (x) */ + else + { + } + } + ldap_value_free_len(bv); + } + } + } + } else { + DBG(DBG_PARSING, + DBG_log("ldap_search_st error: %s", ldap_err2string(rc)); + ); + ldap_unbind(xm->ld); + xm->ld = NULL; + } + pfree(s1); + } + return sk; +} + +static void +sanitize(const char *buf, char *buf2, int buf2len) +{ + int l = strlen(buf), i, nl, p; + char *b; + + for(i=0, nl=l; ildapsource); + DBG_log("Host = %s:%d (Timeout = %d seconds)", + xm->ldap_host, xm->ldap_port, xm->ldap_timeout); + DBG_log("Search base = %s", xm->ldap_base); + DBG_log("Bind = (\"%s\", \"%s\")", xm->ldap_bindDN, xm->ldap_bindPW); + ); + for(i=0; ((i < MAXFLIST) && (xm->ldap_filter[i].index)); i++) { + snprintf(buf, sizeof(buf)-1, "Filter[%d] (%s,", + i+1, xm->ldap_filter[i].index); + switch(xm->ldap_filter[i].type) { + case X509_LU_X509: strncat(buf, "Cert", sizeof(buf)-1); break; + case X509_LU_CRL: strncat(buf, "CRL", sizeof(buf)-1); break; + default: strncat(buf, "*", sizeof(buf)-1); break; + } + strncat(buf, ") ", sizeof(buf)-1); + strncat(buf, xm->ldap_filter[i].filter, sizeof(buf)-1); + sanitize(buf, buf, sizeof(buf)-1); + DBG(DBG_PARSING, + DBG_log(buf); + ); + } + for(i=0; ((i < MAXFLIST) && (xm->ldap_attrs[i].index)); i++) { + snprintf(buf, sizeof(buf), "Attributes[%d] (%s,", + i+1, xm->ldap_attrs[i].index); + switch(xm->ldap_attrs[i].type) { + case X509_LU_X509: strncat(buf, "Cert", sizeof(buf)-1); break; + case X509_LU_CRL: strncat(buf, "CRL", sizeof(buf)-1); break; + default: strncat(buf, "*", sizeof(buf)-1); break; + } + strncat(buf, ") ", sizeof(buf)-1); + for(j=0; xm->ldap_attrs[i].attrs[j]; j++) { + strncat(buf, ",", sizeof(buf)-1); + strncat(buf, xm->ldap_attrs[i].attrs[j], sizeof(buf)-1); + } + sanitize(buf, buf, sizeof(buf)-1); + DBG(DBG_PARSING, + DBG_log(buf); + ); + } +} + +static void +XMAP_LDAP_free(void *this) +{ + XMAP_LDAP *xm = (XMAP_LDAP *)this; + int i; + + if (!xm) return; + if (xm->ld) ldap_unbind(xm->ld); + if (xm->ldapsource) pfree(xm->ldapsource); + if (xm->ldap_host) pfree(xm->ldap_host); + if (xm->ldap_base) pfree(xm->ldap_base); + if (xm->ldap_bindDN) pfree(xm->ldap_bindDN); + if (xm->ldap_bindPW) pfree(xm->ldap_bindPW); + + for(i=0; ((i < MAXFLIST) && (xm->ldap_filter[i].index)); i++) { + pfree(xm->ldap_filter[i].index); + pfree(xm->ldap_filter[i].filter); + } + + for(i=0; ((i < MAXFLIST) && (xm->ldap_attrs[i].index)); i++) { + pfree(xm->ldap_attrs[i].index); + attrs_free(xm->ldap_attrs[i].attrs); + } + + pfree(xm); +} + +XMAP_METHOD * +XMAP_METHOD_ldap( void ) +{ + return (&xmap_ldap_methods); +} + +#endif /* HAVE_LDAP */ diff -ruN freeswan-1.5.orig/pluto/xmap_ldap.h freeswan-1.5/pluto/xmap_ldap.h --- freeswan-1.5.orig/pluto/xmap_ldap.h Wed Dec 31 19:00:00 1969 +++ freeswan-1.5/pluto/xmap_ldap.h Thu Oct 5 07:05:59 2000 @@ -0,0 +1,49 @@ +#ifndef XMAP_LDAP_H +#define XMAP_LDAP_H + +#ifdef HAVE_LDAP + +#include +#include + +#include +#include + +#include "x_sobj.h" +#include "xmap.h" + +typedef struct _flist_st { + char *index; + int type; + char *filter; +} flist; + +#define MAXFLIST 10 + +typedef struct _alist_st { + char *index; + int type; + char **attrs; +} alist; + +typedef struct xmap_ldap_st { + char *ldapsource; + char *ldap_host; + int ldap_port; + char *ldap_base; + int ldap_timeout; + char *ldap_bindDN; + char *ldap_bindPW; + flist ldap_filter[MAXFLIST]; + alist ldap_attrs[MAXFLIST]; + + LDAP *ld; + + XMAP_METHOD *meth; +} XMAP_LDAP; + +XMAP_METHOD *XMAP_METHOD_ldap( void ); + +#endif /* HAVE_LDAP */ + +#endif /* XMAP_LDAP_H */ diff -ruN freeswan-1.5.orig/utils/_confread freeswan-1.5/utils/_confread --- freeswan-1.5.orig/utils/_confread Wed May 31 08:10:40 2000 +++ freeswan-1.5/utils/_confread Thu Oct 5 07:05:59 2000 @@ -118,6 +118,7 @@ left = " left leftsubnet leftnexthop leftfirewall leftupdown" akey = " keyexchange auth pfs keylife rekeymargin rekeyfuzz" obs = " lifetime rekeystart rekeytries" + cert = " certfile keyfile peerfile certopts certpath" akey = akey " keyingtries ikelifetime" obs mkey = " spibase spi esp espenckey espauthkey espreplay_window" left = left " leftespenckey leftespauthkey leftahkey" @@ -125,7 +126,7 @@ mkey = mkey " ah ahkey ahreplay_window" right = left gsub(/left/, "right", right) - n = split(good left right akey mkey, g) + n = split(good left right akey mkey cert, g) for (i = 1; i <= n; i++) goodnames["conn:" g[i]] = 1 diff -ruN freeswan-1.5.orig/utils/auto freeswan-1.5/utils/auto --- freeswan-1.5.orig/utils/auto Sat Apr 15 12:25:07 2000 +++ freeswan-1.5/utils/auto Thu Oct 5 07:05:59 2000 @@ -307,6 +307,18 @@ need("rightrsasigkey") } + if (s["authby"] == "cert") { + default("certfile", "") + default("certopts", "send") + default("keyfile", "") + default("certpath", "") + authtype = "--openssl" + need("certfile") + need("certopts") + need("keyfile") + need("certpath") + } + print "PATH=\"'"$PATH"'\"" print "export PATH" @@ -341,6 +353,17 @@ if ("rekeyfuzz" in s) fuzz = "--rekeyfuzz " s["rekeyfuzz"] + if (authtype == "--openssl") { + if ("certfile" in s) + certfile = "--certfile " s["certfile"] + if ("certopts" in s) + certopts = "--certopts " s["certopts"] + if ("keyfile" in s) + keyfile = "--keyfile " s["keyfile"] + if ("certpath" in s) + certpath = "--certpath " s["certpath"] + } + print "ipsec whack --name", name, settings, "\\" print "\t--host", s["left"], lc, "--nexthop", s["leftnexthop"], lud, lid, "\\" @@ -348,8 +371,19 @@ "--nexthop", s["rightnexthop"], rud, rid, "\\" print "\t--ipseclifetime", s["keylife"], "--rekeywindow", s["rekeymargin"], "\\" + if (authtype == "--openssl") { + if (certfile != "--certfile ") + print certfile, "\\" + if (certopts != "--certopts ") + print certopts, "\\" + if (keyfile != "--keyfile ") + print keyfile, "\\" + if (certpath != "--certpath ") + print certpath, "\\" + } + print "\t--keyingtries", s["keyingtries"], fuzz - if (authtype != "--psk") { + if (authtype == "--rsasig") { whackkey("left") whackkey("right") }