| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549 | /* * $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 "freeradius-client.h"#include "util.h"#include "radius_user.h"#include "parameters.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 RECV_BUF_LEN    100static char recv_buffer[RECV_BUF_LEN];int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags){    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;        char            rcNetParams[20];    uint32_t        port;    uint8_t         tmpLen;        initFdsets(&sets);        memset(secret, 0, MAX_SECRET_LENGTH + 1);    GetRDSPasswordkStr(secret, &tmpLen);/*        	if(data->secret != NULL) {		//strlcpy(secret, data->secret, MAX_SECRET_LENGTH);		strlcpy(secret, "R04ekR4MP2", MAX_SECRET_LENGTH);    }*/    // Устанавливаем сетевые параметры    memset(rcNetParams, 0, 20);    GetRDSIpStr(rcNetParams, &tmpLen);            // IP radius server    memset(&ra, 0, sizeof(struct sockaddr_in));    ra.sin_family = AF_INET;    ra.sin_addr.s_addr = inet_addr(rcNetParams);        // port    memset(rcNetParams, 0, 20);    GetRDSPortStr(rcNetParams, &tmpLen);    port = atoi(rcNetParams);    ra.sin_port = htons(port);            socket = socket(PF_INET, SOCK_DGRAM, 0);    if ( socket < 0 )    {        //printf("socket call failed");        return -1;    }            // Build a request  (PW_ACCESS_REQUEST)    auth = (AUTH_HDR *) msg;	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(rcNetParams, 0, 20);    GetIpStr(rcNetParams, &tmpLen);        memset(&sa, 0, sizeof(struct sockaddr_in));    sa.sin_family = AF_INET;    sa.sin_addr.s_addr = inet_addr(rcNetParams);    sa.sin_port = htons(port);        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 -1;    }           sendLen = sendto(socket, (char*)auth, total_length, 0, (struct sockaddr*)&ra, sizeof(ra));    if(sendLen < 0)    {        //printf("send failed\n");        close(socket);        return NET_ERR_RC;    }       // Подготовка буфера для приема    memset(recv_buffer, 0, RECV_BUF_LEN);        // Получение ответа, select    if (!recvSelect(&sets, &socket, 2000)) {        //printf("SOCK recv timeout!\r\n");        close(socket);        return NET_ERR_RC;    }        // Данные можно принимать    socklen_t sl = sizeof(sa);    recvLen = recvfrom(socket, recv_buffer, RECV_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 NET_ERR_RC;	}        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 NET_ERR_RC;		}		if (attr[1] < 2) {            //printf("radius_server: attribute length is too small\r\n");            close(socket);            return NET_ERR_RC;		}		if ((attr + attr[1]) > (recv_buffer + recvLen)) {            //printf("radius_server: attribute overflows the packet\r\n");            close(socket);            return NET_ERR_RC;		}		attr += attr[1];	}            result = rc_check_reply(recv_auth, RECV_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 = RC_GetAccessRights(recv_buffer);	}	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("\r\nRadius server end communication\r\n");    close(socket);    return result;}/** 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;}
 |