~ubuntu-branches/ubuntu/precise/lightdm/precise-proposed

« back to all changes in this revision

Viewing changes to src/pam-session.c

  • Committer: Package Import Robot
  • Author(s): Michael Terry
  • Date: 2012-03-02 15:03:26 UTC
  • mfrom: (1.1.22)
  • Revision ID: package-import@ubuntu.com-20120302150326-4ac7hlxzjxywf27a
Tags: 1.1.4.is.1.1.3-0ubuntu1
Revert to 1.1.3 until we can figure out proper solution to bug 944736

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2010-2011 Robert Ancell.
 
3
 * Author: Robert Ancell <robert.ancell@canonical.com>
 
4
 * 
 
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
 
9
 * license.
 
10
 */
 
11
 
 
12
#include <stdlib.h>
 
13
#include <string.h>
 
14
 
 
15
#include "ldm-marshal.h"
 
16
#include "pam-session.h"
 
17
 
 
18
enum {
 
19
    AUTHENTICATION_STARTED,
 
20
    GOT_MESSAGES,
 
21
    AUTHENTICATION_RESULT,
 
22
    LAST_SIGNAL
 
23
};
 
24
static guint signals[LAST_SIGNAL] = { 0 };
 
25
 
 
26
struct PAMSessionPrivate
 
27
{
 
28
    /* Service to authenticate against */
 
29
    gchar *service;
 
30
 
 
31
    /* User being authenticated */
 
32
    User *user;
 
33
 
 
34
    /* TRUE if can handle provide interaction with PAM */
 
35
    gboolean interactive;
 
36
 
 
37
    /* Authentication thread */
 
38
    GThread *authentication_thread;
 
39
  
 
40
    /* TRUE if the thread is being intentionally stopped */
 
41
    gboolean stop_thread;
 
42
 
 
43
    /* Messages requested */
 
44
    int num_messages;
 
45
    const struct pam_message **messages;
 
46
    int authentication_result;
 
47
 
 
48
    /* Queue to feed responses to the authentication thread */
 
49
    GAsyncQueue *authentication_response_queue;
 
50
 
 
51
    /* Authentication handle */
 
52
    pam_handle_t *pam_handle;
 
53
  
 
54
    /* TRUE if in an authentication */
 
55
    gboolean in_authentication;
 
56
 
 
57
    /* TRUE if is authenticated */
 
58
    gboolean is_authenticated;
 
59
 
 
60
    /* TRUE if in a session */
 
61
    gboolean in_session;
 
62
};
 
63
 
 
64
G_DEFINE_TYPE (PAMSession, pam_session, G_TYPE_OBJECT);
 
65
 
 
66
static int pam_conv_cb (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *app_data);
 
67
 
 
68
PAMSession *
 
69
pam_session_new (const gchar *service, const gchar *username)
 
70
{
 
71
    PAMSession *self = g_object_new (PAM_SESSION_TYPE, NULL);
 
72
    struct pam_conv conversation = { pam_conv_cb, self };
 
73
    int result;
 
74
 
 
75
    self->priv->service = g_strdup (service);
 
76
 
 
77
    result = pam_start (self->priv->service, username, &conversation, &self->priv->pam_handle);
 
78
    g_debug ("pam_start(\"%s\", \"%s\") -> (%p, %d)",
 
79
             self->priv->service,
 
80
             username,
 
81
             self->priv->pam_handle,
 
82
             result);
 
83
 
 
84
    return self;
 
85
}
 
86
 
 
87
void
 
88
pam_session_set_interactive (PAMSession *session, gboolean interactive)
 
89
{
 
90
    g_return_if_fail (session != NULL);
 
91
    session->priv->interactive = interactive;
 
92
}
 
93
 
 
94
gboolean
 
95
pam_session_get_interactive (PAMSession *session)
 
96
{
 
97
    g_return_val_if_fail (session != NULL, FALSE);
 
98
    return session->priv->interactive;
 
99
}
 
100
 
 
101
gboolean
 
102
pam_session_get_is_authenticated (PAMSession *session)
 
103
{
 
104
    g_return_val_if_fail (session != NULL, FALSE);
 
105
    return session->priv->is_authenticated;
 
106
}
 
107
 
 
108
gboolean
 
109
pam_session_set_item (PAMSession *session, int item_type, const gchar *value)
 
110
{
 
111
    int result = PAM_SUCCESS;
 
112
 
 
113
    g_return_val_if_fail (session != NULL, FALSE);
 
114
    g_return_val_if_fail (value != NULL, FALSE);
 
115
 
 
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,
 
119
             item_type,
 
120
             value,
 
121
             result,
 
122
             pam_strerror (session->priv->pam_handle, result));
 
123
 
 
124
    return result == PAM_SUCCESS;
 
125
}
 
126
 
 
127
gboolean
 
128
pam_session_open (PAMSession *session)
 
