/src/p11-kit/common/lexer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2005 Stefan Walter |
3 | | * Copyright (c) 2011 Collabora Ltd. |
4 | | * Copyright (c) 2013 Red Hat Inc. |
5 | | * |
6 | | * Redistribution and use in source and binary forms, with or without |
7 | | * modification, are permitted provided that the following conditions |
8 | | * are met: |
9 | | * |
10 | | * * Redistributions of source code must retain the above |
11 | | * copyright notice, this list of conditions and the |
12 | | * following disclaimer. |
13 | | * * Redistributions in binary form must reproduce the |
14 | | * above copyright notice, this list of conditions and |
15 | | * the following disclaimer in the documentation and/or |
16 | | * other materials provided with the distribution. |
17 | | * * The names of contributors to this software may not be |
18 | | * used to endorse or promote products derived from this |
19 | | * software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
22 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
23 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
24 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
25 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
26 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
27 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
28 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
29 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
30 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
31 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
32 | | * DAMAGE. |
33 | | * |
34 | | * |
35 | | * CONTRIBUTORS |
36 | | * Stef Walter <stefw@redhat.com> |
37 | | */ |
38 | | |
39 | | #include "config.h" |
40 | | |
41 | | #define P11_DEBUG_FLAG P11_DEBUG_CONF |
42 | | #include "debug.h" |
43 | | #include "lexer.h" |
44 | | #include "message.h" |
45 | | |
46 | | #include <assert.h> |
47 | | #include <ctype.h> |
48 | | #include <errno.h> |
49 | | #include <stdio.h> |
50 | | #include <stdlib.h> |
51 | | #include <string.h> |
52 | | |
53 | | void |
54 | | p11_lexer_init (p11_lexer *lexer, |
55 | | const char *filename, |
56 | | const char *data, |
57 | | size_t length) |
58 | 0 | { |
59 | 0 | return_if_fail (lexer != NULL); |
60 | | |
61 | 0 | memset (lexer, 0, sizeof (p11_lexer)); |
62 | 0 | lexer->at = data; |
63 | 0 | lexer->remaining = length; |
64 | |
|
65 | 0 | return_if_fail (filename != NULL); |
66 | 0 | lexer->filename = strdup (filename); |
67 | 0 | return_if_fail (lexer->filename != NULL); |
68 | 0 | } |
69 | | |
70 | | static void |
71 | | clear_state (p11_lexer *lexer) |
72 | 0 | { |
73 | 0 | switch (lexer->tok_type) { |
74 | 0 | case TOK_FIELD: |
75 | 0 | free (lexer->tok.field.name); |
76 | 0 | free (lexer->tok.field.value); |
77 | 0 | break; |
78 | 0 | case TOK_SECTION: |
79 | 0 | free (lexer->tok.section.name); |
80 | 0 | break; |
81 | 0 | case TOK_PEM: |
82 | 0 | case TOK_EOF: |
83 | 0 | break; |
84 | 0 | } |
85 | | |
86 | 0 | memset (&lexer->tok, 0, sizeof (lexer->tok)); |
87 | 0 | lexer->tok_type = TOK_EOF; |
88 | 0 | lexer->complained = false; |
89 | 0 | } |
90 | | |
91 | | bool |
92 | | p11_lexer_next (p11_lexer *lexer, |
93 | | bool *failed) |
94 | 0 | { |
95 | 0 | const char *colon; |
96 | 0 | const char *value; |
97 | 0 | const char *line; |
98 | 0 | const char *end; |
99 | 0 | const char *pos; |
100 | 0 | char *part; |
101 | |
|
102 | 0 | return_val_if_fail (lexer != NULL, false); |
103 | | |
104 | 0 | clear_state (lexer); |
105 | 0 | if (failed) |
106 | 0 | *failed = false; |
107 | | |
108 | | /* Go through lines and process them */ |
109 | 0 | while (lexer->remaining != 0) { |
110 | 0 | assert (lexer->remaining > 0); |
111 | | |
112 | | /* Is this line the start of a PEM block? */ |
113 | 0 | if (strncmp (lexer->at, "-----BEGIN ", 11) == 0) { |
114 | 0 | pos = strnstr (lexer->at, "\n-----END ", lexer->remaining); |
115 | 0 | if (pos != NULL) { |
116 | 0 | end = memchr (pos + 1, '\n', lexer->remaining - (pos - lexer->at) - 1); |
117 | 0 | if (end) |
118 | 0 | end += 1; |
119 | 0 | else |
120 | 0 | end = lexer->at + lexer->remaining; |
121 | | /* Count newlines in the PEM block */ |
122 | 0 | pos = lexer->at; |
123 | 0 | while (pos < end) { |
124 | 0 | pos = memchr (pos, '\n', end - pos); |
125 | 0 | if (!pos) |
126 | 0 | break; |
127 | 0 | lexer->line++; |
128 | 0 | pos++; |
129 | 0 | } |
130 | 0 | lexer->tok_type = TOK_PEM; |
131 | 0 | lexer->tok.pem.begin = lexer->at; |
132 | 0 | lexer->tok.pem.length = end - lexer->at; |
133 | 0 | assert (end - lexer->at <= lexer->remaining); |
134 | 0 | lexer->remaining -= (end - lexer->at); |
135 | 0 | lexer->at = end; |
136 | 0 | return true; |
137 | 0 | } |
138 | | |
139 | 0 | p11_lexer_msg (lexer, "invalid pem block: no ending line"); |
140 | 0 | if (failed) |
141 | 0 | *failed = true; |
142 | 0 | return false; |
143 | 0 | } |
144 | | |
145 | 0 | line = lexer->at; |
146 | 0 | end = memchr (lexer->at, '\n', lexer->remaining); |
147 | 0 | if (end == NULL) { |
148 | 0 | end = lexer->at + lexer->remaining; |
149 | 0 | lexer->remaining = 0; |
150 | 0 | lexer->at = end; |
151 | 0 | } else { |
152 | 0 | assert ((end - lexer->at) + 1 <= lexer->remaining); |
153 | 0 | lexer->line++; |
154 | 0 | lexer->remaining -= (end - lexer->at) + 1; |
155 | 0 | lexer->at = end + 1; |
156 | 0 | } |
157 | | |
158 | | /* Strip whitespace from line */ |
159 | 0 | while (line != end && isspace (line[0])) |
160 | 0 | ++line; |
161 | 0 | while (line != end && isspace (*(end - 1))) |
162 | 0 | --end; |
163 | | |
164 | | /* Empty lines / comments at start */ |
165 | 0 | if (line == end || line[0] == '#') |
166 | 0 | continue; |
167 | | |
168 | | /* Is the the a section ? */ |
169 | 0 | if (line[0] == '[') { |
170 | 0 | if (*(end - 1) != ']') { |
171 | 0 | part = strndup (line, end - line); |
172 | 0 | p11_lexer_msg (lexer, "invalid section header: missing braces"); |
173 | 0 | free (part); |
174 | 0 | if (failed) |
175 | 0 | *failed = true; |
176 | 0 | return false; |
177 | 0 | } |
178 | | |
179 | 0 | lexer->tok_type = TOK_SECTION; |
180 | 0 | lexer->tok.section.name = strndup (line + 1, (end - line) - 2); |
181 | 0 | return_val_if_fail (lexer->tok.section.name != NULL, false); |
182 | 0 | return true; |
183 | 0 | } |
184 | | |
185 | | /* Look for the break between name: value on the same line */ |
186 | 0 | colon = memchr (line, ':', end - line); |
187 | 0 | if (!colon) { |
188 | 0 | part = strndup (line, end - line); |
189 | 0 | p11_lexer_msg (lexer, "invalid field line: no colon"); |
190 | 0 | free (part); |
191 | 0 | if (failed) |
192 | 0 | *failed = true; |
193 | 0 | return false; |
194 | 0 | } |
195 | | |
196 | | /* Strip whitespace from name and value */ |
197 | 0 | value = colon + 1; |
198 | 0 | while (value != end && isspace (value[0])) |
199 | 0 | ++value; |
200 | 0 | while (line != colon && isspace (*(colon - 1))) |
201 | 0 | --colon; |
202 | |
|
203 | 0 | lexer->tok_type = TOK_FIELD; |
204 | 0 | lexer->tok.field.name = strndup (line, colon - line); |
205 | 0 | lexer->tok.field.value = strndup (value, end - value); |
206 | 0 | return_val_if_fail (lexer->tok.field.name && lexer->tok.field.value, false); |
207 | 0 | return true; |
208 | 0 | } |
209 | | |
210 | 0 | return false; |
211 | 0 | } |
212 | | |
213 | | void |
214 | | p11_lexer_done (p11_lexer *lexer) |
215 | 0 | { |
216 | 0 | return_if_fail (lexer != NULL); |
217 | 0 | clear_state (lexer); |
218 | 0 | free (lexer->filename); |
219 | 0 | memset (lexer, 0, sizeof (p11_lexer)); |
220 | 0 | } |
221 | | |
222 | | void |
223 | | p11_lexer_msg (p11_lexer *lexer, |
224 | | const char *msg) |
225 | 0 | { |
226 | 0 | return_if_fail (lexer != NULL); |
227 | | |
228 | 0 | if (lexer->complained) |
229 | 0 | return; |
230 | | |
231 | 0 | switch (lexer->tok_type) { |
232 | 0 | case TOK_FIELD: |
233 | 0 | p11_message ("%s:%zu: %s: %s", lexer->filename, lexer->line, |
234 | 0 | lexer->tok.field.name, msg); |
235 | 0 | break; |
236 | 0 | case TOK_SECTION: |
237 | 0 | p11_message ("%s:%zu: [%s]: %s", lexer->filename, lexer->line, |
238 | 0 | lexer->tok.section.name, msg); |
239 | 0 | break; |
240 | 0 | case TOK_PEM: |
241 | 0 | p11_message ("%s:%zu: BEGIN ...: %s", lexer->filename, |
242 | 0 | lexer->line, msg); |
243 | 0 | break; |
244 | 0 | default: |
245 | 0 | p11_message ("%s:%zu: %s", lexer->filename, lexer->line, msg); |
246 | 0 | break; |
247 | 0 | } |
248 | | |
249 | 0 | lexer->complained = true; |
250 | 0 | } |