t_openpam_readlinev.c revision 236099
1/*-
2 * Copyright (c) 2012 Dag-Erling Sm��rgrav
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 *    in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote
15 *    products derived from this software without specific prior written
16 *    permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $Id: t_openpam_readlinev.c 581 2012-04-06 01:08:37Z des $
31 */
32
33#ifdef HAVE_CONFIG_H
34# include "config.h"
35#endif
36
37#include <err.h>
38#include <errno.h>
39#include <fcntl.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include <security/pam_appl.h>
46#include <security/openpam.h>
47
48#include "openpam_impl.h"
49#include "t.h"
50
51static char filename[1024];
52static FILE *f;
53
54/*
55 * Open the temp file and immediately unlink it so it doesn't leak in case
56 * of premature exit.
57 */
58static void
59orlv_open(void)
60{
61	int fd;
62
63	if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0)
64		err(1, "%s(): %s", __func__, filename);
65	if ((f = fdopen(fd, "r+")) == NULL)
66		err(1, "%s(): %s", __func__, filename);
67	if (unlink(filename) < 0)
68		err(1, "%s(): %s", __func__, filename);
69}
70
71/*
72 * Write text to the temp file.
73 */
74static void
75orlv_output(const char *fmt, ...)
76{
77	va_list ap;
78
79	va_start(ap, fmt);
80	vfprintf(f, fmt, ap);
81	va_end(ap);
82	if (ferror(f))
83		err(1, "%s", filename);
84}
85
86/*
87 * Rewind the temp file.
88 */
89static void
90orlv_rewind(void)
91{
92
93	errno = 0;
94	rewind(f);
95	if (errno != 0)
96		err(1, "%s(): %s", __func__, filename);
97}
98
99/*
100 * Read a line from the temp file and verify that the result matches our
101 * expectations: whether a line was read at all, how many and which words
102 * it contained, how many lines were read (in case of quoted or escaped
103 * newlines) and whether we reached the end of the file.
104 */
105static int
106orlv_expect(const char **expectedv, int lines, int eof)
107{
108	int expectedc, gotc, i, lineno = 0;
109	char **gotv;
110
111	expectedc = 0;
112	if (expectedv != NULL)
113		while (expectedv[expectedc] != NULL)
114			++expectedc;
115	gotv = openpam_readlinev(f, &lineno, &gotc);
116	if (ferror(f))
117		err(1, "%s(): %s", __func__, filename);
118	if (expectedv != NULL && gotv == NULL) {
119		t_verbose("expected %d words, got nothing\n", expectedc);
120		return (0);
121	}
122	if (expectedv == NULL && gotv != NULL) {
123		t_verbose("expected nothing, got %d words\n", gotc);
124		FREEV(gotc, gotv);
125		return (0);
126	}
127	if (expectedv != NULL && gotv != NULL) {
128		if (expectedc != gotc) {
129			t_verbose("expected %d words, got %d\n",
130			    expectedc, gotc);
131			FREEV(gotc, gotv);
132			return (0);
133		}
134		for (i = 0; i < gotc; ++i) {
135			if (strcmp(expectedv[i], gotv[i]) != 0) {
136				t_verbose("word %d: expected <<%s>>, "
137				    "got <<%s>>\n", i, expectedv[i], gotv[i]);
138				FREEV(gotc, gotv);
139				return (0);
140			}
141		}
142		FREEV(gotc, gotv);
143	}
144	if (lineno != lines) {
145		t_verbose("expected to advance %d lines, advanced %d lines\n",
146		    lines, lineno);
147		return (0);
148	}
149	if (eof && !feof(f)) {
150		t_verbose("expected EOF, but didn't get it\n");
151		return (0);
152	}
153	if (!eof && feof(f)) {
154		t_verbose("didn't expect EOF, but got it anyway\n");
155		return (0);
156	}
157	return (1);
158}
159
160/*
161 * Close the temp file.
162 */
163void
164orlv_close(void)
165{
166
167	if (fclose(f) != 0)
168		err(1, "%s(): %s", __func__, filename);
169	f = NULL;
170}
171
172/***************************************************************************
173 * Commonly-used lines
174 */
175
176static const char *empty[] = {
177	NULL
178};
179
180static const char *hello[] = {
181	"hello",
182	NULL
183};
184
185static const char *hello_world[] = {
186	"hello",
187	"world",
188	NULL
189};
190
191
192/***************************************************************************
193 * Lines without words
194 */
195
196T_FUNC(empty_input, "empty input")
197{
198	int ret;
199
200	orlv_open();
201	ret = orlv_expect(NULL, 0 /*lines*/, 1 /*eof*/);
202	orlv_close();
203	return (ret);
204}
205
206T_FUNC(empty_line, "empty line")
207{
208	int ret;
209
210	orlv_open();
211	orlv_output("\n");
212	orlv_rewind();
213	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
214	orlv_close();
215	return (ret);
216}
217
218T_FUNC(unterminated_empty_line, "unterminated empty line")
219{
220	int ret;
221
222	orlv_open();
223	orlv_output(" ");
224	orlv_rewind();
225	ret = orlv_expect(NULL, 0 /*lines*/, 1 /*eof*/);
226	orlv_close();
227	return (ret);
228}
229
230T_FUNC(whitespace, "whitespace")
231{
232	int ret;
233
234	orlv_open();
235	orlv_output(" \n");
236	orlv_rewind();
237	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
238	orlv_close();
239	return (ret);
240}
241
242T_FUNC(comment, "comment")
243{
244	int ret;
245
246	orlv_open();
247	orlv_output("# comment\n");
248	orlv_rewind();
249	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
250	orlv_close();
251	return (ret);
252}
253
254T_FUNC(whitespace_before_comment, "whitespace before comment")
255{
256	int ret;
257
258	orlv_open();
259	orlv_output(" # comment\n");
260	orlv_rewind();
261	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
262	orlv_close();
263	return (ret);
264}
265
266
267/***************************************************************************
268 * Simple words
269 */
270
271T_FUNC(one_word, "one word")
272{
273	int ret;
274
275	orlv_open();
276	orlv_output("hello\n");
277	orlv_rewind();
278	ret = orlv_expect(hello, 1 /*lines*/, 0 /*eof*/);
279	orlv_close();
280	return (ret);
281}
282
283T_FUNC(two_words, "two words")
284{
285	int ret;
286
287	orlv_open();
288	orlv_output("hello world\n");
289	orlv_rewind();
290	ret = orlv_expect(hello_world, 1 /*lines*/, 0 /*eof*/);
291	orlv_close();
292	return (ret);
293}
294
295T_FUNC(unterminated_line, "unterminated line")
296{
297	int ret;
298
299	orlv_open();
300	orlv_output("hello world");
301	orlv_rewind();
302	ret = orlv_expect(hello_world, 0 /*lines*/, 1 /*eof*/);
303	orlv_close();
304	return (ret);
305}
306
307
308/***************************************************************************
309 * Boilerplate
310 */
311
312const struct t_test *t_plan[] = {
313	T(empty_input),
314	T(empty_line),
315	T(unterminated_empty_line),
316	T(whitespace),
317	T(comment),
318	T(whitespace_before_comment),
319
320	T(one_word),
321	T(two_words),
322	T(unterminated_line),
323
324	NULL
325};
326
327const struct t_test **
328t_prepare(int argc, char *argv[])
329{
330
331	(void)argc;
332	(void)argv;
333	snprintf(filename, sizeof filename, "%s.%d.tmp", t_progname, getpid());
334	if (filename == NULL)
335		err(1, "asprintf()");
336	return (t_plan);
337}
338
339void
340t_cleanup(void)
341{
342}
343