├── .bzr-builddeb └── default.conf ├── .gitlab-ci.yml ├── dbconfig └── install │ └── mysql ├── debian ├── README ├── changelog ├── compat ├── config ├── control ├── control.in ├── copyright ├── grase-conf-freeradius.install ├── postinst ├── postrm ├── rules ├── source │ └── format └── transform_clients.conf.grase ├── freeradius └── 3.0 │ ├── dictionary.grase │ ├── mods-available │ └── graseperl │ ├── perl_modules │ ├── Attributes.pm │ ├── SQLConnector.pm │ ├── SQLCounter.pm │ ├── SQLExpire.pm │ ├── conf │ │ ├── settings.conf │ │ └── settings.conf.24.3.9 │ ├── sql_counters.pl │ └── sqlcounter.conf │ └── sites-available │ └── default.grase └── templates ├── radius.conf └── sql.conf.grase /.bzr-builddeb/default.conf: -------------------------------------------------------------------------------- 1 | [BUILDDEB] 2 | native = True 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | .build_template: &build_definition 2 | stage: build 3 | script: 4 | - dch -v $(git describe |tr '-' '.')~$(lsb_release -cs) -b "" 5 | - dpkg-buildpackage 6 | - mkdir artifacts 7 | - mv -v ../grase-conf-freeradius_* artifacts/ 8 | artifacts: 9 | paths: 10 | - artifacts/ 11 | expire_in: 1 week 12 | 13 | 14 | jessie-all: 15 | <<: *build_definition 16 | image: grase/build-images:jessie 17 | 18 | trusty-all: 19 | <<: *build_definition 20 | image: grase/build-images:trusty 21 | 22 | xenial-all: 23 | <<: *build_definition 24 | image: grase/build-images:xenial 25 | 26 | bionic-all: 27 | <<: *build_definition 28 | image: grase/build-images:bionic 29 | -------------------------------------------------------------------------------- /dbconfig/install/mysql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GraseHotspot/grase-conf-freeradius/f0111ed20bb4afdaf0ef34103bd37f3a373f3cac/dbconfig/install/mysql -------------------------------------------------------------------------------- /debian/README: -------------------------------------------------------------------------------- 1 | The Debian Package grase-conf-freeeradius 2 | ---------------------------- 3 | 4 | This package installs the glue between freeradius and mysql and sets it up to 5 | run the hotspot software. 6 | 7 | -- Tim White Tue, 04 May 2010 21:49:42 +0800 8 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | grase-conf-freeradius (1.9.5) UNRELEASED; urgency=medium 2 | 3 | * Ensure during purge we remove cached UCF files 4 | * Fix order of Perl and logintime to ensure logintime isn't overriden 5 | * Fix Perl Module config so we return Gigawords for Data 6 | * Major rework for freeradius 3 support 7 | 8 | -- Tim White Thu, 24 Jul 2014 15:41:54 +1000 9 | 10 | grase-conf-freeradius (1.9.4) purewhite; urgency=low 11 | 12 | * New Perl Modules to handle SQLCounter stuff (most code from Yfi Hotcakes) 13 | 14 | -- Tim White Sat, 15 Dec 2012 13:43:36 +1000 15 | 16 | grase-conf-freeradius (1.9.3) purewhite; urgency=low 17 | 18 | * Log failed logins to postauth as well 19 | 20 | -- Tim White Sat, 30 Jun 2012 07:39:25 +1000 21 | 22 | grase-conf-freeradius (1.9.2) purewhite; urgency=low 23 | 24 | * Updating things to point to grasehotspot.org 25 | * Prevent detail logging to /var/log/freeradius/radacct as it fills up 26 | drives slowly 27 | 28 | -- Tim White Fri, 18 May 2012 08:09:42 +1000 29 | 30 | grase-conf-freeradius (1.9.1) purewhite; urgency=low 31 | 32 | * Post Auth query update 33 | 34 | -- Tim White Fri, 09 Mar 2012 13:52:34 +1000 35 | 36 | grase-conf-freeradius (1.9) purewhite; urgency=low 37 | 38 | * Send back correct Reply-message when trying to login outside of Login-Time 39 | 40 | -- Tim White Fri, 09 Mar 2012 12:29:21 +1000 41 | 42 | grase-conf-freeradius (1.8) purewhite; urgency=low 43 | 44 | * Recurring limits not applied on first login due to NULL fixes #60 45 | 46 | -- Tim White Wed, 22 Feb 2012 10:00:12 +1000 47 | 48 | grase-conf-freeradius (1.7) purewhite; urgency=low 49 | 50 | * Simultaneous-Use error message broke JSON in Coova Chilli 51 | 52 | -- Tim White Mon, 10 Oct 2011 15:39:33 +1000 53 | 54 | grase-conf-freeradius (1.6) purewhite; urgency=low 55 | 56 | * Changes for Simultaneous Use Checking 57 | * Changes for DEFAULT group 58 | 59 | -- Tim White Wed, 03 Aug 2011 15:43:17 +1000 60 | 61 | grase-conf-freeradius (1.5) purewhite; urgency=low 62 | 63 | * Added time counters for month, week, day, hour 64 | 65 | -- Tim White Sat, 25 Jun 2011 08:45:44 +1000 66 | 67 | grase-conf-freeradius (1.4) purewhite; urgency=low 68 | 69 | * Database upgrade (radreply removed uniq key) 70 | 71 | -- Tim White Thu, 05 May 2011 18:00:14 +1000 72 | 73 | grase-conf-freeradius (1.3) purewhite; urgency=low 74 | 75 | * Database upgrades 76 | * Change to config so proper error messages are return to coovachilli 77 | 78 | -- Tim White Mon, 04 Apr 2011 19:22:48 +1000 79 | 80 | grase-conf-freeradius (1.2) purewhite; urgency=low 81 | 82 | * Fix for initial install missing /etc/grase dir 83 | 84 | -- Tim White Mon, 14 Mar 2011 23:00:28 +1000 85 | 86 | grase-conf-freeradius (1.1) purewhite; urgency=low 87 | 88 | * Database upgrades 89 | 90 | -- Tim White Mon, 13 Dec 2010 14:14:16 +1000 91 | 92 | grase-conf-freeradius (1.0) purewhite; urgency=low 93 | 94 | [ Tim White ] 95 | * Initial Release. 96 | 97 | -- Tim White Tue, 04 May 2010 21:47:39 +0800 98 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/config: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # config maintainer script for grase-conf-freeradius 3 | 4 | # source debconf stuff 5 | . /usr/share/debconf/confmodule 6 | # we support mysql and pgsql 7 | dbc_first_version=1.0 8 | dbc_dbtypes="mysql" 9 | dbc_dbuser="grase" 10 | dbc_dbname="grase" 11 | dbc_dbpassword="" 12 | # source dbconfig-common stuff 13 | . /usr/share/dbconfig-common/dpkg/frontend.config 14 | dbc_go grase-db $@ 15 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: grase-conf-freeradius 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: Tim White 5 | Build-Depends: debhelper (>= 7) 6 | Standards-Version: 3.8.3 7 | Homepage: 8 | 9 | Package: grase-conf-freeradius 10 | Architecture: all 11 | Depends: ${misc:Depends}, libconfig-simple-perl, libxml-simple-perl, libdbd-mysql-perl 12 | Description: 13 | 14 | -------------------------------------------------------------------------------- /debian/control.in: -------------------------------------------------------------------------------- 1 | Source: grase-conf-freeradius 2 | Section: config 3 | Priority: extra 4 | Maintainer: Tim White 5 | Build-Depends: @cdbs@, config-package-dev 6 | Standards-Version: 3.8.4 7 | Homepage: http://grasehotspot.org/ 8 | 9 | Package: grase-conf-freeradius 10 | Architecture: all 11 | Pre-Depends: freeradius (>= 3.0.0), freeradius-mysql (>= 3.0.0), coova-chilli (>= 1.4.0 ), mariadb-client | virtual-mysql-client 12 | Depends: dbconfig-common, dbconfig-mysql | dbconfig-no-thanks, ${misc:Depends}, libconfig-simple-perl, libxml-simple-perl, libtime-modules-perl 13 | Provides: ${diverted-files} 14 | Conflicts: ${diverted-files} 15 | Description: Config files for FreeRADIUS for the GRASE Hotspot System 16 | The GRASE Hotspot System binds together CoovaChilli, FreeRADIUS, MySQL, 17 | and other systems as well as providing an administrative frontend to managing 18 | users and other hotspot related tasks. 19 | . 20 | This package provides Configuration files for the FreeRADIUS subsystem 21 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | This work was packaged for Debian by: 2 | 3 | Tim White on Sun, 25 Apr 2010 05:38:26 +0800 4 | 5 | Upstream Author(s): 6 | 7 | 8 | 9 | 10 | Copyright: 11 | 12 | 13 | 14 | 15 | License: 16 | 17 | This program is free software: you can redistribute it and/or modify 18 | it under the terms of the GNU General Public License as published by 19 | the Free Software Foundation, either version 3 of the License, or 20 | (at your option) any later version. 21 | 22 | This package is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | 27 | You should have received a copy of the GNU General Public License 28 | along with this program. If not, see . 29 | 30 | On Debian systems, the complete text of the GNU General 31 | Public License version 3 can be found in `/usr/share/common-licenses/GPL-3'. 32 | 33 | The Debian packaging is: 34 | 35 | Copyright (C) 2010 unknown 36 | 37 | # Please chose a license for your packaging work. If the program you package 38 | # uses a mainstream license, using the same license is the safest choice. 39 | # Please avoid to pick license terms that are more restrictive than the 40 | # packaged work, as it may make Debian's contributions unacceptable upstream. 41 | # If you just want it to be GPL version 3, leave the following line in. 42 | 43 | and is licensed under the GPL version 3, see above. 44 | 45 | # Please also look if there are files or directories which have a 46 | # different copyright/license attached and list them here. 47 | -------------------------------------------------------------------------------- /debian/grase-conf-freeradius.install: -------------------------------------------------------------------------------- 1 | templates/* /usr/share/grase/templates/ 2 | dbconfig/* /usr/share/dbconfig-common/data/grase-conf-freeradius/ 3 | freeradius/* /etc/freeradius/ 4 | -------------------------------------------------------------------------------- /debian/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | action="$1" 4 | oldversion="$2" 5 | 6 | umask 022 7 | 8 | # source debconf stuff 9 | . /usr/share/debconf/confmodule 10 | # source dbconfig-common stuff 11 | . /usr/share/dbconfig-common/dpkg/frontend.postinst.mysql 12 | 13 | mkdir -p /etc/grase/ 14 | 15 | dbc_first_version=3.0 16 | dbc_generate_include="template:/etc/freeradius/3.0/mods-available/grasesql" 17 | dbc_generate_include_perms="640" 18 | dbc_generate_include_owner="root:freerad" 19 | dbc_generate_include_args="-o template_infile=/usr/share/grase/templates/sql.conf.grase -U" 20 | 21 | dbc_go grase-db $@ 22 | ## Generate Perl config module from the preinstalled grase-db 23 | dbconfig-generate-include -a -f template -O root:freerad -m 640 -o template_infile=/usr/share/grase/templates/radius.conf /etc/dbconfig-common/grase-db.conf /etc/grase/radius.conf 24 | 25 | ln -fs /etc/freeradius/3.0/mods-available/grasesql /etc/freeradius/3.0/mods-enabled/grasesql 26 | ln -fs /etc/freeradius/3.0/mods-available/graseperl /etc/freeradius/3.0/mods-enabled/graseperl 27 | 28 | # Disable default and enable ours 29 | rm -f /etc/freeradius/3.0/sites-enabled/default 30 | ln -fs /etc/freeradius/3.0/sites-available/default.grase /etc/freeradius/3.0/sites-enabled/grase 31 | 32 | if [ "$action" != configure ]; then 33 | exit 0 34 | fi 35 | 36 | sed -i 's/testing123/hotspotradius/m' /etc/freeradius/3.0/clients.conf 37 | 38 | # On the off chance the file permissions are wrong, we need to make sure its right 39 | chown root:freerad /etc/freeradius/3.0/mods-available/grasesql 40 | chmod 640 /etc/freeradius/3.0/mods-available/grasesql 41 | chown root:freerad /etc/grase/radius.conf 42 | chmod 640 /etc/grase/radius.conf 43 | 44 | 45 | # Fix for Debian/Ubuntu bug to do with Perl 46 | # preloader=`grep "export LD_PRELOAD" /etc/default/freeradius || true` 47 | # if [ "$preloader" = "" ] 48 | # then 49 | # #libperl=$(find /usr/lib/ -name "libperl.so*"|awk '(NR == 1 || length < length(shortest)) { shortest = $0 } END { print shortest }') 50 | # touch /etc/default/freeradius 51 | # echo 'export LD_PRELOAD=$(find /usr/lib/ -name "libperl.so*"|sort -n |tail -n 1)' >> /etc/default/freeradius 52 | # #echo "export LD_PRELOAD=$libperl" >> /etc/default/freeradius 53 | # fi 54 | 55 | #DEBHELPER# 56 | 57 | if which invoke-rc.d >/dev/null 2>&1; then 58 | invoke-rc.d freeradius restart 3>&- || true 59 | else 60 | /etc/init.d/freeradius restart 3>&- || true 61 | fi 62 | 63 | # Ensure we enable freeradius at boot 64 | deb-systemd-helper enable freeradius.service 65 | update-rc.d freeradius defaults 66 | 67 | exit 0 68 | -------------------------------------------------------------------------------- /debian/postrm: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # postrm script for grase-conf-freeradius 3 | 4 | set -e 5 | #set -u 6 | 7 | if [ -f /usr/share/debconf/confmodule ]; then 8 | . /usr/share/debconf/confmodule 9 | fi 10 | if [ -f /usr/share/dbconfig-common/dpkg/frontend.postrm.mysql ]; then 11 | . /usr/share/dbconfig-common/dpkg/frontend.postrm.mysql 12 | dbc_go grase-db $@ 13 | fi 14 | 15 | if [ "$1" = "purge" ]; then 16 | rm -f /etc/freeradius/3.0/mods-available/grasesql 17 | rm -f /etc/freeradius/3.0/mods-available/graseperl 18 | if which ucf >/dev/null 2>&1; then 19 | ucf --purge /etc/freeradius/3.0/mods-available/grasesql 20 | ucf --purge /etc/freeradius/3.0/mods-available/graseperl 21 | ucf --purge /etc/grase/radius.conf 22 | ucfr --purge grase-conf-freeradius /etc/grase/radius.conf 23 | ucfr --purge grase-conf-freeradius /etc/freeradius/3.0/mods-available/grasesql 24 | ucfr --purge grase-conf-freeradius /etc/freeradius/3.0/mods-available/graseperl 25 | fi 26 | fi 27 | 28 | #DEBHELPER# 29 | 30 | if which invoke-rc.d >/dev/null 2>&1; then 31 | invoke-rc.d freeradius stop || true 32 | else 33 | /etc/init.d/freeradius stop || true 34 | fi 35 | 36 | exit 0 37 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | DEB_AUTO_UPDATE_DEBIAN_CONTROL = 1 4 | 5 | DEB_DIVERT_EXTENSION = .grase 6 | # Replace some base files with simple replacements. 7 | DEB_DIVERT_FILES_grase-conf-freeradius += \ 8 | /etc/freeradius/3.0/dictionary.grase \ 9 | # /etc/freeradius/sql/mysql/counter.conf.grase \ 10 | # /etc/freeradius/modules/perl.grase 11 | # /etc/mailname.debathena \ 12 | # /etc/papersize.debathena 13 | #DEB_TRANSFORM_FILES_grase-conf-freeradius += \ 14 | # /etc/freeradius/clients.conf.grase 15 | # /etc/default/monit.grase \ 16 | # /etc/monit/monitrc.grase 17 | 18 | # These files are installed via dh_install from the files/ directory 19 | # (see debian/grase-conf-freeradius.install) 20 | 21 | include /usr/share/cdbs/1/rules/debhelper.mk 22 | include /usr/share/cdbs/1/rules/config-package.mk 23 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/transform_clients.conf.grase: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -0p 2 | # perl -0p is useful for writing efficient transform scripts. 3 | 4 | # Set freeradius to use sql 5 | 6 | s/testing123/hotspotradius/m or die; 7 | -------------------------------------------------------------------------------- /freeradius/3.0/dictionary.grase: -------------------------------------------------------------------------------- 1 | # 2 | # This is the local dictionary file which can be 3 | # edited by local administrators. It will be loaded 4 | # AFTER the main dictionary files are loaded. 5 | # 6 | # As of version 3.0.2, FreeRADIUS will automatically 7 | # load the main dictionary files from 8 | # 9 | # ${prefix}/share/freeradius/dictionary 10 | # 11 | # It is no longer necessary for this file to $INCLUDE 12 | # the main dictionaries. However, if the $INCLUDE 13 | # line is here, nothing bad will happen. 14 | # 15 | # Any new/changed attributes MUST be placed in this file. 16 | # The pre-defined dictionaries SHOULD NOT be edited. 17 | # 18 | # See "man dictionary" for documentation on its format. 19 | # 20 | # $Id: eed5d70f41b314f9ed3f006a22d9f9a2be2c9516 $ 21 | # 22 | 23 | # 24 | # All local attributes and $INCLUDE's should go into 25 | # this file. 26 | # 27 | 28 | $INCLUDE /usr/share/doc/coova-chilli/dictionary.coovachilli 29 | 30 | # If you want to add entries to the dictionary file, 31 | # which are NOT going to be placed in a RADIUS packet, 32 | # add them to the 'dictionary.local' file. 33 | # 34 | # The numbers you pick should be between 3000 and 4000. 35 | # These attributes will NOT go into a RADIUS packet. 36 | # 37 | # If you want that, you will need to use VSAs. This means 38 | # requesting allocation of a Private Enterprise Code from 39 | # http://iana.org. We STRONGLY suggest doing that only if 40 | # you are a vendor of RADIUS equipment. 41 | # 42 | # See RFC 6158 for more details. 43 | # http://ietf.org/rfc/rfc6158.txt 44 | # 45 | 46 | # 47 | # These attributes are examples 48 | # 49 | #ATTRIBUTE My-Local-String 3000 string 50 | #ATTRIBUTE My-Local-IPAddr 3001 ipaddr 51 | #ATTRIBUTE My-Local-Integer 3002 integer 52 | 53 | ATTRIBUTE Max-Octets 3050 string 54 | ATTRIBUTE Max-Hourly-Octets 3051 string 55 | ATTRIBUTE Max-Daily-Octets 3052 string 56 | ATTRIBUTE Max-Weekly-Octets 3053 string 57 | ATTRIBUTE Max-Monthly-Octets 3054 string 58 | ATTRIBUTE Max-All-Session 3060 string 59 | ATTRIBUTE Max-Hourly-Session 3061 string 60 | ATTRIBUTE Max-Daily-Session 3062 string 61 | ATTRIBUTE Max-Weekly-Session 3063 string 62 | ATTRIBUTE Max-Monthly-Session 3064 string 63 | ATTRIBUTE GRASE-ExpireAfter 3065 string 64 | -------------------------------------------------------------------------------- /freeradius/3.0/mods-available/graseperl: -------------------------------------------------------------------------------- 1 | # -*- text -*- 2 | # 3 | # $Id$ 4 | 5 | # Persistent, embedded Perl interpreter. 6 | # 7 | perl graseperl { 8 | # 9 | # The Perl script to execute on authorize, authenticate, 10 | # accounting, xlat, etc. This is very similar to using 11 | # 'rlm_exec' module, but it is persistent, and therefore 12 | # faster. 13 | # 14 | filename = ${confdir}/perl_modules/sql_counters.pl 15 | 16 | # 17 | # The following hashes are given to the module and 18 | # filled with value-pairs (Attribute names and values) 19 | # 20 | # %RAD_CHECK Check items 21 | # %RAD_REQUEST Attributes from the request 22 | # %RAD_REPLY Attributes for the reply 23 | # 24 | # The return codes from functions in the perl_script 25 | # are passed directly back to the server. These 26 | # codes are defined in doc/configurable_failover, 27 | # src/include/modules.h (RLM_MODULE_REJECT, etc), 28 | # and are pre-defined in the 'example.pl' program 29 | # which is included. 30 | # 31 | 32 | # 33 | # List of functions in the module to call. 34 | # Uncomment and change if you want to use function 35 | # names other than the defaults. 36 | # 37 | func_authenticate = authenticate 38 | func_authorize = authorize 39 | #func_preacct = preacct 40 | #func_accounting = accounting 41 | #func_checksimul = checksimul 42 | #func_pre_proxy = pre_proxy 43 | #func_post_proxy = post_proxy 44 | #func_post_auth = post_auth 45 | #func_recv_coa = recv_coa 46 | #func_send_coa = send_coa 47 | #func_xlat = xlat 48 | #func_detach = detach 49 | 50 | # 51 | # Uncomment the following lines if you wish 52 | # to use separate functions for Start and Stop 53 | # accounting packets. In that case, the 54 | # func_accounting function is not called. 55 | # 56 | #func_start_accounting = accounting_start 57 | #func_stop_accounting = accounting_stop 58 | } 59 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/Attributes.pm: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Attributes - A Module which will get the check and reply attributes for a user in FreeRADIUS mysql database 4 | 5 | It will first get the group attributes and then override them if there are specific personal attributes declared 6 | 7 | Part of YFi Hotspot Manager's custom rlm_perl module 8 | 9 | =head1 VERSION 10 | 11 | This documentation refers to Attributes version 0.1 12 | 13 | =head1 SYNOPSIS 14 | 15 | use Attributes; 16 | use Data::Dumper; 17 | 18 | print Dumper(reply_attributes('alee')); 19 | print Dumper(check_attributes('alee')); 20 | 21 | 22 | =head1 DESCRIPTION 23 | 24 | This hash of attribute / value pairs can be used in the response of a Auth request from FreeRADIUS 25 | 26 | 27 | =head1 AUTHOR 28 | 29 | Dirk van der Walt (dirkvanderwalt at gmail dot com) 30 | 31 | =head1 LICENCE_AND_COPYRIGHT 32 | 33 | Copyright (c) 2006 Dirk van der Walt (dirkvanderwalt at gmail dot com). All rights reserved. 34 | 35 | This module is free software; you can redistribute it and/or 36 | modify it under the same terms as Perl itself. See L. 37 | 38 | This program is distributed in the hope that it will be usefull, 39 | but WITHOUT ANY WARRANTY; without even implied warranty of 40 | MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. 41 | 42 | =cut 43 | 44 | package Attributes; 45 | 46 | use strict; 47 | use warnings; 48 | use Data::Dumper; 49 | 50 | 51 | #Initialise this with a sql_connector object 52 | sub new { 53 | 54 | print " Attributes::new called\n"; 55 | my $type = shift; # The package/type name 56 | my $self = {'sql_connector' => shift}; # Reference to empty hash 57 | return bless $self, $type; 58 | } 59 | 60 | 61 | sub check_attributes { 62 | #------------------------------------------------------------ 63 | #----RETURN a Hash of Check attributes ---------------------- 64 | #------------------------------------------------------------ 65 | my ($self,$username) = @_; 66 | 67 | if(!defined $username ){ 68 | 69 | print "Empty value for username... return\n"; 70 | return; 71 | }else{ 72 | 73 | return $self->_get_check_attributes($username); 74 | } 75 | } 76 | 77 | sub reply_attributes { 78 | #------------------------------------------------------------ 79 | #----RETURN a Hash of Reply attributes ---------------------- 80 | #------------------------------------------------------------ 81 | my ($self,$username) = @_; 82 | 83 | if(!defined $username ){ 84 | 85 | print "Empty value for username... return\n"; 86 | return; 87 | }else{ 88 | 89 | return $self->_get_reply_attributes($username); 90 | } 91 | } 92 | 93 | sub _get_check_attributes { 94 | 95 | #----------------------------------------------- 96 | #--- This sub will do the following: 97 | #--- + Find out if the user is defined (in radcheck table) 98 | #--- + Find out which group the user belongs to (in radusergroup) 99 | #--- + Find the check attributes for the group 100 | #--- + override these check attributes if there are defined for the user itself in the radcheck table 101 | #--- + return a hash containing this attributes. 102 | my ($self,$user) = @_; 103 | my $return_hash; 104 | 105 | #--- + Find out if the user is defined (in radcheck table) 106 | my $return_data = $self->{'sql_connector'}->one_statement_value('radcheck_username',$user); 107 | 108 | if(!exists $return_data->{'value'}){ 109 | print "User does not exists\n"; 110 | return $return_hash; 111 | } 112 | 113 | #--- + Find out which group(s) the user belongs to (in radusergroup) 114 | #--- + Find the check attributes for the group 115 | $return_data = $self->{'sql_connector'}->many_statement_value('radusergroup_username',$user); 116 | 117 | foreach my $line(@{$return_data}){ 118 | 119 | my $check_group_name = $line->[1]; 120 | my $check_hash = $self->_attributes_for_group($check_group_name,'check'); 121 | 122 | if(defined $check_hash){ 123 | foreach my $key (keys %{$check_hash}){ 124 | $return_hash->{$key} = $check_hash->{$key}; 125 | } 126 | } 127 | } 128 | 129 | #--- + override these check attributes if there are defined for the the user itself in the radcheck table 130 | $return_data = $self->{'sql_connector'}->many_statement_value('radcheck_username',$user); 131 | 132 | foreach my $line(@{$return_data}){ 133 | my $attribute = $line->[2]; 134 | my $value = $line->[4]; 135 | $return_hash->{$attribute} = $value; 136 | } 137 | return $return_hash; 138 | } 139 | 140 | 141 | sub _get_reply_attributes { 142 | 143 | #----------------------------------------------- 144 | #--- This sub will do the following: 145 | #--- + Find out if the user is defined (in radcheck table) 146 | #--- + Find out which group the user belongs to (in radusergroup) 147 | #--- + Find the reply attributes for the group 148 | #--- + override these reply attributes if there are defined for the the user itself in the radreply table 149 | #--- + return a hash containing this attributes. 150 | my ($self,$user) = @_; 151 | my $return_hash; 152 | 153 | #--- + Find out if the user is defined (in radcheck table) 154 | my $return_data = $self->{'sql_connector'}->one_statement_value('radcheck_username',$user); 155 | 156 | if(!exists $return_data->{'value'}){ 157 | print "User does not exists\n"; 158 | return $return_hash; 159 | } 160 | 161 | #--- + Find out which group(s) the user belongs to (in radusergroup) 162 | #--- + Find the reply attributes for the group 163 | $return_data = $self->{'sql_connector'}->many_statement_value('radusergroup_username',$user); 164 | 165 | foreach my $line(@{$return_data}){ 166 | 167 | my $reply_group_name = $line->[1]; 168 | my $reply_hash = $self->_attributes_for_group($reply_group_name,'reply'); 169 | 170 | if(defined $reply_hash){ 171 | 172 | foreach my $key (keys %{$reply_hash}){ 173 | $return_hash->{$key} = $reply_hash->{$key}; 174 | } 175 | 176 | } 177 | } 178 | 179 | #--- + override these reply attributes if there are defined for the the user itself in the radreply table 180 | $return_data = $self->{'sql_connector'}->many_statement_value('radreply_username',$user); 181 | foreach my $line(@{$return_data}){ 182 | 183 | my $attribute = $line->[2]; 184 | my $value = $line->[4]; 185 | $return_hash->{$attribute} = $value; 186 | } 187 | return $return_hash; 188 | } 189 | 190 | sub _attributes_for_group { 191 | 192 | my ($self,$groupname,$type) = @_; 193 | 194 | my $query_string; 195 | my $return_hash; 196 | my $return_data; 197 | 198 | if($type eq 'check'){ 199 | $return_data = $self->{'sql_connector'}->many_statement_value('radgroupcheck_groupname',$groupname); 200 | } 201 | 202 | if($type eq 'reply'){ 203 | $return_data = $self->{'sql_connector'}->many_statement_value('radgroupreply_groupname',$groupname); 204 | } 205 | 206 | foreach my $line(@{$return_data}){ 207 | 208 | my $attribute = $line->[2]; 209 | my $value = $line->[4]; 210 | $return_hash->{$attribute} = $value; 211 | } 212 | return $return_hash; 213 | } 214 | 215 | 1; 216 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/SQLConnector.pm: -------------------------------------------------------------------------------- 1 | package SQLConnector; 2 | 3 | use strict; 4 | use Data::Dumper; 5 | use DBI; 6 | #use XML::Simple; 7 | use Config::Simple; 8 | 9 | 10 | sub new { 11 | 12 | print " SQLConnector::new called\n"; 13 | my $type = shift; # The package/type name 14 | #my $self = {'config_file' => '/etc/grase/radius.conf'}; # Reference to empty hash 15 | 16 | my $self = {}; 17 | my $self->{'radiusconfig'} = {}; 18 | Config::Simple->import_from('/etc/grase/radius.conf', $self->{'radiusconfig'}) || die "Couldn't read our config file for database details in SQLConnector" . Config::Simple->error(); 19 | 20 | print Dumper $self; 21 | 22 | my $db_server = $self->{'radiusconfig'}->{'sql_server'}; 23 | my $db_name = $self->{'radiusconfig'}->{'sql_database'}; 24 | my $db_user = $self->{'radiusconfig'}->{'sql_username'}; 25 | my $db_password = $self->{'radiusconfig'}->{'sql_password'}; 26 | 27 | $self->{'db_handle'} = DBI->connect("DBI:mysql:database=$db_name;host=$db_server", 28 | "$db_user", 29 | "$db_password", 30 | { RaiseError => 1, 31 | AutoCommit => 1, 32 | FetchHashKeyName => "NAME_lc" }) || die "Unable to connect to $db_server because $DBI::errstr"; 33 | $self->{'db_handle'}->{'mysql_auto_reconnect'} = 1; 34 | return bless $self, $type; 35 | } 36 | 37 | 38 | sub DESTROY 39 | { 40 | print " SQLConnector::DESTROY called\n"; 41 | } 42 | 43 | 44 | sub query { 45 | my($self,$q) = @_; 46 | my $StatementHandle = $self->{'db_handle'}->prepare("$q"); 47 | $StatementHandle->execute(); 48 | return $StatementHandle->fetchall_arrayref(); 49 | } 50 | 51 | 52 | sub prepare_statements { 53 | 54 | my($self,$q) = @_; 55 | #____________ Description ____________________________________________________ 56 | #__ This file prepares the various SQL queries which will later be executed___ 57 | #_____________________________________________________________________________ 58 | 59 | #List the statements here so we can loop through them and 'finish' them 60 | $self->{'statements'} = [ 61 | 'na_nasname', 'na_id', 'device_name','device_update_id', 'user_id', 'user_username','radcheck_username', 62 | 'realm_id', 'radacct_count_username', 'radacct_time_username', 'na_realm_na_id', 'radusergroup_username', 63 | 'radgroupcheck_groupname', 'radgroupreply_groupname', 'radcheck_username', 'radreply_username' 64 | ]; 65 | 66 | #================================================== 67 | #=== Return only ONE line / Require One value ===== 68 | #================================================== 69 | 70 | #____ NAS Related Queries _______ 71 | $self->{'na_nasname'} = $self->{'db_handle'}->prepare("SELECT * FROM nas WHERE nasname=?"); 72 | $self->{'na_id'} = $self->{'db_handle'}->prepare("SELECT * FROM nas WHERE id=?"); 73 | 74 | #____ Devices Related Queries _______ 75 | $self->{'device_name'} = $self->{'db_handle'}->prepare("SELECT * FROM devices WHERE name=?"); 76 | $self->{'device_update_id'} = $self->{'db_handle'}->prepare("UPDATE devices SET modified=now() WHERE id=?"); 77 | 78 | #____ Users Related Queries ________ 79 | $self->{'user_id'} = $self->{'db_handle'}->prepare("SELECT * FROM users WHERE id=?"); 80 | $self->{'user_username'} = $self->{'db_handle'}->prepare("SELECT * FROM users WHERE username=?"); 81 | 82 | #____ Radcheck Queries _______ 83 | $self->{'radcheck_username'} = $self->{'db_handle'}->prepare("SELECT value FROM radcheck WHERE username=? and attribute='Cleartext-Password'"); 84 | 85 | #_____ Realm Related Queries _____ 86 | $self->{'realm_id'} = $self->{'db_handle'}->prepare("SELECT * FROM realms WHERE id=?"); 87 | 88 | #_____ Radacct Queries ______ 89 | $self->{'radacct_count_username'} = $self->{'db_handle'}->prepare("SELECT COUNT(*) as count FROM radacct WHERE username=? AND acctstoptime is NULL"); 90 | $self->{'radacct_time_username'} = $self->{'db_handle'}->prepare("SELECT UNIX_TIMESTAMP(acctstarttime) as acctstarttime FROM radacct WHERE username=? ORDER BY acctstarttime ASC LIMIT 1"); 91 | $self->{'radacct_sum_username'} = $self->{'db_handle'}->prepare("SELECT SUM(acctinputoctets) as input, SUM(acctoutputoctets) as output, SUM(acctsessiontime) as time FROM radacct where username=?"); 92 | $self->{'credit_sum_used_by_id'} = $self->{'db_handle'}->prepare("SELECT SUM(data) as data, SUM(time) as time FROM credits where used_by_id=?"); 93 | 94 | #_____ Prime / Normal differentiation _____ 95 | $self->{'times_last_entry'} = $self->{'db_handle'}->prepare("SELECT * from times where acctsessionid=? ORDER BY id DESC LIMIT 1"); 96 | 97 | 98 | 99 | #================================================== 100 | #=== Return only ONE line / Require Three values===== 101 | #================================================== 102 | #____ Extra CAPS ______ 103 | $self->{'extra_sum'} = $self->{'db_handle'}->prepare("SELECT SUM(value) as sum FROM extras WHERE user_id=? AND type=? AND UNIX_TIMESTAMP(created) > ?"); 104 | #____ Prime Time / Normal Time ____ 105 | $self->{'prime_totals'} = $self->{'db_handle'}->prepare("SELECT SUM(data) as data, SUM(time) as time FROM times where UNIX_TIMESTAMP(created) >= ? AND UNIX_TIMESTAMP(created) <= ? AND username=? AND type='Prime'"); 106 | $self->{'normal_totals_start'} = $self->{'db_handle'}->prepare("SELECT SUM(data) as data, SUM(time) as time FROM times where UNIX_TIMESTAMP(created) >= ? AND UNIX_TIMESTAMP(created) <= ? AND username=? AND type='Normal'"); 107 | $self->{'normal_totals_end'} = $self->{'db_handle'}->prepare("SELECT SUM(data) as data, SUM(time) as time FROM times where UNIX_TIMESTAMP(created) >= ? AND username=? AND type='Normal' AND UNIX_TIMESTAMP(modified) <= ?"); 108 | 109 | 110 | #================================================== 111 | #=== Return ziltzh / Require Two values============ 112 | #================================================== 113 | 114 | $self->{'user_update_data'} = $self->{'db_handle'}->prepare("UPDATE users SET data=? WHERE id=?"); 115 | $self->{'user_update_time'} = $self->{'db_handle'}->prepare("UPDATE users SET time=? WHERE id=?"); 116 | 117 | 118 | #================================================== 119 | #=== Return many lines / Require One value ======== 120 | #================================================== 121 | $self->{'na_realm_na_id'} = $self->{'db_handle'}->prepare("SELECT * FROM na_realms WHERE na_id=?"); 122 | $self->{'radusergroup_username'} = $self->{'db_handle'}->prepare("SELECT * FROM radusergroup WHERE username=? ORDER BY priority"); 123 | $self->{'radgroupcheck_groupname'} = $self->{'db_handle'}->prepare("SELECT * FROM radgroupcheck WHERE groupname=?"); 124 | $self->{'radgroupreply_groupname'} = $self->{'db_handle'}->prepare("SELECT * FROM radgroupreply WHERE groupname=?"); 125 | $self->{'radcheck_username'} = $self->{'db_handle'}->prepare("SELECT * FROM radcheck WHERE username=?"); 126 | $self->{'radreply_username'} = $self->{'db_handle'}->prepare("SELECT * FROM radreply WHERE username=?"); 127 | 128 | #==================================================== 129 | #=== Return Zitzh / Require Five Values ============= 130 | #==================================================== 131 | $self->{'update_times_entry'} = $self->{'db_handle'}->prepare("UPDATE times SET time= ?, data= ?, modified=now() where id = ?"); 132 | 133 | #==================================================== 134 | #=== Return Zitzh / Require Five Values ============= 135 | #==================================================== 136 | $self->{'add_times_entry'} = $self->{'db_handle'}->prepare("INSERT INTO times(acctsessionid, username, time, data, type, created, modified) VALUES(?,?,?,?,?,now(),now())"); 137 | 138 | 139 | #======================================================= 140 | #==== SQL Counter Quick ================================ 141 | #======================================================= 142 | #$self->{'yfi_max_bytes_monthly'} = $self->{'db_handle'}->prepare("SELECT SUM(acctinputoctets - GREATEST((? - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((? - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username=? AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > ?" 143 | 144 | 145 | # 146 | # $self->{'user_count'} = $self->{'db_handle'}->prepare("SELECT COUNT(*) FROM users WHERE username=?"); 147 | # 148 | # $self->{'user_detail_for_name'} = $self->{'db_handle'}->prepare("SELECT id,cap,active FROM users WHERE username=?"); 149 | # $self->{'user_username'} = $self->{'db_handle'}->prepare("SELECT username FROM users WHERE id=?"); 150 | # 151 | # $self->{'user_id'} = $self->{'db_handle'}->prepare("SELECT SUM(value) FROM extras WHERE user_id=? AND type=? AND UNIX_TIMESTAMP(created) > ?"); 152 | # 153 | # #For the Voucher Module: 154 | # $self->{'radacct_username'} = $self->{'db_handle'}->prepare("SELECT DISTINCT username FROM radacct WHERE nasipaddress=?"); 155 | # $self->{'radacct_start'} = $self->{'db_handle'}->prepare("SELECT UNIX_TIMESTAMP(acctstarttime) FROM radacct WHERE username=? ORDER BY acctstarttime ASC LIMIT 1"); 156 | 157 | # $self->{'radcheck_password'} = $self->{'db_handle'}->prepare("SELECT value FROM radcheck WHERE username=? and attribute='Cleartext-Password'"); 158 | } 159 | 160 | 161 | #_____________________________________________________________ 162 | #______________ NAS Related Methods___________________________ 163 | #_____________________________________________________________ 164 | 165 | #Only return one line for the query of a nasname 166 | sub one_statement_value { #Select the statement handle name and supply the value 167 | 168 | my($self,$statement_name,$value) = @_; 169 | 170 | $self->{"$statement_name"}->execute($value) 171 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 172 | 173 | my $hash_ref = $self->{$statement_name}->fetchrow_hashref(); 174 | return $hash_ref; # Return a hash with ALL the fields in the NAS 175 | } 176 | 177 | sub one_statement_no_return { 178 | my($self,$statement_name,$value) = @_; 179 | 180 | my $fb = $self->{"$statement_name"}->execute($value) 181 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 182 | 183 | #Auto Commmit had to be turned on because of mysql_auto_reconnect 184 | #$self->{"db_handle"}->commit(); 185 | 186 | return; 187 | } 188 | 189 | 190 | sub one_statement_no_return_value_value { 191 | my($self,$statement_name,$value1,$value2) = @_; 192 | 193 | my $fb = $self->{"$statement_name"}->execute($value1,$value2) 194 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 195 | 196 | #Auto Commmit had to be turned on because of mysql_auto_reconnect 197 | #$self->{"db_handle"}->commit(); 198 | return; 199 | } 200 | 201 | sub no_return_three_values { 202 | 203 | my($self,$statement_name,$value1,$value2,$value3) = @_; 204 | my $fb = $self->{"$statement_name"}->execute($value1,$value2,$value3) 205 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 206 | #Auto Commmit had to be turned on because of mysql_auto_reconnect 207 | #$self->{"db_handle"}->commit(); 208 | return; 209 | } 210 | 211 | sub no_return_five_values { 212 | 213 | my($self,$statement_name,$value1,$value2,$value3,$value4,$value5) = @_; 214 | my $fb = $self->{"$statement_name"}->execute($value1,$value2,$value3,$value4,$value5) 215 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 216 | #Auto Commmit had to be turned on because of mysql_auto_reconnect 217 | #$self->{"db_handle"}->commit(); 218 | return; 219 | } 220 | 221 | 222 | #Only return one line for the query 223 | sub one_statement_value_value_value { #Select the statement handle name and supply the value 224 | 225 | my($self,$statement_name,$value1,$value2,$value3) = @_; 226 | 227 | $self->{"$statement_name"}->execute($value1,$value2,$value3) 228 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 229 | 230 | my $hash_ref = $self->{$statement_name}->fetchrow_hashref(); 231 | return $hash_ref; # Return a hash with ALL the fields in the NAS 232 | } 233 | 234 | 235 | sub many_statement_value { 236 | 237 | my($self,$statement_name,$value) = @_; 238 | 239 | $self->{"$statement_name"}->execute($value) 240 | or die "Couldn't execute statement: " .$self->{$statement_name}->errstr; 241 | 242 | my $array_ref = $self->{$statement_name}->fetchall_arrayref(); 243 | return $array_ref; # Return a hash with ALL the fields in the NAS 244 | } 245 | 246 | 247 | 248 | sub finish_statements { #Select the statement handle name and supply the value 249 | 250 | my($self) = @_; 251 | 252 | foreach my $item(@{$self->{statements}}){ 253 | 254 | # print "Destroying statement handle: $item\n"; 255 | $self->{$item}->finish(); 256 | 257 | } 258 | } 259 | 260 | 261 | 262 | 1; 263 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/SQLCounter.pm: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | SQLCounter - A Module which will is used as a drop in replacement for sqlcounter. 4 | 5 | This module was created due to the limitations of the sqlcounter module's handling of big numbers 6 | 7 | =head1 VERSION 8 | 9 | This documentation refers to SQLCounter version 0.1 10 | 11 | =head1 SYNOPSIS 12 | 13 | use SQLCounter; 14 | use Data::Dumper; 15 | 16 | my $sql_counter_reply = sqlcounter_check($username, $check_hash); 17 | if(defined $sql_counter_reply){ 18 | 19 | foreach my $key (keys %{$sql_counter_reply}){ 20 | 21 | $RAD_REPLY{$key} = $sql_counter_reply->{$key}; 22 | #If there was an error the 'Reply-Message' will have a value, if so return with a 0 23 | if($key eq 'Reply-Message'){ 24 | return 0; 25 | } 26 | } 27 | } 28 | 29 | 30 | =head1 DESCRIPTION 31 | 32 | You have to pass it the $check_hash so it can check if the counter is tied to the specific user. 33 | This hash of attribute / value pairs are returned which should be used to $RAD_REPLY 34 | 35 | 36 | =head1 AUTHOR 37 | 38 | Dirk van der Walt (dvdwalt at csir dot co dot za) 39 | 40 | =head1 LICENCE_AND_COPYRIGHT 41 | 42 | Copyright (c) 2006 Dirk van der Walt (dirkvanderwalt at gmail dot com). All rights reserved. 43 | 44 | This module is free software; you can redistribute it and/or 45 | modify it under the same terms as Perl itself. See L. 46 | 47 | This program is distributed in the hope that it will be usefull, 48 | but WITHOUT ANY WARRANTY; without even implied warranty of 49 | MERCHANTABILITY of FITNESS FOR A PARTICULAR PURPOSE. 50 | 51 | =cut 52 | 53 | package SQLCounter; 54 | 55 | use strict; 56 | use warnings; 57 | use XML::Simple; 58 | use Data::Dumper; 59 | use DBI; 60 | use POSIX; 61 | 62 | #=============================================================== 63 | #===== CONFIGURATION DATA ====================================== 64 | #=============================================================== 65 | my $config_file = '/etc/freeradius/3.0/perl_modules/conf/settings.conf'; 66 | #=============================================================== 67 | #===== END of Configuration Data =============================== 68 | #=============================================================== 69 | 70 | 71 | #Initialise this with a sql_connector object 72 | sub new { 73 | 74 | print " SQLCounter::new called\n"; 75 | my $type = shift; # The package/type name 76 | 77 | my $xml = new XML::Simple; 78 | my $data = $xml->XMLin($config_file); 79 | my $self = {'config_file' => $config_file, 'config_data' => $data, 'sql_connector' => shift }; # Reference to empty hash 80 | return bless $self, $type; 81 | } 82 | 83 | 84 | sub counter_check { 85 | #------------------------------------------------------------ 86 | #----RETURN a Hash of Check attributes ---------------------- 87 | #------------------------------------------------------------ 88 | my ($self,$username,$check_hash) = @_; 89 | 90 | if(!defined $username ){ 91 | 92 | print "Empty value for username... return\n"; 93 | return; 94 | }else{ 95 | 96 | my $return_hash; 97 | #Get a hash of all the defined sqlcounters (active or not) 98 | my $counters_detail = $self->{'counter_hash'}; 99 | #print(Dumper($counters_detail)); 100 | 101 | #Loop through a list of defined counters 102 | foreach my $key(keys(%{$counters_detail})){ 103 | 104 | #print Dumper($check_hash); 105 | #Check if the couter is active 106 | if($counters_detail->{$key}{'active'}){ 107 | 108 | #Determine the check attribute 109 | my $check_name = $counters_detail->{$key}{'check-name'}; 110 | my $reply_name = $counters_detail->{$key}{'reply-name'}; 111 | 112 | # Our reject reply-message when the counter is finished 113 | my $reply_message = $counters_detail->{$key}{'reply-message'}; 114 | 115 | my $giga_reply_name = $counters_detail->{$key}{'giga-reply-name'}; 116 | if(!defined $giga_reply_name){ ($giga_reply_name = $reply_name) =~ s/Octets/Gigawords/;} 117 | 118 | #Have we got a key like this? (in the $check_hash for the user) 119 | if(exists $check_hash->{$check_name}){ 120 | 121 | my $reset = $counters_detail->{$key}{'reset'}; 122 | my $timestamp = $self->get_timestamp($reset); #When should the reset time be 123 | my $sql_query = $counters_detail->{$key}{'query'}; 124 | 125 | #Filter out the ='%{%k}' to add our own user eg username='%{%k}' 126 | $sql_query =~ s/%\{%k}/$username/g; 127 | #Filter out the '%b' and replace it wit the correct unix timestamp 128 | $sql_query =~ s/%b/$timestamp/g; 129 | $sql_query =~ s/"//g; 130 | # print "HEADS UP $sql_query\n"; 131 | #--- NOTE: This may be a performance bottleneck! because the 'prepare' happens every time --- 132 | #--- IF performance is a problem --- start here!--------------------------------------------- 133 | #-------------------------------------------------------------------------------------------- 134 | my $return_data = $self->{'sql_connector'}->query($sql_query); 135 | #-------------------------------------------------------------------------------------------- 136 | my $big_val = $check_hash->{$check_name}; 137 | 138 | my $current_val =0; 139 | if(defined($return_data->[0][0])){ 140 | $current_val = $return_data->[0][0]; 141 | } 142 | my $result = $big_val - ($current_val); 143 | #print "NEED TO SUBTRACT $current_val FROM $big_val RESULT $result\n"; 144 | if($result <= 0){ 145 | #Some attribute values we want to return as a negative value 146 | #--------------------------------------------------------- 147 | #--- YFi FEATURE : IGNORE Yfi-Data & Yfi-Time Depletion--- 148 | #--------------------------------------------------------- 149 | if(($reply_name eq 'Yfi-Data')or($reply_name eq 'Yfi-Time')){ 150 | $return_hash->{$reply_name} = $result; 151 | }else{ 152 | ## This is where the reject messages are if they have finished a counter 153 | $return_hash->{'Reply-Message'} = defined($reply_message) ? $reply_message : "Depleted value for $reply_name"; 154 | } 155 | }else{ 156 | # Easiest way to see if we need Gigawords splitting is if the reply-name has Octets in it 157 | if($reply_name =~ /Octets/) 158 | { 159 | my $int_max = 4294967296; 160 | my $octets = 0; 161 | my $gigawords = 0; 162 | if($result >= $int_max) 163 | { 164 | 165 | # Split it into gigawords and octets 166 | $octets = $result % $int_max; 167 | $gigawords = int($result / $int_max); 168 | }else{ 169 | $octets = $result; 170 | $gigawords = 0; 171 | } 172 | 173 | if(exists $return_hash->{$reply_name}) { 174 | my $current_val = $return_hash->{$reply_name}; 175 | my $current_gigawords = $return_hash->{$giga_reply_name}; 176 | 177 | if($gigawords <= $current_gigawords and $octets < $current_val) { 178 | $return_hash->{$reply_name} = $octets; 179 | $return_hash->{$giga_reply_name} = $gigawords; 180 | } 181 | } else { 182 | $return_hash->{$reply_name} = $octets; 183 | $return_hash->{$giga_reply_name} = $gigawords; 184 | } 185 | 186 | }elsif($reply_name =~ /Session-Timeout/) { 187 | if(exists $return_hash->{$reply_name}) { 188 | if($result < $return_hash->{$reply_name}) { 189 | $return_hash->{$reply_name} = $result; 190 | } 191 | } else { 192 | $return_hash->{$reply_name} = $result; 193 | } 194 | 195 | }else{ 196 | $return_hash->{$reply_name} = $result; 197 | } 198 | } 199 | } 200 | } 201 | } 202 | return $return_hash; 203 | } 204 | } 205 | 206 | sub get_usage_for_counter { 207 | 208 | #------------------------------------------------ 209 | #--- Get the usage (NOT the amount LEFT) -------- 210 | #------------------------------------------------ 211 | 212 | my ($self,$username,$counter_name) = @_; 213 | 214 | my $current_val; 215 | #Get a hash of all the defined sqlcounters (active or not) 216 | my $counters_detail = $self->{'counter_hash'}; 217 | #Loop through a list of defined counters 218 | foreach my $key(keys(%{$counters_detail})){ 219 | 220 | #Check if the couter is active 221 | if($counters_detail->{$key}{'active'} eq '1'){ 222 | 223 | #print Dumper($counters_detail->{$key}); 224 | #Determine the check attribute 225 | my $check_name = $counters_detail->{$key}{'check-name'}; 226 | my $reply_name = $counters_detail->{$key}{'reply-name'}; 227 | 228 | #is this the one we want 229 | if($counter_name eq $counters_detail->{$key}{'counter-name'}){ #Is this the counter that we want?? 230 | 231 | my $reset = $counters_detail->{$key}{'reset'}; 232 | my $timestamp = $self->get_timestamp($reset); #When should the reset time be 233 | my $sql_query = $counters_detail->{$key}{'query'}; 234 | #Filter out the ='%{%k}' to add our own user eg username='%{%k}' 235 | $sql_query =~ s/%\{%k}/$username/g; 236 | #Filter out the '%b' and replace it wit the correct unix timestamp 237 | $sql_query =~ s/%b/$timestamp/g; 238 | $sql_query =~ s/"//g; 239 | my $return_data = $self->{'sql_connector'}->query($sql_query); 240 | $current_val = $return_data->[0][0]; 241 | } 242 | } 243 | } 244 | return $current_val; 245 | } 246 | 247 | #----------------------------- 248 | #---Sub to build a hash of -- 249 | #---each sqlcounter----------- 250 | #----------------------------- 251 | sub create_sql_counter_hash { 252 | 253 | my ($self) = @_; 254 | 255 | my $counter_record = 0; 256 | my $counter_name; 257 | my $sql_counter_hash; 258 | my $sql_counter_file = $self->{'config_data'}->{radius_conf}->{sql_counter_file}; 259 | 260 | my @sql_counter_raw = `cat $sql_counter_file`; 261 | foreach my $line (@sql_counter_raw){ 262 | chomp $line; 263 | 264 | #BEGIN THE RECORDING 265 | if($line =~ m/^\s*sqlcounter/){ 266 | $counter_record = 1; 267 | $counter_name = $line; 268 | $counter_name =~ s/^\s*sqlcounter\s*//; 269 | $counter_name =~ s/\s+{//; 270 | #print "COUNTER FOUND $counter_name\n"; 271 | $sql_counter_hash->{$counter_name}{'active'} = $self->find_if_sqlcounter_is_active($counter_name); 272 | } 273 | 274 | if(($counter_record)&&($line =~ m/\s*counter-name/)){ 275 | $sql_counter_hash->{$counter_name}{'counter-name'} = $self->get_sql_counter_atom($line); 276 | } 277 | 278 | if(($counter_record)&&($line =~ m/\s*check-name/)){ 279 | $sql_counter_hash->{$counter_name}{'check-name'} = $self->get_sql_counter_atom($line); 280 | } 281 | 282 | if(($counter_record)&&($line =~ m/\s*reply-name/)){ 283 | $sql_counter_hash->{$counter_name}{'reply-name'} = $self->get_sql_counter_atom($line); 284 | } 285 | 286 | if(($counter_record)&&($line =~ m/\s*reply-message/)){ 287 | $line =~ s/\s*reply-message\s*=\s*//; 288 | $line =~ s/^"//; 289 | $line =~ s/"$//; 290 | $sql_counter_hash->{$counter_name}{'reply-message'} = $line; 291 | } 292 | 293 | if(($counter_record)&&($line =~ m/\s*gigareplyname/)){ 294 | $sql_counter_hash->{$counter_name}{'giga-reply-name'} = $self->get_sql_counter_atom($line); 295 | } 296 | 297 | if(($counter_record)&&($line =~ m/\s*sqlmod-inst/)){ 298 | $sql_counter_hash->{$counter_name}{'sqlmod-inst'} = $self->get_sql_counter_atom($line); 299 | } 300 | 301 | if(($counter_record)&&($line =~ m/\s*key/)){ 302 | $sql_counter_hash->{$counter_name}{'key'} = $self->get_sql_counter_atom($line); 303 | } 304 | 305 | if(($counter_record)&&($line =~ m/\s*reset/)){ 306 | $sql_counter_hash->{$counter_name}{'reset'} = $self->get_sql_counter_atom($line); 307 | } 308 | 309 | if(($counter_record)&&($line =~ m/\s*query/)){ 310 | $line =~ s/\s*query\s*=\s*//; 311 | $sql_counter_hash->{$counter_name}{'query'} = $line; 312 | } 313 | 314 | #END THE RECORDING 315 | if($line =~ m/^\s*}/){ 316 | $counter_record = 0; 317 | # print "COUNTER END $counter_name\n"; 318 | } 319 | } 320 | 321 | $self->{'counter_hash'} = $sql_counter_hash; 322 | } 323 | 324 | sub get_sql_counter_atom { 325 | my ($self,$line) = @_; 326 | 327 | $line =~ s/.+\s*=\s*//; 328 | return $line; 329 | } 330 | 331 | 332 | sub find_if_sqlcounter_is_active { 333 | 334 | my ($self,$sql_counter_name) = @_; 335 | my @auth_section_ent = $self->get_active_counters_from_settings(); 336 | 337 | foreach my $entry (@auth_section_ent){ 338 | 339 | if($entry eq $sql_counter_name){ 340 | return 1; 341 | } 342 | } 343 | return 0; 344 | } 345 | 346 | 347 | sub get_active_counters_from_settings { 348 | 349 | my ($self) = @_; 350 | my @active_counters; 351 | foreach my $counter(@{$self->{'config_data'}->{sql_counters}{'counter'}}){ 352 | push(@active_counters,$counter); 353 | } 354 | return @active_counters; 355 | } 356 | 357 | 358 | sub get_timestamp { 359 | 360 | my($self,$reset) = @_; 361 | 362 | if($reset eq "monthly"){ 363 | return $self->start_of_month(); 364 | } 365 | 366 | if($reset eq "weekly"){ 367 | return start_of_week(); 368 | } 369 | 370 | if($reset eq "daily"){ 371 | return start_of_day(); 372 | } 373 | 374 | if($reset eq "hourly"){ 375 | return start_of_hour(); 376 | } 377 | return mktime (0, 0, 0, 1, 1, (2004 - 1900), 0, 0); 378 | } 379 | 380 | 381 | 382 | sub start_of_month { 383 | 384 | my($self) = @_; 385 | 386 | #Get the current timestamp; 387 | #------------------------------------------------------- 388 | #--- If we need to reset the user's account on the 25--- 389 | #------------------------------------------------------- 390 | my $reset_on = $self->{'config_data'}->{sql_counters}{start_of_month}; #New Feature which lets you decide when the monthly CAP will reset 391 | my $unixtime; 392 | 393 | my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); 394 | if($mday < $reset_on ){ 395 | $unixtime = mktime (0, 0, 0, $reset_on, $mon-1, $year, 0, 0); #We use the previous month 396 | }else{ 397 | $unixtime = mktime (0, 0, 0, $reset_on, $mon, $year, 0, 0); #We use this month 398 | } 399 | #printf "%4d-%02d-%02d %02d:%02d:%02d\n",$year+1900,$mon+1,$mday,$hour,$min,$sec; 400 | #create a new timestamp: 401 | return $unixtime; 402 | } 403 | 404 | sub start_of_week { 405 | 406 | #Get the current timestamp; 407 | my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); 408 | #create a new timestamp: 409 | my $unixtime = mktime (0, 0, 0, $mday-$wday, $mon, $year, 0, 0); 410 | return $unixtime; 411 | 412 | #Debug info 413 | #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($unixtime); 414 | #printf "%4d-%02d-%02d %02d:%02d:%02d\n", 415 | #$year+1900,$mon+1,$mday,$hour,$min,$sec; 416 | } 417 | 418 | sub start_of_day { 419 | 420 | #Get the current timestamp; 421 | my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); 422 | #create a new timestamp: 423 | my $unixtime = mktime (0, 0, 0, $mday, $mon, $year, 0, 0); 424 | return $unixtime; 425 | 426 | #Debug info 427 | #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($unixtime); 428 | #printf "%4d-%02d-%02d %02d:%02d:%02d\n", 429 | #$year+1900,$mon+1,$mday,$hour,$min,$sec; 430 | } 431 | 432 | sub start_of_hour { 433 | 434 | #Get the current timestamp; 435 | my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time); 436 | #create a new timestamp: 437 | my $unixtime = mktime (0, 0, $hour, $mday, $mon, $year, 0, 0); 438 | return $unixtime; 439 | 440 | #Debug info 441 | #($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($unixtime); 442 | #printf "%4d-%02d-%02d %02d:%02d:%02d\n", 443 | #$year+1900,$mon+1,$mday,$hour,$min,$sec; 444 | } 445 | 446 | 1; 447 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/SQLExpire.pm: -------------------------------------------------------------------------------- 1 | package SQLExpire; 2 | 3 | use strict; 4 | use warnings; 5 | use Time::ParseDate; 6 | use DBI; 7 | use POSIX; 8 | 9 | sub new { 10 | print " SQLExpire::new called\n"; 11 | my $type = shift; 12 | my $self = {'sql_connector' => shift}; 13 | return bless $self, $type; 14 | } 15 | 16 | sub expire_check { 17 | # RETURN a hash of Check Attributes 18 | my ($self,$username,$check_hash,$reply_hash) = @_; 19 | 20 | if(!defined $username ){ 21 | print "Empty value for username... return\n"; 22 | return; 23 | }else{ 24 | my $return_hash; 25 | 26 | if(exists $check_hash->{'GRASE-ExpireAfter'}){ 27 | my $sql_query = 'SELECT AcctStartTime FROM radacct WHERE UserName = \'%{%k}\' AND AcctSessionTime >= 1 ORDER BY AcctStartTime LIMIT 1'; 28 | 29 | #Filter out the ='%{%k}' to add our own user eg username='%{%k}' 30 | $sql_query =~ s/%\{%k}/$username/g; 31 | 32 | my $return_data = $self->{'sql_connector'}->query($sql_query); 33 | 34 | my $first_login = 0; 35 | my $now = time(); 36 | if(defined($return_data->[0][0])){ 37 | # We have a firstlogin date from the DB 38 | $first_login = parsedate($return_data->[0][0]); 39 | }else{ 40 | # This is the users first login 41 | $first_login = $now; 42 | } 43 | 44 | ## Parse ExpireAfter with parsedate and NOW being first_login 45 | my $expiretime = parsedate($check_hash->{'GRASE-ExpireAfter'}, NOW => $first_login); 46 | 47 | if ($expiretime < $now) { 48 | # We have already passed our expiry time, just send a reject 49 | $return_hash->{'Reply-Message'} = "Your account has expired"; 50 | }else{ 51 | # We haven't yet passed our expiry time, adjust max session if 52 | # required 53 | my $expiretime_left = $expiretime - $now; 54 | if(exists $reply_hash->{'Session-Timeout'}){ 55 | my $current_val = $reply_hash->{'Session-Timeout'}; 56 | if ($expiretime_left < $current_val){ 57 | $return_hash->{'Session-Timeout'} = $expiretime_left; 58 | } 59 | }else{ 60 | $return_hash->{'Session-Timeout'} = $expiretime_left; 61 | } 62 | } 63 | } 64 | return $return_hash; 65 | } 66 | } 67 | 68 | 1; 69 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/conf/settings.conf: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | /etc/freeradius/3.0/perl_modules/sqlcounter.conf 11 | 12 | 19 | 20 | 21 | 1 22 | time_noreset 23 | time_hourly 24 | time_daily 25 | time_weekly 26 | time_monthly 27 | chillispot_max_bytes_noreset 28 | chillispot_max_bytes_hourly 29 | chillispot_max_bytes_daily 30 | chillispot_max_bytes_weekly 31 | chillispot_max_bytes_monthly 32 | yfi_max_bytes_monthly 33 | yfi_max_time_monthly 34 | mikrotik_max_bytes_noreset 35 | 36 | 37 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/conf/settings.conf.24.3.9: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 127.0.0.1 11 | radius 12 | radius 13 | radius 14 | 15 | 16 | /usr/local/etc/raddb/sqlcounter.conf 17 | /usr/local/etc/raddb/sites-available/default 18 | 19 | 20 | LOCAL_LIMITED 21 | 22 | 23 | isp@co.za 24 | smtp.co.za 25 | 26 | 27 | /var/www/cake/hotcakes/webroot/files/radpod_telkom 28 | 29 | 30 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/sql_counters.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Snippits taken from example.pl that comes with rlm_perl 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use lib "/etc/freeradius/3.0/perl_modules"; 9 | #use DatabaseConnection; 10 | use SQLConnector; 11 | use SQLCounter; 12 | use SQLExpire; 13 | use Attributes; 14 | use Data::Dumper; 15 | 16 | 17 | 18 | 19 | 20 | #print Dumper $db->{'radius_db'}->selectall_hashref('SELECT * from radcheck', 'id'); 21 | 22 | # Bring the global hashes into the package scope 23 | our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK); 24 | 25 | our $db; 26 | our $sql_counter; 27 | our $expirecheck; 28 | sub CLONE { 29 | 30 | $db = new SQLConnector(); 31 | $db->prepare_statements(); 32 | #$db = new DatabaseConnection(); 33 | #Create a $sql_counter object which will read the counters defined once (its good to avoid unnecesary file reads) 34 | $sql_counter = SQLCounter->new($db); 35 | $sql_counter->create_sql_counter_hash(); 36 | $expirecheck = SQLExpire->new($db); 37 | } 38 | 39 | 40 | # This the remapping of return values 41 | # 42 | use constant { 43 | RLM_MODULE_REJECT => 0, # immediately reject the request 44 | RLM_MODULE_OK => 2, # the module is OK, continue 45 | RLM_MODULE_HANDLED => 3, # the module handled the request, so stop 46 | RLM_MODULE_INVALID => 4, # the module considers the request invalid 47 | RLM_MODULE_USERLOCK => 5, # reject the request (user is locked out) 48 | RLM_MODULE_NOTFOUND => 6, # user not found 49 | RLM_MODULE_NOOP => 7, # module succeeded without doing anything 50 | RLM_MODULE_UPDATED => 8, # OK (pairs modified) 51 | RLM_MODULE_NUMCODES => 9 # How many return codes there are 52 | }; 53 | 54 | # Function to handle authorize 55 | sub authorize { 56 | # For debugging purposes only 57 | # &log_request_attributes; 58 | 59 | # Here's where your authorization code comes 60 | # You can call another function from here: 61 | #&test_call; 62 | 63 | my $user = $RAD_REQUEST{'User-Name'}; 64 | 65 | my $attributes = Attributes->new($db); 66 | 67 | my $check_hash = $attributes->check_attributes($user); 68 | 69 | foreach my $checkkey (keys %RAD_CHECK){ 70 | $check_hash->{$checkkey} = $RAD_CHECK{$checkkey}; 71 | } 72 | 73 | my $sql_counter_reply = $sql_counter->counter_check($user,$check_hash); 74 | 75 | #print "======SQL Counter Reply=======\n"; 76 | #print Dumper($sql_counter_reply); 77 | #print "==============================\n"; 78 | 79 | if(defined $sql_counter_reply){ 80 | 81 | foreach my $key (keys %{$sql_counter_reply}){ 82 | 83 | $RAD_REPLY{$key} = $sql_counter_reply->{$key}; 84 | #If there was an error the 'Reply-Message' will have a value, if so return with a 0 85 | if($key eq 'Reply-Message'){ 86 | return RLM_MODULE_REJECT; 87 | } 88 | } 89 | } 90 | 91 | my $reply_hash; 92 | foreach my $replykey (keys %RAD_REPLY){ 93 | $reply_hash->{$replykey} = $RAD_REPLY{$replykey}; 94 | } 95 | 96 | my $expire_reply = $expirecheck->expire_check($user,$check_hash,$reply_hash); 97 | 98 | if(defined $expire_reply){ 99 | 100 | foreach my $key (keys %{$expire_reply}){ 101 | 102 | $RAD_REPLY{$key} = $expire_reply->{$key}; 103 | #If there was an error the 'Reply-Message' will have a value, if so return with a 0 104 | if($key eq 'Reply-Message'){ 105 | return RLM_MODULE_REJECT; 106 | } 107 | } 108 | } 109 | # TODO Only send UPDATED if we have actually changed things 110 | return RLM_MODULE_UPDATED; 111 | } 112 | 113 | # Function to handle authenticate 114 | sub authenticate { 115 | # For debugging purposes only 116 | # &log_request_attributes; 117 | 118 | #if ($RAD_REQUEST{'User-Name'} =~ /^baduser/i) { 119 | # # Reject user and tell him why 120 | # $RAD_REPLY{'Reply-Message'} = "Denied access by rlm_perl function"; 121 | # return RLM_MODULE_REJECT; 122 | #} else { 123 | # # Accept user and set some attribute 124 | # $RAD_REPLY{'h323-credit-amount'} = "100"; 125 | return RLM_MODULE_OK; 126 | #} 127 | } 128 | -------------------------------------------------------------------------------- /freeradius/3.0/perl_modules/sqlcounter.conf: -------------------------------------------------------------------------------- 1 | 2 | sqlcounter chillispot_max_bytes_noreset { 3 | counter-name = ChilliSpot-Max-Total-Octets 4 | check-name = Max-Octets 5 | reply-name = ChilliSpot-Max-Total-Octets 6 | reply-message = "You have reached your bandwidth limit" 7 | sqlmod-inst = sql 8 | key = User-Name 9 | reset = never 10 | query = "SELECT SUM(acctinputoctets) + SUM(acctoutputoctets) FROM radacct WHERE username='%{%k}'" 11 | 12 | } 13 | 14 | sqlcounter chillispot_max_bytes_hourly { 15 | counter-name = ChilliSpot-Max-Total-Octets 16 | check-name = Max-Hourly-Octets 17 | reply-name = ChilliSpot-Max-Total-Octets 18 | reply-message = "You have reached your hourly bandwidth limit" 19 | sqlmod-inst = sql 20 | key = User-Name 21 | reset = hourly 22 | query = "SELECT SUM(acctinputoctets - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 23 | } 24 | 25 | sqlcounter chillispot_max_bytes_daily { 26 | counter-name = ChilliSpot-Max-Total-Octets 27 | check-name = Max-Daily-Octets 28 | reply-name = ChilliSpot-Max-Total-Octets 29 | reply-message = "You have reached your daily bandwidth limit" 30 | sqlmod-inst = sql 31 | key = User-Name 32 | reset = daily 33 | query = "SELECT SUM(acctinputoctets - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 34 | } 35 | 36 | 37 | sqlcounter chillispot_max_bytes_weekly { 38 | counter-name = ChilliSpot-Max-Total-Octets 39 | check-name = Max-Weekly-Octets 40 | reply-name = ChilliSpot-Max-Total-Octets 41 | reply-message = "You have reached your weekly bandwidth limit" 42 | sqlmod-inst = sql 43 | key = User-Name 44 | reset = weekly 45 | query = "SELECT SUM(acctinputoctets - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 46 | } 47 | 48 | sqlcounter chillispot_max_bytes_monthly { 49 | counter-name = ChilliSpot-Max-Total-Octets 50 | check-name = Max-Monthly-Octets 51 | reply-name = ChilliSpot-Max-Total-Octets 52 | reply-message = "You have reached your monthly bandwidth limit" 53 | sqlmod-inst = sql 54 | key = User-Name 55 | reset = monthly 56 | query = "SELECT SUM(acctinputoctets - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 57 | } 58 | 59 | sqlcounter time_noreset { 60 | counter-name = Session-Timeout 61 | check-name = Max-All-Session 62 | reply-name = Session-Timeout 63 | reply-message = "You have reached your time limit" 64 | sqlmod-inst = sql 65 | key = User-Name 66 | reset = never 67 | query = "SELECT SUM(acctsessiontime) FROM radacct WHERE username='%{%k}'" 68 | } 69 | 70 | sqlcounter time_hourly { 71 | counter-name = Hourly-Session-Time 72 | check-name = Max-Hourly-Session 73 | reply-name = Session-Timeout 74 | reply-message = "You have reached your hourly time limit" 75 | sqlmod-inst = sql 76 | key = User-Name 77 | reset = hourly 78 | query = "SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username = '%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 79 | } 80 | 81 | sqlcounter time_daily { 82 | counter-name = Daily-Session-Time 83 | check-name = Max-Daily-Session 84 | reply-name = Session-Timeout 85 | reply-message = "You have reached your daily time limit" 86 | sqlmod-inst = sql 87 | key = User-Name 88 | reset = daily 89 | query = "SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username = '%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 90 | } 91 | 92 | sqlcounter time_weekly { 93 | counter-name = Weekly-Session-Time 94 | check-name = Max-Weekly-Session 95 | reply-name = Session-Timeout 96 | reply-message = "You have reached your weekly time limit" 97 | sqlmod-inst = sql 98 | key = User-Name 99 | reset = weekly 100 | query = "SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username = '%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 101 | } 102 | 103 | sqlcounter time_monthly { 104 | counter-name = Weekly-Session-Time 105 | check-name = Max-Monthly-Session 106 | reply-name = Session-Timeout 107 | reply-message = "You have reached your monthly time limit" 108 | sqlmod-inst = sql 109 | key = User-Name 110 | reset = monthly 111 | query = "SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username = '%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 112 | } 113 | 114 | sqlcounter yfi_max_bytes_monthly { 115 | counter-name = Yfi-Data 116 | check-name = Yfi-Data 117 | reply-name = Yfi-Data 118 | sqlmod-inst = sql 119 | key = User-Name 120 | reset = monthly 121 | query = "SELECT SUM(acctinputoctets - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0))+ SUM(acctoutputoctets -GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 122 | } 123 | 124 | sqlcounter yfi_max_time_monthly { 125 | counter-name = Yfi-Time 126 | check-name = Yfi-Time 127 | reply-name = Yfi-Time 128 | sqlmod-inst = sql 129 | key = User-Name 130 | reset =monthly 131 | query = "SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) FROM radacct WHERE username='%{%k}' AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'" 132 | } 133 | 134 | sqlcounter mikrotik_max_bytes_noreset { 135 | counter-name = Mikrotik-Total-Limit 136 | check-name = Mikrotik-Total-Limit 137 | reply-name = Mikrotik-Total-Limit 138 | sqlmod-inst = sql 139 | key = User-Name 140 | reset = never 141 | query = "SELECT SUM(acctinputoctets) + SUM(acctoutputoctets) FROM radacct WHERE username='%{%k}'" 142 | } 143 | -------------------------------------------------------------------------------- /freeradius/3.0/sites-available/default.grase: -------------------------------------------------------------------------------- 1 | ###################################################################### 2 | # 3 | # As of 2.0.0, FreeRADIUS supports virtual hosts using the 4 | # "server" section, and configuration directives. 5 | # 6 | # Virtual hosts should be put into the "sites-available" 7 | # directory. Soft links should be created in the "sites-enabled" 8 | # directory to these files. This is done in a normal installation. 9 | # 10 | # If you are using 802.1X (EAP) authentication, please see also 11 | # the "inner-tunnel" virtual server. You will likely have to edit 12 | # that, too, for authentication to work. 13 | # 14 | # $Id: cfb973a9a8fd3d83e8e30c0599ddb911a3bdde9b $ 15 | # 16 | ###################################################################### 17 | # 18 | # Read "man radiusd" before editing this file. See the section 19 | # titled DEBUGGING. It outlines a method where you can quickly 20 | # obtain the configuration you want, without running into 21 | # trouble. See also "man unlang", which documents the format 22 | # of this file. 23 | # 24 | # This configuration is designed to work in the widest possible 25 | # set of circumstances, with the widest possible number of 26 | # authentication methods. This means that in general, you should 27 | # need to make very few changes to this file. 28 | # 29 | # The best way to configure the server for your local system 30 | # is to CAREFULLY edit this file. Most attempts to make large 31 | # edits to this file will BREAK THE SERVER. Any edits should 32 | # be small, and tested by running the server with "radiusd -X". 33 | # Once the edits have been verified to work, save a copy of these 34 | # configuration files somewhere. (e.g. as a "tar" file). Then, 35 | # make more edits, and test, as above. 36 | # 37 | # There are many "commented out" references to modules such 38 | # as ldap, sql, etc. These references serve as place-holders. 39 | # If you need the functionality of that module, then configure 40 | # it in radiusd.conf, and un-comment the references to it in 41 | # this file. In most cases, those small changes will result 42 | # in the server being able to connect to the DB, and to 43 | # authenticate users. 44 | # 45 | ###################################################################### 46 | 47 | server default { 48 | # 49 | # If you want the server to listen on additional addresses, or on 50 | # additional ports, you can use multiple "listen" sections. 51 | # 52 | # Each section make the server listen for only one type of packet, 53 | # therefore authentication and accounting have to be configured in 54 | # different sections. 55 | # 56 | # The server ignore all "listen" section if you are using '-i' and '-p' 57 | # on the command line. 58 | # 59 | 60 | listen { 61 | # Type of packets to listen for. 62 | # Allowed values are: 63 | # auth listen for authentication packets 64 | # acct listen for accounting packets 65 | # proxy IP to use for sending proxied packets 66 | # detail Read from the detail file. For examples, see 67 | # raddb/sites-available/copy-acct-to-home-server 68 | # status listen for Status-Server packets. For examples, 69 | # see raddb/sites-available/status 70 | # coa listen for CoA-Request and Disconnect-Request 71 | # packets. For examples, see the file 72 | # raddb/sites-available/coa 73 | # 74 | 75 | type = auth 76 | 77 | # Note: "type = proxy" lets you control the source IP used for 78 | # proxying packets, with some limitations: 79 | # 80 | # * A proxy listener CANNOT be used in a virtual server section. 81 | # * You should probably set "port = 0". 82 | # * Any "clients" configuration will be ignored. 83 | # 84 | # See also proxy.conf, and the "src_ipaddr" configuration entry 85 | # in the sample "home_server" section. When you specify the 86 | # source IP address for packets sent to a home server, the 87 | # proxy listeners are automatically created. 88 | 89 | # ipaddr/ipv4addr/ipv6addr - IP address on which to listen. 90 | # If multiple ones are listed, only the first one will 91 | # be used, and the others will be ignored. 92 | # 93 | # The configuration options accept the following syntax: 94 | # 95 | # ipv4addr - IPv4 address (e.g.192.0.2.3) 96 | # - wildcard (i.e. *) 97 | # - hostname (radius.example.com) 98 | # Only the A record for the host name is used. 99 | # If there is no A record, an error is returned, 100 | # and the server fails to start. 101 | # 102 | # ipv6addr - IPv6 address (e.g. 2001:db8::1) 103 | # - wildcard (i.e. *) 104 | # - hostname (radius.example.com) 105 | # Only the AAAA record for the host name is used. 106 | # If there is no AAAA record, an error is returned, 107 | # and the server fails to start. 108 | # 109 | # ipaddr - IPv4 address as above 110 | # - IPv6 address as above 111 | # - wildcard (i.e. *), which means IPv4 wildcard. 112 | # - hostname 113 | # If there is only one A or AAAA record returned 114 | # for the host name, it is used. 115 | # If multiple A or AAAA records are returned 116 | # for the host name, only the first one is used. 117 | # If both A and AAAA records are returned 118 | # for the host name, only the A record is used. 119 | # 120 | # ipv4addr = * 121 | # ipv6addr = * 122 | ipaddr = * 123 | 124 | # Port on which to listen. 125 | # Allowed values are: 126 | # integer port number (1812) 127 | # 0 means "use /etc/services for the proper port" 128 | port = 0 129 | 130 | # Some systems support binding to an interface, in addition 131 | # to the IP address. This feature isn't strictly necessary, 132 | # but for sites with many IP addresses on one interface, 133 | # it's useful to say "listen on all addresses for eth0". 134 | # 135 | # If your system does not support this feature, you will 136 | # get an error if you try to use it. 137 | # 138 | # interface = eth0 139 | 140 | # Per-socket lists of clients. This is a very useful feature. 141 | # 142 | # The name here is a reference to a section elsewhere in 143 | # radiusd.conf, or clients.conf. Having the name as 144 | # a reference allows multiple sockets to use the same 145 | # set of clients. 146 | # 147 | # If this configuration is used, then the global list of clients 148 | # is IGNORED for this "listen" section. Take care configuring 149 | # this feature, to ensure you don't accidentally disable a 150 | # client you need. 151 | # 152 | # See clients.conf for the configuration of "per_socket_clients". 153 | # 154 | # clients = per_socket_clients 155 | 156 | # 157 | # Set the default UDP receive buffer size. In most cases, 158 | # the default values set by the kernel are fine. However, in 159 | # some cases the NASes will send large packets, and many of 160 | # them at a time. It is then possible to overflow the 161 | # buffer, causing the kernel to drop packets before they 162 | # reach FreeRADIUS. Increasing the size of the buffer will 163 | # avoid these packet drops. 164 | # 165 | # recv_buff = 65536 166 | 167 | # 168 | # Connection limiting for sockets with "proto = tcp". 169 | # 170 | # This section is ignored for other kinds of sockets. 171 | # 172 | limit { 173 | # 174 | # Limit the number of simultaneous TCP connections to the socket 175 | # 176 | # The default is 16. 177 | # Setting this to 0 means "no limit" 178 | max_connections = 16 179 | 180 | # The per-socket "max_requests" option does not exist. 181 | 182 | # 183 | # The lifetime, in seconds, of a TCP connection. After 184 | # this lifetime, the connection will be closed. 185 | # 186 | # Setting this to 0 means "forever". 187 | lifetime = 0 188 | 189 | # 190 | # The idle timeout, in seconds, of a TCP connection. 191 | # If no packets have been received over the connection for 192 | # this time, the connection will be closed. 193 | # 194 | # Setting this to 0 means "no timeout". 195 | # 196 | # We STRONGLY RECOMMEND that you set an idle timeout. 197 | # 198 | idle_timeout = 30 199 | } 200 | } 201 | 202 | # 203 | # This second "listen" section is for listening on the accounting 204 | # port, too. 205 | # 206 | 207 | listen { 208 | ipaddr = * 209 | # ipv6addr = :: 210 | port = 0 211 | type = acct 212 | # interface = eth0 213 | # clients = per_socket_clients 214 | 215 | limit { 216 | # The number of packets received can be rate limited via the 217 | # "max_pps" configuration item. When it is set, the server 218 | # tracks the total number of packets received in the previous 219 | # second. If the count is greater than "max_pps", then the 220 | # new packet is silently discarded. This helps the server 221 | # deal with overload situations. 222 | # 223 | # The packets/s counter is tracked in a sliding window. This 224 | # means that the pps calculation is done for the second 225 | # before the current packet was received. NOT for the current 226 | # wall-clock second, and NOT for the previous wall-clock second. 227 | # 228 | # Useful values are 0 (no limit), or 100 to 10000. 229 | # Values lower than 100 will likely cause the server to ignore 230 | # normal traffic. Few systems are capable of handling more than 231 | # 10K packets/s. 232 | # 233 | # It is most useful for accounting systems. Set it to 50% 234 | # more than the normal accounting load, and you can be sure that 235 | # the server will never get overloaded 236 | # 237 | # max_pps = 0 238 | 239 | # Only for "proto = tcp". These are ignored for "udp" sockets. 240 | # 241 | # idle_timeout = 0 242 | # lifetime = 0 243 | # max_connections = 0 244 | } 245 | } 246 | 247 | # IPv6 versions of the above - read their full config to understand options 248 | listen { 249 | type = auth 250 | ipv6addr = :: # any. ::1 == localhost 251 | port = 0 252 | # interface = eth0 253 | # clients = per_socket_clients 254 | limit { 255 | max_connections = 16 256 | lifetime = 0 257 | idle_timeout = 30 258 | } 259 | } 260 | 261 | listen { 262 | ipv6addr = :: 263 | port = 0 264 | type = acct 265 | # interface = eth0 266 | # clients = per_socket_clients 267 | 268 | limit { 269 | # max_pps = 0 270 | # idle_timeout = 0 271 | # lifetime = 0 272 | # max_connections = 0 273 | } 274 | } 275 | 276 | # Authorization. First preprocess (hints and huntgroups files), 277 | # then realms, and finally look in the "users" file. 278 | # 279 | # Any changes made here should also be made to the "inner-tunnel" 280 | # virtual server. 281 | # 282 | # The order of the realm modules will determine the order that 283 | # we try to find a matching realm. 284 | # 285 | # Make *sure* that 'preprocess' comes before any realm if you 286 | # need to setup hints for the remote radius server 287 | authorize { 288 | # 289 | # Take a User-Name, and perform some checks on it, for spaces and other 290 | # invalid characters. If the User-Name appears invalid, reject the 291 | # request. 292 | # 293 | # See policy.d/filter for the definition of the filter_username policy. 294 | # 295 | 296 | filter_username 297 | 298 | # 299 | # Some broken equipment sends passwords with embedded zeros. 300 | # i.e. the debug output will show 301 | # 302 | # User-Password = "password\000\000" 303 | # 304 | # This policy will fix it to just be "password". 305 | # 306 | # filter_password 307 | 308 | # 309 | # The preprocess module takes care of sanitizing some bizarre 310 | # attributes in the request, and turning them into attributes 311 | # which are more standard. 312 | # 313 | # It takes care of processing the 'raddb/mods-config/preprocess/hints' 314 | # and the 'raddb/mods-config/preprocess/huntgroups' files. 315 | preprocess 316 | 317 | # If you intend to use CUI and you require that the Operator-Name 318 | # be set for CUI generation and you want to generate CUI also 319 | # for your local clients then uncomment the operator-name 320 | # below and set the operator-name for your clients in clients.conf 321 | # operator-name 322 | 323 | # 324 | # If you want to generate CUI for some clients that do not 325 | # send proper CUI requests, then uncomment the 326 | # cui below and set "add_cui = yes" for these clients in clients.conf 327 | # cui 328 | 329 | # 330 | # If you want to have a log of authentication requests, 331 | # un-comment the following line. 332 | # auth_log 333 | 334 | # 335 | # The chap module will set 'Auth-Type := CHAP' if we are 336 | # handling a CHAP request and Auth-Type has not already been set 337 | chap 338 | 339 | # 340 | # If the users are logging in with an MS-CHAP-Challenge 341 | # attribute for authentication, the mschap module will find 342 | # the MS-CHAP-Challenge attribute, and add 'Auth-Type := MS-CHAP' 343 | # to the request, which will cause the server to then use 344 | # the mschap module for authentication. 345 | mschap 346 | 347 | # 348 | # If you have a Cisco SIP server authenticating against 349 | # FreeRADIUS, uncomment the following line, and the 'digest' 350 | # line in the 'authenticate' section. 351 | # digest 352 | 353 | # 354 | # The WiMAX specification says that the Calling-Station-Id 355 | # is 6 octets of the MAC. This definition conflicts with 356 | # RFC 3580, and all common RADIUS practices. Un-commenting 357 | # the "wimax" module here means that it will fix the 358 | # Calling-Station-Id attribute to the normal format as 359 | # specified in RFC 3580 Section 3.21 360 | # wimax 361 | 362 | # 363 | # Look for IPASS style 'realm/', and if not found, look for 364 | # '@realm', and decide whether or not to proxy, based on 365 | # that. 366 | # IPASS 367 | 368 | # 369 | # Look for realms in user@domain format 370 | suffix 371 | # ntdomain 372 | 373 | # 374 | # This module takes care of EAP-MD5, EAP-TLS, and EAP-LEAP 375 | # authentication. 376 | # 377 | # It also sets the EAP-Type attribute in the request 378 | # attribute list to the EAP type from the packet. 379 | # 380 | # The EAP module returns "ok" or "updated" if it is not yet ready 381 | # to authenticate the user. The configuration below checks for 382 | # "ok", and stops processing the "authorize" section if so. 383 | # 384 | # Any LDAP and/or SQL servers will not be queried for the 385 | # initial set of packets that go back and forth to set up 386 | # TTLS or PEAP. 387 | # 388 | # The "updated" check is commented out for compatibility with 389 | # previous versions of this configuration, but you may wish to 390 | # uncomment it as well; this will further reduce the number of 391 | # LDAP and/or SQL queries for TTLS or PEAP. 392 | # 393 | eap { 394 | ok = return 395 | # updated = return 396 | } 397 | 398 | # 399 | # Pull crypt'd passwords from /etc/passwd or /etc/shadow, 400 | # using the system API's to get the password. If you want 401 | # to read /etc/passwd or /etc/shadow directly, see the 402 | # mods-available/passwd module. 403 | # 404 | # unix 405 | 406 | # 407 | # Read the 'users' file. In v3, this is located in 408 | # raddb/mods-config/files/authorize 409 | # files 410 | 411 | # 412 | # Look in an SQL database. The schema of the database 413 | # is meant to mirror the "users" file. 414 | # 415 | # See "Authorization Queries" in mods-available/sql 416 | grasesql 417 | 418 | # 419 | # If you are using /etc/smbpasswd, and are also doing 420 | # mschap authentication, the un-comment this line, and 421 | # configure the 'smbpasswd' module. 422 | # smbpasswd 423 | 424 | # 425 | # The ldap module reads passwords from the LDAP database. 426 | #-ldap 427 | 428 | # 429 | # Enforce daily limits on time spent logged in. 430 | # daily 431 | 432 | # 433 | expiration{ 434 | userlock = 1 435 | } 436 | if(userlock){ 437 | update reply { 438 | Reply-Message := "Your account has expired" 439 | } 440 | reject 441 | } 442 | 443 | # Perl now handles our counters 444 | graseperl 445 | 446 | logintime { 447 | userlock = 1 448 | } 449 | if(userlock){ 450 | update reply { 451 | Reply-Message := "Your are not allowed to login at this time" 452 | } 453 | reject 454 | } 455 | 456 | # 457 | # If no other module has claimed responsibility for 458 | # authentication, then try to use PAP. This allows the 459 | # other modules listed above to add a "known good" password 460 | # to the request, and to do nothing else. The PAP module 461 | # will then see that password, and use it to do PAP 462 | # authentication. 463 | # 464 | # This module should be listed last, so that the other modules 465 | # get a chance to set Auth-Type for themselves. 466 | # 467 | 468 | pap 469 | 470 | # 471 | # If "status_server = yes", then Status-Server messages are passed 472 | # through the following section, and ONLY the following section. 473 | # This permits you to do DB queries, for example. If the modules 474 | # listed here return "fail", then NO response is sent. 475 | # 476 | # Autz-Type Status-Server { 477 | # 478 | # } 479 | } 480 | 481 | 482 | # Authentication. 483 | # 484 | # 485 | # This section lists which modules are available for authentication. 486 | # Note that it does NOT mean 'try each module in order'. It means 487 | # that a module from the 'authorize' section adds a configuration 488 | # attribute 'Auth-Type := FOO'. That authentication type is then 489 | # used to pick the appropriate module from the list below. 490 | # 491 | 492 | # In general, you SHOULD NOT set the Auth-Type attribute. The server 493 | # will figure it out on its own, and will do the right thing. The 494 | # most common side effect of erroneously setting the Auth-Type 495 | # attribute is that one authentication method will work, but the 496 | # others will not. 497 | # 498 | # The common reasons to set the Auth-Type attribute by hand 499 | # is to either forcibly reject the user (Auth-Type := Reject), 500 | # or to or forcibly accept the user (Auth-Type := Accept). 501 | # 502 | # Note that Auth-Type := Accept will NOT work with EAP. 503 | # 504 | # Please do not put "unlang" configurations into the "authenticate" 505 | # section. Put them in the "post-auth" section instead. That's what 506 | # the post-auth section is for. 507 | # 508 | 509 | authenticate { 510 | # 511 | # PAP authentication, when a back-end database listed 512 | # in the 'authorize' section supplies a password. The 513 | # password can be clear-text, or encrypted. 514 | # 515 | # We need PAP in Grase for the CoovaChilli user to login 516 | Auth-Type PAP { 517 | pap 518 | } 519 | 520 | # 521 | # Most people want CHAP authentication 522 | # A back-end database listed in the 'authorize' section 523 | # MUST supply a CLEAR TEXT password. Encrypted passwords 524 | # won't work. 525 | Auth-Type CHAP { 526 | chap 527 | } 528 | 529 | # 530 | # MSCHAP authentication. 531 | Auth-Type MS-CHAP { 532 | mschap 533 | } 534 | 535 | # 536 | # For old names, too. 537 | # 538 | 539 | mschap 540 | 541 | # 542 | # If you have a Cisco SIP server authenticating against 543 | # FreeRADIUS, uncomment the following line, and the 'digest' 544 | # line in the 'authorize' section. 545 | # digest 546 | 547 | # 548 | # Pluggable Authentication Modules. 549 | # pam 550 | 551 | # Uncomment it if you want to use ldap for authentication 552 | # 553 | # Note that this means "check plain-text password against 554 | # the ldap database", which means that EAP won't work, 555 | # as it does not supply a plain-text password. 556 | # 557 | # We do NOT recommend using this. LDAP servers are databases. 558 | # They are NOT authentication servers. FreeRADIUS is an 559 | # authentication server, and knows what to do with authentication. 560 | # LDAP servers do not. 561 | # 562 | # Auth-Type LDAP { 563 | # ldap 564 | # } 565 | 566 | # 567 | # Allow EAP authentication. 568 | #eap 569 | 570 | # 571 | # The older configurations sent a number of attributes in 572 | # Access-Challenge packets, which wasn't strictly correct. 573 | # If you want to filter out these attributes, uncomment 574 | # the following lines. 575 | # 576 | # Auth-Type eap { 577 | # eap { 578 | # handled = 1 579 | # } 580 | # if (handled && (Response-Packet-Type == Access-Challenge)) { 581 | # attr_filter.access_challenge.post-auth 582 | # handled # override the "updated" code from attr_filter 583 | # } 584 | # } 585 | } 586 | 587 | 588 | # 589 | # Pre-accounting. Decide which accounting type to use. 590 | # 591 | 592 | preacct { 593 | preprocess 594 | 595 | # 596 | # Merge Acct-[Input|Output]-Gigawords and Acct-[Input-Output]-Octets 597 | # into a single 64bit counter Acct-[Input|Output]-Octets64. 598 | # 599 | # acct_counters64 600 | 601 | # 602 | # Session start times are *implied* in RADIUS. 603 | # The NAS never sends a "start time". Instead, it sends 604 | # a start packet, *possibly* with an Acct-Delay-Time. 605 | # The server is supposed to conclude that the start time 606 | # was "Acct-Delay-Time" seconds in the past. 607 | # 608 | # The code below creates an explicit start time, which can 609 | # then be used in other modules. It will be *mostly* correct. 610 | # Any errors are due to the 1-second resolution of RADIUS, 611 | # and the possibility that the time on the NAS may be off. 612 | # 613 | # The start time is: NOW - delay - session_length 614 | # 615 | 616 | # update request { 617 | # &FreeRADIUS-Acct-Session-Start-Time = "%{expr: %l - %{%{Acct-Session-Time}:-0} - %{%{Acct-Delay-Time}:-0}}" 618 | # } 619 | 620 | 621 | # 622 | # Ensure that we have a semi-unique identifier for every 623 | # request, and many NAS boxes are broken. 624 | acct_unique 625 | 626 | # 627 | # Look for IPASS-style 'realm/', and if not found, look for 628 | # '@realm', and decide whether or not to proxy, based on 629 | # that. 630 | # 631 | # Accounting requests are generally proxied to the same 632 | # home server as authentication requests. 633 | # IPASS 634 | suffix 635 | # ntdomain 636 | 637 | # 638 | # Read the 'acct_users' file 639 | # files 640 | } 641 | 642 | # 643 | # Accounting. Log the accounting data. 644 | # 645 | 646 | accounting { 647 | # Update accounting packet by adding the CUI attribute 648 | # recorded from the corresponding Access-Accept 649 | # use it only if your NAS boxes do not support CUI themselves 650 | # cui 651 | # 652 | # Create a 'detail'ed log of the packets. 653 | # Note that accounting requests which are proxied 654 | # are also logged in the detail file. 655 | # detail 656 | # daily 657 | 658 | # Update the wtmp file 659 | # 660 | # If you don't use "radlast", you can delete this line. 661 | # unix 662 | 663 | # 664 | # For Simultaneous-Use tracking. 665 | # 666 | # Due to packet losses in the network, the data here 667 | # may be incorrect. There is little we can do about it. 668 | # radutmp 669 | # sradutmp 670 | 671 | # Return an address to the IP Pool when we see a stop record. 672 | # main_pool 673 | 674 | # 675 | # Log traffic to an SQL database. 676 | # 677 | # See "Accounting queries" in mods-available/sql 678 | grasesql 679 | 680 | # 681 | # If you receive stop packets with zero session length, 682 | # they will NOT be logged in the database. The SQL module 683 | # will print a message (only in debugging mode), and will 684 | # return "noop". 685 | # 686 | # You can ignore these packets by uncommenting the following 687 | # three lines. Otherwise, the server will not respond to the 688 | # accounting request, and the NAS will retransmit. 689 | # 690 | # if (noop) { 691 | # ok 692 | # } 693 | 694 | # Cisco VoIP specific bulk accounting 695 | # pgsql-voip 696 | 697 | # For Exec-Program and Exec-Program-Wait 698 | exec 699 | 700 | # Filter attributes from the accounting response. 701 | attr_filter.accounting_response 702 | 703 | # 704 | # See "Autz-Type Status-Server" for how this works. 705 | # 706 | # Acct-Type Status-Server { 707 | # 708 | # } 709 | } 710 | 711 | 712 | # Session database, used for checking Simultaneous-Use. Either the radutmp 713 | # or rlm_sql module can handle this. 714 | # The rlm_sql module is *much* faster 715 | session { 716 | # radutmp 717 | 718 | # 719 | # See "Simultaneous Use Checking Queries" in mods-available/sql 720 | # sql 721 | } 722 | 723 | 724 | # Post-Authentication 725 | # Once we KNOW that the user has been authenticated, there are 726 | # additional steps we can take. 727 | post-auth { 728 | # 729 | # If you need to have a State attribute, you can 730 | # add it here. e.g. for later CoA-Request with 731 | # State, and Service-Type = Authorize-Only. 732 | # 733 | # if (!&reply:State) { 734 | # update reply { 735 | # State := "0x%{randstr:16h}" 736 | # } 737 | # } 738 | 739 | # 740 | # For EAP-TTLS and PEAP, add the cached attributes to the reply. 741 | # The "session-state" attributes are automatically cached when 742 | # an Access-Challenge is sent, and automatically retrieved 743 | # when an Access-Request is received. 744 | # 745 | # The session-state attributes are automatically deleted after 746 | # an Access-Reject or Access-Accept is sent. 747 | # 748 | # If both session-state and reply contain a User-Name attribute, remove 749 | # the one in the reply if it is just a copy of the one in the request, so 750 | # we don't end up with two User-Name attributes. 751 | 752 | if (session-state:User-Name && reply:User-Name && request:User-Name && (reply:User-Name == request:User-Name)) { 753 | update reply { 754 | &User-Name !* ANY 755 | } 756 | } 757 | update { 758 | &reply: += &session-state: 759 | } 760 | 761 | # Get an address from the IP Pool. 762 | # main_pool 763 | 764 | 765 | # Create the CUI value and add the attribute to Access-Accept. 766 | # Uncomment the line below if *returning* the CUI. 767 | # cui 768 | 769 | # Create empty accounting session to make simultaneous check 770 | # more robust. See the accounting queries configuration in 771 | # raddb/mods-config/sql/main/*/queries.conf for details. 772 | # 773 | # The "sql_session_start" policy is defined in 774 | # raddb/policy.d/accounting. See that file for more details. 775 | # sql_session_start 776 | 777 | # 778 | # If you want to have a log of authentication replies, 779 | # un-comment the following line, and enable the 780 | # 'detail reply_log' module. 781 | # reply_log 782 | 783 | # 784 | # After authenticating the user, do another SQL query. 785 | # 786 | # See "Authentication Logging Queries" in mods-available/sql 787 | grasesql 788 | 789 | # 790 | # Un-comment the following if you want to modify the user's object 791 | # in LDAP after a successful login. 792 | # 793 | # ldap 794 | 795 | # For Exec-Program and Exec-Program-Wait 796 | exec 797 | 798 | # 799 | # Calculate the various WiMAX keys. In order for this to work, 800 | # you will need to define the WiMAX NAI, usually via 801 | # 802 | # update request { 803 | # WiMAX-MN-NAI = "%{User-Name}" 804 | # } 805 | # 806 | # If you want various keys to be calculated, you will need to 807 | # update the reply with "template" values. The module will see 808 | # this, and replace the template values with the correct ones 809 | # taken from the cryptographic calculations. e.g. 810 | # 811 | # update reply { 812 | # WiMAX-FA-RK-Key = 0x00 813 | # WiMAX-MSK = "%{EAP-MSK}" 814 | # } 815 | # 816 | # You may want to delete the MS-MPPE-*-Keys from the reply, 817 | # as some WiMAX clients behave badly when those attributes 818 | # are included. See "raddb/modules/wimax", configuration 819 | # entry "delete_mppe_keys" for more information. 820 | # 821 | # wimax 822 | 823 | 824 | # If there is a client certificate (EAP-TLS, sometimes PEAP 825 | # and TTLS), then some attributes are filled out after the 826 | # certificate verification has been performed. These fields 827 | # MAY be available during the authentication, or they may be 828 | # available only in the "post-auth" section. 829 | # 830 | # The first set of attributes contains information about the 831 | # issuing certificate which is being used. The second 832 | # contains information about the client certificate (if 833 | # available). 834 | # 835 | # update reply { 836 | # Reply-Message += "%{TLS-Cert-Serial}" 837 | # Reply-Message += "%{TLS-Cert-Expiration}" 838 | # Reply-Message += "%{TLS-Cert-Subject}" 839 | # Reply-Message += "%{TLS-Cert-Issuer}" 840 | # Reply-Message += "%{TLS-Cert-Common-Name}" 841 | # Reply-Message += "%{TLS-Cert-Subject-Alt-Name-Email}" 842 | # 843 | # Reply-Message += "%{TLS-Client-Cert-Serial}" 844 | # Reply-Message += "%{TLS-Client-Cert-Expiration}" 845 | # Reply-Message += "%{TLS-Client-Cert-Subject}" 846 | # Reply-Message += "%{TLS-Client-Cert-Issuer}" 847 | # Reply-Message += "%{TLS-Client-Cert-Common-Name}" 848 | # Reply-Message += "%{TLS-Client-Cert-Subject-Alt-Name-Email}" 849 | # } 850 | 851 | # Insert class attribute (with unique value) into response, 852 | # aids matching auth and acct records, and protects against duplicate 853 | # Acct-Session-Id. Note: Only works if the NAS has implemented 854 | # RFC 2865 behaviour for the class attribute, AND if the NAS 855 | # supports long Class attributes. Many older or cheap NASes 856 | # only support 16-octet Class attributes. 857 | # insert_acct_class 858 | 859 | # MacSEC requires the use of EAP-Key-Name. However, we don't 860 | # want to send it for all EAP sessions. Therefore, the EAP 861 | # modules put required data into the EAP-Session-Id attribute. 862 | # This attribute is never put into a request or reply packet. 863 | # 864 | # Uncomment the next few lines to copy the required data into 865 | # the EAP-Key-Name attribute 866 | # if (&reply:EAP-Session-Id) { 867 | # update reply { 868 | # EAP-Key-Name := &reply:EAP-Session-Id 869 | # } 870 | # } 871 | 872 | # Remove reply message if the response contains an EAP-Message 873 | remove_reply_message_if_eap 874 | 875 | # 876 | # Access-Reject packets are sent through the REJECT sub-section of the 877 | # post-auth section. 878 | # 879 | # Add the ldap module name (or instance) if you have set 880 | # 'edir_account_policy_check = yes' in the ldap module configuration 881 | # 882 | # The "session-state" attributes are not available here. 883 | # 884 | Post-Auth-Type REJECT { 885 | # log failed authentications in SQL, too. 886 | grasesql 887 | if ( &reply:Reply-Message == "\r\nYou are already logged in - access denied\r\n\n" ) { 888 | update reply { 889 | Reply-Message := "You are already logged in at another location. Please try again in 5 minutes" 890 | } 891 | } 892 | 893 | update reply { 894 | # Fallback error message 895 | Reply-Message = "Login Failed. Please check your username and password" 896 | } 897 | attr_filter.access_reject 898 | 899 | # Insert EAP-Failure message if the request was 900 | # rejected by policy instead of because of an 901 | # authentication failure 902 | eap 903 | 904 | # Remove reply message if the response contains an EAP-Message 905 | remove_reply_message_if_eap 906 | } 907 | 908 | # 909 | # Filter access challenges. 910 | # 911 | 912 | Post-Auth-Type Challenge { 913 | # remove_reply_message_if_eap 914 | # attr_filter.access_challenge.post-auth 915 | } 916 | 917 | } 918 | 919 | # 920 | # When the server decides to proxy a request to a home server, 921 | # the proxied request is first passed through the pre-proxy 922 | # stage. This stage can re-write the request, or decide to 923 | # cancel the proxy. 924 | # 925 | # Only a few modules currently have this method. 926 | # 927 | 928 | pre-proxy { 929 | # Before proxing the request add an Operator-Name attribute identifying 930 | # if the operator-name is found for this client. 931 | # No need to uncomment this if you have already enabled this in 932 | # the authorize section. 933 | # operator-name 934 | 935 | # The client requests the CUI by sending a CUI attribute 936 | # containing one zero byte. 937 | # Uncomment the line below if *requesting* the CUI. 938 | # cui 939 | 940 | # Uncomment the following line if you want to change attributes 941 | # as defined in the preproxy_users file. 942 | # files 943 | 944 | # Uncomment the following line if you want to filter requests 945 | # sent to remote servers based on the rules defined in the 946 | # 'attrs.pre-proxy' file. 947 | # attr_filter.pre-proxy 948 | 949 | # If you want to have a log of packets proxied to a home 950 | # server, un-comment the following line, and the 951 | # 'detail pre_proxy_log' section, above. 952 | # pre_proxy_log 953 | } 954 | 955 | # 956 | # When the server receives a reply to a request it proxied 957 | # to a home server, the request may be massaged here, in the 958 | # post-proxy stage. 959 | # 960 | 961 | post-proxy { 962 | 963 | # If you want to have a log of replies from a home server, 964 | # un-comment the following line, and the 'detail post_proxy_log' 965 | # section, above. 966 | # post_proxy_log 967 | 968 | # Uncomment the following line if you want to filter replies from 969 | # remote proxies based on the rules defined in the 'attrs' file. 970 | # attr_filter.post-proxy 971 | 972 | # 973 | # If you are proxying LEAP, you MUST configure the EAP 974 | # module, and you MUST list it here, in the post-proxy 975 | # stage. 976 | # 977 | # You MUST also use the 'nostrip' option in the 'realm' 978 | # configuration. Otherwise, the User-Name attribute 979 | # in the proxied request will not match the user name 980 | # hidden inside of the EAP packet, and the end server will 981 | # reject the EAP request. 982 | # 983 | 984 | eap 985 | 986 | # 987 | # If the server tries to proxy a request and fails, then the 988 | # request is processed through the modules in this section. 989 | # 990 | # The main use of this section is to permit robust proxying 991 | # of accounting packets. The server can be configured to 992 | # proxy accounting packets as part of normal processing. 993 | # Then, if the home server goes down, accounting packets can 994 | # be logged to a local "detail" file, for processing with 995 | # radrelay. When the home server comes back up, radrelay 996 | # will read the detail file, and send the packets to the 997 | # home server. 998 | # 999 | # With this configuration, the server always responds to 1000 | # Accounting-Requests from the NAS, but only writes 1001 | # accounting packets to disk if the home server is down. 1002 | # 1003 | # Post-Proxy-Type Fail-Accounting { 1004 | # detail 1005 | # } 1006 | } 1007 | } 1008 | -------------------------------------------------------------------------------- /templates/radius.conf: -------------------------------------------------------------------------------- 1 | sql_type: _DBC_DBTYPE_ 2 | sql_server: _DBC_DBSERVER_ 3 | sql_username: _DBC_DBUSER_ 4 | sql_password: _DBC_DBPASS_ 5 | sql_database: _DBC_DBNAME_ 6 | sql_command: /usr/bin/mysql 7 | -------------------------------------------------------------------------------- /templates/sql.conf.grase: -------------------------------------------------------------------------------- 1 | # -*- text -*- 2 | ## 3 | ## sql.conf -- SQL modules 4 | ## 5 | ## $Id: 4d2442aaec9ce2b64c5fb2f78fc030e4fc8e8a48 $ 6 | 7 | ###################################################################### 8 | # 9 | # Configuration for the SQL module 10 | # 11 | # The database schemas and queries are located in subdirectories: 12 | # 13 | # sql//main/schema.sql Schema 14 | # sql//main/queries.conf Authorisation and Accounting queries 15 | # 16 | # Where "DB" is mysql, mssql, oracle, or postgresql. 17 | # 18 | # 19 | 20 | sql grasesql { 21 | # 22 | # The dialect of SQL being used. 23 | # 24 | # Allowed dialects are: 25 | # 26 | # mssql 27 | # mysql 28 | # oracle 29 | # postgresql 30 | # sqlite 31 | # 32 | dialect = "_DBC_DBTYPE_" 33 | 34 | # 35 | # Which FreeRADIUS driver to use. 36 | # 37 | driver = "rlm_sql_${dialect}" 38 | 39 | mysql { 40 | # If yes, (or auto and libmysqlclient reports warnings are 41 | # available), will retrieve and log additional warnings from 42 | # the server if an error has occured. Defaults to 'auto' 43 | warnings = auto 44 | } 45 | 46 | # Connection info: 47 | server = "_DBC_DBSERVER_" 48 | #port = 3306 49 | login = "_DBC_DBUSER_" 50 | password = "_DBC_DBPASS_" 51 | 52 | # Database table configuration for everything except Oracle 53 | radius_db = "_DBC_DBNAME_" 54 | 55 | # If you want both stop and start records logged to the 56 | # same SQL table, leave this as is. If you want them in 57 | # different tables, put the start table in acct_table1 58 | # and stop table in acct_table2 59 | acct_table1 = "radacct" 60 | acct_table2 = "radacct" 61 | 62 | # Allow for storing data after authentication 63 | postauth_table = "radpostauth" 64 | 65 | # Tables containing 'check' items 66 | authcheck_table = "radcheck" 67 | 68 | groupcheck_table = "radgroupcheck" 69 | # Tables containing 'reply' items 70 | authreply_table = "radreply" 71 | groupreply_table = "radgroupreply" 72 | 73 | # Table to keep group info 74 | usergroup_table = "radusergroup" 75 | 76 | # If set to 'yes' (default) we read the group tables unless Fall-Through = no in the reply table. 77 | # If set to 'no' we do not read the group tables unless Fall-Through = yes in the reply table. 78 | # read_groups = yes 79 | 80 | # If set to 'yes' (default) we read profiles unless Fall-Through = no in the groupreply table. 81 | # If set to 'no' we do not read profiles unless Fall-Through = yes in the groupreply table. 82 | # read_profiles = yes 83 | 84 | # Remove stale session if checkrad does not see a double login 85 | delete_stale_sessions = yes 86 | 87 | # Write SQL queries to a logfile. This is potentially useful for tracing 88 | # issues with authorization queries. See also "logfile" directives in 89 | # mods-config/sql/main/*/queries.conf. You can enable per-section logging 90 | # by enabling "logfile" there, or global logging by enabling "logfile" here. 91 | # 92 | # Per-section logging can be disabled by setting "logfile = ''" 93 | #logfile = ${logdir}/sqllog.sql 94 | 95 | # Set the maximum query duration and connection timeout 96 | # for rlm_sql_mysql. 97 | #query_timeout = 5 98 | 99 | # As of version 3.0, the "pool" section has replaced the 100 | # following configuration items: 101 | # 102 | # num_sql_socks 103 | # connect_failure_retry_delay 104 | # lifetime 105 | # max_queries 106 | 107 | # 108 | # The connection pool is new for 3.0, and will be used in many 109 | # modules, for all kinds of connection-related activity. 110 | # 111 | # When the server is not threaded, the connection pool 112 | # limits are ignored, and only one connection is used. 113 | # 114 | # If you want to have multiple SQL modules re-use the same 115 | # connection pool, use "pool = name" instead of a "pool" 116 | # section. e.g. 117 | # 118 | # sql1 { 119 | # ... 120 | # pool { 121 | # ... 122 | # } 123 | # } 124 | # 125 | # # sql2 will use the connection pool from sql1 126 | # sql2 { 127 | # ... 128 | # pool = sql1 129 | # } 130 | # 131 | pool { 132 | # Connections to create during module instantiation. 133 | # If the server cannot create specified number of 134 | # connections during instantiation it will exit. 135 | # Set to 0 to allow the server to start without the 136 | # database being available. 137 | start = ${thread[pool].start_servers} 138 | 139 | # Minimum number of connections to keep open 140 | min = ${thread[pool].min_spare_servers} 141 | 142 | # Maximum number of connections 143 | # 144 | # If these connections are all in use and a new one 145 | # is requested, the request will NOT get a connection. 146 | # 147 | # Setting 'max' to LESS than the number of threads means 148 | # that some threads may starve, and you will see errors 149 | # like 'No connections available and at max connection limit' 150 | # 151 | # Setting 'max' to MORE than the number of threads means 152 | # that there are more connections than necessary. 153 | max = ${thread[pool].max_servers} 154 | 155 | # Spare connections to be left idle 156 | # 157 | # NOTE: Idle connections WILL be closed if "idle_timeout" 158 | # is set. This should be less than or equal to "max" above. 159 | spare = ${thread[pool].max_spare_servers} 160 | 161 | # Number of uses before the connection is closed 162 | # 163 | # 0 means "infinite" 164 | uses = 0 165 | 166 | # The number of seconds to wait after the server tries 167 | # to open a connection, and fails. During this time, 168 | # no new connections will be opened. 169 | retry_delay = 30 170 | 171 | # The lifetime (in seconds) of the connection 172 | lifetime = 0 173 | 174 | # idle timeout (in seconds). A connection which is 175 | # unused for this length of time will be closed. 176 | idle_timeout = 60 177 | 178 | # NOTE: All configuration settings are enforced. If a 179 | # connection is closed because of "idle_timeout", 180 | # "uses", or "lifetime", then the total number of 181 | # connections MAY fall below "min". When that 182 | # happens, it will open a new connection. It will 183 | # also log a WARNING message. 184 | # 185 | # The solution is to either lower the "min" connections, 186 | # or increase lifetime/idle_timeout. 187 | } 188 | 189 | # Set to 'yes' to read radius clients from the database ('nas' table) 190 | # Clients will ONLY be read on server startup. 191 | #read_clients = yes 192 | 193 | # Table to keep radius client info 194 | client_table = "nas" 195 | 196 | # 197 | # The group attribute specific to this instance of rlm_sql 198 | # 199 | 200 | # This entry should be used for additional instances (sql foo {}) 201 | # of the SQL module. 202 | group_attribute = "${.:instance}-SQL-Group" 203 | 204 | # This entry should be used for the default instance (sql {}) 205 | # of the SQL module. 206 | #group_attribute = "SQL-Group" 207 | 208 | # GRASE Hotspot overrides 209 | default_user_profile = "DEFAULT_GRASE" 210 | 211 | ## Read GRASE postauth before loading queriesconf as first loaded overrides 212 | post-auth { 213 | # Write SQL queries to a logfile. This is potentially useful for bulk inserts 214 | # when used with the rlm_sql_null driver. 215 | #logfile = ${logdir}/post-auth.sql 216 | 217 | query = "\ 218 | INSERT INTO ${..postauth_table} \ 219 | (username, pass, reply, authdate, ServiceType, FramedIPAddress, CallingStationId) \ 220 | VALUES ( \ 221 | '%{SQL-User-Name}', \ 222 | '%{%{User-Password}:-%{Chap-Password}}', \ 223 | '%{reply:Packet-Type}', \ 224 | '%S', \ 225 | '%{Service-Type}', '%{Framed-IP-Address}', '%{Calling-Station-Id}')" 226 | } 227 | 228 | # Read database-specific queries 229 | $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf 230 | } 231 | --------------------------------------------------------------------------------