typedef struct http_nonce {
RB_ENTRY(http_nonce) link;
mtimer_t expire;
- int algo;
- char nonce[64*2+1];
+ char nonce[64];
} http_nonce_t;
static RB_HEAD(, http_nonce) http_nonces;
static int
http_nonce_cmp(const void *a, const void *b)
{
- int algo = ((http_nonce_t *)a)->algo - ((http_nonce_t *)b)->algo;
- if (algo != 0) return algo;
return strcmp(((http_nonce_t *)a)->nonce, ((http_nonce_t *)b)->nonce);
}
}
}
-static void
+static int
http_get_nonce(http_connection_t *hc)
{
struct http_nonce *n = calloc(1, sizeof(*n));
- const int algo = config.http_auth_algo;
char stamp[33], *m;
int64_t mono;
+ static int64_t xor;
while (1) {
mono = getmonoclock();
mono ^= 0xa1687211885fcd30LL;
- snprintf(stamp, sizeof(stamp), "%"PRId64, mono);
- m = http_get_digest_hash(algo, stamp);
- n->algo = algo;
+ xor ^= 0xf6e398624aa55013LL;
+ snprintf(stamp, sizeof(stamp), "A!*Fz32%"PRId64"%"PRId64, mono, xor);
+ m = sha512sum256_base64(stamp);
+ if (m == NULL) return -1;
strlcpy(n->nonce, m, sizeof(n->nonce));
tvh_mutex_lock(&global_lock);
if (RB_INSERT_SORTED(&http_nonces, n, link, http_nonce_cmp)) {
tvh_mutex_unlock(&global_lock);
break;
}
- hc->hc_nonce_algo = algo;
hc->hc_nonce = m;
+ return 0;
}
static int
-http_nonce_exists(int algo, const char *nonce)
+http_nonce_exists(const char *nonce)
{
struct http_nonce *n, tmp;
if (nonce == NULL)
return 0;
- tmp.algo = algo;
strlcpy(tmp.nonce, nonce, sizeof(tmp.nonce));
tvh_mutex_lock(&global_lock);
n = RB_FIND(&http_nonces, &tmp, link, http_nonce_cmp);
char *a = alloca(strlen(realm) + strlen(hc->hc_nonce) + 1);
strcpy(a, realm);
strcat(a, hc->hc_nonce);
- return http_get_digest_hash(hc->hc_nonce_algo, a);
+ return sha512sum256_base64(a);
}
/**
http_alive(http_connection_t *hc)
{
if (hc->hc_nonce)
- http_nonce_exists(hc->hc_nonce_algo, hc->hc_nonce); /* update timer */
+ http_nonce_exists(hc->hc_nonce); /* update timer */
+}
+
+/**
+ *
+ */
+static void
+http_auth_header
+ (htsbuf_queue_t *hdrs, const char *realm, const char *algo,
+ const char *nonce, const char *opaque)
+{
+ htsbuf_qprintf(hdrs, "WWW-Authenticate: Digest realm=\"%s\", qop=auth", realm);
+ if (algo)
+ htsbuf_qprintf(hdrs, ", algorithm=%s", algo);
+ htsbuf_qprintf(hdrs, ", nonce=\"%s\"", nonce);
+ htsbuf_qprintf(hdrs, ", opaque=\"%s\"\r\n", opaque);
}
/**
const char *realm = tvh_str_default(config.realm, "tvheadend");
if (config.http_auth == HTTP_AUTH_DIGEST ||
config.http_auth == HTTP_AUTH_PLAIN_DIGEST) {
- if (hc->hc_nonce == NULL)
- http_get_nonce(hc);
- char *opaque = http_get_opaque(hc, realm);
- htsbuf_qprintf(&hdrs, "WWW-Authenticate: Digest realm=\"%s\", qop=auth", realm);
+ char *opaque;
+ if (hc->hc_nonce == NULL && http_get_nonce(hc)) goto __noauth;
+ opaque = http_get_opaque(hc, realm);
if (config.http_auth_algo != HTTP_AUTH_ALGO_MD5)
- htsbuf_qprintf(&hdrs, ", algorithm=%s",
- config.http_auth_algo == HTTP_AUTH_ALGO_SHA256 ?
- "SHA-256" : "SHA-512-256");
- htsbuf_qprintf(&hdrs, ", nonce=\"%s\"", hc->hc_nonce);
- htsbuf_qprintf(&hdrs, ", opaque=\"%s\"\r\n", opaque);
+ http_auth_header(&hdrs, realm,
+ config.http_auth_algo == HTTP_AUTH_ALGO_SHA256 ?
+ "SHA-256" : "SHA-512-256", hc->hc_nonce, opaque);
+ http_auth_header(&hdrs, realm, NULL, hc->hc_nonce, opaque);
free(opaque);
} else {
htsbuf_qprintf(&hdrs, "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm);
}
}
+__noauth:
if (hc->hc_version != RTSP_VERSION_1_0)
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
char *d_ha1;
char *d_all;
char *d_response;
+ int algo;
http_connection_t *hc;
};
int res;
if (v->d_ha1) {
- const int algo = v->hc->hc_nonce_algo;
snprintf(ha1, sizeof(ha1), "%s:%s", v->d_ha1, passwd);
- m = http_get_digest_hash(algo, ha1);
+ m = http_get_digest_hash(v->algo, ha1);
snprintf(all, sizeof(all), "%s:%s", m, v->d_all);
free(m);
- m = http_get_digest_hash(algo, all);
+ m = http_get_digest_hash(v->algo, all);
res = strcmp(m, v->d_response) == 0;
free(m);
return res;
char *response = http_get_header_value(hc->hc_authhdr, "response");
char *qop = http_get_header_value(hc->hc_authhdr, "qop");
char *uri = http_get_header_value(hc->hc_authhdr, "uri");
+ char *algo1 = http_get_header_value(hc->hc_authhdr, "algorithm");
char *realm = NULL, *nonce_count = NULL, *cnonce = NULL, *m = NULL;
char all[1024];
int res = -1;
+ if (algo1 == NULL) {
+ v->algo = HTTP_AUTH_ALGO_MD5;
+ } else if (strcasecmp(algo1, "SHA-256") == 0) {
+ v->algo = HTTP_AUTH_ALGO_SHA256;
+ } else if (strcasecmp(algo1, "SHA-512-256") == 0) {
+ v->algo = HTTP_AUTH_ALGO_SHA512_256;
+ } else {
+ goto end;
+ }
+
if (qop == NULL || uri == NULL)
goto end;
if (strcasecmp(qop, "auth-int") == 0) {
- m = http_get_digest_hash(hc->hc_nonce_algo, hc->hc_post_data ?: "");
+ m = http_get_digest_hash(v->algo, hc->hc_post_data ?: "");
snprintf(all, sizeof(all), "%s:%s:%s", method, uri, m);
free(m);
m = NULL;
goto end;
}
- m = http_get_digest_hash(hc->hc_nonce_algo, all);
+ m = http_get_digest_hash(v->algo, all);
if (tvh_str_default(qop, NULL) == NULL) {
snprintf(all, sizeof(all), "%s:%s", hc->hc_nonce, m);
goto set;
if (config.http_auth == HTTP_AUTH_DIGEST ||
config.http_auth == HTTP_AUTH_PLAIN_DIGEST) {
v = http_get_header_value(argv[1], "nonce");
- if (v == NULL || !http_nonce_exists(config.http_auth_algo, v)) {
+ if (v == NULL || !http_nonce_exists(v)) {
free(v);
http_error(hc, HTTP_STATUS_UNAUTHORIZED);
return -1;
{
static const char b64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- char *ret, *dst;
+ char *dst;
unsigned i_bits = 0;
int i_shift = 0;
int bytes_remaining = in_size;
if (in_size >= UINT_MAX / 4 ||
out_size < BASE64_SIZE(in_size))
return NULL;
- ret = dst = out;
+ dst = out;
while (bytes_remaining) {
i_bits = (i_bits << 8) + *in++;
bytes_remaining--;
i_shift -= 6;
} while (i_shift > 6 || (bytes_remaining == 0 && i_shift > 0));
}
- while ((dst - ret) & 3)
+ while ((dst - out) & 3)
*dst++ = '=';
*dst = '\0';
- return ret;
+ return out;
}
/**
return n;
}
-static char *
-openssl_hash ( const char *str, int lowercase, const EVP_MD *md, int len )
+static uint8_t *
+openssl_hash ( uint8_t *hash, const uint8_t *msg, size_t msglen, const EVP_MD *md )
{
- uint8_t hash[len];
- char *ret = malloc((len * 2) + 1);
- const char *fmt = lowercase ? "%02x" : "%02X";
EVP_MD_CTX *mdctx;
- int i;
if ((mdctx = EVP_MD_CTX_create()) == NULL)
return NULL;
if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
goto __error;
- if (EVP_DigestUpdate(mdctx, str, strlen(str)) != 1)
+ if (EVP_DigestUpdate(mdctx, msg, msglen) != 1)
goto __error;
if (EVP_DigestFinal_ex(mdctx, hash, NULL) != 1)
goto __error;
- for (i = 0; i < len; i++)
- sprintf(ret + i*2, fmt, hash[i]);
- ret[len*2] = '\0';
EVP_MD_CTX_destroy(mdctx);
- return ret;
+ return hash;
__error:
EVP_MD_CTX_destroy(mdctx);
return NULL;
}
+static char *
+openssl_hash_hexstr ( const char *str, int lowercase, const EVP_MD *md, int len )
+{
+ int i;
+ uint8_t hash[len];
+ char *ret = malloc((len * 2) + 1);
+ const char *fmt = lowercase ? "%02x" : "%02X";
+ if (ret == NULL) return NULL;
+ if (openssl_hash(hash, (const uint8_t *)str, strlen(str), md) == NULL) {
+ free(ret);
+ return NULL;
+ }
+ for (i = 0; i < len; i++)
+ sprintf(ret + i*2, fmt, hash[i]);
+ ret[len*2] = '\0';
+ return ret;
+}
+
char *
md5sum ( const char *str, int lowercase )
{
- return openssl_hash(str, lowercase, EVP_md5(), 16);
+ return openssl_hash_hexstr(str, lowercase, EVP_md5(), 16);
}
char *
sha256sum ( const char *str, int lowercase )
{
- return openssl_hash(str, lowercase, EVP_sha256(), 32);
+ return openssl_hash_hexstr(str, lowercase, EVP_sha256(), 32);
}
char *
sha512sum256 ( const char *str, int lowercase )
{
- return openssl_hash(str, lowercase, EVP_sha512_256(), 32);
+ return openssl_hash_hexstr(str, lowercase, EVP_sha512_256(), 32);
+}
+
+char *
+sha512sum256_base64 ( const char *str )
+{
+ uint8_t hash[32];
+ char *out = malloc(64);
+ if (out == NULL) return NULL;
+ if (openssl_hash(hash, (const uint8_t *)str, strlen(str), EVP_sha512_256()) == NULL) {
+ free(out);
+ return NULL;
+ }
+ return base64_encode(out, 64, hash, 32);
}
#define FILE_MODE_BITS(x) (x&(S_IRWXU|S_IRWXG|S_IRWXO))