]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
http server: add support for SHA-256 and SHA-512/256 digest hashes
authorJaroslav Kysela <perex@perex.cz>
Wed, 13 Feb 2019 17:14:40 +0000 (18:14 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 13 Feb 2019 17:14:40 +0000 (18:14 +0100)
src/config.c
src/config.h
src/http.c
src/http.h
src/tvheadend.h
src/utils.c

index 8e9f7f016f9de4db8750f07fb102b11b622dc0a0..351a1aa47cf401f2cd587c5e4f802cafec23b447 100644 (file)
@@ -1691,6 +1691,7 @@ config_boot
   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");
@@ -2042,6 +2043,17 @@ config_class_http_auth_list ( void *o, const char *lang )
   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)
@@ -2392,6 +2404,16 @@ const idclass_t config_class = {
       .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),
index 07e05b22a9a21f3c5c5af981b83a61e65b8384b3..c81c20f75f470550146236cb58e59ffeca49a6cd 100644 (file)
@@ -35,6 +35,7 @@ typedef struct config {
   int uilevel_nochange;
   int ui_quicktips;
   int http_auth;
+  int http_auth_algo;
   int proxy;
   char *realm;
   char *wizard;
index c3355110e13bea9780a5980fb51c344e08a5348e..8daa840177207864bdb66d874921b689fe299b58 100644 (file)
@@ -221,7 +221,8 @@ static const char *cachemonths[12] = {
 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;
@@ -229,6 +230,8 @@ 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);
 }
 
@@ -241,9 +244,23 @@ http_nonce_timeout(void *aux)
 }
 
 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;
 
@@ -251,28 +268,31 @@ http_get_nonce(void)
     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);
@@ -286,12 +306,12 @@ http_nonce_exists(const char *nonce)
 }
 
 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);
 }
 
 /**
@@ -301,7 +321,7 @@ void
 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 */
 }
 
 /**
@@ -368,20 +388,18 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
     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);
     }
   }
 
@@ -979,11 +997,12 @@ http_verify_callback(void *aux, const char *passwd)
    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;
@@ -1016,7 +1035,7 @@ http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v)
       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;
@@ -1026,7 +1045,7 @@ http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v)
       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;
@@ -1444,7 +1463,7 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill)
         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;
index 6c6bb6cd8c100585289104bfc5af61c80487e151..6f1f759f3fe6413142af2fc396d3a9648bae2a8e 100644 (file)
@@ -92,6 +92,10 @@ typedef struct http_arg {
 #define HTTP_AUTH_DIGEST            1
 #define HTTP_AUTH_PLAIN_DIGEST      2
 
+#define HTTP_AUTH_ALGO_MD5          0
+#define HTTP_AUTH_ALGO_SHA256       1
+#define HTTP_AUTH_ALGO_SHA512_256   2
+
 typedef enum http_state {
   HTTP_CON_WAIT_REQUEST,
   HTTP_CON_READ_HEADER,
@@ -170,6 +174,7 @@ typedef struct http_connection {
   char *hc_password;
   char *hc_authhdr;
   char *hc_nonce;
+  int   hc_nonce_algo;
   access_t *hc_access;
   enum {
     HC_AUTH_NONE,
index 181017a6cb77fa419a6767941266faf61308e2c7..92e7bed0c852a39323a9fa1540a74155f127d6c8 100644 (file)
@@ -272,6 +272,8 @@ static inline int64_t ts_rescale_inv(int64_t ts, int tb)
 }
 
 char *md5sum ( const char *str, int lowercase );
+char *sha256sum ( const char *str, int lowercase );
+char *sha512sum256 ( const char *str, int lowercase );
 
 int makedirs ( int subsys, const char *path, int mode, int mstrict, gid_t gid, uid_t uid );
 
index 5d2fb8deeb0af9411c79ed3381d85be1c75a0993..8fc5da8b73451c75534dfe1314626cf976c519a8 100644 (file)
@@ -20,7 +20,7 @@
 #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>
@@ -557,18 +557,49 @@ sbuf_read(sbuf_t *sb, int fd)
   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))