bug-gnu-radius
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug-gnu-radius] Sybase and SecurID patch for GNU RADIUS 0.96.2


From: Steve Bleazard
Subject: [Bug-gnu-radius] Sybase and SecurID patch for GNU RADIUS 0.96.2
Date: Mon, 10 Jun 2002 20:47:51 +0100

Hi

Attached are the context diffs to add SecurID and Sybase support to GNU
RADIUS 0.96.2.  The patch.README file contains the details of the
changes and the patch.sybase is a context diff file

Please let me know if there are any problems and I will try try to
resolve them.

Steve

Attachment: patch.README
Description: application/unknown-content-type-readme_auto_file

diff -N -r -u gnuradius-0.96.2.orig/README gnuradius-0.96.2/README
--- gnuradius-0.96.2.orig/README        Tue Mar 19 22:31:59 2002
+++ gnuradius-0.96.2/README     Thu Jun  6 21:51:44 2002
@@ -48,10 +48,88 @@
     --enable-pam
        Enable PAM support.
        
+    --enable-securid
+       Enable SecurID support.  This option automatically detects libaceclnt.a
+       first and then falls back to sdiclient.a.  However, on OS's that
+       require a lib prefix to recognise a library it may be necessary to
+       ln -s sdiclient.a libsdiclient.a before running configure.  In
+       addition it may be necessary to set --with-lib-path and
+       --with-include-path to include the directory containing the Ace
+       client libraries.
+       
+       SecurID Inc / RSA have a number of versions of the client library:
+       Version prior to 3.x where called sdiclient.a and supported basic
+       synchronous authentication.  Version 3.x and 4.x were shipped with
+       both sdiclient.a and an additional libaceclnt.a library that supported
+       asynchronous (multithreaded) operation.
+       
+       From 5.x onwards the two libaries were combined into a single
+       libaceclnt.a and (on Solaris and maybe others) a shared version called
+       libaceclnt.so.  These versions also supports all version of the
+       Ace/Server from 3.3.1.  The 5.x library can be downloaded for free
+       from RSA's web site. However, access to this may require SecurCare
+       Online to have been purchased.  For 5.02 of the library the file is
+       called Agentsdk_502.zip
+
+       With the release of the asynchronous library it was possible to pass
+       the client IP address so that authentication proxies (such as RADIUS)
+       could have the Ace/Server perform client authorisation for the
+       original client and not the proxy.
+       
+       Unfortunately the asychronous libraries are tricky to work with,
+       requiring threads programming that can be hard to make portable.
+       However, with the introduction of the 5.x client library a new set
+       of sychronous client functions has been added and it is now easy
+       to pass the client ip address to the Ace/Server.
+
+       As a consquence support for passing the client address to the
+       Ace/Server is only available with version 5.x and later of the
+       libraries.
+
+       The code has been tested with 3.3.1 and 5.02 libraries against a
+       4.1 ACE/Server.  However, experience shows that all libraries after
+       2.3 (and possibly earlier) work with ACE/Server 2.3 onwards.  The
+       reverse (where the library is newer than the server) may not be the
+       case except with 5.02 and (presumably) later version.
+
+    --enable-securid-resend-hack
+       Enable code to prevent duplicate request from disabling a users token.
+       SecurID and the RADIUS retry timeouts can interact very badly and cause
+       the users token to be disabled.  This code attempts to fix the problem.
+       
+       The problem is caused by the SecurID server inserting, by design, a
+       configurable 1 second delay before replying.  Unfortunately, this is
+       very similar to a common NAS retry timeout of around 1 second.  As a
+       consequence the request is often retried.
+
+       Unfortunately with some NASes the resent packet is different enough to
+       make it difficult to spot that it is infact a resend and the request
+       is passed to the backend authentication code.
+
+       At this point the SecurID server is sent the same passcode again and
+       detects a simultaneous login.  After a couple of login attempts the
+       users account is disabled.
+
+       The code added by this option detects these duplicate requests using
+       the username, passcode and NAS ip.  Any request that received within
+       auth request-cleanup-delay seconds (see the config file auth statement)
+       are treated as resends and replied to in the same way as previously.
+
+       Note that the ACE/Server delay should be set to 1 second (the default).
+       Any longer and the code is unlikely to work with resend timouts of a
+       second.
+
     --enable-dbm[={dbm|ndbm}]
        Enable dbm support. If no argument is specified, usual DBM is
        assumed. The `ndbm' argument instructs to enable support of NDBM.
                
+    --enable-nas-ip-hack
+       Enable adding of missing NAS-IP-Address attribute.  Some devices do
+       not include the NAS-IP-Address attribute in the request.  This can
+       make NAS based access control difficult if not impossibe. Enabling
+       this option includes code to add the NAS-IP-Address attribute with the
+       source address of the request if it's missing
+
     --with-sql
        Enable MySQL support. Usually this requires adding appropriate
        --with-lib-path and --with-include-path options.
@@ -60,6 +138,14 @@
        Enable PostgreSQL support. Usually this requires adding appropriate
        --with-lib-path and --with-include-path options.
 
+    --with-sybase
+       Enable Sybase support. Usually this requires adding appropriate
+       --with-lib-path and --with-include-path options.  Note that with
+       Sybase the SYBASE environment variable must be set appropriately
+       before running the server.  In addition, the name specified in the sql
+       server parameter must have an entry in the interfaces file and the
+       port option is ignored.
+    
     --with-odbc[={odbc|iodbc}]
         Configure to work with ODBC. This is an experimental feature, it
         has not been tested thoroughly.
diff -N -r -u gnuradius-0.96.2.orig/acconfig.h gnuradius-0.96.2/acconfig.h
--- gnuradius-0.96.2.orig/acconfig.h    Tue Mar 19 22:31:59 2002
+++ gnuradius-0.96.2/acconfig.h Thu Jun  6 14:03:17 2002
@@ -67,12 +67,16 @@
 
 #undef HAVE_LIBPAM
 
+#undef HAVE_LIBSECURID
+
 #undef HAVE_LIBMYSQL
 
 #undef HAVE_LIBPQ
 
 #undef HAVE_LIBODBC
 
+#undef HAVE_LIBSYBASE
+
 /* Define this to disable shadow support */
 #undef PWD_SHADOW
 
@@ -101,6 +105,9 @@
 /* Define this to enable ODBC subsystem of SQL support */
 #undef USE_SQL_ODBC
 
+/* Define this to enable Sybase subsystem of SQL support */
+#undef USE_SQL_SYBASE
+
 /* Define this to enable Livingston-compatible menus */
 #undef USE_LIVINGSTON_MENUS
 
@@ -110,6 +117,18 @@
 
 /* Define this to enable PAM support */
 #undef USE_PAM
+
+/* Define this to enable SecurID support */
+#undef USE_SECURID
+
+/* Define this to enable SecurID legacy function support */
+#undef USE_SECURID_LEGACY
+
+/* Define this to enable SecurID resend detection */
+#undef USE_SECURID_RESEND_HACK
+
+/* Define this to enable NAS IP address adding code */
+#undef USE_NAS_IP_HACK
 
 /* Define this if you wish the DBM support */
 #undef USE_DBM
diff -N -r -u gnuradius-0.96.2.orig/aclocal.m4 gnuradius-0.96.2/aclocal.m4
--- gnuradius-0.96.2.orig/aclocal.m4    Wed Mar 20 18:34:16 2002
+++ gnuradius-0.96.2/aclocal.m4 Mon May 27 17:47:39 2002
@@ -4035,7 +4035,7 @@
    do
       LIBS="$save_LIBS -L$path"
       AC_CHECK_LIB($1, $2,
-                   [rad_cv_lib_$1="$3 -L$path -l$1"
+                   [rad_cv_lib_$1="-L$path -l$1 $3"
                     break],
                    [rad_cv_lib_$1=no],$3)
    done
diff -N -r -u gnuradius-0.96.2.orig/config.h.in gnuradius-0.96.2/config.h.in
--- gnuradius-0.96.2.orig/config.h.in   Wed Mar 20 18:20:35 2002
+++ gnuradius-0.96.2/config.h.in        Thu Jun  6 14:03:17 2002
@@ -273,12 +273,16 @@
 
 #undef HAVE_LIBPAM
 
+#undef HAVE_LIBSECURID
+
 #undef HAVE_LIBMYSQL
 
 #undef HAVE_LIBPQ
 
 #undef HAVE_LIBODBC
 
+#undef HAVE_LIBSYBASE
+
 /* Define this to disable shadow support */
 #undef PWD_SHADOW
 
@@ -307,6 +311,9 @@
 /* Define this to enable ODBC subsystem of SQL support */
 #undef USE_SQL_ODBC
 
+/* Define this to enable Sybase subsystem of SQL support */
+#undef USE_SQL_SYBASE
+
 /* Define this to enable Livingston-compatible menus */
 #undef USE_LIVINGSTON_MENUS
 
@@ -316,6 +323,18 @@
 
 /* Define this to enable PAM support */
 #undef USE_PAM
+
+/* Define this to enable SecurID support */
+#undef USE_SECURID
+
+/* Define this to enable legacy SecurID support */
+#undef USE_SECURID_LEGACY
+
+/* Define this to enable SecurID resend detection */
+#undef USE_SECURID_RESEND_HACK
+
+/* Define this to enable NAS IP address adding code */
+#undef USE_NAS_IP_HACK
 
 /* Define this if you wish the DBM support */
 #undef USE_DBM
diff -N -r -u gnuradius-0.96.2.orig/configure.in gnuradius-0.96.2/configure.in
--- gnuradius-0.96.2.orig/configure.in  Tue Mar 19 23:32:38 2002
+++ gnuradius-0.96.2/configure.in       Thu Jun  6 14:03:17 2002
@@ -316,6 +316,76 @@
                  exit 1])
 fi
 
+## **************
+## SecurID support
+## **************
+       
+AC_ARG_ENABLE(securid,
+       [  --enable-securid                      enable securid support],
+       [case $enableval in
+               yes)            USE_SECURID=yes; AC_DEFINE(USE_SECURID);;
+               no)             ;;
+               *)              AC_MSG_ERROR(--enable-securid can't be used 
with an argument)
+                               exit 1;;
+       esac])
+
+AC_ARG_ENABLE(securid-resend-hack,
+       [  --enable-securid-resend-hack          enable securid resend 
detection],
+       [case $enableval in
+               yes)            USE_SECURID_RESEND_HACK=yes;
+                               AC_DEFINE(USE_SECURID_RESEND_HACK);;
+               no)             ;;
+               *)              AC_MSG_ERROR(--enable-securid-resend-hack can't 
be used with an argument)
+                               exit 1;;
+       esac])
+
+if test x$USE_SECURID = xyes; then
+       HAVE_ACECLNT=no
+       HAVE_SDICLIENT=no
+       AC_CHECK_LIB(aceclnt, SD_Init,
+                       [ AC_DEFINE(HAVE_LIBSECURID)
+                       HAVE_ACECLNT=yes
+                       HAVE_ACE=yes
+                               RADIUSD_LDADD="$RADIUSD_LDADD -laceclnt" ])
+
+       if test x$HAVE_ACECLNT = xno; then
+               save_LIBS=$LIBS
+               LIBS="-lsdiclient $LIBS"
+               AC_TRY_RUN([char configure; tst(){creadcfg();}main(){exit(0);}],
+                          [ HAVE_SDICLIENT=yes
+                          HAVE_ACE=yes
+                          AC_DEFINE(USE_SECURID_LEGACY)
+                                  RADIUSD_LDADD="$RADIUSD_LDADD -lsdiclient"],
+                          [ AC_MSG_ERROR(can't find aceclnt or sdiclient 
libraries - have you linked sdiclient.a to libsdiclient.a)],
+                          [ HAVE_SDICLIENT=yes
+                          AC_DEFINE(USE_SECURID_LEGACY)
+                                  RADIUSD_LDADD="$RADIUSD_LDADD -lsdiclient"])
+               LIBS=$save_LIBS
+       fi
+       if test x$HAVE_ACECLNT = xyes; then
+               AC_TRY_CPP(acexport.h,,
+                               [ AC_MSG_ERROR(can't find header acexport.h)
+                               exit 0] )
+       fi
+       if test x$HAVE_SDICLIENT = xyes; then
+               AC_TRY_CPP(sdi_athd.h,,
+                               [ AC_MSG_ERROR(can't find header sdi_athd.h)
+                               exit 0] )
+       fi
+       if test x$HAVE_ACE = xyes; then
+               if test x$USE_SECURID_RESEND_HACK = xyes; then
+                       AC_CHECK_HEADER(ndbm.h,,
+                                       [ AC_MSG_ERROR(can't find header ndbm.h 
- ndbm is required by SecurID)
+                                       exit 0] )
+                       AC_CHECK_FUNC(dbm_open,,
+                               AC_CHECK_LIB(ndbm, dbm_open,
+                                       [ AC_DEFINE(HAVE_LIBNDBM) 
+                                       RADIUSD_LDADD="$RADIUSD_LDADD -lndbm" ],
+                               [ AC_MSG_ERROR(-lndbm not found - ndbm is 
required by SecurID) ]))
+               fi
+       fi
+fi
+       
 
 ## **************
 ## DBM 
@@ -395,6 +465,20 @@
         no)    ;;
         esac])
 
+AC_ARG_WITH(sybase,
+        [  --with-sybase       Configure to work with Sybase],
+       [case $withval in
+        yes)   rad_CHECK_LIB(ct, ct_init, [-lcs -ltcl -lcomn],
+                           [ USE_SQL=1
+                              AC_DEFINE(USE_SQL_SYBASE)
+                              AC_DEFINE(HAVE_LIBSYBASE) 
+                              RADIUSD_LDADD="$RADIUSD_LDADD $rad_cv_lib_ct" ],
+                           [ AC_MSG_ERROR(-lct not found) ],
+                            [/usr/sybase/lib])
+               ;;
+        no)    ;;
+        esac])
+
 rad_lib_odbc() {
 rad_CHECK_LIB(odbc, SQLAllocHandle, [],
                            [ USE_SQL=1
@@ -483,6 +567,21 @@
                                ;;
                no)             ;;
                *)              AC_MSG_ERROR(--enable-snmp can't be used with 
an argument)                              
+                               exit 1;;
+       esac])
+       
+## **************
+## NAS_IP_HACK
+## **************
+
+AC_ARG_ENABLE(nas-ip-hack,
+       [  --enable-nas-ip-hack          enable NAS IP Hack support],
+       [case $enableval in
+               yes)            USE_NAS_IP_HACK=yes
+                               AC_DEFINE(USE_NAS_IP_HACK)
+                               ;;
+               no)             ;;
+               *)              AC_MSG_ERROR(--enable-nas-ip-hack can't be used 
with an argument)                               
                                exit 1;;
        esac])
        
diff -N -r -u gnuradius-0.96.2.orig/doc/texinfo/configure.texi 
gnuradius-0.96.2/doc/texinfo/configure.texi
--- gnuradius-0.96.2.orig/doc/texinfo/configure.texi    Tue Mar 19 22:31:59 2002
+++ gnuradius-0.96.2/doc/texinfo/configure.texi Thu Jun  6 21:56:28 2002
@@ -86,6 +86,7 @@
 * acct::        Configure accounting service.
 * proxy::       Configure proxy service.
 * usedbm::      Enable the DBM feature.
+* aceclientip:: Enable the DBM feature.
 * snmp::        Configure SNMP service.
 * guile::       Configure Guile interface.
 * message::     Configure server reply messages.
@@ -566,6 +567,33 @@
 @end table
 
 @comment **L3***************************************************************
address@hidden aceclientip
address@hidden @code{aceclientip} statement
address@hidden Ace: enabling
address@hidden Enabling Ace Client IP
address@hidden aceclientip 
+
address@hidden Syntax:
address@hidden
+aceclientip ( yes | no ) ;
address@hidden example
+
address@hidden Usage
+The @code{aceclientip} statement determines whether the the NAS' IP address
+is passed to the Ace/Server or the IP address of the RADIUS server itself.
+
address@hidden @code
address@hidden no
+The IP address of the RADIUS server will be passed to the Ace/Server. As a
+consequence the Ace/Server will see the RADIUS server as the client.
+
address@hidden yes
+The IP address of the original NAS server will be passed to the Ace/Server.
+As a consequence the Ace/Server will see each NAS as a seperate client.
+
address@hidden table
+
address@hidden 
**L3***************************************************************
 @node snmp
 @subsection @code{snmp} statement 
 @cindex SNMP service parameters
@@ -1555,11 +1583,12 @@
 @table @code
 @item interface @var{iface-type}
 Specifies the @sc{sql} interface to use. Currently supported values
-for @var{iface-type} are @code{mysql} and @code{postgres}. Depending
-on this, the default communication port number is set: it is 3306 for
+for @var{iface-type} are @code{mysql}, @code{sybase} and @code{postgres}.
+Depending on this, the default communication port number is set: it is 3306 for
 @code{interface mysql} and 5432 for @code{interface postgres}. Use of
 this statement is only meaningful when the package was configured with
-both @option{--with-mysql} and @option{--with-postgres} option.
+a combination of @option{--with-mysql}, @option{--with-sybase} and
address@hidden option.
 @item server @var{string}
 Specifies the hostname or @IP{} of the @sc{sql} server.
 @item port @var{number}
diff -N -r -u gnuradius-0.96.2.orig/include/debugmod.h 
gnuradius-0.96.2/include/debugmod.h
--- gnuradius-0.96.2.orig/include/debugmod.h    Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/include/debugmod.h Fri May 24 13:17:29 2002
@@ -61,3 +61,6 @@
 #ifdef RADIUS_MODULE_GRAM
 # define RADIUS_MODULE 5
 #endif
+#ifdef RADIUS_MODULE_SECURID_C
+# define RADIUS_MODULE 20
+#endif
diff -N -r -u gnuradius-0.96.2.orig/include/radiusd.h 
gnuradius-0.96.2/include/radiusd.h
--- gnuradius-0.96.2.orig/include/radiusd.h     Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/include/radiusd.h  Tue Jun  4 17:25:20 2002
@@ -238,6 +238,7 @@
 extern UINT4 warning_seconds;
 extern int radius_pid;
 extern int use_dbm;
+extern int ace_client_ip;
 extern UINT4 myip;
 extern UINT4 warning_seconds;
 extern int auth_port;
@@ -319,6 +320,12 @@
 #ifdef USE_PAM
 int pam_pass(char *name, char *passwd, const char *pamauth, char **reply_msg);
 # define PAM_DEFAULT_TYPE    "radius"
+#endif
+
+/* securid.c */
+#ifdef USE_SECURID
+int securid_pass(char *name, char *passcode, char **reply_msg,
+                RADIUS_REQ *radreq);
 #endif
 
 /* proxy.c */
diff -N -r -u gnuradius-0.96.2.orig/include/radsql.h 
gnuradius-0.96.2/include/radsql.h
--- gnuradius-0.96.2.orig/include/radsql.h      Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/include/radsql.h   Mon May 27 17:18:25 2002
@@ -19,7 +19,8 @@
 #define SQLT_MYSQL    1
 #define SQLT_POSTGRES 2
 #define SQLT_ODBC     3
-#define SQLT_MAX      4
+#define SQLT_SYBASE   4
+#define SQLT_MAX      5
 
 #ifdef USE_SQL
 
@@ -115,6 +116,11 @@
 extern SQL_DISPATCH_TAB odbc_dispatch_tab[];
 #else
 #define odbc_dispatch_tab NULL
+#endif
+#ifdef USE_SQL_SYBASE
+extern SQL_DISPATCH_TAB sybase_dispatch_tab[];
+#else
+#define sybase_dispatch_tab NULL
 #endif
 
 #else
diff -N -r -u gnuradius-0.96.2.orig/m4/lib.m4 gnuradius-0.96.2/m4/lib.m4
--- gnuradius-0.96.2.orig/m4/lib.m4     Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/m4/lib.m4  Fri Jun  7 17:52:30 2002
@@ -14,7 +14,7 @@
    do
       LIBS="$save_LIBS -L$path"
       AC_CHECK_LIB($1, $2,
-                   [rad_cv_lib_$1="$3 -L$path -l$1"
+                   [rad_cv_lib_$1="-L$path -l$1 $3"
                     break],
                    [rad_cv_lib_$1=no],$3)
    done
diff -N -r -u gnuradius-0.96.2.orig/radiusd/Makefile.am 
gnuradius-0.96.2/radiusd/Makefile.am
--- gnuradius-0.96.2.orig/radiusd/Makefile.am   Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/radiusd/Makefile.am        Thu May 23 15:55:06 2002
@@ -25,6 +25,7 @@
  log.c\
  menu.c\
  pam.c\
+ securid.c\
  proxy.c\
  radck.c\
  radius.c\
@@ -79,7 +80,7 @@
        $(LINT.c) -u $(radiusd_LNS) -L../radlib -lrad
 
 SRCS=radiusd.c acct.c auth.c exec.c files.c menu.c sql.c\
-         config.y pam.c proxy.c radius.c\
+         config.y pam.c securid.c proxy.c radius.c\
          timestr.c version.c log.c stat.c\
          snmpserv.c shmem.c radutil.c rewrite.y\
          checkrad.c radck.c builddbm.c
diff -N -r -u gnuradius-0.96.2.orig/radiusd/Makefile.in 
gnuradius-0.96.2/radiusd/Makefile.in
--- gnuradius-0.96.2.orig/radiusd/Makefile.in   Wed Mar 20 19:52:03 2002
+++ gnuradius-0.96.2/radiusd/Makefile.in        Thu May 23 15:57:34 2002
@@ -129,7 +129,7 @@
 lispdir = @lispdir@
 
 sbin_PROGRAMS = radiusd
-radiusd_SOURCES =   acct.c  auth.c  builddbm.c  checkrad.c  config.c  
config_kw.c  config_tok.h  debugmod.c  exec.c  files.c  log.c  menu.c  pam.c  
proxy.c  radck.c  radius.c  radiusd.c  radutil.c  rewrite.c  scheme.c  shmem.c  
snmpserv.c  sql.c  stat.c  timestr.c  version.c
+radiusd_SOURCES =   acct.c  auth.c  builddbm.c  checkrad.c  config.c  
config_kw.c  config_tok.h  debugmod.c  exec.c  files.c  log.c  menu.c  pam.c 
securid.c  proxy.c  radck.c  radius.c  radiusd.c  radutil.c  rewrite.c  
scheme.c  shmem.c  snmpserv.c  sql.c  stat.c  timestr.c  version.c
 
 
 radiusd_LDADD = @LEXLIB@ @SQLLIB@ ../snmplib/libradsnmp.a @RADIUSD_LDADD@ 
../radlib/librad.la 
@@ -148,7 +148,7 @@
 @address@hidden = -gezra -DHAVE_CONFIG_H=1 -DMAINTAINER_MODE=1 $(INCLUDES)
 @address@hidden = $(radiusd_SOURCES:.c=.ln)
 
address@hidden@SRCS = radiusd.c acct.c auth.c exec.c files.c menu.c sql.c       
  config.y pam.c proxy.c radius.c         timestr.c version.c log.c stat.c      
   snmpserv.c shmem.c radutil.c rewrite.y         checkrad.c radck.c builddbm.c
address@hidden@SRCS = radiusd.c acct.c auth.c exec.c files.c menu.c sql.c       
  config.y pam.c securid.c proxy.c radius.c         timestr.c version.c log.c 
stat.c         snmpserv.c shmem.c radutil.c rewrite.y         checkrad.c 
radck.c builddbm.c
 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
 CONFIG_HEADER = ../config.h
 CONFIG_CLEAN_FILES = 
@@ -163,7 +163,7 @@
 radiusd_OBJECTS =  acct.$(OBJEXT) auth.$(OBJEXT) builddbm.$(OBJEXT) \
 checkrad.$(OBJEXT) config.$(OBJEXT) config_kw.$(OBJEXT) \
 debugmod.$(OBJEXT) exec.$(OBJEXT) files.$(OBJEXT) log.$(OBJEXT) \
-menu.$(OBJEXT) pam.$(OBJEXT) proxy.$(OBJEXT) radck.$(OBJEXT) \
+menu.$(OBJEXT) pam.$(OBJEXT) securid.$(OBJEXT) proxy.$(OBJEXT) radck.$(OBJEXT) 
\
 radius.$(OBJEXT) radiusd.$(OBJEXT) radutil.$(OBJEXT) rewrite.$(OBJEXT) \
 scheme.$(OBJEXT) shmem.$(OBJEXT) snmpserv.$(OBJEXT) sql.$(OBJEXT) \
 stat.$(OBJEXT) timestr.$(OBJEXT) version.$(OBJEXT)
@@ -381,6 +381,10 @@
        ../include/log.h ../include/mem.h ../include/radpaths.h \
        ../include/radsnmp.h
 pam.o: pam.c ../config.h ../include/debugmod.h ../include/radiusd.h \
+       ../include/sysdep.h ../include/radius.h ../include/raddict.h \
+       ../include/log.h ../include/mem.h ../include/radpaths.h \
+       ../include/radsnmp.h
+securid.o: securid.c ../config.h ../include/debugmod.h ../include/radiusd.h \
        ../include/sysdep.h ../include/radius.h ../include/raddict.h \
        ../include/log.h ../include/mem.h ../include/radpaths.h \
        ../include/radsnmp.h
diff -N -r -u gnuradius-0.96.2.orig/radiusd/auth.c 
gnuradius-0.96.2/radiusd/auth.c
--- gnuradius-0.96.2.orig/radiusd/auth.c        Tue Mar 19 23:10:18 2002
+++ gnuradius-0.96.2/radiusd/auth.c     Tue Jun  4 17:24:16 2002
@@ -335,6 +335,19 @@
 #endif
                break;
 
+       case DV_AUTH_TYPE_SECURID:
+#ifdef USE_SECURID
+               debug(1, ("  auth: SecurID"));
+               if (securid_pass(name, userpass, user_msg, radreq) != 0)
+                       result = AUTH_FAIL;
+#else
+               radlog(L_ERR,
+                      _("%s: SecurID authentication not available"),
+                      name);
+               result = AUTH_NOUSER;
+#endif
+               break;
+
        case DV_AUTH_TYPE_CRYPT_LOCAL:
                debug(1, ("  auth: Crypt"));
                if (real_password == NULL) {
diff -N -r -u gnuradius-0.96.2.orig/radiusd/config.kw 
gnuradius-0.96.2/radiusd/config.kw
--- gnuradius-0.96.2.orig/radiusd/config.kw     Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/radiusd/config.kw  Tue Jun  4 01:07:36 2002
@@ -33,6 +33,7 @@
 %%
 acct, T_ACCT
 acct-dir, T_ACCT_DIR
+aceclientip, T_ACECLIENTIP
 acl, T_ACL
 alert, T_SEVERITY, _Y(L_ALERT)
 allow, T_ALLOW,
diff -N -r -u gnuradius-0.96.2.orig/radiusd/config.y 
gnuradius-0.96.2/radiusd/config.y
--- gnuradius-0.96.2.orig/radiusd/config.y      Tue Mar 19 23:10:18 2002
+++ gnuradius-0.96.2/radiusd/config.y   Tue Jun  4 22:50:07 2002
@@ -154,7 +154,7 @@
 %token T_USEDBM T_CHECKRAD_ASSUME_LOGGED T_DELAY T_DETAIL T_HOST           
 %token T_EXEC_PROGRAM_GROUP T_EXEC_PROGRAM_USER T_LOAD T_LOAD_PATH T_LOG_DIR
 %token T_MAX_REQUESTS T_MESSAGE T_PORT T_REQUEST_CLEANUP_DELAY T_RETRY T_SPAWN 
-%token T_STRIP_NAMES T_TTL T_USERNAME_CHARS T_USR2DELAY              
+%token T_STRIP_NAMES T_TTL T_USERNAME_CHARS T_USR2DELAY T_ACECLIENTIP
 
 %token T_SOURCE_IP T_ACCT_DIR T_ACCT T_CNTL T_PROXY T_CHANNEL
 %token T_SYSLOG T_NOTIFY T_SNMP T_COMMUNITY T_ACL
@@ -200,6 +200,7 @@
                 | options_stmt
                 | notify_stmt
                 | usedbm_stmt
+                | aceclientip_stmt
                 | auth_stmt
                 | acct_stmt
                 | proxy_stmt 
@@ -732,6 +733,30 @@
 #endif
                  }
                 ;
+
+       /* ACE Client IP support */
+
+aceclientip_stmt     : T_ACECLIENTIP T_BOOL
+                  {
+#ifdef USE_SECURID
+#ifdef USE_SECURID_LEGACY
+                         radlog(L_WARN,
+                                _("%s:%d: aceclientip statement ignored: 
library does not support SD_ClientCheck"),
+                                filename, line_num);
+#else
+                         ace_client_ip = $2;
+                         if (debug_config)
+                                 radlog(L_DEBUG, _("ace client ip: %d"),
+                                        ace_client_ip);
+#endif
+#else
+                         radlog(L_WARN,
+                                _("%s:%d: aceclientip statement ignored: 
radiusd compiled without SecurID support"),
+                                filename, line_num);
+#endif
+                 }
+                ;
+
 
        /* SNMP server parameters */
 
diff -N -r -u gnuradius-0.96.2.orig/radiusd/radck.c 
gnuradius-0.96.2/radiusd/radck.c
--- gnuradius-0.96.2.orig/radiusd/radck.c       Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/radiusd/radck.c    Thu May 23 20:07:24 2002
@@ -354,10 +354,23 @@
                break;
 
        case DV_AUTH_TYPE_SECURID:
+#ifdef USE_SECURID
+               if (password || crypt_password) {
+                       radlog(L_WARN,
+                 _("%s:%d: Password attribute ignored for this Auth-Type"),
+                              filename, line);
+               }
+               if (pass_loc) {
+                       radlog(L_WARN,
+                 _("%s:%d: Password-Location attribute ignored for this 
Auth-Type"),
+                              filename, line);
+               }
+#else
                radlog(L_ERR,
                       _("%s:%d: Authentication type not supported"),
                       filename, line);
                errcnt++;
+#endif
                break;
                
        case DV_AUTH_TYPE_SQL:
diff -N -r -u gnuradius-0.96.2.orig/radiusd/radius.c 
gnuradius-0.96.2/radiusd/radius.c
--- gnuradius-0.96.2.orig/radiusd/radius.c      Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/radiusd/radius.c   Mon May 27 17:21:42 2002
@@ -345,6 +345,9 @@
        VALUE_PAIR      *prev;
        VALUE_PAIR      *pair;
        RADIUS_REQ      *radreq;
+#ifdef USE_NAS_IP_HACK
+       int             has_nas_addr;
+#endif
 
        /*
         *      Pre-allocate the new request data structure
@@ -384,6 +387,9 @@
        length -= AUTH_HDR_LEN;
        first_pair = NULL;
        prev = NULL;
+#ifdef USE_NAS_IP_HACK
+       has_nas_addr = 0;
+#endif
 
        while (length > 0) {
 
@@ -412,6 +418,10 @@
                                length -= 6;
                        }
                }
+#ifdef USE_NAS_IP_HACK
+               else if (attribute == DA_NAS_IP_ADDRESS)
+                       has_nas_addr = 1;
+#endif
 
                if ((attr = attr_number_to_dict(attribute)) == NULL) {
                        debug(1, ("Received unknown attribute %d", attribute));
@@ -474,6 +484,39 @@
                ptr += attrlen;
                length -= attrlen;
        }
+#ifdef USE_NAS_IP_HACK
+       if (!has_nas_addr)
+       {
+               /*      Some vendors fail to include the NAS-IP-Address
+                *      field in the request.  This makes enforcing NAS
+                *      specific controls difficult in many cases.  The
+                *      following adds the source host as the NAS-IP-Address
+                *      if it's missing.
+                */
+               if ((attr = attr_number_to_dict(DA_NAS_IP_ADDRESS)) == NULL) {
+                       debug(1, ("Failed to find standard attribute %d",
+                                 DA_NAS_IP_ADDRESS));
+               } else {
+                       pair = avp_alloc();
+                       
+                       pair->name = attr->name;
+                       pair->attribute = attr->value;
+                       pair->type = attr->type;
+                       pair->prop = attr->prop;
+                       pair->next = NULL;
+                       pair->lvalue = host;
+
+                       debug(10, ("recv: added: %s", format_pair(pair)));
+
+                       if (first_pair == NULL) 
+                               first_pair = pair;
+                       else 
+                               prev->next = pair;
+
+                       prev = pair;
+               }
+       }
+#endif
        radreq->request = first_pair;
        return(radreq);
 }
diff -N -r -u gnuradius-0.96.2.orig/radiusd/radiusd.c 
gnuradius-0.96.2/radiusd/radiusd.c
--- gnuradius-0.96.2.orig/radiusd/radiusd.c     Tue Mar 19 23:30:53 2002
+++ gnuradius-0.96.2/radiusd/radiusd.c  Fri Jun  7 17:19:25 2002
@@ -149,6 +149,7 @@
 static int foreground; /* Stay in the foreground */
 static int spawn_flag; 
 int use_dbm = 0;
+int ace_client_ip = 0;
 int open_acct = 1;
 int auth_detail = 0;
 int acct_detail = 1;      
diff -N -r -u gnuradius-0.96.2.orig/radiusd/securid.c 
gnuradius-0.96.2/radiusd/securid.c
--- gnuradius-0.96.2.orig/radiusd/securid.c     Thu Jan  1 07:30:00 1970
+++ gnuradius-0.96.2/radiusd/securid.c  Thu Jun  6 20:38:58 2002
@@ -0,0 +1,500 @@
+/* This file is part of GNU RADIUS.
+   Copyright (C) 2001 Sergey Poznyakoff
+  
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#define RADIUS_MODULE_SECURID_C
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef USE_SECURID
+
+/** SecurID interface.
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#) $Id: pam.c,v 1.9 2001/11/27 12:13:36 gray Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <time.h>
+#include <ctype.h>
+
+#include <radiusd.h>
+
+#ifdef USE_SECURID_LEGACY
+#include "sdi_athd.h"
+#include "sdi_size.h"
+#include "sdi_type.h"
+#include "sdi_defs.h"
+#include "sdconf.h"
+#include "sdacmvls.h"
+
+unsigned long strtoul ();
+
+union config_record configure;
+#else
+#include "acexport.h"
+#endif
+
+#ifndef SDACE_FILE
+#define SDACE_FILE "/etc/sdace.txt"
+#endif
+
+#ifdef USE_SECURID_LEGACY
+
+/** Fake the new (5.x onward) synchronous functions
+ */
+typedef struct SD_CLIENT *SDI_HANDLE;
+
+int AceInitialize(void)
+{
+       return creadcfg() < 0 ? 0 : 1;
+}
+
+int SD_Init(hdlp)
+       SDI_HANDLE *hdlp;
+{
+       SDI_HANDLE hdl;
+
+       *hdlp = hdl = calloc(1, sizeof(struct SD_CLIENT));
+       if (sd_init(hdl) == 0)
+               return ACM_OK;
+       free(hdl);
+
+       return 1;
+}
+
+int SD_Check(hdl, passcode, name)
+       SDI_HANDLE hdl;
+       char *passcode;
+       char *name;
+{
+       char *spasscode;
+       int ret;
+
+       /* sd_check overwrites the argument, but the 5.x SD_Check does not */
+
+       spasscode = estrdup(passcode);
+       ret = sd_check(spasscode, name, hdl);
+       memset(spasscode, '\0', strlen(spasscode));
+       efree(spasscode);
+
+       return ret;
+}
+
+int SD_ClientCheck(hdl, passcode, name, client)
+       SDI_HANDLE hdl;
+       char *passcode;
+       char *name;
+       unsigned long client;
+{
+       /* not implemented - treat as a normal non-client specific check */
+
+       return SD_Check(hdl, passcode, name);
+}
+
+int SD_Close(hdl)
+       SDI_HANDLE hdl;
+{
+
+       sd_close();
+       free(hdl);
+}
+#endif /* USE_SECURID_LEGACY */
+
+#ifdef USE_SECURID_RESEND_HACK
+
+/** See README for an explanation for this code.
+ */
+
+/** hash_passcode - generate a hash of the passcode using md5crypt so that
+ *  PINs (which are the first part of the passcode - except for the soft
+ *  token) are not exposed.
+ */
+static char *hash_passcode(passcode, usesalt)
+       char *passcode;
+       char *usesalt;
+{
+       int i;
+       char salt[9];
+       static char *m =
+         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+       static int srand_called = 0;
+
+       if (usesalt == NULL) {
+               strcpy(salt, "$1$");
+               if (!srand_called) {
+                       srand(time(NULL));
+                       srand_called = 1;
+               }
+               for (i = 0; i < 5; i++)
+                       salt[i+3] = m[rand()%64];
+               salt[8] = '\0';
+               usesalt = salt;
+       }
+       return md5crypt(passcode, usesalt);
+}
+
+struct databuf {
+       int len;
+       void *buf;
+};
+
+#define USE_SECURID_NDBM 1
+
+#ifdef USE_SECURID_NDBM
+
+#include <ndbm.h>
+#include <fcntl.h>
+#include <errno.h>
+
+typedef DBM *DBMH;
+
+/** Attempt to avoid multiple db access as this may corrupt the database.
+ *  Gives up after 1 sec.
+ */
+static void
+getlock()
+{
+       char *fpath, *flink, ffile[32];
+       int fd, pid, ret, i;
+       struct timeval tm;
+
+       sprintf(ffile, "securid.%d", getpid());
+       fpath = mkfilename(radius_dir, ffile);
+       debug(20, ("file: [%s]\n", fpath));
+
+       if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
+       {
+               debug(20, ("lock file <%s> open failed: %d", fpath, errno));
+               efree(fpath);
+               return;
+       }
+       close(fd);
+
+       flink = mkfilename(radius_dir, "securid.lock");
+       debug(20, ("link: [%s]\n", flink));
+
+       for (i = 0; i<20 && (ret=link(fpath, flink))<0 && errno==EEXIST; i++) {
+               tm.tv_sec = 0;  tm.tv_usec = 50000;
+               select(1, NULL, NULL, NULL, &tm);
+       }
+
+       if (ret < 0)
+               debug(20, ("failed to link: %d", errno));
+
+       unlink(fpath);
+       efree(fpath);
+       efree(flink);
+}
+
+static void
+releaselock()
+{
+       char *flink = mkfilename(radius_dir, "securid.lock");
+
+       debug(20, ("unlink: [%s]\n", flink));
+       efree(flink);
+       unlink(flink);
+}
+
+static DBMH
+open_securid_db(void)
+{
+       char *path;
+       DBMH dbh;
+
+       path = mkfilename(radius_dir, "securid");
+       getlock();
+       dbh = dbm_open(path, O_RDWR|O_CREAT, 0600);
+       if (dbh == NULL)
+       {
+               releaselock();
+               debug(1, ("Failed to create securid database: %s", path));
+       }
+       efree(path);
+
+       return dbh;
+}
+
+close_securid_db(DBMH dbh)
+{
+       dbm_close(dbh);
+       releaselock();
+}
+
+static void
+putdb(key, data)
+       char *key;
+       struct databuf *data;
+{
+       datum k, d;
+       DBMH dbmfile;
+
+       k.dptr = key;
+       k.dsize = strlen(key);
+       d.dptr = data->buf;
+       d.dsize = data->len;
+
+       if ((dbmfile = open_securid_db()) == NULL)
+               return;
+       dbm_store(dbmfile, k, d, DBM_REPLACE);
+       close_securid_db(dbmfile);
+}
+
+static struct databuf
+getdb(key)
+       char *key;
+{
+       datum k, d;
+       struct databuf b;
+       DBMH dbmfile;
+
+       k.dptr = key;
+       k.dsize = strlen(key);
+       b.len = 0;
+       b.buf = NULL;
+
+       if ((dbmfile = open_securid_db()) == NULL)
+               return b;
+       d = dbm_fetch(dbmfile, k);
+       b.len = d.dsize;
+       if (d.dsize > 0) {
+               b.buf = emalloc(d.dsize);
+               memcpy(b.buf, d.dptr, d.dsize);
+       }
+       else
+               b.buf = NULL;
+       close_securid_db(dbmfile);
+
+       return b;
+}
+
+#endif /* USE_SECURID_NDBM */
+
+/** check_resend - compare the current request name, passcode and ip address
+ *  with the last one for the user and assume a resend if all match and the
+ *  request is younger that auth.request-cleanup-delay seconds.
+ */
+char check_resend(name, passcode, tm, ipaddr)
+       char *name;
+       char *passcode;
+       time_t tm;
+       UINT4 ipaddr;
+{
+       char *path, *s, *t, *p, *f, *hash, *j;
+       int ret = 0;
+       struct databuf b;
+       time_t lt;
+       char flag;
+       UINT4 sip;
+
+       debug(20, ("check resend: name=%s tm=%ld", name, tm));
+
+       b = getdb(name);
+       if (b.buf == NULL || b.len == 0)
+       {
+               debug(20, ("no data for: %s", name));
+               return '\0';
+       }
+       
+       s = (char *)b.buf;
+       t = s + strlen(s) + 1;
+       p = t + strlen(t) + 1;
+       f = p + strlen(p) + 1;
+       if (f - s > b.len)
+       {
+               debug(20, ("malformed data: name=%s len=%d(%d)", name,
+                          b.len, f-s));
+               efree(b.buf);
+               return '\0';
+       }
+       flag = *f;
+       debug(20, ("saved: name=%s hash=%s time=%s ip=%s flag=%s", name, s,
+                  t, p, f));
+
+       lt = atol(t);
+       sip = strtoul(p, &j, 10);
+
+       if (tm - lt > request_class[R_AUTH].cleanup_delay)
+       {
+               debug(20, ("%d second timeout - not resend: %ld - %ld = %ld",
+                          request_class[R_AUTH].cleanup_delay, tm, lt, tm-lt));
+               efree(b.buf);
+               return '\0';
+       }
+
+       if (ipaddr != sip)
+       {
+               debug(20, ("ip addresses differ: %08x != %08x", ipaddr, sip));
+               efree(b.buf);
+               return '\0';
+       }
+
+       hash = hash_passcode(passcode, s);
+       debug(20, ("passcode hash: hash=%s salt=%s", hash, s));
+
+       if (strcmp(hash, s) == 0)
+       {
+               debug(20, ("same passcode, returning flag: %c", flag));
+               efree(b.buf);
+               return flag;
+       }
+       debug(20, ("passcode differ, not resend"));
+
+       efree(b.buf);
+       return '\0';
+
+       return ret;
+}
+
+void save_resend_info(name, passcode, tm, ip, flag)
+       char *name;
+       char *passcode;
+       time_t tm;
+       UINT4 ip;
+       char flag;
+{
+       char stm[20], sip[20];
+       char *data, *s, *hash;
+       int len;
+       struct databuf dat;
+
+       hash = hash_passcode(passcode, NULL);
+       sprintf(stm, "%ld", tm);
+       sprintf(sip, "%lu", ip);
+       debug(20, ("saving: name=%s hash=%s time=%s ip=%s flag=%c",
+                 name, hash, stm, sip, flag));
+       len = strlen(hash) + strlen(stm) + strlen(sip) + 1 + 4;
+
+       s = data = emalloc(len);
+       strcpy(s, hash);
+       s += strlen(s);
+       *s++ = '\0';
+
+       strcpy(s, stm);
+       s += strlen(s);
+       *s++ = '\0';
+
+       strcpy(s, sip);
+       s += strlen(s);
+       *s++ = '\0';
+
+       *s++ = flag;
+       *s++ = '\0';
+
+       dat.len = len;
+       dat.buf = data;
+
+       putdb(name, dat);
+       efree(data);
+}
+
+#endif /* USE_SECURID_RESEND_HACK */
+
+int
+securid_pass(name, passcode, reply_msg, radreq)
+       char *name;
+       char *passcode;
+       char **reply_msg;
+       RADIUS_REQ *radreq;
+{
+       char buf[1024];
+       SDI_HANDLE acehdl;
+       int status;
+       time_t t;
+
+       debug(1, ("securid_pass: %s", name));
+       
+       time(&t);
+#ifdef USE_SECURID_RESEND_HACK
+       switch (check_resend(name, passcode, t, radreq->ipaddr)) {
+       case '\0':
+               break;
+       case 'n':
+               return 1;
+       case 'y':
+               return 0;
+       }
+#endif /* USE_SECURID_RESEND_HACK */
+
+       *reply_msg = NULL;
+       if (!AceInitialize()) {
+               debug(1, ("AceInitialize failed"));
+               *reply_msg = make_string("internal error: AceInitialize");
+               return 1;
+       }
+
+       if ((status = SD_Init(&acehdl)) != ACM_OK) {
+               debug(1, ("SD_init failed: %d", status));
+               *reply_msg = make_string("internal error: SD_Init");
+               return 1;
+       }
+
+       if (ace_client_ip)
+               status = SD_ClientCheck(acehdl, passcode, name, radreq->ipaddr);
+       else
+               status = SD_Check(acehdl, passcode, name);
+       SD_Close(acehdl);
+
+       switch (status)
+       {
+         case ACM_OK:
+               debug(1, ("%s/%s, access allowed",name,passcode));
+#ifdef USE_SECURID_RESEND_HACK
+               save_resend_info(name, passcode, t, radreq->ipaddr, 'y');
+#endif 
+               return 0;
+
+#ifndef USE_SECURID_LEGACY
+         case ACE_UNDEFINED_PASSCODE:
+         case ACE_UNDEFINED_USERNAME:
+         case ACE_ERR_INVALID_HANDLE:
+               debug(1, ("%s/%s, internal error - status: %d",
+                         name, passcode, status));
+               *reply_msg = make_string("internal error");
+               break;
+#endif
+         case ACM_ACCESS_DENIED:
+               debug(1, ("%s/%s, access denied",name,passcode));
+               *reply_msg = make_string("access denied");
+               break;
+         case ACM_NEXT_CODE_REQUIRED:
+               debug(1,("%s/%s, next token mode",name,passcode));
+               radlog(L_INFO, _("SecurID token in next token mode: %s"), name);
+               *reply_msg = make_string("next token mode");
+               break;
+         case ACM_NEW_PIN_REQUIRED:
+               debug(1,("%s/%s, new pin mode", name, passcode));
+               radlog(L_INFO, _("SecurID token in new pin mode: %s"), name);
+               *reply_msg = make_string("new pin mode");
+               break;
+       }
+#ifdef USE_SECURID_RESEND_HACK
+       save_resend_info(name, passcode, t, radreq->ipaddr, 'n');
+#endif
+       return 1;
+}
+
+#endif
diff -N -r -u gnuradius-0.96.2.orig/radiusd/version.c 
gnuradius-0.96.2/radiusd/version.c
--- gnuradius-0.96.2.orig/radiusd/version.c     Tue Mar 19 22:32:00 2002
+++ gnuradius-0.96.2/radiusd/version.c  Thu Jun  6 14:03:17 2002
@@ -85,6 +85,18 @@
 #if defined(USE_PAM)
        "USE_PAM",
 #endif
+#if defined(USE_SECURID)
+       "USE_SECURID",
+#endif
+#if defined(USE_SECURID_LEGACY)
+       "USE_SECURID_LEGACY",
+#endif
+#if defined(USE_SECURID_RESEND_HACK)
+       "USE_SECURID_RESEND_HACK",
+#endif
+#if defined(USE_NAS_IP_HACK)
+       "USE_NAS_IP_HACK",
+#endif
 #if defined(USE_DBM)
 # if USE_DBM == DBM_DBM        
        "USE_DBM=DBM",
@@ -100,6 +112,9 @@
 #endif 
 #ifdef USE_SQL_ODBC
        "USE_SQL_ODBC",
+#endif 
+#ifdef USE_SQL_SYBASE
+       "USE_SQL_SYBASE",
 #endif 
 #if defined(USE_SNMP)
        "USE_SNMP",
diff -N -r -u gnuradius-0.96.2.orig/sql/Makefile.am 
gnuradius-0.96.2/sql/Makefile.am
--- gnuradius-0.96.2.orig/sql/Makefile.am       Tue Mar 19 22:32:05 2002
+++ gnuradius-0.96.2/sql/Makefile.am    Mon May 27 17:25:33 2002
@@ -14,7 +14,7 @@
 EXTRA_LTLIBRARIES=libsql.la
 
 INCLUDES= -I$(top_srcdir)/include @INCLUDEPATH@
-libsql_la_SOURCES=disp.c mysql.c postgres.c odbc.c
+libsql_la_SOURCES=disp.c mysql.c postgres.c odbc.c sybase.c
 
 SUFFIXES=.S .c .ln .o .s
 
diff -N -r -u gnuradius-0.96.2.orig/sql/Makefile.in 
gnuradius-0.96.2/sql/Makefile.in
--- gnuradius-0.96.2.orig/sql/Makefile.in       Wed Mar 20 19:52:00 2002
+++ gnuradius-0.96.2/sql/Makefile.in    Mon May 27 17:26:06 2002
@@ -133,7 +133,7 @@
 EXTRA_LTLIBRARIES = libsql.la
 
 INCLUDES = -I$(top_srcdir)/include @INCLUDEPATH@
-libsql_la_SOURCES = disp.c mysql.c postgres.c odbc.c
+libsql_la_SOURCES = disp.c mysql.c postgres.c odbc.c sybase.c
 
 SUFFIXES = .S .c .ln .o .s
 
@@ -151,7 +151,7 @@
 LIBS = @LIBS@
 libsql_la_LDFLAGS = 
 libsql_la_LIBADD = 
-libsql_la_OBJECTS =  disp.lo mysql.lo postgres.lo odbc.lo
+libsql_la_OBJECTS =  disp.lo mysql.lo postgres.lo odbc.lo sybase.lo
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) 
$(CFLAGS)
 LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) 
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -310,6 +310,9 @@
        ../include/radius.h ../include/sysdep.h ../include/raddict.h \
        ../include/log.h ../include/mem.h ../include/radsql.h
 postgres.lo postgres.o : postgres.c ../config.h ../include/debugmod.h \
+       ../include/radius.h ../include/sysdep.h ../include/raddict.h \
+       ../include/log.h ../include/mem.h ../include/radsql.h
+sybase.lo sybase.o : sybase.c ../config.h ../include/debugmod.h \
        ../include/radius.h ../include/sysdep.h ../include/raddict.h \
        ../include/log.h ../include/mem.h ../include/radsql.h
 
diff -N -r -u gnuradius-0.96.2.orig/sql/disp.c gnuradius-0.96.2/sql/disp.c
--- gnuradius-0.96.2.orig/sql/disp.c    Tue Mar 19 22:32:05 2002
+++ gnuradius-0.96.2/sql/disp.c Thu Jun  6 22:07:01 2002
@@ -23,10 +23,27 @@
 #include <radsql.h>
 
 static SQL_DISPATCH_TAB *sql_dispatch_tab[] = {
+#ifdef USE_SQL_MYSQL
+       mysql_dispatch_tab,
+#else
+# ifdef USE_SQL_POSTGRES 
+       postgres_dispatch_tab,
+# else
+#   ifdef USE_SQL_ODBC
+       odbc_dispatch_tab,
+#   else
+#     ifdef USE_SQL_SYBASE
+       sybase_dispatch_tab,
+#     else
        NULL,
+#     endif
+#   endif
+# endif
+#endif
        mysql_dispatch_tab,
        postgres_dispatch_tab,
        odbc_dispatch_tab,
+       sybase_dispatch_tab,
 };
 
 #define NDISP sizeof(sql_dispatch_tab)/sizeof(sql_dispatch_tab[0])
diff -N -r -u gnuradius-0.96.2.orig/sql/sybase.c gnuradius-0.96.2/sql/sybase.c
--- gnuradius-0.96.2.orig/sql/sybase.c  Thu Jan  1 07:30:00 1970
+++ gnuradius-0.96.2/sql/sybase.c       Fri Jun  7 16:34:19 2002
@@ -0,0 +1,968 @@
+/* This file is part of GNU RADIUS.
+   Copyright (C) 2000,2001 Sergey Poznyakoff
+   Copyright (C) 2002 Steve Bleazard
+  
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/
+
+#define RADIUS_MODULE_SYBASE_C
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef lint
+static char rcsid[] =
+ "@(#) $Id: sybase.c,v 1.3 2001/08/22 13:18:04 gray Exp $" ;
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <radiusd.h>
+#include <radsql.h>
+
+#ifdef USE_SQL_SYBASE
+
+#include <ctpublic.h>
+
+struct sybase_connection {
+       CS_CONTEXT *context;
+       CS_CONNECTION *connection;
+       CS_COMMAND *cmd;
+};
+
+static int rad_sybase_reconnect(int type, struct sql_connection *conn);
+static void rad_sybase_disconnect(struct sql_connection *conn);
+static int rad_sybase_query(struct sql_connection *conn, char *query, int 
*return_count);
+static char *rad_sybase_getpwd(struct sql_connection *conn, char *query);
+static void *rad_sybase_exec(struct sql_connection *conn, char *query);
+static char *rad_sybase_column(void *data, int ncol);
+static int rad_sybase_next_tuple(struct sql_connection *conn, void *data);
+static void rad_sybase_free(struct sql_connection *conn, void *data);
+
+/* a suitable exit macro to handle the closedown and any messages */
+
+#define CLEANUP_ON_FAIL(conn, ret, func, dbg) \
+       if (ret != CS_SUCCEED) { \
+               debug(1, dbg); \
+               radlog(L_ERR, _(func " failed: %d"), ret); \
+               if (conn != NULL) { \
+                       rad_sql_need_reconnect(conn->type); \
+               } \
+       }
+
+#define RETURN_ON_FAIL(conn, ret, func, dbg, retval) \
+       if (ret != CS_SUCCEED) { \
+               CLEANUP_ON_FAIL(conn, ret, func, dbg) \
+               return retval; \
+       }
+
+/** Error handler helper functions 
+ */
+static void
+force_close_sybase_connection(sconn)
+       struct sybase_connection *sconn;
+{
+       CS_RETCODE ret;
+
+       if (sconn->cmd) {
+               ret = ct_cmd_drop(sconn->cmd);
+               if (ret != CS_SUCCEED)
+               {
+                       debug(1, ("ct_cmd_drop failed: %d", ret));
+                       radlog(L_ERR, _("ct_cmd_drop failed: %d"), ret);
+               }
+               sconn->cmd = NULL;
+       }
+       if (sconn->connection) {
+               ret = ct_close(sconn->connection, CS_FORCE_CLOSE);
+               if (ret != CS_SUCCEED)
+               {
+                       debug(1, ("ct_close failed: %d", ret));
+                       radlog(L_ERR, _("ct_close failed: %d"), ret);
+               }
+               ret = ct_con_drop(sconn->connection);
+               if (ret != CS_SUCCEED)
+               {
+                       debug(1, ("ct_con_drop failed: %d", ret));
+                       radlog(L_ERR, _("ct_con_drop failed: %d"), ret);
+               }
+               sconn->connection = NULL;
+       }
+}
+
+
+/** Error callback functions - just log errors */
+
+CS_RETCODE
+clientmsg_callback(context, conn, emsgp)
+       CS_CONTEXT *context;
+       CS_CONNECTION *conn;
+       CS_CLIENTMSG *emsgp;
+{
+       debug(1, ("sybase client library error: severity(%ld) layer(%ld) "
+                 "origin(%ld) number(%ld), %s",
+                 (long) CS_SEVERITY(emsgp->severity),
+                 (long) CS_LAYER(emsgp->msgnumber),
+                 (long) CS_ORIGIN(emsgp->msgnumber),
+                 (long) CS_NUMBER(emsgp->msgnumber),
+                 emsgp->msgstring));
+
+       radlog(L_ERR,
+                 _("sybase error: severity(%ld) layer(%ld) origin(%ld) "
+                   "number(%ld), %s"),
+                 (long) CS_SEVERITY(emsgp->severity),
+                 (long) CS_LAYER(emsgp->msgnumber),
+                 (long) CS_ORIGIN(emsgp->msgnumber),
+                 (long) CS_NUMBER(emsgp->msgnumber),
+                 emsgp->msgstring);
+
+       if (emsgp->osstringlen > 0) {
+               debug(1, ("sybase client library OS error: %ld (%s)",
+                         emsgp->osnumber, emsgp->osstring));
+               radlog(L_ERR, _("sybase os error: %ld (%s)"), 
+                             (long) emsgp->osnumber, emsgp->osstring);
+       }
+
+       return CS_SUCCEED;
+}
+
+CS_RETCODE
+csmsg_callback(context, conn, emsgp)
+       CS_CONTEXT *context;
+       CS_CLIENTMSG *emsgp;
+{
+       debug(1, ("sybase CS library error: severity(%ld) layer(%ld) "
+                 "origin(%ld) number(%ld), %s",
+                 (long) CS_SEVERITY(emsgp->severity),
+                 (long) CS_LAYER(emsgp->msgnumber),
+                 (long) CS_ORIGIN(emsgp->msgnumber),
+                 (long) CS_NUMBER(emsgp->msgnumber),
+                 emsgp->msgstring));
+
+       radlog(L_ERR,
+                 _("sybase error: severity(%ld) layer(%ld) origin(%ld) "
+                   "number(%ld), %s"),
+                 (long) CS_SEVERITY(emsgp->severity),
+                 (long) CS_LAYER(emsgp->msgnumber),
+                 (long) CS_ORIGIN(emsgp->msgnumber),
+                 (long) CS_NUMBER(emsgp->msgnumber),
+                 emsgp->msgstring);
+
+       if (emsgp->osstringlen > 0) {
+               debug(1, ("sybase CS library OS error: %ld (%s)",
+                         (long)emsgp->osnumber, emsgp->osstring));
+               radlog(L_ERR, _("sybase os error: %ld (%s)"), 
+                             (long) emsgp->osnumber, emsgp->osstring);
+       }
+
+       return CS_SUCCEED;
+}
+
+CS_RETCODE
+servermsg_callback(context, conn, smsgp)
+       CS_CONTEXT *context;
+       CS_CONNECTION *conn;
+       CS_SERVERMSG *smsgp;
+{
+       char *s, *p, *sf, *pf;
+
+       s = p = sf = pf = "";
+       if (smsgp->svrnlen > 0) {
+               s = smsgp->svrname;
+               sf = ", server: ";
+       }
+       if (smsgp->proclen > 0) {
+               s = smsgp->proc;
+               sf = ", proc: ";
+       }
+
+       debug(1, ("sybase server message: number(%ld) severity(%ld) "
+                 "state(%ld) line(%ld)%s%s%s%s, %s",
+                 smsgp->msgnumber, smsgp->severity, smsgp->state, 
+                 smsgp->line, sf, s, pf, p, smsgp->text));
+
+       radlog(L_ERR, _("sybase server message: number(%ld) severity(%ld) "
+                       "state(%ld) line(%ld)%s%s%s%s, %s"),
+                       smsgp->msgnumber, smsgp->severity, smsgp->state, 
+                       smsgp->line, sf, s, pf, p, smsgp->text);
+
+       return CS_SUCCEED;
+
+
+}
+
+/** Helper functions */
+/* TODO */
+
+static CS_COMMAND *
+sybase_alloc_cmd(sybcon, conn) 
+       struct sybase_connection *sybcon;
+       struct sql_connection *conn;
+{
+       CS_RETCODE ret;
+       CS_COMMAND *cmd;
+
+       ret = ct_cmd_alloc(sybcon->connection, &cmd);
+       RETURN_ON_FAIL(conn, ret, "ct_cmd_alloc",
+                       ("ct_cmnd_alloc failed: %d", ret), NULL);
+
+       return cmd;
+}
+
+static int
+sybase_db_use(sybcon, database, conn)
+       struct sybase_connection *sybcon;
+       char *database;
+       struct sql_connection *conn;
+{
+       CS_COMMAND *cmd = sybase_alloc_cmd(sybcon, conn);
+       CS_RETCODE ret;
+       CS_INT restype;
+       char statement[255];
+
+       if(!cmd)
+               return -1;
+
+       sprintf(statement, "use %s", database);
+       ret = ct_command(cmd, CS_LANG_CMD, statement, CS_NULLTERM, CS_UNUSED);
+       RETURN_ON_FAIL(conn, ret, "ct_command",
+                       ("ct_command failed: %d", ret), -1);
+
+       ret = ct_send(cmd);
+       RETURN_ON_FAIL(conn, ret, "ct_send",
+                       ("ct_send failed: %d", ret), -1);
+
+       while ((ret = ct_results(cmd, &restype)) == CS_SUCCEED) {
+               if (restype == CS_CMD_FAIL)
+                       radlog(L_ERR, _("'%s' failed: %d"), statement, ret);
+       }
+       ret = ct_cmd_drop(cmd);
+       RETURN_ON_FAIL(conn, ret, "ct_cmd_drop",
+                       ("ct_cmd_drop failed: %d", ret), -1);
+
+       return 0;
+}
+
+
+/* ************************************************************************* */
+/* Interface routines */
+int
+rad_sybase_reconnect(type, conn)
+       int    type;
+       struct sql_connection *conn;
+{
+       struct sybase_connection *sybcon;
+       char *dbname;
+       CS_RETCODE ret;
+#define XRETURN_ON_FAIL(ret, func, dbg) \
+       if (ret != CS_SUCCEED) { \
+               debug(1, dbg); \
+               radlog(L_ERR, _(func " failed: %d"), ret); \
+               force_close_sybase_connection(sybcon); \
+               if (sybcon->context != NULL) { \
+                       ct_exit(sybcon->context, CS_FORCE_EXIT); \
+                       cs_ctx_drop(sybcon->context); \
+                       sybcon->context = NULL; \
+               } \
+               conn->data = NULL; \
+               conn->connected = 0; \
+               efree(sybcon); \
+               return -1; \
+       }
+
+       switch (type) {
+       case SQL_AUTH:
+               dbname = sql_cfg.auth_db;
+               break;
+       case SQL_ACCT:
+               dbname = sql_cfg.acct_db;
+               break;
+       }
+
+       /* Create a context and initialise */
+
+       conn->data = sybcon = emalloc(sizeof *sybcon);
+       conn->connected = 0;
+       sybcon->context = NULL;
+
+       debug(10, ("Initialising sybase cs/ct library"));
+       ret = cs_ctx_alloc(CS_VERSION_100, &sybcon->context);
+       XRETURN_ON_FAIL(ret,"cs_ctx_alloc",("cs_ctx_alloc failed: %d",ret));
+
+       ret = ct_init(sybcon->context, CS_VERSION_100);
+       XRETURN_ON_FAIL(ret, "ct_init", ("ct_init failed: %d", ret));
+
+       /* install callbacks */
+
+       debug(10, ("Installing callbacks"));
+       ret = cs_config(sybcon->context, CS_SET, CS_MESSAGE_CB,
+                       (CS_VOID *)csmsg_callback, CS_UNUSED, NULL);
+       XRETURN_ON_FAIL(ret, "cs_config",
+               ("cs_config failed installing cs callback: %d", ret));
+
+       ret = ct_callback(sybcon->context, NULL, CS_SET, CS_CLIENTMSG_CB,
+                         (CS_VOID *)clientmsg_callback);
+       XRETURN_ON_FAIL(ret, "ct_callback",
+               ("ct_callback failed installing client callback: %d", ret));
+
+       ret = ct_callback(sybcon->context, NULL, CS_SET, CS_SERVERMSG_CB,
+                         (CS_VOID *)servermsg_callback);
+       XRETURN_ON_FAIL(ret, "ct_callback",
+               ("ct_callback failed installing server callback: %d", ret));
+
+       /* Connect to the server, first alloc a connection structure and
+        * set the username and password.
+        */
+
+       debug(10, ("connecting to server '%s' with username/pw: %s/%s",
+                  sql_cfg.server, sql_cfg.login, sql_cfg.password));
+       ret = ct_con_alloc(sybcon->context, &sybcon->connection);
+       XRETURN_ON_FAIL(ret,"ct_con_alloc",("ct_con_alloc failed: %d", ret));
+       
+       ret = ct_con_props(sybcon->connection, CS_SET, CS_USERNAME,
+                          sql_cfg.login, CS_NULLTERM, NULL);
+       XRETURN_ON_FAIL(ret, "ct_con_props",
+                       ("ct_con_props(username) failed: %d", ret));
+
+       ret = ct_con_props(sybcon->connection, CS_SET, CS_PASSWORD,
+                          sql_cfg.password, CS_NULLTERM, NULL);
+       XRETURN_ON_FAIL(ret, "ct_con_props",
+                       ("ct_con_props(password) failed: %d", ret));
+
+       /** Now connect to the server.  Note that the server connection
+        *  details (ipaddress, port etc) are contained in the Sybase
+        *  "interfaces" file and the server name is simply a reference into
+        *  this file.
+        */
+
+       ret = ct_connect(sybcon->connection, sql_cfg.server, CS_NULLTERM);
+       XRETURN_ON_FAIL(ret, "ct_connect", ("ct_connect failed: %d", ret));
+
+       /* connection complete - mark the connection connected */
+
+       debug(10, ("connected to database"));
+       conn->connected = 1;
+       
+       /* force the correct database */
+
+       if (sybase_db_use(sybcon, dbname, conn) < 0)
+       {
+               conn->data = NULL;
+               conn->connected = 0;
+               efree(sybcon);
+               return -1;
+       }
+
+       return 0;
+
+#undef XRETURN_ON_FAIL
+}
+
+void 
+rad_sybase_disconnect(conn)
+       struct sql_connection *conn;
+{
+       struct sybase_connection *sybcon;
+       CS_RETCODE ret;
+
+       conn->connected = 0;
+       if (!conn->data)
+               return;
+       sybcon = conn->data;
+       conn->data = NULL;
+
+       if (sybcon->context != NULL) {
+               force_close_sybase_connection(sybcon);
+
+               ret = ct_exit(sybcon->context, CS_FORCE_EXIT);
+               if (ret != CS_SUCCEED)
+               {
+                       debug(1, ("ct_exit failed: %d", ret));
+                       radlog(L_ERR, _("ct_exit failed: %d"), ret);
+               }
+
+               cs_ctx_drop(sybcon->context);
+               if (ret != CS_SUCCEED)
+               {
+                       debug(1, ("cs_ctx_drop failed: %d", ret));
+                       radlog(L_ERR, _("cs_ctx_drop failed: %d"), ret);
+               }
+               sybcon->context = NULL;
+       }
+
+       efree(sybcon);
+}
+
+static int
+sybase_do_query(conn, query)
+       struct sql_connection *conn;
+       char *query;
+{
+       struct sybase_connection *sybcon;
+       CS_RETCODE ret;
+
+       sybcon = conn->data;
+       if (!conn->connected || sybcon == NULL || sybcon->context == NULL)
+       {
+               if (rad_sybase_reconnect(conn->type, conn) < 0)
+                       return -1;
+               sybcon = conn->data;
+       }
+
+       if ((sybcon->cmd = sybase_alloc_cmd(sybcon, conn)) == NULL)
+               return -1;
+
+       ret = ct_command(sybcon->cmd,CS_LANG_CMD,query,CS_NULLTERM,CS_UNUSED);
+       RETURN_ON_FAIL(conn, ret, "ct_command",
+                       ("ct_command failed: %d", ret), -1);
+
+       ret = ct_send(sybcon->cmd);
+       RETURN_ON_FAIL(conn, ret, "ct_send", ("ct_send failed: %d", ret), -1);
+
+       return 0;
+}
+
+/** rad_sybase_query - action a query without returning data.  This is usually
+ *  used for insert queries.  Return the number of rows effected.
+ */
+int
+rad_sybase_query(conn, query, return_count)
+       struct sql_connection *conn;
+       char *query;
+       int *return_count;
+{
+       struct sybase_connection *sybcon;
+       CS_RETCODE ret, res_ret;
+       CS_INT result_type;
+       CS_INT count, ocnt, fcnt;
+
+       if (!conn)
+               return -1;
+       debug(2, ("query: %s", query));
+
+       if (sybase_do_query(conn, query) < 0)
+               return -1;
+
+       sybcon = conn->data;
+       count = -1;
+
+       /* run through the rows until complete */
+
+       while ((res_ret = ct_results(sybcon->cmd, &result_type))==CS_SUCCEED) {
+               switch (result_type) {
+               case CS_ROW_RESULT:
+               case CS_PARAM_RESULT:
+               case CS_STATUS_RESULT:
+               case CS_CURSOR_RESULT:
+               case CS_COMPUTE_RESULT:
+                       debug(1, ("results return in rad_sybase_query"));
+                       while ((ret = ct_fetch(sybcon->cmd, CS_UNUSED,
+                                               CS_UNUSED, CS_UNUSED,
+                                               &fcnt)) == CS_SUCCEED ||
+                               ret == CS_ROW_FAIL) {
+
+                               /* report any row errors */
+                               if (ret == CS_ROW_FAIL) {
+                                       debug(1, ("ct_fetch row error", ret));
+                                       radlog(L_ERR, _("ct_fetch row error"));
+                               }
+                       }
+                       if (ret != CS_END_DATA)
+                       {
+                               RETURN_ON_FAIL(conn, ret, "ct_fetch",
+                                       ("ct_fetch failed: %d", ret), -1);
+                       }
+                       break;
+               case CS_CMD_SUCCEED:
+               case CS_CMD_DONE:
+                       debug(1, ("ct_results cmd success"));
+                       ret = ct_res_info(sybcon->cmd, CS_ROW_COUNT, &count,
+                                               CS_UNUSED, &ocnt);
+                       RETURN_ON_FAIL(conn, ret, "ct_res_info",
+                                       ("ct_res_info failed: %d", ret), -1);
+                       break;
+               case CS_CMD_FAIL:
+                       debug(1, ("ct_results cmd fail"));
+                       break;
+               default:
+                       RETURN_ON_FAIL(conn, CS_FAIL, "ct_results",
+                                       ("ct_results failed: %d", res_ret),-1);
+
+               }
+       }
+
+       if (res_ret == CS_END_RESULTS) {
+               if (count == -1 &&
+                   (result_type==CS_CMD_SUCCEED || result_type==CS_CMD_DONE)) {
+                       ret = ct_res_info(sybcon->cmd, CS_ROW_COUNT, &count,
+                                               CS_UNUSED, &ocnt);
+                       RETURN_ON_FAIL(conn, ret, "ct_res_info",
+                                       ("ct_res_info failed: %d", ret), -1);
+               }
+       } else {
+               RETURN_ON_FAIL(conn, res_ret, "ct_results",
+                               ("ct_results failed: %d", res_ret), -1);
+       }
+
+       if (count == -1)
+               count = 0;
+
+       ret = ct_cmd_drop(sybcon->cmd);
+       sybcon->cmd = NULL;
+       RETURN_ON_FAIL(conn, ret, "ct_cmd_drop",
+                       ("ct_cmd_drop failed: %d", ret), -1);
+
+       debug(2, ("query effected %d row(s)", count));
+       if (return_count)
+               *return_count = count;
+       
+       return 0;
+}
+
+char *
+rad_sybase_getpwd(conn, query)
+       struct sql_connection *conn;
+       char *query;
+{
+       struct sybase_connection *sybcon;
+       CS_RETCODE ret, res_ret;
+       CS_INT result_type;
+       CS_INT fcnt;
+       CS_DATAFMT passwd_data;
+       CS_SMALLINT indicator;
+       char passwd[512+1];
+       char *return_passwd = NULL;
+
+       if (!conn)
+               return NULL;
+
+       debug(1, ("query: %s", query));
+
+       if (sybase_do_query(conn, query) < 0)
+               return NULL;
+
+       sybcon = conn->data;
+
+       /* run through the rows getting the first row data */
+
+       while ((res_ret = ct_results(sybcon->cmd, &result_type))==CS_SUCCEED) {
+               switch (result_type) {
+               case CS_ROW_RESULT:
+               case CS_PARAM_RESULT:
+               case CS_STATUS_RESULT:
+               case CS_CURSOR_RESULT:
+               case CS_COMPUTE_RESULT:
+                       /* Bind the first column to the password buffer */
+
+                       passwd_data.datatype = CS_CHAR_TYPE;
+                       passwd_data.format = CS_FMT_NULLTERM;
+                       passwd_data.maxlength = sizeof passwd - 1;
+                       passwd_data.count = 1;
+                       passwd_data.locale = NULL;
+                       ret = ct_bind(sybcon->cmd, 1, &passwd_data, passwd,
+                                     NULL, &indicator);
+                       RETURN_ON_FAIL(conn, ret, "cs_bind",
+                                       ("ct_bind failed: %d", ret), NULL);
+
+                       while ((ret = ct_fetch(sybcon->cmd, CS_UNUSED,
+                                               CS_UNUSED, CS_UNUSED,
+                                               &fcnt)) == CS_SUCCEED ||
+                               ret == CS_ROW_FAIL) {
+
+                               /* report any row errors */
+                               if (ret == CS_ROW_FAIL) {
+                                       debug(1, ("ct_fetch row error", ret));
+                                       radlog(L_ERR, _("ct_fetch row error"));
+                               }
+                               if (return_passwd == NULL && indicator >= 0)
+                                       return_passwd = estrdup(passwd);
+                       }
+                       if (ret != CS_END_DATA) {
+                               RETURN_ON_FAIL(conn, ret, "ct_fetch",
+                                       ("ct_fetch failed: %d", ret), NULL);
+                       }
+                       break;
+               case CS_CMD_SUCCEED:
+               case CS_CMD_DONE:
+               case CS_CMD_FAIL:
+                       break;
+               default:
+                       RETURN_ON_FAIL(conn, CS_FAIL, "ct_results",
+                                       ("ct_results failed: %d",res_ret),NULL);
+
+               }
+       }
+
+       ret = ct_cmd_drop(sybcon->cmd);
+       sybcon->cmd = NULL;
+       RETURN_ON_FAIL(conn, ret, "ct_cmd_drop",
+                       ("ct_cmd_drop failed: %d", ret), NULL);
+
+       debug(2, ("password %sfound", return_passwd ? "" : "not "));
+       debug(3, ("returning password:[%s]", return_passwd?return_passwd:""));
+
+       return return_passwd;
+}
+
+#define SYBASE_EXEC_STATE_START                0
+#define SYBASE_EXEC_STATE_RESULTS      1
+#define SYBASE_EXEC_STATE_FETCH                2
+#define SYBASE_EXEC_STATE_END          3
+#define SYBASE_EXEC_STATE_ERROR                4
+
+struct colinfo {
+       CS_SMALLINT indicator;
+       char *data;
+       struct colinfo *next;
+};
+
+struct sybase_exec_data {
+       int state;
+       CS_RETCODE results_ret;
+       CS_INT result_type;
+       CS_RETCODE fetch_ret;
+       struct colinfo *coldata;
+       CS_COMMAND *cmd;
+};
+
+void *
+rad_sybase_exec(conn, query)
+       struct sql_connection *conn;
+       char *query;
+{
+       struct sybase_connection *sybcon;
+       struct sybase_exec_data *sinfo;
+
+       if (!conn)
+               return NULL;
+       debug(2, ("query: %s", query));
+
+       if (sybase_do_query(conn, query) < 0)
+               return NULL;
+       
+       sybcon = conn->data;
+       sinfo = emalloc(sizeof *sinfo);
+       sinfo->state = SYBASE_EXEC_STATE_START;
+       sinfo->cmd = sybcon->cmd;
+       sinfo->results_ret = CS_SUCCEED;
+       sinfo->result_type = CS_CMD_SUCCEED;
+       sinfo->fetch_ret = CS_SUCCEED;
+       sinfo->coldata = NULL;
+       sybcon->cmd = NULL;
+
+       return (void*)sinfo;
+}
+
+char *
+rad_sybase_column(data, ncol)
+       void *data;
+       int ncol;
+{
+       struct sybase_exec_data *sinfo;
+       struct colinfo *cp;
+       int i;
+
+       if (!data)
+               return NULL;
+
+       sinfo = (struct sybase_exec_data *)data;
+       for (i=0, cp=sinfo->coldata; i<ncol && cp!=NULL; cp=cp->next, i++)
+               ;
+       
+       if (cp == NULL) {
+               radlog(L_ERR,
+                      _("too few columns returned (%d req'd)"), ncol);
+               return NULL;
+       }
+
+       debug(10, ("col %d:[%s]", ncol, cp->data ? cp->data : ""));
+       return cp->data;
+}
+
+#define MIN(a, b) (a < b ? a : b)
+#define MAX(a, b) (a > b ? a : b)
+#define MAX_CHAR_BUF 1024
+
+static CS_INT
+data_len(column)
+CS_DATAFMT *column;
+{
+       CS_INT len;
+
+       switch ((int) column->datatype) {
+       case CS_CHAR_TYPE:
+       case CS_VARCHAR_TYPE:
+       case CS_TEXT_TYPE:
+       case CS_IMAGE_TYPE:
+               len = MIN(column->maxlength, MAX_CHAR_BUF);
+               break;
+       case CS_BINARY_TYPE:
+       case CS_VARBINARY_TYPE:
+               len = MIN((2 * column->maxlength) + 2, MAX_CHAR_BUF);
+               break;
+       case CS_BIT_TYPE:
+       case CS_TINYINT_TYPE:
+               len = 3;
+               break;
+       case CS_SMALLINT_TYPE:
+               len = 6;
+               break;
+       case CS_INT_TYPE:
+               len = 11;
+               break;
+       case CS_REAL_TYPE:
+       case CS_FLOAT_TYPE:
+               len = 20;
+               break;
+       case CS_MONEY_TYPE:
+       case CS_MONEY4_TYPE:
+               len = 24;
+               break;
+       case CS_DATETIME_TYPE:
+       case CS_DATETIME4_TYPE:
+               len = 30;
+               break;
+       case CS_NUMERIC_TYPE:
+       case CS_DECIMAL_TYPE:
+               len = (CS_MAX_PREC + 2);
+               break;
+       default:
+               len = column->maxlength;
+               break;
+       }
+    
+       return MAX(strlen(column->name) + 1, len);
+}
+
+static struct colinfo *
+describe(cmd)
+       CS_COMMAND *cmd;
+{
+       
+       CS_RETCODE ret;
+       CS_INT num_cols, i;
+       CS_DATAFMT datafmt;
+       struct colinfo start, *curr;
+
+       ret = ct_res_info(cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL);
+       if (ret != CS_SUCCEED)
+       {
+               debug(1, ("ct_res_info failed: %d", ret));
+               radlog(L_ERR, _("ct_res_info failed: %d"), ret);
+               return NULL;
+       }
+       if (num_cols <= 0)
+       {
+               debug(1, ("num_cols <= 0: %d", num_cols));
+               radlog(L_ERR, _("num_cols <= 0: %d"), num_cols);
+               return NULL;
+       }
+
+       debug(10, ("num_cols: %d", num_cols));
+       for (curr = &start, i = 0; i < num_cols; i++)
+       {
+               if((ret = ct_describe(cmd, (i+1), &datafmt)) != CS_SUCCEED)
+               {
+                       debug(1, ("ct_describe failed: %d", ret));
+                       radlog(L_ERR, _("ct_describe failed: %d"), ret);
+                       break;
+               }
+               datafmt.maxlength = data_len(&datafmt) + 1;
+               datafmt.datatype = CS_CHAR_TYPE;
+               datafmt.format   = CS_FMT_NULLTERM;
+               debug(10, ("col %d len: %d", i, datafmt.maxlength));
+
+               curr->next = emalloc(sizeof(struct colinfo));
+               curr = curr->next;
+               curr->data = emalloc(datafmt.maxlength);
+
+               if ((ret = ct_bind(cmd, (i+1), &datafmt, curr->data, NULL,
+                                           &curr->indicator)) != CS_SUCCEED)
+               {
+                       debug(1, ("ct_bind failed: %d", ret));
+                       radlog(L_ERR, _("ct_bind failed: %d"), ret);
+                       break;
+               }
+       }
+
+       curr->next = NULL;
+       return start.next;
+}
+
+static void
+free_coldata(data)
+       struct colinfo *data;
+{
+       struct colinfo *curr, *junk;
+
+       for (curr = data; curr != NULL; ) {
+               if (curr->data != NULL)
+                       efree(curr->data);
+               junk = curr;
+               curr = curr->next;
+               efree(junk);
+       }
+}
+
+/*ARGSUSED*/
+int
+rad_sybase_next_tuple(conn, data)
+       struct sql_connection *conn;
+       void *data;
+{
+       struct sybase_connection *sybcon;
+       struct sybase_exec_data *sinfo;
+       CS_INT fcnt;
+       int loop = 1;
+
+       if (!conn || !data)
+               return -1;
+
+       sybcon = conn->data;
+       sinfo = (struct sybase_exec_data *)data;
+
+       while (loop) {
+               debug(10, ("state = %d", sinfo->state));
+               switch (sinfo->state) {
+               case SYBASE_EXEC_STATE_START:
+               case SYBASE_EXEC_STATE_RESULTS:
+                       sinfo->state = SYBASE_EXEC_STATE_RESULTS;
+                       sinfo->results_ret =
+                               ct_results(sinfo->cmd, &sinfo->result_type);
+                       switch (sinfo->results_ret) {
+                       case CS_SUCCEED:
+                               switch (sinfo->result_type)
+                               {
+                               case CS_ROW_RESULT:
+                               case CS_PARAM_RESULT:
+                               case CS_STATUS_RESULT:
+                               case CS_CURSOR_RESULT:
+                               case CS_COMPUTE_RESULT:
+                                       /* Setup the column bindings */
+                                       if (sinfo->coldata != NULL)
+                                               free_coldata(sinfo->coldata);
+                                       sinfo->coldata = describe(sinfo->cmd);
+                                       sinfo->state = SYBASE_EXEC_STATE_FETCH;
+                                       break;
+                               case CS_CMD_SUCCEED:
+                               case CS_CMD_DONE:
+                                       break;
+                               case CS_CMD_FAIL:
+                                       /* error reported via callback */
+                                       break;
+                               default:
+                                       debug(2, ("ct_results err: %d",
+                                                 sinfo->result_type));
+                                       break;
+                               }
+                               break;
+                       case CS_END_RESULTS:
+                               sinfo->state = SYBASE_EXEC_STATE_END;
+                               break;
+                       default:
+                               sinfo->state = SYBASE_EXEC_STATE_ERROR;
+                               RETURN_ON_FAIL(conn, sinfo->results_ret,
+                                       "ct_results", ("ct_results failed: %d",
+                                               sinfo->results_ret), -1);
+                               loop = 0;
+                               return -1;
+                       }
+                       break;
+               case SYBASE_EXEC_STATE_FETCH:
+                       loop = 0;
+                       sinfo->fetch_ret = ct_fetch(sinfo->cmd, CS_UNUSED,
+                                                   CS_UNUSED, CS_UNUSED,&fcnt);
+                       switch (sinfo->fetch_ret) {
+                       case CS_SUCCEED:
+                               break;
+                       case CS_ROW_FAIL:
+                               debug(2, ("ct_fetch row fail: %d",
+                                         sinfo->fetch_ret));
+                               break;
+                       case CS_END_DATA:
+                               sinfo->state = SYBASE_EXEC_STATE_RESULTS;
+                               loop = 1;
+                               break;
+                       default:
+                               debug(2,("ct_fetch fail: %d",sinfo->fetch_ret));
+                               sinfo->state = SYBASE_EXEC_STATE_ERROR;
+                               loop = 1;
+                               break;
+                       }
+                       break;
+               case SYBASE_EXEC_STATE_END:
+                       loop = 0;
+                       return 1;
+                       break;
+               case SYBASE_EXEC_STATE_ERROR:
+                       loop = 0;
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/*ARGSUSED*/
+void
+rad_sybase_free(conn, data)
+       struct sql_connection *conn;
+       void *data;
+{
+       struct sybase_connection *sybcon;
+       struct sybase_exec_data *sinfo;
+       CS_RETCODE ret;
+
+       if (!data)
+               return;
+
+       debug(20, ("freeing command data"));
+       sinfo = (struct sybase_exec_data *)data;
+
+       if (conn && conn->data)
+       {
+               if (sinfo->state != SYBASE_EXEC_STATE_END) {
+                       debug(20, ("cancelling pending command"));
+                       ret = ct_cancel(NULL, sinfo->cmd, CS_CANCEL_ALL);
+                       debug(20, ("cancelled pending command"));
+               }
+               ret = ct_cmd_drop(sinfo->cmd);
+       }
+
+       if (sinfo->coldata)
+               free_coldata(sinfo->coldata);
+
+       debug(20, ("data freed"));
+
+       efree(sinfo);
+}
+
+SQL_DISPATCH_TAB sybase_dispatch_tab[] = {
+       "sybase",     
+       3000,
+       rad_sybase_reconnect,
+       rad_sybase_disconnect,
+       rad_sybase_query,
+       rad_sybase_getpwd,
+       rad_sybase_exec,
+       rad_sybase_column,
+       rad_sybase_next_tuple,
+       rad_sybase_free
+};
+
+#endif
+

reply via email to

[Prev in Thread] Current Thread [Next in Thread]