Logo Search packages:      
Sourcecode: radiusd-livingston version File versions

proxy.c

/***********************************************************************

RADIUS
Remote Authentication Dial In User Service

Lucent Technologies Remote Access
4464 Willow Road
Pleasanton, CA   94588

Copyright 1992-1999 Lucent Technologies Inc.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.

   * 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.

   * All advertising materials mentioning features or use of this
     software must display the following acknowledgement:

        This product includes software developed by Lucent
        Technologies and its contributors.

   * Neither the name of the copyright holder nor the names of its
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

This software is provided by the copyright holders and contributors
``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 copyright
holder or contributors 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.

************************************************************************/


static char sccsid[] =
"$Id: proxy.c,v 1.2 1999/06/23 23:40:41 cdr Exp $ Copyright 1997-1999 Lucent Technologies Inc";

#include    <sys/types.h>
#include    <sys/socket.h>
#include    <sys/time.h>
#include    <sys/file.h>
#include    <sys/ipc.h>
#include    <netinet/in.h>
#include    <sys/stat.h>

#include    <stdio.h>
#include    <netdb.h>
#include    <fcntl.h>
#include    <pwd.h>
#include    <time.h>
#include    <ctype.h>
#include    <unistd.h>
#include    <signal.h>
#include    <errno.h>
#include    <sys/wait.h>
#include    <syslog.h>
#include    <grp.h>
#include    <string.h>

#include    "radius.h"
#include    "users.h"

extern      char  recv_buffer[4096];
extern      char  send_buffer[4096];
extern      int   sockfd;
extern      int   acctfd;
extern      int   proxyfd;
extern      int   proxyacctfd;
extern      u_short     radius_port;
extern      char  *radius_dir;
extern      UINT4 now;

char        *ipaddr2strp();
char        *req2strp();
void        req2str();

static  AUTH_REQ *first_forwarded = (AUTH_REQ *)NULL;
static      PEER  *servers;
static      PEER  *server_default;
static      PEER  *server_norealm;

/*************************************************************************
 *************************************************************************

      Subroutines related to forwarding a Proxy reply from a server
      to the client:

      rad_proxy(fd)
      pop_proxy(authreq, pstate)
      send_proxy2client(fd, authreq, server, pstate)
      update_proxy()
      find_server()

 *************************************************************************
 *************************************************************************/

/*************************************************************************
 *
 *    Function: rad_proxy
 *
 *    Purpose: Receive UDP Proxy server replies and forward to client
 *
 *    Uses External: sockfd, acctfd
 *
 *************************************************************************/

