123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /*
- * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus 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 <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, *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, REQUEST_INFO *info)
- {
- 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));
- error("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(rc_own_bind_ipaddress());
- 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));
- error("rc_send_server: bind: %s: %m", server_name);
- 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 (auth->vector, 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;
- error("rc_send_server: select: %m");
- 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)
- {
- error("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)
- {
- error("rc_send_server: recvfrom: %s:%d: %m", server_name,\
- data->svc_port);
- 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);
- if (info)
- {
- memcpy(info->secret, secret, sizeof(info->secret));
- memcpy(info->request_vector, vector,
- sizeof(info->request_vector));
- }
- 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))
- {
- error("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)
- {
- error("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)
- {
- error("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
- error("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;
- 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 */
- for (i = 0; i < AUTH_VECTOR_LEN;)
- {
- randno = magic();
- memcpy ((char *) vector, (char *) &randno, sizeof (int));
- vector += sizeof (int);
- i += sizeof (int);
- }
- return;
- }
|