Logo Search packages:      
Sourcecode: radiusclient version File versions  Download package

sendserver.c

/*
 * $Id: sendserver.c,v 1.8 2002/02/03 13:51:41 lf Exp $
 *
 * Copyright (C) 1995,1996,1997 Lars Fenneberg
 *
 * Copyright 1992 Livingston Enterprises, Inc.
 *
 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan 
 * and Merit Network, Inc. All Rights Reserved
 *
 * See the file COPYRIGHT for the respective terms and conditions. 
 * If the file is missing contact me at lf@elemental.net 
 * and I'll send you a copy.
 *
 */

#include <config.h>
#include <includes.h>
#include <radiusclient.h>
#include <pathnames.h>

static void rc_random_vector (unsigned char *);
static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char);

/*
 * Function: rc_pack_list
 *
 * Purpose: Packs an attribute value pair list into a buffer.
 *
 * Returns: Number of octets packed.
 *
 */

static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
{
      int             length, i, pc, secretlen, padded_length;
      int             total_length = 0;
      UINT4           lvalue;
      unsigned char   passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
      unsigned char   md5buf[256];
      unsigned char   *buf, *vector;
      
      buf = auth->data;

      while (vp != (VALUE_PAIR *) NULL)
      {
            *buf++ = vp->attribute;

            switch (vp->attribute)
            {
             case PW_USER_PASSWORD:

              /* Encrypt the password */
      
              /* Chop off password at AUTH_PASS_LEN */
              length = vp->lvalue;
              if (length > AUTH_PASS_LEN)
                  length = AUTH_PASS_LEN;

              /* Calculate the padded length */
              padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1);

              /* Record the attribute length */       
              *buf++ = padded_length + 2;
              
              /* Pad the password with zeros */
              memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
              memcpy ((char *) passbuf, vp->strvalue, (size_t) length);

              secretlen = strlen (secret);
              vector = (char *)auth->vector;
              for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN)
              {
                  /* Calculate the MD5 digest*/
                  strcpy ((char *) md5buf, secret);
                  memcpy ((char *) md5buf + secretlen, vector,
                          AUTH_VECTOR_LEN);
                  rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
              
                    /* Remeber the start of the digest */
                  vector = buf;
                  
                  /* Xor the password into the MD5 digest */
                  for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++)
                  {
                        *buf++ ^= passbuf[pc];
                  }
              }

              total_length += padded_length + 2;
              
              break;
#if 0       
             case PW_CHAP_PASSWORD:

              *buf++ = CHAP_VALUE_LENGTH + 2;

              /* Encrypt the Password */
              length = vp->lvalue;
              if (length > CHAP_VALUE_LENGTH)
              {
                  length = CHAP_VALUE_LENGTH;
              }
              memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH);
              memcpy ((char *) passbuf, vp->strvalue, (size_t) length);

              /* Calculate the MD5 Digest */
              secretlen = strlen (secret);
              strcpy ((char *) md5buf, secret);
              memcpy ((char *) md5buf + secretlen, (char *) auth->vector,
                    AUTH_VECTOR_LEN);
              rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);

              /* Xor the password into the MD5 digest */
              for (i = 0; i < CHAP_VALUE_LENGTH; i++)
              {
                  *buf++ ^= passbuf[i];
              }
              total_length += CHAP_VALUE_LENGTH + 2;
             
              break;
#endif
             default:
              switch (vp->type)
              {
                case PW_TYPE_STRING:
                  length = vp->lvalue;
                  *buf++ = length + 2;
                  memcpy (buf, vp->strvalue, (size_t) length);
                  buf += length;
                  total_length += length + 2;
                  break;

                case PW_TYPE_INTEGER:
                case PW_TYPE_IPADDR:
                  *buf++ = sizeof (UINT4) + 2;
                  lvalue = htonl (vp->lvalue);
                  memcpy (buf, (char *) &lvalue, sizeof (UINT4));
                  buf += sizeof (UINT4);
                  total_length += sizeof (UINT4) + 2;
                  break;

                default:
                  break;
              }
              break;
            }
            vp = vp->next;
      }
      return total_length;
}

/*
 * Function: rc_send_server
 *
 * Purpose: send a request to a RADIUS server and wait for the reply
 *
 */

