From 2ea1fe3a473218d26f1ed6bfd19202a8e188db00 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 1 Mar 2013 12:04:52 -0500 Subject: [PATCH] generate stun responses and clean up some code mess --- daemon/stun.c | 257 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 195 insertions(+), 62 deletions(-) diff --git a/daemon/stun.c b/daemon/stun.c index b56f08a08..6ce13cf58 100644 --- a/daemon/stun.c +++ b/daemon/stun.c @@ -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: