diff options
author | Kozlov Dmitry <xeb@mail.ru> | 2013-04-19 13:29:06 +0400 |
---|---|---|
committer | Kozlov Dmitry <xeb@mail.ru> | 2013-04-19 13:29:06 +0400 |
commit | 65159a74bd8e234f24bff44c267c251a080e1f14 (patch) | |
tree | 19561f39824d52c953f0acea26e450bfb31f72bc /accel-pppd/ctrl | |
parent | bff0ea1c99ff68ca6f1ab58dcdfa0054caf42fca (diff) | |
parent | d3ad0e29c57c74c93db97f165bfa43024e180db3 (diff) | |
download | accel-ppp-65159a74bd8e234f24bff44c267c251a080e1f14.tar.gz accel-ppp-65159a74bd8e234f24bff44c267c251a080e1f14.zip |
Merge branch 'master' of ssh://git.code.sf.net/p/accel-ppp/code
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 2 | ||||
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.c | 160 | ||||
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.h | 11 | ||||
-rw-r--r-- | accel-pppd/ctrl/l2tp/packet.c | 489 |
4 files changed, 566 insertions, 96 deletions
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 116e83c..09bd887 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)) diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 7c53e6e..19b90ab 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; @@ -57,6 +58,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; @@ -77,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; @@ -100,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; @@ -222,11 +226,10 @@ 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 || strlen(conf_secret) == 0) { + || conf_secret == NULL || conf_secret_len == 0) { if (conn->challenge) { _free(conn->challenge); conn->challenge = NULL; @@ -247,26 +250,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, @@ -302,7 +297,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 +335,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 +368,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 +395,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:" @@ -422,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"); @@ -464,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"); @@ -508,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"); @@ -871,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; @@ -998,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); @@ -1127,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; @@ -1466,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"); @@ -1489,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"); @@ -1505,11 +1507,14 @@ 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"); 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"); @@ -1545,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; @@ -1570,11 +1587,14 @@ 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"); 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"); @@ -1616,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; @@ -1645,7 +1678,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"); @@ -1681,7 +1715,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"); @@ -1720,7 +1755,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"); @@ -1754,7 +1790,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"); @@ -1798,7 +1835,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"); @@ -1862,7 +1900,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"); @@ -1896,7 +1935,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"); @@ -1962,6 +2002,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; @@ -2014,7 +2056,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); @@ -2079,6 +2122,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: @@ -2214,6 +2258,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; @@ -2312,6 +2357,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; @@ -2425,6 +2471,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: @@ -2519,6 +2566,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; @@ -2676,6 +2724,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: @@ -2775,6 +2824,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; @@ -2838,6 +2888,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; @@ -2902,6 +2953,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; @@ -3113,7 +3165,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, @@ -3320,7 +3373,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) @@ -3528,6 +3582,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; @@ -3570,6 +3625,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]); @@ -3593,7 +3651,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; @@ -3663,7 +3721,7 @@ static void l2tp_create_tunnel_help(char * const *fields, int fields_cnt, { cli_send(client, "l2tp create tunnel peer-addr <ip_addr> [host-addr <ip_addr>]" - " [mode <lac|lns>]" + " [hide-avps <0|1>] [mode <lac|lns>]" " - initiate new tunnel to peer\r\n"); } @@ -3689,6 +3747,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; @@ -3716,8 +3778,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) diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 3c26c9f..b816bc8 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; @@ -73,15 +77,18 @@ 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, ...)); 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); +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 87735a2..e4278a3 100644 --- a/accel-pppd/ctrl/l2tp/packet.c +++ b/accel-pppd/ctrl/l2tp/packet.c @@ -1,16 +1,19 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <endian.h> #include <errno.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <arpa/inet.h> +#include "crypto.h" #include "triton.h" #include "log.h" #include "mempool.h" #include "memdebug.h" +#include "utils.h" #include "l2tp.h" #include "attr_defs.h" @@ -38,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: @@ -58,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) @@ -71,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)) { @@ -88,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); @@ -97,7 +107,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; @@ -112,6 +241,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; @@ -252,51 +383,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 = *(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; } @@ -319,7 +464,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"); @@ -356,19 +501,24 @@ 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; 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); break; - } - + } ptr += sizeof(*avp) + attr->length; len += sizeof(*avp) + attr->length; } @@ -399,7 +549,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; @@ -423,76 +686,210 @@ 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; + + 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_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; - 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 = 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); + 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; + } } - memcpy(attr->val.string, val, attr->length); 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) |