int rc_send_server (SEND_DATA *data, char *msg)
{
      int             sockfd;
      struct sockaddr salocal;
      struct sockaddr saremote;
      struct sockaddr_in *sin;
      struct timeval  authtime;
      fd_set          readfds;
      AUTH_HDR       *auth, *recv_auth;
      UINT4           auth_ipaddr;
      char           *server_name;  /* Name of server to query */
      int             salen;
      int             result;
      int             total_length;
      int             length;
      int             retry_max;
      int         secretlen;
      char            secret[MAX_SECRET_LENGTH + 1];
      unsigned char   vector[AUTH_VECTOR_LEN];
      char            recv_buffer[BUFFER_LEN];
      char            send_buffer[BUFFER_LEN];
      int         retries;
      VALUE_PAIR  *vp;

      server_name = data->server;
      if (server_name == (char *) NULL || server_name[0] == '\0')
            return (ERROR_RC);

      if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \
          (vp->lvalue == PW_ADMINISTRATIVE)) 
      {
            strcpy(secret, MGMT_POLL_SECRET);
            if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0)
                  return (ERROR_RC);
      } 
      else 
      {
            if (rc_find_server (server_name, &auth_ipaddr, secret) != 0)
            {
                  return (ERROR_RC);
            }
      }

      sockfd = socket (AF_INET, SOCK_DGRAM, 0);
      if (sockfd < 0)
      {
            memset (secret, '\0', sizeof (secret));
            rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno));
            return (ERROR_RC);
      }

      length = sizeof (salocal);
      sin = (struct sockaddr_in *) & salocal;
      memset ((char *) sin, '\0', (size_t) length);
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = htonl(INADDR_ANY);
      sin->sin_port = htons ((unsigned short) 0);
      if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
               getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
      {
            close (sockfd);
            memset (secret, '\0', sizeof (secret));
            rc_log(LOG_ERR, "rc_send_server: bind: %s: %s", server_name, strerror(errno));
            return (ERROR_RC);
      }
      
      retry_max = data->retries;    /* Max. numbers to try for reply */
      retries = 0;                  /* Init retry cnt for blocking call */

      /* Build a request */
      auth = (AUTH_HDR *) send_buffer;
      auth->code = data->code;
      auth->id = data->seq_nbr;

      if (data->code == PW_ACCOUNTING_REQUEST)
      {
            total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;

            auth->length = htons ((unsigned short) total_length);
            
            memset((char *) auth->vector, 0, AUTH_VECTOR_LEN);
            secretlen = strlen (secret);
            memcpy ((char *) auth + total_length, secret, secretlen);
            rc_md5_calc (vector, (char *) auth, total_length + secretlen);
            memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
      }
      else
      {
            rc_random_vector (vector);
            memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);

            total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;

            auth->length = htons ((unsigned short) total_length);
      }

      sin = (struct sockaddr_in *) & saremote;
      memset ((char *) sin, '\0', sizeof (saremote));
      sin->sin_family = AF_INET;
      sin->sin_addr.s_addr = htonl (auth_ipaddr);
      sin->sin_port = htons ((unsigned short) data->svc_port);

      for (;;)
      {
            sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0,
                  (struct sockaddr *) sin, sizeof (struct sockaddr_in));

            authtime.tv_usec = 0L;
            authtime.tv_sec = (long) data->timeout;
            FD_ZERO (&readfds);
            FD_SET (sockfd, &readfds);
            if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
            {
                  if (errno == EINTR)
                        continue;
                  rc_log(LOG_ERR, "rc_send_server: select: %s", strerror(errno));
                  memset (secret, '\0', sizeof (secret));
                  close (sockfd);
                  return (ERROR_RC);
            }
            if (FD_ISSET (sockfd, &readfds))
                  break;

            /*
             * Timed out waiting for response.  Retry "retry_max" times
             * before giving up.  If retry_max = 0, don't retry at all.
             */
            if (++retries >= retry_max)
            {
                  rc_log(LOG_ERR,
                        "rc_send_server: no reply from RADIUS server %s:%u",
                         rc_ip_hostname (auth_ipaddr), data->svc_port);
                  close (sockfd);
                  memset (secret, '\0', sizeof (secret));
                  return (TIMEOUT_RC);
            }
      }
      salen = sizeof (saremote);
      length = recvfrom (sockfd, (char *) recv_buffer,
                     (int) sizeof (recv_buffer),
                     (int) 0, &saremote, &salen);

      if (length <= 0)
      {
            rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: %s", server_name,\
                   data->svc_port, strerror(errno));
            close (sockfd);
            memset (secret, '\0', sizeof (secret));
            return (ERROR_RC);
      }
      
      recv_auth = (AUTH_HDR *)recv_buffer;
      
      result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);

      data->receive_pairs = rc_avpair_gen(recv_auth);

      close (sockfd);
      memset (secret, '\0', sizeof (secret));

      if (result != OK_RC) return (result);
      
      *msg = '\0';
      vp = data->receive_pairs;
      while (vp)
      {
            if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE)))
            {
                  strcat(msg, vp->strvalue);
                  strcat(msg, "\n");
                  vp = vp->next;
            }
      }
      
      if ((recv_auth->code == PW_ACCESS_ACCEPT) ||
            (recv_auth->code == PW_PASSWORD_ACK) ||
            (recv_auth->code == PW_ACCOUNTING_RESPONSE))
      {
            result = OK_RC;
      }
      else
      {
            result = BADRESP_RC;
      }

      return (result);
}

