fr_dict_autofree(libfreeradius_tacacs_dict);
}
+/** XOR the body based on the secret key.
+ *
+ * This function encrypts (or decrypts) TACACS+ packets, and sets the "encrypted" flag.
+ */
int fr_tacacs_body_xor(fr_tacacs_packet_t const *pkt, uint8_t *body, size_t body_len, char const *secret, size_t secret_len)
{
uint8_t pad[MD5_DIGEST_LENGTH];
- uint8_t *buf;
+ uint8_t *buf, *end;
int pad_offset;
- size_t pos;
-
- if (!secret || !secret_len) {
- if (pkt->hdr.flags & FR_TAC_PLUS_UNENCRYPTED_FLAG)
- return 0;
- else {
- fr_strerror_const("Packet is encrypted but no secret for the client is set");
- return -1;
- }
- }
- if (pkt->hdr.flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) {
- fr_strerror_const("Packet is unencrypted but a secret has been set for the client");
+ /*
+ * Do some basic sanity checks.
+ */
+ if (!secret_len) {
+ fr_strerror_const("Failed to encrypt/decrept the packet, as the secret has zero length.");
return -1;
}
/* MD5_1 = MD5{session_id, key, version, seq_no} */
/* MD5_n = MD5{session_id, key, version, seq_no, MD5_n-1} */
buf = talloc_array(NULL, uint8_t, pad_offset + MD5_DIGEST_LENGTH);
+ if (!buf) return -1;
memcpy(&buf[0], &pkt->hdr.session_id, sizeof(pkt->hdr.session_id));
memcpy(&buf[sizeof(pkt->hdr.session_id)], secret, secret_len);
fr_md5_calc(pad, buf, pad_offset);
- pos = 0;
- do {
- for (size_t i = 0; i < MD5_DIGEST_LENGTH && pos < body_len; i++, pos++)
- body[pos] ^= pad[i];
+ end = body + body_len;
+ while (body < end) {
+ size_t i;
+
+ for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
+ *body ^= pad[i];
- if (pos == body_len)
- break;
+ if (++body == end) goto done;
+ }
memcpy(&buf[pad_offset], pad, MD5_DIGEST_LENGTH);
fr_md5_calc(pad, buf, pad_offset + MD5_DIGEST_LENGTH);
- } while (1);
+ }
+done:
talloc_free(buf);
return 0;
return -1;
}
+ if (!secret && packet_is_encrypted(pkt)) {
+ fr_strerror_const("Packet is encrypted, but there is no secret to decrypt it");
+ return -1;
+ }
+
+ if (secret && !packet_is_encrypted(pkt)) {
+ fr_strerror_const("Packet is clear-text but we expected it to be encrypted");
+ return -1;
+ }
+
/*
* Call the struct encoder to do the actual work.
*/
/*
* 3.6. Encryption
*
- * Packets are encrypted if the unencrypted flag is clear.
+ * If there's a secret, we alway decrypt the packets.
*/
- if ((pkt->hdr.flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) {
+ if (secret) {
size_t length;
- if (!secret || secret_len < 1) {
- fr_strerror_const("Packet is encrypted, but no secret is set.");
+ if (!secret_len) {
+ fr_strerror_const("Packet should be encrypted, but the secret has zero length");
return -1;
}
return -1;
}
- decrypted[3] |= 0x01; /* say it's decrypted */
+ decrypted[3] |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
FR_PROTO_HEX_DUMP(decrypted, buffer_len, "fr_tacacs_packet_t (unencrypted)");
}
*/
if (original &&
((original->flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0)) {
- packet->hdr.flags = packet->hdr.flags & ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
+ packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
}
- /*
- * Packets which have no secret cannot be encrypted.
- */
- if (!secret) packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
-
#ifndef NDEBUG
if (fr_debug_lvl >= L_DBG_LVL_4) {
uint8_t flags = packet->hdr.flags;
*
* Packets are encrypted if the unencrypted flag is clear.
*/
- if ((packet->hdr.flags & FR_TAC_PLUS_UNENCRYPTED_FLAG) == 0) {
- if (!secret || secret_len < 1) {
- fr_strerror_const("decode: Packet is supposed to be encrypted, but no secret is set.");
+ if (secret) {
+ if (!secret_len) {
+ fr_strerror_const("Packet should be decrypted, but the secret has zero length");
return -1;
}
FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (unencrypted)");
if (fr_tacacs_body_xor(packet, fr_dbuff_current(&body), body_len, secret, secret_len) != 0) return -1;
+
+ packet->hdr.flags &= ~FR_TAC_PLUS_UNENCRYPTED_FLAG;
+ } else {
+ /*
+ * Packets which have no secret cannot be encrypted.
+ */
+ packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG;
+
}
FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), packet_len, "fr_tacacs_packet_t (encoded)");