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