]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
http: digest - show the SHA hash as an authentication alternative (tested with curl)
authorJaroslav Kysela <perex@perex.cz>
Thu, 14 Feb 2019 13:36:46 +0000 (14:36 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 14 Feb 2019 13:36:53 +0000 (14:36 +0100)
src/config.c
src/http.c
src/http.h
src/tvheadend.h
src/utils.c

index 2293d278be4b980f7f8f3db8dbb8139ce0224529..72ddcf94cf296b5abf09b3d0f2bf301ae004347b 100644 (file)
@@ -1691,7 +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_MD5;
+  config.http_auth_algo = HTTP_AUTH_ALGO_SHA256;
   config.proxy = 0;
   config.realm = strdup("tvheadend");
   config.info_area = strdup("login,storage,time");
index 0b3c68529d8a90e7fee89cf444a38f81a233b647..443493152e17af7c40fe8de2b5d3da14cc4e4bef 100644 (file)
@@ -221,8 +221,7 @@ static const char *cachemonths[12] = {
 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;
@@ -230,8 +229,6 @@ 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);
 }
 
@@ -256,20 +253,21 @@ http_get_digest_hash(int algo, const char *msg)
   }
 }
 
-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)) {
@@ -281,18 +279,17 @@ http_get_nonce(http_connection_t *hc)
     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);
@@ -311,7 +308,7 @@ http_get_opaque(http_connection_t *hc, const char *realm)
   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);
 }
 
 /**
@@ -321,7 +318,22 @@ void
 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);
 }
 
 /**
@@ -387,21 +399,20 @@ http_send_header(http_connection_t *hc, int rc, const char *content,
     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",
@@ -984,6 +995,7 @@ struct http_verify_structure {
   char *d_ha1;
   char *d_all;
   char *d_response;
+  int algo;
   http_connection_t *hc;
 };
 
@@ -997,12 +1009,11 @@ 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 = 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;
@@ -1027,15 +1038,26 @@ http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v)
     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;
@@ -1045,7 +1067,7 @@ http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v)
       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;
@@ -1463,7 +1485,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(config.http_auth_algo, v)) {
+          if (v == NULL || !http_nonce_exists(v)) {
             free(v);
             http_error(hc, HTTP_STATUS_UNAUTHORIZED);
             return -1;
index 6f1f759f3fe6413142af2fc396d3a9648bae2a8e..43fb7103fb8daa713497ad3610beba217b71a44d 100644 (file)
@@ -174,7 +174,6 @@ 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 92e7bed0c852a39323a9fa1540a74155f127d6c8..1d40a458dd69af1265a1b564c1d3ba6836c2e086 100644 (file)
@@ -274,6 +274,7 @@ 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 );
+char *sha512sum256_base64 ( const char *str );
 
 int makedirs ( int subsys, const char *path, int mode, int mstrict, gid_t gid, uid_t uid );
 
index 1cdc30f3c35d712b329c9e1439ae883bb7c635ef..f6ce8340f9eda38c3188c127c5cf68d3f14ce854 100644 (file)
@@ -213,7 +213,7 @@ char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
 {
   static const char b64[] =
       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  char *ret, *dst;
+  char *dst;
   unsigned i_bits = 0;
   int i_shift = 0;
   int bytes_remaining = in_size;
@@ -221,7 +221,7 @@ char *base64_encode(char *out, int out_size, const uint8_t *in, int 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--;
@@ -231,11 +231,11 @@ char *base64_encode(char *out, int out_size, const uint8_t *in, int in_size)
       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;
 }
 
 /**
@@ -557,49 +557,73 @@ sbuf_read(sbuf_t *sb, int fd)
   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))