From 04b0e31929380f2f879b3e2e5597dcbc04dfd0c2 Mon Sep 17 00:00:00 2001 From: Thilo Raufeisen Date: Fri, 7 Oct 2005 18:59:50 +0000 Subject: [PATCH] initial release --- EXPERIMENTAL | 0 LICENSE | 68 ++++ README | 131 +++++++ config.m4 | 62 ++++ examples/clearsign.php | 8 + examples/decrypt.php | 30 ++ examples/encrypt.php | 7 + examples/keyinfo.php | 6 + examples/main.php | 9 + examples/verify.php | 24 ++ gnupg.c | 820 +++++++++++++++++++++++++++++++++++++++++ package.xml | 51 +++ package2.xml | 69 ++++ php_gnupg.h | 96 +++++ 14 files changed, 1381 insertions(+) create mode 100644 EXPERIMENTAL create mode 100644 LICENSE create mode 100644 README create mode 100644 config.m4 create mode 100644 examples/clearsign.php create mode 100644 examples/decrypt.php create mode 100644 examples/encrypt.php create mode 100644 examples/keyinfo.php create mode 100644 examples/main.php create mode 100644 examples/verify.php create mode 100644 gnupg.c create mode 100644 package.xml create mode 100644 package2.xml create mode 100644 php_gnupg.h diff --git a/EXPERIMENTAL b/EXPERIMENTAL new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7b99a14 --- /dev/null +++ b/LICENSE @@ -0,0 +1,68 @@ +-------------------------------------------------------------------- + The PHP License, version 3.0 +Copyright (c) 1999 - 2002 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, 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 . + +This product includes the Zend Engine, freely available at +. diff --git a/README b/README new file mode 100644 index 0000000..6b257ca --- /dev/null +++ b/README @@ -0,0 +1,131 @@ +Installation +------------ +tar xvzf gnupg-x.y.tgz +cd gnupg_x-y +phpize +make +make install + +This extension requires the gpgme library, which is available at http://www.gnupg.org/(en)/download/index.html#gpgme + + +Notes +----- + +- This extension requires PHP 5. + It was tested with 5.0.5 + +- This is a beta version. + Donīt use it on production systems. + See this release more like a "preview" or something like that. + +- This extension makes currently no write-operations on your keyring. + But a backup of them is always a good idea + +- Only the Open_PGP protocol is currently supported. + This shouldnīt be a problem for the most people. + +- extending this class is currently not tested. + +- only 1 key per operation is currently supported. + so you canīt add x keys for encryption. + +- Whenever you provide a key to a method, you should make sure, that your given pattern is unique. + Otherwise it could happen, that the wrong key is selected from the keyring. + The best would be to provide the fingerprint, whenever needed. + +- The underlying lib checks for the presence of the gpg-agent. + If a passphrase is required for an operation, this agent is asked first. + To avoid this, clear the enviroment-variable GPG_AGENT_INFO (see http://de3.php.net/manual/en/function.putenv.php) + +- This extension is class based + No "global" constants are defined. Only class constants + + SIG_MODE_NORMAL + SIG_MODE_DETACH + SIG_MODE_CLEAR + + VALIDITY_UNKNOWN + VALIDITY_UNDEFINED + VALIDITY_NEVER + VALIDITY_MARGINAL + VALIDITY_FULL + VALIDITY_ULTIMATE + + PROTOCOL_OpenPGP + PROTOCOL_CMS + + SIGSUM_VALID + SIGSUM_GREEN + SIGSUM_RED + SIGSUM_KEY_REVOKED + SIGSUM_KEY_EXPIRED + SIGSUM_SIG_EXPIRED + SIGSUM_KEY_MISSING + SIGSUM_CRL_MISSING + SIGSUM_CRL_TOO_OLD + SIGSUM_BAD_POLICY + SIGSUM_SYS_ERROR + + +Methods +------- + +- __construct() + sets up a new gnupg object + ( new gnupg() ) + +- bool setarmor(int armor) + turn on/off armor mode + 0 = off + >0 = on (default) + +- bool setsignmode(int signmode) + sets the mode for signing operations + see the SIG_MODE_* constants + default is SIG_MODE_CLEAR + +- bool setpassphrase(string passphrase) + sets the passphrase for all next operations + +- string geterror(void) + returns the last errormessage + +- int getprotocol(void) + returns the currently used pgp-protocol. + atm only PROTOCOL_OpenPGP is supported + +- array keyinfo(string pattern) + returns an array with informations about all keys, that matches the given pattern + +- bool setsignerkey(string key) + sets the private key for the next sign operation. + please note, that the given key must return only 1 result from the keyring + it should be the best to provide a fingerprint here + +- bool setencryptkey(string key) + sets the public key for next encrypt operation. + please note, that the given key must return only 1 result from the keyring + it should be the best to provide a fingerprint here + +- bool clearsignerkey(void) + removes all keys which are set for signing + +- bool clearencryptkey(void) + removes all keys which are set for encryption + +- string sign(string text) + signs the given test with the key, which was set with setsignerkey before + and returns the signed text + the signmode depends on gnupg_setsignmode + +- string encrypt(string text) + encrypts the given text with the key, which was set with setencryptkey before + and returns the encrypted text + +- array verify(string text [, string &plaintext]) + verifies the given clearsigned text and returns information about the result in an array + if plaintext is passed, it is filled with the plaintext (the text without signature) + +- string decrypt(string enctext) + decrypts the given enctext diff --git a/config.m4 b/config.m4 new file mode 100644 index 0000000..1f795fb --- /dev/null +++ b/config.m4 @@ -0,0 +1,62 @@ +dnl $Id$ +dnl config.m4 for extension gnupg + +dnl Comments in this file start with the string 'dnl'. +dnl Remove where necessary. This file will not work +dnl without editing. + +dnl If your extension references something external, use with: + +PHP_ARG_WITH(gnupg, for gnupg support, +dnl Make sure that the comment is aligned: +[ --with-gnupg Include gnupg support]) + +dnl Otherwise use enable: + +dnl PHP_ARG_ENABLE(gnupg, whether to enable gnupg support, +dnl Make sure that the comment is aligned: +dnl [ --enable-gnupg Enable gnupg support]) + +if test "$PHP_GNUPG" != "no"; then + dnl Write more examples of tests here... + + dnl # --with-gnupg -> check with-path + SEARCH_PATH="/usr/local /usr" # you might want to change this + SEARCH_FOR="/include/gpgme.h" # you most likely want to change this + if test -r $PHP_GNUPG/$SEARCH_FOR; then # path given as parameter + GNUPG_DIR=$PHP_GNUPG + else # search default path list + AC_MSG_CHECKING([for gnupg files in default path]) + for i in $SEARCH_PATH ; do + if test -r $i/$SEARCH_FOR; then + GNUPG_DIR=$i + AC_MSG_RESULT(found in $i) + fi + done + fi + + if test -z "$GNUPG_DIR"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Please reinstall the gnupg distribution]) + fi + + dnl # --with-gnupg -> add include path + PHP_ADD_INCLUDE($GNUPG_DIR/include) + + dnl # --with-gnupg -> check for lib and symbol presence + LIBNAME=gpgme # you may want to change this + LIBSYMBOL=gpgme_check_version # you most likely want to change this + + PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, + [ + PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $GNUPG_DIR/lib, GNUPG_SHARED_LIBADD) + AC_DEFINE(HAVE_GNUPGLIB,1,[ ]) + ],[ + AC_MSG_ERROR([wrong gnupg lib version or lib not found]) + ],[ + -L$GNUPG_DIR/lib -lm -ldl + ]) + PHP_SUBST(GNUPG_SHARED_LIBADD) + + PHP_NEW_EXTENSION(gnupg, gnupg.c, $ext_shared) +fi diff --git a/examples/clearsign.php b/examples/clearsign.php new file mode 100644 index 0000000..6ffe5b7 --- /dev/null +++ b/examples/clearsign.php @@ -0,0 +1,8 @@ + setSignerKey ($fingerprint); +$gnupg -> setPassPhrase ($passphrase); +$text = $gnupg -> sign ($mailtext); +echo $text; +?> diff --git a/examples/decrypt.php b/examples/decrypt.php new file mode 100644 index 0000000..e82b636 --- /dev/null +++ b/examples/decrypt.php @@ -0,0 +1,30 @@ + setPassPhrase ($passphrase); +$plaintext = $gnupg -> decrypt ($mailtext); + +echo "\n".$plaintext."\n"; +?> diff --git a/examples/encrypt.php b/examples/encrypt.php new file mode 100644 index 0000000..89fcddd --- /dev/null +++ b/examples/encrypt.php @@ -0,0 +1,7 @@ + setEncryptKey ($fingerprint); +$text = $gnupg -> encrypt ($mailtext); +echo $text; +?> diff --git a/examples/keyinfo.php b/examples/keyinfo.php new file mode 100644 index 0000000..bde2a76 --- /dev/null +++ b/examples/keyinfo.php @@ -0,0 +1,6 @@ + keyinfo ($fingerprint); +print_r($keyinfo) +?> diff --git a/examples/main.php b/examples/main.php new file mode 100644 index 0000000..3da6cb5 --- /dev/null +++ b/examples/main.php @@ -0,0 +1,9 @@ + diff --git a/examples/verify.php b/examples/verify.php new file mode 100644 index 0000000..634907a --- /dev/null +++ b/examples/verify.php @@ -0,0 +1,24 @@ + verify ($mailtext,$plaintext); + +print_r($info); +echo "\n".$plaintext."\n"; +?> diff --git a/gnupg.c b/gnupg.c new file mode 100644 index 0000000..7cf1097 --- /dev/null +++ b/gnupg.c @@ -0,0 +1,820 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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: Thilo Raufeisen | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "php_gnupg.h" + +static int le_gnupg; + +static zend_object_handlers gnupg_object_handlers; + +/* {{{ macros */ +#define GNUPG_FROM_OBJECT(intern, object){ \ + ze_gnupg_object *obj = (ze_gnupg_object*) zend_object_store_get_object(object TSRMLS_CC); \ + intern = obj->gnupg_ptr; \ + if(!intern){ \ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid or unitialized gnupg object"); \ + RETURN_FALSE; \ + } \ +} + +#define GNUPG_ERROR(intern, this){ \ + zend_update_property_string(Z_OBJCE_P(this), this, "error", 5, (char*)gpg_strerror(intern->err) TSRMLS_DC); \ + RETURN_FALSE; \ +} +/* }}} */ + +/* {{{ free_resource */ +static void gnupg_free_resource_ptr(gnupg_object *intern TSRMLS_DC){ + if(intern){ + + if(intern->ctx){ + gpgme_signers_clear (intern->ctx); + gpgme_release(intern->ctx); + intern->ctx = NULL; + } + zval_dtor(&intern->passphrase); + efree(intern); + } +} +/* }}} */ + +/* {{{ free_storage */ +static void gnupg_object_free_storage(void *object TSRMLS_DC){ + ze_gnupg_object * intern = (ze_gnupg_object *) object; + if(!intern){ + return; + } + if(intern->gnupg_ptr){ + gnupg_free_resource_ptr(intern->gnupg_ptr TSRMLS_CC); + } + intern->gnupg_ptr = NULL; + if(intern->zo.properties){ + zend_hash_destroy(intern->zo.properties); + FREE_HASHTABLE(intern->zo.properties); + } + efree(intern); +} +/* }}} */ + +/* {{{ objects_new */ +zend_object_value gnupg_objects_new(zend_class_entry *class_type TSRMLS_DC){ + ze_gnupg_object *intern; + zval *tmp; + zend_object_value retval; + intern = emalloc(sizeof(ze_gnupg_object)); + intern->zo.ce = class_type; + intern->zo.in_get = 0; + intern->zo.in_set = 0; + intern->zo.properties = NULL; + + ALLOC_HASHTABLE(intern->zo.properties); + zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0); + zend_hash_copy(intern->zo.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); + + retval.handle = zend_objects_store_put(intern,NULL,(zend_objects_free_object_storage_t) gnupg_object_free_storage,NULL TSRMLS_CC); + retval.handlers = (zend_object_handlers *) & gnupg_object_handlers; + + /* hmmm. better in userspace + unsetenv ("GPG_AGENT_INFO"); + */ + return retval; +} +/* }}} */ + +/* {{{ resource_destructor */ +void gnupg_resource_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC){ + /* + if(rsrc->ptr){ + printf("debug"); + } + */ +} +/* }}} */ + +/* {{{ functionlist */ +function_entry gnupg_functions[] = { + {NULL, NULL, NULL} /* Must be the last line in gnupg_functions[] */ +}; +/* }}} */ + +/* {{{ methodlist */ +static zend_function_entry gnupg_methods[] = { + PHP_ME_MAPPING(__construct, gnupg_construct, NULL) + PHP_ME_MAPPING(keyinfo, gnupg_keyinfo, NULL) + PHP_ME_MAPPING(verify, gnupg_verify, NULL) + PHP_ME_MAPPING(getError, gnupg_geterror, NULL) + PHP_ME_MAPPING(setpassphrase, gnupg_setpassphrase, NULL) + PHP_ME_MAPPING(setsignerkey, gnupg_setsignerkey, NULL) + PHP_ME_MAPPING(clearsignerkey, gnupg_clearsignerkey, NULL) + PHP_ME_MAPPING(setencryptkey, gnupg_setencryptkey, NULL) + PHP_ME_MAPPING(setarmor, gnupg_setarmor, NULL) + PHP_ME_MAPPING(encrypt, gnupg_encrypt, NULL) + PHP_ME_MAPPING(decrypt, gnupg_decrypt, NULL) + PHP_ME_MAPPING(export, gnupg_export, NULL) + PHP_ME_MAPPING(getprotocol, gnupg_getprotocol, NULL) + PHP_ME_MAPPING(setsignmode, gnupg_setsignmode, NULL) + PHP_ME_MAPPING(sign, gnupg_sign, NULL) + {NULL, NULL, NULL} +}; +/* }}} */ + +/* {{{ class constants */ +static void gnupg_declare_long_constant(const char *const_name, long value TSRMLS_DC){ +#if PHP_MAJOR_VERSION > 5 || PHP_MINOR_VERSION >= 1 + zend_declare_class_constant_long(gnupg_class_entry, (char*)const_name, strlen(const_name), value TSRMLS_CC); +#else + zval *constant = malloc(sizeof(*constant)); + ZVAL_LONG(constant,value); + INIT_PZVAL(constant); + zend_hash_update(&gnupg_class_entry->constants_table, (char*)const_name, strlen(const_name)+1, &constant, sizeof(zval*), NULL); +#endif + +} +/* }}} */ + +/* {{{ properties */ +void register_gnupgProperties(TSRMLS_D){ + zend_declare_property_long (gnupg_class_entry, "protocol", 8, GPGME_PROTOCOL_OpenPGP, ZEND_ACC_PROTECTED TSRMLS_DC); + zend_declare_property_string (gnupg_class_entry, "error", 5, "", ZEND_ACC_PROTECTED TSRMLS_DC); +} +/* }}} */ + +/* {{{ gnupg_module_entry + */ +zend_module_entry gnupg_module_entry = { +#if ZEND_MODULE_API_NO >= 20010901 + STANDARD_MODULE_HEADER, +#endif + "gnupg", + gnupg_functions, + PHP_MINIT(gnupg), + PHP_MSHUTDOWN(gnupg), + NULL, /* Replace with NULL if there's nothing to do at request start */ + NULL, /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(gnupg), +#if ZEND_MODULE_API_NO >= 20010901 + "0.1", /* Replace with version number for your extension */ +#endif + STANDARD_MODULE_PROPERTIES +}; +/* }}} */ + +#ifdef COMPILE_DL_GNUPG +ZEND_GET_MODULE(gnupg) +#endif + + + +/* {{{ PHP_MINIT_FUNCTION + */ +PHP_MINIT_FUNCTION(gnupg) +{ + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "gnupg", gnupg_methods); + + ce.create_object = gnupg_objects_new; + gnupg_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + memcpy(&gnupg_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + le_gnupg = zend_register_list_destructors_ex(gnupg_resource_destructor, NULL, "ctx", module_number); + + register_gnupgProperties(TSRMLS_CC); + gnupg_declare_long_constant("SIG_MODE_NORMAL", GPGME_SIG_MODE_NORMAL TSRMLS_DC); + gnupg_declare_long_constant("SIG_MODE_DETACH", GPGME_SIG_MODE_DETACH TSRMLS_DC); + gnupg_declare_long_constant("SIG_MODE_CLEAR", GPGME_SIG_MODE_CLEAR TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_UNKNOWN", GPGME_VALIDITY_UNKNOWN TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_UNDEFINED", GPGME_VALIDITY_UNDEFINED TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_NEVER", GPGME_VALIDITY_NEVER TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_MARGINAL", GPGME_VALIDITY_MARGINAL TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_FULL", GPGME_VALIDITY_FULL TSRMLS_DC); + gnupg_declare_long_constant("VALIDITY_ULTIMATE", GPGME_VALIDITY_ULTIMATE TSRMLS_DC); + gnupg_declare_long_constant("PROTOCOL_OpenPGP", GPGME_PROTOCOL_OpenPGP TSRMLS_DC); + gnupg_declare_long_constant("PROTOCOL_CMS", GPGME_PROTOCOL_CMS TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_VALID", GPGME_SIGSUM_VALID TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_GREEN", GPGME_SIGSUM_GREEN TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_RED", GPGME_SIGSUM_RED TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_KEY_REVOKED", GPGME_SIGSUM_KEY_REVOKED TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_KEY_EXPIRED", GPGME_SIGSUM_KEY_EXPIRED TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_SIG_EXPIRED", GPGME_SIGSUM_SIG_EXPIRED TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_KEY_MISSING", GPGME_SIGSUM_KEY_MISSING TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_CRL_MISSING", GPGME_SIGSUM_CRL_MISSING TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_CRL_TOO_OLD", GPGME_SIGSUM_CRL_TOO_OLD TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_BAD_POLICY", GPGME_SIGSUM_BAD_POLICY TSRMLS_DC); + gnupg_declare_long_constant("SIGSUM_SYS_ERROR", GPGME_SIGSUM_SYS_ERROR TSRMLS_DC); + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MSHUTDOWN_FUNCTION + */ +PHP_MSHUTDOWN_FUNCTION(gnupg) +{ + return SUCCESS; +} +/* }}} */ + +/* {{{ PHP_MINFO_FUNCTION + */ +PHP_MINFO_FUNCTION(gnupg) +{ + php_info_print_table_start(); + php_info_print_table_header(2, "gnupg support", "enabled"); + php_info_print_table_row(2,"GPGme Version",gpgme_check_version(NULL)); + php_info_print_table_end(); +} +/* }}} */ + +/* {{{ callback func for setting the passphrase + */ +gpgme_error_t passphrase_cb (gnupg_object *intern, const char *uid_hint, const char *passphrase_info,int last_was_bad, int fd){ + if(last_was_bad){ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Incorrent passphrase"); + return 1; + } + if(Z_STRLEN(intern->passphrase) < 1){ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "no passphrase set"); + return 1; + } + write (fd, Z_STRVAL(intern->passphrase), Z_STRLEN(intern->passphrase)); + write (fd, "\n", 1); + return 0; + +} +/* }}} */ + +/* {{{proto object gnupg_construct([PROTOCOL]) + * constructor. + * if passed, only GPGME_PROTOCOL_OpenPGP is currently valid + */ +PHP_FUNCTION(gnupg_construct){ + gnupg_object *intern; + zval *this = getThis(); + ze_gnupg_object *ze_obj; + + int protocol = GPGME_PROTOCOL_OpenPGP; + gpgme_ctx_t ctx; + gpgme_error_t err; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &protocol) == FAILURE){ + return; + } + if(protocol != GPGME_PROTOCOL_OpenPGP){ + zend_throw_exception(zend_exception_get_default(),"only OpenPGP is currently supported",1 TSRMLS_CC); + } + if((err = gpgme_new(&ctx))!=GPG_ERR_NO_ERROR){ + zend_throw_exception(zend_exception_get_default(),gpg_strerror(err),1 TSRMLS_CC); + } + gpgme_set_armor (ctx,1); + + ze_obj = (ze_gnupg_object*) zend_object_store_get_object(this TSRMLS_CC); + intern = emalloc(sizeof(gnupg_object)); + intern->ctx = ctx; + intern->encryptkey = NULL; + intern->signmode = GPGME_SIG_MODE_CLEAR; + ze_obj->gnupg_ptr = intern; +} +/* }}} */ + +/* {{{ proto bool gnupg_setarmor(int armor) + * turn on/off armor mode + * 0 = off + * >0 = on + * */ +PHP_FUNCTION(gnupg_setarmor){ + int armor; + zval *this = getThis(); + gnupg_object *intern; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &armor) == FAILURE){ + return; + } + + if(armor > 1){ + armor = 1; /*just to make sure */ + } + + gpgme_set_armor (intern->ctx,armor); + RETURN_TRUE; +} +/* }}} */ + + +/* {{{ proto bool gnupg_setsignmode(int signmode) + * sets the mode for signing operations + */ +PHP_FUNCTION(gnupg_setsignmode){ + int signmode; + zval *this = getThis(); + gnupg_object *intern; + + GNUPG_FROM_OBJECT(intern, this); + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"l", &signmode) == FAILURE){ + return; + } + switch(signmode){ + case GPGME_SIG_MODE_NORMAL: + case GPGME_SIG_MODE_DETACH: + case GPGME_SIG_MODE_CLEAR: + intern->signmode = signmode; + RETURN_TRUE; + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid signmode: %i",signmode); + RETURN_FALSE; /* not really needed */ + break; + } +} +/* }}} */ + +/* {{{ proto bool gnupg_setpassphrase(string passphrase) + * sets the passphrase for all next operations + */ +PHP_FUNCTION(gnupg_setpassphrase){ + zval *tmp; + zval *this = getThis(); + gnupg_object *intern; + + GNUPG_FROM_OBJECT(intern, this); + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"z", &tmp) == FAILURE){ + return; + } + intern->passphrase = *tmp; + zval_copy_ctor(&intern->passphrase); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string gnupg_geterror(void) + * returns the last errormessage + */ +PHP_FUNCTION(gnupg_geterror){ + zval *error; + zval *this = getThis(); + + error = zend_read_property(Z_OBJCE_P(this), this, "error", 5, 1 TSRMLS_CC); + RETURN_STRINGL(Z_STRVAL_P(error), Z_STRLEN_P(error), 1); +} +/* }}} */ + +/* {{{ proto int gnupg_getprotocol(void) + * returns the currently used pgp-protocol. + * atm only OpenPGP is supported + */ +PHP_FUNCTION(gnupg_getprotocol){ + zval *this = getThis(); + zval *protocol; + gnupg_object *intern; + GNUPG_FROM_OBJECT(intern, this); + protocol = zend_read_property(Z_OBJCE_P(this), this, "protocol", 8, 1 TSRMLS_CC); + RETURN_LONG(Z_LVAL_P(protocol)); +} + +/* }}} */ + +/* {{{ proto array gnupg_keyinfo(string pattern) + * returns an array with informations about all keys, that matches the given pattern + */ +PHP_FUNCTION(gnupg_keyinfo) +{ + char *searchkey = NULL; + int *searchkey_len; + int idx; + zval *this = getThis(); + zval *subarr; + zval *userid; + zval *userids; + zval *subkey; + zval *subkeys; + gnupg_object *intern; + + gpgme_key_t gpgkey; + gpgme_subkey_t gpgsubkey; + gpgme_user_id_t gpguserid; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &searchkey, &searchkey_len) == FAILURE){ + return; + } + + GNUPG_FROM_OBJECT(intern, this); + + if((intern->err = gpgme_op_keylist_start(intern->ctx, searchkey, 0)) != GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + + array_init(return_value); + + while(!(intern->err = gpgme_op_keylist_next(intern->ctx, &gpgkey))){ + ALLOC_INIT_ZVAL (subarr); + array_init (subarr); + + ALLOC_INIT_ZVAL (subkeys); + array_init (subkeys); + + ALLOC_INIT_ZVAL (userids); + array_init (userids); + + add_assoc_bool (subarr, "disabled", gpgkey->disabled ); + add_assoc_bool (subarr, "expired", gpgkey->expired ); + add_assoc_bool (subarr, "revoked", gpgkey->revoked ); + add_assoc_bool (subarr, "is_secret", gpgkey->secret ); + add_assoc_bool (subarr, "can_sign", gpgkey->can_sign ); + add_assoc_bool (subarr, "can_encrypt", gpgkey->can_encrypt ); + + for(idx = 1, gpguserid = gpgkey->uids; gpguserid; idx++, gpguserid = gpguserid->next){ + ALLOC_INIT_ZVAL (userid); + array_init (userid); + + add_assoc_string (userid, "name", gpguserid->name, 1); + add_assoc_string (userid, "comment", gpguserid->comment, 1); + add_assoc_string (userid, "email", gpguserid->email, 1); + add_assoc_string (userid, "uid", gpguserid->uid, 1); + + add_assoc_bool (userid, "revoked", gpguserid->revoked ); + add_assoc_bool (userid, "invalid", gpguserid->invalid ); + + add_next_index_zval (userids, userid); + } + + add_assoc_zval (subarr, "uids", userids); + + for(idx = 1, gpgsubkey = gpgkey->subkeys; gpgsubkey; idx++, gpgsubkey = gpgsubkey->next){ + ALLOC_INIT_ZVAL (subkey); + array_init (subkey); + + if(gpgsubkey->fpr){ + add_assoc_string (subkey, "fingerprint", gpgsubkey->fpr, 1); + } + + add_assoc_string (subkey, "keyid", gpgsubkey->keyid, 1); + + add_assoc_long (subkey, "timestamp", gpgsubkey->timestamp ); + add_assoc_long (subkey, "expires", gpgsubkey->expires ); + add_assoc_bool (subkey, "is_secret", gpgsubkey->secret ); + add_assoc_bool (subkey, "invalid", gpgsubkey->invalid ); + add_assoc_bool (subkey, "can_encrypt", gpgsubkey->can_encrypt ); + add_assoc_bool (subkey, "can_sign", gpgsubkey->can_sign ); + add_assoc_bool (subkey, "disabled", gpgsubkey->disabled ); + add_assoc_bool (subkey, "expired", gpgsubkey->expired ); + add_assoc_bool (subkey, "revoked", gpgsubkey->revoked ); + + add_next_index_zval (subkeys, subkey); + } + + add_assoc_zval (subarr, "subkeys", subkeys); + + add_next_index_zval (return_value, subarr); + } + return; +} +/* }}} */ + +/* {{{ proto bool gnupg_setsignerkey(string key) + * sets the private key for the next sign operation. + * please note, that the given key must return only 1 result from the keyring + * it should be the best to provide a fingerprint here + */ +PHP_FUNCTION(gnupg_setsignerkey){ + char *key_id = NULL; + int key_id_len; + + zval *this = getThis(); + gnupg_object *intern; + + gpgme_sign_result_t result; + gpgme_key_t gpgme_key; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key_id, &key_id_len) == FAILURE){ + return; + } + if((intern->err = gpgme_get_key(intern->ctx, key_id, &gpgme_key, 1)) != GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + + gpgme_signers_clear (intern->ctx); + + if((intern->err = gpgme_signers_add(intern->ctx, gpgme_key))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool gnupg_setencryptkey(string key) + * sets the public key for next encrypt operation. + * please note, that the given key must return only 1 result from the keyring + * it should be the best to provide a fingerprint here + */ +PHP_FUNCTION(gnupg_setencryptkey){ + char *key_id = NULL; + int key_id_len; + + zval *this = getThis(); + gnupg_object *intern; + + gpgme_sign_result_t result; + gpgme_key_t gpgme_key; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key_id, &key_id_len) == FAILURE){ + return; + } + if((intern->err = gpgme_get_key(intern->ctx, key_id, &gpgme_key, 0)) != GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if(intern->encryptkey){ + gpgme_key_release(intern->encryptkey); + } + intern->encryptkey = gpgme_key; + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool gnupg_clearsignerkey(void) + * removes all keys which are set for signing + */ +PHP_FUNCTION(gnupg_clearsignerkey){ + zval *this = getThis(); + gnupg_object *intern; + + GNUPG_FROM_OBJECT (intern, this); + + gpgme_signers_clear (intern->ctx); + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool gnupg_clearencryptkey(void) + * removes all keys which are set for encryption + */ +PHP_FUNCTION(gnupg_clearencryptkey){ + zval *this = getThis(); + gnupg_object *intern; + + GNUPG_FROM_OBJECT (intern, this); + + gpgme_key_release (intern->encryptkey); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto string gnupg_sign(string text) + * signs the given test with the key, which was set with setsignerkey before + * and returns the signed text + * the signmode depends on gnupg_setsignmode + */ +PHP_FUNCTION(gnupg_sign){ + char *value = NULL; + int value_len; + + zval *this = getThis(); + gnupg_object *intern; + + char *userret; + int ret_size; + + gpgme_data_t in, out; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE){ + return; + } + gpgme_set_passphrase_cb (intern->ctx, (void*) passphrase_cb, intern); + if((intern->err = gpgme_data_new_from_mem (&in, value, value_len, 0))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_data_new(&out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_op_sign(intern->ctx, in, out, intern->signmode))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + userret = gpgme_data_release_and_get_mem(out,&ret_size); + if(ret_size < 1){ + RETURN_FALSE; + } + gpgme_data_release (in); + free (out); + RETURN_STRINGL (userret,ret_size,1); +} + +/* }}} */ + +/* {{{ proto string gnupg_encrypt(string text) + * encrypts the given text with the key, which was set with setencryptkey before + * and returns the encrypted text + */ +PHP_FUNCTION(gnupg_encrypt){ + char *value = NULL; + int value_len; + char *userret = NULL; + int ret_size; + zval *this = getThis(); + gnupg_object *intern; + + gpgme_data_t in, out; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &value, &value_len) == FAILURE){ + return; + } + + if(!intern->encryptkey){ + zend_update_property_string(Z_OBJCE_P(this), this, "error", 5, "no key for encryption set" TSRMLS_DC); + RETURN_FALSE; + } + if((intern->err = gpgme_data_new_from_mem (&in, value, value_len, 0))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_data_new(&out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_op_encrypt(intern->ctx, &intern->encryptkey, GPGME_ENCRYPT_ALWAYS_TRUST, in, out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + userret = gpgme_data_release_and_get_mem(out,&ret_size); + if(ret_size < 1){ + RETURN_FALSE; + } + gpgme_data_release (in); + free (out); + /* + gpgme_key_release (gpgme_key); + */ + RETURN_STRINGL (userret,ret_size,1); +} +/* }}} */ + +/* {{{ proto array gnupg_verify(string text [, string &plaintext]) + * verifies the given clearsigned text and returns information about the result in an array + */ +PHP_FUNCTION(gnupg_verify){ + char *value = NULL; + char *sigtext = NULL; + int value_len; + int tmp; + zval *plaintext; + zval *this = getThis(); + gnupg_object *intern; + + char *userret; + int ret_size; + + gpgme_data_t in, out; + gpgme_verify_result_t result; + gpgme_signature_t nextsig; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &value, &value_len, &plaintext) == FAILURE){ + return; + } + if((intern->err = gpgme_data_new_from_mem (&in, value, value_len, 0))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_data_new_from_mem (&out, sigtext, 0, 0))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_op_verify (intern->ctx, in, NULL, out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + result = gpgme_op_verify_result (intern->ctx); + + array_init (return_value); + + add_assoc_string (return_value, "fingerprint", result->signatures->fpr, 1); + add_assoc_long (return_value, "validity", result->signatures->validity ); + add_assoc_long (return_value, "timestamp", result->signatures->timestamp ); + add_assoc_long (return_value, "status", result->signatures->status ); + + nextsig = result->signatures->next; + if(nextsig){ + zend_update_property_string(Z_OBJCE_P(this), this, "error", 5, "multiple signatures found" TSRMLS_DC); + RETURN_FALSE; + } + + userret = gpgme_data_release_and_get_mem(out,&ret_size); + if(plaintext){ + ZVAL_STRINGL (plaintext,userret,ret_size,1); + } + gpgme_data_release (in); + free (out); +} +/* }}} */ + +/* {{{ proto string gnupg_decrypt(string enctext) + * decrypts the given enctext + */ +PHP_FUNCTION(gnupg_decrypt){ + char *enctxt; + int enctxt_len; + + zval *this = getThis(); + gnupg_object *intern; + + char *userret; + int ret_size; + + gpgme_data_t in, out; + gpgme_decrypt_result_t result; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &enctxt, &enctxt_len) == FAILURE){ + return; + } + + gpgme_set_passphrase_cb (intern->ctx, (void*) passphrase_cb, intern); + + if((intern->err = gpgme_data_new_from_mem (&in, enctxt, enctxt_len, 0))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_data_new (&out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_op_decrypt (intern->ctx, in, out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + result = gpgme_op_decrypt_result (intern->ctx); + + userret = gpgme_data_release_and_get_mem(out,&ret_size); + RETURN_STRINGL (userret,ret_size,1); + gpgme_data_release (in); + free (out); +} +/* }}} */ + +/* {{{ proto string gnupg_export(string pattern) + * exports the first public key which matches against the given pattern + */ +PHP_FUNCTION(gnupg_export){ + char *searchkey = NULL; + int *searchkey_len; + char *userret; + int ret_size; + + zval *this = getThis(); + gnupg_object *intern; + + gpgme_data_t out; + + GNUPG_FROM_OBJECT(intern, this); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &searchkey, &searchkey_len) == FAILURE){ + return; + } + if((intern->err = gpgme_data_new (&out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + if((intern->err = gpgme_op_export (intern->ctx, searchkey, 0, out))!=GPG_ERR_NO_ERROR){ + GNUPG_ERROR(intern,this); + } + userret = gpgme_data_release_and_get_mem(out,&ret_size); + if(ret_size < 1){ + RETURN_FALSE; + } + RETURN_STRINGL (userret,ret_size,1); + free (out); +} +/* }}} */ +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/package.xml b/package.xml new file mode 100644 index 0000000..765e076 --- /dev/null +++ b/package.xml @@ -0,0 +1,51 @@ + + + + gnupg + wrapper around the gpgme library + This extension provides methods to interact with gnupg. +So you can sign, encrypt, verify directly from php + + + + traufeisen + Thilo Raufeisen + traufeisen@php.net + lead + + + + 0.1 + 2005-10-07 + PHP License + beta + First release and not feature complete. Don't use in production enviroments + + + + + + + + + + + + + + + + + + + + + + 0.1 + 2005-10-07 + beta + initial release + + + + diff --git a/package2.xml b/package2.xml new file mode 100644 index 0000000..15d0b5e --- /dev/null +++ b/package2.xml @@ -0,0 +1,69 @@ + + + gnupg + pecl.php.net + wrapper around the gpgme library + This extension provides methods to interact with gnupg. +So you can sign, encrypt, verify directly from php + + Thilo Raufeisen + traufeisen + traufeisen@php.net + yes + + 2005-10-07 + + + 0.1 + 0.1 + + + beta + beta + + PHP License + First release and not feature complete. Don't use in production enviroments + + + + + + + + + + + + + + + + + + + + 5.0 + + + 1.4.0b1 + + + + gnupg + + + + + 0.1 + 0.1 + + + beta + beta + + 2005-10-07 + PHP License + initial release + + + diff --git a/php_gnupg.h b/php_gnupg.h new file mode 100644 index 0000000..f2aefc1 --- /dev/null +++ b/php_gnupg.h @@ -0,0 +1,96 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 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_0.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: Thilo Raufeisen | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_GNUPG_H +#define PHP_GNUPG_H + +extern zend_module_entry gnupg_module_entry; +#define phpext_gnupg_ptr &gnupg_module_entry + +#ifdef PHP_WIN32 +#define PHP_GNUPG_API __declspec(dllexport) +#else +#define PHP_GNUPG_API +#endif + +#ifdef ZTS +#include "TSRM.h" +#endif + +#include + +typedef struct _gnupg_object{ + gpgme_ctx_t ctx; + zval passphrase; + gpgme_key_t encryptkey; + gpgme_error_t err; + int signmode; +} gnupg_object; + +typedef struct _ze_gnupg_object{ + zend_object zo; + gnupg_object *gnupg_ptr; +} ze_gnupg_object; + + + +zend_class_entry *gnupg_class_entry; + +PHP_MINIT_FUNCTION(gnupg); +PHP_MSHUTDOWN_FUNCTION(gnupg); +/* +PHP_RINIT_FUNCTION(gnupg); +PHP_RSHUTDOWN_FUNCTION(gnupg); +*/ +PHP_MINFO_FUNCTION(gnupg); + +PHP_FUNCTION(gnupg_construct); +PHP_FUNCTION(gnupg_keyinfo); +PHP_FUNCTION(gnupg_verify); +PHP_FUNCTION(gnupg_geterror); +PHP_FUNCTION(gnupg_setpassphrase); +PHP_FUNCTION(gnupg_setsignerkey); +PHP_FUNCTION(gnupg_setencryptkey); +PHP_FUNCTION(gnupg_setsignmode); +PHP_FUNCTION(gnupg_setarmor); +PHP_FUNCTION(gnupg_sign); +PHP_FUNCTION(gnupg_clearsignerkey); +PHP_FUNCTION(gnupg_getprotocol); +PHP_FUNCTION(gnupg_encrypt); +PHP_FUNCTION(gnupg_decrypt); +PHP_FUNCTION(gnupg_export); + +#ifdef ZTS +#define GNUPG_G(v) TSRMG(gnupg_globals_id, zend_gnupg_globals *, v) +#else +#define GNUPG_G(v) (gnupg_globals.v) +#endif + +#endif /* PHP_GNUPG_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */