25RCSID(
"$Id: b07964d38eae4944aeabd33fc85cacca9ffac3df $")
33#include <freeradius-devel/io/pair.h>
34#include <freeradius-devel/util/md5.h>
35#include <freeradius-devel/util/net.h>
36#include <freeradius-devel/util/proto.h>
37#include <freeradius-devel/util/table.h>
38#include <freeradius-devel/util/udp.h>
39#include <freeradius-devel/protocol/radius/freeradius.internal.h>
81#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf_push
117 "Accounting-Request",
118 "Accounting-Response",
123 "Accounting-Message",
134 "Resource-Free-Request",
135 "Resource-Free-Response",
136 "Resource-Query-Request",
137 "Resource-Query-Response",
138 "Alternate-Resource-Reclaim-Request",
139 "NAS-Reboot-Request",
140 "NAS-Reboot-Response",
153 "Disconnect-Request",
163 "IP-Address-Allocate",
164 "IP-Address-Release",
219 {
L(
"abinary"), { .func = dict_flag_abinary } },
220 {
L(
"concat"), { .func = dict_flag_concat } },
222 {
L(
"extended"), { .func = dict_flag_extended } },
223 {
L(
"has_tag"), { .func = dict_flag_has_tag } },
224 {
L(
"long_extended"), { .func = dict_flag_long_extended } }
263 if (
inlen >
sizeof(digest))
inlen =
sizeof(digest);
264 for (i = 0; i <
inlen; i++) digest[i] ^=
in[i];
292 if ((errno == EAGAIN) || (errno == EINTR))
return 0;
300 char buffer[INET6_ADDRSTRLEN];
314 packet_len = (header[2] * 256) + header[3];
322 "data, got %zd bytes", packet_len);
370 (
unsigned int) UINT16_MAX, secret_len);
385 end = packet + packet_len;
388 if ((end -
msg) < 2)
goto invalid_attribute;
390 if (
msg[0] != FR_MESSAGE_AUTHENTICATOR) {
391 if (
msg[1] < 2)
goto invalid_attribute;
393 if ((
msg +
msg[1]) > end) {
422 if (!vector)
goto need_original;
520 bool seen_ma =
false;
523 size_t packet_len = *packet_len_p;
551 if ((packet[0] == 0) ||
605 if (totallen > packet_len) {
607 packet_len, totallen);
618 if (totallen < packet_len) {
619 *packet_len_p = packet_len = totallen;
635 end = packet + packet_len;
643 if ((end - attr) < 2) {
664 attr[0], attr - packet);
673 if ((attr + attr[1]) > end) {
675 attr[0], attr - packet);
692 require_message_authenticator =
true;
695 case FR_MESSAGE_AUTHENTICATOR:
698 attr[1] - 2, attr - packet);
726 if ((max_attributes > 0) &&
727 (num_attributes > max_attributes)) {
729 num_attributes, max_attributes);
745 if (require_message_authenticator && !seen_ma) {
780 bool require_message_authenticator,
bool limit_proxy_state)
782 bool found_message_authenticator =
false;
783 bool found_proxy_state =
false;
802 memcpy(request_authenticator, packet + 4,
sizeof(request_authenticator));
810 end = packet + packet_len;
813 if ((end -
msg) < 2)
goto invalid_attribute;
815 if (
msg[0] != FR_MESSAGE_AUTHENTICATOR) {
816 if (
msg[1] < 2)
goto invalid_attribute;
823 if (limit_proxy_state && (
msg[0] == FR_PROXY_STATE)) found_proxy_state =
true;
825 if ((
msg +
msg[1]) > end) {
842 memcpy(message_authenticator,
msg + 2,
sizeof(message_authenticator));
843 found_message_authenticator =
true;
848 if (limit_proxy_state && found_proxy_state && !found_message_authenticator) {
853 if (require_message_authenticator && !found_message_authenticator) {
854 fr_strerror_const(
"Access-Request is missing the required Message-Authenticator attribute");
881 (
fr_digest_cmp(message_authenticator,
msg + 2,
sizeof(message_authenticator)) != 0)) {
882 memcpy(
msg + 2, message_authenticator,
sizeof(message_authenticator));
883 memcpy(packet + 4, request_authenticator,
sizeof(request_authenticator));
900 if (
fr_digest_cmp(request_authenticator, packet + 4,
sizeof(request_authenticator)) != 0) {
901 memcpy(packet + 4, request_authenticator,
sizeof(request_authenticator));
903 fr_strerror_const(
"invalid Response Authenticator (shared secret is incorrect)");
922 if ((c->
da->dict == dict) &&
923 (!c->
da->flags.internal || ((c->
da->attr > FR_TAG_BASE) && (c->
da->attr < (FR_TAG_BASE + 0x20))))) {
968 length_dbuff =
FR_DBUFF(&work_dbuff);
971 switch (packet_ctx->
code) {
985 for (i = 0; i < 4; i++) {
1040 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1041 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
1066 if (slen < 0)
return slen;
1089 uint8_t *packet,
size_t packet_len,
1097 switch (packet[0]) {
1116 unsigned int code = packet[0];
1154 if (decode_ctx->
verify) {
1165 end = packet + packet_len;
1171 while (attr < end) {
1173 if (slen < 0)
return slen;
1184 talloc_free_children(decode_ctx->
tmp_ctx);
1197 uint8_t *packet,
size_t packet_len,
1207 packet_ctx.
common = &common_ctx;
1210 packet_ctx.
end = packet + packet_len;
1254 fr_strerror_const(
"Attributes of type 'extended' cannot be used inside of a 'struct'");
1259 fr_strerror_const(
"Attributes of type 'long_extended' cannot be used inside of a 'struct'");
1265 fr_strerror_const(
"Attributes of type 'concat' cannot be used inside of a 'struct'");
1270 fr_strerror_const(
"Attributes of type 'concat' cannot be used inside of a 'struct'");
1275 fr_strerror_const(
"Attributes of type 'abinary' cannot be used inside of a 'struct'");
1280 fr_strerror_const(
"Attributes of type 'encrypt' cannot be used inside of a 'struct'");
1287 if (da->flags.length > 253) {
1294 if (flags->
encrypt != 0) da->flags.secret =
true;
1297 if (!da->parent->flags.is_root) {
1298 fr_strerror_const(
"Attributes with the 'concat' flag MUST be at the root of the dictionary");
1303 fr_strerror_const(
"Attributes with the 'concat' flag MUST be of data type 'octets'");
1316 fr_strerror_printf(
"The 'has_tag' flag can only be used for attributes of type 'integer' "
1321 if (!(da->parent->flags.is_root ||
1323 (da->parent->parent && da->parent->parent->type ==
FR_TYPE_VSA)))) {
1324 fr_strerror_const(
"The 'has_tag' flag can only be used with RFC and VSA attributes");
1333 fr_strerror_const(
"The 'long' or 'extended' flag can only be used for attributes of type 'tlv'");
1337 if (!da->parent->flags.is_root) {
1338 fr_strerror_const(
"The 'long' flag can only be used for top-level RFC attributes");
1356 "attributes of type 'string'");
1360 if (da->flags.length == 0) {
1361 fr_strerror_printf(
"The 'encrypt=User-Password' flag MUST be used with an explicit length for "
1362 "'octets' data types");
1392 .default_type_size = 1,
1393 .default_type_length = 1,
static int const char char buffer[256]
#define L(_str)
Helper for initialising arrays of string literals.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
#define FR_DBUFF_EXTEND_LOWAT_OR_RETURN(_dbuff_or_marker, _lowat)
Extend if we're below _lowat and return if we can't extend above _lowat.
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
#define fr_dbuff_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
#define FR_DBUFF_MEMSET_RETURN(_dbuff_or_marker, _c, _inlen)
Set _inlen bytes of a dbuff or marker to _c returning if there is insufficient space.
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
#define fr_dbuff_in_memcpy(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into a dbuff or marker.
#define fr_dbuff_in(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff or marker.
#define FR_DBUFF_IN_RETURN(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff returning if there is insufficient space.
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
#define FR_DBUFF_MAX(_dbuff_or_marker, _max)
Limit the maximum number of bytes available in the dbuff when passing it to another function.
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
fr_radius_packet_code_t
RADIUS packet codes.
@ FR_RADIUS_CODE_ACCESS_CHALLENGE
RFC2865 - Access-Challenge.
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
@ FR_RADIUS_CODE_DISCONNECT_REQUEST
RFC3575/RFC5176 - Disconnect-Request.
@ FR_RADIUS_CODE_MAX
Maximum possible protocol code.
@ FR_RADIUS_CODE_DISCONNECT_ACK
RFC3575/RFC5176 - Disconnect-Ack (positive)
@ FR_RADIUS_CODE_STATUS_SERVER
RFC2865/RFC5997 - Status Server (request)
@ FR_RADIUS_CODE_COA_REQUEST
RFC3575/RFC5176 - CoA-Request.
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
@ FR_RADIUS_CODE_ACCOUNTING_RESPONSE
RFC2866 - Accounting-Response.
@ FR_RADIUS_CODE_COA_NAK
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
@ FR_RADIUS_CODE_UNDEFINED
Packet code has not been set.
@ FR_RADIUS_CODE_COA_ACK
RFC3575/RFC5176 - CoA-Ack (positive)
@ FR_RADIUS_CODE_DISCONNECT_NAK
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
@ FR_RADIUS_CODE_PROTOCOL_ERROR
RFC7930 - Protocol-Error (generic NAK)
@ FR_RADIUS_CODE_ACCOUNTING_REQUEST
RFC2866 - Accounting-Request.
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
#define fr_dict_autofree(_to_free)
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
@ FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC
Protocol specific extensions.
#define fr_dict_autoload(_to_load)
char const * name
name of this protocol
#define FR_DICT_ATTR_FLAG_FUNC(_struct, _name)
Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
Protocol specific custom flag definitnion.
Protocol-specific callbacks in libfreeradius-PROTOCOL.
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Head of a doubly linked list.
int fr_hmac_md5(uint8_t digest[MD5_DIGEST_LENGTH], uint8_t const *in, size_t inlen, uint8_t const *key, size_t key_len)
Calculate HMAC using internal MD5 implementation.
union fr_ipaddr_t::@130 addr
static fr_dict_t const * dict_freeradius
fr_dict_attr_t const * attr_packet_type
fr_dict_attr_t const * attr_state
fr_dict_attr_t const * attr_eap_message
fr_dict_t const * dict_radius
fr_dict_attr_t const * attr_message_authenticator
static int dict_flag_encrypt(fr_dict_attr_t **da_p, char const *value, UNUSED fr_dict_flag_parser_rule_t const *rules)
static uint32_t instance_count
int udp_recv_discard(int sockfd)
Discard the next UDP packet.
ssize_t udp_recv_peek(int sockfd, void *data, size_t data_len, int flags, fr_ipaddr_t *src_ipaddr, uint16_t *src_port)
Peek at the header of a UDP packet.
fr_md5_update_t fr_md5_update
fr_md5_final_t fr_md5_final
void fr_md5_ctx_free_from_list(fr_md5_ctx_t **ctx)
fr_md5_ctx_t * fr_md5_ctx_alloc_from_list(void)
#define MD5_DIGEST_LENGTH
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_OCTETS
Raw octets.
int fr_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length)
Do a comparison of two authentication digests by comparing the FULL data.
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
#define RADIUS_HEADER_LENGTH
#define RADIUS_AUTH_VECTOR_LENGTH
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
static uint8_t const zeros[6]
static bool attr_valid(fr_dict_attr_t *da)
fr_dict_protocol_t libfreeradius_radius_dict_protocol
fr_dict_autoload_t libfreeradius_radius_dict[]
fr_dict_attr_t const * attr_nas_filter_rule
static const bool disallow_tunnel_passwords[FR_RADIUS_CODE_MAX]
void * fr_radius_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
static fr_dict_flag_parser_t const radius_flags[]
fr_dict_attr_t const * attr_packet_authentication_vector
static const fr_radius_packet_code_t allowed_replies[FR_RADIUS_CODE_MAX]
If we get a reply, the request must come from one of a small number of packet types.
ssize_t fr_radius_ascend_secret(fr_dbuff_t *dbuff, uint8_t const *in, size_t inlen, char const *secret, uint8_t const *vector)
Do Ascend-Send / Recv-Secret calculation.
ssize_t fr_radius_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t *packet, size_t packet_len, fr_radius_decode_ctx_t *decode_ctx)
size_t fr_radius_limit_proxy_state_table_len
int fr_radius_sign(uint8_t *packet, uint8_t const *vector, uint8_t const *secret, size_t secret_len)
Sign a previously encoded packet.
int fr_radius_verify(uint8_t *packet, uint8_t const *vector, uint8_t const *secret, size_t secret_len, bool require_message_authenticator, bool limit_proxy_state)
Verify a request / response packet.
fr_dict_attr_autoload_t libfreeradius_radius_dict_attr[]
size_t fr_radius_require_ma_table_len
fr_dict_attr_t const * attr_chap_challenge
fr_dict_attr_t const * attr_vendor_specific
int fr_radius_global_init(void)
size_t fr_radius_request_name_table_len
#define FR_DEBUG_STRERROR_PRINTF
fr_dict_attr_t const * attr_chargeable_user_identity
ssize_t fr_radius_decode_simple(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t *packet, size_t packet_len, uint8_t const *vector, char const *secret)
Simple wrapper for callers who just need a shared secret.
void fr_radius_global_free(void)
fr_table_num_sorted_t const fr_radius_limit_proxy_state_table[]
fr_table_num_sorted_t const fr_radius_request_name_table[]
fr_table_num_sorted_t const fr_radius_require_ma_table[]
bool fr_radius_ok(uint8_t const *packet, size_t *packet_len_p, uint32_t max_attributes, bool require_message_authenticator, fr_radius_decode_fail_t *reason)
See if the data pointed to by PTR is a valid RADIUS packet.
ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx)
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
ssize_t fr_radius_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, unsigned int *code)
Basic validation of RADIUS packet header.
int fr_radius_allow_reply(int code, bool allowed[static FR_RADIUS_CODE_MAX])
ssize_t fr_radius_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, fr_radius_decode_ctx_t *packet_ctx)
Create a "normal" fr_pair_t from the given data.
ssize_t fr_radius_encode_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a data structure into a RADIUS attribute.
ssize_t fr_radius_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
static rc_request_t * current
unsigned int has_tag
Attribute has a tag.
bool secure_transport
for TLS
@ FR_RADIUS_REQUIRE_MA_NO
Do not require Message-Authenticator.
@ FR_RADIUS_REQUIRE_MA_YES
Require Message-Authenticator.
@ FR_RADIUS_REQUIRE_MA_AUTO
Only require Message-Authenticator if we've previously received a packet from this client with Messag...
fr_radius_ctx_t const * common
uint8_t request_code
original code for the request.
uint8_t const * request_authenticator
unsigned int abinary
Attribute is in "abinary" format.
fr_radius_decode_fail_t
Failure reasons.
@ DECODE_FAIL_INVALID_ATTRIBUTE
@ DECODE_FAIL_ATTRIBUTE_UNDERFLOW
@ DECODE_FAIL_MIN_LENGTH_FIELD
@ DECODE_FAIL_HEADER_OVERFLOW
@ DECODE_FAIL_ATTRIBUTE_TOO_SHORT
@ DECODE_FAIL_ATTRIBUTE_OVERFLOW
@ DECODE_FAIL_TOO_MANY_ATTRIBUTES
@ DECODE_FAIL_MIN_LENGTH_PACKET
@ DECODE_FAIL_MIN_LENGTH_MISMATCH
@ DECODE_FAIL_MA_INVALID_LENGTH
@ DECODE_FAIL_UNKNOWN_PACKET_CODE
bool disallow_tunnel_passwords
not all packets can have tunnel passwords
unsigned int concat
Attribute is concatenated.
uint8_t const * end
end of the packet
bool limit_proxy_state
Don't allow Proxy-State in requests.
uint8_t const * request_authenticator
unsigned int extended
Attribute is an extended attribute.
fr_radius_attr_flags_encrypt_t encrypt
Attribute is encrypted.
bool require_message_authenticator
bool verify
can skip verify for dynamic clients
fr_radius_ctx_t const * common
@ FR_RADIUS_LIMIT_PROXY_STATE_NO
Do not limit Proxy-State.
@ FR_RADIUS_LIMIT_PROXY_STATE_AUTO
Do not allow Proxy-State unless:
@ FR_RADIUS_LIMIT_PROXY_STATE_YES
Limit Proxy-State.
unsigned int long_extended
Attribute is a long extended attribute.
fr_radius_attr_flags_encrypt_t
@ RADIUS_FLAG_ENCRYPT_INVALID
Invalid encryption flag.
@ RADIUS_FLAG_ENCRYPT_USER_PASSWORD
Encrypt attribute RFC 2865 style.
@ RADIUS_FLAG_ENCRYPT_ASCEND_SECRET
Encrypt attribute ascend style.
@ RADIUS_FLAG_ENCRYPT_TUNNEL_PASSWORD
Encrypt attribute RFC 2868 style.
static fr_radius_attr_flags_t const * fr_radius_attr_flags(fr_dict_attr_t const *da)
Return RADIUS-specific flags for a given attribute.
bool add_proxy_state
do we add a Proxy-State?
bool seen_message_authenticator
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
uint32_t fr_rand(void)
Return a 32-bit random number.
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
An element in a lexicographically sorted array of name to num mappings.
#define fr_pair_dcursor_iter_init(_cursor, _list, _iter, _uctx)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const_push(_msg)
#define fr_strerror_const(_msg)
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
static size_t char fr_sbuff_t size_t inlen
static size_t char ** out