title: Using pam_python in a script running with mod_python
body: <p>I would like to develop a web interface to allow users of a Linux system to do certain tasks related to their account. I decided to write the backend of the site using Python and mod_python on Apache. To authenticate the users, I thought I could use <a href="http://packages.debian.org/squeeze/python-pam" rel="nofollow noreferrer" title="python_pam">python_pam</a> to query the PAM service. I adapted the <a href="http://pastebin.com/isNCUAuD" rel="nofollow noreferrer" title="python_pam example">example</a> bundled with the module and got this:</p>
  
  <pre><code># out is the output stream used to print debug
  def auth(username, password, out):
      def pam_conv(aut, query_list, user_data):
          out.write("Query list: " + str(query_list) + "\n")
  
          # List to store the responses to the different queries
          resp = []
  
          for item in query_list:
              query, qtype = item
  
              # If PAM asks for an input, give the password
              if qtype == PAM.PAM_PROMPT_ECHO_ON or qtype == PAM.PAM_PROMPT_ECHO_OFF:
                  resp.append((str(password), 0))
  
              elif qtype == PAM.PAM_PROMPT_ERROR_MSG or qtype == PAM.PAM_PROMPT_TEXT_INFO:
                  resp.append(('', 0))
  
          out.write("Our response: " + str(resp) + "\n")
          return resp
  
      # If username of password is undefined, fail
      if username is None or password is None:
          return False
  
      service = 'login'
      pam_ = PAM.pam()
      pam_.start(service)
  
      # Set the username
      pam_.set_item(PAM.PAM_USER, str(username))
  
      # Set the conversation callback
      pam_.set_item(PAM.PAM_CONV, pam_conv)
  
      try:
          pam_.authenticate()
          pam_.acct_mgmt()
      except PAM.error, resp:
          out.write("Error: " + str(resp) + "\n")
          return False
      except:
          return False
  
      # If we get here, the authentication worked
      return True 
  </code></pre>
  
  <p>My problem is that this function does not behave the same whether I use it in a simple script or through mod_python. To illustrate this, I wrote these simple cases:</p>
  
  <pre><code>my_username = "markys"
  my_good_password = "lalala"
  my_bad_password = "lololo"
  
  def handler(req):
   req.content_type = "text/plain"
   req.write("1- " + str(auth(my_username,my_good_password,req) + "\n"))
   req.write("2- " + str(auth(my_username,my_bad_password,req) + "\n"))
   return apache.OK
  
  if __name__ == "__main__":
   print "1- " + str(auth(my_username,my_good_password,sys.__stdout__))
   print "2- " + str(auth(my_username,my_bad_password,sys.__stdout__))
  </code></pre>
  
  <p>The result from the script is :</p>
  
  <pre><code>Query list: [('Password: ', 1)]
  Our response: [('lalala', 0)]
  1- True
  Query list: [('Password: ', 1)]
  Our response: [('lololo', 0)]
  Error: ('Authentication failure', 7)
  2- False
  </code></pre>
  
  <p>but the result from mod_python is :</p>
  
  <pre><code>Query list: [('Password: ', 1)]
  Our response: [('lalala', 0)]
  Error: ('Authentication failure', 7)
  1- False
  Query list: [('Password: ', 1)]
  Our response: [('lololo', 0)]
  Error: ('Authentication failure', 7)
  2- False
  </code></pre>
  
  <p>I don't understand why the <code>auth</code> function does not return the same value given the same inputs. Any idea where I got this wrong ? <a href="http://pastebin.com/c4BeDsy1" rel="nofollow noreferrer" title="Complete script">Here</a> is the original script, if that could help you.</p>
  
  <p>Thanks a lot !</p>
  
  <p>EDIT: All right I found the error. I was running the script as root. mod_python was running the script as the webserver's user. Only root has the right to read shadow. I am not sure how I will circumvent this, but at least now I know what is the problem !</p>