config.idnode.in_class = &config_class;
config.ui_quicktips = 1;
config.http_auth = HTTP_AUTH_DIGEST;
+ config.http_auth_algo = HTTP_AUTH_ALGO_SHA512_256;
config.proxy = 0;
config.realm = strdup("tvheadend");
config.info_area = strdup("login,storage,time");
return strtab2htsmsg(tab, 1, lang);
}
+static htsmsg_t *
+config_class_http_auth_algo_list ( void *o, const char *lang )
+{
+ static const struct strtab tab[] = {
+ { N_("MD5"), HTTP_AUTH_ALGO_MD5 },
+ { N_("SHA-256"), HTTP_AUTH_ALGO_SHA256 },
+ { N_("SHA-512/256"), HTTP_AUTH_ALGO_SHA512_256 },
+ };
+ return strtab2htsmsg(tab, 1, lang);
+}
+
#if ENABLE_MPEGTS_DVB
static void
config_muxconfpath_notify_cb(void *opaque, int disarmed)
.opts = PO_EXPERT,
.group = 5
},
+ {
+ .type = PT_INT,
+ .id = "digest_algo",
+ .name = N_("Digest hash type"),
+ .desc = N_("The hash algorithm type for the digest authentication."),
+ .list = config_class_http_auth_algo_list,
+ .off = offsetof(config_t, http_auth_algo),
+ .opts = PO_EXPERT,
+ .group = 5
+ },
{
.type = PT_U32,
.intextra = INTEXTRA_RANGE(1, 0x7ff, 1),
typedef struct http_nonce {
RB_ENTRY(http_nonce) link;
mtimer_t expire;
- char nonce[16*2+1];
+ int algo;
+ char nonce[64*2+1];
} 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 char *
-http_get_nonce(void)
+http_get_digest_hash(int algo, const char *msg)
+{
+ switch (algo) {
+ case HTTP_AUTH_ALGO_MD5:
+ return md5sum(msg, 1);
+ case HTTP_AUTH_ALGO_SHA256:
+ return sha256sum(msg, 1);
+ default:
+ return sha512sum256(msg, 1);
+ }
+}
+
+static void
+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;
mono = getmonoclock();
mono ^= 0xa1687211885fcd30LL;
snprintf(stamp, sizeof(stamp), "%"PRId64, mono);
- m = md5sum(stamp, 1);
- strlcpy(n->nonce, m, sizeof(stamp));
+ m = http_get_digest_hash(algo, stamp);
+ n->algo = algo;
+ 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);
free(m);
- continue; /* get unique md5 */
+ continue; /* get unique hash */
}
mtimer_arm_rel(&n->expire, http_nonce_timeout, n, sec2mono(30));
tvh_mutex_unlock(&global_lock);
break;
}
- return m;
+ hc->hc_nonce_algo = algo;
+ hc->hc_nonce = m;
}
static int
-http_nonce_exists(const char *nonce)
+http_nonce_exists(int algo, 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);
}
static char *
-http_get_opaque(const char *realm, const char *nonce)
+http_get_opaque(http_connection_t *hc, const char *realm)
{
- char *a = alloca(strlen(realm) + strlen(nonce) + 1);
+ char *a = alloca(strlen(realm) + strlen(hc->hc_nonce) + 1);
strcpy(a, realm);
- strcat(a, nonce);
- return md5sum(a, 1);
+ strcat(a, hc->hc_nonce);
+ return http_get_digest_hash(hc->hc_nonce_algo, a);
}
/**
http_alive(http_connection_t *hc)
{
if (hc->hc_nonce)
- http_nonce_exists(hc->hc_nonce); /* update timer */
+ http_nonce_exists(hc->hc_nonce_algo, hc->hc_nonce); /* update timer */
}
/**
if (config.http_auth == HTTP_AUTH_DIGEST ||
config.http_auth == HTTP_AUTH_PLAIN_DIGEST) {
if (hc->hc_nonce == NULL)
- hc->hc_nonce = http_get_nonce();
- char *opaque = http_get_opaque(realm, hc->hc_nonce);
- htsbuf_append_str(&hdrs, "WWW-Authenticate: Digest realm=\"");
- htsbuf_append_str(&hdrs, realm);
- htsbuf_append_str(&hdrs, "\", qop=\"auth\", nonce=\"");
- htsbuf_append_str(&hdrs, hc->hc_nonce);
- htsbuf_append_str(&hdrs, "\", opaque=\"");
- htsbuf_append_str(&hdrs, opaque);
- htsbuf_append_str(&hdrs, "\"\r\n");
+ http_get_nonce(hc);
+ char *opaque = http_get_opaque(hc, realm);
+ htsbuf_qprintf(&hdrs, "WWW-Authenticate: Digest realm=\"%s\"", 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, ", qop=\"auth\", nonce=\"%s\"", hc->hc_nonce);
+ htsbuf_qprintf(&hdrs, ", opaque=\"%s\"\r\n", opaque);
free(opaque);
} else {
- htsbuf_append_str(&hdrs, "WWW-Authenticate: Basic realm=\"");
- htsbuf_append_str(&hdrs, realm);
- htsbuf_append_str(&hdrs, "\"\r\n");
+ htsbuf_qprintf(&hdrs, "WWW-Authenticate: Basic realm=\"%s\"\r\n", realm);
}
}
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 = md5sum(ha1, 1);
+ m = http_get_digest_hash(algo, ha1);
snprintf(all, sizeof(all), "%s:%s", m, v->d_all);
free(m);
- m = md5sum(all, 1);
+ m = http_get_digest_hash(algo, all);
res = strcmp(m, v->d_response) == 0;
free(m);
return res;
goto end;
if (strcasecmp(qop, "auth-int") == 0) {
- m = md5sum(hc->hc_post_data ?: "", 1);
+ m = http_get_digest_hash(hc->hc_nonce_algo, hc->hc_post_data ?: "");
snprintf(all, sizeof(all), "%s:%s:%s", method, uri, m);
free(m);
m = NULL;
goto end;
}
- m = md5sum(all, 1);
+ m = http_get_digest_hash(hc->hc_nonce_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(v)) {
+ if (v == NULL || !http_nonce_exists(config.http_auth_algo, v)) {
free(v);
http_error(hc, HTTP_STATUS_UNAUTHORIZED);
return -1;
#include <limits.h>
#include <string.h>
#include <assert.h>
-#include <openssl/md5.h>
+#include <openssl/evp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
return n;
}
-char *
-md5sum ( const char *str, int lowercase )
+static char *
+openssl_hash ( const char *str, int lowercase, const EVP_MD *md, int len )
{
- uint8_t md5[MD5_DIGEST_LENGTH];
- char *ret = malloc((MD5_DIGEST_LENGTH * 2) + 1);
+ uint8_t hash[len];
+ char *ret = malloc((len * 2) + 1);
+ const char *fmt = lowercase ? "%02x" : "%02X";
+ EVP_MD_CTX *mdctx;
int i;
- MD5((const unsigned char*)str, strlen(str), md5);
- for (i = 0; i < MD5_DIGEST_LENGTH; i++)
- sprintf(&ret[i*2], lowercase ? "%02x" : "%02X", md5[i]);
- ret[MD5_DIGEST_LENGTH*2] = '\0';
+ if ((mdctx = EVP_MD_CTX_create()) == NULL)
+ return NULL;
+ if (EVP_DigestInit_ex(mdctx, md, NULL) != 0)
+ goto __error;
+ if (EVP_DigestUpdate(mdctx, str, strlen(str)) != 0)
+ goto __error;
+ if (EVP_DigestFinal_ex(mdctx, hash, NULL))
+ 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;
+__error:
+ EVP_MD_CTX_destroy(mdctx);
+ return NULL;
+}
+
+char *
+md5sum ( const char *str, int lowercase )
+{
+ return openssl_hash(str, lowercase, EVP_md5(), 16);
+}
+
+char *
+sha256sum ( const char *str, int lowercase )
+{
+ return openssl_hash(str, lowercase, EVP_sha256(), 32);
+}
+
+char *
+sha512sum256 ( const char *str, int lowercase )
+{
+ return openssl_hash(str, lowercase, EVP_sha512_256(), 32);
}
#define FILE_MODE_BITS(x) (x&(S_IRWXU|S_IRWXG|S_IRWXO))