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