645 lines
19 KiB
C
645 lines
19 KiB
C
/*
|
|
* $Id$
|
|
*
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
1. Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <qse/rad/radmsg.h>
|
|
#include "../cmn/mem-prv.h"
|
|
#include "../cmn/syscall.h"
|
|
#include <qse/cmn/hton.h>
|
|
#include <qse/cmn/str.h>
|
|
#include <qse/cmn/mbwc.h>
|
|
#include <qse/cmn/time.h>
|
|
#include <qse/cmn/alg.h>
|
|
#include <qse/cry/md5.h>
|
|
|
|
|
|
void qse_rad_initialize (qse_rad_hdr_t* hdr, qse_rad_code_t code, qse_uint8_t id)
|
|
{
|
|
QSE_MEMSET (hdr, 0, sizeof(*hdr));
|
|
hdr->code = code;
|
|
hdr->id = id;
|
|
hdr->length = qse_hton16(sizeof(*hdr));
|
|
}
|
|
|
|
static QSE_INLINE void xor (void* p, void* q, int length)
|
|
{
|
|
int i;
|
|
qse_uint8_t* pp = (qse_uint8_t*)p;
|
|
qse_uint8_t* qq = (qse_uint8_t*)q;
|
|
for (i = 0; i < length; i++) *(pp++) ^= *(qq++);
|
|
}
|
|
|
|
static void fill_authenticator_randomly (void* authenticator, int length)
|
|
{
|
|
qse_uint8_t* v = (qse_uint8_t*)authenticator;
|
|
int total = 0;
|
|
|
|
#if defined(__linux)
|
|
int fd;
|
|
|
|
fd = QSE_OPEN("/dev/urandom", O_RDONLY, 0); /* Linux: get *real* random numbers */
|
|
if (fd >= 0)
|
|
{
|
|
while (total < length)
|
|
{
|
|
int bytes = QSE_READ (fd, &v[total], length - total);
|
|
if (bytes <= 0) break;
|
|
total += bytes;
|
|
}
|
|
QSE_CLOSE(fd);
|
|
}
|
|
#endif
|
|
|
|
if (total < length)
|
|
{
|
|
qse_ntime_t now;
|
|
qse_uint32_t seed;
|
|
|
|
qse_gettime (&now);
|
|
seed = QSE_GETPID() + now.sec + now.nsec;
|
|
|
|
while (total < length)
|
|
{
|
|
seed = qse_randxs32(seed);
|
|
v[total] = seed % QSE_TYPE_MAX(qse_uint8_t);
|
|
total++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static qse_rad_attr_hdr_t* find_attribute (qse_rad_attr_hdr_t* attr, int* len, qse_uint8_t attrid)
|
|
{
|
|
int rem = *len;
|
|
|
|
while (rem >= QSE_SIZEOF(*attr))
|
|
{
|
|
/* sanity checks */
|
|
if (rem < attr->length) return NULL;
|
|
if (attr->length < QSE_SIZEOF(*attr))
|
|
{
|
|
/* attribute length cannot be less than the header size.
|
|
* the packet could be corrupted... */
|
|
return NULL;
|
|
}
|
|
|
|
rem -= attr->length;
|
|
if (attr->id == attrid)
|
|
{
|
|
*len = rem;
|
|
return attr;
|
|
}
|
|
|
|
attr = (qse_rad_attr_hdr_t*) ((char*) attr + attr->length);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
qse_rad_attr_hdr_t* qse_rad_find_attribute (qse_rad_hdr_t* hdr, qse_uint8_t attrid, int index)
|
|
{
|
|
qse_rad_attr_hdr_t *attr = (qse_rad_attr_hdr_t*)(hdr+1);
|
|
int len = qse_ntoh16(hdr->length) - QSE_SIZEOF(*hdr);
|
|
attr = find_attribute (attr, &len, attrid);
|
|
while (attr)
|
|
{
|
|
if (index <= 0) return attr;
|
|
index--;
|
|
attr = find_attribute ((qse_rad_attr_hdr_t*)((char*)attr+attr->length), &len, attrid);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
qse_rad_attr_hdr_t* qse_rad_find_vendor_specific_attribute (qse_rad_hdr_t* hdr, qse_uint32_t vendor, qse_uint8_t attrid, int index)
|
|
{
|
|
qse_rad_attr_hdr_t *attr = (qse_rad_attr_hdr_t*)(hdr+1);
|
|
int len = qse_ntoh16(hdr->length) - QSE_SIZEOF(*hdr);
|
|
|
|
attr = find_attribute (attr, &len, QSE_RAD_ATTR_VENDOR_SPECIFIC);
|
|
while (attr)
|
|
{
|
|
qse_rad_vsattr_hdr_t* vsattr;
|
|
|
|
if (attr->length >= QSE_SIZEOF(*vsattr)) /* sanity check */
|
|
{
|
|
vsattr = (qse_rad_vsattr_hdr_t*)attr;
|
|
|
|
if (qse_ntoh32(vsattr->vendor) == vendor)
|
|
{
|
|
qse_rad_attr_hdr_t* subattr;
|
|
int sublen = vsattr->length - QSE_SIZEOF(*vsattr);
|
|
|
|
if (sublen >= QSE_SIZEOF(*subattr)) /* sanity check */
|
|
{
|
|
subattr = (qse_rad_attr_hdr_t*)(vsattr + 1);
|
|
if (subattr->id == attrid && subattr->length == sublen)
|
|
{
|
|
if (index <= 0) return subattr;
|
|
index--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
attr = find_attribute ((qse_rad_attr_hdr_t*)((char*)attr+attr->length), &len, QSE_RAD_ATTR_VENDOR_SPECIFIC);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int qse_rad_walk_attributes (const qse_rad_hdr_t* hdr, qse_rad_attr_walker_t walker, void* ctx)
|
|
{
|
|
int totlen, rem;
|
|
qse_rad_attr_hdr_t *attr;
|
|
|
|
totlen = qse_ntoh16(hdr->length);
|
|
if (totlen < QSE_SIZEOF(*hdr)) return -1;
|
|
|
|
rem = totlen - QSE_SIZEOF(*hdr);
|
|
attr = (qse_rad_attr_hdr_t*)(hdr + 1);
|
|
while (rem >= QSE_SIZEOF(*attr))
|
|
{
|
|
/* sanity checks */
|
|
if (rem < attr->length) return -1;
|
|
if (attr->length < QSE_SIZEOF(*attr))
|
|
{
|
|
/* attribute length cannot be less than the header size.
|
|
* the packet could be corrupted... */
|
|
return -1;
|
|
}
|
|
|
|
rem -= attr->length;
|
|
|
|
if (attr->id == QSE_RAD_ATTR_VENDOR_SPECIFIC)
|
|
{
|
|
qse_rad_vsattr_hdr_t* vsattr;
|
|
qse_rad_attr_hdr_t* subattr;
|
|
int sublen;
|
|
|
|
if (attr->length < QSE_SIZEOF(*vsattr)) return -1;
|
|
vsattr = (qse_rad_vsattr_hdr_t*)attr;
|
|
|
|
sublen = vsattr->length - QSE_SIZEOF(*vsattr);
|
|
if (sublen < QSE_SIZEOF(*subattr)) return -1;
|
|
subattr = (qse_rad_attr_hdr_t*)(vsattr + 1);
|
|
if (subattr->length != sublen) return -1;
|
|
|
|
/* if this vendor happens to be 0, walker can't tell
|
|
* if it is vendor specific or not because 0 is passed in
|
|
* for non-VSAs. but i don't care. in reality,
|
|
* 0 is reserved in IANA enterpirse number assignments.
|
|
* (http://www.iana.org/assignments/enterprise-numbers) */
|
|
if (walker (hdr, qse_ntoh32(vsattr->vendor), subattr, ctx) <= -1) return -1;
|
|
}
|
|
else
|
|
{
|
|
if (walker (hdr, 0, attr, ctx) <= -1) return -1;
|
|
}
|
|
|
|
attr = (qse_rad_attr_hdr_t*) ((char*) attr + attr->length);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int qse_rad_insert_attribute (
|
|
qse_rad_hdr_t* auth, int max,
|
|
qse_uint8_t id, const void* ptr, qse_uint8_t len)
|
|
{
|
|
qse_rad_attr_hdr_t* attr;
|
|
int auth_len = qse_ntoh16(auth->length);
|
|
int new_auth_len;
|
|
|
|
/*if (len > QSE_RAD_MAX_ATTR_VALUE_LEN) return -1;*/
|
|
if (len > QSE_RAD_MAX_ATTR_VALUE_LEN) len = QSE_RAD_MAX_ATTR_VALUE_LEN;
|
|
new_auth_len = auth_len + len + QSE_SIZEOF(*attr);
|
|
|
|
if (new_auth_len > max) return -1;
|
|
|
|
attr = (qse_rad_attr_hdr_t*) ((char*)auth + auth_len);
|
|
attr->id = id;
|
|
attr->length = new_auth_len - auth_len;
|
|
QSE_MEMCPY (attr + 1, ptr, len);
|
|
auth->length = qse_hton16(new_auth_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qse_rad_insert_vendor_specific_attribute (
|
|
qse_rad_hdr_t* auth, int max,
|
|
qse_uint32_t vendor, qse_uint8_t attrid, const void* ptr, qse_uint8_t len)
|
|
{
|
|
qse_rad_vsattr_hdr_t* attr;
|
|
qse_rad_attr_hdr_t* subattr;
|
|
int auth_len = qse_ntoh16(auth->length);
|
|
int new_auth_len;
|
|
|
|
|
|
/*if (len > QSE_RAD_MAX_VSATTR_VALUE_LEN) return -1;*/
|
|
if (len > QSE_RAD_MAX_VSATTR_VALUE_LEN) len = QSE_RAD_MAX_VSATTR_VALUE_LEN;
|
|
new_auth_len = auth_len + len + QSE_SIZEOF(*attr) + QSE_SIZEOF(*subattr);
|
|
|
|
if (new_auth_len > max) return -1;
|
|
|
|
attr = (qse_rad_vsattr_hdr_t*) ((char*)auth + auth_len);
|
|
attr->id = QSE_RAD_ATTR_VENDOR_SPECIFIC;
|
|
attr->length = new_auth_len - auth_len;
|
|
attr->vendor = qse_hton32 (vendor);
|
|
|
|
subattr = (qse_rad_attr_hdr_t*)(attr + 1);
|
|
subattr->id = attrid;
|
|
subattr->length = len + QSE_SIZEOF(*subattr);
|
|
QSE_MEMCPY (subattr + 1, ptr, len);
|
|
|
|
auth->length = qse_hton16(new_auth_len);
|
|
return 0;
|
|
}
|
|
|
|
static int delete_attribute (qse_rad_hdr_t* auth, qse_rad_attr_hdr_t* attr)
|
|
{
|
|
qse_uint16_t auth_len;
|
|
qse_uint16_t tmp_len;
|
|
|
|
auth_len = qse_ntoh16(auth->length);
|
|
tmp_len = ((qse_uint8_t*)attr - (qse_uint8_t*)auth) + attr->length;
|
|
if (tmp_len > auth_len) return -1; /* can this happen? */
|
|
|
|
QSE_MEMCPY (attr, (qse_uint8_t*)attr + attr->length, auth_len - tmp_len);
|
|
|
|
auth_len -= attr->length;
|
|
auth->length = qse_hton16(auth_len);
|
|
return 0;
|
|
}
|
|
|
|
int qse_rad_delete_attribute (qse_rad_hdr_t* auth, qse_uint8_t attrid)
|
|
{
|
|
qse_rad_attr_hdr_t* attr;
|
|
|
|
attr = qse_rad_find_attribute (auth, attrid, 0);
|
|
if (attr == NULL) return 0; /* not found */
|
|
return (delete_attribute (auth, attr) <= -1)? -1: 1;
|
|
}
|
|
|
|
int qse_rad_delete_vendor_specific_attribute (
|
|
qse_rad_hdr_t* auth, qse_uint32_t vendor, qse_uint8_t attrid)
|
|
{
|
|
qse_rad_attr_hdr_t* attr;
|
|
qse_rad_vsattr_hdr_t* vsattr;
|
|
|
|
attr = qse_rad_find_vendor_specific_attribute (auth, vendor, attrid, 0);
|
|
if (attr == NULL) return 0; /* not found */
|
|
|
|
vsattr = (qse_rad_vsattr_hdr_t*)((qse_uint8_t*)attr - QSE_SIZEOF(qse_rad_vsattr_hdr_t));
|
|
return (delete_attribute (auth, (qse_rad_attr_hdr_t*)vsattr) <= -1)? -1: 1;
|
|
}
|
|
|
|
int qse_rad_insert_string_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor,
|
|
qse_uint8_t id, const qse_mchar_t* value)
|
|
{
|
|
return (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, value, qse_mbslen(value)):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, value, qse_mbslen(value));
|
|
}
|
|
|
|
int qse_rad_insert_wide_string_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor,
|
|
qse_uint8_t id, const qse_wchar_t* value)
|
|
{
|
|
int n;
|
|
qse_mchar_t* val;
|
|
qse_size_t mbslen;
|
|
|
|
val = qse_wcstombsdup (value, &mbslen, QSE_MMGR_GETDFL());
|
|
n = (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, val, mbslen):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, val, mbslen);
|
|
QSE_MMGR_FREE (QSE_MMGR_GETDFL(), val);
|
|
|
|
return n;
|
|
}
|
|
|
|
int qse_rad_insert_string_attribute_with_length (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor,
|
|
qse_uint8_t id, const qse_mchar_t* value, qse_uint8_t length)
|
|
{
|
|
return (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, value, length):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, value, length);
|
|
}
|
|
|
|
int qse_rad_insert_wide_string_attribute_with_length (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor,
|
|
qse_uint8_t id, const qse_wchar_t* value, qse_uint8_t length)
|
|
{
|
|
int n;
|
|
qse_mchar_t* val;
|
|
qse_size_t mbslen;
|
|
|
|
val = qse_wcsntombsdup (value, length, &mbslen, QSE_MMGR_GETDFL());
|
|
n = (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, val, mbslen):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, val, mbslen);
|
|
QSE_MMGR_FREE (QSE_MMGR_GETDFL(), val);
|
|
|
|
return n;
|
|
}
|
|
|
|
int qse_rad_insert_uint32_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor, qse_uint8_t id, qse_uint32_t value)
|
|
{
|
|
qse_uint32_t val = qse_hton32(value);
|
|
return (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, &val, QSE_SIZEOF(val)):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, &val, QSE_SIZEOF(val));
|
|
}
|
|
|
|
int qse_rad_insert_ipv6prefix_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor, qse_uint8_t id,
|
|
qse_uint8_t prefix_bits, const qse_ip6ad_t* value)
|
|
{
|
|
struct ipv6prefix_t
|
|
{
|
|
qse_uint8_t reserved;
|
|
qse_uint8_t bits;
|
|
qse_ip6ad_t value;
|
|
} __attribute__((__packed__));
|
|
|
|
struct ipv6prefix_t ipv6prefix;
|
|
qse_uint8_t i, j;
|
|
|
|
if (prefix_bits > 128) prefix_bits = 128;
|
|
|
|
QSE_MEMSET (&ipv6prefix, 0, sizeof(ipv6prefix));
|
|
ipv6prefix.bits = prefix_bits;
|
|
|
|
for (i = 0, j = 0; i < prefix_bits; i += 8, j++)
|
|
{
|
|
qse_uint8_t bits = prefix_bits - i;
|
|
if (bits >= 8)
|
|
{
|
|
ipv6prefix.value.value[j] = value->value[j];
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
1 -> 10000000
|
|
2 -> 11000000
|
|
3 -> 11100000
|
|
4 -> 11110000
|
|
5 -> 11111000
|
|
6 -> 11111100
|
|
7 -> 11111110
|
|
*/
|
|
ipv6prefix.value.value[j] = value->value[j] & (0xFF << (8 - bits));
|
|
}
|
|
}
|
|
|
|
return (vendor == 0)?
|
|
qse_rad_insert_attribute (auth, max, id, &ipv6prefix, j + 2):
|
|
qse_rad_insert_vendor_specific_attribute (auth, max, vendor, id, &ipv6prefix, j + 2);
|
|
}
|
|
|
|
int qse_rad_insert_giga_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint32_t vendor, int low_id, int high_id, qse_uint64_t value)
|
|
{
|
|
qse_uint32_t low;
|
|
low = value & QSE_TYPE_MAX(qse_uint32_t);
|
|
low = qse_hton32(low);
|
|
|
|
if (vendor == 0)
|
|
{
|
|
if (qse_rad_insert_attribute (auth, max, low_id, &low, QSE_SIZEOF(low)) <= -1) return -1;
|
|
|
|
if (value > QSE_TYPE_MAX(qse_uint32_t))
|
|
{
|
|
qse_uint32_t high;
|
|
high = value >> (QSE_SIZEOF(qse_uint32_t) * 8);
|
|
high = qse_hton32(high);
|
|
if (qse_rad_insert_attribute (auth, max, high_id, &high, QSE_SIZEOF(high)) <= -1) return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qse_rad_insert_vendor_specific_attribute (auth, max, vendor, low_id, &low, QSE_SIZEOF(low)) <= -1) return -1;
|
|
|
|
if (value > QSE_TYPE_MAX(qse_uint32_t))
|
|
{
|
|
qse_uint32_t high;
|
|
high = value >> (QSE_SIZEOF(qse_uint32_t) * 8);
|
|
high = qse_hton32(high);
|
|
if (qse_rad_insert_vendor_specific_attribute (auth, max, vendor, high_id, &high, QSE_SIZEOF(high)) <= -1) return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int qse_rad_insert_extended_vendor_specific_attribute (
|
|
qse_rad_hdr_t* auth, int max, qse_uint8_t base, qse_uint32_t vendor,
|
|
qse_uint8_t attrid, const void* ptr, qse_uint8_t len)
|
|
{
|
|
/* RFC6929 */
|
|
qse_rad_extvsattr_hdr_t* attr;
|
|
int auth_len = qse_ntoh16(auth->length);
|
|
int new_auth_len;
|
|
|
|
if (base < 241 && base > 244) return -1;
|
|
/* TODO: for 245 and 246, switch to long-extended format */
|
|
|
|
/*if (len > QSE_RAD_MAX_EXTVSATTR_VALUE_LEN) return -1;*/
|
|
if (len > QSE_RAD_MAX_EXTVSATTR_VALUE_LEN) len = QSE_RAD_MAX_EXTVSATTR_VALUE_LEN;
|
|
new_auth_len = auth_len + len + QSE_SIZEOF(*attr);
|
|
|
|
if (new_auth_len > max) return -1;
|
|
|
|
attr = (qse_rad_extvsattr_hdr_t*) ((char*)auth + auth_len);
|
|
attr->id = base;
|
|
attr->length = new_auth_len - auth_len;
|
|
attr->xid = QSE_RAD_ATTR_VENDOR_SPECIFIC;
|
|
attr->vendor = qse_hton32(vendor);
|
|
attr->evsid = attrid;
|
|
|
|
/* no special header for the evs-value */
|
|
QSE_MEMCPY (attr + 1, ptr, len);
|
|
|
|
auth->length = qse_hton16(new_auth_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
#define PASS_BLKSIZE QSE_RAD_MAX_AUTHENTICATOR_LEN
|
|
#define ALIGN(x,factor) ((((x) + (factor) - 1) / (factor)) * (factor))
|
|
|
|
int qse_rad_set_user_password (qse_rad_hdr_t* auth, int max, const qse_mchar_t* password, const qse_mchar_t* secret)
|
|
{
|
|
qse_md5_t md5;
|
|
|
|
qse_uint8_t hashed[QSE_RAD_MAX_ATTR_VALUE_LEN]; /* can't be longer than this */
|
|
qse_uint8_t tmp[PASS_BLKSIZE];
|
|
|
|
int i, pwlen, padlen;
|
|
|
|
QSE_ASSERT (QSE_SIZEOF(tmp) >= QSE_MD5_DIGEST_LEN);
|
|
|
|
pwlen = qse_mbslen(password);
|
|
|
|
/* calculate padlen to be the multiples of 16.
|
|
* 0 is forced to 16. */
|
|
padlen = (pwlen <= 0)? PASS_BLKSIZE: ALIGN(pwlen,PASS_BLKSIZE);
|
|
|
|
/* keep the padded length limited within the maximum attribute length */
|
|
if (padlen > QSE_RAD_MAX_ATTR_VALUE_LEN)
|
|
{
|
|
padlen = QSE_RAD_MAX_ATTR_VALUE_LEN;
|
|
padlen = ALIGN(padlen,PASS_BLKSIZE);
|
|
if (padlen > QSE_RAD_MAX_ATTR_VALUE_LEN) padlen -= PASS_BLKSIZE;
|
|
|
|
/* also limit the original length */
|
|
if (pwlen > padlen) pwlen = padlen;
|
|
}
|
|
|
|
QSE_MEMSET (hashed, 0, padlen);
|
|
QSE_MEMCPY (hashed, password, pwlen);
|
|
|
|
/*
|
|
* c1 = p1 XOR MD5(secret + authenticator)
|
|
* c2 = p2 XOR MD5(secret + c1)
|
|
* ...
|
|
* cn = pn XOR MD5(secret + cn-1)
|
|
*/
|
|
qse_md5_initialize (&md5);
|
|
qse_md5_update (&md5, secret, qse_mbslen(secret));
|
|
qse_md5_update (&md5, auth->authenticator, QSE_SIZEOF(auth->authenticator));
|
|
qse_md5_digest (&md5, tmp, QSE_SIZEOF(tmp));
|
|
|
|
xor (&hashed[0], tmp, QSE_SIZEOF(tmp));
|
|
|
|
for (i = 1; i < (padlen >> 4); i++)
|
|
{
|
|
qse_md5_initialize (&md5);
|
|
qse_md5_update (&md5, secret, qse_mbslen(secret));
|
|
qse_md5_update (&md5, &hashed[(i-1) * PASS_BLKSIZE], PASS_BLKSIZE);
|
|
qse_md5_digest (&md5, tmp, QSE_SIZEOF(tmp));
|
|
xor (&hashed[i * PASS_BLKSIZE], tmp, QSE_SIZEOF(tmp));
|
|
}
|
|
|
|
/* ok if not found or deleted. but not ok if an error occurred */
|
|
if (qse_rad_delete_attribute (auth, QSE_RAD_ATTR_USER_PASSWORD) <= -1) goto oops;
|
|
if (qse_rad_insert_attribute (auth, max, QSE_RAD_ATTR_USER_PASSWORD, hashed, padlen) <= -1) goto oops;
|
|
|
|
return 0;
|
|
|
|
oops:
|
|
return -1;
|
|
}
|
|
|
|
void qse_rad_fill_authenticator (qse_rad_hdr_t* auth)
|
|
{
|
|
fill_authenticator_randomly (auth->authenticator, QSE_SIZEOF(auth->authenticator));
|
|
}
|
|
|
|
void qse_rad_copy_authenticator (qse_rad_hdr_t* dst, const qse_rad_hdr_t* src)
|
|
{
|
|
QSE_MEMCPY (dst->authenticator, src->authenticator, QSE_SIZEOF(dst->authenticator));
|
|
}
|
|
|
|
int qse_rad_set_authenticator (qse_rad_hdr_t* req, const qse_mchar_t* secret)
|
|
{
|
|
qse_md5_t md5;
|
|
|
|
/* this assumes that req->authentcator at this point
|
|
* is filled with zeros. so make sure that it contains zeros
|
|
* before you call this function */
|
|
|
|
qse_md5_initialize (&md5);
|
|
qse_md5_update (&md5, req, qse_ntoh16(req->length));
|
|
if (*secret) qse_md5_update (&md5, secret, qse_mbslen(secret));
|
|
qse_md5_digest (&md5, req->authenticator, QSE_SIZEOF(req->authenticator));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int qse_rad_verify_request (qse_rad_hdr_t* req, const qse_mchar_t* secret)
|
|
{
|
|
qse_md5_t md5;
|
|
qse_uint8_t orgauth[QSE_RAD_MAX_AUTHENTICATOR_LEN];
|
|
int ret;
|
|
|
|
QSE_MEMCPY (orgauth, req->authenticator, QSE_SIZEOF(req->authenticator));
|
|
QSE_MEMSET (req->authenticator, 0, QSE_SIZEOF(req->authenticator));
|
|
|
|
qse_md5_initialize (&md5);
|
|
qse_md5_update (&md5, req, qse_ntoh16(req->length));
|
|
if (*secret) qse_md5_update (&md5, secret, qse_mbslen(secret));
|
|
qse_md5_digest (&md5, req->authenticator, QSE_SIZEOF(req->authenticator));
|
|
|
|
ret = (QSE_MEMCMP (req->authenticator, orgauth, QSE_SIZEOF(req->authenticator)) == 0)? 1: 0;
|
|
QSE_MEMCPY (req->authenticator, orgauth, QSE_SIZEOF(req->authenticator));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int qse_rad_verify_response (qse_rad_hdr_t* res, const qse_rad_hdr_t* req, const qse_mchar_t* secret)
|
|
{
|
|
qse_md5_t md5;
|
|
|
|
qse_uint8_t calculated[QSE_RAD_MAX_AUTHENTICATOR_LEN];
|
|
qse_uint8_t reply[QSE_RAD_MAX_AUTHENTICATOR_LEN];
|
|
|
|
QSE_ASSERT (QSE_SIZEOF(req->authenticator) == QSE_RAD_MAX_AUTHENTICATOR_LEN);
|
|
QSE_ASSERT (QSE_SIZEOF(res->authenticator) == QSE_RAD_MAX_AUTHENTICATOR_LEN);
|
|
|
|
/*
|
|
* We could dispense with the QSE_MEMCPY, and do MD5's of the packet
|
|
* + authenticator piece by piece. This is easier understand,
|
|
* and maybe faster.
|
|
*/
|
|
QSE_MEMCPY(reply, res->authenticator, QSE_SIZEOF(res->authenticator)); /* save the reply */
|
|
QSE_MEMCPY(res->authenticator, req->authenticator, QSE_SIZEOF(req->authenticator)); /* sent authenticator */
|
|
|
|
/* MD5(response packet header + authenticator + response packet data + secret) */
|
|
qse_md5_initialize (&md5);
|
|
qse_md5_update (&md5, res, qse_ntoh16(res->length));
|
|
|
|
/*
|
|
* This next bit is necessary because of a bug in the original Livingston
|
|
* RADIUS server. The authentication authenticator is *supposed* to be
|
|
* MD5'd with the old password (as the secret) for password changes.
|
|
* However, the old password isn't used. The "authentication" authenticator
|
|
* for the server reply packet is simply the MD5 of the reply packet.
|
|
* Odd, the code is 99% there, but the old password is never copied
|
|
* to the secret!
|
|
*/
|
|
if (*secret) qse_md5_update (&md5, secret, qse_mbslen(secret));
|
|
qse_md5_digest (&md5, calculated, QSE_SIZEOF(calculated));
|
|
|
|
/* Did he use the same random authenticator + shared secret? */
|
|
return (QSE_MEMCMP(calculated, reply, QSE_SIZEOF(reply)) != 0)? 0: 1;
|
|
}
|