diff options
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/event.c | 29 | ||||
-rw-r--r-- | src/lib/packet.c | 18 | ||||
-rw-r--r-- | src/lib/print.c | 1 | ||||
-rw-r--r-- | src/lib/radius.c | 154 |
4 files changed, 135 insertions, 67 deletions
diff --git a/src/lib/event.c b/src/lib/event.c index 9eb9d1a..0d926ad 100644 --- a/src/lib/event.c +++ b/src/lib/event.c @@ -47,7 +47,9 @@ typedef struct fr_event_fd_t { void *ctx; } fr_event_fd_t; -#define FR_EV_MAX_FDS (512) +#define FR_EV_MAX_EVENTS (512) + +int fr_ev_max_fds = FR_EV_MAX_EVENTS; #undef USEC #define USEC (1000000) @@ -71,10 +73,10 @@ struct fr_event_list_t { fd_set write_fds; #else int kq; - struct kevent events[FR_EV_MAX_FDS]; /* so it doesn't go on the stack every time */ + struct kevent events[FR_EV_MAX_EVENTS]; /* so it doesn't go on the stack every time */ #endif - fr_event_fd_t readers[FR_EV_MAX_FDS]; + fr_event_fd_t readers[1]; }; /* @@ -128,11 +130,12 @@ fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status) int i; fr_event_list_t *el; - el = talloc_zero(ctx, fr_event_list_t); + el = (fr_event_list_t *) talloc_zero_array(ctx, uint8_t, sizeof(fr_event_list_t) + fr_ev_max_fds * sizeof(el->readers[0])); if (!fr_assert(el)) { return NULL; } talloc_set_destructor(el, _event_list_free); + talloc_set_type(el, fr_event_list_t); el->times = fr_heap_create(fr_event_list_time_cmp, offsetof(fr_event_t, heap)); if (!el->times) { @@ -140,7 +143,7 @@ fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status) return NULL; } - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { el->readers[i].fd = -1; } @@ -363,7 +366,7 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, return 0; } - if (el->num_readers >= FR_EV_MAX_FDS) { + if (el->num_readers >= fr_ev_max_fds) { fr_strerror_printf("Too many readers"); return 0; } @@ -385,11 +388,11 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, * usually less than 256, we do "FD & 0xff", which is a * good guess, and makes the lookups mostly O(1). */ - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd >= 0) continue; @@ -469,11 +472,11 @@ int fr_event_fd_write_handler(fr_event_list_t *el, int type, int fd, if (type != 0) return 0; #ifdef HAVE_KQUEUE - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd != fd) continue; @@ -528,11 +531,11 @@ int fr_event_fd_delete(fr_event_list_t *el, int type, int fd) if (type != 0) return 0; #ifdef HAVE_KQUEUE - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd != fd) continue; @@ -676,7 +679,7 @@ int fr_event_loop(fr_event_list_t *el) ts_wake = NULL; } - rcode = kevent(el->kq, NULL, 0, el->events, FR_EV_MAX_FDS, ts_wake); + rcode = kevent(el->kq, NULL, 0, el->events, FR_EV_MAX_EVENTS, ts_wake); #endif /* HAVE_KQUEUE */ if (fr_heap_num_elements(el->times) > 0) { diff --git a/src/lib/packet.c b/src/lib/packet.c index 971980b..0f870f5 100644 --- a/src/lib/packet.c +++ b/src/lib/packet.c @@ -723,6 +723,8 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, */ id = fd = -1; + if (request->id >= 0 && request->id < 256) + id = request->id; start_i = fr_rand() & SOCKOFFSET_MASK; #define ID_i ((i + start_i) & SOCKOFFSET_MASK) @@ -832,6 +834,18 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, */ /* + * An explicit ID was requested + */ + + if (id != -1) { + if ((ps->id[(id >> 3) & 0x1f] & (1 << (id & 0x07))) != 0) continue; + + ps->id[(id >> 3) & 0x1f] |= (1 << (id & 0x07)); + fd = i; + break; + } + + /* * Look for a free Id, starting from a random number. */ start_j = fr_rand() & 0x1f; @@ -1076,7 +1090,7 @@ void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received) * This really belongs in a utility library */ if (is_radius_code(packet->code)) { - fprintf(fp, "%s %s Id %i from %s%s%s:%x to %s%s%s:%u length %zu\n", + fprintf(fp, "%s %s Id %i from %s%s%s:%u to %s%s%s:%u length %zu\n", received ? "Received" : "Sent", fr_packet_codes[packet->code], packet->id, @@ -1094,7 +1108,7 @@ void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received) packet->dst_port, packet->data_len); } else { - fprintf(fp, "%s code %u Id %i from %s%s%s:%u to %s%s%s:%i length %zu\n", + fprintf(fp, "%s code %u Id %i from %s%s%s:%u to %s%s%s:%u length %zu\n", received ? "Received" : "Sent", packet->code, packet->id, diff --git a/src/lib/print.c b/src/lib/print.c index 57455b6..83aa267 100644 --- a/src/lib/print.c +++ b/src/lib/print.c @@ -529,6 +529,7 @@ size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp, bool switch (vp->da->type) { case PW_TYPE_STRING: + case PW_TYPE_OCTETS: for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->vp_length; q++) { /* Indicate truncation */ if (freespace < 3) return outlen + 1; diff --git a/src/lib/radius.c b/src/lib/radius.c index b2de15b..a19c9a4 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -145,8 +145,9 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = { "47", "48", "49", - "IP-Address-Allocate", - "IP-Address-Release", //!< 50 + "IP-Address-Allocate", //!< 50 + "IP-Address-Release", + "Protocol-Error", }; @@ -536,7 +537,7 @@ static void make_secret(uint8_t *digest, uint8_t const *vector, fr_md5_destroy(&context); } -#define MAX_PASS_LEN (128) +#define MAX_PASS_LEN (256) static void make_passwd(uint8_t *output, ssize_t *outlen, uint8_t const *input, size_t inlen, char const *secret, uint8_t const *vector) @@ -1819,6 +1820,14 @@ int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original, return rad_vp2vsa(packet, original, secret, pvp, start, room); } +static const bool code2ma[FR_MAX_PACKET_CODE] = { + [ PW_CODE_ACCESS_REQUEST ] = true, + [ PW_CODE_ACCESS_ACCEPT ] = true, + [ PW_CODE_ACCESS_REJECT ] = true, + [ PW_CODE_ACCESS_CHALLENGE ] = true, + [ PW_CODE_STATUS_SERVER ] = true, + [ PW_CODE_PROTOCOL_ERROR ] = true, +}; /** Encode a packet * @@ -1831,6 +1840,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, uint16_t total_length; int len; VALUE_PAIR const *reply; + bool seen_ma = false; /* * A 4K packet, aligned on 64-bits. @@ -1883,6 +1893,12 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, id = htonl(id); memcpy(hdr->vector, &id, sizeof(id)); memset(hdr->vector + sizeof(id), 0, sizeof(hdr->vector) - sizeof(id)); + + /* + * We don't encode Message-Authenticator + */ + seen_ma = true; + packet->offset = -1; } else #endif { @@ -1909,6 +1925,27 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, */ /* + * Always add Message-Authenticator for replies to + * Access-Request packets, and for all Access-Accept, + * Access-Reject, Access-Challenge. + * + * It must be the FIRST attribute in the packet. + */ + if (!packet->tls && + ((code2ma[packet->code]) || (original && code2ma[original->code]))) { + seen_ma = true; + + packet->offset = RADIUS_HDR_LEN; + + ptr[0] = PW_MESSAGE_AUTHENTICATOR; + ptr[1] = 18; + memset(ptr + 2, 0, 16); + + ptr += 18; + total_length += 18; + } + + /* * Loop over the reply attributes for the packet. */ reply = packet->vps; @@ -1943,18 +1980,9 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, #ifdef WITH_RADIUSV11 /* - * Do not encode Message-Authenticator for RADIUS/1.1 - */ - if ((reply->da->vendor == 0) && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { - reply = reply->next; - continue; - } - - - /* * Do not encode Original-Packet-Code for RADIUS/1.1 */ - if (reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { + if (packet->radiusv11 && reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { reply = reply->next; continue; } @@ -1984,15 +2012,13 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, * length and initial value. */ if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { -#ifdef WITH_RADIUSV11 /* - * RADIUSV11 does not encode or verify Message-Authenticator. + * We have already encoded the Message-Authenticator, don't do it again. */ - if (packet->radiusv11) { + if (seen_ma) { reply = reply->next; continue; } -#endif if (room < 18) break; @@ -2152,11 +2178,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_DISCONNECT_REQUEST: - case PW_CODE_DISCONNECT_ACK: - case PW_CODE_DISCONNECT_NAK: case PW_CODE_COA_REQUEST: - case PW_CODE_COA_ACK: - case PW_CODE_COA_NAK: memset(hdr->vector, 0, AUTH_VECTOR_LEN); break; @@ -2164,6 +2186,10 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_REJECT: case PW_CODE_ACCESS_CHALLENGE: + case PW_CODE_DISCONNECT_ACK: + case PW_CODE_DISCONNECT_NAK: + case PW_CODE_COA_ACK: + case PW_CODE_COA_NAK: memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); break; @@ -2510,6 +2536,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) char host_ipaddr[128]; #ifndef WITH_RADIUSV11_ONLY bool require_ma = false; + bool limit_proxy_state = false; + bool seen_proxy_state = false; bool seen_ma = false; bool eap = false; bool non_eap = false; @@ -2559,15 +2587,23 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } /* - * Message-Authenticator is required in Status-Server - * packets, otherwise they can be trivially forged. + * If the caller requires Message-Authenticator, then set + * the flag. + * + * We also require Message-Authenticator if the packet + * code is Status-Server. + * + * If we're receiving packets from a proxy socket, then + * require Message-Authenticator for Access-* replies, + * and for Protocol-Error. */ - if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true; + require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER) || (((flags & 0x08) != 0) && code2ma[hdr->code]); /* - * It's also required if the caller asks for it. + * We only limit Proxy-State if we're not requiring + * Message-Authenticator. */ - if (flags) require_ma = true; + limit_proxy_state = ((flags & 0x04) != 0) && !require_ma; /* * Repeat the length checks. This time, instead of @@ -2723,6 +2759,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) case PW_EAP_MESSAGE: require_ma = true; eap = true; + packet->eap_message = true; break; case PW_USER_PASSWORD: @@ -2731,6 +2768,11 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) non_eap = true; break; + case PW_PROXY_STATE: + seen_proxy_state = true; + packet->proxy_state = true; + break; + case PW_MESSAGE_AUTHENTICATOR: #ifdef WITH_RADIUSV11 /* @@ -2749,6 +2791,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) goto finish; } seen_ma = true; + packet->message_authenticator = true; break; } #endif @@ -2813,7 +2856,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) !packet->radiusv11 && #endif !seen_ma) { - FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute. You may need to set \"require_message_authenticator = no\" in the configuration.", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -2822,6 +2865,18 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } #ifndef WITH_RADIUSV11_ONLY + /* + * The client is a NAS which shouldn't send Proxy-State, but it did! + */ + if (limit_proxy_state && seen_proxy_state && !seen_ma) { + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute, but still has one or more Proxy-State attributes", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr))); + failure = DECODE_FAIL_MA_MISSING; + goto finish; + } + if (eap && non_eap) { FR_DEBUG_STRERROR_PRINTF("Bad packet from host %s: Packet contains EAP-Message and non-EAP authentication attribute", inet_ntop(packet->src_ipaddr.af, @@ -3938,7 +3993,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, VALUE_PAIR *vp; uint8_t const *data = start; char *p; - uint8_t buffer[256]; + uint8_t buffer[MAX_PASS_LEN]; /* * FIXME: Attrlen can be larger than 253 for extended attrs! @@ -4054,7 +4109,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, attrlen, secret, packet->vector); } - buffer[253] = '\0'; + buffer[attrlen] = '\0'; /* * MS-CHAP-MPPE-Keys are 24 octets, and @@ -4654,22 +4709,24 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, ssize_t my_len; #ifdef WITH_RADIUSV11 - /* - * Don't decode Message-Authenticator - */ - if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; - } + if (packet->radiusv11) { + /* + * Don't decode Message-Authenticator + */ + if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } - /* - * Don't decode Original-Packet-Code - */ - if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; + /* + * Don't decode Original-Packet-Code + */ + if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } } #endif @@ -4761,7 +4818,7 @@ int rad_pwencode(char *passwd, size_t *pwlen, char const *secret, */ len = *pwlen; - if (len > 128) len = 128; + if (len > MAX_STRING_LEN) len = MAX_STRING_LEN; if (len == 0) { memset(passwd, 0, AUTH_PASS_LEN); @@ -4821,13 +4878,6 @@ int rad_pwdecode(char *passwd, size_t pwlen, char const *secret, size_t n, secretlen; /* - * The RFC's say that the maximum is 128. - * The buffer we're putting it into above is 254, so - * we don't need to do any length checking. - */ - if (pwlen > 128) pwlen = 128; - - /* * Catch idiots. */ if (pwlen == 0) goto done; |