From d667da5b69928ec81f3856e55b45c525aa8706d0 Mon Sep 17 00:00:00 2001 From: Ian Kelling Date: Tue, 13 Oct 2020 17:15:12 -0400 Subject: [PATCH 1/1] 4.2.1 that we had on our wiki --- LICENSE | 504 ++++++++++++++++++++++++ Makefile.in | 10 + README | 110 ++++++ config.m4 | 95 +++++ curltest.php | 47 +++ mem.c | 166 ++++++++ php_tclink.c | 235 ++++++++++++ php_tclink.h | 49 +++ tcexample.php | 101 +++++ tclink.c | 1017 +++++++++++++++++++++++++++++++++++++++++++++++++ tclink.h | 83 ++++ tctest.php | 34 ++ validate.c | 154 ++++++++ 13 files changed, 2605 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile.in create mode 100644 README create mode 100644 config.m4 create mode 100644 curltest.php create mode 100644 mem.c create mode 100644 php_tclink.c create mode 100644 php_tclink.h create mode 100644 tcexample.php create mode 100644 tclink.c create mode 100644 tclink.h create mode 100644 tctest.php create mode 100644 validate.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..a137c97 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,10 @@ +# $Id: Makefile.in,v 1.5.10.1 2015-10-15 23:20:29 mlai Exp $ + +LTLIBRARY_NAME = libtclink.la +LTLIBRARY_SOURCES = tclink.c php_tclink.c mem.c validate.c +LTLIBRARY_SHARED_NAME = tclink.la +LTLIBRARY_SHARED_LIBADD = $(TCLINK_SHARED_LIBADD) + +CFLAGS = @CFLAGS@ @DEFS@ + +include $(top_srcdir)/build/dynlib.mk diff --git a/README b/README new file mode 100644 index 0000000..e4f323c --- /dev/null +++ b/README @@ -0,0 +1,110 @@ +Some countries have regulations on the use of cryptographic libraries +like the ones embedded in TCLink. It may be unlawful to download TCLink +in these countries. + + + TCLink v4.2.1 + PHP Implementation + copyright (C) TrustCommerce 2016 + http://www.trustcommerce.com + techsupport@trustcommerce.com + + September 13, 2016 + +I. DESCRIPTION + + TCLink is a thin client library to allow your e-commerce servers to +connect to the TrustCommerce payment gateway easily and consistently. +The protocol (which is the same across all platforms and languages) is +well-documented in the Web Developer's Guide, so please consult it for +any questions you may have about the protocol syntax itself. + + If you are on a Windows environment, do not use this client. Please +download the COM object and use the COM functions available for Windows +to access the TCLink object. See here for more information: + http://www.php.net/manual/en/ref.com.php + + TCLink was originally ported to PHP by Andrew Barnett of Data Clarity. +Thanks go to both him (for the code) and his business (for their support +of open source). + + +II. LICENSE + + TCLink for PHP is released under the GNU LGPL. Please read LICENSE +for details. + + +III. REQUIREMENTS + + You must be running PHP 4.0.4pl1 or higher. + + You need to have the OpenSSL development libraries installed. +It is recommended you use the most recent version provided by the +vendor for PCI reasons. + + + Besides the normal PHP install, you'll need the php-devel package, +which contains files needed for building PHP modules. You can tell +if this package is installed if the binary "phpize" is in your path. + + +IV. BUILDING + + At the root directory of this archive, execute the following: + + phpize + + Afterwards, type the following commands: + + ./configure --with-ssl + make + + Note 1: On some systems, the SSL library may be in a different place, such as /usr/lib64. + In that case, type the following commands instead: + + ./configure --with-ssl=/usr/lib64 + make + + If you are not sure where your SSL library is installed, contact your system administrator or hosting provider + for more information. + + If the module builds without errors and you have access to PHP at the command line, + you can test it with the following command: + + php -d extension=modules/tclink.so tctest.php + + This script will run a test transaction and print the results. + + +V. INSTALLATION + + If you have root access to the machine, you will probably want to +install TCLink as a global extension. You can do this by copying the +module library (modules/tclink.so) to your PHP extensions directory +(typically /usr/lib/php5). Your extensions directory is defined by the +"extension_dir" directive in php.ini. + + Edit php.ini (typically found in /etc) and add the following line: + + extension=tclink.so + + Restart your webserver, and all PHP scripts will have access to TCLink. + + If you can't or don't want to install the module system wide, you can +still load it manually in each script that needs to access it through +the dl() call. See the top of tctest.php for an example. + + +VI. USAGE + + The tctest.php script shows a simple example of running transactions +through the TCLink API. A more complex example is contained in +tcexample.php. For further information, please consult the TC +Developer's Guide, located in the doc subdirectory. + + The curltest.php script shows a simple example of running transactions +through the HTTPS post, as describd in the Developer's Guide. It is +provided here in as a conveinence to users who are unable to install +TCLink and have Curl available in their installation. + diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..fc9447e --- /dev/null +++ b/config.m4 @@ -0,0 +1,95 @@ +dnl $Id: config.m4,v 2.00 2001/07/26 +AC_DEFUN(SSL_LIB_CHK,[if ((test -r $i$1/libssl.so) || (test -r $i$1/libssl.dylib) || test -n "`ls $i$1/libssl.so* 2>/dev/null`"); then SSL_DIR=$i;]) + +PHP_ARG_WITH(tclink,for TCLink support, +[ --with-tclink[=DIR] Include TCLink support. DIR is the TCLink + base install directory, defaults to ../C. + Set DIR to "shared" to build as a dl, or "shared,DIR" + to build as a dl and still specify DIR.]) + +PHP_ARG_WITH(ssl,for TCLink SSL support, +[ --with-ssl[=DIR] Include TCLink SSL support. DIR is the SSL + library directory, which defaults to /usr/lib.]) + +PHP_ARG_WITH(ca-path,for CA path, +[ --with-ca-path[=DIR] ca-bundle file or CA directory]) + +if test "$PHP_TCLINK" != "no"; then + AC_DEFINE(HAVE_TCLINK,1,[ ]) + PHP_EXTENSION(tclink,$ext_shared) +fi + +dnl Need to have a way to sepcify the include dir as well +if test "$PHP_SSL" != "no"; then + for i in /lib /usr/local/lib /usr/lib $PHP_SSL; do + SSL_LIB_CHK(/) + fi + done + if test -z "$SSL_DIR"; then + AC_MSG_ERROR(Cannot find libssl.so. Please specify the installation path for SSL) + fi + + AC_DEFINE(HAVE_SSL,1,[ ]) + +fi + +if test "$PHP_CA_PATH" != "no"; then + if test "$PHP_CA_PATH" != "yes"; then + AC_MSG_NOTICE([User specified CA path: $PHP_CA_PATH]) + TCLINK_CA_PATH="$PHP_CA_PATH" + fi +fi +if test -z "$TCLINK_CA_PATH"; then + OPENSSL_CA_PATH=`openssl version -d | sed 's/[[^"]]*"\(.*\)"/\1/'` # grab whatever between the double quotes + if test -n "$OPENSSL_CA_PATH"; then + OPENSSL_CA_PATH="$OPENSSL_CA_PATH/certs" + AC_MSG_NOTICE([Openssl CA path: $OPENSSL_CA_PATH]) + + for item in \ + "$OPENSSL_CA_PATH/ca-bundle.crt" \ + "$OPENSSL_CA_PATH/ca-certificates.crt" \ + "$OPENSSL_CA_PATH/ca-bundle.trust.crt" \ + "$OPENSSL_CA_PATH/tls-ca-bundle.pem" \ + "$OPENSSL_CA_PATH" + do + AC_MSG_NOTICE([Checking '$item']) + if test -f $item || test -d $item; then + AC_MSG_NOTICE([Found '$item']) + TCLINK_CA_PATH=$item + break + fi + done + fi +fi +if test -z "$TCLINK_CA_PATH"; then + AC_MSG_NOTICE([Search from known locations]) + for item in \ + "/etc/pki/tls/certs/ca-bundle.crt" \ + "/etc/ssl/certs/ca-certificates.crt" \ + "/etc/pki/tls/certs/ca-bundle.trust.crt" \ + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" \ + "/etc/ssl/certs" + do + AC_MSG_NOTICE([Checking '$item']) + if test -f $item || test -d $item; then + AC_MSG_NOTICE([Found '$item']) + TCLINK_CA_PATH=$item + break + fi + done +fi +if test -z "$TCLINK_CA_PATH"; then + AC_MSG_ERROR([Cannot determine CA path. Please use --with-ca-path= to specify ca-bundle file or CA directory.]) +fi + +TCLINK_SHARED_LIBADD="-lssl -lcrypto -L$SSL_DIR"; +CFLAGS="-I$SSL_DIR/../include" +TCLINK_VERSION="4.2.1-PHP-`uname -sm | tr ' ' -`" +AC_DEFINE_UNQUOTED([TCLINK_VERSION], "$TCLINK_VERSION", [TCLink version string.]) + +AC_MSG_NOTICE([CA path: $TCLINK_CA_PATH]) +AC_DEFINE_UNQUOTED([TCLINK_CA_PATH], "$TCLINK_CA_PATH", [CA path]) + +PHP_SUBST(TCLINK_INCLUDE) +PHP_SUBST(TCLINK_SHARED_LIBADD) + diff --git a/curltest.php b/curltest.php new file mode 100644 index 0000000..de2044b --- /dev/null +++ b/curltest.php @@ -0,0 +1,47 @@ + $value) + { + if ($use_amp) $post_string .= '&'; + $post_string .= "$key=$value"; + $use_amp = 1; + } + $curl_object = curl_init('https://vault.trustcommerce.com/trans/'); + curl_setopt($curl_object, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl_object, CURLOPT_POST, 1); + curl_setopt($curl_object, CURLOPT_POSTFIELDS, $post_string); + + $unformatted_results = curl_exec($curl_object); + curl_close($curl_object); + $result_array = explode("\n", $unformatted_results); + $tclink_results = array(); + foreach ($result_array as $key => $value) + { + $key_pair = explode('=', $value); + if (count($key_pair) == 2) + { + $tclink_results[$key_pair[0]] = $key_pair[1]; + } + } + return $tclink_results; + } + + $transaction = array( + 'custid' => 'XXXX', + 'password' => 'PPPP', + 'action' => 'preauth', + 'amount' => '100', + 'cc' => '4111111111111111', + 'exp' => '1213', + 'address1' => '123 Anywhere St', + 'zip' => '92606' + ); + + print_r(curl_tc_send($transaction)); + +?> diff --git a/mem.c b/mem.c new file mode 100644 index 0000000..95c6be5 --- /dev/null +++ b/mem.c @@ -0,0 +1,166 @@ +/* originally from crypto/x509/by_file.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* Added code to use a memory object as opposed to a file for lookup purposes. */ + +#include + +#include +#include +#include +#include +#include + +static int by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **); +static int X509_load_cert_crl_mem(X509_LOOKUP *ctx, const char *str, int type); + +X509_LOOKUP_METHOD x509_mem_lookup = +{ + "Memory from user provided string", + NULL, + NULL, + NULL, + NULL, + by_mem_ctrl, + NULL, + NULL, + NULL, + NULL +}; + +static X509_LOOKUP_METHOD * X509_LOOKUP_mem() +{ + return (&x509_mem_lookup); +} + +static int by_mem_ctrl(X509_LOOKUP *ctx, int cmd, const char * str, long argl, char ** ret) +{ + int status = 0; + if (cmd == X509_L_FILE_LOAD) + { + if (argl == X509_FILETYPE_PEM) + return X509_load_cert_crl_mem(ctx, str, X509_FILETYPE_PEM) != 0; + } + return status; +} + +static int X509_load_cert_crl_mem(X509_LOOKUP *ctx, const char *str, int type) +{ + BIO *in = NULL; + int status = 0; + int count = 0; + int i; + X509 *x = NULL; + + if (str == NULL) return 1; + in = BIO_new(BIO_s_mem()); + if ((in == NULL) || (BIO_write(in, str, strlen(str)) != strlen(str)) || type != X509_FILETYPE_PEM) + { + /* this error isn't the same as the real file one, but it'll serve the same purpose */ + X509err(X509_F_X509_LOAD_CERT_FILE,ERR_R_SYS_LIB); + goto err; + } + + for (;;) + { + x=PEM_read_bio_X509_AUX(in,NULL,NULL,NULL); + if (x == NULL) + { + if ((ERR_GET_REASON(ERR_peek_last_error()) == + PEM_R_NO_START_LINE) && (count > 0)) + { + ERR_clear_error(); + break; + } + else + { + X509err(X509_F_X509_LOAD_CERT_FILE, + ERR_R_PEM_LIB); + goto err; + } + } + i=X509_STORE_add_cert(ctx->store_ctx,x); + if (!i) goto err; + count++; + X509_free(x); + x=NULL; + } + status=count; + +err: + if (x != NULL) X509_free(x); + if (in != NULL) BIO_free(in); + return status; + +} + +static int X509_STORE_load_mem(X509_STORE *ctx, const char *str) +{ + X509_LOOKUP *lookup; + if (!str) return 1; + + lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_mem()); + if (lookup == NULL) return 0; + if (X509_LOOKUP_ctrl(lookup,X509_L_FILE_LOAD,str,X509_FILETYPE_PEM, NULL) != 1) + return 0; + return 1; +} +int SSL_CTX_load_verify_locations_mem(SSL_CTX * ctx, const char *str) +{ + return X509_STORE_load_mem(ctx->cert_store, str); +} diff --git a/php_tclink.c b/php_tclink.c new file mode 100644 index 0000000..08756f6 --- /dev/null +++ b/php_tclink.c @@ -0,0 +1,235 @@ +/* + * TCLink PHP Module + * + * TCLink Copyright (c) 2010 TrustCommerce. + * http://www.trustcommerce.com + * techsupport@trustcommerce.com + * (949) 387-3747 + * + * PHP Port Copyright (c) 2000 + * Andrew Barnett + * 2000-11-21 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "php.h" +#include "php_config.h" + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_tclink.h" + +#if PHP_API_VERSION >= 20151012 +#include "ext/standard/info.h" +#endif + +#if HAVE_TCLINK + +zend_function_entry php_tclink_functions[] = { + PHP_FE(tclink_send, NULL) + PHP_FE(tclink_getversion, NULL) + {NULL, NULL, NULL} +}; + +zend_module_entry php_tclink_module_entry = { +#ifdef STANDARD_MODULE_HEADER + STANDARD_MODULE_HEADER, +#endif + "tclink", + php_tclink_functions, + NULL, + NULL, + NULL, + NULL, + PHP_MINFO(tclink), + NO_VERSION_YET, +#if PHP_API_VERSION < 20090626 + STANDARD_MODULE_PROPERTIES +#else + 0, + NULL, + NULL, + NULL, + NULL, + STANDARD_MODULE_PROPERTIES_EX +#endif +}; + +#define PHP_TCLINK_DEFAULT_BUFFER_SIZE 8196 + +#ifdef COMPILE_DL_TCLINK +ZEND_GET_MODULE(php_tclink) +#endif + +PHP_MINFO_FUNCTION(tclink) +{ + char *tmp = (char *)malloc(1024); + php_info_print_table_start(); + if(tmp == NULL) { + php_info_print_table_row(2, "TCLink PHP Module", "enabled"); + } else { + php_info_print_table_row(2, "TCLink PHP Module", TCLinkGetVersion(tmp)); + free(tmp); + } + php_info_print_table_end(); +} + +/* {{{ proto void tclink_send(array params) + Send the transaction in for processing. */ +PHP_FUNCTION(tclink_send) +{ +/* start with a clean break using PHP7 APIs */ +#if PHP_API_VERSION >= 20151012 + zval *arr; + TCLinkHandle tclink; + HashTable *hash; + zval *zvalue; + zend_string * zkey; + char buf[4096]; + char *key, *value, *next_key; + + if (ZEND_NUM_ARGS() != 1) + { + WRONG_PARAM_COUNT; + } + + /* initialize it here so that it always has a return value is always something */ + array_init(return_value); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { + return; + } + + tclink = TCLinkCreate(); + hash = HASH_OF(arr); + + ZEND_HASH_FOREACH_STR_KEY_VAL(hash, zkey, zvalue) + /* there aren't any numeric keys in tclink parameters */ + if (ZSTR_VAL(zkey)) + { + /* for converting the numeric values in case they are provided */ + if (Z_TYPE_P(zvalue) != IS_STRING) + { + convert_to_string(zvalue); + } + TCLinkPushParam(tclink, ZSTR_VAL(zkey), Z_STRVAL_P(zvalue)); + } + ZEND_HASH_FOREACH_END(); + + /* send the transaction */ + TCLinkSend(tclink); + + /* pull out the parameters and put them in a hash */ + TCLinkGetEntireResponse(tclink, buf, sizeof(buf)); + + key = value = buf; + while (key && (value = strchr(key, '='))) + { + *value++ = 0; + next_key = strchr(value, '\n'); + if (next_key) *next_key++ = 0; + + add_assoc_string(return_value, key, value); + + key = next_key; + } + + TCLinkDestroy(tclink); +/* for older PHP 4 / PHP 5 clients */ +#else + zval **params, **zvalue; + HashTable *hash; + char *key, *value, *next_key; + + TCLinkHandle h; + char buf[4096]; + + /* check parameters */ + if((ZEND_NUM_ARGS() != 1) || + (zend_get_parameters_ex(1, ¶ms) == FAILURE)) { + WRONG_PARAM_COUNT; + } + + convert_to_array_ex(params); + + h = TCLinkCreate(); + + /* grab the hash and stuff each parameter set into TCLink */ + hash = HASH_OF(*params); + zend_hash_internal_pointer_reset(hash); + while (zend_hash_get_current_data(hash, (void **)&zvalue) == SUCCESS) + { + /* The Zend API added an extra parameter between 4.04 (sometime in + * 1999) and 4.06 (in early 2001). Assume that anything prior to + * 1/1/2001 is the older version. */ +#if PHP_API_VERSION < 20000101 + zend_hash_get_current_key(hash, &key, NULL); +#else + zend_hash_get_current_key(hash, &key, NULL, 0); +#endif + convert_to_string_ex(zvalue); + value = Z_STRVAL_PP(zvalue); + TCLinkPushParam(h, key, value); + zend_hash_move_forward(hash); + } + + /* send the transaction */ + TCLinkSend(h); + + /* pull out the parameters and put them in a hash */ + TCLinkGetEntireResponse(h, buf, sizeof(buf)); + + array_init(return_value); + + key = value = buf; + while (key && (value = strchr(key, '='))) + { + *value++ = 0; + next_key = strchr(value, '\n'); + if (next_key) *next_key++ = 0; + + add_assoc_string(return_value, key, value, 1); + + key = next_key; + } + + TCLinkDestroy(h); + + /* return_value is returned automatically, we need not explictly call a + return macro */ +#endif +} +/* }}} */ + +/* {{{ proto string tclink_getversion() + returns the API version information */ +PHP_FUNCTION(tclink_getversion) +{ + char version[1024]; + + TCLinkGetVersion(version); + +#if PHP_API_VERSION >= 20151012 + char *str = estrdup(version); + RETURN_STRING(str); +#else + RETURN_STRING(version, 1); +#endif +} +/* }}} */ + +#endif diff --git a/php_tclink.h b/php_tclink.h new file mode 100644 index 0000000..b5efd2a --- /dev/null +++ b/php_tclink.h @@ -0,0 +1,49 @@ +/* + * TCLink PHP Module + * + * TCLink Copyright (c) 2002 TrustCommerce. + * http://www.trustcommerce.com + * developer@trustcommerce.com + * (626) 744-7700 + * + * PHP Port Copyright (c) 2000 + * Andrew Barnett + * 2000-11-21 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PHP_TCLINK_H +#define PHP_TCLINK_H + +#if HAVE_TCLINK + +#include + +extern zend_module_entry php_tclink_module_entry; +#define tclink_module_ptr &php_tclink_module_entry + +PHP_MINFO_FUNCTION(tclink); + +PHP_FUNCTION(tclink_send); +PHP_FUNCTION(tclink_getversion); + +#else +#define tclink_module_ptr NULL +#endif /* HAVE_TCLINK */ + +#define phpext_tclink_ptr tclink_module_ptr + +#endif /* PHP_TCLINK_H */ diff --git a/tcexample.php b/tcexample.php new file mode 100644 index 0000000..88ebb45 --- /dev/null +++ b/tcexample.php @@ -0,0 +1,101 @@ + + +TCLink PHP Example + + +
+ + + + + + + + + + + + + + + + + "; + print ""; + + if ($result['transid']) + printf("\n", $result['transid']); + + printf("\n", $result['status']); + switch($result['status']) + { + case 'accepted': + case 'approved': + break; + + case 'decline': + case 'rejected': + printf("\n", $result['declinetype']); + break; + + case 'error': + printf("\n", $result['errortype']); + break; + + case 'baddata': + printf("\n", htmlspecialchars($result['$offenders'])); + break; + } + + print ""; + + while(list($key, $value) = each($result)) + printf("\n", htmlspecialchars($key), htmlspecialchars($value)); + } + ?> + +
+ TrustCommerce PHP Example - TCLink ver. ) +
CustID:
Password:
Action:
Amount (in cents):
Sales and Pre-Authorizations Only:
Card Number:
Expiration: + +
+
Cardholder Name:
Credits and Post-Authorizations Only:
Transaction ID:

