2007-07-13 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-auth-script.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
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
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
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*
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 **
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
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
244 _dbus_auth_script_run (const DBusString *filename)
245 {
246   DBusString file;
247   DBusError error;
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   dbus_error_init (&error);
280   if (!_dbus_file_get_contents (&file, filename, &error))    {
281       _dbus_warn ("Getting contents of %s failed: %s\n",
282                   _dbus_string_get_const_data (filename), error.message);
283       dbus_error_free (&error);
284       goto out;
285     }
286
287   state = DBUS_AUTH_STATE_NEED_DISCONNECT;
288   line_no = 0;
289
290  next_iteration:
291   while (_dbus_string_pop_line (&file, &line))
292     {      
293       line_no += 1;
294
295       /* _dbus_warn ("%s\n", _dbus_string_get_const_data (&line)); */
296       
297       _dbus_string_delete_leading_blanks (&line);
298
299       if (auth != NULL)
300         {
301           while ((state = _dbus_auth_do_work (auth)) ==
302                  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
303             {
304               const DBusString *tmp;
305               if (_dbus_auth_get_bytes_to_send (auth, &tmp))
306                 {
307                   int count = _dbus_string_get_length (tmp);
308
309                   if (_dbus_string_copy (tmp, 0, &from_auth,
310                                          _dbus_string_get_length (&from_auth)))
311                     _dbus_auth_bytes_sent (auth, count);
312                 }
313             }
314         }
315       
316       if (_dbus_string_get_length (&line) == 0)
317         {
318           /* empty line */
319           goto next_iteration;
320         }
321       else if (_dbus_string_starts_with_c_str (&line,
322                                                "#"))
323         {
324           /* Ignore this comment */
325           goto next_iteration;
326         }
327 #ifdef DBUS_WIN
328       else if (_dbus_string_starts_with_c_str (&line,
329                                                "WIN_ONLY"))
330         {
331           /* Ignore this line */
332           goto next_iteration;
333         }
334       else if (_dbus_string_starts_with_c_str (&line,
335                                                "UNIX_ONLY"))
336         {
337           /* skip this file */
338           _dbus_warn ("skipping unix only auth script\n");
339           retval = TRUE;
340           goto out;
341         }
342 #endif
343 #ifdef DBUS_UNIX
344       else if (_dbus_string_starts_with_c_str (&line,
345                                                "UNIX_ONLY"))
346         {
347           /* Ignore this line */
348           goto next_iteration;
349         }
350       else if (_dbus_string_starts_with_c_str (&line,
351                                                "WIN_ONLY"))
352         {
353           /* skip this file */
354           _dbus_warn ("skipping windows only auth script\n");
355           retval = TRUE;
356           goto out;
357         }
358 #endif
359       else if (_dbus_string_starts_with_c_str (&line,
360                                                "CLIENT"))
361         {
362           DBusCredentials *creds;
363           
364           if (auth != NULL)
365             {
366               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
367               goto out;
368             }
369
370           auth = _dbus_auth_client_new ();
371           if (auth == NULL)
372             {
373               _dbus_warn ("no memory to create DBusAuth\n");
374               goto out;
375             }
376
377           /* test ref/unref */
378           _dbus_auth_ref (auth);
379           _dbus_auth_unref (auth);
380
381           creds = _dbus_credentials_new_from_current_process ();
382           if (creds == NULL)
383             {
384               _dbus_warn ("no memory for credentials\n");
385               _dbus_auth_unref (auth);
386               auth = NULL;
387               goto out;
388             }
389               
390           if (!_dbus_auth_set_credentials (auth, creds))
391             {
392               _dbus_warn ("no memory for setting credentials\n");
393               _dbus_auth_unref (auth);
394               auth = NULL;
395               _dbus_credentials_unref (creds);
396               goto out;
397             }
398           
399           _dbus_credentials_unref (creds);
400         }
401       else if (_dbus_string_starts_with_c_str (&line,
402                                                "SERVER"))
403         {
404           DBusCredentials *creds;
405           
406           if (auth != NULL)
407             {
408               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
409               goto out;
410             }
411
412           auth = _dbus_auth_server_new (&guid);
413           if (auth == NULL)
414             {
415               _dbus_warn ("no memory to create DBusAuth\n");
416               goto out;
417             }
418
419           /* test ref/unref */
420           _dbus_auth_ref (auth);
421           _dbus_auth_unref (auth);
422
423           creds = _dbus_credentials_new_from_current_process ();
424           if (creds == NULL)
425             {
426               _dbus_warn ("no memory for credentials\n");
427               _dbus_auth_unref (auth);
428               auth = NULL;
429               goto out;
430             }
431               
432           if (!_dbus_auth_set_credentials (auth, creds))
433             {
434               _dbus_warn ("no memory for setting credentials\n");
435               _dbus_auth_unref (auth);
436               auth = NULL;
437               _dbus_credentials_unref (creds);
438               goto out;
439             }
440           
441           _dbus_credentials_unref (creds);
442
443           _dbus_auth_set_context (auth, &context);
444         }
445       else if (auth == NULL)
446         {
447           _dbus_warn ("must specify CLIENT or SERVER\n");
448           goto out;
449
450         }
451       else if (_dbus_string_starts_with_c_str (&line,
452                                                "NO_CREDENTIALS"))
453         {
454           auth_set_unix_credentials (auth, DBUS_UID_UNSET, DBUS_PID_UNSET);
455         }
456       else if (_dbus_string_starts_with_c_str (&line,
457                                                "ROOT_CREDENTIALS"))
458         {
459           auth_set_unix_credentials (auth, 0, DBUS_PID_UNSET);
460         }
461       else if (_dbus_string_starts_with_c_str (&line,
462                                                "SILLY_CREDENTIALS"))
463         {
464           auth_set_unix_credentials (auth, 4312, DBUS_PID_UNSET);
465         }
466       else if (_dbus_string_starts_with_c_str (&line,
467                                                "ALLOWED_MECHS"))
468         {
469           char **mechs;
470
471           _dbus_string_delete_first_word (&line);
472           mechs = split_string (&line);
473           _dbus_auth_set_mechanisms (auth, (const char **) mechs);
474           dbus_free_string_array (mechs);
475         }
476       else if (_dbus_string_starts_with_c_str (&line,
477                                                "SEND"))
478         {
479           DBusString to_send;
480           
481           _dbus_string_delete_first_word (&line);
482
483           if (!_dbus_string_init (&to_send))
484             {
485               _dbus_warn ("no memory to allocate string\n");
486               goto out;
487             }
488
489           if (!append_quoted_string (&to_send, &line))
490             {
491               _dbus_warn ("failed to append quoted string line %d\n",
492                           line_no);
493               _dbus_string_free (&to_send);
494               goto out;
495             }
496
497           _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
498           
499           if (!_dbus_string_append (&to_send, "\r\n"))
500             {
501               _dbus_warn ("failed to append \r\n from line %d\n",
502                           line_no);
503               _dbus_string_free (&to_send);
504               goto out;
505             }
506
507           /* Replace USERID_HEX with our username in hex */
508           {
509             int where;
510             
511             if (_dbus_string_find (&to_send, 0,
512                                    "USERID_HEX", &where))
513               {
514                 DBusString username;
515
516                 if (!_dbus_string_init (&username))
517                   {
518                     _dbus_warn ("no memory for userid\n");
519                     _dbus_string_free (&to_send);
520                     goto out;
521                   }
522
523                 if (!_dbus_append_user_from_current_process (&username))
524                   {
525                     _dbus_warn ("no memory for userid\n");
526                     _dbus_string_free (&username);
527                     _dbus_string_free (&to_send);
528                     goto out;
529                   }
530
531                 _dbus_string_delete (&to_send, where, strlen ("USERID_HEX"));
532                 
533                 if (!_dbus_string_hex_encode (&username, 0,
534                                               &to_send, where))
535                   {
536                     _dbus_warn ("no memory to subst USERID_HEX\n");
537                     _dbus_string_free (&username);
538                     _dbus_string_free (&to_send);
539                     goto out;
540                   }
541
542                 _dbus_string_free (&username);
543               }
544             else if (_dbus_string_find (&to_send, 0,
545                                         "USERNAME_HEX", &where))
546               {
547                 DBusString username;
548                 
549                 if (!_dbus_string_init (&username))
550                   {
551                     _dbus_warn ("no memory for username\n");
552                     _dbus_string_free (&to_send);
553                     goto out;
554                   }
555
556                 if (!_dbus_append_user_from_current_process (&username))
557                   {
558                     _dbus_warn ("no memory for username\n");
559                     _dbus_string_free (&username);
560                     _dbus_string_free (&to_send);
561                     goto out;
562                   }
563
564                 _dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX"));
565                 
566                 if (!_dbus_string_hex_encode (&username, 0,
567                                               &to_send, where))
568                   {
569                     _dbus_warn ("no memory to subst USERNAME_HEX\n");
570                     _dbus_string_free (&username);
571                     _dbus_string_free (&to_send);
572                     goto out;
573                   }
574
575                 _dbus_string_free (&username);
576               }
577           }
578
579           {
580             DBusString *buffer;
581
582             _dbus_auth_get_buffer (auth, &buffer);
583             if (!_dbus_string_copy (&to_send, 0,
584                                     buffer, _dbus_string_get_length (buffer)))
585               {
586                 _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
587                 _dbus_string_free (&to_send);
588                 _dbus_auth_return_buffer (auth, buffer, 0);
589                 goto out;
590               }
591
592             _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
593           }
594           
595           _dbus_string_free (&to_send);
596         }
597       else if (_dbus_string_starts_with_c_str (&line,
598                                                "EXPECT_STATE"))
599         {
600           DBusAuthState expected;
601           
602           _dbus_string_delete_first_word (&line);
603
604           expected = auth_state_from_string (&line);
605           if (expected < 0)
606             {
607               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
608               goto parse_failed;
609             }
610
611           if (expected != state)
612             {
613               _dbus_warn ("expected auth state %s but got %s on line %d\n",
614                           auth_state_to_string (expected),
615                           auth_state_to_string (state),
616                           line_no);
617               goto out;
618             }
619         }
620       else if (_dbus_string_starts_with_c_str (&line,
621                                                "EXPECT_COMMAND"))
622         {
623           DBusString received;
624           
625           _dbus_string_delete_first_word (&line);
626
627           if (!_dbus_string_init (&received))
628             {
629               _dbus_warn ("no mem to allocate string received\n");
630               goto out;
631             }
632
633           if (!_dbus_string_pop_line (&from_auth, &received))
634             {
635               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
636                           _dbus_string_get_const_data (&line), line_no);
637               _dbus_string_free (&received);
638               goto out;
639             }
640
641           if (!same_first_word (&received, &line))
642             {
643               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
644                           line_no,
645                           _dbus_string_get_const_data (&line),
646                           _dbus_string_get_const_data (&received));
647               _dbus_string_free (&received);
648               goto out;
649             }
650           
651           _dbus_string_free (&received);
652         }
653       else if (_dbus_string_starts_with_c_str (&line,
654                                                "EXPECT_UNUSED"))
655         {
656           DBusString expected;
657           const DBusString *unused;
658           
659           _dbus_string_delete_first_word (&line);
660
661           if (!_dbus_string_init (&expected))
662             {
663               _dbus_warn ("no mem to allocate string expected\n");
664               goto out;
665             }
666
667           if (!append_quoted_string (&expected, &line))
668             {
669               _dbus_warn ("failed to append quoted string line %d\n",
670                           line_no);
671               _dbus_string_free (&expected);
672               goto out;
673             }
674
675           _dbus_auth_get_unused_bytes (auth, &unused);
676           
677           if (_dbus_string_equal (&expected, unused))
678             {
679               _dbus_auth_delete_unused_bytes (auth);
680               _dbus_string_free (&expected);
681             }
682           else
683             {
684               _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
685                           _dbus_string_get_const_data (&expected),
686                           _dbus_string_get_const_data (unused));
687               _dbus_string_free (&expected);
688               goto out;
689             }
690         }
691       else if (_dbus_string_starts_with_c_str (&line,
692                                                "EXPECT_HAVE_NO_CREDENTIALS"))
693         {
694           DBusCredentials *authorized_identity;
695           
696           authorized_identity = _dbus_auth_get_identity (auth);
697           if (!_dbus_credentials_are_anonymous (authorized_identity))
698             {
699               _dbus_warn ("Expected anonymous login or failed login, but some credentials were authorized\n");
700               goto out;
701             }
702         }
703       else if (_dbus_string_starts_with_c_str (&line,
704                                                "EXPECT_HAVE_SOME_CREDENTIALS"))
705         {
706           DBusCredentials *authorized_identity;
707           
708           authorized_identity = _dbus_auth_get_identity (auth);
709           if (_dbus_credentials_are_anonymous (authorized_identity))
710             {
711               _dbus_warn ("Expected to have some credentials, but we don't\n");
712               goto out;
713             }
714         }
715       else if (_dbus_string_starts_with_c_str (&line,
716                                                "EXPECT"))
717         {
718           DBusString expected;
719           
720           _dbus_string_delete_first_word (&line);
721
722           if (!_dbus_string_init (&expected))
723             {
724               _dbus_warn ("no mem to allocate string expected\n");
725               goto out;
726             }
727
728           if (!append_quoted_string (&expected, &line))
729             {
730               _dbus_warn ("failed to append quoted string line %d\n",
731                           line_no);
732               _dbus_string_free (&expected);
733               goto out;
734             }
735
736           if (_dbus_string_equal_len (&expected, &from_auth,
737                                       _dbus_string_get_length (&expected)))
738             {
739               _dbus_string_delete (&from_auth, 0,
740                                    _dbus_string_get_length (&expected));
741               _dbus_string_free (&expected);
742             }
743           else
744             {
745               _dbus_warn ("Expected exact string '%s' and have '%s'\n",
746                           _dbus_string_get_const_data (&expected),
747                           _dbus_string_get_const_data (&from_auth));
748               _dbus_string_free (&expected);
749               goto out;
750             }
751         }
752       else
753         goto parse_failed;
754
755       goto next_iteration; /* skip parse_failed */
756       
757     parse_failed:
758       {
759         _dbus_warn ("couldn't process line %d \"%s\"\n",
760                     line_no, _dbus_string_get_const_data (&line));
761         goto out;
762       }
763     }
764
765   if (auth == NULL)
766     {
767       _dbus_warn ("Auth script is bogus, did not even have CLIENT or SERVER\n");
768       goto out;
769     }
770   else if (state == DBUS_AUTH_STATE_AUTHENTICATED)
771     {
772       const DBusString *unused;
773
774       _dbus_auth_get_unused_bytes (auth, &unused);
775
776       if (_dbus_string_get_length (unused) > 0)
777         {
778           _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
779           goto out;
780         }
781     }
782
783   if (_dbus_string_get_length (&from_auth) > 0)
784     {
785       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
786       _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
787       goto out;
788     }
789   
790   retval = TRUE;
791   
792  out:
793   if (auth)
794     _dbus_auth_unref (auth);
795
796   _dbus_string_free (&file);
797   _dbus_string_free (&line);
798   _dbus_string_free (&from_auth);
799   
800   return retval;
801 }
802
803 /** @} */
804 #endif /* DBUS_BUILD_TESTS */