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