From 8db0824d9296d1634a592b2c4e5e21f50db1b6d9 Mon Sep 17 00:00:00 2001 From: Johannes Findeisen Date: Tue, 13 Dec 2011 16:57:24 +0000 Subject: [PATCH] Initial commit of the PC/SC (pcsc) extension. There is some stuff that needs to be romoved like the build.sh script and the subfolders tmp/ and php/. I think the .m4 file has some bugs because it fails to build without my weird build.sh script. I will fix that ASAP. git-svn-id: https://svn.php.net/repository/pecl/pcsc/trunk@320951 c90b9560-bf6c-de11-be94-00142212c4b1 --- COPYING | 69 ++++++ CREDITS | 2 + README | 3 + TODO | 7 + build.sh | 24 ++ config.m4 | 39 ++++ pcsc.c | 536 +++++++++++++++++++++++++++++++++++++++++++++ php/pcsc.class.php | 55 +++++ php/test.php | 43 ++++ php_pcsc.h | 32 +++ tmp/pcsc2.c | 496 +++++++++++++++++++++++++++++++++++++++++ 11 files changed, 1306 insertions(+) create mode 100644 COPYING create mode 100644 CREDITS create mode 100644 README create mode 100644 TODO create mode 100755 build.sh create mode 100644 config.m4 create mode 100644 pcsc.c create mode 100644 php/pcsc.class.php create mode 100644 php/test.php create mode 100644 php_pcsc.h create mode 100644 tmp/pcsc2.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..68e03c7 --- /dev/null +++ b/COPYING @@ -0,0 +1,69 @@ +-------------------------------------------------------------------- + The PHP License, version 3.01 +Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. +-------------------------------------------------------------------- + +Redistribution and use in source and binary forms, with or without +modification, is permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + +THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND +ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP +DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. + +-------------------------------------------------------------------- + +This software consists of voluntary contributions made by many +individuals on behalf of the PHP Group. + +The PHP Group can be contacted via Email at group@php.net. + +For more information on the PHP Group and the PHP project, +please see . + +PHP includes the Zend Engine, freely available at +. + diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..b4953c5 --- /dev/null +++ b/CREDITS @@ -0,0 +1,2 @@ +pcsc +Johann Dantant, Johannes Findeisen diff --git a/README b/README new file mode 100644 index 0000000..073b7d2 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +This is totally unstable! Don't use in production environments! + +You have been warned... ;) diff --git a/TODO b/TODO new file mode 100644 index 0000000..4bc0433 --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +Some things that need to be done: + +- Find all leaks and fix them +- Write all function documentation +- Write complete API documentation +- ... + diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ced013e --- /dev/null +++ b/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +#echo "Cleaning up ...\n" + +make clean +phpize --clean + +#echo "Compile package...\n" + +phpize +aclocal +libtoolize --force +autoheader +autoconf + +#echo "Configure package...\n" + +./configure --enable-pcsc --prefix=/usr + +make + +#echo "Now type su and the make install...\n" + + diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..567292d --- /dev/null +++ b/config.m4 @@ -0,0 +1,39 @@ +dnl $Id$ +dnl config.m4 for PC/SC extension + +PHP_ARG_ENABLE(pcsc, PC/SC Smart Card support for PHP, +[ --enable-pcsc Enable PC/SC Smart Card support]) + +dnl Check whether the extension is enabled at all +if test "$PHP_PCSC" != "no"; then + if test -r $PHP_PCSC/include/PCSC/winscard.h; then + PCSC_DIR=$PHP_PCSC + else + AC_MSG_CHECKING(for PC/SC in default path) + for i in /usr/local /usr; do + if test -r $i/include/PCSC/winscard.h; then + PCSC_DIR=$i + AC_MSG_RESULT(found in $i) + break + fi + done + fi + + if test -z "$PCSC_DIR"; then + AC_MSG_RESULT(not found) + AC_MSG_ERROR(Please reinstall the pcsc-lite distribution - + winscard.h should be in /include/PCSC/) + fi + AC_MSG_RESULT($PCSC_DIR) + AC_MSG_RESULT($PCSC_DIR) + AC_MSG_RESULT($PCSC_DIR) + PHP_ADD_INCLUDE($PCSC_DIR/include/PCSC/) + dnl PHP_EVAL_LIBLINE($PCSC_LIBS, PCSC_SHARED_LIBADD) + PHP_ADD_LIBRARY_WITH_PATH(pcsclite, "$PCSC_DIR/lib/", PCSC_SHARED_LIBADD) + + dnl Finally, tell the build system about the extension and what files are needed + PHP_NEW_EXTENSION(pcsc, pcsc.c, $ext_shared) + PHP_INSTALL_HEADERS([php_pcsc.h]) + PHP_SUBST(PCSC_SHARED_LIBADD) +fi + diff --git a/pcsc.c b/pcsc.c new file mode 100644 index 0000000..a3f4f0c --- /dev/null +++ b/pcsc.c @@ -0,0 +1,536 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2006 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Johannes Findeisen - 2010 | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_pcsc.h" + +#include +#include + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* {{{ pcsc_functions[] + * + * Every user visible function must have an entry in pcsc_functions[]. + */ +function_entry pcsc_functions[] = { + PHP_FE(scard_establish_context, NULL) + PHP_FE(scard_release_context, NULL) + PHP_FE(scard_is_valid_context, NULL) + PHP_FE(scard_list_readers, NULL) + PHP_FE(scard_connect, NULL) + //PHP_FE(scard_reconnect, NULL) + PHP_FE(scard_disconnect, NULL) + PHP_FE(scard_transmit, NULL) + PHP_FE(scard_status, NULL) + //PHP_FE(scard_cancel, NULL) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ pcsc_module_entry + */ +zend_module_entry pcsc_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "PC/SC", + pcsc_functions, + PHP_MINIT(pcsc), + PHP_MSHUTDOWN(pcsc), + NULL, + NULL, + PHP_MINFO(pcsc), +#if ZEND_MODULE_API_NO >= 20010901 + "0.0.1", /* Replace with version number for your extension */ +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_PCSC +ZEND_GET_MODULE(pcsc) +#endif + +#define REGISTER_PCSC_CONSTANT(__c) REGISTER_LONG_CONSTANT(#__c, __c, CONST_CS | CONST_PERSISTENT) + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(pcsc) +{ + /* Return Codes */ + REGISTER_PCSC_CONSTANT(SCARD_E_BAD_SEEK); + REGISTER_PCSC_CONSTANT(SCARD_E_CANCELLED); + REGISTER_PCSC_CONSTANT(SCARD_E_CANT_DISPOSE); + REGISTER_PCSC_CONSTANT(SCARD_E_CARD_UNSUPPORTED); + REGISTER_PCSC_CONSTANT(SCARD_E_CERTIFICATE_UNAVAILABLE); + REGISTER_PCSC_CONSTANT(SCARD_E_COMM_DATA_LOST); + REGISTER_PCSC_CONSTANT(SCARD_E_DIR_NOT_FOUND); + REGISTER_PCSC_CONSTANT(SCARD_E_DUPLICATE_READER); + REGISTER_PCSC_CONSTANT(SCARD_E_FILE_NOT_FOUND); + REGISTER_PCSC_CONSTANT(SCARD_E_ICC_CREATEORDER); + REGISTER_PCSC_CONSTANT(SCARD_E_ICC_INSTALLATION); + REGISTER_PCSC_CONSTANT(SCARD_E_INSUFFICIENT_BUFFER); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_ATR); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_CHV); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_HANDLE); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_PARAMETER); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_TARGET); + REGISTER_PCSC_CONSTANT(SCARD_E_INVALID_VALUE); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_ACCESS); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_DIR); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_FILE); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_KEY_CONTAINER); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_MEMORY); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_READERS_AVAILABLE); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_SERVICE); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_SMARTCARD); + REGISTER_PCSC_CONSTANT(SCARD_E_NO_SUCH_CERTIFICATE); + REGISTER_PCSC_CONSTANT(SCARD_E_NOT_READY); + REGISTER_PCSC_CONSTANT(SCARD_E_NOT_TRANSACTED); + REGISTER_PCSC_CONSTANT(SCARD_E_PCI_TOO_SMALL); + REGISTER_PCSC_CONSTANT(SCARD_E_PROTO_MISMATCH); + REGISTER_PCSC_CONSTANT(SCARD_E_READER_UNAVAILABLE); + REGISTER_PCSC_CONSTANT(SCARD_E_READER_UNSUPPORTED); + REGISTER_PCSC_CONSTANT(SCARD_E_SERVER_TOO_BUSY); + REGISTER_PCSC_CONSTANT(SCARD_E_SERVICE_STOPPED); + REGISTER_PCSC_CONSTANT(SCARD_E_SHARING_VIOLATION); + REGISTER_PCSC_CONSTANT(SCARD_E_SYSTEM_CANCELLED); + REGISTER_PCSC_CONSTANT(SCARD_E_TIMEOUT); + REGISTER_PCSC_CONSTANT(SCARD_E_UNEXPECTED); + REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_CARD); + REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_READER); + REGISTER_PCSC_CONSTANT(SCARD_E_UNKNOWN_RES_MNG); + REGISTER_PCSC_CONSTANT(SCARD_E_UNSUPPORTED_FEATURE); + REGISTER_PCSC_CONSTANT(SCARD_E_WRITE_TOO_MANY); + + REGISTER_PCSC_CONSTANT(SCARD_F_COMM_ERROR); + REGISTER_PCSC_CONSTANT(SCARD_F_INTERNAL_ERROR); + REGISTER_PCSC_CONSTANT(SCARD_F_UNKNOWN_ERROR); + REGISTER_PCSC_CONSTANT(SCARD_F_WAITED_TOO_LONG); + + REGISTER_PCSC_CONSTANT(SCARD_P_SHUTDOWN); + + REGISTER_PCSC_CONSTANT(SCARD_S_SUCCESS); + + REGISTER_PCSC_CONSTANT(SCARD_W_CANCELLED_BY_USER); + REGISTER_PCSC_CONSTANT(SCARD_W_CARD_NOT_AUTHENTICATED); + REGISTER_PCSC_CONSTANT(SCARD_W_CHV_BLOCKED); + REGISTER_PCSC_CONSTANT(SCARD_W_EOF); + REGISTER_PCSC_CONSTANT(SCARD_W_REMOVED_CARD); + REGISTER_PCSC_CONSTANT(SCARD_W_RESET_CARD); + REGISTER_PCSC_CONSTANT(SCARD_W_SECURITY_VIOLATION); + REGISTER_PCSC_CONSTANT(SCARD_W_UNPOWERED_CARD); + REGISTER_PCSC_CONSTANT(SCARD_W_UNRESPONSIVE_CARD); + REGISTER_PCSC_CONSTANT(SCARD_W_UNSUPPORTED_CARD); + REGISTER_PCSC_CONSTANT(SCARD_W_WRONG_CHV); + + /* Protocols */ + REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_RAW); + REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_T0); + REGISTER_PCSC_CONSTANT(SCARD_PROTOCOL_T1); + + /* Disconnect Modes */ + REGISTER_PCSC_CONSTANT(SCARD_EJECT_CARD); + REGISTER_PCSC_CONSTANT(SCARD_LEAVE_CARD); + REGISTER_PCSC_CONSTANT(SCARD_RESET_CARD); + REGISTER_PCSC_CONSTANT(SCARD_UNPOWER_CARD); + + /* Status codes */ + REGISTER_PCSC_CONSTANT(SCARD_ABSENT); + REGISTER_PCSC_CONSTANT(SCARD_NEGOTIABLE); + REGISTER_PCSC_CONSTANT(SCARD_POWERED); + REGISTER_PCSC_CONSTANT(SCARD_PRESENT); + REGISTER_PCSC_CONSTANT(SCARD_SPECIFIC); + REGISTER_PCSC_CONSTANT(SCARD_SWALLOWED); + + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(pcsc) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(pcsc) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "PC/SC support", "enabled"); + php_info_print_table_end(); +} +/* }}} */ + +static char *e_bytes_to_hex(BYTE *buffer, DWORD length) +{ + DWORD i; + char tmp[3]; + char *str; + + str = emalloc(2 * length + 1); + str[0] = '\0'; + + for (i = 0; i < length; i++) { + sprintf(tmp, "%02X", buffer[i]); + strcat(str, tmp); + } + return str; +} + +static BYTE hex_to_nibble(char chr) +{ + if ((chr >= '0') && (chr <= '9')) { + return chr - '0'; + } + if ((chr >= 'A') && (chr <= 'F')) { + return chr - 'A' + 10; + } + if ((chr >= 'a') && (chr <= 'f')) { + return chr - 'a' + 10; + } + return 0; +} + +static BYTE hex_to_byte(char *str) +{ + return hex_to_nibble(str[0]) * 0x10 + hex_to_nibble(str[1]); +} + +static BYTE *e_hex_to_bytes(char *str, DWORD *length) +{ + DWORD i, l; + BYTE *buf; + + if (str == NULL) { + return NULL; + } + + l = strlen(str) / 2; + if (length != NULL) { + *length = l; + } + + buf = emalloc(l); + + for (i = 0; i < l; i++) { + buf[i] = hex_to_byte(&str[2 * i]); + } + return buf; +} + +/* {{{ proto array pcsc_establishcontext() + Return a a context to the PC/SC layer */ +PHP_FUNCTION(scard_establish_context) +{ + /* TODO: Make the use of SCARDCONTEXT for the context everywhere instead of LONG */ + SCARDCONTEXT scard_context = 0; + LONG rc = 0; + + rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &scard_context); + if (rc != SCARD_S_SUCCESS) + { + RETURN_FALSE; + } + RETURN_LONG(scard_context); +} +/* }}} */ + +/* {{{ proto boolean scard_is_valid_context(long context) + Return a a context to the PC/SC layer */ +PHP_FUNCTION(scard_is_valid_context) +{ + LONG rc = 0; + LONG *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { + return; + } + + rc = SCardIsValidContext(context); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array pcsc_establishcontext(long context) + Return a a context to the PC/SC layer */ +PHP_FUNCTION(scard_release_context) +{ + LONG rc = 0; + LONG *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { + return; + } + + rc = SCardIsValidContext(context); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + rc = SCardReleaseContext(context); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + RETURN_LONG(context); +} +/* }}} */ + +/* {{{ proto array pcsc_listreaders(long context) + Return a an array of available PC/SC readers */ +PHP_FUNCTION(scard_list_readers) +{ + char *strReaders = NULL; + char *ptrReader; + DWORD dwLen = SCARD_AUTOALLOCATE; + LONG rc = 0; + LONG *context; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &context) == FAILURE) { + return; + } + + rc = SCardIsValidContext(context); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + rc = SCardListReaders(context, NULL, (char *) &strReaders, &dwLen); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + if (strReaders == NULL) { + RETURN_FALSE; + } + + array_init(return_value); + + ptrReader = strReaders; + do { + add_next_index_string(return_value, ptrReader, TRUE); + ptrReader+= strlen(ptrReader); + ptrReader++; + } while (*ptrReader != '\0'); + + SCardFreeMemory(context, strReaders); +} +/* }}} */ + +/* {{{ proto long scardconnect(long context, string reader_name, [long preferred_protocol], [long ¤t_protocol]) + Return a handle to the card */ + /* TODO: Add support for the protocol parameters; default is "T=1" now and not changeable */ +PHP_FUNCTION(scard_connect) +{ + DWORD dwPreferredProtocol = SCARD_PROTOCOL_T1; + DWORD dwCurrentProtocol; + SCARDHANDLE hCard = 0; + LONG rc = 0; + LONG *context; + char *strReaderName; + int strReaderNameLen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &context, &strReaderName, &strReaderNameLen) == FAILURE) { + return; + } + + rc = SCardIsValidContext(context); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + rc = SCardConnect(context, strReaderName, SCARD_SHARE_SHARED, dwPreferredProtocol, &hCard, &dwCurrentProtocol); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + RETURN_LONG(hCard); +} +/* }}} */ + +/* {{{ proto boolean scarddisconnect(long card_handle, [long disposition]) + Close a card connection obtained with scardconnect */ + /* TODO: Add support for disposition. */ +PHP_FUNCTION(scard_disconnect) +{ + DWORD dwDisposition = SCARD_EJECT_CARD; + LONG rc; + SCARDHANDLE *hCard; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) { + return; + } + + /*if (argc >= 2) { + convert_to_long(ARG(1)); + dwDisposition = ARG(1)->value.lval; + }*/ + + rc = SCardDisconnect(hCard, dwDisposition); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string scardtransmit(long card_handle, string send_command) + Transmit (and receive) data to (and from) the card */ +PHP_FUNCTION(scard_transmit) +{ + SCARDHANDLE hCard; + DWORD dwProtocol; + DWORD dwState; + const SCARD_IO_REQUEST *sendPci = NULL; + SCARD_IO_REQUEST *recvPci = NULL; + BYTE *sendBuffer; + BYTE *recvBuffer; + DWORD sendLen, recvLen; + LONG rc; + char *apdu; + int apduLen; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ls", &hCard, &apdu, &apduLen) == FAILURE) { + return; + } + + /* Are we in T=0 or T=1 ? */ + rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, NULL, NULL); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + if (!(dwState & SCARD_POWERED)) { + RETURN_FALSE; + } + + switch (dwProtocol) { + case SCARD_PROTOCOL_RAW : sendPci = SCARD_PCI_RAW; break; + case SCARD_PROTOCOL_T0 : sendPci = SCARD_PCI_T0; break; + case SCARD_PROTOCOL_T1 : sendPci = SCARD_PCI_T1; break; + } + + /* Get buffer to send */ + sendBuffer = e_hex_to_bytes(apdu, &sendLen); + + /* Guess the size of the buffer to recv */ + recvLen = 2 + 256; + recvBuffer = emalloc(recvLen); + + /* Perform the exchange */ + recvPci = emalloc(1024); + recvPci->dwProtocol = dwProtocol; + recvPci->cbPciLength = 1024; + + rc = SCardTransmit(hCard, sendPci, sendBuffer, sendLen, recvPci, recvBuffer, &recvLen); + if (rc != SCARD_S_SUCCESS) { + efree(recvPci); + efree(recvBuffer); + efree(sendBuffer); + RETURN_FALSE; + } + efree(recvPci); + + /* Return the response */ + RETVAL_STRING(e_bytes_to_hex(recvBuffer, recvLen), TRUE); + + /* Free local data */ + efree(recvBuffer); + efree(sendBuffer); +} +/* }}} */ + +/* {{{ proto array scardstatus([long card_handle]) + Retrieve reader's or card's status */ +PHP_FUNCTION(scard_status) +{ + SCARDHANDLE hCard = 0; + char *strReader = NULL; + DWORD dwProtocol, dwState; + BYTE atrBuffer[32]; + DWORD atrLen; + LONG rc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &hCard) == FAILURE) { + return; + } + + atrLen = sizeof(atrBuffer); + rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, atrBuffer, &atrLen); + if (rc != SCARD_S_SUCCESS) { + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_long(return_value, "state", dwState); + if (dwState & SCARD_ABSENT) + add_assoc_long(return_value, "SCARD_ABSENT", 1); + + if (dwState & SCARD_PRESENT) + add_assoc_long(return_value, "SCARD_PRESENT", 1); + + if (dwState & SCARD_SWALLOWED) + add_assoc_long(return_value, "SCARD_SWALLOWED", 1); + + if (dwState & SCARD_POWERED) + add_assoc_long(return_value, "SCARD_POWERED", 1); + + if (dwState & SCARD_NEGOTIABLE) + add_assoc_long(return_value, "SCARD_NEGOTIABLE", 1); + + if (dwState & SCARD_SPECIFIC) + add_assoc_long(return_value, "SCARD_SPECIFIC", 1); + + switch (dwProtocol) { + /* Maybe change to bool type here instead of add_assoc_long...? */ + case SCARD_PROTOCOL_RAW : add_assoc_long(return_value, "SCARD_PROTOCOL_RAW", 1); + add_assoc_string(return_value, "PROTOCOL", "RAW", TRUE); + break; + case SCARD_PROTOCOL_T0 : add_assoc_long(return_value, "SCARD_PROTOCOL_T0", 1); + add_assoc_string(return_value, "PROTOCOL", "T=0", TRUE); + break; + case SCARD_PROTOCOL_T1 : add_assoc_long(return_value, "SCARD_PROTOCOL_T1", 1); + add_assoc_string(return_value, "PROTOCOL", "T=1", TRUE); + break; + default : add_assoc_long(return_value, "PROTOCOL", dwProtocol); + } + + if (atrLen) { + char *atrString; + atrString = e_bytes_to_hex(atrBuffer, atrLen); + add_assoc_string(return_value, "ATR", atrString, TRUE); + efree(atrString); + } +} +/* }}} */ diff --git a/php/pcsc.class.php b/php/pcsc.class.php new file mode 100644 index 0000000..897edda --- /dev/null +++ b/php/pcsc.class.php @@ -0,0 +1,55 @@ +context = scard_establish_context(); + } + + function __destruct() { + $this->context = scard_release_context($this->context); + } + + function connect($reader) { + return $this->connection = scard_connect($this->context, $reader); + } + + /*function reconnect() { + return $this->connection = scard_reconnect($this->connection); + }*/ + + function disconnect() { + return scard_disconnect($this->connection); + } + + function listreaders() { + return $this->readers = scard_list_readers($this->context); + } + + function transmit($apdu) { + return scard_transmit($this->connection, $apdu); + } + + function isvalidcontext() { + return scard_is_valid_context($this->context); + } + + function releasecontext() { + return scard_release_context($this->context); + } + + function status() { + return scard_status($this->connection); + } +} + +?> diff --git a/php/test.php b/php/test.php new file mode 100644 index 0000000..61aa86a --- /dev/null +++ b/php/test.php @@ -0,0 +1,43 @@ +context); + +echo "List Readers:\n"; +var_dump($foo->listreaders()); + +echo "Connect Card:\n"; +var_dump($foo->connect("OMNIKEY CardMan 5x21 00 01")); + +#echo "Reconnect:\n"; +#var_dump($foo->reconnect()); + +echo "Some Transmits:\n"; +var_dump($foo->transmit("00a4040c0cD2760001354B414E4D30310000")); +var_dump($foo->transmit("0084000008")); +var_dump($foo->transmit("0084000008")); +var_dump($foo->transmit("0084000008")); + +echo "Is Valid Context?\n"; +var_dump($foo->isvalidcontext()); + +echo "Status:\n"; +var_dump($foo->status()); + +echo "Disconnect Card:\n"; +var_dump($foo->disconnect()); + +echo "Release Context:\n"; +var_dump($foo->releasecontext()); + +?> diff --git a/php_pcsc.h b/php_pcsc.h new file mode 100644 index 0000000..42ba42f --- /dev/null +++ b/php_pcsc.h @@ -0,0 +1,32 @@ +#ifndef PHP_PCSC_H +#define PHP_PCSC_H + +extern zend_module_entry pcsc_module_entry; +#define phpext_pcsc_ptr &pcsc_module_entry + +#ifdef PHP_WIN32 +#define PHP_PCSC_API __declspec(dllexport) +#else +#define PHP_PCSC_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +PHP_MINIT_FUNCTION(pcsc); +PHP_MSHUTDOWN_FUNCTION(pcsc); +PHP_MINFO_FUNCTION(pcsc); + +PHP_FUNCTION(scard_establish_context); +PHP_FUNCTION(scard_is_valid_context); +PHP_FUNCTION(scard_release_context); +PHP_FUNCTION(scard_list_readers); +PHP_FUNCTION(scard_connect); +//PHP_FUNCTION(scard_reconnect); +PHP_FUNCTION(scard_disconnect); +PHP_FUNCTION(scard_status); +PHP_FUNCTION(scard_transmit); +//PHP_FUNCTION(scard_cancel); + +#endif /* PHP_PCSC_H */ diff --git a/tmp/pcsc2.c b/tmp/pcsc2.c new file mode 100644 index 0000000..1226c79 --- /dev/null +++ b/tmp/pcsc2.c @@ -0,0 +1,496 @@ +PHP_FUNCTION(pcsc_test) +{ + + //php_printf("Huhudeldiehu: %d::\n", hContext); + + char *strReaders = ""; + char *ptrReader; + DWORD dwLen = SCARD_AUTOALLOCATE; + LONG rc3; + + /* + LONG rc; + rc = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext); + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + return FAILURE; + } + */ + + + + +/* php_printf("%d: %d
\n", __LINE__, SCARD_S_SUCCESS); + php_printf("%d: %d
\n", __LINE__, lLastError); + php_printf("%d: %d
\n", __LINE__, hContext); + rc3 = SCardListReaders(hContext, NULL, (char *) &strReaders, &dwLen); + php_printf("%d: %d
\n", __LINE__, hContext); + php_printf("%d: %d
\n", __LINE__, lLastError); +*/ + + if (rc3 != SCARD_S_SUCCESS) + { + php_printf("%d: %d
\n", __LINE__, 1); + lLastError = rc3; + php_printf("%d: %d
\n", __LINE__, rc3); + //RETURN_FALSE; + } + + if (strReaders == NULL) { + php_printf("%d: %d
\n", __LINE__, 2); + //RETURN_FALSE; + } + + array_init(return_value); + + ptrReader = strReaders; + do + { + add_next_index_string(return_value, ptrReader, TRUE); + ptrReader += strlen(ptrReader); /* End of the line */ + ptrReader ++; /* Beginning of next list */ + } while (*ptrReader != '\0'); + + SCardFreeMemory(hContext, strReaders); + + + +} + + +/* {{{ proto long scardconnect(string reader_name, [long preferred_protocol], [long ¤t_protocol]) + Return a handle to the card */ +PHP_FUNCTION(scardconnect) +{ + DWORD dwPreferredProtocol, dwCurrentProtocol; + SCARDHANDLE hCard; + char *strReader; + LONG rc; + PARSE_ARGS(1, 3); + + hLastCard = 0; + + if (ARG(0)->type == IS_LONG) + { + /* Find reader by index */ + char *strReaders = NULL; + char *ptrReader; + LONG idxReader; + DWORD dwLen = SCARD_AUTOALLOCATE; + + /* Reader name not supplied, select it in readers list */ + rc = SCardListReaders(hContext, NULL, (char *) &strReaders, &dwLen); + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + if (strReaders == NULL) + RETURN_FALSE; + + /* Loop */ + idxReader = 0; + ptrReader = strReaders; + do + { + if (idxReader == ARG(0)->value.lval) + { + strReader = estrdup(ptrReader); + break; + } + idxReader ++; + ptrReader += strlen(ptrReader); /* End of the line */ + ptrReader ++; /* Beginning of next list */ + } while (*ptrReader != '\0'); + + SCardFreeMemory(hContext, strReaders); + + /* Check */ + if (strReader == NULL) + RETURN_FALSE; /* Reader not found ? */ + } else + { + /* Use reader's name */ + strReader = estrdup(ARG(0)->value.str.val); + } + + /* Preferred protocol */ + if (argc >= 2) + { + convert_to_long(ARG(1)); + dwPreferredProtocol = ARG(1)->value.lval; + } else + { + /* dwPreferredProtocol = SCARD_PROTOCOL_DEFAULT | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; */ + dwPreferredProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; + } + + /* Perform the function */ + rc = SCardConnect(hContext, strReader, SCARD_SHARE_SHARED, dwPreferredProtocol, &hCard, &dwCurrentProtocol); + + /* Free the string ASAP */ + efree(strReader); + + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + /* Current protocol */ + if ((argc >= 3) && (PZVAL_IS_REF(ARG(2)))) + { + ARG(2)->value.lval = dwCurrentProtocol; + ARG(2)->type = IS_LONG; + } + + /* We store a copy of the handle in case we need it in a shortcut function */ + hLastCard = hCard; + + /* We return the handle */ + RETURN_LONG(hCard); +} +/* }}} */ + +/* {{{ proto boolean scardreconnect([long card_handle], [long preferred_protocol], [long initialisation], [long ¤t_protocol]) + Return a handle to the card */ +PHP_FUNCTION(scardreconnect) +{ + DWORD dwPreferredProtocol, dwInitialization, dwCurrentProtocol; + SCARDHANDLE hCard; + LONG rc; + PARSE_ARGS(0, 4); + + hLastCard = 0; + + if (argc >= 1) + { + convert_to_long(ARG(0)); + hCard = ARG(0)->value.lval; + } else + { + hCard = hLastCard; + } + + /* Preferred protocol */ + if (ARG(1) != NULL) + { + convert_to_long(ARG(1)); + dwPreferredProtocol = ARG(1)->value.lval; + } else + { + /* + dwPreferredProtocol = SCARD_PROTOCOL_DEFAULT | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; + */ + dwPreferredProtocol = SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; + } + + /* Initialisation */ + if (ARG(2) != NULL) + { + convert_to_long(ARG(2)); + dwInitialization = ARG(2)->value.lval; + } else + { + dwInitialization = SCARD_LEAVE_CARD; + } + + /* Perform the function */ + rc = SCardReconnect(hCard, SCARD_SHARE_SHARED, dwPreferredProtocol, dwInitialization, &dwCurrentProtocol); + + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + /* Current protocol */ + if ((ARG(3) != NULL) && (PZVAL_IS_REF(ARG(3)))) + { + ARG(3)->value.lval = dwCurrentProtocol; + ARG(3)->type = IS_LONG; + } + + /* OK */ + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto boolean scarddisconnect([long card_handle], [long disposition]) + Close a card connection obtained with scardconnect */ +PHP_FUNCTION(scarddisconnect) +{ + SCARDHANDLE hCard; + DWORD dwDisposition; + LONG rc; + PARSE_ARGS(0, 2); + + /* Retrieve parameters */ + hCard = hLastCard; + dwDisposition = SCARD_EJECT_CARD; + + if (argc >= 1) + { + convert_to_long(ARG(0)); + hLastCard = ARG(0)->value.lval; + } + + if (argc >= 2) + { + convert_to_long(ARG(1)); + dwDisposition = ARG(1)->value.lval; + } + + /* Disconnect */ + rc = SCardDisconnect(hCard, dwDisposition); + + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + /* Drop current card handle */ + if (hLastCard == hCard) + hLastCard = 0; + + /* OK */ + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto array scardstatus([long card_handle]) + Retrieve reader's or card's status */ +PHP_FUNCTION(scardstatus) +{ + SCARDHANDLE hCard = 0; + char *strReader = NULL; + DWORD dwProtocol, dwState; + BYTE atrBuffer[32]; + DWORD atrLen; + LONG rc; + PARSE_ARGS(0, 1); + + /* Retrieve parameters */ + if (argc >= 1) + { + hCard = ARG(0)->value.lval; + } else + { + hCard = hLastCard; + } + + /* Read card's status */ + atrLen = sizeof(atrBuffer); + rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, atrBuffer, &atrLen); + + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + /* Put result in an array */ + array_init(return_value); + + add_assoc_long(return_value, "state", dwState); + + if (dwState & SCARD_ABSENT) + add_assoc_long(return_value, "SCARD_ABSENT", 1); + + if (dwState & SCARD_PRESENT) + add_assoc_long(return_value, "SCARD_PRESENT", 1); + + if (dwState & SCARD_SWALLOWED) + add_assoc_long(return_value, "SCARD_SWALLOWED", 1); + + if (dwState & SCARD_POWERED) + add_assoc_long(return_value, "SCARD_POWERED", 1); + + if (dwState & SCARD_NEGOTIABLE) + add_assoc_long(return_value, "SCARD_NEGOTIABLE", 1); + + if (dwState & SCARD_SPECIFIC) + add_assoc_long(return_value, "SCARD_SPECIFIC", 1); + + switch (dwProtocol) + { + case SCARD_PROTOCOL_RAW : add_assoc_long(return_value, "SCARD_PROTOCOL_RAW", 1); + add_assoc_string(return_value, "protocol", "RAW", TRUE); + break; + case SCARD_PROTOCOL_T0 : add_assoc_long(return_value, "SCARD_PROTOCOL_T0", 1); + add_assoc_string(return_value, "protocol", "T=0", TRUE); + break; + case SCARD_PROTOCOL_T1 : add_assoc_long(return_value, "SCARD_PROTOCOL_T1", 1); + add_assoc_string(return_value, "protocol", "T=1", TRUE); + break; + default : add_assoc_long(return_value, "protocol", dwProtocol); + } + + if (atrLen) + { + char *atrString; + atrString = e_bytes_to_hex(atrBuffer, atrLen); + add_assoc_string(return_value, "atr", atrString, TRUE); + efree(atrString); + } +} +/* }}} */ + +/* {{{ proto array scardgetstatuschange(string reader_name, [long current_state]) + Retrieve the status of a reader */ +PHP_FUNCTION(scardgetstatuschange) +{ + SCARD_READERSTATE rgReaderState = { 0 }; + LONG rc; + PARSE_ARGS(1, 2); + + /* Retrieve parameters */ + rgReaderState.szReader = ARG(0)->value.str.val; + + if (argc >= 2) + { + convert_to_long(ARG(1)); + rgReaderState.dwCurrentState = ARG(1)->value.lval; + } else + { + rgReaderState.dwCurrentState = SCARD_STATE_UNAWARE; + } + + /* Read reader's status */ + rc = SCardGetStatusChange(hContext, 0, &rgReaderState, 1); + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + /* Put result in an array */ + array_init(return_value); + + add_assoc_long(return_value, "state", rgReaderState.dwEventState); + + if (rgReaderState.dwEventState & SCARD_STATE_IGNORE) + add_assoc_long(return_value, "SCARD_STATE_IGNORE", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_UNKNOWN) + add_assoc_long(return_value, "SCARD_STATE_UNKNOWN", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_UNAVAILABLE) + add_assoc_long(return_value, "SCARD_STATE_UNAVAILABLE", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_EMPTY) + add_assoc_long(return_value, "SCARD_STATE_EMPTY", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_PRESENT) + add_assoc_long(return_value, "SCARD_STATE_PRESENT", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_ATRMATCH) + add_assoc_long(return_value, "SCARD_STATE_ATRMATCH", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_EXCLUSIVE) + add_assoc_long(return_value, "SCARD_STATE_EXCLUSIVE", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_INUSE) + add_assoc_long(return_value, "SCARD_STATE_INUSE", 1); + + if (rgReaderState.dwEventState & SCARD_STATE_MUTE) + add_assoc_long(return_value, "SCARD_STATE_MUTE", 1); + + if (rgReaderState.cbAtr) + { + char *atrString; + atrString = e_bytes_to_hex(rgReaderState.rgbAtr, rgReaderState.cbAtr); + add_assoc_string(return_value, "atr", atrString, TRUE); + efree(atrString); + } +} +/* }}} */ + + +/* {{{ proto string scardtransmit([long card_handle], string send_command) + Transmit (and receive) data to (and from) the card */ +PHP_FUNCTION(scardtransmit) +{ + SCARDHANDLE hCard; + DWORD dwProtocol, dwState; + const SCARD_IO_REQUEST *sendPci = NULL; + SCARD_IO_REQUEST *recvPci = NULL; + BYTE *sendBuffer; + BYTE *recvBuffer; + DWORD sendLen, recvLen; + zval *argb = NULL; + LONG rc; + PARSE_ARGS(0, 3); + + /* Retrieve parameters */ + hCard = hLastCard; + argb = ARG(0); + if (argc >= 2) + { + convert_to_long(ARG(0)); + hCard = ARG(0)->value.lval; + argb = ARG(1); + } + + /* Are we in T=0 or T=1 ? */ + rc = SCardStatus(hCard, NULL, NULL, &dwState, &dwProtocol, NULL, NULL); + if (rc != SCARD_S_SUCCESS) + { + lLastError = rc; + RETURN_FALSE; + } + + if (!(dwState & SCARD_POWERED)) + RETURN_FALSE; + + switch (dwProtocol) + { + case SCARD_PROTOCOL_RAW : sendPci = SCARD_PCI_RAW; break; + case SCARD_PROTOCOL_T0 : sendPci = SCARD_PCI_T0; break; + case SCARD_PROTOCOL_T1 : sendPci = SCARD_PCI_T1; break; + } + + /* Get buffer to send */ + convert_to_string(argb); + sendBuffer = e_hex_to_bytes(argb->value.str.val, &sendLen); + + /* Guess the size of the buffer to recv */ + recvLen = 2+256; + recvBuffer = emalloc(recvLen); + + /* Perform the exchange */ + recvPci = emalloc(1024); + recvPci->dwProtocol = dwProtocol; + recvPci->cbPciLength = 1024; + + rc = SCardTransmit(hCard, sendPci, sendBuffer, sendLen, recvPci, recvBuffer, &recvLen); + if (rc != SCARD_S_SUCCESS) + { + efree(recvPci); + efree(recvBuffer); + efree(sendBuffer); + lLastError = rc; + RETURN_FALSE; + } + efree(recvPci); + + /* Return the response */ + RETVAL_STRING(e_bytes_to_hex(recvBuffer, recvLen), TRUE); + + /* Free local data */ + efree(recvBuffer); + efree(sendBuffer); +} +/* }}} */ + +/* {{{ proto long scardgetlasterror() + Retrieve value of last error */ +PHP_FUNCTION(scardgetlasterror) +{ + RETVAL_LONG(lLastError); + lLastError = SCARD_S_SUCCESS; +} +/* }}} */