Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
libsecret
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
28
Issues
28
List
Boards
Labels
Service Desk
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
GNOME
libsecret
Commits
2d642b5b
Commit
2d642b5b
authored
Aug 13, 2019
by
Daiki Ueno
Committed by
Daiki Ueno
Oct 13, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
secret-file-backend: New backend for storing secrets in file
This adds a new backend based on locally stored file.
parent
9cfad7c6
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2124 additions
and
3 deletions
+2124
-3
libsecret/Makefile.am
libsecret/Makefile.am
+21
-0
libsecret/fixtures/default.keyring
libsecret/fixtures/default.keyring
+0
-0
libsecret/meson.build
libsecret/meson.build
+17
-3
libsecret/secret-backend.c
libsecret/secret-backend.c
+8
-0
libsecret/secret-file-backend.c
libsecret/secret-file-backend.c
+498
-0
libsecret/secret-file-backend.h
libsecret/secret-file-backend.h
+31
-0
libsecret/secret-file-collection.c
libsecret/secret-file-collection.c
+842
-0
libsecret/secret-file-collection.h
libsecret/secret-file-collection.h
+56
-0
libsecret/secret-file-item.c
libsecret/secret-file-item.c
+252
-0
libsecret/secret-file-item.h
libsecret/secret-file-item.h
+34
-0
libsecret/secret-types.h
libsecret/secret-types.h
+1
-0
libsecret/test-file-collection.c
libsecret/test-file-collection.c
+364
-0
No files found.
libsecret/Makefile.am
View file @
2d642b5b
...
...
@@ -60,6 +60,17 @@ libsecret_PRIVATE = \
libsecret/secret-util.c
\
$(NULL)
if
WITH_GCRYPT
libsecret_PRIVATE
+=
\
libsecret/secret-file-backend.h
\
libsecret/secret-file-backend.c
\
libsecret/secret-file-collection.h
\
libsecret/secret-file-collection.c
\
libsecret/secret-file-item.h
\
libsecret/secret-file-item.c
\
$(NULL)
endif
libsecret_@SECRET_MAJOR@
_la_SOURCES
=
\
$(libsecret_PUBLIC)
\
$(libsecret_PRIVATE)
\
...
...
@@ -247,6 +258,15 @@ test_session_LDADD = $(libsecret_LIBS)
test_value_SOURCES
=
libsecret/test-value.c
test_value_LDADD
=
$(libsecret_LIBS)
if
WITH_GCRYPT
C_TESTS
+=
\
test-file-collection
\
$(NULL)
test_file_collection_SOURCES
=
libsecret/test-file-collection.c
test_file_collection_LDADD
=
$(libsecret_LIBS)
endif
JS_TESTS
=
\
libsecret/test-js-lookup.js
\
libsecret/test-js-clear.js
\
...
...
@@ -377,4 +397,5 @@ EXTRA_DIST += \
libsecret/mock-service-prompt.py
\
$(JS_TESTS)
\
$(PY_TESTS)
\
libsecret/fixtures
\
$(NULL)
libsecret/fixtures/default.keyring
0 → 100644
View file @
2d642b5b
File added
libsecret/meson.build
View file @
2d642b5b
...
...
@@ -35,6 +35,14 @@ libsecret_headers = [
'secret-value.h',
]
if with_gcrypt
libsecret_sources += [
'secret-file-backend.c',
'secret-file-collection.c',
'secret-file-item.c',
]
endif
version_numbers = meson.project_version().split('.')
version_major = version_numbers[0].to_int()
version_minor = version_numbers[1].to_int()
...
...
@@ -168,7 +176,7 @@ pkg.generate(description: 'GObject bindings for Secret Service API (Unstable)',
requires: libsecret)
# Tests
mock
_cflags = [
test
_cflags = [
libsecret_cflags,
'-DSRCDIR="@0@"'.format(meson.source_root()),
]
...
...
@@ -176,7 +184,7 @@ mock_cflags = [
mock_service_lib = static_library('mock-service',
'mock-service.c',
dependencies: glib_deps,
c_args:
mock
_cflags,
c_args:
test
_cflags,
include_directories: config_h_dir,
)
...
...
@@ -193,6 +201,12 @@ test_names = [
'test-collection',
]
if with_gcrypt
test_names += [
'test-file-collection',
]
endif
foreach _test : test_names
test_bin = executable(_test,
...
...
@@ -200,7 +214,7 @@ foreach _test : test_names
dependencies: libsecret_dep,
link_with: mock_service_lib,
include_directories: config_h_dir,
c_args:
libsecre
t_cflags,
c_args:
tes
t_cflags,
)
test(_test, test_bin)
...
...
libsecret/secret-backend.c
View file @
2d642b5b
...
...
@@ -15,6 +15,11 @@
#include "config.h"
#include "secret-backend.h"
#ifdef WITH_GCRYPT
#include "secret-file-backend.h"
#endif
#include "secret-private.h"
#include "libsecret/secret-enum-types.h"
...
...
@@ -151,6 +156,9 @@ backend_get_impl_type (void)
extension_name
=
envvar
;
g_type_ensure
(
secret_service_get_type
());
#ifdef WITH_GCRYPT
g_type_ensure
(
secret_file_backend_get_type
());
#endif
ep
=
g_io_extension_point_lookup
(
SECRET_BACKEND_EXTENSION_POINT_NAME
);
e
=
g_io_extension_point_get_extension_by_name
(
ep
,
extension_name
);
...
...
libsecret/secret-file-backend.c
0 → 100644
View file @
2d642b5b
/* libsecret - GLib wrapper for Secret Service
*
* Copyright 2019 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: Daiki Ueno
*/
#include "config.h"
#include "secret-backend.h"
#include "secret-file-backend.h"
#include "secret-file-collection.h"
#include "secret-file-item.h"
#include "secret-private.h"
#include "secret-retrievable.h"
static
void
secret_file_backend_async_initable_iface
(
GAsyncInitableIface
*
iface
);
static
void
secret_file_backend_backend_iface
(
SecretBackendInterface
*
iface
);
struct
_SecretFileBackend
{
GObject
parent
;
SecretFileCollection
*
collection
;
SecretServiceFlags
init_flags
;
};
G_DEFINE_TYPE_WITH_CODE
(
SecretFileBackend
,
secret_file_backend
,
G_TYPE_OBJECT
,
G_IMPLEMENT_INTERFACE
(
G_TYPE_ASYNC_INITABLE
,
secret_file_backend_async_initable_iface
);
G_IMPLEMENT_INTERFACE
(
SECRET_TYPE_BACKEND
,
secret_file_backend_backend_iface
);
_secret_backend_ensure_extension_point
();
g_io_extension_point_implement
(
SECRET_BACKEND_EXTENSION_POINT_NAME
,
g_define_type_id
,
"file"
,
0
)
);
enum
{
PROP_0
,
PROP_FLAGS
};
static
void
secret_file_backend_init
(
SecretFileBackend
*
self
)
{
}
static
void
secret_file_backend_set_property
(
GObject
*
object
,
guint
prop_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
object
);
switch
(
prop_id
)
{
case
PROP_FLAGS
:
self
->
init_flags
=
g_value_get_flags
(
value
);
break
;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
prop_id
,
pspec
);
break
;
}
}
static
void
secret_file_backend_get_property
(
GObject
*
object
,
guint
prop_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
object
);
switch
(
prop_id
)
{
case
PROP_FLAGS
:
g_value_set_flags
(
value
,
self
->
init_flags
);
break
;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
object
,
prop_id
,
pspec
);
break
;
}
}
static
void
secret_file_backend_finalize
(
GObject
*
object
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
object
);
g_clear_object
(
&
self
->
collection
);
G_OBJECT_CLASS
(
secret_file_backend_parent_class
)
->
finalize
(
object
);
}
static
void
secret_file_backend_class_init
(
SecretFileBackendClass
*
klass
)
{
GObjectClass
*
object_class
=
G_OBJECT_CLASS
(
klass
);
object_class
->
set_property
=
secret_file_backend_set_property
;
object_class
->
get_property
=
secret_file_backend_get_property
;
object_class
->
finalize
=
secret_file_backend_finalize
;
/**
* SecretFileBackend:flags:
*
* A set of flags describing which parts of the secret file have
* been initialized.
*/
g_object_class_override_property
(
object_class
,
PROP_FLAGS
,
"flags"
);
}
static
void
on_collection_new_async
(
GObject
*
source_object
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
GTask
*
task
=
G_TASK
(
user_data
);
SecretFileBackend
*
self
=
g_task_get_source_object
(
task
);
GObject
*
object
;
GError
*
error
=
NULL
;
object
=
g_async_initable_new_finish
(
G_ASYNC_INITABLE
(
source_object
),
result
,
&
error
);
if
(
object
==
NULL
)
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
self
->
collection
=
SECRET_FILE_COLLECTION
(
object
);
g_task_return_boolean
(
task
,
TRUE
);
g_object_unref
(
task
);
}
static
void
secret_file_backend_real_init_async
(
GAsyncInitable
*
initable
,
int
io_priority
,
GCancellable
*
cancellable
,
GAsyncReadyCallback
callback
,
gpointer
user_data
)
{
gchar
*
path
;
GFile
*
file
;
GFile
*
dir
;
SecretValue
*
password
;
const
gchar
*
envvar
;
GTask
*
task
;
GError
*
error
=
NULL
;
gboolean
ret
;
task
=
g_task_new
(
initable
,
cancellable
,
callback
,
user_data
);
envvar
=
g_getenv
(
"SECRET_FILE_TEST_PATH"
);
if
(
envvar
!=
NULL
&&
*
envvar
!=
'\0'
)
path
=
g_strdup
(
envvar
);
else
{
path
=
g_build_filename
(
g_get_user_data_dir
(),
"keyrings"
,
SECRET_COLLECTION_DEFAULT
".keyring"
,
NULL
);
}
file
=
g_file_new_for_path
(
path
);
g_free
(
path
);
dir
=
g_file_get_parent
(
file
);
if
(
dir
==
NULL
)
{
g_task_return_new_error
(
task
,
G_IO_ERROR
,
G_IO_ERROR_INVALID_ARGUMENT
,
"not a valid path"
);
g_object_unref
(
file
);
g_object_unref
(
task
);
return
;
}
ret
=
g_file_make_directory_with_parents
(
dir
,
cancellable
,
&
error
);
g_object_unref
(
dir
);
if
(
!
ret
)
{
if
(
g_error_matches
(
error
,
G_IO_ERROR
,
G_IO_ERROR_EXISTS
))
g_clear_error
(
&
error
);
else
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
file
);
g_object_unref
(
task
);
return
;
}
}
envvar
=
g_getenv
(
"SECRET_FILE_TEST_PASSWORD"
);
if
(
envvar
!=
NULL
&&
*
envvar
!=
'\0'
)
password
=
secret_value_new
(
envvar
,
-
1
,
"text/plain"
);
else
{
g_task_return_new_error
(
task
,
G_IO_ERROR
,
G_IO_ERROR_INVALID_ARGUMENT
,
"master password is not retrievable"
);
g_object_unref
(
task
);
return
;
}
g_async_initable_new_async
(
SECRET_TYPE_FILE_COLLECTION
,
io_priority
,
cancellable
,
on_collection_new_async
,
task
,
"file"
,
file
,
"password"
,
password
,
NULL
);
g_object_unref
(
file
);
secret_value_unref
(
password
);
}
static
gboolean
secret_file_backend_real_init_finish
(
GAsyncInitable
*
initable
,
GAsyncResult
*
result
,
GError
**
error
)
{
g_return_val_if_fail
(
g_task_is_valid
(
result
,
initable
),
FALSE
);
return
g_task_propagate_boolean
(
G_TASK
(
result
),
error
);
}
static
void
secret_file_backend_async_initable_iface
(
GAsyncInitableIface
*
iface
)
{
iface
->
init_async
=
secret_file_backend_real_init_async
;
iface
->
init_finish
=
secret_file_backend_real_init_finish
;
}
static
void
on_collection_write
(
GObject
*
source_object
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
SecretFileCollection
*
collection
=
SECRET_FILE_COLLECTION
(
source_object
);
GTask
*
task
=
G_TASK
(
user_data
);
GError
*
error
=
NULL
;
if
(
!
secret_file_collection_write_finish
(
collection
,
result
,
&
error
))
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
g_task_return_boolean
(
task
,
TRUE
);
g_object_unref
(
task
);
}
static
void
secret_file_backend_real_store
(
SecretBackend
*
backend
,
const
SecretSchema
*
schema
,
GHashTable
*
attributes
,
const
gchar
*
collection
,
const
gchar
*
label
,
SecretValue
*
value
,
GCancellable
*
cancellable
,
GAsyncReadyCallback
callback
,
gpointer
user_data
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
backend
);
GTask
*
task
;
GError
*
error
=
NULL
;
/* Warnings raised already */
if
(
schema
!=
NULL
&&
!
_secret_attributes_validate
(
schema
,
attributes
,
G_STRFUNC
,
FALSE
))
return
;
task
=
g_task_new
(
self
,
cancellable
,
callback
,
user_data
);
if
(
!
secret_file_collection_replace
(
self
->
collection
,
attributes
,
label
,
value
,
&
error
))
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
secret_file_collection_write
(
self
->
collection
,
cancellable
,
on_collection_write
,
task
);
}
static
gboolean
secret_file_backend_real_store_finish
(
SecretBackend
*
backend
,
GAsyncResult
*
result
,
GError
**
error
)
{
g_return_val_if_fail
(
g_task_is_valid
(
result
,
backend
),
FALSE
);
return
g_task_propagate_boolean
(
G_TASK
(
result
),
error
);
}
static
void
on_retrieve_secret
(
GObject
*
source_object
,
GAsyncResult
*
result
,
gpointer
user_data
)
{
SecretRetrievable
*
retrievable
=
SECRET_RETRIEVABLE
(
source_object
);
GTask
*
task
=
G_TASK
(
user_data
);
SecretValue
*
value
;
GError
*
error
;
value
=
secret_retrievable_retrieve_secret_finish
(
retrievable
,
result
,
&
error
);
g_object_unref
(
retrievable
);
if
(
value
==
NULL
)
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
}
g_task_return_pointer
(
task
,
value
,
secret_value_unref
);
g_object_unref
(
task
);
}
static
void
secret_file_backend_real_lookup
(
SecretBackend
*
backend
,
const
SecretSchema
*
schema
,
GHashTable
*
attributes
,
GCancellable
*
cancellable
,
GAsyncReadyCallback
callback
,
gpointer
user_data
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
backend
);
GTask
*
task
;
GList
*
matches
;
GVariant
*
variant
;
SecretFileItem
*
item
;
GError
*
error
=
NULL
;
/* Warnings raised already */
if
(
schema
!=
NULL
&&
!
_secret_attributes_validate
(
schema
,
attributes
,
G_STRFUNC
,
TRUE
))
return
;
task
=
g_task_new
(
self
,
cancellable
,
callback
,
user_data
);
matches
=
secret_file_collection_search
(
self
->
collection
,
attributes
);
if
(
matches
==
NULL
)
{
g_task_return_pointer
(
task
,
NULL
,
NULL
);
g_object_unref
(
task
);
return
;
}
variant
=
g_variant_ref
(
matches
->
data
);
g_list_free_full
(
matches
,
(
GDestroyNotify
)
g_variant_unref
);
item
=
_secret_file_item_decrypt
(
variant
,
self
->
collection
,
&
error
);
g_variant_unref
(
variant
);
if
(
item
==
NULL
)
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
secret_retrievable_retrieve_secret
(
SECRET_RETRIEVABLE
(
item
),
cancellable
,
on_retrieve_secret
,
task
);
}
static
SecretValue
*
secret_file_backend_real_lookup_finish
(
SecretBackend
*
backend
,
GAsyncResult
*
result
,
GError
**
error
)
{
g_return_val_if_fail
(
g_task_is_valid
(
result
,
backend
),
NULL
);
return
g_task_propagate_pointer
(
G_TASK
(
result
),
error
);
}
static
void
secret_file_backend_real_clear
(
SecretBackend
*
backend
,
const
SecretSchema
*
schema
,
GHashTable
*
attributes
,
GCancellable
*
cancellable
,
GAsyncReadyCallback
callback
,
gpointer
user_data
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
backend
);
GTask
*
task
;
GError
*
error
=
NULL
;
gboolean
ret
;
/* Warnings raised already */
if
(
schema
!=
NULL
&&
!
_secret_attributes_validate
(
schema
,
attributes
,
G_STRFUNC
,
TRUE
))
return
;
task
=
g_task_new
(
self
,
cancellable
,
callback
,
user_data
);
ret
=
secret_file_collection_clear
(
self
->
collection
,
attributes
,
&
error
);
if
(
error
!=
NULL
)
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
/* No need to write as nothing has been removed. */
if
(
!
ret
)
{
g_task_return_boolean
(
task
,
FALSE
);
g_object_unref
(
task
);
return
;
}
secret_file_collection_write
(
self
->
collection
,
cancellable
,
on_collection_write
,
task
);
}
static
gboolean
secret_file_backend_real_clear_finish
(
SecretBackend
*
backend
,
GAsyncResult
*
result
,
GError
**
error
)
{
g_return_val_if_fail
(
g_task_is_valid
(
result
,
backend
),
FALSE
);
return
g_task_propagate_boolean
(
G_TASK
(
result
),
error
);
}
static
void
unref_objects
(
gpointer
data
)
{
GList
*
list
=
data
;
g_list_free_full
(
list
,
g_object_unref
);
}
static
void
secret_file_backend_real_search
(
SecretBackend
*
backend
,
const
SecretSchema
*
schema
,
GHashTable
*
attributes
,
SecretSearchFlags
flags
,
GCancellable
*
cancellable
,
GAsyncReadyCallback
callback
,
gpointer
user_data
)
{
SecretFileBackend
*
self
=
SECRET_FILE_BACKEND
(
backend
);
GTask
*
task
;
GList
*
matches
;
GList
*
results
=
NULL
;
GList
*
l
;
GError
*
error
=
NULL
;
/* Warnings raised already */
if
(
schema
!=
NULL
&&
!
_secret_attributes_validate
(
schema
,
attributes
,
G_STRFUNC
,
FALSE
))
return
;
task
=
g_task_new
(
self
,
cancellable
,
callback
,
user_data
);
matches
=
secret_file_collection_search
(
self
->
collection
,
attributes
);
for
(
l
=
matches
;
l
;
l
=
g_list_next
(
l
))
{
SecretFileItem
*
item
=
_secret_file_item_decrypt
(
l
->
data
,
self
->
collection
,
&
error
);
if
(
item
==
NULL
)
{
g_task_return_error
(
task
,
error
);
g_object_unref
(
task
);
return
;
}
results
=
g_list_append
(
results
,
item
);
}
g_list_free_full
(
matches
,
(
GDestroyNotify
)
g_variant_unref
);
g_task_return_pointer
(
task
,
results
,
unref_objects
);
g_object_unref
(
task
);
}
static
GList
*
secret_file_backend_real_search_finish
(
SecretBackend
*
backend
,
GAsyncResult
*
result
,
GError
**
error
)
{
g_return_val_if_fail
(
g_task_is_valid
(
result
,
backend
),
NULL
);
return
g_task_propagate_pointer
(
G_TASK
(
result
),
error
);
}
static
void
secret_file_backend_backend_iface
(
SecretBackendInterface
*
iface
)
{
iface
->
store
=
secret_file_backend_real_store
;
iface
->
store_finish
=
secret_file_backend_real_store_finish
;
iface
->
lookup
=
secret_file_backend_real_lookup
;
iface
->
lookup_finish
=
secret_file_backend_real_lookup_finish
;
iface
->
clear
=
secret_file_backend_real_clear
;
iface
->
clear_finish
=
secret_file_backend_real_clear_finish
;
iface
->
search
=
secret_file_backend_real_search
;
iface
->
search_finish
=
secret_file_backend_real_search_finish
;
}
libsecret/secret-file-backend.h
0 → 100644
View file @
2d642b5b
/* libsecret - GLib wrapper for Secret Service
*
* Copyright 2019 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the licence or (at
* your option) any later version.
*
* See the included COPYING file for more information.
*
* Author: Daiki Ueno