From: Nicolas BESNARD Date: Fri, 31 Jan 2025 13:44:14 +0000 (+0000) Subject: odhcp6c: implement RKAP: Reconfiguration Key Authentication Protocol X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=4839bf6d0feb49eefd1f9b6a0b58a3cff39eff0d;p=project%2Fodhcp6c.git odhcp6c: implement RKAP: Reconfiguration Key Authentication Protocol Problem: odhcp6c doesn't support authentication protocol other than the Configuration Token protocol (RFC 3118). https://www.ietf.org/archive/id/draft-ietf-dhc-rfc8415bis-12.html#name-reconfiguration-key-authent Solution: implement Reconfiguration Key Authentication Protocol (RKAP) and add the option to disable all authentication protocols (will discard all reconfigure messages received). Signed-off-by: Nicolas BESNARD Signed-off-by: Paul Donald Link: https://github.com/openwrt/odhcp6c/pull/106 Signed-off-by: Álvaro Fernández Rojas --- diff --git a/README b/README index 1afed15..e8a725a 100644 --- a/README +++ b/README @@ -127,3 +127,23 @@ The following RPC methods are available: - *req_addresses* (string{try|force|none}) : Request addresses - *req_prefixes* (int) : Request Prefixes (0 = auto) - *stateful_only* (bool) : Discard advertisements without any address or prefix proposed + - *irt_default* (int) : Default information refresh time (expressed in seconds) + - *irt_min* (int) : Minimum information refresh time (expressed in seconds) + - *rand_factor* (int) : Randomization factor for retransmission timeout + - *msg_solicit* (table) : Retransmission settings for SOLICIT + - *msg_request* (table) : Retransmission settings for REQUEST + - *msg_renew* (table) : Retransmission settings for RENEW + - *msg_rebind* (table) : Retransmission settings for REBIND + - *msg_release* (table) : Retransmission settings for RELEASE + - *msg_decline* (table) : Retransmission settings for DECLINE + - *msg_inforeq* (table) : Retransmission settings for INFORMATION-REQUEST + - *auth_protocol* (string) : Authentication protocol to be used ("None","ConfigurationToken", "ReconfigureKeyAuthentication") + - *auth_token* (string) : Authentication token to be used when AuthenticationProtocol is set to ConfigurationToken +* *renew()* : Force transmission of RENEW/INFORMATION-REQUEST messages +* *release()* : Force transmission of RELEASE message and start new cycle + +Input arguments for retransmission settings : + - *delay_max* (int) : Maximum delay of first message (expressed in seconds) + - *timeout_init* (int) : Initial message timeout (expressed in seconds) + - *timeout_max* (int) : Initial message timeout (expressed in seconds) + - *rc_max* (int) : Maximum message retry attempts \ No newline at end of file diff --git a/src/config.c b/src/config.c index b0ba835..dc07d2c 100644 --- a/src/config.c +++ b/src/config.c @@ -108,6 +108,9 @@ void config_dhcp_reset(void) { config_dhcp.irt_default = DHCPV6_IRT_DEFAULT; config_dhcp.irt_min = DHCPV6_IRT_MIN; config_dhcp.rand_factor = DHCPV6_RAND_FACTOR; + config_dhcp.auth_protocol = AUTH_PROT_RKAP; + free(config_dhcp.auth_token); + config_dhcp.auth_token = NULL; } void config_set_release(bool enable) { @@ -294,6 +297,30 @@ bool config_set_rand_factor(unsigned int value) return true; } +bool config_set_auth_protocol(const char* protocol) +{ + if (!strcmp(protocol, "None")) { + config_dhcp.auth_protocol = AUTH_PROT_NONE; + } else if (!strcmp(protocol, "ConfigurationToken")) { + config_dhcp.auth_protocol = AUTH_PROT_TOKEN; + } else if (!strcmp(protocol, "ReconfigureKeyAuthentication")) { + config_dhcp.auth_protocol = AUTH_PROT_RKAP; + } else { + syslog(LOG_ERR, "Invalid Authentication protocol"); + return false; + } + + return true; +} + +bool config_set_auth_token(const char* token) +{ + free(config_dhcp.auth_token); + + config_dhcp.auth_token = strdup(token); + return config_dhcp.auth_token != NULL; +} + static int config_parse_opt_u8(const char *src, uint8_t **dst) { int len = strlen(src); diff --git a/src/config.h b/src/config.h index f0edade..8356360 100644 --- a/src/config.h +++ b/src/config.h @@ -97,6 +97,8 @@ struct config_dhcp { uint32_t irt_default; uint32_t irt_min; uint16_t rand_factor; + enum odhcp6c_auth_protocol auth_protocol; + char* auth_token; }; struct config_dhcp *config_dhcp_get(void); @@ -121,6 +123,8 @@ bool config_set_rtx_rc_max(enum config_dhcp_msg msg, unsigned int value); bool config_set_irt_default(unsigned int value); bool config_set_irt_min(unsigned int value); bool config_set_rand_factor(unsigned int value); +bool config_set_auth_protocol(const char* protocol); +bool config_set_auth_token(const char* token); int config_add_opt(const uint16_t code, const uint8_t *data, const uint16_t len); int config_parse_opt_data(const char *data, uint8_t **dst, const unsigned int type, const bool array); diff --git a/src/dhcpv6.c b/src/dhcpv6.c index d97a732..2640b77 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -251,7 +251,8 @@ static struct in6_addr server_addr = IN6ADDR_ANY_INIT; static enum dhcpv6_state dhcpv6_state = DHCPV6_INIT; static int dhcpv6_state_timeout = 0; -// Reconfigure key +// Authentication options +static enum odhcp6c_auth_protocol auth_protocol = AUTH_PROT_RKAP; static uint8_t reconf_key[16]; // client options @@ -554,6 +555,7 @@ int init_dhcpv6(const char *ifname) na_mode = config_dhcp->ia_na_mode; pd_mode = config_dhcp->ia_pd_mode; stateful_only_mode = config_dhcp->stateful_only_mode; + auth_protocol = config_dhcp->auth_protocol; sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); if (sock < 0) @@ -1040,7 +1042,7 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len, rcmsg = DHCPV6_MSG_UNKNOWN; uint16_t otype, olen = UINT16_MAX; bool clientid_ok = false, serverid_ok = false, rcauth_ok = false, - ia_present = false, options_valid = true; + auth_present = false, ia_present = false, options_valid = true; size_t client_id_len, server_id_len; void *client_id = odhcp6c_get_state(STATE_CLIENT_ID, &client_id_len); @@ -1056,40 +1058,57 @@ static bool dhcpv6_response_is_valid(const void *buf, ssize_t len, &odata[-4], server_id, server_id_len); else serverid_ok = true; - } else if (otype == DHCPV6_OPT_AUTH && olen == -4 + - sizeof(struct dhcpv6_auth_reconfigure)) { - struct dhcpv6_auth_reconfigure *r = (void*)&odata[-4]; - if (r->protocol != 3 || r->algorithm != 1 || r->reconf_type != 2) + } else if (otype == DHCPV6_OPT_AUTH) { + struct dhcpv6_auth *r = (void*)&odata[-4]; + if (auth_present) { + options_valid = false; continue; + } - md5_ctx_t md5; - uint8_t serverhash[16], secretbytes[64]; - uint32_t hash[4]; - memcpy(serverhash, r->key, sizeof(serverhash)); - memset(r->key, 0, sizeof(r->key)); + auth_present = true; + if (auth_protocol == AUTH_PROT_RKAP) { + struct dhcpv6_auth_reconfigure *rkap = (void*)r->data; + if (r->protocol != AUTH_PROT_RKAP || r->algorithm != AUTH_ALG_HMACMD5 || r->len != 28 || rkap->reconf_type != RKAP_TYPE_HMACMD5) + continue; - memset(secretbytes, 0, sizeof(secretbytes)); - memcpy(secretbytes, reconf_key, sizeof(reconf_key)); + md5_ctx_t md5; + uint8_t serverhash[16], secretbytes[64]; + uint32_t hash[4]; + memcpy(serverhash, rkap->key, sizeof(serverhash)); + memset(rkap->key, 0, sizeof(rkap->key)); - for (size_t i = 0; i < sizeof(secretbytes); ++i) - secretbytes[i] ^= 0x36; + memset(secretbytes, 0, sizeof(secretbytes)); + memcpy(secretbytes, reconf_key, sizeof(reconf_key)); - md5_begin(&md5); - md5_hash(secretbytes, sizeof(secretbytes), &md5); - md5_hash(buf, len, &md5); - md5_end(hash, &md5); + for (size_t i = 0; i < sizeof(secretbytes); ++i) + secretbytes[i] ^= 0x36; - for (size_t i = 0; i < sizeof(secretbytes); ++i) { - secretbytes[i] ^= 0x36; - secretbytes[i] ^= 0x5c; - } + md5_begin(&md5); + md5_hash(secretbytes, sizeof(secretbytes), &md5); + md5_hash(buf, len, &md5); + md5_end(hash, &md5); + + for (size_t i = 0; i < sizeof(secretbytes); ++i) { + secretbytes[i] ^= 0x36; + secretbytes[i] ^= 0x5c; + } + + md5_begin(&md5); + md5_hash(secretbytes, sizeof(secretbytes), &md5); + md5_hash(hash, 16, &md5); + md5_end(hash, &md5); - md5_begin(&md5); - md5_hash(secretbytes, sizeof(secretbytes), &md5); - md5_hash(hash, 16, &md5); - md5_end(hash, &md5); + rcauth_ok = !memcmp(hash, serverhash, sizeof(hash)); + } else if (auth_protocol == AUTH_PROT_TOKEN) { + if (r->protocol != AUTH_PROT_TOKEN || r->algorithm != AUTH_ALG_TOKEN || r->len < 12) + continue; + + uint16_t token_len = r->len - 11; + if (config_dhcp->auth_token == NULL || strlen(config_dhcp->auth_token) != token_len) + continue; - rcauth_ok = !memcmp(hash, serverhash, sizeof(hash)); + rcauth_ok = !memcmp(r->data, config_dhcp->auth_token, token_len); + } } else if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1) { rcmsg = odata[0]; } else if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)) { @@ -1419,12 +1438,12 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc, else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4) { refresh = ntohl_unaligned(odata); } else if (otype == DHCPV6_OPT_AUTH) { - if (olen == -4 + sizeof(struct dhcpv6_auth_reconfigure)) { - struct dhcpv6_auth_reconfigure *r = (void*)&odata[-4]; - if (r->protocol == 3 && r->algorithm == 1 && - r->reconf_type == 1) - memcpy(reconf_key, r->key, sizeof(r->key)); - } + struct dhcpv6_auth *r = (void*)&odata[-4]; + if (auth_protocol == AUTH_PROT_RKAP) { + struct dhcpv6_auth_reconfigure *rkap = (void*)r->data; + if (r->protocol == AUTH_PROT_RKAP || r->algorithm == AUTH_ALG_HMACMD5 || r->len == 28 || rkap->reconf_type == RKAP_TYPE_KEY) + memcpy(reconf_key, rkap->key, sizeof(rkap->key)); + } } else if (otype == DHCPV6_OPT_AFTR_NAME && olen > 3) { size_t cur_len; odhcp6c_get_state(STATE_AFTR_NAME, &cur_len); @@ -1951,6 +1970,7 @@ int dhcpv6_promote_server_cand(void) odhcp6c_add_state(STATE_SERVER_ID, hdr, sizeof(hdr)); odhcp6c_add_state(STATE_SERVER_ID, cand->duid, cand->duid_len); accept_reconfig = cand->wants_reconfigure; + memset(reconf_key, 0, sizeof(reconf_key)); if (cand->ia_na_len) { odhcp6c_add_state(STATE_IA_NA, cand->ia_na, cand->ia_na_len); diff --git a/src/odhcp6c.h b/src/odhcp6c.h index b117e8f..a3d9b54 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -279,13 +279,17 @@ struct dhcpv6_duid { uint8_t data[128]; } _packed; -struct dhcpv6_auth_reconfigure { +struct dhcpv6_auth { uint16_t type; uint16_t len; uint8_t protocol; uint8_t algorithm; uint8_t rdm; uint64_t replay; + uint8_t data[]; +} _packed; + +struct dhcpv6_auth_reconfigure { uint8_t reconf_type; uint8_t key[16]; } _packed; @@ -414,6 +418,26 @@ enum odhcp6c_ia_mode { IA_MODE_FORCE, }; +enum odhcp6c_auth_protocol { + AUTH_PROT_NONE = -1, + /* RFC3118 */ + AUTH_PROT_TOKEN = 0, + /* draft-ietf-dhc-rfc8415bis-12 */ + AUTH_PROT_RKAP = 3, +}; + +enum odhcp6c_auth_algorithm { + /* RFC3118 */ + AUTH_ALG_TOKEN = 0, + /* draft-ietf-dhc-rfc8415bis-12 */ + AUTH_ALG_HMACMD5 = 1 +}; + +enum odhcp6c_rkap_type { + /* draft-ietf-dhc-rfc8415bis-12 */ + RKAP_TYPE_KEY = 1, + RKAP_TYPE_HMACMD5 = 2, +}; struct odhcp6c_entry { struct in6_addr router; diff --git a/src/ubus.c b/src/ubus.c index 2573220..7f53ca5 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -122,6 +122,8 @@ enum { RECONFIGURE_DHCP_ATTR_IRT_DEFAULT, RECONFIGURE_DHCP_ATTR_IRT_MIN, RECONFIGURE_DHCP_ATTR_RAND_FACTOR, + RECONFIGURE_DHCP_ATTR_AUTH_PROTO, + RECONFIGURE_DHCP_ATTR_AUTH_TOKEN, RECONFIGURE_DHCP_ATTR_MAX, }; @@ -166,6 +168,8 @@ static const struct blobmsg_policy reconfigure_dhcp_policy[RECONFIGURE_DHCP_ATTR [RECONFIGURE_DHCP_ATTR_IRT_DEFAULT] = { .name = "irt_default", .type = BLOBMSG_TYPE_INT32}, [RECONFIGURE_DHCP_ATTR_IRT_MIN] = { .name = "irt_min", .type = BLOBMSG_TYPE_INT32}, [RECONFIGURE_DHCP_ATTR_RAND_FACTOR] = { .name = "rand_factor", .type = BLOBMSG_TYPE_INT32}, + [RECONFIGURE_DHCP_ATTR_AUTH_PROTO] = { .name = "auth_protocol", .type = BLOBMSG_TYPE_STRING}, + [RECONFIGURE_DHCP_ATTR_AUTH_TOKEN] = { .name = "auth_token", .type = BLOBMSG_TYPE_STRING}, }; static struct ubus_method odhcp6c_object_methods[] = { @@ -905,6 +909,26 @@ static int ubus_handle_reconfigure_dhcp(_unused struct ubus_context *ctx, _unuse valid_args = true; } + if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_PROTO])) { + string = blobmsg_get_string(cur); + + if (string == NULL || !config_set_auth_protocol(string)) + return UBUS_STATUS_INVALID_ARGUMENT; + + need_reinit = true; + valid_args = true; + } + + if ((cur = tb[RECONFIGURE_DHCP_ATTR_AUTH_TOKEN])) { + string = blobmsg_get_string(cur); + + if (string == NULL || !config_set_auth_token(string)) + return UBUS_STATUS_INVALID_ARGUMENT; + + need_reinit = true; + valid_args = true; + } + if (need_reinit) raise(SIGUSR2);