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
15
#include "ldm-marshal.h"
16
#include "pam-session.h"
19
AUTHENTICATION_STARTED,
21
AUTHENTICATION_RESULT,
24
static guint signals[LAST_SIGNAL] = { 0 };
26
struct PAMSessionPrivate
28
/* Service to authenticate against */
31
/* User being authenticated */
34
/* TRUE if can handle provide interaction with PAM */
37
/* Authentication thread */
38
GThread *authentication_thread;
40
/* TRUE if the thread is being intentionally stopped */
43
/* Messages requested */
45
const struct pam_message **messages;
46
int authentication_result;
48
/* Queue to feed responses to the authentication thread */
49
GAsyncQueue *authentication_response_queue;
51
/* Authentication handle */
52
pam_handle_t *pam_handle;
54
/* TRUE if in an authentication */
55
gboolean in_authentication;
57
/* TRUE if is authenticated */
58
gboolean is_authenticated;
60
/* TRUE if in a session */
64
G_DEFINE_TYPE (PAMSession, pam_session, G_TYPE_OBJECT);
66
static int pam_conv_cb (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *app_data);
69
pam_session_new (const gchar *service, const gchar *username)
71
PAMSession *self = g_object_new (PAM_SESSION_TYPE, NULL);
72
struct pam_conv conversation = { pam_conv_cb, self };
75
self->priv->service = g_strdup (service);
77
result = pam_start (self->priv->service, username, &conversation, &self->priv->pam_handle);
78
g_debug ("pam_start(\"%s\", \"%s\") -> (%p, %d)",
81
self->priv->pam_handle,
88
pam_session_set_interactive (PAMSession *session, gboolean interactive)
90
g_return_if_fail (session != NULL);
91
session->priv->interactive = interactive;
95
pam_session_get_interactive (PAMSession *session)
97
g_return_val_if_fail (session != NULL, FALSE);
98
return session->priv->interactive;
102
pam_session_get_is_authenticated (PAMSession *session)
104
g_return_val_if_fail (session != NULL, FALSE);
105
return session->priv->is_authenticated;
109
pam_session_set_item (PAMSession *session, int item_type, const gchar *value)
111
int result = PAM_SUCCESS;
113
g_return_val_if_fail (session != NULL, FALSE);
114
g_return_val_if_fail (value != NULL, FALSE);
116
result = pam_set_item (session->priv->pam_handle, item_type, value);
117
g_debug ("pam_set_item(%p, %d, \"%s\") -> %d (%s)",
118
session->priv->pam_handle,
122
pam_strerror (session->priv->pam_handle, result));
124
return result == PAM_SUCCESS;
128
pam_session_open (PAMSession *session)
130
int result = PAM_SUCCESS;
132
g_return_val_if_fail (session != NULL, FALSE);
134
session->priv->in_session = TRUE;
138
result = pam_open_session (session->priv->pam_handle, 0);
139
g_debug ("pam_open_session(%p, 0) -> %d (%s)",
140
session->priv->pam_handle,
142
pam_strerror (session->priv->pam_handle, result));
145
return result == PAM_SUCCESS;
149
pam_session_setup (PAMSession *session)
153
result = pam_setcred (session->priv->pam_handle, PAM_ESTABLISH_CRED);
154
g_debug ("pam_setcred(%p, PAM_ESTABLISH_CRED) -> %d (%s)",
155
session->priv->pam_handle,
157
pam_strerror (session->priv->pam_handle, result));
159
return result == PAM_SUCCESS;
163
pam_session_get_in_session (PAMSession *session)
165
g_return_val_if_fail (session != NULL, FALSE);
166
return session->priv->in_session;
170
notify_messages_cb (gpointer data)
172
PAMSession *session = data;
174
g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
180
pam_conv_cb (int num_msg, const struct pam_message **msg,
181
struct pam_response **resp, void *app_data)
183
PAMSession *session = app_data;
184
struct pam_response *response;
186
/* For some reason after cancelling we still end up here so check for stop as well */
187
if (session->priv->stop_thread)
190
/* If not interactive then fail the authentication */
191
if (!session->priv->interactive)
195
session->priv->num_messages = num_msg;
196
session->priv->messages = msg;
197
g_idle_add (notify_messages_cb, session);
199
/* Wait for response */
200
response = g_async_queue_pop (session->priv->authentication_response_queue);
201
session->priv->num_messages = 0;
202
session->priv->messages = NULL;
204
/* Cancelled by user */
205
if (session->priv->stop_thread)
214
report_result (PAMSession *session, int result)
216
session->priv->in_authentication = FALSE;
217
session->priv->is_authenticated = result == PAM_SUCCESS;
218
g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, result);
222
notify_auth_complete_cb (gpointer data)
224
PAMSession *session = data;
227
result = session->priv->authentication_result;
228
session->priv->authentication_result = 0;
230
g_thread_join (session->priv->authentication_thread);
231
session->priv->authentication_thread = NULL;
232
g_async_queue_unref (session->priv->authentication_response_queue);
233
session->priv->authentication_response_queue = NULL;
235
report_result (session, result);
237
/* Authentication was cancelled */
238
if (session->priv->stop_thread)
239
pam_session_cancel (session);
241
/* The thread is complete, drop the reference */
242
g_object_unref (session);
248
authenticate_cb (gpointer data)
250
PAMSession *session = data;
252
session->priv->authentication_result = pam_authenticate (session->priv->pam_handle, 0);
253
g_debug ("pam_authenticate(%p, 0) -> %d (%s)",
254
session->priv->pam_handle,
255
session->priv->authentication_result,
256
pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
258
if (session->priv->authentication_result == PAM_SUCCESS)
260
session->priv->authentication_result = pam_acct_mgmt (session->priv->pam_handle, 0);
261
g_debug ("pam_acct_mgmt(%p, 0) -> %d (%s)",
262
session->priv->pam_handle,
263
session->priv->authentication_result,
264
pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
266
if (session->priv->authentication_result == PAM_NEW_AUTHTOK_REQD)
268
session->priv->authentication_result = pam_chauthtok (session->priv->pam_handle, PAM_CHANGE_EXPIRED_AUTHTOK);
269
g_debug ("pam_chauthtok(%p, PAM_CHANGE_EXPIRED_AUTHTOK) -> %d (%s)",
270
session->priv->pam_handle,
271
session->priv->authentication_result,
272
pam_strerror (session->priv->pam_handle, session->priv->authentication_result));
277
g_idle_add (notify_auth_complete_cb, session);
283
pam_session_authenticate (PAMSession *session, GError **error)
285
g_return_val_if_fail (session != NULL, FALSE);
286
g_return_val_if_fail (session->priv->in_authentication == FALSE, FALSE);
287
g_return_val_if_fail (session->priv->is_authenticated == FALSE, FALSE);
289
session->priv->in_authentication = TRUE;
290
g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_STARTED], 0);
292
/* Hold a reference to this object while the thread may access it */
293
g_object_ref (session);
296
session->priv->authentication_response_queue = g_async_queue_new ();
297
session->priv->authentication_thread = g_thread_create (authenticate_cb, session, TRUE, error);
298
if (!session->priv->authentication_thread)
305
pam_session_strerror (PAMSession *session, int error)
307
g_return_val_if_fail (session != NULL, NULL);
308
return pam_strerror (session->priv->pam_handle, error);
312
pam_session_get_username (PAMSession *session)
314
const char *username;
315
g_return_val_if_fail (session != NULL, NULL);
316
pam_get_item (session->priv->pam_handle, PAM_USER, (const void **) &username);
321
pam_session_get_user (PAMSession *session)
323
g_return_val_if_fail (session != NULL, NULL);
325
if (!session->priv->user && pam_session_get_username (session))
326
session->priv->user = accounts_get_user_by_name (pam_session_get_username (session));
328
return session->priv->user;
331
const struct pam_message **
332
pam_session_get_messages (PAMSession *session)
334
g_return_val_if_fail (session != NULL, NULL);
335
return session->priv->messages;
339
pam_session_get_num_messages (PAMSession *session)
341
g_return_val_if_fail (session != NULL, 0);
342
return session->priv->num_messages;
346
pam_session_respond (PAMSession *session, struct pam_response *response)
348
g_return_if_fail (session != NULL);
349
g_return_if_fail (session->priv->authentication_thread != NULL);
350
g_async_queue_push (session->priv->authentication_response_queue, response);
354
pam_session_cancel (PAMSession *session)
356
g_return_if_fail (session != NULL);
358
/* If authenticating cancel first */
359
if (session->priv->authentication_thread)
361
session->priv->stop_thread = TRUE;
362
g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
367
pam_session_getenv (PAMSession *session, const gchar *name)
369
g_return_val_if_fail (session != NULL, NULL);
370
return pam_getenv (session->priv->pam_handle, name);
374
pam_session_get_envlist (PAMSession *session)
376
g_return_val_if_fail (session != NULL, NULL);
377
return pam_getenvlist (session->priv->pam_handle);
381
pam_session_close (PAMSession *session)
385
g_return_if_fail (session != NULL);
387
session->priv->in_session = FALSE;
391
g_return_if_fail (session->priv->pam_handle != NULL);
393
result = pam_close_session (session->priv->pam_handle, 0);
394
g_debug ("pam_close_session(%p) -> %d (%s)",
395
session->priv->pam_handle,
397
pam_strerror (session->priv->pam_handle, result));
399
result = pam_setcred (session->priv->pam_handle, PAM_DELETE_CRED);
400
g_debug ("pam_setcred(%p, PAM_DELETE_CRED) -> %d (%s)",
401
session->priv->pam_handle,
403
pam_strerror (session->priv->pam_handle, result));
405
result = pam_end (session->priv->pam_handle, PAM_SUCCESS);
406
g_debug ("pam_end(%p) -> %d",
407
session->priv->pam_handle,
410
session->priv->pam_handle = NULL;
415
pam_session_init (PAMSession *session)
417
session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, PAM_SESSION_TYPE, PAMSessionPrivate);
418
session->priv->interactive = TRUE;
422
pam_session_finalize (GObject *object)
426
self = PAM_SESSION (object);
428
g_free (self->priv->service);
429
if (self->priv->user)
430
g_object_unref (self->priv->user);
431
if (self->priv->pam_handle)
432
pam_end (self->priv->pam_handle, PAM_SUCCESS);
434
G_OBJECT_CLASS (pam_session_parent_class)->finalize (object);
438
pam_session_class_init (PAMSessionClass *klass)
440
GObjectClass *object_class = G_OBJECT_CLASS (klass);
442
object_class->finalize = pam_session_finalize;
444
g_type_class_add_private (klass, sizeof (PAMSessionPrivate));
446
signals[AUTHENTICATION_STARTED] =
447
g_signal_new ("authentication-started",
448
G_TYPE_FROM_CLASS (klass),
450
G_STRUCT_OFFSET (PAMSessionClass, authentication_started),
452
g_cclosure_marshal_VOID__VOID,
455
signals[GOT_MESSAGES] =
456
g_signal_new ("got-messages",
457
G_TYPE_FROM_CLASS (klass),
459
G_STRUCT_OFFSET (PAMSessionClass, got_messages),
461
ldm_marshal_VOID__INT_POINTER,
462
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
464
signals[AUTHENTICATION_RESULT] =
465
g_signal_new ("authentication-result",
466
G_TYPE_FROM_CLASS (klass),
468
G_STRUCT_OFFSET (PAMSessionClass, authentication_result),
470
g_cclosure_marshal_VOID__INT,
471
G_TYPE_NONE, 1, G_TYPE_INT);