static bool request_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { struct stun_attr *mi, *user, *realm, *nonce; const time_t now = time(NULL); char nstr[NONCE_MAX_SIZE + 1]; int err; (void)dst; if (ctx->key) return false; mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY); user = stun_msg_attr(msg, STUN_ATTR_USERNAME); realm = stun_msg_attr(msg, STUN_ATTR_REALM); nonce = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!mi) { err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!user || !realm || !nonce) { err = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Request", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!nonce_validate(nonce->v.nonce, now, src)) { err = stun_ereply(proto, sock, src, 0, msg, 438, "Stale Nonce", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->key = mem_alloc(MD5_SIZE, NULL); if (!ctx->key) { restund_warning("auth: can't to allocate memory for MI key\n"); err = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->keylen = MD5_SIZE; if (auth.sharedsecret_length > 0 || auth.sharedsecret2_length > 0) { if (!((sharedsecret_auth_calc_ha1(user, (uint8_t*) auth.sharedsecret, auth.sharedsecret_length, ctx->key) && !stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) || (sharedsecret_auth_calc_ha1(user, (uint8_t*) auth.sharedsecret2, auth.sharedsecret2_length, ctx->key) && !stun_msg_chk_mi(msg, ctx->key, ctx->keylen)))) { restund_info("auth: shared secret auth for user '%s' (%j) failed\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } else { /* restund_info("auth: shared secret auth for user '%s' (%j) worked\n", user->v.username, src); */ if (STUN_METHOD_ALLOCATE == stun_msg_method(msg) && !sharedsecret_auth_check_timestamp(user, now)) { restund_info("auth: shared secret auth for user '%s' expired)\n", user->v.username); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } } } else if (restund_get_ha1(user->v.username, ctx->key)) { restund_info("auth: unknown user '%s' (%j)\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) { restund_info("auth: bad password for user '%s' (%j)\n", user->v.username, src); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } return false; unauth: if (err) { restund_warning("auth reply error: %m\n", err); } return true; }
int icem_stund_recv(struct icem_comp *comp, const struct sa *src, struct stun_msg *req, size_t presz) { struct icem *icem = comp->icem; struct ice *ice = icem->ice; struct stun_attr *attr; struct pl lu, ru; enum role rrole = ROLE_UNKNOWN; uint64_t tiebrk = 0; uint32_t prio_prflx; bool use_cand = false; int err; /* RFC 5389: Fingerprint errors are silently discarded */ err = stun_msg_chk_fingerprint(req); if (err) return err; err = stun_msg_chk_mi(req, (uint8_t *)ice->lpwd, strlen(ice->lpwd)); if (err) { if (err == EBADMSG) goto unauth; else goto badmsg; } attr = stun_msg_attr(req, STUN_ATTR_USERNAME); if (!attr) goto badmsg; err = re_regex(attr->v.username, strlen(attr->v.username), "[^:]+:[^]+", &lu, &ru); if (err) { DEBUG_WARNING("could not parse USERNAME attribute (%s)\n", attr->v.username); goto unauth; } if (pl_strcmp(&lu, ice->lufrag)) goto unauth; if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) goto unauth; attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED); if (attr) { rrole = ROLE_CONTROLLED; tiebrk = attr->v.uint64; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING); if (attr) { rrole = ROLE_CONTROLLING; tiebrk = attr->v.uint64; } if (rrole == ice->lrole) { if (ice->tiebrk >= tiebrk) ice_switch_local_role(ice); else goto conflict; } attr = stun_msg_attr(req, STUN_ATTR_PRIORITY); if (attr) prio_prflx = attr->v.uint32; else goto badmsg; attr = stun_msg_attr(req, STUN_ATTR_USE_CAND); if (attr) use_cand = true; err = handle_stun(ice, icem, comp, src, prio_prflx, use_cand, presz > 0); if (err) goto badmsg; return stun_reply(icem->proto, comp->sock, src, presz, req, (uint8_t *)ice->lpwd, strlen(ice->lpwd), true, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, sw); badmsg: return stunsrv_ereply(comp, src, presz, req, 400, "Bad Request"); unauth: return stunsrv_ereply(comp, src, presz, req, 401, "Unauthorized"); conflict: return stunsrv_ereply(comp, src, presz, req, 487, "Role Conflict"); }
int test_stun_req(void) { struct stun_msg *msg = NULL; struct mbuf *mb; struct stun_attr *attr; int err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, tid, NULL, (uint8_t *)password.p, password.l, true, 0x20, 4, STUN_ATTR_SOFTWARE, client_sw, STUN_ATTR_PRIORITY, &ice_prio, STUN_ATTR_CONTROLLED, &ice_contr, STUN_ATTR_USERNAME, username); if (err) goto out; TEST_MEMCMP(req, sizeof(req)-1, mb->buf, mb->end); /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_REQUEST != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto out; err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); if (err) goto out; err = stun_msg_chk_fingerprint(msg); if (err) goto out; attr = stun_msg_attr(msg, STUN_ATTR_PRIORITY); if (!attr || ice_prio != attr->v.priority) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_CONTROLLED); if (!attr || ice_contr != attr->v.controlled) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); if (!attr || strcmp(username, attr->v.username)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); if (!attr || strcmp(client_sw, attr->v.software)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }
int test_stun_reqltc(void) { struct stun_msg *msg = NULL; struct stun_attr *attr; struct mbuf *mb; uint8_t md5_hash[MD5_SIZE]; int r, err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } /* use long-term credentials */ err = md5_printf(md5_hash, "%s:%s:%s", username_ltc, realm_ltc, password_ltc); if (err) goto out; err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, tid_ltc, NULL, md5_hash, sizeof(md5_hash), false, 0x00, 3, STUN_ATTR_USERNAME, username_ltc, STUN_ATTR_NONCE, nonce_ltc, STUN_ATTR_REALM, realm_ltc); if (err) goto out; r = memcmp(mb->buf, reqltc, mb->end); if ((sizeof(reqltc)-1) != mb->end || 0 != r) { err = EBADMSG; DEBUG_WARNING("compare failed (r=%d)\n", r); (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); (void)re_printf("ref: [%02w]\n", reqltc, sizeof(reqltc)-1); goto out; } /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_REQUEST != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto bad; err = stun_msg_chk_mi(msg, md5_hash, sizeof(md5_hash)); if (err) goto out; if (EPROTO != stun_msg_chk_fingerprint(msg)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); if (!attr || strcmp(username_ltc, attr->v.username)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!attr || strcmp(nonce_ltc, attr->v.nonce)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_REALM); if (!attr || strcmp(realm_ltc, attr->v.realm)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }
static int test_resp(const struct pl *resp, const struct sa *addr) { struct stun_msg *msg = NULL; struct stun_attr *attr; struct mbuf *mb = NULL; int err; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_SUCCESS_RESP, tid, NULL, (uint8_t *)password.p, password.l, true, 0x20, 2, STUN_ATTR_SOFTWARE, server_sw, STUN_ATTR_XOR_MAPPED_ADDR, addr); if (err) goto out; if (resp->l != mb->end || 0 != memcmp(mb->buf, resp->p, mb->end)) { err = EBADMSG; DEBUG_WARNING("compare failed (%J)\n", addr); (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); (void)re_printf("ref: [%02w]\n", resp->p, resp->l); goto out; } /* Decode STUN message */ mb->pos = 0; err = stun_msg_decode(&msg, mb, NULL); if (err) goto out; if (STUN_CLASS_SUCCESS_RESP != stun_msg_class(msg)) goto bad; if (STUN_METHOD_BINDING != stun_msg_method(msg)) goto bad; err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); if (err) goto out; err = stun_msg_chk_fingerprint(msg); if (err) goto out; attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr || !sa_cmp(&attr->v.xor_mapped_addr, addr, SA_ALL)) goto bad; attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); if (!attr || strcmp(server_sw, attr->v.software)) goto bad; goto out; bad: err = EBADMSG; out: mem_deref(msg); mem_deref(mb); return err; }
int trice_stund_recv(struct trice *icem, struct ice_lcand *lcand, void *sock, const struct sa *src, struct stun_msg *req, size_t presz) { struct stun_attr *attr; struct pl lu, ru; bool remote_controlling; uint64_t tiebrk = 0; uint32_t prio_prflx; bool use_cand = false; int err; /* RFC 5389: Fingerprint errors are silently discarded */ err = stun_msg_chk_fingerprint(req); if (err) return err; err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd)); if (err) { DEBUG_WARNING("message-integrity failed (src=%J)\n", src); if (err == EBADMSG) goto unauth; else goto badmsg; } attr = stun_msg_attr(req, STUN_ATTR_USERNAME); if (!attr) goto badmsg; err = re_regex(attr->v.username, strlen(attr->v.username), "[^:]+:[^]+", &lu, &ru); if (err) { DEBUG_WARNING("could not parse USERNAME attribute (%s)\n", attr->v.username); goto unauth; } if (pl_strcmp(&lu, icem->lufrag)) { DEBUG_WARNING("local ufrag err (expected %s, actual %r)\n", icem->lufrag, &lu); goto unauth; } if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) { DEBUG_WARNING("remote ufrag err (expected %s, actual %r)\n", icem->rufrag, &ru); goto unauth; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED); if (attr) { remote_controlling = false; tiebrk = attr->v.uint64; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING); if (attr) { remote_controlling = true; tiebrk = attr->v.uint64; } if (remote_controlling == icem->controlling) { if (icem->tiebrk >= tiebrk) trice_switch_local_role(icem); else goto conflict; } attr = stun_msg_attr(req, STUN_ATTR_PRIORITY); if (attr) prio_prflx = attr->v.uint32; else goto badmsg; attr = stun_msg_attr(req, STUN_ATTR_USE_CAND); if (attr) use_cand = true; err = handle_stun_full(icem, lcand, sock, src, prio_prflx, use_cand); if (err) goto badmsg; trice_tracef(icem, 32, "[%u] STUNSRV: Tx success respons [%H ---> %J]\n", lcand->attr.compid, trice_cand_print, lcand, src); return stun_reply(lcand->attr.proto, sock, src, presz, req, (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, icem->sw ? icem->sw : sw); badmsg: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 400, "Bad Request"); unauth: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 401, "Unauthorized"); conflict: return stunsrv_ereply(icem, lcand, sock, src, presz, req, 487, "Role Conflict"); }
static bool request_handler(struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct sa *dst, const struct stun_msg *msg) { struct stun_attr *mi, *user, *realm, *nonce; const uint32_t now = (uint32_t)time(NULL); char nstr[NONCE_SIZE + 1]; int err; (void)dst; if (ctx->key) return false; mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY); user = stun_msg_attr(msg, STUN_ATTR_USERNAME); realm = stun_msg_attr(msg, STUN_ATTR_REALM); nonce = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!mi) { err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!user || !realm || !nonce) { err = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Request", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (!nonce_validate(nonce->v.nonce, now, src)) { err = stun_ereply(proto, sock, src, 0, msg, 438, "Stale Nonce", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->key = mem_alloc(MD5_SIZE, NULL); if (!ctx->key) { restund_warning("auth: can't to allocate memory for MI key\n"); err = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", NULL, 0, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto unauth; } ctx->keylen = MD5_SIZE; if (restund_get_ha1(user->v.username, ctx->key)) { restund_info("auth: unknown user '%s'\n", user->v.username); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } if (stun_msg_chk_mi(msg, ctx->key, ctx->keylen)) { restund_info("auth: bad passwd for '%s'\n", user->v.username); err = stun_ereply(proto, sock, src, 0, msg, 401, "Unauthorized", NULL, 0, ctx->fp, 3, STUN_ATTR_REALM, restund_realm(), STUN_ATTR_NONCE, mknonce(nstr, now, src), STUN_ATTR_SOFTWARE, restund_software); goto unauth; } return false; unauth: if (err) { restund_warning("auth reply error: %s\n", strerror(err)); } return true; }