2003-02-19 Havoc Pennington <hp@pobox.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 1.2
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-marshal.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_WITH_UNUSED_BYTES"))
144     return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
145   else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
146     return DBUS_AUTH_STATE_AUTHENTICATED;
147   else
148     return -1;
149 }
150
151 static const char*
152 auth_state_to_string (DBusAuthState state)
153 {
154   switch (state)
155     {
156     case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
157       return "WAITING_FOR_INPUT";
158     case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
159       return "WAITING_FOR_MEMORY";
160     case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
161       return "HAVE_BYTES_TO_SEND";
162     case DBUS_AUTH_STATE_NEED_DISCONNECT:
163       return "NEED_DISCONNECT";
164     case DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES:
165       return "AUTHENTICATED_WITH_UNUSED_BYTES";
166     case DBUS_AUTH_STATE_AUTHENTICATED:
167       return "AUTHENTICATED";
168     }
169
170   return "unknown";
171 }
172
173 /**
174  * Runs an "auth script" which is a script for testing the
175  * authentication protocol. Scripts send and receive data, and then
176  * include assertions about the state of both ends of the connection
177  * after processing the data. A script succeeds if these assertions
178  * hold.
179  *
180  * @param filename the file containing the script to run
181  * @returns #TRUE if the script succeeds, #FALSE otherwise
182  */
183 dbus_bool_t
184 _dbus_auth_script_run (const DBusString *filename)
185 {
186   DBusString file;
187   DBusResultCode result;
188   DBusString line;
189   dbus_bool_t retval;
190   int line_no;
191   DBusAuth *auth;
192   DBusString from_auth;
193   DBusAuthState state;
194   
195   retval = FALSE;
196   auth = NULL;
197   
198   if (!_dbus_string_init (&file, _DBUS_INT_MAX))
199     return FALSE;
200
201   if (!_dbus_string_init (&line, _DBUS_INT_MAX))
202     {
203       _dbus_string_free (&file);
204       return FALSE;
205     }
206
207   if (!_dbus_string_init (&from_auth, _DBUS_INT_MAX))
208     {
209       _dbus_string_free (&file);
210       _dbus_string_free (&line);
211       return FALSE;
212     }
213   
214   if ((result = _dbus_file_get_contents (&file, filename)) != DBUS_RESULT_SUCCESS)
215     {
216       const char *s;
217       _dbus_string_get_const_data (filename, &s);
218       _dbus_warn ("Getting contents of %s failed: %s\n",
219                   s, dbus_result_to_string (result));
220                      
221       goto out;
222     }
223
224   state = DBUS_AUTH_STATE_NEED_DISCONNECT;
225   line_no = 0;
226  next_iteration:
227   while (_dbus_string_pop_line (&file, &line))
228     {      
229       line_no += 1;
230
231       _dbus_string_delete_leading_blanks (&line);
232
233       if (auth != NULL)
234         {
235           while ((state = _dbus_auth_do_work (auth)) ==
236                  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
237             {
238               const DBusString *tmp;
239               if (_dbus_auth_get_bytes_to_send (auth, &tmp))
240                 {
241                   int count = _dbus_string_get_length (tmp);
242
243                   if (_dbus_string_copy (tmp, 0, &from_auth,
244                                          _dbus_string_get_length (&from_auth)))
245                     _dbus_auth_bytes_sent (auth, count);
246                 }
247             }
248         }
249       
250       if (_dbus_string_get_length (&line) == 0)
251         {
252           /* empty line */
253           goto next_iteration;
254         }
255       else if (_dbus_string_starts_with_c_str (&line,
256                                                "#"))
257         {
258           /* Ignore this comment */
259           goto next_iteration;
260         }
261       else if (_dbus_string_starts_with_c_str (&line,
262                                                "CLIENT"))
263         {
264           DBusCredentials creds;
265           
266           if (auth != NULL)
267             {
268               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
269               goto out;
270             }
271
272           auth = _dbus_auth_client_new ();
273           if (auth == NULL)
274             {
275               _dbus_warn ("no memory to create DBusAuth\n");
276               goto out;
277             }
278
279           _dbus_credentials_from_current_process (&creds);
280           _dbus_auth_set_credentials (auth, &creds);
281         }
282       else if (_dbus_string_starts_with_c_str (&line,
283                                                "SERVER"))
284         {
285           DBusCredentials creds;
286           
287           if (auth != NULL)
288             {
289               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
290               goto out;
291             }
292
293           auth = _dbus_auth_server_new ();
294           if (auth == NULL)
295             {
296               _dbus_warn ("no memory to create DBusAuth\n");
297               goto out;
298             }
299
300           _dbus_credentials_from_current_process (&creds);
301           _dbus_auth_set_credentials (auth, &creds);
302         }
303       else if (auth == NULL)
304         {
305           _dbus_warn ("must specify CLIENT or SERVER\n");
306           goto out;
307
308         }
309       else if (_dbus_string_starts_with_c_str (&line,
310                                                "NO_CREDENTIALS"))
311         {
312           DBusCredentials creds = { -1, -1, -1 };
313           _dbus_auth_set_credentials (auth, &creds);
314         }
315       else if (_dbus_string_starts_with_c_str (&line,
316                                                "ROOT_CREDENTIALS"))
317         {
318           DBusCredentials creds = { -1, 0, 0 };
319           _dbus_auth_set_credentials (auth, &creds);          
320         }
321       else if (_dbus_string_starts_with_c_str (&line,
322                                                "SILLY_CREDENTIALS"))
323         {
324           DBusCredentials creds = { -1, 4312, 1232 };
325           _dbus_auth_set_credentials (auth, &creds);          
326         }
327       else if (_dbus_string_starts_with_c_str (&line,
328                                                "SEND"))
329         {
330           DBusString to_send;
331           
332           _dbus_string_delete_first_word (&line);
333
334           if (!_dbus_string_init (&to_send, _DBUS_INT_MAX))
335             {
336               _dbus_warn ("no memory to allocate string\n");
337               goto out;
338             }
339
340           if (!append_quoted_string (&to_send, &line))
341             {
342               _dbus_warn ("failed to append quoted string line %d\n",
343                           line_no);
344               _dbus_string_free (&to_send);
345               goto out;
346             }
347
348           {
349             const char *s4;
350             _dbus_string_get_const_data (&to_send, &s4);
351             _dbus_verbose ("Sending '%s'\n", s4);
352           }
353           
354           if (!_dbus_string_append (&to_send, "\r\n"))
355             {
356               _dbus_warn ("failed to append \r\n from line %d\n",
357                           line_no);
358               _dbus_string_free (&to_send);
359               goto out;
360             }
361
362           /* Replace USERNAME_BASE64 with our username in base64 */
363           {
364             int where;
365             
366             if (_dbus_string_find (&to_send, 0,
367                                    "USERNAME_BASE64", &where))
368               {
369                 DBusString username;
370
371                 if (!_dbus_string_init (&username, _DBUS_INT_MAX))
372                   {
373                     _dbus_warn ("no memory for username\n");
374                     _dbus_string_free (&to_send);
375                     goto out;
376                   }
377
378                 if (!_dbus_string_append_our_uid (&username))
379                   {
380                     _dbus_warn ("no memory for username\n");
381                     _dbus_string_free (&username);
382                     _dbus_string_free (&to_send);
383                     goto out;
384                   }
385
386                 _dbus_string_delete (&to_send, where, strlen ("USERNAME_BASE64"));
387                 
388                 if (!_dbus_string_base64_encode (&username, 0,
389                                                  &to_send, where))
390                   {
391                     _dbus_warn ("no memory to subst USERNAME_BASE64\n");
392                     _dbus_string_free (&username);
393                     _dbus_string_free (&to_send);
394                     goto out;
395                   }
396
397                 _dbus_string_free (&username);
398               }
399           }
400           
401           if (!_dbus_auth_bytes_received (auth, &to_send))
402             {
403               _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
404               _dbus_string_free (&to_send);
405               goto out;
406             }
407
408           _dbus_string_free (&to_send);
409         }
410       else if (_dbus_string_starts_with_c_str (&line,
411                                                "EXPECT_STATE"))
412         {
413           DBusAuthState expected;
414           
415           _dbus_string_delete_first_word (&line);
416
417           expected = auth_state_from_string (&line);
418           if (expected < 0)
419             {
420               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
421               goto parse_failed;
422             }
423
424           if (expected != state)
425             {
426               _dbus_warn ("expected auth state %s but got %s on line %d\n",
427                           auth_state_to_string (expected),
428                           auth_state_to_string (state),
429                           line_no);
430               goto out;
431             }
432         }
433       else if (_dbus_string_starts_with_c_str (&line,
434                                                "EXPECT_COMMAND"))
435         {
436           DBusString received;
437           
438           _dbus_string_delete_first_word (&line);
439
440           if (!_dbus_string_init (&received, _DBUS_INT_MAX))
441             {
442               _dbus_warn ("no mem to allocate string received\n");
443               goto out;
444             }
445
446           if (!_dbus_string_pop_line (&from_auth, &received))
447             {
448               const char *command;
449               _dbus_string_get_const_data (&line, &command);
450               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
451                           command, line_no);
452               _dbus_string_free (&received);
453               goto out;
454             }
455
456           if (!same_first_word (&received, &line))
457             {
458               const char *s1, *s2;
459               _dbus_string_get_const_data (&line, &s1);
460               _dbus_string_get_const_data (&received, &s2);
461               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
462                           line_no, s1, s2);
463               _dbus_string_free (&received);
464               goto out;
465             }
466           
467           _dbus_string_free (&received);
468         }
469       else if (_dbus_string_starts_with_c_str (&line,
470                                                "EXPECT_UNUSED"))
471         {
472           DBusString expected;
473           DBusString unused;
474           
475           _dbus_string_delete_first_word (&line);
476
477           if (!_dbus_string_init (&expected, _DBUS_INT_MAX))
478             {
479               _dbus_warn ("no mem to allocate string expected\n");
480               goto out;
481             }
482
483           if (!append_quoted_string (&expected, &line))
484             {
485               _dbus_warn ("failed to append quoted string line %d\n",
486                           line_no);
487               _dbus_string_free (&expected);
488               goto out;
489             }
490
491           if (!_dbus_string_init (&unused, _DBUS_INT_MAX))
492             {
493               _dbus_warn ("no mem to allocate string unused\n");
494               _dbus_string_free (&expected);
495               goto out;
496             }
497
498           if (!_dbus_auth_get_unused_bytes (auth, &unused))
499             {
500               _dbus_warn ("couldn't get unused bytes\n");
501               _dbus_string_free (&expected);
502               _dbus_string_free (&unused);
503               goto out;
504             }
505           
506           if (_dbus_string_equal (&expected, &unused))
507             {
508               _dbus_string_free (&expected);
509               _dbus_string_free (&unused);
510             }
511           else
512             {
513               const char *e1, *h1;
514               _dbus_string_get_const_data (&expected, &e1);
515               _dbus_string_get_const_data (&unused, &h1);
516               _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
517                           e1, h1);
518               _dbus_string_free (&expected);
519               _dbus_string_free (&unused);
520               goto out;
521             }
522         }
523       else if (_dbus_string_starts_with_c_str (&line,
524                                                "EXPECT"))
525         {
526           DBusString expected;
527           
528           _dbus_string_delete_first_word (&line);
529
530           if (!_dbus_string_init (&expected, _DBUS_INT_MAX))
531             {
532               _dbus_warn ("no mem to allocate string expected\n");
533               goto out;
534             }
535
536           if (!append_quoted_string (&expected, &line))
537             {
538               _dbus_warn ("failed to append quoted string line %d\n",
539                           line_no);
540               _dbus_string_free (&expected);
541               goto out;
542             }
543
544           if (_dbus_string_equal_len (&expected, &from_auth,
545                                       _dbus_string_get_length (&expected)))
546             {
547               _dbus_string_delete (&from_auth, 0,
548                                    _dbus_string_get_length (&expected));
549               _dbus_string_free (&expected);
550             }
551           else
552             {
553               const char *e1, *h1;
554               _dbus_string_get_const_data (&expected, &e1);
555               _dbus_string_get_const_data (&from_auth, &h1);
556               _dbus_warn ("Expected exact string '%s' and have '%s'\n",
557                           e1, h1);
558               _dbus_string_free (&expected);
559               goto out;
560             }
561         }
562       else
563         goto parse_failed;
564
565       goto next_iteration; /* skip parse_failed */
566       
567     parse_failed:
568       {
569         const char *s;
570         _dbus_string_get_const_data (&line, &s);
571         _dbus_warn ("couldn't process line %d \"%s\"\n",
572                     line_no, s);
573         goto out;
574       }
575     }
576
577   if (auth != NULL &&
578       state == DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES)
579     {
580       _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
581       goto out;
582     }
583
584   if (_dbus_string_get_length (&from_auth) > 0)
585     {
586       const char *s;
587       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
588       _dbus_string_get_const_data (&from_auth, &s);
589       _dbus_warn ("Leftover data: %s\n", s);
590       goto out;
591     }
592   
593   retval = TRUE;
594   
595  out:
596   if (auth)
597     _dbus_auth_unref (auth);
598
599   _dbus_string_free (&file);
600   _dbus_string_free (&line);
601   _dbus_string_free (&from_auth);
602   
603   return retval;
604 }
605
606 /** @} */
607 #endif /* DBUS_BUILD_TESTS */