Home | History | Annotate | Download | only in radius
      1 /*
      2  * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
      3  *
      4  * Copyright (C) 1995,1996,1997 Lars Fenneberg
      5  *
      6  * Copyright 1992 Livingston Enterprises, Inc.
      7  *
      8  * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
      9  * and Merit Network, Inc. All Rights Reserved
     10  *
     11  * See the file COPYRIGHT for the respective terms and conditions.
     12  * If the file is missing contact me at lf (at) elemental.net
     13  * and I'll send you a copy.
     14  *
     15  */
     16 
     17 #include <includes.h>
     18 #include <radiusclient.h>
     19 #include <pathnames.h>
     20 
     21 static void rc_random_vector (unsigned char *);
     22 static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char);
     23 
     24 /*
     25  * Function: rc_pack_list
     26  *
     27  * Purpose: Packs an attribute value pair list into a buffer.
     28  *
     29  * Returns: Number of octets packed.
     30  *
     31  */
     32 
     33 static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
     34 {
     35     int             length, i, pc, secretlen, padded_length;
     36     int             total_length = 0;
     37     UINT4           lvalue;
     38     unsigned char   passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
     39     unsigned char   md5buf[256];
     40     unsigned char   *buf, *vector, *lenptr;
     41 
     42     buf = auth->data;
     43 
     44     while (vp != (VALUE_PAIR *) NULL)
     45 	{
     46 
     47 	    if (vp->vendorcode != VENDOR_NONE) {
     48 		*buf++ = PW_VENDOR_SPECIFIC;
     49 
     50 		/* Place-holder for where to put length */
     51 		lenptr = buf++;
     52 
     53 		/* Insert vendor code */
     54 		*buf++ = 0;
     55 		*buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255;
     56 		*buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255;
     57 		*buf++ = ((unsigned int) vp->vendorcode) & 255;
     58 
     59 		/* Insert vendor-type */
     60 		*buf++ = vp->attribute;
     61 
     62 		/* Insert value */
     63 		switch(vp->type) {
     64 		case PW_TYPE_STRING:
     65 		    length = vp->lvalue;
     66 		    *lenptr = length + 8;
     67 		    *buf++ = length+2;
     68 		    memcpy(buf, vp->strvalue, (size_t) length);
     69 		    buf += length;
     70 		    total_length += length+8;
     71 		    break;
     72 		case PW_TYPE_INTEGER:
     73 		case PW_TYPE_IPADDR:
     74 		    length = sizeof(UINT4);
     75 		    *lenptr = length + 8;
     76 		    *buf++ = length+2;
     77 		    lvalue = htonl(vp->lvalue);
     78 		    memcpy(buf, (char *) &lvalue, sizeof(UINT4));
     79 		    buf += length;
     80 		    total_length += length+8;
     81 		    break;
     82 		default:
     83 		    break;
     84 		}
     85 	    } else {
     86 		*buf++ = vp->attribute;
     87 		switch (vp->attribute) {
     88 		case PW_USER_PASSWORD:
     89 
     90 		    /* Encrypt the password */
     91 
     92 		    /* Chop off password at AUTH_PASS_LEN */
     93 		    length = vp->lvalue;
     94 		    if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN;
     95 
     96 		    /* Calculate the padded length */
     97 		    padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1);
     98 
     99 		    /* Record the attribute length */
    100 		    *buf++ = padded_length + 2;
    101 
    102 		    /* Pad the password with zeros */
    103 		    memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
    104 		    memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
    105 
    106 		    secretlen = strlen (secret);
    107 		    vector = (char *)auth->vector;
    108 		    for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) {
    109 			/* Calculate the MD5 digest*/
    110 			strcpy ((char *) md5buf, secret);
    111 			memcpy ((char *) md5buf + secretlen, vector,
    112 				AUTH_VECTOR_LEN);
    113 			rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
    114 
    115 			/* Remeber the start of the digest */
    116 			vector = buf;
    117 
    118 			/* Xor the password into the MD5 digest */
    119 			for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) {
    120 			    *buf++ ^= passbuf[pc];
    121 			}
    122 		    }
    123 
    124 		    total_length += padded_length + 2;
    125 
    126 		    break;
    127 #if 0
    128 		case PW_CHAP_PASSWORD:
    129 
    130 		    *buf++ = CHAP_VALUE_LENGTH + 2;
    131 
    132 		    /* Encrypt the Password */
    133 		    length = vp->lvalue;
    134 		    if (length > CHAP_VALUE_LENGTH) {
    135 			length = CHAP_VALUE_LENGTH;
    136 		    }
    137 		    memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH);
    138 		    memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
    139 
    140 		    /* Calculate the MD5 Digest */
    141 		    secretlen = strlen (secret);
    142 		    strcpy ((char *) md5buf, secret);
    143 		    memcpy ((char *) md5buf + secretlen, (char *) auth->vector,
    144 			    AUTH_VECTOR_LEN);
    145 		    rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
    146 
    147 		    /* Xor the password into the MD5 digest */
    148 		    for (i = 0; i < CHAP_VALUE_LENGTH; i++) {
    149 			*buf++ ^= passbuf[i];
    150 		    }
    151 		    total_length += CHAP_VALUE_LENGTH + 2;
    152 
    153 		    break;
    154 #endif
    155 		default:
    156 		    switch (vp->type) {
    157 		    case PW_TYPE_STRING:
    158 			length = vp->lvalue;
    159 			*buf++ = length + 2;
    160 			memcpy (buf, vp->strvalue, (size_t) length);
    161 			buf += length;
    162 			total_length += length + 2;
    163 			break;
    164 
    165 		    case PW_TYPE_INTEGER:
    166 		    case PW_TYPE_IPADDR:
    167 			*buf++ = sizeof (UINT4) + 2;
    168 			lvalue = htonl (vp->lvalue);
    169 			memcpy (buf, (char *) &lvalue, sizeof (UINT4));
    170 			buf += sizeof (UINT4);
    171 			total_length += sizeof (UINT4) + 2;
    172 			break;
    173 
    174 		    default:
    175 			break;
    176 		    }
    177 		    break;
    178 		}
    179 	    }
    180 	    vp = vp->next;
    181 	}
    182     return total_length;
    183 }
    184 
    185 /*
    186  * Function: rc_send_server
    187  *
    188  * Purpose: send a request to a RADIUS server and wait for the reply
    189  *
    190  */
    191 
    192 int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info)
    193 {
    194 	int             sockfd;
    195 	struct sockaddr salocal;
    196 	struct sockaddr saremote;
    197 	struct sockaddr_in *sin;
    198 	struct timeval  authtime;
    199 	fd_set          readfds;
    200 	AUTH_HDR       *auth, *recv_auth;
    201 	UINT4           auth_ipaddr;
    202 	char           *server_name;	/* Name of server to query */
    203 	int             salen;
    204 	int             result;
    205 	int             total_length;
    206 	int             length;
    207 	int             retry_max;
    208 	int		secretlen;
    209 	char            secret[MAX_SECRET_LENGTH + 1];
    210 	unsigned char   vector[AUTH_VECTOR_LEN];
    211 	char            recv_buffer[BUFFER_LEN];
    212 	char            send_buffer[BUFFER_LEN];
    213 	int		retries;
    214 	VALUE_PAIR	*vp;
    215 
    216 	server_name = data->server;
    217 	if (server_name == (char *) NULL || server_name[0] == '\0')
    218 		return (ERROR_RC);
    219 
    220 	if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \
    221 	    (vp->lvalue == PW_ADMINISTRATIVE))
    222 	{
    223 		strcpy(secret, MGMT_POLL_SECRET);
    224 		if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0)
    225 			return (ERROR_RC);
    226 	}
    227 	else
    228 	{
    229 		if (rc_find_server (server_name, &auth_ipaddr, secret) != 0)
    230 		{
    231 			return (ERROR_RC);
    232 		}
    233 	}
    234 
    235 	sockfd = socket (AF_INET, SOCK_DGRAM, 0);
    236 	if (sockfd < 0)
    237 	{
    238 		memset (secret, '\0', sizeof (secret));
    239 		error("rc_send_server: socket: %s", strerror(errno));
    240 		return (ERROR_RC);
    241 	}
    242 
    243 	length = sizeof (salocal);
    244 	sin = (struct sockaddr_in *) & salocal;
    245 	memset ((char *) sin, '\0', (size_t) length);
    246 	sin->sin_family = AF_INET;
    247 	sin->sin_addr.s_addr = htonl(INADDR_ANY);
    248 	sin->sin_port = htons ((unsigned short) 0);
    249 	if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
    250 		   getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
    251 	{
    252 		close (sockfd);
    253 		memset (secret, '\0', sizeof (secret));
    254 		error("rc_send_server: bind: %s: %m", server_name);
    255 		return (ERROR_RC);
    256 	}
    257 
    258 	retry_max = data->retries;	/* Max. numbers to try for reply */
    259 	retries = 0;			/* Init retry cnt for blocking call */
    260 
    261 	/* Build a request */
    262 	auth = (AUTH_HDR *) send_buffer;
    263 	auth->code = data->code;
    264 	auth->id = data->seq_nbr;
    265 
    266 	if (data->code == PW_ACCOUNTING_REQUEST)
    267 	{
    268 		total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
    269 
    270 		auth->length = htons ((unsigned short) total_length);
    271 
    272 		memset((char *) auth->vector, 0, AUTH_VECTOR_LEN);
    273 		secretlen = strlen (secret);
    274 		memcpy ((char *) auth + total_length, secret, secretlen);
    275 		rc_md5_calc (vector, (char *) auth, total_length + secretlen);
    276 		memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
    277 	}
    278 	else
    279 	{
    280 		rc_random_vector (vector);
    281 		memcpy (auth->vector, vector, AUTH_VECTOR_LEN);
    282 
    283 		total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
    284 
    285 		auth->length = htons ((unsigned short) total_length);
    286 	}
    287 
    288 	sin = (struct sockaddr_in *) & saremote;
    289 	memset ((char *) sin, '\0', sizeof (saremote));
    290 	sin->sin_family = AF_INET;
    291 	sin->sin_addr.s_addr = htonl (auth_ipaddr);
    292 	sin->sin_port = htons ((unsigned short) data->svc_port);
    293 
    294 	for (;;)
    295 	{
    296 		sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0,
    297 			(struct sockaddr *) sin, sizeof (struct sockaddr_in));
    298 
    299 		authtime.tv_usec = 0L;
    300 		authtime.tv_sec = (long) data->timeout;
    301 		FD_ZERO (&readfds);
    302 		FD_SET (sockfd, &readfds);
    303 		if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
    304 		{
    305 			if (errno == EINTR)
    306 				continue;
    307 			error("rc_send_server: select: %m");
    308 			memset (secret, '\0', sizeof (secret));
    309 			close (sockfd);
    310 			return (ERROR_RC);
    311 		}
    312 		if (FD_ISSET (sockfd, &readfds))
    313 			break;
    314 
    315 		/*
    316 		 * Timed out waiting for response.  Retry "retry_max" times
    317 		 * before giving up.  If retry_max = 0, don't retry at all.
    318 		 */
    319 		if (++retries >= retry_max)
    320 		{
    321 			error("rc_send_server: no reply from RADIUS server %s:%u",
    322 			      rc_ip_hostname (auth_ipaddr), data->svc_port);
    323 			close (sockfd);
    324 			memset (secret, '\0', sizeof (secret));
    325 			return (TIMEOUT_RC);
    326 		}
    327 	}
    328 	salen = sizeof (saremote);
    329 	length = recvfrom (sockfd, (char *) recv_buffer,
    330 			   (int) sizeof (recv_buffer),
    331 			   (int) 0, &saremote, &salen);
    332 
    333 	if (length <= 0)
    334 	{
    335 		error("rc_send_server: recvfrom: %s:%d: %m", server_name,\
    336 		      data->svc_port);
    337 		close (sockfd);
    338 		memset (secret, '\0', sizeof (secret));
    339 		return (ERROR_RC);
    340 	}
    341 
    342 	recv_auth = (AUTH_HDR *)recv_buffer;
    343 
    344 	result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);
    345 
    346 	data->receive_pairs = rc_avpair_gen(recv_auth);
    347 
    348 	close (sockfd);
    349 	if (info)
    350 	{
    351 		memcpy(info->secret, secret, sizeof(info->secret));
    352 		memcpy(info->request_vector, vector,
    353 		       sizeof(info->request_vector));
    354 	}
    355 	memset (secret, '\0', sizeof (secret));
    356 
    357 	if (result != OK_RC) return (result);
    358 
    359 	*msg = '\0';
    360 	vp = data->receive_pairs;
    361 	while (vp)
    362 	{
    363 		if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE)))
    364 		{
    365 			strcat(msg, vp->strvalue);
    366 			strcat(msg, "\n");
    367 			vp = vp->next;
    368 		}
    369 	}
    370 
    371 	if ((recv_auth->code == PW_ACCESS_ACCEPT) ||
    372 		(recv_auth->code == PW_PASSWORD_ACK) ||
    373 		(recv_auth->code == PW_ACCOUNTING_RESPONSE))
    374 	{
    375 		result = OK_RC;
    376 	}
    377 	else
    378 	{
    379 		result = BADRESP_RC;
    380 	}
    381 
    382 	return (result);
    383 }
    384 
    385 /*
    386  * Function: rc_check_reply
    387  *
    388  * Purpose: verify items in returned packet.
    389  *
    390  * Returns:	OK_RC       -- upon success,
    391  *		BADRESP_RC  -- if anything looks funny.
    392  *
    393  */
    394 
    395 static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret,
    396 			   unsigned char *vector, unsigned char seq_nbr)
    397 {
    398 	int             secretlen;
    399 	int             totallen;
    400 	unsigned char   calc_digest[AUTH_VECTOR_LEN];
    401 	unsigned char   reply_digest[AUTH_VECTOR_LEN];
    402 
    403 	totallen = ntohs (auth->length);
    404 
    405 	secretlen = strlen (secret);
    406 
    407 	/* Do sanity checks on packet length */
    408 	if ((totallen < 20) || (totallen > 4096))
    409 	{
    410 		error("rc_check_reply: received RADIUS server response with invalid length");
    411 		return (BADRESP_RC);
    412 	}
    413 
    414 	/* Verify buffer space, should never trigger with current buffer size and check above */
    415 	if ((totallen + secretlen) > bufferlen)
    416 	{
    417 		error("rc_check_reply: not enough buffer space to verify RADIUS server response");
    418 		return (BADRESP_RC);
    419 	}
    420 	/* Verify that id (seq. number) matches what we sent */
    421 	if (auth->id != seq_nbr)
    422 	{
    423 		error("rc_check_reply: received non-matching id in RADIUS server response");
    424 		return (BADRESP_RC);
    425 	}
    426 
    427 	/* Verify the reply digest */
    428 	memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
    429 	memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
    430 	memcpy ((char *) auth + totallen, secret, secretlen);
    431 	rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen);
    432 
    433 #ifdef DIGEST_DEBUG
    434 	{
    435 		int i;
    436 
    437 		fputs("reply_digest: ", stderr);
    438 		for (i = 0; i < AUTH_VECTOR_LEN; i++)
    439 		{
    440 			fprintf(stderr,"%.2x ", (int) reply_digest[i]);
    441 		}
    442 		fputs("\ncalc_digest:  ", stderr);
    443 		for (i = 0; i < AUTH_VECTOR_LEN; i++)
    444 		{
    445 			fprintf(stderr,"%.2x ", (int) calc_digest[i]);
    446 		}
    447 		fputs("\n", stderr);
    448 	}
    449 #endif
    450 
    451 	if (memcmp ((char *) reply_digest, (char *) calc_digest,
    452 		    AUTH_VECTOR_LEN) != 0)
    453 	{
    454 #ifdef RADIUS_116
    455 		/* the original Livingston radiusd v1.16 seems to have
    456 		   a bug in digest calculation with accounting requests,
    457 		   authentication request are ok. i looked at the code
    458 		   but couldn't find any bugs. any help to get this
    459 		   kludge out are welcome. preferably i want to
    460 		   reproduce the calculation bug here to be compatible
    461 		   to stock Livingston radiusd v1.16.	-lf, 03/14/96
    462 		 */
    463 		if (auth->code == PW_ACCOUNTING_RESPONSE)
    464 			return (OK_RC);
    465 #endif
    466 		error("rc_check_reply: received invalid reply digest from RADIUS server");
    467 		return (BADRESP_RC);
    468 	}
    469 
    470 	return (OK_RC);
    471 
    472 }
    473 
    474 /*
    475  * Function: rc_random_vector
    476  *
    477  * Purpose: generates a random vector of AUTH_VECTOR_LEN octets.
    478  *
    479  * Returns: the vector (call by reference)
    480  *
    481  */
    482 
    483 static void rc_random_vector (unsigned char *vector)
    484 {
    485 	int             randno;
    486 	int             i;
    487 	int		fd;
    488 
    489 /* well, I added this to increase the security for user passwords.
    490    we use /dev/urandom here, as /dev/random might block and we don't
    491    need that much randomness. BTW, great idea, Ted!     -lf, 03/18/95	*/
    492 
    493 	if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0)
    494 	{
    495 		unsigned char *pos;
    496 		int readcount;
    497 
    498 		i = AUTH_VECTOR_LEN;
    499 		pos = vector;
    500 		while (i > 0)
    501 		{
    502 			readcount = read(fd, (char *)pos, i);
    503 			pos += readcount;
    504 			i -= readcount;
    505 		}
    506 
    507 		close(fd);
    508 		return;
    509 	} /* else fall through */
    510 
    511 	for (i = 0; i < AUTH_VECTOR_LEN;)
    512 	{
    513 		randno = magic();
    514 		memcpy ((char *) vector, (char *) &randno, sizeof (int));
    515 		vector += sizeof (int);
    516 		i += sizeof (int);
    517 	}
    518 
    519 	return;
    520 }
    521