void
rad_proxy(fd)
int fd;
{
      extern AUTH_REQ         *first_forwarded;
      extern int        max_proxy_time;
      AUTH_HDR          *auth;
      AUTH_REQ          *authreq;
      AUTH_REQ          *oldqp;
      AUTH_REQ          *qp;
      AUTH_REQ          *qpop;
      AUTH_REQ          *pop_proxy();
      AUTH_REQ          *radrecv();
      PEER              *find_server_byaddr();
      PEER              *server;
      UINT4             host;
      char              *ip_hostname();
      char              digest[AUTH_VECTOR_LEN];
      char              hold_digest[AUTH_VECTOR_LEN];
      char              *sentreqauth = NULL;
      int               result;
      int               secretlen;
      size_t                  salen;
      struct      sockaddr_in *sin;
      struct  sockaddr_in     rad_saremote;
      u_short                 port;
      void              hexdump();
      void              md5_calc();
      void              reqfree();
      void              send_proxy2client();

      salen = sizeof(rad_saremote);
      sin = (struct sockaddr_in *) & rad_saremote;

      auth = (AUTH_HDR *)recv_buffer;

      result = recvfrom (fd, (char *) recv_buffer,
            (int) sizeof(recv_buffer),
            (int) 0, (struct sockaddr *)&rad_saremote,
            (socklen_t *) &salen);

      host = ntohl(sin->sin_addr.s_addr);
      port = ntohs(sin->sin_port);

      /* Drop the packet if we do not know the proxy */
      if ((server = find_server_byaddr(host, port)) == (PEER *)NULL) {
            log_err("auth: packet from unknown proxy server %s.%d ignored\n", ipaddr2strp(host), port);
            return;
      }

      authreq = radrecv( host, port, server->secret, recv_buffer, result);

        if (authreq == (AUTH_REQ *)NULL) {
                return;
        }

      /* Verify response authenticator */

      switch (authreq->code) {
            case  PW_AUTHENTICATION_ACK:
            case  PW_AUTHENTICATION_REJECT:
            case  PW_ACCESS_CHALLENGE:
                  fd = sockfd;
                  break;
            case  PW_ACCOUNTING_RESPONSE:
                  fd = acctfd;
                  break;
            case  PW_AUTHENTICATION_REQUEST:
            case  PW_ACCOUNTING_REQUEST:
            default:
                  log_err("rad_proxy: proxy server %s/%d.%d sent back invalid packet type %d, ignoring\n", ipaddr2strp(host), port, authreq->id, authreq->code);
                  reqfree(authreq, "rad_proxy");
                  return;
                  break;
      }

      DEBUG("proxy server %s/%d.%d replied with code=%d, length=%d\n", ipaddr2strp(host), port, authreq->id, authreq->code, result);
      if (debug_flag > 2) {
            hexdump(recv_buffer,result);
      }

      /* Find original request authenticator */

      oldqp = (AUTH_REQ *)NULL;
      qp = first_forwarded;
      while ((qp != (AUTH_REQ *)NULL) && (qp->forw_id != authreq->id)) {
            oldqp = qp;
            if (qp == qp->next) {   /* should never happen */
                  log_err("ERROR: circular queue detected at %d\n", __LINE__);
                  qp->next = (AUTH_REQ *)NULL;
            }
            qp = qp->next;
      }

      if (qp != (AUTH_REQ *)NULL) {
            if (oldqp == (AUTH_REQ *)NULL) {
                  first_forwarded = qp->next;
            } else {
                  oldqp->next = qp->next;
            }
            qp->next = (AUTH_REQ *)NULL;
            sentreqauth = qp->forw_vector;
      }

      qpop = (AUTH_REQ *)NULL;
      if (server->flags & PEER_NOPROXY) { /* server can't do proxy */
            if (qp == (AUTH_REQ *)NULL) { /* not found in queue */
                  DEBUG("rad_proxy: response from %s/%d.%d not matched in forwarded queue, dropped\n", ipaddr2strp(host), port, authreq->id);
                  reqfree(authreq, "rad_proxy");
                  return;
            } else {
                  qpop = qp;
            }
      } else { /* proxy-capable server */
             /* pop last Proxy-State and fill in qp, if needed */
            if ((qpop = pop_proxy(authreq, qp)) == (AUTH_REQ *)NULL) {
                  log_err("rad_proxy: invalid Proxy-State from proxy server %s/%d.%d, dropped\n", ipaddr2strp(host), port, authreq->id);
                  if (debug_flag > 0) {
                        hexdump(authreq->packet,result);
                  }
                  reqfree(authreq, "rad_proxy");
                  return;
            }
      }

      if (qp == (AUTH_REQ *)NULL) {
            if (authreq->code == PW_ACCOUNTING_RESPONSE) {
                  /* radiusd has restarted or we've already
                     forwarded this response, so drop this response
                   */
                  DEBUG("rad_proxy: accounting-response from %s/%d.%d not matched in forwarded queue, dropped\n",
                        ipaddr2strp(host), port, authreq->id);
                  reqfree(authreq, "rad_proxy");
                  reqfree(qpop, "rad_proxy");
                  return;
            } else {
                  /* for access-responses, we can use the Request
                   * authenticator we included in the proxy, if we
                   * do not find it in our queue
                   */
                  qp = qpop;
                  sentreqauth = qp->vector;
            }
      }
      if ((authreq->code == PW_ACCOUNTING_RESPONSE) &&
          (server->flags & PEER_OLDACCT)) {     /* ignore acct signature */
            send_proxy2client(fd, authreq, server, qp);
            reqfree(qp,"rad_proxy");
      } else {
            /* Check response authenticator */
            memcpy(hold_digest, auth->vector, AUTH_VECTOR_LEN);
            memcpy(auth->vector, sentreqauth, AUTH_VECTOR_LEN);
            secretlen = strlen(server->secret);
            memcpy(recv_buffer + result, server->secret, secretlen);
            md5_calc(digest, (char *)auth, result + secretlen);
            memset(recv_buffer + result, 0, secretlen);
            /* no need to restore auth->vector */

            if (memcmp(hold_digest, digest, AUTH_VECTOR_LEN) == 0) {
                  send_proxy2client(fd, authreq, server, qp);
                  reqfree(qp,"rad_proxy");
            } else {
                  log_err("rad_proxy: remote server %s/%d.%d sent invalid reply, dropping\n", ipaddr2strp(host), port, authreq->id);
                  if (debug_flag > 0) {
                        hexdump(authreq->packet,result);
                  }
                  /* requeue qp if it hasn't timed out */
                  if (qp != (AUTH_REQ *)NULL) {
                        if (qp->timestamp + max_proxy_time > now) {
                              qp->next = first_forwarded;
                              first_forwarded = qp;
                        } else {
                              reqfree(qp,"rad_proxy");
                        }
                  }

            }
      }
      reqfree(authreq,"rad_proxy");
      return;
}

