yardradius-1.1.2/0000777000175000017500000000000010640702676010677 500000000000000yardradius-1.1.2/doc/0000777000175000017500000000000010640702676011444 500000000000000yardradius-1.1.2/doc/txt/0000777000175000017500000000000010640702676012263 500000000000000yardradius-1.1.2/doc/txt/Makefile.am0000644000175000017500000000013710640412017014217 00000000000000AUTOMAKE_OPTIONS = foreign INSTALL = @INSTALL@ txtdir = $(YARDDOC)/txt txt_DATA = PAM-INFO yardradius-1.1.2/doc/txt/Makefile.in0000644000175000017500000002247710640702510014243 00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = ../.. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = doc/txt DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/autotools/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; am__installdirs = "$(DESTDIR)$(txtdir)" txtDATA_INSTALL = $(INSTALL_DATA) DATA = $(txt_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) INSTALL = @INSTALL@ ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_VERSION = @CC_VERSION@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ MKDIR = @MKDIR@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ YARDCONF = @YARDCONF@ YARDDOC = @YARDDOC@ YARDLOG = @YARDLOG@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ AUTOMAKE_OPTIONS = foreign txtdir = $(YARDDOC)/txt txt_DATA = PAM-INFO all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/txt/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/txt/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh uninstall-info-am: install-txtDATA: $(txt_DATA) @$(NORMAL_INSTALL) test -z "$(txtdir)" || $(mkdir_p) "$(DESTDIR)$(txtdir)" @list='$(txt_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(txtDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(txtdir)/$$f'"; \ $(txtDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(txtdir)/$$f"; \ done uninstall-txtDATA: @$(NORMAL_UNINSTALL) @list='$(txt_DATA)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(txtdir)/$$f'"; \ rm -f "$(DESTDIR)$(txtdir)/$$f"; \ done tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(txtdir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-txtDATA install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-info-am uninstall-txtDATA .PHONY: all all-am check check-am clean clean-generic distclean \ distclean-generic distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-exec \ install-exec-am install-info install-info-am install-man \ install-strip install-txtDATA installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ uninstall-am uninstall-info-am uninstall-txtDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: yardradius-1.1.2/doc/txt/PAM-INFO0000644000175000017500000001106010640412017013251 00000000000000-------------------------------------- PAM-ified RADIUS -------------------------------------- Written by Jeff Blaize All credit goes to him. Slightly modified by Cristian Gafton. At this point with the new PAMified radiusd installed, it should operate just as it always has. However, I've added a two more items to the dictionary that make it all worth our while. Here are some examples for the /etc/raddb/users file: DEFAULT Auth-Type = PAM, Framed-Protocol = PPP, Service-Type = Framed-User, Framed-Protocol = PPP, Framed-IP-Address = 255.255.255.254, Framed-MTU = 1500, Framed-Compression = Van-Jacobson-TCP-IP, DEFAULT Auth-Type = PAM, Menu = "menu1" This allows you to specify Auth-Type of "PAM" (instead of "System") which then by default uses the "radius" entry of the pam.conf for PAM authentication. An /etc/pam.d/radius file for this to check the /etc/x2users file may look like this: ---- /etc/pam.d/radius ---- # radius stuff auth required /lib/security/pam_unix_auth.so auth required /lib/security/pam_listfile.so \ item=user sense=allow file=/etc/x2users onerr=fail account required /lib/security/pam_unix_acct.so You may not want to bother with the last line for accounting since RADIUS does a wonderful job of accounting on its own. At least that's what I'm assuming it does, but I've never bothered to look into it, so I couldn't tell you for sure. Now let's say you want multiple PAM entries for different users. Then you can do something like this in your /etc/raddb/users file: Joe Auth-Type = PAM, Framed-Protocol = PPP Service-Type = Framed-User, Framed-Protocol = PPP, Framed-IP-Address = 255.255.255.254, Framed-MTU = 1500, Framed-Compression = Van-Jacobson-TCP-IP, DEFAULT Auth-Type = PAM, Framed-Protocol = PPP, PAM-Auth="x2" Service-Type = Framed-User, Framed-Protocol = PPP, Framed-IP-Address = 255.255.255.254, Framed-MTU = 1500, Framed-Compression = Van-Jacobson-TCP-IP, DEFAULT Auth-Type = PAM, PAM-Auth="mradius" Menu = "menu1" With something like this in the /etc/pam.d/ directory: ---- /etc/pam.d/radius ---- auth required /lib/security/pam_unix_auth.so account required /lib/security/pam_unix_acct.so ---- /etc/pam.d/x2 ---- auth required /lib/security/pam_unix_auth.so auth required /lib/security/pam_listfile.so \ item=user sense=allow file=/etc/x2users onerr=fail account required /lib/security/pam_unix_acct.so ---- /etc/pam.d/mradius ---- auth required /lib/security/pam_unix_auth.so auth required /lib/security/pam_nologin.so account required /lib/security/pam_unix_acct.so This would allow user Joe to login using /etc/passwd or shadow. All other PPP users would authenticate via PAP or CHAP by /etc/passwd or shadow, but only if they're in the /etc/x2users file. Then all non PPP users will get the menu, but not if /etc/nologin exists. Of course, you could just as easily have Joe's entry with Auth-Type=System, but then you wouldn't be using your new toy, see. --------------------------------------------- PAM module support for session logging --------------------------------------------- Information for module writers only: This PAMified version of radiusd supports the PAM session modules. A module writer willing to use this will have to be aware, however, of the non-standard way of passing the information contained in the acct network packet from radiusd to the module. The value of the authreq->request is passed to the module in the appdata_ptr pointer of the conversation structure. Thus, a module willing to do session logging for this version of radiusd will have to start from the provided source for the dummy pam_radius_session module. Check that out, the source is self explanatory. Of course, radiusd will call pam_open_session each time an acct START packets come in, and will call pam_close_session each time the STOP packet is seen. Having this model you should be able to write your module for logging into a SQL database or whatever you want, and list that module in the session section of the PAM configuration file of the "radius" application (this radiusd will call only the session modules for the "radius" application, don't get foold by PAM-Auth extension described above): ---- /etc/pam.d/radius ---- session required /some/path/pam_radius_session.so ---- ---- ---------------------------------------------------------------------------- Please send your comments to gafton@sorosis.ro yardradius-1.1.2/doc/README0000644000175000017500000000145110640415635012235 00000000000000 Yet Another Radius Daemon (YARD) Documentation ---------------------------------------------- This directory contains documentation of the Radius server. Currently the most of documentation is contained in man pages. YardRadius used to contain a group of RFCs which are 'the bible' of the RADIUS authorization and accounting protocols. They can be consulted on the net (e.g. on ietf.org). Relevant RFCs are http://www.ietf.org/rfc/rfc1157.txt http://www.ietf.org/rfc/rfc2618.txt http://www.ietf.org/rfc/rfc2619.txt http://www.ietf.org/rfc/rfc2620.txt http://www.ietf.org/rfc/rfc2621.txt http://www.ietf.org/rfc/rfc2865.txt http://www.ietf.org/rfc/rfc2866.txt http://www.ietf.org/rfc/rfc2867.txt http://www.ietf.org/rfc/rfc2868.txt http://www.ietf.org/rfc/rfc2869.txt http://www.ietf.org/rfc/rfc2882.txt yardradius-1.1.2/doc/Makefile.am0000644000175000017500000000014710640416363013411 00000000000000AUTOMAKE_OPTIONS = foreign INSTALL = @INSTALL@ docdir = $(YARDDOC) doc_DATA = README SUBDIRS = txt yardradius-1.1.2/doc/Makefile.in0000644000175000017500000003513510640702510013417 00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = doc DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/autotools/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-exec-recursive install-info-recursive \ install-recursive installcheck-recursive installdirs-recursive \ pdf-recursive ps-recursive uninstall-info-recursive \ uninstall-recursive am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; am__installdirs = "$(DESTDIR)$(docdir)" docDATA_INSTALL = $(INSTALL_DATA) DATA = $(doc_DATA) ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) INSTALL = @INSTALL@ ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_VERSION = @CC_VERSION@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ MKDIR = @MKDIR@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ YARDCONF = @YARDCONF@ YARDDOC = @YARDDOC@ YARDLOG = @YARDLOG@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = $(YARDDOC) dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ AUTOMAKE_OPTIONS = foreign doc_DATA = README SUBDIRS = txt all: all-recursive .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign doc/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh uninstall-info-am: install-docDATA: $(doc_DATA) @$(NORMAL_INSTALL) test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)" @list='$(doc_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \ $(docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \ done uninstall-docDATA: @$(NORMAL_UNINSTALL) @list='$(doc_DATA)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \ rm -f "$(DESTDIR)$(docdir)/$$f"; \ done # This directory's subdirectories are mostly independent; you can cd # into them and run `make' without going through this Makefile. # To change the values of `make' variables: instead of editing Makefiles, # (1) if the variable is set in `config.status', edit `config.status' # (which will cause the Makefiles to be regenerated when you run `make'); # (2) otherwise, pass the desired values on the `make' command line. $(RECURSIVE_TARGETS): @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ list='$(SUBDIRS)'; for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" mostlyclean-recursive clean-recursive distclean-recursive \ maintainer-clean-recursive: @failcom='exit 1'; \ for f in x $$MAKEFLAGS; do \ case $$f in \ *=* | --[!k]*);; \ *k*) failcom='fail=yes';; \ esac; \ done; \ dot_seen=no; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ rev=''; for subdir in $$list; do \ if test "$$subdir" = "."; then :; else \ rev="$$subdir $$rev"; \ fi; \ done; \ rev="$$rev ."; \ target=`echo $@ | sed s/-recursive//`; \ for subdir in $$rev; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done && test -z "$$fail" tags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ done ctags-recursive: list='$(SUBDIRS)'; for subdir in $$list; do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ done ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ mkid -fID $$unique tags: TAGS TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$tags $$unique; \ fi ctags: CTAGS CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $(TAGS_FILES) $(LISP) tags=; \ here=`pwd`; \ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ test -z "$(CTAGS_ARGS)$$tags$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$tags $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && cd $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) $$here distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test -d "$(distdir)/$$subdir" \ || $(mkdir_p) "$(distdir)/$$subdir" \ || exit 1; \ distdir=`$(am__cd) $(distdir) && pwd`; \ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ (cd $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$top_distdir" \ distdir="$$distdir/$$subdir" \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(DATA) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(docdir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-generic distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive info: info-recursive info-am: install-data-am: install-docDATA install-exec-am: install-info: install-info-recursive install-man: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-docDATA uninstall-info-am uninstall-info: uninstall-info-recursive .PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ clean clean-generic clean-recursive ctags ctags-recursive \ distclean distclean-generic distclean-recursive distclean-tags \ distdir dvi dvi-am html html-am info info-am install \ install-am install-data install-data-am install-docDATA \ install-exec install-exec-am install-info install-info-am \ install-man install-strip installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic maintainer-clean-recursive \ mostlyclean mostlyclean-generic mostlyclean-recursive pdf \ pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \ uninstall-docDATA uninstall-info-am # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: yardradius-1.1.2/man/0000777000175000017500000000000010640702677011453 500000000000000yardradius-1.1.2/man/radiusd_attributes.5.man0000644000175000017500000001455510640412036016125 00000000000000'\" t .\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: radiusd_attributes.5.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADIUSD_ATTRIBUTES 5 "Aug 25, 2004" "1.1" "Yard Radius Manual" .SH NAME radiusd_attributes \- extended users attributes .SH DESCRIPTION This page describes the differences between YARD RADIUS syntax of .I users file and the `standard' one of Livingston RADIUS Daemon 2.1. A complete description of the syntax of that file is not the scope of this document. The .I users text file contains security and configuration information for each user. The first field is the user's name and can be up to 8 characters in length. This is followed (on the same line) with the list of authentication requirements for that user. This can include password, comm server name, comm server port number, and an expiration date of the user's password. When an authentication request is received from the comm server, these values are tested. Special users named "DEFAULT", "DEFAULT2", "DEFAULT3" can be created (and should be placed at the end of the user file) to specify what to do with users not contained in the user file. Indented (with the tab character) lines following the first line indicate the configuration values to be passed back to the comm server to allow the initiation of a user session. This can include things like the PPP configuration values or the host to log the user onto. Again, a description of all attributes and values is not the topic of this document. See NOTES section below for a complete reference about. .SH "YARD RADIUS ATTRIBUTES" YARD RADIUS uses some private non-protocol attributes to support its specific features. They are integer or string attributes that you could set to manage in some ways user accesses: .TP .BR Yard-Simultaneous-Use : The maximum number of simultaneous logins for a user. It's a positive value. .TP .BR Yard-Time : It's a list of the access times (week day(s) and hours) during which the user is authorized to login. It is a comma-separated list of items such as "Wk0800-1800,Sa0800-2400,Su0800-2400". Each item follows a syntax like "DDHHMM-HHMM", where DD=Mo,Tu,We,Th,Fr,Sa,Su,Al,Wk and HHMM are the times of access in 4 characters form. 'Wk' means all 5 weekdays ('Mo'-'Fr') and 'Al' is the whole week. .TP .BR Yard-Max-Monthly-Time : The maximum number of on-line hours the user can be on-line per month. It is a positive value. .TP .BR Yard-Max-Monthly-Traffic : The maximum number of Kbytes of traffic the user can totalize per month. It is a positive value. .PP .BR "Yard-Max-Daily-Time" : .BR "Yard-Max-Daily-Traffic" : .BR "Yard-Max-Yearly-Time" : .TP .BR "Yard-Max-Yearly-Traffic" : At this point, all these attributes are obvious. .TP .BR "Yard-Pam-Auth" : This string is the name of the PAM authentication service to use instead of the default one, which is "yard". This is used to parse the .BR pam.conf , or the .B pam.d directory to get the PAM module to use for auth/acct. You could prefer something like "radius", for instance. .PP YARD RADIUS extends also the predefined values of the standard Auth-Type attribute, with the following ones: .TP .BR PAM Use PAM authentication module. The service name could be specified with a .B Yard-Pam-Auth attribute or it implies the default one "yard". .TP .B System Use system passwd file with or without shadowing. Shadow support should be enabled when calling the `configure' script only if your system requires the use of getspnam() in order to get the encrypted password. Not all systems that support shadow password have that function. If your system has a transparent shadowing support, you do not need any specific enabling. Notably this is true for FreeBSD. If you like so, you can also enable 'shadow expirations'. Systems which support this feature must have a compatible getspnam() with an expiration field in the spwd structure. So, enabling this feature implies enabling shadow support. When shadow expiration is enabled you can require system-based expirations by using a conventional attribute value like Expiration="SHADOW". .TP .B Safeword Not yet supported. .TP .B Defender Not yet supported. .PP But for the above attributes and values, many vendor specific attributes and values are parsed and legal for YARD RADIUS server. You can refer to the .I dictionary file for a complete list. Vendor attributes are useful only when the communication server is configured to send VSA mode requests. Some old communication servers could be unable to do this, and in that case you should modify manually the dictionary. .SH FILES .TP .B PREFIX/conf/users This file contains the human readable information for users' accounting and authorization. .TP .B PREFIX/conf/users.db The same of the previous one as compiled in by .B builddbm in GDBM format. .TP .B PREFIX/conf/dictionary This read-only file contains the codes and formats for standard and vendor RADIUS protocol attributes and values along with their human readable representation. It is subject to change, due to new access server supports. It is a plain text file with a pletora of comments in it. .TP .B PREFIX/docs/rfc/rfc2138.txt Request For Comments about Remote Authentication Dial In User Service (RADIUS). .TP .B PREFIX/docs/rfc/rfc2139.txt Request For Comments about RADIUS Accounting. .SH "SEE ALSO" .BR radiusd (8), RFC2138, RFC2139 .SH AUTHOR Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1992-1999 Lucent Inc. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . .SH NOTES See the .IR "RADIUS for UNIX Administrator's Guide" as a complete reference for all other attributes and values. It is freely available at .I http://www.livingston.com/tech/docs/manuals.html at the time of this document. Note that many vendor attributes are described only within vendor's documentation. Currently YARD RADIUS dictionary is updated with vendor's dictionary by Cisco, Lucent, 3COM, Redback, Springtide, Nortel and possibly others, whenever available. yardradius-1.1.2/man/radwatch.8.man0000644000175000017500000001144410640412036014016 00000000000000'\" t .\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: radwatch.8.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADWATCH 8 "Aug 28, 2004" "1.1" "Yard Radius Manual" .SH NAME radwatch \- A watchdog for RADIUS users .SH SYNOPSIS .BR radwatch " [ " .BR "\-mhx " "] [" .B "\-a " .IR acct_dir " ] [" .B "\-d " .IR db_dir " ]" .SH DESCRIPTION .B radwatch reads its configuration file .I PREFIX/conf/radwatch.conf and uses the radlast and user-stats information to verify that users listed in configuration file are not exceeding their time quota limit. .P For each user logged into user-stats database radwatch will try to match a restriction line from its configuration file and will compute the user's online time for the time interval coresponding to the restriction. If the online time is bigger than the restriction, the user name is printed to the standard output AND it is added to the list of the 'stopped' users contained in .IR PREFIX/conf/stopuser . .SH OPTIONS .TP .BR "\-a " acct_dir Sets the accounting directory instead of the builtin default. The default is choosen at configuration time and it is generally .I PREFIX/logs .TP .BR "\-d " db_dir Sets the database directory instead of the builtin default one. The default is choosen at configuration time and it is generally .I PREFIX/logs. .TP .B \-h Prints out usage of the command. .TP .B \-m Use monthly/yearly hierarchy structure for radlast logging files. You should always use this argument with YARD RADIUS. .SH FILES .B radiusd requires a group of configuration files under .I PREFIX/conf in order to properly work. Examples of those working files are provided with sources and should be present under the same directory, with extension .IR .example . All files are well commented and it should be easy to customize them. The work files are the following ones: .TP .I PREFIX/conf/users This file contains the human readable information for users' accounting and authorization. See .BR radius_attributes (5) for details about its syntax. .TP .I PREFIX/conf/users.db The same of the previous one as compiled in by .B builddbm in GDBM format. It needs to be compiled again every time you make changes to the previous one and without restarting .B radiusd . .TP .I PREFIX/conf/stopuser This text file is created by .B radwatch to deny access to users, when certain conditions are reached (as selected in the radwatch configuration file). The authentication daemon .B radiusd consults that file along with `denyuser' in order to grant access or not. It has an entry per line, which should be a valid system or `users' username. .TP .I PREFIX/conf/radwatch.conf This is the configuration file for .BR radwatch . It is a text files each line of which is of the form: .I "user_list:restriction:time_list" where `user_list' is a comma-separated list of usernames for which this line apply. You can use @group syntax to denote the standard UNIX user groups. The field `restriction' is the value in seconds of the maximum permitted online time within the `time_list'. This one is the third colon separated field and is a list of days of the week and times during which this restriction apply to this user. The valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'. In addition, the value 'Al' represents all 7 days, and 'Wk' represents the 5 weekdays. Times are given as HHMM-HHMM. The ending time may be before the starting time. Days are presumed to wrap at 0000. .SH "SEE ALSO" .BR radiusd (8) .SH AUTHORS Cristian Gafton . .P Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1997 Cristian Gafton. All rights reserved. Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . .SH BUGS The parser for the configuration file is quite fragile, please DO NOT use spaces in the configuration lines. The maximum length of a configuration line in /etc/raddb/radwatch.conf is set at 1024 byts, if you need more group your users in UNIX groups and abuse @group syntax. .SH NOTES In order to use this program you need also a script or a program to stop user sessions on your access server(s). There is not a unique product to do this. Sometimes you can use a SNMP oid to do this; often the only way is opening a telnet admin session to issue a suitable clear/reset line command. It depends on the specific access server and it is not easy to support this kind of feature. You are definitively on your own with this. yardradius-1.1.2/man/radlast.1.man0000644000175000017500000000773010640412036013647 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: radlast.1.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADLAST 1 "Aug 25, 2004" "1.1" "Yard Radius Manual" .SH NAME radlast \- lists last users access and stats .SH SYNOPSIS .BR radlast " [ " .BR "\-chbx" " ] [" .B "\-a " .IR acct_dir " ] [" .B "\-f " .IR file " ] [" .B "\-F " .IR format_string " ] [" .B "\-H " .IR format_string " ] [" .B "\-m " .IR month " ] [" .B "\-u " .IR user " ] [" .B "\-y " .IR year " ]" .SH DESCRIPTION This program gives a detailed list of last accesses for one or more users. It does not collect statistical information. It's a fast and compact dumper of the traditional `detail' files of Livingston's Radius. It's output is in some way similar to that of the Unix command `last' but also high-customizable (see FORMATS section below). .SH OPTIONS .TP .BR "\-a " acct_dir Sets the accounting files directory prefix, instead of the default one, which is .I PREFIX/logs .TP .B \-b Uses a single-row brief output format. .TP .B \-c Shows records in cronological order. .TP .BR "\-f " file Uses this radlast logfile instead of the default stdout. .TP .B \-h Prints out usage of the command. .TP .BR "\-F \-H " format_string Define customized formats for output rows and the header. See FORMATS section below. .TP .BR "\-m " month Reports statistics for month .I month where month is in the range 1-12. .TP .BR "\-y " year Reports statistics for year .I year that is a four digits number. .TP .B \-x Shows extedend information (include traffic and phone CLIs) .P .SH FORMATS .B radlast does allow the user to have a customized output format with the `-F' command line option (see above). The program accepts and outputs any character in a format string and parse it to find some %-prefixed one-letter tokens. Those `a la' printf tokens are substituted with corresponding entry values. Normal C substitution are performed for \\\\,\\t,\\n,\\r and \\%. The list of legal % tokens follows, any oher combination is parsed and ignored. .TS l l. Token Value _ %l Username %p Port ID %a NAS ip address %k Port type %c Client ip address %d Date in ctime format %t Online time in secs %T Online time in HH:MM:SS format %i Input traffic in bytes %I Input traffic in KBytes %o Output traffic in bytes %O Output traffic in KBytes %m Total traffic in bytes %M Total traffic in KBytes %s Input speed in bps (or UNKNOWN) %S Output speed in bps (or UNKNOWN) %A NAS called id (or UNKNOWN) %# Client calling id (or UNKNOWN) %% literal '%' .TE .SH FILES .TP .I "PREFIX/logs/YEAR/radlast-XX" These are the files which constitutes the radlast database. They are in binary not-indexed format and functionally equivalent to the text files `detail-XX'. The main difference is that detail files are organized on a per client base. See below. .TP .I "PREFIX/logs/NAS/YEAR/detail-XX" These text files stores accounting information for every access server (NAS) listed in the .I clients file. Each user session is identified by a unique session-id and these accounting files store start/stop records for every single authenticated session. Information stored in those records, but for a group of standard ones, strictly depend on the NAS model and operating system. .SH BUGS The .BR "-x " option apparently is not working. .SH "SEE ALSO" .BR builddbm (8), .BR radiusd (8), .BR radlist (1), .BR radtest (1), .BR radwatch (1), .BR radius_attributes (5), .SH AUTHOR Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1997 Cristian Gafton. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . yardradius-1.1.2/man/Makefile.am0000644000175000017500000000026110640412036013405 00000000000000AUTOMAKE_OPTIONS = foreign INSTALL = @INSTALL@ man_MANS = builddbm.8 md5test.1 radiusd.8 radlast.1 \ radlist.1 radtest.1 radwatch.8 radiusd_attributes.5 radiusd_pam.7 yardradius-1.1.2/man/Makefile.in0000644000175000017500000003230010640702511013415 00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = man DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/autotools/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = man1dir = $(mandir)/man1 am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(man8dir)" man5dir = $(mandir)/man5 man8dir = $(mandir)/man8 NROFF = nroff MANS = $(man_MANS) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) INSTALL = @INSTALL@ ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_VERSION = @CC_VERSION@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ MKDIR = @MKDIR@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ YARDCONF = @YARDCONF@ YARDDOC = @YARDDOC@ YARDLOG = @YARDLOG@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ AUTOMAKE_OPTIONS = foreign man_MANS = builddbm.8 md5test.1 radiusd.8 radlast.1 \ radlist.1 radtest.1 radwatch.8 radiusd_attributes.5 all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign man/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign man/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh uninstall-info-am: install-man1: $(man1_MANS) $(man_MANS) @$(NORMAL_INSTALL) test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(man1dir)" @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.1*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ else file=$$i; fi; \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 1*) ;; \ *) ext='1' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \ done uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.1*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 1*) ;; \ *) ext='1' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \ rm -f "$(DESTDIR)$(man1dir)/$$inst"; \ done install-man5: $(man5_MANS) $(man_MANS) @$(NORMAL_INSTALL) test -z "$(man5dir)" || $(mkdir_p) "$(DESTDIR)$(man5dir)" @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.5*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ else file=$$i; fi; \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 5*) ;; \ *) ext='5' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \ done uninstall-man5: @$(NORMAL_UNINSTALL) @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.5*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 5*) ;; \ *) ext='5' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \ rm -f "$(DESTDIR)$(man5dir)/$$inst"; \ done install-man8: $(man8_MANS) $(man_MANS) @$(NORMAL_INSTALL) test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.8*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ else file=$$i; fi; \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 8*) ;; \ *) ext='8' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \ done uninstall-man8: @$(NORMAL_UNINSTALL) @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ for i in $$l2; do \ case "$$i" in \ *.8*) list="$$list $$i" ;; \ esac; \ done; \ for i in $$list; do \ ext=`echo $$i | sed -e 's/^.*\\.//'`; \ case "$$ext" in \ 8*) ;; \ *) ext='8' ;; \ esac; \ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ inst=`echo $$inst | sed -e 's/^.*\///'`; \ inst=`echo $$inst | sed '$(transform)'`.$$ext; \ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \ done tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(MANS) installdirs: for dir in "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-man install-exec-am: install-info: install-info-am install-man: install-man1 install-man5 install-man8 installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-info-am uninstall-man uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 .PHONY: all all-am check check-am clean clean-generic distclean \ distclean-generic distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-exec \ install-exec-am install-info install-info-am install-man \ install-man1 install-man5 install-man8 install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ pdf-am ps ps-am uninstall uninstall-am uninstall-info-am \ uninstall-man uninstall-man1 uninstall-man5 uninstall-man8 radiusd_pam.7 # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: yardradius-1.1.2/man/radiusd_pam.7.man0000644000175000017500000000305110640412036014503 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .TH RADIUSD_PAM 7 "Sep 1, 2004" "1.0.20" "Yard Radius Manual" .SH NAME radiusd_pam \- information about PAM and radiusd .SH DESCRIPTION .B radiusd is able to use PAM (Pluggable Authentication Modules) for authentication and logging. PAM support is available under Linux, FreeBSD, HP-UX, Solaris and possibly other flavors of Unix. You need to enable PAM support at configuration time for compiling in the needed additional code, and also to activate it at run-time with the .BR "-P " and " -A " "options for " radiusd . The two above options enable separately authorization and accounting via PAM. You should use the special .BR "Auth-Type=PAM" " attribute in your" .IR users " file" in order to enable PAM authorization. As default .B radiusd will look for a .SH "SEE ALSO" .BR radiusd (8), .BR radiusd_attributes (5), .BR pam (8) .SH AUTHOR Original PAM support for RADIUS is due to Jeff Blaize . Minor modification are due to C. Gafton and Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . yardradius-1.1.2/man/md5test.1.man0000644000175000017500000000213410640412036013573 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of rednistribution .\" $Id: md5test.1.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH MD5TEST 1 "Aug 28, 2004" "1.1" "Yard Radius Manual" .SH NAME md5test \- MD5 test program for RADIUS .SH SYNOPSIS .BI md5test " shared" .SH DESCRIPTION .B md5test is a poorly written program for testing purpose. It takes the shared secret as an argument, reads a hex dump on standard input, and outputs the length, shared secret, standard input, followed by the MD5 checksum of that input followed by the shared secret. .SH "SEE ALSO" .BR radiusd (8), .BR md5sum (1) .SH BUGS No decent argument and input checking is done. Use it with caution, due to easy segmentation faulting. .SH AUTHOR Lucent Technologies Remote Access. .SH COPYRIGHT Copyright (C) 1992-1999 Lucent Inc. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . yardradius-1.1.2/man/radtest.1.man0000644000175000017500000000611010640412036013652 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of rednistribution .\" $Id: radtest.1.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADTEST 1 "Aug 28, 2004" "1.1" "Yard Radius Manual" .SH NAME radtest \- RADIUS server test program .SH SYNOPSIS .BR radtest " [" .B "\-afnrvx" " ] [" .B "\-d " .IR called_station_id " ] [" .B "\-g " .IR calling_station_id " ] [" .B "\-i " .IR id " ] [" .B "\-p " .IR port " ] [" .B "\-s " .IR secret " ] [" .B "\-t " .IR type " ] " .B "\-u " .IR username " " .I password .SH DESCRIPTION .B radtest is a program useful to test the configuration of a running RADIUS daemon. You need to add localhost to servers' authorized clients. This program is a client for authentication protocol only, it does not manage accounting packets currently. You could use it to test either the YARD RADIUS daemon or any other standard RADIUS server. .SH OPTIONS .TP .B "\-a" Sets accounting protocol on. Not yet implemented. .TP .BR "\-d " called_station_id Sets the called station identifier for building packets. .TP .B \-f Sets user type to framed. This is overrided by .B -t option below. .TP .BR "\-g " calling_station_id Sets the calling station identifier for building packets. .TP .B \-h Prints out usage of the command. .TP .BR "\-i " id Sets the packet identifier to number `id'. .TP .B \-n Not yet implemented. .TP .BR "\-p " port Uses udp port `port' instead of the value specified in .I /etc/services or the default one (1645). .TP .B \-r Not yet implemented. .TP .B "\-s " secret Specifies the secret key to use (default is localkey) .TP .B \-v Prints version information. .TP .BR "\-l " file Changes the logging file. .TP .BR "\-x" Prints (or should do so) debugging messages while compiling. .SH FILES .TP .I PREFIX/conf/users This file contains the human readable information for users' accounting and authorization. See .BR radius_attributes (5) for details about its syntax. .TP .I PREFIX/conf/users.db The same of the previous one as compiled in by .B builddbm in GDBM format. It needs to be compiled again every time you make changes to the previous one and without restarting .B radiusd . .TP .I PREFIX/conf/dictionary This read-only file contains the codes and formats for standard and vendor RADIUS protocol attributes and values along with their human readable representation. It is subject to change, due to new access server supports. It is a plain text file with a pletora of comments in it. .SH "SEE ALSO" .BR radiusd (8), .BR radlast (1), .BR radlist (1), .BR radtest (1), .BR radwatch (1), .BR radius_attributes (1), .BR gdbm (3) .SH COPYRIGHT Copyright (C) 1992-1999 Lucent Inc. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . .SH NOTES The syntax of the source .I users file is not described here. Please, refer to the official Livingston documentation, which includes the .IR "RADIUS for UNIX Administrator's Guide" . yardradius-1.1.2/man/radiusd.8.man0000644000175000017500000003072210640412036013654 00000000000000'\" t .\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: radiusd.8.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADIUSD 8 "Aug 25, 2004" "1.1" "Yard Radius Manual" .SH NAME radiusd \- Yet Another Radius Daemon (YARD RADIUS) .SH SYNOPSIS .B radiusd " [" .BR \-AbchoPsvxz " ] [" .B "\-a " .IR acct_dir " ] [" .B "\-d " .IR db_dir " ] [" .B "\-f " .IR alt_passwd_file " ] [" .B "\-i " .IR ip_addr " ] [" .B "\-l " .IR log_file " ] [" .B "\-p " .IR udp_port " ] [" .B "\-q " .IR max_outstanding_reqs " ] [" .B "\-t " .IR max_queue_secs " ] [" .B "\-w " .IR max_proxy_secs " ]" .SH DESCRIPTION YARD .B radiusd is a program that provides authorization and accounting services for remote hosts, based on RADIUS protocols. RADIUS protocols are defined in a pair of RFC documents and currently used by the majority of network access servers and routers in order to manage incoming dialup connections. Open source products of RADIUS clients are also available for general use on *nix hosts. .P YARD RADIUS daemon is largerly based on the original Livingston Inc. RADIUS 2.1 daemon (currently known as Lucent Inc. Remote Access RADIUS server 2.1 \- Livingston Inc. is now disappeared...). It enhances the original code with a number of useful features, such as control of simultaneous logins, support of many non standard vendor clients, autoconfiguration capabilities, PAM services, MD5 passwords, etc. All them are very useful in real world area of application (e.g. ISPs). A complete and up-to-date list of extensions currently present in YARD RADIUS is available in the Changelog file, which should be enclosed in sources. .P The daemon listens to a couple of non privileged UDP ports (1645 and 1646) and possibly to other two ones (1815 and 1816), when proxy is enabled. Those ports could also be changed at run-time, but you are not encouraged to do this. If your authorization information are available either as a separate passwd file or self-contained in .BR users file (i.e. in some form independent from system passwd file, see below) you could run .B radiusd as a non privileged users. .P All configuration files of YARD RADIUS are contained under .IR PREFIX/conf directory if not spe .SH OPTIONS .TP .BR "\-a " acct_dir Sets the accounting directory instead of the builtin default. The default is choosen at configuration time and it is generally .I PREFIX/logs .TP .B \-A Enable accounting via PAM. See below. .TP .B \-b Uses GDBM for the users file ( .I users.db ) instead of the plain text version ( .I users ) This improve performances of users file checking for authentication. It's strongly suggested. But it's not completely equivalent to plain text, because GDBM files are strictly unsorted. This could be ok or not, it depens on your specific choices of attributes. You need to run .B builddbm to convert the plain .I users file in the GDBM indexed file and this needs to be done every time you changes users file contents. .TP .B \-c Clears user stats database. This should be done to solve troubles due to unsynchonized status among the servers and one or more of its clients. Mabye, after a cold-reboot of an access server. .TP .BR "\-d " db_dir Sets the database directory instead of the builtin default one. The default is choosen at configuration time and it is generally .B PREFIX/logs. .TP .B \-h Prints out usage of the command. .TP .BR "\-f " alt_passwd_file Sets an alternate password file name to use instead of the system password file .B /etc/passwd. .TP .BR "\-i " ip_addr Sets an alternate IP for the server host, instead of the default one. This is useful if the host on which the daemon is runnig has multiple interfaces or ip aliases. .TP .BR "\-l " log_file Sets a logging text file, to use instead of .BR syslog . .TP .B \-o Accept all-zero accounting requests authenticator. A damned thing to use with some old non-RFC compliant clients. Use this if you see this kind of errors in the logging file, only. .TP .BR "\-p " udp_port Set an alternate radius port number. Default ports should be (optionally) defined in .B /etc/services as follows: .TS l l. Name Port _ radius 1645/udp radacct 1646/udp radius-proxy 1815/udp radacct-proxy 1816/udp .TE If they are not in that file, the above ones are used. If you specify the port `n' as the argument of .BR "\-p " "option, then" .B radiusd tries to use the following ports: .TS l l. Name Port _ radius n/udp radacct n+1/udp radius-proxy n+5/udp radacct-proxy n+6/udp .TE .TP .B \-P Enable authorization via PAM. See below. .TP .BR "\-q " max_outstanding_reqs Sets the incoming packets queue size. 100 is the default. .TP .B \-s Forks another process for accounting. This is not generally suggested, due to dependencies among auth and acct modules in YARD .B radiusd . .TP .BR "\-t " max_queue_secs Set time out for requests queue. .TP .B \-v Print version. It shows also enabled features. Version number should be a group of three point-separated numbers, such as .B major.minor.patch where meaning of the three values should be obvious. It's not easy to define a `major' advancement in respect of a `minor' one. Anyway, any minor/major number should correspond to a different branch in the CVS repository. This is not true for a patching release. .TP .B "\-w " max_proxy_secs Set time out for proxy requests. .TP .B \-x Set debug mode on. It increases verbosity level. .TP .B \-z The same of .BR "\-b \-x \-d . \-a ra" . This is intended for debugging. .P .SH FILES .B radiusd requires a group of configuration files under .I PREFIX/conf in order to properly work. Examples of those working files are provided with sources and should be present under the same directory, with extension .IR .example . All files are well commented and it should be easy to customize them. The work files are the following ones: .TP .I PREFIX/conf/users This file contains the human readable information for users' accounting and authorization. See .BR radius_attributes (5) for details about its syntax. .TP .I PREFIX/conf/users.db The same of the previous one as compiled in by .B builddbm in GDBM format. It needs to be compiled again every time you make changes to the previous one and without restarting .B radiusd . .TP .I PREFIX/conf/dictionary This read-only file contains the codes and formats for standard and vendor RADIUS protocol attributes and values along with their human readable representation. It is subject to change, due to new access server supports. It is a plain text file with a pletora of comments in it. .TP .I PREFIX/conf/clients It contains names or ip addresses of remote clients authorized to use the server for authentication and accounting, .IR "along with their passwords in clear text" . So this file should be protected with mode 600. .TP .I PREFIX/conf/clcache The same of the previous file as cached in GDBM format for fast access at daemon startup. With the same recommendations for file access modes. .TP .I PREFIX/conf/proxy This file is used to collect proxy hosts and their associated realms and passwords. It contains a list of remote servers to forward to authentication and accounting requests. Every line refers to a different proxy server: the first field is a valid hostname or ip address; the second field (seperated by blanks or tabs) is the shared secret); the third field is the named or numeric authentication realm; the fourth field can contain the optional RADIUS UDP Port number of the remote server, the RADIUS and RADIUS Accounting Port numbers, and any of following optional keywords: .TS l lw42. old T{ Strip realm and do not attach Proxy-State when forwarding T} secure T{ Allow remote server to authorize admin logins for your client T} ipass T{ Use the ipass protocol T} .TE The realm string must follow an `@' sign after the username to identify the correct proxy server. .TP .I PREFIX/conf/allowuser You can list here (one per line) usernames/groupnames who are granted for having access (if their password are correct). Each entry must respect one of the following syntaxes: .TS l l. USER: GROUP: GECOS: SHELL: .TE so you can match users by usernames, groupnames, gcos substrings (i.e. case-sensitive sub-strings in the fifth field of the system /etc/passwd file or the alternate password file), or shell paths. You can use the special string `ANY' as a matching argument too (e.g. `USER: ANY'). An empty or missing file grants access to anyone which is not listed in the next file. .TP .I PREFIX/conf/denyuser The same syntax of allowuser can be used to deny access to specific classes of users, with the same previous matching criteria. An empty or missing file grants access to anyone which is listed in the previous file or not. .P Note that all users have always to match their password with the authorization module selected in their `users' file entry, after the above files allowed to login. You cannot use these files to grant access without any other additional authentication. .TP .I PREFIX/conf/stopuser This text file is created by .B radwatch to deny access to users, when certain conditions are reached (as selected in the radwatch configuration file). The authentication daemon .B radiusd consults that file along with `denyuser' in order to grant access or not. It has an entry per line, which should be a valid system or `users' username. .TP .I PREFIX/conf/radwatch.conf This is the configuration file for .BR radwatch . It is a text files each line of which is of the form: .I "user_list:restriction:time_list" where `user_list' is a comma-separated list of usernames for which this line apply. You can use @group syntax to denote the standard UNIX user groups. The field `restriction' is the value in seconds of the maximum permitted online time within the `time_list'. This one is the third colon separated field and is a list of days of the week and times during which this restriction apply to this user. The valid days are 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', and 'Sa'. In addition, the value 'Al' represents all 7 days, and 'Wk' represents the 5 weekdays. Times are given as HHMM-HHMM. The ending time may be before the starting time. Days are presumed to wrap at 0000. .TP .I PREFIX/conf/config.aeg This text file contains the configuration information necessary for .B radiusd to connect to the ActivEngine, which is the .BR "ActivCard " "Authentication Server." See comments contained in the example file provided for details. .SH "LOGGING FILES" All logging and accounting files of YARD RADIUS are stored under `PREFIX/logs'. Accounting files are organized on a per-month and per-year basis. All files written by Livingston's server are also written by YARD, but it also creates some specific binary files to store the on-line status of users, and collect users statistics. It's important to ensure that those files are synchronized with the real status of the clients, to avoid annoying denial-of-service troubles to your users (e.g. in conjunction with a Yard-Simultaneuous-Use attribute). This could happen when one or more clients reboots without sending suitable stop accouting records before. In those cases, YARD has to be killed too and restarted with a `clean up' argument `-c', in order to reset its internal status. The logging file structure is as follows: .TS l l. /user-stats GDBM yearly file /radlast-XX Binary compact monthly file //detail-XX Livingston-like logging text file .TE This allows very fast computing of statistics and maintaining on-line status. .SH BUGS Bugs? What's a bug? .SH "SEE ALSO" .BR builddbm (8), .BR radlast (1), .BR radlist (1), .BR radtest (1), .BR radwatch (1), .BR radius_attributes (5), .BR gdbm (3) .SH AUTHOR Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1992-1999 Lucent Inc. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . .SH NOTES The configuration of a RADIUS server is an argument too long to deal with it here. Please, refer to the official Livingston documentation, which includes the .IR "RADIUS for UNIX Administrator's Guide" . It is freely available at .I http://www.livingston.com/tech/docs/manuals.html at the time of this document. .P It's a very good point to start with. yardradius-1.1.2/man/builddbm.8.man0000644000175000017500000000471110640412036014002 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: builddbm.8.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH BUILDDBM 8 "Aug 25, 2004" "1.1" "Yard Radius Manual" .SH NAME builddbm \- users.db compiler .SH SYNOPSIS .BR builddbm " [ " .BR \-hvx " ] [ " .B \-d .IR directory " ] [ " .BI \-l " file " ] .SH DESCRIPTION YARD RADIUS .B builddbm is a program that builds the GDBM format of the `users' file, which could be used in conjunction with the .B radiusd `-b' flag. The users file should resides in the standard configuration directory, as selected at configuration time. It should be run as root. Be also sure to set mode 600 for the `users' file as well as the resulting `users.db'. .SH OPTIONS .TP .BR "\-d " directory Changes the source `users' file directory. The default value is PREFIX/conf. The destination directory of `users.db' is the same one. .TP .B \-h Prints out usage of the command. .TP .B \-v Prints version information. .TP .BR "\-l " file Changes the logging file. .TP .BR "\-x" Prints (or should do so) debugging messages while compiling. .SH FILES .TP .I PREFIX/conf/users This file contains the human readable information for users' accounting and authorization. See .BR radius_attributes (5) for details about its syntax. .TP .I PREFIX/conf/users.db The same of the previous one as compiled in by .B builddbm in GDBM format. It needs to be compiled again every time you make changes to the previous one and without restarting .B radiusd . .TP .I PREFIX/conf/dictionary This read-only file contains the codes and formats for standard and vendor RADIUS protocol attributes and values along with their human readable representation. It is subject to change, due to new access server supports. It is a plain text file with a pletora of comments in it. .SH "SEE ALSO" .BR radiusd (8), .BR radlast (1), .BR radlist (1), .BR radtest (1), .BR radwatch (1), .BR radius_attributes (1), .BR gdbm (3) .SH COPYRIGHT Copyright (C) 1992-1999 Lucent Inc. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . .SH NOTES The syntax of the source .I users file is not described here. Please, refer to the official Livingston documentation, which includes the .IR "RADIUS for UNIX Administrator's Guide" . yardradius-1.1.2/man/radlist.1.man0000644000175000017500000001032410640412036013650 00000000000000.\" Copyright (c) 2004 Francesco Paolo Lovergine .\" See copyright section for conditions of redistribution .\" $Id: radlist.1.man 83 2004-08-28 13:32:47Z flovergine $ .\" .TH RADLIST 1 "Aug 25, 2004" "1.1" "Yard Radius Manual" .SH NAME radlist \- lists current on-line users and per user stats .SH SYNOPSIS .BR radlist " [ " .BR "\-bhnstx" " ] [" .BR "\-D|M|Y " "] [" .B "\-F " .IR format_string " ] [" .B "\-d " .IR day " ] [" .B "\-m " .IR month " ] [" .B "\-y " .IR year " ] [" .B "\-u " .IR user " ] [" .B "\-y " .IR year " ]" .SH DESCRIPTION This program gives detailed statistics for current users on line (which is the default) or any user you like. Its statistics are on a per-day/month/year base and can be used to produced useful reports. It shows on-line time and traffic, along with numbers of logins and current port usage. Its output can be largerly customized as you prefer with a suitable format string. See FORMATS section below. .SH OPTIONS .TP .B \-b Uses a brief output format. .TP .B \-t Uses a `traditional' tabular output format. .TP .BR \-n Skips the header when `-t' option is used. .TP .B \-h Prints out usage of the command. .TP .BR "\-F \-H" format_string Define customized formats for output rows and the header. See FORMATS section below. .TP .BR "\-m " month Reports statistics for month .I month where month is in the range 1-12. .TP .BR "\-y " year Reports statistics for year .I year that is a four digits number. .TP .B \-x Shows extedend information (port information). .TP .B "\- u" user Gets a single username database entry, i.e. select a specific username to report statistics for that user only. .TP .B "\-M \-D \-Y" Reports statistics on a dayly/monthly/yearly basis. They should be used along with d,m,y options. If not specified current date and .B \-M is considered. .P .SH FORMATS .B radlist does allow the user to have a customized output format with the `-F' command line option (see above). The program accepts and outputs any character in a format string and parse it to find some %-prefixed one-letter tokens. Those `a la' printf tokens are substituted with corresponding entry values. Normal C substitution are performed for \\\\,\\t,\\n,\\r and \\%. The list of legal % tokens follows, any oher combination is parsed and ignored. .TS l l. Token Value _ %l Username %s Number of current sessions %n Total number of sessions in the period specified %t Total online time in secs %T Total online time in HH:MM:SS format %i Total input traffic in bytes %I Total input traffic in KBytes %o Total output traffic in bytes %O Total output traffic in KBytes %m Total traffic in bytes %M Total traffic in KBytes %k Mean online time per session in secs %K Mean online time per session in HH:MM:SS format %g Mean traffic per session in bytes %G Mean traffic per session in KBytes %% literal '%' .TE No alignment decimal values are allowed in this version of format strings. .SH FILES .TP .I "PREFIX/logs/YEAR/user-stats" These are files which constitutes the yearly radlist database. They are in binary GDBM format and collect statistics on a per user base, day by day. .TP .I "PREFIX/logs/NAS/YEAR/detail-XX" These text files stores accounting information for every access server (NAS) listed in the .I clients file. Each user session is identified by a unique session-id and these accounting files store start/stop records for every single authenticated session. Information stored in those records, but for a group of standard ones, strictly depend on the NAS model and operating system. .SH BUGS The .BR "-x " option apparently is not working. .SH "SEE ALSO" .BR radiusd (8), .BR radlast (1) .BR builddbm (8), .BR radtest (1), .BR radwatch (1), .BR radius_attributes (5) .SH AUTHOR Francesco Paolo Lovergine . .P A complete list of contributors is contained in CREDITS file. You should get that file among other ones within your distribution and possibly installed under .BR "PREFIX/docs " directory .SH COPYRIGHT Copyright (C) 1997 Cristian Gafton. All rights reserved. .P Copyright (C) 1999-2004 Francesco Paolo Lovergine. All rights reserved. .P See the LICENSE file enclosed within this software for conditions of use and distribution. This is a pure .B "ISO BSD Open Source License" . yardradius-1.1.2/pam/0000777000175000017500000000000010640702676011454 500000000000000yardradius-1.1.2/pam/README0000644000175000017500000000223610640412037012240 00000000000000$Id: README 56 2002-01-19 16:38:56Z kiavik $ Code here is contributed by Christian Gafton who worked (or works?) about PAM in RedHat Inc. What follows is his original README file. I hope to contribute in the near future to this section. - Francesco README for the sample pam_radius_session module for PAM ------------------------------------------------------- This is a sample module you can use with the pamified version of the radius daemon. His own purpose is to fill your logs with acct packet information if you call it with the 'debug' argument. Otherwise, it is silent. Usage: If you want it to be silent, but to be available to the PAM library, configure your /etc/pam.d/radius configuration by adding the session entry for this module: ---- /etc/pam.d/radius ---- [... auth and account entries ...] session required /lib/security/pam_radius_session ---- ---- If you want it verbose: ---- /etc/pam.d/radius ---- [... auth and account entries ...] session required /lib/security/pam_radius_session debug ---- ---- Of course, you can modify it to do you SQL database logging, etc. You are on your own from this moment. :-) yardradius-1.1.2/pam/pam.conf0000644000175000017500000000107110640412037013000 00000000000000#%PAM-1.0 # # PAM configuration for radius on RH Linux 4.1 or later # put this file in /etc/pam.d/yard # Cristian Gafton # auth required /lib/security/pam_pwdb.so shadow nullok # if you want /etc/nologin thing with radius, uncomment the next line #auth required /lib/security/pam_nologin.so account required /lib/security/pam_pwdb.so password required /lib/security/pam_pwdb.so shadow nullok session required /lib/security/pam_pwdb.so session required /lib/security/pam_radius_session.so # # end of file # yardradius-1.1.2/pam/current-draft.txt0000644000175000017500000006744010640412037014711 00000000000000PAM working group A.G. Morgan Internet Draft: October 6, 1999 Document: draft-morgan-pam-07.txt Expires: June 13, 2000 Obsoletes: draft-morgan-pam-06.txt Pluggable Authentication Modules 1 Status of this memo This document is an draft specification. The latest version of this draft may be obtained from here: http://linux.kernel.org/pub/linux/libs/pam/pre/doc/ As Linux-PAM-'version'-docs.tar.gz It is also contained in the Linux-PAM tar ball. 2 Abstract This document is concerned with the definition of a general infrastructure for module based authentication. The infrastructure is named Pluggable Authentication Modules (PAM for short). 3 Introduction Computers are tools. They provide services to people and other computers (collectively we shall call these _users_ entities). In order to provide convenient, reliable and individual service to different entities, it is common for entities to be labelled. Having defined a label as referring to a some specific entity, the label is used for the purpose of protecting and allocating data resources. All modern operating systems have a notion of labelled entities and all modern operating systems face a common problem: how to authenticate the association of a predefined label with applicant entities. There are as many authentication methods as one might care to count. None of them are perfect and none of them are invulnerable. In general, any given authentication method becomes weaker over time. It is common then for new authentication methods to be developed in response to newly discovered weaknesses in the old authentication methods. The problem with inventing new authentication methods is the fact that old applications do not support them. This contributes to an inertia that discourages the overhaul of weakly protected systems. Another problem is that individuals (people) are frequently powerless to layer the protective authentication around their systems. They are forced to rely on single (lowest common denominator) authentication schemes even in situations where this is far from appropriate. PAM, as discussed in this document, is a generalization of the approach first introduced in [1]. In short, it is a general framework of interfaces that abstract the process of authentication. With PAM, a service provider can custom protect individual services to the level that they deem is appropriate. PAM has nothing explicit to say about transport layer encryption. Within the context of this document encryption and/or compression of data exchanges are application specific (strictly between client and server) and orthogonal to the process of authentication. 4 Definitions Here we pose the authentication problem as one of configuring defined interfaces between two entities. 4.1 Players in the authentication process PAM reserves the following words to specify unique entities in the authentication process: applicant the entity (user) initiating an application for service [PAM associates the PAM_RUSER _item_ with this requesting user]. arbitrator the entity (user) under whose identity the service application is negotiated and with whose authority service is granted. user the entity (user) whose identity is being authenticated [PAM associates the PAM_USER _item_ with this identity]. server the application that provides service, or acts as an authenticated gateway to the requested service. This application is completely responsible for the server end of the transport layer connecting the server to the client. PAM makes no assumptions about how data is encapsulated for exchanges between the server and the client, only that full octet sequences can be freely exchanged without corruption. client application providing the direct/primary interface to applicant. This application is completely responsible for the client end of the transport layer connecting the server to the client. PAM makes no assumptions about how data is encapsulated for exchanges between the server and the client, only that full octet sequences can be freely exchanged without corruption. module authentication binary that provides server-side support for some (arbitrary) authentication method. agent authentication binary that provides client-side support for some (arbitrary) authentication method. Here is a diagram to help orient the reader: +-------+ +--------+ . . . . .| agent | .| module | . +-------+ .+--------+ V | . | . | V | +---------+ +-------+ . +------+ | | |libpamc| . |libpam| | | +-------+ . +------+ |applicant| | . | | | +--------+ +----------+ | |---| client |-----------| server | +---------+ +--------+ +----------+ Solid lines connecting the boxes represent two-way interaction. The dotted-directed lines indicate an optional connection beteween the plugin module (agent) and the server (applicant). In the case of the module, this represents the module invoking the 'conversation' callback function provided to libpam by the server application when it inititializes the libpam library. In the case of the agent, this may be some out-of-PAM API interaction (for example directly displaying a dialog box under X). 4.2 Defined Data Types In this draft, we define two composite data types, the text string and the binary prompt. They are the data types used to communicate authentication requests and responses. 4.2.1 text string The text string is a simple sequence of non-NUL (NUL = 0x00) octets. Terminated with a single NUL (0x00) octet. The character set employed in the octet sequence may be negotiated out of band, but defaults to utf-8. --------------------------- [ character data | NUL ] [ octet sequence | 0x00 ] --------------------------- Within the rest of this text, PAM text strings are delimited with a pair of double quotes. Example, "this" = {'t';'h';'i';'s';0x00}. 4.2.2 binary prompt A binary prompt consists of a stream of octets arranged as follows: ---------------------------------------- [ u32 | u8 | (length-5 octets) ] [ length | control | data ] ---------------------------------------- That is, a 32-bit unsigned integer in network byte order, a single unsigned byte of control information and a sequence of octets of length (length-5). The composition of the _data_ is context dependent but is generally not a concern for either the server or the client. It is very much the concern of modules and agents. For purposes of interoperability, we define the following control characters as legal. value symbol description ------------------------------------------------- 0x01 PAM_BPC_OK - continuation packet 0x02 PAM_BPC_SELECT - initialization packet 0x03 PAM_BPC_DONE - termination packet 0x04 PAM_BPC_FAIL - unable to execute The following control characters are only legal for exchanges between an agent and a client (it is the responsibility of the client to enforce this rule in the face of a rogue server): 0x41 PAM_BPC_GETENV - obtain client env.var 0x42 PAM_BPC_PUTENV - set client env.var 0x43 PAM_BPC_TEXT - display message 0x44 PAM_BPC_ERROR - display error message 0x45 PAM_BPC_PROMPT - echo'd text prompt 0x46 PAM_BPC_PASS - non-echo'd text prompt Note, length is always equal to the total length of the binary prompt and represented by a network ordered unsigned 32 bit integer. 4.2.2.1 PAM_BPC_SELECT binary prompts Binary prompts of control type PAM_BPC_SELECT have a defined data part. It is composed of three elements: {agent_id;'/';data} The agent_id is a sequence of characters satisfying the following regexp: /^[a-z0-9\_]+(@[a-z0-9\_.]+)?$/ and has a specific form for each independent agent. o Agent_ids that do not contain an at-sign (@) are reserved to be assigned by IANA (Internet Assigned Numbers Authority). Names of this format MUST NOT be used without first registering with IANA. Registered names MUST NOT contain an at-sign (@). o Anyone can define additional agents by using names in the format name@domainname, e.g. "ouragent@example.com". The part following the at-sign MUST be a valid fully qualified internet domain name [RFC-1034] controlled by the person or organization defining the name. (Said another way, if you control the email address that your agent has as an identifier, they you are entitled to use this identifier.) It is up to each domain how it manages its local namespace. The '/' character is a mandatory delimiter, indicating the end of the agent_id. The trailing data is of a format specific to the agent with the given agent_id. 4.3 Special cases In a previous section (4.1) we identified the most general selection of authentication participants. In the case of network authentication, it is straightforward to ascribe identities to the defined participants. However, there are also special (less general) cases that we recognize here. The primary authentication step, when a user is directly introduced into a computer system (log's on to a workstation) is a special case. In this situation, the client and the server are generally one application. Before authenticating such a user, the applicant is formally unknown: PAM_RUSER is NULL. Some client-server implementations (telnet for example) provide effective full tty connections. In these cases, the four simple text string prompting cases (see below) can be handled as in the primary login step. In other words, the server absorbs most of the overhead of propagating authentication messages. In these cases, there is special client/server support for handling binary prompts. 5 Defined interfaces for information flow Here, we discuss the information exchange interfaces between the players in the authentication process. It should be understood that the server side is responsible for driving the authentication of the applicant. Notably, every request received by the client from the server must be matched with a single response from the client to the server. 5.1 Applicant <-> client Once the client is invoked, requests to the applicant entity are initiated by the client application. General clients are able to make the following requests directly to an applicant: echo text string echo error text string prompt with text string for echo'd text string input prompt with text string for concealed text string input the nature of the interface provided by the client for the benefit of the applicant entity is client specific and not defined by PAM. 5.2 Client <-> agent In general, authentication schemes require more modes of exchange than the four defined in the previous section (5.1). This provides a role for client-loadable agents. The client and agent exchange binary-messages that can have one of the following forms: client -> agent binary prompt agent expecting binary prompt reply to client agent -> client binary prompt reply from agent to clients binary prompt Following the acceptance of a binary prompt by the agent, the agent may attempt to exchange information with the client before returning its binary prompt reply. Permitted exchanges are binary prompts of the following types: agent -> client set environment variable (A) get environment variable (B) echo text string (C) echo error text string (D) prompt for echo'd text string input (E) prompt for concealed text string input (F) In response to these prompts, the client must legitimately respond with a corresponding binary prompt reply. We list a complete set of example exchanges, including each type of legitimate response (passes and a single fail): Type | Agent request | Client response --------------------------------------------------------------- (A) | {13;PAM_BPC_PUTENV;"FOO=BAR"} | {5;PAM_BPC_OK;} | {10;PAM_BPC_PUTENV;"FOO="} | {5;PAM_BPC_OK;} | {9;PAM_BPC_PUTENV;"FOO"} (*) | {5;PAM_BPC_OK;} | {9;PAM_BPC_PUTENV;"BAR"} (*) | {5;PAM_BPC_FAIL;} --------------------------------------------------------------- (B) | {10;PAM_BPC_GETENV;"TERM"} | {11;PAM_BPC_OK;"vt100"} | {9;PAM_BPC_GETENV;"FOO"} | {5;PAM_BPC_FAIL;} --------------------------------------------------------------- (C) | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_OK;} | {12;PAM_BPC_TEXT;"hello!"} | {5;PAM_BPC_FAIL;} --------------------------------------------------------------- (D) | {11;PAM_BPC_TEXT;"ouch!"} | {5;PAM_BPC_OK;} | {11;PAM_BPC_TEXT;"ouch!"} | {5;PAM_BPC_FAIL;} --------------------------------------------------------------- (E) | {13;PAM_BPC_PROMPT;"login: "} | {9;PAM_BPC_OK;"joe"} | {13;PAM_BPC_PROMPT;"login: "} | {6;PAM_BPC_OK;""} | {13;PAM_BPC_PROMPT;"login: "} | {5;PAM_BPC_FAIL;} --------------------------------------------------------------- (F) | {16;PAM_BPC_PASS;"password: "} | {9;PAM_BPC_OK;"XYZ"} | {16;PAM_BPC_PASS;"password: "} | {6;PAM_BPC_OK;""} | {16;PAM_BPC_PASS;"password: "} | {5;PAM_BPC_FAIL;} (*) Used to attempt the removal of a pre-existing environment variable. 5.3 Client <-> server Once the client has established a connection with the server (the nature of the transport protocol is not specified by PAM), the server is responsible for driving the authentication process. General servers can request the following from the client: (to be forwarded by the client to the applicant) echo text string echo error text string prompt for echo'd text string response prompt for concealed text string response (to be forwarded by the client to the appropriate agent) binary prompt for a binary prompt response Client side agents are required to process binary prompts. The agents' binary prompt responses are returned to the server. 5.4 Server <-> module Modules drive the authentication process. The server provides a conversation function with which it encapsulates module-generated requests and exchanges them with the client. Every message sent by a module should be acknowledged. General conversation functions can support the following five conversation requests: echo text string echo error string prompt for echo'd text string response prompt for concealed text string response binary prompt for binary prompt response The server is responsible for redirecting these requests to the client. 6 C API for application interfaces (client and server) 6.1 Applicant <-> client No API is defined for this interface. The interface is considered to be specific to the client application. Example applications include terminal login, (X)windows login, machine file transfer applications. All that is important is that the client application is able to present the applicant with textual output and to receive textual input from the applicant. The forms of textual exchange are listed in an earlier section (5.1). Other methods of data input/output are better suited to being handled via an authentication agent. 6.2 Client <-> agent The client makes use of a general API for communicating with agents. The client is not required to communicate directly with available agents, instead a layer of abstraction (in the form of a library: libpamc) takes care of loading and maintaining communication with all requested agents. This layer of abstraction will choose which agents to interact with based on the content of binary prompts it receives that have the control type PAM_BPC_SELECT. 6.2.1 Client <-> libpamc 6.2.1.1 Compilation information The C-header file provided for client-agent abstraction is included with the following source line: #include The library providing the corresponding client-agent abstraction functions is, libpamc. cc .... -lpamc 6.2.1.2 Initializing libpamc The libpamc library is initialized with a call to the following function: pamc_handle_t pamc_start(void); This function is responsible for configuring the library and registering the location of available agents. The location of the available agents on the system is implementation specific. pamc_start() function returns NULL on failure. Otherwise, the return value is a pointer to an opaque data type which provides a handle to the libpamc library. On systems where threading is available, the libpamc libraray is thread safe provided a single (pamc_handler_t *) is used by each thread. 6.2.1.3 Client (Applicant) selection of agents For the purpose of applicant and client review of available agents, the following function is provided. char **pamc_list_agents(pamc_handle_t pch); This returns a list of pointers to the agent_id's of the agents which are available on the system. The list is terminated by a NULL pointer. It is the clients responsibility to free this memory area by calling free() on each agent id and the block of agent_id pointers in the result. PAM represents a server-driven authentication model, so by default any available agent may be invoked in the authentication process. 6.2.1.3.1 Client demands agent If the client requires that a specific authentication agent is satisfied during the authentication process, then the client should call the following function, immediately after obtaining a pamc_handle_t from pamc_start(). int pamc_load(pamc_handle_t pch, const char *agent_id); agent_id is a PAM text string (see section 4.2.2.1) and is not suffixed with a '/' delimiter. The return value for this function is: PAM_BPC_TRUE - agent located and loaded. PAM_BPC_FALSE - agent is not available. Note, although the agent is loaded, no data is fed to it. The agent's opportunity to inform the client that it does not trust the server is when the agent is shutdown. 6.2.1.3.2 Client marks agent as unusable The applicant might prefer that a named agent is marked as not available. To do this, the client would invoke the following function immediately after obtaining a pamc_handle_t from pam_start(). int pamc_disable(pamc_handle_t pch, const char *agent_id); here agent_id is a PAM text string containing an agent_id (section 4.2.2.1). The return value for this function is: PAM_BPC_TRUE - agent is disabled. This is the response independent of whether the agent is locally available. PAM_BPC_FALSE - agent cannot be disabled (this may be because it has already been invoked). 6.2.1.4 Allocating and manipulating binary prompts All conversation between an client and an agent takes place with respect to binary prompts. A binary prompt (see section 4.2.2), is obtained, resized and deleted via the following C-macro: CREATION of a binary prompt with control X1 and data length Y1: pamc_bp_t prompt = NULL; PAM_BP_RENEW(&prompt, X1, Y1); REPLACEMENT of a binary prompt with a control X2 and data length Y2: PAM_BP_RENEW(&prompt, X2, Y2); DELETION of a binary prompt (the referenced prompt is scrubbed): PAM_BP_RENEW(&prompt, 0, 0); Note, the PAM_BP_RENEW macro always overwrites any prompt that you call it with, deleting and liberating the old contents in a secure fashion. Also note that PAM_BP_RENEW, when returning a prompt of data size Y1>0, will always append a '\0' byte to the end of the prompt (at data offset Y1). It is thus, by definition, acceptable to treat the data contents of a binary packet as a text string (see 4.2.1). FILLING a binary prompt from a memory pointer U1 from offset O1 of length L1: PAM_BP_FILL(prompt, O1, L1, U1); the CONTROL type for the packet can be obtained as follows: control = PAM_PB_CONTROL(prompt); the LENGTH of a data within the prompt (_excluding_ its header information) can be obtained as follows: length = PAM_BP_LENGTH(prompt); the total SIZE of the prompt (_including_ its header information) can be obtained as follows: size = PAM_BP_SIZE(prompt); EXTRACTING data from a binary prompt from offset O2 of length L2 to a memory pointer U2: PAM_BP_EXTRACT(prompt, O2, L2, U2); If you require direct access to the raw prompt DATA, you should use the following macro: __u8 *raw_data = PAM_BP_DATA(prompt); 6.2.1.5 Client<->agent conversations All exchanges of binary prompts with agents are handled with the single function: int pamc_converse(pamc_handle_t *pch, pamc_bp_t *prompt_p); The return value for pamc_converse(...) is PAM_BPC_TRUE when there is a response packet and PAM_BPC_FALSE when the client is unable to handle the request represented by the original prompt. In this latter case, *prompt_p is set to NULL. This function takes a binary prompt and returns a replacement binary prompt that is either a request from an agent to be acted upon by the client or the 'result' which should be forwarded to the server. In the former case, the following macro will return 1 (PAM_BPC_TRUE) and in all other cases, 0 (PAM_BPC_FALSE): PAM_BPC_FOR_CLIENT(/* pamc_bp_t */ prompt) Note, all non-NULL binary prompts returned by pamc_converse(...), are terminated with a '\0', even when the full length of the prompt (as returned by the agent) does not contain this delimiter. This is a defined property of the PAM_BP_RENEW macro, and can be relied upon. Important security note: in certain implementations, agents are implemented by executable binaries, which are transparently loaded and managed by the PAM client library. To ensure there is never a leakage of elevated privilege to an unprivileged agent, the client application should go to some effort to lower its level of privilege. It remains the responsibility of the applicant and the client to ensure that it is not compromised by a rogue agent. 6.2.1.6 Termination of agents When closing the authentication session and severing the connection between a client and a selection of agents, the following function is used: int pamc_end(pamc_handle_t *pch); Following a call to pamc_end, the pamc_handle_t will be invalid. The return value for this function is one of the following: PAM_BPC_TRUE - all invoked agents are content with authentication (the server is _not_ judged _un_trustworthy by any agent) PAM_BPC_FALSE - one or more agents were unsatisfied at being terminated. In general, the client should terminate its connection to the server and indicate to the applicant that the server is untrusted. 6.2.2 libpamc <-> agents The agents are manipulated from within libpamc. Each agent is an executable in its own right. This permits the agent to have access to sensitive data not accessible directly from the client. The mode of communication between libpamc and an agent is through a pair of pipes. The agent reads binary prompts (section 4.2.2) through its standard input file descriptor and writes response (to the server) binary prompts and instruction binary prompts (instructions for the client) through its standard output file descriptor. 6.3 Client <-> server This interface is concerned with the exchange of text and binary prompts between the client application and the server application. No API is provided for this as it is considered specific to the transport protocol shared by the client and the server. 6.4 Server <-> modules The server makes use of a general API for communicating with modules. The client is not required to communicate directly with available modules. By abstracting the authentication interface, it becomes possible for the local administrator to make a run time decision about the authentication method adopted by the server. 6.4.1 Functions and definitions available to servers and modules [This section will document the following functions pam_set_item() pam_get_item() pam_fail_delay(pam_handle_t *pamh, unsigned int micro_sec) pam_get_env(pam_handle_t *pamh, const char *varname) pam_strerror(pam_handle_t *pamh, int pam_errno) ] 6.4.2 Server <-> libpam [This section will document the following pam_ calls: pam_start pam_end pam_authenticate (*) pam_setcred pam_acct_mgmt pam_open_session pam_close_session pam_chauthtok (*) The asterisked functions may return PAM_INCOMPLETE. In such cases, the application should be aware that the conversation function was called and that it returned PAM_CONV_AGAIN to a module. The correct action for the application to take in response to receiving PAM_INCOMPLETE, is to acquire the replies so that the next time the conversation function is called it will be able to provide the desired responses. And then recall pam_authenticate (pam_chauthtok) with the same arguments. Libpam will arrange that the module stack is resumed from the module that returned before. This functionality is required for programs whose user interface is maintained by an event loop. ] 6.4.3 libpam <-> modules [This section will document the following pam_ and pam_sm_ calls: functions provided by libpam pam_set_data pam_get_data functions provided to libpam by each module groups: AUTHENTICATION pam_sm_authenticate pam_sm_setcred ACCOUNT pam_sm_acct_mgmt SESSION pam_sm_open_session pam_sm_close_session AUTHENTICATION TOKEN MANAGEMENT pam_sm_chauthtok ] 7 Security considerations This document is devoted to standardizing authentication infrastructure: everything in this document has implications for security. 8 Contact The email list for discussing issues related to this document is . 9 References [1] OSF RFC 86.0, "Unified Login with Pluggable Authentication Modules (PAM)", October 1995 10 Author's Address Andrew G. Morgan Email: morgan@ftp.kernel.org $Id: current-draft.txt 19 2001-01-05 22:07:00Z kiavik $ yardradius-1.1.2/pam/pam_radius_session.c0000644000175000017500000001664510640412037015424 00000000000000/* pam_radius_session module */ /* * Written by Cristian Gafton 1997/07/23 * See the end of the file for Copyright Information */ #include #include #include #include #include #include #include #include /* for time() */ #include #include #include #include /* indicate the following groups are defined */ #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_SESSION #define PAM_SM_PASSWORD #include #include #include "../src/radius.h" /* some syslogging */ static void _pam_log(int err, const char *format, ...) { va_list args; va_start(args, format); openlog("PAM_radius_session", LOG_CONS|LOG_PID, LOG_AUTH); vsyslog(err, format, args); va_end(args); closelog(); } /* argument parsing */ #define PAM_DEBUG_ARG 0x0001 static int _pam_parse(int argc, const char **argv) { int ctrl = 0; /* step through arguments */ for (ctrl=0; argc-- > 0; ++argv) { /* generic options */ if (!strcmp(*argv,"debug")) ctrl |= PAM_DEBUG_ARG; else { _pam_log(LOG_ERR,"pam_parse: unknown option; %s",*argv); } } return ctrl; } /* * PAM framework looks for these entry-points to pass control to the * authentication module. */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { _pam_log(LOG_ERR, "there is no point using this module for auth services"); return PAM_IGNORE; } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { _pam_log(LOG_ERR, "there is no point using this module for auth services"); return PAM_IGNORE; } /* * PAM framework looks for these entry-points to pass control to the * account management module. */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { _pam_log(LOG_ERR, "there is no point using this module for account services"); return PAM_IGNORE; } /* * PAM framework looks for these entry-points to pass control to the * session module. */ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; struct pam_conv *conv; int ctrl; retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); ctrl = pam_parse(argc, argv); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not get the application conv data"); return PAM_SESSION_ERR; } else if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG, "opening radius session..."); if (ctrl & PAM_DEBUG_ARG) { VALUE_PAIR *pair; pair = (VALUE_PAIR *)(conv->appdata_ptr); while (pair != (VALUE_PAIR *)NULL) { switch (pair->type) { case PW_TYPE_STRING: _pam_log(LOG_DEBUG, "Item: %s, value: %s\n", pair->name, pair->strvalue); break; case PW_TYPE_INTEGER: _pam_log(LOG_DEBUG, "Item: %s, value: %d\n", pair->name, pair->lvalue); break; case PW_TYPE_IPADDR: _pam_log(LOG_DEBUG, "Item: %s, value: %d.%d.%d.%d\n", pair->name, (pair->lvalue >> 24) & 0xFF, (pair->lvalue >> 16) & 0xFF, (pair->lvalue >> 8) & 0xFF, pair->lvalue & 0xFF); break; default: _pam_log(LOG_DEBUG, "Item: %s, value: (unknown: UINT4=%d, STRING='%s')\n", pair->name, pair->lvalue, pair->strvalue); } pair = pair->next; } } if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG, "done opening radius session"); return PAM_SUCCESS; } PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) { int retval; struct pam_conv *conv; int ctrl; ctrl = _pam_parse(argc, argv); retval = pam_get_item(pamh, PAM_CONV, (const void **) &conv); if (retval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not get the application conv data"); return PAM_SESSION_ERR; } else if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG, "closing radius session..."); if (ctrl & PAM_DEBUG_ARG) { VALUE_PAIR *pair; pair = (VALUE_PAIR *)(conv->appdata_ptr); while (pair != (VALUE_PAIR *)NULL) { switch (pair->type) { case PW_TYPE_STRING: _pam_log(LOG_DEBUG, "Item: %s, value: %s\n", pair->name, pair->strvalue); break; case PW_TYPE_INTEGER: _pam_log(LOG_DEBUG, "Item: %s, value: %d\n", pair->name, pair->lvalue); break; case PW_TYPE_IPADDR: _pam_log(LOG_DEBUG, "Item: %s, value: %d.%d.%d.%d\n", pair->name, (pair->lvalue >> 24) & 0xFF, (pair->lvalue >> 16) & 0xFF, (pair->lvalue >> 8) & 0xFF, pair->lvalue & 0xFF); break; default: _pam_log(LOG_DEBUG, "Item: %s, value: (unknown: UINT4=%d, STRING='%s')\n", pair->name, pair->lvalue, pair->strvalue); } pair = pair->next; } } if (ctrl & PAM_DEBUG_ARG) _pam_log(LOG_DEBUG, "done closing radius session"); return PAM_SUCCESS; } /* * PAM framework looks for these entry-points to pass control to the * password changing module. */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { _pam_log(LOG_ERR, "there is no point using this module for password services"); return PAM_IGNORE; } #ifdef PAM_STATIC /* static module data */ struct pam_module _pam_radius_session_modstruct = { "pam_radius_session", pam_sm_authenticate, pam_sm_setcred, pam_sm_acct_mgmt, pam_sm_open_session, pam_sm_close_session, pam_sm_chauthtok, }; #endif /* * Copyright (c) Cristian Gafton , 1997. * All rights reserved * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED `AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ yardradius-1.1.2/pam/Makefile.am0000644000175000017500000000023310640412037013407 00000000000000AUTOMAKE_OPTIONS = foreign INSTALL = @INSTALL@ pamdir = $(YARDDOC)/pam pam_DATA = pam.conf pam_radius_session.c Makefile-linux README current-draft.txt yardradius-1.1.2/pam/Makefile.in0000644000175000017500000002256310640702511013431 00000000000000# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, # 2003, 2004, 2005 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = pam DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(SHELL) $(top_srcdir)/autotools/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = SOURCES = DIST_SOURCES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; am__installdirs = "$(DESTDIR)$(pamdir)" pamDATA_INSTALL = $(INSTALL_DATA) DATA = $(pam_DATA) DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) INSTALL = @INSTALL@ ACLOCAL = @ACLOCAL@ AMDEP_FALSE = @AMDEP_FALSE@ AMDEP_TRUE = @AMDEP_TRUE@ AMTAR = @AMTAR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CC_VERSION = @CC_VERSION@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ MKDIR = @MKDIR@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ YARDCONF = @YARDCONF@ YARDDOC = @YARDDOC@ YARDLOG = @YARDLOG@ ac_ct_CC = @ac_ct_CC@ am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ sysconfdir = @sysconfdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ AUTOMAKE_OPTIONS = foreign pamdir = $(YARDDOC)/pam pam_DATA = pam.conf pam_radius_session.c Makefile-linux README current-draft.txt all: all-am .SUFFIXES: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pam/Makefile'; \ cd $(top_srcdir) && \ $(AUTOMAKE) --foreign pam/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh uninstall-info-am: install-pamDATA: $(pam_DATA) @$(NORMAL_INSTALL) test -z "$(pamdir)" || $(mkdir_p) "$(DESTDIR)$(pamdir)" @list='$(pam_DATA)'; for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ f=$(am__strip_dir) \ echo " $(pamDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pamdir)/$$f'"; \ $(pamDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pamdir)/$$f"; \ done uninstall-pamDATA: @$(NORMAL_UNINSTALL) @list='$(pam_DATA)'; for p in $$list; do \ f=$(am__strip_dir) \ echo " rm -f '$(DESTDIR)$(pamdir)/$$f'"; \ rm -f "$(DESTDIR)$(pamdir)/$$f"; \ done tags: TAGS TAGS: ctags: CTAGS CTAGS: distdir: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ case $$file in \ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ esac; \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ if test "$$dir" != "$$file" && test "$$dir" != "."; then \ dir="/$$dir"; \ $(mkdir_p) "$(distdir)$$dir"; \ else \ dir=''; \ fi; \ if test -d $$d/$$file; then \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ fi; \ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ else \ test -f $(distdir)/$$file \ || cp -p $$d/$$file $(distdir)/$$file \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(pamdir)"; do \ test -z "$$dir" || $(mkdir_p) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ `test -z '$(STRIP)' || \ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am info: info-am info-am: install-data-am: install-pamDATA install-exec-am: install-info: install-info-am install-man: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-info-am uninstall-pamDATA .PHONY: all all-am check check-am clean clean-generic distclean \ distclean-generic distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-exec \ install-exec-am install-info install-info-am install-man \ install-pamDATA install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ uninstall-am uninstall-info-am uninstall-pamDATA # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: yardradius-1.1.2/pam/Makefile-linux0000644000175000017500000000154610640412037014160 00000000000000# # Makefile for the pam_radius_session module # TITLE = pam_radius_session FILES = pam_radius_session ####################### don't edit below ####################### CC = gcc WARNINGS = -ansi -D_POSIX_SOURCE -Wall -Wwrite-strings -Wpointer-arith \ -Wcast-qual -Wcast-align -Wtraditional -Wstrict-prototypes \ -Wmissing-prototypes -Wnested-externs -Winline -Wshadow -pedantic \ -D_LINUX_GLIBC -D_GNU_SOURCE PIC = -fPIC LD_FLAGS = -shared -Xlinker -x CFLAGS = $(WARNINGS) DYNAMIC = -DPAM_DYNAMIC LIBSRC = $(addsuffix .c,$(FILES)) LIBOBJ = $(addsuffix .o,$(FILES)) %.o : %.c Makefile $(CC) $(CFLAGS) $(DYNAMIC) $(PIC) -c $< -o $@ LIBSHARED = $(TITLE).so all: $(LIBSHARED) $(LIBSHARED): $(LIBOBJ) $(CC) $(LD_FLAGS) $(PIC) -o $@ $(LIBOBJ) clean: rm -f $(LIBOBJ) $(LIBSHARED) core *~ install: all install -m 0644 -o root -g root $(LIBSHARED) /lib/security yardradius-1.1.2/src/0000777000175000017500000000000010640702676011466 500000000000000yardradius-1.1.2/src/extens.c0000644000175000017500000014053110640412013013037 00000000000000/* * Copyright (C) 1999-2004 Francesco P. Lovergine. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms stated in the LICENSE file which should be * enclosed with sources. */ static char rcsid[] = "$Id: extens.c 83 2004-08-28 13:32:47Z flovergine $"; #include "yard.h" #include "global.h" /* This structure is used for 3COM boxes */ static int usr_speeds[53]={ 0, 300, 1200, 2400, 4800, 7200, 9600, 12000, 14400, 16800, 19200, 21600, 28800, 38400, 57600, 115200, 288000, 751200, 120075, 24000, 26400, 31200, 33600, 33333, 37333, 41333, 42666, 44000, 45333, 46666, 48000, 49333, 50666, 52000, 53333, 54666, 56000, 57333, 64000, 25333, 26666, 28000, 29333, 30666, 32000, 34666, 36000, 38666, 40000, 58666, 60000, 61333, 62666 }; /* * check_logins - see if maximum number of logins was not reached */ int check_logins(char *user, const int max_logins) { char dbfile_name[PATH_MAX]; GDBM_FILE dbf; datum key, content; user_entry *ue; struct tm *time_info; time_t crt_time = time(NULL); time_info = localtime(&crt_time); snprintf(dbfile_name,sizeof(dbfile_name), "%s/%d/%s", radacct_dir,1900+time_info->tm_year,RADIUS_USER_STATS); dbf = gdbm_open(dbfile_name,0,GDBM_READER,0600,NULL); if (dbf == NULL) { return 0; } /* Build the key */ key.dptr = user; key.dsize = strlen(user); content = gdbm_fetch(dbf,key); if (content.dptr == NULL) { /* not here, at least one login is allowed */ gdbm_close(dbf); return 0; } ue = (user_entry *)content.dptr; if (ue->logins >= max_logins) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return 0; } /* * check_maxtime - see if maximum DAILY/MONTHLY/YEARLY online time is reached */ int check_maxtime(char *user, const int hours, const int kind) { char dbfile_name[PATH_MAX]; GDBM_FILE dbf; datum key, content; user_entry *ue; struct tm *time_info; UINT4 counter; int i,j; time_t crt_time = time(NULL); time_info = localtime(&crt_time); snprintf(dbfile_name, sizeof(dbfile_name), "%s/%d/%s", radacct_dir,1900+time_info->tm_year,RADIUS_USER_STATS); dbf = gdbm_open(dbfile_name,0,GDBM_READER,0600,NULL); if (dbf == NULL) { return 0; } /* Build the key */ key.dptr = user; key.dsize = strlen(user); content = gdbm_fetch(dbf,key); if (content.dptr == NULL) { /* not here, login is allowed */ gdbm_close(dbf); return 0; } ue = (user_entry *)content.dptr; switch ( kind ) { case DAY_LIMIT: if (ue->day[time_info->tm_mon][time_info->tm_mday-1].on_line >= hours*3600) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; case MONTH_LIMIT: for ( i=0, counter=0; itm_mday; i++ ) counter += ue->day[time_info->tm_mon][i].on_line; if ( counter >= hours*3600 ) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; case YEAR_LIMIT: for ( i=0, counter=0; i<=time_info->tm_mon; i++ ) for ( j=0; itm_mday; i++ ) counter += ue->day[i][j].on_line; if ( counter >= hours*3600 ) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; default: log_err("internal error: invalid kind of limit in a" "check_maxtime() call\n"); break; } gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return 0; } /* * clean_user_stats() * * initializes the user_stats database (clear the number of concurent logins) * to avoid starting a new radiusd server with invalid data * */ int clean_user_stats(void) { char dbfile_name[PATH_MAX]; GDBM_FILE dbf; datum key, content, nextkey; struct tm *time_info; time_t crt_time = time(NULL); time_info = localtime(&crt_time); memset(dbfile_name, 0, PATH_MAX); snprintf(dbfile_name, sizeof(dbfile_name), "%s/%d/%s", radacct_dir,1900+time_info->tm_year,RADIUS_USER_STATS); dbf = gdbm_open(dbfile_name,0,GDBM_WRITER|GDBM_SYNC,0600,NULL ); if (dbf == NULL) { /* we couldn't open the databse. That's "okay" in this case... */ return 0; } key = gdbm_firstkey(dbf); while (key.dptr != NULL) { user_entry *p_ue; int retval; content = gdbm_fetch(dbf, key); if (content.dptr == NULL) { nextkey = gdbm_nextkey(dbf,key); free(key.dptr); key = nextkey; continue; } p_ue = (user_entry *)content.dptr; p_ue->logins = 0; content.dsize = sizeof(user_entry); /* rip off any port_entry */ /* update this modified entry */ retval = gdbm_store(dbf, key, content, GDBM_REPLACE); if (retval != 0) { /* odd error updating the database */ key.dptr[key.dsize] = '\0'; log_err("could not reset user_stats entry for '%s'\n", (char *)key.dptr); gdbm_close(dbf); if (content.dptr != NULL) free(content.dptr); if (key.dptr != NULL) free(key.dptr); return -1; } /* update went okay */ if (content.dptr != NULL) free(content.dptr); nextkey = gdbm_nextkey(dbf,key); free(key.dptr); key = nextkey; } gdbm_close(dbf); /* close the database */ /* finished */ return 0; } /* * deny_user returns: * -2 if user is present in RADIUS_DIR/RADIUS_DENY * -3 if user is present in RADIUS_DIR/RADIUS_STOP * 1 if no such user (misa) * 0 if okay * * allow_user returns: * -4 if user is NOT present in RADIUS_DIR/RADIUS_ALLOW * 0 if okay */ /* Function: listed_on_file() Purpose: it matches a user against many matching criteria: GROUP: the user belongs to a certain group SHELL: the user has a certain shell GECOS: the user has a Gecos field which _contains_ a certain string USER: the user has a specific name. Notes: string match is case-sensitive. Maybe a regex-match could be interesting. A value of ANY match any user. Francesco Lovergine */ static int listed_on_file(const char *filename, const char *username) { FILE *fp; char buffer[1024]; char value[1024]; static char special[]="ANY"; if ((fp=fopen(filename, "r")) != NULL) { while (fgets(buffer, sizeof(buffer), fp) != NULL) { if ( strlen(buffer)==0 || buffer[0] == '\n' || buffer[0] == '#' || buffer[0] == ';' ) continue; else if ( sscanf(buffer,"USER: %s",value ) == 1 ) { if (strcmp(value,special)==0 || strcmp(username,value) == 0) { fclose(fp); return -1; } } else if ( sscanf(buffer,"GROUP: %s",value ) == 1 ) { if (strcmp(value,special)==0 || unix_group(username,value)) { fclose(fp); return -1; } } else if ( sscanf(buffer,"GECOS: %s",value ) == 1 ) { if (strcmp(value,special)==0 || (strlen(value) && strstr(unix_gecos(username),value)!=NULL) ) { fclose(fp); return -1; } } else if ( sscanf(buffer,"SHELL: %s",value ) == 1 ) { if (strcmp(value,special)==0 || (strlen(value) && strcmp(unix_shell(username),value)==0)) { fclose(fp); return -1; } } else log_err("warning: syntax error in file '%s', line '%s' ignored", filename,buffer); } fclose(fp); } return 0; } /* * The actual function is quite simple ... */ int deny_user(const char *username) { char file_name[PATH_MAX]; char buffer[1024]; if (!username || (strlen(username) >= sizeof(buffer))) { /* well, an error, but for this function purpose * should respond with okay... I hate this... */ return 0; } /* first try to open RADIUS_DIR/RADIUS_DENY */ snprintf(file_name, sizeof(file_name), "%s/%s", radius_dir, RADIUS_DENY); if (listed_on_file(file_name, username) < 0) return -2; snprintf(file_name, sizeof(file_name), "%s/%s", radius_dir, RADIUS_STOP); if (listed_on_file(file_name, username) < 0) return -3; return 0; } int allow_user(const char *username) { char file_name[PATH_MAX]; char buffer[1024]; if (!username || (strlen(username) >= sizeof(buffer))) { return -4; } snprintf(file_name,sizeof(file_name),"%s/%s", radius_dir, RADIUS_ALLOW); if (listed_on_file(file_name, username) < 0) return 0; return -4; } /* * Builds a radlast structure from an AUTHREQ_HDR packet */ static int build_radlast_from_authreq(radlast *rl, AUTH_REQ *authreq) { int status_type = 0; time_t crt_time; VALUE_PAIR *pair; int vj = 0; crt_time = time(NULL); if (!rl || !authreq || !authreq->request) return -1; pair = authreq->request; memset(rl, 0, sizeof(radlast)); rl->ut_time = crt_time; while (pair != (VALUE_PAIR *)NULL) { switch (pair->attribute) { case PW_ACCT_STATUS_TYPE: status_type = pair->lvalue; break; case PW_USER_NAME: strncpy(rl->login, (char *)pair->strvalue, sizeof(rl->login)); break; case PW_CLIENT_PORT_ID: rl->ent.port = pair->lvalue; break; case PW_FRAMED_ADDRESS: case PW_LOGIN_HOST: rl->client_ip = pair->lvalue; break; case PW_ACCT_SESSION_TIME: rl->length = pair->lvalue; rl->ut_time -= pair->lvalue; break; case PW_ACCT_INPUT_OCTETS: rl->inb = pair->lvalue; break; case PW_ACCT_OUTPUT_OCTETS: rl->outb = pair->lvalue; break; case PW_CLIENT_ID: rl->nas_ip = pair->lvalue; break; case PW_NAS_PORT_TYPE: rl->ent.port_type = pair->lvalue; break; case PW_LOGIN_SERVICE: switch ((int)(pair->lvalue)) { case PW_TELNET: rl->ent.proto = P_TELNET; break; case PW_RLOGIN: rl->ent.proto = P_RLOGIN; break; case PW_TCP_CLEAR: rl->ent.proto = P_TCP_CLEAR; break; case PW_PORTMASTER: rl->ent.proto = P_PORTMASTER; break; default: rl->ent.proto = P_LOGIN_UNK; } break; case PW_FRAMED_PROTOCOL: switch ((int)(pair->lvalue)) { case PW_PPP: rl->ent.proto = P_PPP; break; case PW_SLIP: rl->ent.proto = P_SLIP; break; default: rl->ent.proto = P_FRAMED_UNK; } break; case PW_ACCT_DELAY_TIME: rl->ut_time -= pair->lvalue; break; case PW_FRAMED_COMPRESSION: vj++; break; case PW_ACCT_TERMINATE_CAUSE: rl->ent.term_cause = pair->lvalue; break; case PW_ACCT_CALLED_STATION_ID: strncpy(rl->calledid,(char*)pair->strvalue, sizeof(rl->calledid)); break; case PW_ACCT_CALLING_STATION_ID: strncpy(rl->callingid,(char*)pair->strvalue, sizeof(rl->callingid)); break; /* Next attributes are VSAs */ case PW_ACCT_ASCEND_DATA_RATE: rl->rxrate = pair->lvalue; break; case PW_ACCT_ASCEND_XMIT_RATE: rl->txrate = pair->lvalue; break; case PW_CONNECT_INFO_OLD: rl->txrate = (UINT4)atol(pair->strvalue); rl->rxrate = rl->txrate; break; case PW_ACCT_USR_CONNECT_SPEED: rl->rxrate = rl->txrate = usr_speeds[pair->lvalue%54 - 1]; break; case PW_CONNECT_INFO: sscanf(pair->strvalue,"%d/%d%*s",&rl->txrate,&rl->rxrate); break; }; pair = pair->next; }; if ((rl->ent.proto == P_SLIP) && vj) rl->ent.proto = P_CSLIP; return status_type; } /* * Updates the user entry from the user_stats databse (add one more login * or clear out an entry... */ static int update_db_record(int action, datum *content, radlast *rl,int month, int day) { void *data; int datasize; user_entry *tmp = (user_entry *)(content->dptr); port_entry *tpe; /* update fields */ tmp->day[month][day].on_line += rl->length; tmp->day[month][day].input_octets += rl->inb; tmp->day[month][day].output_octets += rl->outb; /* now it is time to check if this is a start or stop */ if (action == PW_STATUS_START) { tmp->day[month][day].nr_logins++; tmp->logins++; /* okay, start a new one ... */ datasize = content->dsize + sizeof(port_entry); data = (void *) malloc(datasize); if (data==NULL) { /* malloc error */ log_err("could not malloc for handling user acct\n"); content->dptr = NULL; return -1; } /* we have the required memory */ memcpy(data,content->dptr,content->dsize); /* paste the original data */ tpe = (port_entry *)((char *)data + content->dsize); /* store the new entry */ tpe->time = rl->ut_time; tpe->client_ip = rl->client_ip; tpe->port_type = rl->ent.port_type; tpe->port_number = rl->ent.port; tpe->nas_ip = rl->nas_ip; tpe->proto = rl->ent.proto; } else { /* this is a stop record */ int port_found = 0; /* now it is ugly ... */ datasize = sizeof(user_entry); /* do not attemp to take more */ data = (void *)malloc(content->dsize); /* make enough room */ if (data==NULL) { /* malloc error */ log_err("could not malloc for handling user acct\n"); content->dptr = NULL; return -1; } /* memory is okay */ if (content->dsize > sizeof(user_entry)) { /* do we have any port entry to rip off ? */ int ports_copied = 0; int nr_port_entry = 0; port_entry *dptr = (port_entry *)((char*)data+sizeof(user_entry)); /* go to first port entry */ tpe = (port_entry *)((char *)(content->dptr) + sizeof(user_entry)); /* be super-safe */ nr_port_entry = (content->dsize-sizeof(user_entry))/sizeof(port_entry); if (nr_port_entry*sizeof(port_entry)+sizeof(user_entry) != content->dsize) { /* somehow we've managed to break the port entries, we * don't have a valid number of entries */ log_err("ERROR: database is inconsistent for user '%s'." " Cleaned up.\n",rl->login); /* clean up the mess */ tmp->logins = 0; } else { /* we have a valid number of port entries, * now just in case tmp->logins have a different opinion ... */ if (tmp->logins != nr_port_entry) log_err("ERROR: database not okay for '%s': velived %d" "logins, have only %d\n", rl->login, tmp->logins, nr_port_entry); tmp->logins = nr_port_entry; } while (ports_copied < tmp->logins) { /* is this the port we are looking for ? */ if ((tpe->nas_ip!=rl->nas_ip) || (tpe->port_type != rl->ent.port_type) || (tpe->port_number != rl->ent.port) || port_found) { /* not our port */ memcpy((void *)dptr, (void *)tpe, sizeof(port_entry)); dptr++; datasize += sizeof(port_entry); } else /* port found */ port_found++; /* skip the current port */ tpe++; /* count any port as a copied one */ ports_copied++; } } if (port_found) tmp->logins--; /* all done */ memcpy(data, content->dptr, sizeof(user_entry)); /* paste orig. data */ } /* Now no matter what *data contains our data, with a size of datasize */ if ( content->dptr != NULL ) free(content->dptr); content->dsize = datasize; content->dptr = (char*)data; return 0; } /* * inserts a new entry for the username in the user_stats database */ static int insert_db_record(int action,datum *content,radlast *rl,int month,int day) { user_entry *tmp; content->dsize = sizeof(user_entry); if (action == PW_STATUS_START) content->dsize += sizeof(port_entry); tmp = (user_entry *)malloc(content->dsize); if (tmp == NULL) { log_err("insert_db_record(): malloc error!\n"); content->dptr = NULL; return -2; } /* init things */ memset((char *)tmp, 0, sizeof(user_entry)); tmp->day[month][day].on_line += rl->length; tmp->day[month][day].input_octets += rl->inb; tmp->day[month][day].output_octets += rl->outb; if (action == PW_STATUS_START) { port_entry *tpe; tmp->logins = 1; tmp->day[month][day].nr_logins = 1; /* get the port_entry pointer */ tpe = (port_entry *)((char *)tmp + sizeof(user_entry)); tpe->time = rl->ut_time; tpe->client_ip = rl->client_ip; tpe->port_type = rl->ent.port_type; tpe->port_number = rl->ent.port; tpe->nas_ip = rl->nas_ip; tpe->proto = rl->ent.proto; } content->dptr = (char *)tmp; return 0; } /* * Updates the radlast logs... */ extern int sockfd; extern int acctfd; static int update_radlast(radlast *rl) { pid_t child_pid; struct tm *time_info; { time_t crt_time = time(NULL); time_info = localtime(&crt_time); } /* fork a child to handle this job */ child_pid = fork(); if (child_pid < 0) { log_err("severe: Cannot fork() to writte radlast information\n"); return -1; } if (child_pid > 0) /* in the parent all is okay */ return 0; /* now in the child context do the lastlog update */ if (child_pid == 0) { FILE *fp; char log_file[PATH_MAX]; /* first close the acct and sock fd, we don't listen here */ close(acctfd); acctfd = -1; close(sockfd); sockfd = -1; memset(log_file, 0, sizeof(log_file)); snprintf(log_file, sizeof(log_file), "%s/%d/%s-%02d", radacct_dir, 1900+time_info->tm_year, RADIUS_LAST, time_info->tm_mon+1); /* extra safety never hurts */ umask(0027); /* now we should be safe */ if ((fp = fopen(log_file, "a")) != NULL) { if (fwrite(rl, sizeof(radlast), 1, fp) < 1) log_err("Could not write lastlog info for %s to %s\n", rl->login, log_file); else fclose(fp); } else log_err("Cannot open lastlog file '%s' for logging\n",log_file); exit(0); } return 0; } /* * * Function: update_user_status * * Given an acct packet, it updates the internal databse structure to be in * sync with the reality... * --cristiang */ int update_user_status(AUTH_REQ *authreq) { GDBM_FILE dbf; int retval; datum key, content; int flag = GDBM_INSERT; char dbfile_name[PATH_MAX]; radlast rl; int action; struct tm *time_info; if (!authreq || !authreq->request) return -1; /* Now build the radlast packet ... */ action = build_radlast_from_authreq(&rl, authreq); if (!strlen(rl.login)) return -1; if ((action != PW_STATUS_STOP) && (action != PW_STATUS_START)) return -1; time_info = localtime(&rl.ut_time); /* Use start session time */ debug("acct: %s user='%s', port=%d, time=%d, input=%d, output=%d\n", (action==PW_STATUS_START)?"start":"stop", rl.login, rl.ent.port, rl.length, rl.inb, rl.outb); /* Prepare the DB filename... */ snprintf(dbfile_name,sizeof(dbfile_name),"%s/%d",radacct_dir,1900+time_info->tm_year); mkdir(dbfile_name,0755); snprintf(dbfile_name,sizeof(dbfile_name), "%s/%d/%s", radacct_dir,1900+time_info->tm_year,RADIUS_USER_STATS); dbf = gdbm_open(dbfile_name,0,GDBM_WRCREAT|GDBM_SYNC,0600,NULL ); if (dbf == NULL) { log_err("Could not open database %s for updating user stats", dbfile_name); return -1; } /* Build the key */ key.dptr = rl.login; key.dsize = strlen(rl.login); /* Search for a previous entry */ content = gdbm_fetch(dbf, key); if (content.dptr != NULL) { /* found */ retval = update_db_record(action, &content, &rl, time_info->tm_mon,time_info->tm_mday-1 ); if (retval < 0) { gdbm_close(dbf); if (content.dptr != NULL) free(content.dptr); return retval; } flag = GDBM_REPLACE; } else { /* key not found, insert new */ retval = insert_db_record(action, &content, &rl, time_info->tm_mon,time_info->tm_mday-1 ); if (retval < 0) { gdbm_close(dbf); if (content.dptr != NULL) free(content.dptr); return retval; } flag = GDBM_INSERT; } retval = gdbm_store(dbf, key, content, flag); gdbm_close(dbf); if (content.dptr != NULL) free(content.dptr); if (retval != 0) { log_err("acct: could not store entry for '%s' (session %s)\n", rl.login, (action==PW_STATUS_START)?"start":"stop"); return -2; } if (action == PW_STATUS_STOP) retval = update_radlast(&rl); return retval; } /* * shadow_expire - checks if an account is expired according to * the shadow aging fields */ #if defined(SHADOW_EXPIRATION) && defined(SHADOW) #ifndef DAY #define DAY (3600*24) #endif int shadow_expired(const char *user) { struct spwd *spw; int crt_day = time(NULL)/DAY; /* today date */ int expire; if ((user == NULL) || !strlen(user)) { /* invalid call to this function */ debug("shadow_expired: called with null argument\n"); /* but the account is not expired... */ return 0; } debug("shadow_expired: checking shadow expiration for user '%s'\n", user); setspent(); spw = getspnam(user); if (spw == NULL) { /* Oops, sorry, all missing accounts are not expired :-) */ endspent(); return 0; } if ((spw->sp_expire > 0) && (spw->sp_expire < crt_day)) { /* Expired */ endspent(); return -1; } if (spw->sp_lstchg == 0) { /* expired by root */ endspent(); return -1; } if ((spw->sp_max <= 0) || (spw->sp_max == 99999)) { /* password never expires */ endspent(); return 0; } if (spw->sp_lstchg + spw->sp_max + spw->sp_inact < crt_day) { /* password expired - password aged */ endspent(); return -1; } /* be nice and also report when password is due to expire */ expire = spw->sp_lstchg + spw->sp_max + spw->sp_inact - crt_day; if ((expire >= 0) && (expire <= spw->sp_warn)) { /* warn the looser */ endspent(); return expire; } endspent(); return 0; } #endif #if defined(PAM) && defined(HAVE_LIBPAM) /* local variables */ static const char *PAM_username; static const char *PAM_password; /* * Function: PAM_conv * Purpose: Dialogue between RADIUS and PAM modules. */ static int PAM_conv_acct(int num_msg, const struct pam_message **msg,struct pam_response **resp, void *appdata_ptr) { int count = 0, replies = 0; struct pam_response *reply = NULL; int size = sizeof(struct pam_response); #define GET_MEM \ if (reply) realloc(reply, size); \ else reply = malloc(size); \ if (!reply) return PAM_CONV_ERR; \ size += sizeof(struct pam_response); #define COPY_STRING(s) (s) ? strdup(s) : NULL for (count = 0; count < num_msg; count++) { switch (msg[count]->msg_style) { case PAM_PROMPT_ECHO_ON: GET_MEM; reply[replies].resp_retcode = PAM_SUCCESS; reply[replies++].resp = COPY_STRING(PAM_username); /* PAM frees resp */ break; case PAM_PROMPT_ECHO_OFF: GET_MEM; reply[replies].resp_retcode = PAM_SUCCESS; reply[replies++].resp = COPY_STRING(PAM_password); /* PAM frees resp */ break; case PAM_TEXT_INFO: /* ignore it... */ break; case PAM_ERROR_MSG: default: /* Must be an error of some sort... */ free (reply); return PAM_CONV_ERR; } } if (reply) *resp = reply; return PAM_SUCCESS; } static struct pam_conv conv_acct = { (int (*)())PAM_conv_acct, NULL }; /* * Function: unix_pam * Purpose: Check the users password against the standard UNIX * password table + PAM. */ int unix_pam(const char *name, const char *passwd, const char *pamauth) { pam_handle_t *pamh=NULL; int retval; PAM_username = name; PAM_password = passwd; debug("unix_PAM: using pamauth string <%s> for pam.conf lookup\n", pamauth); retval = pam_start(pamauth, name, &conv_acct, &pamh); if (retval == PAM_SUCCESS) { debug("unix_PAM: function pam_start succeeded for <%s>\n", name); retval = pam_authenticate(pamh, 0); } if (retval == PAM_SUCCESS) { debug("unix_PAM: function pam_authenticate succeeded for <%s>\n", name); retval = pam_acct_mgmt(pamh, 0); } /* do not remove block braces, it could break things due to debug * macro */ if (retval == PAM_SUCCESS) { debug("unix_PAM: function pam_acct_mgmt succeeded for <%s>\n", name); } else { debug("unix_PAM: PAM FAILED for <%s> failed\n", name); } if (pam_end(pamh, retval) != PAM_SUCCESS) { pamh = NULL; log_err("ERROR!!!: PAM failed to release authenticator items"); } return (retval == PAM_SUCCESS)?0:-1; } /* * Function: PAM_conv_session * Purpose: A dummy conversation function between the radiusd and the * session modules. */ static int PAM_conv_session(int num_msg, const struct pam_message **msg,struct pam_response **resp, void *appdata_ptr) { /* this one should never be called; use appdata_ptr instead */ return PAM_CONV_ERR; } /* * We use this structure to pass the network packet pairs to * the session module(s) */ static struct pam_conv conv_session = { (int (*)())PAM_conv_session, NULL }; /* * This is to save the handle of the PAM session betwenn incantations... */ static pam_handle_t *PAM_handle = NULL; /* * Function: pam_session * Purpose: This is called when we are using PAM to inform the PAM * library about the new sessions being open or closed by * the NASes. */ void pam_session(AUTH_REQ *authreq) { char *username = NULL; VALUE_PAIR *pair; int retval; pam_handle_t *pamh = NULL; int type=0; /* First, get some important data: username and the type of the packet */ pair = authreq->request; while (pair != (VALUE_PAIR *)NULL) { switch (pair->attribute) { case PW_USER_NAME: username = pair->strvalue; break; case PW_ACCT_STATUS_TYPE: type = pair->lvalue; break; } pair = pair->next; } /* To do: * -we should maybe look at the NAS ip and set the * PAM_RHOST item too. But for now it is not critical, * and I am lazy.. * --cristiang */ if ((username == NULL) || !type) /* nothing to do */ return; /* initialize the conv_session struct */ conv_session.appdata_ptr = authreq->request; /* we avoid to open to many sessions with pam_start because of the * overhead induced by this call. On a busy accounting server, * this is critical. We open just one session and reuse it later... */ if (PAM_handle == (pam_handle_t *)NULL) { /* we need to open a new session */ debug("about to init PAM session call for %s\n", username); retval = pam_start(RADIUS_PAM_SERVICE, username, &conv_session, &pamh); if (retval != PAM_SUCCESS) { #if !defined(OLD_PAM) log_err("cannot initialize PAM session for %s (%s)\n", username, pam_strerror(PAM_handle, retval)); #else log_err("cannot initialize PAM session for %s (%s)\n", username, pam_strerror(retval)); #endif return; } else /* opened successfuly - save it for later use... */ PAM_handle = pamh; } else { /* we have already an open session - use that... */ pamh = PAM_handle; /* and update the username */ retval = pam_set_item(pamh, PAM_USER, username); if (retval != PAM_SUCCESS) { #if !defined(OLD_PAM) /* The new pam libraries have 2 params for pam_strerr - misa*/ log_err("can not set username (%s) for acct packet: %s\n", username, pam_strerror(PAM_handle, retval)); #else log_err("can not set username (%s) for acct packet: %s\n", username, pam_strerror(retval)); #endif /*OLD_PAM*/ return; } /* and the conversation structure, to get appdata pointer * updated with the new pointer to the network packet... */ retval = pam_set_item(pamh, PAM_CONV, &conv_session); if (retval != PAM_SUCCESS) { #if !defined(OLD_PAM) /* The new pam libraries have 2 params for pam_strerr - misa*/ log_err("can not pass acct packet for %s: %s\n", username, pam_strerror(PAM_handle, retval)); #else log_err("can not pass acct packet for %s: %s\n", username, pam_strerror(retval)); #endif /*OLD_PAM*/ return; } } /* all good, now do we start or close a session ? */ if (type == PW_STATUS_START) { /* start session */ debug("opening session for user %s\n", username); retval = pam_open_session(pamh, 0); if (retval != PAM_SUCCESS) { #if !defined(OLD_PAM) /* The new pam libraries have 2 params for pam_strerr - misa*/ log_err("could not start session for user %s (%s)\n", username, pam_strerror(PAM_handle, retval)); #else log_err("could not start session for user %s (%s)\n", username, pam_strerror(retval)); #endif /*OLD_PAM*/ return; } } if (type == PW_STATUS_STOP) { /* close session */ debug("closing session for user %s\n", username); retval = pam_close_session(pamh, 0); if (retval != PAM_SUCCESS) { #if !defined(OLD_PAM) /* The new pam libraries have 2 params for pam_strerr - misa*/ log_err("could not close session for user %s (%s)\n", username, pam_strerror(PAM_handle, retval)); #else log_err("could not close session for user %s (%s)\n", username, pam_strerror(retval)); #endif /*OLD_PAM*/ return; } } /* we don't call pam_end because this code is not supposed to die()... * --cristiang */ return; } #else #if defined(PAM) && !defined(HAVE_LIBPAM) #error "PAM development library missed. Check your configuration." #endif #endif /* PAM */ /* * SOME BASIC PACKET QUEUE MANAGEMENT STUFF * * Idea: eliminate the duplicate acct packets problem and sort out the * out-of-order packets to get better accounting */ static acct_packet *root_acct_packet = NULL; static int acct_queue_entries = 0; static acct_packet * in_acct_queue(acct_packet *packet, int type) { acct_packet *this; this = root_acct_packet; while (this != (acct_packet *)NULL) { if ( (this->port == packet->port) && (this->nas_ip == packet->nas_ip) && !strncmp(this->username, packet->username, sizeof(packet->username)) && !strncmp(this->sessionid, packet->sessionid, sizeof(packet->sessionid)) && (this->type & type) ) return this; this = this->next; } return (acct_packet *)NULL; } /* * Adds a pack to the queue */ static int add_acct_queue(acct_packet *packet) { acct_packet *this, *temp; this = root_acct_packet; temp = in_acct_queue(packet, packet->type); if (temp != (acct_packet *)NULL) return 0; temp = in_acct_queue(packet, PW_STATUS_STOP | PW_STATUS_START); if (temp != (acct_packet *)NULL) { temp->type |= packet->type; return 1; } temp = (acct_packet *)NULL; while (this != (acct_packet *)NULL) { temp = this; this = this->next; } this = (acct_packet *)malloc(sizeof(acct_packet)); if (this == (acct_packet *)NULL) { log_err("MEMORY ERROR !!! Can not alloc memory for acct packet %s (user %s)", packet->sessionid, packet->username); return 0; } if (temp == (acct_packet *)NULL) temp = this; else { temp->next = this; temp = this; } temp->next = (acct_packet *)NULL; /* one more entry */ acct_queue_entries++; /* root list empty */ if (root_acct_packet == (acct_packet *)NULL) root_acct_packet = temp; /* copy over */ memcpy(temp, packet, sizeof(acct_packet)); return 1; } /* * Removes a packet from the queue */ static int remove_acct_queue(acct_packet *packet) { acct_packet *temp, *temp1; temp = in_acct_queue(packet, PW_STATUS_START | PW_STATUS_STOP); if (temp == (acct_packet *)NULL) return 0; if (temp->type != (PW_STATUS_START | PW_STATUS_STOP)) return 0; if (temp == root_acct_packet) { /* oops. the root is here ... */ root_acct_packet = root_acct_packet->next; } else { temp1 = root_acct_packet; while (temp1->next != temp) temp1 = temp1->next; temp1->next = temp->next; }; free(temp); acct_queue_entries--; return 1; } /* * Delete the oldest pair of (start,stop) packets from the queue * to make room for more */ static int cleanup_acct_queue(void) { acct_packet *temp; temp = root_acct_packet; while (temp != (acct_packet*)NULL) { if (temp->type == (PW_STATUS_START | PW_STATUS_STOP)) return remove_acct_queue(temp); temp = temp->next; } return 1; } /* * Returns > 0 if the packet passed is valid, 0 otherwise * A valid packet is: * - a new one (we haven't seen it before) * - a stop packet * - a start packet which is not out of order (we haven't seen * the corresponsing packet for it yet */ int validate_acct_packet(AUTH_REQ *authreq) { acct_packet packet; VALUE_PAIR *pair; /* Some run-time debugging */ if (acct_queue_entries > 2*MAX_ACCT_QUEUE) { log_debug("WARNING: acct queue is large: %d entries, max %d\n", acct_queue_entries, MAX_ACCT_QUEUE); log_debug("possible unreliable communication with term servers ?\n"); } pair = authreq->request; memset(&packet, 0, sizeof(acct_packet)); while (pair != (VALUE_PAIR *)NULL) { switch (pair->attribute) { case PW_ACCT_STATUS_TYPE: packet.type = pair->lvalue; break; case PW_USER_NAME: strncpy(packet.username, (char *)pair->strvalue, sizeof(packet.username)); break; case PW_CLIENT_PORT_ID: packet.port = pair->lvalue; break; case PW_CLIENT_ID: packet.nas_ip = pair->lvalue; break; case PW_ACCT_SESSION_ID: strncpy(packet.sessionid, (char *)pair->strvalue, sizeof(packet.sessionid)); break; }; pair = pair->next; }; /* we have a complete packet ... */ if (in_acct_queue(&packet, packet.type) != (acct_packet *)NULL) return 0; if (packet.type == PW_STATUS_START) { if (!add_acct_queue(&packet)) log_err("ERROR!!!: could not add acct packet to queue (%s/%s)", packet.sessionid, packet.username); if (in_acct_queue(&packet, PW_STATUS_STOP) != (acct_packet *)NULL) return 0; } else if (packet.type == PW_STATUS_STOP) { if (!add_acct_queue(&packet)) log_err("ERROR!!!: could not add acct packet to queue (%s/%s)", packet.sessionid, packet.username); if (in_acct_queue(&packet, PW_STATUS_START) != (acct_packet *)NULL) if (acct_queue_entries > MAX_ACCT_QUEUE) if (!cleanup_acct_queue()) log_err("ERROR!!!: could not cleanup the acct packet queue!"); } else return 0; return 1; } /* * MD5 crypt() support for systems using both DES and MD5 */ static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static void to64(char *s, unsigned long v, int n) { while (--n >= 0) { *s++ = itoa64[v&0x3f]; v >>= 6; } } /* * UNIX password * * Use MD5 for what it is best at... */ char * crypt_md5(const char *pw, const char *salt) { const char *magic = "$1$"; /* This string is magic for this algorithm. Having * it this way, we can get get better later on */ static char passwd[120], *p; static const char *sp,*ep; unsigned char final[16]; int sl,pl,i,j; MD5_CTX ctx,ctx1; unsigned long l; /* Refine the Salt first */ sp = salt; /* If it starts with the magic string, then skip that */ if(!strncmp(sp,magic,strlen(magic))) sp += strlen(magic); /* It stops at the first '$', max 8 chars */ for(ep=sp;*ep && *ep != '$' && ep < (sp+8);ep++) continue; /* get the length of the true salt */ sl = ep - sp; MD5Init(&ctx); /* The password first, since that is what is most unknown */ MD5Update(&ctx, (unsigned const char *)pw, strlen(pw)); /* Then our magic string */ MD5Update(&ctx, (unsigned const char *)magic,strlen(magic)); /* Then the raw salt */ MD5Update(&ctx, (unsigned const char *)sp, sl); /* Then just as many characters of the MD5(pw,salt,pw) */ MD5Init(&ctx1); MD5Update(&ctx1, (unsigned const char *)pw, strlen(pw)); MD5Update(&ctx1, (unsigned const char *)sp, sl); MD5Update(&ctx1, (unsigned const char *)pw, strlen(pw)); MD5Final(final,&ctx1); for(pl = strlen(pw); pl > 0; pl -= 16) MD5Update(&ctx, (unsigned const char *)final,pl>16 ? 16 : pl); /* Don't leave anything around in vm they could use. */ memset(final,0,sizeof final); /* Then something really weird... */ for (j=0,i = strlen(pw); i ; i >>= 1) if(i&1) MD5Update(&ctx, (unsigned const char *)final+j, 1); else MD5Update(&ctx, (unsigned const char *)pw+j, 1); /* Now make the output string */ strcpy(passwd,magic); strncat(passwd,sp,sl); strcat(passwd,"$"); MD5Final(final,&ctx); /* * and now, just to make sure things don't run too fast * On a 60 Mhz Pentium this takes 34 msec, so you would * need 30 seconds to build a 1000 entry dictionary... */ for(i=0; i<1000; i++) { MD5Init(&ctx1); if(i & 1) MD5Update(&ctx1, (unsigned const char *)pw, strlen(pw)); else MD5Update(&ctx1, (unsigned const char *)final, 16); if(i % 3) MD5Update(&ctx1, (unsigned const char *)sp, sl); if(i % 7) MD5Update(&ctx1, (unsigned const char *)pw, strlen(pw)); if(i & 1) MD5Update(&ctx1, (unsigned const char *)final, 16); else MD5Update(&ctx1, (unsigned const char *)pw, strlen(pw)); MD5Final(final,&ctx1); } p = passwd + strlen(passwd); l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p,l,4); p += 4; l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p,l,4); p += 4; l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p,l,4); p += 4; l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p,l,4); p += 4; l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p,l,4); p += 4; l = final[11] ; to64(p,l,2); p += 2; *p = '\0'; /* Don't leave anything around in vm they could use. */ memset(final,0,sizeof final); return passwd; } /* * Check if the current time is an allowed time to log in according * to the parameter * Modified by Misa -- time interval function completely rewritten */ #define MAX_TIMES 10 /* max number of time specifications * in one shot using commas */ #define SEPARATOR ';' int allowed_time(const char *time_str) { struct time_frame times[MAX_TIMES]; /* Structures for storing the priorities in the time intervals */ int priority[MAX_TIMES], cur_priority; int i, j, dtime; const char *cp; time_t curtime; struct tm *tm; int error_tokens; /* Number of bogus tokens */ long session_timeout; /* This is the number of seconds * allowed in this session */ int beg_day, end_day; #ifdef EXT_DEBUG FILE* auxdebug; auxdebug = fopen("nighthawk","a"); #endif if (time_str == NULL) { /* cry loud */ debug("allowed_times: passed a null pointer !!!\n"); /* After all, it is okay... */ return 0; } debug("allowed_time: checking time against %s\n", time_str); #ifdef EXT_DEBUG Debug(auxdebug,"allowed_time: checking time against %s\n", time_str); #endif /* Initialize the time structures */ curtime = time(NULL); tm = localtime(&curtime); error_tokens = 0; /* We assume all tokens are correct */ cp = time_str; beg_day = end_day = 0; for (j = 0; *cp && j < MAX_TIMES; j++) { /* * Start off with no days of the week */ times[j].t_days = 0; priority[j] = 1; /* One-day priority, the highest */ /* * Check each two letter sequence to see if it is * one of the abbreviations for the days of the * week or the other two values. */ for (i = 0; cp[i] && cp[i+1] && isalpha(cp[i]); i+=2) { switch ((cp[i] << 8) | (cp[i+1])) { case ('S' << 8) | 'u': times[j].t_days |= Su_DAY; beg_day = Su_DAY; break; case ('M' << 8) | 'o': times[j].t_days |= Mo_DAY; beg_day = Mo_DAY; break; case ('T' << 8) | 'u': times[j].t_days |= Tu_DAY; beg_day = Tu_DAY; break; case ('W' << 8) | 'e': times[j].t_days |= We_DAY; beg_day = We_DAY; break; case ('T' << 8) | 'h': times[j].t_days |= Th_DAY; beg_day = Th_DAY; break; case ('F' << 8) | 'r': times[j].t_days |= Fr_DAY; beg_day = Fr_DAY; break; case ('S' << 8) | 'a': times[j].t_days |= Sa_DAY; beg_day = Sa_DAY; break; case ('W' << 8) | 'k': /* communists, watch out ! :-) */ times[j].t_days |= (Mo_DAY|Tu_DAY|We_DAY|Th_DAY|Fr_DAY); beg_day = 0; /* Not a valid day to begin an interval */ priority[j] = 3; /* The third priority */ break; case ('A' << 8) | 'l': times[j].t_days |= (Mo_DAY|Tu_DAY|We_DAY|Th_DAY|Fr_DAY|Sa_DAY|Su_DAY); beg_day = 0; /* Not a valid day to begin an interval */ priority[j] = 4; /* The lowest priority */ break; default: log_err("syntax error in string %s specifying times allowed to log in\n", time_str); return 0; /* syntax error, can't continue ... */ } } /* * The default is 'Al' if no days were seen. */ if (i == 0) { debug("allowed_time: time specification incompete - assuming Al; i=%d\n",i); times[j].t_days = Mo_DAY|Tu_DAY|We_DAY|Th_DAY|Fr_DAY|Sa_DAY|Su_DAY; beg_day = 0; /* Not a valid day to begin an interval */ } /* * Testing if we have a day interval * */ if (cp[i]=='-') { if (beg_day==0) /* Error, cunt have an interval */ { debug("Error in the token %d: cannot have such a day interval; skipped\n",j+1); error_tokens++; for (;cp[i] && cp[i]!=SEPARATOR;i++); cp += cp[i] ? i+1 : i; /* We jump the bogus token */ continue; } i++; cp += i; i=0; if (cp[0] && cp[1] && isalpha(cp[i])) { switch ((cp[0] << 8) | (cp[1])) { case ('S' << 8) | 'u': times[j].t_days |= Su_DAY; end_day = Su_DAY; break; case ('M' << 8) | 'o': times[j].t_days |= Mo_DAY; end_day = Mo_DAY; break; case ('T' << 8) | 'u': times[j].t_days |= Tu_DAY; end_day = Tu_DAY; break; case ('W' << 8) | 'e': times[j].t_days |= We_DAY; end_day = We_DAY; break; case ('T' << 8) | 'h': times[j].t_days |= Th_DAY; end_day = Th_DAY; break; case ('F' << 8) | 'r': times[j].t_days |= Fr_DAY; end_day = Fr_DAY; break; case ('S' << 8) | 'a': times[j].t_days |= Sa_DAY; end_day = Sa_DAY; break; case ('W' << 8) | 'k': /* communists, watch out ! :-) */ end_day = 0; /* Not a valid day to begin an interval */ break; case ('A' << 8) | 'l': end_day = 0; /* Not a valid day to begin an interval */ break; default: log_err("syntax error in string %s specifying times allowed to log in\n", time_str); return 0; /* syntax error, can't continue ... */ } } /* * Nothing means error */ if (end_day==0) { debug("Error in the token %d: not a valid day\n",j); times[j].t_days = Mo_DAY|Tu_DAY|We_DAY|Th_DAY|Fr_DAY|Sa_DAY|Su_DAY; end_day = 0; /* Not a valid day to end an interval */ } for (; beg_day != end_day;) { /* mark the day */ times[j].t_days |= beg_day; /* go to the next day, with wrapping if needed */ beg_day = (beg_day==Sa_DAY) ? Su_DAY : beg_day << 1; } times[j].t_days |= beg_day; priority[j]=2; /* second priority */ i+=2; } /* * The start and end times are separated from each * other by a '-'. The times are four digit numbers * representing the times of day. */ /* * We jump the checked items, to be able to easily count up to 4 digits */ cp += i; i = 0; for (dtime = 0; i<4 && cp[i] && isdigit(cp[i]); i++) dtime = dtime * 10 + cp[i] - '0'; if (cp[i] && isdigit(cp[i])) { /* Ooops, problem, too many digits */ debug("Error in the token %d: too many digits for the begin time; skipped\n",j+1); /* * I try to give up, jumping to the next * token separator */ error_tokens++; for (;cp[i] && cp[i]!=SEPARATOR;i++); cp += cp[i] ? i+1 : i; /* We jump the bogus token */ continue; } if (cp[i] != '-' || dtime > 2400 || dtime % 100 > 59) { debug("Error in the token %d: begin time invalid; skipped\n",j+1); error_tokens++; /* See the coments above */ for (;cp[i] && cp[i]!=SEPARATOR;i++); cp += cp[i] ? i+1 : i; /* We jump the bogus token */ continue; } times[j].t_start = dtime; cp += i+1; for (dtime = i = 0; i<4 && cp[i] && isdigit (cp[i]); i++) dtime = dtime * 10 + cp[i] - '0'; if (cp[i] && isdigit(cp[i])) { /* * Ooops, problem, too many digits * But this time we go over this error, since * we have found a valid time stamp */ debug("Error in the token %d: too many digits for the end time\n",j+1); error_tokens = -error_tokens - 1; } if (dtime > 2400 || dtime % 100 > 59) { /* * Yet another error in the string */ debug("Error in the token %d: end time invalid; skipped\n",j+1); error_tokens = error_tokens>=0 ? error_tokens+1 : -error_tokens; for (; cp[i] && cp[i]!=SEPARATOR; i++); cp += cp[i] ? i+1 : i; /* We jump the bogus token */ continue; } if (error_tokens<0) error_tokens = -error_tokens; times[j].t_end = dtime; for (; cp[i] && cp[i]!=SEPARATOR; i++); cp += cp[i] ? i+1 : i; } /* we now have j entries */ for (session_timeout = 0, cur_priority = 100, i = 0; i < j; i++) { /* check the current time against each timeframe */ int s_time, e_time; if ( times[i].t_start <= times[i].t_end ) /* No time wrapping */ { if ( (times[i].t_days & (1<<(tm->tm_wday))) == 0 ) /* No matching for this day */ continue; } else /* We do have time wrapping */ { if ( (times[i].t_days & (1<<(tm->tm_wday))) == 0 && (times[i].t_days & (1<<((tm->tm_wday + 6)%7)) ) == 0 ) /* Hey, don't you like LISP? */ /* No match for today or yesterday */ continue; } e_time = s_time = curtime - tm->tm_hour*3600 - tm->tm_min*60 - tm->tm_sec; s_time += (times[i].t_start/100)*3600 + (times[i].t_start%100)*60; e_time += (times[i].t_end/100)*3600 + (times[i].t_end%100)*60; /* check for time wrap ... */ if (times[i].t_start > times[i].t_end) { /* time is wrapping accross 0 */ if (curtime >= s_time) /* current time passed start_time, * end_time will be tomorrow */ e_time += 24*3600; else if (curtime <= e_time) /* current time before end time, start_time was yesterday */ s_time -= 24*3600; } /* now check if it is allowed to log in now */ if ( priority[i] > cur_priority) continue; /* We already have a higher priority option */ if (priority[i] == cur_priority && curtime > s_time && session_timeout < e_time - curtime) { session_timeout = e_time - curtime; continue; } cur_priority = priority[i]; /* This is a higher priority option */ session_timeout = (s_time < curtime && e_time-curtime>0) ? e_time-curtime : 0; } if (error_tokens) debug(" %d error(s) found in the time string\n",error_tokens); if (session_timeout <= 0) { debug("allowed_time: access denied\n"); #ifdef EXT_DEBUG Debug(auxdebug,"allowed_time: access denied\n"); fclose(auxdebug); #endif return -1; } debug("allowed_time: access allowed for %d seconds\n",session_timeout); #ifdef EXT_DEBUg Debug(auxdebug,"allowed_time: access allowed for %d seconds\n",session_timeout); fclose(auxdebug); #endif return 0; } /* * Implement the traffic quota for an user */ int check_maxtraffic(char *user, const int size,const int kind) { char dbfile_name[PATH_MAX]; GDBM_FILE dbf; datum key, content; user_entry *ue; struct tm *time_info; int i,j; UINT4 counter; time_t crt_time = time(NULL); time_info = localtime(&crt_time); memset(dbfile_name, 0, PATH_MAX); snprintf(dbfile_name, sizeof(dbfile_name), "%s/%d/%s", radacct_dir, 1900+time_info->tm_year,RADIUS_USER_STATS ); dbf = gdbm_open(dbfile_name,0,GDBM_READER,0600,NULL ); if (dbf == NULL) { return 0; } /* sanity checks */ if (user == NULL) return -1; /* Build the key */ key.dptr = user; key.dsize = strlen(user); content = gdbm_fetch(dbf,key); if (content.dptr == NULL) { /* not here, login is allowed */ gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return 0; } ue = (user_entry *)content.dptr; switch ( kind ) { case DAY_LIMIT: if (ue->day[time_info->tm_mon][time_info->tm_mday-1].input_octets+ ue->day[time_info->tm_mon][time_info->tm_mday-1].output_octets >= size*1024) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; case MONTH_LIMIT: for ( i=0, counter=0; itm_mday; i++ ) { counter += ue->day[time_info->tm_mon][i].input_octets; counter += ue->day[time_info->tm_mon][i].output_octets; } if ( counter >= size*1024 ) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; case YEAR_LIMIT: for ( i=0, counter=0; i<=time_info->tm_mon; i++ ) for ( j=0; itm_mday; i++ ) { counter += ue->day[i][j].input_octets; counter += ue->day[i][j].output_octets; } if ( counter >= size*1024 ) { gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return -2; } break; default: log_err("internal error: invalid kind of limit in a " "check_maxtraffic() call\n"); break; } gdbm_close(dbf); if ( content.dptr!=NULL ) free( content.dptr ); return 0; } yardradius-1.1.2/src/radiusd.c0000644000175000017500000020325510640412013013167 00000000000000/* * Copyright (C) 1999-2004 Francesco P. Lovergine. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms stated in the LICENSE file which should be * enclosed with sources. */ static char rcsid[] = "$Id: radiusd.c 83 2004-08-28 13:32:47Z flovergine $"; #define __MAIN__ #include "yard.h" #include "global.h" static AUTH_REQ *first_request; /* * */ int main( int argc,char **argv ) { UINT4 then; char argval; char argnum; int i; int t; int pid; fd_set readfds; int status; progname = *argv++; argc--; sockaddr = 0; debug_flag = 0; debug_mem = 0; spawn_flag = 0; radius_gdbm = 0; accept_zero = 0; do_clean = 0; max_requests = MAX_REQUESTS; max_request_time = MAX_REQUEST_TIME; max_proxy_time = MAX_PROXY_TIME; radacct_dir = RADACCT_DIR; radius_dir = RADIUS_DIR; alt_passwd = (char *)NULL; signal(SIGHUP, sig_hup); signal(SIGINT, sig_fatal); /* disable handler when debugging */ signal(SIGQUIT, sig_fatal); signal(SIGILL, sig_fatal); signal(SIGTRAP, sig_fatal); #if defined(SIGABRT) /* SIGABRT is POSIX.1 */ signal(SIGABRT, sig_fatal); #else signal(SIGIOT, sig_fatal); /* SIGIOT is obsolete */ #endif signal(SIGFPE, sig_fatal); signal(SIGTERM, sig_fatal); signal(SIGCHLD, sig_cleanup); #if defined(SIGWINCH) signal(SIGWINCH, sig_hup); #endif signal(SIGUSR1, sig_usr1); signal(SIGUSR2, sig_usr2); for (i=0; i< RR_MAX; i++) { report[i] = 0; } while(argc) { if(**argv != '-') usage(); argval = *(*argv + 1); argnum = *(*argv + 2); argc--; argv++; switch(argval) { case 'a': if(argc == 0) usage(); radacct_dir = *argv; argc--; argv++; break; case 'b': /* use gdbm users file */ radius_gdbm = 1; break; case 'c': do_clean++; break; case 'd': if(argc == 0) usage(); radius_dir = *argv; argc--; argv++; break; case 'f': if(argc == 0) usage(); alt_passwd = *argv; argc--; argv++; break; case 'h': usage(); break; case 'i': if(argc == 0) usage(); sockaddr = get_ipaddr(*argv); argc--; argv++; break; case 'l': /* change logging from syslog */ if(argc == 0) usage(); radius_log = *argv; argc--; argv++; break; case 'm': /* debug memory */ if (isdigit(argnum)) debug_mem = argnum - '0'; else debug_mem++; break; case 'p': /* set radius port */ if(argc == 0) usage(); radius_port = (u_short)atoi(*argv); argc--; argv++; break; case 'o': /* accept all-zero accounting request authenticators */ accept_zero = 1; break; case 'q': /* set max queue size */ if(argc == 0) usage(); max_requests = (int)atoi(*argv); argc--; argv++; break; case 's': /* spawing processes mode */ spawn_flag = 1; break; case 't': /* set max time out in seconds */ if(argc == 0) usage(); max_request_time = (int)atoi(*argv); argc--; argv++; break; case 'v': version(); break; case 'w': /* set proxy time in seconds */ if(argc == 0) usage(); max_proxy_time = (int)atoi(*argv); argc--; argv++; break; case 'x': if (isdigit(argnum)) debug_flag = argnum - '0'; else debug_flag++; break; case 'z': /* debugging: -b -s -x -d . -a ra */ radius_gdbm = 1; spawn_flag = 0; debug_flag++; radius_dir = "."; radacct_dir = "ra"; break; #if defined(PAM) case 'P': usepamauth=1; break; case 'A': usepamacct=1; break; #endif default: usage(); break; } } if (debug_flag) { if (radius_log == NULL) { /* * for backward compatibility * send messages to users tty */ radius_log = "/dev/tty"; } else if (strcmp(radius_log, "syslog") == 0) { /* * allow user to override backward compatibility * and send debug to syslog */ radius_log = (char *)NULL; } } debug("initializing dictionary\n"); /* Initialize the dictionary */ if(dict_init() != 0) { rad_exit(-1); } dict_dump(); debug("initializing configuration values\n"); /* Initialize Configuration Values */ if(config_init() != 0) { rad_exit(-1); } /* * Disconnect from session */ debug("Disconnecting from session\n"); if(debug_flag == 0) { pid = fork(); if(pid < 0) { log_err("system error: could not fork at startup\n"); rad_exit(-1); } if(pid > 0) { exit(0); } } /* * Disconnect from tty */ debug("Disconnecting from tty\n"); for (t = 32; t >= 3; t--) { close(t); } /* Show our stuff */ log_version(); if (debug_flag) { log_err("debug mode %d\n",debug_flag); } if (debug_mem) { log_err("memory debug mode %d\n",debug_mem); } /* Open RADIUS socket */ sockfd = open_udpsock(&radius_port,PW_AUTH_UDP_PORT,"radius"); /* Open Accounting socket */ radacct_port = radius_port + 1; acctfd = open_udpsock(&radacct_port,PW_ACCT_UDP_PORT,"radacct"); /* * Open Proxy Socket. * We send to proxy servers from this socket, so replies return to it */ radproxy_port = radius_port + 5; radpracct_port = radius_port + 6; proxyfd = open_udpsock(&radproxy_port,PW_PROXY_UDP_PORT,"radius-proxy"); proxyacctfd = open_udpsock(&radpracct_port,PW_PROXYACCT_UDP_PORT,"radacct-proxy"); if (ipassinit() != 0) { log_err("ipass not in use\n"); } #ifdef ACTIVCARD /* establish aeg session before attending to any user requests */ if (activcard_init() < 0) { log_err("activcard not in use\n"); } #endif update_clients(); update_proxy(); #ifdef VPORTS vports_flag = vports_init(); if (vports_flag == 1 && spawn_flag == 1) { spawn_flag = 0; debug("virtual ports disable spawning\n"); } #endif /* VPORTS */ /* * If we are able to spawn processes, we will start a child * to listen for Accounting-Requests. If not, we will * listen for them ourself. */ if(spawn_flag) { acct_pid = fork(); if(acct_pid < 0) { log_err("could not fork to spawn accounting daemon\n"); rad_exit(-1); } if(acct_pid > 0) { close(acctfd); acctfd = -1; close(proxyacctfd); proxyacctfd = -1; } else { close(sockfd); sockfd = -1; close(proxyfd); proxyfd = -1; } } then = 0; /* * Receive user requests */ for(;;) { FD_ZERO(&readfds); if(sockfd >= 0) { FD_SET(sockfd, &readfds); } if(proxyfd >= 0) { FD_SET(proxyfd, &readfds); } if(acctfd >= 0) { FD_SET(acctfd, &readfds); } if(proxyacctfd >= 0) { FD_SET(proxyacctfd, &readfds); } status = select(32, &readfds, NULL, NULL, (struct timeval *)NULL); if(status == -1) { if (errno == EINTR) continue; log_err("exiting after select returned error %d, %s\n",errno,strerror(errno)); sig_fatal(101); } now = (UINT4)time((time_t *)NULL); if (now > then) { then = now; if(sockfd != -1) { update_clients(); } update_proxy(); } if(proxyfd >=0 && FD_ISSET(proxyfd, &readfds)) { rad_proxy(proxyfd); report[RR_PORT3]++; } if(sockfd >= 0 && FD_ISSET(sockfd, &readfds)) { rad_request(sockfd); report[RR_PORT1]++; } if(proxyacctfd >=0 && FD_ISSET(proxyacctfd, &readfds)) { rad_proxy(proxyacctfd); report[RR_PORT4]++; } if(acctfd >=0 && FD_ISSET(acctfd, &readfds)) { rad_acctreq(acctfd); report[RR_PORT2]++; } } } /************************************************************************* * * Function: open_udpsock * * Purpose: open desired UDP socket and return file descripter * Exit program if socket is unavailable * place port number used in first argument * *************************************************************************/ int open_udpsock(u_short *port,int defport,char *service) { int fd; int result; struct servent *svp; struct sockaddr_in *sin; struct sockaddr_in salocal; u_short lport; if (*port>5) { lport = htons(*port); } else { svp = getservbyname(service, "udp"); if (svp != (struct servent *) NULL) { lport = (u_short) svp->s_port; } else { lport = htons(defport); } *port = ntohs(lport); } debug("using udp port %d for %s\n", *port,service); fd = socket (AF_INET, SOCK_DGRAM, 0); if (fd < 0) { log_err("%s socket error %s\n", service, strerror(errno)); rad_exit(-1); } sin = (struct sockaddr_in *) & salocal; memset ((char *) sin, '\0', sizeof (salocal)); sin->sin_family = AF_INET; if (sockaddr != 0) { sin->sin_addr.s_addr = htonl(sockaddr); } else { sin->sin_addr.s_addr = INADDR_ANY; } sin->sin_port = lport; result = bind (fd, (struct sockaddr *)&salocal, sizeof (*sin)); if (result < 0) { log_err("%s bind error %s\n", service, strerror(errno)); rad_exit(-1); } return fd; } /************************************************************************* * * Function: send_packet * * Purpose: Send RADIUS UDP packet * *************************************************************************/ void send_packet(int fd, UINT4 ipaddr, u_short port, char * buffer, int length) { AUTH_HDR *auth; struct sockaddr_in saremote; struct sockaddr_in *sin; sin = (struct sockaddr_in *) &saremote; memset ((char *) sin, '\0', sizeof (saremote)); sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(ipaddr); sin->sin_port = htons(port); auth = (AUTH_HDR *)buffer; debug("message sent to %s/%d.%d code=%d, length=%d\n", ipaddr2strp(ipaddr), port, auth->id, auth->code, length); if (debug_flag > 1) { hexdump((u_char*)buffer,length); } /* Send it */ sendto(fd, buffer, (int)length, (int)0, (struct sockaddr *)&saremote, sizeof(struct sockaddr_in)); } /************************************************************************* * * Function: rad_request * * Purpose: Receive UDP client requests * *************************************************************************/ void rad_request(int fd) { AUTH_REQ *authreq; UINT4 addr; char secret[20]; char hostnm[128]; int result; size_t salen; struct sockaddr_in *sin; u_short port; #if defined(SMARTCARD) int child_pid; AUTH_HDR *auth; AUTH_REQ *curreq; VALUE_PAIR *pair; key_t msg_key; int msg_id; char *buf; #endif salen = sizeof (rad_saremote); sin = (struct sockaddr_in *) & rad_saremote; result = recvfrom (fd, (char *) recv_buffer, (int) sizeof(recv_buffer), (int) 0, (struct sockaddr *)&rad_saremote, &salen); addr = ntohl(sin->sin_addr.s_addr); port = ntohs(sin->sin_port); if (result < AUTH_HDR_LEN) { log_err("rad_request: runt packet of %d bytes from %s.%d\n", result,ipaddr2strp(addr),port); return; } /* * Validate the requesting IP address - * Not secure, but worth the check for accidental requests * find_client() logs an error message if needed */ if(find_client(addr, secret, sizeof(secret), hostnm, sizeof(hostnm)) != 0) { log_err("rad_request: request from unknown" " client %s.%d ignored\n",ipaddr2strp(addr),port); return; } authreq = radrecv( addr, port, secret, recv_buffer, result ); if (authreq == (AUTH_REQ *)NULL) { /* malformed packet */ return; } /* handle_proxy places the user name in authreq->name, * and forwards request to a proxy server if necessary */ if (handle_proxy(authreq) != 0) { /* error or forwarded */ if (authreq->flags & REQ_FREE) { reqfree(authreq,"rad_request"); } /* otherwise authreq will be freed when proxy response is seen */ return; } #if defined(SMARTCARD) if (spawn_flag == 0) { radrespond(authreq, fd); return; } /* * We need to see if this is a challenge response */ child_pid = -1; if ((pair = get_attribute(authreq->request,PW_STATE)) != (VALUE_PAIR *)NULL) { buf = pair->strvalue; debug("rad_request: PW_STATE<%s>\n", buf); #ifdef SECURID /* * the format for SECURID state string is * * SECURID_xxxx=n * * xxxx is commands: next or npin or wait * n is the child pid */ if (strncmp(buf, "SECURID_", 8) == 0) { child_pid = (int)atoi(&buf[13]); } #endif /* SECURID */ #ifdef ACTIVCARD /* * the format for ACTIVCARD state string is * * ACTIVCARD_999...=n * * 999... is the challenge returned by ActivEngine * n is the child pid */ if (strncmp(buf, "ACTIVCARD_", 10) == 0) { child_pid = (int)atoi(strchr(buf, (int)'=')+1); } #endif /* ACTIVCARD */ } if (child_pid == -1) { radrespond(authreq, fd); return; } debug("rad_request: challenge_response from %s for child %d\n", req2strp(authreq), child_pid); curreq = first_request; while(curreq != (AUTH_REQ *)NULL) { if (curreq->child_pid == child_pid) { break; } curreq = curreq->next; } if (curreq == (AUTH_REQ *)NULL) { log_err("rad_request: child %d not found\n", child_pid); reqfree(authreq,"rad_request"); return; } if (curreq->ipaddr != addr) { log_err("rad_request: error: mismatched IP addresses in request %x != %x for ID %d %d\n", curreq->ipaddr, addr, curreq->id, auth->id); reqfree(authreq,"rad_request"); return; } if (curreq->udp_port != port) { log_err("rad_request: error: mismatched source ports in request %d != %d for ID %d %d\n", curreq->udp_port, port, curreq->id, auth->id); reqfree(authreq,"rad_request"); return; } if (curreq->id == authreq->id) { /* This is a duplicate request - just drop it */ log_err("rad_request: dropped duplicate ID %d\n", authreq->id); reqfree(authreq,"rad_request"); return; } msg_key = RADIUS_MSG_KEY(child_pid); if ((msg_id = msgget(msg_key, 0600)) == -1) { log_err("rad_request: error: msgget for key %x for id %d returned error %d\n",msg_key, msg_id, errno); reqfree(authreq,"rad_request"); return; } if (msgsnd(msg_id, recv_buffer, result, IPC_NOWAIT) == -1) { log_err("rad_request: error: msgsnd for key %x for id %d returned error %d\n", msg_key, msg_id, errno); reqfree(authreq,"rad_request"); return; } curreq->id = authreq->id; #else /* not SMARTCARD */ radrespond(authreq, fd); #endif /* not SMARTCARD */ } /************************************************************************* * * Function: radrecv * * Purpose: Receive UDP client requests, build an authorization request * structure, and attach attribute-value pairs contained in * the request to the new structure. * *************************************************************************/ AUTH_REQ * radrecv(UINT4 host,u_short udp_port,char* secret,u_char* buffer,int length) { u_char *ptr; AUTH_HDR *auth; int totallen; int attribute; int attrlen; int vendor; int vsa; int vsattrlen; DICT_ATTR *attr; UINT4 lvalue; VALUE_PAIR *first_pair; VALUE_PAIR *prev; VALUE_PAIR *pair; AUTH_REQ *authreq; if (length < AUTH_HDR_LEN) { /* too short to be real */ log_err("radrecv: runt packet of %d bytes from %s/%d\n", length, ipaddr2strp(host), udp_port); return ((AUTH_REQ *)NULL); } /* * Pre-allocate the new request data structure */ authreq = reqalloc("radrecv"); auth = (AUTH_HDR *)buffer; totallen = ntohs(auth->length); if (totallen > length) { /* truncated packet, ignore */ log_err("radrecv: message from %s/%d claimed length %d, only %d bytes received\n", ipaddr2strp(host), udp_port, totallen, length); reqfree(authreq,"radrecv"); return((AUTH_REQ *)NULL); } debug("message received from %s/%d.%d code=%d, length=%d\n", ipaddr2strp(host), udp_port, auth->id, auth->code, totallen); if (debug_flag > 1) { hexdump((u_char*)buffer,totallen); } /* * Fill header fields */ authreq->ipaddr = host; authreq->udp_port = udp_port; authreq->id = auth->id; authreq->code = auth->code; memcpy(authreq->vector, auth->vector, AUTH_VECTOR_LEN); strncpy(authreq->secret,secret,20); authreq->secret[19]='\0'; /* * Extract attribute-value pairs */ ptr = auth->data; length = totallen - AUTH_HDR_LEN; first_pair = (VALUE_PAIR *)NULL; prev = (VALUE_PAIR *)NULL; while(length > 0) { attribute = *ptr++; attrlen = *ptr++; if(attrlen < 2) { length = 0; continue; } attrlen -= 2; if ( attrlen > AUTH_STRING_LEN ) { log_err("radrecv: attribute %d from %s too long, length of %d > %d\n", attribute, req2strp(authreq), attrlen, AUTH_STRING_LEN); reqfree(authreq,"radrecv"); return((AUTH_REQ *)NULL); } pair = pairalloc("radrecv"); if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) { snprintf(pair->name,(size_t)ID_LENGTH,"Unknown-%d",attribute); pair->attribute = attribute; pair->type = PW_TYPE_STRING; } else { strncpy(pair->name, attr->name, VALUE_PAIR_NAME_LEN-1); pair->name[VALUE_PAIR_NAME_LEN-1]='\0'; pair->attribute = attr->value; pair->type = attr->type; } if (pair->attribute == PW_VENDOR) { if (attrlen < 6) { pair->vendor = 0; pair->vsattribute = 0; pair->type = PW_TYPE_STRING; } else { memcpy(&vendor, ptr, sizeof(int)); vendor = ntohl(vendor); ptr += 4; vsattrlen = *ptr; if ( vendor == VENDOR_USROBOTICS ) { if (attrlen < 8) { pair->vendor = 0; pair->vsattribute = 0; pair->type = PW_TYPE_STRING; } else { memcpy(&vsa, ptr, sizeof(int)); vsa = ntohl(vsa); ptr += 4; attrlen -= 8; length -= 8; } } else { vsa = *ptr; ptr += 2; attrlen -= 6; length -= 6; } pair->vendor = vendor; pair->vsattribute = vsa; if((attr = dict_vsattrget(vendor,vsa)) != NULL) { strncpy(pair->name, attr->name, VALUE_PAIR_NAME_LEN-1); pair->name[VALUE_PAIR_NAME_LEN-1]='\0'; pair->type = attr->type; } else { log_debug("Attribute %d for vendor %d not found\n",vsa,vendor); snprintf(pair->name,(size_t)ID_LENGTH,"Vendor-Specific-%d-%d",vendor,vsa); pair->type = PW_TYPE_STRING; } } } switch(pair->type) { #if defined(ASCEND_BINARY) case PW_TYPE_ABINARY: #endif case PW_TYPE_STRING: memcpy(pair->strvalue, ptr, attrlen); pair->strvalue[attrlen] = '\0'; pair->lvalue = attrlen; debug_pair(pair); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: memcpy(&lvalue, ptr, sizeof(UINT4)); pair->lvalue = ntohl(lvalue); debug_pair(pair); if(first_pair == (VALUE_PAIR *)NULL) { first_pair = pair; } else { prev->next = pair; } prev = pair; break; default: debug(" %s (Unknown Type %d)\n", attr->name,attr->type); pairfree(pair,"radrecv"); break; } ptr += attrlen; length -= attrlen + 2; } authreq->request = first_pair; authreq->timestamp = now; /* now was set in main() */ /* copy the packet */ authreq->packet = bufalloc(totallen,"radrecv"); memcpy(authreq->packet,buffer,totallen); return(authreq); } /************************************************************************* * * Function: radrespond * * Purpose: Respond to supported requests * * PW_AUTHENTICATION_REQUEST - Authentication request from * a client network access server. * *************************************************************************/ void radrespond(AUTH_REQ *authreq,int activefd) { if (authreq == (AUTH_REQ *)NULL) { return; } switch(authreq->code) { case PW_AUTHENTICATION_REQUEST: if(spawn_flag) { rad_spawn_child(authreq, activefd); } else { rad_authenticate(authreq, activefd); } break; default: log_err("unknown request type %d from %s ignored\n", authreq->code, req2strp(authreq)); reqfree(authreq,"radrespond"); break; } } /************************************************************************* * * Function: rad_spawn_child * * Purpose: Spawns child processes to perform password authentication * and respond to RADIUS clients. This functions also * cleans up complete child requests, and verifies that there * is only one process responding to each request (duplicate * requests are filtered out. * *************************************************************************/ int rad_spawned_child_pid; void rad_spawn_child(AUTH_REQ *authreq,int activefd) { AUTH_REQ *curreq; AUTH_REQ *prevreq; UINT4 curtime; int request_count; int child_pid; #ifdef SMARTCARD key_t msg_key; int msg_id; #endif curtime = authreq->timestamp; request_count = 0; curreq = first_request; prevreq = (AUTH_REQ *)NULL; while(curreq != (AUTH_REQ *)NULL) { if(curreq->child_pid == -1 && curreq->timestamp + CLEANUP_DELAY <= curtime) { /* Request completed, delete it */ if(prevreq == (AUTH_REQ *)NULL) { first_request = curreq->next; reqfree(curreq,"rad_spawn_child"); curreq = first_request; } else { prevreq->next = curreq->next; reqfree(curreq,"rad_spawn_child"); curreq = prevreq->next; } } else if(curreq->ipaddr == authreq->ipaddr && curreq->udp_port == authreq->udp_port && curreq->id == authreq->id) { /* This is a duplicate request - just drop it */ log_err("dropping duplicate request from %s\n", req2strp(authreq)); reqfree(authreq,"rad_spawn_child"); return; } else { if(curreq->timestamp + max_request_time <= curtime && curreq->child_pid != -1) { /* This request seems to have hung - kill it */ child_pid = curreq->child_pid; log_err("sending SIGHUP to unresponsive child process %d\n", child_pid); curreq->child_pid = -1; kill(child_pid, SIGHUP); #ifdef SMARTCARD /* * delete childs message queue */ msg_key = RADIUS_MSG_KEY(child_pid); if ((msg_id = msgget(msg_key, 0600)) != -1) { msgctl(msg_id, IPC_RMID, 0); } #endif /* SMARTCARD */ } prevreq = curreq; curreq = curreq->next; request_count++; } } /* This is a new request */ if(request_count > max_requests) { request_count--; log_err("dropping request from %s; %d requests already in queue\n", req2strp(authreq), request_count); reqfree(authreq,"rad_spawn_child"); return; } /* Add this request to the list */ /* authreq->timestamp already set by radrecv */ authreq->next = (AUTH_REQ *)NULL; authreq->child_pid = -1; if(prevreq == (AUTH_REQ *)NULL) { first_request = authreq; } else { prevreq->next = authreq; } /* fork our child */ cleanup_pid = -1; rad_spawned_child_pid = fork(); if(rad_spawned_child_pid < 0) { log_err("system error: fork failed with error %d for request from %s\n", errno,req2strp(authreq)); reqfree(authreq,"rad_spawn_child"); return; } if(rad_spawned_child_pid == 0) { /* This is the child, it should go ahead and respond */ child_authenticate(authreq, activefd); exit(0); } /* Register the Child */ authreq->child_pid = rad_spawned_child_pid; /* * If cleanup_pid is not -1, then we received a SIGCHLD between * the time we forked and the time we got here, so clean up after it */ if(cleanup_pid != -1) { clean_child(cleanup_pid); cleanup_pid = -1; } } void clean_child(int pid) { AUTH_REQ *curreq; curreq = first_request; while(curreq != (AUTH_REQ *)NULL) { if(curreq->child_pid == pid) { curreq->child_pid = -1; curreq->timestamp = (UINT4)time((time_t *)NULL); return; } curreq = curreq->next; } cleanup_pid = (int)pid; return; } void sig_cleanup(int sig) { int status; pid_t pid; for (;;) { pid = waitpid((pid_t)-1,&status,WNOHANG); signal(SIGCHLD, sig_cleanup); if (pid <= 0) return; #if defined (aix) kill(pid, SIGKILL); #endif if(pid == acct_pid) { sig_fatal(100); } clean_child(pid); } } /************************************************************************* * * Function: child_authenticate * * Purpose: Process and reply to an authentication request * *************************************************************************/ void child_authenticate(AUTH_REQ *authreq,int activefd) { #ifdef SMARTCARD key_t msg_key; int msg_id; int length; struct sockaddr_in *sin; msg_key = RADIUS_MSG_KEY(getpid()); #endif /* SMARTCARD */ for (;;) { if (rad_authenticate(authreq, activefd) == 0) { break; } #ifdef SMARTCARD if ((msg_id = msgget(msg_key, IPC_CREAT | 0600)) == -1) { log_err("child_authenticate: msgget for key %x for id %d returned error: %s\n", msg_key, msg_id, strerror(errno)); break; } if ((length = msgrcv(msg_id, recv_buffer, sizeof recv_buffer - sizeof(long), 0, 0)) == -1) { log_err("child_authenticate: msgrcv for msgid %d returned error: %s\n", msg_id, strerror(errno)); break; } if (msgctl(msg_id, IPC_RMID, 0) == -1) { log_err("child_authenticate: msgctl for msgid %d returned error: %s\n", msg_id, strerror(errno)); } sin = (struct sockaddr_in *) &rad_saremote; authreq = radrecv( ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port), authreq->secret, recv_buffer, length); #else /* not SMARTCARD */ break; #endif /* not SMARTCARD */ } } /************************************************************************* * * Function: rad_authenticate * * Purpose: Process and reply to an authentication request * *************************************************************************/ int rad_authenticate(AUTH_REQ *authreq,int activefd) { USER_FILE user_desc; VALUE_PAIR *attr; VALUE_PAIR *auth_item; VALUE_PAIR *callpair; VALUE_PAIR *challenge; VALUE_PAIR *check_item; VALUE_PAIR *password_item; VALUE_PAIR *user_check; VALUE_PAIR *user_reply; char auth_name[AUTH_STRING_LEN + 2]; char callfrom[ID_LENGTH]; char pw_digest[16]; char string[AUTH_STRING_LEN + 20 + 2]; char umsg[AUTH_STRING_LEN + 2]; char *encpw; char *ptr; char *user_msg; char *pass; int authtype; int chlen; int result; int retval; int speed; /* The username was placed in authreq->name by handle_proxy */ if(strlen(authreq->name) <= (size_t)0) { log_err("auth: access-request from %s ignored; no user name\n", req2strp(authreq)); reqfree(authreq,"rad_authenticate"); return(0); } #ifdef VPORTS if (vports_flag == 1) { switch(vp_check_req(authreq)) { case VP_RET_REJECT: send_reject(authreq, (char *)NULL, activefd); reqfree(authreq,"rad_authenticate"); return(0); break; case VP_RET_ACCEPT: send_accept(authreq, (VALUE_PAIR *)NULL, (char *)NULL, activefd); reqfree(authreq,"rad_authenticate"); return(0); break; case VP_RET_IGNORE: default: break; } } #endif /* VPORTS */ /* calculate the MD5 Password Digest */ calc_digest((u_char*)pw_digest, authreq, (u_char*)authreq->secret); /* * If the request is processing a menu, service it here. */ if((attr = get_attribute(authreq->request, PW_STATE)) != (VALUE_PAIR *)NULL && strncmp(attr->strvalue, "MENU=", 5) == 0){ process_menu(authreq, activefd, pw_digest); return(0); } callpair = get_attribute(authreq->request, PW_CALLING); if (callpair == (VALUE_PAIR *)NULL || callpair->lvalue > 20) { callfrom[0] = '\0'; } else { snprintf(callfrom,(size_t)ID_LENGTH," at %s",callpair->strvalue); } /* * Open the user table */ user_desc = user_open(); if(user_desc.gdbm == NULL && user_desc.flat == NULL) { reqfree(authreq,"rad_authenticate"); return(0); } for (;;) { /* Get the user from the database */ if ((result = user_find(authreq->name, auth_name, &user_check, &user_reply, user_desc)) != 0) { log_err("auth: access-request from %s denied for unknown user \"%s\"%s\n", req2strp(authreq), authreq->name, callfrom); send_reject(authreq, (char *)NULL, activefd); reqfree(authreq,"rad_authenticate"); user_close(user_desc); return(0); } /* Validate the user */ /* Look for matching check items */ password_item = (VALUE_PAIR *)NULL; authtype = PW_AUTHTYPE_NONE; user_msg = (char *)NULL; check_item = user_check; result = allow_user(authreq->name); if (result != 0) { result = deny_user(authreq->name); if (result != 0) { log_err("auth: denied connection for '%s' (listed in '%s/%s')", authreq->name,radius_dir, (result==-2)?"denyusers":"stopusers"); } } while(result == 0 && check_item != (VALUE_PAIR *)NULL) { auth_item = get_attribute(authreq->request, check_item->attribute); switch(check_item->attribute) { case PW_PREFIX: case PW_SUFFIX: break; case PW_EXPIRATION: /* * Check expiration date if we are * doing password aging. */ #if defined(SHADOW_EXPIRATION) if (!strncasecmp(check_item->strvalue,"SHADOW", 6)) retval = shadow_expired(authreq->name); else #endif retval = pw_expired(check_item->lvalue); if(retval < 0) { result = -2; snprintf(umsg,sizeof(umsg),"Password Has Expired\r\n"); user_msg = umsg; log_err("auth: Password expired for '%s'\n",authreq->name); } else { if(retval > 0) { snprintf(umsg,(size_t)(AUTH_STRING_LEN)+2,"Password Will Expire in %d Days\r\n", retval); user_msg = umsg; log_err("auth: Password for '%s' will expire in %d days\n", retval); } } break; case PW_PASSWORD: if(strcmp(check_item->strvalue, "UNIX") == 0) { authtype = PW_AUTHTYPE_UNIX; } else { authtype = PW_AUTHTYPE_LOCAL; password_item = check_item; } break; case PW_AUTHTYPE: authtype = check_item->lvalue; break; case PW_GROUP: if(!unix_group(auth_name, check_item->strvalue)) { result = -1; } break; case PW_CRYPT_PASSWORD: authtype = PW_AUTHTYPE_CRYPT; password_item = check_item; break; /** FIXME This is a problem for Yard. Connect-Info or Connect-Info-Old are often not used by non-Livingston boxes, so this check item is unuseful. Ascend and USR boxes uses their VSAs to register connection speed. Cisco too. Not RFC compliant at this moment. **/ case PW_CONNECT_RATE: auth_item = get_attribute(authreq->request, PW_CONNECT_INFO); if (auth_item != (VALUE_PAIR *)NULL) { if ( sscanf(auth_item->strvalue,"%d/%*d%*s",&speed)==1|| (speed=atoi(auth_item->strvalue)) ) if ( speed>check_item->lvalue ) result=-1; } else { auth_item = get_attribute(authreq->request, PW_CONNECT_INFO_OLD); if (auth_item != (VALUE_PAIR *)NULL) { speed = atoi(auth_item->strvalue); if (speed > check_item->lvalue) result=-1; } } break; #if defined(PAM) && defined(HAVE_LIBPAM) case PW_PAM_AUTH: pam_auth = check_item->strvalue; break; #endif case PW_LOGINS: retval = check_logins(auth_name, check_item->lvalue); if (retval != 0) { result = -2; snprintf(umsg,(size_t)(AUTH_STRING_LEN+2),"Too many logins - max %d\r\n",(unsigned int)check_item->lvalue); user_msg = umsg; log_err("Too many logins for '%s' (max %d)\n",authreq->name,(unsigned int)check_item->lvalue); } break; case PW_MAXDTIME: retval = check_maxtime(auth_name,check_item->lvalue,DAY_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Total on-line daily time expired (%d hours)\r\n",(unsigned int)check_item->lvalue); user_msg = umsg; log_err("Total on-line daily time expired (%d hours) for '%s'\n", (unsigned int)check_item->lvalue, authreq->name); } break; case PW_MAXMTIME: retval = check_maxtime(auth_name,check_item->lvalue,MONTH_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Total on-line time expired (%d hours)\r\n", (unsigned int)check_item->lvalue); user_msg = umsg; log_err("Total on-line time expired (%d hours) for '%s'\n", (unsigned int)check_item->lvalue, authreq->name); } break; case PW_MAXYTIME: retval = check_maxtime(auth_name,check_item->lvalue,YEAR_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Total on-line yearly time expired (%d hours)\r\n", (unsigned int)check_item->lvalue); user_msg = umsg; log_err("Total on-line yearly time expired (%d hours) for '%s'\n", (unsigned int)check_item->lvalue, authreq->name); } break; case PW_TIME: retval = allowed_time(check_item->strvalue); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Not allowed to login at this time\r\n"); user_msg = umsg; log_err( "Not allowed to login at this time for '%s'\n",authreq->name ); } break; case PW_MAXDTRAFFIC: retval = check_maxtraffic(auth_name,check_item->lvalue,DAY_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Maximum allowed daily traffic size reached (%d KB)\r\n",(unsigned int)check_item->lvalue); user_msg=umsg; log_err("Maximum allowed daily traffic size reached (%dKB) for '%s'\r\n", (unsigned int)check_item->lvalue, authreq->name); } break; case PW_MAXMTRAFFIC: retval = check_maxtraffic(auth_name,check_item->lvalue,MONTH_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Maximum allowed monthly traffic size reached (%d KB)\r\n", (unsigned int)check_item->lvalue); user_msg=umsg; log_err("Maximum allowed monthly traffic size reached (%d KB) for '%s'\r\n", (unsigned int)check_item->lvalue, authreq->name); } break; case PW_MAXYTRAFFIC: retval = check_maxtraffic(auth_name,check_item->lvalue,YEAR_LIMIT); if (retval != 0) { result = -2; snprintf(umsg,sizeof(umsg),"Maximum allowed yearly traffic size reached (%d KB)\r\n", (unsigned int)check_item->lvalue); user_msg=umsg; log_err("Maximum allowed yearly traffic size reached (%d KB) for '%s'\r\n", (unsigned int)check_item->lvalue, authreq->name); } break; default: if(auth_item == (VALUE_PAIR *)NULL) { result = -1; break; } switch(check_item->type) { case PW_TYPE_STRING: if(strcmp(check_item->strvalue, auth_item->strvalue) != 0) { result = -1; } break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: if(check_item->lvalue != auth_item->lvalue) { result = -1; } break; default: result = -1; break; } break; } check_item = check_item->next; } if (result != -1) { break; } pairfree(user_check,"rad_authenticate"); pairfree(user_reply,"rad_authenticate"); } user_close(user_desc); /* * At this point we have validated all normal comparisons * for the user. All that is left is the actual authentication. * Authentication will be done based on the authentication type * previously specified. */ if(result == 0) { /* * Decrypt the password in the request. */ pass = decrypt_password(authreq,authreq->secret); if (pass != (char *)NULL) { strncpy(string,pass,AUTH_STRING_LEN); string[AUTH_STRING_LEN] = '\0'; /* always null-term */ } else { string[0] = '\0'; } switch(authtype) { case PW_AUTHTYPE_LOCAL: /* * The local authentication type supports normal * password comparison and the Three-Way CHAP. */ if (password_item == (VALUE_PAIR *)NULL) { log_err("Warning: entry for user \"%s\" is missing Password check item\n",authreq->name); result = -1; } /* * Check to see if we have a CHAP password. */ else if ((auth_item = get_attribute(authreq->request, PW_CHAP_PASSWORD)) != (VALUE_PAIR *)NULL) { /* Use MD5 to verify */ ptr = string; *ptr++ = *auth_item->strvalue; strcpy(ptr, password_item->strvalue); ptr += strlen(password_item->strvalue); if ((challenge = get_attribute(authreq->request, PW_CHAP_CHALLENGE)) != (VALUE_PAIR *)NULL) { chlen = challenge->lvalue; memcpy(ptr, challenge->strvalue, chlen); } else { chlen = AUTH_VECTOR_LEN; memcpy(ptr, authreq->vector, chlen); } md5_calc(pw_digest, string, 1 + chlen + strlen(password_item->strvalue)); /* Compare them */ if(memcmp(pw_digest, auth_item->strvalue + 1, CHAP_VALUE_LENGTH) != 0) { result = -1; } else { result = 0; } } else if (strcmp(password_item->strvalue, string) == 0) { result = 0; } else { result = -1; } break; case PW_AUTHTYPE_UNIX: if(unix_pass(auth_name, string, callfrom) != 0) { result = -1; } break; #ifdef SECURID case PW_AUTHTYPE_SECURID: if(pass != (char *)NULL) { pairfree(user_check,"rad_authenticate"); return( securid(auth_name, string, authreq, user_reply, activefd) ); } else { result = -1; } break; #endif /* SECURID */ case PW_AUTHTYPE_CRYPT: /* password is stored encrypted in string */ if(password_item == (VALUE_PAIR *)NULL) { if(string[0] != '\0') { result = -1; } } else if (pass != NULL) { encpw = (char*)crypt(string,password_item->strvalue); if(strcmp(encpw,password_item->strvalue) != 0) { result = -1; } } else { result = -1; } break; case PW_AUTHTYPE_REJECT: result = -1; break; #if defined(PAM) && defined(HAVE_LIBPAM) case PW_AUTHTYPE_PAM: if (usepamauth) { if (unix_pam(auth_name, string, pam_auth) != 0) { result = -1; user_msg = (char *)NULL; } } else { log_err("PAM auth not enabled. Please re-run daemon with the correct argument"); user_msg = (char *)NULL; result=-1; } break; #endif #ifdef ACTIVCARD case PW_AUTHTYPE_ACTIVCARD: if (pass != (char *)NULL) { pairfree(user_check,"rad_authenticate"); /* activcard calls send_*() as needed */ return ( activcard_auth(auth_name, string, authreq, user_reply, activefd) ); } else { result = -1; } break; #endif /* ACTIVCARD */ case PW_AUTHTYPE_NONE: /* No Password or Auth-Type found in check-items */ debug("entry for user \"%s\" has no Password or Auth-Type check-item\n",authreq->name); result = 0; break; default: log_err("Warning: entry for user \"%s\" has unknown Auth-Type = %d\n",authreq->name, authtype); result = -1; break; } } if(result != 0) { send_reject(authreq, user_msg, activefd); } else { send_accept(authreq, user_reply, user_msg, activefd); } reqfree(authreq,"rad_authenticate"); pairfree(user_check,"rad_authenticate"); pairfree(user_reply,"rad_authenticate"); return(0); } /************************************************************************* * * Function: send_reject * * Purpose: Reply to the request with a REJECT. Also attach * any user message provided. * *************************************************************************/ void send_reject(AUTH_REQ *authreq,char *msg,int activefd) { u_char code; int total_length; code = PW_AUTHENTICATION_REJECT; report[RR_REJECT]++; debug("sending reject to %s\n", req2strp(authreq)); total_length = build_packet(authreq,(VALUE_PAIR *)NULL,msg,code, FW_REPLY,send_buffer,sizeof(send_buffer)); /* Send it to the user */ send_packet(activefd, authreq->ipaddr, authreq->udp_port, (u_char*)send_buffer, total_length); } /************************************************************************* * * Function: send_challenge * * Purpose: Reply to the request with a CHALLENGE. Also attach * any user message provided and a state value. * *************************************************************************/ void send_challenge(AUTH_REQ *authreq,char *msg,char *state,int activefd) { VALUE_PAIR *reply; int len; int total_length; report[RR_CHALLENGE]++; if((state != (char *)NULL) && ((len=strlen(state)) > (size_t)0)) { reply = pairalloc("send_challenge"); memcpy(reply->name,"State",5); reply->attribute = PW_STATE; reply->type = PW_TYPE_STRING; if (len > AUTH_STRING_LEN) { len = AUTH_STRING_LEN; } reply->lvalue = len; memcpy(reply->strvalue,state,len); } else { reply = (VALUE_PAIR *)NULL; } debug("sending challenge to %s\n", req2strp(authreq)); total_length = build_packet(authreq,reply,msg,PW_ACCESS_CHALLENGE,FW_REPLY,send_buffer,sizeof(send_buffer)); send_packet(activefd,authreq->ipaddr,authreq->udp_port,send_buffer,total_length); } /************************************************************************* * * Function: send_accept * * Purpose: Reply to the request with an ACKNOWLEDGE. Also attach * reply attribute value pairs and any user message provided. * *************************************************************************/ void send_accept(AUTH_REQ *authreq,VALUE_PAIR *reply,char *msg,int activefd) { VALUE_PAIR *menu_attr; char state_value[120]; int total_length; /* Check to see if the response is a menu */ if((menu_attr = get_attribute(reply, PW_MENU)) != (VALUE_PAIR *)NULL) { msg = get_menu(menu_attr->strvalue); snprintf(state_value,sizeof(state_value),"MENU=%s", menu_attr->strvalue); send_challenge(authreq, msg, state_value, activefd); return; } report[RR_ACCEPT]++; debug("sending accept to %s\n", req2strp(authreq)); total_length = build_packet(authreq,reply,msg,PW_AUTHENTICATION_ACK,FW_REPLY,send_buffer,sizeof(send_buffer)); /* Send it to the user */ send_packet(activefd, authreq->ipaddr, authreq->udp_port, (u_char*)send_buffer, total_length); } /************************************************************************* * * Function: build_packet * * Purpose: called by routines to build RADIUS packet * * forward = 0 FW_REPLY replying to client * 1 FW_SERVER forwarding request to remote server * 2 FW_CLIENT forwarding response to client * *************************************************************************/ int build_packet(AUTH_REQ*authreq,VALUE_PAIR*reply,char*msg,u_char code,int forward,u_char*buffer,size_t buflen ) { AUTH_HDR *auth; VALUE_PAIR *item; u_short total_length; u_char *ptr; u_char *lptr; int len; UINT4 lvalue; UINT4 vendor; u_char digest[16]; int secretlen; int block_len; auth = (AUTH_HDR *)buffer; /* Build standard header */ auth->code = code; auth->id = authreq->id; total_length = AUTH_HDR_LEN; /* Load up the configuration values for the user */ ptr = auth->data; while(reply != (VALUE_PAIR *)NULL) { debug_pair(reply); *ptr++ = reply->attribute; lptr = ptr; if (reply->attribute == PW_VENDOR && reply->vendor != 0) { ptr++; total_length += 6; vendor = htonl(reply->vendor); memcpy(ptr,&vendor,sizeof(UINT4)); ptr += 4; *ptr++ = reply->vsattribute; if (reply->type == PW_TYPE_STRING) { *lptr = reply->lvalue + 8; } else { *lptr = 12; } } switch(reply->type) { #if defined(ASCEND_BINARY) case PW_TYPE_ABINARY: #endif case PW_TYPE_STRING: len = reply->lvalue; if (len > AUTH_STRING_LEN) { len = AUTH_STRING_LEN; } *ptr++ = len + 2; memcpy(ptr, reply->strvalue,len); ptr += len; total_length += len + 2; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: *ptr++ = sizeof(UINT4) + 2; lvalue = htonl(reply->lvalue); memcpy(ptr, &lvalue, sizeof(UINT4)); ptr += sizeof(UINT4); total_length += sizeof(UINT4) + 2; break; default: break; } reply = reply->next; } /* Append the user message */ if(msg != (char *)NULL && (len = strlen(msg)) > 0) { while(len > 0) { if(len > AUTH_STRING_LEN) { block_len = AUTH_STRING_LEN; } else { block_len = len; } *ptr++ = PW_PORT_MESSAGE; *ptr++ = block_len + 2; memcpy(ptr, msg, block_len); msg += block_len; ptr += block_len; total_length += block_len + 2; len -= block_len; } } /* Copy over any proxy-states, in order */ if (forward == FW_REPLY) { item = authreq->request; while (item != (VALUE_PAIR *)NULL) { if (item->attribute == PW_PROXY) { debug_pair(item); *ptr++ = PW_PROXY; len = item->lvalue; *ptr++ = len + 2; memcpy(ptr,item->strvalue,len); ptr += len; total_length += len + 2; } item = item->next; } } auth->length = htons(total_length); if (code == PW_AUTHENTICATION_ACK || code == PW_AUTHENTICATION_REJECT || code == PW_ACCESS_CHALLENGE || code == PW_ACCOUNTING_RESPONSE) { /* The Authenticator field in an Response packet is called the Response Authenticator, and contains a one-way MD5 hash calculated over a stream of octets consisting of the Response Code, Identifier, Length, the Request Authenticator field from the Request packet being replied to, and the response attributes if any, followed by the shared secret. The resulting 16 octet MD5 hash value is stored in the Authenticator field of the Response packet. */ /* Append secret and calculate the response digest */ memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); secretlen = strlen((const char *)authreq->secret); if ((size_t)(total_length+secretlen)<=buflen) { memcpy(buffer + total_length, authreq->secret, secretlen); md5_calc(digest, (u_char *)auth, total_length + secretlen); memcpy(auth->vector, digest, AUTH_VECTOR_LEN); memset(buffer + total_length, 0, secretlen); } else { log_err("build_packet: execeeding buffer size"); } } else if (code == PW_ACCOUNTING_REQUEST) { /* Forwarding */ memset(auth->vector, 0, AUTH_VECTOR_LEN); secretlen = strlen(authreq->forw_secret); if ((size_t)(total_length+secretlen)<=buflen) { memcpy(buffer + total_length, authreq->forw_secret, secretlen); md5_calc(digest, buffer, total_length + secretlen); memcpy(auth->vector,digest,AUTH_VECTOR_LEN); memset(buffer + total_length, 0, secretlen); } else { log_err("build_packet: execeeding buffer size"); } } else if (code == PW_AUTHENTICATION_REQUEST) { /* Forwarding */ memcpy(auth->vector, authreq->vector, AUTH_VECTOR_LEN); } return ((int)total_length); } /************************************************************************* * * Function: decrypt_password * * Purpose: decrypts the User-Password attribute in place in * authreq->request using authreq->vector and * secret and the algorithm specified in the * RADIUS RFC, and returns a pointer to the Password * if successful, otherwise NULL * *************************************************************************/ char * decrypt_password(AUTH_REQ *authreq,char *secret) { VALUE_PAIR *auth_item; int i; int j; int passlen; char hold_vector[AUTH_VECTOR_LEN]; char pw_digest[16]; char *string; char *ptr; calc_digest(pw_digest, authreq, secret); /* * Decrypt the password in the request. */ if((auth_item = get_attribute(authreq->request, PW_PASSWORD)) == (VALUE_PAIR *)NULL) { return (char *)NULL; } passlen = auth_item->lvalue; if(passlen > AUTH_MAXPASS_LEN) { log_err("decrypt_password: Password length %d > %d max not allowed\n",passlen, AUTH_MAXPASS_LEN); passlen = AUTH_MAXPASS_LEN; } string = auth_item->strvalue; ptr = string; for(i = 0;i < passlen;i += AUTH_PASS_LEN) { /* * Store the vector to be used in next segment * of the encrypted password. */ memcpy(hold_vector, ptr, AUTH_VECTOR_LEN); /* Decrypt from the digest */ for(j = 0;j < AUTH_PASS_LEN;j++) { *ptr ^= pw_digest[j]; ptr++; } /* Calculate the next digest if necessary */ if(i + AUTH_PASS_LEN < passlen) { calc_next_digest((u_char*)pw_digest, (u_char*)secret, (u_char*)hold_vector); } } *ptr = '\0'; /* this depends on the fact that auth_item->strvalue always has an extra byte available */ return string; } /************************************************************************* * * Function: encrypt_password * * Purpose: encrypts the User-Password attribute in place in * authreq->request using authreq->vector and * secret and the algorithm specified in the * RADIUS RFC, and returns a pointer to the Password * if successful, otherwise NULL * *************************************************************************/ char * encrypt_password(AUTH_REQ *authreq,char *secret) { VALUE_PAIR *auth_item; int i; int j; int passlen; char *hold_vector; char pw_digest[16]; char *string; char *ptr; calc_digest((u_char*)pw_digest, authreq, (u_char*)secret); /* * Decrypt the password in the request. */ if((auth_item = get_attribute(authreq->request, PW_PASSWORD)) == (VALUE_PAIR *)NULL) { return (char *)NULL; } passlen = auth_item->lvalue; if(passlen > AUTH_MAXPASS_LEN) { log_err("encrypt_password: Password length %d > %d not allowed, truncating\n",passlen, AUTH_MAXPASS_LEN); passlen = AUTH_MAXPASS_LEN; auth_item->lvalue = AUTH_MAXPASS_LEN; auth_item->strvalue[AUTH_MAXPASS_LEN] = '\0'; } string = auth_item->strvalue; ptr = string; for(i = 0;i < passlen;i += AUTH_PASS_LEN) { /* Encrypt using the digest */ hold_vector = ptr; for(j = 0;j < AUTH_PASS_LEN;j++) { *ptr ^= pw_digest[j]; ptr++; } /* Calculate the next digest if necessary */ if(i < passlen) { calc_next_digest((u_char*)pw_digest, (u_char*)secret, (u_char*)hold_vector); } } *ptr = '\0'; /* this depends on the fact that auth_item->strvalue always has an extra byte available */ return string; } /************************************************************************* * * Function: calc_digest * * Purpose: Validates the requesting client NAS. Calculates the * digest to be used for decrypting the users password * based on the clients private key. * *************************************************************************/ void calc_digest(u_char *digest,AUTH_REQ *authreq,u_char *secret) { u_char buffer[128]; int secretlen; /* Use the secret to setup the decryption digest */ memset(buffer, 0, sizeof(buffer)); secretlen = strlen((char *)secret); memcpy((char *)buffer, (char *)secret,secretlen); memcpy(buffer + secretlen, authreq->vector, AUTH_VECTOR_LEN); md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN); memset(buffer, 0, secretlen+AUTH_VECTOR_LEN); return; } /************************************************************************* * * Function: calc_next_digest * * Purpose: Calculates the digest to be used for decrypting the * users password past the first 16 octets based on the clients * private key. * *************************************************************************/ void calc_next_digest(u_char*digest,u_char*secret,u_char*vector) { u_char buffer[128]; int secretlen; /* Use the secret to setup the decryption digest */ memset(buffer, 0, sizeof(buffer)); secretlen = strlen((const char *)secret); strcpy((char *)buffer, (const char *)secret); memcpy(buffer + secretlen, vector, AUTH_VECTOR_LEN); md5_calc(digest, buffer, secretlen + AUTH_VECTOR_LEN); memset(buffer, 0, sizeof(buffer)); } /************************************************************************* * * Function: client_hostname * * Purpose: Return the cached client name if we have one. Otherwise * use the regular ip_hostname() function. * *************************************************************************/ char* client_hostname(UINT4 ipaddr) { u_char secret[64]; char hostnm[MAX_HOST_SIZE]; /* Look at the last used entry first */ if(ipaddr == cached_ipaddr) { return(cached_hostnm); } if(find_client(ipaddr, secret, sizeof(secret), hostnm, sizeof(hostnm)) != 0) { memset(secret, 0, sizeof(secret)); return(cached_hostnm); /* set by find_client() */ } return(ip_hostname(ipaddr)); } /************************************************************************* * * Function: find_client * * Purpose: Retrieve the client name and secret from the temporary * DBM client database. * *************************************************************************/ int find_client(UINT4 ipaddr,char*secret,int secretlen,char*hostnm,size_t hostnmlen) { GDBM_FILE db; char buffer[MAX_LINE_SIZE]; char ip_str[32]; datum contentd; datum named; int ret; char fmt[16]; if (snprintf(fmt,sizeof(fmt), "%%%ds%%%ds", secretlen, hostnmlen)>=sizeof(fmt)) { log_err("format too long\n"); return(-1); } /* Find the client in the database */ snprintf((char *)buffer,sizeof(buffer),"%s/%s", radius_dir, RADIUS_CLIENT_CACHE); if ((db = gdbm_open(buffer,0,GDBM_READER,0600,NULL )) == NULL) { log_err("could not read %s to find clients\n", buffer); return(-1); } ipaddr2str(ip_str, sizeof(ip_str), ipaddr); named.dptr = ip_str; named.dsize = strlen(ip_str); contentd = gdbm_fetch(db, named); if(contentd.dsize == 0 || contentd.dptr == NULL) { gdbm_close(db); log_err("client %s not found in client cache\n",ip_str); if (contentd.dptr!=NULL) free(contentd.dptr); return(-1); } if (contentd.dsize >= MAX_LINE_SIZE) { gdbm_close(db); log_err("client %s length %d > %d in client cache\n", ip_str, contentd.dsize, MAX_LINE_SIZE-1); free(contentd.dptr); return(-1); } /* convoluted method of working around Solaris x86 2.5 bug */ memcpy(buffer,contentd.dptr,contentd.dsize-1); buffer[contentd.dsize - 1] = '\n'; buffer[contentd.dsize] = '\0'; if((ret=sscanf((const char *)buffer, fmt, hostnm, secret)) != 2) { gdbm_close(db); log_err("client cache entry for %s could not be parsed (%d)\n", ip_str,ret); free(contentd.dptr); return(-1); } /* Build a cached hostname entry for client_hostname() to use */ strncpy(cached_hostnm, hostnm, MAX_HOST_SIZE); cached_hostnm[MAX_HOST_SIZE-1] = '\0'; cached_ipaddr = ipaddr; gdbm_close(db); free(contentd.dptr); return(0); } /************************************************************************* * * Function: update_clients * * Purpose: Check last modified time on clients file and build a * new temporary DBM client database if the file has been * changed. * *************************************************************************/ int update_clients(void) { static time_t last_update_time; struct stat statbuf; struct stat statbuf2; datum named; datum contentd; GDBM_FILE db; FILE *clientfd; u_char buffer[256]; u_char oldcache[256]; u_char newcache[256]; u_char secret[64]; char hostnm[128]; char ip_str[64]; int nclients; int rcode; int s1; int s2; UINT4 ipaddr; nclients = 0; rcode = 0; /* Check last modified time of clients file */ snprintf((char *)buffer,sizeof(buffer),"%s/%s", radius_dir, RADIUS_CLIENTS); if(stat(buffer, &statbuf) != 0) { log_err("Error: clients file %s not found\n", buffer); return(-1); } if(statbuf.st_mtime == last_update_time) { /* nothing to update */ return(0); } cached_ipaddr = 0; /* Open the standard clients file */ if((clientfd = fopen((const char *)buffer, "r")) == (FILE *)NULL) { log_err("Error: could not read clients file %s\n", buffer); return(-1); } /* Open and truncate the clients DBM cache file */ snprintf((char *)oldcache,sizeof(oldcache),"%s/%s", radius_dir, RADIUS_CLIENT_CACHE); snprintf((char *)newcache,sizeof(oldcache),"%s.lock", oldcache); if((db=gdbm_open(newcache,0,GDBM_NEWDB,0600,NULL)) == NULL) { log_err("Error: could not create temporary client cache file %s\n",newcache); return(-1); } while(fgets((char *)buffer, sizeof(buffer), clientfd) != (char *)NULL) { if(*buffer == '#') { continue; } if(sscanf((const char *)buffer, "%s%s", hostnm, secret) != 2) { continue; } if((ipaddr = get_ipaddr(hostnm)) != (UINT4)0) { ipaddr2str(ip_str, sizeof(ip_str), ipaddr); named.dptr = ip_str; named.dsize = strlen(ip_str); contentd.dptr = (char *)buffer; contentd.dsize = strlen(buffer); if(gdbm_store(db, named, contentd, GDBM_INSERT) != 0) { log_err("could not cache client datum for host %s\n", hostnm); rcode = -1; } else { nclients++; } } } gdbm_close(db); fclose(clientfd); if (rename(newcache,oldcache) != 0) { log_err("Error: could not move client cache file %s to %s," "error %d\n",newcache,oldcache,errno); return(-1); } else { debug("updated client cache with %d clients\n",nclients); } if (rcode == 0) last_update_time = statbuf.st_mtime; return(rcode); } /************************************************************************* * * Function: debug_pair * * Purpose: Print the Attribute-value pair to the desired File. * *************************************************************************/ void debug_pair(VALUE_PAIR * pair) { if(debug_flag) { fprint_attr_val(stdout, pair); } } /************************************************************************* * * Function: usage * * Purpose: Display the syntax for starting this program. * *************************************************************************/ void usage(void) { fprintf(stderr, "Usage: %s\n", progname); fprintf(stderr, " [-a ] set accounting directory\n"); fprintf(stderr, " [-b] use GDBM for users file\n"); fprintf(stderr, " [-c] clear user stats database\n"); fprintf(stderr, " [-d ] set radiusd database directory\n"); fprintf(stderr, " [-h] print this usage\n"); fprintf(stderr, " [-f ] set alternate password file\n"); fprintf(stderr, " [-i ] set alternate IP\n"); fprintf(stderr, " [-l ] set radius log file\n"); fprintf(stderr, " [-o] accept all-zero accounting requests authenticator\n"); fprintf(stderr, " [-p ] set alternate radius port number\n"); fprintf(stderr, " [-q ] set incoming packets queue size\n"); fprintf(stderr, " [-s] do fork\n"); fprintf(stderr, " [-t ] set time out for requests queue\n"); fprintf(stderr, " [-v] print version\n"); fprintf(stderr, " [-w ] set time out for proxy requests\n"); fprintf(stderr, " [-x] set debug mode on\n"); #if defined(PAM) fprintf(stderr, " [-P] set PAM auth mode on\n"); fprintf(stderr, " [-A] set PAM acct mode on\n"); #endif fprintf(stderr, " [-z] as -b -x -d . -a ra\n\n"); exit(-1); } /************************************************************************* * * Function: config_init * * Purpose: intializes configuration values * * expiration_seconds - When updating a user password, * the amount of time to add to the current time * to set the time when the password will expire. * This is stored as the VALUE Password-Expiration * in the dictionary as number of days. * * warning_seconds - When acknowledging a user authentication * time remaining for valid password to notify user * of password expiration. * *************************************************************************/ int config_init(void) { DICT_VALUE *dval; DICT_VALUE *dict_valfind(); if((dval = dict_valfind("Password-Expiration")) == (DICT_VALUE *)NULL) { expiration_seconds = (UINT4)0; } else { expiration_seconds = dval->value * (UINT4)SECONDS_PER_DAY; } if((dval = dict_valfind("Password-Warning")) == (DICT_VALUE *)NULL) { warning_seconds = (UINT4)0; } else { warning_seconds = dval->value * (UINT4)SECONDS_PER_DAY; } strcpy(unknown,"unknown"); /* for client caching error return */ return(0); } /************************************************************************* * * Function: set_expiration * * Purpose: Set the new expiration time by updating or adding the Expiration attribute-value pair. * *************************************************************************/ int set_expiration(VALUE_PAIR*user_check,UINT4 expiration) { VALUE_PAIR *exppair; VALUE_PAIR *prev; struct timeval tp; struct timezone tzp; if(user_check == (VALUE_PAIR *)NULL) { return(-1); } /* Look for an existing expiration entry */ exppair = user_check; prev = (VALUE_PAIR *)NULL; while(exppair != (VALUE_PAIR *)NULL) { if(exppair->attribute == PW_EXPIRATION) { break; } prev = exppair; exppair = exppair->next; } if(exppair == (VALUE_PAIR *)NULL) { /* Add a new attr-value pair */ exppair = pairalloc("set_expiration"); /* Initialize it */ strcpy(exppair->name, "Expiration"); exppair->attribute = PW_EXPIRATION; exppair->type = PW_TYPE_DATE; /* Attach it to the list. */ prev->next = exppair; } /* calculate a new expiration */ gettimeofday(&tp, &tzp); exppair->lvalue = tp.tv_sec + expiration; return(0); } /************************************************************************* * * Function: pw_expired * * Purpose: Tests to see if the users password has expired. * * Return: Number of days before expiration if a warning is required * otherwise 0 for success and -1 for failure. * *************************************************************************/ int pw_expired(UINT4 exptime) { struct timeval tp; struct timezone tzp; UINT4 exp_remain; int exp_remain_int; if(expiration_seconds == (UINT4)0) { /* expiration not enabled */ return(0); } gettimeofday(&tp, &tzp); if(tp.tv_sec > exptime) { return(-1); } if(warning_seconds != (UINT4)0) { if(tp.tv_sec > exptime - warning_seconds) { exp_remain = exptime - tp.tv_sec; exp_remain /= (UINT4)SECONDS_PER_DAY; exp_remain_int = exp_remain; return(exp_remain_int); } } return(0); } /************************************************************************* * * Function: get_attribute * * Purpose: Retrieve a specific value-pair from a list of value-pairs. * *************************************************************************/ VALUE_PAIR* get_attribute(VALUE_PAIR*value_list,int attribute) { while(value_list != (VALUE_PAIR *)NULL) { if(value_list->attribute == attribute) { return(value_list); } value_list = value_list->next; } return((VALUE_PAIR *)NULL); } void sig_fatal(int sig) { if(acct_pid > 0) { kill(acct_pid, SIGKILL); } #ifdef ACTIVCARD activcard_exit(); /* close activcard session */ #endif log_err("exit on signal %d\n", sig); rad_exit(1); } void sig_hup(int sig) { return; } void sig_usr1(int sig) { extern int debug_flag; debug_flag++; log_err("debug mode %d\n",debug_flag); log_counters(); return; } void sig_usr2(int sig) { extern int debug_flag; if (debug_flag) { log_err("debug mode 0\n"); } debug_flag = 0; log_counters(); return; } void rad_exit(int rc) { #ifdef SECURID AUTH_REQ *curreq; key_t msg_key; int msg_id; if (rad_spawned_child_pid == 0) { /* * child clean up */ msg_key = RADIUS_MSG_KEY(getpid()); if ((msg_id = msgget(msg_key, 0600)) != -1) { msgctl(msg_id, IPC_RMID, 0); } } else { /* * parent clean up */ curreq = first_request; while(curreq != (AUTH_REQ *)NULL) { msg_key = RADIUS_MSG_KEY(curreq->child_pid); if ((msg_id = msgget(msg_key, 0600)) != -1) { msgctl(msg_id, IPC_RMID, 0); } curreq = curreq->next; } } #endif /* SECURID */ log_counters(); exit(rc); } void log_counters(void) { extern int report[]; log_err("counters %d %d / %d %d / accept %d reject %d challenge %d response %d\n", report[RR_PORT1],report[RR_PORT2],report[RR_PORT3],report[RR_PORT4], report[RR_ACCEPT],report[RR_REJECT],report[RR_CHALLENGE], report[RR_ACCOUNT]); memreport(); proxy_report(); } yardradius-1.1.2/src/radlast.c0000644000175000017500000002562010640412013013164 00000000000000/* * Copyright (C) 1999-2002 Francesco P. Lovergine. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms stated in the LICENSE file which should be * enclosed with sources. */ static char rcsid[] = "$Id: radlast.c 75 2004-08-02 18:40:07Z flovergine $"; # define __MAIN__ #include "yard.h" #include "global.h" char *progname = NULL; char username[USERNAME_MAX]; static int brief = 0; static int extended = 0; static const char * port_type_str(int type) { switch (type) { case 0: return "ASYN"; case 1: return "SYNC"; case 2: return "ISDN"; case 3: return "V120"; case 4: return "V110"; default: return "UNKN"; } /* not reached */ return "Unknown"; } static const char * proto_type_str(int proto) { switch (proto) { case P_TELNET: return "TELN"; case P_RLOGIN: return "RLOG"; case P_TCP_CLEAR: return "DATA"; case P_PORTMASTER: return "PM"; case P_PPP: return "PPP"; case P_SLIP: return "SLIP"; case P_CSLIP: return "CSLP"; default: return "UNK"; } /* not reached */ return "UNK"; } static int parse_width(char **format) { int width=0; char *endstr; if ( isdigit(**format) || **format=='+' || **format=='-' ) { width=(int)strtol(*format,&endstr,10); *format=endstr; } return width; } /* * Function: parse_formatstr() * Purpose: parse a -F format string and outputs as required all * radlast fields. * * Format tokens are: * %l Username * %p Port ID * %a NAS ip address * %k Port type * %c Client ip address * %d Date in ctime format * %t Online time in secs * %T Online time in HH:MM:SS format * %i Input traffic in bytes * %I Input traffic in KBytes * %o Output traffic in bytes * %O Output traffic in KBytes * %m Total traffic in bytes * %M Total traffic in KBytes * %s Input speed in bps (or UNKNOWN) * %S Output speed in bps (or UNKNOWN) * %A NAS called id (or UNKNOWN) * %# Client calling id (or UNKNOWN) * \n \t \\ \% %% As obvious :-) * * Tokens can include an integer signed value for alignment with * the same sense of printf() format value. */ static void parse_formatstr( char *formatstr,radlast rl ) { char port_str[100]; char nas_ip[100]; char client_ip[100]; char fstr[100]; char *str_time; int width; char *ptr; time_t login_time; if ( formatstr == NULL ) return; memset(port_str, 0, sizeof(port_str)); memset(nas_ip, 0, sizeof(nas_ip)); memset(client_ip, 0, sizeof(client_ip)); login_time = rl.ut_time; str_time = ctime(&login_time); str_time[19] = '\0'; snprintf(port_str, sizeof(port_str), "%4.4s-%02d", port_type_str(rl.ent.port_type), rl.ent.port); ipaddr2str(nas_ip, sizeof(nas_ip), rl.nas_ip); ipaddr2str(client_ip, sizeof(client_ip), rl.client_ip); while ( *formatstr ) { switch ( *formatstr ) { case '%': formatstr++; width=parse_width(&formatstr); if (width) snprintf(fstr,sizeof(fstr),"%%%d",width); else strcpy(fstr,"%"); switch (*formatstr) { case 'l': strcat(fstr,"s"); printf(fstr,rl.login); break; case 'p': strcat(fstr,"s"); printf(fstr,port_str); break; case 'a': strcat(fstr,"s"); printf(fstr,nas_ip); break; case 'k': strcat(fstr,"s"); printf(fstr,proto_type_str(rl.ent.proto)); break; case 'c': strcat(fstr,"s"); printf(fstr,client_ip); break; case 'd': strcat(fstr,"s"); printf(fstr,str_time); break; case 't': strcat(fstr,"ld"); printf(fstr,rl.length); break; case 'T': ptr=fstr+50; snprintf(ptr,sizeof(fstr)/2,"%dh:%dm:%ds", (int)(rl.length/3600), (int)((rl.length%3600)/60), (int)((rl.length%3600)%60)); strcat(fstr,"s"); printf(fstr,ptr); break; case 'i': strcat(fstr,"ld"); printf(fstr,rl.inb); break; case 'I': strcat(fstr,"ldKB"); printf(fstr,rl.inb/1024); break; case 'o': strcat(fstr,"ld"); printf(fstr,rl.outb); break; case 'O': strcat(fstr,"ldKB"); printf(fstr,rl.outb/1024); break; case 'm': strcat(fstr,"ld"); printf(fstr,rl.outb+rl.inb); break; case 'M': strcat(fstr,"ldKB"); printf(fstr,(rl.outb+rl.inb)/1024); break; case 's': if (rl.rxrate) { strcat(fstr,"d"); printf(fstr,rl.rxrate); } else { strcat(fstr,"s"); printf(fstr,"NONE"); } break; case 'S': if (rl.txrate) { strcat(fstr,"d"); printf(fstr,rl.txrate); } else { strcat(fstr,"s"); printf(fstr,"NONE"); } break; case 'A': strcat(fstr,"s"); printf(fstr,strlen(rl.calledid)?rl.calledid:"NONE"); break; case '#': strcat(fstr,"s"); printf(fstr,strlen(rl.callingid)?rl.callingid:"NONE"); break; case '%': putchar('%'); default: break; } break; case '\\': formatstr++; switch(*formatstr) { case 'n': printf("\n"); break; case 't': printf("\t"); break; case 'r': printf("\r"); break; case '\\': default: printf("\\"); break; } break; default: putchar(*formatstr); break; } formatstr++; } } /* * Function: parse_header() * Purpose: parse a -H header string * * Format tokens are: * \n \t \r \\ As obvious :-) */ static void parse_header( char *header ) { while (*header) { switch (*header) { case '\\': header++; switch(*header) { case 'n': printf("\n"); break; case 't': printf("\t"); break; case 'r': printf("\r"); break; case '\\': default: printf("\\"); break; } break; default: putchar(*header); break; } header++; } } static void print_entry(radlast rad_last) { if (extended) if (brief) parse_formatstr("%l/%p/%a/%k/%c/%d/%T/%I/%O/%M/%s/%S/%A/%#\n", rad_last); else parse_formatstr( "\nUsername = %l\n" "Port-Number = %p\n" "NAS-IP = %a\n" "Service-Type = %k\n" "User-IP = %c\n" "Date = %d\n" "Session-Time = %T\n" "Input-Traffic = %I\n" "Output-Traffic = %O\n" "Total-Traffic = %M\n" "Rx-Speed = %s\n" "Tx-Speed = %S\n" "NAS-CLI = %A\n" "User-CLI = %#\n",rad_last ); else /* !extended */ if (brief) parse_formatstr("%l/%p/%a/%k/%c/%d/%T\n",rad_last); else parse_formatstr( "\nUsername = %l\n" "Port-Number = %p\n" "NAS-IP = %a\n" "Service-Type = %k\n" "User-IP = %c\n" "Date = %d\n" "Session-Time = %T\n",rad_last); } static void radlast_usage(void) { printf("\nUsage: %s