]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/openpam/lib/openpam_readword.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / openpam / lib / openpam_readword.c
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: openpam_readword.c 588 2012-04-08 11:52:25Z des $
31  */
32
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include <errno.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40
41 #include <security/pam_appl.h>
42
43 #include "openpam_impl.h"
44 #include "openpam_ctype.h"
45
46 #define MIN_WORD_SIZE   32
47
48 /*
49  * OpenPAM extension
50  *
51  * Read a word from a file, respecting shell quoting rules.
52  */
53
54 char *
55 openpam_readword(FILE *f, int *lineno, size_t *lenp)
56 {
57         char *word;
58         size_t size, len;
59         int ch, comment, escape, quote;
60         int serrno;
61
62         errno = 0;
63
64         /* skip initial whitespace */
65         comment = 0;
66         while ((ch = getc(f)) != EOF && ch != '\n') {
67                 if (ch == '#')
68                         comment = 1;
69                 if (!is_lws(ch) && !comment)
70                         break;
71         }
72         if (ch == EOF)
73                 return (NULL);
74         ungetc(ch, f);
75         if (ch == '\n')
76                 return (NULL);
77
78         word = NULL;
79         size = len = 0;
80         escape = quote = 0;
81         while ((ch = fgetc(f)) != EOF && (!is_ws(ch) || quote || escape)) {
82                 if (ch == '\\' && !escape && quote != '\'') {
83                         /* escape next character */
84                         escape = ch;
85                 } else if ((ch == '\'' || ch == '"') && !quote && !escape) {
86                         /* begin quote */
87                         quote = ch;
88                         /* edge case: empty quoted string */
89                         if (openpam_straddch(&word, &size, &len, 0) != 0)
90                                 return (NULL);
91                 } else if (ch == quote && !escape) {
92                         /* end quote */
93                         quote = 0;
94                 } else if (ch == '\n' && escape && quote != '\'') {
95                         /* line continuation */
96                         escape = 0;
97                 } else {
98                         if (escape && quote && ch != '\\' && ch != quote &&
99                             openpam_straddch(&word, &size, &len, '\\') != 0) {
100                                 free(word);
101                                 errno = ENOMEM;
102                                 return (NULL);
103                         }
104                         if (openpam_straddch(&word, &size, &len, ch) != 0) {
105                                 free(word);
106                                 errno = ENOMEM;
107                                 return (NULL);
108                         }
109                         escape = 0;
110                 }
111                 if (lineno != NULL && ch == '\n')
112                         ++*lineno;
113         }
114         if (ch == EOF && ferror(f)) {
115                 serrno = errno;
116                 free(word);
117                 errno = serrno;
118                 return (NULL);
119         }
120         if (ch == EOF && (escape || quote)) {
121                 /* Missing escaped character or closing quote. */
122                 openpam_log(PAM_LOG_ERROR, "unexpected end of file");
123                 free(word);
124                 errno = EINVAL;
125                 return (NULL);
126         }
127         ungetc(ch, f);
128         if (lenp != NULL)
129                 *lenp = len;
130         return (word);
131 }
132
133 /**
134  * The =openpam_readword function reads the next word from a file, and
135  * returns it in a NUL-terminated buffer allocated with =!malloc.
136  *
137  * A word is a sequence of non-whitespace characters.
138  * However, whitespace characters can be included in a word if quoted or
139  * escaped according to the following rules:
140  *
141  *  - An unescaped single or double quote introduces a quoted string,
142  *    which ends when the same quote character is encountered a second
143  *    time.
144  *    The quotes themselves are stripped.
145  *
146  *  - Within a single- or double-quoted string, all whitespace characters,
147  *    including the newline character, are preserved as-is.
148  *
149  *  - Outside a quoted string, a backslash escapes the next character,
150  *    which is preserved as-is, unless that character is a newline, in
151  *    which case it is discarded and reading continues at the beginning of
152  *    the next line as if the backslash and newline had not been there.
153  *    In all cases, the backslash itself is discarded.
154  *
155  *  - Within a single-quoted string, double quotes and backslashes are
156  *    preserved as-is.
157  *
158  *  - Within a double-quoted string, a single quote is preserved as-is,
159  *    and a backslash is preserved as-is unless used to escape a double
160  *    quote.
161  *
162  * In addition, if the first non-whitespace character on the line is a
163  * hash character (#), the rest of the line is discarded.
164  * If a hash character occurs within a word, however, it is preserved
165  * as-is.
166  * A backslash at the end of a comment does cause line continuation.
167  *
168  * If =lineno is not =NULL, the integer variable it points to is
169  * incremented every time a quoted or escaped newline character is read.
170  *
171  * If =lenp is not =NULL, the length of the word (after quotes and
172  * backslashes have been removed) is stored in the variable it points to.
173  *
174  * RETURN VALUES
175  *
176  * If successful, the =openpam_readword function returns a pointer to a
177  * dynamically allocated NUL-terminated string containing the first word
178  * encountered on the line.
179  *
180  * The caller is responsible for releasing the returned buffer by passing
181  * it to =!free.
182  *
183  * If =openpam_readword reaches the end of the line or file before any
184  * characters are copied to the word, it returns =NULL.  In the former
185  * case, the newline is pushed back to the file.
186  *
187  * If =openpam_readword reaches the end of the file while a quote or
188  * backslash escape is in effect, it sets :errno to =EINVAL and returns
189  * =NULL.
190  *
191  * IMPLEMENTATION NOTES
192  *
193  * The parsing rules are intended to be equivalent to the normal POSIX
194  * shell quoting rules.
195  * Any discrepancy is a bug and should be reported to the author along
196  * with sample input that can be used to reproduce the error.
197  *
198  * >openpam_readline
199  * >openpam_readlinev
200  *
201  * AUTHOR DES
202  */