]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/pam-session.c
33b1209075503e0081061f954c53c90e35548952
[sojka/lightdm.git] / src / pam-session.c
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 <string.h>
13
14 #include "ldm-marshal.h"
15 #include "pam-session.h"
16 #include "user.h"
17
18 enum {
19     AUTHENTICATION_STARTED,
20     STARTED,
21     GOT_MESSAGES,
22     AUTHENTICATION_RESULT,
23     ENDED,
24     LAST_SIGNAL
25 };
26 static guint signals[LAST_SIGNAL] = { 0 };
27
28 struct PAMSessionPrivate
29 {
30     /* Service to authenticate against */
31     gchar *service;
32
33     /* User being authenticated */
34     gchar *username;
35
36     /* Authentication thread */
37     GThread *authentication_thread;
38   
39     /* TRUE if the thread is being intentionally stopped */
40     gboolean stop;
41
42     /* TRUE if the conversation should be cancelled */
43     gboolean cancel;
44
45     /* Messages requested */
46     int num_messages;
47     const struct pam_message **messages;
48     int result;
49
50     /* Queue to feed responses to the authentication thread */
51     GAsyncQueue *authentication_response_queue;
52
53     /* Authentication handle */
54     pam_handle_t *pam_handle;
55   
56     /* TRUE if in a session */
57     gboolean in_session;
58 };
59
60 G_DEFINE_TYPE (PAMSession, pam_session, G_TYPE_OBJECT);
61
62 static gchar *passwd_file = NULL;
63
64 void
65 pam_session_set_use_pam (void)
66 {
67     pam_session_set_use_passwd_file (NULL);
68 }
69
70 void
71 pam_session_set_use_passwd_file (gchar *passwd_file_)
72 {
73     g_free (passwd_file);
74     passwd_file = g_strdup (passwd_file_);
75 }
76
77 PAMSession *
78 pam_session_new (const gchar *service, const gchar *username)
79 {
80     PAMSession *self = g_object_new (PAM_SESSION_TYPE, NULL);
81
82     self->priv->service = g_strdup (service);
83     self->priv->username = g_strdup (username);
84
85     return self;
86 }
87
88 gboolean
89 pam_session_get_in_session (PAMSession *session)
90 {
91     g_return_val_if_fail (session != NULL, FALSE);
92     return session->priv->in_session;
93 }
94
95 void
96 pam_session_authorize (PAMSession *session)
97 {
98     g_return_if_fail (session != NULL);
99
100     session->priv->in_session = TRUE;
101
102     if (!passwd_file)
103     {
104         int result;
105
106         // FIXME:-
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));
111
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));
114     }
115
116     g_signal_emit (G_OBJECT (session), signals[STARTED], 0);
117 }
118
119 static gboolean
120 notify_messages_cb (gpointer data)
121 {
122     PAMSession *session = data;
123
124     g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
125
126     return FALSE;
127 }
128
129 static int
130 pam_conv_cb (int num_msg, const struct pam_message **msg,
131              struct pam_response **resp, void *app_data)
132 {
133     PAMSession *session = app_data;
134     struct pam_response *response;
135
136     /* Notify user */
137     session->priv->num_messages = num_msg;
138     session->priv->messages = msg;
139     g_idle_add (notify_messages_cb, session);
140
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;
145
146     /* Cancelled by user */
147     if (session->priv->stop || session->priv->cancel)
148     {
149         session->priv->cancel = FALSE;
150         return PAM_CONV_ERR;
151     }
152
153     *resp = response;
154
155     return PAM_SUCCESS;
156 }
157
158 static gboolean
159 notify_auth_complete_cb (gpointer data)
160 {
161     PAMSession *session = data;
162     int result;
163
164     result = session->priv->result;
165     session->priv->result = 0;
166
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;
171
172     /* Authentication was cancelled */
173     if (session->priv->stop)
174         pam_session_end (session);
175     else
176         g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, result);
177
178     /* The thread is complete, drop the reference */
179     g_object_unref (session);
180
181     return FALSE;
182 }
183
184 static gpointer
185 authenticate_cb (gpointer data)
186 {
187     PAMSession *session = data;
188     struct pam_conv conversation = { pam_conv_cb, session };
189
190     pam_start (session->priv->service, session->priv->username, &conversation, &session->priv->pam_handle);
191
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));
194   
195     if (session->priv->result == PAM_SUCCESS)
196     {
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));
199
200         if (session->priv->result == PAM_NEW_AUTHTOK_REQD)
201         {
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));
204         }
205     }
206
207     /* Notify user */
208     g_idle_add (notify_auth_complete_cb, session);
209
210     return NULL;
211 }
212
213 static gchar *
214 get_password (const gchar *username)
215 {
216     gchar *data = NULL, **lines, *password = NULL;
217     gint i;
218     GError *error = NULL;
219
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);
223
224     if (!data)
225         return NULL;
226
227     lines = g_strsplit (data, "\n", -1);
228     g_free (data);
229
230     for (i = 0; lines[i] && password == NULL; i++)
231     {
232         gchar *line, **fields;
233
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]);
238         g_strfreev (fields);
239     }
240     g_strfreev (lines);
241
242     return password;
243 }
244
245 static void
246 send_message (PAMSession *session, gint style, const gchar *text)
247 {
248     struct pam_message **messages;
249
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;
256
257     g_signal_emit (G_OBJECT (session), signals[GOT_MESSAGES], 0, session->priv->num_messages, session->priv->messages);
258 }
259
260 gboolean
261 pam_session_start (PAMSession *session, GError **error)
262 {
263     g_return_val_if_fail (session != NULL, FALSE);
264     g_return_val_if_fail (session->priv->authentication_thread == NULL, FALSE);
265
266     g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_STARTED], 0);
267
268     if (passwd_file)
269     {
270         if (session->priv->username == NULL)
271             send_message (session, PAM_PROMPT_ECHO_ON, "login:");
272         else
273         {
274             gchar *password;
275
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);
281             else
282                 send_message (session, PAM_PROMPT_ECHO_OFF, "Password:");
283             g_free (password);
284         }      
285     }
286     else
287     {
288         /* Hold a reference to this object while the thread may access it */
289         g_object_ref (session);
290
291         /* Start thread */
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)
295             return FALSE;
296     }
297
298     return TRUE;
299 }
300
301 const gchar *
302 pam_session_strerror (PAMSession *session, int error)
303 {
304     g_return_val_if_fail (session != NULL, NULL);
305     return pam_strerror (session->priv->pam_handle, error);
306 }
307
308 const gchar *
309 pam_session_get_username (PAMSession *session)
310 {
311     const char *username;
312
313     g_return_val_if_fail (session != NULL, NULL);
314
315     if (session->priv->pam_handle)
316     {
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);
320     }
321
322     return session->priv->username;
323 }
324
325 const struct pam_message **
326 pam_session_get_messages (PAMSession *session)
327 {
328     g_return_val_if_fail (session != NULL, NULL);
329     return session->priv->messages;  
330 }
331
332 gint
333 pam_session_get_num_messages (PAMSession *session)
334 {
335     g_return_val_if_fail (session != NULL, 0);
336     return session->priv->num_messages;
337 }
338
339 void
340 pam_session_respond (PAMSession *session, struct pam_response *response)
341 {
342     g_return_if_fail (session != NULL);
343
344     if (passwd_file)
345     {
346         gchar *password;
347
348         if (session->priv->messages)
349         {
350             int i;
351             struct pam_message **messages = (struct pam_message **) session->priv->messages;
352
353             for (i = 0; i < session->priv->num_messages; i++)
354             {
355                 g_free ((gchar *) messages[i]->msg);
356                 g_free (messages[i]);
357             }
358             g_free (messages);
359             session->priv->messages = NULL;
360             session->priv->num_messages = 0;
361         }
362
363
364         if (session->priv->username == NULL)
365         {
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);
370             else
371                 send_message (session, PAM_PROMPT_ECHO_OFF, "Password:");
372         }
373         else
374         {
375             User *user;
376
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);
381             else
382                 g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_AUTH_ERR);
383
384             if (user)
385                 g_object_unref (user);
386         }
387         g_free (password);
388         g_free (response->resp);
389         g_free (response);
390     }
391     else
392     {
393         g_return_if_fail (session->priv->authentication_thread != NULL);
394         g_async_queue_push (session->priv->authentication_response_queue, response);
395     }
396 }
397
398 void
399 pam_session_cancel (PAMSession *session)
400 {
401     g_return_if_fail (session != NULL);
402
403     if (!passwd_file)
404     {
405         g_signal_emit (G_OBJECT (session), signals[AUTHENTICATION_RESULT], 0, PAM_CONV_ERR);
406     }
407     else
408     {
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));
412     }
413 }
414
415 const gchar *
416 pam_session_getenv (PAMSession *session, const gchar *name)
417 {
418     g_return_val_if_fail (session != NULL, NULL);
419     if (passwd_file)
420         return NULL;
421     else
422         return pam_getenv (session->priv->pam_handle, name);
423 }
424
425 gchar **
426 pam_session_get_envlist (PAMSession *session)
427 {
428     g_return_val_if_fail (session != NULL, NULL);
429     if (passwd_file)
430     {
431         char **env_list = calloc (1, sizeof (gchar *));
432         env_list[0] = NULL;
433         return env_list;
434     }
435     else
436         return pam_getenvlist (session->priv->pam_handle);
437 }
438
439 void
440 pam_session_end (PAMSession *session)
441 {
442     g_return_if_fail (session != NULL);
443
444     /* If authenticating cancel first */
445     if (session->priv->authentication_thread)
446     {
447         session->priv->stop = TRUE;
448         g_async_queue_push (session->priv->authentication_response_queue, GINT_TO_POINTER (-1));
449     }
450     else if (session->priv->in_session)
451     {
452         int result;
453
454         if (!passwd_file && session->priv->pam_handle)
455         {
456             result = pam_close_session (session->priv->pam_handle, 0);
457             g_debug ("pam_close_session -> %s", pam_strerror (session->priv->pam_handle, result));
458
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));
461         }
462
463         session->priv->in_session = FALSE;
464         g_signal_emit (G_OBJECT (session), signals[ENDED], 0);
465     }
466 }
467
468 static void
469 pam_session_init (PAMSession *session)
470 {
471     session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, PAM_SESSION_TYPE, PAMSessionPrivate);
472 }
473
474 static void
475 pam_session_finalize (GObject *object)
476 {
477     PAMSession *self;
478
479     self = PAM_SESSION (object);
480   
481     pam_session_end (self);
482
483     g_free (self->priv->username);
484     if (self->priv->pam_handle)
485         pam_end (self->priv->pam_handle, PAM_SUCCESS);
486   
487     G_OBJECT_CLASS (pam_session_parent_class)->finalize (object);
488 }
489
490 static void
491 pam_session_class_init (PAMSessionClass *klass)
492 {
493     GObjectClass *object_class = G_OBJECT_CLASS (klass);
494
495     object_class->finalize = pam_session_finalize;  
496
497     g_type_class_add_private (klass, sizeof (PAMSessionPrivate));
498
499     signals[AUTHENTICATION_STARTED] =
500         g_signal_new ("authentication-started",
501                       G_TYPE_FROM_CLASS (klass),
502                       G_SIGNAL_RUN_LAST,
503                       G_STRUCT_OFFSET (PAMSessionClass, authentication_started),
504                       NULL, NULL,
505                       g_cclosure_marshal_VOID__VOID,
506                       G_TYPE_NONE, 0);
507
508     signals[STARTED] =
509         g_signal_new ("started",
510                       G_TYPE_FROM_CLASS (klass),
511                       G_SIGNAL_RUN_LAST,
512                       G_STRUCT_OFFSET (PAMSessionClass, started),
513                       NULL, NULL,
514                       g_cclosure_marshal_VOID__VOID,
515                       G_TYPE_NONE, 0);
516
517     signals[GOT_MESSAGES] =
518         g_signal_new ("got-messages",
519                       G_TYPE_FROM_CLASS (klass),
520                       G_SIGNAL_RUN_LAST,
521                       G_STRUCT_OFFSET (PAMSessionClass, got_messages),
522                       NULL, NULL,
523                       ldm_marshal_VOID__INT_POINTER,
524                       G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
525
526     signals[AUTHENTICATION_RESULT] =
527         g_signal_new ("authentication-result",
528                       G_TYPE_FROM_CLASS (klass),
529                       G_SIGNAL_RUN_LAST,
530                       G_STRUCT_OFFSET (PAMSessionClass, authentication_result),
531                       NULL, NULL,
532                       g_cclosure_marshal_VOID__INT,
533                       G_TYPE_NONE, 1, G_TYPE_INT);
534
535     signals[ENDED] =
536         g_signal_new ("ended",
537                       G_TYPE_FROM_CLASS (klass),
538                       G_SIGNAL_RUN_LAST,
539                       G_STRUCT_OFFSET (PAMSessionClass, ended),
540                       NULL, NULL,
541                       g_cclosure_marshal_VOID__VOID,
542                       G_TYPE_NONE, 0);
543 }