* is unsigned, and the attacker can use resources on the server,
* even if the end request is rejected.
*/
-int fr_max_attributes = 0;
+uint32_t fr_max_attributes = 0;
FILE *fr_log_fp = NULL;
typedef struct radius_packet_t {
static uint8_t nullvector[AUTH_VECTOR_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* for CoA decode */
char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
- "",
+ "", //!< 0
"Access-Request",
"Access-Accept",
"Access-Reject",
"Password-Request",
"Password-Accept",
"Password-Reject",
- "Accounting-Message",
+ "Accounting-Message", //!< 10
"Access-Challenge",
"Status-Server",
"Status-Client",
"17",
"18",
"19",
- "20",
+ "20", //!< 20
"Resource-Free-Request",
"Resource-Free-Response",
"Resource-Query-Request",
"NAS-Reboot-Response",
"28",
"Next-Passcode",
- "New-Pin",
+ "New-Pin", //!< 30
"Terminate-Session",
"Password-Expired",
"Event-Request",
"37",
"38",
"39",
- "Disconnect-Request",
+ "Disconnect-Request", //!< 40
"Disconnect-ACK",
"Disconnect-NAK",
"CoA-Request",
"48",
"49",
"IP-Address-Allocate",
- "IP-Address-Release"
+ "IP-Address-Release", //!< 50
};
return;
}
-static char const *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
{
*/
static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
#ifdef WITH_UDPFROMTO
- fr_ipaddr_t *src_ipaddr, int src_port,
+ fr_ipaddr_t *src_ipaddr, uint16_t src_port,
#else
- UNUSED fr_ipaddr_t *src_ipaddr, UNUSED int src_port,
+ UNUSED fr_ipaddr_t *src_ipaddr, UNUSED uint16_t src_port,
#endif
- fr_ipaddr_t *dst_ipaddr, int dst_port)
+ fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
{
int rcode;
struct sockaddr_storage dst;
}
-ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
- int *code)
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
{
ssize_t data_len, packet_len;
uint8_t header[4];
ssize_t data_len;
uint8_t header[4];
size_t len;
- int port;
+ uint16_t port;
memset(&src, 0, sizeof_src);
memset(&dst, 0, sizeof_dst);
FR_MD5_CTX context;
int i;
- fr_MD5Init(&context);
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, (uint8_t const *) secret, strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_init(&context);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(digest, &context);
for ( i = 0; i < AUTH_VECTOR_LEN; i++ ) {
digest[i] ^= value[i];
}
*outlen = len;
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
old = context;
/*
* Do first pass.
*/
- fr_MD5Update(&context, vector, AUTH_PASS_LEN);
+ fr_md5_update(&context, vector, AUTH_PASS_LEN);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
passwd + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + n] ^= digest[i];
}
passwd[1] = fr_rand();
passwd[2] = inlen; /* length of the password string */
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
old = context;
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, &passwd[0], 2);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, &passwd[0], 2);
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n > 0) {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
passwd + 2 + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
}
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
for (i = 0; i < AUTH_PASS_LEN; i++) {
passwd[i + 2 + n] ^= digest[i];
break;
case PW_TYPE_IFID:
- case PW_TYPE_IPADDR:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_IPV6_ADDR:
+ case PW_TYPE_IPV6_PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
case PW_TYPE_ABINARY:
+ case PW_TYPE_ETHERNET: /* just in case */
data = (uint8_t const *) &vp->data;
break;
case PW_TYPE_BYTE:
len = 1; /* just in case */
- array[0] = vp->vp_integer & 0xff;
+ array[0] = vp->vp_byte;
data = array;
break;
case PW_TYPE_SHORT:
len = 2; /* just in case */
- array[0] = (vp->vp_integer >> 8) & 0xff;
- array[1] = vp->vp_integer & 0xff;
+ array[0] = (vp->vp_short >> 8) & 0xff;
+ array[1] = vp->vp_short & 0xff;
data = array;
break;
}
default: /* unknown type: ignore it */
- fr_strerror_printf("ERROR: Unknown attribute type %d",
- vp->da->type);
+ fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
return -1;
}
if (room < (18 + lvalue)) return 0;
switch (packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
default:
if (!original) {
fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->da->name);
return -1;
}
- if (lvalue) ptr[0] = vp->tag;
+ if (lvalue) ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
make_tunnel_passwd(ptr + lvalue, &len, data, len,
room - lvalue,
secret, original->vector);
break;
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_COA_REQUEST:
- ptr[0] = vp->tag;
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
secret, packet->vector);
break;
*/
uint64_t data[MAX_PACKET_LEN / sizeof(uint64_t)];
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ if (is_radius_code(packet->code)) {
what = fr_packet_codes[packet->code];
} else {
what = "Reply";
}
- DEBUG("Sending %s of id %d from %s port %u to %s port %u\n",
+ DEBUG("Sending %s Id %d from %s:%u to %s:%u\n",
what, packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
* Double-check some things based on packet code.
*/
switch (packet->code) {
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
if (!original) {
- fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
return -1;
}
break;
/*
* These packet vectors start off as all zero.
*/
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_COA_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
memset(packet->vector, 0, sizeof(packet->vector));
break;
* It wasn't assigned an Id, this is bad!
*/
if (packet->id < 0) {
- fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
+ fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id");
return -1;
}
uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
switch (packet->code) {
- case PW_ACCOUNTING_RESPONSE:
- if (original && original->code == PW_STATUS_SERVER) {
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ if (original && original->code == PW_CODE_STATUS_SERVER) {
goto do_ack;
}
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_REQUEST:
- case PW_COA_ACK:
- case PW_COA_NAK:
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_REQUEST:
+ case PW_CODE_COA_ACK:
memset(hdr->vector, 0, AUTH_VECTOR_LEN);
break;
do_ack:
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
if (!original) {
- fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
return -1;
}
memcpy(hdr->vector, original->vector,
* into the Message-Authenticator
* attribute.
*/
- fr_hmac_md5(packet->data, packet->data_len,
- (uint8_t const *) secret, strlen(secret),
- calc_auth_vector);
+ fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+ (uint8_t const *) secret, strlen(secret));
memcpy(packet->data + packet->offset + 2,
calc_auth_vector, AUTH_VECTOR_LEN);
* Request packets are not signed, bur
* have a random authentication vector.
*/
- case PW_AUTHENTICATION_REQUEST:
- case PW_STATUS_SERVER:
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_STATUS_SERVER:
break;
/*
uint8_t digest[16];
FR_MD5_CTX context;
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (uint8_t const *) secret,
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret,
strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
memcpy(hdr->vector, digest, AUTH_VECTOR_LEN);
memcpy(packet->vector, digest, AUTH_VECTOR_LEN);
return 0;
}
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+ if (is_radius_code(packet->code)) {
what = fr_packet_codes[packet->code];
} else {
what = "Reply";
* the VP list again only for debugging.
*/
} else if (fr_debug_flag) {
- DEBUG("Sending %s of id %d from %s port %u to %s port %u\n", what,
- packet->id,
- inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
- ip_src_buffer, sizeof(ip_src_buffer)),
+ DEBUG("Sending %s Id %d from %s:%u to %s:%u\n", what,
+ packet->id,
+ inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
+ ip_src_buffer, sizeof(ip_src_buffer)),
packet->src_port,
inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr,
ip_dst_buffer, sizeof(ip_dst_buffer)),
if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
#endif
+#ifdef WITH_TCP
+ /*
+ * If the socket is TCP, call write(). Calling sendto()
+ * is allowed on some platforms, but it's not nice. Even
+ * worse, if UDPFROMTO is defined, we *can't* use it on
+ * TCP sockets. So... just call write().
+ */
+ if (packet->proto == IPPROTO_TCP) {
+ ssize_t rcode;
+
+ rcode = write(packet->sockfd, packet->data, packet->data_len);
+ if (rcode >= 0) return rcode;
+
+ fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
+ return -1;
+ }
+#endif
+
/*
* And send it on it's way.
*/
/*
* MD5(packet + secret);
*/
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (uint8_t const *) secret, strlen(secret));
- fr_MD5Final(digest, &context);
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(digest, &context);
/*
* Return 0 if OK, 2 if not OK.
/*
* MD5(packet + secret);
*/
- fr_MD5Init(&context);
- fr_MD5Update(&context, packet->data, packet->data_len);
- fr_MD5Update(&context, (uint8_t const *) secret, strlen(secret));
- fr_MD5Final(calc_digest, &context);
+ fr_md5_init(&context);
+ fr_md5_update(&context, packet->data, packet->data_len);
+ fr_md5_update(&context, (uint8_t const *) secret, strlen(secret));
+ fr_md5_final(calc_digest, &context);
/*
* Copy the packet's vector back to the packet.
}
-/**
- * @brief See if the data pointed to by PTR is a valid RADIUS packet.
+/** See if the data pointed to by PTR is a valid RADIUS packet.
*
- * packet is not 'const * const' because we may update data_len,
- * if there's more data in the UDP packet than in the RADIUS packet.
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
+ *
+ * @param packet to check
+ * @param flags to control decoding
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return bool, true on success, false on failure.
*/
-int rad_packet_ok(RADIUS_PACKET *packet, int flags)
+bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
{
uint8_t *attr;
size_t totallen;
int count;
radius_packet_t *hdr;
char host_ipaddr[128];
- int require_ma = 0;
- int seen_ma = 0;
- int num_attributes;
+ bool require_ma = false;
+ bool seen_ma = false;
+ uint32_t num_attributes;
+ decode_fail_t failure = DECODE_FAIL_NONE;
/*
* Check for packets smaller than the packet header.
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
packet->data_len, AUTH_HDR_LEN);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_PACKET;
+ goto finish;
}
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
hdr->code);
- return 0;
+ failure = DECODE_FAIL_UNKNOWN_PACKET_CODE;
+ goto finish;
}
/*
* Message-Authenticator is required in Status-Server
* packets, otherwise they can be trivially forged.
*/
- if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+ if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true;
/*
* It's also required if the caller asks for it.
*/
- if (flags) require_ma = 1;
+ if (flags) require_ma = true;
/*
* Repeat the length checks. This time, instead of
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
totallen, AUTH_HDR_LEN);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_FIELD;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
packet->data_len, totallen);
- return 0;
+ failure = DECODE_FAIL_MIN_LENGTH_MISMATCH;
+ goto finish;
}
/*
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_HEADER_OVERFLOW;
+ goto finish;
}
/*
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_INVALID_ATTRIBUTE;
+ goto finish;
}
/*
* Attributes are at LEAST as long as the ID & length
* fields. Anything shorter is an invalid attribute.
*/
- if (attr[1] < 2) {
+ if (attr[1] < 2) {
fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[0]);
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_TOO_SHORT;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[0]);
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_OVERFLOW;
+ goto finish;
}
/*
* a Message-Authenticator.
*/
case PW_EAP_MESSAGE:
- require_ma = 1;
+ require_ma = true;
break;
case PW_MESSAGE_AUTHENTICATOR:
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
attr[1] - 2);
- return 0;
+ failure = DECODE_FAIL_MA_INVALID_LENGTH;
+ goto finish;
}
- seen_ma = 1;
+ seen_ma = true;
break;
}
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_ATTRIBUTE_UNDERFLOW;
+ goto finish;
}
/*
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)),
num_attributes, fr_max_attributes);
- return 0;
+ failure = DECODE_FAIL_TOO_MANY_ATTRIBUTES;
+ goto finish;
}
/*
* Similarly, Status-Server packets MUST contain
* Message-Authenticator attributes.
*/
- if (require_ma && ! seen_ma) {
+ if (require_ma && !seen_ma) {
fr_strerror_printf("WARNING: Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute",
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
host_ipaddr, sizeof(host_ipaddr)));
- return 0;
+ failure = DECODE_FAIL_MA_MISSING;
+ goto finish;
}
/*
packet->id = hdr->id;
memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
- return 1;
+
+ finish:
+
+ if (reason) {
+ *reason = failure;
+ }
+ return (failure == DECODE_FAIL_NONE);
}
/*
* Allocate the new request data structure
*/
- packet = rad_alloc(NULL, 0);
+ packet = rad_alloc(NULL, false);
if (!packet) {
fr_strerror_printf("out of memory");
return NULL;
* packet.
*/
if (packet->data_len > MAX_PACKET_LEN) {
- fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+ fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
/* packet->data is NULL */
rad_free(&packet);
return NULL;
* packet->data == NULL
*/
if ((packet->data_len == 0) || !packet->data) {
- fr_strerror_printf("Empty packet: Socket is not ready.");
+ fr_strerror_printf("Empty packet: Socket is not ready");
rad_free(&packet);
return NULL;
}
/*
* See if it's a well-formed RADIUS packet.
*/
- if (!rad_packet_ok(packet, flags)) {
+ if (!rad_packet_ok(packet, flags, NULL)) {
rad_free(&packet);
return NULL;
}
packet->vps = NULL;
if (fr_debug_flag) {
- char host_ipaddr[128];
+ char src_ipaddr[128];
+ char dst_ipaddr[128];
- if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
- DEBUG("rad_recv: %s packet from host %s port %d",
+ if (is_radius_code(packet->code)) {
+ DEBUG("Received %s Id %d from %s:%d to %s:%d length %d\n",
fr_packet_codes[packet->code],
+ packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
- packet->src_port);
+ src_ipaddr, sizeof(src_ipaddr)),
+ packet->src_port,
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_port,
+ (int) packet->data_len);
} else {
- DEBUG("rad_recv: Packet from host %s port %d code=%d",
+ DEBUG("Received code %d Id %d from %s:%d to %s:%d length %d\n",
+ packet->code,
+ packet->id,
inet_ntop(packet->src_ipaddr.af,
&packet->src_ipaddr.ipaddr,
- host_ipaddr, sizeof(host_ipaddr)),
+ src_ipaddr, sizeof(src_ipaddr)),
packet->src_port,
- packet->code);
+ inet_ntop(packet->dst_ipaddr.af,
+ &packet->dst_ipaddr.ipaddr,
+ dst_ipaddr, sizeof(dst_ipaddr)),
+ packet->dst_port,
+ (int) packet->data_len);
}
- DEBUG(", id=%d, length=%d\n",
- packet->id, (int) packet->data_len);
}
#ifndef NDEBUG
default:
break;
- case PW_ACCOUNTING_RESPONSE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
if (original &&
- (original->code == PW_STATUS_SERVER)) {
+ (original->code == PW_CODE_STATUS_SERVER)) {
goto do_ack;
}
- case PW_ACCOUNTING_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_REQUEST:
- case PW_COA_ACK:
- case PW_COA_NAK:
- memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
+ case PW_CODE_ACCOUNTING_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
break;
do_ack:
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
if (!original) {
- fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
+ fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet");
return -1;
}
memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
break;
}
- fr_hmac_md5(packet->data, packet->data_len,
- (uint8_t const *) secret, strlen(secret),
- calc_auth_vector);
+ fr_hmac_md5(calc_auth_vector, packet->data, packet->data_len,
+ (uint8_t const *) secret, strlen(secret));
if (rad_digest_cmp(calc_auth_vector, msg_auth_vector,
sizeof(calc_auth_vector)) != 0) {
char buffer[32];
/*
* Calculate and/or verify Request or Response Authenticator.
*/
- switch(packet->code) {
+ switch (packet->code) {
int rcode;
char buffer[32];
- case PW_AUTHENTICATION_REQUEST:
- case PW_STATUS_SERVER:
+ case PW_CODE_ACCESS_REQUEST:
+ case PW_CODE_STATUS_SERVER:
/*
* The authentication vector is random
* nonsense, invented by the client.
*/
break;
- case PW_COA_REQUEST:
- case PW_DISCONNECT_REQUEST:
- case PW_ACCOUNTING_REQUEST:
+ case PW_CODE_COA_REQUEST:
+ case PW_CODE_DISCONNECT_REQUEST:
+ case PW_CODE_ACCOUNTING_REQUEST:
if (calc_acctdigest(packet, secret) > 1) {
fr_strerror_printf("Received %s packet "
"from client %s with invalid Request Authenticator! (Shared secret is incorrect.)",
break;
/* Verify the reply digest */
- case PW_AUTHENTICATION_ACK:
- case PW_AUTHENTICATION_REJECT:
- case PW_ACCESS_CHALLENGE:
- case PW_ACCOUNTING_RESPONSE:
- case PW_DISCONNECT_ACK:
- case PW_DISCONNECT_NAK:
- case PW_COA_ACK:
- case PW_COA_NAK:
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ case PW_CODE_ACCOUNTING_RESPONSE:
+ case PW_CODE_DISCONNECT_ACK:
+ case PW_CODE_DISCONNECT_NAK:
+ case PW_CODE_COA_ACK:
+ case PW_CODE_COA_NAK:
rcode = calc_replydigest(packet, original, secret);
if (rcode > 1) {
fr_strerror_printf("Received %s packet "
}
-static ssize_t data2vp(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
- char const *secret,
- DICT_ATTR const *da, uint8_t const *start,
- size_t const attrlen, size_t const packetlen,
- VALUE_PAIR **pvp);
-
/**
* @brief convert a "concatenated" attribute to one long VP.
*/
-static ssize_t data2vp_concat(RADIUS_PACKET *packet,
+static ssize_t data2vp_concat(TALLOC_CTX *ctx,
DICT_ATTR const *da, uint8_t const *start,
size_t const packetlen, VALUE_PAIR **pvp)
{
if (ptr[0] != attr) break;
}
- vp = pairalloc(packet, da);
+ vp = pairalloc(ctx, da);
if (!vp) return -1;
vp->length = total;
/**
* @brief convert TLVs to one or more VPs
*/
-static ssize_t data2vp_tlvs(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+static ssize_t data2vp_tlvs(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
char const *secret, DICT_ATTR const *da,
uint8_t const *start, size_t length,
VALUE_PAIR **pvp)
}
}
- tlv_len = data2vp(packet, original, secret, child,
+ tlv_len = data2vp(ctx, packet, original, secret, child,
data + 2, data[1] - 2, data[1] - 2, tail);
if (tlv_len < 0) {
pairfree(&head);
*
* "length" can be LONGER than just this sub-vsa
*/
-static ssize_t data2vp_vsa(RADIUS_PACKET *packet,
+static ssize_t data2vp_vsa(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
RADIUS_PACKET const *original,
char const *secret, DICT_VENDOR *dv,
uint8_t const *data, size_t length,
if (!da) da = dict_attrunknown(attribute, dv->vendorpec, true);
if (!da) return -1;
- my_len = data2vp(packet, original, secret, da,
+ my_len = data2vp(ctx, packet, original, secret, da,
data + dv->type + dv->length,
attrlen - (dv->type + dv->length),
attrlen - (dv->type + dv->length),
* But for the first fragment, we get passed a pointer to the
* "extended-attr".
*/
-static ssize_t data2vp_extended(RADIUS_PACKET *packet,
+static ssize_t data2vp_extended(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
RADIUS_PACKET const *original,
char const *secret, DICT_ATTR const *da,
uint8_t const *data,
VP_HEXDUMP("long-extended fragments", head, fraglen);
- rcode = data2vp(packet, original, secret, da,
+ rcode = data2vp(ctx, packet, original, secret, da,
head, fraglen, fraglen, pvp);
free(head);
if (rcode < 0) return rcode;
*
* Called ONLY for Vendor-Specific
*/
-static ssize_t data2vp_wimax(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+static ssize_t data2vp_wimax(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
char const *secret, uint32_t vendor,
uint8_t const *data,
size_t attrlen, size_t packetlen,
if (!child) return -1;
if ((data[6] & 0x80) == 0) {
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 7, data[5] - 3, data[5] - 3,
pvp);
if (rcode < 0) return -1;
VP_HEXDUMP("wimax fragments", head, fraglen);
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
head, fraglen, fraglen, pvp);
free(head);
if (rcode < 0) return rcode;
/**
* @brief Convert a top-level VSA to one or more VPs
*/
-static ssize_t data2vp_vsas(RADIUS_PACKET *packet,
+static ssize_t data2vp_vsas(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
RADIUS_PACKET const *original,
char const *secret, uint8_t const *data,
size_t attrlen, size_t packetlen,
* WiMAX craziness
*/
if ((vendor == VENDORPEC_WIMAX) && dv->flags) {
- rcode = data2vp_wimax(packet, original, secret, vendor,
+ rcode = data2vp_wimax(ctx, packet, original, secret, vendor,
data, attrlen, packetlen, pvp);
return rcode;
}
while (attrlen > 0) {
ssize_t vsa_len;
- vsa_len = data2vp_vsa(packet, original, secret, dv,
+ vsa_len = data2vp_vsa(ctx, packet, original, secret, dv,
data, attrlen, tail);
if (vsa_len < 0) {
pairfree(&head);
*
* @return -1 on error, or "length".
*/
-static ssize_t data2vp(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
- char const *secret,
- DICT_ATTR const *da, uint8_t const *start,
- size_t const attrlen, size_t const packetlen,
- VALUE_PAIR **pvp)
+ssize_t data2vp(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+ char const *secret,
+ DICT_ATTR const *da, uint8_t const *start,
+ size_t const attrlen, size_t const packetlen,
+ VALUE_PAIR **pvp)
{
- int tag = 0;
+ int8_t tag = TAG_NONE;
size_t datalen;
ssize_t rcode;
uint32_t vendor;
* will break assumptions about CUI. We know
* this, but Coverity doesn't.
*/
- if (da->type != PW_TYPE_STRING) return -1;
+ if (da->type != PW_TYPE_OCTETS) return -1;
#endif
- data = (uint8_t const *) "";
- datalen = 1;
+ data = NULL;
+ datalen = 0;
goto alloc_cui; /* skip everything */
}
* Decrypt the attribute.
*/
if (secret && packet && (da->flags.encrypt != FLAG_ENCRYPT_NONE)) {
+ /*
+ * Encrypted attributes can only exist for the
+ * old-style format. Extended attributes CANNOT
+ * be encrypted.
+ */
+ if (attrlen > 253) {
+ return -1;
+ }
+
if (data == start) {
- if (attrlen < sizeof(buffer)) {
- memcpy(buffer, data, attrlen);
- } else {
- memcpy(buffer, data, sizeof(buffer));
- }
+ memcpy(buffer, data, attrlen);
}
data = buffer;
break;
case PW_TYPE_INTEGER:
- case PW_TYPE_IPADDR:
+ case PW_TYPE_IPV4_ADDR:
case PW_TYPE_DATE:
case PW_TYPE_SIGNED:
if (datalen != 4) goto raw;
if (datalen != 8) goto raw;
break;
- case PW_TYPE_IPV6ADDR:
+ case PW_TYPE_IPV6_ADDR:
if (datalen != 16) goto raw;
break;
- case PW_TYPE_IPV6PREFIX:
+ case PW_TYPE_IPV6_PREFIX:
if ((datalen < 2) || (datalen > 18)) goto raw;
if (data[1] > 128) goto raw;
break;
if (datalen != 6) goto raw;
break;
- case PW_TYPE_COMBO_IP:
+ case PW_TYPE_IP_ADDR:
if (datalen == 4) {
child = dict_attrbytype(da->attr, da->vendor,
- PW_TYPE_IPADDR);
+ PW_TYPE_IPV4_ADDR);
} else if (datalen == 16) {
child = dict_attrbytype(da->attr, da->vendor,
- PW_TYPE_IPV6ADDR);
+ PW_TYPE_IPV6_ADDR);
} else {
goto raw;
}
da = child; /* re-write it */
break;
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
if (datalen != 6) goto raw;
if ((data[1] & 0x3f) > 32) goto raw;
break;
* the current attribute, and we ignore any extra
* data after it.
*/
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 1, attrlen - 1, attrlen - 1, pvp);
if (rcode < 0) goto raw;
return 1 + rcode;
*
*/
if ((data[1] & 0x80) == 0) {
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 2, attrlen - 2, attrlen - 2,
pvp);
if (rcode < 0) goto raw;
/*
* This requires a whole lot more work.
*/
- return data2vp_extended(packet, original, secret, child,
+ return data2vp_extended(ctx, packet, original, secret, child,
start, attrlen, packetlen, pvp);
case PW_TYPE_EVS:
}
if (!child) goto raw;
- rcode = data2vp(packet, original, secret, child,
+ rcode = data2vp(ctx, packet, original, secret, child,
data + 5, attrlen - 5, attrlen - 5, pvp);
if (rcode < 0) goto raw;
return 5 + rcode;
* attribute, OR they've already been grouped
* into a contiguous memory buffer.
*/
- rcode = data2vp_tlvs(packet, original, secret, da,
+ rcode = data2vp_tlvs(ctx, packet, original, secret, da,
data, attrlen, pvp);
if (rcode < 0) goto raw;
return rcode;
* VSAs can be WiMAX, in which case they don't
* fit into one attribute.
*/
- rcode = data2vp_vsas(packet, original, secret,
+ rcode = data2vp_vsas(ctx, packet, original, secret,
data, attrlen, packetlen, pvp);
if (rcode < 0) goto raw;
return rcode;
fr_strerror_printf("Internal sanity check %d", __LINE__);
return -1;
}
- tag = 0;
+ tag = TAG_NONE;
#ifndef NDEBUG
/*
* Fix for Coverity.
* information, decode the actual data.
*/
alloc_cui:
- vp = pairalloc(packet, da);
+ vp = pairalloc(ctx, da);
if (!vp) return -1;
vp->length = datalen;
break;
case PW_TYPE_OCTETS:
- vp->vp_octets = talloc_memdup(vp, data, vp->length);
+ pairmemcpy(vp, data, vp->length);
break;
case PW_TYPE_ABINARY:
break;
case PW_TYPE_BYTE:
- vp->vp_integer = data[0];
+ vp->vp_byte = data[0];
break;
case PW_TYPE_SHORT:
- vp->vp_integer = (data[0] << 8) | data[1];
+ vp->vp_short = (data[0] << 8) | data[1];
break;
case PW_TYPE_INTEGER:
memcpy(&vp->vp_ether, data, 6);
break;
- case PW_TYPE_IPADDR:
+ case PW_TYPE_IPV4_ADDR:
memcpy(&vp->vp_ipaddr, data, 4);
break;
memcpy(&vp->vp_ifid, data, 8);
break;
- case PW_TYPE_IPV6ADDR:
+ case PW_TYPE_IPV6_ADDR:
memcpy(&vp->vp_ipv6addr, data, 16);
break;
- case PW_TYPE_IPV6PREFIX:
+ case PW_TYPE_IPV6_PREFIX:
/*
* FIXME: double-check that
* (vp->vp_octets[1] >> 3) matches vp->length + 2
}
break;
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
/* FIXME: do the same double-check as for IPv6Prefix */
- memcpy(&vp->vp_ipv4prefix, buffer, sizeof(vp->vp_ipv4prefix));
+ memcpy(&vp->vp_ipv4prefix, data, vp->length);
/*
* /32 means "keep all bits". Otherwise, mask
memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
mask = 1;
- mask <<= (32 - (buffer[1] & 0x3f));
+ mask <<= (32 - (data[1] & 0x3f));
mask--;
mask = ~mask;
mask = htonl(mask);
/**
* @brief Create a "normal" VALUE_PAIR from the given data.
*/
-ssize_t rad_attr2vp(RADIUS_PACKET *packet,
- RADIUS_PACKET const *original,
+ssize_t rad_attr2vp(TALLOC_CTX *ctx,
+ RADIUS_PACKET *packet, RADIUS_PACKET const *original,
char const *secret,
uint8_t const *data, size_t length,
VALUE_PAIR **pvp)
* Pass the entire thing to the decoding function
*/
if (da->flags.concat) {
- return data2vp_concat(packet, da, data, length, pvp);
+ return data2vp_concat(ctx, da, data, length, pvp);
}
/*
* attributes may have the "continuation" bit set, and
* will thus be more than one attribute in length.
*/
- rcode = data2vp(packet, original, secret, da,
+ rcode = data2vp(ctx, packet, original, secret, da,
data + 2, data[1] - 2, length - 2, pvp);
if (rcode < 0) return rcode;
return 2 + rcode;
}
-
-/**
- * @brief Converts data in network byte order to a VP
- * @return -1 on error, or the length of the data read
- */
-ssize_t rad_data2vp(unsigned int attribute, unsigned int vendor,
- uint8_t const *data, size_t length,
- VALUE_PAIR **pvp)
-{
- DICT_ATTR const *da;
-
- if (!data || (length == 0) || !pvp) return -1;
-
- da = dict_attrbyvalue(attribute, vendor);
- if (!da) da = dict_attrunknown(attribute, vendor, true);
- if (!da) return -1;
-
- return data2vp(NULL, NULL, NULL, da,
- data, length, length, pvp);
-}
-
fr_thread_local_setup(uint8_t *, rad_vp2data_buff);
/** Converts vp_data to network byte order
ret = fr_thread_local_set(rad_vp2data_buff, buffer);
if (ret != 0) {
- fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", fr_syserror(errno));
+ fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", strerror(errno));
free(buffer);
return -1;
}
* All of these values are at the same location.
*/
case PW_TYPE_IFID:
- case PW_TYPE_IPADDR:
- case PW_TYPE_IPV6ADDR:
- case PW_TYPE_IPV6PREFIX:
- case PW_TYPE_IPV4PREFIX:
+ case PW_TYPE_IPV4_ADDR:
+ case PW_TYPE_IPV6_ADDR:
+ case PW_TYPE_IPV6_PREFIX:
+ case PW_TYPE_IPV4_PREFIX:
case PW_TYPE_ABINARY:
case PW_TYPE_ETHERNET:
- case PW_TYPE_COMBO_IP:
+ case PW_TYPE_IP_ADDR:
+ case PW_TYPE_IP_PREFIX:
{
void const *p = &vp->data;
memcpy(out, &p, sizeof(*out));
break;
}
+ case PW_TYPE_BOOLEAN:
+ buffer[0] = vp->vp_integer & 0x01;
+ *out = buffer;
+ break;
+
case PW_TYPE_BYTE:
buffer[0] = vp->vp_integer & 0xff;
*out = buffer;
case PW_TYPE_LONG_EXTENDED:
case PW_TYPE_EVS:
case PW_TYPE_VSA:
+ case PW_TYPE_TIMEVAL:
case PW_TYPE_MAX:
fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
return -1;
char const *secret)
{
int packet_length;
- int num_attributes;
+ uint32_t num_attributes;
uint8_t *ptr;
radius_packet_t *hdr;
VALUE_PAIR *head, **tail, *vp;
/*
* This may return many VPs
*/
- my_len = rad_attr2vp(packet, original, secret,
+ my_len = rad_attr2vp(packet, packet, original, secret,
ptr, packet_length, &vp);
if (my_len < 0) {
pairfree(&head);
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < len; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_MD5Update(&context, vector, AUTH_PASS_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_update(&context, vector, AUTH_PASS_LEN);
+ fr_md5_final(digest, &context);
} else {
context = old;
- fr_MD5Update(&context,
+ fr_md5_update(&context,
(uint8_t *) passwd + n - AUTH_PASS_LEN,
AUTH_PASS_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
}
for (i = 0; i < AUTH_PASS_LEN; i++) {
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*/
for (n = 0; n < pwlen; n += AUTH_PASS_LEN) {
if (n == 0) {
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Final(digest, &context);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_final(digest, &context);
context = old;
if (pwlen > AUTH_PASS_LEN) {
- fr_MD5Update(&context, (uint8_t *) passwd,
+ fr_md5_update(&context, (uint8_t *) passwd,
AUTH_PASS_LEN);
}
} else {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
if (pwlen > (n + AUTH_PASS_LEN)) {
- fr_MD5Update(&context, (uint8_t *) passwd + n,
+ fr_md5_update(&context, (uint8_t *) passwd + n,
AUTH_PASS_LEN);
}
}
*/
secretlen = strlen(secret);
- fr_MD5Init(&context);
- fr_MD5Update(&context, (uint8_t const *) secret, secretlen);
+ fr_md5_init(&context);
+ fr_md5_update(&context, (uint8_t const *) secret, secretlen);
old = context; /* save intermediate work */
/*
*
* b(1) = MD5(secret + vector + salt)
*/
- fr_MD5Update(&context, vector, AUTH_VECTOR_LEN);
- fr_MD5Update(&context, passwd, 2);
+ fr_md5_update(&context, vector, AUTH_VECTOR_LEN);
+ fr_md5_update(&context, passwd, 2);
reallen = 0;
for (n = 0; n < len; n += AUTH_PASS_LEN) {
int base = 0;
if (n == 0) {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
return -1;
}
- fr_MD5Update(&context, passwd + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + 2, AUTH_PASS_LEN);
base = 1;
} else {
- fr_MD5Final(digest, &context);
+ fr_md5_final(digest, &context);
context = old;
- fr_MD5Update(&context, passwd + n + 2, AUTH_PASS_LEN);
+ fr_md5_update(&context, passwd + n + 2, AUTH_PASS_LEN);
}
for (i = base; i < AUTH_PASS_LEN; i++) {
sizeof(fr_rand_pool.randrsl) - total);
if ((this < 0) && (errno != EINTR)) break;
if (this > 0) total += this;
- }
+ }
close(fd);
} else {
fr_rand_pool.randrsl[0] = fd;
*
* @param ctx the context in which the packet is allocated. May be NULL if
* the packet is not associated with a REQUEST.
- * @param newvector if true a new request authenticator will be generated.
+ * @param new_vector if true a new request authenticator will be generated.
* @return a new RADIUS_PACKET or NULL on error.
*/
-RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, int newvector)
+RADIUS_PACKET *rad_alloc(TALLOC_CTX *ctx, bool new_vector)
{
RADIUS_PACKET *rp;
rp->id = -1;
rp->offset = -1;
- if (newvector) {
+ if (new_vector) {
int i;
uint32_t hash, base;
if (!packet) return NULL;
- reply = rad_alloc(ctx, 0);
+ reply = rad_alloc(ctx, false);
if (!reply) return NULL;
/*
reply->data = NULL;
reply->data_len = 0;
+#ifdef WITH_TCP
+ reply->proto = packet->proto;
+#endif
return reply;
}
if (!radius_packet_ptr || !*radius_packet_ptr) return;
radius_packet = *radius_packet_ptr;
+ VERIFY_PACKET(radius_packet);
+
pairfree(&radius_packet->vps);
talloc_free(radius_packet);
*radius_packet_ptr = NULL;
}
+
+/** Duplicate a RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ * the packet is not associated with a REQUEST.
+ * @param in The packet to copy
+ * @return a new RADIUS_PACKET or NULL on error.
+ */
+RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
+{
+ RADIUS_PACKET *out;
+
+ out = rad_alloc(ctx, false);
+ if (!out) return NULL;
+
+ /*
+ * Bootstrap by copying everything.
+ */
+ memcpy(out, in, sizeof(*out));
+
+ /*
+ * Then reset necessary fields
+ */
+ out->sockfd = -1;
+
+ out->data = NULL;
+ out->data_len = 0;
+
+ out->vps = paircopy(out, in->vps);
+ out->offset = 0;
+
+ return out;
+}