Implemented Auth Basic
authorPhilipp Spitzer <philipp@spitzer.priv.at>
Tue, 25 Mar 2014 20:46:17 +0000 (21:46 +0100)
committerPhilipp Spitzer <philipp@spitzer.priv.at>
Tue, 25 Mar 2014 22:53:07 +0000 (23:53 +0100)
cgi-bin/dyndns.py

index 04e18f9..b2d33a7 100755 (executable)
@@ -2,10 +2,11 @@
 """Dynamic DNS script. Expects URLs from routers in the form
 http://dyndns.colgarra.priv.at/nic/update??username=<username>&password=<pass>&hostname=<domain>&myip=<ipaddr>
 """
-
+import os
 import re
 import cgi
 import pwd
+import base64
 from subprocess import call
 import ipaddr
 
@@ -22,22 +23,6 @@ if DEBUG:
        cgitb.enable()
 
 
-fields = cgi.FieldStorage()
-
-# the following fields are supported by most dyndns providers
-# if a parameter is not provided, the .getvalue method returns None
-username = fields.getvalue('username')
-password = fields.getvalue('password')
-hostname = fields.getvalue('hostname')
-myip     = fields.getvalue('myip')
-wildcard = fields.getvalue('wildcard')
-mx       = fields.getvalue('mx')
-backmx   = fields.getvalue('backmx')
-offline  = fields.getvalue('offline')
-system   = fields.getvalue('system')
-url      = fields.getvalue('url')
-
-
 # Base class for our exceptions
 class DynDnsError(Exception):
        pass
@@ -45,6 +30,9 @@ class DynDnsError(Exception):
 class AuthError(DynDnsError):
        returncode = 'badauth'
 
+class CredentialsMissing(AuthError):
+       pass
+
 class UsernameMissing(AuthError):
        pass
 
@@ -57,6 +45,12 @@ class PasswordMissing(AuthError):
 class PasswordWrong(AuthError):
        pass
 
+class AuthWrongMethod(AuthError):
+       returncode = 'wrongauthmethod' # not documented at dyn.com
+
+class AuthBasicError(AuthError):
+       returncode = 'authbasicerror' # not documented at dyn.com
+
 class HostnameError(DynDnsError):
        returncode = 'notfqdn'
 
@@ -78,7 +72,45 @@ class MyipInvalid(MyipError):
 class OfflineInvalid(DynDnsError):
        returncode = 'badparam' # not documented at dyn.com
 
+
+fields = cgi.FieldStorage()
+
+# the following fields are supported by most dyndns providers
+# if a parameter is not provided, the .getvalue method returns None
+username = fields.getvalue('username')
+password = fields.getvalue('password')
+hostname = fields.getvalue('hostname')
+myip     = fields.getvalue('myip')
+wildcard = fields.getvalue('wildcard')
+mx       = fields.getvalue('mx')
+backmx   = fields.getvalue('backmx')
+offline  = fields.getvalue('offline')
+system   = fields.getvalue('system')
+url      = fields.getvalue('url')
+
+
+# Optional Auth Basic
+auth = os.environ.get('HTTP_AUTHORIZATION') # auth == 'Basic cGhpbGlwcDpka2ZhamRrZg=='
+if auth: # empty string if HTTP_AUTHORIZATION not present
+       auth_parts = auth.split(' ')
+       auth_method = 'Basic'
+       if len(auth_parts) != 2 or auth_parts[0] != auth_method:
+               raise AuthWrongMethod()
+       try:
+               auth_decoded = base64.b64decode(auth_parts[1]) # auth_decoded == 'philipp:dkfajdkf'
+       except TypeError:
+               raise AuthBasicError()
+       auth_decoded_parts = auth_decoded.split(':')
+       if len(auth_decoded_parts) != 2:
+               raise AuthBasicError()
+       username, password = auth_decoded_parts
+
+
 try:
+       # check username and password
+       if username is None and password is None:
+               raise CredentialsMissing()
+
        # check username
        if username is None:
                raise UsernameMissing()
@@ -143,9 +175,15 @@ try:
        # Note: we should return 'nochg' in case the IP has not changed, however we don't know yet.
 
 
+except CredentialsMissing as error:
+       print "Content-Type: text/html"
+       print "Status: 401 Unauthorized"
+       print "WWW-Authenticate: Basic realm='tdyndns'"
+       print
+
 except DynDnsError as error:
        print "Content-Type: text/html"
-       print "Status: 200"
+       print "Status: 200 OK"
        print
        print error.returncode