/*************************************************************************
 *
 *    Function: pop_proxy
 *
 *      Purpose: Remove the last proxy-state attribute from authreq->request,
 *    and returns a pointer to qp, allocating it if necessary.
 *
 *    Any format changes made here must also be made in push_proxy()
 *
 *************************************************************************/

AUTH_REQ *
pop_proxy(authreq, qp)
AUTH_REQ *authreq;
AUTH_REQ *qp;
{
      AUTH_REQ    *reqalloc();
      VALUE_PAIR  *value_list;
      VALUE_PAIR  *old_value;
      VALUE_PAIR  *prev_value;
      VALUE_PAIR  *lastproxy;
      UINT4       tmp;
      int         find_client();
      u_short           tmps;
      u_char            hostnm[256];      /* passed as an argument, but unused */
      void        pairfree();

      old_value = (VALUE_PAIR *) NULL;
      prev_value = (VALUE_PAIR *) NULL;
      lastproxy = (VALUE_PAIR *) NULL;

      value_list = authreq->request;

      while(value_list != (VALUE_PAIR *)NULL) {
            if(value_list->attribute == PW_PROXY) {
                  lastproxy = value_list;
                  prev_value = old_value;
            }
            old_value = value_list;
            value_list = value_list->next;
      }
      if (lastproxy == (VALUE_PAIR *)NULL) { /* not found */
            return (qp);
      }

      /* pop lastproxy from linked list */
      if (prev_value != (VALUE_PAIR *)NULL) {
            prev_value->next = lastproxy->next;
      } else {
            authreq->request = lastproxy->next;
      }
      lastproxy->next = (VALUE_PAIR *)NULL;

      /* Proxy-State contains: Timestamp, Client, Port, Id in network order,
         a pad byte, and 16 octets of Request Authenticator */
      if (lastproxy->lvalue < 28) {
            log_err("pop_proxy: Proxy-State from %s has too short length %d < 28, ignoring\n", req2strp(authreq), lastproxy->lvalue);
      }

      if (qp != (AUTH_REQ *)NULL) {
            pairfree(lastproxy, "pop_proxy");
            return (qp);
      } else {
            qp = reqalloc("pop_proxy");
      }

      memcpy((char *)&tmp, lastproxy->strvalue, 4);
      qp->timestamp = ntohl(tmp);
      memcpy((char *)&tmp, lastproxy->strvalue+4, 4);
      qp->ipaddr = ntohl(tmp);
      memcpy((char *)&tmps, lastproxy->strvalue+8, 2);
      qp->udp_port = ntohs(tmps);
      memcpy((char *)&(qp->id), lastproxy->strvalue+10, 1);
      memcpy((char *)qp->vector, lastproxy->strvalue+12, AUTH_VECTOR_LEN);

      pairfree(lastproxy, "pop_proxy");

      if(find_client(qp->ipaddr, qp->secret, hostnm) != 0) {
            reqfree(qp, "pop_proxy");
            qp = (AUTH_REQ *)NULL;
      }

      return qp;
}


/*************************************************************************
 *
 *    Function: send_proxy2client
 *
 *    Purpose: Forward a proxy reply to a client defined in qp.
 *           Calling routine has already removed our Proxy-State
 *
 *************************************************************************/

