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 51 static char filename[1024]; 52 static 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 */ 58 static void 59 orlv_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 */ 74 static void 75 orlv_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 */ 89 static void 90 orlv_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 */ 105 static int 106 orlv_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 */ 163 void 164 orlv_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 176 static const char *empty[] = { 177 NULL 178 }; 179 180 static const char *hello[] = { 181 "hello", 182 NULL 183 }; 184 185 static const char *hello_world[] = { 186 "hello", 187 "world", 188 NULL 189 }; 190 191 192 /*************************************************************************** 193 * Lines without words 194 */ 195 196 T_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 206 T_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 218 T_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 230 T_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 242 T_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 254 T_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 271 T_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 283 T_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 295 T_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 312 const 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 327 const struct t_test ** 328 t_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 339 void 340 t_cleanup(void) 341 { 342 } 343