From 4bbba963b5f02284908df147e3d9f702eb5fe967 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Mon, 15 Apr 2013 17:02:59 +0400 Subject: ipoe: fixed ses->client_id and ses->relay_agent initialization --- accel-pppd/ctrl/ipoe/ipoe.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 214f93a9..1ba53ea7 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -816,6 +816,7 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s if (pack->client_id) { ses->client_id = (struct dhcpv4_option *)ptr; ses->client_id->len = pack->client_id->len; + ses->client_id->data = (uint8_t *)(ses->client_id + 1); memcpy(ses->client_id->data, pack->client_id->data, pack->client_id->len); ptr += sizeof(struct dhcpv4_option) + pack->client_id->len; } @@ -823,6 +824,7 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s if (pack->relay_agent) { ses->relay_agent = (struct dhcpv4_option *)ptr; ses->relay_agent->len = pack->relay_agent->len; + ses->relay_agent->data = (uint8_t *)(ses->relay_agent + 1); memcpy(ses->relay_agent->data, pack->relay_agent->data, pack->relay_agent->len); ptr += sizeof(struct dhcpv4_option) + pack->relay_agent->len; if (dhcpv4_parse_opt82(ses->relay_agent, &ses->agent_circuit_id, &ses->agent_remote_id)) -- cgit v1.2.3 From faf185d337229fd521bfbadbb8c8ad3a34e5b705 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:42:35 +0200 Subject: l2tp: Fix double string AVP copy When adding a string AVP to an L2TP packet, the attribute value is allocated and set using strdup(). There's no need to memcpy() it again afterwards. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/packet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 87735a2a..03816d50 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -469,7 +469,6 @@ int l2tp_packet_add_string(struct l2tp_packet_t *pack, int id, const char *val, mempool_free(attr); return -1; } - memcpy(attr->val.string, val, attr->length); list_add_tail(&attr->entry, &pack->attrs); return 0; -- cgit v1.2.3 From a5313507e0213647c6e745b9f2746493a1b0ddc5 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:42:44 +0200 Subject: l2tp: Fix endianness of 64 bits AVPs (reception) Received attributes of type ATTR_TYPE_INT64 are transferred to upper layer in network byte order while any other integer type uses host byte order. This patch converts int64 values to host byte order so that they can be used like other integer types. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/packet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 03816d50..2546866d 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -284,7 +285,7 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info) case ATTR_TYPE_INT64: if (avp->length != sizeof(*avp) + 8) goto out_err_len; - attr->val.uint64 = *(uint64_t *)avp->val; + attr->val.uint64 = be64toh(*(uint64_t *)avp->val); break; case ATTR_TYPE_OCTETS: attr->val.octets = _malloc(attr->length); -- cgit v1.2.3 From 95c7a75fe8405832dfae02d787afffb54bc1f70e Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:42:54 +0200 Subject: l2tp: Store length of conf_secret Secret length is used quite often especially when handling hidden AVPs. Store conf_secret length together with conf_secret to avoid calling strlen(conf_secret) every time. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 7c53e6e3..455e1a15 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -57,6 +57,7 @@ static int conf_hello_interval = 60; static int conf_dir300_quirk = 0; static const char *conf_host_name = "accel-ppp"; static const char *conf_secret = NULL; +static size_t conf_secret_len = 0; static int conf_mppe = MPPE_UNSET; static unsigned int stat_active; @@ -226,7 +227,7 @@ static int l2tp_tunnel_genchall(uint16_t chall_len, ssize_t rdlen; if (chall_len == 0 - || conf_secret == NULL || strlen(conf_secret) == 0) { + || conf_secret == NULL || conf_secret_len == 0) { if (conn->challenge) { _free(conn->challenge); conn->challenge = NULL; @@ -302,7 +303,7 @@ static int l2tp_tunnel_storechall(struct l2tp_conn_t *conn, return 0; } - if (conf_secret == NULL || strlen(conf_secret) == 0) { + if (conf_secret == NULL || conf_secret_len == 0) { log_tunnel(log_error, conn, "authentication required by peer," " but no secret has been set for this tunnel\n"); goto err; @@ -340,21 +341,21 @@ static int l2tp_tunnel_genchallresp(uint8_t msgident, uint8_t challresp[MD5_DIGEST_LENGTH]; if (conn->challenge == NULL) { - if (conf_secret && strlen(conf_secret) > 0) { + if (conf_secret && conf_secret_len > 0) { log_tunnel(log_warn, conn, "no Challenge sent by peer\n"); } return 0; } - if (conf_secret == NULL || strlen(conf_secret) == 0) { + if (conf_secret == NULL || conf_secret_len == 0) { log_tunnel(log_error, conn, "impossible to generate Challenge Response:" " no secret set for this tunnel\n"); return -1; } - comp_chap_md5(challresp, msgident, conf_secret, strlen(conf_secret), + comp_chap_md5(challresp, msgident, conf_secret, conf_secret_len, conn->challenge, conn->challenge_len); if (l2tp_packet_add_octets(pack, Challenge_Response, challresp, MD5_DIGEST_LENGTH, 1) < 0) { @@ -373,7 +374,7 @@ static int l2tp_tunnel_checkchallresp(uint8_t msgident, { uint8_t challref[MD5_DIGEST_LENGTH]; - if (conf_secret == NULL || strlen(conf_secret) == 0) { + if (conf_secret == NULL || conf_secret_len == 0) { if (challresp) { log_tunnel(log_warn, conn, "discarding unexpected Challenge Response" @@ -400,7 +401,7 @@ static int l2tp_tunnel_checkchallresp(uint8_t msgident, return -1; } - comp_chap_md5(challref, msgident, conf_secret, strlen(conf_secret), + comp_chap_md5(challref, msgident, conf_secret, conf_secret_len, conn->challenge, conn->challenge_len); if (memcmp(challref, challresp->val.octets, MD5_DIGEST_LENGTH) != 0) { log_tunnel(log_error, conn, "impossible to authenticate peer:" @@ -3716,8 +3717,10 @@ static void load_config(void) conf_host_name = "accel-ppp"; opt = conf_get_opt("l2tp", "secret"); - if (opt) + if (opt) { conf_secret = opt; + conf_secret_len = strlen(opt); + } opt = conf_get_opt("l2tp", "dir300_quirk"); if (opt) -- cgit v1.2.3 From 76f30e4e2d664d3c10f988b8d9660b478718c777 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:42:58 +0200 Subject: l2tp: Handle incoming hidden AVPs Decode hidden AVPs on reception. This is transparent for functions in l2tp.c (except for the presence of the Random Vector AVP). Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.c | 17 ++++- accel-pppd/ctrl/l2tp/l2tp.h | 3 +- accel-pppd/ctrl/l2tp/packet.c | 168 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 169 insertions(+), 19 deletions(-) diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 455e1a15..267a47f8 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -1963,6 +1963,8 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv, list_for_each_entry(attr, &pack->attrs, entry) { switch (attr->attr->id) { + case Random_Vector: + break; case Protocol_Version: protocol_version = attr; break; @@ -2080,6 +2082,7 @@ static int l2tp_recv_SCCRP(struct l2tp_conn_t *conn, list_for_each_entry(attr, &pack->attrs, entry) { switch (attr->attr->id) { case Message_Type: + case Random_Vector: case Host_Name: case Bearer_Capabilities: case Firmware_Revision: @@ -2215,6 +2218,7 @@ static int l2tp_recv_SCCCN(struct l2tp_conn_t *conn, list_for_each_entry(attr, &pack->attrs, entry) { switch (attr->attr->id) { case Message_Type: + case Random_Vector: break; case Challenge_Response: challenge_resp = attr; @@ -2313,6 +2317,7 @@ static int l2tp_recv_StopCCN(struct l2tp_conn_t *conn, list_for_each_entry(attr, &pack->attrs, entry) { switch(attr->attr->id) { case Message_Type: + case Random_Vector: break; case Assigned_Tunnel_ID: assigned_tid = attr; @@ -2426,6 +2431,7 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, assigned_sid = attr; break; case Message_Type: + case Random_Vector: case Call_Serial_Number: case Bearer_Type: case Calling_Number: @@ -2520,6 +2526,7 @@ static int l2tp_recv_ICRP(struct l2tp_sess_t *sess, list_for_each_entry(attr, &pack->attrs, entry) { switch(attr->attr->id) { case Message_Type: + case Random_Vector: break; case Assigned_Session_ID: assigned_sid = attr; @@ -2677,6 +2684,7 @@ static int l2tp_recv_OCRQ(struct l2tp_conn_t *conn, list_for_each_entry(attr, &pack->attrs, entry) { switch (attr->attr->id) { case Message_Type: + case Random_Vector: case Call_Serial_Number: case Minimum_BPS: case Maximum_BPS: @@ -2776,6 +2784,7 @@ static int l2tp_recv_OCRP(struct l2tp_sess_t *sess, list_for_each_entry(attr, &pack->attrs, entry) { switch(attr->attr->id) { case Message_Type: + case Random_Vector: break; case Assigned_Session_ID: assigned_sid = attr; @@ -2839,6 +2848,7 @@ static int l2tp_recv_OCCN(struct l2tp_sess_t *sess, list_for_each_entry(attr, &pack->attrs, entry) { switch (attr->attr->id) { case Message_Type: + case Random_Vector: case TX_Speed: case Framing_Type: break; @@ -2903,6 +2913,7 @@ static int l2tp_recv_CDN(struct l2tp_sess_t *sess, list_for_each_entry(attr, &pack->attrs, entry) { switch(attr->attr->id) { case Message_Type: + case Random_Vector: break; case Assigned_Session_ID: assigned_sid = attr; @@ -3114,7 +3125,8 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) int res; while (1) { - res = l2tp_recv(h->fd, &pack, NULL); + res = l2tp_recv(h->fd, &pack, NULL, + conf_secret, conf_secret_len); if (res) { if (res == -2) { log_tunnel(log_info1, conn, @@ -3321,7 +3333,8 @@ static int l2tp_udp_read(struct triton_md_handler_t *h) char src_addr[17]; while (1) { - if (l2tp_recv(h->fd, &pack, &pkt_info)) + if (l2tp_recv(h->fd, &pack, &pkt_info, + conf_secret, conf_secret_len) < 0) break; if (!pack) diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 3c26c9fe..82dafd72 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.h +++ b/accel-pppd/ctrl/l2tp/l2tp.h @@ -73,7 +73,8 @@ struct l2tp_dict_attr_t *l2tp_dict_find_attr_by_id(int id); const struct l2tp_dict_value_t *l2tp_dict_find_value(const struct l2tp_dict_attr_t *attr, l2tp_value_t val); -int l2tp_recv(int fd, struct l2tp_packet_t **, struct in_pktinfo *); +int l2tp_recv(int fd, struct l2tp_packet_t **, struct in_pktinfo *, + const char *secret, size_t secret_len); void l2tp_packet_free(struct l2tp_packet_t *); void l2tp_packet_print(const struct l2tp_packet_t *, void (*print)(const char *fmt, ...)); diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 2546866d..5a408405 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -8,6 +8,7 @@ #include #include +#include "crypto.h" #include "triton.h" #include "log.h" #include "mempool.h" @@ -98,7 +99,126 @@ void l2tp_packet_free(struct l2tp_packet_t *pack) mempool_free(pack); } -int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info) +static void memxor(uint8_t *dst, const uint8_t *src, size_t sz) +{ + const uintmax_t *umax_src = (const uintmax_t *)src; + uintmax_t *umax_dst = (uintmax_t *)dst; + size_t left = sz % sizeof(uintmax_t); + size_t indx; + + for (indx = 0; indx < sz / sizeof(uintmax_t); ++indx) + umax_dst[indx] ^= umax_src[indx]; + + src += sz - left; + dst += sz - left; + while (left) { + if (left >= sizeof(uint32_t)) { + *(uint32_t *)dst ^= *(uint32_t *)src; + src += sizeof(uint32_t); + dst += sizeof(uint32_t); + left -= sizeof(uint32_t); + } else if (left >= sizeof(uint16_t)) { + *(uint16_t *)dst ^= *(uint16_t *)src; + src += sizeof(uint16_t); + dst += sizeof(uint16_t); + left -= sizeof(uint16_t); + } else { + *dst ^= *src; + src += sizeof(uint8_t); + dst += sizeof(uint8_t); + left -= sizeof(uint8_t); + } + } +} + +/* + * Decipher hidden AVPs, keeping the Hidden AVP Subformat (i.e. the attribute + * value is prefixed by 2 bytes indicating its length in network byte order). + */ +static int decode_avp(struct l2tp_avp_t *avp, const struct l2tp_attr_t *RV, + const char *secret, size_t secret_len) +{ + MD5_CTX md5_ctx; + uint8_t md5[MD5_DIGEST_LENGTH]; + uint8_t p1[MD5_DIGEST_LENGTH]; + uint8_t *prev_block = NULL; + uint16_t attr_len; + uint16_t orig_attr_len; + uint16_t bytes_left; + uint16_t blocks_left; + uint16_t last_block_len; + + if (avp->length < sizeof(struct l2tp_avp_t) + 2) { + /* Hidden AVPs must contain at least two bytes + for storing original attribute length */ + log_warn("l2tp: incorrect hidden avp received (type %hu):" + " length too small (%hu bytes)\n", + ntohs(avp->type), avp->length); + return -1; + } + attr_len = avp->length - sizeof(struct l2tp_avp_t); + + /* Decode first block */ + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &avp->type, sizeof(avp->type)); + MD5_Update(&md5_ctx, secret, secret_len); + MD5_Update(&md5_ctx, RV->val.octets, RV->length); + MD5_Final(p1, &md5_ctx); + + if (attr_len <= MD5_DIGEST_LENGTH) { + memxor(avp->val, p1, attr_len); + return 0; + } + + memxor(p1, avp->val, MD5_DIGEST_LENGTH); + orig_attr_len = ntohs(*(uint16_t *)p1); + + if (orig_attr_len <= MD5_DIGEST_LENGTH - 2) { + /* Enough bytes decoded already, no need to decode padding */ + memcpy(avp->val, p1, MD5_DIGEST_LENGTH); + return 0; + } + + if (orig_attr_len > attr_len - 2) { + log_warn("l2tp: incorrect hidden avp received (type %hu):" + " original attribute length too big (ciphered" + " attribute length: %hu bytes, advertised original" + " attribute length: %hu bytes)\n", + ntohs(avp->type), attr_len, orig_attr_len); + return -1; + } + + /* Decode remaining blocks. Start from the last block as + preceding blocks must be kept hidden for computing MD5s */ + bytes_left = orig_attr_len + 2 - MD5_DIGEST_LENGTH; + last_block_len = bytes_left % MD5_DIGEST_LENGTH; + blocks_left = bytes_left / MD5_DIGEST_LENGTH; + if (last_block_len) { + prev_block = avp->val + blocks_left * MD5_DIGEST_LENGTH; + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, secret, secret_len); + MD5_Update(&md5_ctx, prev_block, MD5_DIGEST_LENGTH); + MD5_Final(md5, &md5_ctx); + memxor(prev_block + MD5_DIGEST_LENGTH, md5, last_block_len); + prev_block -= MD5_DIGEST_LENGTH; + } else + prev_block = avp->val + (blocks_left - 1) * MD5_DIGEST_LENGTH; + + while (prev_block >= avp->val) { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, secret, secret_len); + MD5_Update(&md5_ctx, prev_block, MD5_DIGEST_LENGTH); + MD5_Final(md5, &md5_ctx); + memxor(prev_block + MD5_DIGEST_LENGTH, md5, MD5_DIGEST_LENGTH); + prev_block -= MD5_DIGEST_LENGTH; + } + memcpy(avp->val, p1, MD5_DIGEST_LENGTH); + + return 0; +} + +int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info, + const char *secret, size_t secret_len) { int n, length; uint8_t *buf; @@ -113,6 +233,8 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info) struct msghdr msg; char msg_control[128]; struct cmsghdr *cmsg; + uint16_t orig_avp_len; + void *orig_avp_val; *p = NULL; @@ -253,51 +375,65 @@ int l2tp_recv(int fd, struct l2tp_packet_t **p, struct in_pktinfo *pkt_info) if (conf_verbose) log_warn("l2tp: incorrect avp received (type=%i, H=1, but Random-Vector is not received)\n", ntohs(avp->type)); goto out_err; - } else { - if (conf_verbose) - log_warn("l2tp: hidden avp received (type=%i)\n", ntohs(avp->type)); } + if (secret == NULL || secret_len == 0) { + log_error("l2tp: impossible to decode" + " hidden avp (type %hu): no" + " secret set)\n", + ntohs(avp->type)); + goto out_err; + } + if (decode_avp(avp, RV, secret, secret_len) < 0) + goto out_err; } attr = mempool_alloc(attr_pool); memset(attr, 0, sizeof(*attr)); list_add_tail(&attr->entry, &pack->attrs); + if (avp->H) { + orig_avp_len = ntohs(*(uint16_t *)avp->val) + sizeof(*avp); + orig_avp_val = avp->val + sizeof(uint16_t); + } else { + orig_avp_len = avp->length; + orig_avp_val = avp->val; + } + attr->attr = da; attr->M = avp->M; - attr->H = avp->H; - attr->length = avp->length - sizeof(*avp); - + attr->H = 0; + attr->length = orig_avp_len - sizeof(*avp); + if (attr->attr->id == Random_Vector) RV = attr; switch (da->type) { case ATTR_TYPE_INT16: - if (avp->length != sizeof(*avp) + 2) + if (orig_avp_len != sizeof(*avp) + 2) goto out_err_len; - attr->val.uint16 = ntohs(*(uint16_t *)avp->val); + attr->val.uint16 = ntohs(*(uint16_t *)orig_avp_val); break; case ATTR_TYPE_INT32: - if (avp->length != sizeof(*avp) + 4) + if (orig_avp_len != sizeof(*avp) + 4) goto out_err_len; - attr->val.uint32 = ntohl(*(uint32_t *)avp->val); + attr->val.uint32 = ntohl(*(uint32_t *)orig_avp_val); break; case ATTR_TYPE_INT64: - if (avp->length != sizeof(*avp) + 8) + if (orig_avp_len != sizeof(*avp) + 8) goto out_err_len; - attr->val.uint64 = be64toh(*(uint64_t *)avp->val); + attr->val.uint64 = be64toh(*(uint64_t *)orig_avp_val); break; case ATTR_TYPE_OCTETS: attr->val.octets = _malloc(attr->length); if (!attr->val.octets) goto out_err_mem; - memcpy(attr->val.octets, avp->val, attr->length); + memcpy(attr->val.octets, orig_avp_val, attr->length); break; case ATTR_TYPE_STRING: attr->val.string = _malloc(attr->length + 1); if (!attr->val.string) goto out_err_mem; - memcpy(attr->val.string, avp->val, attr->length); + memcpy(attr->val.string, orig_avp_val, attr->length); attr->val.string[attr->length] = 0; break; } @@ -320,7 +456,7 @@ out_err_hdr: return 0; out_err_len: if (conf_verbose) - log_warn("l2tp: incorrect avp received (type=%i, incorrect length %i)\n", ntohs(avp->type), avp->length); + log_warn("l2tp: incorrect avp received (type=%i, incorrect length %i)\n", ntohs(avp->type), orig_avp_len); goto out_err; out_err_mem: log_emerg("l2tp: out of memory\n"); -- cgit v1.2.3 From b041db6c60fb80846ad6b6770adc3971e41b2194 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:43:04 +0200 Subject: utils: Add random buffer generation function Define and export the u_randbuf() function that fills a buffer with random data. Convert L2TP's challenge generation code for using it. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.c | 27 +++++++++------------------ accel-pppd/utils.c | 30 ++++++++++++++++++++++++++++++ accel-pppd/utils.h | 1 + 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 267a47f8..c6ba82ff 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -223,8 +223,7 @@ static int l2tp_tunnel_genchall(uint16_t chall_len, struct l2tp_packet_t *pack) { void *ptr = NULL; - size_t urandlen; - ssize_t rdlen; + int err; if (chall_len == 0 || conf_secret == NULL || conf_secret_len == 0) { @@ -248,26 +247,18 @@ static int l2tp_tunnel_genchall(uint16_t chall_len, conn->challenge_len = chall_len; } - for (urandlen = 0; urandlen < chall_len; urandlen += rdlen) { - rdlen = read(urandom_fd, conn->challenge + urandlen, - chall_len - urandlen); - if (rdlen < 0) { - if (errno == EINTR) - rdlen = 0; - else { - log_tunnel(log_error, conn, - "impossible to generate Challenge:" - " reading from urandom failed: %s\n", - strerror(errno)); - goto err; - } - } else if (rdlen == 0) { + if (u_randbuf(conn->challenge, chall_len, &err) < 0) { + if (err) + log_tunnel(log_error, conn, + "impossible to generate Challenge:" + " reading from urandom failed: %s\n", + strerror(err)); + else log_tunnel(log_error, conn, "impossible to generate Challenge:" " end of file reached while reading" " from urandom\n"); - goto err; - } + goto err; } if (l2tp_packet_add_octets(pack, Challenge, conn->challenge, diff --git a/accel-pppd/utils.c b/accel-pppd/utils.c index a6a76c93..45e8709c 100644 --- a/accel-pppd/utils.c +++ b/accel-pppd/utils.c @@ -1,12 +1,15 @@ #include #include #include +#include #include "triton.h" #include "utils.h" #include "memdebug.h" +extern int urandom_fd; + void __export u_inet_ntoa(in_addr_t addr, char *str) { sprintf(str, "%i.%i.%i.%i", addr & 0xff, (addr >> 8) & 0xff, (addr >> 16) & 0xff, (addr >> 24) & 0xff); @@ -30,3 +33,30 @@ int __export u_readlong(long int *dst, const char *src, return 0; } } + +int __export u_randbuf(void *buf, size_t buf_len, int *err) +{ + uint8_t *u8buf = buf; + ssize_t rd_len; + + while (buf_len) { + rd_len = read(urandom_fd, u8buf, buf_len); + if (rd_len < 0) { + if (errno == EINTR) + rd_len = 0; + else { + if (err) + *err = errno; + return -1; + } + } else if (rd_len == 0) { + if (err) + *err = 0; + return -1; + } + u8buf += rd_len; + buf_len -= rd_len; + } + + return 0; +} diff --git a/accel-pppd/utils.h b/accel-pppd/utils.h index bb1a00af..be62f6a3 100644 --- a/accel-pppd/utils.h +++ b/accel-pppd/utils.h @@ -5,5 +5,6 @@ void u_inet_ntoa(in_addr_t, char *str); int u_readlong(long int *dst, const char *src, long int min, long int max); +int u_randbuf(void *buf, size_t buf_len, int *err); #endif -- cgit v1.2.3 From 9ec0d6beda4249ca0483e461bd5fb361f7a28efa Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:43:12 +0200 Subject: l2tp: Full hidden AVPs support Add option "hide-avps" in the "l2tp" section for hiding attributes sent to peer. This same option is also made available on accel-ppp's command line interface: accel-ppp# tunnel create tunnel peer-addr 192.0.2.1 hide-avps 1 Attribute hiding is performed upon attribute creation (in the l2tp_packet_add_*() functions family) rather than upon packet sending. This avoid running the cipher for every retransmission; the counterpart is that l2tp_packet_print() can't dump original attributes of hidden AVPs. Currently, only one random vector is used for all hidden AVPs in a packet. This is easily extensible though, as the 'last_RV' field in struct l2tp_packet_t may be overridden to use new vectors for next AVPs. Signed-off-by: Guillaume Nault --- accel-pppd/accel-ppp.conf.5 | 6 + accel-pppd/ctrl/l2tp/l2tp.c | 64 +++++++--- accel-pppd/ctrl/l2tp/l2tp.h | 7 +- accel-pppd/ctrl/l2tp/packet.c | 289 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 318 insertions(+), 48 deletions(-) diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5 index 08540215..636fe184 100644 --- a/accel-pppd/accel-ppp.conf.5 +++ b/accel-pppd/accel-ppp.conf.5 @@ -424,6 +424,12 @@ is greater of zero then l2tp module will produce verbose logging. .TP .BI "secret=" string Specifies secret to connect to server. +.TP +.BI "hide-avps=" n +If this option is given and +.B n +is greater than 0, then attributes sent in L2TP packets will be hidden (for +AVPs that support it). .SH [radius] .br Configuration of RADIUS module. diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index c6ba82ff..d2c6982d 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -49,6 +49,7 @@ #define STATE_CLOSE 0 int conf_verbose = 0; +int conf_hide_avps = 0; int conf_avp_permissive = 0; static int conf_timeout = 60; static int conf_rtimeout = 5; @@ -78,6 +79,7 @@ struct l2tp_sess_t int state1; uint16_t lns_mode:1; + uint16_t hide_avps:1; struct triton_context_t sctx; struct triton_timer_t timeout_timer; @@ -101,6 +103,7 @@ struct l2tp_conn_t uint16_t peer_tid; uint32_t framing_cap; uint16_t lns_mode:1; + uint16_t hide_avps:1; uint16_t port_set:1; uint16_t challenge_len; uint8_t *challenge; @@ -414,7 +417,8 @@ static int l2tp_send_StopCCN(struct l2tp_conn_t *conn, res, err); pack = l2tp_packet_alloc(2, Message_Type_Stop_Ctrl_Conn_Notify, - &conn->peer_addr); + &conn->peer_addr, conn->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_tunnel(log_error, conn, "impossible to send StopCCN:" " packet allocation failed\n"); @@ -456,7 +460,8 @@ static int l2tp_send_CDN(struct l2tp_sess_t *sess, uint16_t res, uint16_t err) res, err); pack = l2tp_packet_alloc(2, Message_Type_Call_Disconnect_Notify, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_session(log_error, sess, "impossible to send CDN:" " packet allocation failed\n"); @@ -500,7 +505,8 @@ static int l2tp_tunnel_send_CDN(uint16_t sid, uint16_t peer_sid, res, err); pack = l2tp_packet_alloc(2, Message_Type_Call_Disconnect_Notify, - &conn->peer_addr); + &conn->peer_addr, conn->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_tunnel(log_error, conn, "impossible to send CDN:" " packet allocation failed\n"); @@ -863,6 +869,7 @@ static struct l2tp_sess_t *l2tp_tunnel_alloc_session(struct l2tp_conn_t *conn) sess->peer_sid = 0; sess->state1 = STATE_CLOSE; sess->lns_mode = conn->lns_mode; + sess->hide_avps = conn->hide_avps; sess->sctx.before_switch = log_switch; sess->sctx.close = l2tp_sess_close; @@ -990,7 +997,8 @@ err: static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, const struct sockaddr_in *host, uint32_t framing_cap, - int lns_mode, int port_set) + int lns_mode, int port_set, + int hide_avps) { struct l2tp_conn_t *conn; socklen_t hostaddrlen = sizeof(conn->host_addr); @@ -1119,6 +1127,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, conn->sess_count = 0; conn->lns_mode = lns_mode; conn->port_set = port_set; + conn->hide_avps = hide_avps; return conn; @@ -1458,7 +1467,7 @@ static int l2tp_send_ZLB(struct l2tp_conn_t *conn) log_tunnel(log_debug, conn, "sending ZLB\n"); - pack = l2tp_packet_alloc(2, 0, &conn->peer_addr); + pack = l2tp_packet_alloc(2, 0, &conn->peer_addr, 0, NULL, 0); if (!pack) { log_tunnel(log_error, conn, "impossible to send ZLB:" " packet allocation failed\n"); @@ -1481,7 +1490,8 @@ static void l2tp_send_HELLO(struct triton_timer_t *t) log_tunnel(log_debug, conn, "sending HELLO\n"); - pack = l2tp_packet_alloc(2, Message_Type_Hello, &conn->peer_addr); + pack = l2tp_packet_alloc(2, Message_Type_Hello, &conn->peer_addr, + conn->hide_avps, conf_secret, conf_secret_len); if (!pack) { log_tunnel(log_error, conn, "impossible to send HELLO:" " packet allocation failed\n"); @@ -1501,7 +1511,8 @@ static void l2tp_send_SCCRQ(void *peer_addr) log_tunnel(log_info2, conn, "sending SCCRQ\n"); pack = l2tp_packet_alloc(2, Message_Type_Start_Ctrl_Conn_Request, - &conn->peer_addr); + &conn->peer_addr, conn->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_tunnel(log_error, conn, "impossible to send SCCRQ:" " packet allocation failed\n"); @@ -1566,7 +1577,8 @@ static void l2tp_send_SCCRP(struct l2tp_conn_t *conn) log_tunnel(log_info2, conn, "sending SCCRP\n"); pack = l2tp_packet_alloc(2, Message_Type_Start_Ctrl_Conn_Reply, - &conn->peer_addr); + &conn->peer_addr, conn->hide_avps, + conf_secret, conf_secret_len); if (!pack) { log_tunnel(log_error, conn, "impossible to send SCCRP:" " packet allocation failed\n"); @@ -1637,7 +1649,8 @@ static int l2tp_send_SCCCN(struct l2tp_conn_t *conn) log_tunnel(log_info2, conn, "sending SCCCN\n"); pack = l2tp_packet_alloc(2, Message_Type_Start_Ctrl_Conn_Connected, - &conn->peer_addr); + &conn->peer_addr, conn->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_tunnel(log_error, conn, "impossible to send SCCCN:" " packet allocation failed\n"); @@ -1673,7 +1686,8 @@ static int l2tp_send_ICRQ(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending ICRQ\n"); pack = l2tp_packet_alloc(2, Message_Type_Incoming_Call_Request, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_session(log_error, sess, "impossible to send ICRQ:" " packet allocation failed\n"); @@ -1712,7 +1726,8 @@ static int l2tp_send_ICRP(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending ICRP\n"); pack = l2tp_packet_alloc(2, Message_Type_Incoming_Call_Reply, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (!pack) { log_session(log_error, sess, "impossible to send ICRP:" " packet allocation failed\n"); @@ -1746,7 +1761,8 @@ static int l2tp_send_ICCN(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending ICCN\n"); pack = l2tp_packet_alloc(2, Message_Type_Incoming_Call_Connected, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (pack == 0) { log_session(log_error, sess, "impossible to send ICCN:" " packet allocation failed\n"); @@ -1790,7 +1806,8 @@ static int l2tp_send_OCRQ(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending OCRQ\n"); pack = l2tp_packet_alloc(2, Message_Type_Outgoing_Call_Request, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (!pack) { log_session(log_error, sess, "impossible to send OCRQ:" " packet allocation failed\n"); @@ -1854,7 +1871,8 @@ static int l2tp_send_OCRP(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending OCRP\n"); pack = l2tp_packet_alloc(2, Message_Type_Outgoing_Call_Reply, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_session(log_error, sess, "impossible to send OCRP:" " packet allocation failed\n"); @@ -1888,7 +1906,8 @@ static int l2tp_send_OCCN(struct l2tp_sess_t *sess) log_session(log_info2, sess, "sending OCCN\n"); pack = l2tp_packet_alloc(2, Message_Type_Outgoing_Call_Connected, - &sess->paren_conn->peer_addr); + &sess->paren_conn->peer_addr, sess->hide_avps, + conf_secret, conf_secret_len); if (pack == NULL) { log_session(log_error, sess, "impossible to send OCCN:" " packet allocation failed\n"); @@ -2008,7 +2027,8 @@ static int l2tp_recv_SCCRQ(const struct l2tp_serv_t *serv, host_addr.sin_port = 0; conn = l2tp_tunnel_alloc(&pack->addr, &host_addr, - framing_cap->val.uint32, 1, 1); + framing_cap->val.uint32, 1, 1, + conf_hide_avps); if (conn == NULL) { log_error("l2tp: impossible to handle SCCRQ from %s:" " tunnel allocation failed\n", src_addr); @@ -3533,6 +3553,7 @@ static int l2tp_create_tunnel_exec(const char *cmd, char * const *fields, int peer_indx = -1; int host_indx = -1; int lns_mode = 0; + int hide_avps = conf_hide_avps; uint16_t tid; int indx; @@ -3575,6 +3596,9 @@ static int l2tp_create_tunnel_exec(const char *cmd, char * const *fields, fields[indx]); return CLI_CMD_INVAL; } + } else if (strcmp("hide-avps", fields[indx]) == 0) { + ++indx; + hide_avps = atoi(fields[indx]) > 0; } else { cli_sendv(client, "invalid option: \"%s\"\r\n", fields[indx]); @@ -3598,7 +3622,7 @@ static int l2tp_create_tunnel_exec(const char *cmd, char * const *fields, return CLI_CMD_INVAL; } - conn = l2tp_tunnel_alloc(&peer, &host, 3, lns_mode, 0); + conn = l2tp_tunnel_alloc(&peer, &host, 3, lns_mode, 0, hide_avps); if (conn == NULL) { cli_send(client, "tunnel allocation failed\r\n"); return CLI_CMD_FAILED; @@ -3668,7 +3692,7 @@ static void l2tp_create_tunnel_help(char * const *fields, int fields_cnt, { cli_send(client, "l2tp create tunnel peer-addr [host-addr ]" - " [mode ]" + " [hide-avps <0|1>] [mode ]" " - initiate new tunnel to peer\r\n"); } @@ -3694,6 +3718,10 @@ static void load_config(void) if (opt && atoi(opt) >= 0) conf_verbose = atoi(opt) > 0; + opt = conf_get_opt("l2tp", "hide-avps"); + if (opt && atoi(opt) >= 0) + conf_hide_avps = atoi(opt) > 0; + opt = conf_get_opt("l2tp", "avp_permissive"); if (opt && atoi(opt) >= 0) conf_avp_permissive = atoi(opt) > 0; diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 82dafd72..566212a3 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.h +++ b/accel-pppd/ctrl/l2tp/l2tp.h @@ -63,6 +63,10 @@ struct l2tp_packet_t struct sockaddr_in addr; struct l2tp_hdr_t hdr; struct list_head attrs; + struct l2tp_attr_t *last_RV; + const char *secret; + size_t secret_len; + int hide_avps; }; extern int conf_verbose; @@ -79,7 +83,8 @@ void l2tp_packet_free(struct l2tp_packet_t *); void l2tp_packet_print(const struct l2tp_packet_t *, void (*print)(const char *fmt, ...)); struct l2tp_packet_t *l2tp_packet_alloc(int ver, int msg_type, - const struct sockaddr_in *addr); + const struct sockaddr_in *addr, int H, + const char *secret, size_t secret_len); int l2tp_packet_send(int sock, struct l2tp_packet_t *); int l2tp_packet_add_int16(struct l2tp_packet_t *pack, int id, int16_t val, int M); int l2tp_packet_add_int32(struct l2tp_packet_t *pack, int id, int32_t val, int M); diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 5a408405..9bd11eb1 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -13,6 +13,7 @@ #include "log.h" #include "mempool.h" #include "memdebug.h" +#include "utils.h" #include "l2tp.h" #include "attr_defs.h" @@ -40,6 +41,8 @@ void l2tp_packet_print(const struct l2tp_packet_t *pack, val = l2tp_dict_find_value(attr->attr, attr->val); if (val) print(" %s", val->name); + else if (attr->H) + print(" (hidden, %hu bytes)", attr->length); else { switch (attr->attr->type) { case ATTR_TYPE_INT16: @@ -60,7 +63,8 @@ void l2tp_packet_print(const struct l2tp_packet_t *pack, } struct l2tp_packet_t *l2tp_packet_alloc(int ver, int msg_type, - const struct sockaddr_in *addr) + const struct sockaddr_in *addr, int H, + const char *secret, size_t secret_len) { struct l2tp_packet_t *pack = mempool_alloc(pack_pool); if (!pack) @@ -73,6 +77,9 @@ struct l2tp_packet_t *l2tp_packet_alloc(int ver, int msg_type, pack->hdr.L = 1; pack->hdr.S = 1; memcpy(&pack->addr, addr, sizeof(*addr)); + pack->hide_avps = H; + pack->secret = secret; + pack->secret_len = secret_len; if (msg_type) { if (l2tp_packet_add_int16(pack, Message_Type, msg_type, 1)) { @@ -90,7 +97,8 @@ void l2tp_packet_free(struct l2tp_packet_t *pack) while (!list_empty(&pack->attrs)) { attr = list_entry(pack->attrs.next, typeof(*attr), entry); - if (attr->attr->type == ATTR_TYPE_OCTETS || attr->attr->type == ATTR_TYPE_STRING) + if (attr->H || attr->attr->type == ATTR_TYPE_OCTETS + || attr->attr->type == ATTR_TYPE_STRING) _free(attr->val.octets); list_del(&attr->entry); mempool_free(attr); @@ -493,7 +501,10 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) avp->H = attr->H; avp->length = sizeof(*avp) + attr->length; *(uint16_t *)ptr = htons(*(uint16_t *)ptr); - switch (attr->attr->type) { + if (attr->H) + memcpy(avp->val, attr->val.octets, attr->length); + else + switch (attr->attr->type) { case ATTR_TYPE_INT16: *(int16_t *)avp->val = htons(attr->val.int16); break; @@ -504,8 +515,7 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) case ATTR_TYPE_OCTETS: memcpy(avp->val, attr->val.string, attr->length); break; - } - + } ptr += sizeof(*avp) + attr->length; len += sizeof(*avp) + attr->length; } @@ -536,7 +546,120 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) return 0; } -static struct l2tp_attr_t *attr_alloc(int id, int M) +int encode_attr(const struct l2tp_packet_t *pack, struct l2tp_attr_t *attr, + const void *val, uint16_t val_len) +{ + uint8_t *u8_ptr = NULL; + uint8_t md5[MD5_DIGEST_LENGTH]; + MD5_CTX md5_ctx; + uint16_t pad_len; + uint16_t attr_type; + uint16_t blocks_left; + uint16_t last_block_len; + int err; + + if (pack->secret == NULL || pack->secret_len == 0) { + log_error("l2tp: impossible to hide AVP: no secret\n"); + goto err; + } + if (pack->last_RV == NULL) { + log_error("l2tp: impossible to hide AVP: no random vector\n"); + goto err; + } + + if (u_randbuf(&pad_len, sizeof(pad_len), &err) < 0) { + if (err) + log_error("l2tp: impossible to hide AVP:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_error("l2tp: impossible to hide AVP:" + " end of file reached while reading" + " from urandom\n"); + goto err; + } + /* Use at least 16 bytes of padding */ + pad_len = (pad_len & 0x007F) + 16; + + /* Generate Hidden AVP Subformat: + * -original AVP size (2 bytes, network byte order) + * -original AVP value ('val_len' bytes) + * -padding ('pad_len' bytes of random values) + */ + attr->length = sizeof(val_len) + val_len + pad_len; + attr->val.octets = _malloc(attr->length); + if (attr->val.octets == NULL) { + log_error("l2tp: impossible to hide AVP:" + " memory allocation failed\n"); + goto err; + } + + *(uint16_t *)attr->val.octets = htons(val_len); + memcpy(attr->val.octets + sizeof(val_len), val, val_len); + + if (u_randbuf(attr->val.octets + sizeof(val_len) + val_len, + pad_len, &err) < 0) { + if (err) + log_error("l2tp: impossible to hide AVP:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_error("l2tp: impossible to hide AVP:" + " end of file reached while reading" + " from urandom\n"); + goto err_free; + } + + /* Hidden AVP cipher: + * ciphered[0] = clear[0] xor MD5(attr_type, secret, RV) + * ciphered[1] = clear[1] xor MD5(secret, ciphered[0]) + * ... + * ciphered[n] = clear[n] xor MD5(secret, ciphered[n-1]) + */ + attr_type = htons(attr->attr->id); + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, &attr_type, sizeof(attr_type)); + MD5_Update(&md5_ctx, pack->secret, pack->secret_len); + MD5_Update(&md5_ctx, pack->last_RV->val.octets, pack->last_RV->length); + MD5_Final(md5, &md5_ctx); + + if (attr->length <= MD5_DIGEST_LENGTH) { + memxor(attr->val.octets, md5, attr->length); + return 0; + } + + memxor(attr->val.octets, md5, MD5_DIGEST_LENGTH); + + blocks_left = attr->length / MD5_DIGEST_LENGTH - 1; + last_block_len = attr->length % MD5_DIGEST_LENGTH; + + for (u8_ptr = attr->val.octets; blocks_left; --blocks_left) { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, pack->secret, pack->secret_len); + MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH); + MD5_Final(md5, &md5_ctx); + u8_ptr += MD5_DIGEST_LENGTH; + memxor(u8_ptr, md5, MD5_DIGEST_LENGTH); + } + + if (last_block_len) { + MD5_Init(&md5_ctx); + MD5_Update(&md5_ctx, pack->secret, pack->secret_len); + MD5_Update(&md5_ctx, u8_ptr, MD5_DIGEST_LENGTH); + MD5_Final(md5, &md5_ctx); + memxor(u8_ptr + MD5_DIGEST_LENGTH, md5, last_block_len); + } + + return 0; + +err_free: + _free(attr->val.octets); + attr->val.octets = NULL; +err: + return -1; +} + +static struct l2tp_attr_t *attr_alloc(int id, int M, int H) { struct l2tp_attr_t *attr; struct l2tp_dict_attr_t *da; @@ -560,75 +683,183 @@ static struct l2tp_attr_t *attr_alloc(int id, int M) else attr->M = M; - //if (da->H != -1) - //attr->H = da->H; + if (da->H != -1) + attr->H = da->H; + else + attr->H = H; return attr; } +static int l2tp_packet_add_random_vector(struct l2tp_packet_t *pack) +{ + struct l2tp_attr_t *attr = attr_alloc(Random_Vector, 1, 0); + uint16_t ranvec_len; + int err; + + if (!attr) + goto err; + + if (u_randbuf(&ranvec_len, sizeof(ranvec_len), &err) < 0) { + if (err) + log_error("l2tp: impossible to build Random Vector:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_error("l2tp: impossible to build Random Vector:" + " end of file reached while reading" + " from urandom\n"); + goto err_attr; + } + /* RFC 2661 recommends that Random Vector be least 16 bytes long */ + ranvec_len = (ranvec_len & 0x007F) + 16; + + attr->length = ranvec_len; + attr->val.octets = _malloc(ranvec_len); + if (!attr->val.octets) { + log_emerg("l2tp: out of memory\n"); + goto err_attr; + } + + if (u_randbuf(attr->val.octets, ranvec_len, &err) < 0) { + if (err) + log_error("l2tp: impossible to build Random Vector:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_error("l2tp: impossible to build Random Vector:" + " end of file reached while reading" + " from urandom\n"); + goto err_attr_val; + } + + list_add_tail(&attr->entry, &pack->attrs); + pack->last_RV = attr; + + return 0; + +err_attr_val: + _free(attr->val.octets); +err_attr: + mempool_free(attr); +err: + return -1; +} + int l2tp_packet_add_int16(struct l2tp_packet_t *pack, int id, int16_t val, int M) { - struct l2tp_attr_t *attr = attr_alloc(id, M); + struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); if (!attr) return -1; - attr->length = 2; - attr->val.int16 = val; + if (attr->H) { + if (pack->last_RV == NULL) + if (l2tp_packet_add_random_vector(pack) < 0) + goto err; + val = htons(val); + if (encode_attr(pack, attr, &val, sizeof(val)) < 0) + goto err; + } else { + attr->length = sizeof(val); + attr->val.int16 = val; + } list_add_tail(&attr->entry, &pack->attrs); return 0; + +err: + mempool_free(attr); + return -1; } + int l2tp_packet_add_int32(struct l2tp_packet_t *pack, int id, int32_t val, int M) { - struct l2tp_attr_t *attr = attr_alloc(id, M); + struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); if (!attr) return -1; - attr->length = 4; - attr->val.int32 = val; + if (attr->H) { + if (pack->last_RV == NULL) + if (l2tp_packet_add_random_vector(pack) < 0) + goto err; + val = htonl(val); + if (encode_attr(pack, attr, &val, sizeof(val)) < 0) + goto err; + } else { + attr->length = sizeof(val); + attr->val.int32 = val; + } list_add_tail(&attr->entry, &pack->attrs); return 0; + +err: + mempool_free(attr); + return -1; } + int l2tp_packet_add_string(struct l2tp_packet_t *pack, int id, const char *val, int M) { - struct l2tp_attr_t *attr = attr_alloc(id, M); + struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); + size_t val_len = strlen(val); if (!attr) return -1; - attr->length = strlen(val); - attr->val.string = _strdup(val); - if (!attr->val.string) { - log_emerg("l2tp: out of memory\n"); - mempool_free(attr); - return -1; + if (attr->H) { + if (pack->last_RV == NULL) + if (l2tp_packet_add_random_vector(pack) < 0) + goto err; + if (encode_attr(pack, attr, val, val_len) < 0) + goto err; + } else { + attr->length = val_len; + attr->val.string = _strdup(val); + if (!attr->val.string) { + log_emerg("l2tp: out of memory\n"); + goto err; + } } list_add_tail(&attr->entry, &pack->attrs); return 0; + +err: + mempool_free(attr); + return -1; } int l2tp_packet_add_octets(struct l2tp_packet_t *pack, int id, const uint8_t *val, int size, int M) { - struct l2tp_attr_t *attr = attr_alloc(id, M); + struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); if (!attr) return -1; - attr->length = size; - attr->val.octets = _malloc(size); - if (!attr->val.octets) { - log_emerg("l2tp: out of memory\n"); - mempool_free(attr); - return -1; + if (attr->H) { + if (pack->last_RV == NULL) + if (l2tp_packet_add_random_vector(pack) < 0) + goto err; + if (encode_attr(pack, attr, val, size) < 0) + goto err; + } else { + attr->length = size; + attr->val.octets = _malloc(size); + if (!attr->val.octets) { + log_emerg("l2tp: out of memory\n"); + goto err; + } + memcpy(attr->val.octets, val, attr->length); } - memcpy(attr->val.octets, val, attr->length); list_add_tail(&attr->entry, &pack->attrs); return 0; + +err: + mempool_free(attr); + return -1; } static void init(void) -- cgit v1.2.3 From 967f5689f8a38e7ac5e095e6c26edd765611f0e3 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:43:16 +0200 Subject: l2tp: Use random length Challenge AVP Set Challenge attribute using a random length so that its size can't be guessed when hide-avps is on. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index d2c6982d..19b90ab4 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -1507,6 +1507,8 @@ static void l2tp_send_SCCRQ(void *peer_addr) { struct l2tp_conn_t *conn = l2tp_tunnel_self(); struct l2tp_packet_t *pack = NULL; + uint16_t chall_len; + int err; log_tunnel(log_info2, conn, "sending SCCRQ\n"); @@ -1548,7 +1550,19 @@ static void l2tp_send_SCCRQ(void *peer_addr) goto pack_err; } - if (l2tp_tunnel_genchall(MD5_DIGEST_LENGTH, conn, pack) < 0) { + if (u_randbuf(&chall_len, sizeof(chall_len), &err) < 0) { + if (err) + log_tunnel(log_error, conn, "impossible to send SCCRQ:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_tunnel(log_error, conn, "impossible to send SCCRQ:" + " end of file reached while reading" + " from urandom\n"); + goto pack_err; + } + chall_len = (chall_len & 0x007F) + MD5_DIGEST_LENGTH; + if (l2tp_tunnel_genchall(chall_len, conn, pack) < 0) { log_tunnel(log_error, conn, "impossible to send SCCRQ:" " Challenge generation failed\n"); goto pack_err; @@ -1573,6 +1587,8 @@ err: static void l2tp_send_SCCRP(struct l2tp_conn_t *conn) { struct l2tp_packet_t *pack; + uint16_t chall_len; + int err; log_tunnel(log_info2, conn, "sending SCCRP\n"); @@ -1620,7 +1636,20 @@ static void l2tp_send_SCCRP(struct l2tp_conn_t *conn) " Challenge Response generation failed\n"); goto out_err; } - if (l2tp_tunnel_genchall(MD5_DIGEST_LENGTH, conn, pack) < 0) { + + if (u_randbuf(&chall_len, sizeof(chall_len), &err) < 0) { + if (err) + log_tunnel(log_error, conn, "impossible to send SCCRP:" + " reading from urandom failed: %s\n", + strerror(err)); + else + log_tunnel(log_error, conn, "impossible to send SCCRP:" + " end of file reached while reading" + " from urandom\n"); + goto out_err; + } + chall_len = (chall_len & 0x007F) + MD5_DIGEST_LENGTH; + if (l2tp_tunnel_genchall(chall_len, conn, pack) < 0) { log_tunnel(log_error, conn, "impossible to send SCCRP:" " Challenge generation failed\n"); goto out_err; -- cgit v1.2.3 From d3ad0e29c57c74c93db97f165bfa43024e180db3 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 9 Apr 2013 21:43:21 +0200 Subject: l2tp: Handle 64 bits attributes Define l2tp_packet_add_int64() to create attributes of 64 bits long integers. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.h | 1 + accel-pppd/ctrl/l2tp/packet.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 566212a3..b816bc85 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.h +++ b/accel-pppd/ctrl/l2tp/l2tp.h @@ -88,6 +88,7 @@ struct l2tp_packet_t *l2tp_packet_alloc(int ver, int msg_type, int l2tp_packet_send(int sock, struct l2tp_packet_t *); int l2tp_packet_add_int16(struct l2tp_packet_t *pack, int id, int16_t val, int M); int l2tp_packet_add_int32(struct l2tp_packet_t *pack, int id, int32_t val, int M); +int l2tp_packet_add_int64(struct l2tp_packet_t *pack, int id, int64_t val, int M); int l2tp_packet_add_string(struct l2tp_packet_t *pack, int id, const char *val, int M); int l2tp_packet_add_octets(struct l2tp_packet_t *pack, int id, const uint8_t *val, int size, int M); diff --git a/accel-pppd/ctrl/l2tp/packet.c b/accel-pppd/ctrl/l2tp/packet.c index 9bd11eb1..e4278a3c 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -511,6 +511,9 @@ int l2tp_packet_send(int sock, struct l2tp_packet_t *pack) case ATTR_TYPE_INT32: *(int32_t *)avp->val = htonl(attr->val.int32); break; + case ATTR_TYPE_INT64: + *(uint64_t *)avp->val = htobe64(attr->val.uint64); + break; case ATTR_TYPE_STRING: case ATTR_TYPE_OCTETS: memcpy(avp->val, attr->val.string, attr->length); @@ -800,6 +803,33 @@ err: return -1; } +int l2tp_packet_add_int64(struct l2tp_packet_t *pack, int id, int64_t val, int M) +{ + struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); + + if (!attr) + return -1; + + if (attr->H) { + if (pack->last_RV == NULL) + if (l2tp_packet_add_random_vector(pack) < 0) + goto err; + val = htobe64(val); + if (encode_attr(pack, attr, &val, sizeof(val)) < 0) + goto err; + } else { + attr->length = sizeof(val); + attr->val.uint64 = val; + } + list_add_tail(&attr->entry, &pack->attrs); + + return 0; + +err: + mempool_free(attr); + return -1; +} + int l2tp_packet_add_string(struct l2tp_packet_t *pack, int id, const char *val, int M) { struct l2tp_attr_t *attr = attr_alloc(id, M, pack->hide_avps); -- cgit v1.2.3