Product SiteDocumentation Site

4.3.2. Simple SSH 2-Factor Authentication Module

We need a quick 2-factor authentication module for SSH. Instead of going with one of the popular solutions like Duo or Google Authenticator, it seemed like a good excuse to whip up some code.
Module works by generating a one time use personal identification number for each login attempt. The module then looks up the local user's mail address, which we'll be storing in the standard comments slot in each pw entry in /etc/passwd. Once it has the user's mail address the module sends the one time use PIN to the user's mail address . If the correct PIN is entered, the login procedure continues with normal password based authentication.
  1. Start VirtualBox and run Debian Virtual Machine
  2. Login to virtual as root/password
  3. Enter:
    $ apt-get update
    $ apt-get install mc
    
    
  4. Locate a line in the file starting with ChallengeResponseAuthentication and change the value to yes:
    $ mcedit /etc/ssh/sshd_config 
    ChallengeResponseAuthentication     yes
  5. Restart SSH:
    $ /etc/init.d/ssh restart
  6. Add new user, setup password and define your email:
    $ useradd -m emailuser 
    $ passwd emailuser 
    $ usermod -c vas@tuke.mail emailuser
  7. Install required package:
    $ apt-get install python-pam libpam-python
  8. Download pam module source
    $ wget http://zeus.fei.tuke.sk/bps3r/pam.py
    $ mv pam.py /lib/security/pam.py
  9. Modify python pam module, and change ephasised variables:
    $ mcedit /lib/security/pam.py
    
    import random, string,hashlib 
    import pwd, syslog import smtplib
    
    #change properly following emphasized lines... fromaddr = 'your tuke email address' 
    toaddrs = 'your tuke email address' 
    username = 'your mais login' 
    password = 'your mais password'
    
    
    def mail(to,pin):
    
            msg = 'Subject: Ssh access verification\n\nEnter your PIN:'+ pin
    
            server = smtplib.SMTP_SSL('smtp.tuke.sk',465)
            #server.set_debuglevel(1)                    
            server.login(username, password)             
            server.sendmail(fromaddr, to, msg)           
            server.quit()                                
    
    def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
          return ''.join(random.choice(chars) for x in range(size))        
    
    
    def get_user_mail(user):
                            
            try:            
                    comments = pwd.getpwnam(user).pw_gecos
            except KeyError: # Bad user name              
                    auth_log("No local user (%s) found." % user)
                    return -1                                   
    
            return comments
    
    
    
    def pam_sm_authenticate(pamh, flags, argv):
            try:                               
                    user = pamh.get_user()     
                    user_mail = get_user_mail(user)
            except pamh.exception, e:              
                    return e.pam_result            
                                                   
            if user is None or user_mail == -1:
                    msg = pamh.Message(pamh.PAM_ERROR_MSG, "Not such user")
                    pamh.conversation(msg)
                    return pamh.PAM_ABORT
    
            #pin=nahodna hodnota
            #poslanie mailu s pinom na adresu uzivatela
            pin=id_generator()
            mail(user_mail,pin)
            for attempt in range(0,3): # 3 attempts to enter the one time PIN
                    msg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Enter one time mail PIN:")
                    resp = pamh.conversation(msg)
    
                    if resp.resp == pin:
                            return pamh.PAM_SUCCESS
                    else:
                            continue
            return pamh.PAM_AUTH_ERR
    
    def pam_sm_setcred(pamh, flags, argv):
            return pamh.PAM_SUCCESS
    
    def pam_sm_acct_mgmt(pamh, flags, argv):
            return pamh.PAM_SUCCESS
    
    def pam_sm_open_session(pamh, flags, argv):
            return pamh.PAM_SUCCESS
    
    def pam_sm_close_session(pamh, flags, argv):
            return pamh.PAM_SUCCESS
    
    def pam_sm_chauthtok(pamh, flags, argv):
            return pamh.PAM_SUCCESS
    
  10. Check your code for errors:
    $ python /lib/security/pam.py
  11. Configure pam to use the new pam module.
    $ mcedit /etc/pam.d/sshd 
    # Standard Un*xauthentication. 
    @include common-auth 
    auth requisite pam_python.so pam.py
  12. Open web browser and login to your tuke email account
    http://posta.tuke.sk
  13. Try to login as google user with SSH protocol, enter PIN from email (it's case sensitive)
    $ ssh emailuser@localhost
  14. Check your mailbox for new email and try to finish ssh login