|
|
|
@ -12,22 +12,17 @@ |
|
|
|
|
|
|
|
|
|
|
|
#define STUN_CRC_XOR 0x5354554eUL |
|
|
|
|
|
|
|
#define STUN_USERNAME 0x0006 |
|
|
|
#define STUN_MESSAGE_INTEGRITY 0x0008 |
|
|
|
#define STUN_ERROR_CODE 0x0009 |
|
|
|
#define STUN_XOR_MAPPED_ADDRESS 0x0020 |
|
|
|
#define STUN_FINGERPRINT 0x8028 |
|
|
|
|
|
|
|
#define STUN_BINDING_SUCCESS_RESPONSE 0x0101 |
|
|
|
#define STUN_BINDING_ERROR_RESPONSE 0x0111 |
|
|
|
|
|
|
|
struct stun { |
|
|
|
u_int16_t msg_type; |
|
|
|
u_int16_t msg_len; |
|
|
|
u_int32_t cookie; |
|
|
|
unsigned char transaction[12]; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct tlv { |
|
|
|
u_int16_t type; |
|
|
|
u_int16_t len; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct stun_attrs { |
|
|
|
str username; |
|
|
|
@ -41,17 +36,40 @@ struct stun_attrs { |
|
|
|
controlling:1; |
|
|
|
}; |
|
|
|
|
|
|
|
struct stun_error { |
|
|
|
struct stun stun; |
|
|
|
struct tlv error_code; |
|
|
|
struct header { |
|
|
|
u_int16_t msg_type; |
|
|
|
u_int16_t msg_len; |
|
|
|
u_int32_t cookie; |
|
|
|
u_int32_t transaction[3]; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct tlv { |
|
|
|
u_int16_t type; |
|
|
|
u_int16_t len; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct error_code { |
|
|
|
struct tlv tlv; |
|
|
|
u_int32_t codes; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct stun_fingerprint { |
|
|
|
struct fingerprint { |
|
|
|
struct tlv tlv; |
|
|
|
u_int32_t crc; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct msg_integrity { |
|
|
|
struct tlv tlv; |
|
|
|
char digest[20]; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
struct xor_mapped_address { |
|
|
|
struct tlv tlv; |
|
|
|
u_int16_t family; |
|
|
|
u_int16_t port; |
|
|
|
u_int32_t address[4]; |
|
|
|
} __attribute__ ((packed)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int stun_attributes(struct stun_attrs *out, str *s) { |
|
|
|
@ -119,45 +137,126 @@ out: |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static inline void stun_error_len(int fd, struct sockaddr_in6 *sin, struct stun *req, |
|
|
|
static void output_init(struct msghdr *mh, struct iovec *iov, struct sockaddr_in6 *sin, |
|
|
|
struct header *hdr, unsigned short code, u_int32_t *transaction) |
|
|
|
{ |
|
|
|
ZERO(*mh); |
|
|
|
mh->msg_name = sin; |
|
|
|
mh->msg_namelen = sizeof(*sin); |
|
|
|
mh->msg_iov = iov; |
|
|
|
mh->msg_iovlen = 1; |
|
|
|
|
|
|
|
iov->iov_base = hdr; |
|
|
|
iov->iov_len = sizeof(*hdr); |
|
|
|
|
|
|
|
hdr->msg_type = htons(code); |
|
|
|
hdr->msg_len = 0; |
|
|
|
hdr->cookie = htonl(STUN_COOKIE); |
|
|
|
memcpy(&hdr->transaction, transaction, sizeof(hdr->transaction)); |
|
|
|
} |
|
|
|
|
|
|
|
static inline void __output_add(struct msghdr *mh, struct tlv *tlv, unsigned int len, u_int16_t code, |
|
|
|
void *append, unsigned int append_len) |
|
|
|
{ |
|
|
|
struct iovec *iov; |
|
|
|
struct header *hdr; |
|
|
|
|
|
|
|
iov = &mh->msg_iov[mh->msg_iovlen++]; |
|
|
|
iov->iov_base = tlv; |
|
|
|
iov->iov_len = len; |
|
|
|
|
|
|
|
tlv->type = htons(code); |
|
|
|
tlv->len = htons(len - sizeof(*tlv) + append_len); |
|
|
|
|
|
|
|
hdr = mh->msg_iov->iov_base; |
|
|
|
hdr->msg_len += len + ((append_len + 3) & 0xfffc); |
|
|
|
|
|
|
|
if (append_len) { |
|
|
|
iov = &mh->msg_iov[mh->msg_iovlen++]; |
|
|
|
iov->iov_base = append; /* must have space for padding */ |
|
|
|
iov->iov_len = (append_len + 3) & 0xfffc; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#define output_add(mh, attr, code) \ |
|
|
|
__output_add(mh, &(attr)->tlv, sizeof(*(attr)), code, NULL, 0) |
|
|
|
#define output_add_len(mh, attr, code, len) \ |
|
|
|
__output_add(mh, &(attr)->tlv, len + sizeof(struct tlv), code, NULL, 0) |
|
|
|
#define output_add_data(mh, attr, code, data, len) \ |
|
|
|
__output_add(mh, &(attr)->tlv, sizeof(*(attr)), code, data, len) |
|
|
|
|
|
|
|
|
|
|
|
static void output_finish(struct msghdr *mh) { |
|
|
|
struct header *hdr; |
|
|
|
|
|
|
|
hdr = mh->msg_iov->iov_base; |
|
|
|
hdr->msg_len = htons(hdr->msg_len); |
|
|
|
} |
|
|
|
|
|
|
|
static void fingerprint(struct msghdr *mh, struct fingerprint *fp) { |
|
|
|
int i; |
|
|
|
struct iovec *iov; |
|
|
|
struct header *hdr; |
|
|
|
|
|
|
|
output_add(mh, fp, STUN_FINGERPRINT); |
|
|
|
iov = mh->msg_iov; |
|
|
|
hdr = iov->iov_base; |
|
|
|
hdr->msg_len = htons(hdr->msg_len); |
|
|
|
|
|
|
|
fp->crc = crc32(0, NULL, 0); |
|
|
|
for (i = 0; i < mh->msg_iovlen - 1; i++) |
|
|
|
fp->crc = crc32(fp->crc, iov[i].iov_base, iov[i].iov_len); |
|
|
|
|
|
|
|
fp->crc = htons(fp->crc ^ STUN_CRC_XOR); |
|
|
|
hdr->msg_len = ntohs(hdr->msg_len); |
|
|
|
} |
|
|
|
|
|
|
|
static void __integrity(struct iovec *iov, int iov_cnt, str *pwd, char *digest) { |
|
|
|
int i; |
|
|
|
HMAC_CTX ctx; |
|
|
|
|
|
|
|
HMAC_CTX_init(&ctx); |
|
|
|
/* do we need to SASLprep here? */ |
|
|
|
HMAC_Init(&ctx, pwd->s, pwd->len, EVP_sha1()); |
|
|
|
|
|
|
|
for (i = 0; i < iov_cnt; i++) |
|
|
|
HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); |
|
|
|
|
|
|
|
HMAC_Final(&ctx, (void *) digest, NULL); |
|
|
|
HMAC_CTX_cleanup(&ctx); |
|
|
|
} |
|
|
|
|
|
|
|
static void integrity(struct msghdr *mh, struct msg_integrity *mi, str *pwd) { |
|
|
|
struct iovec *iov; |
|
|
|
struct header *hdr; |
|
|
|
|
|
|
|
output_add(mh, mi, STUN_MESSAGE_INTEGRITY); |
|
|
|
iov = mh->msg_iov; |
|
|
|
hdr = iov->iov_base; |
|
|
|
hdr->msg_len = htons(hdr->msg_len); |
|
|
|
|
|
|
|
__integrity(mh->msg_iov, mh->msg_iovlen - 1, pwd, mi->digest); |
|
|
|
|
|
|
|
hdr->msg_len = ntohs(hdr->msg_len); |
|
|
|
} |
|
|
|
|
|
|
|
static void stun_error_len(int fd, struct sockaddr_in6 *sin, struct header *req, |
|
|
|
int code, char *reason, int len) |
|
|
|
{ |
|
|
|
struct stun_error err; |
|
|
|
struct stun_fingerprint fp; |
|
|
|
struct header hdr; |
|
|
|
struct error_code ec; |
|
|
|
struct fingerprint fp; |
|
|
|
struct msghdr mh; |
|
|
|
struct iovec iov[3]; |
|
|
|
struct iovec iov[4]; /* hdr, ec, reason, fp */ |
|
|
|
|
|
|
|
output_init(&mh, iov, sin, &hdr, STUN_BINDING_ERROR_RESPONSE, req->transaction); |
|
|
|
|
|
|
|
ec.codes = htonl(((code / 100) << 8) | (code % 100)); |
|
|
|
output_add_data(&mh, &ec, STUN_ERROR_CODE, reason, len); |
|
|
|
|
|
|
|
err.stun.msg_type = htons(0x0111); /* binding error response */ |
|
|
|
err.stun.cookie = htonl(STUN_COOKIE); |
|
|
|
memcpy(&err.stun.transaction, &req->transaction, sizeof(err.stun.transaction)); |
|
|
|
err.error_code.type = htons(0x0009); /* error-code */ |
|
|
|
err.error_code.len = htons(len + sizeof(err.codes)); |
|
|
|
err.codes = htonl(((code / 100) << 8) | (code % 100)); |
|
|
|
|
|
|
|
ZERO(mh); |
|
|
|
ZERO(iov); |
|
|
|
|
|
|
|
iov[0].iov_base = &err; |
|
|
|
iov[0].iov_len = sizeof(err); |
|
|
|
iov[1].iov_base = reason; |
|
|
|
iov[1].iov_len = (len + 3) & 0xfffc; |
|
|
|
iov[2].iov_base = &fp; |
|
|
|
iov[2].iov_len = sizeof(fp); |
|
|
|
|
|
|
|
err.stun.msg_len = htons(iov[1].iov_len + sizeof(err.codes) + sizeof(err.error_code) |
|
|
|
+ iov[2].iov_len); |
|
|
|
|
|
|
|
fp.crc = crc32(0, iov[0].iov_base, iov[0].iov_len); |
|
|
|
fp.crc = crc32(fp.crc, iov[1].iov_base, iov[1].iov_len); |
|
|
|
fp.crc = htonl(fp.crc ^ STUN_CRC_XOR); |
|
|
|
fp.tlv.type = htons(STUN_FINGERPRINT); |
|
|
|
fp.tlv.len = htons(4); |
|
|
|
|
|
|
|
mh.msg_name = sin; |
|
|
|
mh.msg_namelen = sizeof(*sin); |
|
|
|
mh.msg_iov = iov; |
|
|
|
mh.msg_iovlen = 3; |
|
|
|
fingerprint(&mh, &fp); |
|
|
|
|
|
|
|
output_finish(&mh); |
|
|
|
sendmsg(fd, &mh, 0); |
|
|
|
} |
|
|
|
|
|
|
|
@ -180,11 +279,10 @@ static int check_fingerprint(str *msg, struct stun_attrs *attrs) { |
|
|
|
} |
|
|
|
|
|
|
|
static int check_auth(str *msg, struct stun_attrs *attrs, struct peer *peer) { |
|
|
|
HMAC_CTX ctx; |
|
|
|
u_int16_t lenX; |
|
|
|
unsigned char digest[20]; |
|
|
|
int ret; |
|
|
|
char digest[20]; |
|
|
|
str ufrag[2]; |
|
|
|
struct iovec iov[3]; |
|
|
|
|
|
|
|
if (!peer->ice_ufrag[0].s || !peer->ice_ufrag[0].len) |
|
|
|
return -1; |
|
|
|
@ -203,25 +301,58 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct peer *peer) { |
|
|
|
if (str_cmp_str(&ufrag[0], &peer->ice_ufrag[0])) |
|
|
|
return -1; |
|
|
|
|
|
|
|
HMAC_CTX_init(&ctx); |
|
|
|
HMAC_Init(&ctx, peer->ice_pwd.s, peer->ice_pwd.len, EVP_sha1()); |
|
|
|
HMAC_Update(&ctx, (void *) msg->s, OFFSET_OF(struct stun, msg_len)); |
|
|
|
lenX = htons((attrs->msg_integrity_attr - msg->s) - 20 + 24); |
|
|
|
HMAC_Update(&ctx, (void *) &lenX, sizeof(lenX)); |
|
|
|
HMAC_Update(&ctx, (void *) msg->s + OFFSET_OF(struct stun, cookie), |
|
|
|
ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct stun, cookie)); |
|
|
|
HMAC_Final(&ctx, digest, NULL); |
|
|
|
iov[0].iov_base = msg->s; |
|
|
|
iov[0].iov_len = OFFSET_OF(struct header, msg_len); |
|
|
|
iov[1].iov_base = &lenX; |
|
|
|
iov[1].iov_len = sizeof(lenX); |
|
|
|
iov[2].iov_base = msg->s + OFFSET_OF(struct header, cookie); |
|
|
|
iov[2].iov_len = ntohs(lenX) + - 24 + 20 - OFFSET_OF(struct header, cookie); |
|
|
|
|
|
|
|
ret = memcmp(digest, attrs->msg_integrity.s, 20) ? -1 : 0; |
|
|
|
__integrity(iov, ARRAYSIZE(iov), &peer->ice_pwd, digest); |
|
|
|
|
|
|
|
HMAC_CTX_cleanup(&ctx); |
|
|
|
return memcmp(digest, attrs->msg_integrity.s, 20) ? -1 : 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int stun_binding_success(int fd, struct header *req, struct stun_attrs *attrs, |
|
|
|
struct sockaddr_in6 *sin, struct peer *peer) |
|
|
|
{ |
|
|
|
struct header hdr; |
|
|
|
struct xor_mapped_address xma; |
|
|
|
struct msg_integrity mi; |
|
|
|
struct fingerprint fp; |
|
|
|
struct msghdr mh; |
|
|
|
struct iovec iov[4]; /* hdr, xma, mi, fp */ |
|
|
|
|
|
|
|
output_init(&mh, iov, sin, &hdr, STUN_BINDING_SUCCESS_RESPONSE, req->transaction); |
|
|
|
|
|
|
|
xma.port = sin->sin6_port ^ htons(STUN_COOKIE >> 16); |
|
|
|
if (IN6_IS_ADDR_V4MAPPED(&sin->sin6_addr)) { |
|
|
|
xma.family = htons(0x01); |
|
|
|
xma.address[0] = sin->sin6_addr.s6_addr32[3] ^ htonl(STUN_COOKIE); |
|
|
|
output_add_len(&mh, &xma, STUN_XOR_MAPPED_ADDRESS, 8); |
|
|
|
} |
|
|
|
else { |
|
|
|
xma.family = htons(0x02); |
|
|
|
xma.address[0] = sin->sin6_addr.s6_addr32[0] ^ htonl(STUN_COOKIE); |
|
|
|
xma.address[1] = sin->sin6_addr.s6_addr32[1] ^ req->transaction[0]; |
|
|
|
xma.address[2] = sin->sin6_addr.s6_addr32[2] ^ req->transaction[1]; |
|
|
|
xma.address[3] = sin->sin6_addr.s6_addr32[3] ^ req->transaction[2]; |
|
|
|
output_add(&mh, &xma, STUN_XOR_MAPPED_ADDRESS); |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
integrity(&mh, &mi, &peer->ice_pwd); |
|
|
|
fingerprint(&mh, &fp); |
|
|
|
|
|
|
|
output_finish(&mh); |
|
|
|
sendmsg(fd, &mh, 0); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* XXX add error reporting */ |
|
|
|
int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { |
|
|
|
struct stun *s = (void *) b->s; |
|
|
|
struct header *s = (void *) b->s; |
|
|
|
int msglen, method, class; |
|
|
|
str attr_str; |
|
|
|
struct stun_attrs attrs; |
|
|
|
@ -253,6 +384,8 @@ int stun(str *b, struct streamrelay *sr, struct sockaddr_in6 *sin) { |
|
|
|
if (check_auth(b, &attrs, sr->up)) |
|
|
|
goto unauth; |
|
|
|
|
|
|
|
stun_binding_success(sr->fd.fd, s, &attrs, sin, sr->up); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
bad_req: |
|
|
|
|