/*
 * Function: rc_check_reply
 *
 * Purpose: verify items in returned packet.
 *
 * Returns: OK_RC       -- upon success,
 *          BADRESP_RC  -- if anything looks funny.
 *
 */

static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret, unsigned char *vector,\
                     unsigned char seq_nbr)
{
      int             secretlen;
      int             totallen;
      unsigned char   calc_digest[AUTH_VECTOR_LEN];
      unsigned char   reply_digest[AUTH_VECTOR_LEN];

      totallen = ntohs (auth->length);
      secretlen = strlen (secret);

      /* Do sanity checks on packet length */
      if ((totallen < 20) || (totallen > 4096))
      {
            rc_log(LOG_ERR, "rc_check_reply: received RADIUS server response with invalid length");
            return (BADRESP_RC);
      } 

      /* Verify buffer space, should never trigger with current buffer size and check above */
      if ((totallen + secretlen) > bufferlen)
      {
            rc_log(LOG_ERR, "rc_check_reply: not enough buffer space to verify RADIUS server response");
            return (BADRESP_RC);
      } 

      /* Verify that id (seq. number) matches what we sent */
      if (auth->id != seq_nbr)
      {
            rc_log(LOG_ERR, "rc_check_reply: received non-matching id in RADIUS server response");
            return (BADRESP_RC);
      }

      /* Verify the reply digest */
      memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
      memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
      memcpy ((char *) auth + totallen, secret, secretlen);
      rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen);

#ifdef DIGEST_DEBUG
      {
            int i;
            
            fputs("reply_digest: ", stderr);
            for (i = 0; i < AUTH_VECTOR_LEN; i++)
            {
                  fprintf(stderr,"%.2x ", (int) reply_digest[i]);             
            }
            fputs("\ncalc_digest:  ", stderr);
            for (i = 0; i < AUTH_VECTOR_LEN; i++)
            {
                  fprintf(stderr,"%.2x ", (int) calc_digest[i]);              
            }
            fputs("\n", stderr);
      }
#endif

      if (memcmp ((char *) reply_digest, (char *) calc_digest,
                AUTH_VECTOR_LEN) != 0)
      {
#ifdef RADIUS_116
            /* the original Livingston radiusd v1.16 seems to have
               a bug in digest calculation with accounting requests,
               authentication request are ok. i looked at the code
               but couldn't find any bugs. any help to get this
               kludge out are welcome. preferably i want to
               reproduce the calculation bug here to be compatible
               to stock Livingston radiusd v1.16.     -lf, 03/14/96 
             */
            if (auth->code == PW_ACCOUNTING_RESPONSE)
                  return (OK_RC);
#endif
            rc_log(LOG_ERR, "rc_check_reply: received invalid reply digest from RADIUS server");
            return (BADRESP_RC);
      }

      return (OK_RC);
      
}

/*
 * Function: rc_random_vector 
 *
 * Purpose: generates a random vector of AUTH_VECTOR_LEN octets.
 *
 * Returns: the vector (call by reference)
 *
 */

static void rc_random_vector (unsigned char *vector)
{
      int             randno;
      int             i;
#if defined(HAVE_DEV_URANDOM)
      int         fd;

/* well, I added this to increase the security for user passwords.
   we use /dev/urandom here, as /dev/random might block and we don't
   need that much randomness. BTW, great idea, Ted!     -lf, 03/18/95   */
      
      if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0)
      {
            unsigned char *pos;
            int readcount;
      
            i = AUTH_VECTOR_LEN;
            pos = vector;
            while (i > 0)
            {
                  readcount = read(fd, (char *)pos, i);
                  pos += readcount;
                  i -= readcount;
            }

            close(fd);
            return;                 
      } /* else fall through */
#endif
      srand (time (0) + getppid() + getpid()); /* random enough :) */
      for (i = 0; i < AUTH_VECTOR_LEN;)
      {
            randno = rand ();
            memcpy ((char *) vector, (char *) &randno, sizeof (int));
            vector += sizeof (int);
            i += sizeof (int);
      }

      return;
} 

Generated by  Doxygen 1.6.0   Back to index