Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

07 November, 2021

Breaking recursion in Python and fix-ing it back up again

Trying out github repos for what started off as a blog post, on how fixpoint combinators relate to Python:

https://github.com/benclifford/python-fixpoint#readme

07 May, 2011

PAM python module for out-of-band one-time tokens

I previous wrote about integrating openid with unix shell logins for my public shell server barwen.ch. This post talks about the out-of-band PAM token module that I wrote as part of that - where I'm generating the tokens when the user is authenticated by OpenID. But I guess it could also have use when there's some other out of band mechanism (such as sending something by SMS?)

Subject to some other non-PAM web-based authentication (openid in this case, but it could be anything really), I want to issue a token value to the user in-band with respect to that other authentication (i.e. in a web page shown to the user), which is out-of-band with respect to PAM. That token should then be usable for a short period of time to make a single PAM authorisation to sshd.

That is, if you can log into the web-based authentication, you should then be able to log into the ssh system.

So on one side I need a PAM module (which I will write in Python) to check the tokens, and on the other side I need something (a command line tool) to issue the tokens. To complete the loop, I need some database (the filesystem) to store the tokens on the server side.

So here's the code. The PAM module comes complete with documented security vulnerability which allows anyone to delete certain files on your file system. ho ho.


First the token creator:

#!/usr/bin/python

import base64
import os
import pickle
import sys
import time

VALIDTIME = 60

tokenbits = os.urandom(8)
token = base64.b64encode(tokenbits, "+-")

print token

fn = token + ".token"
fh = open(fn, 'w')

obj = (sys.argv[1], time.time() + VALIDTIME)

pickle.dump(obj, fh)

and secondly the PAM module, which is based on the PAM module in my previous post:

import os
import syslog
import pickle
import time

def pam_sm_authenticate(pamh, flags, argv):
  syslog.syslog("start benc")
  pamh.authtok
  if pamh.authtok == None:
    syslog.syslog("got no password in authtok - trying through conversation")
    passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Monkeyballs?")
    rsp = pamh.conversation(passmsg)
    syslog.syslog("response is "+rsp.resp)
    pamh.authtok = rsp.resp
  # so we should at this point have the password either through the
  # prompt or from previous module
  syslog.syslog("got password: "+pamh.authtok)

  # now look for token
  # SECURITY BUG TODO: we're using this to make a path so we need to make sure
  # we're not being directed out into some other directory. We know the
  # range of characters that can be used in a token so we can reject if
  # anything other than those exists.
  # Especially as we delete the token file on use - otherwise could delete
  # arbitrary files on the system...
  tfn = "/root/" + pamh.authtok + ".token"

  if os.path.exists(tfn):
    fh = open(tfn)
    tokendata = pickle.load(fh)
    tokenuser = tokendata[0]
    tokentime = tokendata[1]
    fh.close()
    os.remove(tfn);

    # will remove the token even if it was for the wrong user
    # not sure if there's any security different wrt leaving it there if
    # its the wrong user?

    if tokentime < time.time():
      syslog.syslog("token time expired")
      return pamh.PAM_AUTH_ERR

    if tokenuser != pamh.user:
      syslog.syslog("token user "+tokenuser+" does not match requested user "+pamh.user)
      return pamh.PAM_AUTH_ERR
    

    return pamh.PAM_SUCCESS

  return pamh.PAM_AUTH_ERR

def pam_sm_setcred(pamh, flags, argv):
  return pamh.PAM_SUCCESS

23 April, 2011

pam_python

I came across pam_python, a PAM module that lets you write PAM modules in Python. I've come across things scripted by python a couple of times in the last few weeks at work so it seems interesting to play in this direction.

The first module I got sort-of working is this, which lets anyone log in with the password poop53.

import syslog

def pam_sm_authenticate(pamh, flags, argv):
  syslog.syslog("start benc")
  at = pamh.authtok
  syslog.syslog("got password: "+at)
  if at == "poop53" : 
    return pamh.PAM_SUCCESS
  else:
    return pamh.PAM_AUTH_ERR

def pam_sm_setcred(pamh, flags, argv):
  return pamh.PAM_SUCCESS

Now this cheats a bit - it assumes that some other module has read in the password from the user - I used pam_unix to do that, configured as below, so that first a check against the unix password happens and then if that fails, check against poop53.

auth sufficient pam_unix.so
auth sufficient pam_python.so /root/auth.py

The specific use I am thinking of, I don't want unix passwords to work. So in that case, I need to read in the password myself if it isn't already set.

Here's how I made that work:

def pam_sm_authenticate(pamh, flags, argv):
  syslog.syslog("start benc")
  pamh.authtok
  if pamh.authtok == None:
    syslog.syslog("got no password in authtok - trying through conversation")
    passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Monkeyballs?")
    rsp = pamh.conversation(passmsg)
    syslog.syslog("response is "+rsp.resp)
    pamh.authtok = rsp.resp
  # so we should at this point have the password either through the
  # prompt or from previous module
  syslog.syslog("got password: "+pamh.authtok)
  if pamh.authtok == "poop53" : 
    return pamh.PAM_SUCCESS
  else:
    return pamh.PAM_AUTH_ERR

def pam_sm_setcred(pamh, flags, argv):
  return pamh.PAM_SUCCESS

To use this with sshd, I need to enable sshd options UsePAM and ChallengeResponseAuthentication, and now I get this:
$ ssh root@192.168.141.128
Monkeyballs?poop53
Linux alcf3 2.6.35-28-generic #49-Ubuntu SMP Tue Mar 1 14:40:58 UTC 2011 i686 GNU/Linux
#

So I'm happy that I can grab some string from the remote user now, and process it to get an authentication decision.

Thought its pretty weird to have a regular ssh client giving me a Monkeyballs? prompt at auth time...

Modified: 2011-05-08: Later I used pam_python to write an out of band token module