1 /*
2 * Copyright (c) 1987, 1993, 1994
3 * The Regents of the University of California. 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 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #include <config.h>
35
36 #include "ftmacros.h"
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <signal.h>
41 #include <pcap.h> // for PCAP_ERRBUF_SIZE
42
43 #include "portability.h"
44 #include "rpcapd.h"
45 #include "config_params.h" // configuration file parameters
46 #include "fileconf.h"
47 #include "rpcap-protocol.h"
48 #include "log.h"
49
50 //
51 // Parameter names.
52 //
53 #define PARAM_ACTIVECLIENT "ActiveClient"
54 #define PARAM_PASSIVECLIENT "PassiveClient"
55 #define PARAM_NULLAUTHPERMIT "NullAuthPermit"
56
57 static char *skipws(char *ptr);
58
59 /*
60 * Locale-independent version checks for alphabetical and alphanumerical
61 * characters that also can handle being handed a char value that might
62 * be negative.
63 */
64 #define FILECONF_ISALPHA(c) \
65 (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
66 #define FILECONF_ISALNUM(c) \
67 (FILECONF_ISALPHA(c) || ((c) >= '0' && (c) <= '9'))
68
fileconf_read(void)69 void fileconf_read(void)
70 {
71 FILE *fp;
72 unsigned int num_active_clients;
73
74 if ((fp = fopen(loadfile, "r")) != NULL)
75 {
76 char line[MAX_LINE + 1];
77 unsigned int lineno;
78
79 hostlist[0] = 0;
80 num_active_clients = 0;
81 lineno = 0;
82
83 while (fgets(line, MAX_LINE, fp) != NULL)
84 {
85 size_t linelen;
86 char *ptr;
87 char *param;
88 size_t result;
89 size_t toklen;
90
91 lineno++;
92
93 linelen = strlen(line);
94 if (line[linelen - 1] != '\n')
95 {
96 int c;
97
98 //
99 // Either the line doesn't fit in
100 // the buffer, or we got an EOF
101 // before the EOL. Assume it's the
102 // former.
103 //
104 rpcapd_log(LOGPRIO_ERROR,
105 "%s, line %u is longer than %u characters",
106 loadfile, lineno, MAX_LINE);
107
108 //
109 // Eat characters until we get an NL.
110 //
111 while ((c = getc(fp)) != '\n')
112 {
113 if (c == EOF)
114 goto done;
115 }
116
117 //
118 // Try the next line.
119 //
120 continue;
121 }
122 ptr = line;
123
124 //
125 // Skip leading white space, if any.
126 //
127 ptr = skipws(ptr);
128 if (ptr == NULL)
129 {
130 // Blank line.
131 continue;
132 }
133
134 //
135 // Is the next character a "#"? If so, this
136 // line is a comment; skip to the next line.
137 //
138 if (*ptr == '#')
139 continue;
140
141 //
142 // Is the next character alphabetic? If not,
143 // this isn't a valid parameter name.
144 //
145 if (FILECONF_ISALPHA(*ptr))
146 {
147 rpcapd_log(LOGPRIO_ERROR,
148 "%s, line %u doesn't have a valid parameter name",
149 loadfile, lineno);
150 continue;
151 }
152
153 //
154 // Grab the first token, which is made of
155 // alphanumerics, underscores, and hyphens.
156 // That's the name of the parameter being set.
157 //
158 param = ptr;
159 while (FILECONF_ISALNUM(*ptr) || *ptr == '-' || *ptr == '_')
160 ptr++;
161
162 //
163 // Skip over white space, if any.
164 //
165 ptr = skipws(ptr);
166 if (ptr == NULL || *ptr != '=')
167 {
168 //
169 // We hit the end of the line before
170 // finding a non-white space character,
171 // or we found one but it's not an "=".
172 // That means there's no "=", so this
173 // line is invalid. Complain and skip
174 // this line.
175 //
176 rpcapd_log(LOGPRIO_ERROR,
177 "%s, line %u has a parameter but no =",
178 loadfile, lineno);
179 continue;
180 }
181
182 //
183 // We found the '='; set it to '\0', and skip
184 // past it.
185 //
186 *ptr++ = '\0';
187
188 //
189 // Skip past any white space after the "=".
190 //
191 ptr = skipws(ptr);
192 if (ptr == NULL)
193 {
194 //
195 // The value is empty.
196 //
197 rpcapd_log(LOGPRIO_ERROR,
198 "%s, line %u has a parameter but no value",
199 loadfile, lineno);
200 continue;
201 }
202
203 //
204 // OK, what parameter is this?
205 //
206 if (strcmp(param, PARAM_ACTIVECLIENT) == 0) {
207 //
208 // Add this to the list of active clients.
209 //
210 char *address, *port;
211
212 //
213 // We can't have more than MAX_ACTIVE_LIST
214 // active clients.
215 //
216 if (num_active_clients >= MAX_ACTIVE_LIST)
217 {
218 //
219 // Too many entries for the active
220 // client list. Complain and
221 // ignore it.
222 //
223 rpcapd_log(LOGPRIO_ERROR,
224 "%s, line %u has an %s parameter, but we already have %u active clients",
225 loadfile, lineno, PARAM_ACTIVECLIENT,
226 MAX_ACTIVE_LIST);
227 continue;
228 }
229
230 //
231 // Get the address.
232 // It's terminated by a host list separator
233 // *or* a #; there *shouldn't* be a #, as
234 // that starts a comment, and that would
235 // mean that we have no port.
236 //
237 address = ptr;
238 toklen = strcspn(ptr, RPCAP_HOSTLIST_SEP "#");
239 ptr += toklen; // skip to the terminator
240 if (toklen == 0)
241 {
242 if (*ptr == ' ' || *ptr == '\t' ||
243 *ptr == '\r' || *ptr == '\n' ||
244 *ptr == '#' || *ptr == '\0')
245 {
246 //
247 // The first character it saw
248 // was a whitespace character
249 // or a comment character,
250 // or we ran out of characters.
251 // This means that there's
252 // no value.
253 //
254 rpcapd_log(LOGPRIO_ERROR,
255 "%s, line %u has a parameter but no value",
256 loadfile, lineno);
257 }
258 else
259 {
260 //
261 // This means that the first
262 // character it saw was a
263 // separator. This means that
264 // there's no address in the
265 // value, just a port.
266 //
267 rpcapd_log(LOGPRIO_ERROR,
268 "%s, line %u has an %s parameter with a value containing no address",
269 loadfile, lineno, PARAM_ACTIVECLIENT);
270 }
271 continue;
272 }
273
274 //
275 // Null-terminate the address, and skip past
276 // it.
277 //
278 *ptr++ = '\0';
279
280 //
281 // Skip any white space following the
282 // separating character.
283 //
284 ptr = skipws(ptr);
285 if (ptr == NULL)
286 {
287 //
288 // The value is empty, so there's
289 // no port in the value.
290 //
291 rpcapd_log(LOGPRIO_ERROR,
292 "%s, line %u has an %s parameter with a value containing no port",
293 loadfile, lineno, PARAM_ACTIVECLIENT);
294 continue;
295 }
296
297 //
298 // Get the port.
299 // We look for a white space character
300 // or a # as a terminator; the # introduces
301 // a comment that runs to the end of the
302 // line.
303 //
304 port = ptr;
305 toklen = strcspn(ptr, " \t#\r\n");
306 ptr += toklen;
307 if (toklen == 0)
308 {
309 //
310 // The value is empty, so there's
311 // no port in the value.
312 //
313 rpcapd_log(LOGPRIO_ERROR,
314 "%s, line %u has an %s parameter with a value containing no port",
315 loadfile, lineno, PARAM_ACTIVECLIENT);
316 continue;
317 }
318
319 //
320 // Null-terminate the port, and skip past
321 // it.
322 //
323 *ptr++ = '\0';
324 result = pcapint_strlcpy(activelist[num_active_clients].address, address, sizeof(activelist[num_active_clients].address));
325 if (result >= sizeof(activelist[num_active_clients].address))
326 {
327 //
328 // It didn't fit.
329 //
330 rpcapd_log(LOGPRIO_ERROR,
331 "%s, line %u has an %s parameter with an address with more than %u characters",
332 loadfile, lineno, PARAM_ACTIVECLIENT,
333 (unsigned int)(sizeof(activelist[num_active_clients].address) - 1));
334 continue;
335 }
336 if (strcmp(port, "DEFAULT") == 0) // the user choose a custom port
337 result = pcapint_strlcpy(activelist[num_active_clients].port, RPCAP_DEFAULT_NETPORT_ACTIVE, sizeof(activelist[num_active_clients].port));
338 else
339 result = pcapint_strlcpy(activelist[num_active_clients].port, port, sizeof(activelist[num_active_clients].port));
340 if (result >= sizeof(activelist[num_active_clients].address))
341 {
342 //
343 // It didn't fit.
344 //
345 rpcapd_log(LOGPRIO_ERROR,
346 "%s, line %u has an %s parameter with an port with more than %u characters",
347 loadfile, lineno, PARAM_ACTIVECLIENT,
348 (unsigned int)(sizeof(activelist[num_active_clients].port) - 1));
349 continue;
350 }
351
352 num_active_clients++;
353 }
354 else if (strcmp(param, PARAM_PASSIVECLIENT) == 0)
355 {
356 char *eos;
357 char *host;
358
359 //
360 // Get the host.
361 // We look for a white space character
362 // or a # as a terminator; the # introduces
363 // a comment that runs to the end of the
364 // line.
365 //
366 host = ptr;
367 toklen = strcspn(ptr, " \t#\r\n");
368 if (toklen == 0)
369 {
370 //
371 // The first character it saw
372 // was a whitespace character
373 // or a comment character.
374 // This means that there's
375 // no value.
376 //
377 rpcapd_log(LOGPRIO_ERROR,
378 "%s, line %u has a parameter but no value",
379 loadfile, lineno);
380 continue;
381 }
382 ptr += toklen;
383 *ptr++ = '\0';
384
385 //
386 // Append this to the host list.
387 // Save the current end-of-string for the
388 // host list, in case the new host doesn't
389 // fit, so that we can discard the partially-
390 // copied host name.
391 //
392 eos = hostlist + strlen(hostlist);
393 if (eos != hostlist)
394 {
395 //
396 // The list is not empty, so prepend
397 // a comma before adding this host.
398 //
399 result = pcapint_strlcat(hostlist, ",", sizeof(hostlist));
400 if (result >= sizeof(hostlist))
401 {
402 //
403 // It didn't fit. Discard
404 // the comma (which wasn't
405 // added, but...), complain,
406 // and ignore this line.
407 //
408 *eos = '\0';
409 rpcapd_log(LOGPRIO_ERROR,
410 "%s, line %u has a %s parameter with a host name that doesn't fit",
411 loadfile, lineno, PARAM_PASSIVECLIENT);
412 continue;
413 }
414 }
415 result = pcapint_strlcat(hostlist, host, sizeof(hostlist));
416 if (result >= sizeof(hostlist))
417 {
418 //
419 // It didn't fit. Discard the comma,
420 // complain, and ignore this line.
421 //
422 *eos = '\0';
423 rpcapd_log(LOGPRIO_ERROR,
424 "%s, line %u has a %s parameter with a host name that doesn't fit",
425 loadfile, lineno, PARAM_PASSIVECLIENT);
426 continue;
427 }
428 }
429 else if (strcmp(param, PARAM_NULLAUTHPERMIT) == 0)
430 {
431 char *setting;
432
433 //
434 // Get the setting.
435 // We look for a white space character
436 // or a # as a terminator; the # introduces
437 // a comment that runs to the end of the
438 // line.
439 //
440 setting = ptr;
441 toklen = strcspn(ptr, " \t#\r\n");
442 ptr += toklen;
443 if (toklen == 0)
444 {
445 //
446 // The first character it saw
447 // was a whitespace character
448 // or a comment character.
449 // This means that there's
450 // no value.
451 //
452 rpcapd_log(LOGPRIO_ERROR,
453 "%s, line %u has a parameter but no value",
454 loadfile, lineno);
455 continue;
456 }
457 *ptr++ = '\0';
458
459 //
460 // XXX - should we complain if it's
461 // neither "yes" nor "no"?
462 //
463 if (strcmp(setting, "YES") == 0)
464 nullAuthAllowed = 1;
465 else
466 nullAuthAllowed = 0;
467 }
468 else
469 {
470 rpcapd_log(LOGPRIO_ERROR,
471 "%s, line %u has an unknown parameter %s",
472 loadfile, lineno, param);
473 continue;
474 }
475 }
476
477 done:
478 // clear the remaining fields of the active list
479 for (int i = num_active_clients; i < MAX_ACTIVE_LIST; i++)
480 {
481 activelist[i].address[0] = 0;
482 activelist[i].port[0] = 0;
483 num_active_clients++;
484 }
485
486 rpcapd_log(LOGPRIO_DEBUG, "New passive host list: %s", hostlist);
487 fclose(fp);
488 }
489 }
490
fileconf_save(const char * savefile)491 int fileconf_save(const char *savefile)
492 {
493 FILE *fp;
494
495 if ((fp = fopen(savefile, "w")) != NULL)
496 {
497 char *token; /*, *port;*/ // temp, needed to separate items into the hostlist
498 char temphostlist[MAX_HOST_LIST + 1];
499 int i = 0;
500 char *lasts;
501
502 fprintf(fp, "# Configuration file help.\n\n");
503
504 // Save list of clients which are allowed to connect to us in passive mode
505 fprintf(fp, "# Hosts which are allowed to connect to this server (passive mode)\n");
506 fprintf(fp, "# Format: PassiveClient = <name or address>\n\n");
507
508 pcapint_strlcpy(temphostlist, hostlist, sizeof (temphostlist));
509
510 token = pcapint_strtok_r(temphostlist, RPCAP_HOSTLIST_SEP, &lasts);
511 while(token != NULL)
512 {
513 fprintf(fp, "%s = %s\n", PARAM_PASSIVECLIENT, token);
514 token = pcapint_strtok_r(NULL, RPCAP_HOSTLIST_SEP, &lasts);
515 }
516
517
518 // Save list of clients which are allowed to connect to us in active mode
519 fprintf(fp, "\n\n");
520 fprintf(fp, "# Hosts to which this server is trying to connect to (active mode)\n");
521 fprintf(fp, "# Format: ActiveClient = <name or address>, <port | DEFAULT>\n\n");
522
523
524 while ((i < MAX_ACTIVE_LIST) && (activelist[i].address[0] != 0))
525 {
526 fprintf(fp, "%s = %s, %s\n", PARAM_ACTIVECLIENT,
527 activelist[i].address, activelist[i].port);
528 i++;
529 }
530
531 // Save if we want to permit NULL authentication
532 fprintf(fp, "\n\n");
533 fprintf(fp, "# Permit NULL authentication: YES or NO\n\n");
534
535 fprintf(fp, "%s = %s\n", PARAM_NULLAUTHPERMIT,
536 nullAuthAllowed ? "YES" : "NO");
537
538 fclose(fp);
539 return 0;
540 }
541 else
542 {
543 return -1;
544 }
545
546 }
547
548 //
549 // Skip over white space.
550 // If we hit a CR or LF, return NULL, otherwise return a pointer to
551 // the first non-white space character. Replace white space characters
552 // other than CR or LF with '\0', so that, if we're skipping white space
553 // after a token, the token is null-terminated.
554 //
skipws(char * ptr)555 static char *skipws(char *ptr)
556 {
557 while (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n') {
558 if (*ptr == '\r' || *ptr == '\n')
559 return NULL;
560 *ptr++ = '\0';
561 }
562 return ptr;
563 }
564