]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
make TACACS+ PAP CHAP. and (maybe) MS-CHAP work
authorAlan T. DeKok <aland@freeradius.org>
Sat, 11 Feb 2023 13:42:05 +0000 (08:42 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Sat, 11 Feb 2023 13:42:05 +0000 (08:42 -0500)
now that the namespaces are parsed correctly for subrequests,
we can make more things work automagically

raddb/policy.d/tacacs
share/dictionary/tacacs/dictionary.tacacs
src/protocols/tacacs/attrs.h
src/protocols/tacacs/base.c
src/protocols/tacacs/decode.c

index 5635ad1de84fa5603430e658c629b92c3f5cac56..c351c2fefea5f2f0ef6c024bf4f3a1b9c2538282 100644 (file)
@@ -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
index 6afd10c53cb1e0e3f1857582d690f77cbe11f91b..4856c6c028c95962bfb823538c244622ec01dd72 100644 (file)
@@ -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
index 4ebaa190023305b16fc20a559db89559dd6e4805..e4ccb4103960c203250c91ada48499a1e706c8ee 100644 (file)
@@ -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;
index f99bf313ccaace6b43052388fc032744fce0e5c4..799325ea00bcd1c37a798d6990d6483ee47fad0c 100644 (file)
@@ -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 }
 };
 
index c8ee750f2ce2c35e1db699f3f15653e247dcf272..cb139dcf47038a8196901ddfb47ab987f8f5b2df 100644 (file)
@@ -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)) {