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