void
send_proxy2client(fd, authreq, server, qp)
int         fd;
AUTH_REQ    *authreq;
PEER        *server;
AUTH_REQ    *qp;
{
      AUTH_HDR          *auth;
      VALUE_PAIR        *curpair;
      VALUE_PAIR        *get_attribute();
      VALUE_PAIR        *pair;
      VALUE_PAIR        *reply;
      char              ip_str[32];
      int               build_packet();
      u_short                 total_length;
      void              ipaddr2str();
      void              pairfree();
      void              send_packet();

      auth = (AUTH_HDR *)send_buffer;

      reply = authreq->request;

      req2str(ip_str, authreq);

      /* if realm is marked insecure and has returned a Admin or NAS-Prompt
         service type, send a reject to the client instead, and log it */
      if ( (!(server->flags & PEER_ADMINOK))
           && (((pair = get_attribute(authreq->request,
                  PW_USER_SERVICE_TYPE)) != (VALUE_PAIR *)NULL)
                && ((pair->lvalue == PW_ADMIN_USER)
                   || (pair->lvalue == PW_PROMPT_USER)))) {

            authreq->code = PW_AUTHENTICATION_REJECT;
            log_err("remote server %s returned insecure service for client %s, sending reject instead\n",
                  ip_str, req2strp(qp));
            /* copy over only proxy-states */
            authreq->request = (VALUE_PAIR *)NULL;
            curpair = (VALUE_PAIR *)NULL;
            while(reply != (VALUE_PAIR *)NULL) {
                  if (reply->attribute == PW_PROXY) {
                        if (curpair == (VALUE_PAIR *)NULL) {
                              curpair = reply;
                              authreq->request = reply;
                        } else {
                              curpair->next = reply;
                              curpair = reply;
                        }
                  } else {
                        pairfree(reply, "send_proxy2client");
                  }
                  reply=reply->next;
            }
            if (curpair != (VALUE_PAIR *)NULL) {
                  curpair->next = (VALUE_PAIR *)NULL;
            }
      }

      DEBUG("forwarding reply code %d from %s to %s\n",
            authreq->code, ip_str, req2strp(qp));

      /* Load up the configuration values for the user */
      total_length = build_packet(qp, authreq->request, (char *)NULL,
                            authreq->code, FW_CLIENT, send_buffer);

      /* send it to the client */
      send_packet(fd, qp->ipaddr, qp->udp_port, send_buffer, total_length);

}


/*************************************************************************
 *
 *    Function: update_proxy
 *
 *    Purpose: Check last modified time on proxy file and build a
 *           new servers list if the file has been changed.
 *
 *************************************************************************/

