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