1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-auth-script.c Test DBusAuth using a special script file (internal to D-Bus implementation)
3 *
4 * Copyright (C) 2003 Red Hat, Inc.
5 *
6 * Licensed under the Academic Free License version 2.1
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23 #include <config.h>
24
25 #ifdef DBUS_BUILD_TESTS
26
27 #include "dbus-auth-script.h"
28 #include "dbus-auth.h"
29 #include "dbus-string.h"
30 #include "dbus-hash.h"
31 #include "dbus-credentials.h"
32 #include "dbus-internals.h"
33
34 /**
35 * @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
36 * @ingroup DBusInternals
37 * @brief DBusAuth unit test scripting
38 *
39 * The code in here is used for unit testing, it loads
40 * up a script that tests DBusAuth.
41 *
42 * @{
43 */
44
45 /* this is slightly different from the other append_quoted_string
46 * in dbus-message-builder.c
47 */
48 static dbus_bool_t
append_quoted_string(DBusString * dest,const DBusString * quoted)49 append_quoted_string (DBusString *dest,
50 const DBusString *quoted)
51 {
52 dbus_bool_t in_quotes = FALSE;
53 dbus_bool_t in_backslash = FALSE;
54 int i;
55
56 i = 0;
57 while (i < _dbus_string_get_length (quoted))
58 {
59 unsigned char b;
60
61 b = _dbus_string_get_byte (quoted, i);
62
63 if (in_backslash)
64 {
65 unsigned char a;
66
67 if (b == 'r')
68 a = '\r';
69 else if (b == 'n')
70 a = '\n';
71 else if (b == '\\')
72 a = '\\';
73 else
74 {
75 _dbus_warn ("bad backslashed byte %c\n", b);
76 return FALSE;
77 }
78
79 if (!_dbus_string_append_byte (dest, a))
80 return FALSE;
81
82 in_backslash = FALSE;
83 }
84 else if (b == '\\')
85 {
86 in_backslash = TRUE;
87 }
88 else if (in_quotes)
89 {
90 if (b == '\'')
91 in_quotes = FALSE;
92 else
93 {
94 if (!_dbus_string_append_byte (dest, b))
95 return FALSE;
96 }
97 }
98 else
99 {
100 if (b == '\'')
101 in_quotes = TRUE;
102 else if (b == ' ' || b == '\n' || b == '\t')
103 break; /* end on whitespace if not quoted */
104 else
105 {
106 if (!_dbus_string_append_byte (dest, b))
107 return FALSE;
108 }
109 }
110
111 ++i;
112 }
113
114 return TRUE;
115 }
116
117 static dbus_bool_t
same_first_word(const DBusString * a,const DBusString * b)118 same_first_word (const DBusString *a,
119 const DBusString *b)
120 {
121 int first_a_blank, first_b_blank;
122
123 _dbus_string_find_blank (a, 0, &first_a_blank);
124 _dbus_string_find_blank (b, 0, &first_b_blank);
125
126 if (first_a_blank != first_b_blank)
127 return FALSE;
128
129 return _dbus_string_equal_len (a, b, first_a_blank);
130 }
131
132 static DBusAuthState
auth_state_from_string(const DBusString * str)133 auth_state_from_string (const DBusString *str)
134 {
135 if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT"))
136 return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
137 else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY"))
138 return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
139 else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND"))
140 return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
141 else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT"))
142 return DBUS_AUTH_STATE_NEED_DISCONNECT;
143 else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
144 return DBUS_AUTH_STATE_AUTHENTICATED;
145 else
146 return -1;
147 }
148
149 static const char*
auth_state_to_string(DBusAuthState state)150 auth_state_to_string (DBusAuthState state)
151 {
152 switch (state)
153 {
154 case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
155 return "WAITING_FOR_INPUT";
156 case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
157 return "WAITING_FOR_MEMORY";
158 case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
159 return "HAVE_BYTES_TO_SEND";
160 case DBUS_AUTH_STATE_NEED_DISCONNECT:
161 return "NEED_DISCONNECT";
162 case DBUS_AUTH_STATE_AUTHENTICATED:
163 return "AUTHENTICATED";
164 }
165
166 return "unknown";
167 }
168
169 static char **
split_string(DBusString * str)170 split_string (DBusString *str)
171 {
172 int i, j, k, count, end;
173 char **array;
174
175 end = _dbus_string_get_length (str);
176
177 i = 0;
178 _dbus_string_skip_blank (str, i, &i);
179 for (count = 0; i < end; count++)
180 {
181 _dbus_string_find_blank (str, i, &i);
182 _dbus_string_skip_blank (str, i, &i);
183 }
184
185 array = dbus_new0 (char *, count + 1);
186 if (array == NULL)
187 return NULL;
188
189 i = 0;
190 _dbus_string_skip_blank (str, i, &i);
191 for (k = 0; k < count; k++)
192 {
193 _dbus_string_find_blank (str, i, &j);
194
195 array[k] = dbus_malloc (j - i + 1);
196 if (array[k] == NULL)
197 {
198 dbus_free_string_array (array);
199 return NULL;
200 }
201 memcpy (array[k],
202 _dbus_string_get_const_data_len (str, i, j - i), j - i);
203 array[k][j - i] = '\0';
204
205 _dbus_string_skip_blank (str, j, &i);
206 }
207 array[k] = NULL;
208
209 return array;
210 }
211
212 static void
auth_set_unix_credentials(DBusAuth * auth,dbus_uid_t uid,dbus_pid_t pid)213 auth_set_unix_credentials(DBusAuth *auth,
214 dbus_uid_t uid,
215 dbus_pid_t pid)
216 {
217 DBusCredentials *credentials;
218
219 credentials = _dbus_credentials_new ();
220 if (credentials == NULL)
221 _dbus_assert_not_reached ("no memory");
222
223 if (uid != DBUS_UID_UNSET)
224 _dbus_credentials_add_unix_uid (credentials, uid);
225 if (pid != DBUS_PID_UNSET)
226 _dbus_credentials_add_unix_pid (credentials, pid);
227
228 _dbus_auth_set_credentials (auth, credentials);
229
230 _dbus_credentials_unref (credentials);
231 }
232
233 /**
234 * Runs an "auth script" which is a script for testing the
235 * authentication protocol. Scripts send and receive data, and then
236 * include assertions about the state of both ends of the connection
237 * after processing the data. A script succeeds if these assertions
238 * hold.
239 *
240 * @param filename the file containing the script to run
241 * @returns #TRUE if the script succeeds, #FALSE otherwise
242 */
243 dbus_bool_t
_dbus_auth_script_run(const DBusString * filename)244 _dbus_auth_script_run (const DBusString *filename)
245 {
246 DBusString file;
247 DBusError error = DBUS_ERROR_INIT;
248 DBusString line;
249 dbus_bool_t retval;
250 int line_no;
251 DBusAuth *auth;
252 DBusString from_auth;
253 DBusAuthState state;
254 DBusString context;
255 DBusString guid;
256
257 retval = FALSE;
258 auth = NULL;
259
260 _dbus_string_init_const (&guid, "5fa01f4202cd837709a3274ca0df9d00");
261 _dbus_string_init_const (&context, "org_freedesktop_test");
262
263 if (!_dbus_string_init (&file))
264 return FALSE;
265
266 if (!_dbus_string_init (&line))
267 {
268 _dbus_string_free (&file);
269 return FALSE;
270 }
271
272 if (!_dbus_string_init (&from_auth))
273 {
274 _dbus_string_free (&file);
275 _dbus_string_free (&line);
276 return FALSE;
277 }
278
279 if (!_dbus_file_get_contents (&file, filename, &error)) {
280 _dbus_warn ("Getting contents of %s failed: %s\n",
281 _dbus_string_get_const_data (filename), error.message);
282 dbus_error_free (&error);
283 goto out;
284 }
285
286 state = DBUS_AUTH_STATE_NEED_DISCONNECT;
287 line_no = 0;
288
289 next_iteration:
290 while (_dbus_string_pop_line (&file, &line))
291 {
292 line_no += 1;
293
294 /* _dbus_warn ("%s\n", _dbus_string_get_const_data (&line)); */
295
296 _dbus_string_delete_leading_blanks (&line);
297
298 if (auth != NULL)
299 {
300 while ((state = _dbus_auth_do_work (auth)) ==
301 DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
302 {
303 const DBusString *tmp;
304 if (_dbus_auth_get_bytes_to_send (auth, &tmp))
305 {
306 int count = _dbus_string_get_length (tmp);
307
308 if (_dbus_string_copy (tmp, 0, &from_auth,
309 _dbus_string_get_length (&from_auth)))
310 _dbus_auth_bytes_sent (auth, count);
311 }
312 }
313 }
314
315 if (_dbus_string_get_length (&line) == 0)
316 {
317 /* empty line */
318 goto next_iteration;
319 }
320 else if (_dbus_string_starts_with_c_str (&line,
321 "#"))
322 {
323 /* Ignore this comment */
324 goto next_iteration;
325 }
326 #ifdef DBUS_WIN
327 else if (_dbus_string_starts_with_c_str (&line,
328 "WIN_ONLY"))
329 {
330 /* Ignore this line */
331 goto next_iteration;
332 }
333 else if (_dbus_string_starts_with_c_str (&line,
334 "UNIX_ONLY"))
335 {
336 /* skip this file */
337 _dbus_warn ("skipping unix only auth script\n");
338 retval = TRUE;
339 goto out;
340 }
341 #endif
342 #ifdef DBUS_UNIX
343 else if (_dbus_string_starts_with_c_str (&line,
344 "UNIX_ONLY"))
345 {
346 /* Ignore this line */
347 goto next_iteration;
348 }
349 else if (_dbus_string_starts_with_c_str (&line,
350 "WIN_ONLY"))
351 {
352 /* skip this file */
353 _dbus_warn ("skipping windows only auth script\n");
354 retval = TRUE;
355 goto out;
356 }
357 #endif
358 else if (_dbus_string_starts_with_c_str (&line,
359 "CLIENT"))
360 {
361 DBusCredentials *creds;
362
363 if (auth != NULL)
364 {
365 _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
366 goto out;
367 }
368
369 auth = _dbus_auth_client_new ();
370 if (auth == NULL)
371 {
372 _dbus_warn ("no memory to create DBusAuth\n");
373 goto out;
374 }
375
376 /* test ref/unref */
377 _dbus_auth_ref (auth);
378 _dbus_auth_unref (auth);
379
380 creds = _dbus_credentials_new_from_current_process ();
381 if (creds == NULL)
382 {
383 _dbus_warn ("no memory for credentials\n");
384 _dbus_auth_unref (auth);
385 auth = NULL;
386 goto out;
387 }
388
389 if (!_dbus_auth_set_credentials (auth, creds))
390 {
391 _dbus_warn ("no memory for setting credentials\n");
392 _dbus_auth_unref (auth);
393 auth = NULL;
394 _dbus_credentials_unref (creds);
395 goto out;
396 }
397
398 _dbus_credentials_unref (creds);
399 }
400 else if (_dbus_string_starts_with_c_str (&line,
401 "SERVER"))
402 {
403 DBusCredentials *creds;
404
405 if (auth != NULL)
406 {
407 _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
408 goto out;
409 }
410
411 auth = _dbus_auth_server_new (&guid);
412 if (auth == NULL)
413 {
414 _dbus_warn ("no memory to create DBusAuth\n");
415 goto out;
416 }
417
418 /* test ref/unref */
419 _dbus_auth_ref (auth);
420 _dbus_auth_unref (auth);
421
422 creds = _dbus_credentials_new_from_current_process ();
423 if (creds == NULL)
424 {
425 _dbus_warn ("no memory for credentials\n");
426 _dbus_auth_unref (auth);
427 auth = NULL;
428 goto out;
429 }
430
431 if (!_dbus_auth_set_credentials (auth, creds))
432 {
433 _dbus_warn ("no memory for setting credentials\n");
434 _dbus_auth_unref (auth);
435 auth = NULL;
436 _dbus_credentials_unref (creds);
437 goto out;
438 }
439
440 _dbus_credentials_unref (creds);
441
442 _dbus_auth_set_context (auth, &context);
443 }
444 else if (auth == NULL)
445 {
446 _dbus_warn ("must specify CLIENT or SERVER\n");
447 goto out;
448
449 }
450 else if (_dbus_string_starts_with_c_str (&line,
451 "NO_CREDENTIALS"))
452 {
453 auth_set_unix_credentials (auth, DBUS_UID_UNSET, DBUS_PID_UNSET);
454 }
455 else if (_dbus_string_starts_with_c_str (&line,
456 "ROOT_CREDENTIALS"))
457 {
458 auth_set_unix_credentials (auth, 0, DBUS_PID_UNSET);
459 }
460 else if (_dbus_string_starts_with_c_str (&line,
461 "SILLY_CREDENTIALS"))
462 {
463 auth_set_unix_credentials (auth, 4312, DBUS_PID_UNSET);
464 }
465 else if (_dbus_string_starts_with_c_str (&line,
466 "ALLOWED_MECHS"))
467 {
468 char **mechs;
469
470 _dbus_string_delete_first_word (&line);
471 mechs = split_string (&line);
472 _dbus_auth_set_mechanisms (auth, (const char **) mechs);
473 dbus_free_string_array (mechs);
474 }
475 else if (_dbus_string_starts_with_c_str (&line,
476 "SEND"))
477 {
478 DBusString to_send;
479
480 _dbus_string_delete_first_word (&line);
481
482 if (!_dbus_string_init (&to_send))
483 {
484 _dbus_warn ("no memory to allocate string\n");
485 goto out;
486 }
487
488 if (!append_quoted_string (&to_send, &line))
489 {
490 _dbus_warn ("failed to append quoted string line %d\n",
491 line_no);
492 _dbus_string_free (&to_send);
493 goto out;
494 }
495
496 _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
497
498 if (!_dbus_string_append (&to_send, "\r\n"))
499 {
500 _dbus_warn ("failed to append \r\n from line %d\n",
501 line_no);
502 _dbus_string_free (&to_send);
503 goto out;
504 }
505
506 /* Replace USERID_HEX with our username in hex */
507 {
508 int where;
509
510 if (_dbus_string_find (&to_send, 0,
511 "USERID_HEX", &where))
512 {
513 DBusString username;
514
515 if (!_dbus_string_init (&username))
516 {
517 _dbus_warn ("no memory for userid\n");
518 _dbus_string_free (&to_send);
519 goto out;
520 }
521
522 if (!_dbus_append_user_from_current_process (&username))
523 {
524 _dbus_warn ("no memory for userid\n");
525 _dbus_string_free (&username);
526 _dbus_string_free (&to_send);
527 goto out;
528 }
529
530 _dbus_string_delete (&to_send, where, strlen ("USERID_HEX"));
531
532 if (!_dbus_string_hex_encode (&username, 0,
533 &to_send, where))
534 {
535 _dbus_warn ("no memory to subst USERID_HEX\n");
536 _dbus_string_free (&username);
537 _dbus_string_free (&to_send);
538 goto out;
539 }
540
541 _dbus_string_free (&username);
542 }
543 else if (_dbus_string_find (&to_send, 0,
544 "USERNAME_HEX", &where))
545 {
546 DBusString username;
547
548 if (!_dbus_string_init (&username))
549 {
550 _dbus_warn ("no memory for username\n");
551 _dbus_string_free (&to_send);
552 goto out;
553 }
554
555 if (!_dbus_append_user_from_current_process (&username))
556 {
557 _dbus_warn ("no memory for username\n");
558 _dbus_string_free (&username);
559 _dbus_string_free (&to_send);
560 goto out;
561 }
562
563 _dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX"));
564
565 if (!_dbus_string_hex_encode (&username, 0,
566 &to_send, where))
567 {
568 _dbus_warn ("no memory to subst USERNAME_HEX\n");
569 _dbus_string_free (&username);
570 _dbus_string_free (&to_send);
571 goto out;
572 }
573
574 _dbus_string_free (&username);
575 }
576 }
577
578 {
579 DBusString *buffer;
580
581 _dbus_auth_get_buffer (auth, &buffer);
582 if (!_dbus_string_copy (&to_send, 0,
583 buffer, _dbus_string_get_length (buffer)))
584 {
585 _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
586 _dbus_string_free (&to_send);
587 _dbus_auth_return_buffer (auth, buffer, 0);
588 goto out;
589 }
590
591 _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
592 }
593
594 _dbus_string_free (&to_send);
595 }
596 else if (_dbus_string_starts_with_c_str (&line,
597 "EXPECT_STATE"))
598 {
599 DBusAuthState expected;
600
601 _dbus_string_delete_first_word (&line);
602
603 expected = auth_state_from_string (&line);
604 if (expected < 0)
605 {
606 _dbus_warn ("bad auth state given to EXPECT_STATE\n");
607 goto parse_failed;
608 }
609
610 if (expected != state)
611 {
612 _dbus_warn ("expected auth state %s but got %s on line %d\n",
613 auth_state_to_string (expected),
614 auth_state_to_string (state),
615 line_no);
616 goto out;
617 }
618 }
619 else if (_dbus_string_starts_with_c_str (&line,
620 "EXPECT_COMMAND"))
621 {
622 DBusString received;
623
624 _dbus_string_delete_first_word (&line);
625
626 if (!_dbus_string_init (&received))
627 {
628 _dbus_warn ("no mem to allocate string received\n");
629 goto out;
630 }
631
632 if (!_dbus_string_pop_line (&from_auth, &received))
633 {
634 _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
635 _dbus_string_get_const_data (&line), line_no);
636 _dbus_string_free (&received);
637 goto out;
638 }
639
640 if (!same_first_word (&received, &line))
641 {
642 _dbus_warn ("line %d expected command '%s' and got '%s'\n",
643 line_no,
644 _dbus_string_get_const_data (&line),
645 _dbus_string_get_const_data (&received));
646 _dbus_string_free (&received);
647 goto out;
648 }
649
650 _dbus_string_free (&received);
651 }
652 else if (_dbus_string_starts_with_c_str (&line,
653 "EXPECT_UNUSED"))
654 {
655 DBusString expected;
656 const DBusString *unused;
657
658 _dbus_string_delete_first_word (&line);
659
660 if (!_dbus_string_init (&expected))
661 {
662 _dbus_warn ("no mem to allocate string expected\n");
663 goto out;
664 }
665
666 if (!append_quoted_string (&expected, &line))
667 {
668 _dbus_warn ("failed to append quoted string line %d\n",
669 line_no);
670 _dbus_string_free (&expected);
671 goto out;
672 }
673
674 _dbus_auth_get_unused_bytes (auth, &unused);
675
676 if (_dbus_string_equal (&expected, unused))
677 {
678 _dbus_auth_delete_unused_bytes (auth);
679 _dbus_string_free (&expected);
680 }
681 else
682 {
683 _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
684 _dbus_string_get_const_data (&expected),
685 _dbus_string_get_const_data (unused));
686 _dbus_string_free (&expected);
687 goto out;
688 }
689 }
690 else if (_dbus_string_starts_with_c_str (&line,
691 "EXPECT_HAVE_NO_CREDENTIALS"))
692 {
693 DBusCredentials *authorized_identity;
694
695 authorized_identity = _dbus_auth_get_identity (auth);
696 if (!_dbus_credentials_are_anonymous (authorized_identity))
697 {
698 _dbus_warn ("Expected anonymous login or failed login, but some credentials were authorized\n");
699 goto out;
700 }
701 }
702 else if (_dbus_string_starts_with_c_str (&line,
703 "EXPECT_HAVE_SOME_CREDENTIALS"))
704 {
705 DBusCredentials *authorized_identity;
706
707 authorized_identity = _dbus_auth_get_identity (auth);
708 if (_dbus_credentials_are_anonymous (authorized_identity))
709 {
710 _dbus_warn ("Expected to have some credentials, but we don't\n");
711 goto out;
712 }
713 }
714 else if (_dbus_string_starts_with_c_str (&line,
715 "EXPECT"))
716 {
717 DBusString expected;
718
719 _dbus_string_delete_first_word (&line);
720
721 if (!_dbus_string_init (&expected))
722 {
723 _dbus_warn ("no mem to allocate string expected\n");
724 goto out;
725 }
726
727 if (!append_quoted_string (&expected, &line))
728 {
729 _dbus_warn ("failed to append quoted string line %d\n",
730 line_no);
731 _dbus_string_free (&expected);
732 goto out;
733 }
734
735 if (_dbus_string_equal_len (&expected, &from_auth,
736 _dbus_string_get_length (&expected)))
737 {
738 _dbus_string_delete (&from_auth, 0,
739 _dbus_string_get_length (&expected));
740 _dbus_string_free (&expected);
741 }
742 else
743 {
744 _dbus_warn ("Expected exact string '%s' and have '%s'\n",
745 _dbus_string_get_const_data (&expected),
746 _dbus_string_get_const_data (&from_auth));
747 _dbus_string_free (&expected);
748 goto out;
749 }
750 }
751 else
752 goto parse_failed;
753
754 goto next_iteration; /* skip parse_failed */
755
756 parse_failed:
757 {
758 _dbus_warn ("couldn't process line %d \"%s\"\n",
759 line_no, _dbus_string_get_const_data (&line));
760 goto out;
761 }
762 }
763
764 if (auth == NULL)
765 {
766 _dbus_warn ("Auth script is bogus, did not even have CLIENT or SERVER\n");
767 goto out;
768 }
769 else if (state == DBUS_AUTH_STATE_AUTHENTICATED)
770 {
771 const DBusString *unused;
772
773 _dbus_auth_get_unused_bytes (auth, &unused);
774
775 if (_dbus_string_get_length (unused) > 0)
776 {
777 _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
778 goto out;
779 }
780 }
781
782 if (_dbus_string_get_length (&from_auth) > 0)
783 {
784 _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
785 _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
786 goto out;
787 }
788
789 retval = TRUE;
790
791 out:
792 if (auth)
793 _dbus_auth_unref (auth);
794
795 _dbus_string_free (&file);
796 _dbus_string_free (&line);
797 _dbus_string_free (&from_auth);
798
799 return retval;
800 }
801
802 /** @} */
803 #endif /* DBUS_BUILD_TESTS */
804