xref: /freebsd/contrib/libpcap/rpcapd/fileconf.c (revision 87b759f0fa1f7554d50ce640c40138512bbded44)
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 
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 
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 //
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