You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
8.0 KiB
329 lines
8.0 KiB
/* |
|
* gfsec - Secret sharing tools |
|
* Copyright (C) 2016 Damien Goutte-Gattat |
|
* |
|
* 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 3 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, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include <config.h> |
|
#endif |
|
|
|
#include "scheme-gio.h" |
|
#include "scheme-module.h" |
|
|
|
#ifdef HAVE_GIO |
|
|
|
#include <string.h> |
|
#include <gio/gio.h> |
|
#include "util.h" |
|
|
|
typedef struct mount_cb_data { |
|
gboolean success; |
|
GMainLoop *loop; |
|
} mount_cb_data_t; |
|
|
|
static void |
|
mount_cb(GObject *source, GAsyncResult *res, gpointer user) |
|
{ |
|
GVolume *volume = G_VOLUME(source); |
|
mount_cb_data_t *cb_data = user; |
|
|
|
cb_data->success = g_volume_mount_finish(volume, res, NULL); |
|
g_main_loop_quit(cb_data->loop); |
|
} |
|
|
|
static GMount * |
|
mount_volume(GVolume *volume) |
|
{ |
|
GMount *mount = NULL; |
|
mount_cb_data_t cb_data; |
|
|
|
cb_data.loop = g_main_loop_new(NULL, FALSE); |
|
g_volume_mount(volume, G_MOUNT_MOUNT_NONE, NULL, NULL, mount_cb, &cb_data); |
|
g_main_loop_run(cb_data.loop); |
|
|
|
if ( cb_data.success ) |
|
mount = g_volume_get_mount(volume); |
|
|
|
g_main_loop_unref(cb_data.loop); |
|
|
|
return mount; |
|
} |
|
|
|
static void |
|
unmount_cb(GObject *source, GAsyncResult *res, gpointer user) |
|
{ |
|
GMount *mount = G_MOUNT(source); |
|
mount_cb_data_t *cb_data = user; |
|
|
|
cb_data->success = g_mount_unmount_with_operation_finish(mount, res, NULL); |
|
g_main_loop_quit(cb_data->loop); |
|
} |
|
|
|
static int |
|
unmount_volume(GMount *mount) |
|
{ |
|
mount_cb_data_t cb_data; |
|
|
|
cb_data.loop = g_main_loop_new(NULL, FALSE); |
|
g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_FORCE, NULL, NULL, |
|
unmount_cb, &cb_data); |
|
g_main_loop_run(cb_data.loop); |
|
|
|
g_main_loop_unref(cb_data.loop); |
|
g_object_unref(mount); |
|
|
|
return cb_data.success ? 0 : -1; |
|
} |
|
|
|
/** |
|
* Finds an external volume with the specified identifier. |
|
* |
|
* @param authority The identifier to look for. |
|
* @param by_uuid If non-zero, the \a authority parameter is a UUID; |
|
* otherwise, it is a volume label. |
|
* |
|
* @return A GVolume object if the desired volume was found, |
|
* otherwise NULL. |
|
*/ |
|
static GVolume * |
|
find_volume(const char *authority, int by_uuid) |
|
{ |
|
GVolumeMonitor *volmon; |
|
GList *volumes, *l; |
|
GVolume *volume = NULL; |
|
|
|
volmon = g_volume_monitor_get(); |
|
volumes = g_volume_monitor_get_volumes(volmon); |
|
|
|
for ( l = volumes; l != NULL; l = l->next ) { |
|
|
|
if ( ! volume ) { |
|
char *id; |
|
|
|
volume = G_VOLUME(l->data); |
|
id = by_uuid ? g_volume_get_uuid(volume) : g_volume_get_name(volume); |
|
|
|
if ( strcmp(authority, id) != 0 ) { |
|
g_object_unref(volume); |
|
volume = NULL; |
|
} |
|
|
|
g_free(id); |
|
} |
|
else |
|
g_object_unref(l->data); |
|
} |
|
|
|
g_list_free(volumes); |
|
g_object_unref(volmon); |
|
|
|
return volume; |
|
} |
|
|
|
/** |
|
* Gets the contents of a file on the given volume. |
|
* |
|
* @param[in] volume The volume the file is to be searched into. |
|
* @param[in] path The pathname of the file to search. |
|
* @param[out] buffer The buffer to fill with the file contents. |
|
* It will be automatically allocated. |
|
* @param[out] len The size of the allocated buffer. |
|
* |
|
* @return 0 if successful, -1 if an error occured. |
|
*/ |
|
static int |
|
get_file_contents(GVolume *volume, |
|
const char *path, |
|
unsigned char **buffer, |
|
size_t *len) |
|
{ |
|
GMount *mount; |
|
GFile *root; |
|
char *root_path, *full_path; |
|
int was_mounted, rc = -1; |
|
|
|
if ( (mount = g_volume_get_mount(volume)) ) |
|
was_mounted = 1; |
|
else if ( (mount = mount_volume(volume)) ) |
|
was_mounted = 0; |
|
else |
|
return -1; |
|
|
|
root = g_mount_get_root(mount); |
|
root_path = g_file_get_path(root); |
|
full_path = g_strjoin("/", root_path, path, NULL); |
|
|
|
if ( (*buffer = read_file(full_path, len, GFSEC_SECRET_MAX_SIZE)) ) |
|
rc = 0; |
|
|
|
if ( was_mounted ) |
|
g_object_unref(mount); |
|
else |
|
unmount_volume(mount); |
|
|
|
g_free(full_path); |
|
g_free(root_path); |
|
g_object_unref(root); |
|
|
|
return rc; |
|
} |
|
|
|
/** |
|
* Write a file on the given volume. |
|
* |
|
* @param volume The volume on which the file should be written. |
|
* @param path The pathname of the file to write. |
|
* @param buffer The contents to write. |
|
* @param len The size of the contents buffer. |
|
* |
|
* @return 0 if successful, -1 if an error occured. |
|
*/ |
|
static int |
|
put_file_contents(GVolume *volume, |
|
const char *path, |
|
unsigned char *buffer, |
|
size_t len) |
|
{ |
|
GMount *mount; |
|
GFile *root; |
|
char *root_path, *full_path; |
|
int was_mounted, rc = -1; |
|
|
|
if ( (mount = g_volume_get_mount(volume)) ) |
|
was_mounted = 1; |
|
else if ( (mount = mount_volume(volume)) ) |
|
was_mounted = 0; |
|
else |
|
return -1; |
|
|
|
root = g_mount_get_root(mount); |
|
root_path = g_file_get_path(root); |
|
full_path = g_strjoin("/", root_path, path, NULL); |
|
|
|
if ( write_file(full_path, buffer, len) != -1 ) |
|
rc = 0; |
|
|
|
if ( was_mounted ) |
|
g_object_unref(mount); |
|
else |
|
unmount_volume(mount); |
|
|
|
g_free(full_path); |
|
g_free(root_path); |
|
g_object_unref(root); |
|
|
|
return rc; |
|
} |
|
|
|
int |
|
gfsec_scheme_gio_get_file(gfsec_scheme_t scheme, |
|
const char *authority, |
|
const char *path, |
|
unsigned char **buffer, |
|
size_t *len) |
|
{ |
|
GVolume *volume = NULL; |
|
int rc = GFSEC_SCHEME_STATUS_ERROR; |
|
|
|
switch ( scheme ) { |
|
case GFSEC_SCHEME_UUID: |
|
volume = find_volume(authority, 1); |
|
break; |
|
|
|
case GFSEC_SCHEME_LABEL: |
|
volume = find_volume(authority, 0); |
|
break; |
|
|
|
default: |
|
return GFSEC_SCHEME_STATUS_UNSUPPORTED_SCHEME; |
|
} |
|
|
|
if ( volume ) { |
|
if ( get_file_contents(volume, path, buffer, len) == 0 ) |
|
rc = GFSEC_SCHEME_STATUS_SUCCESS; |
|
g_object_unref(volume); |
|
} |
|
|
|
return rc; |
|
} |
|
|
|
int |
|
gfsec_scheme_gio_put_file(gfsec_scheme_t scheme, |
|
const char *authority, |
|
const char *path, |
|
unsigned char *buffer, |
|
size_t len) |
|
{ |
|
GVolume *volume = NULL; |
|
int rc = GFSEC_SCHEME_STATUS_ERROR; |
|
|
|
switch ( scheme ) { |
|
case GFSEC_SCHEME_UUID: |
|
volume = find_volume(authority, 1); |
|
break; |
|
|
|
case GFSEC_SCHEME_LABEL: |
|
volume = find_volume(authority, 0); |
|
break; |
|
|
|
default: |
|
return GFSEC_SCHEME_STATUS_UNSUPPORTED_SCHEME; |
|
} |
|
|
|
if ( volume ) { |
|
if ( put_file_contents(volume, path, buffer, len) == 0 ) |
|
rc = GFSEC_SCHEME_STATUS_SUCCESS; |
|
g_object_unref(volume); |
|
} |
|
|
|
return rc; |
|
} |
|
|
|
void |
|
gfsec_scheme_gio_lst_auth(FILE *f) |
|
{ |
|
GVolumeMonitor *volmon; |
|
GList *volumes, *l; |
|
|
|
volmon = g_volume_monitor_get(); |
|
volumes = g_volume_monitor_get_volumes(volmon); |
|
|
|
for ( l = volumes; l != NULL; l = l->next ) { |
|
char *label, *uuid; |
|
GVolume *volume; |
|
|
|
volume = G_VOLUME(l->data); |
|
label = g_volume_get_name(volume); |
|
uuid = g_volume_get_uuid(volume); |
|
|
|
if ( label ) { |
|
fprintf(f, "label://%s GIO volume\n", label); |
|
free(label); |
|
} |
|
|
|
if ( uuid ) { |
|
fprintf(f, "uuid://%s GIO volume\n", uuid); |
|
free(uuid); |
|
} |
|
|
|
g_object_unref(l->data); |
|
} |
|
|
|
g_list_free(volumes); |
|
g_object_unref(volmon); |
|
} |
|
|
|
#endif /* HAVE_GIO */
|
|
|