|
@@ -0,0 +1,863 @@
|
|
|
+/*
|
|
|
+ * $Id: sendserver.c,v 1.30 2010/06/15 09:22:52 aland 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 <poll.h>
|
|
|
+
|
|
|
+#include <radius_config.h>
|
|
|
+#include <includes.h>
|
|
|
+#include <freeradius-client.h>
|
|
|
+#include <pathnames.h>
|
|
|
+#include "util.h"
|
|
|
+#include "radius_user.h"
|
|
|
+
|
|
|
+#include "lwip/sockets.h"
|
|
|
+
|
|
|
+#define SA(p) ((struct sockaddr *)(p))
|
|
|
+
|
|
|
+static void rc_random_vector (unsigned char *);
|
|
|
+static int rc_check_reply (AUTH_HDR *, int, char const *, unsigned char const *, unsigned char);
|
|
|
+
|
|
|
+/** Packs an attribute value pair list into a buffer
|
|
|
+ *
|
|
|
+ * @param vp a pointer to a #VALUE_PAIR.
|
|
|
+ * @param secret the secret used by the server.
|
|
|
+ * @param auth a pointer to #AUTH_HDR.
|
|
|
+ * @return The number of octets packed.
|
|
|
+ */
|
|
|
+static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
|
|
|
+{
|
|
|
+ int length, i, pc, padded_length;
|
|
|
+ int total_length = 0;
|
|
|
+ size_t secretlen;
|
|
|
+ uint32_t lvalue, vendor;
|
|
|
+ unsigned char passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
|
|
|
+ unsigned char md5buf[256];
|
|
|
+ unsigned char *buf, *vector, *vsa_length_ptr;
|
|
|
+
|
|
|
+ buf = auth->data;
|
|
|
+
|
|
|
+ while (vp != NULL)
|
|
|
+ {
|
|
|
+ vsa_length_ptr = NULL;
|
|
|
+ if (VENDOR(vp->attribute) != 0) {
|
|
|
+ *buf++ = PW_VENDOR_SPECIFIC;
|
|
|
+ vsa_length_ptr = buf;
|
|
|
+ *buf++ = 6;
|
|
|
+ vendor = htonl(VENDOR(vp->attribute));
|
|
|
+ memcpy(buf, &vendor, sizeof(uint32_t));
|
|
|
+ buf += 4;
|
|
|
+ total_length += 6;
|
|
|
+ }
|
|
|
+ *buf++ = (vp->attribute & 0xff);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ if (vsa_length_ptr != NULL) *vsa_length_ptr += 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 = (unsigned 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;
|
|
|
+ default:
|
|
|
+ switch (vp->type)
|
|
|
+ {
|
|
|
+ case PW_TYPE_STRING:
|
|
|
+ length = vp->lvalue;
|
|
|
+ *buf++ = length + 2;
|
|
|
+ if (vsa_length_ptr != NULL) *vsa_length_ptr += length + 2;
|
|
|
+ memcpy (buf, vp->strvalue, (size_t) length);
|
|
|
+ buf += length;
|
|
|
+ total_length += length + 2;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PW_TYPE_IPV6ADDR:
|
|
|
+ length = 16;
|
|
|
+ *buf++ = length + 2;
|
|
|
+ if (vsa_length_ptr != NULL) *vsa_length_ptr += length + 2;
|
|
|
+ memcpy (buf, vp->strvalue, (size_t) length);
|
|
|
+ buf += length;
|
|
|
+ total_length += length + 2;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PW_TYPE_IPV6PREFIX:
|
|
|
+ length = vp->lvalue;
|
|
|
+ *buf++ = length + 2;
|
|
|
+ if (vsa_length_ptr != NULL) *vsa_length_ptr += length + 2;
|
|
|
+ memcpy (buf, vp->strvalue, (size_t) length);
|
|
|
+ buf += length;
|
|
|
+ total_length += length + 2;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PW_TYPE_INTEGER:
|
|
|
+ case PW_TYPE_IPADDR:
|
|
|
+ case PW_TYPE_DATE:
|
|
|
+ *buf++ = sizeof (uint32_t) + 2;
|
|
|
+ if (vsa_length_ptr != NULL) *vsa_length_ptr += sizeof(uint32_t) + 2;
|
|
|
+ lvalue = htonl (vp->lvalue);
|
|
|
+ memcpy (buf, (char *) &lvalue, sizeof (uint32_t));
|
|
|
+ buf += sizeof (uint32_t);
|
|
|
+ total_length += sizeof (uint32_t) + 2;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ vp = vp->next;
|
|
|
+ }
|
|
|
+ return total_length;
|
|
|
+}
|
|
|
+
|
|
|
+/** Appends a string to the provided buffer
|
|
|
+ *
|
|
|
+ * @param dest the destination buffer.
|
|
|
+ * @param max_size the maximum size available in the destination buffer.
|
|
|
+ * @param pos the current position in the dest buffer; initially must be zero.
|
|
|
+ * @param src the source buffer to append.
|
|
|
+ */
|
|
|
+static void strappend(char *dest, unsigned max_size, int *pos, const char *src)
|
|
|
+{
|
|
|
+ unsigned len = strlen(src) + 1;
|
|
|
+
|
|
|
+ if (*pos == -1)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (len + *pos > max_size) {
|
|
|
+ *pos = -1;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(&dest[*pos], src, len);
|
|
|
+ *pos += len-1;
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/** Sends a request to a RADIUS server and waits for the reply
|
|
|
+ *
|
|
|
+ * @param rh a handle to parsed configuration
|
|
|
+ * @param data a pointer to a #SEND_DATA structure
|
|
|
+ * @param msg must be an array of %PW_MAX_MSG_SIZE or %NULL; will contain the concatenation of
|
|
|
+ * any %PW_REPLY_MESSAGE received.
|
|
|
+ * @param flags must be %AUTH or %ACCT
|
|
|
+ * @return %OK_RC (0) on success, %TIMEOUT_RC on timeout %REJECT_RC on acess reject, or negative
|
|
|
+ * on failure as return value.
|
|
|
+ */
|
|
|
+
|
|
|
+#define RS_PORT_NUM 1812
|
|
|
+//#define RS_IP_ADDR "192.168.1.2"
|
|
|
+#define RS_IP_ADDR "192.168.14.234"
|
|
|
+
|
|
|
+#define DEVICE_PORT_NUM 1812
|
|
|
+//#define DEVICE_IP_ADDR "192.168.1.6"
|
|
|
+#define DEVICE_IP_ADDR "192.168.14.37"
|
|
|
+
|
|
|
+#define BUF_LEN 300
|
|
|
+
|
|
|
+static uint8_t send_buffer[BUF_LEN];
|
|
|
+static uint8_t recv_buffer[BUF_LEN];
|
|
|
+
|
|
|
+int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags)
|
|
|
+{
|
|
|
+#if 1
|
|
|
+ struct sockaddr_in sa,ra;
|
|
|
+ int socket;
|
|
|
+ fdsets sets;
|
|
|
+ AUTH_HDR* auth;
|
|
|
+ AUTH_HDR* recv_auth;
|
|
|
+ unsigned char vector[AUTH_VECTOR_LEN];
|
|
|
+ char secret[MAX_SECRET_LENGTH + 1];
|
|
|
+ int total_length;
|
|
|
+ int sendLen, recvLen;
|
|
|
+ int length;
|
|
|
+ int pos;
|
|
|
+ uint8_t* attr;
|
|
|
+ int result = 0;
|
|
|
+ VALUE_PAIR* vp;
|
|
|
+
|
|
|
+
|
|
|
+ initFdsets(&sets);
|
|
|
+
|
|
|
+ if(data->secret != NULL) {
|
|
|
+ strlcpy(secret, data->secret, MAX_SECRET_LENGTH);
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(&ra, 0, sizeof(struct sockaddr_in));
|
|
|
+ ra.sin_family = AF_INET;
|
|
|
+ ra.sin_addr.s_addr = inet_addr(RS_IP_ADDR);
|
|
|
+ ra.sin_port = htons(RS_PORT_NUM);
|
|
|
+
|
|
|
+ socket = socket(PF_INET, SOCK_DGRAM, 0);
|
|
|
+ if ( socket < 0 )
|
|
|
+ {
|
|
|
+ printf("socket call failed");
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO bind?
|
|
|
+
|
|
|
+ // Build a request (PW_ACCESS_REQUEST)
|
|
|
+ auth = (AUTH_HDR *) send_buffer;
|
|
|
+ auth->code = data->code;
|
|
|
+ auth->id = data->seq_nbr;
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+
|
|
|
+ // Bind socket
|
|
|
+ memset(&sa, 0, sizeof(struct sockaddr_in));
|
|
|
+ sa.sin_family = AF_INET;
|
|
|
+ sa.sin_addr.s_addr = inet_addr(DEVICE_IP_ADDR);
|
|
|
+ sa.sin_port = htons(DEVICE_PORT_NUM);
|
|
|
+
|
|
|
+ if (bind(socket, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) == -1)
|
|
|
+ {
|
|
|
+ printf("Bind to Port Number %d ,IP address %s failed\n", DEVICE_PORT_NUM, DEVICE_IP_ADDR);
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ sendLen = sendto(socket, (char*)auth, total_length, 0, (struct sockaddr*)&ra, sizeof(ra));
|
|
|
+ if(sendLen < 0)
|
|
|
+ {
|
|
|
+ printf("send failed\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Получение ответа, select
|
|
|
+ if (!recvSelect(&sets, &socket, 2000)) {
|
|
|
+ //timeCount = HAL_GetTick() - timeCount;
|
|
|
+ printf("SOCK recv timeout!\r\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Данные можно принимать
|
|
|
+ socklen_t sl = sizeof(sa);
|
|
|
+ recvLen = recvfrom(socket, recv_buffer, BUF_LEN, 0, (struct sockaddr*)&ra, &sl);
|
|
|
+
|
|
|
+ recv_auth = (AUTH_HDR*)recv_buffer;
|
|
|
+
|
|
|
+ // Проверки размера входящего сообщения
|
|
|
+ if (recvLen < AUTH_HDR_LEN || recvLen < ntohs(recv_auth->length)) {
|
|
|
+ printf("radius_server: reply is too short\r\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (recvLen > ntohs(recv_auth->length))
|
|
|
+ {
|
|
|
+ recvLen = ntohs(recv_auth->length);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Verify that it's a valid RADIUS packet before doing ANYTHING with it.
|
|
|
+ attr = recv_buffer + AUTH_HDR_LEN;
|
|
|
+ while (attr < (recv_buffer + recvLen)) {
|
|
|
+ if (attr[0] == 0) {
|
|
|
+ printf("radius_server: attribute zero is invalid\r\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (attr[1] < 2) {
|
|
|
+ printf("radius_server: attribute length is too small\r\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((attr + attr[1]) > (recv_buffer + recvLen)) {
|
|
|
+ printf("radius_server: attribute overflows the packet\r\n");
|
|
|
+ close(socket);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ attr += attr[1];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ result = rc_check_reply (recv_auth, BUF_LEN, secret, vector, data->seq_nbr);
|
|
|
+
|
|
|
+ length = ntohs(recv_auth->length) - AUTH_HDR_LEN;
|
|
|
+ if (length > 0) {
|
|
|
+ data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data, length, 0);
|
|
|
+ } else {
|
|
|
+ data->receive_pairs = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (result != OK_RC) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (msg) {
|
|
|
+ *msg = '\0';
|
|
|
+ pos = 0;
|
|
|
+ vp = data->receive_pairs;
|
|
|
+ while (vp)
|
|
|
+ {
|
|
|
+ if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0)))
|
|
|
+ {
|
|
|
+ strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue);
|
|
|
+ strappend(msg, PW_MAX_MSG_SIZE, &pos, "\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 if ((recv_auth->code == PW_ACCESS_REJECT) ||
|
|
|
+ (recv_auth->code == PW_PASSWORD_REJECT))
|
|
|
+ {
|
|
|
+ result = REJECT_RC;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: received RADIUS server response neither ACCEPT nor REJECT, invalid");
|
|
|
+ result = BADRESP_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ printf("Radius server end communication\r\n");
|
|
|
+ close(socket);
|
|
|
+ return result;
|
|
|
+
|
|
|
+ //getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST);
|
|
|
+ //getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST);
|
|
|
+
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
+#if 0
|
|
|
+ int sockfd;
|
|
|
+ AUTH_HDR *auth, *recv_auth;
|
|
|
+ char *server_name; /* Name of server to query */
|
|
|
+ struct sockaddr_storage our_sockaddr;
|
|
|
+ struct addrinfo *auth_addr = NULL;
|
|
|
+ socklen_t salen;
|
|
|
+ int result = 0;
|
|
|
+ int total_length;
|
|
|
+ int length, pos;
|
|
|
+ int retry_max;
|
|
|
+ unsigned discover_local_ip;
|
|
|
+ size_t secretlen;
|
|
|
+ char secret[MAX_SECRET_LENGTH + 1];
|
|
|
+ unsigned char vector[AUTH_VECTOR_LEN];
|
|
|
+ uint8_t recv_buffer[BUFFER_LEN];
|
|
|
+ uint8_t send_buffer[BUFFER_LEN];
|
|
|
+ char our_addr_txt[50]; /* hold a text IP */
|
|
|
+ char auth_addr_txt[50]; /* hold a text IP */
|
|
|
+ uint8_t *attr;
|
|
|
+ int retries;
|
|
|
+ VALUE_PAIR *vp;
|
|
|
+ //struct pollfd pfd;
|
|
|
+ double start_time, timeout;
|
|
|
+
|
|
|
+
|
|
|
+ server_name = data->server;
|
|
|
+ if (server_name == NULL || server_name[0] == '\0')
|
|
|
+ return ERROR_RC;
|
|
|
+
|
|
|
+ if(data->secret != NULL)
|
|
|
+ {
|
|
|
+ //strlcpy(secret, data->secret, MAX_SECRET_LENGTH);
|
|
|
+ memcpy(secret, data->secret, MAX_SECRET_LENGTH);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rc_find_server_addr (rh, server_name, &auth_addr, secret, flags) != 0)
|
|
|
+ {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: unable to find server: %s", server_name);
|
|
|
+ return ERROR_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc_own_bind_addr(rh, &our_sockaddr);
|
|
|
+ discover_local_ip = 0;
|
|
|
+ if (our_sockaddr.ss_family == AF_INET) {
|
|
|
+ if (((struct sockaddr_in*)(&our_sockaddr))->sin_addr.s_addr == INADDR_ANY) {
|
|
|
+ discover_local_ip = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ DEBUG(LOG_ERR, "DEBUG: rc_send_server: creating socket to: %s", server_name);
|
|
|
+ if (discover_local_ip) {
|
|
|
+ result = rc_get_srcaddr(SA(&our_sockaddr), auth_addr->ai_addr);
|
|
|
+ if (result != 0) {
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: cannot figure our own address");
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sockfd = socket (our_sockaddr.ss_family, SOCK_DGRAM, 0);
|
|
|
+ if (sockfd < 0)
|
|
|
+ {
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: socket: %s", strerror(errno));
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (our_sockaddr.ss_family == AF_INET)
|
|
|
+ ((struct sockaddr_in*)&our_sockaddr)->sin_port = 0;
|
|
|
+ else
|
|
|
+ ((struct sockaddr_in6*)&our_sockaddr)->sin6_port = 0;
|
|
|
+
|
|
|
+ if (bind(sockfd, SA(&our_sockaddr), SS_LEN(&our_sockaddr)) < 0)
|
|
|
+ {
|
|
|
+ close (sockfd);
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: bind: %s: %s", server_name, strerror(errno));
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ retry_max = data->retries; /* Max. numbers to try for reply */
|
|
|
+ retries = 0; /* Init retry cnt for blocking call */
|
|
|
+
|
|
|
+ if (data->svc_port) {
|
|
|
+ if (our_sockaddr.ss_family == AF_INET)
|
|
|
+ ((struct sockaddr_in*)auth_addr->ai_addr)->sin_port = htons ((unsigned short) data->svc_port);
|
|
|
+ else
|
|
|
+ ((struct sockaddr_in6*)auth_addr->ai_addr)->sin6_port = htons ((unsigned short) data->svc_port);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Fill in NAS-IP-Address (if needed)
|
|
|
+ */
|
|
|
+ if (rc_avpair_get(data->send_pairs, PW_NAS_IP_ADDRESS, 0) == NULL &&
|
|
|
+ rc_avpair_get(data->send_pairs, PW_NAS_IPV6_ADDRESS, 0) == NULL) {
|
|
|
+ if (our_sockaddr.ss_family == AF_INET) {
|
|
|
+ uint32_t ip;
|
|
|
+ ip = *((uint32_t*)(&((struct sockaddr_in*)&our_sockaddr)->sin_addr));
|
|
|
+ ip = ntohl(ip);
|
|
|
+
|
|
|
+ rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IP_ADDRESS,
|
|
|
+ &ip, 0, 0);
|
|
|
+ } else {
|
|
|
+ void *p;
|
|
|
+ p = &((struct sockaddr_in6*)&our_sockaddr)->sin6_addr;
|
|
|
+
|
|
|
+ rc_avpair_add(rh, &(data->send_pairs), PW_NAS_IPV6_ADDRESS,
|
|
|
+ p, 0, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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, (unsigned 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ getnameinfo(SA(&our_sockaddr), SS_LEN(&our_sockaddr), NULL, 0, our_addr_txt, sizeof(our_addr_txt), NI_NUMERICHOST);
|
|
|
+ getnameinfo(auth_addr->ai_addr, auth_addr->ai_addrlen, NULL, 0, auth_addr_txt, sizeof(auth_addr_txt), NI_NUMERICHOST);
|
|
|
+
|
|
|
+ DEBUG(LOG_ERR, "DEBUG: local %s : 0, remote %s : %u\n",
|
|
|
+ our_addr_txt, auth_addr_txt, data->svc_port);
|
|
|
+
|
|
|
+ for (;;)
|
|
|
+ {
|
|
|
+ do {
|
|
|
+ result = sendto (sockfd, (char *) auth, (unsigned int)total_length,
|
|
|
+ (int) 0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen);
|
|
|
+ } while (result == -1 && errno == EINTR);
|
|
|
+ if (result == -1) {
|
|
|
+ rc_log(LOG_ERR, "%s: socket: %s", __FUNCTION__, strerror(errno));
|
|
|
+ }
|
|
|
+
|
|
|
+ pfd.fd = sockfd;
|
|
|
+ pfd.events = POLLIN;
|
|
|
+ pfd.revents = 0;
|
|
|
+ start_time = rc_getctime();
|
|
|
+ for (timeout = data->timeout; timeout > 0;
|
|
|
+ timeout -= rc_getctime() - start_time) {
|
|
|
+ result = poll(&pfd, 1, timeout * 1000);
|
|
|
+ if (result != -1 || errno != EINTR)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (result == -1)
|
|
|
+ {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: poll: %s", strerror(errno));
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+ close (sockfd);
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ if (result == 1 && (pfd.revents & POLLIN) != 0)
|
|
|
+ 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",
|
|
|
+ auth_addr_txt, data->svc_port);
|
|
|
+ close (sockfd);
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+ result = TIMEOUT_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ salen = auth_addr->ai_addrlen;
|
|
|
+ do {
|
|
|
+ length = recvfrom (sockfd, (char *) recv_buffer,
|
|
|
+ (int) sizeof (recv_buffer),
|
|
|
+ (int) 0, SA(auth_addr->ai_addr), &salen);
|
|
|
+ } while(length == -1 && errno == EINTR);
|
|
|
+
|
|
|
+ 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));
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ recv_auth = (AUTH_HDR *)recv_buffer;
|
|
|
+
|
|
|
+ if (length < AUTH_HDR_LEN || length < ntohs(recv_auth->length)) {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: reply is too short",
|
|
|
+ server_name, data->svc_port);
|
|
|
+ close(sockfd);
|
|
|
+ memset(secret, '\0', sizeof(secret));
|
|
|
+ result = ERROR_RC;
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If UDP is larger than RADIUS, shorten it to RADIUS.
|
|
|
+ */
|
|
|
+ if (length > ntohs(recv_auth->length)) length = ntohs(recv_auth->length);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Verify that it's a valid RADIUS packet before doing ANYTHING with it.
|
|
|
+ */
|
|
|
+ attr = recv_buffer + AUTH_HDR_LEN;
|
|
|
+ while (attr < (recv_buffer + length)) {
|
|
|
+ if (attr[0] == 0) {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute zero is invalid",
|
|
|
+ server_name, data->svc_port);
|
|
|
+ close(sockfd);
|
|
|
+ memset(secret, '\0', sizeof(secret));
|
|
|
+ return ERROR_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (attr[1] < 2) {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute length is too small",
|
|
|
+ server_name, data->svc_port);
|
|
|
+ close(sockfd);
|
|
|
+ memset(secret, '\0', sizeof(secret));
|
|
|
+ return ERROR_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((attr + attr[1]) > (recv_buffer + length)) {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: recvfrom: %s:%d: attribute overflows the packet",
|
|
|
+ server_name, data->svc_port);
|
|
|
+ close(sockfd);
|
|
|
+ memset(secret, '\0', sizeof(secret));
|
|
|
+ return ERROR_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ attr += attr[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);
|
|
|
+
|
|
|
+ length = ntohs(recv_auth->length) - AUTH_HDR_LEN;
|
|
|
+ if (length > 0) {
|
|
|
+ data->receive_pairs = rc_avpair_gen(rh, NULL, recv_auth->data,
|
|
|
+ length, 0);
|
|
|
+ } else {
|
|
|
+ data->receive_pairs = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ close (sockfd);
|
|
|
+ memset (secret, '\0', sizeof (secret));
|
|
|
+
|
|
|
+ if (result != OK_RC) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (msg) {
|
|
|
+ *msg = '\0';
|
|
|
+ pos = 0;
|
|
|
+ vp = data->receive_pairs;
|
|
|
+ while (vp)
|
|
|
+ {
|
|
|
+ if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE, 0)))
|
|
|
+ {
|
|
|
+ strappend(msg, PW_MAX_MSG_SIZE, &pos, vp->strvalue);
|
|
|
+ strappend(msg, PW_MAX_MSG_SIZE, &pos, "\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 if ((recv_auth->code == PW_ACCESS_REJECT) ||
|
|
|
+ (recv_auth->code == PW_PASSWORD_REJECT))
|
|
|
+ {
|
|
|
+ result = REJECT_RC;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ rc_log(LOG_ERR, "rc_send_server: received RADIUS server response neither ACCEPT nor REJECT, invalid");
|
|
|
+ result = BADRESP_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ cleanup:
|
|
|
+ if (auth_addr)
|
|
|
+ freeaddrinfo(auth_addr);
|
|
|
+
|
|
|
+ return result;
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+/** Verify items in returned packet
|
|
|
+ *
|
|
|
+ * @param auth a pointer to #AUTH_HDR.
|
|
|
+ * @param bufferlen the available buffer length.
|
|
|
+ * @param secret the secret used by the server.
|
|
|
+ * @param vector a random vector of %AUTH_VECTOR_LEN.
|
|
|
+ * @param seq_nbr a unique sequence number.
|
|
|
+ * @return %OK_RC upon success, %BADRESP_RC if anything looks funny.
|
|
|
+ */
|
|
|
+static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char const *secret, unsigned char const *vector, uint8_t seq_nbr)
|
|
|
+{
|
|
|
+ int secretlen;
|
|
|
+ int totallen;
|
|
|
+ unsigned char calc_digest[AUTH_VECTOR_LEN];
|
|
|
+ unsigned char reply_digest[AUTH_VECTOR_LEN];
|
|
|
+#ifdef DIGEST_DEBUG
|
|
|
+ uint8_t *ptr;
|
|
|
+#endif
|
|
|
+
|
|
|
+ totallen = ntohs (auth->length);
|
|
|
+ secretlen = (int)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);
|
|
|
+#ifdef DIGEST_DEBUG
|
|
|
+ rc_log(LOG_ERR, "Calculating digest on:");
|
|
|
+ for (ptr = (u_char *)auth; ptr < ((u_char *)auth) + totallen + secretlen; ptr += 32) {
|
|
|
+ char buf[65];
|
|
|
+ int i;
|
|
|
+
|
|
|
+ buf[0] = '\0';
|
|
|
+ for (i = 0; i < 32; i++) {
|
|
|
+ if (ptr + i >= ((u_char *)auth) + totallen + secretlen)
|
|
|
+ break;
|
|
|
+ sprintf(buf + i * 2, "%.2X", ptr[i]);
|
|
|
+ }
|
|
|
+ rc_log(LOG_ERR, " %s", buf);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ rc_md5_calc (calc_digest, (unsigned char *) auth, totallen + secretlen);
|
|
|
+#ifdef DIGEST_DEBUG
|
|
|
+ rc_log(LOG_ERR, "Calculated digest is:");
|
|
|
+ for (ptr = (u_char *)calc_digest; ptr < ((u_char *)calc_digest) + 16; ptr += 32) {
|
|
|
+ char buf[65];
|
|
|
+ int i;
|
|
|
+
|
|
|
+ buf[0] = '\0';
|
|
|
+ for (i = 0; i < 32; i++) {
|
|
|
+ if (ptr + i >= ((u_char *)calc_digest) + 16)
|
|
|
+ break;
|
|
|
+ sprintf(buf + i * 2, "%.2X", ptr[i]);
|
|
|
+ }
|
|
|
+ rc_log(LOG_ERR, " %s", buf);
|
|
|
+ }
|
|
|
+ rc_log(LOG_ERR, "Reply digest is:");
|
|
|
+ for (ptr = (u_char *)reply_digest; ptr < ((u_char *)reply_digest) + 16; ptr += 32) {
|
|
|
+ char buf[65];
|
|
|
+ int i;
|
|
|
+
|
|
|
+ buf[0] = '\0';
|
|
|
+ for (i = 0; i < 32; i++) {
|
|
|
+ if (ptr + i >= ((u_char *)reply_digest) + 16)
|
|
|
+ break;
|
|
|
+ sprintf(buf + i * 2, "%.2X", ptr[i]);
|
|
|
+ }
|
|
|
+ rc_log(LOG_ERR, " %s", buf);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ if (memcmp ((char *) reply_digest, (char *) calc_digest,
|
|
|
+ AUTH_VECTOR_LEN) != 0)
|
|
|
+ {
|
|
|
+ rc_log(LOG_ERR, "rc_check_reply: received invalid reply digest from RADIUS server");
|
|
|
+ return BADRESP_RC;
|
|
|
+ }
|
|
|
+
|
|
|
+ return OK_RC;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/** Generates a random vector of AUTH_VECTOR_LEN octets
|
|
|
+ *
|
|
|
+ * @param vector a buffer with at least %AUTH_VECTOR_LEN bytes.
|
|
|
+ */
|
|
|
+static void rc_random_vector (unsigned char *vector)
|
|
|
+{
|
|
|
+ int randno;
|
|
|
+ int i;
|
|
|
+#if defined(HAVE_GETENTROPY)
|
|
|
+ if (getentropy(vector, AUTH_VECTOR_LEN) >= 0) {
|
|
|
+ return;
|
|
|
+ } /* else fall through */
|
|
|
+#elif 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);
|
|
|
+ if (readcount >= 0) {
|
|
|
+ pos += readcount;
|
|
|
+ i -= readcount;
|
|
|
+ } else {
|
|
|
+ if (errno != EINTR && errno != EAGAIN)
|
|
|
+ goto fallback;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ close(fd);
|
|
|
+ return;
|
|
|
+ } /* else fall through */
|
|
|
+#endif
|
|
|
+ fallback:
|
|
|
+ for (i = 0; i < AUTH_VECTOR_LEN;)
|
|
|
+ {
|
|
|
+ randno = random ();
|
|
|
+ memcpy ((char *) vector, (char *) &randno, sizeof (int));
|
|
|
+ vector += sizeof (int);
|
|
|
+ i += sizeof (int);
|
|
|
+ }
|
|
|
+
|
|
|
+ return;
|
|
|
+}
|