コード例 #1
0
ファイル: main.c プロジェクト: mgrzeschik/rauc
static gboolean checksum_start(int argc, char **argv)
{
	GError *error = NULL;
	gboolean sign = FALSE;

	g_message("checksum start");

	if (r_context()->certpath != NULL &&
	    r_context()->keypath != NULL) {
		sign = TRUE;
	} else if (r_context()->certpath != NULL ||
	    r_context()->keypath != NULL) {
		g_warning("Either both or none of cert and key files must be provided");
		r_exit_status = 1;
		goto out;
	}

	if (argc != 3) {
		g_warning("A directory name must be provided");
		r_exit_status = 1;
		goto out;
	}

	g_message("updating checksums for: %s", argv[2]);

	if (!update_manifest(argv[2], sign, &error)) {
		g_warning("Failed to update manifest: %s", error->message);
		g_clear_error(&error);
		r_exit_status = 1;
	}

out:
	return TRUE;
}
コード例 #2
0
ファイル: bootchooser.c プロジェクト: mgrzeschik/rauc
static gboolean grub_env_set(GPtrArray *pairs) {
	GSubprocess *sub;
	GError *error = NULL;
	gboolean res = FALSE;

	g_assert_cmpuint(pairs->len, >, 0);
	g_assert_nonnull(r_context()->config->grubenv_path);

	g_ptr_array_insert(pairs, 0, g_strdup("grub-editenv"));
	g_ptr_array_insert(pairs, 1, g_strdup(r_context()->config->grubenv_path));
	g_ptr_array_insert(pairs, 2, g_strdup("set"));
	g_ptr_array_add(pairs, NULL);

	sub = g_subprocess_newv((const gchar * const *)pairs->pdata,
				  G_SUBPROCESS_FLAGS_NONE, &error);
	if (!sub) {
		g_warning("starting grub-editenv failed: %s", error->message);
		g_clear_error(&error);
		goto out;
	}

	res = g_subprocess_wait_check(sub, NULL, &error);
	if (!res) {
		g_warning("grub-editenv failed: %s", error->message);
		g_clear_error(&error);
		goto out;
	}

out:
	g_ptr_array_remove_index(pairs, pairs->len-1);
	g_ptr_array_remove_index(pairs, 2);
	g_ptr_array_remove_index(pairs, 1);
	g_ptr_array_remove_index(pairs, 0);
	return res;
}
コード例 #3
0
ファイル: config_file.c プロジェクト: ukleinek/rauc
static gboolean save_slot_status_globally(GError **error)
{
	g_autoptr(GKeyFile) key_file = g_key_file_new();
	GError *ierror = NULL;
	GHashTableIter iter;
	RaucSlot *slot;
	gboolean res;

	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
	g_return_val_if_fail(r_context()->config->statusfile_path, FALSE);

	g_debug("Saving global slot status");

	/* Save all slot status information */
	g_hash_table_iter_init(&iter, r_context()->config->slots);
	while (g_hash_table_iter_next(&iter, NULL, (gpointer*) &slot)) {
		g_autofree gchar *group;

		if (!slot->status) {
			continue;
		}
		group = g_strdup_printf(RAUC_SLOT_PREFIX ".%s", slot->name);
		status_file_set_slot_status(key_file, group, slot->status);
	}

	res = g_key_file_save_to_file(key_file, r_context()->config->statusfile_path, &ierror);
	if (!res)
		g_propagate_error(error, ierror);

	return res;
}
コード例 #4
0
ファイル: bootchooser.c プロジェクト: mgrzeschik/rauc
gboolean r_boot_set_primary(RaucSlot *slot) {
	if (g_strcmp0(r_context()->config->system_bootloader, "barebox") == 0) {
		return barebox_set_primary(slot);
	} else if (g_strcmp0(r_context()->config->system_bootloader, "grub") == 0) {
		return grub_set_primary(slot);
	}

	g_print("Warning: Your bootloader '%s' is not supported yet\n", r_context()->config->system_bootloader);
	return TRUE;
}
コード例 #5
0
ファイル: signature.c プロジェクト: BastianStender/rauc
int main(int argc, char *argv[])
{
	setlocale(LC_ALL, "C");

	r_context_conf()->configpath = g_strdup("test/test.conf");
	r_context_conf()->certpath = g_strdup("test/openssl-ca/rel/release-1.cert.pem");
	r_context_conf()->keypath = g_strdup("test/openssl-ca/rel/private/release-1.pem");
	r_context();

	g_assert(test_prepare_dummy_file("test/", "random.dat",
				         256 * 1024, "/dev/urandom") == 0);

	g_assert(test_prepare_dummy_file("test/", "empty.dat",
				         0, "/dev/zero") == 0);

	g_test_init(&argc, &argv, NULL);

	g_test_add_func("/signature/sign", signature_sign);
	g_test_add_func("/signature/sign_file", signature_sign_file);
	g_test_add_func("/signature/verify", signature_verify);
	g_test_add_func("/signature/verify_file", signature_verify_file);
	g_test_add_func("/signature/loopback", signature_loopback);

	return g_test_run();
}
コード例 #6
0
ファイル: signature.c プロジェクト: BastianStender/rauc
static void signature_loopback(void)
{
	GBytes *content = read_file("test/openssl-ca/manifest", NULL);
	GBytes *sig = NULL;
	g_assert_nonnull(content);
	sig = cms_sign(content,
		       r_context()->certpath,
		       r_context()->keypath,
		       NULL);
	g_assert_nonnull(sig);
	g_assert_true(cms_verify(content, sig, NULL));
	((char *)g_bytes_get_data(content, NULL))[0] = 0x00;
	g_assert_false(cms_verify(content, sig, NULL));
	g_bytes_unref(content);
	g_bytes_unref(sig);
}
コード例 #7
0
ファイル: config_file.c プロジェクト: ukleinek/rauc
gboolean save_slot_status(RaucSlot *dest_slot, GError **error)
{
	g_return_val_if_fail(dest_slot, FALSE);
	g_return_val_if_fail(error == NULL || *error == NULL, FALSE);

	if (r_context()->config->statusfile_path)
		return save_slot_status_globally(error);
	else
		return save_slot_status_locally(dest_slot, error);
}
コード例 #8
0
ファイル: config_file.c プロジェクト: ukleinek/rauc
static void load_slot_status_globally(void)
{
	GError *ierror = NULL;
	GHashTable *slots = r_context()->config->slots;
	g_autoptr(GKeyFile) key_file = g_key_file_new();
	g_auto(GStrv) groups = NULL;
	gchar **group, *slotname;
	GHashTableIter iter;
	RaucSlot *slot;

	g_return_if_fail(r_context()->config->statusfile_path);

	g_key_file_load_from_file(key_file, r_context()->config->statusfile_path, G_KEY_FILE_NONE, &ierror);
	if (ierror && !g_error_matches(ierror, G_FILE_ERROR, G_FILE_ERROR_NOENT))
		g_message("load_slot_status_globally: %s.", ierror->message);
	g_clear_error(&ierror);

	/* Load all slot states included in the statusfile */
	groups = g_key_file_get_groups(key_file, NULL);
	for (group = groups; *group != NULL; group++) {
		if (!g_str_has_prefix(*group, RAUC_SLOT_PREFIX "."))
			continue;

		slotname = *group + strlen(RAUC_SLOT_PREFIX ".");
		slot = g_hash_table_lookup(slots, slotname);
		if (!slot || slot->status)
			continue;

		slot->status = g_new0(RaucSlotStatus, 1);
		g_debug("Load status for slot %s.", slot->name);
		status_file_get_slot_status(key_file, *group, slot->status);
	}

	/* Set all other slots to the default status */
	g_hash_table_iter_init(&iter, slots);
	while (g_hash_table_iter_next(&iter, NULL, (gpointer*) &slot)) {
		if (slot->status)
			continue;

		g_debug("Set default status for slot %s.", slot->name);
		slot->status = g_new0(RaucSlotStatus, 1);
	}
}
コード例 #9
0
ファイル: main.c プロジェクト: mgrzeschik/rauc
static gboolean bundle_start(int argc, char **argv)
{
	GError *ierror = NULL;
	g_debug("bundle start");

	if (r_context()->certpath == NULL ||
	    r_context()->keypath == NULL) {
		g_warning("cert and key files must be provided");
		r_exit_status = 1;
		goto out;
	}

	if (argc < 3) {
		g_warning("an input directory name must be provided");
		r_exit_status = 1;
		goto out;
	}

	if (argc != 4) {
		g_warning("an output bundle name must be provided");
		r_exit_status = 1;
		goto out;
	}

	g_print("input directory: %s\n", argv[2]);
	g_print("output bundle: %s\n", argv[3]);

	if (!update_manifest(argv[2], FALSE, &ierror)) {
		g_warning("failed to update manifest: %s", ierror->message);
		r_exit_status = 1;
		goto out;
	}

	if (!create_bundle(argv[3], argv[2], &ierror)) {
		g_warning("failed to create bundle: %s", ierror->message);
		r_exit_status = 1;
		goto out;
	}

out:
	return TRUE;
}
コード例 #10
0
ファイル: config_file.c プロジェクト: ukleinek/rauc
void load_slot_status(RaucSlot *dest_slot)
{
	g_return_if_fail(dest_slot);

	if (dest_slot->status)
		return;

	if (r_context()->config->statusfile_path)
		load_slot_status_globally();
	else
		load_slot_status_locally(dest_slot);
}
コード例 #11
0
ファイル: signature.c プロジェクト: BastianStender/rauc
static void signature_sign(void)
{
	GBytes *content = read_file("test/openssl-ca/manifest", NULL);
	GBytes *sig = NULL;
	GError *error = NULL;
	g_assert_nonnull(content);

	// Test valid signing
	sig = cms_sign(content,
		       r_context()->certpath,
		       r_context()->keypath,
		       &error);
	g_assert_nonnull(sig);
	g_assert_null(error);

	g_bytes_unref(sig);

	// Test signing fail with invalid key
	sig = cms_sign(content,
		       r_context()->certpath,
		       "test/random.dat",
		       &error);
	g_assert_null(sig);
	g_assert_nonnull(error);

	g_clear_error(&error);

	// Test signing fail with invalid cert
	sig = cms_sign(content,
		       "test/random.dat",
		       r_context()->keypath,
		       &error);
	g_assert_null(sig);
	g_assert_nonnull(error);

	g_clear_error(&error);

	g_bytes_unref(content);
}
コード例 #12
0
ファイル: signature.c プロジェクト: BastianStender/rauc
static void signature_sign_file(void)
{
	GBytes *sig = NULL;
	GError *error = NULL;

	// Test valid file
	sig = cms_sign_file("test/openssl-ca/manifest",
			    r_context()->certpath,
			    r_context()->keypath,
			    &error);
	g_assert_nonnull(sig);
	g_assert_null(error);

	g_bytes_unref(sig);
	g_clear_error(&error);


	// Test non-existing file
	sig = cms_sign_file("path/to/nonexisting/file",
			    r_context()->certpath,
			    r_context()->keypath,
			    &error);
	g_assert_null(sig);
	g_assert_nonnull(error);

	g_bytes_unref(sig);
	g_clear_error(&error);

	// Test invalid certificate
	sig = cms_sign_file("test/openssl-ca/manifest",
			    NULL,
			    r_context()->keypath,
			    &error);
	g_assert_null(sig);
	g_assert_nonnull(error);

	g_bytes_unref(sig);
	g_clear_error(&error);
}
コード例 #13
0
ファイル: bootchooser.c プロジェクト: mgrzeschik/rauc
/* Set slot as primary boot slot */
static gboolean barebox_set_primary(RaucSlot *slot) {
	GPtrArray *pairs = g_ptr_array_new_full(10, g_free);
	int prio1 = 20, prio2 = 10;
	gboolean res = FALSE;
	GList *slots;

	g_assert_nonnull(slot);

	/* Iterate over class members */
	slots = g_hash_table_get_values(r_context()->config->slots);
	for (GList *l = slots; l != NULL; l = l->next) {
		RaucSlot *s = l->data;
		int prio;

		if (s->sclass != slot->sclass)
			continue;

		if (s == slot) {
			prio = prio1;
		} else {
			prio = prio2;
		}
		g_ptr_array_add(pairs, g_strdup_printf("%s.%s.priority=%i",
				BOOTSTATE_PREFIX, s->bootname, prio));
	}

	g_ptr_array_add(pairs, g_strdup_printf("%s.%s.ok=%i",
			BOOTSTATE_PREFIX, slot->bootname, 1));
	g_ptr_array_add(pairs, g_strdup_printf("%s.%s.remaining_attempts=%i",
			BOOTSTATE_PREFIX, slot->bootname, 3));

	res = barebox_state_set(pairs);
	if (!res) {
		g_warning("failed marking as primary");
		goto out;
	}

	res = TRUE;
out:
	return res;
}
コード例 #14
0
ファイル: bootchooser.c プロジェクト: mgrzeschik/rauc
/* Set slot as primary boot slot */
static gboolean grub_set_primary(RaucSlot *slot) {
	GPtrArray *pairs = g_ptr_array_new_full(10, g_free);
	GString *order = g_string_sized_new(10);
	gboolean res = FALSE;
	GList *slots;

	g_assert_nonnull(slot);

	g_string_append(order, slot->bootname);

	/* Iterate over class members */
	slots = g_hash_table_get_values(r_context()->config->slots);
	for (GList *l = slots; l != NULL; l = l->next) {
		RaucSlot *s = l->data;
		if (s == slot)
			continue;
		if (s->sclass != slot->sclass)
			continue;

		g_string_append_c(order, ' ');
		g_string_append(order, s->bootname);
	}

	g_ptr_array_add(pairs, g_strdup_printf("%s_OK=%i", slot->bootname, 1));
	g_ptr_array_add(pairs, g_strdup_printf("%s_TRY=%i", slot->bootname, 0));
	g_ptr_array_add(pairs, g_strdup_printf("ORDER=%s", order->str));

	res = grub_env_set(pairs);
	if (!res) {
		g_warning("failed marking as primary");
		goto out;
	}

	res = TRUE;
out:
	g_string_free(order, TRUE);
	g_ptr_array_unref(pairs);
	return res;
}
コード例 #15
0
ファイル: mount.c プロジェクト: gavinschenk/rauc
/* Creates a mount subdir in mount path prefix */
gchar* r_create_mount_point(const gchar *name, GError **error) {
	gchar* prefix;
	gchar* mountpoint = NULL;

	prefix = r_context()->config->mount_prefix;
	if (!g_file_test (prefix, G_FILE_TEST_IS_DIR)) {
		g_set_error(
				error,
				G_FILE_ERROR,
				G_FILE_ERROR_NOTDIR,
				"mount prefix path %s does not exist",
				prefix);
		goto out;
	}


	mountpoint = g_build_filename(prefix, name, NULL);

	if (!g_file_test (mountpoint, G_FILE_TEST_IS_DIR)) {
		gint ret;
		ret = g_mkdir(mountpoint, 0777);

		if (ret != 0) {
			g_set_error(
					error,
					G_FILE_ERROR,
					G_FILE_ERROR_FAILED,
					"Failed creating mount path '%s'",
					mountpoint);
			g_free(mountpoint);
			mountpoint = NULL;
			goto out;
		}
	}

out:

	return mountpoint;
}
コード例 #16
0
ファイル: main.c プロジェクト: mgrzeschik/rauc
static gboolean status_start(int argc, char **argv)
{
	GHashTableIter iter;
	gpointer key, value;
	gboolean res = FALSE;
	RaucSlot *booted = NULL;

	g_message("status start\n");

	g_print("booted from: %s\n", get_bootname());

	res = determine_slot_states();
	if (!res) {
		g_warning("Failed to determine slot states");
		r_exit_status = 1;
		goto out;
	}

	g_print("slot states:\n");
	g_hash_table_iter_init(&iter, r_context()->config->slots);
	while (g_hash_table_iter_next(&iter, &key, &value)) {
		gchar *name = key;
		RaucSlot *slot = value;
		const gchar *state = NULL;
		switch (slot->state) {
		case ST_ACTIVE:
			state = "active";
			break;
		case ST_INACTIVE:
			state = "inactive";
			break;
		case ST_BOOTED:
			state = "booted";
			booted = slot;
			break;
		case ST_UNKNOWN:
		default:
			g_error("invalid slot status");
			r_exit_status = 1;
			break;
		}
		g_print("  %s: class=%s, device=%s, type=%s, bootname=%s\n",
			name, slot->sclass, slot->device, slot->type, slot->bootname);
		g_print("      state=%s", state);
		if (slot->parent)
			g_print(", parent=%s", slot->parent->name);
		else
			g_print(", parent=(none)");
		if (slot->mountpoint)
			g_print(", mountpoint=%s", slot->mountpoint);
		else
			g_print(", mountpoint=(none)");
		g_print("\n");
	}

	if (argc < 3) {
		r_exit_status = 1;
		goto out;
	}

	if (!booted) {
		g_warning("Failed to determine booted slot");
		r_exit_status = 1;
		goto out;
	}

	if (g_strcmp0(argv[2], "mark-good") == 0) {
		g_print("marking slot %s as good\n", booted->name);
		r_exit_status = r_boot_set_state(booted, TRUE) ? 0 : 1;
	} else if (g_strcmp0(argv[2], "mark-bad") == 0) {
		g_print("marking slot %s as bad\n", booted->name);
		r_exit_status = r_boot_set_state(booted, FALSE) ? 0 : 1;
	} else {
		g_message("unknown subcommand %s", argv[2]);
		r_exit_status = 1;
	}

out:
	return TRUE;
}
コード例 #17
0
ファイル: bundle.c プロジェクト: gavinschenk/rauc
gboolean check_bundle(const gchar *bundlename, gsize *size, gboolean verify, GError **error) {
	GError *ierror = NULL;
	GBytes *sig = NULL;
	GFile *bundlefile = NULL;
	GFileInputStream *bundlestream = NULL;
	guint64 sigsize;
	goffset offset;
	gboolean res = FALSE;

	r_context_begin_step("check_bundle", "Checking bundle", verify);

	if (!r_context()->config->keyring_path) {
		g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_EXIST, "No keyring file provided");
		goto out;
	}

	g_message("Reading bundle: %s", bundlename);

	bundlefile = g_file_new_for_path(bundlename);
	bundlestream = g_file_read(bundlefile, NULL, &ierror);
	if (bundlestream == NULL) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to open bundle for reading: ");
		goto out;
	}

	offset = sizeof(sigsize);
	res = g_seekable_seek(G_SEEKABLE(bundlestream),
			      -offset, G_SEEK_END, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to seek to end of bundle: ");
		goto out;
	}
	offset = g_seekable_tell((GSeekable *)bundlestream);

	res = input_stream_read_uint64_all(G_INPUT_STREAM(bundlestream),
			                   &sigsize, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to read signature size from bundle: ");
		goto out;
	}

	offset -= sigsize;

	if (size)
		*size = offset;

	res = g_seekable_seek(G_SEEKABLE(bundlestream),
			      offset, G_SEEK_SET, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to seek to start of bundle signature: ");
		goto out;
	}

	res = input_stream_read_bytes_all(G_INPUT_STREAM(bundlestream),
			                  &sig, sigsize, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to read signature from bundle: ");
		goto out;
	}

	if (verify) {
		g_message("Verifying bundle... ");
		/* the squashfs image size is in offset */
		res = cms_verify_file(bundlename, sig, offset, &ierror);
		if (!res) {
			g_propagate_error(error, ierror);
			goto out;
		}
	}

	res = TRUE;
out:
	g_clear_object(&bundlestream);
	g_clear_object(&bundlefile);
	g_clear_pointer(&sig, g_bytes_unref);
	r_context_end_step("check_bundle", res);
	return res;
}
コード例 #18
0
ファイル: bundle.c プロジェクト: gavinschenk/rauc
gboolean create_bundle(const gchar *bundlename, const gchar *contentdir, GError **error) {
	GError *ierror = NULL;
	GBytes *sig = NULL;
	GFile *bundlefile = NULL;
	GFileOutputStream *bundlestream = NULL;
	gboolean res = FALSE;
	guint64 offset;

	g_assert_nonnull(r_context()->certpath);
	g_assert_nonnull(r_context()->keypath);

	res = mksquashfs(bundlename, contentdir, &ierror);
	if (!res) {
		g_propagate_error(error, ierror);
		goto out;
	}

	sig = cms_sign_file(bundlename,
			    r_context()->certpath,
			    r_context()->keypath,
			    &ierror);
	if (sig == NULL) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed signing bundle: ");
		goto out;
	}

	bundlefile = g_file_new_for_path(bundlename);
	bundlestream = g_file_append_to(bundlefile, G_FILE_CREATE_NONE, NULL, &ierror);
	if (bundlestream == NULL) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to open bundle for appending: ");
		goto out;
	}

	res = g_seekable_seek(G_SEEKABLE(bundlestream),
			      0, G_SEEK_END, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to seek to end of bundle: ");
		goto out;
	}

	offset = g_seekable_tell((GSeekable *)bundlestream);
	res = output_stream_write_bytes_all((GOutputStream *)bundlestream, sig, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to append signature to bundle: ");
		goto out;
	}


	offset = g_seekable_tell((GSeekable *)bundlestream) - offset;
	res = output_stream_write_uint64_all((GOutputStream *)bundlestream, offset, NULL, &ierror);
	if (!res) {
		g_propagate_prefixed_error(
				error,
				ierror,
				"failed to append signature size to bundle: ");
		goto out;
	}


	res = TRUE;
out:
	g_clear_object(&bundlestream);
	g_clear_object(&bundlefile);
	g_clear_pointer(&sig, g_bytes_unref);
	return res;
}
コード例 #19
0
ファイル: signature.c プロジェクト: ukleinek/rauc
GBytes *cms_sign(GBytes *content, const gchar *certfile, const gchar *keyfile, gchar **interfiles, GError **error)
{
	GError *ierror = NULL;
	BIO *incontent = BIO_new_mem_buf((void *)g_bytes_get_data(content, NULL),
			g_bytes_get_size(content));
	BIO *outsig = BIO_new(BIO_s_mem());
	X509 *signcert = NULL;
	EVP_PKEY *pkey = NULL;
	STACK_OF(X509) *intercerts = NULL;
	CMS_ContentInfo *cms = NULL;
	GBytes *res = NULL;
	int flags = CMS_DETACHED | CMS_BINARY;

	g_return_val_if_fail(content != NULL, NULL);
	g_return_val_if_fail(certfile != NULL, NULL);
	g_return_val_if_fail(keyfile != NULL, NULL);
	g_return_val_if_fail(error == NULL || *error == NULL, NULL);

	signcert = load_cert(certfile, &ierror);
	if (signcert == NULL) {
		g_propagate_error(error, ierror);
		goto out;
	}

	pkey = load_key(keyfile, &ierror);
	if (pkey == NULL) {
		g_propagate_error(error, ierror);
		goto out;
	}

	intercerts = sk_X509_new_null();

	for (gchar **intercertpath = interfiles; intercertpath && *intercertpath != NULL; intercertpath++) {

		X509 *intercert = load_cert(*intercertpath, &ierror);
		if (intercert == NULL) {
			g_propagate_error(error, ierror);
			goto out;
		}

		sk_X509_push(intercerts, intercert);
	}

	cms = CMS_sign(signcert, pkey, intercerts, incontent, flags);
	if (cms == NULL) {
		unsigned long err;
		const gchar *data;
		int errflags;
		err = ERR_get_error_line_data(NULL, NULL, &data, &errflags);
		g_set_error(
				error,
				R_SIGNATURE_ERROR,
				R_SIGNATURE_ERROR_INVALID,
				"failed to create signature: %s", (errflags & ERR_TXT_STRING) ? data : ERR_error_string(err, NULL));
		goto out;
	}
	if (!i2d_CMS_bio(outsig, cms)) {
		g_set_error_literal(
				error,
				R_SIGNATURE_ERROR,
				R_SIGNATURE_ERROR_SERIALIZE_SIG,
				"failed to serialize signature");
		goto out;
	}

	res = bytes_from_bio(outsig);

	if (!res) {
		g_set_error_literal(
				error,
				R_SIGNATURE_ERROR,
				R_SIGNATURE_ERROR_UNKNOWN,
				"Read zero bytes");
		goto out;
	}

	/* keyring was given, perform verification to obtain trust chain */
	if (r_context()->config->keyring_path) {
		g_autoptr(CMS_ContentInfo) vcms = NULL;
		g_autoptr(X509_STORE) store = NULL;
		STACK_OF(X509) *verified_chain = NULL;

		g_message("Keyring given, doing signature verification");
		if (!cms_verify(content, res, &vcms, &store, &ierror)) {
			g_propagate_error(error, ierror);
			res = NULL;
			goto out;
		}

		if (!cms_get_cert_chain(vcms, store, &verified_chain, &ierror)) {
			g_propagate_error(error, ierror);
			res = NULL;
			goto out;
		}

		for (int i = 0; i < sk_X509_num(verified_chain); i++) {
			const ASN1_TIME *expiry_time;
			struct tm *next_month;
			time_t now;
			time_t comp;
			time(&now);

			next_month = gmtime(&now);
			next_month->tm_mon += 1;
			if (next_month->tm_mon == 12)
				next_month->tm_mon = 0;
			comp = timegm(next_month);

			expiry_time = X509_get0_notAfter(sk_X509_value(verified_chain, i));

			/* Check if expiry time is within last month */
			if (X509_cmp_current_time(expiry_time) == 1 && X509_cmp_time(expiry_time, &comp) == -1) {
				char buf[BUFSIZ];
				X509_NAME_oneline(X509_get_subject_name(sk_X509_value(verified_chain, i)),
						buf, sizeof buf);
				g_warning("Certificate %d (%s) will exipre in less than a month!", i + 1, buf);
			}
		}

		sk_X509_pop_free(verified_chain, X509_free);
	} else {
		g_message("No keyring given, skipping signature verification");
	}
out:
	ERR_print_errors_fp(stdout);
	BIO_free_all(incontent);
	BIO_free_all(outsig);
	return res;
}