📄 radius.c
字号:
if (!packet->data) { librad_log("Out of memory"); return -1; } memcpy(packet->data, data, packet->data_len); hdr = (radius_packet_t *) packet->data; total_length = htons(total_length); memcpy(hdr->length, &total_length, sizeof(u_short)); /* * If this is not an authentication request, we * need to calculate the md5 hash over the entire packet * and put it in the vector. */ secretlen = strlen(secret); /* * If there's a Message-Authenticator, update it * now, BEFORE updating the authentication vector. */ if (msg_auth_offset) { uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; switch (packet->code) { default: break; case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: /* this was checked above */ memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); break; } /* * Set the authentication vector to zero, * calculate the signature, and put it * into the Message-Authenticator * attribute. */ memset(packet->data + msg_auth_offset + 2, 0, AUTH_VECTOR_LEN); lrad_hmac_md5(packet->data, packet->data_len, secret, secretlen, calc_auth_vector); memcpy(packet->data + msg_auth_offset + 2, calc_auth_vector, AUTH_VECTOR_LEN); /* * Copy the original request vector back * to the raw packet. */ memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN); } /* * Switch over the packet code, deciding how to * sign the packet. */ switch (packet->code) { /* * Request packets are not signed, bur * have a random authentication vector. */ case PW_AUTHENTICATION_REQUEST: case PW_STATUS_SERVER: break; /* * Reply packets are signed with the * authentication vector of the request. */ default: { MD5_CTX context; MD5Init(&context); MD5Update(&context, packet->data, packet->data_len); MD5Update(&context, secret, strlen(secret)); MD5Final(digest, &context); memcpy(hdr->vector, digest, AUTH_VECTOR_LEN); memcpy(packet->vector, digest, AUTH_VECTOR_LEN); break; } } /* switch over packet codes */ /* * If packet->data points to data, then we print out * the VP list again only for debugging. */ } else if (librad_debug) { DEBUG("Re-sending %s of id %d to %s:%d\n", what, packet->id, ip_ntoa((char *)ip_buffer, packet->dst_ipaddr), packet->dst_port); for (reply = packet->vps; reply; reply = reply->next) { /* FIXME: ignore attributes > 0xff */ debug_pair(reply); } } /* * And send it on it's way. */ sa = (struct sockaddr_in *) &saremote; memset ((char *) sa, '\0', sizeof (saremote)); sa->sin_family = AF_INET; sa->sin_addr.s_addr = packet->dst_ipaddr; sa->sin_port = htons(packet->dst_port);#ifndef WITH_UDPFROMTO return sendto(packet->sockfd, packet->data, (int)packet->data_len, 0, (struct sockaddr *)&saremote, sizeof(struct sockaddr_in));#else { struct sockaddr_in salocal; memset ((char *) &salocal, '\0', sizeof (salocal)); salocal.sin_family = AF_INET; salocal.sin_addr.s_addr = packet->src_ipaddr; return sendfromto(packet->sockfd, packet->data, (int)packet->data_len, 0, (struct sockaddr *)&salocal, sizeof(struct sockaddr_in), (struct sockaddr *)&saremote, sizeof(struct sockaddr_in)); }#endif}/* * Validates the requesting client NAS. Calculates the * signature based on the clients private key. */static int calc_acctdigest(RADIUS_PACKET *packet, const char *secret){ u_char digest[AUTH_VECTOR_LEN]; MD5_CTX context; /* * Older clients have the authentication vector set to * all zeros. Return `1' in that case. */ memset(digest, 0, sizeof(digest)); if (memcmp(packet->vector, digest, AUTH_VECTOR_LEN) == 0) { packet->verified = 1; return 1; } /* * Zero out the auth_vector in the received packet. * Then append the shared secret to the received packet, * and calculate the MD5 sum. This must be the same * as the original MD5 sum (packet->vector). */ memset(packet->data + 4, 0, AUTH_VECTOR_LEN); /* * MD5(packet + secret); */ MD5Init(&context); MD5Update(&context, packet->data, packet->data_len); MD5Update(&context, secret, strlen(secret)); MD5Final(digest, &context); /* * Return 0 if OK, 2 if not OK. */ packet->verified = memcmp(digest, packet->vector, AUTH_VECTOR_LEN) ? 2 : 0; return packet->verified;}/* * Validates the requesting client NAS. Calculates the * signature based on the clients private key. */static int calc_replydigest(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secret){ uint8_t calc_digest[AUTH_VECTOR_LEN]; MD5_CTX context; /* * Very bad! */ if (original == NULL) { return 3; } /* * Copy the original vector in place. */ memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN); /* * MD5(packet + secret); */ MD5Init(&context); MD5Update(&context, packet->data, packet->data_len); MD5Update(&context, secret, strlen(secret)); MD5Final(calc_digest, &context); /* * Copy the packet's vector back to the packet. */ memcpy(packet->data + 4, packet->vector, AUTH_VECTOR_LEN); /* * Return 0 if OK, 2 if not OK. */ packet->verified = memcmp(packet->vector, calc_digest, AUTH_VECTOR_LEN) ? 2 : 0; return packet->verified;}/* * Receive UDP client requests, and fill in * the basics of a RADIUS_PACKET structure. */RADIUS_PACKET *rad_recv(int fd){ RADIUS_PACKET *packet; struct sockaddr_in saremote; int totallen; socklen_t salen; uint8_t *attr; int count; radius_packet_t *hdr; char host_ipaddr[16]; int seen_eap; uint8_t data[MAX_PACKET_LEN]; int num_attributes; /* * Allocate the new request data structure */ if ((packet = malloc(sizeof(RADIUS_PACKET))) == NULL) { librad_log("out of memory"); return NULL; } memset(packet, 0, sizeof(RADIUS_PACKET)); /* * Receive the packet. */ salen = sizeof(saremote); memset(&saremote, 0, sizeof(saremote));#ifndef WITH_UDPFROMTO packet->data_len = recvfrom(fd, data, sizeof(data), 0, (struct sockaddr *)&saremote, &salen); packet->dst_ipaddr = htonl(INADDR_ANY); /* i.e. unknown */#else { socklen_t salen_local; struct sockaddr_in salocal; salen_local = sizeof(salocal); memset(&salocal, 0, sizeof(salocal)); packet->data_len = recvfromto(fd, data, sizeof(data), 0, (struct sockaddr *)&saremote, &salen, (struct sockaddr *)&salocal, &salen_local); packet->dst_ipaddr = salocal.sin_addr.s_addr; }#endif /* * Check for socket errors. */ if (packet->data_len < 0) { librad_log("Error receiving packet: %s", strerror(errno)); free(packet); return NULL; } /* * Fill IP header fields. We need these for the error * messages which may come later. */ packet->sockfd = fd; packet->src_ipaddr = saremote.sin_addr.s_addr; packet->src_port = ntohs(saremote.sin_port); /* * FIXME: Do even more filtering by only permitting * certain IP's. The problem is that we don't know * how to do this properly for all possible clients... */ /* * Explicitely set the VP list to empty. */ packet->vps = NULL; /* * Check for packets smaller than the packet header. * * RFC 2865, Section 3., subsection 'length' says: * * "The minimum length is 20 ..." */ if (packet->data_len < AUTH_HDR_LEN) { librad_log("WARNING: Malformed RADIUS packet from host %s: too short (received %d < minimum %d)", ip_ntoa(host_ipaddr, packet->src_ipaddr), packet->data_len, AUTH_HDR_LEN); free(packet); return NULL; } /* * RFC 2865, Section 3., subsection 'length' says: * * " ... and maximum length is 4096." */ if (packet->data_len > MAX_PACKET_LEN) { librad_log("WARNING: Malformed RADIUS packet from host %s: too long (received %d > maximum %d)", ip_ntoa(host_ipaddr, packet->src_ipaddr), packet->data_len, MAX_PACKET_LEN); free(packet); return NULL; } /* * Check for packets with mismatched size. * i.e. We've received 128 bytes, and the packet header * says it's 256 bytes long. */ totallen = (data[2] << 8) | data[3]; hdr = (radius_packet_t *)data; /* * Code of 0 is not understood. * Code of 16 or greate is not understood. */ if ((hdr->code == 0) || (hdr->code >= 52)) { librad_log("WARNING: Bad RADIUS packet from host %s: unknown packet code %d", ip_ntoa(host_ipaddr, packet->src_ipaddr), hdr->code); free(packet); return NULL; } /* * Repeat the length checks. This time, instead of * looking at the data we received, look at the value * of the 'length' field inside of the packet. * * Check for packets smaller than the packet header. * * RFC 2865, Section 3., subsection 'length' says: * * "The minimum length is 20 ..." */ if (totallen < AUTH_HDR_LEN) { librad_log("WARNING: Malformed RADIUS packet from host %s: too short (length %d < minimum %d)", ip_ntoa(host_ipaddr, packet->src_ipaddr), totallen, AUTH_HDR_LEN); free(packet); return NULL; } /* * And again, for the value of the 'length' field. * * RFC 2865, Section 3., subsection 'length' says: * * " ... and maximum length is 4096." */ if (totallen > MAX_PACKET_LEN) { librad_log("WARNING: Malformed RADIUS packet from host %s: too long (length %d > maximum %d)", ip_ntoa(host_ipaddr, packet->src_ipaddr), totallen, MAX_PACKET_LEN); free(packet); return NULL; } /* * RFC 2865, Section 3., subsection 'length' says: * * "If the packet is shorter than the Length field * indicates, it MUST be silently discarded." * * i.e. No response to the NAS. */ if (packet->data_len < totallen) { librad_log("WARNING: Malformed RADIUS packet from host %s: received %d octets, packet length says %d", ip_ntoa(host_ipaddr, packet->src_ipaddr), packet->data_len, totallen); free(packet); return NULL; } /* * RFC 2865, Section 3., subsection 'length' says: * * "Octets outside the range of the Length field MUST be * treated as padding and ignored on reception." */ if (packet->data_len > totallen) { /* * We're shortening the packet below, but just * to be paranoid, zero out the extra data. */ memset(data + totallen, 0, packet->data_len - totallen); packet->data_len = totallen; } /* * Walk through the packet's attributes, ensuring that * they add up EXACTLY to the size of the packet. * * If they don't, then the attributes either under-fill * or over-fill the packet. Any parsing of the packet * is impossible, and will result in unknown side effects. * * This would ONLY happen with buggy RADIUS implementations, * or with an intentional attack. Either way, we do NOT want * to be vulnerable to this problem. */ attr = hdr->data; count = totallen - AUTH_HDR_LEN; seen_eap = 0; num_attributes = 0; while (count > 0) { /* * Attribute number zero is NOT defined. */ if (attr[0] == 0) { librad_log("WARNING: Malformed RADIUS packet from host %s: Invalid attribute 0", ip_ntoa(host_ipaddr, packet->src_ipaddr)); free(packet); return NULL; } /* * Attributes are at LEAST as long as the ID & length * fields. Anything shorter is an invalid attribute. */ if (attr[1] < 2) { librad_log("WARNING: Malformed RADIUS packet from host %s: attribute %d too short", ip_ntoa(host_ipaddr, packet->src_ipaddr), attr[0]); free(packet); return NULL; } /* * Sanity check the attributes for length. */ switch (attr[0]) { default: /* don't do anything by default */ break; case PW_EAP_MESSAGE: seen_eap |= PW_EAP_MESSAGE; break; case PW_MESSAGE_AUTHENTICATOR: if (attr[1] != 2 + AUTH_VECTOR_LEN) { librad_log("WARNING: Malformed RADIUS packet from host %s: Message-Authenticator has invalid length %d", ip_ntoa(host_ipaddr, packet->src_ipaddr), attr[1] - 2); free(packet); return NULL; } seen_eap |= PW_MESSAGE_AUTHENTICATOR; break; case PW_VENDOR_SPECIFIC: if (attr[1] <= 6) { librad_log("WARNING: Malformed RADIUS packet from host %s: Vendor-Specific has invalid length %d", ip_ntoa(host_ipaddr, packet->src_ipaddr), attr[1] - 2); free(packet); return NULL; } /* * Don't allow VSA's with vendor zero. */ if ((attr[2] == 0) && (attr[3] == 0) && (attr[4] == 0) && (attr[5] == 0)) { librad_log("WARNING: Malformed RADIUS packet from host %s: Vendor-Specific has vendor ID of zero", ip_ntoa(host_ipaddr, packet->src_ipaddr)); free(packet); return NULL; } /* * Don't look at the contents of VSA's, * too many vendors have non-standard * formats. */ break; } /* * FIXME: Look up the base 255 attributes in the * dictionary, and switch over their type. For * integer/date/ip, the attribute length SHOULD * be 6. */ count -= attr[1]; /* grab the attribute length */ attr += attr[1]; num_attributes++; /* seen one more attribute */ } /* * If the attributes add up to a packet, it's allowed. * * If not, we complain, and throw the packet away. */ if (count != 0) { librad_log("WARNING: Malformed RADIUS packet from host %s: packet attributes do NOT exactly fill the packet", ip_ntoa(host_ipaddr, packet->src_ipaddr)); free(packet); return NULL; } /* * If we're configured to look for a maximum number of * attributes, and we've seen more than that maximum, * then throw the packet away, as a possible DoS. */ if ((librad_max_attributes > 0) && (num_attributes > librad_max_attributes)) { librad_log("WARNING: Possible DoS attack from host %s: Too many attributes in request (received %d, max %d are allowed).", ip_ntoa(host_ipaddr, packet->src_ipaddr), num_attributes, librad_max_attributes); free(packet); return NULL; } /* * http://www.freeradius.org/rfc/rfc2869.html#EAP-Message * * A packet with an EAP-Message attribute MUST also have * a Message-Authenticator attribute. * * A Message-Authenticator all by itself is OK, though. */ if (seen_eap && (seen_eap != PW_MESSAGE_AUTHENTICATOR) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -