diff options
author | mav <mav@FreeBSD.org> | 2009-09-11 11:42:56 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2009-09-11 11:42:56 +0000 |
commit | 972d9b272230173e6ef0275570d6cc08fe5163c9 (patch) | |
tree | 62d154acac53838b9afff7d63c697fe60f8f19bc /lib/libradius/radlib.c | |
parent | 115c0d027c275a438e6a64297b2380f71c326821 (diff) | |
download | FreeBSD-src-972d9b272230173e6ef0275570d6cc08fe5163c9.zip FreeBSD-src-972d9b272230173e6ef0275570d6cc08fe5163c9.tar.gz |
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
Diffstat (limited to 'lib/libradius/radlib.c')
-rw-r--r-- | lib/libradius/radlib.c | 346 |
1 files changed, 269 insertions, 77 deletions
diff --git a/lib/libradius/radlib.c b/lib/libradius/radlib.c index b21447e..8faaf7e 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); |