--- /dev/null
+/*
+ * $Id: sendserver.c,v 1.1 2002/01/22 16:03:02 dfs 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 *, 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, *lenptr;
+
+ buf = auth->data;
+
+ while (vp != (VALUE_PAIR *) NULL)
+ {
+
+ if (vp->vendorcode != VENDOR_NONE) {
+ *buf++ = PW_VENDOR_SPECIFIC;
+
+ /* Place-holder for where to put length */
+ lenptr = buf++;
+
+ /* Insert vendor code */
+ *buf++ = 0;
+ *buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255;
+ *buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255;
+ *buf++ = ((unsigned int) vp->vendorcode) & 255;
+
+ /* Insert vendor-type */
+ *buf++ = vp->attribute;
+
+ /* Insert value */
+ switch(vp->type) {
+ case PW_TYPE_STRING:
+ length = vp->lvalue;
+ *lenptr = length + 8;
+ *buf++ = length+2;
+ memcpy(buf, vp->strvalue, (size_t) length);
+ buf += length;
+ total_length += length+8;
+ break;
+ case PW_TYPE_INTEGER:
+ case PW_TYPE_IPADDR:
+ length = sizeof(UINT4);
+ *lenptr = length + 8;
+ *buf++ = length+2;
+ lvalue = htonl(vp->lvalue);
+ memcpy(buf, (char *) &lvalue, sizeof(UINT4));
+ buf += length;
+ total_length += length+8;
+ break;
+ default:
+ break;
+ }
+ } else {
+ *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, 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, 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);
+
+ /* Verify that id (seq. number) matches what we sent */
+ if (auth->id != seq_nbr)
+ {
+ rc_log(LOG_ERR, "check_radius_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);
+ secretlen = strlen (secret);
+ 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, "check_radius_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;
+}