1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
/* pam_assemble_line.h -- routine to parse configuration lines */
#ifndef PAM_ASSEMBLE_LINE_H
#define PAM_ASSEMBLE_LINE_H
#include "pam_inline.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct line_buffer {
char *assembled;
char *chunk;
size_t chunk_size;
size_t len;
size_t size;
};
static int _pam_buffer_add(struct line_buffer *buffer, char *start, char *end)
{
size_t len = end - start;
D(("assembled: [%zu/%zu] '%s', adding [%zu] '%s'",
buffer->len, buffer->size,
buffer->assembled == NULL ? "" : buffer->assembled, len, start));
if (start == end)
return 0;
if (buffer->assembled == NULL && buffer->chunk == start) {
/* no extra allocation needed, just move chunk to assembled */
buffer->assembled = buffer->chunk;
buffer->len = len;
buffer->size = buffer->chunk_size;
buffer->chunk = NULL;
buffer->chunk_size = 0;
D(("exiting with quick exchange"));
return 0;
}
if (buffer->len + len + 1 > buffer->size) {
size_t size;
char *p;
size = buffer->len + len + 1;
if ((p = realloc(buffer->assembled, size)) == NULL)
return -1;
buffer->assembled = p;
buffer->size = size;
}
memcpy(buffer->assembled + buffer->len, start, len);
buffer->len += len;
buffer->assembled[buffer->len] = '\0';
D(("exiting"));
return 0;
}
static inline int _pam_buffer_add_eol(struct line_buffer *buffer,
char *start, char *end)
{
if (buffer->assembled != NULL || (*start != '\0' && *start != '\n'))
return _pam_buffer_add(buffer, start, end);
return 0;
}
static void _pam_buffer_clear(struct line_buffer *buffer)
{
_pam_drop(buffer->assembled);
_pam_drop(buffer->chunk);
buffer->chunk_size = 0;
buffer->len = 0;
buffer->size = 0;
}
static void _pam_buffer_init(struct line_buffer *buffer)
{
buffer->assembled = NULL;
buffer->chunk = NULL;
_pam_buffer_clear(buffer);
}
static void _pam_buffer_purge(struct line_buffer *buffer)
{
_pam_drop(buffer->chunk);
buffer->chunk_size = 0;
}
static void _pam_buffer_shift(struct line_buffer *buffer)
{
if (buffer->assembled == NULL)
return;
_pam_buffer_purge(buffer);
buffer->chunk = buffer->assembled;
buffer->chunk_size = buffer->size;
buffer->assembled = NULL;
buffer->size = 0;
buffer->len = 0;
}
static inline int _pam_buffer_valid(struct line_buffer *buffer)
{
return buffer->assembled != NULL && *buffer->assembled != '\0';
}
/*
* Trim string to relevant parts of a configuration line.
*
* Preceding whitespaces are skipped and comment (#) marks the end of
* configuration line.
*
* Returns start of configuration line.
*/
static inline char *_pam_str_trim(char *str)
{
/* skip leading spaces */
str += strspn(str, " \t");
/*
* we are only interested in characters before the first '#'
* character
*/
str[strcspn(str, "#")] = '\0';
return str;
}
/*
* Remove escaped newline from end of string.
*
* Configuration lines may span across multiple lines in a file
* by ending a line with a backslash (\).
*
* If an escaped newline is encountered, the backslash will be
* replaced with a blank ' ' and the newline itself removed.
* Then the variable "end" will point to the new end of line.
*
* Returns 0 if escaped newline was found and replaced, 1 otherwise.
*/
static inline int _pam_str_unescnl(char *start, char **end)
{
int ret = 1;
char *p = *end;
/*
* Check for backslash by scanning back from the end of
* the entered line, the '\n' should be included since
* normally a line is terminated with this character.
*/
while (p > start && ((*--p == ' ') || (*p == '\t') || (*p == '\n')))
;
if (*p == '\\') {
*p++ = ' '; /* replace backslash with ' ' */
*p = '\0'; /* truncate the line here */
*end = p;
ret = 0;
}
return ret;
}
/*
* Prepare line from file for configuration line parsing.
*
* A configuration line may span across multiple lines in a file.
* Remove comments and skip preceding whitespaces.
*
* Returns 0 if line spans across multiple lines, 1 if
* end of line is encountered.
*/
static inline int _pam_str_prepare(char *line, ssize_t len,
char **start, char **end)
{
int ret;
*start = line;
*end = line + len;
ret = _pam_str_unescnl(*start, end) || strchr(*start, '#') != NULL;
*start = _pam_str_trim(*start);
return ret;
}
/*
* This is where we read a line of the PAM config file. The line may be
* preceded by lines of comments and also extended with "\\\n"
*
* Returns 0 on EOF, 1 on successful line parsing, or -1 on error.
*/
static int _pam_assemble_line(FILE *f, struct line_buffer *buffer)
{
int ret = 0;
/* loop broken with a 'break' when a non-'\\n' ended line is read */
D(("called."));
_pam_buffer_shift(buffer);
for (;;) {
char *start, *end;
ssize_t n;
int eol;
if ((n = getline(&buffer->chunk, &buffer->chunk_size, f)) == -1) {
if (ret) {
/* Incomplete read */
ret = -1;
} else {
/* EOF */
ret = 0;
}
break;
}
eol = _pam_str_prepare(buffer->chunk, n, &start, &end);
if (eol) {
if (_pam_buffer_add_eol(buffer, start, end)) {
ret = -1;
break;
}
if (_pam_buffer_valid(buffer)) {
/* Successfully parsed a line */
ret = 1;
break;
}
/* Start parsing next line */
_pam_buffer_shift(buffer);
ret = 0;
} else {
/* Configuration line spans across multiple lines in file */
if (_pam_buffer_add(buffer, start, end)) {
ret = -1;
break;
}
/* Keep parsing line */
ret = 1;
}
}
if (ret == 1)
_pam_buffer_purge(buffer);
else
_pam_buffer_clear(buffer);
return ret;
}
#endif /* PAM_ASSEMBLE_LINE_H */
|