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