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