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