Bug Summary

File:rc/gs-auth-pam.c
Warning:line 280, column 2
Value stored to 'res' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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