/**************************************************************** * * * Copyright 2009, 2010 Fidelity Information Services, Inc * * * * This source code contains the intellectual property * * of its copyright holder(s), and is made available * * under a license. If you do not know the terms of * * the license, please stop and do not read further. * * * ****************************************************************/ #ifndef GTMCRYPT_H #define GTMCRYPT_H #include #include #include "stack_frame.h" #include "gtmxc_types.h" #include "gtmcrypt_interface.h" #include "gtmimagename.h" #include "gtm_stdio.h" #include "gtm_string.h" #include "gtm_stdlib.h" #include "have_crit.h" #include "deferred_signal_handler.h" #include "gtmmsg.h" #include "gtmci.h" #include "wbox_test_init.h" typedef xc_status_t (*gtmcrypt_init_t)(int); typedef xc_status_t (*gtmcrypt_close_t)(); typedef xc_status_t (*gtmcrypt_hash_gen_t)(gtmcrypt_key_t, xc_string_t *); typedef xc_status_t (*gtmcrypt_encode_t)(gtmcrypt_key_t, xc_string_t *, xc_string_t *); typedef xc_status_t (*gtmcrypt_decode_t)(gtmcrypt_key_t, xc_string_t *, xc_string_t *); typedef xc_status_t (*gtmcrypt_getkey_by_name_t)(xc_string_t *, gtmcrypt_key_t *); typedef xc_status_t (*gtmcrypt_getkey_by_hash_t)(xc_string_t *, gtmcrypt_key_t *); typedef char* (*gtmcrypt_strerror_t)(); GBLREF gtmcrypt_init_t gtmcrypt_init_fnptr; GBLREF gtmcrypt_close_t gtmcrypt_close_fnptr; GBLREF gtmcrypt_hash_gen_t gtmcrypt_hash_gen_fnptr; GBLREF gtmcrypt_encode_t gtmcrypt_encode_fnptr; GBLREF gtmcrypt_decode_t gtmcrypt_decode_fnptr; GBLREF gtmcrypt_getkey_by_name_t gtmcrypt_getkey_by_name_fnptr; GBLREF gtmcrypt_getkey_by_hash_t gtmcrypt_getkey_by_hash_fnptr; GBLREF gtmcrypt_strerror_t gtmcrypt_strerror_fnptr; /* The standard shared library suffix for HPUX on HPPA is .sl. * On HPUX/IA64, the standard suffix was changed to .so (to match other Unixes) but for * the sake of compatibility, they still accept (and look for) .sl if .so is not present. * Nevertheless, we use the standard suffix on all platforms. */ #if (defined(__hpux) && defined(__hppa)) # define GTMCRYPT_LIBNAME "libgtmcrypt.sl" #elif defined(__MVS__) # define GTMCRYPT_LIBNAME "libgtmcrypt.dll" #else # define GTMCRYPT_LIBNAME "libgtmcrypt.so" #endif #define GTMCRYPT_LIBFLAGS (RTLD_NOW | RTLD_GLOBAL) #define GTMCRYPT_INIT_FNAME "gtmcrypt_init" #define GTMCRYPT_CLOSE_FNAME "gtmcrypt_close" #define GTMCRYPT_HASH_GEN_FNAME "gtmcrypt_hash_gen" #define GTMCRYPT_ENCODE_FNAME "gtmcrypt_encode" #define GTMCRYPT_DECODE_FNAME "gtmcrypt_decode" #define GTMCRYPT_GETKEY_BY_NAME "gtmcrypt_getkey_by_name" #define GTMCRYPT_GETKEY_BY_HASH "gtmcrypt_getkey_by_hash" #define GTMCRYPT_STRERROR "gtmcrypt_strerror" #define GTM_PASSWD "gtm_passwd" /* Global variable GBLDEF'ed in gbldefs.c */ GBLREF int4 gtmcrypt_init_state; /* Possible states for encryption library */ typedef enum { GTMCRYPT_UNINITIALIZED, /* This is when, so far in the code, gtmcrypt_setup or gtmcrypt_init hasn't been called */ GTMCRYPT_INITIALIZED, /* This is the state when both dlopen and gtmcrypt_init has successfully passed */ } gtmcrypt_init_values; void gtmcrypt_entry(void); /* =====================================================================================================*/ /* Error Reporting Macros */ /* =====================================================================================================*/ /* Call the plugin's strerror equivalent to get the last error message */ #define GC_GET_ERR_STRING(err) \ { \ assert(NULL != gtmcrypt_strerror_fnptr); \ err = (*gtmcrypt_strerror_fnptr)(); \ } /* ERR_CRYPTKEYFETCHFAILED is a unique error that has to be handled a bit differently. Whenever GT.M calls the plugin * to get the encryption key for a give hash, the plugin is not aware of the filename for which the hash was * passed. Also, GT.M itself might not be aware of the filenames in cases like bin_load. To handle this case, * GC_GTM_PUTMSG and GC_RTS_ERROR will explicitly carry the filename for which the current operation * is being done. In case GT.M cannot figure out the filename, it will pass NULL. The following constructs the * appropriate error message in the event that the filename is present and for the case where GT.M has passed * NULL. */ #define GC_GTM_PUTMSG(err_id, FNAME) \ { \ char *err; \ GBLREF char dl_err[]; \ \ error_def(ERR_CRYPTKEYFETCHFAILED); \ error_def(ERR_CRYPTKEYFETCHFAILEDNF); \ error_def(ERR_CRYPTINIT); \ error_def(ERR_CRYPTDLNOOPEN); \ \ if (ERR_CRYPTDLNOOPEN != err_id) \ { \ GC_GET_ERR_STRING(err); \ if (ERR_CRYPTKEYFETCHFAILED == err_id) \ { \ if (NULL != FNAME) \ gtm_putmsg(VARLSTCNT(6) ERR_CRYPTKEYFETCHFAILED, 4, LEN_AND_STR(FNAME), LEN_AND_STR(err)); \ else \ gtm_putmsg(VARLSTCNT(4) ERR_CRYPTKEYFETCHFAILEDNF, 2, LEN_AND_STR(err)); \ } else \ gtm_putmsg(VARLSTCNT(4) err_id, 2, LEN_AND_STR(err)); \ } else \ gtm_putmsg(VARLSTCNT(4) ERR_CRYPTDLNOOPEN, 2, LEN_AND_STR(dl_err)); \ } /* Fetch the last error string from the encryption plugin. If the error is ERR_KEYFETCHFAILED, we need to * handle them a bit differently as described above. This macro should be called whenever GT.M wants to do * rts_error. */ #define GC_RTS_ERROR(err_id, FNAME) \ { \ char *err; \ GBLREF char dl_err[]; \ \ error_def(ERR_CRYPTKEYFETCHFAILED); \ error_def(ERR_CRYPTKEYFETCHFAILEDNF); \ error_def(ERR_CRYPTINIT); \ error_def(ERR_CRYPTDLNOOPEN); \ \ if (ERR_CRYPTDLNOOPEN != err_id) \ { \ GC_GET_ERR_STRING(err); \ if (ERR_CRYPTKEYFETCHFAILED == err_id) \ { \ if (NULL != FNAME) \ rts_error(VARLSTCNT(6) ERR_CRYPTKEYFETCHFAILED, 4, LEN_AND_STR(FNAME), LEN_AND_STR(err)); \ else \ rts_error(VARLSTCNT(4) ERR_CRYPTKEYFETCHFAILEDNF, 2, LEN_AND_STR(err)); \ } else \ rts_error(VARLSTCNT(4) err_id, 2, LEN_AND_STR(err)); \ } else \ rts_error(VARLSTCNT(4) ERR_CRYPTDLNOOPEN, 2, LEN_AND_STR(dl_err)); \ } /* Following are the error identifiers visible to the GT.M user. Depending on the operation performed, * mark the appropriate status. */ #define GC_MARK_STATUS(status, RC, ERR_ID) \ { \ error_def(ERR_CRYPTDLNOOPEN); \ error_def(ERR_CRYPTINIT); \ error_def(ERR_CRYPTKEYFETCHFAILED); \ error_def(ERR_CRYPTOPFAILED); \ error_def(ERR_CRYPTHASHGENFAILED); \ \ RC = (0 != status) ? ERR_ID : 0; \ } /* =====================================================================================================*/ /* Utility Macros */ /* =====================================================================================================*/ /* Package the required addresses in various xc_string_t which can be used by various decode, encode macros */ #define PACKAGE_XCSTRING(xcstring, buf, buflen) \ { \ xcstring.address = buf; \ xcstring.length = buflen; \ } /* =====================================================================================================*/ /* GT.M Related Macros */ /* =====================================================================================================*/ #define IS_BLK_ENCRYPTED(LEVL, BSIZ) ((0 <= ((char)LEVL)) && (0 < BSIZ)) #define BLK_NEEDS_ENCRYPTION(LEVL, BSIZ) IS_BLK_ENCRYPTED(LEVL, BSIZ) #define BLOCK_REQUIRE_ENCRYPTION(FLAG, LEVL, BSIZ) (FLAG && IS_BLK_ENCRYPTED(LEVL, BSIZ)) #define ENCR_INITIALIZED (GTMCRYPT_INITIALIZED == gtmcrypt_init_state) #define ENCR_WBOX_ENABLED (gtm_white_box_test_case_enabled \ && (WBTEST_ENCRYPT_INIT_ERROR == gtm_white_box_test_case_number)) #define ASSERT_ENCRYPTION_INITIALIZED assert(ENCR_INITIALIZED || ENCR_WBOX_ENABLED) #define GTMCRYPT_COPY_HASH(src, dst) \ { \ memcpy(dst->encryption_hash, src->encryption_hash, GTMCRYPT_HASH_LEN); \ dst->is_encrypted = src->is_encrypted; \ } \ #define ALLOC_BUFF_GET_ENCR_KEY(CSA, HASH, ALLOC_SIZE, RC) \ { \ assert(0 < ALLOC_SIZE && NULL != CSA); \ RC = 0; \ GTMCRYPT_GETKEY(HASH, CSA->encr_key_handle, RC); \ if (0 == RC) \ (CSA)->encrypted_blk_contents = (char *)malloc(ALLOC_SIZE); \ } #define INIT_DB_ENCRYPTION(fname, CSA, CSD, RC) \ { \ GBLREF stack_frame *frame_pointer; \ GBLREF uint4 dollar_tlevel; \ char *ptr, *key_hash = CSD->encryption_hash; \ boolean_t call_ci_ret_code_quit = FALSE, prompt_passwd = FALSE; \ \ error_def(ERR_CRYPTNOPSWDINTP); \ RC = 0; \ /* If we are in a TP transaction and the environment is setup in such a way that we will be doing a gtm_ci call \ * then let's error out as gtm_ci doesn't work inside a TP transaction */ \ prompt_passwd = PROMPT_PASSWD; \ if (prompt_passwd && dollar_tlevel) \ rts_error(VARLSTCNT(1) ERR_CRYPTNOPSWDINTP); \ /* Make sure we are not in gtm_ci already. */ \ assert(!IS_MUMPS_IMAGE || !(frame_pointer->flags & SFF_CI)); \ /* The below macro eventually calls gtmcrypt_getkey_by_hash to get the encryption key for the database based on \ * the hash in the database file header. It could be possible that initially the user had a wrong password and \ * tried accessing a global which might have landed in db_init and would have successfully done encryption \ * initialization. But later call to this macro would have failed since the password turned out to be wrong and \ * the encryption library failed to do the decryption. Now, after setting the password to a null string, the user \ * now tries to access the global again which will reach this macro again but now the call to gtmcrypt_getkey_by_hash \ * will call gtm_ci for prompting password. Hence before calling the encryption library, make a note if we had \ * to do ci_ret_code_quit below. */ \ call_ci_ret_code_quit = (prompt_passwd && !(frame_pointer->flags & SFF_CI)); \ ALLOC_BUFF_GET_ENCR_KEY(CSA, key_hash, (CSD->blk_size + SIZEOF(int4)), RC); \ if (call_ci_ret_code_quit) \ ci_ret_code_quit(); \ } #define PROMPT_PASSWD (IS_MUMPS_IMAGE \ && (NULL != (ptr = (char *)getenv(GTM_PASSWD))) \ && (0 == strlen(ptr))) /* =====================================================================================================*/ /* Plugin Related Macros */ /* =====================================================================================================*/ /* INIT_PROC_ENCRYPTION is called whenever GT.M wants to initialize the encryption library and it's related * modules. The context in which the caller calls INIT_PROC_ENCRYPTION might force non encryption related * tasks (like MUPIP JOURNAL -SHOW=HEADER -NOVERIFY -FORWARD) to error out in case the below macro fails. To * avoid this, we note down the error status and the corresponding error string in global variable. This way, * any task requiring the actual encryption (MUPIP INTEG -FILE) will verify if the global error code is holding * a non zero value and error out accordingly. */ #define INIT_PROC_ENCRYPTION(RC) \ { \ GBLREF int4 gbl_encryption_ecode; \ GBLREF stack_frame *frame_pointer; \ GBLREF uint4 dollar_tlevel; \ boolean_t call_ci_ret_code_quit = FALSE, prompt_passwd = FALSE; \ \ error_def(ERR_CRYPTNOPSWDINTP); \ error_def(ERR_CRYPTINIT); \ RC = 0; \ gbl_encryption_ecode = 0; \ if (GTMCRYPT_UNINITIALIZED == gtmcrypt_init_state) \ { \ gtmcrypt_entry(); \ /* If in the above call, dlopen failed for some reason, then gbl_encryption_ecode will be set to \ * ERR_CRYPTDLNOOPEN. Also, the dlopen error message will be stored in dl_err. */ \ RC = gbl_encryption_ecode; \ if (0 == gbl_encryption_ecode) \ { \ char *ptr; \ boolean_t has_prompted_passwd; \ \ /* dlopen on the encryption library succeeded. */ \ assert(NULL != gtmcrypt_init_fnptr); \ /* If we are in a TP transaction and the environment is setup in such a way that we will be doing a \ * gtm_ci call then let's error out as gtm_ci doesn't work inside a TP transaction */ \ prompt_passwd = PROMPT_PASSWD; \ if (prompt_passwd && dollar_tlevel) \ rts_error(VARLSTCNT(1) ERR_CRYPTNOPSWDINTP); \ /* Make sure we are not in gtm_ci already. */ \ assert(!IS_MUMPS_IMAGE || !(frame_pointer->flags & SFF_CI)); \ /* The call to gtmcrypt_init below will try to call gtm_ci on finding that password is set to \ * empty string and if the calling process is MUMPS. Make a note of the condition under which we \ * would be calling ci_ret_code_quit. */ \ call_ci_ret_code_quit = (prompt_passwd && !(frame_pointer->flags & SFF_CI)); \ /* Call the encryption library's init routine. Also, pass a boolean indicating whether \ * the library should do the password prompting(for MUMPS) or not(for MUPIP, DSE, etc.)*/ \ xc_status_t init_ret_status = (*gtmcrypt_init_fnptr)(IS_MUMPS_IMAGE); \ /* Unwind the stack frames if necessary. */ \ if (call_ci_ret_code_quit) \ ci_ret_code_quit(); \ /* If the call failed, we have to indicate the caller that an error happened. Also, we \ * will mark the gbl_encryption_ecode to ERR_CRYPTINIT. */ \ if (0 != init_ret_status) \ RC = gbl_encryption_ecode = ERR_CRYPTINIT; \ else \ { \ /* Everything went on smoothly. So indicate that we don't need to do the init \ * again. */ \ gtmcrypt_init_state = GTMCRYPT_INITIALIZED; \ gbl_encryption_ecode = RC = 0; \ } \ } \ } \ } /* GTMCRYPT_GETKEY is mostly called when the caller wants to get the encryption key well before the actual * encryption or decryption happens. As mentioned above the context in which GTMCRYPT_GETKEY is called * might force the non encryption related tasks to error out. So we follow a similar approach as mentioned above. */ #define GTMCRYPT_GETKEY(hash, key_handle, RC) \ { \ xc_string_t xc_hash; \ xc_status_t status; \ GBLREF int4 gbl_encryption_ecode; \ \ error_def(ERR_CRYPTKEYFETCHFAILED); \ /* Call the encryption library only if we are clean from the last encryption \ * call. We find this from gbl_encryption_ecode. */ \ RC = gbl_encryption_ecode; \ if (0 == RC) \ { \ PACKAGE_XCSTRING(xc_hash, hash, GTMCRYPT_HASH_LEN); \ assert(NULL != gtmcrypt_getkey_by_hash_fnptr); \ status = (*gtmcrypt_getkey_by_hash_fnptr)(&xc_hash, &key_handle); \ RC = gbl_encryption_ecode = (0 != status) ? ERR_CRYPTKEYFETCHFAILED : 0; \ } \ } #define GTMCRYPT_HASH_CHK(hash, RC) \ { \ gtmcrypt_key_t handle; \ \ GTMCRYPT_GETKEY(hash, handle, RC); \ } /* DSE CHANGE -FILE -ENCRYPTION_HASH will call the GTMCRYPT_HASH_GEN macro to reset the encryption hash in the database file * header. But, before doing so, it would have encountered an error in db_init but instead of reporting the error in db_init, it * stores the error code in gbl_encryption_ecode. But the GTMCRYPT_HASH_GEN macro cannot proceed if it finds that the error code is * non-zero. The below GTMCRYPT_RESET_ERR macro will reset this global error code back to zero so that GTMCRYPT_HASH_GEN can * proceed. However, we don't want to reset the error if the initialization failed because of a dlopen error as this would mean * that we won't have the function pointers to encryption library APIs properly initialized which would be used by GTMCRYPT_HASH_GEN * when we should ideally be reporting an error. */ #define GTMCRYPT_RESET_HASH_MISMATCH_ERR \ { \ GBLREF int4 gbl_encryption_ecode; \ \ error_def(ERR_CRYPTDLNOOPEN); \ if (0 != gbl_encryption_ecode && (ERR_CRYPTDLNOOPEN != gbl_encryption_ecode)) \ gbl_encryption_ecode = 0; \ } #define GTMCRYPT_HASH_GEN(filename, filename_len, hash, RC) \ { \ xc_status_t status; \ xc_string_t xc_filename, xc_hash; \ gtmcrypt_key_t handle; \ GBLREF int4 gbl_encryption_ecode; \ \ error_def(ERR_CRYPTDLNOOPEN); \ RC = gbl_encryption_ecode; \ if (0 == RC || (ERR_CRYPTDLNOOPEN != RC)) \ { \ PACKAGE_XCSTRING(xc_filename, filename, filename_len); \ PACKAGE_XCSTRING(xc_hash, hash, GTMCRYPT_HASH_LEN); \ assert(NULL != gtmcrypt_getkey_by_name_fnptr); \ status = (*gtmcrypt_getkey_by_name_fnptr)(&xc_filename, &handle); \ GC_MARK_STATUS(status, RC, ERR_CRYPTKEYFETCHFAILED); \ if (0 == RC) \ { \ assert(NULL != gtmcrypt_hash_gen_fnptr); \ status = (*gtmcrypt_hash_gen_fnptr)(handle, &xc_hash); \ GC_MARK_STATUS(status, RC, ERR_CRYPTHASHGENFAILED); \ if (0 == RC) \ memcpy(hash, xc_hash.address, GTMCRYPT_HASH_LEN); \ } \ } \ } #define GTMCRYPT_ENCODE_FAST(key_handle, inbuf, inbuf_len, outbuf, RC) \ { \ GBLREF volatile int4 fast_lock_count; \ xc_string_t unencrypted_block, encrypted_block; \ xc_status_t status; \ int save_fast_lock_count; \ GBLREF int4 gbl_encryption_ecode; \ \ RC = gbl_encryption_ecode; \ if (0 == RC) \ { \ PACKAGE_XCSTRING(unencrypted_block, inbuf, inbuf_len); \ PACKAGE_XCSTRING(encrypted_block, outbuf, inbuf_len); \ assert(NULL != gtmcrypt_encode_fnptr); \ save_fast_lock_count = fast_lock_count; \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION); \ fast_lock_count++; \ status = (*gtmcrypt_encode_fnptr)(key_handle, \ &unencrypted_block, \ &encrypted_block); \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION); \ GC_MARK_STATUS(status, RC, ERR_CRYPTOPFAILED); \ fast_lock_count = save_fast_lock_count; \ } \ } #define GTMCRYPT_DECODE_FAST(key_handle, inbuf, inbuf_len, outbuf, RC) \ { \ GBLREF volatile int4 fast_lock_count; \ xc_string_t unencrypted_block, encrypted_block; \ xc_status_t status; \ int save_fast_lock_count; \ GBLREF int4 gbl_encryption_ecode; \ \ RC = gbl_encryption_ecode; \ if (0 == RC) \ { \ PACKAGE_XCSTRING(encrypted_block, inbuf, inbuf_len); \ PACKAGE_XCSTRING(unencrypted_block, outbuf, inbuf_len); \ assert(NULL != gtmcrypt_decode_fnptr); \ save_fast_lock_count = fast_lock_count; \ fast_lock_count++; \ DEFER_INTERRUPTS(INTRPT_IN_CRYPT_SECTION); \ status = (*gtmcrypt_decode_fnptr)(key_handle, \ &encrypted_block, \ &unencrypted_block); \ ENABLE_INTERRUPTS(INTRPT_IN_CRYPT_SECTION); \ GC_MARK_STATUS(status, RC, ERR_CRYPTOPFAILED); \ fast_lock_count = save_fast_lock_count; \ } \ } #define GTMCRYPT_CLOSE \ { \ if (GTMCRYPT_INITIALIZED == gtmcrypt_init_state) \ { \ (*gtmcrypt_close_fnptr)(); \ gtmcrypt_init_state = GTMCRYPT_UNINITIALIZED; \ } \ } #endif /* GTMCRYPT_H */