1 /*- 2 * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <errno.h> 29 #include <pwd.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include <atf-c.h> 36 37 #include "testutil.h" 38 39 enum test_methods { 40 TEST_GETPWENT, 41 TEST_GETPWENT_INTERLEAVED_GETPWNAM, 42 TEST_GETPWENT_INTERLEAVED_GETPWUID, 43 TEST_GETPWNAM, 44 TEST_GETPWUID, 45 TEST_GETPWENT_2PASS, 46 TEST_BUILD_SNAPSHOT 47 }; 48 49 DECLARE_TEST_DATA(passwd) 50 DECLARE_TEST_FILE_SNAPSHOT(passwd) 51 DECLARE_1PASS_TEST(passwd) 52 DECLARE_2PASS_TEST(passwd) 53 54 static void clone_passwd(struct passwd *, struct passwd const *); 55 static int compare_passwd(struct passwd *, struct passwd *, void *); 56 static void free_passwd(struct passwd *); 57 58 static void sdump_passwd(struct passwd *, char *, size_t); 59 #ifdef DEBUG 60 static void dump_passwd(struct passwd *); 61 #endif 62 63 static int passwd_read_snapshot_func(struct passwd *, char *); 64 65 static int passwd_check_ambiguity(struct passwd_test_data *, struct passwd *); 66 static int passwd_fill_test_data(struct passwd_test_data *, 67 int (*cb)(struct passwd *, void *)); 68 static int passwd_test_correctness(struct passwd *, void *); 69 static int passwd_test_getpwnam(struct passwd *, void *); 70 static int passwd_test_getpwuid(struct passwd *, void *); 71 static int passwd_test_getpwent(struct passwd *, void *); 72 73 IMPLEMENT_TEST_DATA(passwd) 74 IMPLEMENT_TEST_FILE_SNAPSHOT(passwd) 75 IMPLEMENT_1PASS_TEST(passwd) 76 IMPLEMENT_2PASS_TEST(passwd) 77 78 static void 79 clone_passwd(struct passwd *dest, struct passwd const *src) 80 { 81 ATF_REQUIRE(dest != NULL); 82 ATF_REQUIRE(src != NULL); 83 84 memcpy(dest, src, sizeof(struct passwd)); 85 if (src->pw_name != NULL) 86 dest->pw_name = strdup(src->pw_name); 87 if (src->pw_passwd != NULL) 88 dest->pw_passwd = strdup(src->pw_passwd); 89 if (src->pw_class != NULL) 90 dest->pw_class = strdup(src->pw_class); 91 if (src->pw_gecos != NULL) 92 dest->pw_gecos = strdup(src->pw_gecos); 93 if (src->pw_dir != NULL) 94 dest->pw_dir = strdup(src->pw_dir); 95 if (src->pw_shell != NULL) 96 dest->pw_shell = strdup(dest->pw_shell); 97 } 98 99 static int 100 compare_passwd(struct passwd *pwd1, struct passwd *pwd2, void *mdata __unused) 101 { 102 ATF_REQUIRE(pwd1 != NULL); 103 ATF_REQUIRE(pwd2 != NULL); 104 105 if (pwd1 == pwd2) 106 return (0); 107 108 if (pwd1->pw_uid != pwd2->pw_uid || 109 pwd1->pw_gid != pwd2->pw_gid || 110 pwd1->pw_change != pwd2->pw_change || 111 pwd1->pw_expire != pwd2->pw_expire || 112 pwd1->pw_fields != pwd2->pw_fields || 113 strcmp(pwd1->pw_name, pwd2->pw_name) != 0 || 114 strcmp(pwd1->pw_passwd, pwd2->pw_passwd) != 0 || 115 strcmp(pwd1->pw_class, pwd2->pw_class) != 0 || 116 strcmp(pwd1->pw_gecos, pwd2->pw_gecos) != 0 || 117 strcmp(pwd1->pw_dir, pwd2->pw_dir) != 0 || 118 strcmp(pwd1->pw_shell, pwd2->pw_shell) != 0) 119 return (-1); 120 else 121 return (0); 122 } 123 124 static void 125 free_passwd(struct passwd *pwd) 126 { 127 free(pwd->pw_name); 128 free(pwd->pw_passwd); 129 free(pwd->pw_class); 130 free(pwd->pw_gecos); 131 free(pwd->pw_dir); 132 free(pwd->pw_shell); 133 } 134 135 static void 136 sdump_passwd(struct passwd *pwd, char *buffer, size_t buflen) 137 { 138 snprintf(buffer, buflen, "%s:%s:%d:%d:%jd:%s:%s:%s:%s:%jd:%d", 139 pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid, 140 (uintmax_t)pwd->pw_change, pwd->pw_class, pwd->pw_gecos, 141 pwd->pw_dir, pwd->pw_shell, (uintmax_t)pwd->pw_expire, 142 pwd->pw_fields); 143 } 144 145 #ifdef DEBUG 146 static void 147 dump_passwd(struct passwd *pwd) 148 { 149 if (pwd != NULL) { 150 char buffer[2048]; 151 sdump_passwd(pwd, buffer, sizeof(buffer)); 152 printf("%s\n", buffer); 153 } else 154 printf("(null)\n"); 155 } 156 #endif 157 158 static int 159 passwd_read_snapshot_func(struct passwd *pwd, char *line) 160 { 161 char *s, *ps, *ts; 162 int i; 163 164 #ifdef DEBUG 165 printf("1 line read from snapshot:\n%s\n", line); 166 #endif 167 168 i = 0; 169 ps = line; 170 memset(pwd, 0, sizeof(struct passwd)); 171 while ((s = strsep(&ps, ":")) != NULL) { 172 switch (i) { 173 case 0: 174 pwd->pw_name = strdup(s); 175 ATF_REQUIRE(pwd->pw_name != NULL); 176 break; 177 case 1: 178 pwd->pw_passwd = strdup(s); 179 ATF_REQUIRE(pwd->pw_passwd != NULL); 180 break; 181 case 2: 182 pwd->pw_uid = (uid_t)strtol(s, &ts, 10); 183 if (*ts != '\0') 184 goto fin; 185 break; 186 case 3: 187 pwd->pw_gid = (gid_t)strtol(s, &ts, 10); 188 if (*ts != '\0') 189 goto fin; 190 break; 191 case 4: 192 pwd->pw_change = (time_t)strtol(s, &ts, 10); 193 if (*ts != '\0') 194 goto fin; 195 break; 196 case 5: 197 pwd->pw_class = strdup(s); 198 ATF_REQUIRE(pwd->pw_class != NULL); 199 break; 200 case 6: 201 pwd->pw_gecos = strdup(s); 202 ATF_REQUIRE(pwd->pw_gecos != NULL); 203 break; 204 case 7: 205 pwd->pw_dir = strdup(s); 206 ATF_REQUIRE(pwd->pw_dir != NULL); 207 break; 208 case 8: 209 pwd->pw_shell = strdup(s); 210 ATF_REQUIRE(pwd->pw_shell != NULL); 211 break; 212 case 9: 213 pwd->pw_expire = (time_t)strtol(s, &ts, 10); 214 if (*ts != '\0') 215 goto fin; 216 break; 217 case 10: 218 pwd->pw_fields = (int)strtol(s, &ts, 10); 219 if (*ts != '\0') 220 goto fin; 221 break; 222 default: 223 break; 224 } 225 ++i; 226 } 227 228 fin: 229 if (i != 11) { 230 free_passwd(pwd); 231 memset(pwd, 0, sizeof(struct passwd)); 232 return (-1); 233 } 234 235 return (0); 236 } 237 238 static int 239 passwd_fill_test_data(struct passwd_test_data *td, 240 int (*cb)(struct passwd *, void *)) 241 { 242 struct passwd *pwd; 243 const int limit = 1024; 244 int count = 0; 245 246 setpassent(1); 247 while ((pwd = getpwent()) != NULL) { 248 if (passwd_test_correctness(pwd, NULL) == 0) { 249 TEST_DATA_APPEND(passwd, td, pwd); 250 if (cb != NULL && cb(pwd, td) != 0) 251 return (-1); 252 } else { 253 return (-1); 254 } 255 if (++count >= limit) 256 break; 257 } 258 endpwent(); 259 260 return (0); 261 } 262 263 static int 264 passwd_test_correctness(struct passwd *pwd, void *mdata __unused) 265 { 266 267 #ifdef DEBUG 268 printf("testing correctness with the following data:\n"); 269 dump_passwd(pwd); 270 #endif 271 272 if (pwd == NULL) 273 return (-1); 274 275 if (pwd->pw_name == NULL) 276 goto errfin; 277 278 if (pwd->pw_passwd == NULL) 279 goto errfin; 280 281 if (pwd->pw_class == NULL) 282 goto errfin; 283 284 if (pwd->pw_gecos == NULL) 285 goto errfin; 286 287 if (pwd->pw_dir == NULL) 288 goto errfin; 289 290 if (pwd->pw_shell == NULL) 291 goto errfin; 292 293 #ifdef DEBUG 294 printf("correct\n"); 295 #endif 296 297 return (0); 298 errfin: 299 #ifdef DEBUG 300 printf("incorrect\n"); 301 #endif 302 303 return (-1); 304 } 305 306 /* passwd_check_ambiguity() is needed here because when doing the getpwent() 307 * calls sequence, records from different nsswitch sources can be different, 308 * though having the same pw_name/pw_uid */ 309 static int 310 passwd_check_ambiguity(struct passwd_test_data *td, struct passwd *pwd) 311 { 312 313 return (TEST_DATA_FIND(passwd, td, pwd, compare_passwd, NULL) != 314 NULL ? 0 : -1); 315 } 316 317 static int 318 passwd_test_getpwnam(struct passwd *pwd_model, void *mdata) 319 { 320 struct passwd *pwd; 321 322 #ifdef DEBUG 323 printf("testing getpwnam() with the following data:\n"); 324 dump_passwd(pwd_model); 325 #endif 326 327 pwd = getpwnam(pwd_model->pw_name); 328 if (passwd_test_correctness(pwd, NULL) != 0) 329 goto errfin; 330 331 if (compare_passwd(pwd, pwd_model, NULL) != 0 && 332 passwd_check_ambiguity((struct passwd_test_data *)mdata, pwd) != 0) 333 goto errfin; 334 335 #ifdef DEBUG 336 printf("ok\n"); 337 #endif 338 return (0); 339 340 errfin: 341 #ifdef DEBUG 342 printf("not ok\n"); 343 #endif 344 return (-1); 345 } 346 347 static int 348 passwd_test_getpwuid(struct passwd *pwd_model, void *mdata) 349 { 350 struct passwd *pwd; 351 352 #ifdef DEBUG 353 printf("testing getpwuid() with the following data...\n"); 354 dump_passwd(pwd_model); 355 #endif 356 357 pwd = getpwuid(pwd_model->pw_uid); 358 if (passwd_test_correctness(pwd, NULL) != 0 || 359 (compare_passwd(pwd, pwd_model, NULL) != 0 && 360 passwd_check_ambiguity((struct passwd_test_data *)mdata, 361 pwd) != 0)) { 362 #ifdef DEBUG 363 printf("not ok\n"); 364 #endif 365 return (-1); 366 } else { 367 #ifdef DEBUG 368 printf("ok\n"); 369 #endif 370 return (0); 371 } 372 } 373 374 static int 375 passwd_test_getpwent(struct passwd *pwd, void *mdata __unused) 376 { 377 /* 378 * Only correctness can be checked when doing 1-pass test for 379 * getpwent(). 380 */ 381 return (passwd_test_correctness(pwd, NULL)); 382 } 383 384 static int 385 run_tests(const char *snapshot_file, enum test_methods method) 386 { 387 struct passwd_test_data td, td_snap, td_2pass, td_interleaved; 388 int rv; 389 390 TEST_DATA_INIT(passwd, &td, clone_passwd, free_passwd); 391 TEST_DATA_INIT(passwd, &td_snap, clone_passwd, free_passwd); 392 if (snapshot_file != NULL) { 393 if (access(snapshot_file, W_OK | R_OK) != 0) { 394 if (errno == ENOENT) 395 method = TEST_BUILD_SNAPSHOT; 396 else { 397 printf("can't access the file %s\n", 398 snapshot_file); 399 rv = -1; 400 goto fin; 401 } 402 } else { 403 if (method == TEST_BUILD_SNAPSHOT) { 404 rv = 0; 405 goto fin; 406 } 407 408 TEST_SNAPSHOT_FILE_READ(passwd, snapshot_file, 409 &td_snap, passwd_read_snapshot_func); 410 } 411 } 412 413 rv = passwd_fill_test_data(&td, NULL); 414 if (rv == -1) 415 return (-1); 416 417 switch (method) { 418 case TEST_GETPWNAM: 419 if (snapshot_file == NULL) 420 rv = DO_1PASS_TEST(passwd, &td, 421 passwd_test_getpwnam, (void *)&td); 422 else 423 rv = DO_1PASS_TEST(passwd, &td_snap, 424 passwd_test_getpwnam, (void *)&td_snap); 425 break; 426 case TEST_GETPWUID: 427 if (snapshot_file == NULL) 428 rv = DO_1PASS_TEST(passwd, &td, 429 passwd_test_getpwuid, (void *)&td); 430 else 431 rv = DO_1PASS_TEST(passwd, &td_snap, 432 passwd_test_getpwuid, (void *)&td_snap); 433 break; 434 case TEST_GETPWENT: 435 if (snapshot_file == NULL) 436 rv = DO_1PASS_TEST(passwd, &td, passwd_test_getpwent, 437 (void *)&td); 438 else 439 rv = DO_2PASS_TEST(passwd, &td, &td_snap, 440 compare_passwd, NULL); 441 break; 442 case TEST_GETPWENT_2PASS: 443 TEST_DATA_INIT(passwd, &td_2pass, clone_passwd, free_passwd); 444 rv = passwd_fill_test_data(&td_2pass, NULL); 445 if (rv != -1) 446 rv = DO_2PASS_TEST(passwd, &td, &td_2pass, 447 compare_passwd, NULL); 448 TEST_DATA_DESTROY(passwd, &td_2pass); 449 break; 450 case TEST_GETPWENT_INTERLEAVED_GETPWNAM: 451 TEST_DATA_INIT(passwd, &td_interleaved, clone_passwd, free_passwd); 452 rv = passwd_fill_test_data(&td_interleaved, passwd_test_getpwnam); 453 if (rv != -1) 454 rv = DO_2PASS_TEST(passwd, &td, &td_interleaved, 455 compare_passwd, NULL); 456 TEST_DATA_DESTROY(passwd, &td_interleaved); 457 break; 458 case TEST_GETPWENT_INTERLEAVED_GETPWUID: 459 TEST_DATA_INIT(passwd, &td_interleaved, clone_passwd, free_passwd); 460 rv = passwd_fill_test_data(&td_interleaved, passwd_test_getpwuid); 461 if (rv != -1) 462 rv = DO_2PASS_TEST(passwd, &td, &td_interleaved, 463 compare_passwd, NULL); 464 TEST_DATA_DESTROY(passwd, &td_interleaved); 465 break; 466 case TEST_BUILD_SNAPSHOT: 467 if (snapshot_file != NULL) 468 rv = TEST_SNAPSHOT_FILE_WRITE(passwd, snapshot_file, 469 &td, sdump_passwd); 470 break; 471 default: 472 rv = 0; 473 break; 474 } 475 476 fin: 477 TEST_DATA_DESTROY(passwd, &td_snap); 478 TEST_DATA_DESTROY(passwd, &td); 479 480 return (rv); 481 } 482 483 #define SNAPSHOT_FILE "snapshot_pwd" 484 485 ATF_TC_WITHOUT_HEAD(getpwent); 486 ATF_TC_BODY(getpwent, tc) 487 { 488 ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT) == 0); 489 } 490 491 ATF_TC_WITHOUT_HEAD(getpwent_with_snapshot); 492 ATF_TC_BODY(getpwent_with_snapshot, tc) 493 { 494 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 495 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWENT) == 0); 496 } 497 498 ATF_TC_WITHOUT_HEAD(getpwent_with_two_pass); 499 ATF_TC_BODY(getpwent_with_two_pass, tc) 500 { 501 ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_2PASS) == 0); 502 } 503 504 ATF_TC_WITHOUT_HEAD(getpwnam); 505 ATF_TC_BODY(getpwnam, tc) 506 { 507 ATF_REQUIRE(run_tests(NULL, TEST_GETPWNAM) == 0); 508 } 509 510 ATF_TC_WITHOUT_HEAD(getpwnam_with_snapshot); 511 ATF_TC_BODY(getpwnam_with_snapshot, tc) 512 { 513 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 514 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWNAM) == 0); 515 } 516 517 ATF_TC_WITHOUT_HEAD(getpwuid); 518 ATF_TC_BODY(getpwuid, tc) 519 { 520 ATF_REQUIRE(run_tests(NULL, TEST_GETPWUID) == 0); 521 } 522 523 ATF_TC_WITHOUT_HEAD(getpwuid_with_snapshot); 524 ATF_TC_BODY(getpwuid_with_snapshot, tc) 525 { 526 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0); 527 ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWUID) == 0); 528 } 529 530 ATF_TC_WITHOUT_HEAD(getpwent_interleaved_getpwnam); 531 ATF_TC_BODY(getpwent_interleaved_getpwnam, tc) 532 { 533 ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_INTERLEAVED_GETPWNAM) == 0); 534 } 535 536 ATF_TC_WITHOUT_HEAD(getpwent_interleaved_getpwuid); 537 ATF_TC_BODY(getpwent_interleaved_getpwuid, tc) 538 { 539 ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_INTERLEAVED_GETPWUID) == 0); 540 } 541 542 ATF_TP_ADD_TCS(tp) 543 { 544 ATF_TP_ADD_TC(tp, getpwent); 545 ATF_TP_ADD_TC(tp, getpwent_with_snapshot); 546 ATF_TP_ADD_TC(tp, getpwent_with_two_pass); 547 ATF_TP_ADD_TC(tp, getpwnam); 548 ATF_TP_ADD_TC(tp, getpwnam_with_snapshot); 549 ATF_TP_ADD_TC(tp, getpwuid); 550 ATF_TP_ADD_TC(tp, getpwuid_with_snapshot); 551 ATF_TP_ADD_TC(tp, getpwent_interleaved_getpwnam); 552 ATF_TP_ADD_TC(tp, getpwent_interleaved_getpwuid); 553 554 return (atf_no_error()); 555 } 556