1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
/****************************************************************
* *
* Copyright 2009, 2014 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. *
* *
****************************************************************/
#define _FILE_OFFSET_BITS 64 /* Needed to compile gpgme client progs also with large file support */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <fcntl.h>
#include <errno.h>
#include <dlfcn.h>
#include <sys/mman.h>
#include <sys/stat.h> /* BYPASSOK -- see above */
#include <sys/types.h>
#include <gpgme.h> /* gpgme functions */
#include <gpg-error.h> /* gcry*_err_t */
#include <libconfig.h>
#include "gtmxc_types.h" /* xc_string, xc_status_t and other callin interfaces xc_fileid */
#include "gtmcrypt_util.h"
#include "gtmcrypt_interface.h" /* Function prototypes for gtmcrypt*.* functions */
#include "gtmcrypt_ref.h"
#include "gtmcrypt_dbk_ref.h"
#include "gtmcrypt_sym_ref.h"
#include "gtmcrypt_pk_ref.h"
GBLDEF passwd_entry_t *gtmcrypt_pwent;
GBLDEF gpgme_ctx_t pk_crypt_ctx;
void gc_pk_scrub_passwd()
{
/* Nullify the key strings, so that any generated cores will not contain the unencrypted keys */
gc_freeup_pwent(gtmcrypt_pwent);
/* Finally release the gpgme context */
if (NULL != pk_crypt_ctx)
gpgme_release(pk_crypt_ctx);
}
/* This function is called whenever gpg needs the passphrase with which the secret key is encrypted. In this case, the passphrase
* is obtained from the ENVIRONMENT VARIABLE - $gtm_passwd or by invoking the mumps engine during the "gtmcrypt_init()".
* In either ways, it's guaranteed that when this function is called, the passphrase is already set in the global variable.
*/
int gc_pk_crypt_passphrase_callback(void *opaque, const char *uid_hint, const char *passphrase_info, int last_was_bad, int fd)
{
int write_ret, len;
assert(0 != fd);
assert(NULL != gtmcrypt_pwent->passwd);
len = STRLEN(gtmcrypt_pwent->passwd);
write_ret = write(fd, gtmcrypt_pwent->passwd, len);
if (len == write_ret)
{
write_ret = write(fd, "\n", 1);
if (1 == write_ret)
return 0;
}
/* Problem with one of the writes so let gpgme know */
return -1;
}
/* Given the structure that holds the plain data, this function reads through the structure and retrieves the plain text. We
* also return the number of bytes actually read from the structure.
*/
int gc_pk_crypt_retrieve_plain_text(gpgme_data_t plain_data, unsigned char *plain_text)
{
int ret;
assert(NULL != plain_text);
/* Clear the temporary buffer */
memset(plain_text, 0, SYMMETRIC_KEY_MAX);
gpgme_data_seek(plain_data, 0, SEEK_SET);
ret = (int)gpgme_data_read(plain_data, plain_text, SYMMETRIC_KEY_MAX);
return ret;
}
/* This is currently necessary to work around what seems to be a gpgme issue in not clearing the plaintext keys
* from the C stack (shows up in a core dump otherwise). When gpgme is fixed, this code can be removed.
* The size of lclarray (8K) is determined purely from experimentation on all platforms.
*/
int gc_pk_scrub_plaintext_keys_from_c_stack()
{
char lclarray[8192];
memset(lclarray, 0, SIZEOF(lclarray));
return 0;
}
/* This function tries to decrypt the cipher file (the file containing the symmetric key with which the database is encrypted).
* It's assumed that the context is initialized and is set with the appropriate passphrase callback. The cipher_file
* should contain the fully qualified path of the encrypted database key file. Also, plain text is supposed to be allocated with
* sufficient space to hold the decrypted text.
*/
gpgme_error_t gc_pk_get_decrypted_key(const char *cipher_file, unsigned char *plain_text, int *plain_text_length)
{
gpgme_error_t err;
gpgme_data_t cipher_data = NULL, plain_data = NULL;
gpg_err_code_t ecode;
char null_buffer[SYMMETRIC_KEY_MAX];
assert(NULL != cipher_file);
assert(NULL != plain_text);
assert(NULL != pk_crypt_ctx);
assert(0 != STRLEN(cipher_file));
/* Convert the cipher content in the cipher file into in-memory content.
* This in-memory content is stored in gpgme_data_t structure.
*/
err = gpgme_data_new_from_file(&cipher_data, cipher_file, 1);
if (!err)
{
err = gpgme_data_new(&plain_data);
if (!err)
{ /* Try decrypting the cipher content with the context.
* The decrypted content will also be stored in gpgme_data_t structure.
*/
err = gpgme_op_decrypt(pk_crypt_ctx, cipher_data, plain_data);
if (!err) /* Once decrypted, the plain text has to be obtained from the plain_data structure. */
*plain_text_length = gc_pk_crypt_retrieve_plain_text(plain_data, plain_text);
gc_pk_scrub_plaintext_keys_from_c_stack();
}
}
ecode = gpgme_err_code(err);
if (0 != ecode)
{
switch (ecode)
{
case GPG_ERR_BAD_PASSPHRASE:
UPDATE_ERROR_STRING("Incorrect password or error while obtaining password");
break;
case GPG_ERR_ENOENT:
UPDATE_ERROR_STRING("Encryption key file " STR_ARG " not found", ELLIPSIZE(cipher_file));
break;
case GPG_ERR_EACCES:
UPDATE_ERROR_STRING("Encryption key file " STR_ARG " not accessible", ELLIPSIZE(cipher_file));
break;
case GPG_ERR_ENAMETOOLONG:
UPDATE_ERROR_STRING("Path, or a component of the path, to encryption key file " STR_ARG
" is too long", ELLIPSIZE(cipher_file));
break;
default:
UPDATE_ERROR_STRING("Error while accessing key file " STR_ARG ": %s", ELLIPSIZE(cipher_file),
gpgme_strerror(err));
break;
}
} else
{
assert((0 == plain_text_length) || (NULL != plain_data));
}
if (NULL != plain_data)
{ /* Scrub plaintext data before releasing it */
assert(SYMMETRIC_KEY_MAX == SIZEOF(null_buffer));
memset(null_buffer, 0, SYMMETRIC_KEY_MAX);
assert((0 != ecode) || (0 != plain_text_length) || memcmp(plain_text, null_buffer, SYMMETRIC_KEY_MAX));
gpgme_data_write(plain_data, null_buffer, SYMMETRIC_KEY_MAX);
gpgme_data_release(plain_data);
}
if (NULL != cipher_data)
gpgme_data_release(cipher_data);
return ecode;
}
int gc_pk_gpghome_has_permissions()
{
char pathname[GTM_PATH_MAX], *ptr;
int gnupghome_set, perms;
/* See if GNUPGHOME is set in the environment */
if (!(ptr = getenv(GNUPGHOME)))
{ /* $GNUPGHOME is not set, use $HOME/.gnupg as the GPG home directory */
gnupghome_set = FALSE;
if (!(ptr = getenv(HOME)))
{
UPDATE_ERROR_STRING(ENV_UNDEF_ERROR, HOME);
return -1;
}
SNPRINTF(pathname, GTM_PATH_MAX, "%s/%s", ptr, DOT_GNUPG);
} else
{
if (0 == strlen(ptr))
{
UPDATE_ERROR_STRING(ENV_EMPTY_ERROR, GNUPGHOME);
return -1;
}
gnupghome_set = TRUE;
strcpy(pathname, ptr);
}
if (-1 != (perms = access(pathname, R_OK | X_OK)))
return 0;
else if (EACCES == errno)
{
if (gnupghome_set)
{
UPDATE_ERROR_STRING("No read permissions on $%s", GNUPGHOME);
} else
UPDATE_ERROR_STRING("No read permissions on $%s/%s", HOME, DOT_GNUPG);
} else /* Some other error */
UPDATE_ERROR_STRING("Cannot stat on " STR_ARG " - %d", ELLIPSIZE(pathname), errno);
return -1;
}
|