• Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Search
  • Register
  • Login
Netgate Discussion Forum
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Search
  • Register
  • Login

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

Scheduled Pinned Locked Moved pfSense Packages
6 Posts 3 Posters 1.7k Views
Loading More Posts
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • W
    waterstorm
    last edited by Oct 27, 2017, 6:34 AM Oct 17, 2017, 9:40 AM

    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?

    1 Reply Last reply Reply Quote 0
    • W
      waterstorm
      last edited by Oct 26, 2017, 2:33 PM Oct 25, 2017, 1:39 PM

      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

      A 1 Reply Last reply May 16, 2019, 6:33 PM Reply Quote 0
      • A
        avs262 @waterstorm
        last edited by May 16, 2019, 6:33 PM

        Can you share your python script with me?

        W 1 Reply Last reply Aug 5, 2019, 9:21 AM Reply Quote 0
        • W
          waterstorm @avs262
          last edited by waterstorm Aug 5, 2019, 9:22 AM Aug 5, 2019, 9:21 AM

          @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 ;-)

          1 Reply Last reply Reply Quote 0
          • L
            lawern
            last edited by Feb 21, 2025, 7:49 AM

            @waterstorm Thanks for your documentation!
            It helped me a lot with the setup, and with minor adjustments it also works with pfSense 2.7.2.

            But is this still the way to go or is there now a more elegant solution?

            W 1 Reply Last reply Feb 21, 2025, 10:25 AM Reply Quote 0
            • W
              waterstorm @lawern
              last edited by Feb 21, 2025, 10:25 AM

              @lawern Good to hear :) I think I also updated it once or twice to be compatible, but we used it until we replaced the gateway. Until then I think there was no easy alternative to this.

              1 Reply Last reply Reply Quote 0
              • First post
                Last post
              Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.
                This community forum collects and processes your personal information.
                consent.not_received