static bool nonce_validate(char *nonce, uint32_t now, const struct sa *src) { struct pl pl; uint32_t v; if (strlen(nonce) != NONCE_SIZE) { restund_info("auth: bad nonce length (%u)\n", strlen(nonce)); return false; } pl.p = nonce; pl.l = 8; v = pl_x32(&pl) ^ auth.rand_time; if (v + auth.nonce_expiry < now) { restund_debug("auth: nonce expired\n"); return false; } pl.p += 8; v = pl_x32(&pl) ^ auth.rand_addr; if (v != sa_hash(src, SA_ADDR)) { restund_info("auth: bad nonce src address (%j)\n", src); return false; } return true; }
static void reload_handler(struct mbuf *mb) { bool dbg = force_debug; struct conf *lconf; struct pl opt; int err; (void)mb; err = conf_alloc(&lconf, configfile); if (err) { restund_error("error loading configuration: %s: %m\n", configfile, err); return; } conf = mem_deref(conf); conf = lconf; if (!conf_get(conf, "debug", &opt) && !pl_strcasecmp(&opt, "yes")) dbg = true; restund_log_enable_debug(dbg); restund_info("configuration reloaded from %s (debug%s)\n", configfile, dbg ? " enabled" : " disabled"); }
static bool nonce_validate(char *nonce, time_t now, const struct sa *src) { uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE]; uint64_t nv[3]; struct pl pl; int64_t age; unsigned i; pl.p = nonce; pl.l = str_len(nonce); if (pl.l < NONCE_MIN_SIZE || pl.l > NONCE_MAX_SIZE) { restund_info("auth: bad nonce length (%zu)\n", pl.l); return false; } for (i=0; i<sizeof(nkey); i++) { nkey[i] = ch_hex(*pl.p++) << 4; nkey[i] += ch_hex(*pl.p++); pl.l -= 2; } nv[0] = pl_x64(&pl); nv[1] = auth.secret; nv[2] = sa_hash(src, SA_ADDR); md5((uint8_t *)nv, sizeof(nv), ckey); if (memcmp(nkey, ckey, MD5_SIZE)) { restund_debug("auth: invalid nonce (%j)\n", src); return false; } age = now - nv[0]; if (age < 0 || age > auth.nonce_expiry) { restund_debug("auth: nonce expired, age: %lli secs\n", age); return false; } return true; }
void chanbind_request(struct allocation *al, struct restund_msgctx *ctx, int proto, void *sock, const struct sa *src, const struct stun_msg *msg) { struct chan *chan = NULL, *ch_numb = NULL, *ch_peer; struct perm *perm = NULL, *permx = NULL; struct stun_attr *chnr, *peer; int err = ENOMEM, rerr; chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); if (!chnr || !chan_numb_valid(chnr->v.channel_number) || !peer) { restund_info("turn: bad chanbind attributes\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Bad Attributes", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (sa_af(&peer->v.xor_peer_addr) != sa_af(&al->rel_addr)) { restund_info("turn: chanbind peer address family mismatch\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 443, "Peer Address Family Mismatch", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } ch_numb = chan_numb_find(al->chans, chnr->v.channel_number); ch_peer = chan_peer_find(al->chans, &peer->v.xor_peer_addr); if (ch_numb != ch_peer) { restund_info("turn: channel %p/peer %p already bound\n", ch_numb, ch_peer); rerr = stun_ereply(proto, sock, src, 0, msg, 400, "Channel/Peer Already Bound", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); goto out; } if (!ch_numb) { chan = chan_create(al->chans, chnr->v.channel_number, &peer->v.xor_peer_addr, al); if (!chan) { restund_info("turn: unable to create channel\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } permx = perm_find(al->perms, &peer->v.xor_peer_addr); if (!permx) { perm = perm_create(al->perms, &peer->v.xor_peer_addr, al); if (!perm) { restund_info("turn: unable to create permission\n"); rerr = stun_ereply(proto, sock, src, 0, msg, 500, "Server Error", ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE,restund_software); goto out; } } err = rerr = stun_reply(proto, sock, src, 0, msg, ctx->key, ctx->keylen, ctx->fp, 1, STUN_ATTR_SOFTWARE, restund_software); out: if (rerr) restund_warning("turn: chanbind reply: %m\n", rerr); if (err) { mem_deref(chan); mem_deref(perm); } else { chan_refresh(ch_numb); perm_refresh(permx); } }
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; }
static void signal_handler(int sig) { restund_info("caught signal %d\n", sig); re_cancel(); }
int main(int argc, char *argv[]) { bool daemon = true; int err = 0; struct pl opt; (void)sys_coredump_set(true); #ifdef HAVE_GETOPT for (;;) { const int c = getopt(argc, argv, "dhnf:"); if (0 > c) break; switch (c) { case 'd': force_debug = true; restund_log_enable_debug(true); break; case 'f': configfile = optarg; break; case 'n': daemon = false; break; case '?': err = EINVAL; /*@fallthrough@*/ case 'h': usage(); return err; } } #else (void)argc; (void)argv; #endif restund_cmd_subscribe(&cmd_reload); err = fd_setsize(4096); if (err) { restund_warning("fd_setsize error: %m\n", err); goto out; } err = libre_init(); if (err) { restund_error("re init failed: %m\n", err); goto out; } /* configuration file */ err = conf_alloc(&conf, configfile); if (err) { restund_error("error loading configuration: %s: %m\n", configfile, err); goto out; } /* debug config */ if (!conf_get(conf, "debug", &opt) && !pl_strcasecmp(&opt, "yes")) restund_log_enable_debug(true); /* udp */ err = restund_udp_init(); if (err) goto out; /* tcp */ err = restund_tcp_init(); if (err) goto out; /* daemon config */ if (!conf_get(conf, "daemon", &opt) && !pl_strcasecmp(&opt, "no")) daemon = false; /* module config */ if (conf_get(conf, "module_path", &opt)) pl_set_str(&opt, "."); err = conf_apply(conf, "module", module_handler, &opt); if (err) goto out; /* daemon */ if (daemon) { err = sys_daemon(); if (err) { restund_error("daemon error: %m\n", err); goto out; } restund_log_enable_stderr(false); } /* database */ err = restund_db_init(); if (err) { restund_warning("database error: %m\n", err); goto out; } restund_info("stun server ready\n"); /* main loop */ err = re_main(signal_handler); out: restund_db_close(); mod_close(); restund_udp_close(); restund_tcp_close(); conf = mem_deref(conf); libre_close(); restund_cmd_unsubscribe(&cmd_reload); /* check for memory leaks */ tmr_debug(); mem_debug(); return err; }
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; }