Transaction Results:
Transaction ID:%s
Status:%s
Decline Type:%s
Error Type%s
Offenders:%s
All Results:
%s%s
+
+ + + diff --git a/tclink.c b/tclink.c new file mode 100644 index 0000000..9510798 --- /dev/null +++ b/tclink.c @@ -0,0 +1,1017 @@ +/* tclink.c - Library code for the TCLink client API. + * + * TCLink Copyright (c) 2013 TrustCommerce. + * http://www.trustcommerce.com + * techsupport@trustcommerce.com + * (949) 387-3747 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tclink.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#define OPENSSL_NO_KRB5 1 + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#define strcasecmp(x,y) stricmp(x,y) +#else +#define closesocket(x) close(x) +#endif + +#define DEFAULT_HOST "pgw1.trustcommerce.com" + +/* changed from forty second to one hundred second to reflect more complicated transaction processing logic */ +#define TIMEOUT 100 /* seconds */ +#define TC_BUFF_MAX 16000 +#define TC_LINE_MAX ((PARAM_MAX_LEN * 2) + 2) + +char *tclink_version = TCLINK_VERSION; /* TCLINK_VERSION is defined in Makefile */ +char *tclink_host = DEFAULT_HOST; +int tclink_port = 443; + +/*************************************************/ +/* Data structures used only within this module. */ +/*************************************************/ + +/* Variables used for transaction data. */ + +typedef struct param_data +{ + char *name; + char *value; + struct param_data *next; +} param; + +typedef struct _TCLinkCon +{ + /* Connection data */ + int *ip; + int num_ips; + int sd; + + /* SSL encryption */ + const SSL_METHOD *meth; + long ctx_options; + SSL_CTX *ctx; + SSL *ssl; + + /* Transaction parameters, sent and received */ + param *send_param_list, *send_param_tail; + param *recv_param_list; + + /* Connection status */ + int is_error; + int pass; + time_t start_time; + int dns; + + char * trusted_ca_pem; + int (*validate_cert)(int, void *); + int full_ssl_close; + +} TCLinkCon; + +/************************************* + * Internal functions, not exported. * + *************************************/ + +/* Random number from min to max. */ +static int number(int min, int max) +{ + return (rand() % (max - min + 1)) + min; +} + +/* Check if path points to a regular file */ +int is_regular_file(const char* path) +{ + struct stat st; + stat(path, &st); + return S_ISREG(st.st_mode); +} + +/* Safe string copy and append functions. */ +#define SAFE_COPY(d, s) safe_copy((d), (s), sizeof(d)); +#define SAFE_APPEND(d, s) safe_append((d), (s), sizeof(d)); + +static void safe_copy(char *dst, const char *src, int size) +{ + int len = strlen(src); + if (len < size) + strcpy(dst, src); + else { + strncpy(dst, src, size - 1); + dst[size-1] = 0; + } +} + +static void safe_append(char *dst, const char *src, int size) +{ + int dlen = strlen(dst); + int slen = strlen(src); + int avail = size - dlen; + if (avail < 1) + return; + + if (slen < avail) + strcpy(dst+dlen, src); + else { + strncpy(dst+dlen, src, avail - 1); + dst[size-1] = 0; + } +} + +/* Add a parameter-value pair to the recieved list. */ +static void AddRecvParam(TCLinkCon *c, const char *name, const char *value) +{ + param *p; + + if (name[0] == 0 || value[0] == 0) + return; + + p = (param *)malloc(sizeof(param)); + p->name = strdup(name); + p->value = strdup(value); + p->next = c->recv_param_list; + c->recv_param_list = p; +} + +/* Add a string to the received list. */ +static int AddRecvString(TCLinkCon *c, char *string) +{ + char *ptr = strchr(string, '='); + if (ptr == NULL) + return 0; + + *ptr = 0; + AddRecvParam(c, string, ptr+1); + + return 1; +} + +/* Deallocate the send list. */ +static void ClearSendList(TCLinkCon *c) +{ + param *p, *next; + for (p = c->send_param_list; p; p = next) + { + next = p->next; + free(p->name); + free(p->value); + free(p); + } + + c->send_param_list = c->send_param_tail = NULL; +} + +/* Deallocate the recv list. */ +static void ClearRecvList(TCLinkCon *c) +{ + param *p, *next; + for (p = c->recv_param_list; p; p = next) + { + next = p->next; + free(p->name); + free(p->value); + free(p); + } + + c->recv_param_list = NULL; +} + +/* Open a socket to the host_ip specified. Returns the socket's file + * descriptor on success (the open attempt is underway) or -1 for failure + * (should never happen in practice). Note that this function DOES NOT block + * and wait for the connection; you'll need to select() on the socket later to see + * if it opened successfully. + */ +static int BeginConnection(TCLinkCon *c, int host_ip) +{ + struct sockaddr_in sa; + int sd; + + sd = socket(AF_INET, SOCK_STREAM, 0); + if (sd < 0) + return -1; + +#ifdef WIN32 + u_long param = 1; + ioctlsocket(sd, FIONBIO, ¶m); +#else + fcntl(sd, F_SETFL, O_NONBLOCK); +#endif + + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = host_ip; + sa.sin_port = htons(tclink_port); + + connect(sd, (struct sockaddr *) &sa, sizeof(sa)); + + return sd; +} + +/* This function is called on a socket file descriptor once the connection has been + * established and we're ready to negotiate SSL. If the SSL handshake fails for some + * reason (such as the host on the other end not using SSL), it will return 0 for + * failure. Success returns 1. + */ +static int FinishConnection(TCLinkCon *c, int sd) +{ + int ssl_connected, is_error, errcode, res; + X509 *server_cert; + time_t start, remaining; + fd_set in, out, err; + struct timeval tv; + + /* check if socket has connected successfully */ + int val; + int /*socklen_t*/ size = 4; + getsockopt(sd, SOL_SOCKET, SO_ERROR, (char*)&val, &size); + if (val != 0) + return 0; + + SSL_clear(c->ssl); + + SSL_set_fd(c->ssl, sd); + + ssl_connected = 0; + is_error = 0; + start = time(0); + + while (!ssl_connected && !is_error) + { + + remaining = 5 - (time(0) - start); + if (remaining <= 0) { + is_error = 1; + break; + } + + res = SSL_connect(c->ssl); + + ssl_connected = ((res == 1) && SSL_is_init_finished(c->ssl)); + + if (!ssl_connected) + { + FD_ZERO(&in); FD_SET((unsigned)sd, &in); + FD_ZERO(&out); FD_SET((unsigned)sd, &out); + FD_ZERO(&err); FD_SET((unsigned)sd, &err); + /* the documentation does not suggest that both error types occur at the same time so + * the retry logic will consume all the outstanding events + * we do not actually use oob data, but if it is sent, it is treated as an error all the + * same + */ + errcode = SSL_get_error(c->ssl, res); + switch (errcode) + { + case SSL_ERROR_NONE: + /* no error, we should have a connection, check again */ + break; + + case SSL_ERROR_WANT_READ: + /* no error, just wait for more data */ + tv.tv_sec = remaining; tv.tv_usec = 0; + /* posix-2001 says the function will modify the appropriate descriptors */ + if (select(sd+1, &in, NULL, &err, &tv) < 0 || + FD_ISSET((unsigned)sd, &err) + ) + is_error = 1; + break; + case SSL_ERROR_WANT_WRITE: + /* no error, just wait for more data */ + tv.tv_sec = remaining; tv.tv_usec = 0; + if (select(sd+1, NULL, &out, &err, &tv) < 0 || + FD_ISSET((unsigned)sd, &err) + ) + is_error = 1; + break; + case SSL_ERROR_ZERO_RETURN: /* peer closed the connection */ + case SSL_ERROR_SSL: /* error in SSL handshake */ + default: + is_error = 1; + } + } + } + + if (is_error) { + return 0; + } + + +#ifdef WIN32 + u_long param = 0; + ioctlsocket(sd, FIONBIO, ¶m); // make the socket blocking again +#else + fcntl(sd, F_SETFL, 0); /* make the socket blocking again */ +#endif + + /* verify that server certificate is authentic */ + server_cert = SSL_get_peer_certificate(c->ssl); + if (!server_cert) { + return 0; + } + if (c->validate_cert && c->validate_cert(0, server_cert) != 0) + { + X509_free(server_cert); + return 0; + } + X509_free(server_cert); + + return 1; +} + +/* This function should be called on list of socket file descriptors (sd) to determine + * if any have opened successfully. If so, it will return which one (index into + * the array). Otherwise it returns -1 if none have successfully opened. + * This function will block for a maximum of 3 seconds. + * As this function calls FinishConnection(), you shouldn't need to do anything special + * after it returns success - the socket is set up and ready for use. + */ +static int CheckConnection(TCLinkCon *c, int *sd, int num_sd) +{ + fd_set wr_set, err_set; + struct timeval tv; + int max_sd = -1, i; + + tv.tv_sec = 3; /* wait 3 seconds for soc->mething to happen */ + tv.tv_usec = 0; + + /* build the fd_sets used for select() */ + FD_ZERO(&wr_set); + FD_ZERO(&err_set); + for (i = 0; i < num_sd; i++) + { + if (sd[i] < 0) continue; + FD_SET(sd[i], &wr_set); + FD_SET(sd[i], &err_set); + if (sd[i] > max_sd) + max_sd = sd[i]; + } + + /* run the select and see what we have waiting for us */ + if (select(max_sd + 1, NULL, &wr_set, &err_set, &tv) < 1) + return -1; /* I hope this never happens */ + + for (i = 0; i < num_sd; i++) + if (sd[i] >= 0) + { + if (FD_ISSET(sd[i], &err_set)) + { + /* error - close the socket and mark it defunct */ + close(sd[i]); + sd[i] = -1; + } + else if (FD_ISSET(sd[i], &wr_set)) + { + /* socket has opened! try to negotiate SSL */ + if (FinishConnection(c, sd[i])) { + /* socket is ready to go, so return success */ + c->sd = sd[i]; + return i; + } + else { + /* SSL handshake had errors, close the socket and mark it defunct */ + close(sd[i]); + sd[i] = -1; + } + } + } + + /* if we get here, nothing much interesting happened during those 3 seconds */ + return -1; +} + +void do_SSL_randomize() +{ + enum { RAND_VALS = 32 }; + int randbuf[RAND_VALS]; + char fname[512]; + int use_rand_file; + time_t t; + int i, c; + + /* if they have a /dev/urandom we can skip this function */ + if (RAND_status() != 0) + return; + + t = time(0); + RAND_seed((char *)&t, sizeof(time_t)); + + /* have they specified a random file with RANDFILE environment variable? */ + use_rand_file = RAND_file_name(fname, sizeof(fname)) ? 1 : 0; + if (use_rand_file) + RAND_load_file(fname, 4096); + + /* stuff it with packets of random numbers until it is satisfied */ + for (i = 0; i < 256 && RAND_status() == 0; i++) + { + for (c = 0; c < RAND_VALS; c++) + randbuf[c] = rand(); + RAND_seed((char *)randbuf, sizeof(int) * RAND_VALS); + } +} + +/* Open a connection to one of the TrustCommerce gateway servers. */ +static int Connect(TCLinkCon *c, int host_hash) +{ + struct hostent default_he; + char *addr_list[3]; int addr[2]; + struct hostent *he; + unsigned int **gw; + + enum { MAX_HOSTS = 32 }; + time_t last_connect[MAX_HOSTS]; + int sd[MAX_HOSTS]; + int num_sd = 0; + int host; + + int i, j, sort, sort_val; + + c->sd = -1; + c->is_error = 0; + + srand(time(0)); + + /* These are used as BACKUP ONLY if the DNS if offline. */ + addr[0] = inet_addr("207.38.46.42"); + addr[1] = inet_addr("208.42.227.151"); + addr_list[0] = (char *)&addr[0]; + addr_list[1] = (char *)&addr[1]; + addr_list[2] = 0; + default_he.h_addr_list = addr_list; + + /* determine IP addresses of gateway */ + if (!c->ip) + { + he = gethostbyname(tclink_host); + if (he) + c->dns = 1; + else { + /* fall back to hardcoded IPs in an emergency */ + c->dns = 0; + he = &default_he; + } + + for (c->num_ips = 0; he->h_addr_list[c->num_ips]; c->num_ips++) + ; + + c->ip = (int *)malloc(c->num_ips * sizeof(int)); + gw = (int unsigned **)he->h_addr_list; + + /* sort the IP address list before storing it */ + for (i = 0; i < c->num_ips; i++) + { + sort = 0; sort_val = *gw[0]; + for (j = 1; j < c->num_ips; j++) + if (*gw[j] > sort_val) + { + sort = j; + sort_val = *gw[j]; + } + + c->ip[i] = sort_val; + *gw[sort] = 0; + } + } + + /* do some SSL setup */ + if (!c->meth) + { + do_SSL_randomize(); /* handle systems without /dev/urandom */ + SSLeay_add_ssl_algorithms(); + c->meth = SSLv23_client_method(); + c->ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; // Disable all known SSL versions + } + + if (!c->ctx) + { + int val; + + c->ctx = SSL_CTX_new(c->meth); + if (!c->ctx) return 0; + /* set options */ + if (c->ctx_options) + SSL_CTX_set_options(c->ctx, c->ctx_options); + + if (!c->trusted_ca_pem) + { + int is_file = is_regular_file(TCLINK_CA_PATH); + val = SSL_CTX_load_verify_locations(c->ctx, is_file?TCLINK_CA_PATH:NULL, is_file?NULL:TCLINK_CA_PATH); + } + else + { + extern int SSL_CTX_load_verify_locations_mem(SSL_CTX*, const char *); + val = SSL_CTX_load_verify_locations_mem(c->ctx, c->trusted_ca_pem); + } + + if (!val) return 0; // failed to populate cert store + + /* turn on certificate chain validation */ + SSL_CTX_set_verify(c->ctx, SSL_VERIFY_PEER, NULL); + } + + if (!c->ssl) + { + c->ssl = SSL_new(c->ctx); + if (!c->ssl) + { + SSL_CTX_free(c->ctx); + return 0; + } + } + + /* This loop works as follows: + * Grab the first host. Try to open a connection to it. If there was an + * error (host down or unreachable) go to the next one. If nothing has happened + * after 3 seconds, open a second socket (the first one is still open!) and try + * with the next fail-over host. Continue to do this for a maximum of MAX_HOSTS + * sockets, or until our TIMEOUT value runs out. We also keep track of how recently + * we tried to connect to a given host, so that we avoid saturating the machines + * in a heavy-load situation (which could be caused by anything from heavy internet + * lag between the local host and the TrustCommerce servers, to heavy load on the + * servers themselves due to half a million people trying to run credit card + * transactions in the same half second - unlikely, but certainly possible.) + */ + c->start_time = time(0); + c->pass = 1; + memset(last_connect, 0, MAX_HOSTS * sizeof(time_t)); + host = host_hash % c->num_ips; + + for ( ; time(0) < (c->start_time + TIMEOUT); c->pass++) + { + /* retry the first host at least once */ + if (c->pass > 2) host += 1; + if (host >= c->num_ips) host = 0; + + /* only connect if we haven't tried this host before, or it's been a little + * while (note random modifier to help stagger network traffic) */ + if (last_connect[host] == 0 || + (time(0) - last_connect[host]) >= number(TIMEOUT / 4, TIMEOUT)) + { + if (num_sd < MAX_HOSTS) + { + /* fire up a new connection to this host */ + if (c->pass != 1) + last_connect[host] = time(0); + + sd[num_sd] = BeginConnection(c, c->ip[host]); + if (sd[num_sd] >= 0) + num_sd++; + } + } + + /* scan all current sockets and see if we've made a successful connection + * somewhere. note that this also includes SSL and all that sort of fun, + * so once it returns success, we're all done. */ + if (num_sd > 0) + { + if (CheckConnection(c, sd, num_sd) >= 0) + { + /* Success: close all other file handles and return */ + for (i = 0; i < num_sd; i++) + if (sd[i] >= 0 && sd[i] != c->sd) + close(sd[i]); + + return 1; + } + } + + usleep(1000); // sleep for 1 millisecond + } + + return 0; +} + +/* Send a chunk of data through a connection previously opened with Connect(). */ +static int Send(TCLinkCon *c, const char *string) +{ + if (SSL_write(c->ssl, string, strlen(string)) < 0) + return 0; + + return 1; +} + +/* Peel a line off the current input. Note that this DOESN'T necessarily wait for all + * input to come in, only up to a "\n". -1 is returned for a network error, otherwise + * it returns the length of the line read. If there is not a complete line pending + * for read this will block until there is, or an error occurs. + */ +static int ReadLine(TCLinkCon *c, char *buffer, char *destbuf) +{ + struct timeval tv; + fd_set read; + fd_set error; + int sel; + + while (1) /* we wait for a line to come in or an error to occur */ + { + char *eol = strchr(buffer, '\n'); + if (eol != NULL) + { + /* peel off the line and return it */ + *eol++ = 0; + safe_copy(destbuf, buffer, TC_LINE_MAX); + memmove(buffer, eol, strlen(eol)+1); + return strlen(destbuf); + } + else + { + if (c->is_error == 1) + return -1; + + /* do socket work to grab the most recent chunk of incoming data */ + FD_ZERO(&read); FD_SET(c->sd, &read); + FD_ZERO(&error); FD_SET(c->sd, &error); + tv.tv_sec = TIMEOUT; + tv.tv_usec = 0; + + sel = select(c->sd+1, &read, NULL, &error, &tv); + if (sel < 1) + c->is_error = 1; + else if (FD_ISSET(c->sd, &error)) + c->is_error = 1; + else if (FD_ISSET(c->sd, &read)) + { + int buffer_end = strlen(buffer); + int size = SSL_read(c->ssl, buffer + buffer_end, TC_BUFF_MAX-1 - buffer_end); + if (size == 0) + { + int error_type = SSL_get_error(c->ssl, size); + switch (error_type) + { + /* this would never happen in practice */ + case SSL_ERROR_NONE: + /* this wouldn't happen either because the ssl transport is blocking */ + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + buffer[buffer_end] = 0; + break; + + /* these others should not really happen but if they do, we bail */ + /* we would never get any more data and it looks like the callee is expecting something */ + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_WANT_CONNECT: + case SSL_ERROR_WANT_ACCEPT: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_WANT_X509_LOOKUP: + case SSL_ERROR_SSL: + default: + c->is_error = 1; + break; + } + } + else if (size < 0) + c->is_error = 1; + else + buffer[buffer_end + size] = 0; + } + } + } +} + +/* Closes a connection opened with Connect() and frees memory associated with it. + * You ONLY need to Close() connections which opened successfully; those that don't + * clean up after themselves before Connect() returns. + */ +static int Close(TCLinkCon *c) +{ + if (c->ssl) + { + /* The full shutdown presented here is more for completeness than necessity; at this point in the + * application, we have already received the end trailer (or bust) which is generally accompanied by + * a close notify message. If the software chooses to respond to the close notify (per TLS specification) + * this would result in at least reading the incoming close notify and issuing our own. Because this entails + * an additional round trip that is not needed (the transaction is done after the accompanying END), there + * does not appear to be a benefit to it at all. By default though, this configuration is enabled and + * can be disabled by the integrator for performance reasons. + */ + if (c->full_ssl_close) + { + int status = SSL_shutdown(c->ssl); + if (status == 0) status = SSL_shutdown(c->ssl); + } + else + SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); + } + + if (c->sd >= 0) { + close(c->sd); + c->sd = -1; + } + + if (c->trusted_ca_pem) { + free(c->trusted_ca_pem); + c->trusted_ca_pem = NULL; + } + + return 1; +} + +static void stuff_string(char *buf, int *len, int size, const char *add) +{ + int newlen = strlen(add); + if ((*len + newlen) >= size) + newlen = size - *len - 1; + if (newlen < 1) return; + strncpy(buf + *len, add, newlen); + *len += newlen; + buf[*len] = 0; +} + +/********************************************** + * API functions exported to the user client. * + **********************************************/ + +TCLinkHandle TCLinkCreate() +{ + extern int TCLinkDefaultValidate(int, void *); + + TCLinkCon *c = (TCLinkCon *)malloc(sizeof(TCLinkCon)); + + c->ip = NULL; + c->num_ips = 0; + c->sd = -1; + + c->meth = NULL; + c->ctx = NULL; + c->ssl = NULL; + + c->send_param_list = NULL; + c->send_param_tail = NULL; + c->recv_param_list = NULL; + + c->is_error = 0; + c->pass = 0; + c->start_time = 0; + c->dns = -1; + + c->trusted_ca_pem = NULL; + c->validate_cert = TCLinkDefaultValidate; + c->full_ssl_close = 1; + + return (TCLinkHandle)c; +} + +int TCLinkSetFullClose(TCLinkHandle handle, int full_ssl_close) +{ + TCLinkCon *c = (TCLinkCon *)handle; + int swap = c->full_ssl_close; + c->full_ssl_close = full_ssl_close ? 1 : 0; + return swap; +} + +void TCLinkSetTrustedCABundle(TCLinkHandle handle, const char *str, int len) +{ + TCLinkCon *c = (TCLinkCon *)handle; + + if (c->trusted_ca_pem) + free(c->trusted_ca_pem); + + if (str == NULL) + { + c->trusted_ca_pem = NULL; + return; + } + + c->trusted_ca_pem = malloc(len+1); + strncpy(c->trusted_ca_pem,str,len); + c->trusted_ca_pem[len] = 0; +} + +void TCLinkSetValidateCallback(TCLinkHandle handle, int (*validate_cert)(int, void *)) +{ + TCLinkCon *c = (TCLinkCon *)handle; + if (validate_cert == NULL) + { + extern int TCLinkDefaultValidate(int, void *); + c->validate_cert = TCLinkDefaultValidate; + } + else + c->validate_cert = validate_cert; +} + +void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value) +{ + param *p; + char *ch; + + TCLinkCon *c = (TCLinkCon *)handle; + + if (name && value) + { + p = (param *)malloc(sizeof(param)); + p->name = strdup(name); + p->value = strdup(value); + p->next = NULL; + if (c->send_param_tail) + c->send_param_tail->next = p; + else + c->send_param_list = p; + c->send_param_tail = p; + + /* remove newlines and equals signs from the parameter name */ + for (ch = p->name; *ch; ch++) + if (*ch == '=' || *ch == '\n') *ch = ' '; + + /* remove newlines from the value */ + for (ch = p->value; *ch; ch++) + if (*ch == '\n') *ch = ' '; + } +} + +void TCLinkSend(TCLinkHandle handle) +{ + param *p, *next; + char buf[TC_BUFF_MAX], destbuf[TC_LINE_MAX]; + char buf2[1024]; + int host_hash = 1; + int retval = 0; + + TCLinkCon *c = (TCLinkCon *)handle; + + ClearRecvList(c); + + /* build most of the string we will send to the processor */ + sprintf(buf, "BEGIN\nversion=%s\n", tclink_version); + + for (p = c->send_param_list; p; p = next) + { + next = p->next; + SAFE_COPY(buf2, p->name); + SAFE_APPEND(buf2, "="); + SAFE_APPEND(buf2, p->value); + SAFE_APPEND(buf2, "\n"); + SAFE_APPEND(buf, buf2); + if (!strcasecmp(p->name, "custid")) { + host_hash = atoi(p->value); + host_hash = (host_hash / 100) + (host_hash % 100); + } + free(p->name); + free(p->value); + free(p); + } + + c->send_param_list = c->send_param_tail = NULL; + + /* try to make the connection */ + if (!Connect(c, host_hash)) + { + Close(c); /* clean up any memory Connect() may have left lying around */ + AddRecvParam(c, "status", "error"); + AddRecvParam(c, "errortype", "cantconnect"); + return; + } + + /* append some data about the connection */ + sprintf(buf+strlen(buf), "pass=%d\ntime=%ld\n", c->pass, time(0) - c->start_time); + if (c->dns != 1) SAFE_APPEND(buf, "dns=n\n"); + SAFE_APPEND(buf, "END\n"); + + /* send the data */ + if (Send(c, buf)) + { + int state = 0; + buf[0] = destbuf[0] = 0; /* recycle buf */ + c->is_error = 0; + while (1) + { + int len = ReadLine(c, buf, destbuf); + if (len == 0) continue; + if (len < 0) break; + if (strcasecmp(destbuf, "BEGIN") == 0) + { + if (state != 0) + { state = -1; break; } + state = 1; + } + else if (strcasecmp(destbuf, "END") == 0) + { + state = (state != 1) ? -1 : 2; + break; + } + else + { + if (state != 1 || !AddRecvString(c, destbuf)) + { state = -1; break; } + } + } + if (state == 2) + retval = 1; + } + + Close(c); + + if (!retval) + { + ClearRecvList(c); + AddRecvParam(c, "status", "error"); + AddRecvParam(c, "errortype", "linkfailure"); + } +} + +char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value) +{ + param *p; + TCLinkCon *c = (TCLinkCon *)handle; + + for (p = c->recv_param_list; p; p = p->next) + if (strcasecmp(name, p->name) == 0) + { + safe_copy(value, p->value, PARAM_MAX_LEN); + return value; + } + + return NULL; +} + +char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size) +{ + param *p; + int len = 0; + TCLinkCon *c = (TCLinkCon *)handle; + + for (p = c->recv_param_list; p; p = p->next) { + stuff_string(buf, &len, size, p->name); + stuff_string(buf, &len, size, "="); + stuff_string(buf, &len, size, p->value); + stuff_string(buf, &len, size, "\n"); + } + + return buf; +} + +void TCLinkDestroy(TCLinkHandle handle) +{ + TCLinkCon *c = (TCLinkCon *)handle; + if (!c) return; + + ClearSendList(c); + ClearRecvList(c); + Close(c); + + if (c->ip) + free(c->ip); + + if (c->ssl) { + SSL_free(c->ssl); + c->ssl = NULL; + } + + if (c->ctx) { + SSL_CTX_free(c->ctx); + c->ctx = NULL; + } + + free(c); +} + +char *TCLinkGetVersion(char *buf) +{ + strcpy(buf, tclink_version); + return buf; +} + diff --git a/tclink.h b/tclink.h new file mode 100644 index 0000000..83862ef --- /dev/null +++ b/tclink.h @@ -0,0 +1,83 @@ +/* tclink.h - Header file for TCLink library. + * + * TCLink Copyright (c) 2013 TrustCommerce. + * http://www.trustcommerce.com + * techsupport@trustcommerce.com + * (949) 387-3747 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TCLINK_H +#define _TCLINK_H + +#include "config.h" + +/* Handle passed to all TCLink functions. A unique handle must be created + * for each concurrent thread, but the same handle can be shared by transactions + * occurring one after another (such as a for loop). + */ +#define TCLinkHandle void * + +/* Parameter names and values cannot exceed this size. + */ +#define PARAM_MAX_LEN 256 + +/* Create a new TCLinkHandle. + */ +TCLinkHandle TCLinkCreate(); + +/* Add a parameter to be sent to the server. + */ +void TCLinkPushParam(TCLinkHandle handle, const char *name, const char *value); + +/* Flush the parameters to the server. + */ +void TCLinkSend(TCLinkHandle handle); + +/* Look up a response value from the server. + * Returns NULL if no such parameter, or stores the value in 'value' and + * returns a pointer to value. value should be at least PARAM_MAX_LEN in size. + */ +char *TCLinkGetResponse(TCLinkHandle handle, const char *name, char *value); + +/* Get all response values from the server in one giant string. + * Stores the string into buf and returns a pointer to it. Size should be + * sizeof(buf), which will limit the string so that no buffer overruns occur. + */ +char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size); + +/* Destory a handle, ending that transaction and freeing the memory associated with it. */ +void TCLinkDestroy(TCLinkHandle handle); + +/* Store version string into buf. Returns a pointer to buf. */ +char *TCLinkGetVersion(char *buf); + + +/* The API methods below are subject to change. */ + +/* Enables (1) or Disables (0) the full SSL close_notify sequence. By default, this is set to 1.*/ +int TCLinkSetFullClose(TCLinkHandle handle, int full_ssl_close); + +/* Provides a method, before the first send call is initiated, of loading a set of trusted CA certificates (PEM format). */ +void TCLinkSetTrustedCABundle(TCLinkHandle handle, const char *str, int len); + +/* Provides a method, once the handshake is completed, a means to verify the contents of that certificate independently. + * Note that the certificate may not be set depending on the negotation type (in which case the pointer would be NULL) + */ +void TCLinkSetValidateCallback(TCLinkHandle handle, int (*validate_cert)(int, void *)); + +#endif + diff --git a/tctest.php b/tctest.php new file mode 100644 index 0000000..1edab30 --- /dev/null +++ b/tctest.php @@ -0,0 +1,34 @@ + + diff --git a/validate.c b/validate.c new file mode 100644 index 0000000..3c7dcc5 --- /dev/null +++ b/validate.c @@ -0,0 +1,154 @@ +/* +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2010, Daniel Stenberg, . + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. +*/ +/* simplified to a basic host name check */ +#include +#include +#include + +#define bool int +#define false 0 +#define true 1 +/** @fn static bool cert_hostcheck(const char *hostname, char *pattern) + * Verifies that the hostname matches against the pattern specified. + * Handles wildcard patterns and ignores the distinction between upper and lower case letters. + * Note: Ported over from ssluse.c in curl (7.1.16) lib + * Note: Explicit pattern match disabled as we do not use that for processing node certificate. + * Note: No longer ignores the distinction between upper and lower case letters. Our certificate is generated with lowercase letters. + * @return true if matches, false otherwise + * @param hostname The hostname we want to check. e.g: vault.trustcommerce.com + * @param pattern The pattern we wish to match against. e.g: *.trustcommerce.com + */ +bool cert_hostcheck(const char *pattern, const char *hostname) +{ + if (!hostname || !pattern || !*hostname || !*pattern) return false; + if (!strcmp(hostname,pattern)) return true; + return false; +} +/** @fn static bool checkCertificate(X509 *cert, char *host) + * Provides validation of the hostname associated with a certificate. + * See RFC2818 - Server Identity for an overview of the concept. + * This implementation is based off the one found in curl-7.16.1: ssluse.c + * but we treat the subjectAltName as a recommendation... so if it fails, + * we will proceed to the CN check. + * The rationale for this is that we are not always using HTTP (over SSL) + * and its more of a certification generation / CA issue and we want + * maximum interoperability (as opposed to strict compliance). + * @param cert The X509 certificate in question. + * @param host The hostname or ip we wish to check. + * @return true if matches, false otherwise + */ +static bool checkCertificate(X509 * cert, const char *host) +{ + int i,j; + bool matched = false; + STACK_OF(GENERAL_NAME) * altnames; + unsigned char *nulstr = { '\0' }; + unsigned char *peer_CN = nulstr; + X509_NAME *name; + ASN1_STRING * tmp; + bool status = false; + + if (!cert || !host) return false; + + altnames = (STACK_OF(GENERAL_NAME) *)(X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL)); + + if (altnames != NULL) + { + int numalts = sk_GENERAL_NAME_num(altnames); + for (i=0; (id.ia5)); + size_t altlen; + switch (check->type) + { + case GEN_DNS: + altlen = ASN1_STRING_length(check->d.ia5); + if (altlen == strlen(host) && cert_hostcheck(altptr, host)) + matched = true; + break; + case GEN_IPADD: + altlen = ASN1_STRING_length(check->d.ia5); + if (altlen == strlen(host) && !memcmp(altptr, host, altlen)) + matched = true; + break; + } + } + GENERAL_NAMES_free(altnames); + if (matched != false) return true; + } + + i = j = -1; + + + name = X509_get_subject_name(cert); + if (!name) return false; + + + // get the last CN found in the subject (supposedly its the most distinguished one) + while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0) + i=j; + + if (i<0) return false; + + tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); + /* workaround for version of openssl < 0.9.7d */ + if (tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) + { + j = ASN1_STRING_length(tmp); + if (j >= 0) { + peer_CN = (unsigned char *)(OPENSSL_malloc(j+1)); + if (peer_CN) + { + memcpy(peer_CN, ASN1_STRING_data(tmp), j); + peer_CN[j] = '\0'; + } + } + } + else + { + j = ASN1_STRING_to_UTF8(&peer_CN, tmp); + } + + if (peer_CN == nulstr) + peer_CN = NULL; + + if (peer_CN == NULL) + return false; // the cn isnt missing in virtually all cases + else if(!cert_hostcheck((char *)(peer_CN), host)) + status = false; + else + status = true; + + if (peer_CN) + OPENSSL_free(peer_CN); + return status; +} + +int TCLinkDefaultValidate(int x, void * cert) +{ + if (x != 0 || cert == NULL) return 0; + return !checkCertificate((X509 *)cert, "pgw1.trustcommerce.com"); + +} -- 2.25.1