int
update_proxy()
{
      extern u_short    radacct_port;
      extern u_short    radius_port;
      static UINT4      ouraddress = 0;
      static int  first = 0;
      static time_t     last_update_time;
      FILE        *fd;
      PEER        *curserv = NULL;
      PEER        *peeralloc();
      PEER        *server;
      UINT4       get_ipaddr();
      UINT4       ipaddr;
      char        *arg;
      char        *hostnm;
      char        *realm;
      char        *secret;
      char        *strtok();
      char        ourname[256];
      int         gethostname();
      int         lineno;
      int         nproxy;
      struct stat       statbuf;
      u_char            buffer[256];
      u_short           rport;
      void        peerfree();
      int         fclose();

      nproxy = 0;

      /* Check last modified time of proxy file */
      sprintf((char *)buffer, "%s/%s", radius_dir, RADIUS_PROXY);
      if(stat(buffer, &statbuf) != 0) {
            if (first == 0) {
                  log_err("proxy file %s not found; not using proxy\n", buffer);
                  first++;
            }
            return(0);
      }
      if(statbuf.st_mtime == last_update_time) {
            /* nothing to update */
            return(0);
      }

      /* Get our address if we have not already */
      /* This will need to be changed to support multi-homed hosts */

      if (ouraddress == 0) {
            errno = 0;
            if (gethostname(ourname, 128) != 0) {
                  log_err("update_proxy: unable to get own hostname; %s\n",
                        strerror(errno));
            }
            ouraddress = get_ipaddr(ourname);
            if (ouraddress == 0) {
                  log_err("update_proxy: unable to resolve own hostname \"%s\"\n",ourname);
            }
      }

      /* Proxy file format:
       *    hostname secret realm options...
       *
       * realm can be user@realm or a Called-Station-Id
       *
       */

      /* Open the proxy file */
      if((fd = fopen((const char *)buffer, "r")) == (FILE *)NULL) {
            log_err("Error: could not read proxy file %s; %s\n", buffer,
                  strerror(errno));
            return(-1);
      }

      /* free up existing linked list of servers */

      while (servers != (PEER *)NULL) {
            server = servers;
            servers = servers->next;
            server->next = (PEER *)NULL;
            peerfree(server, "update_proxy");
      }
      server = (PEER *)NULL;
      server_default = (PEER *)NULL;
      server_norealm = (PEER *)NULL;

      lineno=0;
      while (fgets((char *)buffer, sizeof(buffer), fd) != (char *)NULL) {
            lineno++;
            if(*buffer == '#' || *buffer == ' ' || *buffer == '\t'
               || *buffer == '\n') {
                  continue;
            }
            hostnm = strtok(buffer, " \t\n");
            secret = strtok((char *)NULL, " \t\n");
            realm = strtok((char *)NULL, " \t\n");
            if (realm == (char *)NULL) {
                  log_err("syntax error on line %d in proxy file\n", lineno);
                  continue;
            }
            if((ipaddr = get_ipaddr(hostnm)) == (UINT4)0) {
                  log_err("could not resolve proxy hostname %s at line %d\n", hostnm, lineno);
                  continue;
            }

            /* store in realm linked list */
            server = peeralloc("update_proxy");

            /* parse arguments to proxy line */

            while ((arg = strtok((char *)NULL, " \t\n, ")) != (char *)NULL) {
                  if (isdigit(arg[0])) {
                        rport = atoi(arg);
                        if (rport > 0 && rport <= 0xffff) {
                              if (server->radport == 0) {
                                    server->radport = rport;
                              } else if (server->acctport == 0) {
                                    server->acctport = rport;
                              } else {
                                    log_err("unknown argument \"%s\" on line %d in proxy file\n", arg, lineno);
                              }
                        }
                        continue;
                  }

                  if (strcmp(arg, "old") == 0) {
                        server->flags |= PEER_NOPROXY;
                        server->flags |= PEER_OLDACCT;
                  }
                  else if (strcmp(arg, "secure") == 0) {
                        server->flags |= PEER_ADMINOK;
                  }
                  else if (strcmp(arg, "ipass") == 0) {
                        server->flags |= PEER_IPASS;
                  } else {
                        log_err("unknown argument \"%s\" on line %d in proxy file ignored\n", arg, lineno);
                  }

            }
            if (server->radport == 0) {
                  server->radport = radius_port;
            }
            if (server->acctport == 0) {
                  server->acctport = radacct_port;
            }

            if ((ipaddr == ouraddress)
                && ((server->radport == radius_port)
                  || (server->acctport == radacct_port))) {
                  ipaddr = 0; /* handle ourselves, do not forward */
            }
            server->ipaddr = ipaddr;

            memcpy(server->secret, secret, strlen(secret));
            memcpy(server->realm, realm, strlen(realm));

            if (strcmp(realm, "DEFAULT") == 0) {
                  server_default = server;
            } else if (strcmp(realm, "NOREALM") == 0) {
                  server_norealm = server;
            } 

            if (servers == (PEER *)NULL) {      /* first one */
                  servers = server;
                  curserv = server;
            } else {
                  curserv->next = server;
                  curserv = server;
            }
            nproxy++;
      }
      fclose(fd);
      last_update_time = statbuf.st_mtime;
      DEBUG("found %d proxy realms\n", nproxy);
      return(0);
}



/*************************************************************************
 *************************************************************************

      Subroutines related to forwarding a Proxy request from a client
      to the appropriate server

      handle_proxy(authreq)
      push_proxy(authreq)
      find_server(number, realm)
      find_server_byaddr(ipaddr, port)
      send_proxy2server(authreq, server)
      getnextid(authreq)

 *************************************************************************
 *************************************************************************/


/*************************************************************************
 *
 *    Function: handle_proxy
 *
 *    Purpose: Called by rad_request() to check if access-request should
 *           be forwarded to another server.
 *           Called by rad_acctreq() to forward acct-request if needed.
 *
 *    Returns:
 *          -1 on error
 *           0 if this request was not forwarded
 *           1 if this request was forwarded
 *    Side-Effects:
 *          Sets REQ_PROXY flag in authreq if request was forwarded
 *
 *
 *************************************************************************/

