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