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
# @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
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
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
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;
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 },
{ .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 }
};
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
* 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)) {