########################################################################### # # BEHAVIOUR - Expression that defines the server's operating rules # # This is compiled at startup and ran for every request that comes in. # Upon entry, the REQUEST list of A/V pairs is already populated with # information from and about the request. Upon exit, the REPLY list is # used to build a response to send to the client. # # Other than the attributes / fixed fields you want to send, you need # to set the first instance of the attribute 'Secret' (see subdicts/dict. # internal) to the shared secret to be used for signing the response. # You also need to set the first instance of the RAD-Authenticator # attribute to the value that this attribute had in the original request; # i.e. copying the attribute from the REQUEST list to the REPLY list. # The same goes for the RAD-Identifier attribute and all instances of # the Proxy-State attribute. See RFC 2865 why. # # Then, when the expression completes (that is without being aborted), # the server will first build the packet only based on the attributes # on the REPLY list, so at first also putting in the original request # authenticator as the response authenticator. It will then sign the # packet using the shared secret provided on the reply list, putting the # signature over the original response authenticator, to create a valid # RADIUS response. # # See openradius-language.html for a list neatly showing all operators, # with contexts, precedence, association and auto-conversion properties. # # The 'and' and 'or' operators do short-circuit boolean evaluation as they do # in C, Perl and shell scripts - that's how conditional subexpressions # are implemented. # # This language has hardly any syntax, other than that a term may be # not be directly followed by another term. # ## # # This is an example expression file that goes together with the distributed # file 'configuration.sample-ldap'. It allows RADIUS clients to be listed # in LDAP as well as the users. See modules/radldap/radldap.attrmap, # modules/radldap/radldap.schema and modules/radldap/radldap-ldif.sample # for more information. # ########################################################################### # # Step 1 & 2: Look up client's secret by packet's source IP address; log # error and drop request if not found Ldap(str = "dc=e-advies,dc=nl", str = "(&(objectclass=openradiusClient)(cn=" . IP-Source . "))"), delall str, delall REP:int, delall REP:str, # del args & most ret values REP:Secret or ( Errorlogger(str = Timestamp as "%c" . ": Request from unknown client " . IP-Source . " identified as NAS " . (NAS-Identifier or NAS-IP-Address) . " for user " . User-Name . " - ignored."), abort ), ########################################################################### # # Step 3: Initialise reply list RAD-Code = Access-Reject, RAD-Identifier = RAD-Identifier, RAD-Authenticator = "" . RAD-Authenticator, # we need a copy moveall Proxy-State, ########################################################################### # # Step 4: Handle accounting RAD-Code == Accounting-Request and ( # Handle accounting. First verify request authenticator. Note that # REP:RAD-Authenticator contains a saved copy of REQ:RAD-Authenticator REQ:Acct-Authenticator = Mismatch, REQ:RAD-Authenticator pokedwith "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", REP:RAD-Authenticator == md5 (RAD-Packet . REP:Secret) and REQ:Acct-Authenticator := Verified, # Always log it; drop request if that fails Acctlogger(str = Timestamp as "%c", REQ:Record-Unique-Key = hex (NAS-IP-Address toraw . Timestamp toraw) . Acct-Session-Id), int or abort, del str, del REP:int, # Set response code and halt the expression acctresp ), ########################################################################### # # Step 5: Now we know we're authenticating, set defaults to reject when halted, # so we can do so at anytime RAD-Code = Access-Reject, Reply-Message = "I don't know you. Go away.\n", ########################################################################### # # Step 6: Decrypt PAP password, if any; also set CHAP-Challenge by copying # it from the request authenticator if it wasn't already present User-Password and ( REQ:User-Password := (User-Password papdecrypt (REP:Secret . RAD-Authenticator) . "\x00") beforefirst "\x00" ), CHAP-Password and ( CHAP-Challenge or (REQ:CHAP-Challenge = RAD-Authenticator) ), ########################################################################### # # Step 7: Log the request using the Stdlogger interface #Stdlogger(str=Timestamp as "%c"), #del str, del REP:int, ########################################################################### # # Step 9: Find user in LDAP and get attributes. Reject if not found. # # RadLDAP Expects base DN for search in first 'str', filter in second 'str'; # returns attributes from to LDAP node, translated using radldap's map file. delall REQ:str, delall REP:int, Ldap(str = "dc=e-advies,dc=nl", str = "(&(objectclass=openradiusUser)(cn=" . User-Name . "))"), int or reject, delall str, delall REP:int, # Convert temporary string attributes from LDAP to real ones, if any. # You can safely uncomment all of this if you're using the 'radradiusXxxYyy' # attributes in LDAP. str-Service-Type and (Service-Type = str-Service-Type), str-Framed-Protocol and (Framed-Protocol = str-Framed-Protocol), str-Framed-IP-Address and (Framed-IP-Address = str-Framed-IP-Address), str-Framed-IP-Netmask and (Framed-IP-Netmask = str-Framed-IP-Netmask), str-Login-IP-Host and (Login-IP-Host = str-Login-IP-Host), str-Login-Service and (Login-Service = str-Login-Service), str-Login-TCP-Port and (Login-TCP-Port = str-Login-TCP-Port), str-Session-Timeout and (Session-Timeout = str-Session-Timeout), str-Idle-Timeout and (Idle-Timeout = str-Idle-Timeout), str-Port-Limit and (Port-Limit = str-Port-Limit), ########################################################################### # # Step 10: Handle supported authentication types if we have a cleartext # password (that is either PAP or CHAP). clear-password and ( # See if we're doing PAP User-Password and ( # PAP: check and done User-Password == clear-password and ( Reply-Message := "Welcome, PAP/Login user.\n", accept ), reject ), # See if we're doing CHAP CHAP-Password and ( # CHAP: check and done 16 lastof CHAP-Password == md5 (1 firstof CHAP-Password . clear-password . CHAP-Challenge) and ( Reply-Message := "Welcome, CHAP user.\n", accept ), reject ), # Apparently neither, but the LDAP _did_ contain # clear-password: reject user reject ), ########################################################################### # # Step 11: Handle Md5-Hex style hashed password (PAP only) if the users # file returned a md5-hex-password attribute. # # A cleartext PAP password is checked against a stored md5-hex-password by # adding the first 4 octets of the md5-hex-password (the salt) to the PAP # password, calculating md5 over the whole, converting the resulting 16 # octets to 32 hexadecimal digits and comparing those to the last 32 octets # of the md5-hex-password. # # This is similar to the crypt(3) algorithm, but uses MD5 instead of DES and # a salt up to 32 bits (24 when using the same charset, 16 when using only # hexadecimal digits in the salt) instead of 12 bits. REP:md5-hex-password and ( User-Password or reject, 32 lastof REP:md5-hex-password == hex md5 (User-Password . 4 firstof REP:md5-hex-password) and ( Reply-Message := "Welcome, PAP/Login user with md5 password.\n", accept ), reject ), ########################################################################### # # Halt, rejecting the user reject