"Fossies" - the Fresh Open Source Software Archive

Member "smbnetfs-0.6.3/src/auth-libsecret.c" (4 Jan 2018, 15107 Bytes) of package /linux/misc/smbnetfs-0.6.3.tar.bz2:


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 "auth-libsecret.c" see the Fossies "Dox" file reference documentation.

    1 #include "config.h"
    2 #ifdef HAVE_LIBSECRET
    3 
    4 #include <stdio.h>
    5 #include <stdlib.h>
    6 #include <string.h>
    7 #include <pthread.h>
    8 #include <sys/time.h>
    9 #include <glib.h>
   10 #include <libsecret/secret.h>
   11 
   12 #include "common.h"
   13 #include "auth-libsecret.h"
   14 
   15 
   16 static gboolean req_timeout_prepare (GSource *source, gint *timeout);
   17 static gboolean req_timeout_check   (GSource *source);
   18 static gboolean req_timeout_dispatch(GSource *source, GSourceFunc callback,
   19                      gpointer user_data);
   20 static void     req_timeout_finalize(GSource *source);
   21 
   22 
   23 enum libsecret_status{
   24     LIBSECRET_NOT_AVAILABLE = -1,
   25     LIBSECRET_DISABLED = 0,
   26     LIBSECRET_ENABLED
   27 };
   28 
   29 struct req_timeout{
   30     struct timeval  start_time;
   31     gint        timeout_len;
   32     gboolean        expired;
   33     GCancellable    *cancellable;
   34 };
   35 
   36 struct req_data{
   37     const char          *domain;
   38     const char          *server;
   39     const char          *share;
   40     SecretItem          *secret_item;
   41     struct libsecret_authinfo   *auth_info;
   42     int             suitability;
   43 };
   44 
   45 #define REQ_TIMEOUT(source) (&G_STRUCT_MEMBER(struct req_timeout, source, sizeof(GSource)))
   46 
   47 static pthread_mutex_t          m_auth_libsecret = PTHREAD_MUTEX_INITIALIZER;
   48 static enum libsecret_status        libsecret    = LIBSECRET_NOT_AVAILABLE;
   49 static const SecretSchema       libsecret_schema = {
   50                         "org.gnome.keyring.NetworkPassword", SECRET_SCHEMA_DONT_MATCH_NAME,
   51                         {
   52                         { "protocol", SECRET_SCHEMA_ATTRIBUTE_STRING },
   53                         { "server",   SECRET_SCHEMA_ATTRIBUTE_STRING },
   54                         { "object",   SECRET_SCHEMA_ATTRIBUTE_STRING },
   55                         { "domain",   SECRET_SCHEMA_ATTRIBUTE_STRING },
   56                         { "user",     SECRET_SCHEMA_ATTRIBUTE_STRING },
   57                         { NULL, 0 },
   58                         }
   59                     };
   60 static GMainLoop            *loop        = NULL;
   61 static GHashTable           *search_hash     = NULL;
   62 static GSourceFuncs         req_timeout_func = {
   63                         .prepare  = req_timeout_prepare,
   64                         .check    = req_timeout_check,
   65                         .dispatch = req_timeout_dispatch,
   66                         .finalize = req_timeout_finalize
   67                     };
   68 static struct req_timeout       *req_timeout     = NULL;
   69 static int              max_req_timeout  = 500; /* in milliseconds */
   70 static SecretService            *secret_service  = NULL;
   71 static SecretCollection         *secret_collection = NULL;
   72 
   73 
   74 static struct libsecret_authinfo * libsecret_create_authinfo(const char *domain,
   75                                                              const char *user,
   76                                                              const char *password,
   77                                                              int suitability)
   78 {
   79     struct libsecret_authinfo   *info;
   80 
   81     if (password == NULL) return NULL;
   82     if ((user == NULL) || (*user == '\0')) return NULL;
   83     if (domain == NULL) domain = "";
   84 
   85     info = malloc(sizeof(struct libsecret_authinfo) +
   86                    strlen(domain) + strlen(user) + strlen(password) + 3);
   87     if (info == NULL) return NULL;
   88 
   89     info->domain   = (char *) (info + 1);
   90     info->user     = info->domain + strlen(domain) + 1;
   91     info->password = info->user + strlen(user) + 1;
   92 
   93     strcpy(info->domain,   domain);
   94     strcpy(info->user,     user);
   95     strcpy(info->password, password);
   96     info->suitability = suitability;
   97     return info;
   98 }
   99 
  100 void libsecret_free_authinfo(struct libsecret_authinfo* info){
  101     free(info);
  102 }
  103 
  104 int libsecret_set_request_timeout(int timeout){
  105     if (timeout <= 0) return 0;
  106     DPRINTF(7, "max_req_timeout=%d\n", timeout);
  107     pthread_mutex_lock(&m_auth_libsecret);
  108     max_req_timeout = timeout;
  109     pthread_mutex_unlock(&m_auth_libsecret);
  110     return 1;
  111 }
  112 
  113 int libsecret_enable(int state){
  114     int     ret;
  115 
  116     pthread_mutex_lock(&m_auth_libsecret);
  117     switch(libsecret){
  118     case LIBSECRET_DISABLED:
  119     case LIBSECRET_ENABLED:
  120         libsecret = (state) ?
  121         LIBSECRET_ENABLED : LIBSECRET_DISABLED;
  122         ret = 0;
  123         break;
  124     default:
  125         ret = -1;
  126         break;
  127     }
  128     pthread_mutex_unlock(&m_auth_libsecret);
  129     return ret;
  130 }
  131 
  132 static gboolean req_timeout_prepare(GSource *source, gint *timeout){
  133     struct req_timeout  *req;
  134     struct timeval  tv;
  135     gint        diff;
  136 
  137     req = REQ_TIMEOUT(source);
  138 
  139     /* req->timeout_len have a milliseconds resolution */
  140     gettimeofday(&tv, NULL);
  141     diff = (tv.tv_sec - req->start_time.tv_sec) * 1000 +
  142        (tv.tv_usec - req->start_time.tv_usec) / 1000;
  143 
  144     if (diff < 0){
  145     /* time in the past, redefine req->start_time to avoid long delay */
  146     req->start_time = tv;
  147     *timeout = req->timeout_len;
  148     return FALSE;
  149     }else if (diff < req->timeout_len){
  150     *timeout = req->timeout_len - diff;
  151     return FALSE;
  152     }
  153     if (!g_cancellable_is_cancelled(req->cancellable)) g_cancellable_cancel(req->cancellable);
  154     *timeout = 10;
  155     return FALSE;
  156 }
  157 
  158 static gboolean req_timeout_check(GSource *source){
  159     gint        timeout;
  160 
  161     return req_timeout_prepare(source, &timeout);
  162 }
  163 
  164 static gboolean req_timeout_dispatch(GSource *source,
  165                      GSourceFunc callback,
  166                      gpointer user_data){
  167     (void) source;
  168     (void) callback;
  169     (void) user_data;
  170     /* all termination done via req->cancellable, so do nothing */
  171     return FALSE;
  172 }
  173 
  174 static void req_timeout_finalize(GSource *source){
  175     struct req_timeout  *req;
  176 
  177     req = REQ_TIMEOUT(source);
  178     req->timeout_len = 0;
  179     req->expired = FALSE;
  180     g_object_unref(req->cancellable);
  181     req->cancellable = NULL;
  182 }
  183 
  184 static void request_timeout_init(struct req_timeout *req, int timeout){
  185     req->expired = FALSE;
  186     req->timeout_len = timeout;
  187     gettimeofday(&req->start_time, NULL);
  188     g_cancellable_reset(req->cancellable);
  189 }
  190 
  191 static void secret_service_get_callback(GObject *source_object,
  192                                         GAsyncResult *res,
  193                                         gpointer user_data)
  194 {
  195     (void)source_object;
  196     (void)user_data;
  197 
  198     GError *error = NULL;
  199     secret_service = secret_service_get_finish(res, &error);
  200     if (error != NULL){
  201     secret_service = NULL;
  202     g_error_free(error);
  203     }
  204     if (secret_service == NULL) DPRINTF(10, "can't get secret service\n");
  205     g_main_loop_quit(loop);
  206 }
  207 
  208 static void secret_collection_for_alias_callback(GObject *source_object,
  209                                                  GAsyncResult *res,
  210                                                  gpointer user_data)
  211 {
  212     (void)source_object;
  213     (void)user_data;
  214 
  215     GError *error = NULL;
  216     secret_collection = secret_collection_for_alias_finish(res, &error);
  217     if (error != NULL){
  218     secret_collection = NULL;
  219     g_error_free(error);
  220     }
  221     if (secret_collection == NULL) DPRINTF(10, "can't get secret collection\n");
  222     g_main_loop_quit(loop);
  223 }
  224 
  225 void libsecret_init(void){
  226     GSource     *source;
  227     GCancellable    *cancellable;
  228 
  229     g_set_application_name(PACKAGE_NAME);
  230 
  231     search_hash = g_hash_table_new(g_str_hash, g_str_equal);
  232     if (search_hash == NULL){
  233     DPRINTF(10, "can't create glib hash\n");
  234     goto g_hash_fail;
  235     }
  236     g_hash_table_insert(search_hash, "protocol", "smb");
  237 
  238     loop = g_main_loop_new(NULL, FALSE);
  239     if (loop == NULL){
  240     DPRINTF(10, "can't create glib main loop\n");
  241     goto g_main_loop_fail;
  242     }
  243 
  244     cancellable = g_cancellable_new();
  245     if (cancellable == NULL){
  246     DPRINTF(10, "can't create glib cancellable\n");
  247     goto g_source_fail;
  248     }
  249 
  250     source = g_source_new(&req_timeout_func,
  251               sizeof(GSource) + sizeof(struct req_timeout));
  252     if (source == NULL){
  253     DPRINTF(10, "can't create glib event source\n");
  254     g_object_unref(cancellable);
  255     goto g_source_fail;
  256     }
  257     req_timeout = REQ_TIMEOUT(source);
  258     req_timeout->cancellable = cancellable;
  259     g_source_attach(source, g_main_loop_get_context(loop));
  260 
  261     request_timeout_init(req_timeout, max_req_timeout);
  262     secret_service_get(SECRET_SERVICE_OPEN_SESSION | SECRET_SERVICE_LOAD_COLLECTIONS,
  263                        req_timeout->cancellable,
  264                        secret_service_get_callback,
  265                        NULL);
  266     g_main_loop_run(loop);
  267     if (secret_service == NULL) goto g_source_fail;
  268 
  269     request_timeout_init(req_timeout, max_req_timeout);
  270     secret_collection_for_alias(secret_service, "default",
  271                                 SECRET_COLLECTION_LOAD_ITEMS,
  272                                 req_timeout->cancellable,
  273                                 secret_collection_for_alias_callback,
  274                                 NULL);
  275     g_main_loop_run(loop);
  276     if (secret_collection == NULL) goto secret_collection_fail;
  277 
  278     libsecret = LIBSECRET_ENABLED;
  279     return;
  280 
  281   secret_collection_fail:
  282     g_object_unref(secret_service);
  283     secret_service = NULL;
  284   g_source_fail:
  285     g_main_loop_unref(loop);
  286     loop = NULL;
  287   g_main_loop_fail:
  288     g_hash_table_unref(search_hash);
  289     search_hash = NULL;
  290   g_hash_fail:
  291     libsecret = LIBSECRET_NOT_AVAILABLE;
  292     DPRINTF(1, "libsecret is not available.\n");
  293     return;
  294 }
  295 
  296 void libsecret_done(void){
  297     if (libsecret == LIBSECRET_NOT_AVAILABLE) return;
  298 
  299     if (secret_collection != NULL){
  300     g_object_unref(secret_collection);
  301     secret_collection = NULL;
  302     }
  303     if (secret_service != NULL){
  304     g_object_unref(secret_service);
  305     secret_service = NULL;
  306     }
  307     if (loop != NULL){
  308     g_main_loop_unref(loop);
  309     loop = NULL;
  310     }
  311     if (search_hash != NULL){
  312     g_hash_table_unref(search_hash);
  313     search_hash = NULL;
  314     }
  315     libsecret = LIBSECRET_NOT_AVAILABLE;
  316 }
  317 
  318 /*
  319  * On success req->secret_item field will be filled and corresponding
  320  * reference will be taken.
  321  */
  322 static void secret_collection_search_callback(GObject *source_object,
  323                                               GAsyncResult *res,
  324                                               gpointer user_data)
  325 {
  326     SecretCollection    *secret_collection = (SecretCollection*) source_object;
  327     struct req_data *req = (struct req_data*) user_data;
  328     GError      *error = NULL;
  329     GList       *list = NULL, *elem;
  330     const char      *domain, *user, *server, *share;
  331     SecretItem      *secret_item;
  332     GHashTable      *hash;
  333 
  334     list = secret_collection_search_finish(secret_collection, res, &error);
  335     if (error != NULL){
  336     list = NULL;
  337     g_error_free(error);
  338     }
  339     if (list == NULL) goto search_fail;
  340 
  341     for(elem = list; elem != NULL; elem = elem->next){
  342     secret_item = (SecretItem*)elem->data;
  343 
  344     hash = secret_item_get_attributes(secret_item);
  345     if (hash == NULL){
  346         req->secret_item = NULL;
  347         req->suitability = -1;
  348         g_hash_table_unref(hash);
  349         break;
  350     }
  351 
  352     user = g_hash_table_lookup(hash, "user");
  353     if ((user == NULL) || (*user == '\0')){
  354         /* skip bad record */
  355         goto loop_end;
  356     }
  357 
  358     domain = g_hash_table_lookup(hash, "domain");
  359     server = g_hash_table_lookup(hash, "server");
  360     share  = g_hash_table_lookup(hash, "object");
  361     if (domain == NULL) domain = "";
  362     if (server == NULL) server = "";
  363     if (share  == NULL) share  = "";
  364 
  365     if (*share != '\0'){
  366         if (*server == '\0'){
  367         /* skip bad record */
  368         goto loop_end;
  369         }
  370         if ((req->suitability < AUTH_MATCH_RESOURCE) &&
  371             (strcasecmp(req->server, server) == 0) &&
  372             (strcasecmp(req->share,  share)  == 0))
  373         {
  374         req->secret_item = secret_item;
  375         req->suitability = AUTH_MATCH_RESOURCE;
  376         }
  377         goto loop_end;
  378     }
  379 
  380     if (*server != '\0'){
  381         /* record without share name */
  382         if ((req->suitability < AUTH_MATCH_SERVER) &&
  383             (strcasecmp(req->server, server) == 0))
  384         {
  385         req->secret_item = secret_item;
  386         req->suitability = AUTH_MATCH_SERVER;
  387         }
  388         else
  389         if ((req->suitability < AUTH_MATCH_DOMAIN_COMPAT) &&
  390             (strcasecmp(req->domain, server) == 0))
  391         {
  392         req->secret_item = secret_item;
  393         req->suitability = AUTH_MATCH_DOMAIN_COMPAT;
  394         }
  395         goto loop_end;
  396     }
  397 
  398     if ((*domain != '\0') &&
  399         (req->suitability < AUTH_MATCH_DOMAIN) &&
  400         (strcasecmp(req->domain, domain) == 0))
  401     {
  402         req->secret_item = secret_item;
  403         req->suitability = AUTH_MATCH_DOMAIN;
  404         goto loop_end;
  405     }
  406 
  407     if (req->suitability < AUTH_MATCH_DEFAULT)
  408     {
  409         req->secret_item = secret_item;
  410         req->suitability = AUTH_MATCH_DEFAULT;
  411     }
  412 
  413       loop_end:
  414     g_hash_table_unref(hash);
  415     }
  416 
  417     if (req->secret_item != NULL) g_object_ref(req->secret_item);
  418     g_list_free_full(list, g_object_unref);
  419   search_fail:
  420     g_main_loop_quit(loop);
  421 }
  422 
  423 /*
  424  * On success libsecret_authinfo structure will be allocated and
  425  * req->auth_info field will point to allocated structure.
  426  * The req->secret_item field will be unrefered and cleared in any case.
  427  */
  428 static void secret_item_load_secret_callback(GObject *source_object,
  429                                              GAsyncResult *res,
  430                                              gpointer user_data)
  431 {
  432     SecretItem      *secret_item = (SecretItem*) source_object;
  433     struct req_data *req = (struct req_data*) user_data;
  434     GError      *error = NULL;
  435     GHashTable      *hash;
  436     SecretValue     *secret;
  437     gboolean        result;
  438     const char      *domain, *user, *password;
  439 
  440     result = secret_item_load_secret_finish(secret_item, res, &error);
  441     if (error != NULL){
  442     result = FALSE;
  443     g_error_free(error);
  444     }
  445     if (result == FALSE) goto end;
  446 
  447     secret = secret_item_get_secret(secret_item);
  448     if (secret == NULL) goto end;
  449 
  450     hash = secret_item_get_attributes(secret_item);
  451     if (hash == NULL) goto password_fail;
  452 
  453     domain   = g_hash_table_lookup(hash, "domain");
  454     user     = g_hash_table_lookup(hash, "user");
  455     password = secret_value_get_text(secret);
  456 
  457     req->auth_info = libsecret_create_authinfo(domain, user, password, req->suitability);
  458 
  459     g_hash_table_unref(hash);
  460   password_fail:
  461     secret_value_unref(secret);
  462   end:
  463     g_main_loop_quit(loop);
  464     /* secret_item is not needed anymore, so unref it and clear req->secret_item as well */
  465     g_object_unref(secret_item);
  466     req->secret_item = NULL;
  467 }
  468 
  469 struct libsecret_authinfo * libsecret_get_authinfo(
  470                         const char *domain,
  471                         const char *server,
  472                         const char *share){
  473     struct req_data req;
  474 
  475     DPRINTF(10, "domain=%s, server=%s, share=%s\n", domain, server, share);
  476 
  477     if ((server == NULL) || (*server == '\0')) return NULL;
  478     if (domain == NULL) domain = "";
  479     if (share  == NULL) share  = "";
  480 
  481     req.domain = domain;
  482     req.server = server;
  483     req.share  = share;
  484     req.secret_item = NULL;
  485     req.auth_info   = NULL;
  486     req.suitability = -1;
  487 
  488     pthread_mutex_lock(&m_auth_libsecret);
  489     if (libsecret != LIBSECRET_ENABLED) goto end;
  490 
  491     if (g_main_context_acquire(g_main_loop_get_context(loop)) == FALSE){
  492     DPRINTF(10, "can't acquire GMainContext\n");
  493     goto end;
  494     }
  495 
  496     request_timeout_init(req_timeout, max_req_timeout);
  497     secret_collection_search(secret_collection, &libsecret_schema, search_hash,
  498                              SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK,
  499                              req_timeout->cancellable,
  500                              secret_collection_search_callback,
  501                              &req);
  502     g_main_loop_run(loop);
  503 
  504     if (req.secret_item != NULL){
  505     secret_item_load_secret(req.secret_item,
  506                             req_timeout->cancellable,
  507                             secret_item_load_secret_callback,
  508                             &req);
  509     g_main_loop_run(loop);
  510     }
  511 
  512     g_main_context_release(g_main_loop_get_context(loop));
  513   end:
  514     pthread_mutex_unlock(&m_auth_libsecret);
  515     return req.auth_info;
  516 }
  517 
  518 #endif /* HAVE_LIBSECRET */