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