1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * Copyright (C) 2018 Sean Davis <bluesabre@xfce.org>
4  * Copyright (C) 2006 William Jon McCann <mccann@jhu.edu>
5  * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
6  * Copyright (C) 2003 Bill Nottingham <notting@redhat.com>
7  * Copyright (c) 1993-2003 Jamie Zawinski <jwz@jwz.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22  * 02110-1301, USA.
23  *
24  */
25 
26 #include <config.h>
27 
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <grp.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/types.h>
37 
38 #include <security/pam_appl.h>
39 
40 #include <glib.h>
41 #include <glib/gstdio.h>
42 #include <gtk/gtk.h>
43 
44 #include <libxfce4util/libxfce4util.h>
45 
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 
50 #include "gs-auth.h"
51 #include "subprocs.h"
52 
53 /* Some time between Red Hat 4.2 and 7.0, the words were transposed
54    in the various PAM_x_CRED macro names.  Yay!
55 */
56 #ifndef  PAM_REFRESH_CRED
57 # define PAM_REFRESH_CRED PAM_CRED_REFRESH
58 #endif
59 
60 #ifdef HAVE_PAM_FAIL_DELAY
61 /* We handle delays ourself.*/
62 /* Don't set this to 0 (Linux bug workaround.) */
63 # define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1)
64 #else  /* !HAVE_PAM_FAIL_DELAY */
65 # define PAM_NO_DELAY(pamh) /* */
66 #endif /* !HAVE_PAM_FAIL_DELAY */
67 
68 
69 /* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args.
70    On some other Linux systems with some other version of PAM (e.g.,
71    whichever Debian release comes with a 2.2.5 kernel) it takes one arg.
72    I can't tell which is more "recent" or "correct" behavior, so configure
73    figures out which is in use for us.  Shoot me!
74 */
75 #ifdef PAM_STRERROR_TWO_ARGS
76 # define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status))
77 #else  /* !PAM_STRERROR_TWO_ARGS */
78 # define PAM_STRERROR(pamh, status) pam_strerror((status))
79 #endif /* !PAM_STRERROR_TWO_ARGS */
80 
81 static gboolean      verbose_enabled = FALSE;
82 static pam_handle_t *pam_handle = NULL;
83 static gboolean      did_we_ask_for_password = FALSE;
84 
85 struct pam_closure {
86     const char        *username;
87     GSAuthMessageFunc  cb_func;
88     gpointer           cb_data;
89     int                signal_fd;
90     int                result;
91 };
92 
93 typedef struct {
94     struct pam_closure  *closure;
95     GSAuthMessageStyle   style;
96     const char          *msg;
97     char               **resp;
98     gboolean             should_interrupt_stack;
99 } GsAuthMessageHandlerData;
100 
101 static GCond  message_handled_condition;
102 static GMutex message_handler_mutex;
103 
104 GQuark
gs_auth_error_quark(void)105 gs_auth_error_quark (void) {
106     static GQuark quark = 0;
107     if (!quark) {
108         quark = g_quark_from_static_string ("gs_auth_error");
109     }
110 
111     return quark;
112 }
113 
114 void
gs_auth_set_verbose(gboolean verbose)115 gs_auth_set_verbose (gboolean verbose) {
116     verbose_enabled = verbose;
117 }
118 
119 gboolean
gs_auth_get_verbose(void)120 gs_auth_get_verbose (void) {
121     return verbose_enabled;
122 }
123 
124 static GSAuthMessageStyle
pam_style_to_gs_style(int pam_style)125 pam_style_to_gs_style (int pam_style) {
126     GSAuthMessageStyle style;
127 
128     switch (pam_style) {
129     case PAM_PROMPT_ECHO_ON:
130         style = GS_AUTH_MESSAGE_PROMPT_ECHO_ON;
131         break;
132     case PAM_PROMPT_ECHO_OFF:
133         style = GS_AUTH_MESSAGE_PROMPT_ECHO_OFF;
134         break;
135     case PAM_ERROR_MSG:
136         style = GS_AUTH_MESSAGE_ERROR_MSG;
137         break;
138     case PAM_TEXT_INFO:
139         style = GS_AUTH_MESSAGE_TEXT_INFO;
140         break;
141     default:
142         g_assert_not_reached ();
143         break;
144     }
145 
146     return style;
147 }
148 
149 static gboolean
auth_message_handler(GSAuthMessageStyle style,const char * msg,char ** response,gpointer data)150 auth_message_handler (GSAuthMessageStyle   style,
151                       const char          *msg,
152                       char               **response,
153                       gpointer             data) {
154     gboolean ret;
155 
156     ret = TRUE;
157     *response = NULL;
158 
159     switch (style) {
160         case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
161             break;
162         case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
163             if (msg != NULL && g_str_has_prefix (msg, _("Password:"))) {
164                 did_we_ask_for_password = TRUE;
165             }
166             break;
167         case GS_AUTH_MESSAGE_ERROR_MSG:
168             break;
169         case GS_AUTH_MESSAGE_TEXT_INFO:
170             break;
171         default:
172             g_assert_not_reached ();
173     }
174 
175     return ret;
176 }
177 
178 static gboolean
gs_auth_queued_message_handler(gpointer user_data)179 gs_auth_queued_message_handler (gpointer user_data) {
180     GsAuthMessageHandlerData *data = user_data;
181     gboolean res;
182 
183     if (gs_auth_get_verbose ()) {
184         g_message ("Waiting for lock");
185     }
186 
187     g_mutex_lock (&message_handler_mutex);
188 
189     if (gs_auth_get_verbose ()) {
190         g_message ("Waiting for response");
191     }
192 
193     res = data->closure->cb_func (data->style,
194                                   data->msg,
195                                   data->resp,
196                                   data->closure->cb_data);
197 
198     data->should_interrupt_stack = res == FALSE;
199 
200     g_cond_signal (&message_handled_condition);
201     g_mutex_unlock (&message_handler_mutex);
202 
203     if (gs_auth_get_verbose ()) {
204         g_message ("Got response");
205     }
206 
207     return FALSE;
208 }
209 
210 static gboolean
gs_auth_run_message_handler(struct pam_closure * c,GSAuthMessageStyle style,const char * msg,char ** resp)211 gs_auth_run_message_handler (struct pam_closure  *c,
212                              GSAuthMessageStyle   style,
213                              const char          *msg,
214                              char               **resp) {
215     GsAuthMessageHandlerData data;
216 
217     data.closure = c;
218     data.style = style;
219     data.msg = msg;
220     data.resp = resp;
221     data.should_interrupt_stack = TRUE;
222 
223     g_mutex_lock (&message_handler_mutex);
224 
225     /* Queue the callback in the gui (the main) thread
226      */
227     g_idle_add (gs_auth_queued_message_handler, &data);
228 
229     if (gs_auth_get_verbose ()) {
230         g_message ("Waiting for respose to message style %d: '%s'", style, msg);
231     }
232 
233     /* Wait for the response
234      */
235     g_cond_wait (&message_handled_condition,
236                  &message_handler_mutex);
237     g_mutex_unlock (&message_handler_mutex);
238 
239     if (gs_auth_get_verbose ()) {
240         g_message ("Got respose to message style %d: interrupt:%d", style, data.should_interrupt_stack);
241     }
242 
243     return data.should_interrupt_stack == FALSE;
244 }
245 
246 static int
pam_conversation(int nmsgs,const struct pam_message ** msg,struct pam_response ** resp,void * closure)247 pam_conversation (int                        nmsgs,
248                   const struct pam_message **msg,
249                   struct pam_response      **resp,
250                   void                      *closure) {
251     int                  replies = 0;
252     struct pam_response *reply = NULL;
253     struct pam_closure  *c = (struct pam_closure *) closure;
254     gboolean             res;
255     int                  ret;
256 
257     reply = (struct pam_response *) calloc (nmsgs, sizeof (*reply));
258 
259     if (reply == NULL) {
260         return PAM_CONV_ERR;
261     }
262 
263     res = TRUE;
264     ret = PAM_SUCCESS;
265 
266     for (replies = 0; replies < nmsgs && ret == PAM_SUCCESS; replies++) {
267         GSAuthMessageStyle style;
268         char              *utf8_msg;
269 
270         style = pam_style_to_gs_style (msg[replies]->msg_style);
271 
272         utf8_msg = g_locale_to_utf8 (msg[replies]->msg,
273                                      -1,
274                                      NULL,
275                                      NULL,
276                                      NULL);
277 
278         /* if we couldn't convert text from locale then
279          * assume utf-8 and hope for the best */
280         if (utf8_msg == NULL) {
281             char *p;
282             char *q;
283 
284             utf8_msg = g_strdup (msg[replies]->msg);
285 
286             p = utf8_msg;
287             while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q)) {
288                 *q = '?';
289                 p = q + 1;
290             }
291         }
292 
293         /* handle message locally first */
294         auth_message_handler (style,
295                               utf8_msg,
296                               &reply[replies].resp,
297                               NULL);
298 
299         if (c->cb_func != NULL) {
300             if (gs_auth_get_verbose ()) {
301                 g_message ("Handling message style %d: '%s'", style, utf8_msg);
302             }
303 
304             /* blocks until the gui responds
305              */
306             res = gs_auth_run_message_handler (c,
307                                                style,
308                                                utf8_msg,
309                                                &reply[replies].resp);
310 
311             if (gs_auth_get_verbose ()) {
312                 g_message ("Msg handler returned %d", res);
313             }
314 
315             /* If the handler returns FALSE - interrupt the PAM stack */
316             if (res) {
317                 reply[replies].resp_retcode = PAM_SUCCESS;
318             } else {
319                 int i;
320                 for (i = 0; i <= replies; i++) {
321                     free (reply[i].resp);
322                 }
323                 free (reply);
324                 reply = NULL;
325                 ret = PAM_CONV_ERR;
326             }
327         }
328 
329         g_free (utf8_msg);
330     }
331 
332     *resp = reply;
333 
334     return ret;
335 }
336 
337 static gboolean
close_pam_handle(int status)338 close_pam_handle (int status) {
339     if (pam_handle != NULL) {
340         int status2;
341 
342         status2 = pam_end (pam_handle, status);
343         pam_handle = NULL;
344 
345         if (gs_auth_get_verbose ()) {
346             g_message (" pam_end (...) ==> %d (%s)",
347                        status2,
348                        (status2 == PAM_SUCCESS ? "Success" : "Failure"));
349         }
350     }
351 
352     return TRUE;
353 }
354 
355 static gboolean
create_pam_handle(const char * username,const char * display,struct pam_conv * conv,int * status_code)356 create_pam_handle (const char      *username,
357                    const char      *display,
358                    struct pam_conv *conv,
359                    int             *status_code) {
360     int         status = -1;
361     const char *service = PAM_SERVICE_NAME;
362     char       *disp = NULL;
363     gboolean    ret = TRUE;
364 
365     if (pam_handle != NULL) {
366         g_warning ("create_pam_handle: Stale pam handle around, cleaning up");
367         close_pam_handle (PAM_SUCCESS);
368     }
369 
370     /* init things */
371     pam_handle = NULL;
372 
373     /* Initialize a PAM session for the user */
374     if ((status = pam_start (service, username, conv, &pam_handle)) != PAM_SUCCESS) {
375         pam_handle = NULL;
376         g_warning (_("Unable to establish service %s: %s\n"),
377                    service,
378                    PAM_STRERROR (NULL, status));
379 
380         if (status_code != NULL) {
381             *status_code = status;
382         }
383 
384         ret = FALSE;
385         goto out;
386     }
387 
388     if (gs_auth_get_verbose ()) {
389         g_message ("pam_start (\"%s\", \"%s\", ...) ==> %d (%s)",
390                    service,
391                    username,
392                    status,
393                    PAM_STRERROR (pam_handle, status));
394     }
395 
396     disp = g_strdup (display);
397     if (disp == NULL) {
398         disp = g_strdup (":0.0");
399     }
400 
401     if ((status = pam_set_item (pam_handle, PAM_TTY, disp)) != PAM_SUCCESS) {
402         g_warning (_("Can't set PAM_TTY=%s"), display);
403 
404         if (status_code != NULL) {
405             *status_code = status;
406         }
407 
408         ret = FALSE;
409         goto out;
410     }
411 
412     ret = TRUE;
413     g_cond_init (&message_handled_condition);
414     g_mutex_init (&message_handler_mutex);
415 
416 out:
417     if (status_code != NULL) {
418         *status_code = status;
419     }
420 
421     g_free (disp);
422 
423     return ret;
424 }
425 
426 static void
set_pam_error(GError ** error,int status)427 set_pam_error (GError **error,
428                int      status) {
429     if (status == PAM_AUTH_ERR || status == PAM_USER_UNKNOWN) {
430         char *msg;
431 
432         if (did_we_ask_for_password) {
433             msg = g_strdup (_("Incorrect password."));
434         } else {
435             msg = g_strdup (_("Authentication failed."));
436         }
437 
438         g_set_error (error,
439                      GS_AUTH_ERROR,
440                      GS_AUTH_ERROR_AUTH_ERROR,
441                      "%s",
442                      msg);
443         g_free (msg);
444     } else if (status == PAM_PERM_DENIED) {
445         g_set_error (error,
446                      GS_AUTH_ERROR,
447                      GS_AUTH_ERROR_AUTH_DENIED,
448                      "%s",
449                      _("Not permitted to gain access at this time."));
450     } else if (status == PAM_ACCT_EXPIRED) {
451         g_set_error (error,
452                      GS_AUTH_ERROR,
453                      GS_AUTH_ERROR_AUTH_DENIED,
454                      "%s",
455                      _("No longer permitted to access the system."));
456     }
457 }
458 
459 static int
gs_auth_thread_func(int auth_operation_fd)460 gs_auth_thread_func (int auth_operation_fd) {
461     static const int  flags = 0;
462     int               status;
463     int               status2;
464     struct timespec   timeout;
465     sigset_t          set;
466     const void       *p;
467 
468     timeout.tv_sec = 0;
469     timeout.tv_nsec = 1;
470 
471     set = block_sigchld ();
472 
473     status = pam_authenticate (pam_handle, flags);
474 
475     sigtimedwait (&set, NULL, &timeout);
476     unblock_sigchld ();
477 
478     if (gs_auth_get_verbose ()) {
479         g_message ("   pam_authenticate (...) ==> %d (%s)",
480                    status,
481                    PAM_STRERROR (pam_handle, status));
482     }
483 
484     if (status != PAM_SUCCESS) {
485         goto done;
486     }
487 
488     if ((status = pam_get_item (pam_handle, PAM_USER, &p)) != PAM_SUCCESS) {
489         /* is not really an auth problem, but it will
490            pretty much look as such, it shouldn't really
491            happen */
492         goto done;
493     }
494 
495     /* We don't actually care if the account modules fail or succeed,
496      * but we need to run them anyway because certain pam modules
497      * depend on side effects of the account modules getting run.
498      */
499     status2 = pam_acct_mgmt (pam_handle, 0);
500 
501     if (gs_auth_get_verbose ()) {
502         g_message ("pam_acct_mgmt (...) ==> %d (%s)\n",
503                    status2,
504                    PAM_STRERROR (pam_handle, status2));
505     }
506 
507     /* FIXME: should we handle these? */
508     switch (status2) {
509         case PAM_SUCCESS:
510             break;
511         case PAM_NEW_AUTHTOK_REQD:
512             break;
513         case PAM_AUTHINFO_UNAVAIL:
514             break;
515         case PAM_ACCT_EXPIRED:
516             break;
517         case PAM_PERM_DENIED:
518             break;
519         default :
520             break;
521     }
522 
523     /* Each time we successfully authenticate, refresh credentials,
524        for Kerberos/AFS/DCE/etc.  If this fails, just ignore that
525        failure and blunder along; it shouldn't matter.
526 
527        Note: this used to be PAM_REFRESH_CRED instead of
528        PAM_REINITIALIZE_CRED, but Jason Heiss <jheiss@ee.washington.edu>
529        says that the Linux PAM library ignores that one, and only refreshes
530        credentials when using PAM_REINITIALIZE_CRED.
531     */
532     status2 = pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
533     if (gs_auth_get_verbose ()) {
534         g_message ("   pam_setcred (...) ==> %d (%s)",
535                    status2,
536                    PAM_STRERROR (pam_handle, status2));
537     }
538 
539 done:
540     /* we're done, close the fd and wake up the main
541      * loop
542      */
543     close (auth_operation_fd);
544 
545     return status;
546 }
547 
548 static gboolean
gs_auth_loop_quit(GIOChannel * source,GIOCondition condition,gboolean * thread_done)549 gs_auth_loop_quit (GIOChannel   *source,
550                    GIOCondition  condition,
551                    gboolean     *thread_done) {
552     *thread_done = TRUE;
553     gtk_main_quit ();
554     return FALSE;
555 }
556 
557 static gboolean
gs_auth_pam_verify_user(pam_handle_t * handle,int * status)558 gs_auth_pam_verify_user (pam_handle_t *handle,
559                          int          *status) {
560     GThread    *auth_thread;
561     GIOChannel *channel = NULL;
562     guint       watch_id = 0;
563     int         auth_operation_fds[2];
564     int         auth_status = PAM_AUTH_ERR;
565     gboolean    thread_done;
566 
567     /* This pipe gives us a set of fds we can hook into
568      * the event loop to be notified when our helper thread
569      * is ready to be reaped.
570      */
571     if (pipe (auth_operation_fds) < 0) {
572         goto out;
573     }
574 
575     if (fcntl (auth_operation_fds[0], F_SETFD, FD_CLOEXEC) < 0) {
576         close (auth_operation_fds[0]);
577         close (auth_operation_fds[1]);
578         goto out;
579     }
580 
581     if (fcntl (auth_operation_fds[1], F_SETFD, FD_CLOEXEC) < 0) {
582         close (auth_operation_fds[0]);
583         close (auth_operation_fds[1]);
584         goto out;
585     }
586 
587     channel = g_io_channel_unix_new (auth_operation_fds[0]);
588 
589     /* we use a recursive main loop to process ui events
590      * while we wait on a thread to handle the blocking parts
591      * of pam authentication.
592      */
593     thread_done = FALSE;
594     watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP,
595                                (GIOFunc) gs_auth_loop_quit, &thread_done);
596 
597     auth_thread = g_thread_new ("auththread",
598                                 (GThreadFunc) gs_auth_thread_func,
599                                 GINT_TO_POINTER (auth_operation_fds[1]));
600 
601     if (auth_thread == NULL) {
602         goto out;
603     }
604 
605     gtk_main ();
606 
607     /* if the event loop was quit before the thread is done then we can't
608      * reap the thread without blocking on it finishing.  The
609      * thread may not ever finish though if the pam module is blocking.
610      *
611      * The only time the event loop is going to stop when the thread isn't
612      * done, however, is if the dialog quits early (from, e.g., "cancel"),
613      * so we can just exit.  An alternative option would be to switch to
614      * using pthreads directly and calling pthread_cancel.
615      */
616     if (!thread_done) {
617         raise (SIGTERM);
618     }
619 
620     auth_status = GPOINTER_TO_INT (g_thread_join (auth_thread));
621 
622 out:
623     if (watch_id != 0) {
624         // g_source_remove (watch_id);
625         watch_id = 0;
626     }
627 
628     if (channel != NULL) {
629         g_io_channel_unref (channel);
630     }
631 
632     if (status) {
633         *status = auth_status;
634     }
635 
636     return auth_status == PAM_SUCCESS;
637 }
638 
639 gboolean
gs_auth_verify_user(const char * username,const char * display,GSAuthMessageFunc func,gpointer data,GError ** error)640 gs_auth_verify_user (const char         *username,
641                      const char         *display,
642                      GSAuthMessageFunc   func,
643                      gpointer            data,
644                      GError            **error) {
645     int                status = -1;
646     struct pam_conv    conv;
647     struct pam_closure c;
648     struct passwd     *pwent;
649 
650     pwent = getpwnam (username);
651     if (pwent == NULL) {
652         return FALSE;
653     }
654 
655     c.username = username;
656     c.cb_func = func;
657     c.cb_data = data;
658 
659     conv.conv = &pam_conversation;
660     conv.appdata_ptr = (void *) &c;
661 
662     /* Initialize PAM. */
663     create_pam_handle (username, display, &conv, &status);
664     if (status != PAM_SUCCESS) {
665         goto done;
666     }
667 
668     pam_set_item (pam_handle, PAM_USER_PROMPT, _("Username:"));
669 
670     PAM_NO_DELAY(pam_handle);
671 
672     did_we_ask_for_password = FALSE;
673     if (!gs_auth_pam_verify_user (pam_handle, &status)) {
674         goto done;
675     }
676 
677 done:
678     if (status != PAM_SUCCESS) {
679         set_pam_error (error, status);
680     }
681 
682     close_pam_handle (status);
683 
684     return (status == PAM_SUCCESS ? TRUE : FALSE);
685 }
686 
687 gboolean
gs_auth_init(void)688 gs_auth_init (void) {
689     return TRUE;
690 }
691 
692 gboolean
gs_auth_priv_init(void)693 gs_auth_priv_init (void) {
694     /* We have nothing to do at init-time.
695        However, we might as well do some error checking.
696        If "/usr/local/etc/pam.d" exists and is a directory, but "/usr/local/etc/pam.d/xlock"
697        does not exist, warn that PAM probably isn't going to work.
698 
699        This is a priv-init instead of a non-priv init in case the directory
700        is unreadable or something (don't know if that actually happens.)
701     */
702     const char   dir[] = "/usr/local/etc/pam.d";
703     const char  file[] = "/usr/local/etc/pam.d/" PAM_SERVICE_NAME;
704     const char file2[] = "/etc/pam.conf";
705     struct stat st;
706 
707     if (g_stat (dir, &st) == 0 && st.st_mode & S_IFDIR) {
708         if (g_stat (file, &st) != 0) {
709             g_warning ("%s does not exist.\n"
710                        "Authentication via PAM is unlikely to work.",
711                        file);
712         }
713     } else if (g_stat (file2, &st) == 0) {
714         FILE *f = g_fopen (file2, "r");
715         if (f) {
716             gboolean ok = FALSE;
717             char buf[255];
718             while (fgets (buf, sizeof(buf), f)) {
719                 if (strstr (buf, PAM_SERVICE_NAME)) {
720                     ok = TRUE;
721                     break;
722                 }
723             }
724 
725             fclose (f);
726             if (!ok) {
727                 g_warning ("%s does not list the `%s' service.\n"
728                            "Authentication via PAM is unlikely to work.",
729                            file2, PAM_SERVICE_NAME);
730             }
731         }
732         /* else warn about file2 existing but being unreadable? */
733     } else {
734         g_warning ("Neither %s nor %s exist.\n"
735                    "Authentication via PAM is unlikely to work.",
736                    file2, file);
737     }
738 
739     /* Return true anyway, just in case. */
740     return TRUE;
741 }
742