Line data Source code
1 : /*
2 : Copyright (C) 2014 Red Hat Inc
3 :
4 : The Gnome Keyring Library is free software; you can redistribute it and/or
5 : modify it under the terms of the GNU Library General Public License as
6 : published by the Free Software Foundation; either version 2 of the
7 : License, or (at your option) any later version.
8 :
9 : The Gnome Keyring Library is distributed in the hope that it will be useful,
10 : but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : Library General Public License for more details.
13 :
14 : You should have received a copy of the GNU Library General Public
15 : License along with the Gnome Library; see the file COPYING.LIB. If not,
16 : <http://www.gnu.org/licenses/>.
17 :
18 : Author: Stef Walter <stefw@gnome.org>
19 : */
20 :
21 : #include "config.h"
22 :
23 : #include "daemon/control/gkd-control.h"
24 : #include "daemon/gkd-test.h"
25 :
26 : #include "egg/egg-testing.h"
27 : #include "egg/egg-secure-memory.h"
28 :
29 : #include <security/pam_appl.h>
30 :
31 : #include <glib.h>
32 : #include <glib/gstdio.h>
33 : #include <gio/gio.h>
34 :
35 : #include <sys/wait.h>
36 : #include <errno.h>
37 : #include <fcntl.h>
38 :
39 0 : EGG_SECURE_DEFINE_GLIB_GLOBALS ();
40 :
41 : typedef struct {
42 : GTestDBus *dbus;
43 : GDBusConnection *connection;
44 : gchar *directory;
45 : GPid pid;
46 : gboolean skipping;
47 : pam_handle_t *ph;
48 : struct pam_conv conv;
49 : const gchar *password;
50 : const gchar *new_password;
51 : } Test;
52 :
53 : const gchar *PASS_ENVIRON[] = {
54 : "DBUS_SESSION_ADDRESS",
55 : "XDG_RUNTIME_DIR",
56 : "XDG_DATA_HOME",
57 : NULL
58 : };
59 :
60 : static void
61 10 : skip_test (Test *test,
62 : const gchar *reason)
63 : {
64 10 : test->skipping = TRUE;
65 : #if GLIB_CHECK_VERSION(2, 40, 0)
66 10 : g_test_skip (reason);
67 : #else
68 : if (g_test_verbose ())
69 : g_print ("GTest: skipping: %s\n", reason);
70 : else
71 : g_print ("SKIP: %s ", reason);
72 : #endif
73 10 : }
74 :
75 : static int
76 0 : conv_func (int n,
77 : const struct pam_message **msg,
78 : struct pam_response **resp,
79 : void *arg)
80 : {
81 : struct pam_response *aresp;
82 0 : Test *test = arg;
83 : int i;
84 :
85 0 : g_assert (n > 0 && n < PAM_MAX_NUM_MSG);
86 0 : aresp = g_new0(struct pam_response, n);
87 :
88 0 : for (i = 0; i < n; ++i) {
89 0 : aresp[i].resp_retcode = 0;
90 0 : aresp[i].resp = NULL;
91 0 : switch (msg[i]->msg_style) {
92 0 : case PAM_PROMPT_ECHO_OFF:
93 0 : if (test->password) {
94 0 : aresp[i].resp = strdup (test->password);
95 0 : test->password = NULL;
96 0 : } else if (test->new_password) {
97 0 : aresp[i].resp = strdup (test->new_password);
98 0 : test->new_password = NULL;
99 : }
100 0 : g_assert (aresp[i].resp != NULL);
101 0 : break;
102 0 : case PAM_PROMPT_ECHO_ON:
103 0 : aresp[i].resp = strdup (test->password);
104 0 : g_assert (aresp[i].resp != NULL);
105 0 : break;
106 0 : case PAM_ERROR_MSG:
107 0 : fputs(msg[i]->msg, stderr);
108 0 : if (strlen(msg[i]->msg) > 0 &&
109 0 : msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
110 0 : fputc('\n', stderr);
111 0 : break;
112 0 : case PAM_TEXT_INFO:
113 0 : fprintf(stdout, "# %s", msg[i]->msg);
114 0 : if (strlen(msg[i]->msg) > 0 &&
115 0 : msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
116 0 : fputc('\n', stdout);
117 0 : break;
118 0 : default:
119 0 : return PAM_CONV_ERR;
120 : }
121 : }
122 0 : *resp = aresp;
123 0 : return PAM_SUCCESS;
124 : }
125 :
126 : static void
127 10 : setup (Test *test,
128 : gconstpointer user_data)
129 : {
130 10 : const gchar *pam_conf = user_data;
131 10 : GError *error = NULL;
132 : gchar *contents;
133 : gboolean found;
134 : gchar *filename;
135 : gchar *env;
136 : int ret;
137 :
138 : /* First check if we have the right pam config */
139 10 : filename = g_build_filename (SYSCONFDIR, "pam.d", pam_conf, NULL);
140 10 : g_file_get_contents (filename, &contents, NULL, &error);
141 10 : g_free (filename);
142 :
143 10 : if (error == NULL) {
144 0 : found = (strstr (contents, BUILDDIR) &&
145 0 : strstr (contents, "pam_gnome_keyring.so"));
146 0 : g_free (contents);
147 0 : if (!found) {
148 0 : skip_test (test, "test pam config contents invalid");
149 10 : return;
150 : }
151 10 : } else if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
152 10 : g_error_free (error);
153 10 : skip_test (test, "missing test pam config");
154 10 : return;
155 : }
156 :
157 0 : g_assert_no_error (error);
158 :
159 0 : test->directory = egg_tests_create_scratch_directory (NULL, NULL);
160 :
161 0 : g_setenv ("XDG_RUNTIME_DIR", test->directory, TRUE);
162 :
163 0 : test->dbus = g_test_dbus_new (G_TEST_DBUS_NONE);
164 0 : g_test_dbus_up (test->dbus);
165 :
166 0 : test->conv.conv = conv_func;
167 0 : test->conv.appdata_ptr = test;
168 0 : ret = pam_start (pam_conf, g_get_user_name (), &test->conv, &test->ph);
169 0 : g_assert_cmpint (ret, ==, PAM_SUCCESS);
170 :
171 0 : g_unsetenv ("GNOME_KEYRING_CONTROL");
172 :
173 0 : g_assert_cmpint (pam_putenv (test->ph, "GSETTINGS_SCHEMA_DIR=" BUILDDIR "/schema"), ==, PAM_SUCCESS);
174 0 : g_assert_cmpint (pam_putenv (test->ph, "G_DEBUG=fatal-warnings,fatal-criticals"), ==, PAM_SUCCESS);
175 :
176 0 : env = g_strdup_printf ("GNOME_KEYRING_TEST_PATH=%s", test->directory);
177 0 : g_assert_cmpint (pam_putenv (test->ph, env), ==, PAM_SUCCESS);
178 0 : g_free (env);
179 :
180 0 : env = g_strdup_printf ("DBUS_SESSION_BUS_ADDRESS=%s", g_test_dbus_get_bus_address (test->dbus));
181 0 : g_assert_cmpint (pam_putenv (test->ph, env), ==, PAM_SUCCESS);
182 0 : g_free (env);
183 :
184 0 : test->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
185 0 : g_assert_no_error (error);
186 : }
187 :
188 : static void
189 10 : teardown (Test *test,
190 : gconstpointer unused)
191 : {
192 10 : if (test->skipping)
193 10 : return;
194 :
195 0 : g_object_unref (test->connection);
196 :
197 0 : pam_end (test->ph, PAM_SUCCESS);
198 :
199 0 : if (test->pid) {
200 0 : if (waitpid (test->pid, NULL, WNOHANG) != test->pid) {
201 0 : kill (test->pid, SIGTERM);
202 0 : g_assert_cmpint (waitpid (test->pid, NULL, 0), ==, test->pid);
203 : }
204 0 : g_spawn_close_pid (test->pid);
205 : }
206 :
207 0 : egg_tests_remove_scratch_directory (test->directory);
208 0 : g_free (test->directory);
209 :
210 0 : g_test_dbus_down (test->dbus);
211 0 : g_object_unref (test->dbus);
212 : }
213 :
214 : static gboolean
215 0 : check_if_login_keyring_locked (Test *test)
216 : {
217 : GVariant *retval;
218 0 : GError *error = NULL;
219 : GVariant *prop;
220 : gboolean ret;
221 :
222 0 : retval = g_dbus_connection_call_sync (test->connection,
223 : "org.gnome.keyring",
224 : "/org/freedesktop/secrets/collection/login",
225 : "org.freedesktop.DBus.Properties",
226 : "Get",
227 : g_variant_new ("(ss)",
228 : "org.freedesktop.Secret.Collection", "Locked"),
229 : G_VARIANT_TYPE ("(v)"),
230 : G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
231 : NULL, &error);
232 0 : g_assert_no_error (error);
233 0 : g_variant_get (retval, "(@v)", &prop);
234 0 : ret = g_variant_get_boolean (g_variant_get_variant (prop));
235 0 : g_variant_unref (retval);
236 :
237 0 : return ret;
238 : }
239 :
240 : static gboolean
241 0 : check_if_login_item_1_exists (Test *test)
242 : {
243 : GVariant *retval;
244 0 : GError *error = NULL;
245 : gchar *remote;
246 :
247 0 : retval = g_dbus_connection_call_sync (test->connection,
248 : "org.gnome.keyring",
249 : "/org/freedesktop/secrets/collection/login/1",
250 : "org.freedesktop.DBus.Properties",
251 : "Get",
252 : g_variant_new ("(ss)",
253 : "org.freedesktop.Secret.Item", "Locked"),
254 : G_VARIANT_TYPE ("(v)"),
255 : G_DBUS_CALL_FLAGS_NO_AUTO_START, -1,
256 : NULL, &error);
257 :
258 0 : if (error) {
259 0 : remote = g_dbus_error_get_remote_error (error);
260 0 : if (!remote || !g_str_equal (remote, "org.freedesktop.Secret.Error.NoSuchObject"))
261 0 : g_assert_no_error (error);
262 0 : g_error_free (error);
263 0 : return FALSE;
264 : }
265 :
266 0 : g_variant_unref (retval);
267 0 : return TRUE;
268 : }
269 :
270 : static void
271 2 : test_starts_creates (Test *test,
272 : gconstpointer user_data)
273 : {
274 2 : const char *pam_conf = user_data;
275 : gboolean start_in_session;
276 : const gchar *control;
277 : gchar *login_keyring;
278 :
279 2 : if (test->skipping)
280 2 : return;
281 :
282 : /* We're testing that we create the directory appropriately */
283 0 : g_unsetenv ("XDG_RUNTIME_DIR");
284 :
285 0 : start_in_session = (strstr (pam_conf, "session") != NULL);
286 :
287 0 : login_keyring = g_build_filename (test->directory, "login.keyring", NULL);
288 0 : g_assert (!g_file_test (login_keyring, G_FILE_TEST_EXISTS));
289 :
290 0 : test->password = "booo";
291 0 : g_assert_cmpint (pam_authenticate (test->ph, 0), ==, PAM_SUCCESS);
292 :
293 0 : if (start_in_session)
294 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
295 :
296 0 : g_assert (pam_getenv (test->ph, "GNOME_KEYRING_CONTROL") != NULL);
297 0 : control = pam_getenv (test->ph, "GNOME_KEYRING_CONTROL");
298 :
299 : /* Initialize the daemon for real */
300 0 : g_assert (gkd_control_initialize (control, "secrets", PASS_ENVIRON));
301 :
302 : /* The keyring was created */
303 0 : g_assert (g_file_test (login_keyring, G_FILE_TEST_IS_REGULAR));
304 0 : g_free (login_keyring);
305 :
306 0 : g_assert (check_if_login_keyring_locked (test) == FALSE);
307 0 : g_assert (check_if_login_item_1_exists (test) == FALSE);
308 :
309 0 : if (!start_in_session)
310 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
311 :
312 0 : g_assert (gkd_control_quit (control, 0));
313 : }
314 :
315 : static void
316 1 : test_starts_only_session (Test *test,
317 : gconstpointer user_data)
318 : {
319 1 : const char *pam_conf = user_data;
320 : const gchar *control;
321 : gchar *login_keyring;
322 :
323 1 : if (test->skipping)
324 1 : return;
325 :
326 : /* This is the PAM config that starts the daemon from session handler */
327 0 : g_assert (strstr (pam_conf, "session-start") != NULL);
328 :
329 : /* We're testing that we create the directory appropriately */
330 0 : g_unsetenv ("XDG_RUNTIME_DIR");
331 :
332 0 : login_keyring = g_build_filename (test->directory, "login.keyring", NULL);
333 0 : g_assert (!g_file_test (login_keyring, G_FILE_TEST_EXISTS));
334 :
335 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
336 :
337 0 : g_assert (pam_getenv (test->ph, "GNOME_KEYRING_CONTROL") != NULL);
338 0 : control = pam_getenv (test->ph, "GNOME_KEYRING_CONTROL");
339 :
340 : /* These verify that the daemon was started */
341 0 : g_assert (gkd_control_quit (control, 0));
342 : }
343 :
344 : static void
345 2 : test_starts_exists (Test *test,
346 : gconstpointer user_data)
347 : {
348 2 : const gchar *pam_conf = user_data;
349 : const gchar *control;
350 : gboolean start_in_session;
351 :
352 2 : if (test->skipping)
353 2 : return;
354 :
355 : /* We're testing that we create the directory appropriately */
356 0 : g_unsetenv ("XDG_RUNTIME_DIR");
357 :
358 0 : start_in_session = (strstr (pam_conf, "session") != NULL);
359 :
360 0 : egg_tests_copy_scratch_file (test->directory, SRCDIR "/pam/fixtures/login.keyring");
361 :
362 0 : test->password = "booo";
363 0 : g_assert_cmpint (pam_authenticate (test->ph, 0), ==, PAM_SUCCESS);
364 :
365 0 : if (start_in_session)
366 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
367 :
368 0 : g_assert (pam_getenv (test->ph, "GNOME_KEYRING_CONTROL") != NULL);
369 0 : control = pam_getenv (test->ph, "GNOME_KEYRING_CONTROL");
370 :
371 : /* Initialize the daemon for real */
372 0 : g_assert (gkd_control_initialize (control, "secrets", PASS_ENVIRON));
373 :
374 : /* Lookup the item */
375 0 : g_assert (check_if_login_keyring_locked (test) == FALSE);
376 0 : g_assert (check_if_login_item_1_exists (test) == TRUE);
377 :
378 0 : if (!start_in_session)
379 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
380 :
381 0 : g_assert (gkd_control_quit (control, 0));
382 : }
383 :
384 : static void
385 1 : test_auth_nostart (Test *test,
386 : gconstpointer user_data)
387 : {
388 : gchar *login_keyring;
389 :
390 1 : if (test->skipping)
391 1 : return;
392 :
393 0 : test->password = "booo";
394 0 : g_assert_cmpint (pam_authenticate (test->ph, 0), ==, PAM_SUCCESS);
395 :
396 0 : g_assert (pam_getenv (test->ph, "GNOME_KEYRING_CONTROL") == NULL);
397 :
398 0 : login_keyring = g_build_filename (test->directory, "login.keyring", NULL);
399 0 : g_assert (!g_file_test (login_keyring, G_FILE_TEST_EXISTS));
400 0 : g_free (login_keyring);
401 : }
402 :
403 : static void
404 1 : test_auth_running_unlocks (Test *test,
405 : gconstpointer user_data)
406 : {
407 : gchar *control;
408 : gchar **env;
409 : GPid pid;
410 :
411 1 : const gchar *argv[] = {
412 : BUILDDIR "/gnome-keyring-daemon", "--foreground", NULL,
413 : };
414 :
415 1 : if (test->skipping)
416 1 : return;
417 :
418 0 : egg_tests_copy_scratch_file (test->directory, SRCDIR "/pam/fixtures/login.keyring");
419 :
420 0 : env = gkd_test_launch_daemon (test->directory, argv, &pid, NULL);
421 :
422 0 : g_assert (check_if_login_keyring_locked (test) == TRUE);
423 :
424 0 : test->password = "booo";
425 0 : g_assert_cmpint (pam_authenticate (test->ph, 0), ==, PAM_SUCCESS);
426 :
427 : /* Lookup the item */
428 0 : g_assert (check_if_login_keyring_locked (test) == FALSE);
429 0 : g_assert (check_if_login_item_1_exists (test) == TRUE);
430 :
431 0 : control = g_strdup_printf ("%s/keyring", test->directory);
432 0 : g_assert (gkd_control_quit (control, 0));
433 0 : g_assert_cmpint (waitpid (pid, NULL, 0), ==, pid);
434 :
435 0 : g_strfreev (env);
436 : }
437 :
438 : static void
439 1 : test_password_changes_running (Test *test,
440 : gconstpointer user_data)
441 : {
442 : gchar *control;
443 : gchar **env;
444 : GPid pid;
445 :
446 1 : const gchar *argv[] = {
447 : BUILDDIR "/gnome-keyring-daemon", "--foreground", NULL
448 : };
449 :
450 1 : if (test->skipping)
451 1 : return;
452 :
453 0 : egg_tests_copy_scratch_file (test->directory, SRCDIR "/pam/fixtures/login.keyring");
454 0 : control = g_strdup_printf ("%s/keyring", test->directory);
455 :
456 0 : env = gkd_test_launch_daemon (test->directory, argv, &pid, NULL);
457 0 : g_strfreev (env);
458 :
459 0 : test->password = "booo";
460 0 : test->new_password = "changed";
461 0 : g_assert_cmpint (pam_chauthtok (test->ph, 0), ==, PAM_SUCCESS);
462 :
463 : /* Quit the daemon */
464 0 : g_assert (gkd_control_quit (control, 0));
465 0 : g_assert_cmpint (waitpid (pid, NULL, 0), ==, pid);
466 :
467 : /* Start it again */
468 0 : env = gkd_test_launch_daemon (test->directory, argv, &pid, NULL);
469 0 : g_strfreev (env);
470 :
471 0 : g_assert (gkd_control_unlock (control, "changed"));
472 0 : g_assert (gkd_control_quit (control, 0));
473 0 : g_assert_cmpint (waitpid (pid, NULL, 0), ==, pid);
474 :
475 0 : g_free (control);
476 : }
477 :
478 : static void
479 1 : test_password_changes_starts (Test *test,
480 : gconstpointer user_data)
481 : {
482 : gchar *control;
483 : gchar **env;
484 : GPid pid;
485 :
486 1 : const gchar *argv[] = {
487 : BUILDDIR "/gnome-keyring-daemon", "--foreground", NULL,
488 : };
489 :
490 1 : if (test->skipping)
491 1 : return;
492 :
493 0 : egg_tests_copy_scratch_file (test->directory, SRCDIR "/pam/fixtures/login.keyring");
494 0 : control = g_strdup_printf ("%s/keyring", test->directory);
495 :
496 0 : test->password = "booo";
497 0 : test->new_password = "changed";
498 0 : g_assert_cmpint (pam_chauthtok (test->ph, 0), ==, PAM_SUCCESS);
499 :
500 : /* Start it again */
501 0 : env = gkd_test_launch_daemon (test->directory, argv, &pid,
502 : "GNOME_KEYRING_TEST_SERVICE", "another.Bus.Name",
503 : NULL);
504 :
505 0 : g_assert (gkd_control_unlock (control, "changed"));
506 0 : g_assert (gkd_control_quit (control, 0));
507 0 : g_assert_cmpint (waitpid (pid, NULL, 0), ==, pid);
508 :
509 0 : g_strfreev (env);
510 0 : g_free (control);
511 : }
512 :
513 : static void
514 1 : test_password_change_start_in_session (Test *test,
515 : gconstpointer user_data)
516 : {
517 1 : const char *pam_conf = user_data;
518 : gchar *control;
519 :
520 1 : if (test->skipping)
521 1 : return;
522 :
523 : /* This is the PAM config that starts the daemon from session handler */
524 0 : g_assert (strstr (pam_conf, "session-start") != NULL);
525 :
526 0 : egg_tests_copy_scratch_file (test->directory, SRCDIR "/pam/fixtures/login.keyring");
527 0 : control = g_strdup_printf ("%s/keyring", test->directory);
528 :
529 : /* First we authenticate, but don't start the keyring here */
530 0 : test->password = "booo";
531 0 : g_assert_cmpint (pam_authenticate (test->ph, 0), ==, PAM_SUCCESS);
532 :
533 0 : test->password = "booo";
534 0 : test->new_password = "changed";
535 0 : g_assert_cmpint (pam_chauthtok (test->ph, 0), ==, PAM_SUCCESS);
536 :
537 : /* No daemon should be running, chauthtok started/stopped it */
538 0 : g_assert (gkd_control_quit (control, GKD_CONTROL_QUIET_IF_NO_PEER) == FALSE);
539 :
540 : /* Now session should be able to start and unlock the keyring */
541 0 : g_assert_cmpint (pam_open_session (test->ph, 0), ==, PAM_SUCCESS);
542 :
543 : /* Initialize the daemon */
544 0 : g_assert (gkd_control_initialize (control, "secrets", PASS_ENVIRON));
545 :
546 : /* Lookup the item */
547 0 : g_assert (check_if_login_keyring_locked (test) == FALSE);
548 0 : g_assert (check_if_login_item_1_exists (test) == TRUE);
549 :
550 0 : g_assert (gkd_control_quit (control, 0));
551 0 : g_free (control);
552 : }
553 :
554 : int
555 1 : main (int argc, char **argv)
556 : {
557 1 : g_test_init (&argc, &argv, NULL);
558 :
559 1 : g_test_add ("/pam/auth-no-start", Test,
560 : "gnome-keyring-test-no-start",
561 : setup, test_auth_nostart, teardown);
562 :
563 1 : g_test_add ("/pam/auth-starts-creates-keyring", Test,
564 : "gnome-keyring-test-auth-start",
565 : setup, test_starts_creates, teardown);
566 1 : g_test_add ("/pam/session-starts-creates-keyring", Test,
567 : "gnome-keyring-test-session-start",
568 : setup, test_starts_creates, teardown);
569 :
570 1 : g_test_add ("/pam/auth-starts-unlocks-existing", Test,
571 : "gnome-keyring-test-auth-start",
572 : setup, test_starts_exists, teardown);
573 1 : g_test_add ("/pam/session-starts-unlocks-existing", Test,
574 : "gnome-keyring-test-session-start",
575 : setup, test_starts_exists, teardown);
576 :
577 1 : g_test_add ("/pam/session-starts-without-auth", Test,
578 : "gnome-keyring-test-session-start",
579 : setup, test_starts_only_session, teardown);
580 :
581 1 : g_test_add ("/pam/auth-running-unlocks-existing", Test,
582 : "gnome-keyring-test-no-start",
583 : setup, test_auth_running_unlocks, teardown);
584 :
585 1 : g_test_add ("/pam/password-changes-running", Test,
586 : "gnome-keyring-test-no-start",
587 : setup, test_password_changes_running, teardown);
588 1 : g_test_add ("/pam/password-changes-starts", Test,
589 : "gnome-keyring-test-no-start",
590 : setup, test_password_changes_starts, teardown);
591 :
592 1 : g_test_add ("/pam/password-change-start-in-session", Test,
593 : "gnome-keyring-test-session-start",
594 : setup, test_password_change_start_in_session, teardown);
595 :
596 1 : return g_test_run ();
597 : }
|