📄 radius.c
字号:
} } memcpy(output, passwd, len);}static void make_tunnel_passwd(uint8_t *output, int *outlen, const uint8_t *input, int inlen, int room, const char *secret, const uint8_t *vector){ FR_MD5_CTX context, old; uint8_t digest[AUTH_VECTOR_LEN]; uint8_t passwd[MAX_STRING_LEN + AUTH_VECTOR_LEN]; int i, n; int len; /* * Be paranoid. */ if (room > 253) room = 253; /* * Account for 2 bytes of the salt, and round the room * available down to the nearest multiple of 16. Then, * subtract one from that to account for the length byte, * and the resulting number is the upper bound on the data * to copy. * * We could short-cut this calculation just be forcing * inlen to be no more than 239. It would work for all * VSA's, as we don't pack multiple VSA's into one * attribute. * * However, this calculation is more general, if a little * complex. And it will work in the future for all possible * kinds of weird attribute packing. */ room -= 2; room -= (room & 0x0f); room--; if (inlen > room) inlen = room; /* * Length of the encrypted data is password length plus * one byte for the length of the password. */ len = inlen + 1; if ((len & 0x0f) != 0) { len += 0x0f; len &= ~0x0f; } *outlen = len + 2; /* account for the salt */ /* * Copy the password over. */ memcpy(passwd + 3, input, inlen); memset(passwd + 3 + inlen, 0, sizeof(passwd) - 3 - inlen); /* * Generate salt. The RFC's say: * * The high bit of salt[0] must be set, each salt in a * packet should be unique, and they should be random * * So, we set the high bit, add in a counter, and then * add in some CSPRNG data. should be OK.. */ passwd[0] = (0x80 | ( ((salt_offset++) & 0x0f) << 3) | (fr_rand() & 0x07)); passwd[1] = fr_rand(); passwd[2] = inlen; /* length of the password string */ fr_MD5Init(&context); fr_MD5Update(&context, (const uint8_t *) secret, strlen(secret)); old = context; fr_MD5Update(&context, vector, AUTH_VECTOR_LEN); fr_MD5Update(&context, &passwd[0], 2); for (n = 0; n < len; n += AUTH_PASS_LEN) { if (n > 0) { context = old; fr_MD5Update(&context, passwd + 2 + n - AUTH_PASS_LEN, AUTH_PASS_LEN); } fr_MD5Final(digest, &context); for (i = 0; i < AUTH_PASS_LEN; i++) { passwd[i + 2 + n] ^= digest[i]; } } memcpy(output, passwd, len + 2);}/* * Parse a data structure into a RADIUS attribute. */int rad_vp2attr(const RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret, const VALUE_PAIR *vp, uint8_t *ptr){ int vendorcode; int offset, len, total_length; uint32_t lvalue; uint8_t *length_ptr, *vsa_length_ptr; const uint8_t *data = NULL; uint8_t array[4]; vendorcode = total_length = 0; length_ptr = vsa_length_ptr = NULL; /* * For interoperability, always put vendor attributes * into their own VSA. */ if ((vendorcode = VENDOR(vp->attribute)) == 0) { *(ptr++) = vp->attribute & 0xFF; length_ptr = ptr; *(ptr++) = 2; total_length += 2; } else { int vsa_tlen = 1; int vsa_llen = 1; DICT_VENDOR *dv = dict_vendorbyvalue(vendorcode); /* * This must be an RFC-format attribute. If it * wasn't, then the "decode" function would have * made a Vendor-Specific attribute (i.e. type * 26), and we would have "vendorcode == 0" here. */ if (dv) { vsa_tlen = dv->type; vsa_llen = dv->length; } /* * Build a VSA header. */ *ptr++ = PW_VENDOR_SPECIFIC; vsa_length_ptr = ptr; *ptr++ = 6; lvalue = htonl(vendorcode); memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 6; switch (vsa_tlen) { case 1: ptr[0] = (vp->attribute & 0xFF); break; case 2: ptr[0] = ((vp->attribute >> 8) & 0xFF); ptr[1] = (vp->attribute & 0xFF); break; case 4: ptr[0] = 0; ptr[1] = 0; ptr[2] = ((vp->attribute >> 8) & 0xFF); ptr[3] = (vp->attribute & 0xFF); break; default: return 0; /* silently discard it */ } ptr += vsa_tlen; switch (vsa_llen) { case 0: length_ptr = vsa_length_ptr; vsa_length_ptr = NULL; break; case 1: ptr[0] = 0; length_ptr = ptr; break; case 2: ptr[0] = 0; ptr[1] = 0; length_ptr = ptr + 1; break; default: return 0; /* silently discard it */ } ptr += vsa_llen; total_length += vsa_tlen + vsa_llen; if (vsa_length_ptr) *vsa_length_ptr += vsa_tlen + vsa_llen; *length_ptr += vsa_tlen + vsa_llen; } offset = 0; if (vp->flags.has_tag) { if (TAG_VALID(vp->flags.tag)) { ptr[0] = vp->flags.tag & 0xff; offset = 1; } else if (vp->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { /* * Tunnel passwords REQUIRE a tag, even * if don't have a valid tag. */ ptr[0] = 0; offset = 1; } /* else don't write a tag */ } /* else the attribute doesn't have a tag */ /* * Set up the default sources for the data. */ data = vp->vp_octets; len = vp->length; switch(vp->type) { case PW_TYPE_STRING: case PW_TYPE_OCTETS: case PW_TYPE_IFID: case PW_TYPE_IPV6ADDR: case PW_TYPE_IPV6PREFIX: case PW_TYPE_ABINARY: /* nothing more to do */ break; case PW_TYPE_BYTE: len = 1; /* just in case */ array[0] = vp->vp_integer & 0xff; data = array; offset = 0; break; case PW_TYPE_SHORT: len = 2; /* just in case */ array[0] = (vp->vp_integer >> 8) & 0xff; array[1] = vp->vp_integer & 0xff; data = array; offset = 0; break; case PW_TYPE_INTEGER: len = 4; /* just in case */ lvalue = htonl(vp->vp_integer); memcpy(array, &lvalue, sizeof(lvalue)); /* * Perhaps discard the first octet. */ data = &array[offset]; len -= offset; break; case PW_TYPE_IPADDR: data = (const uint8_t *) &vp->vp_ipaddr; len = 4; /* just in case */ break; /* * There are no tagged date attributes. */ case PW_TYPE_DATE: lvalue = htonl(vp->vp_date); data = (const uint8_t *) &lvalue; len = 4; /* just in case */ break; default: /* unknown type: ignore it */ librad_log("ERROR: Unknown attribute type %d", vp->type); return -1; } /* * Bound the data to 255 bytes. */ if (len + offset + total_length > 255) { len = 255 - offset - total_length; } /* * Encrypt the various password styles * * Attributes with encrypted values MUST be less than * 128 bytes long. */ switch (vp->flags.encrypt) { case FLAG_ENCRYPT_USER_PASSWORD: make_passwd(ptr + offset, &len, data, len, secret, packet->vector); break; case FLAG_ENCRYPT_TUNNEL_PASSWORD: /* * Check if 255 - offset - total_length is less * than 18. If so, we can't fit the data into * the available space, and we discard the * attribute. * * This is ONLY a problem if we have multiple VSA's * in one Vendor-Specific, though. */ if ((255 - offset - total_length) < 18) return 0; switch (packet->code) { case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: default: if (!original) { librad_log("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->name); return -1; } make_tunnel_passwd(ptr + offset, &len, data, len, 255 - offset - total_length, secret, original->vector); break; case PW_ACCOUNTING_REQUEST: case PW_DISCONNECT_REQUEST: case PW_COA_REQUEST: make_tunnel_passwd(ptr + offset, &len, data, len, 255 - offset - total_length, secret, packet->vector); break; } break; /* * The code above ensures that this attribute * always fits. */ case FLAG_ENCRYPT_ASCEND_SECRET: make_secret(ptr + offset, packet->vector, secret, data); len = AUTH_VECTOR_LEN; break; default: /* * Just copy the data over */ memcpy(ptr + offset, data, len); break; } /* switch over encryption flags */ /* * Account for the tag (if any). */ len += offset; /* * RFC 2865 section 5 says that zero-length attributes * MUST NOT be sent. */ if (len == 0) return 0; /* * Update the various lengths. */ *length_ptr += len; if (vsa_length_ptr) *vsa_length_ptr += len; ptr += len; total_length += len; return total_length; /* of attribute */}/* * Encode a packet. */int rad_encode(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret){ radius_packet_t *hdr; uint8_t *ptr; uint16_t total_length; int len; VALUE_PAIR *reply; const char *what; char ip_buffer[128]; /* * For simplicity in the following logic, we allow * the attributes to "overflow" the 4k maximum * RADIUS packet size, by one attribute. * * It's uint32_t, for alignment purposes. */ uint32_t data[(MAX_PACKET_LEN + 256) / 4]; if ((packet->code > 0) && (packet->code < MAX_PACKET_CODE)) { what = packet_codes[packet->code]; } else { what = "Reply"; } DEBUG("Sending %s of id %d to %s port %d\n", what, packet->id, inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, ip_buffer, sizeof(ip_buffer)), packet->dst_port); /* * Double-check some things based on packet code. */ switch (packet->code) { 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; } break; /* * These packet vectors start off as all zero. */ case PW_ACCOUNTING_REQUEST: case PW_DISCONNECT_REQUEST: case PW_COA_REQUEST: memset(packet->vector, 0, sizeof(packet->vector)); break; default: break; } /* * Use memory on the stack, until we know how * large the packet will be. */ hdr = (radius_packet_t *) data; /* * Build standard header */ hdr->code = packet->code; hdr->id = packet->id; memcpy(hdr->vector, packet->vector, sizeof(hdr->vector)); total_length = AUTH_HDR_LEN; /* * Load up the configuration values for the user */ ptr = hdr->data; packet->offset = 0; /* * 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)) {#ifndef NDEBUG /* * Permit the admin to send BADLY formatted * attributes with a debug build. */ if (reply->attribute == PW_RAW_ATTRIBUTE) { memcpy(ptr, reply->vp_octets, reply->length); len = reply->length; goto next; }#endif 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->vp_strvalue, 0, AUTH_VECTOR_LEN); /* * Cache the offset to the * Message-Authenticator */ packet->offset = total_length; } /* * 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; } next: 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -