Search code examples
linuxauthenticationmachine-learningpambiometrics

Pam config file conditional execution of statements


I am working on a project to make a pam module in linux to authenticate via typing behavior of an individual. I have adequately studied and have an idea about the working of the four control flags in the pam config file viz. requisite, required, sufficient and optional. I have the following 2 questions about the PAM config file specific to my project.

  1. As a second factor of authentication I will like to use google-authenticator. Currently my config file has the following code:

    auth sufficient pam_test.so  
    auth required pam_google_authenticator.so
    

The google-authenticator module is invoked correctly if my module fails to authenticate when the password entered is correct but the typing behavior does not match. However, it is also invoked if the password entered itself is incorrect. For the second case I would like to terminate the entire chain. Is there a way to do so? Can I conditionally (based on different pam error status code) invoke the google-authenticator module?

  1. The other question is that I would like to come back to my module from the google-authenticator module and make decisions based on the google-authenticator module's pam status code. Basically, I would like to use the newest training sample for future training (adaptive algorithm). Therefore, to differentiate between true negative and false negative I would like to come back from the google-authenticator module? Is this possible?

This is my first question on stackoverflow. I am sorry if I have made any mistakes in asking the questions.


Solution

  • See man 5 pam.d for further details about these hijinx. I'm assuming you're working on Linux, with Linux-PAM.

    I am assuming pam_test.so is something you've written. As such, you should be returning PAM_AUTH_ERR when the password is invalid. You can specify different actions based on the different return codes with an "advanced" syntax. For reference, from the (my) man page for pam.d, the "simple" actions have the following "advanced" syntax:

       required
           [success=ok new_authtok_reqd=ok ignore=ignore default=bad]
       requisite
           [success=ok new_authtok_reqd=ok ignore=ignore default=die]
       sufficient
           [success=done new_authtok_reqd=done default=ignore]
       optional
           [success=ok new_authtok_reqd=ok default=ignore]
    

    These are of the form retval=action where retval is the PAM_* return code, with the PAM_ removed and converted to lower case (so PAM_SUCCESS becomes success). The actions bad and die both give failure status, but die exits the stack. The actions ok and done (and an integer N) indicate a successful status. The 'done' also stops execution of the stack. Using an integer N skips that many following modules. The special "default" means "any other return value from the module".

    You can then do:

    auth [success=done new_authtok_reqd=ok auth_err=die default=ignore] pam_test.so
    

    This will stop execution of the auth stack with failure when the password is wrong, stop execution with success when the module passes, and move along the auth stack in any other scenario.

    As for getting back to your code... that's a whole new can of worms. You can do it, but I can't think of a way that's not a total hack. For instance, jump through hoops with the control flow by using the code=N to jump around and call your module with special args that then succeed or fail based on the argument. For completeness, (and a complete HACK) something like:

    auth [success=done new_authtok_reqd=ok auth_err=die default=ignore] pam_test.so
    auth [success=ok default=1] pam_google_authenticator.so
    auth [default=1] pam_test.so wasvalid
    auth [default=bad] pam_test.so wasinvalid
    

    This has the following properties: If the password is invalid or it satisfies the pam_test.so conditions, there is no further authentication processing and finishes with failure or success, respectively. If pam_test failed for any other reason, it calls google authenticator. If that succeeds, it moves on to the next line calling pam_test.so wasvalid, and otherwise, it goes to pam_test.so wasinvalid. Only one of the two subsequent pam_test.so calls are made. After this snippet, the return code is success or failure, depending on the google authenticator status. This is effectively "require pam_google_authenticator.so but also call my module with the appropriate flag". If you want auth to end with this no matter what, you can use:

    auth [success=done new_authtok_reqd=ok auth_err=die default=ignore] pam_test.so
    auth [success=ok default=1] pam_google_authenticator.so
    auth [default=done] pam_test.so wasvalid
    auth [default=die] pam_test.so wasinvalid