129
{
 
130
    int result = PAM_SUCCESS;
 
131
 
 
132
    g_return_val_if_fail (session != NULL, FALSE);
 
133
 
 
134
    session->priv->in_session = TRUE;
 
135
 
 
136
    if (getuid () == 0)
 
137
    {
 
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,
 
141
                 result,
 
142
                 pam_strerror (session->priv->pam_handle, result));
 
143
    }
 
144
 
 
145
    return result == PAM_SUCCESS;
 
146
}
 
147
 
 
148
gboolean
 
149
pam_session_setup (PAMSession *session)
 
150
{
 
151
    int result;
 
152
 
 
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,
 
156
             result,
 
157
             pam_strerror (session->priv->pam_handle, result));
 
158
 
 
159
    return result == PAM_SUCCESS;
 
160
}
 
161
 
 
162
gboolean
 
163
pam_session_get_in_session (PAMSession *session)
 
164
{
 
165
    g_return_val_if_fail (session != NULL, FALSE);
 
166
    return session->priv->in_session;
 
167
}
 
168
 
 
169
static gboolean
 
170
notify_messages_cb (gpointer data)
 
171
{
 
172
    PAMSession *session = data;
 
173
 
 
174
    g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
 
175
 
 
176
    return FALSE;
 
177
}
 
178
 
 
179
static int
 
180
pam_conv_cb (int num_msg, const struct pam_message **msg,
 
181
             struct pam_response **resp, void *app_data)
 
182
{
 
183
    PAMSession *session = app_data;
 
184
    struct pam_response *response;
 
185
 
 
186
    /* For some reason after cancelling we still end up here so check for stop as well */
 
187
    if (session->priv->stop_thread)
 
188
        return PAM_CONV_ERR;
 
189
 
 
190
    /* If not interactive then fail the authentication */
 
191
    if (!session->priv->interactive)
 
192
        return PAM_CONV_ERR;
 
193
 
 
194
    /* Notify user */
 
195
    session->priv->num_messages = num_msg;
 
196
    session->priv->messages = msg;
 
197
    g_idle_add (notify_messages_cb, session);
 
198
 
 
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;
 
203
 
 
204
    /* Cancelled by user */
 
205
    if (session->priv->stop_thread)
 
206
        return PAM_CONV_ERR;
 
207
 
 
208
    *resp = response;
 
209
 
 
210
    return PAM_SUCCESS;
 
211
}
 
212
 
 
213
static void
 
214
report_result (PAMSession *session, int result)
 
215
{
 
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);
 
219
}
 
220
 
 
221
static gboolean
 
222
notify_auth_complete_cb (gpointer data)
 
223
{
 
224
    PAMSession *session = data;
 
225
    int result;
 
226
 
 
227
    result = session->priv->authentication_result;
 
228
    session->priv->authentication_result = 0;
 
229
 
 
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;
 
234
 
 
235
    report_result (session, result);
 
236
 
 
237
    /* Authentication was cancelled */
 
238
    if (session->priv->stop_thread)
 
239
        pam_session_cancel (session);
 
240
 
 
241
    /* The thread is complete, drop the reference */
 
242
    g_object_unref (session);
 
243
 
 
244
    return FALSE;
 
245
}
 
246
 
 
247
static gpointer
 
248
authenticate_cb (gpointer data)
 
249
{
 
250
    PAMSession *session = data;
 
251
 
 
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));
 
257
 
 
258
    if (session->priv->authentication_result == PAM_SUCCESS)
 
259
    {
 
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));
 
265
 
 
266
        if (session->priv->authentication_result == PAM_NEW_AUTHTOK_REQD)
 
267
        {
 
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));
 
273
        }
 
274
    }
 
275
 
 
276
    /* Notify user */
 
277
    g_idle_add (notify_auth_complete_cb, session);
 
278
 
 
279
    return NULL;
 
280
}
 
281
 
 
282
gboolean
 
283
pam_session_authenticate (PAMSession *session, GError **error)
 
284
{
 
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);
 
288
 
 
289
    session->priv->in_authentication = TRUE;
 
290
    g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_STARTED], 0);
 
291
 
 
292
    /* Hold a reference to this object while the thread may access it */
 
293
    g_object_ref (session);
 
294
 
 
295
    /* Start thread */
 
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)
 
299
        return FALSE;
 
300
 
 
301
    return TRUE;
 
302
}
 
303
 
 
304
const gchar *
 
305
pam_session_strerror (PAMSession *session, int error)
 
306
{
 
307
    g_return_val_if_fail (session != NULL, NULL);
 
308
    return pam_strerror (session->priv->pam_handle, error);
 
309
}
 
310
 
 
311
const gchar *
 
312
pam_session_get_username (PAMSession *session)
 
313
{
 
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);
 
317
    return username;
 
318
}
 
319
 
 
320
User *
 
321
pam_session_get_user (PAMSession *session)
 
322
{
 
323
    g_return_val_if_fail (session != NULL, NULL);
 
324
 
 
325
    if (!session->priv->user && pam_session_get_username (session))
 
326
        session->priv->user = accounts_get_user_by_name (pam_session_get_username (session));
 
327
 
 
328
    return session->priv->user;
 
329
}
 
330
 
 
331
const struct pam_message **
 
