2003-06-04 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 #include "dbus-userdb.h"
34
35 /**
36  * @defgroup DBusAuthScript code for running unit test scripts for DBusAuth
37  * @ingroup  DBusInternals
38  * @brief DBusAuth unit test scripting
39  *
40  * The code in here is used for unit testing, it loads
41  * up a script that tests DBusAuth.
42  *
43  * @{
44  */
45
46 /* this is slightly different from the other append_quoted_string
47  * in dbus-message-builder.c
48  */
49 static dbus_bool_t
50 append_quoted_string (DBusString       *dest,
51                       const DBusString *quoted)
52 {
53   dbus_bool_t in_quotes = FALSE;
54   dbus_bool_t in_backslash = FALSE;
55   int i;
56
57   i = 0;
58   while (i < _dbus_string_get_length (quoted))
59     {
60       unsigned char b;
61
62       b = _dbus_string_get_byte (quoted, i);
63
64       if (in_backslash)
65         {
66           unsigned char a;
67           
68           if (b == 'r')
69             a = '\r';
70           else if (b == 'n')
71             a = '\n';
72           else if (b == '\\')
73             a = '\\';
74           else
75             {
76               _dbus_warn ("bad backslashed byte %c\n", b);
77               return FALSE;
78             }
79
80           if (!_dbus_string_append_byte (dest, a))
81             return FALSE;
82           
83           in_backslash = FALSE;
84         }
85       else if (b == '\\')
86         {
87           in_backslash = TRUE;
88         }
89       else if (in_quotes)
90         {
91           if (b == '\'')
92             in_quotes = FALSE;
93           else
94             {
95               if (!_dbus_string_append_byte (dest, b))
96                 return FALSE;
97             }
98         }
99       else
100         {
101           if (b == '\'')
102             in_quotes = TRUE;
103           else if (b == ' ' || b == '\n' || b == '\t')
104             break; /* end on whitespace if not quoted */
105           else
106             {
107               if (!_dbus_string_append_byte (dest, b))
108                 return FALSE;
109             }
110         }
111       
112       ++i;
113     }
114
115   return TRUE;
116 }
117
118 static dbus_bool_t
119 same_first_word (const DBusString *a,
120                  const DBusString *b)
121 {
122   int first_a_blank, first_b_blank;
123
124   _dbus_string_find_blank (a, 0, &first_a_blank);
125   _dbus_string_find_blank (b, 0, &first_b_blank);
126
127   if (first_a_blank != first_b_blank)
128     return FALSE;
129
130   return _dbus_string_equal_len (a, b, first_a_blank);
131 }
132
133 static DBusAuthState
134 auth_state_from_string (const DBusString *str)
135
136   if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_INPUT"))
137     return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
138   else if (_dbus_string_starts_with_c_str (str, "WAITING_FOR_MEMORY"))
139     return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
140   else if (_dbus_string_starts_with_c_str (str, "HAVE_BYTES_TO_SEND"))
141     return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
142   else if (_dbus_string_starts_with_c_str (str, "NEED_DISCONNECT"))
143     return DBUS_AUTH_STATE_NEED_DISCONNECT;
144   else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED_WITH_UNUSED_BYTES"))
145     return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
146   else if (_dbus_string_starts_with_c_str (str, "AUTHENTICATED"))
147     return DBUS_AUTH_STATE_AUTHENTICATED;
148   else
149     return -1;
150 }
151
152 static const char*
153 auth_state_to_string (DBusAuthState state)
154 {
155   switch (state)
156     {
157     case DBUS_AUTH_STATE_WAITING_FOR_INPUT:
158       return "WAITING_FOR_INPUT";
159     case DBUS_AUTH_STATE_WAITING_FOR_MEMORY:
160       return "WAITING_FOR_MEMORY";
161     case DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND:
162       return "HAVE_BYTES_TO_SEND";
163     case DBUS_AUTH_STATE_NEED_DISCONNECT:
164       return "NEED_DISCONNECT";
165     case DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES:
166       return "AUTHENTICATED_WITH_UNUSED_BYTES";
167     case DBUS_AUTH_STATE_AUTHENTICATED:
168       return "AUTHENTICATED";
169     }
170
171   return "unknown";
172 }
173
174 /**
175  * Runs an "auth script" which is a script for testing the
176  * authentication protocol. Scripts send and receive data, and then
177  * include assertions about the state of both ends of the connection
178  * after processing the data. A script succeeds if these assertions
179  * hold.
180  *
181  * @param filename the file containing the script to run
182  * @returns #TRUE if the script succeeds, #FALSE otherwise
183  */
184 dbus_bool_t
185 _dbus_auth_script_run (const DBusString *filename)
186 {
187   DBusString file;
188   DBusError error;
189   DBusString line;
190   dbus_bool_t retval;
191   int line_no;
192   DBusAuth *auth;
193   DBusString from_auth;
194   DBusAuthState state;
195   DBusString context;
196   
197   retval = FALSE;
198   auth = NULL;
199
200   _dbus_string_init_const (&context, "org_freedesktop_test");
201   
202   if (!_dbus_string_init (&file))
203     return FALSE;
204
205   if (!_dbus_string_init (&line))
206     {
207       _dbus_string_free (&file);
208       return FALSE;
209     }
210
211   if (!_dbus_string_init (&from_auth))
212     {
213       _dbus_string_free (&file);
214       _dbus_string_free (&line);
215       return FALSE;
216     }
217
218   dbus_error_init (&error);
219   if (!_dbus_file_get_contents (&file, filename, &error))    {
220       _dbus_warn ("Getting contents of %s failed: %s\n",
221                   _dbus_string_get_const_data (filename), error.message);
222       dbus_error_free (&error);
223       goto out;
224     }
225
226   state = DBUS_AUTH_STATE_NEED_DISCONNECT;
227   line_no = 0;
228  next_iteration:
229   while (_dbus_string_pop_line (&file, &line))
230     {      
231       line_no += 1;
232
233       _dbus_string_delete_leading_blanks (&line);
234
235       if (auth != NULL)
236         {
237           while ((state = _dbus_auth_do_work (auth)) ==
238                  DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND)
239             {
240               const DBusString *tmp;
241               if (_dbus_auth_get_bytes_to_send (auth, &tmp))
242                 {
243                   int count = _dbus_string_get_length (tmp);
244
245                   if (_dbus_string_copy (tmp, 0, &from_auth,
246                                          _dbus_string_get_length (&from_auth)))
247                     _dbus_auth_bytes_sent (auth, count);
248                 }
249             }
250         }
251       
252       if (_dbus_string_get_length (&line) == 0)
253         {
254           /* empty line */
255           goto next_iteration;
256         }
257       else if (_dbus_string_starts_with_c_str (&line,
258                                                "#"))
259         {
260           /* Ignore this comment */
261           goto next_iteration;
262         }
263       else if (_dbus_string_starts_with_c_str (&line,
264                                                "CLIENT"))
265         {
266           DBusCredentials creds;
267           
268           if (auth != NULL)
269             {
270               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
271               goto out;
272             }
273
274           auth = _dbus_auth_client_new ();
275           if (auth == NULL)
276             {
277               _dbus_warn ("no memory to create DBusAuth\n");
278               goto out;
279             }
280
281           /* test ref/unref */
282           _dbus_auth_ref (auth);
283           _dbus_auth_unref (auth);
284           
285           _dbus_credentials_from_current_process (&creds);
286           _dbus_auth_set_credentials (auth, &creds);
287         }
288       else if (_dbus_string_starts_with_c_str (&line,
289                                                "SERVER"))
290         {
291           DBusCredentials creds;
292           
293           if (auth != NULL)
294             {
295               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
296               goto out;
297             }
298
299           auth = _dbus_auth_server_new ();
300           if (auth == NULL)
301             {
302               _dbus_warn ("no memory to create DBusAuth\n");
303               goto out;
304             }
305
306           /* test ref/unref */
307           _dbus_auth_ref (auth);
308           _dbus_auth_unref (auth);
309           
310           _dbus_credentials_from_current_process (&creds);
311           _dbus_auth_set_credentials (auth, &creds);
312           _dbus_auth_set_context (auth, &context);
313         }
314       else if (auth == NULL)
315         {
316           _dbus_warn ("must specify CLIENT or SERVER\n");
317           goto out;
318
319         }
320       else if (_dbus_string_starts_with_c_str (&line,
321                                                "NO_CREDENTIALS"))
322         {
323           DBusCredentials creds = { -1, -1, -1 };
324           _dbus_auth_set_credentials (auth, &creds);
325         }
326       else if (_dbus_string_starts_with_c_str (&line,
327                                                "ROOT_CREDENTIALS"))
328         {
329           DBusCredentials creds = { -1, 0, 0 };
330           _dbus_auth_set_credentials (auth, &creds);          
331         }
332       else if (_dbus_string_starts_with_c_str (&line,
333                                                "SILLY_CREDENTIALS"))
334         {
335           DBusCredentials creds = { -1, 4312, 1232 };
336           _dbus_auth_set_credentials (auth, &creds);          
337         }
338       else if (_dbus_string_starts_with_c_str (&line,
339                                                "SEND"))
340         {
341           DBusString to_send;
342           
343           _dbus_string_delete_first_word (&line);
344
345           if (!_dbus_string_init (&to_send))
346             {
347               _dbus_warn ("no memory to allocate string\n");
348               goto out;
349             }
350
351           if (!append_quoted_string (&to_send, &line))
352             {
353               _dbus_warn ("failed to append quoted string line %d\n",
354                           line_no);
355               _dbus_string_free (&to_send);
356               goto out;
357             }
358
359           _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
360           
361           if (!_dbus_string_append (&to_send, "\r\n"))
362             {
363               _dbus_warn ("failed to append \r\n from line %d\n",
364                           line_no);
365               _dbus_string_free (&to_send);
366               goto out;
367             }
368
369           /* Replace USERID_BASE64 with our username in base64 */
370           {
371             int where;
372             
373             if (_dbus_string_find (&to_send, 0,
374                                    "USERID_BASE64", &where))
375               {
376                 DBusString username;
377
378                 if (!_dbus_string_init (&username))
379                   {
380                     _dbus_warn ("no memory for userid\n");
381                     _dbus_string_free (&to_send);
382                     goto out;
383                   }
384
385                 if (!_dbus_string_append_uint (&username,
386                                                _dbus_getuid ()))
387                   {
388                     _dbus_warn ("no memory for userid\n");
389                     _dbus_string_free (&username);
390                     _dbus_string_free (&to_send);
391                     goto out;
392                   }
393
394                 _dbus_string_delete (&to_send, where, strlen ("USERID_BASE64"));
395                 
396                 if (!_dbus_string_base64_encode (&username, 0,
397                                                  &to_send, where))
398                   {
399                     _dbus_warn ("no memory to subst USERID_BASE64\n");
400                     _dbus_string_free (&username);
401                     _dbus_string_free (&to_send);
402                     goto out;
403                   }
404
405                 _dbus_string_free (&username);
406               }
407             else if (_dbus_string_find (&to_send, 0,
408                                         "USERNAME_BASE64", &where))
409               {
410                 DBusString username;
411                 const DBusString *u;
412                 
413                 if (!_dbus_string_init (&username))
414                   {
415                     _dbus_warn ("no memory for username\n");
416                     _dbus_string_free (&to_send);
417                     goto out;
418                   }
419
420                 if (!_dbus_username_from_current_process (&u) ||
421                     !_dbus_string_copy (u, 0, &username,
422                                         _dbus_string_get_length (&username)))
423                   {
424                     _dbus_warn ("no memory for username\n");
425                     _dbus_string_free (&username);
426                     _dbus_string_free (&to_send);
427                     goto out;
428                   }
429
430                 _dbus_string_delete (&to_send, where, strlen ("USERNAME_BASE64"));
431                 
432                 if (!_dbus_string_base64_encode (&username, 0,
433                                                  &to_send, where))
434                   {
435                     _dbus_warn ("no memory to subst USERNAME_BASE64\n");
436                     _dbus_string_free (&username);
437                     _dbus_string_free (&to_send);
438                     goto out;
439                   }
440
441                 _dbus_string_free (&username);
442               }
443           }
444
445           {
446             DBusString *buffer;
447
448             _dbus_auth_get_buffer (auth, &buffer);
449             if (!_dbus_string_copy (&to_send, 0,
450                                     buffer, _dbus_string_get_length (buffer)))
451               {
452                 _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
453                 _dbus_string_free (&to_send);
454                 _dbus_auth_return_buffer (auth, buffer, 0);
455                 goto out;
456               }
457
458             _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
459           }
460           
461           _dbus_string_free (&to_send);
462         }
463       else if (_dbus_string_starts_with_c_str (&line,
464                                                "EXPECT_STATE"))
465         {
466           DBusAuthState expected;
467           
468           _dbus_string_delete_first_word (&line);
469
470           expected = auth_state_from_string (&line);
471           if (expected < 0)
472             {
473               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
474               goto parse_failed;
475             }
476
477           if (expected != state)
478             {
479               _dbus_warn ("expected auth state %s but got %s on line %d\n",
480                           auth_state_to_string (expected),
481                           auth_state_to_string (state),
482                           line_no);
483               goto out;
484             }
485         }
486       else if (_dbus_string_starts_with_c_str (&line,
487                                                "EXPECT_COMMAND"))
488         {
489           DBusString received;
490           
491           _dbus_string_delete_first_word (&line);
492
493           if (!_dbus_string_init (&received))
494             {
495               _dbus_warn ("no mem to allocate string received\n");
496               goto out;
497             }
498
499           if (!_dbus_string_pop_line (&from_auth, &received))
500             {
501               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
502                           _dbus_string_get_const_data (&line), line_no);
503               _dbus_string_free (&received);
504               goto out;
505             }
506
507           if (!same_first_word (&received, &line))
508             {
509               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
510                           line_no,
511                           _dbus_string_get_const_data (&line),
512                           _dbus_string_get_const_data (&received));
513               _dbus_string_free (&received);
514               goto out;
515             }
516           
517           _dbus_string_free (&received);
518         }
519       else if (_dbus_string_starts_with_c_str (&line,
520                                                "EXPECT_UNUSED"))
521         {
522           DBusString expected;
523           const DBusString *unused;
524           
525           _dbus_string_delete_first_word (&line);
526
527           if (!_dbus_string_init (&expected))
528             {
529               _dbus_warn ("no mem to allocate string expected\n");
530               goto out;
531             }
532
533           if (!append_quoted_string (&expected, &line))
534             {
535               _dbus_warn ("failed to append quoted string line %d\n",
536                           line_no);
537               _dbus_string_free (&expected);
538               goto out;
539             }
540
541           _dbus_auth_get_unused_bytes (auth, &unused);
542           
543           if (_dbus_string_equal (&expected, unused))
544             {
545               _dbus_auth_delete_unused_bytes (auth);
546               _dbus_string_free (&expected);
547             }
548           else
549             {
550               _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
551                           _dbus_string_get_const_data (&expected),
552                           _dbus_string_get_const_data (unused));
553               _dbus_string_free (&expected);
554               goto out;
555             }
556         }
557       else if (_dbus_string_starts_with_c_str (&line,
558                                                "EXPECT"))
559         {
560           DBusString expected;
561           
562           _dbus_string_delete_first_word (&line);
563
564           if (!_dbus_string_init (&expected))
565             {
566               _dbus_warn ("no mem to allocate string expected\n");
567               goto out;
568             }
569
570           if (!append_quoted_string (&expected, &line))
571             {
572               _dbus_warn ("failed to append quoted string line %d\n",
573                           line_no);
574               _dbus_string_free (&expected);
575               goto out;
576             }
577
578           if (_dbus_string_equal_len (&expected, &from_auth,
579                                       _dbus_string_get_length (&expected)))
580             {
581               _dbus_string_delete (&from_auth, 0,
582                                    _dbus_string_get_length (&expected));
583               _dbus_string_free (&expected);
584             }
585           else
586             {
587               _dbus_warn ("Expected exact string '%s' and have '%s'\n",
588                           _dbus_string_get_const_data (&expected),
589                           _dbus_string_get_const_data (&from_auth));
590               _dbus_string_free (&expected);
591               goto out;
592             }
593         }
594       else
595         goto parse_failed;
596
597       goto next_iteration; /* skip parse_failed */
598       
599     parse_failed:
600       {
601         _dbus_warn ("couldn't process line %d \"%s\"\n",
602                     line_no, _dbus_string_get_const_data (&line));
603         goto out;
604       }
605     }
606
607   if (auth != NULL &&
608       state == DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES)
609     {
610       _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
611       goto out;
612     }
613
614   if (_dbus_string_get_length (&from_auth) > 0)
615     {
616       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
617       _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
618       goto out;
619     }
620   
621   retval = TRUE;
622   
623  out:
624   if (auth)
625     _dbus_auth_unref (auth);
626
627   _dbus_string_free (&file);
628   _dbus_string_free (&line);
629   _dbus_string_free (&from_auth);
630   
631   return retval;
632 }
633
634 /** @} */
635 #endif /* DBUS_BUILD_TESTS */