int
handle_proxy(authreq)
AUTH_REQ *authreq;
{
      AUTH_REQ    *radrecv();
      PEER        *find_server();
      PEER        *server;
      VALUE_PAIR  *get_attribute();
      VALUE_PAIR  *namepair;
      VALUE_PAIR  *pair;
      char        *decrypt_password();
      char        *encrypt_password();
      char        *name;
      char        namebuf[256];
      char        *number;
      char        *ptr;
      char        *realm;
/*    char        *strchr(); */
      int         rad_forw_ipass();
      int         ret;
      void        push_proxy();
      void        send_proxy2server();

      realm = (char *)NULL;
      number = (char *)NULL;
      namebuf[0] = '\0';
      name = namebuf;

      if (authreq == (AUTH_REQ *)NULL) {
            return -1;
      }

      if (authreq->code != PW_AUTHENTICATION_REQUEST &&
          authreq->code != PW_ACCOUNTING_REQUEST) {
            /* should not happen unless client sent bogus message */
            DEBUG("handle_proxy called for packet type %d unexpectedly\n",
                  authreq->code);
            authreq->flags |= REQ_ERR|REQ_FREE;
            return -1;
      }

        namepair = get_attribute(authreq->request, PW_USER_NAME);
        if (namepair != (VALUE_PAIR *)NULL) {
            memcpy(namebuf, namepair->strvalue, namepair->lvalue);
            namebuf[namepair->lvalue] = '\0';
            if ((realm=strchr((const char *)name, '@')) != (char *)NULL) {
                  *realm = '\0';
                  realm++;
            } else if ((ptr=strchr((const char *)name, '/')) != (char *)NULL) {
                  *ptr = '\0';
                  ptr++;
                  realm = name;
                  name = ptr;
            } else {
                  realm = (char *)NULL;
            }
      }

        pair = get_attribute(authreq->request, PW_CALLED);
      if (pair != (VALUE_PAIR *)NULL) {
            number = pair->strvalue;
      }

      /*    look up number or realm in the list of servers.  If a proxy
       *    is found, parse the packet, unencrypt the password if
       *    any, re-encrypt the password (if any), attach a
       *    proxy-state with push_proxy(), and forward it to the proxy.
       *    Use the same Request Authenticator (in case of CHAP) but
       *    use a new Id
       */

      if ((server = find_server(number, realm)) == (PEER *)NULL) {
            if (namepair != (VALUE_PAIR *)NULL) {
                  strncpy(authreq->name, namepair->strvalue, 64);
                  authreq->name[63] = '\0';
            } else {
                  authreq->name[0] = '\0';
            }
            return 0;   /* no proxy */
      }

      strncpy(authreq->realm, server->realm, 64);
      strncpy(authreq->name, name, 64);
      authreq->name[63] = '\0';

      if (server->ipaddr == 0) {    /* this is the server for this realm */
            return 0;
      }

      authreq->flags |= REQ_PROXY;

      if (server->flags & PEER_IPASS) {
            if (authreq->code == PW_AUTHENTICATION_REQUEST) {
                  ret = rad_forw_ipass(authreq, sockfd, authreq->packet);
                  authreq->flags |= REQ_FREE;
                  return ret;
            } else if (authreq->code == PW_ACCOUNTING_REQUEST) {
                  ret = rad_forw_ipass(authreq, acctfd, authreq->packet);
                  authreq->flags |= REQ_FREE;
                  return 0;
            } else {
                  authreq->flags |= REQ_ERR|REQ_FREE;
                  return -1;
            }
      }

      if (authreq->code == PW_AUTHENTICATION_REQUEST) {
            if (decrypt_password(authreq, authreq->secret) != (char *)NULL) {
                  encrypt_password(authreq, server->secret);
            }
      }
      /* If remote server cannot handle proxy, delete realm from User-Name */
      if ( server->flags & PEER_NOPROXY ) {
            if (namepair != (VALUE_PAIR *)NULL) {
                  strcpy(namepair->strvalue,name);
                  namepair->lvalue = strlen(name);
            }
      } else {
            push_proxy(authreq);
      }

      send_proxy2server(authreq, server);

      return 1;
}


/*************************************************************************
 *
 *    Function: push_proxy
 *
 *    Purpose: Adds a Proxy-State to the end of a packet
 *           Note that RADIUS requires that Proxy-State always be
 *           added after any existing Proxy-State attributes.
 *
 *           Any changes made here must also be made in pop_proxy()
 *
 *************************************************************************/

