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