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