Creating Authen Digest WSGI script

14 views
Skip to first unread message

Neil Verkland

unread,
Jun 14, 2021, 9:45:34 PM6/14/21
to modwsgi

I'm attempting to use mod_wsgi for Authen (Digest) only. Once Authen is complete, all other scripts in the Apache directories will be served as CGI's or static files (or mod_proxy will pass the request on).

At present (with the configs below) the WSGI (Digest) authentication script is being executed and is returning a hex-digest of an md5 sum of 'user:pass:realm' (we can see this in the logs and code is provided below); however, apache is presenting the user with the login form each and every time authentication is successfully completed.

Some things to note: The password (in this case) isn't a password at all. It is an encrypted cookie that is found in the HTTP_COOKIE variable. The process of validating that cookie is to send it over TCP to a propratary java-validation process.

Can anyone see (in the configs and code below) where I have missed telling Apache that the Authentication was successful?

CONFIG httpd.conf:
<LocationMatch "^/private/">
     Options Indexes FollowSymLinks ExecCGI
     AuthType Digest
     #REALM PrivateArea
     AuthName PrivateArea
     AuthDigestProvider wsgi
     WSGIAuthUserScript /sites/www-python/lib/auth/plugin.py
     Require valid-user
     RewriteRule .* - [E=PROXY_USER:%{LA-U:REMOTE_USER},NS]
     RequestHeader set X-WEBAUTH-USER "%{PROXY_USER}e"
</LocationMatch>

CODE plugin.py:
def get_realm_hash(environ, user, realm):
     C = http.cookies.SimpleCookie()
     C.load(environ.get('HTTP_COOKIE',''))
     cval = ''
     if not 'rocacheauth' in C:
          writelog("cookie not present")
          return None
     if 'rocacheauth' in C:
          cval = C['rocacheauth'].value
          port = 2500
          writelog(f"cookie value: {cval}")
          userdata = findSession(cval) # look on disk for saved session
          if userdata: return(digest(userdata,realm))
     writelog(f"session not found")
     userdata = verifyCookie(cval,port=port)
     if userdata:
          writeSession(cval,userdata) #save to disk
          return(digest(userdata,realm))
     writelog(f"session not validated")
     return None

def digest(userdata,realm):
     hasher = hashlib.md5()
     uname = userdata[5]
     ustr = f'{uname}:barkbark:{realm}'
     writelog(f"validated user:{uname}")
     hasher.update(ustr.encode('UTF-8'))
     dgest = hasher.hexdigest()
     writelog(f"digest :{dgest}")
     return(dgest)

LOG1 OUTPUT:
# (user does not have a saved session on disk)
# login form is presented
2021-06-14 17:28:19,326 - authn_plugin - INFO - validated user:nv596r
2021-06-14 17:28:19,327 - authn_plugin - INFO - digest :7159b4ae7e3c2bd736dcf7c9c03d8e64
# login form is presented AGAIN

LOG2 OUTPUT:
# (user does have a saved session on disk):
# login form is presented
2021-06-14 17:47:54,318 - authn_plugin - INFO - Session Located nv596r
2021-06-14 17:47:54,318 - authn_plugin - INFO - validated user:nv596r
2021-06-14 17:47:54,319 - authn_plugin - INFO - digest :9633784b6851713b93506f3201fd53b9
# login form is presented AGAIN

Graham Dumpleton

unread,
Jun 14, 2021, 9:52:41 PM6/14/21
to mod...@googlegroups.com
I don't remember exactly how digest auth works, but it worries me you generating a hash as return value which doesn't have a password as input. I suspect that Apache or something is going to compare that hash with one generated from what the browser submitted and they need to match. Can't see how they would match with what you are doing.

Graham

--
You received this message because you are subscribed to the Google Groups "modwsgi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to modwsgi+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/modwsgi/ba89bbc5-99cb-4ca2-80d4-eb13d37f8fffn%40googlegroups.com.

Neil Verkland

unread,
Jun 14, 2021, 10:21:44 PM6/14/21
to mod...@googlegroups.com
It’s an interesting possibility. I’ll mess with the code (with that in mind) and see if I make any progress. If I do find that the has has to match on that Apache is putting together then I’ll have to switch to mod-Perl where I already have a working solution.

