1168754Sbushman/*- 2168754Sbushman * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org> 3251867Seadler * All rights reserved. 4168754Sbushman * 5168754Sbushman * Redistribution and use in source and binary forms, with or without 6168754Sbushman * modification, are permitted provided that the following conditions 7168754Sbushman * are met: 8168754Sbushman * 1. Redistributions of source code must retain the above copyright 9168754Sbushman * notice, this list of conditions and the following disclaimer. 10168754Sbushman * 2. Redistributions in binary form must reproduce the above copyright 11168754Sbushman * notice, this list of conditions and the following disclaimer in the 12168754Sbushman * documentation and/or other materials provided with the distribution. 13168754Sbushman * 14168754Sbushman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15168754Sbushman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16168754Sbushman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17168754Sbushman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18168754Sbushman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19168754Sbushman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20168754Sbushman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21168754Sbushman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22168754Sbushman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23168754Sbushman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24168754Sbushman * SUCH DAMAGE. 25168754Sbushman * 26168754Sbushman */ 27168754Sbushman 28168754Sbushman#include <sys/cdefs.h> 29168754Sbushman__FBSDID("$FreeBSD$"); 30168754Sbushman 31168754Sbushman#include <arpa/inet.h> 32168754Sbushman#include <assert.h> 33168754Sbushman#include <errno.h> 34168754Sbushman#include <grp.h> 35168754Sbushman#include <stdio.h> 36168754Sbushman#include <stdlib.h> 37168754Sbushman#include <string.h> 38168754Sbushman#include <stringlist.h> 39168754Sbushman#include <unistd.h> 40168754Sbushman#include "testutil.h" 41168754Sbushman 42168754Sbushmanenum test_methods { 43168754Sbushman TEST_GETGRENT, 44168754Sbushman TEST_GETGRNAM, 45168754Sbushman TEST_GETGRGID, 46168754Sbushman TEST_GETGRENT_2PASS, 47168754Sbushman TEST_BUILD_SNAPSHOT 48168754Sbushman}; 49168754Sbushman 50168754Sbushmanstatic int debug = 0; 51168754Sbushmanstatic enum test_methods method = TEST_BUILD_SNAPSHOT; 52168754Sbushman 53168754SbushmanDECLARE_TEST_DATA(group) 54168754SbushmanDECLARE_TEST_FILE_SNAPSHOT(group) 55168754SbushmanDECLARE_1PASS_TEST(group) 56168754SbushmanDECLARE_2PASS_TEST(group) 57168754Sbushman 58168754Sbushmanstatic void clone_group(struct group *, struct group const *); 59168754Sbushmanstatic int compare_group(struct group *, struct group *, void *); 60168754Sbushmanstatic void dump_group(struct group *); 61168754Sbushmanstatic void free_group(struct group *); 62168754Sbushman 63168754Sbushmanstatic void sdump_group(struct group *, char *, size_t); 64168754Sbushmanstatic int group_read_snapshot_func(struct group *, char *); 65168754Sbushman 66168754Sbushmanstatic int group_check_ambiguity(struct group_test_data *, 67168754Sbushman struct group *); 68168754Sbushmanstatic int group_fill_test_data(struct group_test_data *); 69168754Sbushmanstatic int group_test_correctness(struct group *, void *); 70168754Sbushmanstatic int group_test_getgrnam(struct group *, void *); 71168754Sbushmanstatic int group_test_getgrgid(struct group *, void *); 72168754Sbushmanstatic int group_test_getgrent(struct group *, void *); 73168754Sbushman 74168754Sbushmanstatic void usage(void) __attribute__((__noreturn__)); 75168754Sbushman 76168754SbushmanIMPLEMENT_TEST_DATA(group) 77168754SbushmanIMPLEMENT_TEST_FILE_SNAPSHOT(group) 78168754SbushmanIMPLEMENT_1PASS_TEST(group) 79168754SbushmanIMPLEMENT_2PASS_TEST(group) 80168754Sbushman 81168754Sbushmanstatic void 82168754Sbushmanclone_group(struct group *dest, struct group const *src) 83168754Sbushman{ 84168754Sbushman assert(dest != NULL); 85168754Sbushman assert(src != NULL); 86168754Sbushman 87168754Sbushman char **cp; 88168754Sbushman int members_num; 89168754Sbushman 90168754Sbushman memset(dest, 0, sizeof(struct group)); 91168754Sbushman 92168754Sbushman if (src->gr_name != NULL) { 93168754Sbushman dest->gr_name = strdup(src->gr_name); 94168754Sbushman assert(dest->gr_name != NULL); 95168754Sbushman } 96168754Sbushman 97168754Sbushman if (src->gr_passwd != NULL) { 98168754Sbushman dest->gr_passwd = strdup(src->gr_passwd); 99168754Sbushman assert(dest->gr_passwd != NULL); 100168754Sbushman } 101168754Sbushman dest->gr_gid = src->gr_gid; 102168754Sbushman 103168754Sbushman if (src->gr_mem != NULL) { 104168754Sbushman members_num = 0; 105168754Sbushman for (cp = src->gr_mem; *cp; ++cp) 106168754Sbushman ++members_num; 107168754Sbushman 108168754Sbushman dest->gr_mem = (char **)malloc( 109168754Sbushman (members_num + 1) * (sizeof(char *))); 110168754Sbushman assert(dest->gr_mem != NULL); 111168754Sbushman memset(dest->gr_mem, 0, (members_num+1) * (sizeof(char *))); 112168754Sbushman 113168754Sbushman for (cp = src->gr_mem; *cp; ++cp) { 114168754Sbushman dest->gr_mem[cp - src->gr_mem] = strdup(*cp); 115168754Sbushman assert(dest->gr_mem[cp - src->gr_mem] != NULL); 116168754Sbushman } 117168754Sbushman } 118168754Sbushman} 119168754Sbushman 120168754Sbushmanstatic void 121168754Sbushmanfree_group(struct group *grp) 122168754Sbushman{ 123168754Sbushman char **cp; 124168754Sbushman 125168754Sbushman assert(grp != NULL); 126168754Sbushman 127168754Sbushman free(grp->gr_name); 128168754Sbushman free(grp->gr_passwd); 129168754Sbushman 130168754Sbushman for (cp = grp->gr_mem; *cp; ++cp) 131168754Sbushman free(*cp); 132168754Sbushman free(grp->gr_mem); 133168754Sbushman} 134168754Sbushman 135168754Sbushmanstatic int 136168754Sbushmancompare_group(struct group *grp1, struct group *grp2, void *mdata) 137168754Sbushman{ 138168754Sbushman char **c1, **c2; 139168754Sbushman 140168754Sbushman if (grp1 == grp2) 141168754Sbushman return (0); 142168754Sbushman 143168754Sbushman if ((grp1 == NULL) || (grp2 == NULL)) 144168754Sbushman goto errfin; 145168754Sbushman 146168754Sbushman if ((strcmp(grp1->gr_name, grp2->gr_name) != 0) || 147168754Sbushman (strcmp(grp1->gr_passwd, grp2->gr_passwd) != 0) || 148168754Sbushman (grp1->gr_gid != grp2->gr_gid)) 149168754Sbushman goto errfin; 150168754Sbushman 151168754Sbushman c1 = grp1->gr_mem; 152168754Sbushman c2 = grp2->gr_mem; 153168754Sbushman 154168754Sbushman if ((grp1->gr_mem == NULL) || (grp2->gr_mem == NULL)) 155168754Sbushman goto errfin; 156168754Sbushman 157168754Sbushman for (;*c1 && *c2; ++c1, ++c2) 158168754Sbushman if (strcmp(*c1, *c2) != 0) 159168754Sbushman goto errfin; 160168754Sbushman 161168754Sbushman if ((*c1 != '\0') || (*c2 != '\0')) 162168754Sbushman goto errfin; 163168754Sbushman 164168754Sbushman return 0; 165168754Sbushman 166168754Sbushmanerrfin: 167168754Sbushman if ((debug) && (mdata == NULL)) { 168168754Sbushman printf("following structures are not equal:\n"); 169168754Sbushman dump_group(grp1); 170168754Sbushman dump_group(grp2); 171168754Sbushman } 172168754Sbushman 173168754Sbushman return (-1); 174168754Sbushman} 175168754Sbushman 176168754Sbushmanstatic void 177168754Sbushmansdump_group(struct group *grp, char *buffer, size_t buflen) 178168754Sbushman{ 179168754Sbushman char **cp; 180168754Sbushman int written; 181168754Sbushman 182168754Sbushman written = snprintf(buffer, buflen, "%s %s %d", 183168754Sbushman grp->gr_name, grp->gr_passwd, grp->gr_gid); 184168754Sbushman buffer += written; 185168754Sbushman if (written > buflen) 186168754Sbushman return; 187168754Sbushman buflen -= written; 188168754Sbushman 189168754Sbushman if (grp->gr_mem != NULL) { 190168754Sbushman if (*(grp->gr_mem) != '\0') { 191168754Sbushman for (cp = grp->gr_mem; *cp; ++cp) { 192168754Sbushman written = snprintf(buffer, buflen, " %s",*cp); 193168754Sbushman buffer += written; 194168754Sbushman if (written > buflen) 195168754Sbushman return; 196168754Sbushman buflen -= written; 197168754Sbushman 198168754Sbushman if (buflen == 0) 199168754Sbushman return; 200168754Sbushman } 201168754Sbushman } else 202168754Sbushman snprintf(buffer, buflen, " nomem"); 203168754Sbushman } else 204168754Sbushman snprintf(buffer, buflen, " (null)"); 205168754Sbushman} 206168754Sbushman 207168754Sbushmanstatic int 208168754Sbushmangroup_read_snapshot_func(struct group *grp, char *line) 209168754Sbushman{ 210168754Sbushman StringList *sl; 211168754Sbushman char *s, *ps, *ts; 212168754Sbushman int i; 213168754Sbushman 214168754Sbushman if (debug) 215168754Sbushman printf("1 line read from snapshot:\n%s\n", line); 216168754Sbushman 217168754Sbushman i = 0; 218168754Sbushman sl = NULL; 219168754Sbushman ps = line; 220168754Sbushman memset(grp, 0, sizeof(struct group)); 221168754Sbushman while ( (s = strsep(&ps, " ")) != NULL) { 222168754Sbushman switch (i) { 223168754Sbushman case 0: 224168754Sbushman grp->gr_name = strdup(s); 225168754Sbushman assert(grp->gr_name != NULL); 226168754Sbushman break; 227168754Sbushman 228168754Sbushman case 1: 229168754Sbushman grp->gr_passwd = strdup(s); 230168754Sbushman assert(grp->gr_passwd != NULL); 231168754Sbushman break; 232168754Sbushman 233168754Sbushman case 2: 234168754Sbushman grp->gr_gid = (gid_t)strtol(s, &ts, 10); 235168754Sbushman if (*ts != '\0') { 236168754Sbushman free(grp->gr_name); 237168754Sbushman free(grp->gr_passwd); 238168754Sbushman return (-1); 239168754Sbushman } 240168754Sbushman break; 241168754Sbushman 242168754Sbushman default: 243168754Sbushman if (sl == NULL) { 244168754Sbushman if (strcmp(s, "(null)") == 0) 245168754Sbushman return (0); 246168754Sbushman 247168754Sbushman sl = sl_init(); 248168754Sbushman assert(sl != NULL); 249168754Sbushman 250168754Sbushman if (strcmp(s, "nomem") != 0) { 251168754Sbushman ts = strdup(s); 252168754Sbushman assert(ts != NULL); 253168754Sbushman sl_add(sl, ts); 254168754Sbushman } 255168754Sbushman } else { 256168754Sbushman ts = strdup(s); 257168754Sbushman assert(ts != NULL); 258168754Sbushman sl_add(sl, ts); 259168754Sbushman } 260168754Sbushman break; 261168754Sbushman }; 262168754Sbushman ++i; 263168754Sbushman } 264168754Sbushman 265168754Sbushman if (i < 3) { 266168754Sbushman free(grp->gr_name); 267168754Sbushman free(grp->gr_passwd); 268168754Sbushman memset(grp, 0, sizeof(struct group)); 269168754Sbushman return (-1); 270168754Sbushman } 271168754Sbushman 272168754Sbushman sl_add(sl, NULL); 273168754Sbushman grp->gr_mem = sl->sl_str; 274168754Sbushman 275168754Sbushman /* NOTE: is it a dirty hack or not? */ 276168754Sbushman free(sl); 277168754Sbushman return (0); 278168754Sbushman} 279168754Sbushman 280168754Sbushmanstatic void 281168754Sbushmandump_group(struct group *result) 282168754Sbushman{ 283168754Sbushman if (result != NULL) { 284168754Sbushman char buffer[1024]; 285168754Sbushman sdump_group(result, buffer, sizeof(buffer)); 286168754Sbushman printf("%s\n", buffer); 287168754Sbushman } else 288168754Sbushman printf("(null)\n"); 289168754Sbushman} 290168754Sbushman 291168754Sbushmanstatic int 292168754Sbushmangroup_fill_test_data(struct group_test_data *td) 293168754Sbushman{ 294168754Sbushman struct group *grp; 295168754Sbushman 296168754Sbushman setgroupent(1); 297168754Sbushman while ((grp = getgrent()) != NULL) { 298168754Sbushman if (group_test_correctness(grp, NULL) == 0) 299168754Sbushman TEST_DATA_APPEND(group, td, grp); 300168754Sbushman else 301168754Sbushman return (-1); 302168754Sbushman } 303168754Sbushman endgrent(); 304168754Sbushman 305168754Sbushman return (0); 306168754Sbushman} 307168754Sbushman 308168754Sbushmanstatic int 309168754Sbushmangroup_test_correctness(struct group *grp, void *mdata) 310168754Sbushman{ 311168754Sbushman if (debug) { 312168754Sbushman printf("testing correctness with the following data:\n"); 313168754Sbushman dump_group(grp); 314168754Sbushman } 315168754Sbushman 316168754Sbushman if (grp == NULL) 317168754Sbushman goto errfin; 318168754Sbushman 319168754Sbushman if (grp->gr_name == NULL) 320168754Sbushman goto errfin; 321168754Sbushman 322168754Sbushman if (grp->gr_passwd == NULL) 323168754Sbushman goto errfin; 324168754Sbushman 325168754Sbushman if (grp->gr_mem == NULL) 326168754Sbushman goto errfin; 327168754Sbushman 328168754Sbushman if (debug) 329168754Sbushman printf("correct\n"); 330168754Sbushman 331168754Sbushman return (0); 332168754Sbushmanerrfin: 333168754Sbushman if (debug) 334168754Sbushman printf("incorrect\n"); 335168754Sbushman 336168754Sbushman return (-1); 337168754Sbushman} 338168754Sbushman 339168754Sbushman/* group_check_ambiguity() is needed here because when doing the getgrent() 340168754Sbushman * calls sequence, records from different nsswitch sources can be different, 341168754Sbushman * though having the same pw_name/pw_uid */ 342168754Sbushmanstatic int 343168754Sbushmangroup_check_ambiguity(struct group_test_data *td, struct group *pwd) 344168754Sbushman{ 345168754Sbushman 346168754Sbushman return (TEST_DATA_FIND(group, td, pwd, compare_group, 347168754Sbushman NULL) != NULL ? 0 : -1); 348168754Sbushman} 349168754Sbushman 350168754Sbushmanstatic int 351168754Sbushmangroup_test_getgrnam(struct group *grp_model, void *mdata) 352168754Sbushman{ 353168754Sbushman struct group *grp; 354168754Sbushman 355168754Sbushman if (debug) { 356168754Sbushman printf("testing getgrnam() with the following data:\n"); 357168754Sbushman dump_group(grp_model); 358168754Sbushman } 359168754Sbushman 360168754Sbushman grp = getgrnam(grp_model->gr_name); 361168754Sbushman if (group_test_correctness(grp, NULL) != 0) 362168754Sbushman goto errfin; 363168754Sbushman 364168754Sbushman if ((compare_group(grp, grp_model, NULL) != 0) && 365168754Sbushman (group_check_ambiguity((struct group_test_data *)mdata, grp) 366168754Sbushman !=0)) 367168754Sbushman goto errfin; 368168754Sbushman 369168754Sbushman if (debug) 370168754Sbushman printf("ok\n"); 371168754Sbushman return (0); 372168754Sbushman 373168754Sbushmanerrfin: 374168754Sbushman if (debug) 375168754Sbushman printf("not ok\n"); 376168754Sbushman 377168754Sbushman return (-1); 378168754Sbushman} 379168754Sbushman 380168754Sbushmanstatic int 381168754Sbushmangroup_test_getgrgid(struct group *grp_model, void *mdata) 382168754Sbushman{ 383168754Sbushman struct group *grp; 384168754Sbushman 385168754Sbushman if (debug) { 386168754Sbushman printf("testing getgrgid() with the following data...\n"); 387168754Sbushman dump_group(grp_model); 388168754Sbushman } 389168754Sbushman 390168754Sbushman grp = getgrgid(grp_model->gr_gid); 391168754Sbushman if ((group_test_correctness(grp, NULL) != 0) || 392168754Sbushman ((compare_group(grp, grp_model, NULL) != 0) && 393168754Sbushman (group_check_ambiguity((struct group_test_data *)mdata, grp) 394168754Sbushman != 0))) { 395168754Sbushman if (debug) 396168754Sbushman printf("not ok\n"); 397168754Sbushman return (-1); 398168754Sbushman } else { 399168754Sbushman if (debug) 400168754Sbushman printf("ok\n"); 401168754Sbushman return (0); 402168754Sbushman } 403168754Sbushman} 404168754Sbushman 405168754Sbushmanstatic int 406168754Sbushmangroup_test_getgrent(struct group *grp, void *mdata) 407168754Sbushman{ 408168754Sbushman /* Only correctness can be checked when doing 1-pass test for 409168754Sbushman * getgrent(). */ 410168754Sbushman return (group_test_correctness(grp, NULL)); 411168754Sbushman} 412168754Sbushman 413168754Sbushmanstatic void 414168754Sbushmanusage(void) 415168754Sbushman{ 416168754Sbushman (void)fprintf(stderr, 417168754Sbushman "Usage: %s -nge2 [-d] [-s <file>]\n", 418168754Sbushman getprogname()); 419168754Sbushman exit(1); 420168754Sbushman} 421168754Sbushman 422168754Sbushmanint 423168754Sbushmanmain(int argc, char **argv) 424168754Sbushman{ 425168754Sbushman struct group_test_data td, td_snap, td_2pass; 426168754Sbushman char *snapshot_file; 427168754Sbushman int rv; 428168754Sbushman int c; 429168754Sbushman 430168754Sbushman if (argc < 2) 431168754Sbushman usage(); 432168754Sbushman 433168754Sbushman snapshot_file = NULL; 434168754Sbushman while ((c = getopt(argc, argv, "nge2ds:")) != -1) 435168754Sbushman switch (c) { 436168754Sbushman case 'd': 437168754Sbushman debug++; 438168754Sbushman break; 439168754Sbushman case 'n': 440168754Sbushman method = TEST_GETGRNAM; 441168754Sbushman break; 442168754Sbushman case 'g': 443168754Sbushman method = TEST_GETGRGID; 444168754Sbushman break; 445168754Sbushman case 'e': 446168754Sbushman method = TEST_GETGRENT; 447168754Sbushman break; 448168754Sbushman case '2': 449168754Sbushman method = TEST_GETGRENT_2PASS; 450168754Sbushman break; 451168754Sbushman case 's': 452168754Sbushman snapshot_file = strdup(optarg); 453168754Sbushman break; 454168754Sbushman default: 455168754Sbushman usage(); 456168754Sbushman } 457168754Sbushman 458168754Sbushman TEST_DATA_INIT(group, &td, clone_group, free_group); 459168754Sbushman TEST_DATA_INIT(group, &td_snap, clone_group, free_group); 460168754Sbushman if (snapshot_file != NULL) { 461168754Sbushman if (access(snapshot_file, W_OK | R_OK) != 0) { 462168754Sbushman if (errno == ENOENT) 463168754Sbushman method = TEST_BUILD_SNAPSHOT; 464168754Sbushman else { 465168754Sbushman if (debug) 466168754Sbushman printf("can't access the file %s\n", 467168754Sbushman snapshot_file); 468168754Sbushman 469168754Sbushman rv = -1; 470168754Sbushman goto fin; 471168754Sbushman } 472168754Sbushman } else { 473168754Sbushman if (method == TEST_BUILD_SNAPSHOT) { 474168754Sbushman rv = 0; 475168754Sbushman goto fin; 476168754Sbushman } 477168754Sbushman 478168754Sbushman TEST_SNAPSHOT_FILE_READ(group, snapshot_file, 479168754Sbushman &td_snap, group_read_snapshot_func); 480168754Sbushman } 481168754Sbushman } 482168754Sbushman 483168754Sbushman rv = group_fill_test_data(&td); 484168754Sbushman if (rv == -1) 485168754Sbushman return (-1); 486168754Sbushman switch (method) { 487168754Sbushman case TEST_GETGRNAM: 488168754Sbushman if (snapshot_file == NULL) 489168754Sbushman rv = DO_1PASS_TEST(group, &td, 490168754Sbushman group_test_getgrnam, (void *)&td); 491168754Sbushman else 492168754Sbushman rv = DO_1PASS_TEST(group, &td_snap, 493168754Sbushman group_test_getgrnam, (void *)&td_snap); 494168754Sbushman break; 495168754Sbushman case TEST_GETGRGID: 496168754Sbushman if (snapshot_file == NULL) 497168754Sbushman rv = DO_1PASS_TEST(group, &td, 498168754Sbushman group_test_getgrgid, (void *)&td); 499168754Sbushman else 500168754Sbushman rv = DO_1PASS_TEST(group, &td_snap, 501168754Sbushman group_test_getgrgid, (void *)&td_snap); 502168754Sbushman break; 503168754Sbushman case TEST_GETGRENT: 504168754Sbushman if (snapshot_file == NULL) 505168754Sbushman rv = DO_1PASS_TEST(group, &td, group_test_getgrent, 506168754Sbushman (void *)&td); 507168754Sbushman else 508168754Sbushman rv = DO_2PASS_TEST(group, &td, &td_snap, 509168754Sbushman compare_group, NULL); 510168754Sbushman break; 511168754Sbushman case TEST_GETGRENT_2PASS: 512168754Sbushman TEST_DATA_INIT(group, &td_2pass, clone_group, free_group); 513168754Sbushman rv = group_fill_test_data(&td_2pass); 514168754Sbushman if (rv != -1) 515168754Sbushman rv = DO_2PASS_TEST(group, &td, &td_2pass, 516168754Sbushman compare_group, NULL); 517168754Sbushman TEST_DATA_DESTROY(group, &td_2pass); 518168754Sbushman break; 519168754Sbushman case TEST_BUILD_SNAPSHOT: 520168754Sbushman if (snapshot_file != NULL) 521168754Sbushman rv = TEST_SNAPSHOT_FILE_WRITE(group, snapshot_file, &td, 522168754Sbushman sdump_group); 523168754Sbushman break; 524168754Sbushman default: 525168754Sbushman rv = 0; 526168754Sbushman break; 527168754Sbushman }; 528168754Sbushman 529168754Sbushmanfin: 530168754Sbushman TEST_DATA_DESTROY(group, &td_snap); 531168754Sbushman TEST_DATA_DESTROY(group, &td); 532168754Sbushman free(snapshot_file); 533168754Sbushman return (rv); 534168754Sbushman} 535