smbnetfs  0.6.3
About: SMBNetFS is a Linux/FreeBSD filesystem that allow you to use samba/microsoft network in the same manner as the network neighborhood in Microsoft Windows.
  Fossies Dox: smbnetfs-0.6.3.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

auth-libsecret.c
Go to the documentation of this file.
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 */
#define AUTH_MATCH_DEFAULT
Definition: auth-libsecret.h:5
#define AUTH_MATCH_DOMAIN
Definition: auth-libsecret.h:7
#define AUTH_MATCH_SERVER
Definition: auth-libsecret.h:8
#define AUTH_MATCH_RESOURCE
Definition: auth-libsecret.h:9
#define AUTH_MATCH_DOMAIN_COMPAT
Definition: auth-libsecret.h:6
#define DPRINTF(level, fmt, args...)
Definition: common.h:47