📄 radius.c
字号:
hdr->id = packet->id; memcpy(hdr->vector, packet->vector, sizeof(hdr->vector)); total_length = AUTH_HDR_LEN; packet->verified = 0; /* * Load up the configuration values for the user */ ptr = hdr->data; /* * FIXME: Loop twice over the reply list. The first time, * calculate the total length of data. The second time, * allocate the memory, and fill in the VP's. * * Hmm... this may be slower than just doing a small * memcpy. */ /* * Loop over the reply attributes for the packet. */ for (reply = packet->vps; reply; reply = reply->next) { /* * Ignore non-wire attributes */ if ((VENDOR(reply->attribute) == 0) && ((reply->attribute & 0xFFFF) > 0xff)) { continue; } /* * Set the Message-Authenticator to the correct * length and initial value. */ if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) { reply->length = AUTH_VECTOR_LEN; memset(reply->strvalue, 0, AUTH_VECTOR_LEN); packet->verified = total_length; /* HACK! */ } /* * Print out ONLY the attributes which * we're sending over the wire, and print * them out BEFORE they're encrypted. */ debug_pair(reply); len = rad_vp2attr(packet, original, secret, reply, ptr); if (len < 0) return -1; /* * Check that the packet is no more than 4k in * size, AFTER writing the attribute past the 4k * boundary, but BEFORE deciding to increase the * size of the packet. Note that the 'data' * buffer, above, is one attribute longer than * necessary, in order to permit this overflow. */ if ((total_length + len) > MAX_PACKET_LEN) { break; } ptr += len; total_length += len; } /* done looping over all attributes */ /* * Fill in the rest of the fields, and copy the data over * from the local stack to the newly allocated memory. * * Yes, all this 'memcpy' is slow, but it means * that we only allocate the minimum amount of * memory for a request. */ packet->data_len = total_length; packet->data = (uint8_t *) malloc(packet->data_len); 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(total_length)); return 0;}/* * Sign a previously encoded packet. */int rad_sign(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret){ radius_packet_t *hdr = (radius_packet_t *)packet->data; /* * It wasn't assigned an Id, this is bad! */ if (packet->id < 0) { librad_log("ERROR: RADIUS packets must be assigned an Id."); return -1; } if (!packet->data || (packet->data_len < AUTH_HDR_LEN) || (packet->verified < 0)) { librad_log("ERROR: You must call rad_encode() before rad_sign()"); return -1; } /* * If there's a Message-Authenticator, update it * now, BEFORE updating the authentication vector. * * This is a hack... */ if (packet->verified > 0) { uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; switch (packet->code) { case PW_ACCOUNTING_REQUEST: case PW_ACCOUNTING_RESPONSE: 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(hdr->vector, 0, AUTH_VECTOR_LEN); break; case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (!original) { librad_log("ERROR: Cannot sign response packet without a request packet."); return -1; } memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); break; default: /* others have vector already set to zero */ break; } /* * Set the authentication vector to zero, * calculate the signature, and put it * into the Message-Authenticator * attribute. */ lrad_hmac_md5(packet->data, packet->data_len, secret, strlen(secret), calc_auth_vector); memcpy(packet->data + packet->verified + 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: { uint8_t digest[16]; 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 */ return 0;}/* * Reply to the request. Also attach * reply attribute value pairs and any user message provided. */int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret){ VALUE_PAIR *reply; const char *what; char ip_buffer[128]; struct sockaddr_in saremote; struct sockaddr_in *sa; /* * Maybe it's a fake packet. Don't send it. */ if (!packet || (packet->sockfd < 0)) { return 0; } if ((packet->code > 0) && (packet->code < 52)) { what = packet_codes[packet->code]; } else { what = "Reply"; } /* * First time through, allocate room for the packet */ if (!packet->data) { DEBUG("Sending %s of id %d to %s port %d\n", what, packet->id, ip_ntoa(ip_buffer, packet->dst_ipaddr), packet->dst_port); /* * Encode the packet. */ if (rad_encode(packet, original, secret) < 0) { return -1; } /* * Re-sign it, including updating the * Message-Authenticator. */ if (rad_sign(packet, original, secret) < 0) { return -1; } /* * 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 port %d\n", what, packet->id, ip_ntoa(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 require_ma = 0; int seen_ma = 0; 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; } /* * Message-Authenticator is required in Status-Server * packets, otherwise they can be trivially forged. */ if (hdr->code == PW_STATUS_SERVER) require_ma = 1; /* * 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; 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -