"Fossies" - the Fresh Open Source Software Archive

Member "darktable-3.6.1/src/common/pwstorage/backend_kwallet.c" (10 Sep 2021, 16553 Bytes) of package /linux/misc/darktable-3.6.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "backend_kwallet.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.2.1_vs_3.4.0.

    1 // This file is part of darktable
    2 //
    3 // darktable is free software: you can redistribute it and/or modify
    4 // it under the terms of the GNU General Public License as published by
    5 // the Free Software Foundation, either version 3 of the License, or
    6 // (at your option) any later version.
    7 //
    8 // darktable is distributed in the hope that it will be useful,
    9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 // GNU General Public License for more details.
   12 //
   13 // You should have received a copy of the GNU General Public License
   14 // along with darktable.  If not, see <http://www.gnu.org/licenses/>.
   15 
   16 // This file contains code taken from
   17 // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/password_manager/native_backend_kwallet_x.cc?revision=50034&view=markup
   18 
   19 // The original copyright notice was as follows:
   20 
   21 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
   22 //
   23 // Redistribution and use in source and binary forms, with or without
   24 // modification, are permitted provided that the following conditions are
   25 // met:
   26 //
   27 //    * Redistributions of source code must retain the above copyright
   28 // notice, this list of conditions and the following disclaimer.
   29 //    * Redistributions in binary form must reproduce the above
   30 // copyright notice, this list of conditions and the following disclaimer
   31 // in the documentation and/or other materials provided with the
   32 // distribution.
   33 //    * Neither the name of Google Inc. nor the names of its
   34 // contributors may be used to endorse or promote products derived from
   35 // this software without specific prior written permission.
   36 //
   37 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   38 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   39 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   40 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   41 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   42 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   43 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   44 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   45 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   46 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   47 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   48 
   49 // Since the BSD license permits I hereby release this stuff under GPL.
   50 // See the top of this file.
   51 
   52 // Connect do dbus interface of KWallet
   53 // http://websvn.kde.org/trunk/KDE/kdelibs/kdeui/util/org.kde.KWallet.xml?revision=1054210&view=markup
   54 // http://websvn.kde.org/trunk/KDE/kdelibs/kdeui/util/kwallet.cpp?revision=1107541&view=markup
   55 
   56 #ifdef HAVE_CONFIG_H
   57 #include "config.h"
   58 #endif
   59 
   60 #include "backend_kwallet.h"
   61 #include "control/conf.h"
   62 
   63 #include <string.h>
   64 
   65 static const gchar *app_id = "darktable";
   66 static const gchar *kwallet_folder = "darktable credentials";
   67 
   68 static const gchar *kwallet_service_name = "org.kde.kwalletd";
   69 static const gchar *kwallet_path = "/modules/kwalletd";
   70 static const gchar *kwallet_interface = "org.kde.KWallet";
   71 static const gchar *klauncher_service_name = "org.kde.klauncher";
   72 static const gchar *klauncher_path = "/KLauncher";
   73 static const gchar *klauncher_interface = "org.kde.KLauncher";
   74 
   75 // Invalid handle returned by get_wallet_handle().
   76 static const gint invalid_kwallet_handle = -1;
   77 
   78 // http://doc.qt.nokia.com/4.6/datastreamformat.html
   79 // A QString has the length in the first 4 bytes, then the string in UTF-16 encoding
   80 // Has to be stored as big endian!
   81 static gchar *char2qstring(const gchar *in, gsize *size)
   82 {
   83   glong read, written;
   84   GError *error = NULL;
   85   gunichar2 *out = g_utf8_to_utf16(in, -1, &read, &written, &error);
   86 
   87   if(error)
   88   {
   89     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet] ERROR: error converting string: %s\n", error->message);
   90     g_error_free(error);
   91     return NULL;
   92   }
   93 
   94   glong i;
   95   for(i = 0; i < written; ++i)
   96   {
   97     out[i] = g_htons(out[i]);
   98   }
   99 
  100   guint bytes = sizeof(gunichar2) * written;
  101   guint BE_bytes = GUINT_TO_BE(bytes);
  102   *size = sizeof(guint) + bytes;
  103   gchar *result = g_malloc(*size);
  104 
  105   memcpy(result, &BE_bytes, sizeof(guint));
  106   memcpy(result + sizeof(guint), out, bytes);
  107 
  108   g_free(out);
  109   return result;
  110 }
  111 
  112 // For cleaner code ...
  113 static gboolean check_error(GError *error)
  114 {
  115   if(error)
  116   {
  117     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet] ERROR: failed to complete kwallet call: %s\n",
  118              error->message);
  119     g_error_free(error);
  120     return TRUE;
  121   }
  122   return FALSE;
  123 }
  124 
  125 // If kwalletd isn't running: try to start it
  126 static gboolean start_kwallet(backend_kwallet_context_t *context)
  127 {
  128   GError *error = NULL;
  129 
  130   // Sadly kwalletd doesn't use DBUS activation, so we have to make a call to
  131   // klauncher to start it.
  132   /*
  133    * signature:
  134    *
  135    * in  s serviceName,
  136    * in  as urls,
  137    * in  as envs,
  138    * in  s startup_id,
  139    * in  b blind,
  140    *
  141    * out i arg_0,
  142    * out s dbusServiceName,
  143    * out s error,
  144    * out i pid
  145   */
  146   GVariant *ret = g_dbus_connection_call_sync(context->connection, klauncher_service_name, klauncher_path,
  147                                               klauncher_interface, "start_service_by_desktop_name",
  148                                               g_variant_new("(sasassb)", "kwalletd", NULL, NULL, "", FALSE),
  149                                               NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  150 
  151   if(check_error(error))
  152   {
  153     return FALSE;
  154   }
  155 
  156   GVariant *child = g_variant_get_child_value(ret, 2);
  157   gchar *error_string = g_variant_dup_string(child, NULL);
  158   g_variant_unref(child);
  159   g_variant_unref(ret);
  160 
  161   if(error_string && error_string[0] != '\0')
  162   {
  163     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet] ERROR: error launching kwalletd: %s\n", error_string);
  164     g_free(error_string);
  165     return FALSE;
  166   }
  167 
  168   g_free(error_string);
  169 
  170   return TRUE;
  171 }
  172 
  173 // Initialize the connection to KWallet
  174 static gboolean init_kwallet(backend_kwallet_context_t *context)
  175 {
  176   GError *error = NULL;
  177 
  178   // Make a proxy to KWallet.
  179   if(context->proxy) g_object_unref(context->proxy);
  180 
  181   context->proxy = g_dbus_proxy_new_sync(context->connection, G_DBUS_PROXY_FLAGS_NONE, NULL,
  182                                          kwallet_service_name, kwallet_path, kwallet_interface, NULL, &error);
  183 
  184   if(check_error(error))
  185   {
  186     context->proxy = NULL;
  187     return FALSE;
  188   }
  189 
  190   // Check KWallet is enabled.
  191   GVariant *ret
  192       = g_dbus_proxy_call_sync(context->proxy, "isEnabled", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  193 
  194   if(!ret) return FALSE;
  195   GVariant *child = g_variant_get_child_value(ret, 0);
  196   gboolean is_enabled = g_variant_get_boolean(child);
  197   g_variant_unref(child);
  198   g_variant_unref(ret);
  199 
  200   if(check_error(error) || !is_enabled) return FALSE;
  201 
  202   // Get the wallet name.
  203   g_free(context->wallet_name);
  204 
  205   ret = g_dbus_proxy_call_sync(context->proxy, "networkWallet", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL,
  206                                &error);
  207 
  208   child = g_variant_get_child_value(ret, 0);
  209   context->wallet_name = g_variant_dup_string(child, NULL);
  210   g_variant_unref(child);
  211   g_variant_unref(ret);
  212 
  213   if(check_error(error) || !context->wallet_name)
  214   {
  215     context->wallet_name = NULL; // yes, it's stupid. go figure.
  216     return FALSE;
  217   }
  218 
  219   return TRUE;
  220 }
  221 
  222 // General initialization. Takes care of all the other stuff.
  223 const backend_kwallet_context_t *dt_pwstorage_kwallet_new()
  224 {
  225   backend_kwallet_context_t *context = g_malloc0(sizeof(backend_kwallet_context_t));
  226 
  227   GError *error = NULL;
  228   context->connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
  229   if(check_error(error))
  230   {
  231     g_free(context);
  232     return NULL;
  233   }
  234 
  235   if(!init_kwallet(context))
  236   {
  237     // kwalletd may not be running. Try to start it and try again.
  238     if(!start_kwallet(context) || !init_kwallet(context))
  239     {
  240       g_object_unref(context->connection);
  241       g_free(context);
  242       return NULL;
  243     }
  244   }
  245 
  246   return context;
  247 }
  248 
  249 /** Cleanup and destroy kwallet backend context. */
  250 void dt_pwstorage_kwallet_destroy(const backend_kwallet_context_t *context)
  251 {
  252   backend_kwallet_context_t *c = (backend_kwallet_context_t *)context;
  253   g_object_unref(c->connection);
  254   g_object_unref(c->proxy);
  255   g_free(c->wallet_name);
  256   g_free(c);
  257 }
  258 
  259 // get the handle for connections to KWallet
  260 static int get_wallet_handle(const backend_kwallet_context_t *context)
  261 {
  262   // Open the wallet.
  263   int handle = invalid_kwallet_handle;
  264   GError *error = NULL;
  265 
  266   /* signature:
  267    *
  268    * in  s wallet,
  269    * in  x wId,
  270    * in  s appid,
  271    *
  272    * out i arg_0
  273    */
  274   GVariant *ret = g_dbus_proxy_call_sync(context->proxy, "open",
  275                                          g_variant_new("(sxs)", context->wallet_name, 0LL, app_id),
  276                                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  277 
  278   if(check_error(error))
  279   {
  280     g_variant_unref(ret);
  281     return invalid_kwallet_handle;
  282   }
  283 
  284   GVariant *child = g_variant_get_child_value(ret, 0);
  285   handle = g_variant_get_int32(child);
  286   g_variant_unref(child);
  287   g_variant_unref(ret);
  288 
  289   // Check if our folder exists.
  290   gboolean has_folder = FALSE;
  291 
  292   /* signature:
  293    *
  294    * in  i handle,
  295    * in  s folder,
  296    * in  s appid,
  297    *
  298    * out b arg_0
  299    */
  300   ret = g_dbus_proxy_call_sync(context->proxy, "hasFolder",
  301                                g_variant_new("(iss)", handle, kwallet_folder, app_id), G_DBUS_CALL_FLAGS_NONE,
  302                                -1, NULL, &error);
  303 
  304   if(check_error(error))
  305   {
  306     g_variant_unref(ret);
  307     return invalid_kwallet_handle;
  308   }
  309 
  310   child = g_variant_get_child_value(ret, 0);
  311   has_folder = g_variant_get_boolean(child);
  312   g_variant_unref(child);
  313   g_variant_unref(ret);
  314 
  315   // Create it if it didn't.
  316   if(!has_folder)
  317   {
  318 
  319     gboolean success = FALSE;
  320 
  321     /* signature:
  322      *
  323      * in  i handle,
  324      * in  s folder,
  325      * in  s appid,
  326      *
  327      * out b arg_0
  328      */
  329     ret = g_dbus_proxy_call_sync(context->proxy, "createFolder",
  330                                  g_variant_new("(iss)", handle, kwallet_folder, app_id),
  331                                  G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  332 
  333     if(check_error(error))
  334     {
  335       g_variant_unref(ret);
  336       return invalid_kwallet_handle;
  337     }
  338 
  339     child = g_variant_get_child_value(ret, 0);
  340     success = g_variant_get_boolean(child);
  341     g_variant_unref(child);
  342     g_variant_unref(ret);
  343 
  344     if(!success) return invalid_kwallet_handle;
  345   }
  346 
  347   return handle;
  348 }
  349 
  350 // Store (key,value) pairs from a GHashTable in the kwallet.
  351 // Every 'slot' has to take care of it's own data.
  352 gboolean dt_pwstorage_kwallet_set(const backend_kwallet_context_t *context, const gchar *slot,
  353                                   GHashTable *table)
  354 {
  355   printf("slot %s\n", slot);
  356 
  357   GArray *byte_array = g_array_new(FALSE, FALSE, sizeof(gchar));
  358 
  359   GHashTableIter iter;
  360   g_hash_table_iter_init(&iter, table);
  361   gpointer key, value;
  362 
  363   guint size = g_hash_table_size(table);
  364 
  365   size = GINT_TO_BE(size);
  366 
  367   g_array_append_vals(byte_array, &size, sizeof(guint) / sizeof(gchar));
  368 
  369   while(g_hash_table_iter_next(&iter, &key, &value))
  370   {
  371     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet_set] storing (%s, %s)\n", (gchar *)key, (gchar *)value);
  372     gsize length;
  373     gchar *new_key = char2qstring(key, &length);
  374     if(new_key == NULL)
  375     {
  376       g_free(g_array_free(byte_array, FALSE));
  377       return FALSE;
  378     }
  379     g_array_append_vals(byte_array, new_key, length);
  380     g_free(new_key);
  381 
  382     gchar *new_value = char2qstring(value, &length);
  383     if(new_value == NULL)
  384     {
  385       g_free(g_array_free(byte_array, FALSE));
  386       return FALSE;
  387     }
  388     g_array_append_vals(byte_array, new_value, length);
  389     g_free(new_value);
  390   }
  391 
  392   int wallet_handle = get_wallet_handle(context);
  393   GError *error = NULL;
  394 
  395   /* signature:
  396    *
  397    * in  i handle,
  398    * in  s folder,
  399    * in  s key,
  400    * in  ay value,
  401    * in  s appid,
  402    *
  403    * out i arg_0
  404    */
  405   GVariant *ret = g_dbus_proxy_call_sync(
  406       context->proxy, "writeMap",
  407       g_variant_new("(iss@ays)", wallet_handle, kwallet_folder, slot,
  408                     g_variant_new_from_data(G_VARIANT_TYPE_BYTESTRING, byte_array->data, byte_array->len,
  409                                             TRUE, g_free, byte_array->data),
  410                     app_id),
  411       G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  412 
  413   g_array_free(byte_array, FALSE);
  414 
  415   if(check_error(error))
  416   {
  417     g_variant_unref(ret);
  418     return FALSE;
  419   }
  420 
  421   GVariant *child = g_variant_get_child_value(ret, 0);
  422   int return_code = g_variant_get_int32(child);
  423   g_variant_unref(child);
  424   g_variant_unref(ret);
  425 
  426   if(return_code != 0)
  427     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet_set] Warning: bad return code %d from kwallet\n",
  428              return_code);
  429 
  430   return return_code == 0;
  431 }
  432 
  433 static gchar *array2string(const gchar *pos, guint *length)
  434 {
  435   memcpy(length, pos, sizeof(gint));
  436   *length = GUINT_FROM_BE(*length);
  437   pos += sizeof(gint);
  438   guint j;
  439 
  440   gunichar2 *tmp_string = (gunichar2 *)malloc(*length);
  441   memcpy(tmp_string, pos, *length);
  442 
  443   for(j = 0; j < ((*length) / sizeof(gunichar2)); j++)
  444   {
  445     tmp_string[j] = g_ntohs(tmp_string[j]);
  446   }
  447 
  448   glong read, written;
  449   GError *error = NULL;
  450   gchar *out = g_utf16_to_utf8(tmp_string, *length / sizeof(gunichar2), &read, &written, &error);
  451 
  452   free(tmp_string);
  453 
  454   if(error)
  455   {
  456     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet] ERROR: Error converting string: %s\n", error->message);
  457     g_error_free(error);
  458     return NULL;
  459   }
  460 
  461   *length += sizeof(gint);
  462   return out;
  463 }
  464 
  465 // Get the (key,value) pairs back from KWallet.
  466 GHashTable *dt_pwstorage_kwallet_get(const backend_kwallet_context_t *context, const gchar *slot)
  467 {
  468   GHashTable *table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
  469   GError *error = NULL;
  470 
  471   // Is there an entry in the wallet?
  472   gboolean has_entry = FALSE;
  473   int wallet_handle = get_wallet_handle(context);
  474 
  475   /* signature:
  476    *
  477    * in  i handle,
  478    * in  s folder,
  479    * in  s key,
  480    * in  s appid,
  481    *
  482    * out b arg_0
  483    */
  484   GVariant *ret = g_dbus_proxy_call_sync(context->proxy, "hasEntry",
  485                                          g_variant_new("(isss)", wallet_handle, kwallet_folder, slot, app_id),
  486                                          G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  487 
  488   if(check_error(error))
  489   {
  490     g_variant_unref(ret);
  491     return table;
  492   }
  493 
  494   GVariant *child = g_variant_get_child_value(ret, 0);
  495   has_entry = g_variant_get_boolean(child);
  496   g_variant_unref(child);
  497   g_variant_unref(ret);
  498 
  499   if(!has_entry) return table;
  500 
  501   /* signature:
  502    *
  503    * in  i handle,
  504    * in  s folder,
  505    * in  s key,
  506    * in  s appid,
  507    *
  508    * out a{sv} arg_0)
  509    */
  510   ret = g_dbus_proxy_call_sync(context->proxy, "readMapList",
  511                                g_variant_new("(isss)", wallet_handle, kwallet_folder, slot, app_id),
  512                                G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
  513 
  514   if(check_error(error))
  515   {
  516     g_variant_unref(ret);
  517     return table;
  518   }
  519 
  520   child = g_variant_get_child_value(ret, 0);
  521 
  522   // we are only interested in the first child. i am not even sure that there can legally be more than one
  523   if(g_variant_n_children(child) < 1)
  524   {
  525     g_variant_unref(child);
  526     g_variant_unref(ret);
  527     return table;
  528   }
  529 
  530   GVariant *element = g_variant_get_child_value(child, 0);
  531   GVariant *v = NULL;
  532   g_variant_get(element, "{sv}", NULL, &v);
  533 
  534   const gchar *byte_array = g_variant_get_data(v);
  535   if(!byte_array)
  536   {
  537     g_variant_unref(v);
  538     g_variant_unref(element);
  539     g_variant_unref(child);
  540     g_variant_unref(ret);
  541     return table;
  542   }
  543 
  544   int entries = GINT_FROM_BE(*((int *)byte_array));
  545   byte_array += sizeof(gint);
  546 
  547   for(int i = 0; i < entries; i++)
  548   {
  549     guint length;
  550     gchar *key = array2string(byte_array, &length);
  551 
  552     byte_array += length;
  553 
  554     gchar *value = array2string(byte_array, &length);
  555 
  556     byte_array += length;
  557 
  558     dt_print(DT_DEBUG_PWSTORAGE, "[pwstorage_kwallet_get] reading (%s, %s)\n", (gchar *)key, (gchar *)value);
  559 
  560     g_hash_table_insert(table, key, value);
  561   }
  562 
  563   g_variant_unref(v);
  564   g_variant_unref(element);
  565   g_variant_unref(child);
  566   g_variant_unref(ret);
  567 
  568   return table;
  569 }
  570 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
  571 // vim: shiftwidth=2 expandtab tabstop=2 cindent
  572 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;