2 * Copyright (C) 2010-2011 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
14 #include "ldm-marshal.h"
15 #include "pam-session.h"
19 AUTHENTICATION_STARTED,
22 AUTHENTICATION_RESULT,
26 static guint signals[LAST_SIGNAL] = { 0 };
28 struct PAMSessionPrivate
30 /* Service to authenticate against */
33 /* User being authenticated */
36 /* Authentication thread */
37 GThread *authentication_thread;
39 /* TRUE if the thread is being intentionally stopped */
42 /* TRUE if the conversation should be cancelled */
45 /* Messages requested */
47 const struct pam_message **messages;
50 /* Queue to feed responses to the authentication thread */
51 GAsyncQueue *authentication_response_queue;
53 /* Authentication handle */
54 pam_handle_t *pam_handle;
56 /* TRUE if in a session */
60 G_DEFINE_TYPE (PAMSession, pam_session, G_TYPE_OBJECT);
62 static gchar *passwd_file = NULL;
65 pam_session_set_use_pam (void)
67 pam_session_set_use_passwd_file (NULL);
71 pam_session_set_use_passwd_file (gchar *passwd_file_)
74 passwd_file = g_strdup (passwd_file_);
78 pam_session_new (const gchar *service, const gchar *username)
80 PAMSession *self = g_object_new (PAM_SESSION_TYPE, NULL);
82 self->priv->service = g_strdup (service);
83 self->priv->username = g_strdup (username);
89 pam_session_get_in_session (PAMSession *session)
91 g_return_val_if_fail (session != NULL, FALSE);
92 return session->priv->in_session;
96 pam_session_authorize (PAMSession *session)
98 g_return_if_fail (session != NULL);
100 session->priv->in_session = TRUE;
107 //pam_set_item (session->priv->pam_handle, PAM_TTY, &tty);
108 //pam_set_item (session->priv->pam_handle, PAM_XDISPLAY, &display);
109 result = pam_open_session (session->priv->pam_handle, 0);
110 g_debug ("pam_open_session -> %s", pam_strerror (session->priv->pam_handle, result));
112 result = pam_setcred (session->priv->pam_handle, PAM_ESTABLISH_CRED);
113 g_debug ("pam_setcred(PAM_ESTABLISH_CRED) -> %s", pam_strerror (session->priv->pam_handle, result));
116 g_signal_emit (G_OBJECT (session), signals[STARTED], 0);
120 notify_messages_cb (gpointer data)
122 PAMSession *session = data;
124 g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
130 pam_conv_cb (int num_msg, const struct pam_message **msg,
131 struct pam_response **resp, void *app_data)
133 PAMSession *session = app_data;
134 struct pam_response *response;
137 session->priv->num_messages = num_msg;
138 session->priv->messages = msg;
139 g_idle_add (notify_messages_cb, session);
141 /* Wait for response */
142 response = g_async_queue_pop (session->priv->authentication_response_queue);
143 session->priv->num_messages = 0;
144 session->priv->messages = NULL;
146 /* Cancelled by user */
147 if (session->priv->stop || session->priv->cancel)
149 session->priv->cancel = FALSE;
159 notify_auth_complete_cb (gpointer data)
161 PAMSession *session = data;
164 result = session->priv->result;
165 session->priv->result = 0;
167 g_thread_join (session->priv->authentication_thread);
168 session->priv->authentication_thread = NULL;
169 g_async_queue_unref (session->priv->authentication_response_queue);
170 session->priv->authentication_response_queue = NULL;
172 /* Authentication was cancelled */
173 if (session->priv->stop)
174 pam_session_end (session);
176 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, result);
178 /* The thread is complete, drop the reference */
179 g_object_unref (session);
185 authenticate_cb (gpointer data)
187 PAMSession *session = data;
188 struct pam_conv conversation = { pam_conv_cb, session };
190 pam_start (session->priv->service, session->priv->username, &conversation, &session->priv->pam_handle);
192 session->priv->result = pam_authenticate (session->priv->pam_handle, 0);
193 g_debug ("pam_authenticate -> %s", pam_strerror (session->priv->pam_handle, session->priv->result));
195 if (session->priv->result == PAM_SUCCESS)
197 session->priv->result = pam_acct_mgmt (session->priv->pam_handle, 0);
198 g_debug ("pam_acct_mgmt -> %s", pam_strerror (session->priv->pam_handle, session->priv->result));
200 if (session->priv->result == PAM_NEW_AUTHTOK_REQD)
202 session->priv->result = pam_chauthtok (session->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
203 g_debug ("pam_chauthtok -> %s", pam_strerror (session->priv->pam_handle, session->priv->result));
208 g_idle_add (notify_auth_complete_cb, session);
214 get_password (const gchar *username)
216 gchar *data = NULL, **lines, *password = NULL;
218 GError *error = NULL;
220 if (!g_file_get_contents (passwd_file, &data, NULL, &error))
221 g_warning ("Error loading passwd file: %s", error->message);
222 g_clear_error (&error);
227 lines = g_strsplit (data, "\n", -1);
230 for (i = 0; lines[i] && password == NULL; i++)
232 gchar *line, **fields;
234 line = g_strstrip (lines[i]);
235 fields = g_strsplit (line, ":", -1);
236 if (g_strv_length (fields) == 7 && strcmp (fields[0], username) == 0)
237 password = g_strdup (fields[1]);
246 send_message (PAMSession *session, gint style, const gchar *text)
248 struct pam_message **messages;
250 messages = calloc (1, sizeof (struct pam_message *));
251 messages[0] = g_malloc0 (sizeof (struct pam_message));
252 messages[0]->msg_style = style;
253 messages[0]->msg = g_strdup (text);
254 session->priv->messages = (const struct pam_message **) messages;
255 session->priv->num_messages = 1;
257 g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
261 pam_session_start (PAMSession *session, GError **error)
263 g_return_val_if_fail (session != NULL, FALSE);
264 g_return_val_if_fail (session->priv->authentication_thread == NULL, FALSE);
266 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_STARTED], 0);
270 if (session->priv->username == NULL)
271 send_message (session, PAM_PROMPT_ECHO_ON, "login:");
276 password = get_password (session->priv->username);
277 /* Always succeed with autologin, or no password on account otherwise prompt for a password */
278 if (strcmp (session->priv->service, "lightdm-autologin") == 0 ||
279 g_strcmp0 (password, "") == 0)
280 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_SUCCESS);
282 send_message (session, PAM_PROMPT_ECHO_OFF, "Password:");
288 /* Hold a reference to this object while the thread may access it */
289 g_object_ref (session);
292 session->priv->authentication_response_queue = g_async_queue_new ();
293 session->priv->authentication_thread = g_thread_create (authenticate_cb, session, TRUE, error);
294 if (!session->priv->authentication_thread)
302 pam_session_strerror (PAMSession *session, int error)
304 g_return_val_if_fail (session != NULL, NULL);
305 return pam_strerror (session->priv->pam_handle, error);
309 pam_session_get_username (PAMSession *session)
311 const char *username;
313 g_return_val_if_fail (session != NULL, NULL);
315 if (session->priv->pam_handle)
317 g_free (session->priv->username);
318 pam_get_item (session->priv->pam_handle, PAM_USER, (const void **) &username);
319 session->priv->username = g_strdup (username);
322 return session->priv->username;
325 const struct pam_message **
326 pam_session_get_messages (PAMSession *session)
328 g_return_val_if_fail (session != NULL, NULL);
329 return session->priv->messages;
333 pam_session_get_num_messages (PAMSession *session)
335 g_return_val_if_fail (session != NULL, 0);
336 return session->priv->num_messages;
340 pam_session_respond (PAMSession *session, struct pam_response *response)
342 g_return_if_fail (session != NULL);
348 if (session->priv->messages)
351 struct pam_message **messages = (struct pam_message **) session->priv->messages;
353 for (i = 0; i < session->priv->num_messages; i++)
355 g_free ((gchar *) messages[i]->msg);
356 g_free (messages[i]);
359 session->priv->messages = NULL;
360 session->priv->num_messages = 0;
364 if (session->priv->username == NULL)
366 session->priv->username = g_strdup (response->resp);
367 password = get_password (session->priv->username);
368 if (g_strcmp0 (password, "") == 0)
369 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_SUCCESS);
371 send_message (session, PAM_PROMPT_ECHO_OFF, "Password:");
377 user = user_get_by_name (session->priv->username);
378 password = get_password (session->priv->username);
379 if (user && g_strcmp0 (response->resp, password) == 0)
380 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_SUCCESS);
382 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_AUTH_ERR);
385 g_object_unref (user);
388 g_free (response->resp);
393 g_return_if_fail (session->priv->authentication_thread != NULL);
394 g_async_queue_push (session->priv->authentication_response_queue, response);
399 pam_session_cancel (PAMSession *session)
401 g_return_if_fail (session != NULL);
405 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_CONV_ERR);
409 g_return_if_fail (session->priv->authentication_thread != NULL);
410 session->priv->cancel = TRUE;
411 g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
416 pam_session_getenv (PAMSession *session, const gchar *name)
418 g_return_val_if_fail (session != NULL, NULL);
422 return pam_getenv (session->priv->pam_handle, name);
426 pam_session_get_envlist (PAMSession *session)
428 g_return_val_if_fail (session != NULL, NULL);
431 char **env_list = calloc (1, sizeof (gchar *));
436 return pam_getenvlist (session->priv->pam_handle);
440 pam_session_end (PAMSession *session)
442 g_return_if_fail (session != NULL);
444 /* If authenticating cancel first */
445 if (session->priv->authentication_thread)
447 session->priv->stop = TRUE;
448 g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
450 else if (session->priv->in_session)
454 if (!passwd_file && session->priv->pam_handle)
456 result = pam_close_session (session->priv->pam_handle, 0);
457 g_debug ("pam_close_session -> %s", pam_strerror (session->priv->pam_handle, result));
459 pam_setcred (session->priv->pam_handle, PAM_DELETE_CRED);
460 g_debug ("pam_setcred(PAM_DELETE_CRED) -> %s", pam_strerror (session->priv->pam_handle, result));
463 session->priv->in_session = FALSE;
464 g_signal_emit (G_OBJECT (session), signals[ENDED], 0);
469 pam_session_init (PAMSession *session)
471 session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, PAM_SESSION_TYPE, PAMSessionPrivate);
475 pam_session_finalize (GObject *object)
479 self = PAM_SESSION (object);
481 pam_session_end (self);
483 g_free (self->priv->username);
484 if (self->priv->pam_handle)
485 pam_end (self->priv->pam_handle, PAM_SUCCESS);
487 G_OBJECT_CLASS (pam_session_parent_class)->finalize (object);
491 pam_session_class_init (PAMSessionClass *klass)
493 GObjectClass *object_class = G_OBJECT_CLASS (klass);
495 object_class->finalize = pam_session_finalize;
497 g_type_class_add_private (klass, sizeof (PAMSessionPrivate));
499 signals[AUTHENTICATION_STARTED] =
500 g_signal_new ("authentication-started",
501 G_TYPE_FROM_CLASS (klass),
503 G_STRUCT_OFFSET (PAMSessionClass, authentication_started),
505 g_cclosure_marshal_VOID__VOID,
509 g_signal_new ("started",
510 G_TYPE_FROM_CLASS (klass),
512 G_STRUCT_OFFSET (PAMSessionClass, started),
514 g_cclosure_marshal_VOID__VOID,
517 signals[GOT_MESSAGES] =
518 g_signal_new ("got-messages",
519 G_TYPE_FROM_CLASS (klass),
521 G_STRUCT_OFFSET (PAMSessionClass, got_messages),
523 ldm_marshal_VOID__INT_POINTER,
524 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
526 signals[AUTHENTICATION_RESULT] =
527 g_signal_new ("authentication-result",
528 G_TYPE_FROM_CLASS (klass),
530 G_STRUCT_OFFSET (PAMSessionClass, authentication_result),
532 g_cclosure_marshal_VOID__INT,
533 G_TYPE_NONE, 1, G_TYPE_INT);
536 g_signal_new ("ended",
537 G_TYPE_FROM_CLASS (klass),
539 G_STRUCT_OFFSET (PAMSessionClass, ended),
541 g_cclosure_marshal_VOID__VOID,