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