fwknop/lib/gpgme_funcs.c
Michael Rash bfdbb8f260 Updated authorship and copyright information
This commit updates all authorship and copyright information to include a
standard header that references the AUTHORS and CREDITS file. This standard
header was written by the Debian legal team at the request of Franck Joncourt.
2014-03-04 17:53:10 -05:00

543 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*****************************************************************************
*
* File: gpgme_funcs.c
*
* Purpose: gpgme-related functions for GPG encryptions support in libfko.
*
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
* Copyright (C) 20092014 fwknop developers and contributors. For a full
* list of contributors, see the file 'CREDITS'.
*
* License (GNU General Public License):
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*****************************************************************************
*/
#include "fko_common.h"
#include "fko.h"
#if HAVE_LIBGPGME
#include "gpgme_funcs.h"
int
init_gpgme(fko_ctx_t fko_ctx)
{
gpgme_error_t err;
/* If we already have a context, we are done.
*/
if(fko_ctx->have_gpgme_context)
return(FKO_SUCCESS);
/* Because the gpgme manual says you should.
*/
gpgme_check_version(NULL);
/* Check for OpenPGP support
*/
err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
/* GPG engine is not available.
*/
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_NO_OPENPGP);
}
/* Extract the current gpgme engine information.
*/
gpgme_set_engine_info(
GPGME_PROTOCOL_OpenPGP,
(fko_ctx->gpg_exe != NULL) ? fko_ctx->gpg_exe : GPG_EXE,
fko_ctx->gpg_home_dir /* If this is NULL, the default is used */
);
/* Create our gpgme context
*/
err = gpgme_new(&(fko_ctx->gpg_ctx));
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_CONTEXT);
}
fko_ctx->have_gpgme_context = 1;
return(FKO_SUCCESS);
}
/* Callback function that supplies the password when gpgme needs it.
*/
gpgme_error_t
my_passphrase_cb(
void *pw, const char *uid_hint, const char *passphrase_info,
int prev_was_bad, int fd)
{
/* We only need to try once as it is fed by the program
* (for now --DSS).
*/
if(prev_was_bad)
return(GPG_ERR_CANCELED);
if(write(fd, (const char*)pw, strlen((const char*)pw))
!= strlen((const char*)pw))
return(GPG_ERR_SYSTEM_ERROR); /* Must be a GPG error, but which one? */
if(write(fd, "\n", 1) != 1)
return(GPG_ERR_SYSTEM_ERROR); /* Must be a GPG error, but which one? */
return 0;
}
/* Verify gpg signatures in a verify_result set.
*/
int
process_sigs(fko_ctx_t fko_ctx, gpgme_verify_result_t vres)
{
unsigned int sig_cnt = 0;
gpgme_signature_t sig = vres->signatures;
fko_gpg_sig_t fgs;
/* only want to see one signature (for now).
*/
if(!sig)
return(FKO_ERROR_GPGME_NO_SIGNATURE);
/* Iterate over the sigs and store the info we are interested in
* to the context.
*
* NOTE: At present, we support only a single signature. However,
* that may change in a future release. We go a head and
* grab all signatures even though we will only use the first
* one. --DSS
*/
while(sig != NULL)
{
fgs = calloc(1, sizeof(struct fko_gpg_sig));
if(fgs == NULL)
return(FKO_ERROR_MEMORY_ALLOCATION);
/* Grab the summary and status values.
*/
fgs->summary = sig->summary;
fgs->status = sig->status;
fgs->validity = sig->validity;
/* Grab the signature fingerprint.
*/
if(sig->fpr != NULL)
{
fgs->fpr = strdup(sig->fpr);
if(fgs->fpr == NULL)
{
free(fgs);
return(FKO_ERROR_MEMORY_ALLOCATION);
}
}
if(sig_cnt == 0)
fko_ctx->gpg_sigs = fgs;
else
fko_ctx->gpg_sigs->next = fgs;
sig_cnt++;
sig = sig->next;
}
/* If we are ignoring bad signatures, return success here.
*/
if(fko_ctx->ignore_gpg_sig_error != 0)
return(FKO_SUCCESS);
/* Otherwise, we check them here and respond accordingly.
*/
fgs = fko_ctx->gpg_sigs;
if(fgs->status != GPG_ERR_NO_ERROR || fgs->validity < 3) {
fko_ctx->gpg_err = fgs->status;
return(FKO_ERROR_GPGME_BAD_SIGNATURE);
}
return(FKO_SUCCESS);
}
/* Get the GPG key for the given name or ID.
*/
int
get_gpg_key(fko_ctx_t fko_ctx, gpgme_key_t *mykey, const int signer)
{
int res;
const char *name;
gpgme_ctx_t list_ctx = NULL;
gpgme_key_t key = NULL;
gpgme_key_t key2 = NULL;
gpgme_error_t err;
/* Create a gpgme context for the list
*/
/* Initialize gpgme
*/
res = init_gpgme(fko_ctx);
if(res != FKO_SUCCESS)
{
if(signer)
return(FKO_ERROR_GPGME_CONTEXT_SIGNER_KEY);
else
return(FKO_ERROR_GPGME_CONTEXT_RECIPIENT_KEY);
}
list_ctx = fko_ctx->gpg_ctx;
if(signer)
name = fko_ctx->gpg_signer;
else
name = fko_ctx->gpg_recipient;
err = gpgme_op_keylist_start(list_ctx, name, signer);
if (err)
{
gpgme_release(list_ctx);
fko_ctx->gpg_err = err;
if(signer)
return(FKO_ERROR_GPGME_SIGNER_KEYLIST_START);
else
return(FKO_ERROR_GPGME_RECIPIENT_KEYLIST_START);
}
/* Grab the first key in the list (we hope it is the only one).
*/
err = gpgme_op_keylist_next(list_ctx, &key);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
/* Key not found
*/
fko_ctx->gpg_err = err;
if(signer)
return(FKO_ERROR_GPGME_SIGNER_KEY_NOT_FOUND);
else
return(FKO_ERROR_GPGME_RECIPIENT_KEY_NOT_FOUND);
}
/* We try to get the next key match. If we do, then the name is
* ambiguous, so we return an error.
*/
err = gpgme_op_keylist_next(list_ctx, &key2);
if(gpg_err_code(err) == GPG_ERR_NO_ERROR) /* Note: look for NO error */
{
/* Ambiguous specfication of key
*/
gpgme_key_unref(key);
gpgme_key_unref(key2);
fko_ctx->gpg_err = err;
if(signer)
return(FKO_ERROR_GPGME_SIGNER_KEY_AMBIGUOUS);
else
return(FKO_ERROR_GPGME_RECIPIENT_KEY_AMBIGUOUS);
}
gpgme_op_keylist_end(list_ctx);
gpgme_key_unref(key2);
*mykey = key;
return(FKO_SUCCESS);
}
/* The main GPG encryption routine for libfko.
*/
int
gpgme_encrypt(fko_ctx_t fko_ctx, unsigned char *indata, size_t in_len,
const char *pw, unsigned char **out, size_t *out_len)
{
char *tmp_buf;
int res;
gpgme_ctx_t gpg_ctx = NULL;
gpgme_data_t cipher = NULL;
gpgme_data_t plaintext = NULL;
gpgme_key_t key[2] = { NULL, NULL };
gpgme_error_t err;
/* Initialize gpgme
*/
res = init_gpgme(fko_ctx);
if(res != FKO_SUCCESS)
return(res);
gpg_ctx = fko_ctx->gpg_ctx;
/* Initialize the plaintext data (place into gpgme_data object)
*/
err = gpgme_data_new_from_mem(&plaintext, (char*)indata, in_len, 1);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_PLAINTEXT_DATA_OBJ);
}
/* Set protocol
*/
err = gpgme_set_protocol(gpg_ctx, GPGME_PROTOCOL_OpenPGP);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_SET_PROTOCOL);
}
/* Set ascii-armor off (we will be base64-encoding the encrypted data
* ourselves.
*/
gpgme_set_armor(gpg_ctx, 0);
/* The gpgme_encrypt.... functions take a recipient key array, so we add
* our single key here.
*/
key[0] = fko_ctx->recipient_key;
/* Create the buffer for our encrypted data.
*/
err = gpgme_data_new(&cipher);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_CIPHER_DATA_OBJ);
}
/* Here we add the signer to the gpgme context if there is one.
*/
if(fko_ctx->gpg_signer != NULL) {
gpgme_signers_clear(gpg_ctx);
err = gpgme_signers_add(gpg_ctx, fko_ctx->signer_key);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_data_release(cipher);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_ADD_SIGNER);
}
}
/* Set the passphrase callback.
*/
gpgme_set_passphrase_cb(gpg_ctx, my_passphrase_cb, (void*)pw);
/* Encrypt and sign (if a sig was provided) the SPA data.
*/
if(fko_ctx->gpg_signer == NULL)
err = gpgme_op_encrypt(
gpg_ctx, key, GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, cipher
);
else
err = gpgme_op_encrypt_sign(
gpg_ctx, key, GPGME_ENCRYPT_ALWAYS_TRUST, plaintext, cipher
);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_data_release(cipher);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
if(gpgme_err_code(err) == GPG_ERR_CANCELED)
return(FKO_ERROR_GPGME_BAD_PASSPHRASE);
return(FKO_ERROR_GPGME_ENCRYPT_SIGN);
}
/* Done with the plaintext.
*/
gpgme_data_release(plaintext);
/* Get the encrypted data and its length from the gpgme data object.
* BTW, this does free the memory used by cipher.
*/
tmp_buf = gpgme_data_release_and_get_mem(cipher, out_len);
*out = malloc(*out_len); /* This is freed upon fko_ctx destruction. */
if(*out == NULL)
res = FKO_ERROR_MEMORY_ALLOCATION;
else
{
memcpy(*out, tmp_buf, *out_len);
res = FKO_SUCCESS;
}
gpgme_free(tmp_buf);
return(res);
}
/* The main GPG decryption routine for libfko.
*/
int
gpgme_decrypt(fko_ctx_t fko_ctx, unsigned char *indata,
size_t in_len, const char *pw, unsigned char **out, size_t *out_len)
{
char *tmp_buf;
int res;
gpgme_ctx_t gpg_ctx = NULL;
gpgme_data_t cipher = NULL;
gpgme_data_t plaintext = NULL;
gpgme_error_t err;
gpgme_decrypt_result_t decrypt_res;
gpgme_verify_result_t verify_res;
/* Initialize gpgme
*/
res = init_gpgme(fko_ctx);
if(res != FKO_SUCCESS)
return(res);
gpg_ctx = fko_ctx->gpg_ctx;
err = gpgme_data_new(&plaintext);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_PLAINTEXT_DATA_OBJ);
}
/* Initialize the cipher data (place into gpgme_data object)
*/
err = gpgme_data_new_from_mem(&cipher, (char*)indata, in_len, 0);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_CIPHER_DATA_OBJ);
}
/* Set the passphrase callback.
*/
gpgme_set_passphrase_cb(gpg_ctx, my_passphrase_cb, (void*)pw);
/* Now decrypt and verify.
*/
err = gpgme_op_decrypt_verify(gpg_ctx, cipher, plaintext);
if(gpg_err_code(err) != GPG_ERR_NO_ERROR)
{
gpgme_data_release(plaintext);
gpgme_data_release(cipher);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
fko_ctx->gpg_err = err;
return(FKO_ERROR_GPGME_DECRYPT_FAILED);
}
/* Done with the cipher text.
*/
gpgme_data_release(cipher);
/* We check the "usupported_algorithm" flag in the decrypt result.
*/
decrypt_res = gpgme_op_decrypt_result(gpg_ctx);
if(decrypt_res->unsupported_algorithm)
{
gpgme_data_release(plaintext);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
return(FKO_ERROR_GPGME_DECRYPT_UNSUPPORTED_ALGORITHM);
}
/* Now verify the signatures if so configured.
*/
if(fko_ctx->verify_gpg_sigs)
{
verify_res = gpgme_op_verify_result(gpg_ctx);
res = process_sigs(fko_ctx, verify_res);
if(res != FKO_SUCCESS)
{
gpgme_data_release(plaintext);
gpgme_release(gpg_ctx);
fko_ctx->gpg_ctx = NULL;
return(res);
}
}
/* Get the encrypted data and its length from the gpgme data object.
*/
tmp_buf = gpgme_data_release_and_get_mem(plaintext, out_len);
/* Use calloc here with an extra byte because I am not sure if all systems
* will include the terminating NULL with the decrypted data (which is
* expected to be a string).
*/
*out = calloc(1, *out_len+1); /* This is freed upon fko_ctx destruction. */
if(*out == NULL)
res = FKO_ERROR_MEMORY_ALLOCATION;
else
{
memcpy(*out, tmp_buf, *out_len);
res = FKO_SUCCESS;
}
gpgme_free(tmp_buf);
return(res);
}
#endif /* HAVE_LIBGPGME */
/***EOF***/