From 0e7ec4dd193141bff3bc6b0565de76e6b28a8457 Mon Sep 17 00:00:00 2001 From: Michal Hlavinka Date: Jun 30 2025 20:32:37 +0000 Subject: [PATCH 1/2] use g_memdup2 instead of deprecated g_memdup --- diff --git a/lib/crypto.c b/lib/crypto.c index 5b562d1..b78b886 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -298,7 +298,7 @@ encrypt_asymmetric (size_t *res_size, const void *data, size_t size, } *res_size = dest_item.len; - res = g_memdup (dest_item.data, dest_item.len); + res = g_memdup2 (dest_item.data, dest_item.len); PORT_FreeArena (res_arena, PR_FALSE); NSS_CMSMessage_Destroy (cmsg); return res; @@ -343,7 +343,7 @@ decrypt_asymmetric (size_t *res_size, const void *data, size_t size, error_from_pr (error); goto err_cmsg; } - res = g_memdup (dest->data, dest->len); + res = g_memdup2 (dest->data, dest->len); *res_size = dest->len; NSS_CMSMessage_Destroy (cmsg); @@ -449,13 +449,13 @@ wrap_asymmetric (void **wrapped_secret, size_t *wrapped_secret_size, SECKEY_DestroyPublicKey (public_key); PK11_FreeSymKey (secret_key); - *wrapped_secret = g_memdup (wrapped_secret_item.data, + *wrapped_secret = g_memdup2 (wrapped_secret_item.data, wrapped_secret_item.len); *wrapped_secret_size = wrapped_secret_item.len; SECITEM_FreeItem (&wrapped_secret_item, PR_FALSE); - *issuer = g_memdup (isn->derIssuer.data, isn->derIssuer.len); + *issuer = g_memdup2 (isn->derIssuer.data, isn->derIssuer.len); *issuer_size = isn->derIssuer.len; - *sn = g_memdup (isn->serialNumber.data, isn->serialNumber.len); + *sn = g_memdup2 (isn->serialNumber.data, isn->serialNumber.len); *sn_size = isn->serialNumber.len; PORT_FreeArena (isn_arena, PR_FALSE); return 0; @@ -538,7 +538,7 @@ unwrap_asymmetric (size_t *clear_secret_size, const void *wrapped_secret_data, goto err_secret_key; } clear_secret_item = PK11_GetKeyData (secret_key); - ret = g_memdup (clear_secret_item->data, clear_secret_item->len); + ret = g_memdup2 (clear_secret_item->data, clear_secret_item->len); *clear_secret_size = clear_secret_item->len; PK11_FreeSymKey (secret_key); @@ -612,11 +612,11 @@ wrap_symmetric (void **wrapped_secret, size_t *wrapped_secret_size, void **iv, PK11_FreeSymKey (secret_key); iv_data = PK11_IVFromParam (mechanism, wrapping_param, &iv_data_size); - *iv = g_memdup (iv_data, iv_data_size); + *iv = g_memdup2 (iv_data, iv_data_size); *iv_size = iv_data_size; SECITEM_FreeItem (wrapping_param, PR_TRUE); - *wrapped_secret = g_memdup (wrapped_secret_item.data, + *wrapped_secret = g_memdup2 (wrapped_secret_item.data, wrapped_secret_item.len); *wrapped_secret_size = wrapped_secret_item.len; SECITEM_FreeItem (&wrapped_secret_item, PR_FALSE); @@ -671,7 +671,7 @@ unwrap_symmetric (size_t *clear_secret_size, PK11SymKey *wrapping_key, goto err_secret_key; } clear_secret_item = PK11_GetKeyData (secret_key); - ret = g_memdup (clear_secret_item->data, clear_secret_item->len); + ret = g_memdup2 (clear_secret_item->data, clear_secret_item->len); *clear_secret_size = clear_secret_item->len; PK11_FreeSymKey (secret_key); @@ -827,7 +827,7 @@ encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, _("Unknown error getting encryption result")); goto err_ctx; } - res = g_memdup (gpgme_res, *res_size); + res = g_memdup2 (gpgme_res, *res_size); gpgme_free (gpgme_res); gpgme_release (ctx); @@ -883,7 +883,7 @@ decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, _("Unknown error getting decryption result")); goto err_ctx; } - res = g_memdup (gpgme_res, *res_size); + res = g_memdup2 (gpgme_res, *res_size); gpgme_free (gpgme_res); gpgme_release (ctx); diff --git a/lib/kmip.c b/lib/kmip.c index 333603c..9e7ae51 100644 --- a/lib/kmip.c +++ b/lib/kmip.c @@ -98,7 +98,7 @@ kmip_key_value_set_bytes (struct kmip_key_value *value, const void *bytes, { kmip_key_value_free_v (value); value->type = KMIP_KEY_VALUE_BYTES; - value->v.bytes.data = g_memdup (bytes, len); + value->v.bytes.data = g_memdup2 (bytes, len); value->v.bytes.len = len; } @@ -110,7 +110,7 @@ kmip_key_value_set_symmetric_key (struct kmip_key_value *value, const void *key, kmip_key_value_free_v (value); value->type = KMIP_KEY_VALUE_SYMMETRIC_KEY; value->v.key = g_new (struct kmip_symmetric_key, 1); - value->v.key->data = g_memdup (key, len); + value->v.key->data = g_memdup2 (key, len); value->v.key->len = len; } @@ -920,7 +920,7 @@ get_bytes (struct kmip_decoding_state *kmip, void **bytes, size_t *size, _("Unexpected end of data")); return -1; } - *bytes = g_memdup (kmip->data, s); + *bytes = g_memdup2 (kmip->data, s); *size = s; kmip->data += s; kmip->left -= s; @@ -1884,7 +1884,7 @@ kmip_libvk_packet_wrap_secret_symmetric (struct kmip_libvk_packet *packet, key_block->wrapping = g_new (struct kmip_key_wrapping_data, 1); key_block->wrapping->method = KMIP_WRAPPING_LIBVK_ENCRYPT_KEY_ONLY; key_block->wrapping->encryption_key = encryption_key; - key_block->wrapping->iv = g_memdup (iv, iv_len); + key_block->wrapping->iv = g_memdup2 (iv, iv_len); key_block->wrapping->iv_len = iv_len; g_free (iv); diff --git a/lib/volume.c b/lib/volume.c index 696d8b5..bfcc383 100644 --- a/lib/volume.c +++ b/lib/volume.c @@ -83,7 +83,7 @@ volume_create_data_encryption_key_packet (struct kmip_key_value **kv, key_value = g_new (struct kmip_key_value, 1); key_value->type = KMIP_KEY_VALUE_SYMMETRIC_KEY; key_value->v.key = g_new (struct kmip_symmetric_key, 1); - key_value->v.key->data = g_memdup (key, key_bytes); + key_value->v.key->data = g_memdup2 (key, key_bytes); key_value->v.key->len = key_bytes; key_value->attributes = g_ptr_array_new (); add_common_volume_attributes (key_value, vol); @@ -129,7 +129,7 @@ volume_create_passphrase_packet (struct kmip_key_value **kv, key_value = g_new (struct kmip_key_value, 1); key_value->type = KMIP_KEY_VALUE_BYTES; - key_value->v.bytes.data = g_memdup (passphrase, size); + key_value->v.bytes.data = g_memdup2 (passphrase, size); key_value->v.bytes.len = size; key_value->attributes = g_ptr_array_new (); add_common_volume_attributes (key_value, vol); diff --git a/lib/volume_luks.c b/lib/volume_luks.c index 4d32d9b..d92febc 100644 --- a/lib/volume_luks.c +++ b/lib/volume_luks.c @@ -154,7 +154,7 @@ luks_replace_key (struct libvk_volume *vol, const void *key) memset (luks->key, 0, luks->key_bytes); g_free (luks->key); } - luks->key = g_memdup (key, luks->key_bytes); + luks->key = g_memdup2 (key, luks->key_bytes); } /* Replace the passphrase in VOL, if any, by PASSPHRASE */ @@ -791,7 +791,7 @@ luks_parse_escrow_packet (struct libvk_volume *vol, _("Key length mismatch")); goto err; } - vol->v.luks->key = g_memdup (key_value->v.key->data, + vol->v.luks->key = g_memdup2 (key_value->v.key->data, key_value->v.key->len); } break; diff --git a/src/volume_key.c b/src/volume_key.c index 9642072..53dd27d 100644 --- a/src/volume_key.c +++ b/src/volume_key.c @@ -388,7 +388,7 @@ read_batch_string (void) free (buf); return NULL; } - res = g_memdup (buf, len); + res = g_memdup2 (buf, len); free (buf); return res; } @@ -531,7 +531,7 @@ generic_ui_cb (void *id, const char *prompt, int echo) free (buf); return NULL; } - res = g_memdup (buf, len + 1); + res = g_memdup2 (buf, len + 1); free (buf); return res; } From a750748519e758fad2a2f8a738137750d1caabdf Mon Sep 17 00:00:00 2001 From: Michal Hlavinka Date: Jul 02 2025 12:34:46 +0000 Subject: [PATCH 2/2] add option to use Sequoia sq instead of gpgme There are concerns after LibrePGP and OpenPGP split, this should allow downstreams to choose. --- diff --git a/configure.ac b/configure.ac index 7d3d27a..3015c97 100644 --- a/configure.ac +++ b/configure.ac @@ -195,7 +195,13 @@ AC_DEFINE(G_DISABLE_DEPRECATED, 1, PKG_CHECK_MODULES(libcryptsetup, [libcryptsetup]) PKG_CHECK_MODULES(nss, [nss]) -AM_PATH_GPGME +AM_CHECK_SQ([external]) +AM_CONDITIONAL([WITH_SQ], [test x$SQ_PATH != x]) + +# Check for GPGME only if --with-sq was not specified +AS_IF([test "x$SQ_PATH" = "x"], + [AM_PATH_GPGME], + [AC_MSG_NOTICE([Skipping GPGME check, using sq instead])]) AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.18.2]) diff --git a/lib/Makefile.am b/lib/Makefile.am index e69c846..6939061 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -33,6 +33,12 @@ libvolume_key_la_SOURCES = SECerrs.h SSLerrs.h \ ui.c ui.h \ volume.c volume.h \ volume_luks.c volume_luks.h +if WITH_SQ +libvolume_key_la_SOURCES += crypto-sq.c crypto-sq.h +else +libvolume_key_la_SOURCES += crypto-gpgme.c crypto-gpgme.h +endif + libvolume_key_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOCALEDIR_CPPFLAGS) libvolume_key_la_LDFLAGS = -version-info $(VERSION_INFO) libvolume_key_la_LIBADD = $(blkid_LIBS) $(glib_LIBS) $(GPGME_LIBS) \ diff --git a/lib/crypto-gpgme.c b/lib/crypto-gpgme.c new file mode 100644 index 0000000..5befb29 --- /dev/null +++ b/lib/crypto-gpgme.c @@ -0,0 +1,432 @@ +/* libgpgme utils */ +static void +error_from_gpgme (GError **error, gpgme_error_t e) +{ + size_t len; + char *s; + + s = NULL; + len = 100; + for (;;) + { + s = g_realloc (s, len); + if (gpgme_strerror_r (e, s, len) == 0) + break; + len *= 2; + } + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, _("%s: %s"), + gpgme_strsource (e), s); + g_free (s); +} + +static gpgme_error_t +gpgme_passphrase_cb (void *hook, const char *uid_hint, + const char *passphrase_info, int prev_was_bad, int fd) +{ + static const char nl = '\n'; + + const char *pw; + size_t len; + ssize_t res; + + (void)uid_hint; + (void)passphrase_info; + if (prev_was_bad != 0) + return GPG_ERR_CANCELED; + pw = hook; + len = strlen (pw); + while (len != 0) + { + res = write (fd, pw, len); + if (res < 0) + return gpgme_error_from_errno (errno); + pw += res; + len -= res; + } + if (write (fd, &nl, sizeof (nl)) < 0) + return gpgme_error_from_errno (errno); + return 0; +} +// Error helper (simplified from GPGME’s error_from_gpgme) + + +/* Create and configure a gpgme context, to use PASSPHRASE. + * Return 0 if OK, -1 on error. */ +static int +init_gpgme (gpgme_ctx_t *res, const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + + (void)gpgme_check_version (NULL); + e = gpgme_new (&ctx); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err; + } + e = gpgme_set_locale (ctx, LC_CTYPE, setlocale (LC_CTYPE, NULL)); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_set_locale (ctx, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_ctx_set_engine_info (ctx, GPGME_PROTOCOL_OpenPGP, GPG_PATH, NULL); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK); + gpgme_set_passphrase_cb (ctx, gpgme_passphrase_cb, (void *)passphrase); + *res = ctx; + return 0; + + err_ctx: + gpgme_release (ctx); + err: + return -1; +} + +/* LIBVK_PACKET_FORMAT_PASSPHRASE */ + +/* Encrypt DATA of SIZE using PASSPHRASE. + * Return encrypted data (for g_free()), setting RES_SIZE to the size of the + * result, on success, NULL otherwise. */ +void * +encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + gpgme_data_t src_data, dest_data; + void *gpgme_res, *res; + + // FIXME: this should eventually use CMS + if (init_gpgme (&ctx, passphrase, error) != 0) + goto err; + e = gpgme_data_new_from_mem (&src_data, data, size, 0); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_data_new (&dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_src_data; + } + e = gpgme_op_encrypt (ctx, NULL, 0, src_data, dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_dest_data; + } + gpgme_data_release (src_data); + gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); + if (gpgme_res == NULL) + { + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, + _("Unknown error getting encryption result")); + goto err_ctx; + } + res = g_memdup2 (gpgme_res, *res_size); + gpgme_free (gpgme_res); + + gpgme_release (ctx); + return res; + + err_dest_data: + gpgme_data_release (src_data); + err_src_data: + gpgme_data_release (dest_data); + err_ctx: + gpgme_release (ctx); + err: + return NULL; +} + +/* Decrypt DATA of SIZE using PASSPHRASE. + * Return decrypted data (for g_free()), setting RES_SIZE to the size of the + * result, on success, NULL otherwise. */ +void * +decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + gpgme_data_t src_data, dest_data; + void *gpgme_res, *res; + + if (init_gpgme (&ctx, passphrase, error) != 0) + goto err; + e = gpgme_data_new_from_mem (&src_data, data, size, 0); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_data_new (&dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_src_data; + } + e = gpgme_op_decrypt (ctx, src_data, dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_dest_data; + } + gpgme_data_release (src_data); + gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); + if (gpgme_res == NULL) + { + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, + _("Unknown error getting decryption result")); + goto err_ctx; + } + res = g_memdup2 (gpgme_res, *res_size); + gpgme_free (gpgme_res); + + gpgme_release (ctx); + return res; + + err_dest_data: + gpgme_data_release (src_data); + err_src_data: + gpgme_data_release (dest_data); + err_ctx: + gpgme_release (ctx); + err: + return NULL; +} +/* libgpgme utils */ +static void +error_from_gpgme (GError **error, gpgme_error_t e) +{ + size_t len; + char *s; + + s = NULL; + len = 100; + for (;;) + { + s = g_realloc (s, len); + if (gpgme_strerror_r (e, s, len) == 0) + break; + len *= 2; + } + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, _("%s: %s"), + gpgme_strsource (e), s); + g_free (s); +} + +static gpgme_error_t +gpgme_passphrase_cb (void *hook, const char *uid_hint, + const char *passphrase_info, int prev_was_bad, int fd) +{ + static const char nl = '\n'; + + const char *pw; + size_t len; + ssize_t res; + + (void)uid_hint; + (void)passphrase_info; + if (prev_was_bad != 0) + return GPG_ERR_CANCELED; + pw = hook; + len = strlen (pw); + while (len != 0) + { + res = write (fd, pw, len); + if (res < 0) + return gpgme_error_from_errno (errno); + pw += res; + len -= res; + } + if (write (fd, &nl, sizeof (nl)) < 0) + return gpgme_error_from_errno (errno); + return 0; +} +// Error helper (simplified from GPGME’s error_from_gpgme) + + +/* Create and configure a gpgme context, to use PASSPHRASE. + * Return 0 if OK, -1 on error. */ +static int +init_gpgme (gpgme_ctx_t *res, const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + + (void)gpgme_check_version (NULL); + e = gpgme_new (&ctx); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err; + } + e = gpgme_set_locale (ctx, LC_CTYPE, setlocale (LC_CTYPE, NULL)); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_set_locale (ctx, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_ctx_set_engine_info (ctx, GPGME_PROTOCOL_OpenPGP, GPG_PATH, NULL); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK); + gpgme_set_passphrase_cb (ctx, gpgme_passphrase_cb, (void *)passphrase); + *res = ctx; + return 0; + + err_ctx: + gpgme_release (ctx); + err: + return -1; +} + +/* LIBVK_PACKET_FORMAT_PASSPHRASE */ + +/* Encrypt DATA of SIZE using PASSPHRASE. + * Return encrypted data (for g_free()), setting RES_SIZE to the size of the + * result, on success, NULL otherwise. */ +void * +encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + gpgme_data_t src_data, dest_data; + void *gpgme_res, *res; + + // FIXME: this should eventually use CMS + if (init_gpgme (&ctx, passphrase, error) != 0) + goto err; + e = gpgme_data_new_from_mem (&src_data, data, size, 0); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_data_new (&dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_src_data; + } + e = gpgme_op_encrypt (ctx, NULL, 0, src_data, dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_dest_data; + } + gpgme_data_release (src_data); + gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); + if (gpgme_res == NULL) + { + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, + _("Unknown error getting encryption result")); + goto err_ctx; + } + res = g_memdup2 (gpgme_res, *res_size); + gpgme_free (gpgme_res); + + gpgme_release (ctx); + return res; + + err_dest_data: + gpgme_data_release (src_data); + err_src_data: + gpgme_data_release (dest_data); + err_ctx: + gpgme_release (ctx); + err: + return NULL; +} + +/* Decrypt DATA of SIZE using PASSPHRASE. + * Return decrypted data (for g_free()), setting RES_SIZE to the size of the + * result, on success, NULL otherwise. */ +void * +decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + gpgme_ctx_t ctx; + gpgme_error_t e; + gpgme_data_t src_data, dest_data; + void *gpgme_res, *res; + + if (init_gpgme (&ctx, passphrase, error) != 0) + goto err; + e = gpgme_data_new_from_mem (&src_data, data, size, 0); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_ctx; + } + e = gpgme_data_new (&dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_src_data; + } + e = gpgme_op_decrypt (ctx, src_data, dest_data); + if (e != GPG_ERR_NO_ERROR) + { + error_from_gpgme (error, e); + goto err_dest_data; + } + gpgme_data_release (src_data); + gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); + if (gpgme_res == NULL) + { + g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, + _("Unknown error getting decryption result")); + goto err_ctx; + } + res = g_memdup2 (gpgme_res, *res_size); + gpgme_free (gpgme_res); + + gpgme_release (ctx); + return res; + + err_dest_data: + gpgme_data_release (src_data); + err_src_data: + gpgme_data_release (dest_data); + err_ctx: + gpgme_release (ctx); + err: + return NULL; +} diff --git a/lib/crypto-gpgme.h b/lib/crypto-gpgme.h new file mode 100644 index 0000000..985adcc --- /dev/null +++ b/lib/crypto-gpgme.h @@ -0,0 +1,16 @@ +#ifndef CRYPTO_GPGME_H +#define CRYPTO_GPGME_H +void *encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); + +void *decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); +#endif +#ifndef CRYPTO_GPGME_H +#define CRYPTO_GPGME_H +void *encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); + +void *decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); +#endif diff --git a/lib/crypto-sq.c b/lib/crypto-sq.c new file mode 100644 index 0000000..6e94be1 --- /dev/null +++ b/lib/crypto-sq.c @@ -0,0 +1,382 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "crypto-sq.h" +#include "libvolume_key.h" +#include +#include + +struct _sqme_ctx { + char *passphrase; +}; + +struct _sqme_data { + void *buf; + size_t size; + int owned; // Whether we own the buffer (1) or not (0) +}; + +/* Error Handling */ +GQuark sqme_error_quark(void) { + return g_quark_from_static_string("sqme-error-quark"); +} + +void sqme_set_error(GError **error, sqme_error_t e, const char *msg) { + if (error) { + g_set_error(error, SQGPGME_ERROR, e, "%s", msg); + } +} + +const char *sqme_strerror(sqme_error_t err) { + switch (err) { + case SQGPGME_ERR_NO_ERROR: return "No error"; + case SQGPGME_ERR_GENERAL: return "General error"; + case SQGPGME_ERR_ENOMEM: return "Out of memory"; + case SQGPGME_ERR_CANCELED: return "Operation canceled"; + case SQGPGME_ERR_BAD_PASSPHRASE: return "Bad passphrase"; + case SQGPGME_ERR_SYSTEM: return "System error"; + default: return "Unknown error"; + } +} + +/* Context Management */ +sqme_error_t sqme_new(sqme_ctx_t **ctx) { + *ctx = g_malloc0(sizeof(sqme_ctx_t)); + if (!*ctx) return SQGPGME_ERR_ENOMEM; + return SQGPGME_ERR_NO_ERROR; +} + +void sqme_release(sqme_ctx_t *ctx) { + if (!ctx) return; + g_free(ctx->passphrase); + g_free(ctx); +} + +sqme_error_t sqme_set_passphrase(sqme_ctx_t *ctx, const char *passphrase) { + if (!ctx) return SQGPGME_ERR_GENERAL; + g_free(ctx->passphrase); + ctx->passphrase = g_strdup(passphrase); + return SQGPGME_ERR_NO_ERROR; +} + +/* Data Management */ +sqme_error_t sqme_data_new_from_mem(sqme_data_t **data, const void *buf, size_t size, int copy) { + *data = g_malloc0(sizeof(sqme_data_t)); + if (!*data) return SQGPGME_ERR_ENOMEM; + if (copy) { + (*data)->buf = g_memdup2(buf, size); + (*data)->owned = 1; + } else { + (*data)->buf = (void *)buf; + (*data)->owned = 0; + } + (*data)->size = size; + return SQGPGME_ERR_NO_ERROR; +} + +sqme_error_t sqme_data_new(sqme_data_t **data) { + *data = g_malloc0(sizeof(sqme_data_t)); + if (!*data) return SQGPGME_ERR_ENOMEM; + return SQGPGME_ERR_NO_ERROR; +} + +void sqme_data_release(sqme_data_t *data) { + if (!data) return; + if (data->owned) g_free(data->buf); + g_free(data); +} + +void *sqme_data_release_and_get_mem(sqme_data_t *data, size_t *size) { + if (!data) return NULL; + void *buf = data->buf; + *size = data->size; + data->buf = NULL; // Prevent double free + sqme_data_release(data); + return buf; +} + +/* Encryption/Decryption Helpers */ +static sqme_error_t run_sq(const char *passphrase, const char *cmd, const void *in, size_t in_size, void **out, size_t *out_size, GError **error) { + int in_pipe[2], out_pipe[2], err_pipe[2]; + pid_t pid; + ssize_t total_read = 0; + char *out_buf = NULL; + size_t buf_size = 0; + char fifo_prefix[] = "/tmp/sqme_fifoXXXXXX"; + char fifo_path[64]; // reserve some space for fifo_prefix and strcat later + int fifo_fd; + + // Create named pipe (FIFO) + if (mkdtemp(fifo_prefix) == NULL) { + return SQGPGME_ERR_SYSTEM; + } + strcat(strcpy(fifo_path, fifo_prefix), "/fifo"); + if (mkfifo(fifo_path, 0600) < 0) { + rmdir(fifo_prefix); + return SQGPGME_ERR_SYSTEM; + } + + if (pipe(in_pipe) < 0 || pipe(out_pipe) < 0 || pipe(err_pipe) < 0) { + unlink(fifo_path); + return SQGPGME_ERR_SYSTEM; + } + + pid = fork(); + if (pid < 0) { + close(in_pipe[0]); close(in_pipe[1]); + close(out_pipe[0]); close(out_pipe[1]); + close(err_pipe[0]); close(err_pipe[1]); + unlink(fifo_path); + rmdir(fifo_prefix); + return SQGPGME_ERR_SYSTEM; + } + + if (pid == 0) { // Child process + close(in_pipe[1]); + close(out_pipe[0]); + close(err_pipe[0]); + + dup2(in_pipe[0], STDIN_FILENO); + dup2(out_pipe[1], STDOUT_FILENO); + dup2(err_pipe[1], STDERR_FILENO); + + if (strcmp(cmd, "encrypt") == 0) { + execlp(SQ_PATH, SQ_PATH, "encrypt", "--with-password-file", fifo_path, "--without-signature", NULL); + } else if (strcmp(cmd, "decrypt") == 0) { + execlp(SQ_PATH, SQ_PATH, "decrypt", "--password-file", fifo_path, NULL); + } + if (errno == ENOENT) + _exit(127); + _exit(126); // If exec fails + } + + // Parent process + close(in_pipe[0]); + close(out_pipe[1]); + close(err_pipe[1]); + + // Write passphrase to FIFO (non-blocking write in a separate fork to avoid blocking) + pid_t fifo_pid = fork(); + if (fifo_pid < 0) { + close(in_pipe[1]); + close(out_pipe[0]); + close(err_pipe[0]); + waitpid(pid, NULL, 0); + unlink(fifo_path); + return SQGPGME_ERR_SYSTEM; + } + if (fifo_pid == 0) { + fifo_fd = open(fifo_path, O_WRONLY); // Blocks until sq opens for reading + if (fifo_fd >= 0) { + write(fifo_fd, passphrase, strlen(passphrase)); // No \n + close(fifo_fd); + } + _exit(0); + } + + // Write input data to in_pipe + size_t remaining = in_size; + const char *in_ptr = in; + while (remaining > 0) { + ssize_t written = write(in_pipe[1], in_ptr, remaining); + if (written < 0) { + close(in_pipe[1]); + close(out_pipe[0]); + close(err_pipe[0]); + waitpid(pid, NULL, 0); + waitpid(fifo_pid, NULL, 0); + unlink(fifo_path); + rmdir(fifo_prefix); + return SQGPGME_ERR_SYSTEM; + } + in_ptr += written; + remaining -= written; + } + close(in_pipe[1]); + + // Read output from out_pipe + char buffer[4096]; + while (1) { + ssize_t n = read(out_pipe[0], buffer, sizeof(buffer)); + if (n <= 0) break; // EOF or error + if (total_read + n > buf_size) { + buf_size = total_read + n + 4096; // Grow buffer with padding + out_buf = g_realloc(out_buf, buf_size); + } + memcpy(out_buf + total_read, buffer, n); + total_read += n; + } + close(out_pipe[0]); + + // Read stderr for error messages + char err_buf[1024]; + ssize_t err_len = read(err_pipe[0], err_buf, sizeof(err_buf) - 1); + close(err_pipe[0]); + if (err_len > 0) { + err_buf[err_len] = '\0'; + } + + int status; + waitpid(pid, &status, 0); + + int exit_code = 0; + if (WIFEXITED(status) && ((exit_code=WEXITSTATUS(status)) !=0)) { + // exit_code = WEXITSTATUS(status); + kill(fifo_pid, SIGTERM); // Terminate second child if still running + g_free(out_buf); + if (err_len > 0) { + sqme_set_error(error, SQGPGME_ERR_BAD_PASSPHRASE, err_buf); + } else if (exit_code == 126) { + sqme_set_error(error, SQGPGME_ERR_SYSTEM, "Execution of 'sq' failed"); + } else if (exit_code == 127) { + sqme_set_error(error, SQGPGME_ERR_SYSTEM, "sq executable not found"); + } else { + sqme_set_error(error, SQGPGME_ERR_BAD_PASSPHRASE, "Command failed, possibly bad passphrase"); + } + } + waitpid(fifo_pid, NULL, 0); // Clean up FIFO writer + rmdir(fifo_prefix); + unlink(fifo_path); // Remove FIFO + + if (exit_code) { + return SQGPGME_ERR_BAD_PASSPHRASE; + } + + *out = out_buf; + *out_size = total_read; + return SQGPGME_ERR_NO_ERROR; +} + +sqme_error_t sqme_op_encrypt(sqme_ctx_t *ctx, sqme_data_t *in, sqme_data_t *out, GError **error) { + if (!ctx || !in || !out || !ctx->passphrase) return SQGPGME_ERR_GENERAL; + void *out_buf; + size_t out_size; + sqme_error_t err = run_sq(ctx->passphrase, "encrypt", in->buf, in->size, &out_buf, &out_size, error); + if (err == SQGPGME_ERR_NO_ERROR) { + out->buf = out_buf; + out->size = out_size; + out->owned = 1; + } + return err; +} + +sqme_error_t sqme_op_decrypt(sqme_ctx_t *ctx, sqme_data_t *in, sqme_data_t *out, GError **error) { + if (!ctx || !in || !out || !ctx->passphrase) return SQGPGME_ERR_GENERAL; + void *out_buf; + size_t out_size; + sqme_error_t err = run_sq(ctx->passphrase, "decrypt", in->buf, in->size, &out_buf, &out_size, error); + if (err == SQGPGME_ERR_NO_ERROR) { + out->buf = out_buf; + out->size = out_size; + out->owned = 1; + } + return err; +} + +void * +encrypt_with_passphrase(size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + sqme_ctx_t *ctx; + sqme_data_t *src_data, *dest_data; + void *res; + + if (sqme_new(&ctx) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create context"); + goto err; + } + if (sqme_set_passphrase(ctx, passphrase) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_GENERAL, "Failed to set passphrase"); + goto err_ctx; + } + if (sqme_data_new_from_mem(&src_data, data, size, 0) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create source data"); + goto err_ctx; + } + if (sqme_data_new(&dest_data) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create destination data"); + goto err_src_data; + } + if (sqme_op_encrypt(ctx, src_data, dest_data, error) != SQGPGME_ERR_NO_ERROR) { + if (!*error) + sqme_set_error(error, SQGPGME_ERR_BAD_PASSPHRASE, "Encryption failed, possibly bad passphrase"); + goto err_dest_data; + } + + sqme_data_release(src_data); + res = sqme_data_release_and_get_mem(dest_data, res_size); + if (!res) { + g_set_error(error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, _("Unknown error getting encryption result")); + goto err_ctx; + } + sqme_release(ctx); + return res; + + err_dest_data: + sqme_data_release(dest_data); + err_src_data: + sqme_data_release(src_data); + err_ctx: + sqme_release(ctx); + err: + return NULL; +} + + +/* Decrypt DATA of SIZE using PASSPHRASE. + * Return decrypted data (for g_free()), setting RES_SIZE to the size of the + * result, on success, NULL otherwise. */ +void * +decrypt_with_passphrase(size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error) +{ + sqme_ctx_t *ctx; + sqme_data_t *src_data, *dest_data; + void *res; + + if (sqme_new(&ctx) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create context"); + goto err; + } + if (sqme_set_passphrase(ctx, passphrase) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_GENERAL, "Failed to set passphrase"); + goto err_ctx; + } + if (sqme_data_new_from_mem(&src_data, data, size, 0) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create source data"); + goto err_ctx; + } + if (sqme_data_new(&dest_data) != SQGPGME_ERR_NO_ERROR) { + sqme_set_error(error, SQGPGME_ERR_ENOMEM, "Failed to create destination data"); + goto err_src_data; + } + if (sqme_op_decrypt(ctx, src_data, dest_data, error) != SQGPGME_ERR_NO_ERROR) { + if (!*error) + sqme_set_error(error, SQGPGME_ERR_BAD_PASSPHRASE, "Decryption failed, possibly bad passphrase"); + goto err_dest_data; + } + + sqme_data_release(src_data); + res = sqme_data_release_and_get_mem(dest_data, res_size); + if (!res) { + g_set_error(error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, _("Unknown error getting encryption result")); + goto err_ctx; + } + sqme_release(ctx); + return res; + + err_dest_data: + sqme_data_release(dest_data); + err_src_data: + sqme_data_release(src_data); + err_ctx: + sqme_release(ctx); + err: + return NULL; +} diff --git a/lib/crypto-sq.h b/lib/crypto-sq.h new file mode 100644 index 0000000..e7ff6ca --- /dev/null +++ b/lib/crypto-sq.h @@ -0,0 +1,45 @@ +#ifndef SQME_H +#define SQME_H + +#include +#include + +typedef enum { + SQGPGME_ERR_NO_ERROR = 0, + SQGPGME_ERR_GENERAL = 1, + SQGPGME_ERR_ENOMEM = 2, + SQGPGME_ERR_CANCELED = 3, + SQGPGME_ERR_BAD_PASSPHRASE = 4, + SQGPGME_ERR_SYSTEM = 5 +} sqme_error_t; + +typedef struct _sqme_ctx sqme_ctx_t; +typedef struct _sqme_data sqme_data_t; + +// Error domain for sqme +GQuark sqme_error_quark(void); +#define SQGPGME_ERROR (sqme_error_quark()) + +sqme_error_t sqme_new(sqme_ctx_t **ctx); +void sqme_release(sqme_ctx_t *ctx); +sqme_error_t sqme_set_passphrase(sqme_ctx_t *ctx, const char *passphrase); + +sqme_error_t sqme_data_new_from_mem(sqme_data_t **data, const void *buf, size_t size, int copy); +sqme_error_t sqme_data_new(sqme_data_t **data); +void sqme_data_release(sqme_data_t *data); +void *sqme_data_release_and_get_mem(sqme_data_t *data, size_t *size); + +sqme_error_t sqme_op_encrypt(sqme_ctx_t *ctx, sqme_data_t *in, sqme_data_t *out, GError **error); +sqme_error_t sqme_op_decrypt(sqme_ctx_t *ctx, sqme_data_t *in, sqme_data_t *out, GError **error); + +const char *sqme_strerror(sqme_error_t err); +void sqme_set_error(GError **error, sqme_error_t e, const char *msg); + +void *encrypt_with_passphrase(size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); + +void *decrypt_with_passphrase(size_t *res_size, const void *data, size_t size, + const char *passphrase, GError **error); + +#endif /* SQME_H */ + diff --git a/lib/crypto.c b/lib/crypto.c index b78b886..b85246c 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -26,7 +26,6 @@ Author: Miloslav Trmač */ #include #include #include -#include #include #include #include @@ -34,6 +33,11 @@ Author: Miloslav Trmač */ #include #include +#if WITH_SQ +# include "crypto-sq.h" +#else +# include "crypto-gpgme.h" +#endif #include "crypto.h" #include "nss_error.h" #include "libvolume_key.h" @@ -682,219 +686,3 @@ unwrap_symmetric (size_t *clear_secret_size, PK11SymKey *wrapping_key, err: return NULL; } - - /* libgpgme utils */ - -static void -error_from_gpgme (GError **error, gpgme_error_t e) -{ - size_t len; - char *s; - - s = NULL; - len = 100; - for (;;) - { - s = g_realloc (s, len); - if (gpgme_strerror_r (e, s, len) == 0) - break; - len *= 2; - } - g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, _("%s: %s"), - gpgme_strsource (e), s); - g_free (s); -} - -static gpgme_error_t -gpgme_passphrase_cb (void *hook, const char *uid_hint, - const char *passphrase_info, int prev_was_bad, int fd) -{ - static const char nl = '\n'; - - const char *pw; - size_t len; - ssize_t res; - - (void)uid_hint; - (void)passphrase_info; - if (prev_was_bad != 0) - return GPG_ERR_CANCELED; - pw = hook; - len = strlen (pw); - while (len != 0) - { - res = write (fd, pw, len); - if (res < 0) - return gpgme_error_from_errno (errno); - pw += res; - len -= res; - } - if (write (fd, &nl, sizeof (nl)) < 0) - return gpgme_error_from_errno (errno); - return 0; -} - -/* Create and configure a gpgme context, to use PASSPHRASE. - Return 0 if OK, -1 on error. */ -static int -init_gpgme (gpgme_ctx_t *res, const char *passphrase, GError **error) -{ - gpgme_ctx_t ctx; - gpgme_error_t e; - - (void)gpgme_check_version (NULL); - e = gpgme_new (&ctx); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err; - } - e = gpgme_set_locale (ctx, LC_CTYPE, setlocale (LC_CTYPE, NULL)); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - e = gpgme_set_locale (ctx, LC_MESSAGES, setlocale (LC_MESSAGES, NULL)); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - e = gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - e = gpgme_ctx_set_engine_info (ctx, GPGME_PROTOCOL_OpenPGP, GPG_PATH, NULL); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - gpgme_set_pinentry_mode (ctx, GPGME_PINENTRY_MODE_LOOPBACK); - gpgme_set_passphrase_cb (ctx, gpgme_passphrase_cb, (void *)passphrase); - *res = ctx; - return 0; - - err_ctx: - gpgme_release (ctx); - err: - return -1; -} - - /* LIBVK_PACKET_FORMAT_PASSPHRASE */ - -/* Encrypt DATA of SIZE using PASSPHRASE. - Return encrypted data (for g_free()), setting RES_SIZE to the size of the - result, on success, NULL otherwise. */ -void * -encrypt_with_passphrase (size_t *res_size, const void *data, size_t size, - const char *passphrase, GError **error) -{ - gpgme_ctx_t ctx; - gpgme_error_t e; - gpgme_data_t src_data, dest_data; - void *gpgme_res, *res; - - // FIXME: this should eventually use CMS - if (init_gpgme (&ctx, passphrase, error) != 0) - goto err; - e = gpgme_data_new_from_mem (&src_data, data, size, 0); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - e = gpgme_data_new (&dest_data); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_src_data; - } - e = gpgme_op_encrypt (ctx, NULL, 0, src_data, dest_data); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_dest_data; - } - gpgme_data_release (src_data); - gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); - if (gpgme_res == NULL) - { - g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, - _("Unknown error getting encryption result")); - goto err_ctx; - } - res = g_memdup2 (gpgme_res, *res_size); - gpgme_free (gpgme_res); - - gpgme_release (ctx); - return res; - - err_dest_data: - gpgme_data_release (src_data); - err_src_data: - gpgme_data_release (dest_data); - err_ctx: - gpgme_release (ctx); - err: - return NULL; -} - -/* Decrypt DATA of SIZE using PASSPHRASE. - Return decrypted data (for g_free()), setting RES_SIZE to the size of the - result, on success, NULL otherwise. */ -void * -decrypt_with_passphrase (size_t *res_size, const void *data, size_t size, - const char *passphrase, GError **error) -{ - gpgme_ctx_t ctx; - gpgme_error_t e; - gpgme_data_t src_data, dest_data; - void *gpgme_res, *res; - - if (init_gpgme (&ctx, passphrase, error) != 0) - goto err; - e = gpgme_data_new_from_mem (&src_data, data, size, 0); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_ctx; - } - e = gpgme_data_new (&dest_data); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_src_data; - } - e = gpgme_op_decrypt (ctx, src_data, dest_data); - if (e != GPG_ERR_NO_ERROR) - { - error_from_gpgme (error, e); - goto err_dest_data; - } - gpgme_data_release (src_data); - gpgme_res = gpgme_data_release_and_get_mem (dest_data, res_size); - if (gpgme_res == NULL) - { - g_set_error (error, LIBVK_ERROR, LIBVK_ERROR_CRYPTO, - _("Unknown error getting decryption result")); - goto err_ctx; - } - res = g_memdup2 (gpgme_res, *res_size); - gpgme_free (gpgme_res); - - gpgme_release (ctx); - return res; - - err_dest_data: - gpgme_data_release (src_data); - err_src_data: - gpgme_data_release (dest_data); - err_ctx: - gpgme_release (ctx); - err: - return NULL; -} diff --git a/m4/sq.m4 b/m4/sq.m4 new file mode 100644 index 0000000..28d7023 --- /dev/null +++ b/m4/sq.m4 @@ -0,0 +1,34 @@ +AC_DEFUN([AM_CHECK_SQ], +[ + AC_ARG_WITH([sq], + [AS_HELP_STRING([--with-sq=PATH], + [specify the path to the Sequoia 'sq' binary])], + [sq_path="$withval"], + [sq_path=""]) + + + SQ_PATH="" + if test "x$sq_path" != "x" -a "x$sq_path" != "xyes" ; then + AC_MSG_CHECKING([for Sequoia sq]) + if test -x "$sq_path"; then + SQ_PATH="$sq_path" + AC_MSG_RESULT([found at $sq_path]) + else + AC_MSG_ERROR([specified sq binary at $sq_path is not executable]) + fi + else + # Search for sq in PATH + AC_PATH_PROG([SQ_PATH], [sq]) + + if test "x$SQ_PATH" = "x"; then + AC_MSG_ERROR([Sequoia sq binary not found in PATH]) + fi + fi + + # Define variables only if sq is found + if test "x$SQ_PATH" != "x"; then + AC_SUBST([SQ_PATH]) + AC_DEFINE_UNQUOTED([SQ_PATH], ["$SQ_PATH"], [Path to Sequoia sq binary]) + AC_DEFINE([WITH_SQ], [1], [Define to 1 if Sequoia sq binary is available]) + fi +])