source: openpam/trunk/lib/openpam_readword.c @ 567

Last change on this file since 567 was 567, checked in by des, 2 years ago

Fix backslashes within single-quoted strings (no escape function)
Fix line continuation (newline is stripped, not quoted)
Further improve the documentation

  • Property svn:keywords set to Id
File size: 6.3 KB
Line 
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$
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
54char *
55openpam_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 (word == NULL && (word = malloc(1)) == NULL) {
90                                openpam_log(PAM_LOG_ERROR, "malloc(): %m");
91                                errno = ENOMEM;
92                                return (NULL);
93                        }
94                        *word = '\0';
95                        size = 1;
96                } else if (ch == quote && !escape) {
97                        /* end quote */
98                        quote = 0;
99                } else if (ch == '\n' && escape && !quote) {
100                        /* line continuation */
101                        escape = 0;
102                } else {
103                        if (escape && quote && ch != '\\' && ch != quote &&
104                            openpam_straddch(&word, &size, &len, '\\') != 0) {
105                                free(word);
106                                errno = ENOMEM;
107                                return (NULL);
108                        }
109                        if (openpam_straddch(&word, &size, &len, ch) != 0) {
110                                free(word);
111                                errno = ENOMEM;
112                                return (NULL);
113                        }
114                        escape = 0;
115                }
116                if (lineno != NULL && ch == '\n')
117                        ++*lineno;
118        }
119        if (ch == EOF && ferror(f)) {
120                serrno = errno;
121                free(word);
122                errno = serrno;
123                return (NULL);
124        }
125        if (ch == EOF && (escape || quote)) {
126                /* Missing escaped character or closing quote. */
127                openpam_log(PAM_LOG_ERROR, "unexpected end of file");
128                free(word);
129                errno = EINVAL;
130                return (NULL);
131        }
132        ungetc(ch, f);
133        if (lenp != NULL)
134                *lenp = len;
135        return (word);
136}
137
138/**
139 * The =openpam_readword function reads the next word from a file, and
140 * returns it in a NUL-terminated buffer allocated with =!malloc.
141 *
142 * A word is a sequence of non-whitespace characters.
143 * However, whitespace characters can be included in a word if quoted or
144 * escaped according to the following rules:
145 *
146 *  - An unescaped single or double quote introduces a quoted string,
147 *    which ends when the same quote character is encountered a second
148 *    time.
149 *    The quotes themselves are stripped.
150 *
151 *  - Within a single- or double-quoted string, all whitespace characters,
152 *    including the newline character, are preserved as-is.
153 *
154 *  - Outside a quoted string, a backslash escapes the next character,
155 *    which is preserved as-is, unless that character is a newline, in
156 *    which case it is discarded and reading continues at the beginning of
157 *    the next line as if the backslash and newline had not been there.
158 *    In all cases, the backslash itself is discarded.
159 *
160 *  - Within a single-quoted string, double quotes and backslashes are
161 *    preserved as-is.
162 *
163 *  - Within a double-quoted string, a single quote is preserved as-is,
164 *    and a backslash is preserved as-is unless used to escape a double
165 *    quote.
166 *
167 * In addition, if the first non-whitespace character on the line is a
168 * hash character (#), the rest of the line is discarded.
169 * If a hash character occurs within a word, however, it is preserved
170 * as-is.
171 * A backslash at the end of a comment does cause line continuation.
172 *
173 * If =lineno is not =NULL, the integer variable it points to is
174 * incremented every time a quoted or escaped newline character is read.
175 *
176 * If =lenp is not =NULL, the length of the word (after quotes and
177 * backslashes have been removed) is stored in the variable it points to.
178 *
179 *RETURN VALUES
180 *
181 * If successful, the =openpam_readword function returns a pointer to a
182 * dynamically allocated NUL-terminated string containing the first word
183 * encountered on the line.
184 *
185 * The caller is responsible for releasing the returned buffer by passing
186 * it to =!free.
187 *
188 * If =openpam_readword reaches the end of the line or file before any
189 * characters are copied to the word, it returns =NULL.  In the latter
190 * case, the newline is pushed back to the file.
191 *
192 * If =openpam_readword reaches the end of the file while a quote or
193 * backslash escape is in effect, it sets :errno to =EINVAL and returns
194 * =NULL.
195 *
196 *IMPLEMENTATION NOTES
197 *
198 * The parsing rules are intended to be equivalent to the normal POSIX
199 * shell quoting rules.
200 * Any discrepancy is a bug and should be reported to the author along
201 * with sample input that can be used to reproduce the error.
202 *
203 *>openpam_readline
204 *>openpam_readlinev
205 */
Note: See TracBrowser for help on using the repository browser.