2003-04-24 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 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           _dbus_credentials_from_current_process (&creds);
282           _dbus_auth_set_credentials (auth, &creds);
283         }
284       else if (_dbus_string_starts_with_c_str (&line,
285                                                "SERVER"))
286         {
287           DBusCredentials creds;
288           
289           if (auth != NULL)
290             {
291               _dbus_warn ("already created a DBusAuth (CLIENT or SERVER given twice)\n");
292               goto out;
293             }
294
295           auth = _dbus_auth_server_new ();
296           if (auth == NULL)
297             {
298               _dbus_warn ("no memory to create DBusAuth\n");
299               goto out;
300             }
301
302           _dbus_credentials_from_current_process (&creds);
303           _dbus_auth_set_credentials (auth, &creds);
304           _dbus_auth_set_context (auth, &context);
305         }
306       else if (auth == NULL)
307         {
308           _dbus_warn ("must specify CLIENT or SERVER\n");
309           goto out;
310
311         }
312       else if (_dbus_string_starts_with_c_str (&line,
313                                                "NO_CREDENTIALS"))
314         {
315           DBusCredentials creds = { -1, -1, -1 };
316           _dbus_auth_set_credentials (auth, &creds);
317         }
318       else if (_dbus_string_starts_with_c_str (&line,
319                                                "ROOT_CREDENTIALS"))
320         {
321           DBusCredentials creds = { -1, 0, 0 };
322           _dbus_auth_set_credentials (auth, &creds);          
323         }
324       else if (_dbus_string_starts_with_c_str (&line,
325                                                "SILLY_CREDENTIALS"))
326         {
327           DBusCredentials creds = { -1, 4312, 1232 };
328           _dbus_auth_set_credentials (auth, &creds);          
329         }
330       else if (_dbus_string_starts_with_c_str (&line,
331                                                "SEND"))
332         {
333           DBusString to_send;
334           
335           _dbus_string_delete_first_word (&line);
336
337           if (!_dbus_string_init (&to_send))
338             {
339               _dbus_warn ("no memory to allocate string\n");
340               goto out;
341             }
342
343           if (!append_quoted_string (&to_send, &line))
344             {
345               _dbus_warn ("failed to append quoted string line %d\n",
346                           line_no);
347               _dbus_string_free (&to_send);
348               goto out;
349             }
350
351           _dbus_verbose ("Sending '%s'\n", _dbus_string_get_const_data (&to_send));
352           
353           if (!_dbus_string_append (&to_send, "\r\n"))
354             {
355               _dbus_warn ("failed to append \r\n from line %d\n",
356                           line_no);
357               _dbus_string_free (&to_send);
358               goto out;
359             }
360
361           /* Replace USERID_BASE64 with our username in base64 */
362           {
363             int where;
364             
365             if (_dbus_string_find (&to_send, 0,
366                                    "USERID_BASE64", &where))
367               {
368                 DBusString username;
369
370                 if (!_dbus_string_init (&username))
371                   {
372                     _dbus_warn ("no memory for userid\n");
373                     _dbus_string_free (&to_send);
374                     goto out;
375                   }
376
377                 if (!_dbus_string_append_uint (&username,
378                                                _dbus_getuid ()))
379                   {
380                     _dbus_warn ("no memory for userid\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 ("USERID_BASE64"));
387                 
388                 if (!_dbus_string_base64_encode (&username, 0,
389                                                  &to_send, where))
390                   {
391                     _dbus_warn ("no memory to subst USERID_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             else if (_dbus_string_find (&to_send, 0,
400                                         "USERNAME_BASE64", &where))
401               {
402                 DBusString username;
403                 const DBusString *u;
404                 
405                 if (!_dbus_string_init (&username))
406                   {
407                     _dbus_warn ("no memory for username\n");
408                     _dbus_string_free (&to_send);
409                     goto out;
410                   }
411
412                 if (!_dbus_username_from_current_process (&u) ||
413                     !_dbus_string_copy (u, 0, &username,
414                                         _dbus_string_get_length (&username)))
415                   {
416                     _dbus_warn ("no memory for username\n");
417                     _dbus_string_free (&username);
418                     _dbus_string_free (&to_send);
419                     goto out;
420                   }
421
422                 _dbus_string_delete (&to_send, where, strlen ("USERNAME_BASE64"));
423                 
424                 if (!_dbus_string_base64_encode (&username, 0,
425                                                  &to_send, where))
426                   {
427                     _dbus_warn ("no memory to subst USERNAME_BASE64\n");
428                     _dbus_string_free (&username);
429                     _dbus_string_free (&to_send);
430                     goto out;
431                   }
432
433                 _dbus_string_free (&username);
434               }
435           }
436
437           {
438             DBusString *buffer;
439
440             _dbus_auth_get_buffer (auth, &buffer);
441             if (!_dbus_string_copy (&to_send, 0,
442                                     buffer, _dbus_string_get_length (buffer)))
443               {
444                 _dbus_warn ("not enough memory to call bytes_received, or can't add bytes to auth object already in end state\n");
445                 _dbus_string_free (&to_send);
446                 _dbus_auth_return_buffer (auth, buffer, 0);
447                 goto out;
448               }
449
450             _dbus_auth_return_buffer (auth, buffer, _dbus_string_get_length (&to_send));
451           }
452           
453           _dbus_string_free (&to_send);
454         }
455       else if (_dbus_string_starts_with_c_str (&line,
456                                                "EXPECT_STATE"))
457         {
458           DBusAuthState expected;
459           
460           _dbus_string_delete_first_word (&line);
461
462           expected = auth_state_from_string (&line);
463           if (expected < 0)
464             {
465               _dbus_warn ("bad auth state given to EXPECT_STATE\n");
466               goto parse_failed;
467             }
468
469           if (expected != state)
470             {
471               _dbus_warn ("expected auth state %s but got %s on line %d\n",
472                           auth_state_to_string (expected),
473                           auth_state_to_string (state),
474                           line_no);
475               goto out;
476             }
477         }
478       else if (_dbus_string_starts_with_c_str (&line,
479                                                "EXPECT_COMMAND"))
480         {
481           DBusString received;
482           
483           _dbus_string_delete_first_word (&line);
484
485           if (!_dbus_string_init (&received))
486             {
487               _dbus_warn ("no mem to allocate string received\n");
488               goto out;
489             }
490
491           if (!_dbus_string_pop_line (&from_auth, &received))
492             {
493               _dbus_warn ("no line popped from the DBusAuth being tested, expected command %s on line %d\n",
494                           _dbus_string_get_const_data (&line), line_no);
495               _dbus_string_free (&received);
496               goto out;
497             }
498
499           if (!same_first_word (&received, &line))
500             {
501               _dbus_warn ("line %d expected command '%s' and got '%s'\n",
502                           line_no,
503                           _dbus_string_get_const_data (&line),
504                           _dbus_string_get_const_data (&received));
505               _dbus_string_free (&received);
506               goto out;
507             }
508           
509           _dbus_string_free (&received);
510         }
511       else if (_dbus_string_starts_with_c_str (&line,
512                                                "EXPECT_UNUSED"))
513         {
514           DBusString expected;
515           const DBusString *unused;
516           
517           _dbus_string_delete_first_word (&line);
518
519           if (!_dbus_string_init (&expected))
520             {
521               _dbus_warn ("no mem to allocate string expected\n");
522               goto out;
523             }
524
525           if (!append_quoted_string (&expected, &line))
526             {
527               _dbus_warn ("failed to append quoted string line %d\n",
528                           line_no);
529               _dbus_string_free (&expected);
530               goto out;
531             }
532
533           _dbus_auth_get_unused_bytes (auth, &unused);
534           
535           if (_dbus_string_equal (&expected, unused))
536             {
537               _dbus_auth_delete_unused_bytes (auth);
538               _dbus_string_free (&expected);
539             }
540           else
541             {
542               _dbus_warn ("Expected unused bytes '%s' and have '%s'\n",
543                           _dbus_string_get_const_data (&expected),
544                           _dbus_string_get_const_data (unused));
545               _dbus_string_free (&expected);
546               goto out;
547             }
548         }
549       else if (_dbus_string_starts_with_c_str (&line,
550                                                "EXPECT"))
551         {
552           DBusString expected;
553           
554           _dbus_string_delete_first_word (&line);
555
556           if (!_dbus_string_init (&expected))
557             {
558               _dbus_warn ("no mem to allocate string expected\n");
559               goto out;
560             }
561
562           if (!append_quoted_string (&expected, &line))
563             {
564               _dbus_warn ("failed to append quoted string line %d\n",
565                           line_no);
566               _dbus_string_free (&expected);
567               goto out;
568             }
569
570           if (_dbus_string_equal_len (&expected, &from_auth,
571                                       _dbus_string_get_length (&expected)))
572             {
573               _dbus_string_delete (&from_auth, 0,
574                                    _dbus_string_get_length (&expected));
575               _dbus_string_free (&expected);
576             }
577           else
578             {
579               _dbus_warn ("Expected exact string '%s' and have '%s'\n",
580                           _dbus_string_get_const_data (&expected),
581                           _dbus_string_get_const_data (&from_auth));
582               _dbus_string_free (&expected);
583               goto out;
584             }
585         }
586       else
587         goto parse_failed;
588
589       goto next_iteration; /* skip parse_failed */
590       
591     parse_failed:
592       {
593         _dbus_warn ("couldn't process line %d \"%s\"\n",
594                     line_no, _dbus_string_get_const_data (&line));
595         goto out;
596       }
597     }
598
599   if (auth != NULL &&
600       state == DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES)
601     {
602       _dbus_warn ("did not expect unused bytes (scripts must specify explicitly if they are expected)\n");
603       goto out;
604     }
605
606   if (_dbus_string_get_length (&from_auth) > 0)
607     {
608       _dbus_warn ("script did not have EXPECT_ statements for all the data received from the DBusAuth\n");
609       _dbus_warn ("Leftover data: %s\n", _dbus_string_get_const_data (&from_auth));
610       goto out;
611     }
612   
613   retval = TRUE;
614   
615  out:
616   if (auth)
617     _dbus_auth_unref (auth);
618
619   _dbus_string_free (&file);
620   _dbus_string_free (&line);
621   _dbus_string_free (&from_auth);
622   
623   return retval;
624 }
625
626 /** @} */
627 #endif /* DBUS_BUILD_TESTS */