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