aboutsummaryrefslogtreecommitdiff
path: root/lib/libradius/radlib.c
diff options
context:
space:
mode:
authorAlexander Motin <mav@FreeBSD.org>2009-09-11 11:42:56 +0000
committerAlexander Motin <mav@FreeBSD.org>2009-09-11 11:42:56 +0000
commit3fc0b61c51969c20551372e9c4adb4943e96dbd3 (patch)
tree62d154acac53838b9afff7d63c697fe60f8f19bc /lib/libradius/radlib.c
parent493d6f54bc85aee304350c1a063994471d7c2d2c (diff)
downloadsrc-3fc0b61c51969c20551372e9c4adb4943e96dbd3.tar.gz
src-3fc0b61c51969c20551372e9c4adb4943e96dbd3.zip
Add simple embedded RADIUS server support to libradius, by extending existing
API, keeping backward compatibility. First consumer for this functionality is going to become forthcoming MPD-5.4, supporting CoA and DR of RFC 3576: Dynamic Authorization Extensions to RADIUS. MFC after: 1 month
Notes
Notes: svn path=/head/; revision=197086
Diffstat (limited to 'lib/libradius/radlib.c')
-rw-r--r--lib/libradius/radlib.c346
1 files changed, 269 insertions, 77 deletions
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c
index b21447ef9c41..8faaf7e12a60 100644
--- a/lib/libradius/radlib.c
+++ b/lib/libradius/radlib.c
@@ -103,7 +103,7 @@ insert_scrambled_password(struct rad_handle *h, int srv)
srvp = &h->servers[srv];
padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
- memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
+ memcpy(md5, &h->out[POS_AUTH], LEN_AUTH);
for (pos = 0; pos < padded_len; pos += 16) {
int i;
@@ -120,49 +120,55 @@ insert_scrambled_password(struct rad_handle *h, int srv)
* in calculating the scrambler for next time.
*/
for (i = 0; i < 16; i++)
- h->request[h->pass_pos + pos + i] =
+ h->out[h->pass_pos + pos + i] =
md5[i] ^= h->pass[pos + i];
}
}
static void
-insert_request_authenticator(struct rad_handle *h, int srv)
+insert_request_authenticator(struct rad_handle *h, int resp)
{
MD5_CTX ctx;
const struct rad_server *srvp;
- srvp = &h->servers[srv];
+ srvp = &h->servers[h->srv];
/* Create the request authenticator */
MD5Init(&ctx);
- MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
- MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
- MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
+ MD5Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE);
+ if (resp)
+ MD5Update(&ctx, &h->in[POS_AUTH], LEN_AUTH);
+ else
+ MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
+ MD5Update(&ctx, &h->out[POS_ATTRS], h->out_len - POS_ATTRS);
MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
- MD5Final(&h->request[POS_AUTH], &ctx);
+ MD5Final(&h->out[POS_AUTH], &ctx);
}
static void
-insert_message_authenticator(struct rad_handle *h, int srv)
+insert_message_authenticator(struct rad_handle *h, int resp)
{
#ifdef WITH_SSL
u_char md[EVP_MAX_MD_SIZE];
u_int md_len;
const struct rad_server *srvp;
HMAC_CTX ctx;
- srvp = &h->servers[srv];
+ srvp = &h->servers[h->srv];
if (h->authentic_pos != 0) {
HMAC_CTX_init(&ctx);
HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5());
- HMAC_Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
- HMAC_Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
- HMAC_Update(&ctx, &h->request[POS_ATTRS],
- h->req_len - POS_ATTRS);
+ HMAC_Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE);
+ if (resp)
+ HMAC_Update(&ctx, &h->in[POS_AUTH], LEN_AUTH);
+ else
+ HMAC_Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
+ HMAC_Update(&ctx, &h->out[POS_ATTRS],
+ h->out_len - POS_ATTRS);
HMAC_Final(&ctx, md, &md_len);
HMAC_CTX_cleanup(&ctx);
HMAC_cleanup(&ctx);
- memcpy(&h->request[h->authentic_pos + 2], md, md_len);
+ memcpy(&h->out[h->authentic_pos + 2], md, md_len);
}
#endif
}
@@ -195,20 +201,20 @@ is_valid_response(struct rad_handle *h, int srv,
return 0;
/* Check the message length */
- if (h->resp_len < POS_ATTRS)
+ if (h->in_len < POS_ATTRS)
return 0;
- len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
- if (len > h->resp_len)
+ len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1];
+ if (len > h->in_len)
return 0;
/* Check the response authenticator */
MD5Init(&ctx);
- MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
- MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
- MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
+ MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE);
+ MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
+ MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS);
MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
MD5Final(md5, &ctx);
- if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
+ if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0)
return 0;
#ifdef WITH_SSL
@@ -216,42 +222,111 @@ is_valid_response(struct rad_handle *h, int srv,
* For non accounting responses check the message authenticator,
* if any.
*/
- if (h->response[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
+ if (h->in[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
- memcpy(resp, h->response, MSGSIZE);
+ memcpy(resp, h->in, MSGSIZE);
pos = POS_ATTRS;
/* Search and verify the Message-Authenticator */
while (pos < len - 2) {
- if (h->response[pos] == RAD_MESSAGE_AUTHENTIC) {
+ if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) {
/* zero fill the Message-Authenticator */
memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
HMAC_CTX_init(&hctx);
HMAC_Init(&hctx, srvp->secret,
strlen(srvp->secret), EVP_md5());
- HMAC_Update(&hctx, &h->response[POS_CODE],
+ HMAC_Update(&hctx, &h->in[POS_CODE],
POS_AUTH - POS_CODE);
- HMAC_Update(&hctx, &h->request[POS_AUTH],
+ HMAC_Update(&hctx, &h->out[POS_AUTH],
LEN_AUTH);
HMAC_Update(&hctx, &resp[POS_ATTRS],
- h->resp_len - POS_ATTRS);
+ h->in_len - POS_ATTRS);
HMAC_Final(&hctx, md, &md_len);
HMAC_CTX_cleanup(&hctx);
HMAC_cleanup(&hctx);
- if (memcmp(md, &h->response[pos + 2],
+ if (memcmp(md, &h->in[pos + 2],
MD5_DIGEST_LENGTH) != 0)
return 0;
break;
}
- pos += h->response[pos + 1];
+ pos += h->in[pos + 1];
}
}
#endif
return 1;
}
+/*
+ * Return true if the current request is valid for the specified server.
+ */
+static int
+is_valid_request(struct rad_handle *h)
+{
+ MD5_CTX ctx;
+ unsigned char md5[MD5_DIGEST_LENGTH];
+ const struct rad_server *srvp;
+ int len;
+#ifdef WITH_SSL
+ HMAC_CTX hctx;
+ u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
+ u_int md_len;
+ int pos;
+#endif
+
+ srvp = &h->servers[h->srv];
+
+ /* Check the message length */
+ if (h->in_len < POS_ATTRS)
+ return (0);
+ len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1];
+ if (len > h->in_len)
+ return (0);
+
+ if (h->in[POS_CODE] != RAD_ACCESS_REQUEST) {
+ uint32_t zeroes[4] = { 0, 0, 0, 0 };
+ /* Check the request authenticator */
+ MD5Init(&ctx);
+ MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE);
+ MD5Update(&ctx, zeroes, LEN_AUTH);
+ MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS);
+ MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
+ MD5Final(md5, &ctx);
+ if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0)
+ return (0);
+ }
+
+#ifdef WITH_SSL
+ /* Search and verify the Message-Authenticator */
+ pos = POS_ATTRS;
+ while (pos < len - 2) {
+ if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) {
+ memcpy(resp, h->in, MSGSIZE);
+ /* zero fill the Request-Authenticator */
+ if (h->in[POS_CODE] != RAD_ACCESS_REQUEST)
+ memset(&resp[POS_AUTH], 0, LEN_AUTH);
+ /* zero fill the Message-Authenticator */
+ memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
+
+ HMAC_CTX_init(&hctx);
+ HMAC_Init(&hctx, srvp->secret,
+ strlen(srvp->secret), EVP_md5());
+ HMAC_Update(&hctx, resp, h->in_len);
+ HMAC_Final(&hctx, md, &md_len);
+ HMAC_CTX_cleanup(&hctx);
+ HMAC_cleanup(&hctx);
+ if (memcmp(md, &h->in[pos + 2],
+ MD5_DIGEST_LENGTH) != 0)
+ return (0);
+ break;
+ }
+ pos += h->in[pos + 1];
+ }
+#endif
+ return (1);
+}
+
static int
put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
@@ -273,7 +348,7 @@ put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
*/
clear_password(h);
put_raw_attr(h, type, h->pass, padded_len);
- h->pass_pos = h->req_len - padded_len;
+ h->pass_pos = h->out_len - padded_len;
/* Save the cleartext password, padded as necessary */
memcpy(h->pass, value, len);
@@ -289,14 +364,14 @@ put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
generr(h, "Attribute too long");
return -1;
}
- if (h->req_len + 2 + len > MSGSIZE) {
+ if (h->out_len + 2 + len > MSGSIZE) {
generr(h, "Maximum message length exceeded");
return -1;
}
- h->request[h->req_len++] = type;
- h->request[h->req_len++] = len + 2;
- memcpy(&h->request[h->req_len], value, len);
- h->req_len += len;
+ h->out[h->out_len++] = type;
+ h->out[h->out_len++] = len + 2;
+ memcpy(&h->out[h->out_len], value, len);
+ h->out_len += len;
return 0;
}
@@ -523,22 +598,26 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
{
int n;
+ if (h->type == RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
+ }
if (selected) {
struct sockaddr_in from;
socklen_t fromlen;
fromlen = sizeof from;
- h->resp_len = recvfrom(h->fd, h->response,
+ h->in_len = recvfrom(h->fd, h->in,
MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
- if (h->resp_len == -1) {
+ if (h->in_len == -1) {
generr(h, "recvfrom: %s", strerror(errno));
return -1;
}
if (is_valid_response(h, h->srv, &from)) {
- h->resp_len = h->response[POS_LENGTH] << 8 |
- h->response[POS_LENGTH+1];
- h->resp_pos = POS_ATTRS;
- return h->response[POS_CODE];
+ h->in_len = h->in[POS_LENGTH] << 8 |
+ h->in[POS_LENGTH+1];
+ h->in_pos = POS_ATTRS;
+ return h->in[POS_CODE];
}
}
@@ -556,21 +635,22 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
if (++h->srv >= h->num_servers)
h->srv = 0;
- if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
- /* Insert the request authenticator into the request */
- insert_request_authenticator(h, h->srv);
- else
+ if (h->out[POS_CODE] == RAD_ACCESS_REQUEST) {
/* Insert the scrambled password into the request */
if (h->pass_pos != 0)
insert_scrambled_password(h, h->srv);
-
- insert_message_authenticator(h, h->srv);
+ }
+ insert_message_authenticator(h, 0);
+ if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
+ /* Insert the request authenticator into the request */
+ insert_request_authenticator(h, h->srv);
+ }
/* Send the request */
- n = sendto(h->fd, h->request, h->req_len, 0,
+ n = sendto(h->fd, h->out, h->out_len, 0,
(const struct sockaddr *)&h->servers[h->srv].addr,
sizeof h->servers[h->srv].addr);
- if (n != h->req_len) {
+ if (n != h->out_len) {
if (n == -1)
generr(h, "sendto: %s", strerror(errno));
else
@@ -588,22 +668,117 @@ rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
}
int
+rad_receive_request(struct rad_handle *h)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen;
+ int n;
+
+ if (h->type != RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
+ }
+ h->srv = -1;
+ fromlen = sizeof(from);
+ h->in_len = recvfrom(h->fd, h->in,
+ MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
+ if (h->in_len == -1) {
+ generr(h, "recvfrom: %s", strerror(errno));
+ return (-1);
+ }
+ for (n = 0; n < h->num_servers; n++) {
+ if (h->servers[n].addr.sin_addr.s_addr == from.sin_addr.s_addr) {
+ h->servers[n].addr.sin_port = from.sin_port;
+ h->srv = n;
+ break;
+ }
+ }
+ if (h->srv == -1)
+ return (-2);
+ if (is_valid_request(h)) {
+ h->in_len = h->in[POS_LENGTH] << 8 |
+ h->in[POS_LENGTH+1];
+ h->in_pos = POS_ATTRS;
+ return (h->in[POS_CODE]);
+ }
+ return (-3);
+}
+
+int
+rad_send_response(struct rad_handle *h)
+{
+ int n;
+
+ if (h->type != RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
+ }
+ /* Fill in the length field in the message */
+ h->out[POS_LENGTH] = h->out_len >> 8;
+ h->out[POS_LENGTH+1] = h->out_len;
+
+ insert_message_authenticator(h,
+ (h->in[POS_CODE] == RAD_ACCESS_REQUEST) ? 1 : 0);
+ insert_request_authenticator(h, 1);
+
+ /* Send the request */
+ n = sendto(h->fd, h->out, h->out_len, 0,
+ (const struct sockaddr *)&h->servers[h->srv].addr,
+ sizeof h->servers[h->srv].addr);
+ if (n != h->out_len) {
+ if (n == -1)
+ generr(h, "sendto: %s", strerror(errno));
+ else
+ generr(h, "sendto: short write");
+ return -1;
+ }
+
+ return 0;
+}
+
+int
rad_create_request(struct rad_handle *h, int code)
{
int i;
- h->request[POS_CODE] = code;
- h->request[POS_IDENT] = ++h->ident;
- /* Create a random authenticator */
- for (i = 0; i < LEN_AUTH; i += 2) {
- long r;
- r = random();
- h->request[POS_AUTH+i] = (u_char)r;
- h->request[POS_AUTH+i+1] = (u_char)(r >> 8);
+ if (h->type == RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
+ }
+ h->out[POS_CODE] = code;
+ h->out[POS_IDENT] = ++h->ident;
+ if (code == RAD_ACCESS_REQUEST) {
+ /* Create a random authenticator */
+ for (i = 0; i < LEN_AUTH; i += 2) {
+ long r;
+ r = random();
+ h->out[POS_AUTH+i] = (u_char)r;
+ h->out[POS_AUTH+i+1] = (u_char)(r >> 8);
+ }
+ } else
+ memset(&h->out[POS_AUTH], 0, LEN_AUTH);
+ h->out_len = POS_ATTRS;
+ clear_password(h);
+ h->authentic_pos = 0;
+ h->out_created = 1;
+ return 0;
+}
+
+int
+rad_create_response(struct rad_handle *h, int code)
+{
+
+ if (h->type != RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
}
- h->req_len = POS_ATTRS;
+ h->out[POS_CODE] = code;
+ h->out[POS_IDENT] = h->in[POS_IDENT];
+ memset(&h->out[POS_AUTH], 0, LEN_AUTH);
+ h->out_len = POS_ATTRS;
clear_password(h);
- h->request_created = 1;
+ h->authentic_pos = 0;
+ h->out_created = 1;
return 0;
}
@@ -647,20 +822,20 @@ rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
{
int type;
- if (h->resp_pos >= h->resp_len)
+ if (h->in_pos >= h->in_len)
return 0;
- if (h->resp_pos + 2 > h->resp_len) {
+ if (h->in_pos + 2 > h->in_len) {
generr(h, "Malformed attribute in response");
return -1;
}
- type = h->response[h->resp_pos++];
- *len = h->response[h->resp_pos++] - 2;
- if (h->resp_pos + (int)*len > h->resp_len) {
+ type = h->in[h->in_pos++];
+ *len = h->in[h->in_pos++] - 2;
+ if (h->in_pos + (int)*len > h->in_len) {
generr(h, "Malformed attribute in response");
return -1;
}
- *value = &h->response[h->resp_pos];
- h->resp_pos += *len;
+ *value = &h->in[h->in_pos];
+ h->in_pos += *len;
return type;
}
@@ -672,6 +847,10 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
{
int srv;
+ if (h->type == RADIUS_SERVER) {
+ generr(h, "denied function call");
+ return (-1);
+ }
/* Make sure we have a socket to use */
if (h->fd == -1) {
struct sockaddr_in sin;
@@ -694,7 +873,7 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
}
}
- if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
+ if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
/* Make sure no password given */
if (h->pass_pos || h->chap_pass) {
generr(h, "User or Chap Password"
@@ -718,8 +897,8 @@ rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
}
/* Fill in the length field in the message */
- h->request[POS_LENGTH] = h->req_len >> 8;
- h->request[POS_LENGTH+1] = h->req_len;
+ h->out[POS_LENGTH] = h->out_len >> 8;
+ h->out[POS_LENGTH+1] = h->out_len;
/*
* Count the total number of tries we will make, and zero the
@@ -763,7 +942,7 @@ rad_auth_open(void)
h->chap_pass = 0;
h->authentic_pos = 0;
h->type = RADIUS_AUTH;
- h->request_created = 0;
+ h->out_created = 0;
h->eap_msg = 0;
}
return h;
@@ -781,6 +960,19 @@ rad_acct_open(void)
}
struct rad_handle *
+rad_server_open(int fd)
+{
+ struct rad_handle *h;
+
+ h = rad_open();
+ if (h != NULL) {
+ h->type = RADIUS_SERVER;
+ h->fd = fd;
+ }
+ return h;
+}
+
+struct rad_handle *
rad_open(void)
{
return rad_auth_open();
@@ -797,13 +989,13 @@ rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
{
int result;
- if (!h->request_created) {
+ if (!h->out_created) {
generr(h, "Please call rad_create_request()"
" before putting attributes");
return -1;
}
- if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
+ if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
if (type == RAD_EAP_MESSAGE) {
generr(h, "EAP-Message attribute is not valid"
" in accounting requests");
@@ -858,14 +1050,14 @@ rad_put_message_authentic(struct rad_handle *h)
#ifdef WITH_SSL
u_char md_zero[MD5_DIGEST_LENGTH];
- if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
+ if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
generr(h, "Message-Authenticator is not valid"
" in accounting requests");
return -1;
}
if (h->authentic_pos == 0) {
- h->authentic_pos = h->req_len;
+ h->authentic_pos = h->out_len;
memset(md_zero, 0, sizeof(md_zero));
return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero,
sizeof(md_zero)));
@@ -1041,7 +1233,7 @@ rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
struct vendor_attribute *attr;
int res;
- if (!h->request_created) {
+ if (!h->out_created) {
generr(h, "Please call rad_create_request()"
" before putting attributes");
return -1;
@@ -1088,7 +1280,7 @@ rad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
{
if (len < LEN_AUTH)
return (-1);
- memcpy(buf, h->request + POS_AUTH, LEN_AUTH);
+ memcpy(buf, h->out + POS_AUTH, LEN_AUTH);
if (len > LEN_AUTH)
buf[LEN_AUTH] = '\0';
return (LEN_AUTH);