332
pam_session_get_messages (PAMSession *session)
 
333
{
 
334
    g_return_val_if_fail (session != NULL, NULL);
 
335
    return session->priv->messages;  
 
336
}
 
337
 
 
338
gint
 
339
pam_session_get_num_messages (PAMSession *session)
 
340
{
 
341
    g_return_val_if_fail (session != NULL, 0);
 
342
    return session->priv->num_messages;
 
343
}
 
344
 
 
345
void
 
346
pam_session_respond (PAMSession *session, struct pam_response *response)
 
347
{
 
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);
 
351
}
 
352
 
 
353
void
 
354
pam_session_cancel (PAMSession *session)
 
355
{
 
356
    g_return_if_fail (session != NULL);
 
357
 
 
358
    /* If authenticating cancel first */
 
359
    if (session->priv->authentication_thread)
 
360
    {
 
361
        session->priv->stop_thread = TRUE;
 
362
        g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
 
363
    }
 
364
}
 
365
 
 
366
const gchar *
 
367
pam_session_getenv (PAMSession *session, const gchar *name)
 
368
{
 
369
    g_return_val_if_fail (session != NULL, NULL);
 
370
    return pam_getenv (session->priv->pam_handle, name);
 
371
}
 
372
 
 
373
gchar **
 
374
pam_session_get_envlist (PAMSession *session)
 
375
{
 
376
    g_return_val_if_fail (session != NULL, NULL);
 
377
    return pam_getenvlist (session->priv->pam_handle);
 
378
}
 
379
 
 
380
void
 
381
pam_session_close (PAMSession *session)
 
382
{
 
383
    int result;
 
384
 
 
385
    g_return_if_fail (session != NULL);
 
386
 
 
387
    session->priv->in_session = FALSE;
 
388
 
 
389
    if (getuid () == 0)
 
390
    {
 
391
        g_return_if_fail (session->priv->pam_handle != NULL);
 
392
 
 
393
        result = pam_close_session (session->priv->pam_handle, 0);
 
394
        g_debug ("pam_close_session(%p) -> %d (%s)",
 
395
                 session->priv->pam_handle,
 
396
                 result,
 
397
                 pam_strerror (session->priv->pam_handle, result));
 
398
 
 
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,
 
402
                 result,
 
403
                 pam_strerror (session->priv->pam_handle, result));
 
404
 
 
405
        result = pam_end (session->priv->pam_handle, PAM_SUCCESS);
 
406
        g_debug ("pam_end(%p) -> %d",
 
407
                 session->priv->pam_handle,
 
408
                 result);
 
409
 
 
410
        session->priv->pam_handle = NULL;
 
411
    }
 
412
}
 
413
 
 
414
static void
 
415
pam_session_init (PAMSession *session)
 
416
{
 
417
    session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, PAM_SESSION_TYPE, PAMSessionPrivate);
 
418
    session->priv->interactive = TRUE;
 
419
}
 
420
 
 
421
static void
 
422
pam_session_finalize (GObject *object)
 
423
{
 
424
    PAMSession *self;
 
425
 
 
426
    self = PAM_SESSION (object);
 
427
 
 
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);
 
433
 
 
434
    G_OBJECT_CLASS (pam_session_parent_class)->finalize (object);
 
435
}
 
436
 
 
437
static void
 
438
pam_session_class_init (PAMSessionClass *klass)
 
439
{
 
440
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
441
 
 
442
    object_class->finalize = pam_session_finalize;  
 
443
 
 
444
    g_type_class_add_private (klass, sizeof (PAMSessionPrivate));
 
445
 
 
446
    signals[AUTHENTICATION_STARTED] =
 
447
        g_signal_new ("authentication-started",
 
448
                      G_TYPE_FROM_CLASS (klass),
 
449
                      G_SIGNAL_RUN_LAST,
 
450
                      G_STRUCT_OFFSET (PAMSessionClass, authentication_started),
 
451
                      NULL, NULL,
 
452
                      g_cclosure_marshal_VOID__VOID,
 
453
                      G_TYPE_NONE, 0);
 
454
 
 
455
    signals[GOT_MESSAGES] =
 
456
        g_signal_new ("got-messages",
 
457
                      G_TYPE_FROM_CLASS (klass),
 
458
                      G_SIGNAL_RUN_LAST,
 
459
                      G_STRUCT_OFFSET (PAMSessionClass, got_messages),
 
460
                      NULL, NULL,
 
461
                      ldm_marshal_VOID__INT_POINTER,
 
462
                      G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
 
463
 
 
464
    signals[AUTHENTICATION_RESULT] =
 
465
        g_signal_new ("authentication-result",
 
466
                      G_TYPE_FROM_CLASS (klass),
 
467
                      G_SIGNAL_RUN_LAST,
 
468
                      G_STRUCT_OFFSET (PAMSessionClass, authentication_result),
 
469
                      NULL, NULL,
 
470
                      g_cclosure_marshal_VOID__INT,
 
471
                      G_TYPE_NONE, 1, G_TYPE_INT);
 
472
}