This patch adds a simple PCRE-based domain-whitelist feature to "poli- po". If this patch is used, and the file specified by WHITEDOM_LISTPATH exists, the file should contain one or more domain names (google.com, etc.) and/or IP addresses [specified one per line]. Strings passed to this code for DNS-lookup purposes are checked against the list. If they're not equal to one of the specified entries, or to a subdomain of one of the specified domains, the lookups are rejected. The WHITEDOM_LISTPATH file may contain blank lines, "#" comment lines, "#" inline comments, and/or lines starting with "[". All of these objects are ignored. Additionally, leading and trailing white space is ignored. For exam- ple, " google.com " is equivalent to "google.com". If the WHITEDOM_LISTPATH file doesn't exist, the program operates nor- mally. Technical notes: a. If you add, delete, or edit the WHITEDOM_LISTPATH file, you may need to restart "polipo" before the changes will take ef- fect. b. Matches aren't case-sensitive. c. The WHITEDOM_LISTPATH file is limited to WHITEDOM_MAXNUM en- tries. Additional entries are ignored. d. Badly-formatted entries may be ignored, or they may produce unpredictable effects. --- polipo-1.0.4.1.old/dns.c +++ polipo-1.0.4.1/dns.c @@ -256,6 +256,159 @@ #endif } +/* ---------------------------------------------------------------- */ +/* PCRE-whitelist support code */ +/* ---------------------------------------------------------------- */ + +#ifdef WHITEDOM_DOCUMENTATION /* PCRE-whitelist documentation */ + +This version of "dns.c" implements a simple PCRE-based domain-white- +list feature. To disable the feature, comment-out the line that de- +fines WHITEDOMLIST. + +If the file specified by WHITEDOM_LISTPATH exists, it should contain +one or more domain names (google.com, etc.) and/or IP addresses [spec- +ified one per line]. Strings passed to this code for DNS-lookup pur- +poses are checked against the list. If they're not equal to one of the +specified entries, or to a subdomain of one of the specified domains, +the lookups are rejected. + +The WHITEDOM_LISTPATH file may contain blank lines, "#" comment lines, +"#" inline comments, and/or lines starting with "[". All of these +objects are ignored. + +Additionally, leading and trailing white space is ignored. For exam- +ple, " google.com " is equivalent to "google.com". + +If the WHITEDOM_LISTPATH file doesn't exist, the program operates nor- +mally. + +Technical notes: + + a. If you add, delete, or edit the WHITEDOM_LISTPATH file, you + may need to restart "polipo" before the changes will take ef- + fect. + + b. Matches aren't case-sensitive. + + c. The WHITEDOM_LISTPATH file is limited to WHITEDOM_MAXNUM en- + tries. Additional entries are ignored. + + d. Badly-formatted entries may be ignored, or they may produce + unpredictable effects. + +#endif /* Endif WHITEDOM_DOCUMENTATION */ + +/* ---------------------------------------------------------------- */ + +#define WHITEDOMLIST 1 + +#ifdef WHITEDOMLIST /* Use a domain whitelist */ +#include + +#ifndef ZERO +#define ZERO 0 +#define ONE 1 +#define TWO 2 +#endif + /* See the preceding notes */ +#define WHITEDOM_LISTPATH "__META_PREFIX__/etc/polipo/whitedom.lst" +#define WHITEDOM_MAXNUM 1000 + + /* Used in "pcrewhite_status" */ +#define WHITEDOM_NOT_READY ZERO +#define WHITEDOM_NOT_USED ONE +#define WHITEDOM_READY TWO + +static int pcrewhite_status = WHITEDOM_NOT_READY; +static int pcrewhite_number = ZERO; +static pcre *pcrewhite_regex [WHITEDOM_MAXNUM]; + +#define WHITEDOM_TXTBUFSIZE 1024 +#define WHITEDOM_PATBUFSIZE (WHITEDOM_TXTBUFSIZE + 50) + + /* Max. no. of captured substrings */ +#define WHITEDOM_OVECENT TWO + +/* ---------------------------------------------------------------- */ + +/* WHITEDOM_METAPERIOD is used to handle a special case related to */ +/* periods. This parameter should specify a 'C' character constant */ +/* that meets four conditions: It's greater than space, it's not a */ +/* Perl meta-character, it's not equal to '#', and it doesn't occur */ +/* in valid domain names. */ + +#define WHITEDOM_METAPERIOD '~' + +/* ---------------------------------------------------------------- */ + +void setup_pcre (void) +{ + FILE *ifp; /* Input-stream pointer */ + pcre *regex; /* Pointer to compiled expression */ + const char *error; /* See "pcre_compile" documentation */ + int erroffset; /* Ditto */ + char *cp; /* Scratch */ + int c, ii, n; /* Ditto */ + + char txtbuf [WHITEDOM_TXTBUFSIZE]; + char patbuf [WHITEDOM_PATBUFSIZE]; + + if ((ifp = fopen (WHITEDOM_LISTPATH, "r")) == NULL) + { + pcrewhite_status = WHITEDOM_NOT_USED; + return; + } + + while (fgets (txtbuf, sizeof (txtbuf), ifp) != NULL) + { + char *txtp; + if (pcrewhite_number >= WHITEDOM_MAXNUM) break; + + for (txtp = txtbuf; + (*txtp > '\0') && (*txtp <= ' '); txtp++) {} + if ((*txtp == '#') || !*txtp || (*txtp == '[')) continue; + + for (cp = txtp; (c = *cp) != '\0'; cp++) + { + /* '.' is a special case - it's a */ + /* Perl wildcard, so we'll map it */ + /* to a non-wildcard */ + if (c == '.') *cp = WHITEDOM_METAPERIOD; + + /* Discard inline comments */ + if (c == '#') { *cp = '\0'; break; } + } + + ii = strlen (txtp) - ONE; + if (ii < 4) continue; + while ((ii >= ZERO) && (txtp [ii] <= ' ')) txtp [ii--] = '\0'; + if (ii < 4) continue; + if ((ii + ONE) >= WHITEDOM_TXTBUFSIZE) continue; + + strcpy (patbuf, "(^|"); + cp = &patbuf [strlen (patbuf)]; + *cp++ = WHITEDOM_METAPERIOD; + *cp++ = ')'; + strcpy (cp, txtp); + strcat (patbuf, "\\z"); + + error = NULL; + erroffset = ZERO; + regex = pcre_compile + (patbuf, PCRE_CASELESS, &error, &erroffset, NULL); + + if (error || erroffset) continue; + pcrewhite_regex [pcrewhite_number++] = regex; + } + + fclose (ifp); + pcrewhite_status = WHITEDOM_READY; +} +#endif /* Endif WHITEDOMLIST */ + +/* ---------------------------------------------------------------- */ + int do_gethostbyname(char *origname, int count, @@ -305,6 +458,73 @@ return 1; } +/* ---------------------------------------------------------------- */ + +#ifdef WHITEDOMLIST /* Use a domain whitelist */ + /* Need to initialize subsystem? */ + if (pcrewhite_status == WHITEDOM_NOT_READY) + { /* Yes */ + setup_pcre(); /* Initialize it */ + } + /* Did the input file exist? */ + if (pcrewhite_status == WHITEDOM_READY) + { /* Yes */ + int found = ZERO; + int c, ii; + char *cp; + char modname [WHITEDOM_TXTBUFSIZE]; + /* Captured-substring information */ + int ovector [WHITEDOM_OVECENT]; + + if (n < WHITEDOM_TXTBUFSIZE) + { + strcpy (modname, origname); + + for (cp = modname; (c = *cp) != '\0'; cp++) + { + if (c == '.') *cp = WHITEDOM_METAPERIOD; + } + } + else + { + *modname = '\0'; + } + + for (ii = ZERO; ii < pcrewhite_number; ii++) + { + /* Apply patterns to hostname */ + int rcx = pcre_exec + ( + /* Pointer to compiled pattern */ + pcrewhite_regex [ii] , + + NULL , /* We didn't study the pattern */ + modname , /* Subject-string pointer */ + n , /* Subject-string length */ + ZERO , /* Starting offset [in subject] */ + ZERO , /* Option bits */ + ovector , /* Captured-substring information */ + /* Max. no. of captured substrings */ + WHITEDOM_OVECENT + ); + /* Found a match? */ + if (rcx >= ZERO) { found = ONE; break; } + } + + if (!found) + { + request.error_message = internAtom + ("blocked by WHITEDOM"); + done = handler (-EINVAL, &request); + assert (done); + releaseAtom (request.error_message); + return ONE; + } + } +#endif /* Use a PCRE-based whitelist */ + +/* ---------------------------------------------------------------- */ + request.name = name; request.addr = NULL; request.error_message = NULL;