/* ***************************************************************************** * * 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) 2009-2014 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 = calloc(1, *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***/