Monday, November 08, 2010 at 12:05 AM.

system.verbs.builtins.radio.webServer.firewall

on firewall (pta) {
	<<Changes
		<<4/21/02; 11:42:50 PM by JES
			<<Fixed a bug which would errorneously deny access when making a POST via the desktop website as accessed through the domain name instead of through its IP address.
		<<3/7/02; 10:07:38 PM by JES
			<<Follow address values when running callbacks.
		<<2/10/02; 12:36:47 PM by DW
			<<Added support for callbacks.
				<<http://radio.userland.com/stories/storyReader$10132
		<<2/10/02; 12:22:18 PM by DW
			<<Add support for callbacks.
		<<1/23/02; 12:59:03 PM by JES
			<<When checking referers for POST requests, allow requests through if the referer is non-local, and Radio's IP address is a standard NAT IP address. This fixes a problem where people running Radio with NAT and port forwarding could not POST remotely.
		<<1/22/02; 1:22:05 PM by DW
			<<Handle referers without trailing slash (e.g. http://127.0.0.1).
		<<1/21/02; 2:24:39 PM by JES
			<<Don't use tcp.equalNames when checking the referer on POSTs, because it failed for some proxy server users.
		<<1/18/02; 11:37:24 AM by DW
			<<Security issue: Closes a hole where a form appears on another site, and the user unknowingly posts to his Radio 8 site. With this fix, the referer on every post must be on the machine that Radio is running on, or a 500 server error is returned. More info, if available, will be posted here.
				<<http://frontier.userland.com/securityAlerts/remoteFormRadio
		<<1/7/02; 11:43:42 PM by JES
			<<If user.radio.prefs.security.allowRemoteSystemPageAccess is false, and pta^.path begins with /system/ (case-insensitive), access is denied.
		<<7/26/01; 3:28:15 PM by JES
			<<Changed the logical interpretation of the security prefs. Here's the summary:
				<<If you're on the same machine as Radio, you are always allowed in.
				<<If allowRemoteViewing is false, you are never allowed in.
				<<If this is a POST, and allowRemotePost is false, you're not allowed.
				<<If requireRemoteLogin is true *or* this is a POST, you get an authentication challenge.
				<<If you fail authorization, you're not allowed.
		<<6/9/01; 2:38:09 PM by DW
			<<Created. Make sure the user is allowed to access.
			<<Two ways to return -- either by scriptError'ing, canceling the whole operation; or returning false which allows you to talk to the client for security authorization.
	bundle { //call the callbacks
		local (adr);
		for adr in @user.radio.callbacks.firewall {
			try {
				while typeOf (adr^) == addressType {
					adr = adr^};
				if adr^ (pta) {
					return (true)}}}};
	on getHostFromReferer (referer) {
		local (urllist, host);
		try {
			urllist = string.urlsplit (pta^.requestHeaders.referer)}
		else {
			urllist = string.urlsplit (pta^.requestHeaders.referer + "/")};
		host = urllist [2];
		if host contains ":" {
			host = string.nthField (host, ":", 1)};
		return (host)};
	if pta^.radioResponder.flSameMachine {
		if pta^.method == "POST" { //hole closed, 1/18/02 DW
			local (refererhost = getHostFromReferer (pta^.requestHeaders.referer));
			if refererhost != "127.0.0.1" {
				if string.lower (refererhost) != "localhost" {
					scriptError ("Access denied.")}}};
		return (true)};
	if not user.radio.prefs.security.allowRemoteViewing {
		scriptError ("Access denied.")};
	if not user.radio.prefs.security.allowRemoteSystemPageAccess {
		if string.lower (pta^.path) beginsWith ("/" + string.lower (radio.data.folderNames.wwwSystemSubfolderName) + "/") {
			scriptError ("Access denied.")}};
	if pta^.method == "POST" {
		if not user.radio.prefs.security.allowRemotePost {
			scriptError ("Access denied.")}};
	if user.radio.prefs.security.requireRemoteLogin or pta^.method == "POST" {
		local (flsecure = false);
		if webserver.util.parseAuth (pta) { //get username and password
			if string.lower (pta^.username) == string.lower (user.radio.prefs.security.remoteUsername) {
				if string (pta^.password) == string (user.radio.prefs.security.remotePassword) {
					flsecure = true}}};
		if not flsecure { //send challenge
			pta^.responseBody = webserver.util.buildErrorPage ("401 Unauthorized", "A valid username and password are required to access this page.");
			pta^.responseHeaders.["WWW-Authenticate"] = "Basic realm=\"Admin\"";
			pta^.code = 401;
			return (false)};
		if pta^.method == "POST" { //1/23/02 JES: check non-local referers
			try { //some users behind proxy servers can't do DNS lookups -- don't do a 500 Server Error
				local (refererhost = getHostFromReferer (pta^.requestHeaders.referer));
				if refererhost != tcp.dns.getMyDottedId () {
					if not tcp.equalNames (refererhost, tcp.dns.getMyDottedId ()) {
						scriptError ("Access denied.")}}}
			else { //check for local NAT IP addresses, as defined by ICANN
				<<Reference: http://www.riverstonenet.com/technology/nat.shtml
				local (myIp = tcp.dns.getMyDottedId ());
				if myIp beginsWith "10." { //10.x.x.x is ok. (Class A)
					return (true)};
				if myIp beginsWith "172." { //172.31.0.0 to 172.31.255.255 are ok. (Class B)
					local (x = number (string.nthField (myIp, ".", 2)));
					if x >= 0 and x <= 31 {
						return (true)}};
				if myIp beginsWith "192.168." { //192.168.x.x is ok. (Class C)
					return (true)};
				scriptError ("Access denied.")}}};
	return (true)}



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.