void
push_proxy(authreq)
AUTH_REQ          *authreq;
{

      DICT_ATTR       *attr;
        DICT_ATTR       *dict_attrget();
      VALUE_PAIR  *pair;
      VALUE_PAIR  *list;
      VALUE_PAIR  *pairalloc();
      UINT4       tmp;
      u_short           tmps;
      void        rad_exit();

      pair = pairalloc("push_proxy");

      /* Proxy-State contains: Timestamp, Client, Port, Id in network order,
         a pad byte, and 16 octets of Request Authenticator */

      if((attr = dict_attrget(PW_PROXY)) == (DICT_ATTR *)NULL) {
            DEBUG("add Proxy (%d) to dictionary\n", PW_PROXY);
            strcpy(pair->name, "Proxy");
            pair->type = PW_TYPE_STRING;
      } else {
            strcpy(pair->name, attr->name);
            pair->type = attr->type;
      }

      pair->attribute = PW_PROXY;
      pair->lvalue = 28;      /* length of data */

      tmp = htonl(authreq->timestamp);
      memcpy(pair->strvalue, (char *)&tmp, 4);
      tmp = htonl(authreq->ipaddr);
      memcpy(pair->strvalue+4, (char *)&tmp, 4);
      tmps = htons(authreq->udp_port);
      memcpy(pair->strvalue+8, (char *)&tmps, 2);
      memcpy(pair->strvalue+10, (char *)&(authreq->id), 1);
      memset(pair->strvalue+11, 0, 1);                /* zero pad */
      memcpy(pair->strvalue+12, authreq->vector, AUTH_VECTOR_LEN);

      if ((list = authreq->request) == (VALUE_PAIR *)NULL) {
            authreq->request = pair;
      } else {
            while(list->next != (VALUE_PAIR *)NULL) {
                  list = list->next;
            }
            list->next = pair;
      }
}


/*************************************************************************
 *
 *    Function: find_server
 *
 *    Purpose: Returns server to forward to based on number or realm
 *           Number takes precedence
 *
 *************************************************************************/

PEER *
find_server(number, realm)
char *number;
char *realm;
{
      extern PEER *servers;
      extern PEER *server_default;
      extern PEER *server_norealm;
      PEER  *server;
      PEER  *maybe;

      maybe = (PEER *)NULL;

      server=servers;

      /* In a future version we may want to match with or without area code */

      while(server != (PEER *)NULL) {
            if ((number != (char *)NULL)
                && (strcmp(server->realm, number) == 0)) {
                  return server;
            }
            if ((realm != (char *)NULL)
                && (strcmp(server->realm, realm) == 0)) {
                  maybe = server;
            }
            server = server->next;
      }
      if (maybe == (PEER *)NULL) {
            if (realm != (char *)NULL) {
                  maybe = server_default;
            } else {
                  maybe = server_norealm;
            }
      }
      return maybe;
}


/*************************************************************************
 *
 *    Function: find_server_byaddr
 *
 *    Purpose: Returns proxy server based on IP address and source port
 *
 *************************************************************************/

PEER *
find_server_byaddr(ipaddr, port)
UINT4 ipaddr;
u_short port;
{
      PEER  *server;
      extern PEER *servers;

      server=servers;

      while ((server != (PEER *)NULL) && !((server->ipaddr == ipaddr) &&
            ((server->acctport == port) || (server->radport == port))) ) {

            server = server->next;
      }
      return server;
}


/*************************************************************************
 *
 *    Function: send_proxy2server
 *
 *    Purpose: Forward a proxy request to a server
 *           Calling routine has already encrypted password (if any)
 *           and added our Proxy-State
 *
 *************************************************************************/

void
send_proxy2server(authreq, server)
AUTH_REQ    *authreq;
PEER        *server;
{
      extern AUTH_REQ         *first_forwarded;
      AUTH_HDR          *auth;
      char              *bufalloc();
      char              ip_str[32];
      int               build_packet();
      int               fd;
      u_char                  getnextid();
      u_char                  saveid;
      u_short                 total_length;
      void              md5_calc();
      void              send_packet();

      req2str(ip_str, authreq);

      auth = (AUTH_HDR *)send_buffer;

      if (authreq->code == PW_AUTHENTICATION_REQUEST) {
            authreq->forw_port = server->radport;
            fd = proxyfd;
      } else if (authreq->code == PW_ACCOUNTING_REQUEST) {
            authreq->forw_port = server->acctport;
            fd = proxyacctfd;
      } else {
            log_err("unknown request type %d from %s ignored\n",
                  authreq->code, ip_str);
            authreq->flags |= REQ_ERR|REQ_FREE;
            return;
      }

      authreq->forw_id = getnextid(authreq);
      /* getnextid sets REQ_ERR flag if unable to allocate id */
      if ((authreq->flags & REQ_ERR) == REQ_ERR) {
            return;
      }
      saveid = authreq->id;
      authreq->id = authreq->forw_id;

      authreq->forw_addr = server->ipaddr;
      memcpy(authreq->forw_vector, authreq->vector, AUTH_VECTOR_LEN);
      strcpy(authreq->forw_secret, server->secret);

      DEBUG("forwarding request from %s to %s/%d.%d for %s\n",
            ip_str, ipaddr2strp(authreq->forw_addr), authreq->forw_port,
            authreq->forw_id, authreq->realm);

      total_length = build_packet(authreq, authreq->request, (char *)NULL,
                            authreq->code, FW_SERVER, send_buffer);

      authreq->id = saveid;
      memcpy(authreq->forw_vector, auth->vector, AUTH_VECTOR_LEN);

      /* forward it to the server */
        send_packet(fd, authreq->forw_addr, authreq->forw_port,
                send_buffer, total_length);

      if (!(authreq->flags & REQ_DUP)) {
            authreq->next = first_forwarded;
            first_forwarded = authreq;
            if (authreq == authreq->next) {
                  log_err("ERROR: circular queue detected at %d\n", __LINE__);
            }
      }
      return;
}

