[solved] FreeRADIUS 3.x package LDAP/OTP problem



  • Hi folks,

    I just updated to the new pfsense 2.4 and my freeradius suddenly stopped working. I couldn't even start it, telling me "ssl version mismatch".
    After a bit of fiddling, I figured I'd try freeradius 3 instead.

    It installed successfully and it also started up just fine. However our setup from before did not work at all.
    We seem to have a pretty "uncommon" setup here. We're using OpenLDAP + Gauth. As this was not working with freerad2 out of the box we used a pam plugin to do ldap + gauth. Somewhat similar to this old post:
    http://www.supertechguy.com/help/security/freeradius-google-auth

    So I "googled around" noticing that gauth made it into the auth options of pfsense, this is just great. However, I can't figure out how to use it with LDAP auth. Seems I can not use both at the same time. Is this possible at all?

    I just tried setting up the LDAP Login, which is easy and works just fine. But then I do not have a list of users, so I can't define the gauth secrets for them.
    I used to have one folder where all of the secrets were listed for each user and the folder was chosen based on the user login name.
    I could try to adjust the python script, but I'd really love to have this function purely from the GUI so I won't have to modify stuff each update (which I always had to using freerad2)

    Any ideas from you guys?



  • Is anyone using LDAP + OTP and could give me a hint?  :-\

    EDIT: Never mind :-) I wrote my own python script and replaced the gauth one.
    If anyone has the same problem let me know and I'm happy to share it



  • Can you share your python script with me?



  • @avs262 Sorry I did not get any notification from the forum, so I've only seen it now.
    Of course I can ☺ . This is what I came up with.

    I added the following packages:

    pkg add http://pkg.freebsd.org/freebsd:11:x86:64/latest/All/py27-setuptools-36.5.0.txz
    pkg add http://pkg.freebsd.org/freebsd:11:x86:64/latest/All/py27-pyasn1-0.3.7.txz
    pkg add http://pkg.freebsd.org/freebsd:11:x86:64/latest/All/py27-pyasn1-modules-0.1.5.txz
    pkg add http://pkg.freebsd.org/freebsd:11:x86:64/latest/All/py27-ldap-2.4.45.txz
    

    Then I changed the googleauth plugin of FreeRADIUS to use my custom script:

    exec googleauth {
        wait = yes
        #program = "/usr/local/etc/raddb/scripts/googleauth.py %{request:User-Name} %{reply:MOTP-Init-Secret} %{reply:MOTP-PIN} %{request:User-Password}"
        program = "/usr/local/etc/raddb/scripts/googleauth-ldap.py %{request:User-Name} %{reply:MOTP-Init-Secret} %{request:User-Password}"
    }
    

    Finally the script itself, located in /usr/local/etc/raddb/scripts/googleauth-ldap.py

    #!/usr/local/bin/python2.7
    
    # Copyright of the original script: www.brool.com (http://www.brool.com/post/using-google-authenticator-for-your-website/)
    # License: CC0 1.0 Universal License
    
    import sys
    import time
    import struct
    import hmac
    import hashlib
    import base64
    import syslog
    import ldap
    
    def check_credentials(username, password, ldap_server="ldap://ldapserverurl:389"):
        ldap_server_failover = "ldap://urloffailoverldapserver:389"
        base_dn = 'dc=whatever'
        user_dn = "uid="+username+",ou=People,"+base_dn
        ldap_filter = 'uid=%s' % username
        try:
            ldap_client = ldap.initialize(ldap_server)
            ldap_client.start_tls_s()
            ldap_client.set_option(ldap.OPT_REFERRALS,0)
            ldap_client.set_option(ldap.OPT_X_TLS,1)
            ldap_client.set_option(ldap.OPT_X_TLS_CACERTDIR, "/usr/local/etc/raddb/certs/ca_ldap1_cert.pem")
            ldap_client.set_option(ldap.OPT_X_TLS_CACERTFILE, "/usr/local/etc/raddb/certs/")
            ldap_client.simple_bind_s(user_dn, password)
        except ldap.INVALID_CREDENTIALS:
            ldap_client.unbind()
            syslog.syslog(syslog.LOG_ERR, 'Wrong username or password')
            return False
        except ldap.SERVER_DOWN:
            if ldap_server != ldap_server_failover:
                syslog.syslog(syslog.LOG_ERR, 'Main LDAP server not available - Trying Failover')
                return check_credentials(username, password, ldap_server_failover)
            else:
                syslog.syslog(syslog.LOG_ERR, 'Both LDAP servers not available')
                return False
        except Exception, error:
            syslog.syslog(syslog.LOG_ERR, error)
            return False
        ldap_client.unbind()
        syslog.syslog(syslog.LOG_NOTICE, "freeRADIUS: LDAP Authenticator - Password validation successful for user: " + username)
        return True
    
    def authenticate(username, secretkey, code_attempt):
        #split 6 digitas from the end -> code
        password = code_attempt[:len(code_attempt)-6]
    
        if check_credentials(username, password) == False:
            syslog.syslog(syslog.LOG_ERR, "freeRADIUS: Google Authenticator - Authentication failed. User: " + username + ", Reason: Wrong Password")
            return False
    
        code_attempt = code_attempt[len(code_attempt)-6:]
        tm = int(time.time() / 30)
    
        secretkey = base64.b32decode(secretkey)
    
        # try 30 seconds behind and ahead as well
        for ix in [-1, 0, 1]:
            # convert timestamp to raw bytes
            b = struct.pack(">q", tm + ix)
    
            # generate HMAC-SHA1 from timestamp based on secret key
            hm = hmac.HMAC(secretkey, b, hashlib.sha1).digest()
    
            # extract 4 bytes from digest based on LSB
            offset = ord(hm[-1]) & 0x0F
            truncatedHash = hm[offset:offset+4]
    
            # get the code from it
            code = struct.unpack(">L", truncatedHash)[0]
            code &= 0x7FFFFFFF;
            code %= 1000000;
    
            if ("%06d" % code) == str(code_attempt):
                syslog.syslog(syslog.LOG_NOTICE, "freeRADIUS: Google Authenticator - Authentication successful for user: " + username)
                return True
    
        syslog.syslog(syslog.LOG_ERR, "freeRADIUS: Google Authenticator - Authentication failed. User: " + username + ", Reason: wrong tokencode")
        return False
    
    # Check the length of the parameters
    if len(sys.argv) != 4:
        syslog.syslog(syslog.LOG_ERR, "freeRADIUS: Google Authenticator - wrong syntax - USAGE: googleauth.py Username, Secret-Key, Auth-Attempt")
        exit(1)
    
    auth = authenticate(sys.argv[1], sys.argv[2], sys.argv[3])
    
    if auth == True:
        exit(0)
    
    exit(1)
    

    Of course you need to adapt the URL(s) of the ldap server and the dns to fit your environment.
    It works pretty well in our setup, but as always suggestions or improvements are welcome :)

    Reminder: After upgrading pfsense both files will get overwritten! So be sure you create a backup before and restore it after upgrading ;-)


Log in to reply