I was hoping to move to mod-wsgi so all layers would be Python based (all the cgi’s are Python based).

Sent from my iPhone

On Jun 14, 2021, at 6:52 PM, Graham Dumpleton <graham.d...@gmail.com> wrote:

I don't remember exactly how digest auth works, but it worries me you generating a hash as return value which doesn't have a password as input. I suspect that Apache or something is going to compare that hash with one generated from what the browser submitted and they need to match. Can't see how they would match with what you are doing.
You received this message because you are subscribed to a topic in the Google Groups "modwsgi" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/modwsgi/36iEHNSG-XM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to modwsgi+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/modwsgi/4AB4D13B-E14B-4028-AB97-40645BABF624%40gmail.com.

Graham Dumpleton

unread,
Jun 14, 2021, 11:48:04 PM6/14/21
to mod...@googlegroups.com
If mod_perl has a working solution it is possibly because they are rolling their own authentication handler from scratch, where as mod_wsgi hooks into the authentication provider hooks of Apache, which has more rigid rules around how the interfacing works.

Anyway, I was wrong that you weren't providing a password, but you are providing a fixed passed:

    ustr = f'{uname}:barkbark:{realm}'

Is the client definitely sending a password of "barkbark"?

If it is, then it possibly should work.

Graham

Neil Verkland

unread,
Jun 15, 2021, 12:00:53 AM6/15/21
to mod...@googlegroups.com
Today’s tests the client did not send ‘bark bark’. That will be the definitive test (tomorrow) that I spoke of.

Sent from my iPhone

On Jun 14, 2021, at 8:48 PM, Graham Dumpleton <graham.d...@gmail.com> wrote:

If mod_perl has a working solution it is possibly because they are rolling their own authentication handler from scratch, where as mod_wsgi hooks into the authentication provider hooks of Apache, which has more rigid rules around how the interfacing works.

Neil Verkland

unread,
Jun 15, 2021, 2:34:14 PM6/15/21
to mod...@googlegroups.com
Today I tried sending ‘barkbark’ as the password from the client. There was no behavior change. The login form is still continuously presented to the user.

Sent from my iPhone

On Jun 14, 2021, at 9:00 PM, Neil Verkland <nver...@gmail.com> wrote:

Today’s tests the client did not send ‘bark bark’. That will be the definitive test (tomorrow) that I spoke of.

Neil Verkland

unread,
Jun 15, 2021, 3:25:35 PM6/15/21
to modwsgi
The most promising solution is creating a WSGIAccessScript in Python (that checks for valid cookies) except, I need to 'set' the username at the end of the successful authentication. Is there a mechanism with WSGIAccessScript to export variables back to apache (set user or REMOTE_USER or any other variable)? If Access is denied by the script, I can 302 the user to an independent login form that will then create their encrypted cookie.

Neil Verkland

unread,
Jun 15, 2021, 4:07:31 PM6/15/21
to modwsgi
I'm trying the following patch against mod_wsgi 4.8 (since I am outside the design of mod_wsgi). I can integrate this later with a custom Apache directive (for now I will just continue to use WSGIAccessScript).

15595c15595

<                         allow = AUTH_DENIED;

---

>                         allow = -1;

15599,15614c15599

<                             allow = AUTH_GRANTED;

<                     }

<                     else if (result == Py_False) {

<                         allow = AUTH_DENIED;

<                     }

<                     else if (PyUnicode_Check(result)) {

<                         PyObject *str = NULL;

<                         str = PyUnicode_AsUTF8String(result);

<                         if (str) {

<                             adapter->r->user = apr_pstrdup(adapter->r->pool,

<                                     PyString_AsString(str));

<                             allow = AUTH_GRANTED;

<                         }

---

>                             allow = 1;



Neil Verkland

unread,
Jun 15, 2021, 5:13:28 PM6/15/21
to modwsgi
The patch above does solve 'my needs' even if it is technically 'outside' the design of mod_wsgi
Reply all
Reply to author
Forward
0 new messages