#!/usr/bin/python # -*- coding: iso-8859-1 -*- """ A small CGI to politely inform people where they are and open up the firewall for them. """ import cgi import os import sys import re import time ############ Config wlanAddress = "10.10.0.1" wlanNetwork = "10.10.0.0/24" wlanInterface = "eth1" outInterface = "ppp0" senderAddress = "postmaster@foo.org" installPath = os.path.abspath(os.path.dirname(sys.argv[0])) helperName = "cportiphelper" operatorAddress = os.environ.get("CAP_OPERATOR") ############# CGI business welcomeMessage = """Content-type: text/html; charset=iso-8859-1 Connection: close Offenes WLAN

Hallo auch,

Schön, dich hier zu sehen. Weil ich mich freue, wenn es irgendwo offene WLANs gibt, lasse ich auch das hier offen. Das Ganze läuft aber nur, wenn alle eine Regel befolgen: Mach über dieses WLAN nichts, was du nicht über dein eigenes machen würdest.

Es gibt genug Methoden, der Vorratsdatenspeicherung ein Schnippchen zu schlagen, ohne wen anders in Schwierigkeiten zu bringen. Wenn du Nachbar bist, willst du vielleicht überlegen, ob du dir irgendwann mal einen eigenen Netzzugang besorgst -- gegen gelegentliches Mailziehen hat hier zwar niemand was, gewaltige Downloads oder Netzglotze finden wir dagegen auf Dauer nicht so toll.

Warnung: Ich behalte mir vor, dann und wann mal nach dem Rechten zu sehen. Bitte verschlüssele also Daten, von denen du willst, dass sie niemand anders sieht (das solltest du ohnehin tun).

Hi there,

Nice to see you. You're welcome to use this WLAN. Please don't do anything from here that you wouldn't do from your own net connection. We don't want to get into trouble and, this being Germany, the limit is not that terribly high.

Warning: I may be checking what's going on on the net now and then. You should encrypt data you don't want other people to see (but that's something you should do anyway).

Drop me a note if you like:

And if you'd want me to respond, you can (but don't need to) enter a mail address I can reach you at:

""" errorTemplate = """Content-type: text/html; charset=iso-8859-1 Connection: close Error message

An error has occurred

Sorry -- something went wrong. My trouble was:

%(msg)s

If you can't fix this yourself, you try Steubenstraße 28, Brämer, Demleitner, Weineck. Ask for Markus, he may be able to help you.

""" successTemplate = """Content-type: text/html; charset=iso-8859-1 Refresh: 10;%(nextAddr)s Connection: close Success

Welcome

You should now be able to access the net. You will be redirected to %(nextAddr)s in 10 seconds.

""" class Error(Exception): pass def showError(msg): print errorTemplate%{"msg": msg} def getRemoteMac(): try: f = os.popen("/usr/sbin/arp -an -i %s '%s'"%(wlanInterface, os.environ["REMOTE_ADDR"])) s = f.read() if f.close(): raise Error("arp execution failed") return re.search("at ([\da-fA-F:]{17}) ", s).group(1) except Exception, msg: raise Error("Could not determine your MAC address (%s)"% msg) def sendMailTo(user, mailtext): message = """From: User of open WLAN <%s> Content-Type: text/plain; charset=iso-8859-1 To: %s Date: %s %s """%(senderAddress, user, time.asctime(), mailtext) os.popen("/usr/sbin/sendmail -i -t", "w").write(message) def mailToOp(msg): if not msg or not msg.strip() or not operatorAddress: return sendMailTo(operatorAddress, msg) def insertFwRule(remoteMac): # remoteMac comes from arp and thus should be safe. if os.system("%s '%s'"%(os.path.join(installPath, helperName), remoteMac)): raise Error("Insert of firewall rule failed.") def openUpFirewall(): remoteMac = getRemoteMac() insertFwRule(remoteMac) form = cgi.FieldStorage() mailToOp("Original sender claimed to be: %s\n\n%s"%( form.getfirst("userAddr"), form.getfirst("message"))) nextAddr = form.getfirst("nextAddr", "http://www.tfiu.de") print successTemplate%{ "nextAddr": nextAddr, } ############# stuff for initial build ("make") helperSource = """ /* machine generated, do not edit */ #include #include #include void validate(char *s) { char *c; for (c=s; *c; c++) { if (!isxdigit(*c) && *c!=':') { fprintf(stderr, "%s does not look like a MAC address.\\n", s); exit(1); } } } int main(int argc, char **argv) { char *nullEnv[] = {NULL}; if (argc!=2) { fprintf(stderr, "Don't call this program directly, it's a helper for" " the capturing portal.\\n"); exit(1); } validate(argv[1]); /* hardcoding iptable's path -- you may need to change this in the python source. */ if (execle("/sbin/iptables", "/sbin/iptables", "-t", "nat", "-I", "PREROUTING", "-m", "mac", "--mac-source", argv[1], "-j", "ACCEPT", (char*)NULL, nullEnv)) { perror("Capturing portal helper failed: "); } return 1; } """ operatorMessage = """ ----------------- READ THIS: ------------------- You should now set the resulting binary cportiphelper suid root: sudo chown root cportiphelper sudo chmod u+s cportiphelper You can inspect its source in openFw's helperSource variable. After that, just tell apache where your capturing portal resides. The stanza might look like this: # DocumentRoot doesn't matter, nothing will be served from there. DocumentRoot /var/www/wlan ScriptAlias / %(installPath)s/openFw/ To receive the mails people may leave on the portal, add something like SetEnv CAP_OPERATOR somerandom@mail.address.foo in there, replacing the obivious. """ def initialBuild(): """builds the binary for inserting firewall rules and informs the user. This only needs to be run at installation time. """ f = open(helperName+".c", "w") f.write(helperSource) f.close() if os.system("make %s"%helperName): raise Error("Something went wrong when building helper.") print operatorMessage%globals() ############## trivial init.d interface def setupIptables(bringUp): action = {True: "-A", False: "-D"}[bringUp] # DNS queries to avoid poisoning the client's DNS cache os.system("iptables -t nat %s POSTROUTING -o %s" " -j MASQUERADE"%(action, outInterface)) os.system("iptables -t nat %s PREROUTING -s %s" " -p tcp --destination-port 53 -j ACCEPT"%(action, wlanNetwork)) os.system("iptables -t nat %s PREROUTING -s %s" " -p udp --destination-port 53 -j ACCEPT"%(action, wlanNetwork)) # access to self os.system("iptables -t nat %s PREROUTING -s %s" " -j DNAT --to-destination %s"%(action, wlanNetwork, wlanAddress)) def setupDefaultRules(): setupIptables(True) def teardownDefaultRules(): setupIptables(False) ############## "User interface" def cgimain(): if os.environ["PATH_INFO"].strip("/")=="letmein": try: openUpFirewall() except Exception, msg: showError(msg) else: print welcomeMessage def usermain(): if sys.argv[1]=="start": setupDefaultRules() elif sys.argv[1]=="stop": teardownDefaultRules() elif sys.argv[1]=="make": initialBuild() else: raise Error("Usage: %s [start|stop|make]") if __name__=="__main__": if len(sys.argv)==1: cgimain() else: usermain()