diff --git a/Makefile.conf b/Makefile.conf index a8c86bac..c1fadb4e 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -32,7 +32,10 @@ BUILD_FOR=$(BUILD_ON) # Cross-compiling is currently not supported. # Maybe you can hack something with `local.conf`. ifneq ($(BUILD_ON),$(BUILD_FOR)) - $(error I don't know how to build for $(BUILD_FOR) on $(BUILD_ON).) + ifeq ($(BUILD_FOR),OS/390) + $(warning Generating sources for z/OS) + endif + ONLY_GENERATE=true endif # Installation path prefix for libraries and binaries, except for system.db @@ -50,7 +53,9 @@ PREFIX=$(HOME) PER_MACHINE_DIRECTORY=/usr/local/share/pEp # Filename of the pEpEngine library -ifeq ($(BUILD_FOR),Linux) +ifeq ($(BUILD_FOR),OS/390) + TARGET=libpEpEngine.so +else ifeq ($(BUILD_FOR),Linux) TARGET=libpEpEngine.so else ifeq ($(BUILD_FOR),Darwin) TARGET=libpEpEngine.dylib @@ -82,25 +87,38 @@ endif # Are we wiping this on purpose? LDLIBS= -######### C ######### -ifeq ($(BUILD_FOR),Linux) - CC=gcc +ifeq ($(BUILD_FOR),OS/390) + CC=xlclang +else ifeq ($(BUILD_FOR),Linux) + CC=gcc -std=c99 -pthread else ifeq ($(BUILD_FOR),Darwin) CC=clang endif -CFLAGS+=-std=c99 -pthread -CFLAGS+=-fPIC -fstrict-aliasing - -ifeq ($(BUILD_FOR),Linux) - CFLAGS+=-fdiagnostics-color=auto +ifeq ($(BUILD_FOR),OS/390) + CFLAGS= -Wno-gnu-include-next -qnosearch -I$(HERE) -I. + CFLAGS+= -I/usr/include + CFLAGS+= -DZOS -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L -qlanglvl=NOLIBEXT +else ifeq ($(BUILD_FOR),Linux) + CFLAGS+=-fPIC -fstrict-aliasing -fdiagnostics-color=auto else ifeq ($(BUILD_FOR),Darwin) - CFLAGS+=-fcolor-diagnostics + CFLAGS+=-pthread -fPIC -fstrict-aliasing -fcolor-diagnostics endif # The flag -DNDEBUG will always be removed from CFLAGS for compiling tests. # The tests do not work properly, if compiled with -DNDEBUG -ifeq ($(BUILD_FOR),Linux) +ifeq ($(BUILD_FOR),OS/390) + ifdef WARN + CFLAGS+= -Wall -pedantic + else + CFLAGS+= -w + endif + ifdef DEBUG + CFLAGS+= -g -DDEBUG_ERRORSTACK + else + CFLAGS+= -O3 -DNDEBUG + endif +else ifeq ($(BUILD_FOR),Linux) ifdef WARN CFLAGS+= -Wall -pedantic -Wstrict-aliasing=3 else @@ -142,16 +160,34 @@ ifeq ($(BUILD_FOR),Linux) CXX=g++ CXXFLAGS+=-std=gnu++11 else ifeq ($(BUILD_FOR),Darwin) - CXX=clang - CXXFLAGS+=-std=c++11 + # clang issues a warning when "-pthread" is used for linking. So, include it in CXXFLAGS, and not in CXX + CXX=clang -std=c++11 +else ifeq ($(BUILD_FOR),OS/390) + CXX=xlclang++ -std=c++11 endif +ifneq ($(BUILD_FOR),OS/390) CXXFLAGS+=-pthread +endif # The flag -DNDEBUG will always be removed from CPPFLAGS for compiling tests. # # The tests do not work properly, if compiled with -DNDEBUG -ifeq ($(BUILD_FOR),Linux) +ifeq ($(BUILD_FOR),OS/390) + CXXFLAGS+= -fdiagnostics-color=auto -Wno-gnu-include-next + CXXFLAGS+= -qnosearch -I$(HERE) -I. -I../src -I../asn.1 $(ETPAN_INC) -I/usr/lpp/java/J8.0_64/include -I/usr/include + CXXFLAGS+= -DZOS -D_XOPEN_SOURCE=600 -D_POSIX_C_SOURCE=200112L + ifdef WARN + CXXFLAGS+= + else + CXXFLAGS+= -w + endif + ifdef DEBUG + CXXFLAGS+= -g + else + CXXFLAGS+= -O3 -DNDEBUG + endif +else ifeq ($(BUILD_FOR),Linux) CXXFLAGS+=-fdiagnostics-color=auto -I../src -I../asn.1 $(ETPAN_INC) ifdef WARN CXXFLAGS+= @@ -165,7 +201,7 @@ ifeq ($(BUILD_FOR),Linux) CPPFLAGS+= -DNDEBUG endif else ifeq ($(BUILD_FOR),Darwin) - CXXFLAGS+=-fcolor-diagnostics -I../src -I../asn.1 $(ETPAN_INC) + CXXFLAGS+=-pthread -fcolor-diagnostics -I../src -I../asn.1 $(ETPAN_INC) ifdef WARN CXXFLAGS+= else @@ -181,9 +217,10 @@ endif ######### C and C++ ######### -ifeq ($(BUILD_FOR),Darwin) - CPPFLAGS+=-D_DARWIN_C_SOURCE - LDLIBS+=-liconv +ifeq ($(BUILD_FOR),OS/390) + CPPFLAGS=-W "l,xplink,dll" -W "c,float(ieee),xplink,dll,exportall" +else ifeq ($(BUILD_FOR),Darwin) + CPPFLAGS=-D_DARWIN_C_SOURCE else LDLIBS+=-luuid endif @@ -238,7 +275,11 @@ ETPAN_INC= ######### OpenPGP ######### # Selects OpenPGP implementation. must be `SEQUOIA` +ifeq ($(BUILD_FOR),OS/390) +OPENPGP=NETPGP +else OPENPGP=SEQUOIA +endif # Sequoia-specific variables SEQUOIA_CFLAGS+= @@ -254,6 +295,16 @@ SEQUOIA_INC= # EXTRA_MACROS=-DDEFAULT_KEYSERVER=\"default-server.org\" -DCRASHDUMP_DEFAULT_LINES=23 EXTRA_MACROS= +ifdef PER_USER_DIRECTORY + EXTRA_MACROS+= -DPER_USER_DIRECTORY=\"$(PER_USER_DIRECTORY)\" +endif + +ifdef PER_MACHINE_DIRECTORY + EXTRA_MACROS+= -DPER_MACHINE_DIRECTORY=\"$(PER_MACHINE_DIRECTORY)\" +endif + +CFLAGS+=$(EXTRA_MACROS) +CXXFLAGS+=$(EXTRA_MACROS) ######### Misc ######### # FIXME Maybe include these variables here. @@ -313,7 +364,9 @@ ifndef PEP_MIME_SRC $(error "Compiling with the PEP_MIME option set requires the value of PEP_MIME_SRC to be set to the source directory for libpEpMIME") else CPPFLAGS+=-DPEP_BUILTIN_MIME +ifneq ($(BUILD_ON),OS/390) LDLIBS+=-lstdc++ +endif ETPAN_LIB= ETPAN_INC= endif diff --git a/src/Makefile b/src/Makefile index e85453af..0eb3766e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,14 +16,19 @@ ifdef PER_MACHINE_DIRECTORY EXTRA_MACROS+= -DPER_MACHINE_DIRECTORY='"$(PER_MACHINE_DIRECTORY)"' endif +ifneq ($(BUILD_ON),OS/390) +LDFLAGS+= -shared +endif + NO_SOURCE= ifndef PEP_MIME - CFLAGS+= $(ETPAN_INC) - LDFLAGS+= $(ETPAN_LIB) +ifeq ($(BUILD_ON),OS/390) + LDLIBS+= -letpan-no-mt +else LDLIBS+= -letpan +endif else - LDFLAGS+= -L../pEpMIME -shared LDLIBS+= -lpEpMIME NO_SOURCE+= etpan_mime.c endif @@ -31,10 +36,17 @@ endif CFLAGS+= -I../asn.1 $(EXTRA_MACROS) CPPFLAGS+= -DSQLITE_THREADSAFE=1 -LDFLAGS+= -L../asn.1 -shared -LDLIBS+= -lc -lasn1 +LDFLAGS+= -L../asn.1 +ifneq ($(BUILD_ON),OS/390) +LDFLAGS+= -shared +LDLIBS+= -lc +endif +LDLIBS+= -lasn1 -ifeq ($(BUILD_ON),Darwin) +ifeq ($(BUILD_ON),OS/390) + OPENPGP=NETPGP + LDLIBS+= -lzz +else ifeq ($(BUILD_ON),Darwin) ifeq ($(BUILD_FOR),Darwin) LDLIBS+= -lz -liconv -mmacosx-version-min=10.10 else @@ -62,6 +74,13 @@ ifeq ($(OPENPGP),SEQUOIA) CFLAGS+= $(SEQUOIA_CFLAGS) $(SEQUOIA_INC) LDFLAGS+= $(SEQUOIA_LDFLAGS) LDLIBS+= $(SEQUOIA_LIB) + NO_SOURCE+=pgp_netpgp.c +else ifeq ($(OPENPGP),NETPGP) + CPPFLAGS+= -DUSE_NETPGP + CFLAGS+= $(NETPGP_CFLAGS) $(NETPGP_INC) + LDFLAGS+= $(NETPGP_LDFLAGS) + LDLIBS+= $(NETPGP_LIB) + NO_SOURCE+=pgp_sequoia.c else $(error Unknown OpenPGP library: $(OPENPGP)) endif @@ -69,6 +88,10 @@ endif ALL_SOURCE=$(filter-out $(NO_SOURCE),$(wildcard *.c)) DEPENDS=$(subst .c,.d,$(ALL_SOURCE)) ALL_OBJECTS=$(subst .c,.o,$(ALL_SOURCE)) +ifeq ($(BUILD_ON),OS/390) + ALL_OBJECTS+=/usr/lib/GSKCMS64.x + ALL_OBJECTS+=/usr/lib/CSNPCA64.x +endif all: $(TARGET) @@ -86,7 +109,7 @@ ifneq ($(MAKECMDGOALS),clean) endif $(TARGET): libpEpEngine.a - $(CC) $(CFLAGS) $(CPPFLAGS) $(ALL_OBJECTS) $(LDFLAGS) $(LDLIBS) -o $@ + $(CC) -o $@ $(CPPFLAGS) $(LDFLAGS) $(CFLAGS) $(ALL_OBJECTS) $(LDLIBS) .PHONY: objects clean install_headers install uninstall beinstall diff --git a/src/cryptotech.c b/src/cryptotech.c index 92cb7008..94e1983d 100644 --- a/src/cryptotech.c +++ b/src/cryptotech.c @@ -6,10 +6,11 @@ #include "pEp_internal.h" -#ifdef USE_SEQUOIA +#if defined(USE_SEQUOIA) #include "pgp_sequoia.h" +#elif defined(USE_NETPGP) +#include "pgp_netpgp.h" #endif -// #include #include diff --git a/src/etpan_mime.c b/src/etpan_mime.c index d2b94a67..755336b2 100644 --- a/src/etpan_mime.c +++ b/src/etpan_mime.c @@ -566,7 +566,9 @@ struct mailimf_date_time * timestamp_to_etpantime(const timestamp *ts) result->dt_day = ts->tm_mday; result->dt_month = ts->tm_mon + 1; result->dt_year = ts->tm_year + 1900; +#ifndef ZOS result->dt_zone = (int) (ts->tm_gmtoff / 36L); +#endif return result; } @@ -585,13 +587,16 @@ timestamp * etpantime_to_timestamp(const struct mailimf_date_time *et) result->tm_mday = et->dt_day; result->tm_mon = et->dt_month - 1; result->tm_year = et->dt_year - 1900; +#ifndef ZOS result->tm_gmtoff = 36L * (long) et->dt_zone; +#endif // Normalize to UTC and then forget the offset. time_t t = timegm_with_gmtoff(result); gmtime_r(&t, result); +#ifndef ZOS result->tm_gmtoff = 0; - +#endif return result; } diff --git a/src/openpgp_compat.h b/src/openpgp_compat.h index 9b9aabc4..d8ce0575 100644 --- a/src/openpgp_compat.h +++ b/src/openpgp_compat.h @@ -19,8 +19,10 @@ extern "C" { #include "dynamic_api.h" #include "stringpair.h" -#ifdef USE_SEQUOIA +#if defined(USE_SEQUOIA) #include "pgp_sequoia.h" +#elif defined(USE_NETPGP) +#include "pgp_netpgp.h" #endif /** diff --git a/src/pEp_internal.h b/src/pEp_internal.h index c4090d23..38ff0c22 100644 --- a/src/pEp_internal.h +++ b/src/pEp_internal.h @@ -75,7 +75,9 @@ #define LOCAL_DB windoze_local_db() #define SYSTEM_DB windoze_system_db() #else // UNIX +#ifndef ZOS #define _POSIX_C_SOURCE 200809L +#endif #include #define LOCAL_DB unix_local_db() #ifdef ANDROID @@ -108,11 +110,6 @@ #include "keymanagement_internal.h" #include "message_api_internal.h" -// If not specified, build for Sequoia -#ifndef USE_SEQUOIA -#define USE_SEQUOIA -#endif - #if defined(USE_SEQUOIA) #include "pgp_sequoia_internal.h" #endif diff --git a/src/pgp_netpgp.c b/src/pgp_netpgp.c new file mode 100644 index 00000000..6eab60a1 --- /dev/null +++ b/src/pgp_netpgp.c @@ -0,0 +1,1802 @@ +// This file is under GNU General Public License 3.0 +// see LICENSE.txt + +/* +** Check to see if this machine uses EBCDIC. (Yes, believe it or +** not, there are still machines out there that use EBCDIC.) +*/ +#if 'A' == '\301' +# define NETPGP_EBCDIC 1 +#else +# define NETPGP_ASCII 1 +#endif + +#include "pEp_internal.h" +#include "pgp_netpgp.h" + +#include +#include + +#include "wrappers.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if defined(NETPGP_EBCDIC) +#include +#endif + +#if 0 +#define TRACE_FUNCS() printf("Trace fun: %s:%d, %s\n",__FILE__,__LINE__,__FUNCTION__); +#else +#define TRACE_FUNCS() +#endif + +#define _ENDL "\\s*(\r\n|\r|\n)" + +inline char A(char c) +{ + TRACE_FUNCS() +#if defined(NETPGP_EBCDIC) + __e2a_l(&c,1); +#endif + return c; +} + +netpgp_t *netpgp; +static pthread_mutex_t netpgp_mutex; + +static PEP_STATUS init_netpgp() +{ + TRACE_FUNCS() + PEP_STATUS status = PEP_STATUS_OK; + const char *home = NULL; + pgp_io_t *io; + + if(pthread_mutex_init(&netpgp_mutex, NULL)){ + return PEP_OUT_OF_MEMORY; + } + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + if (strcmp(setlocale(LC_ALL, NULL), "C") == 0) + setlocale(LC_ALL, ""); + + netpgp=malloc(sizeof(netpgp_t)); + memset(netpgp, 0x0, sizeof(netpgp_t)); + + //netpgp_setvar(netpgp, "max mem alloc", "4194304"); + netpgp_setvar(netpgp, "need seckey", "1"); + netpgp_setvar(netpgp, "need pubkey", "1"); + netpgp_setvar(netpgp, "batch", "1"); + + //pgp_set_debug_level("keyring.c"); + //netpgp_setvar(netpgp, "need userid", "1"); + + if (!home) + home = getenv("HOME"); + + if (!home) + status = PEP_INIT_CRYPTO_LIB_INIT_FAILED; + + if(home){ + netpgp_set_homedir(netpgp,(char*)home, "/.netpgp", 0); + }else{ + status = PEP_INIT_NO_CRYPTO_HOME; + goto unlock_netpgp; + } + + // pair with gpg's cert-digest-algo + netpgp_setvar(netpgp, "hash", "SHA256"); + + // subset of gpg's personal-cipher-preferences + // here only one cipher can be selected + netpgp_setvar(netpgp, "cipher", "CAST5"); + + if (!netpgp_init(netpgp)) { + status = PEP_INIT_CRYPTO_LIB_INIT_FAILED; + free(netpgp); + netpgp=NULL; + goto unlock_netpgp; + } + + //pgp_set_debug_level("packet-parse.c"); + //pgp_set_debug_level("openssl_crypto.c"); + //pgp_set_debug_level("crypto.c"); + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return status; +} + +static void release_netpgp() +{ + TRACE_FUNCS() + if(pthread_mutex_lock(&netpgp_mutex)){ + return; + } + netpgp_end(netpgp); + memset(netpgp, 0x0, sizeof(netpgp_t)); + + pthread_mutex_destroy(&netpgp_mutex); + + return; +} + +PEP_STATUS pgp_init(PEP_SESSION session, bool in_first) +{ + TRACE_FUNCS() + PEP_STATUS status = PEP_STATUS_OK; + + assert(session); + if(!session) return PEP_ILLEGAL_VALUE; + + if (in_first) { + if((status = init_netpgp()) != PEP_STATUS_OK) + return status; + } + + return PEP_STATUS_OK; +} + +void pgp_release(PEP_SESSION session, bool out_last) +{ + TRACE_FUNCS() + assert(session); + if(!session) return; + + if (out_last){ + release_netpgp(); + } +} + +// return 1 if the file contains ascii-armoured text +static unsigned +_armoured(const char *buf, size_t size, const char *pattern) +{ + TRACE_FUNCS() + unsigned armoured = 0; + regex_t r; + if( regcomp(&r, pattern, REG_EXTENDED|REG_NOSUB) ) printf("_armoured error\n"); + if (regexec(&r, buf, size, 0, NULL) == 0) { + armoured = 1; + } + regfree(&r); + return armoured; +} + +// Iterate through netpgp' reported valid signatures +// fill a list of valid figerprints +// returns PEP_STATUS_OK if all sig reported valid +// error status otherwise. +static PEP_STATUS _validation_results( + netpgp_t *netpgp, + pgp_validation_t *vresult, + stringlist_t **keylist + ) +{ + TRACE_FUNCS() + time_t now; + time_t t; + + *keylist = NULL; + + now = time(NULL); + if (now < vresult->birthtime) { + // signature is not valid yet + return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH; + } + if (vresult->duration != 0 && now > vresult->birthtime + vresult->duration) { + // signature has expired + t = vresult->duration + vresult->birthtime; + return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH; + } + if (vresult->validc && vresult->valid_sigs && + !vresult->invalidc && !vresult->unknownc ) { + + stringlist_t *_keylist; + + // caller responsible to free + _keylist = new_stringlist(NULL); + assert(_keylist); + if (_keylist == NULL) { + return PEP_OUT_OF_MEMORY; + } + + stringlist_t *k = _keylist; + unsigned c = 0; + for (unsigned n = 0; n < vresult->validc; ++n) { + unsigned from = 0; + const pgp_key_t *signer; + char *fprstr = NULL; + const uint8_t *keyid = vresult->valid_sigs[n].signer_id; + + signer = pgp_getkeybyid(netpgp->io, netpgp->pubring, + keyid, &from, NULL, NULL, + 0, 0); /* check neither revocation nor expiry + as is should be checked already */ + if(signer) + uint_to_string(signer->pubkeyfpr.fingerprint, &fprstr, signer->pubkeyfpr.length); + else + continue; + + if (fprstr == NULL){ + free_stringlist(_keylist); + return PEP_OUT_OF_MEMORY; + } + + k = stringlist_add(k, fprstr); + + free(fprstr); + + if(!k){ + free_stringlist(_keylist); + return PEP_OUT_OF_MEMORY; + } + + c++; + } + if(c > 0) { + *keylist = _keylist; + return PEP_STATUS_OK; + } + + free_stringlist(_keylist); + return PEP_VERIFY_NO_KEY; + } + if (vresult->validc + vresult->invalidc + vresult->unknownc == 0) { + // No signatures found - is this memory signed? + return PEP_VERIFY_NO_KEY; + } + + if (vresult->invalidc) { + // some invalid signatures + return PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH; + } + + // only unknown sigs + return PEP_DECRYPTED; +} + +#define ARMOR_HEAD "^-----BEGIN PGP MESSAGE-----"_ENDL +PEP_STATUS pgp_decrypt_and_verify( + PEP_SESSION session, const char *ctext, size_t csize, + const char *dsigtext, size_t dsigsize, + char **ptext, size_t *psize, stringlist_t **keylist, + char** filename_ptr // will be ignored + ) +{ + TRACE_FUNCS() + char *_ptext = NULL; + char* passphrase = NULL; + + PEP_STATUS result; + stringlist_t *_keylist = NULL; + + assert(session); + assert(ctext); + assert(csize); + assert(ptext); + assert(psize); + assert(keylist); + + passphrase = session->curr_passphrase; + if(passphrase && passphrase[0]) { + netpgp_set_validation_password(passphrase); + } + + if(!session || !ctext || !csize || !ptext || !psize || !keylist) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + *ptext = NULL; + *psize = 0; + *keylist = NULL; + + pgp_validation_t *vresult = malloc(sizeof(pgp_validation_t)); + memset(vresult, 0x0, sizeof(pgp_validation_t)); + + key_id_t *recipients_key_ids = NULL; + unsigned recipients_count = 0; + + pgp_memory_t *mem = pgp_decrypt_and_validate_buf( + netpgp, + vresult, + ctext, csize, + netpgp->secring, netpgp->pubring, + _armoured(ctext, csize, ARMOR_HEAD), + &recipients_key_ids, &recipients_count + ); + + if (mem == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + + const size_t _psize = pgp_mem_len(mem); + if (_psize){ + if ((_ptext = malloc(_psize + 1)) == NULL) { + result = PEP_OUT_OF_MEMORY; + goto free_pgp; + } + memcpy(_ptext, pgp_mem_data(mem), _psize); + _ptext[_psize] = '\0'; // safeguard for naive users + result = PEP_DECRYPTED; + }else{ + result = PEP_DECRYPT_NO_KEY; + goto free_pgp; + } + + if (result == PEP_DECRYPTED) { + result = _validation_results(netpgp, vresult, &_keylist); + printf("result=%x\n",result); + if (result == PEP_DECRYPTED || result == PEP_VERIFY_NO_KEY) { + if((_keylist = new_stringlist("")) == NULL) { + result = PEP_OUT_OF_MEMORY; + goto free_ptext; + } + result = PEP_DECRYPTED; + }else if (result != PEP_STATUS_OK) { + goto free_ptext; + }else{ + result = PEP_DECRYPTED_AND_VERIFIED; + } + } + + stringlist_t *k = _keylist; + for (unsigned n = 0; n < recipients_count; ++n) { + unsigned from = 0; + const pgp_key_t *rcpt; + char *fprstr = NULL; + key_id_t *keyid = &recipients_key_ids[n]; + + rcpt = pgp_getkeybyid(netpgp->io, netpgp->pubring, + *keyid, &from, NULL, NULL, + 0, 0); /* check neither revocation nor expiry*/ + if(rcpt) + uint_to_string(rcpt->pubkeyfpr.fingerprint, &fprstr, rcpt->pubkeyfpr.length); + else + // if no key found put ID instead of fpr + uint_to_string(*keyid, &fprstr, sizeof(key_id_t)); + + if (fprstr == NULL){ + result = PEP_OUT_OF_MEMORY; + goto free_keylist; + } + + k = stringlist_add_unique(k, fprstr); + + free(fprstr); + + if(!k){ + result = PEP_OUT_OF_MEMORY; + goto free_keylist; + } + } + + if (result == PEP_DECRYPTED_AND_VERIFIED || result == PEP_DECRYPTED) { + *ptext = _ptext; + *psize = _psize; + (*ptext)[*psize] = 0; // safeguard for naive users + *keylist = _keylist; + + /* _ptext and _keylist ownership transfer, don't free */ + goto free_pgp; + } + +free_keylist: + free_stringlist(_keylist); + +free_ptext: + free(_ptext); + +free_pgp: + pgp_memory_free(mem); + pgp_validate_result_free(vresult); + +unlock_netpgp: + free(recipients_key_ids); + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +#define ARMOR_SIG_HEAD "^-----BEGIN PGP (SIGNATURE|SIGNED MESSAGE)-----"_ENDL +PEP_STATUS pgp_verify_text( + PEP_SESSION session, const char *text, size_t size, + const char *signature, size_t sig_size, stringlist_t **keylist + ) +{ + TRACE_FUNCS() + pgp_memory_t *signedmem; + pgp_memory_t *sig; + pgp_validation_t *vresult; + + PEP_STATUS result; + stringlist_t *_keylist; + + assert(session); + assert(text); + assert(size); + assert(signature); + assert(sig_size); + assert(keylist); + + if(!session || !text || !size || !signature || !sig_size || !keylist) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + *keylist = NULL; + + vresult = malloc(sizeof(pgp_validation_t)); + if (vresult == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + memset(vresult, 0x0, sizeof(pgp_validation_t)); + + signedmem = pgp_memory_new(); + if (signedmem == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + pgp_memory_add(signedmem, (const uint8_t*)text, size); + + sig = pgp_memory_new(); + if (sig == NULL) { + pgp_memory_free(signedmem); + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + pgp_memory_add(sig, (const uint8_t*)signature, sig_size); + + pgp_validate_mem_detached(netpgp->io, vresult, sig, + NULL,/* output */ + _armoured(signature, sig_size, ARMOR_SIG_HEAD), + netpgp->pubring, + signedmem); + + result = _validation_results(netpgp, vresult, &_keylist); + if (result != PEP_STATUS_OK) { + goto free_pgp; + }else{ + result = PEP_VERIFIED; + } + + if (result == PEP_VERIFIED) { + /* TODO : check trust level */ + result = PEP_VERIFIED_AND_TRUSTED; + } + + if (result == PEP_VERIFIED || result == PEP_VERIFIED_AND_TRUSTED) { + *keylist = _keylist; + + /* _keylist ownership transfer, don't free */ + goto free_pgp; + } + + free_stringlist(_keylist); + +free_pgp: + // free done by pgp_validate_mem_detached + // pgp_memory_free(sig); + // pgp_memory_free(signedmem); + pgp_validate_result_free(vresult); + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +PEP_STATUS pgp_sign_only( + PEP_SESSION session, const char* fpr, const char *ptext, + size_t psize, char **stext, size_t *ssize + ) +{ + TRACE_FUNCS() + pgp_key_t *signer = NULL; + pgp_seckey_t *seckey = NULL; + pgp_memory_t *signedmem = NULL; + pgp_memory_t *text = NULL; + pgp_output_t *output; + + const char *hashalg; + pgp_keyring_t *snrs; + + pgp_create_sig_t *sig; + uint8_t keyid[PGP_KEY_ID_SIZE]; + + PEP_STATUS result; + + assert(session); + assert(fpr); + assert(ptext); + assert(psize); + assert(stext); + assert(ssize); + + if(!session || !ptext || !psize || !stext || !ssize || !fpr || !fpr[0]) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + *stext = NULL; + *ssize = 0; + + if ((snrs = calloc(1, sizeof(*snrs))) == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + + assert(fpr && fpr[0]); + + uint8_t *uint_fpr = NULL; + size_t fprlen; + unsigned from = 0; + + if (string_to_uint(fpr, &uint_fpr, &fprlen)) { + if ((signer = (pgp_key_t *)pgp_getkeybyfpr(netpgp->io, netpgp->secring, uint_fpr, fprlen, &from, NULL, 1,1)) == NULL) { + /* reject revoked and expired */ + result = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto free_snrs; + } + } else{ + result = PEP_ILLEGAL_VALUE; + goto free_snrs; + } + + // add key to signers + pgp_keyring_add(snrs, signer); + if(snrs->keys == NULL){ + result = PEP_OUT_OF_MEMORY; + goto free_snrs; + } + + /* Empty keylist ?*/ + if(snrs->keyc == 0){ + result = PEP_ILLEGAL_VALUE; + goto free_snrs; + } + + seckey = pgp_key_get_certkey(signer); + + /* No signing key. Revoked ? */ + if(seckey == NULL){ + result = PEP_GET_KEY_FAILED; + goto free_snrs; + } + + hashalg = netpgp_getvar(netpgp, "hash"); + + const char *_stext; + size_t _ssize; + + text = pgp_memory_new(); + pgp_memory_add(text, (const uint8_t*)ptext, psize); + + pgp_setup_memory_write(&output, &signedmem, psize); + pgp_writer_push_armor_msg(output); + + pgp_hash_alg_t hash_alg = pgp_str_to_hash_alg(hashalg); + + sig = pgp_create_sig_new(); + pgp_start_sig(sig, seckey, hash_alg, PGP_SIG_BINARY); + + pgp_sig_add_data(sig, pgp_mem_data(text), pgp_mem_len(text)); + pgp_memory_free(text); + + pgp_add_creation_time(sig, time(NULL)); + pgp_add_sig_expiration_time(sig, 0); + pgp_keyid(keyid, sizeof(keyid), &seckey->pubkey, hash_alg); + pgp_add_issuer_keyid(sig, keyid); + pgp_end_hashed_subpkts(sig); + + pgp_write_sig(output, sig, &seckey->pubkey, seckey); + pgp_writer_close(output); + pgp_create_sig_delete(sig); + + if (!signedmem) { + result = PEP_UNENCRYPTED; + goto free_snrs; + } + _stext = (char*) pgp_mem_data(signedmem); + _ssize = pgp_mem_len(signedmem); + + // Allocate transferable buffer + char *_buffer = malloc(_ssize + 1); + + assert(_buffer); + if (_buffer == NULL) { + result = PEP_OUT_OF_MEMORY; + goto free_signedmem; + } + + memcpy(_buffer, _stext, _ssize); + *stext = _buffer; + *ssize = _ssize; + (*stext)[*ssize] = 0; // safeguard for naive users + + result = PEP_STATUS_OK; + +free_signedmem : + pgp_memory_free(signedmem); +free_snrs : + pgp_keyring_free(snrs); +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + + +PEP_STATUS pgp_encrypt_and_sign( + PEP_SESSION session, const stringlist_t *keylist, const char *ptext, + size_t psize, char **ctext, size_t *csize + ) +{ + TRACE_FUNCS() + PEP_STATUS result = PEP_STATUS_OK; + stringlist_t *klp; + int ret; + uint8_t *fpr = NULL; + unsigned from; + size_t len = 0; + + pgp_memory_t *mem = NULL; + pgp_keyring_t *rcpts = NULL; + pgp_seckey_t *seckey = NULL; + pgp_key_t *signer = NULL; + pgp_key_t *key = NULL; + + assert(netpgp->secring); + + string_to_uint(keylist->value, &fpr, &len); + + from = 0; + signer = pgp_getkeybyfpr(netpgp->io, netpgp->secring, fpr, len, &from, NULL, 0, 0 ); + if(!signer) return PEP_KEY_NOT_FOUND; + + rcpts = malloc(sizeof(pgp_keyring_t)); + memset(rcpts,0,sizeof(pgp_keyring_t)); + + klp = keylist; + while(klp) { + string_to_uint(klp->value, &fpr, &len); + from = 0; + key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, len, &from, NULL, 0, 0 ); + if(key) { + pgp_keyring_add(rcpts, key); + } + klp = klp->next; + } + + mem = netpgp_encrypt_and_sign(netpgp, rcpts, seckey, ptext, psize, true, 1); + + free(rcpts); + + return result; +} + +PEP_STATUS pgp_encrypt_only( + PEP_SESSION session, const stringlist_t *keylist, const char *ptext, + size_t psize, char **ctext, size_t *csize + ) +{ + TRACE_FUNCS() + PEP_STATUS result = PEP_STATUS_OK; + pgp_memory_t *mem; + pgp_keyring_t *rcpts = NULL; + unsigned from; + size_t len = 0; + uint8_t *fpr = NULL; + pgp_key_t *key = NULL; + stringlist_t *klp; + + rcpts = malloc(sizeof(pgp_keyring_t)); + memset(rcpts,0,sizeof(pgp_keyring_t)); + + klp = keylist; + while(klp) { + string_to_uint(klp->value, &fpr, &len); + from = 0; + key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, len, &from, NULL, 0, 0 ); + if(key) { + pgp_keyring_add(rcpts, key); + } + klp = klp->next; + } + + mem = netpgp_encrypt_and_sign(netpgp, rcpts, NULL, ptext, psize, false, 1); + + free(rcpts); + + return result; +} + + +PEP_STATUS pgp_generate_keypair( + PEP_SESSION session, pEp_identity *identity + ) +{ + TRACE_FUNCS() + pgp_key_t newseckey; + pgp_key_t *newpubkey; + + PEP_STATUS result; + char newid[1024]; + const char *hashalg; + const char *cipher; + + assert(session); + assert(identity); + assert(identity->address); + assert(identity->fpr == NULL); + assert(identity->username); + + if(!session || !identity || + !identity->address || identity->fpr || !identity->username) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + if(snprintf(newid, sizeof(newid), + "%s <%s>", identity->username, identity->address) >= sizeof(newid)){ + result = PEP_BUFFER_TOO_SMALL; + goto unlock_netpgp; + } + + hashalg = netpgp_getvar(netpgp, "hash"); + cipher = netpgp_getvar(netpgp, "cipher"); + + bzero(&newseckey, sizeof(newseckey)); + + // Generate the key + if (!pgp_rsa_generate_keypair(&newseckey, 4096, 65537UL, hashalg, cipher, + (const uint8_t *) "", (const size_t) 0)) + { + result = PEP_CANNOT_CREATE_KEY; + goto free_seckey; + } + + /* make a public key out of generated secret key */ + if((newpubkey = pgp_ensure_pubkey( + netpgp->pubring, + &newseckey.key.seckey.pubkey, + newseckey.pubkeyid))==NULL) + { + result = PEP_OUT_OF_MEMORY; + goto free_seckey; + } + + // "Expire-Date: 1y\n"; + if (!pgp_add_selfsigned_userid(&newseckey, newpubkey, + (uint8_t *)newid, 365*24*3600)) + { + result = PEP_CANNOT_CREATE_KEY; + goto delete_pubkey; + } + + if (newpubkey == NULL) + { + result = PEP_OUT_OF_MEMORY; + goto delete_pubkey; + } + + // Append key to netpgp's rings (key ownership transfered) + if (!pgp_keyring_add(netpgp->secring, &newseckey)){ + result = PEP_OUT_OF_MEMORY; + goto delete_pubkey; + } + + // save rings + if (netpgp_save_pubring(netpgp) && netpgp_save_secring(netpgp)) + { + char *fprstr = NULL; + uint_to_string(newseckey.pubkeyfpr.fingerprint, &fprstr, newseckey.pubkeyfpr.length); + + if (fprstr == NULL) { + result = PEP_OUT_OF_MEMORY; + goto pop_secring; + } + + /* keys saved, pass fingerprint back */ + identity->fpr = fprstr; + result = PEP_STATUS_OK; + + /* free nothing, everything transfered */ + goto unlock_netpgp; + } else { + /* XXX in case only pubring save succeed + * pubring file is left as-is, but backup restore + * could be attempted if such corner case matters */ + result = PEP_UNKNOWN_ERROR; + } + +pop_secring: + ((pgp_keyring_t *)netpgp->secring)->keyc--; +delete_pubkey: + pgp_deletekeybyfpr(netpgp->io, + (pgp_keyring_t *)netpgp->pubring, + newseckey.pubkeyfpr.fingerprint, + newseckey.pubkeyfpr.length); +free_seckey: + pgp_key_free(&newseckey); +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +PEP_STATUS pgp_delete_keypair(PEP_SESSION session, const char *fprstr) +{ + TRACE_FUNCS() + uint8_t *fpr; + size_t length; + + PEP_STATUS result; + + assert(session); + assert(fprstr); + + if (!session || !fprstr) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + if (string_to_uint(fprstr, &fpr, &length)) { + unsigned insec = pgp_deletekeybyfpr(netpgp->io, + (pgp_keyring_t *)netpgp->secring, + (const uint8_t *)fpr, length); + unsigned inpub = pgp_deletekeybyfpr(netpgp->io, + (pgp_keyring_t *)netpgp->pubring, + (const uint8_t *)fpr, length); + if(!insec && !inpub){ + result = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } else { + result = PEP_STATUS_OK; + } + }else{ + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + + // save rings + if (netpgp_save_pubring(netpgp) && + netpgp_save_secring(netpgp)) + { + result = PEP_STATUS_OK; + }else{ + result = PEP_UNKNOWN_ERROR; + } + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +void stringlist_from_keyring(pgp_keyring_t* keyring, stringlist_t **list) +{ + TRACE_FUNCS() + + int i; + char *fpr; + pgp_key_t key; + + for(i = 0; i < keyring->keyc; i++) { + key = keyring->keys[i]; + if(uint_to_string(key.pubkeyfpr.fingerprint, &fpr, PGP_FINGERPRINT_SIZE)) { + if(!*list) *list = new_stringlist(fpr); + else stringlist_add(*list, fpr); + } + } +} + +pEp_identity *ident_from_uid_fpr(char *uid, uint8_t *fpr) +{ + char *address; + char *user_id; + char *username; + + pEp_identity *ident; + ident = malloc(sizeof(pEp_identity)); + + ident->fpr = NULL; + uint_to_string(fpr, &ident->fpr, PGP_FINGERPRINT_SIZE); + printf("FPR: %s\n",ident->fpr); + + username=strtok(uid, "<"); + if(username[strlen(username)-1]==' ') username[strlen(username)-1]=0; + ident->username=malloc(strlen(username)); + strcpy(ident->username,username); + printf("User name: %s\n",ident->username); + + address=strtok(NULL, ">"); + ident->address=malloc(strlen(address)); + strcpy(ident->address,address); + printf("Address: %s\n",ident->address); + + user_id=strtok(address, "@"); + ident->user_id=malloc(strlen(user_id)); + strcpy(ident->user_id,user_id); + printf("User ID: %s\n",ident->address); + + return ident; +} + +identity_list *add_idents_from_keyring(pgp_keyring_t* keyring, identity_list *list) +{ + TRACE_FUNCS() + int i, j; + pgp_key_t key; + char *uid; + pEp_identity *ident; + + for(i = 0; i < keyring->keyc; i++ ) { + key = keyring->keys[i]; + for(j = 0; j < key.uidc; j++) { + uid = strdup(key.uids[j]); + ident = ident_from_uid_fpr(uid, key.pubkeyfpr.fingerprint); + list = identity_list_add(list, ident); + } + } + + return list; +} + +#define ARMOR_KEY_HEAD "^-----BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK-----"_ENDL +PEP_STATUS pgp_import_keydata(PEP_SESSION session, const char *key_data, + size_t size, identity_list **private_idents, + stringlist_t** imported_keys, + uint64_t* changed_key_index) +{ + TRACE_FUNCS() + + int ret; + PEP_STATUS result = PEP_STATUS_OK; + char* passphrase = NULL; + + if (!imported_keys && changed_key_index) + return PEP_ILLEGAL_VALUE; + + stringlist_t* key_fprs = NULL; + pgp_memory_t *mem = NULL; + + assert(session); + assert(key_data); + + passphrase = session->curr_passphrase; + if(passphrase && passphrase[0]) { + netpgp_set_validation_password(passphrase); + } + + //pgp_set_debug_level("validate.c"); + //pgp_set_debug_level("reader.c"); + + if(!session || !key_data) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + mem = pgp_memory_new(); + if (mem == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + + pgp_memory_add(mem, (const uint8_t*)key_data, size); + ret = pgp_keyring_read_from_mem(netpgp, netpgp->pubring, netpgp->secring, _armoured(key_data, size, ARMOR_KEY_HEAD), mem, 1); + switch(ret) { + case PGP_PASSWORD_REQUIRED: + printf("Password required\n"); + result=PEP_PASSPHRASE_REQUIRED; + goto unlock_netpgp; + break; + case PGP_WRONG_PASSWORD: + printf("Wrong password\n"); + result=PEP_WRONG_PASSPHRASE; + goto unlock_netpgp; + break; + default: + result=PEP_KEY_IMPORTED; + if(changed_key_index) { + (*changed_key_index)=0; + if (netpgp->pubring->keyc) { + (*changed_key_index)=pow(2,netpgp->pubring->keyc)-1; + } + //(*changed_key_index)=pow(2,netpgp->secring->keyc+netpgp->pubring->keyc)-1; + } + break; + } + pgp_memory_free(mem); + + if(private_idents) { + printf("Return private indents\n"); + (*private_idents) = NULL; + (*private_idents) = add_idents_from_keyring(netpgp->secring, (*private_idents)); + //(*private_idents) = add_idents_from_keyring(netpgp->pubring, (*private_idents)); + } + + if(imported_keys) { + printf("Return list of imported keys\n"); + stringlist_from_keyring(netpgp->pubring, imported_keys); + } + + netpgp_save_pubring(netpgp); + netpgp_save_secring(netpgp); + + // save rings + //if ( !netpgp_save_pubring(netpgp) || !netpgp_save_secring(netpgp) ) + //{ + // result = PEP_UNKNOWN_ERROR; + //} + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +static PEP_STATUS _export_keydata( + pgp_key_t *key, + char **buffer, + size_t *buflen + ) +{ + TRACE_FUNCS() + PEP_STATUS result; + pgp_output_t *output; + pgp_memory_t *mem; + pgp_setup_memory_write(&output, &mem, 128); + + if (mem == NULL || output == NULL) { + return PEP_ILLEGAL_VALUE; + } + + if (!pgp_write_xfer_key(output, key, 1)) { + result = PEP_UNKNOWN_ERROR; + goto free_mem; + } + + *buffer = NULL; + *buflen = pgp_mem_len(mem); + + // Allocate transferable buffer + *buffer = malloc(*buflen + 1); + assert(*buffer); + if (*buffer == NULL) { + result = PEP_OUT_OF_MEMORY; + goto free_mem; + } + + memcpy(*buffer, pgp_mem_data(mem), *buflen); + (*buffer)[*buflen] = 0; // safeguard for naive users + + return PEP_STATUS_OK; + +free_mem : + pgp_teardown_memory_write(output, mem); + + return result; +} + +PEP_STATUS pgp_export_keydata( + PEP_SESSION session, const char *fprstr, char **key_data, size_t *size, + bool secret + ) +{ + TRACE_FUNCS() + pgp_key_t *key; + uint8_t *fpr = NULL; + size_t fprlen; + + PEP_STATUS result; + char *buffer; + size_t buflen; + const pgp_keyring_t *srcring; + + assert(session); + assert(fprstr); + assert(key_data); + assert(size); + + if (secret) + srcring = netpgp->secring; + else + srcring = netpgp->pubring; + + if (!session || !fprstr || !key_data || !size) + return PEP_ILLEGAL_VALUE; + + if(pthread_mutex_lock(&netpgp_mutex)) { + return PEP_UNKNOWN_ERROR; + } + + if (string_to_uint(fprstr, &fpr, &fprlen)) { + unsigned from = 0; + if ((key = (pgp_key_t *)pgp_getkeybyfpr(netpgp->io, srcring, fpr, fprlen, &from, NULL,0,0)) == NULL) { + result = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + }else{ + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + + result = _export_keydata(key, &buffer, &buflen); + + if(result == PEP_STATUS_OK) + { + *key_data = buffer; + *size = buflen; + result = PEP_STATUS_OK; + } + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +struct HKP_answer { + char *memory; + size_t size; +}; + +PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern) +{ + TRACE_FUNCS() + assert(!"pgp_recv_key not implemented"); + return PEP_UNKNOWN_ERROR; +} + +typedef PEP_STATUS (*find_key_cb_t)(void*, pgp_key_t *); + +static PEP_STATUS find_keys_do(pgp_keyring_t* keyring, + const char *pattern, find_key_cb_t cb, void* cb_arg) +{ + TRACE_FUNCS() + uint8_t *fpr = NULL; + size_t length; + unsigned from; + pgp_key_t *key; + + PEP_STATUS result; + + // Try find a fingerprint in pattern + if (string_to_uint(pattern, &fpr, &length)) { + // Only one fingerprint can match + from = 0; + key = pgp_getkeybyfpr(netpgp->io, keyring, fpr, length, &from, NULL, 0, 0); + if ( key == NULL) { + return PEP_KEY_NOT_FOUND; + } + result = cb(cb_arg, key); + } else { + TRACE_FUNCS() + // Search by name for pattern. Can match many. + from = 0; + result = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + while((key = (pgp_key_t *)pgp_getnextkeybyname(netpgp->io, keyring, (const char *)pattern, &from)) != NULL) { + result = cb(cb_arg, key); + if (result != PEP_STATUS_OK) + break; + from++; + } + } + + return result; +} + +static PEP_STATUS add_key_uint_to_stringinglist(void *arg, pgp_key_t *key) +{ + TRACE_FUNCS() + stringlist_t **keylist = arg; + char *newfprstr = NULL; + + uint_to_string(key->pubkeyfpr.fingerprint, &newfprstr, key->pubkeyfpr.length); + + if (newfprstr == NULL) { + return PEP_OUT_OF_MEMORY; + } else { + + stringlist_add(*keylist, newfprstr); + free(newfprstr); + if (*keylist == NULL) { + return PEP_OUT_OF_MEMORY; + } + } + return PEP_STATUS_OK; +} + +static PEP_STATUS add_secret_key_uint_to_stringinglist(void *arg, pgp_key_t *key) +{ + TRACE_FUNCS() + if (pgp_is_key_secret(key)) { + stringlist_t **keylist = arg; + char *newfprstr = NULL; + uint_to_string(key->pubkeyfpr.fingerprint, &newfprstr, key->pubkeyfpr.length); + if (newfprstr == NULL) { + return PEP_OUT_OF_MEMORY; + } else { + stringlist_add(*keylist, newfprstr); + free(newfprstr); + if (*keylist == NULL) { + return PEP_OUT_OF_MEMORY; + } + } + } + return PEP_STATUS_OK; +} + +static PEP_STATUS add_keyinfo_to_stringpair_list(void* arg, pgp_key_t *key) { + TRACE_FUNCS() + stringpair_list_t** keyinfo_list = (stringpair_list_t**)arg; + stringpair_t* pair = NULL; + char* id_fpr = NULL; + char* primary_userid = (char*)pgp_key_get_primary_userid(key); + +// Unused: +// bool key_revoked = false; + +// PEP_STATUS key_status = pgp_key_revoked(session, id_fpr, &key_revoked); + +// if (key_revoked || key_status == PEP_GET_KEY_FAILED) +// return PEP_STATUS_OK; // we just move on + + uint_to_string(key->pubkeyfpr.fingerprint, &id_fpr, key->pubkeyfpr.length); + + pair = new_stringpair(id_fpr, primary_userid); + + if (pair == NULL) + return PEP_OUT_OF_MEMORY; + + *keyinfo_list = stringpair_list_add(*keyinfo_list, pair); + free(id_fpr); + if (*keyinfo_list == NULL) + return PEP_OUT_OF_MEMORY; + return PEP_STATUS_OK; +} + +PEP_STATUS pgp_find_keys( + PEP_SESSION session, const char *pattern, stringlist_t **keylist + ) +{ + TRACE_FUNCS() + stringlist_t *_keylist, *_k; + + PEP_STATUS result; + + assert(session); + assert(pattern); + assert(keylist); + + if (!session || !pattern || !keylist ) + { + return PEP_ILLEGAL_VALUE; + } + + if (pthread_mutex_lock(&netpgp_mutex)) + { + return PEP_UNKNOWN_ERROR; + } + + *keylist = NULL; + _keylist = new_stringlist(NULL); + if (_keylist == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + _k = _keylist; + + result = find_keys_do(netpgp->pubring, pattern, &add_key_uint_to_stringinglist, &_k); + + if (result == PEP_STATUS_OK) { + *keylist = _keylist; + // Transfer ownership, no free + goto unlock_netpgp; + } + + free_stringlist(_keylist); + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +#define HKP_REQ_PREFIX "keytext=" +#define HKP_REQ_PREFIX_LEN 8 + +PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern) +{ + TRACE_FUNCS() + assert(!"pgp_send_key not implemented"); + return PEP_UNKNOWN_ERROR; +} + + +PEP_STATUS pgp_get_key_rating( + PEP_SESSION session, const char *fprstr, PEP_comm_type *comm_type) +{ + TRACE_FUNCS() + pgp_key_t *key; + uint8_t *fpr = NULL; + unsigned from = 0; + size_t length; + int rating; + + PEP_STATUS status = PEP_STATUS_OK; + + assert(session); + assert(fprstr); + assert(comm_type); + + if (!session || !fprstr || !comm_type ) + return PEP_ILLEGAL_VALUE; + + *comm_type = PEP_ct_unknown; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + if (!string_to_uint(fprstr, &fpr, &length)) { + status = PEP_ILLEGAL_VALUE; + goto unlock_netpgp; + } + + key = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 0, 0); + if(key == NULL) + { + status = PEP_KEY_NOT_FOUND; + goto unlock_netpgp; + } + rating = pgp_key_get_rating(key); + switch(rating){ + case PGP_VALID: + *comm_type = PEP_ct_OpenPGP_unconfirmed; + break; + case PGP_WEAK: + *comm_type = PEP_ct_OpenPGP_weak_unconfirmed; + break; + case PGP_TOOSHORT: + *comm_type = PEP_ct_key_too_short; + break; + case PGP_INVALID: + *comm_type = PEP_ct_key_b0rken; + break; + case PGP_EXPIRED: + *comm_type = PEP_ct_key_expired; + break; + case PGP_REVOKED: + *comm_type = PEP_ct_key_revoked; + break; + default: + break; + } + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return status; +} + +PEP_STATUS pgp_renew_key( + PEP_SESSION session, + const char *fprstr, + const timestamp *ts + ) +{ + TRACE_FUNCS() + pgp_key_t *pkey; + pgp_key_t *skey; + uint8_t fpr[PGP_FINGERPRINT_SIZE]; + size_t length; + unsigned from = 0; + time_t duration; + const uint8_t *primid; + + PEP_STATUS status = PEP_STATUS_OK; + + assert(session); + assert(fprstr); + + if (!session || !fprstr ) + return PEP_ILLEGAL_VALUE; + + if(ts) + { + time_t now, when; + now = time(NULL); + when = mktime((struct tm*)ts); + if(now && when && when > now){ + duration = when - now; + }else{ + return PEP_ILLEGAL_VALUE; + } + }else{ + /* Default 1 year from now */ + duration = 365*24*3600; + } + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + + if (!string_to_uint(fprstr, &fpr, &length)) { + status = PEP_ILLEGAL_VALUE; + goto unlock_netpgp; + } + + pkey = pgp_getkeybyfpr(netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */ + + if(pkey == NULL) + { + status = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + + from = 0; + skey = pgp_getkeybyfpr( netpgp->io, netpgp->secring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */ + + if(skey == NULL) + { + status = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + + if((primid = pgp_key_get_primary_userid(skey)) == NULL) + { + status = PEP_KEY_HAS_AMBIG_NAME; + goto unlock_netpgp; + } + + // FIXME : renew in a more gentle way + if (!pgp_add_selfsigned_userid(skey, pkey, primid, duration)) + { + status = PEP_CANNOT_CREATE_KEY; + goto unlock_netpgp; + } + + // save rings + if (netpgp_save_pubring(netpgp) && + netpgp_save_secring(netpgp)) + { + status = PEP_STATUS_OK; + }else{ + status = PEP_UNKNOWN_ERROR; + } + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return status; +} + +PEP_STATUS pgp_revoke_key( + PEP_SESSION session, + const char *fprstr, + const char *reason + ) +{ + TRACE_FUNCS() + uint8_t *fpr = NULL; + size_t length; + unsigned from = 0; + + PEP_STATUS status = PEP_STATUS_OK; + + assert(session); + assert(fprstr); + + if (!session || !fprstr) + return PEP_UNKNOWN_ERROR; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + // FIXME : deduplicate that code w/ renew + if (!string_to_uint(fprstr, &fpr, &length)) { + status = PEP_ILLEGAL_VALUE; + goto unlock_netpgp; + } + + pgp_key_t *pkey = pgp_getkeybyfpr( netpgp->io, netpgp->pubring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */ + + if(pkey == NULL) + { + status = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + + from = 0; + pgp_key_t *skey = pgp_getkeybyfpr( netpgp->io, netpgp->secring, fpr, length, &from, NULL, 1, 0); /* reject revoked, accept expired */ + + if(skey == NULL) + { + status = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + + pgp_key_revoke(skey, pkey, + 0, /* no reason code specified */ + reason); + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return status; +} + +PEP_STATUS pgp_key_expired( + PEP_SESSION session, + const char *fprstr, + const time_t when, + bool *expired + ) +{ + TRACE_FUNCS() + PEP_STATUS status = PEP_STATUS_OK; + PEP_comm_type comm_type; + + assert(session); + assert(fprstr); + assert(expired); + + if (!session || !fprstr || !expired) + return PEP_UNKNOWN_ERROR; + + // TODO : take "when" in account + + *expired = false; + + status = pgp_get_key_rating(session, fprstr, &comm_type); + + if (status != PEP_STATUS_OK) + return status; + + if (comm_type == PEP_ct_key_expired){ + *expired = true; + } + + return PEP_STATUS_OK; +} + +PEP_STATUS pgp_key_revoked( + PEP_SESSION session, + const char *fprstr, + bool *revoked + ) +{ + TRACE_FUNCS() + PEP_STATUS status = PEP_STATUS_OK; + PEP_comm_type comm_type; + + assert(session); + assert(fprstr); + assert(revoked); + + *revoked = false; + + status = pgp_get_key_rating(session, fprstr, &comm_type); + + if (status != PEP_STATUS_OK) + return status; + + if (comm_type == PEP_ct_key_revoked){ + *revoked = true; + } + + return PEP_STATUS_OK; +} + +PEP_STATUS pgp_key_created( + PEP_SESSION session, + const char *fprstr, + time_t *created + ) +{ + TRACE_FUNCS() + uint8_t *fpr; + pgp_key_t *key; + size_t length; + unsigned from = 0; + + PEP_STATUS status = PEP_STATUS_OK; + + assert(session); + assert(fprstr); + assert(created); + + if (!session || !fprstr || !created) + return PEP_UNKNOWN_ERROR; + + *created = 0; + + if(pthread_mutex_lock(&netpgp_mutex)){ + return PEP_UNKNOWN_ERROR; + } + + if (!string_to_uint(fprstr, &fpr, &length)) { + status = PEP_ILLEGAL_VALUE; + goto unlock_netpgp; + } + + key = pgp_getkeybyfpr( netpgp->io, netpgp->pubring, fpr, length, &from, NULL,0,0); + + if (key) + { + *created = (time_t) key->key.pubkey.birthtime; + } + else + { + status = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + goto unlock_netpgp; + } + + + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return status; +} + + +PEP_STATUS pgp_list_keyinfo( + PEP_SESSION session, const char* pattern, stringpair_list_t** keyinfo_list) +{ + TRACE_FUNCS() + + if (!session || !keyinfo_list) + return PEP_UNKNOWN_ERROR; + + if (pthread_mutex_lock(&netpgp_mutex)) + { + return PEP_UNKNOWN_ERROR; + } + +// Unused: +// pgp_key_t *key; + + PEP_STATUS result; + + result = find_keys_do(netpgp->pubring, pattern, &add_keyinfo_to_stringpair_list, (void*)keyinfo_list); + + if (!keyinfo_list) + result = PEP_KEY_NOT_FOUND; +printf("%s:%d, Key not found\n",__FILE__,__LINE__); + + + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +PEP_STATUS pgp_find_private_keys( + PEP_SESSION session, const char *pattern, stringlist_t **keylist +) +{ + TRACE_FUNCS() + stringlist_t *_keylist, *_k; + + PEP_STATUS result; + + assert(session); + assert(keylist); + + //if (!session || !keylist ) + //{ + // return PEP_ILLEGAL_VALUE; + //} + + if (pthread_mutex_lock(&netpgp_mutex)) + { + return PEP_UNKNOWN_ERROR; + } + + *keylist = NULL; + _keylist = new_stringlist(NULL); + if (_keylist == NULL) { + result = PEP_OUT_OF_MEMORY; + goto unlock_netpgp; + } + _k = _keylist; + + result = find_keys_do(netpgp->secring, pattern, &add_secret_key_uint_to_stringinglist, &_k); + + if (result == PEP_STATUS_OK) { + *keylist = _keylist; + // Transfer ownership, no free + goto unlock_netpgp; + } + + free_stringlist(_keylist); + +unlock_netpgp: + pthread_mutex_unlock(&netpgp_mutex); + + return result; +} + +PEP_STATUS pgp_contains_priv_key( + PEP_SESSION session, + const char *fpr, + bool *has_private) +{ + TRACE_FUNCS() + stringlist_t* keylist = NULL; + PEP_STATUS status = pgp_find_private_keys(session, fpr, &keylist); + if (status == PEP_STATUS_OK && keylist) { + free_stringlist(keylist); + *has_private = true; + } + else { + *has_private = false; + } + return status; +} + +PEP_STATUS pgp_import_ultimately_trusted_keypairs(PEP_SESSION session) { + // Not implemented - netpgp doesn't appear to keep track of trust status in + // a meaningful way, though there is space for it in the structs. + TRACE_FUNCS() + return PEP_STATUS_OK; +} + +PEP_STATUS pgp_config_cipher_suite(PEP_SESSION session, PEP_CIPHER_SUITE suite) { + TRACE_FUNCS() + if (suite == PEP_CIPHER_SUITE_DEFAULT) { + return PEP_STATUS_OK; + } else { + return PEP_CANNOT_CONFIG; + } +} diff --git a/src/pgp_netpgp.h b/src/pgp_netpgp.h new file mode 100644 index 00000000..c54567e9 --- /dev/null +++ b/src/pgp_netpgp.h @@ -0,0 +1,426 @@ +/** + * @file src/pgp_netpgp.h + * + * @brief NETPGP driver - implements required cryptotech + * functions for the engine using netpgp with SSL + * + * @license GNU General Public License 3.0 - see LICENSE.txt + */ + +#ifndef PGP_PEP_NETPGP_H +#define PGP_PEP_NETPGP_H + +#include "pEpEngine.h" + +/** + * + * + * @brief initialise the sequoia driver for this session + * + * @param[in] session session handle + * @param[in] in_first true if this is the first pEp session running + * after startup, else false + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_init(PEP_SESSION session, bool in_first); + +/** + * + * + * @brief release resources used by the sequoia driver in this session + * + * @param[in] session session handle + * @param[in] out_last true if this is the last extant pEp session + * running, else false + * + */ +void pgp_release(PEP_SESSION session, bool out_last); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] ctext const char* + * @param[in] csize size_t + * @param[in] dsigtext const char* + * @param[in] dsigsize size_t + * @param[in] ptext char** + * @param[in] psize size_t* + * @param[in] keylist stringlist_t** + * @param[in] filename_ptr char** + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_decrypt_and_verify( + PEP_SESSION session, const char *ctext, size_t csize, + const char *dsigtext, size_t dsigsize, + char **ptext, size_t *psize, stringlist_t **keylist, + char** filename_ptr + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] keylist const stringlist_t* + * @param[in] ptext const char* + * @param[in] psize size_t + * @param[out] ctext char** + * @param[out] csize size_t* + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_encrypt_and_sign( + PEP_SESSION session, const stringlist_t *keylist, const char *ptext, + size_t psize, char **ctext, size_t *csize + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] ptext const char* + * @param[in] psize size_t + * @param[out] stext char** + * @param[out] ssize size_t* + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_sign_only( + PEP_SESSION session, const char* fpr, const char *ptext, + size_t psize, char **stext, size_t *ssize + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] keylist const stringlist_t* + * @param[in] ptext const char* + * @param[in] psize size_t + * @param[out] ctext char** + * @param[out] csize size_t* + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_encrypt_only( + PEP_SESSION session, const stringlist_t *keylist, const char *ptext, + size_t psize, char **ctext, size_t *csize + ); + + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] text const char* + * @param[in] size size_t + * @param[in] signature const char* + * @param[in] sig_size size_t + * @param[in] keylist stringlist_t** + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_verify_text( + PEP_SESSION session, const char *text, size_t size, + const char *signature, size_t sig_size, stringlist_t **keylist + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_delete_keypair(PEP_SESSION session, const char *fpr); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] key_data char** + * @param[in] size size_t* + * @param[in] secret bool + * + * @retval PEP_STATUS_OK + * @retval any other value on error + */ +PEP_STATUS pgp_export_keydata( + PEP_SESSION session, const char *fpr, char **key_data, size_t *size, + bool secret + ); + +/** + * + * + * @brief Find all keys (as fpr strings) which match this fpr or OpenPGP + * userid pattern + * + * @param[in] session session handle + * @param[in] pattern Pattern to search for; could be empty, + * an fpr, or a mailbox (email, URI, etc). + * @param[in,out] keylist A list of fprs containing matching keys. + * + * @note Unlike pgp_list_keyinfo, this function returns revoked keys. + * + * @see pgp_list_keyinfo() + */ +PEP_STATUS pgp_find_keys( + PEP_SESSION session, const char *pattern, stringlist_t **keylist + ); + +/** + * + * + * @brief Find all keys (returning pairs) which match this fpr + * or OpenPGP userid pattern + * + * @param[in] session session handle + * @param[in] pattern Pattern to search for; could be empty, + * an fpr, or a mailbox (email, URI, etc). + * @param[in,out] keyinfo_list A list of tuples for the + * matching keys. + * + * @note This function filters out revoked keys, but NOT expired keys. + */ +PEP_STATUS pgp_list_keyinfo( + PEP_SESSION session, const char* pattern, stringpair_list_t** keyinfo_list + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] identity pEp_identity* + * + */ +PEP_STATUS pgp_generate_keypair( + PEP_SESSION session, pEp_identity *identity + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] comm_type PEP_comm_type* + * + */ +PEP_STATUS pgp_get_key_rating( + PEP_SESSION session, + const char *fpr, + PEP_comm_type *comm_type + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] key_data const char * + * @param[in] size size_t + * @param[in] private_idents identity_list ** + * @param[in] imported_keys stringlist_t ** + * @param[in] changed_key_index uint64_t * + * + */ +PEP_STATUS pgp_import_keydata(PEP_SESSION session, const char *key_data, + size_t size, identity_list **private_idents, + stringlist_t** imported_keys, + uint64_t* changed_key_index); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] key_data const char* + * @param[in] size size_t + * @param[in] private_idents identity_list** + * + */ +PEP_STATUS pgp_import_private_keydata(PEP_SESSION session, const char *key_data, + size_t size, identity_list **private_idents); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] pattern const char* + * + */ +PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern); +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] pattern const char* + * + */ +PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] ts const timestamp* + * + */ +PEP_STATUS pgp_renew_key( + PEP_SESSION session, + const char *fpr, + const timestamp *ts + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] reason const char* + * + */ +PEP_STATUS pgp_revoke_key( + PEP_SESSION session, + const char *fpr, + const char *reason + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] when const time_t + * @param[in] expired bool* + * + */ +PEP_STATUS pgp_key_expired( + PEP_SESSION session, + const char *fpr, + const time_t when, + bool *expired + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[out] revoked bool* + * + */ +PEP_STATUS pgp_key_revoked( + PEP_SESSION session, + const char *fpr, + bool *revoked + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[out] created time_t* + * + */ +PEP_STATUS pgp_key_created( + PEP_SESSION session, + const char *fpr, + time_t *created + ); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] fpr const char* + * @param[in] has_private bool* + * + */ +PEP_STATUS pgp_contains_priv_key( + PEP_SESSION session, + const char *fpr, + bool *has_private); + +/** + * + * + * @brief Find all keys (as fpr strings) which match this fpr or OpenPGP + * userid pattern AND contain a private key + * + * @param[in] session session handle + * @param[in] pattern Pattern to search for; could be empty, + * an fpr, or a mailbox (email, URI, etc). + * @param[in,out] keylist A list of fprs containing matching keys. + * + * @note Unlike pgp_list_keyinfo, this function returns revoked keys. + * + * @see pgp_list_keyinfo() + */ +PEP_STATUS pgp_find_private_keys( + PEP_SESSION session, const char *pattern, stringlist_t **keylist +); + +/** + * + * + * @brief TODO + * + * @param[in] session session handle + * @param[in] suite PEP_CIPHER_SUITE + * + */ +PEP_STATUS pgp_config_cipher_suite(PEP_SESSION session, + PEP_CIPHER_SUITE suite); + +#endif diff --git a/src/platform_unix.c b/src/platform_unix.c index 46236bf4..81a2cf8d 100644 --- a/src/platform_unix.c +++ b/src/platform_unix.c @@ -4,7 +4,9 @@ // This file is under GNU General Public License 3.0 // see LICENSE.txt +#ifndef ZOS #define _POSIX_C_SOURCE 200809L +#endif #ifdef ANDROID #ifndef __LP64__ @@ -38,9 +40,55 @@ #endif #define SYSTEM_DB_FILENAME "system.db" -#ifdef ANDROID -#include +#ifndef strndup +char *strndup (const char *s, size_t n) +{ + char *result; + size_t len = strnlen (s, n); + + result = (char *) malloc (len + 1); + if (!result) + return 0; + result[len] = '\0'; + return (char *) memcpy (result, s, len); +} +#endif + +#ifndef strnlen +size_t strnlen (const char *s, size_t maxlen) +{ + size_t i; + + for (i = 0; i < maxlen; ++i) + if (s[i] == '\0') + break; + return i; +} +#endif + +#ifndef stpcpy +char *stpcpy(char *dst, const char *src) +{ + for (;; ++dst, ++src) { + *dst = *src; + if (*dst == 0) + break; + } + return dst; +} +#endif + +#ifndef alloca +#pragma linkage(__alloca,builtin) +void *__alloca(unsigned long x); +void *alloca(unsigned long x) +{ + return __alloca(x); +} +#endif + +#if defined(ANDROID) || defined(ZOS) /* FIXME : timegm will miss when linking for x86_64 on android, when supported */ #ifndef __LP64__ time_t timegm(struct tm* const t) { @@ -73,16 +121,6 @@ char *stpncpy(char *dst, const char *src, size_t n) return (dst); } -char *stpcpy(char *dst, const char *src) -{ - for (;; ++dst, ++src) { - *dst = *src; - if (*dst == 0) - break; - } - return dst; -} - /* long int random(void) { @@ -125,8 +163,36 @@ static char *_android_system_db(void) } return buffer; } +#endif + +#ifdef ZOS +char * e2as(const char * str) +{ + char *ret = (char *)malloc(strlen(str)); + strcpy(ret, str); + __e2a_s(ret); + return ret; +} + +char * as2e(const char * str) +{ + char *ret = (char *)malloc(strlen(str)); + strcpy(ret, str); + __a2e_s(ret); + return ret; +} + +void uuid_generate_random(pEpUUID out) +{ +} + +void uuid_unparse_upper(pEpUUID uu, uuid_string_t out) +{ +} +#endif +#ifdef ANDROID void uuid_generate_random(pEpUUID out) { uuid_t *uuid; @@ -147,7 +213,6 @@ void uuid_generate_random(pEpUUID out) } } - void uuid_unparse_upper(pEpUUID uu, uuid_string_t out) { uuid_t *uuid; @@ -171,7 +236,6 @@ void uuid_unparse_upper(pEpUUID uu, uuid_string_t out) uuid_destroy(uuid); } } - #endif #if !defined(BSD) && !defined(__APPLE__) diff --git a/src/platform_unix.h b/src/platform_unix.h index 955462dd..85080a18 100644 --- a/src/platform_unix.h +++ b/src/platform_unix.h @@ -7,7 +7,7 @@ #ifndef PLATFORM_UNIX_H #define PLATFORM_UNIX_H -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(ZOS) #define _POSIX_C_SOURCE 200809L #endif @@ -15,13 +15,30 @@ #include #include #include +#ifndef ZOS #include +#endif #include -#ifndef ANDROID +#ifdef ZOS +char * e2as(const char * str); +char * as2e(const char * str); +#endif + +#if defined(ANDROID) || defined(ZOS) +typedef unsigned char uuid_t[16]; +#else #include #endif +#ifndef MIN +#define MIN(A, B) ((A)>(B) ? (B) : (A)) +#endif + +#ifndef MAX +#define MAX(A, B) ((A)>(B) ? (A) : (B)) +#endif + // pEp files and directories #ifndef PER_USER_DIRECTORY @@ -36,7 +53,6 @@ #endif #endif - #ifdef __cplusplus extern "C" { #endif @@ -152,7 +168,7 @@ typedef char uuid_string_t[37]; // on *nix, uuid_t is an array and already implements pointer semantics #define UUID uuid_t -#ifdef ANDROID +#if defined(ANDROID) || defined(ZOS) typedef char pEpUUID[16]; void uuid_generate_random(pEpUUID out); void uuid_unparse_upper(pEpUUID uu, uuid_string_t out); diff --git a/src/sqlite3.c b/src/sqlite3.c index 7d8779a6..f73c216a 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -19738,7 +19738,7 @@ SQLITE_PRIVATE int sqlite3CorruptPgnoError(int,Pgno); # define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80) #else # define sqlite3Toupper(x) toupper((unsigned char)(x)) -# define sqlite3Isspace(x) isspace((unsigned char)(x)) +# define sqlite3Isspace(x) (isspace((unsigned char)(x))||(x==0x15)) # define sqlite3Isalnum(x) isalnum((unsigned char)(x)) # define sqlite3Isalpha(x) isalpha((unsigned char)(x)) # define sqlite3Isdigit(x) isdigit((unsigned char)(x)) diff --git a/src/stdlib.h b/src/stdlib.h new file mode 100644 index 00000000..5370b1c7 --- /dev/null +++ b/src/stdlib.h @@ -0,0 +1,8 @@ +#ifdef ZOS +#ifdef __cplusplus +#undef _EXT +#define _NO_EXT +#endif +#endif + +#include_next diff --git a/src/string.h b/src/string.h new file mode 100644 index 00000000..ac2323f5 --- /dev/null +++ b/src/string.h @@ -0,0 +1,10 @@ +#ifdef ZOS +#ifndef __cplusplus +char * stpcpy (char *dst, const char *src); +char * strndup (const char *s, size_t n); +size_t strnlen (const char *s, size_t maxlen); +#endif +#include +#endif + +#include_next diff --git a/src/timestamp.c b/src/timestamp.c index 94cdb1d5..d5ac322b 100644 --- a/src/timestamp.c +++ b/src/timestamp.c @@ -22,10 +22,17 @@ DYNAMIC_API time_t timegm_with_gmtoff(const timestamp* ts) _ts = timestamp_dup(ts); if (_ts) { +#if defined(ZOS) + const time_t raw_time = mktime(_ts); + if (raw_time != -1) { + retval = raw_time; + } +#else const time_t raw_time = timegm(_ts); if (raw_time != -1) { - retval = raw_time - ts->tm_gmtoff; + retval = raw_time - _ts->tm_gmtoff; } +#endif } } diff --git a/test/Makefile b/test/Makefile index 5ee55512..c039075c 100644 --- a/test/Makefile +++ b/test/Makefile @@ -16,6 +16,15 @@ SRCS:=$(wildcard src/*.cc) $(wildcard src/*/*.cc) OBJS:=$(addsuffix .o,$(basename $(SRCS))) DEPS:=$(OBJS:.o=.d) +ifeq ($(BUILD_ON),OS/390) + OBJS+=../src/libpEpEngine.a + OBJS+=/usr/lib/GSKCMS64.x + OBJS+=/usr/lib/CSNPCA64.x + OBJS+=/user/lev/lib/libetpan-no-mt.a +endif + +#endif + LDFLAGS+= -L../asn.1 -L../src $(ETPAN_LIB) $(GTEST_LDFLAGS) TARGET:=EngineTests @@ -31,10 +40,12 @@ CPPFLAGS+= -DGTEST_SUITE_SYM=$(GTEST_SUITE_SYM) ifdef PEP_MIME LDLIBS+= -lpEpMIME else - LDLIBS+= -letpan + ifneq ($(BUILD_ON),OS/390) + LDLIBS+= -letpan -lpEpEngine -lstdc++ + endif endif -LDLIBS+= -lpEpEngine -lstdc++ -lasn1 +LDLIBS+= -lasn1 ifeq ($(BUILD_FOR),Linux) LDLIBS+= -luuid @@ -52,8 +63,19 @@ ifdef SQLITE3_FROM_OS LDLIBS+= -lsqlite3 endif -CXXFLAGS:=$(filter-out -DNDEBUG,$(CXXFLAGS)) -I$(GTEST_INC_DIR) -CPPFLAGS:=$(filter-out -DNDEBUG,$(CPPFLAGS)) +ifeq ($(OPENPGP),GPG) + LDLIBS+= -lgpgme +else ifeq ($(OPENPGP),NETPGP) + LDLIBS+=$(NETPGP_LIB) + ifeq ($(BUILD_FOR),Linux) + LDLIBS+= -ltre + endif +endif + +CXXFLAGS:=$(filter-out -DNDEBUG,$(CXXFLAGS)) + +# FIXME Possibly missing incdirs: ASN1C_INC +CXXFLAGS+= -I$(GTEST_INC_DIR) -I./src -I../sync $(INC_FLAGS) -Wno-deprecated -Wno-unused-variable CXXFLAGS+= -I./src -I../src -I ../asn.1 -I../sync $(INC_FLAGS) CXXFLAGS+= -Wno-deprecated -Wno-unused-variable @@ -90,10 +112,17 @@ all: $(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $< \ | sed -e 's,\($*\)\.o[ :]*,\1.o $@: ,g' > $@ -$(TARGET): $(OBJS) - ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS} -L${PREFIX}/lib -o $@ $^ \ - -lpEpEngine -lgtest -lgtest_main -lsequoia_openpgp_ffi $(LDLIBS) +LDLIBS+=-lgtest -lgtest_main +ifeq ($(BUILD_ON),OS/390) + LDLIBS+=-lzz +endif + +ifeq ($(OPENPGP),SEQUOIA) + LDLIBS+=-lsequoia_openpgp_ffi +endif +$(TARGET): $(OBJS) + ${CXX} ${LDFLAGS} -L${PREFIX}/lib -o $@ $^ $(LDLIBS) test: all $(RM) -rf ./pEp_test_home/*