From: Alan T. DeKok Date: Sat, 11 Feb 2023 13:42:05 +0000 (-0500) Subject: make TACACS+ PAP CHAP. and (maybe) MS-CHAP work X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4b7b34fa9e7862d75ddaa9230fb4b7c085df8ea3;p=thirdparty%2Ffreeradius-server.git make TACACS+ PAP CHAP. and (maybe) MS-CHAP work now that the namespaces are parsed correctly for subrequests, we can make more things work automagically --- diff --git a/raddb/policy.d/tacacs b/raddb/policy.d/tacacs index 5635ad1de84..c351c2fefea 100644 --- a/raddb/policy.d/tacacs +++ b/raddb/policy.d/tacacs @@ -13,7 +13,7 @@ tacacs_set_authentication_status { tacacs_pap { subrequest RADIUS.Access-Request { &request.User-Name := &parent.request.User-Name -# &request.User-Password := &parent.request.Data + &request.User-Password := &parent.request.User-Password &control.Password.Cleartext := &parent.control.Password.Cleartext pap.authorize @@ -35,8 +35,8 @@ tacacs_chap { # @todo - update code to create these, so that the # poor user doesn't need to. # -# &request.CHAP-Password := ... -# &request.CHAP-Challenge := ... + &request.CHAP-Password := &parent.request.CHAP-Password + &request.CHAP-Challenge := &parent.request.CHAP-Challenge &control.Password.Cleartext := &parent.control.Password.Cleartext chap.authenticate @@ -49,19 +49,21 @@ tacacs_mschap { subrequest RADIUS.Access-Request { &request.User-Name := &parent.request.User-Name + &control.Password.Cleartext := &parent.control.Password.Cleartext + # - # Data length N is 1 octet of ID, followed by - # N-49 octets of challenge, followed by 49 octets of - # MS-CHAP stuff. + # There's always a challenge # - # @todo - update code to create these, so that the - # poor user doesn't need to. + &request.Vendor-Specific.Microsoft.CHAP-Challenge := &parent.request.MS-CHAP-Challenge + # -# &request.MS-CHAP-Challenge := ... -# &request.MS-CHAP-Response := ... - &control.Password.Cleartext := &parent.control.Password.Cleartext + # One of these two assignments will fail. Using "+=" + # is simpler than checking for it via "if" and then using ":=" + # + &request.Vendor-Specific.Microsoft.CHAP-Response := &parent.request.MS-CHAP-Response + &request.Vendor-Specific.Microsoft.CHAP2-Response := &parent.request.MS-CHAP2-Response - chap.authenticate + mschap.authenticate } tacacs_set_authentication_status diff --git a/share/dictionary/tacacs/dictionary.tacacs b/share/dictionary/tacacs/dictionary.tacacs index 6afd10c53cb..4856c6c028c 100644 --- a/share/dictionary/tacacs/dictionary.tacacs +++ b/share/dictionary/tacacs/dictionary.tacacs @@ -127,3 +127,16 @@ VALUE Accounting-Flags Watchdog 8 VALUE Accounting-Flags Watchdog-Update 10 ATTRIBUTE Argument-List 21 string + +# +# These attributes have similar names to RADIUS attributes, and similar content. +# +# However, they need to be explicitly copied to a RADIUS equivalent in order +# for them to work in RADIUS. +# +ATTRIBUTE User-Password 22 string +ATTRIBUTE CHAP-Password 23 octets[17] +ATTRIBUTE CHAP-Challenge 24 octets +ATTRIBUTE MS-CHAP-Response 25 octets[50] +ATTRIBUTE MS-CHAP2-Response 26 octets[50] +ATTRIBUTE MS-CHAP-Challenge 27 octets diff --git a/src/protocols/tacacs/attrs.h b/src/protocols/tacacs/attrs.h index 4ebaa190023..e4ccb410396 100644 --- a/src/protocols/tacacs/attrs.h +++ b/src/protocols/tacacs/attrs.h @@ -54,3 +54,11 @@ extern HIDDEN fr_dict_attr_t const *attr_tacacs_user_message; extern HIDDEN fr_dict_attr_t const *attr_tacacs_user_name; extern HIDDEN fr_dict_attr_t const *attr_tacacs_version_major; extern HIDDEN fr_dict_attr_t const *attr_tacacs_version_minor; + +extern HIDDEN fr_dict_attr_t const *attr_tacacs_user_name; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_user_password; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_chap_password; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_chap_challenge; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_mschap_response; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_mschap2_response; +extern HIDDEN fr_dict_attr_t const *attr_tacacs_mschap_challenge; diff --git a/src/protocols/tacacs/base.c b/src/protocols/tacacs/base.c index f99bf313cca..799325ea00b 100644 --- a/src/protocols/tacacs/base.c +++ b/src/protocols/tacacs/base.c @@ -67,6 +67,14 @@ fr_dict_attr_t const *attr_tacacs_user_name; fr_dict_attr_t const *attr_tacacs_version_major; fr_dict_attr_t const *attr_tacacs_version_minor; +fr_dict_attr_t const *attr_tacacs_user_name; +fr_dict_attr_t const *attr_tacacs_user_password; +fr_dict_attr_t const *attr_tacacs_chap_password; +fr_dict_attr_t const *attr_tacacs_chap_challenge; +fr_dict_attr_t const *attr_tacacs_mschap_response; +fr_dict_attr_t const *attr_tacacs_mschap2_response; +fr_dict_attr_t const *attr_tacacs_mschap_challenge; + extern fr_dict_attr_autoload_t libfreeradius_tacacs_dict_attr[]; fr_dict_attr_autoload_t libfreeradius_tacacs_dict_attr[] = { { .out = &attr_tacacs_accounting_flags, .name = "Accounting-Flags", .type = FR_TYPE_UINT8, .dict = &dict_tacacs }, @@ -96,6 +104,14 @@ fr_dict_attr_autoload_t libfreeradius_tacacs_dict_attr[] = { { .out = &attr_tacacs_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_tacacs }, { .out = &attr_tacacs_version_major, .name = "Packet.Version-Major", .type = FR_TYPE_UINT8, .dict = &dict_tacacs }, { .out = &attr_tacacs_version_minor, .name = "Packet.Version-Minor", .type = FR_TYPE_UINT8, .dict = &dict_tacacs }, + + { .out = &attr_tacacs_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_tacacs }, + { .out = &attr_tacacs_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_tacacs }, + { .out = &attr_tacacs_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs }, + { .out = &attr_tacacs_chap_challenge, .name = "CHAP-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs }, + { .out = &attr_tacacs_mschap_response, .name = "MS-CHAP-Response", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs }, + { .out = &attr_tacacs_mschap2_response, .name = "MS-CHAP2-Response", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs }, + { .out = &attr_tacacs_mschap_challenge, .name = "MS-CHAP-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_tacacs }, { NULL } }; diff --git a/src/protocols/tacacs/decode.c b/src/protocols/tacacs/decode.c index c8ee750f2ce..cb139dcf470 100644 --- a/src/protocols/tacacs/decode.c +++ b/src/protocols/tacacs/decode.c @@ -484,6 +484,8 @@ ssize_t fr_tacacs_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *bu case FR_TAC_PLUS_AUTHEN: if (packet_is_authen_start_request(pkt)) { uint8_t want; + bool raw; + fr_dict_attr_t const *da, *challenge; /** * 4.1. The Authentication START Packet Body @@ -535,42 +537,97 @@ ssize_t fr_tacacs_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *bu * Check the length on the various * authentication types. */ + raw = false; + challenge = NULL; + switch (pkt->authen_start.authen_type) { default: - want = 255; + raw = true; + want = pkt->authen_start.data_len; + da = attr_tacacs_data; + break; + + case FR_AUTHENTICATION_TYPE_VALUE_PAP: + want = pkt->authen_start.data_len; + da = attr_tacacs_user_password; break; case FR_AUTHENTICATION_TYPE_VALUE_CHAP: - want = 1 + 8 + 16; /* id + 8 octets of challenge + 16 hash */ + want = 1 + 16; /* id + HOPEFULLY 8 octets of challenge + 16 hash */ + da = attr_tacacs_chap_password; + challenge = attr_tacacs_chap_challenge; break; case FR_AUTHENTICATION_TYPE_VALUE_MSCHAP: - want = 1 + 8 + 49; /* id + 8 octets of challenge + 49 MS-CHAP stuff */ + want = 1 + 49; /* id + HOPEFULLY 8 octets of challenge + 49 MS-CHAP stuff */ + da = attr_tacacs_mschap_response; + challenge = attr_tacacs_mschap_challenge; break; case FR_AUTHENTICATION_TYPE_VALUE_MSCHAPV2: - want = 1 + 16 + 49; /* id + 16 octets of challenge + 49 MS-CHAP stuff */ + want = 1 + 16 + 49; /* id + HOPEFULLY 16 octets of challenge + 49 MS-CHAP stuff */ + da = attr_tacacs_mschap2_response; + challenge = attr_tacacs_mschap_challenge; break; } /* * If we have enough data, decode it as * the claimed authentication type. - * Otherwise, decode it as an unknown + * + * Otherwise, decode the entire field as an unknown * attribute. */ - if (pkt->authen_start.data_len <= want) { - DECODE_FIELD_STRING8(attr_tacacs_data, pkt->authen_start.data_len); - } else { - fr_dict_attr_t *da; - - da = fr_dict_unknown_attr_afrom_da(ctx, attr_tacacs_data); - if (da) { - da->flags.is_raw = 1; - DECODE_FIELD_STRING8(da, pkt->authen_start.data_len); - talloc_free(da); /* the VP makes it's own copy */ - } + if (raw || (pkt->authen_start.data_len < want)) { + fr_dict_attr_t *da_unknown; + + da_unknown = fr_dict_unknown_attr_afrom_da(ctx, attr_tacacs_data); + if (!da_unknown) goto fail; + + da_unknown->flags.is_raw = 1; + want = pkt->authen_start.data_len; + + DECODE_FIELD_STRING8(da_unknown, want); + talloc_free(da_unknown); + + } else if (!challenge) { + DECODE_FIELD_STRING8(da, want); + + } else if (pkt->authen_start.data_len == want) { + fr_strerror_printf("%s has zero length", challenge->name); + goto fail; + + } else { /* 1 of ID + ??? of challenge + (want-1) of data */ + uint8_t challenge_len = pkt->authen_start.data_len - want; + uint8_t hash[50]; + + /* + * Rework things to make sense. + */ + hash[0] = p[0]; + memcpy(hash + 1, p + 1 + challenge_len, want - 1); + + vp = fr_pair_afrom_da(ctx, da); + if (!vp) goto fail; + + fr_pair_append(out, vp); + + /* + * ID + hash + */ + if (fr_pair_value_memdup(vp, hash, want, true) < 0) goto fail; + + /* + * And then the challenge. + */ + vp = fr_pair_afrom_da(ctx, challenge); + if (!vp) goto fail; + + fr_pair_append(out, vp); + + if (fr_pair_value_memdup(vp, p + 1, challenge_len, true) < 0) goto fail; + p += pkt->authen_start.data_len; } } else if (packet_is_authen_continue(pkt)) {