2005-02-26 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / dbus / dbus-auth-script.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
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-internals.h"
32 #include "dbus-userdb.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 /**
213  * Runs an "auth script" which is a script for testing the
214  * authentication protocol. Scripts send and receive data, and then
215  * include assertions about the state of both ends of the connection
216  * after processing the data. A script succeeds if these assertions
217  * hold.
218  *
219  * @param filename the file containing the script to run
220  * @returns #TRUE if the script succeeds, #FALSE otherwise
221  */
222 dbus_bool_t
223 _dbus_auth_script_run (const DBusString *filename)
224 {
225   DBusString file;
226   DBusError error;
227   DBusString line;
228   dbus_bool_t retval;
229   int line_no;
230   DBusAuth *auth;
231   DBusString from_auth;
232   DBusAuthState state;
233   DBusString context;
234   DBusString guid;
235   
236   retval = FALSE;
237   auth = NULL;
238
239   _dbus_string_init_const (&guid, "5fa01f4202cd837709a3274ca0df9d00");
240   _dbus_string_init_const (&context, "org_freedesktop_test");
241   
242   if (!_dbus_string_init (&file))
243     return FALSE;
244
245   if (!_dbus_string_init (&line))
246     {
247       _dbus_string_free (&file);
248       return FALSE;
249     }
250
251   if (!_dbus_string_init (&from_auth))
252     {
253       _dbus_string_free (&file);
254       _dbus_string_free (&line);
255       return FALSE;
256     }
257
258   dbus_error_init (&error);
259   if (!_dbus_file_get_contents (&file, filename, &error))    {
260       _dbus_warn ("Getting contents of %s failed: %s\n",
261                   _dbus_string_get_const_data (filename), error.message);
262       dbus_error_free (&error);
263       goto out;
264     }
265
266   state = DBUS_AUTH_STATE_NEED_DISCONNECT;
267   line_no = 0;
268  next_iteration:
269   while (_dbus_string_pop_line (&file, &line))
270     {      
271       line_no += 1;
272
273       _dbus_string_delete_leading_blanks (&line);
274
275       if (auth != NULL)
276         {
277           while ((state = _dbus_auth_do_work (auth)) ==
278                  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
279             {
280               const DBusString *tmp;
281               if (_dbus_auth_get_bytes_to_send (auth, &tmp))
282                 {
283                   int count = _dbus_string_get_length (tmp);
284
285                   if (_dbus_string_copy (tmp, 0, &from_auth,
286                                          _dbus_string_get_length (&from_auth)))
287                     _dbus_auth_bytes_sent (auth, count);
288                 }
289             }
290         }
291       
292       if (_dbus_string_get_length (&line) == 0)
293         {
294           /* empty line */
295           goto next_iteration;
296         }
297       else if (_dbus_string_starts_with_c_str (&line,
298                                                "#"))
299         {
300           /* Ignore this comment */
301           goto next_iteration;
302         }
303       else if (_dbus_string_starts_with_c_str (&line,
304                                                "CLIENT"))
305         {
306           DBusCredentials creds;
307           
308           if (auth != NULL)
309             {
310               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
311               goto out;
312             }
313
314           auth = _dbus_auth_client_new ();
315           if (auth == NULL)
316             {
317               _dbus_warn ("no memory to create DBusAuth\n");
318               goto out;
319             }
320
321           /* test ref/unref */
322           _dbus_auth_ref (auth);
323           _dbus_auth_unref (auth);
324           
325           _dbus_credentials_from_current_process (&creds);
326           _dbus_auth_set_credentials (auth, &creds);
327         }
328       else if (_dbus_string_starts_with_c_str (&line,
329                                                "SERVER"))
330         {
331           DBusCredentials creds;
332           
333           if (auth != NULL)
334             {
335               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
336               goto out;
337             }
338
339           auth = _dbus_auth_server_new (&guid);
340           if (auth == NULL)
341             {
342               _dbus_warn ("no memory to create DBusAuth\n");
343               goto out;
344             }
345
346           /* test ref/unref */
347           _dbus_auth_ref (auth);
348           _dbus_auth_unref (auth);
349           
350           _dbus_credentials_from_current_process (&creds);
351           _dbus_auth_set_credentials (auth, &creds);
352           _dbus_auth_set_context (auth, &context);
353         }
354       else if (auth == NULL)
355         {
356           _dbus_warn ("must specify CLIENT or SERVER\n");
357           goto out;
358
359         }
360       else if (_dbus_string_starts_with_c_str (&line,
361                                                "NO_CREDENTIALS"))
362         {
363           DBusCredentials creds = { -1, -1, -1 };
364           _dbus_auth_set_credentials (auth, &creds);
365         }
366       else if (_dbus_string_starts_with_c_str (&line,
367                                                "ROOT_CREDENTIALS"))
368         {
369           DBusCredentials creds = { -1, 0, 0 };
370           _dbus_auth_set_credentials (auth, &creds);          
371         }
372       else if (_dbus_string_starts_with_c_str (&line,
373                                                "SILLY_CREDENTIALS"))
374         {
375           DBusCredentials creds = { -1, 4312, 1232 };
376           _dbus_auth_set_credentials (auth, &creds);          
377         }
378       else if (_dbus_string_starts_with_c_str (&line,
379                                                "ALLOWED_MECHS"))
380         {
381           char **mechs;
382
383           _dbus_string_delete_first_word (&line);
384           mechs = split_string (&line);
385           _dbus_auth_set_mechanisms (auth, (const char **) mechs);
386           dbus_free_string_array (mechs);
387         }
388       else if (_dbus_string_starts_with_c_str (&line,
389                                                "SEND"))
390         {
391           DBusString to_send;
392           
393           _dbus_string_delete_first_word (&line);
394
395           if (!_dbus_string_init (&to_send))
396             {
397               _dbus_warn ("no memory to allocate string\n");
398               goto out;
399             }
400
401           if (!append_quoted_string (&to_send, &line))
402             {
403               _dbus_warn ("failed to append quoted string line %d\n",
404                           line_no);
405               _dbus_string_free (&to_send);
406               goto out;
407             }
408
409           _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
410           
411           if (!_dbus_string_append (&to_send, "\r\n"))
412             {
413               _dbus_warn ("failed to append \r\n from line %d\n",
414                           line_no);
415               _dbus_string_free (&to_send);
416               goto out;
417             }
418
419           /* Replace USERID_HEX with our username in hex */
420           {
421             int where;
422             
423             if (_dbus_string_find (&to_send, 0,
424                                    "USERID_HEX", &where))
425               {
426                 DBusString username;
427
428                 if (!_dbus_string_init (&username))
429                   {
430                     _dbus_warn ("no memory for userid\n");
431                     _dbus_string_free (&to_send);
432                     goto out;
433                   }
434
435                 if (!_dbus_string_append_uint (&username,
436                                                _dbus_getuid ()))
437                   {
438                     _dbus_warn ("no memory for userid\n");
439                     _dbus_string_free (&username);
440                     _dbus_string_free (&to_send);
441                     goto out;
442                   }
443
444                 _dbus_string_delete (&to_send, where, strlen ("USERID_HEX"));
445                 
446                 if (!_dbus_string_hex_encode (&username, 0,
447                                               &to_send, where))
448                   {
449                     _dbus_warn ("no memory to subst USERID_HEX\n");
450                     _dbus_string_free (&username);
451                     _dbus_string_free (&to_send);
452                     goto out;
453                   }
454
455                 _dbus_string_free (&username);
456               }
457             else if (_dbus_string_find (&to_send, 0,
458                                         "USERNAME_HEX", &where))
459               {
460                 DBusString username;
461                 const DBusString *u;
462                 
463                 if (!_dbus_string_init (&username))
464                   {
465                     _dbus_warn ("no memory for username\n");
466                     _dbus_string_free (&to_send);
467                     goto out;
468                   }
469
470                 if (!_dbus_username_from_current_process (&u) ||
471                     !_dbus_string_copy (u, 0, &username,
472                                         _dbus_string_get_length (&username)))
473                   {
474                     _dbus_warn ("no memory for username\n");
475                     _dbus_string_free (&username);
476                     _dbus_string_free (&to_send);
477                     goto out;
478                   }
479
480                 _dbus_string_delete (&to_send, where, strlen ("USERNAME_HEX"));
481                 
482                 if (!_dbus_string_hex_encode (&username, 0,
483                                               &to_send, where))
484                   {
485                     _dbus_warn ("no memory to subst USERNAME_HEX\n");
486                     _dbus_string_free (&username);
487                     _dbus_string_free (&to_send);
488                     goto out;
489                   }
490
491                 _dbus_string_free (&username);
492               }
493           }
494
495           {
496             DBusString *buffer;
497
498             _dbus_auth_get_buffer (auth, &buffer);
499             if (!_dbus_string_copy (&to_send, 0,
500                                     buffer, _dbus_string_get_length (buffer)))
501               {
502                 _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
503                 _dbus_string_free (&to_send);
504                 _dbus_auth_return_buffer (auth, buffer, 0);
505                 goto out;
506               }
507
508             _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
509           }
510           
511           _dbus_string_free (&to_send);
512         }
513       else if (_dbus_string_starts_with_c_str (&line,
514                                                "EXPECT_STATE"))
515         {
516           DBusAuthState expected;
517           
518           _dbus_string_delete_first_word (&line);
519
520           expected = auth_state_from_string (&line);
521           if (expected < 0)
522             {
523               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
524               goto parse_failed;
525             }
526
527           if (expected != state)
528             {
529               _dbus_warn ("expected auth state %s but got %s on line %d\n",
530                           auth_state_to_string (expected),
531                           auth_state_to_string (state),
532                           line_no);
533               goto out;
534             }
535         }
536       else if (_dbus_string_starts_with_c_str (&line,
537                                                "EXPECT_COMMAND"))
538         {
539           DBusString received;
540           
541           _dbus_string_delete_first_word (&line);
542
543           if (!_dbus_string_init (&received))
544             {
545               _dbus_warn ("no mem to allocate string received\n");
546               goto out;
547             }
548
549           if (!_dbus_string_pop_line (&from_auth, &received))
550             {
551               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
552                           _dbus_string_get_const_data (&line), line_no);
553               _dbus_string_free (&received);
554               goto out;
555             }
556
557           if (!same_first_word (&received, &line))
558             {
559               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
560                           line_no,
561                           _dbus_string_get_const_data (&line),
562                           _dbus_string_get_const_data (&received));
563               _dbus_string_free (&received);
564               goto out;
565             }
566           
567           _dbus_string_free (&received);
568         }
569       else if (_dbus_string_starts_with_c_str (&line,
570                                                "EXPECT_UNUSED"))
571         {
572           DBusString expected;
573           const DBusString *unused;
574           
575           _dbus_string_delete_first_word (&line);
576
577           if (!_dbus_string_init (&expected))
578             {
579               _dbus_warn ("no mem to allocate string expected\n");
580               goto out;
581             }
582
583           if (!append_quoted_string (&expected, &line))
584             {
585               _dbus_warn ("failed to append quoted string line %d\n",
586                           line_no);
587               _dbus_string_free (&expected);
588               goto out;
589             }
590
591           _dbus_auth_get_unused_bytes (auth, &unused);
592           
593           if (_dbus_string_equal (&expected, unused))
594             {
595               _dbus_auth_delete_unused_bytes (auth);
596               _dbus_string_free (&expected);
597             }
598           else
599             {
600               _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
601                           _dbus_string_get_const_data (&expected),
602                           _dbus_string_get_const_data (unused));
603               _dbus_string_free (&expected);
604               goto out;
605             }
606         }
607       else if (_dbus_string_starts_with_c_str (&line,
608                                                "EXPECT"))
609         {
610           DBusString expected;
611           
612           _dbus_string_delete_first_word (&line);
613
614           if (!_dbus_string_init (&expected))
615             {
616               _dbus_warn ("no mem to allocate string expected\n");
617               goto out;
618             }
619
620           if (!append_quoted_string (&expected, &line))
621             {
622               _dbus_warn ("failed to append quoted string line %d\n",
623                           line_no);
624               _dbus_string_free (&expected);
625               goto out;
626             }
627
628           if (_dbus_string_equal_len (&expected, &from_auth,
629                                       _dbus_string_get_length (&expected)))
630             {
631               _dbus_string_delete (&from_auth, 0,
632                                    _dbus_string_get_length (&expected));
633               _dbus_string_free (&expected);
634             }
635           else
636             {
637               _dbus_warn ("Expected exact string '%s' and have '%s'\n",
638                           _dbus_string_get_const_data (&expected),
639                           _dbus_string_get_const_data (&from_auth));
640               _dbus_string_free (&expected);
641               goto out;
642             }
643         }
644       else
645         goto parse_failed;
646
647       goto next_iteration; /* skip parse_failed */
648       
649     parse_failed:
650       {
651         _dbus_warn ("couldn't process line %d \"%s\"\n",
652                     line_no, _dbus_string_get_const_data (&line));
653         goto out;
654       }
655     }
656
657   if (auth != NULL &&
658       state == DBUS_AUTH_STATE_AUTHENTICATED)
659     {
660       const DBusString *unused;
661
662       _dbus_auth_get_unused_bytes (auth, &unused);
663
664       if (_dbus_string_get_length (unused) > 0)
665         {
666           _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
667           goto out;
668         }
669     }
670
671   if (_dbus_string_get_length (&from_auth) > 0)
672     {
673       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
674       _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
675       goto out;
676     }
677   
678   retval = TRUE;
679   
680  out:
681   if (auth)
682     _dbus_auth_unref (auth);
683
684   _dbus_string_free (&file);
685   _dbus_string_free (&line);
686   _dbus_string_free (&from_auth);
687   
688   return retval;
689 }
690
691 /** @} */
692 #endif /* DBUS_BUILD_TESTS */