Remove logging to /var/run/tump and /var/run/wtmp
[platform/core/system/tlm.git] / src / common / tlm-utils.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm (Tiny Login Manager)
5  *
6  * Copyright (C) 2013 Intel Corporation.
7  *
8  * Contact: Amarnath Valluri <amarnath.valluri@linux.intel.com>
9  *          Jussi Laako <jussi.laako@linux.intel.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24  * 02110-1301 USA
25  */
26
27 #include <sys/types.h>
28 #include <pwd.h>
29 #include <sys/stat.h>
30 #include <paths.h>
31 #include <sys/inotify.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <glib/gstdio.h>
35 #include <glib-unix.h>
36 #include <security/pam_appl.h>
37 #include <errno.h>
38
39 #include "tlm-utils.h"
40 #include "tlm-log.h"
41 #include "tlm-config.h"
42 #include "tlm-config-general.h"
43
44 void
45 g_clear_string (gchar **str)
46 {
47     if (str && *str) {
48         g_free (*str);
49         *str = NULL;
50     }
51 }
52
53 gchar *
54 tlm_user_get_name (uid_t user_id)
55 {
56     struct passwd *pwent = NULL;
57     struct passwd buf_pwent;
58     gchar *buf = NULL, *tmp = NULL, *pw_name = NULL;
59     gsize size;
60     glong pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
61     if (pw_size_max <= sizeof(struct passwd))
62         size = 1024;
63     else
64         size = pw_size_max;
65
66     for (; NULL != (tmp = realloc(buf, size)); size*=2)
67     {
68         buf = tmp;
69
70         if (ERANGE == getpwuid_r(user_id, &buf_pwent, buf, size, &pwent))
71             continue;
72         break;
73     }
74
75     if (pwent)
76     {
77         pw_name = g_malloc(strlen(pwent->pw_name)+1);
78         if (pw_name)
79         {
80             memset(pw_name, 0, strlen(pwent->pw_name)+1);
81             memcpy(pw_name, pwent->pw_name, strlen(pwent->pw_name));
82         }
83     }
84
85     if (buf)
86         free(buf);
87
88     return pw_name;
89 }
90
91 uid_t
92 tlm_user_get_uid (const gchar *username)
93 {
94     struct passwd *pwent;
95     struct passwd buf_pwent;
96     __uid_t pw_uid = -1;
97     int ret = -1;
98     gchar *buf = NULL, *tmp = NULL;
99     gsize size;
100     glong pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
101     if (pw_size_max <= sizeof(struct passwd))
102         size = 1024;
103     else
104         size = pw_size_max;
105
106     for (; NULL != (tmp = realloc(buf, size)); size*=2)
107     {
108         buf = tmp;
109
110         ret = getpwnam_r(username, &buf_pwent, buf, size, &pwent);
111         if (0 == ret)
112             pw_uid = pwent->pw_uid;
113         else if (ERANGE == ret)
114             continue;
115         else
116             pw_uid = -1;
117
118         break;
119     }
120
121     if (buf)
122         free(buf);
123
124     return pw_uid;
125 }
126
127 gid_t
128 tlm_user_get_gid (const gchar *username)
129 {
130     struct passwd *pwent;
131     struct passwd buf_pwent;
132     __gid_t pw_gid = -1;
133     int ret = -1;
134     gchar *buf = NULL, *tmp = NULL;
135     gsize size;
136     glong pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
137     if (pw_size_max <= sizeof(struct passwd))
138         size = 1024;
139     else
140         size = pw_size_max;
141
142     for (; NULL != (tmp = realloc(buf, size)); size*=2)
143     {
144         buf = tmp;
145
146         ret = getpwnam_r(username, &buf_pwent, buf, size, &pwent);
147         if (0 == ret)
148             pw_gid = pwent->pw_gid;
149         else if (ERANGE == ret)
150             continue;
151         else
152             pw_gid = -1;
153
154         break;
155     }
156
157     if (buf)
158         free(buf);
159
160     return pw_gid;
161 }
162
163 gchar *
164 tlm_user_get_home_dir (const gchar *username)
165 {
166     struct passwd *pwent = NULL;
167     struct passwd buf_pwent;
168     gchar *buf = NULL, *tmp = NULL, *pw_dir = NULL;
169     gsize size;
170     glong pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
171     if (pw_size_max <= sizeof(struct passwd))
172         size = 1024;
173     else
174         size = pw_size_max;
175
176     for (; NULL != (tmp = realloc(buf, size)); size*=2)
177     {
178         buf = tmp;
179
180         if (ERANGE == getpwnam_r(username, &buf_pwent, buf, size, &pwent))
181             continue;
182         break;
183     }
184
185     if (pwent)
186     {
187         pw_dir = g_malloc(strlen(pwent->pw_dir)+1);
188         if (pw_dir)
189         {
190             memset(pw_dir, 0, strlen(pwent->pw_dir)+1);
191             memcpy(pw_dir, pwent->pw_dir, strlen(pwent->pw_dir));
192         }
193     }
194
195     if (buf)
196         free(buf);
197
198     return pw_dir;
199 }
200
201 gchar *
202 tlm_user_get_shell (const gchar *username)
203 {
204     struct passwd *pwent = NULL;
205     struct passwd buf_pwent;
206     gchar *buf = NULL, *tmp = NULL, *pw_shell = NULL;
207     gsize size;
208     glong pw_size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
209     if (pw_size_max <= sizeof(struct passwd))
210         size = 1024;
211     else
212         size = pw_size_max;
213
214     for (; NULL != (tmp = realloc(buf, size)); size*=2)
215     {
216         buf = tmp;
217
218         if (ERANGE == getpwnam_r(username, &buf_pwent, buf, size, &pwent))
219             continue;
220         break;
221     }
222
223     if (pwent)
224     {
225         pw_shell = g_malloc(strlen(pwent->pw_shell)+1);
226         if (pw_shell)
227         {
228             memset(pw_shell, 0, strlen(pwent->pw_shell)+1);
229             memcpy(pw_shell, pwent->pw_shell, strlen(pwent->pw_shell));
230         }
231     }
232
233     if (buf)
234         free(buf);
235
236     return pw_shell;
237 }
238
239 gboolean
240 tlm_utils_delete_dir (
241         const gchar *dir)
242 {
243     GDir* gdir = NULL;
244     struct stat sent;
245
246     if (!dir || !(gdir = g_dir_open(dir, 0, NULL))) {
247         return FALSE;
248     }
249
250     const gchar *fname = NULL;
251     gint retval = 0;
252     gchar *filepath = NULL;
253     while ((fname = g_dir_read_name (gdir)) != NULL) {
254         if (g_strcmp0 (fname, ".") == 0 ||
255             g_strcmp0 (fname, "..") == 0) {
256             continue;
257         }
258         retval = -1;
259         filepath = g_build_filename (dir, fname, NULL);
260         if (filepath) {
261             retval = lstat(filepath, &sent);
262             if (retval == 0) {
263                 /* recurse the directory */
264                 if (S_ISDIR (sent.st_mode)) {
265                     retval = (gint)!tlm_utils_delete_dir (filepath);
266                 } else {
267                     retval = g_remove (filepath);
268                 }
269             }
270             g_free (filepath);
271         }
272         if (retval != 0) {
273             g_dir_close (gdir);
274             return FALSE;
275         }
276     }
277     g_dir_close (gdir);
278
279     if (g_remove (dir) != 0) {
280         return FALSE;
281     }
282
283     return TRUE;
284 }
285
286 static gchar **
287 _split_command_line_with_regex(const char *command, GRegex *regex) {
288   gchar **temp_strv = NULL;
289   gchar **temp_iter = NULL, **args_iter = NULL;
290   gchar **argv = NULL;
291
292   temp_strv = regex ? g_regex_split (regex, command, G_REGEX_MATCH_NOTEMPTY)
293                     : g_strsplit (command, " ", -1);
294   if (!temp_strv) {
295     WARN("Failed to split command: %s", command);
296     return NULL;
297   }
298
299   argv = g_new0 (gchar *, g_strv_length (temp_strv));
300   for (temp_iter = temp_strv, args_iter = argv;
301       *temp_iter != NULL;
302       temp_iter++) {
303     size_t item_len = 0;
304     gchar *item = g_strstrip (*temp_iter);
305
306     item_len = strlen (item);
307     if (item_len == 0) {
308       continue;
309     }
310     if ((item[0] == '\"' && item[item_len - 1] == '\"') ||
311         (item[0] == '\'' && item[item_len - 1] == '\'')) {
312       item[item_len - 1] = '\0';
313       memmove (item, item + 1, item_len - 1);
314     }
315     *args_iter = g_strcompress (item);
316     args_iter++;
317   }
318   g_strfreev (temp_strv);
319
320   return argv;
321 }
322
323 gchar **
324 tlm_utils_split_command_line(const gchar *command) {
325   const gchar *pattern = "('.*?'|\".*?\"|\\S+)";
326   GError *error = NULL;
327   GRegex *regex = NULL;
328   gchar **argv = NULL;
329  
330   if (!command) {
331     WARN("Cannot pase NULL arguments string");
332     return NULL;
333   }
334  
335   if (!(regex = g_regex_new(pattern, 0, G_REGEX_MATCH_NOTEMPTY, &error))) {
336     WARN("Failed to create regex: %s", error->message);
337     g_error_free(error);
338   }
339
340   argv = _split_command_line_with_regex (command, regex);
341
342   g_regex_unref (regex);
343
344   return argv;
345 }
346
347 GList *
348 tlm_utils_split_command_lines (const GList *commands_list) {
349   const gchar *pattern = "('.*?'|\".*?\"|\\S+)";
350   GError *error = NULL;
351   GRegex *regex = NULL;
352   GList *argv_list = NULL;
353   const GList *tmp_list = NULL;
354
355   if (!commands_list) {
356     return NULL;
357   }
358
359   if (!(regex = g_regex_new(pattern, 0, G_REGEX_MATCH_NOTEMPTY, &error))) {
360     WARN("Failed to create regex: %s", error->message);
361     g_error_free(error);
362   }
363
364   for (tmp_list = commands_list; tmp_list; tmp_list = tmp_list->next) {
365     argv_list = g_list_append (argv_list, _split_command_line_with_regex (
366                     (const gchar *)tmp_list->data, regex));
367   }
368
369   g_regex_unref (regex);
370
371   return argv_list;
372 }
373
374 typedef struct {
375   int ifd;
376   GHashTable *dir_table; /* { gchar*: GList* } */
377   GHashTable *wd_table; /* { int: const gchar* } */
378   WatchCb cb;
379   gpointer userdata;
380 } WatchInfo;
381
382 static WatchInfo*
383 _watch_info_new (
384     int ifd,
385     WatchCb cb,
386     gpointer userdata)
387 {
388   WatchInfo *info = g_slice_new0 (WatchInfo);
389   if (!info) {
390       CRITICAL("g_slice_new0 memory allocation failure");
391       return NULL;
392   }
393   info->ifd = ifd;
394   info->cb = cb;
395   info->userdata = userdata;
396   info->dir_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
397   info->wd_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
398
399   return info;
400 }
401
402 static void
403 _destroy_dir_table_entry (gpointer key, gpointer value, gpointer userdata) {
404   (void)key;
405   (void)userdata;
406   g_list_free_full ((GList *)value, g_free);
407 }
408
409 static void
410 _destroy_wd_table_entry (gpointer key, gpointer value, gpointer userdata) {
411   (void)value;
412   int ifd = GPOINTER_TO_INT(userdata);
413   int wd = GPOINTER_TO_INT(key);
414
415   inotify_rm_watch (ifd, wd);
416 }
417
418 static void
419 _watch_info_free (WatchInfo *info)
420 {
421   if (!info) return;
422
423   if (info->dir_table) {
424     g_hash_table_foreach (info->dir_table, _destroy_dir_table_entry, NULL);
425     g_hash_table_unref (info->dir_table);
426   }
427   if (info->wd_table) {
428     g_hash_table_foreach (info->wd_table, _destroy_wd_table_entry,
429         GINT_TO_POINTER(info->ifd));
430     g_hash_table_unref (info->wd_table);
431   }
432   if (info->ifd) close(info->ifd);
433
434   g_slice_free (WatchInfo, info);
435 }
436
437 typedef enum {
438   WATCH_FAILED,
439   WATCH_ADDED,
440   WATCH_READY
441 } AddWatchResults;
442
443 AddWatchResults
444 _add_watch (int ifd, char *file_path, WatchInfo *info) {
445   GList *file_list = NULL;
446   int wd = 0;
447   gchar *file_name = NULL;
448   gchar *dir = NULL;
449   AddWatchResults res = WATCH_FAILED;
450
451   if (!file_path) return WATCH_FAILED;
452
453   file_name = g_path_get_basename (file_path);
454   if (!file_name) return WATCH_FAILED;
455
456   dir = g_path_get_dirname (file_path);
457   if (!dir) {
458     g_free (file_name);
459     return WATCH_FAILED;
460   }
461   if ((file_list = (GList *)g_hash_table_lookup (
462         info->dir_table, (gconstpointer)dir))) {
463     file_list = g_list_append (file_list, file_name);
464     g_free (dir);
465     return WATCH_ADDED;
466   }
467   file_list = g_list_append (NULL, file_name);
468   g_hash_table_insert (info->dir_table, g_strdup(dir), file_list);
469
470   /* add watch on directory if its not existing */
471   if (g_access (dir, 0)) {
472     return _add_watch (ifd, dir, info);
473   }
474
475   DBG("Adding watch for file '%s' in dir '%s'", file_name, dir);
476   if ((wd = inotify_add_watch (ifd, dir, IN_CREATE)) == -1) {
477     gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
478     WARN ("failed to add inotify watch on %s: %s", dir, strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
479     res = WATCH_FAILED;
480     goto remove_and_return;
481   }
482
483   if (!g_access (file_path, 0)) {
484     /* socket is ready, need not have a inotify watch for this */
485     inotify_rm_watch (ifd, wd);
486     res = WATCH_READY;
487     goto remove_and_return;
488   }
489
490   g_hash_table_insert (info->wd_table, GINT_TO_POINTER(wd), dir);
491
492   return WATCH_ADDED;
493
494 remove_and_return:
495   g_hash_table_remove (info->dir_table, (gconstpointer)dir);
496   g_list_free_full (file_list, (GDestroyNotify)g_free);
497
498   return res;
499 }
500
501 static gboolean
502 _inotify_watcher_cb (gint ifd, GIOCondition condition, gpointer userdata)
503 {
504   WatchInfo *info = (WatchInfo *)userdata;
505   struct inotify_event *ie = NULL;
506   gsize size = sizeof (struct inotify_event) + PATH_MAX + 1;
507   guint nwatch = g_hash_table_size (info->wd_table);
508
509   ie = (struct inotify_event *) g_slice_alloc0(size);
510   if (!ie) {
511     CRITICAL("g_slice_alloc0 memory allocation failure");
512     return nwatch ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
513   }
514
515   while (nwatch &&
516          read (ifd, ie, size) > (ssize_t)sizeof (struct inotify_event)) {
517     GList *file_list = NULL;
518     GList *element = NULL;
519     GList *pending_list = NULL;
520     gboolean is_first = FALSE;
521     gchar *file_path = NULL;
522     const gchar *dir = NULL;
523
524     dir = (gchar *)g_hash_table_lookup (
525         info->wd_table, GINT_TO_POINTER(ie->wd));
526     if (!dir) continue;
527
528     file_list = g_hash_table_lookup (info->dir_table, (gconstpointer)dir);
529     element = g_list_find_custom (file_list,
530         (gpointer)ie->name, (GCompareFunc)g_strcmp0);
531     if (!element) {
532       DBG("Ignoring '%s' file creation", ie->name);
533       continue;
534     }
535     is_first = (file_list == element);
536
537     g_free (element->data);
538     file_list = g_list_delete_link(file_list, element);
539     if (!file_list) {
540       g_hash_table_remove (info->dir_table, dir);
541       g_hash_table_remove (info->wd_table, GINT_TO_POINTER(ie->wd));
542       inotify_rm_watch (ifd, ie->wd);
543       nwatch--;
544     } else if (is_first) {
545       g_hash_table_insert (info->dir_table, g_strdup (dir), file_list);
546     }
547
548     file_path = g_build_filename (dir, ie->name, NULL);
549     if ((pending_list = (GList *)g_hash_table_lookup (info->dir_table,
550           (gconstpointer)file_path)) != NULL) {
551       GList *tmp = NULL;
552
553       // as we are about add real inotify watch, first remove from dir_table
554       g_hash_table_steal (info->dir_table, (gconstpointer)file_path);
555
556       // Add watches to all the files depend on this directory
557       for (tmp = pending_list; tmp; tmp = tmp->next) {
558         gchar *file_name = (gchar *)tmp->data;
559         gchar *new_file_path = g_build_filename (file_path, file_name, NULL);
560         AddWatchResults res = _add_watch (ifd, new_file_path, info);
561         if (res == WATCH_READY) {
562           if (info->cb) {
563             info->cb (new_file_path, nwatch == 0, NULL, info->userdata);
564           }
565         } else if (res == WATCH_ADDED) nwatch++;
566         else {
567           WARN ("Couldn't add watch on '%s'", new_file_path);
568         }
569         g_free (file_name);
570         g_free (new_file_path);
571       }
572       g_list_free (pending_list);
573     } else {
574       DBG("%s", file_path);
575       if (info->cb) info->cb (file_path, nwatch == 0, NULL, info->userdata);
576     }
577     g_free (file_path);
578   }
579
580   g_slice_free1 (size, ie);
581
582   return nwatch ? G_SOURCE_CONTINUE : G_SOURCE_REMOVE;
583 }
584
585 gchar *
586 _expand_file_path (const gchar *file_path)
587 {
588   gchar **items =NULL;
589   gchar **tmp_item =NULL;
590   gchar *expanded_path = NULL;
591
592   if (!file_path) return NULL;
593
594   /* nothing to expand
595    * FIXME: we are not considering filename which having \$ in it
596    */
597   if (g_strrstr (file_path, "$") == NULL) return g_strdup(file_path);
598
599   items = g_strsplit (file_path, G_DIR_SEPARATOR_S, -1);
600   /* soemthing wrong in file path */
601   if (!items) { return g_strdup (file_path); }
602
603   for (tmp_item = items; *tmp_item; tmp_item++) {
604     char *item = *tmp_item;
605     if (item[0] == '$') {
606       const gchar *env = g_getenv (item+1);
607       g_free (item);
608       *tmp_item = g_strdup (env ? env : "");
609     }
610   }
611
612   expanded_path = g_strjoinv (G_DIR_SEPARATOR_S, items);
613
614   g_strfreev(items);
615
616   return expanded_path;
617 }
618
619 guint
620 tlm_utils_watch_for_files (
621     const gchar **watch_list,
622     WatchCb cb,
623     gpointer userdata)
624 {
625   gint nwatch = 0;
626   int ifd = 0;
627   WatchInfo *w_info = NULL;
628
629   if (!watch_list) return 0;
630
631   if ((ifd = inotify_init1 (IN_NONBLOCK | IN_CLOEXEC)) < 0) {
632     gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
633     WARN("Failed to start inotify: %s", strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
634     return 0;
635   }
636
637   w_info = _watch_info_new (ifd, cb, userdata);
638
639   for (; *watch_list; watch_list++) {
640     char *socket_path  = _expand_file_path (*watch_list);
641     AddWatchResults res = _add_watch (ifd, socket_path, w_info);
642     if (res == WATCH_FAILED) {
643       WARN ("Failed to watch for '%s'", socket_path);
644     } else if (res == WATCH_READY) {
645       gboolean is_final = !nwatch && !*(watch_list + 1);
646       if (cb) cb (socket_path, is_final, NULL, userdata);
647     } else {
648       nwatch++;
649     }
650     g_free(socket_path);
651   }
652
653   if (nwatch == 0) {
654     _watch_info_free (w_info);
655     return 0;
656   }
657
658   return g_unix_fd_add_full (G_PRIORITY_DEFAULT, ifd, G_IO_IN,
659       _inotify_watcher_cb, w_info, (GDestroyNotify)_watch_info_free);
660 }
661
662 typedef struct _TlmLoginInfo
663 {
664     gchar* username;
665     gchar* password;
666 } TlmLoginInfo;
667
668 static int func_conv(
669     int num_msg,
670     const struct pam_message **msgs,
671     struct pam_response **resps,
672     void *pdata)
673 {
674     int i;
675     const char *login_prompt = "login";
676     const char *pwd_prompt = "Password";
677
678     if (num_msg <= 0)
679         return PAM_CONV_ERR;
680
681     TlmLoginInfo *info = (TlmLoginInfo *)pdata;
682     *resps = g_malloc0 (num_msg * sizeof(struct pam_response));
683     if (!*resps)
684         return PAM_CONV_ERR;
685
686     for (i=0; i<num_msg; ++i) {
687         struct pam_response *resp = *resps + i;
688
689         if (resp) {
690             if (msgs[i]->msg_style == PAM_PROMPT_ECHO_ON &&
691                 strncmp(msgs[i]->msg, login_prompt, strlen(login_prompt)) == 0) {
692                 resp->resp = strndup (info->username, PAM_MAX_RESP_SIZE - 1);
693             }
694             else if (msgs[i]->msg_style == PAM_PROMPT_ECHO_OFF &&
695                      strncmp(msgs[i]->msg, pwd_prompt, strlen(pwd_prompt)) == 0) {
696                 resp->resp = strndup (info->password, PAM_MAX_RESP_SIZE - 1);
697             }
698             else {
699                 resp->resp = NULL;
700             }
701             resp->resp_retcode = PAM_SUCCESS;
702         }
703     }
704     return PAM_SUCCESS;
705 }
706
707 gboolean
708 tlm_authenticate_user (
709     TlmConfig *config,
710     const gchar *username,
711     const gchar *password)
712 {
713     pam_handle_t *pam_h = NULL;
714     gboolean ret_auth = FALSE;
715     int ret;
716     const gchar *service = NULL;
717     TlmLoginInfo *info = NULL;
718
719     if (!password || !username) {
720         WARN("username or password would be NULL");
721         return FALSE;
722     }
723
724     // If TLM_CONFIG_PAM_AUTHENTICATION_SERVICE is not specified in tlm.conf
725     // use "system-auth" as defult.
726     service = tlm_config_get_string(config, TLM_CONFIG_GENERAL,
727                                     // TLM_CONFIG_PAM_AUTHENTICATION_SERVICE);
728                                     TLM_CONFIG_GENERAL_PAM_SERVICE);
729     if (!service)
730         service = "system-auth";
731
732     info = g_malloc0 (sizeof (*info));
733     if (!info) {
734         CRITICAL("g_malloc0 memory allocation failure");
735         return FALSE;
736     }
737     info->username = strndup (username, PAM_MAX_RESP_SIZE - 1);
738     info->password = strndup (password, PAM_MAX_RESP_SIZE - 1);
739     const struct pam_conv conv = {func_conv, info};
740
741     ret = pam_start (service, username, &conv, &pam_h);
742     if (ret != PAM_SUCCESS) {
743         WARN("Failed to pam_start: %d", ret);
744         free(info->username);
745         free(info->password);
746         g_free(info);
747         return FALSE;
748     }
749
750     ret = pam_authenticate (pam_h, PAM_SILENT);
751     if (ret == PAM_SUCCESS)
752         ret_auth = TRUE;
753     else if (ret == PAM_AUTH_ERR)
754         WARN("Failed to get authentication! username: %s", username);
755     else
756         WARN("Failed to pam_authenticate: %d", ret);
757
758     pam_end(pam_h, ret);
759
760     free(info->username);
761     free(info->password);
762     g_free(info);
763     return ret_auth;
764 }