+ def VerifyBasicAuthPassword(self, req, username, password, expected):
+ """Checks the password for basic authentication.
+
+ As long as they don't start with an opening brace ("E{lb}"), old passwords
+ are supported. A new scheme uses H(A1) from RFC2617, where H is MD5 and A1
+ consists of the username, the authentication realm and the actual password.
+
+ @type req: L{http.server._HttpServerRequest}
+ @param req: HTTP request context
+ @type username: string
+ @param username: Username from HTTP headers
+ @type password: string
+ @param password: Password from HTTP headers
+ @type expected: string
+ @param expected: Expected password with optional scheme prefix (e.g. from
+ users file)
+
+ """
+ # Backwards compatibility for old-style passwords without a scheme
+ if not expected.startswith("{"):
+ expected = self._CLEARTEXT_SCHEME + expected
+
+ # Check again, just to be sure
+ if not expected.startswith("{"):
+ raise AssertionError("Invalid scheme")
+
+ scheme_end_idx = expected.find("}", 1)
+
+ # Ensure scheme has a length of at least one character
+ if scheme_end_idx <= 1:
+ logging.warning("Invalid scheme in password for user '%s'", username)
+ return False
+
+ scheme = expected[:scheme_end_idx + 1].upper()
+ expected_password = expected[scheme_end_idx + 1:]
+
+ # Good old plain text password
+ if scheme == self._CLEARTEXT_SCHEME:
+ return password == expected_password
+
+ # H(A1) as described in RFC2617
+ if scheme == self._HA1_SCHEME:
+ realm = self.GetAuthRealm(req)
+ if not realm:
+ # There can not be a valid password for this case
+ raise AssertionError("No authentication realm")
+
+ expha1 = compat.md5_hash()
+ expha1.update("%s:%s:%s" % (username, realm, password))
+
+ return (expected_password.lower() == expha1.hexdigest().lower())
+
+ logging.warning("Unknown scheme '%s' in password for user '%s'",
+ scheme, username)
+
+ return False
+