t_openpam_readlinev.c revision 236099
1236099Sdes/*-
2236099Sdes * Copyright (c) 2012 Dag-Erling Sm��rgrav
3236099Sdes * All rights reserved.
4236099Sdes *
5236099Sdes * Redistribution and use in source and binary forms, with or without
6236099Sdes * modification, are permitted provided that the following conditions
7236099Sdes * are met:
8236099Sdes * 1. Redistributions of source code must retain the above copyright
9236099Sdes *    notice, this list of conditions and the following disclaimer
10236099Sdes *    in this position and unchanged.
11236099Sdes * 2. Redistributions in binary form must reproduce the above copyright
12236099Sdes *    notice, this list of conditions and the following disclaimer in the
13236099Sdes *    documentation and/or other materials provided with the distribution.
14236099Sdes * 3. The name of the author may not be used to endorse or promote
15236099Sdes *    products derived from this software without specific prior written
16236099Sdes *    permission.
17236099Sdes *
18236099Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19236099Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20236099Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21236099Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22236099Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23236099Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24236099Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25236099Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26236099Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27236099Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28236099Sdes * SUCH DAMAGE.
29236099Sdes *
30236099Sdes * $Id: t_openpam_readlinev.c 581 2012-04-06 01:08:37Z des $
31236099Sdes */
32236099Sdes
33236099Sdes#ifdef HAVE_CONFIG_H
34236099Sdes# include "config.h"
35236099Sdes#endif
36236099Sdes
37236099Sdes#include <err.h>
38236099Sdes#include <errno.h>
39236099Sdes#include <fcntl.h>
40236099Sdes#include <stdio.h>
41236099Sdes#include <stdlib.h>
42236099Sdes#include <string.h>
43236099Sdes#include <unistd.h>
44236099Sdes
45236099Sdes#include <security/pam_appl.h>
46236099Sdes#include <security/openpam.h>
47236099Sdes
48236099Sdes#include "openpam_impl.h"
49236099Sdes#include "t.h"
50236099Sdes
51236099Sdesstatic char filename[1024];
52236099Sdesstatic FILE *f;
53236099Sdes
54236099Sdes/*
55236099Sdes * Open the temp file and immediately unlink it so it doesn't leak in case
56236099Sdes * of premature exit.
57236099Sdes */
58236099Sdesstatic void
59236099Sdesorlv_open(void)
60236099Sdes{
61236099Sdes	int fd;
62236099Sdes
63236099Sdes	if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0)
64236099Sdes		err(1, "%s(): %s", __func__, filename);
65236099Sdes	if ((f = fdopen(fd, "r+")) == NULL)
66236099Sdes		err(1, "%s(): %s", __func__, filename);
67236099Sdes	if (unlink(filename) < 0)
68236099Sdes		err(1, "%s(): %s", __func__, filename);
69236099Sdes}
70236099Sdes
71236099Sdes/*
72236099Sdes * Write text to the temp file.
73236099Sdes */
74236099Sdesstatic void
75236099Sdesorlv_output(const char *fmt, ...)
76236099Sdes{
77236099Sdes	va_list ap;
78236099Sdes
79236099Sdes	va_start(ap, fmt);
80236099Sdes	vfprintf(f, fmt, ap);
81236099Sdes	va_end(ap);
82236099Sdes	if (ferror(f))
83236099Sdes		err(1, "%s", filename);
84236099Sdes}
85236099Sdes
86236099Sdes/*
87236099Sdes * Rewind the temp file.
88236099Sdes */
89236099Sdesstatic void
90236099Sdesorlv_rewind(void)
91236099Sdes{
92236099Sdes
93236099Sdes	errno = 0;
94236099Sdes	rewind(f);
95236099Sdes	if (errno != 0)
96236099Sdes		err(1, "%s(): %s", __func__, filename);
97236099Sdes}
98236099Sdes
99236099Sdes/*
100236099Sdes * Read a line from the temp file and verify that the result matches our
101236099Sdes * expectations: whether a line was read at all, how many and which words
102236099Sdes * it contained, how many lines were read (in case of quoted or escaped
103236099Sdes * newlines) and whether we reached the end of the file.
104236099Sdes */
105236099Sdesstatic int
106236099Sdesorlv_expect(const char **expectedv, int lines, int eof)
107236099Sdes{
108236099Sdes	int expectedc, gotc, i, lineno = 0;
109236099Sdes	char **gotv;
110236099Sdes
111236099Sdes	expectedc = 0;
112236099Sdes	if (expectedv != NULL)
113236099Sdes		while (expectedv[expectedc] != NULL)
114236099Sdes			++expectedc;
115236099Sdes	gotv = openpam_readlinev(f, &lineno, &gotc);
116236099Sdes	if (ferror(f))
117236099Sdes		err(1, "%s(): %s", __func__, filename);
118236099Sdes	if (expectedv != NULL && gotv == NULL) {
119236099Sdes		t_verbose("expected %d words, got nothing\n", expectedc);
120236099Sdes		return (0);
121236099Sdes	}
122236099Sdes	if (expectedv == NULL && gotv != NULL) {
123236099Sdes		t_verbose("expected nothing, got %d words\n", gotc);
124236099Sdes		FREEV(gotc, gotv);
125236099Sdes		return (0);
126236099Sdes	}
127236099Sdes	if (expectedv != NULL && gotv != NULL) {
128236099Sdes		if (expectedc != gotc) {
129236099Sdes			t_verbose("expected %d words, got %d\n",
130236099Sdes			    expectedc, gotc);
131236099Sdes			FREEV(gotc, gotv);
132236099Sdes			return (0);
133236099Sdes		}
134236099Sdes		for (i = 0; i < gotc; ++i) {
135236099Sdes			if (strcmp(expectedv[i], gotv[i]) != 0) {
136236099Sdes				t_verbose("word %d: expected <<%s>>, "
137236099Sdes				    "got <<%s>>\n", i, expectedv[i], gotv[i]);
138236099Sdes				FREEV(gotc, gotv);
139236099Sdes				return (0);
140236099Sdes			}
141236099Sdes		}
142236099Sdes		FREEV(gotc, gotv);
143236099Sdes	}
144236099Sdes	if (lineno != lines) {
145236099Sdes		t_verbose("expected to advance %d lines, advanced %d lines\n",
146236099Sdes		    lines, lineno);
147236099Sdes		return (0);
148236099Sdes	}
149236099Sdes	if (eof && !feof(f)) {
150236099Sdes		t_verbose("expected EOF, but didn't get it\n");
151236099Sdes		return (0);
152236099Sdes	}
153236099Sdes	if (!eof && feof(f)) {
154236099Sdes		t_verbose("didn't expect EOF, but got it anyway\n");
155236099Sdes		return (0);
156236099Sdes	}
157236099Sdes	return (1);
158236099Sdes}
159236099Sdes
160236099Sdes/*
161236099Sdes * Close the temp file.
162236099Sdes */
163236099Sdesvoid
164236099Sdesorlv_close(void)
165236099Sdes{
166236099Sdes
167236099Sdes	if (fclose(f) != 0)
168236099Sdes		err(1, "%s(): %s", __func__, filename);
169236099Sdes	f = NULL;
170236099Sdes}
171236099Sdes
172236099Sdes/***************************************************************************
173236099Sdes * Commonly-used lines
174236099Sdes */
175236099Sdes
176236099Sdesstatic const char *empty[] = {
177236099Sdes	NULL
178236099Sdes};
179236099Sdes
180236099Sdesstatic const char *hello[] = {
181236099Sdes	"hello",
182236099Sdes	NULL
183236099Sdes};
184236099Sdes
185236099Sdesstatic const char *hello_world[] = {
186236099Sdes	"hello",
187236099Sdes	"world",
188236099Sdes	NULL
189236099Sdes};
190236099Sdes
191236099Sdes
192236099Sdes/***************************************************************************
193236099Sdes * Lines without words
194236099Sdes */
195236099Sdes
196236099SdesT_FUNC(empty_input, "empty input")
197236099Sdes{
198236099Sdes	int ret;
199236099Sdes
200236099Sdes	orlv_open();
201236099Sdes	ret = orlv_expect(NULL, 0 /*lines*/, 1 /*eof*/);
202236099Sdes	orlv_close();
203236099Sdes	return (ret);
204236099Sdes}
205236099Sdes
206236099SdesT_FUNC(empty_line, "empty line")
207236099Sdes{
208236099Sdes	int ret;
209236099Sdes
210236099Sdes	orlv_open();
211236099Sdes	orlv_output("\n");
212236099Sdes	orlv_rewind();
213236099Sdes	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
214236099Sdes	orlv_close();
215236099Sdes	return (ret);
216236099Sdes}
217236099Sdes
218236099SdesT_FUNC(unterminated_empty_line, "unterminated empty line")
219236099Sdes{
220236099Sdes	int ret;
221236099Sdes
222236099Sdes	orlv_open();
223236099Sdes	orlv_output(" ");
224236099Sdes	orlv_rewind();
225236099Sdes	ret = orlv_expect(NULL, 0 /*lines*/, 1 /*eof*/);
226236099Sdes	orlv_close();
227236099Sdes	return (ret);
228236099Sdes}
229236099Sdes
230236099SdesT_FUNC(whitespace, "whitespace")
231236099Sdes{
232236099Sdes	int ret;
233236099Sdes
234236099Sdes	orlv_open();
235236099Sdes	orlv_output(" \n");
236236099Sdes	orlv_rewind();
237236099Sdes	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
238236099Sdes	orlv_close();
239236099Sdes	return (ret);
240236099Sdes}
241236099Sdes
242236099SdesT_FUNC(comment, "comment")
243236099Sdes{
244236099Sdes	int ret;
245236099Sdes
246236099Sdes	orlv_open();
247236099Sdes	orlv_output("# comment\n");
248236099Sdes	orlv_rewind();
249236099Sdes	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
250236099Sdes	orlv_close();
251236099Sdes	return (ret);
252236099Sdes}
253236099Sdes
254236099SdesT_FUNC(whitespace_before_comment, "whitespace before comment")
255236099Sdes{
256236099Sdes	int ret;
257236099Sdes
258236099Sdes	orlv_open();
259236099Sdes	orlv_output(" # comment\n");
260236099Sdes	orlv_rewind();
261236099Sdes	ret = orlv_expect(empty, 1 /*lines*/, 0 /*eof*/);
262236099Sdes	orlv_close();
263236099Sdes	return (ret);
264236099Sdes}
265236099Sdes
266236099Sdes
267236099Sdes/***************************************************************************
268236099Sdes * Simple words
269236099Sdes */
270236099Sdes
271236099SdesT_FUNC(one_word, "one word")
272236099Sdes{
273236099Sdes	int ret;
274236099Sdes
275236099Sdes	orlv_open();
276236099Sdes	orlv_output("hello\n");
277236099Sdes	orlv_rewind();
278236099Sdes	ret = orlv_expect(hello, 1 /*lines*/, 0 /*eof*/);
279236099Sdes	orlv_close();
280236099Sdes	return (ret);
281236099Sdes}
282236099Sdes
283236099SdesT_FUNC(two_words, "two words")
284236099Sdes{
285236099Sdes	int ret;
286236099Sdes
287236099Sdes	orlv_open();
288236099Sdes	orlv_output("hello world\n");
289236099Sdes	orlv_rewind();
290236099Sdes	ret = orlv_expect(hello_world, 1 /*lines*/, 0 /*eof*/);
291236099Sdes	orlv_close();
292236099Sdes	return (ret);
293236099Sdes}
294236099Sdes
295236099SdesT_FUNC(unterminated_line, "unterminated line")
296236099Sdes{
297236099Sdes	int ret;
298236099Sdes
299236099Sdes	orlv_open();
300236099Sdes	orlv_output("hello world");
301236099Sdes	orlv_rewind();
302236099Sdes	ret = orlv_expect(hello_world, 0 /*lines*/, 1 /*eof*/);
303236099Sdes	orlv_close();
304236099Sdes	return (ret);
305236099Sdes}
306236099Sdes
307236099Sdes
308236099Sdes/***************************************************************************
309236099Sdes * Boilerplate
310236099Sdes */
311236099Sdes
312236099Sdesconst struct t_test *t_plan[] = {
313236099Sdes	T(empty_input),
314236099Sdes	T(empty_line),
315236099Sdes	T(unterminated_empty_line),
316236099Sdes	T(whitespace),
317236099Sdes	T(comment),
318236099Sdes	T(whitespace_before_comment),
319236099Sdes
320236099Sdes	T(one_word),
321236099Sdes	T(two_words),
322236099Sdes	T(unterminated_line),
323236099Sdes
324236099Sdes	NULL
325236099Sdes};
326236099Sdes
327236099Sdesconst struct t_test **
328236099Sdest_prepare(int argc, char *argv[])
329236099Sdes{
330236099Sdes
331236099Sdes	(void)argc;
332236099Sdes	(void)argv;
333236099Sdes	snprintf(filename, sizeof filename, "%s.%d.tmp", t_progname, getpid());
334236099Sdes	if (filename == NULL)
335236099Sdes		err(1, "asprintf()");
336236099Sdes	return (t_plan);
337236099Sdes}
338236099Sdes
339236099Sdesvoid
340236099Sdest_cleanup(void)
341236099Sdes{
342236099Sdes}
343