/*************************************************************************
 *
 *    Function: getnextid
 *
 *    Purpose: Returns next ID for use in forwarding to this server
 *           Use one ID counter 0..255 for all forwarded packets
 *
 *************************************************************************/

u_char
getnextid(authreq)
AUTH_REQ *authreq;
{
      extern AUTH_REQ   *first_forwarded;
      AUTH_REQ    *qp;
      AUTH_REQ    *prevqp;
      static char inuse[256];
      u_char            newid;
      static u_char     curid = 0;
      static int  flushcount = 0;
      extern int  max_proxy_time;
      extern UINT4      now;

      memset(inuse, 0, 256);
      prevqp = (AUTH_REQ *)NULL;

      qp = first_forwarded;

      while (qp != (AUTH_REQ *)NULL) {
            if (qp->timestamp + max_proxy_time < now) {
                  if (++flushcount % 100 == 0) {
                        DEBUG("%d proxy requests expired unanswered\n",flushcount);
                  }
                  if (prevqp == (AUTH_REQ *)NULL) {
                        first_forwarded = qp->next;
                        reqfree(qp,"getnextid");
                        qp = first_forwarded;
                  } else {
                        prevqp->next = qp->next;
                        reqfree(qp,"getnextid");
                        qp = prevqp->next;
                  }
            } else {
                  if (authreq->ipaddr == qp->ipaddr &&
                      authreq->udp_port == qp->udp_port &&
                      authreq->id == qp->id) {
                        authreq->flags |= REQ_DUP|REQ_FREE;
                        return qp->forw_id;
                  }
                  inuse[qp->forw_id]++;
                  if (qp == qp->next) {
                        log_err("ERROR: circular queue detected at %d\n", __LINE__);
                        qp->next = (AUTH_REQ *)NULL;
                  }
                  prevqp = qp;
                  qp = qp->next;
            }
      }
      for (newid = curid; newid < 256; newid++) {
            if (inuse[newid] == 0) {
                  curid = ((newid+1) & 0xff);
                  return newid;
            }
      }
      for (newid = 0; newid < curid; newid++) {
            if (inuse[newid] == 0) {
                  curid = ((newid+1) & 0xff);
                  return newid;
            }
      }
      /* no ids left, so log an error and mark packet for discard */
      log_err("getnextid: out of IDs, dropping packet from %s\n",
            req2strp(authreq));
      authreq->flags |= REQ_ERR|REQ_FREE;

      return 0;
}



/*************************************************************************
 *************************************************************************

      Misc subroutines for proxy

 *************************************************************************
 *************************************************************************/

/*************************************************************************
 *
 *    Function: proxy_report
 *
 *    Purpose: Log proxy queue status for debugging purposes
 *
 *    Uses External: first_forwarded
 *
 *************************************************************************/

void
proxy_report()
{
      AUTH_REQ    *qp;
      UINT4       oldest;
      UINT4       clock;
      int         n;
      time_t            time();

      if (first_forwarded != (AUTH_REQ *)NULL) {
            qp = first_forwarded;
            n = 0;
            clock = (UINT4)time((time_t *)NULL);
            oldest = clock;
            while (qp != (AUTH_REQ *)NULL) {
                  n++;
                  if (qp->timestamp < oldest) {
                        oldest = qp->timestamp;
                  }
                  qp = qp->next;
            }
            clock = clock - oldest;
            log_err("%d in proxy queue, oldest %d seconds ago\n", n, clock);
      } else {
            log_err("no entries in proxy queue\n");
      }
}

Generated by  Doxygen 1.6.0   Back to index