1 #if defined(HAVE_LIBSECRET)
2 #include <libsecret/secret.h>
3 #endif
4 
5 #include "libsecret_p.h"
6 
7 #include <QLibrary>
8 
9 #if defined(HAVE_LIBSECRET)
qtkeychainSchema(void)10 const SecretSchema* qtkeychainSchema(void) {
11     static const SecretSchema schema = {
12         "org.qt.keychain", SECRET_SCHEMA_DONT_MATCH_NAME,
13         {
14             { "user", SECRET_SCHEMA_ATTRIBUTE_STRING },
15             { "server", SECRET_SCHEMA_ATTRIBUTE_STRING },
16             { "type", SECRET_SCHEMA_ATTRIBUTE_STRING }
17         }
18     };
19 
20     return &schema;
21 }
22 
23 typedef struct {
24     QKeychain::JobPrivate *self;
25     QString user;
26     QString server;
27 } callbackArg;
28 
29 typedef void (*secret_password_lookup_t) (const SecretSchema *schema,
30                                           GCancellable *cancellable,
31                                           GAsyncReadyCallback callback,
32                                           gpointer user_data,
33                                           ...) G_GNUC_NULL_TERMINATED;
34 typedef gchar *(*secret_password_lookup_finish_t) (GAsyncResult *result,
35                                                    GError **error);
36 typedef void (*secret_password_store_t) (const SecretSchema *schema,
37                                          const gchar *collection,
38                                          const gchar *label,
39                                          const gchar *password,
40                                          GCancellable *cancellable,
41                                          GAsyncReadyCallback callback,
42                                          gpointer user_data,
43                                          ...) G_GNUC_NULL_TERMINATED;
44 typedef gboolean (*secret_password_store_finish_t) (GAsyncResult *result,
45                                                     GError **error);
46 typedef void (*secret_password_clear_t) (const SecretSchema *schema,
47                                          GCancellable *cancellable,
48                                          GAsyncReadyCallback callback,
49                                          gpointer user_data,
50                                          ...) G_GNUC_NULL_TERMINATED;
51 typedef gboolean (*secret_password_clear_finish_t) (GAsyncResult *result,
52                                                     GError **error);
53 typedef void (*secret_password_free_t) (gchar *password);
54 typedef GQuark (*secret_error_get_quark_t) (void) G_GNUC_CONST;
55 
56 static secret_password_lookup_t secret_password_lookup_fn = nullptr;
57 static secret_password_lookup_finish_t secret_password_lookup_finish_fn = nullptr;
58 static secret_password_store_t secret_password_store_fn = nullptr;
59 static secret_password_store_finish_t secret_password_store_finish_fn = nullptr;
60 static secret_password_clear_t secret_password_clear_fn = nullptr;
61 static secret_password_clear_finish_t secret_password_clear_finish_fn = nullptr;
62 static secret_password_free_t secret_password_free_fn = nullptr;
63 static secret_error_get_quark_t secret_error_get_quark_fn = nullptr;
64 
gerrorToCode(const GError * error)65 static QKeychain::Error gerrorToCode(const GError *error) {
66     if (error->domain != secret_error_get_quark_fn()) {
67         return QKeychain::OtherError;
68     }
69 
70     switch(error->code) {
71     case SECRET_ERROR_NO_SUCH_OBJECT:
72         return QKeychain::EntryNotFound;
73     case SECRET_ERROR_IS_LOCKED:
74         return QKeychain::AccessDenied;
75     default:
76         return QKeychain::OtherError;
77     }
78 }
79 
80 static void
on_password_lookup(GObject * source,GAsyncResult * result,gpointer inst)81 on_password_lookup (GObject *source,
82                     GAsyncResult *result,
83                     gpointer inst)
84 {
85     GError *error = nullptr;
86     callbackArg *arg = (callbackArg*)inst;
87     gchar *password = secret_password_lookup_finish_fn (result, &error);
88 
89     Q_UNUSED(source);
90 
91     if (arg) {
92         if (error) {
93             QKeychain::Error code = gerrorToCode(error);
94 
95             arg->self->q->emitFinishedWithError( code, QString::fromUtf8(error->message) );
96         } else {
97             if (password) {
98                 QByteArray raw = QByteArray(password);
99                 switch(arg->self->mode) {
100                 case QKeychain::JobPrivate::Binary:
101                     arg->self->data = QByteArray::fromBase64(raw);
102                     break;
103                 case QKeychain::JobPrivate::Text:
104                 default:
105                     arg->self->data = raw;
106                 }
107 
108                 arg->self->q->emitFinished();
109             } else if (arg->self->mode == QKeychain::JobPrivate::Text) {
110                 arg->self->mode = QKeychain::JobPrivate::Binary;
111                 secret_password_lookup_fn (qtkeychainSchema(), nullptr,
112                                            on_password_lookup, arg,
113                                            "user", arg->user.toUtf8().constData(),
114                                            "server", arg->server.toUtf8().constData(),
115                                            "type", "base64",
116                                            nullptr);
117                 return;
118             } else {
119                 arg->self->q->emitFinishedWithError( QKeychain::EntryNotFound, QObject::tr("Entry not found") );
120             }
121         }
122     }
123     if (error) {
124         g_error_free (error);
125     }
126 
127     if (password) {
128         secret_password_free_fn (password);
129     }
130 
131     if (arg) {
132         delete arg;
133     }
134 }
135 
136 static void
on_password_stored(GObject * source,GAsyncResult * result,gpointer inst)137 on_password_stored (GObject *source,
138                     GAsyncResult *result,
139                     gpointer inst)
140 {
141     GError *error = nullptr;
142     QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
143 
144     Q_UNUSED(source);
145 
146     secret_password_store_finish_fn (result, &error);
147 
148     if (self) {
149         if (error) {
150             self->q->emitFinishedWithError( gerrorToCode(error),
151                                             QString::fromUtf8(error->message) );
152         } else {
153             self->q->emitFinished();
154         }
155     }
156     if (error) {
157         g_error_free (error);
158     }
159 }
160 
161 static void
on_password_cleared(GObject * source,GAsyncResult * result,gpointer inst)162 on_password_cleared (GObject *source,
163                      GAsyncResult *result,
164                      gpointer inst)
165 {
166     GError *error = nullptr;
167     QKeychain::JobPrivate *self = (QKeychain::JobPrivate*)inst;
168     gboolean removed = secret_password_clear_finish_fn (result, &error);
169 
170     Q_UNUSED(source);
171     if (self) {
172         if ( error ) {
173             self->q->emitFinishedWithError( gerrorToCode(error),
174                                             QString::fromUtf8(error->message) );
175         } else {
176             Q_UNUSED(removed);
177             self->q->emitFinished();
178         }
179     }
180     if (error) {
181         g_error_free (error);
182     }
183 }
184 
modeToString(QKeychain::JobPrivate::Mode mode)185 static QString modeToString(QKeychain::JobPrivate::Mode mode) {
186     switch(mode) {
187     case QKeychain::JobPrivate::Binary:
188         return "base64";
189     default:
190         return "plaintext";
191     }
192 }
193 #endif
194 
isAvailable()195 bool LibSecretKeyring::isAvailable() {
196 #if defined(HAVE_LIBSECRET)
197     const LibSecretKeyring& keyring = instance();
198     if (!keyring.isLoaded())
199         return false;
200     if (secret_password_lookup_fn == nullptr)
201         return false;
202     if (secret_password_lookup_finish_fn == nullptr)
203         return false;
204     if (secret_password_store_fn == nullptr)
205         return false;
206     if (secret_password_store_finish_fn == nullptr)
207         return false;
208     if (secret_password_clear_fn == nullptr)
209         return false;
210     if (secret_password_clear_finish_fn == nullptr)
211         return false;
212     if (secret_password_free_fn == nullptr)
213         return false;
214     if (secret_error_get_quark_fn == nullptr)
215         return false;
216     return true;
217 #else
218     return false;
219 #endif
220 }
221 
findPassword(const QString & user,const QString & server,QKeychain::JobPrivate * self)222 bool LibSecretKeyring::findPassword(const QString &user, const QString &server,
223                                     QKeychain::JobPrivate *self)
224 {
225 #if defined(HAVE_LIBSECRET)
226     if (!isAvailable()) {
227         return false;
228     }
229 
230     self->mode = QKeychain::JobPrivate::Text;
231     self->data = QByteArray();
232 
233     callbackArg *arg = new callbackArg;
234     arg->self = self;
235     arg->user = user;
236     arg->server = server;
237 
238     secret_password_lookup_fn (qtkeychainSchema(), nullptr, on_password_lookup, arg,
239                                "user", user.toUtf8().constData(),
240                                "server", server.toUtf8().constData(),
241                                "type", "plaintext",
242                                nullptr);
243     return true;
244 #else
245     Q_UNUSED(user)
246     Q_UNUSED(server)
247     Q_UNUSED(self)
248     return false;
249 #endif
250 }
251 
writePassword(const QString & display_name,const QString & user,const QString & server,const QKeychain::JobPrivate::Mode mode,const QByteArray & password,QKeychain::JobPrivate * self)252 bool LibSecretKeyring::writePassword(const QString &display_name,
253                                      const QString &user,
254                                      const QString &server,
255                                      const QKeychain::JobPrivate::Mode mode,
256                                      const QByteArray &password,
257                                      QKeychain::JobPrivate *self)
258 {
259 #if defined(HAVE_LIBSECRET)
260     if (!isAvailable()) {
261         return false;
262     }
263 
264     QString type = modeToString(mode);
265     QByteArray pwd;
266     switch(mode) {
267     case QKeychain::JobPrivate::Binary:
268         pwd = password.toBase64();
269         break;
270     default:
271         pwd = password;
272         break;
273     }
274 
275     secret_password_store_fn (qtkeychainSchema(), SECRET_COLLECTION_DEFAULT,
276                               display_name.toUtf8().constData(),
277                               pwd.constData(), nullptr, on_password_stored, self,
278                               "user", user.toUtf8().constData(),
279                               "server", server.toUtf8().constData(),
280                               "type", type.toUtf8().constData(),
281                               nullptr);
282     return true;
283 #else
284     Q_UNUSED(display_name)
285     Q_UNUSED(user)
286     Q_UNUSED(server)
287     Q_UNUSED(mode)
288     Q_UNUSED(password)
289     Q_UNUSED(self)
290     return false;
291 #endif
292 }
293 
deletePassword(const QString & key,const QString & service,QKeychain::JobPrivate * self)294 bool LibSecretKeyring::deletePassword(const QString &key, const QString &service,
295                                       QKeychain::JobPrivate* self)
296 {
297 #if defined(HAVE_LIBSECRET)
298     if (!isAvailable()) {
299         return false;
300     }
301 
302     secret_password_clear_fn (qtkeychainSchema(), nullptr, on_password_cleared, self,
303                               "user", key.toUtf8().constData(),
304                               "server", service.toUtf8().constData(),
305                               nullptr);
306     return true;
307 #else
308     Q_UNUSED(key)
309     Q_UNUSED(service)
310     Q_UNUSED(self)
311     return false;
312 #endif
313 }
314 
LibSecretKeyring()315 LibSecretKeyring::LibSecretKeyring()
316     : QLibrary(QLatin1String("secret-1"), 0)
317 {
318 #ifdef HAVE_LIBSECRET
319     if (load()) {
320         secret_password_lookup_fn =
321                 (secret_password_lookup_t)resolve("secret_password_lookup");
322         secret_password_lookup_finish_fn =
323                 (secret_password_lookup_finish_t)resolve("secret_password_lookup_finish");
324         secret_password_store_fn =
325                 (secret_password_store_t)resolve("secret_password_store");
326         secret_password_store_finish_fn =
327                 (secret_password_store_finish_t)resolve("secret_password_store_finish");
328         secret_password_clear_fn =
329                 (secret_password_clear_t)resolve("secret_password_clear");
330         secret_password_clear_finish_fn =
331                 (secret_password_clear_finish_t)resolve("secret_password_clear_finish");
332         secret_password_free_fn =
333                 (secret_password_free_t)resolve("secret_password_free");
334         secret_error_get_quark_fn =
335                 (secret_error_get_quark_t)resolve("secret_error_get_quark");
336     }
337 #endif
338 }
339 
instance()340 LibSecretKeyring &LibSecretKeyring::instance() {
341     static LibSecretKeyring instance;
342 
343     return instance;
344 }
345