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