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