From: Jaroslav Kysela Date: Wed, 13 Feb 2019 17:14:40 +0000 (+0100) Subject: http server: add support for SHA-256 and SHA-512/256 digest hashes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e61acb8ad4a3411f4e7acfd8133d222299f6d47e;p=thirdparty%2Ftvheadend.git http server: add support for SHA-256 and SHA-512/256 digest hashes --- diff --git a/src/config.c b/src/config.c index 8e9f7f016..351a1aa47 100644 --- a/src/config.c +++ b/src/config.c @@ -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), diff --git a/src/config.h b/src/config.h index 07e05b22a..c81c20f75 100644 --- a/src/config.h +++ b/src/config.h @@ -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; diff --git a/src/http.c b/src/http.c index c3355110e..8daa84017 100644 --- a/src/http.c +++ b/src/http.c @@ -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; diff --git a/src/http.h b/src/http.h index 6c6bb6cd8..6f1f759f3 100644 --- a/src/http.h +++ b/src/http.h @@ -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, diff --git a/src/tvheadend.h b/src/tvheadend.h index 181017a6c..92e7bed0c 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -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 ); diff --git a/src/utils.c b/src/utils.c index 5d2fb8dee..8fc5da8b7 100644 --- a/src/utils.c +++ b/src/utils.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -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))