From: Jaroslav Kysela Date: Fri, 8 Apr 2016 15:22:41 +0000 (+0200) Subject: http: implement digest authorisation per RFC2617 X-Git-Tag: v4.2.1~701 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=64ecd9c54f7141a7c85499b4b676675e04b21239;p=thirdparty%2Ftvheadend.git http: implement digest authorisation per RFC2617 --- diff --git a/src/access.c b/src/access.c index 005de7b11..2f4d380da 100644 --- a/src/access.c +++ b/src/access.c @@ -49,14 +49,9 @@ const char *superuser_password; int access_noacl; -static int passwd_verify(const char *username, const char *passwd); -static int passwd_verify2(const char *username, const char *passwd, +static int passwd_verify(const char *username, verify_callback_t verify, void *aux); +static int passwd_verify2(const char *username, verify_callback_t verify, void *aux, const char *username2, const char *passwd2); -static int passwd_verify_digest(const char *username, const uint8_t *digest, - const uint8_t *challenge); -static int passwd_verify_digest2(const char *username, const uint8_t *digest, - const uint8_t *challenge, - const char *username2, const char *passwd2); /** * @@ -358,59 +353,6 @@ access_ip_blocked(struct sockaddr *src) return 0; } -/** - * - */ -int -access_verify(const char *username, const char *password, - struct sockaddr *src, uint32_t mask) -{ - uint32_t bits = 0; - access_entry_t *ae; - int match = 0, nouser = username == NULL || username[0] == '\0'; - - if (access_noacl) - return 0; - - if (access_ip_blocked(src)) - return -1; - - if (!passwd_verify2(username, password, - superuser_username, superuser_password)) - return 0; - - if (passwd_verify(username, password)) - username = NULL; - - TAILQ_FOREACH(ae, &access_entries, ae_link) { - - if(!ae->ae_enabled) - continue; - - if(ae->ae_username[0] != '*') { - /* acl entry requires username to match */ - if(username == NULL || strcmp(username, ae->ae_username)) - continue; /* Didn't get one */ - } - - if(!netmask_verify(&ae->ae_ipmasks, src)) - continue; /* IP based access mismatches */ - - if (ae->ae_username[0] != '*') - match = 1; - - bits |= ae->ae_rights; - } - - /* Username was not matched - no access */ - if (!match && !nouser) - bits = 0; - - return (mask & ACCESS_OR) ? - ((mask & bits) ? 0 : -1) : - ((mask & bits) == mask ? 0 : -1); -} - /* * */ @@ -624,7 +566,6 @@ access_update(access_t *a, access_entry_t *ae) } /** - * */ static void access_set_lang_ui(access_t *a) @@ -646,7 +587,7 @@ access_set_lang_ui(access_t *a) * */ access_t * -access_get(const char *username, const char *password, struct sockaddr *src) +access_get(struct sockaddr *src, const char *username, verify_callback_t verify, void *aux) { access_t *a = access_alloc(); access_entry_t *ae; @@ -655,16 +596,16 @@ access_get(const char *username, const char *password, struct sockaddr *src) if (!access_noacl && access_ip_blocked(src)) return a; - if (!passwd_verify(username, password)) { + if (!passwd_verify(username, verify, aux)) { a->aa_username = strdup(username); a->aa_representative = strdup(username); - if(!passwd_verify2(username, password, + if(!passwd_verify2(username, verify, aux, superuser_username, superuser_password)) return access_full(a); } else { a->aa_representative = malloc(50); tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50); - if(!passwd_verify2(username, password, + if(!passwd_verify2(username, verify, aux, superuser_username, superuser_password)) return access_full(a); username = NULL; @@ -708,72 +649,6 @@ access_get(const char *username, const char *password, struct sockaddr *src) return a; } -/** - * - */ -access_t * -access_get_hashed(const char *username, const uint8_t digest[20], - const uint8_t *challenge, struct sockaddr *src) -{ - access_t *a = access_alloc(); - access_entry_t *ae; - int nouser = username == NULL || username[0] == '\0'; - - if (!access_noacl && access_ip_blocked(src)) - return a; - - if (!passwd_verify_digest(username, digest, challenge)) { - a->aa_username = strdup(username); - a->aa_representative = strdup(username); - if(!passwd_verify_digest2(username, digest, challenge, - superuser_username, superuser_password)) - return access_full(a); - } else { - a->aa_representative = malloc(50); - tcp_get_str_from_ip((struct sockaddr*)src, a->aa_representative, 50); - if(!passwd_verify_digest2(username, digest, challenge, - superuser_username, superuser_password)) - return access_full(a); - username = NULL; - } - - if(access_noacl) - return access_full(a); - - TAILQ_FOREACH(ae, &access_entries, ae_link) { - - if(!ae->ae_enabled) - continue; - - if(!netmask_verify(&ae->ae_ipmasks, src)) - continue; /* IP based access mismatches */ - - if(ae->ae_username[0] != '*') { - - if (username == NULL || strcmp(username, ae->ae_username)) - continue; - - a->aa_match = 1; - } - - access_update(a, ae); - } - - /* Username was not matched - no access */ - if (!a->aa_match) { - free(a->aa_username); - a->aa_username = NULL; - if (!nouser) - a->aa_rights = 0; - } - - access_set_lang_ui(a); - - if (tvhtrace_enabled()) - access_dump_a(a); - return a; -} - /** * */ @@ -1716,62 +1591,30 @@ const idclass_t access_entry_class = { static int passwd_entry_class_password_set(void *o, const void *v); static int -passwd_verify_digest2(const char *username, const uint8_t *digest, - const uint8_t *challenge, - const char *username2, const char *passwd2) -{ - uint8_t d[20]; - - if (username == NULL || username[0] == '\0' || - username2 == NULL || username2[0] == '\0' || - passwd2 == NULL || passwd2[0] == '\0') - return -1; - - if (strcmp(username, username2)) - return -1; - - sha1_calc(d, (uint8_t *)passwd2, strlen(passwd2), challenge, 32); - - return memcmp(d, digest, 20) ? -1 : 0; -} - -static int -passwd_verify_digest(const char *username, const uint8_t *digest, - const uint8_t *challenge) -{ - passwd_entry_t *pw; - - TAILQ_FOREACH(pw, &passwd_entries, pw_link) - if (pw->pw_enabled && - !passwd_verify_digest2(username, digest, challenge, - pw->pw_username, pw->pw_password)) - return 0; - return -1; -} - -static int -passwd_verify2(const char *username, const char *passwd, - const char *username2, const char *passwd2) +passwd_verify2 + (const char *username, verify_callback_t verify, void *aux, + const char *username2, const char *passwd2) { if (username == NULL || username[0] == '\0' || username2 == NULL || username2[0] == '\0' || - passwd == NULL || passwd2 == NULL) + passwd2 == NULL) return -1; if (strcmp(username, username2)) return -1; - return strcmp(passwd, passwd2) ? -1 : 0; + return verify(aux, passwd2) ? 0 : -1; } static int -passwd_verify(const char *username, const char *passwd) +passwd_verify + (const char *username, verify_callback_t verify, void *aux) { passwd_entry_t *pw; TAILQ_FOREACH(pw, &passwd_entries, pw_link) if (pw->pw_enabled && - !passwd_verify2(username, passwd, + !passwd_verify2(username, verify, aux, pw->pw_username, pw->pw_password)) return 0; return -1; diff --git a/src/access.h b/src/access.h index a742232fc..4aabc2a4d 100644 --- a/src/access.h +++ b/src/access.h @@ -239,9 +239,6 @@ access_get_theme(access_t *a); * * Return 0 if access is granted, -1 otherwise */ -int access_verify(const char *username, const char *password, - struct sockaddr *src, uint32_t mask); - static inline int access_verify2(access_t *a, uint32_t mask) { return (mask & ACCESS_OR) ? ((a->aa_rights & mask) ? 0 : -1) : @@ -252,15 +249,10 @@ int access_verify_list(htsmsg_t *list, const char *item); /** * Get the access structure */ -access_t *access_get(const char *username, const char *password, - struct sockaddr *src); +typedef int (*verify_callback_t)(void *aux, const char *passwd); -/** - * - */ -access_t * -access_get_hashed(const char *username, const uint8_t digest[20], - const uint8_t *challenge, struct sockaddr *src); +access_t *access_get(struct sockaddr *src, const char *username, + verify_callback_t verify, void *aux); /** * diff --git a/src/config.c b/src/config.c index 2daab0115..6a5b5f7d4 100644 --- a/src/config.c +++ b/src/config.c @@ -1631,6 +1631,8 @@ config_boot ( const char *path, gid_t gid, uid_t uid ) memset(&config, 0, sizeof(config)); config.idnode.in_class = &config_class; config.ui_quicktips = 1; + config.digest = 1; + config.realm = strdup("tvheadend"); config.info_area = strdup("login,storage,time"); config.cookie_expires = 7; config.dscp = -1; @@ -1751,6 +1753,7 @@ void config_done ( void ) free(config.language); free(config.language_ui); free(config.theme_ui); + free(config.realm); free(config.info_area); free(config.muxconf_path); free(config.chicon_path); @@ -2059,6 +2062,17 @@ const idclass_t config_class = { .opts = PO_ADVANCED, .group = 1 }, + { + .type = PT_BOOL, + .id = "digest", + .name = N_("Use HTTP digest authentication"), + .desc = N_("Digest access authentication is intended as a security trade-off. " + "It is intended to replace unencrypted HTTP basic access authentication. " + "This option should be enabled for the standard usage."), + .off = offsetof(config_t, digest), + .opts = PO_ADVANCED, + .group = 1 + }, { .type = PT_U32, .intextra = INTEXTRA_RANGE(1, 0x7ff, 1), diff --git a/src/config.h b/src/config.h index e841e9867..c1c5afe21 100644 --- a/src/config.h +++ b/src/config.h @@ -33,6 +33,8 @@ typedef struct config { int uilevel; int uilevel_nochange; int ui_quicktips; + int digest; + char *realm; char *wizard; char *full_version; char *server_name; diff --git a/src/htsp_server.c b/src/htsp_server.c index a0e031368..ae74a8deb 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2906,12 +2906,32 @@ struct { * Message processing * *************************************************************************/ +/** + * + */ +struct htsp_verify_struct { + const uint8_t *digest; + const uint8_t *challenge; +}; + +static int +htsp_verify_callback(void *aux, const char *passwd) +{ + struct htsp_verify_struct *v = aux; + uint8_t d[20]; + + if (v->digest == NULL || v->challenge == NULL) return 0; + sha1_calc(d, (uint8_t *)passwd, strlen(passwd), v->challenge, 32); + return memcmp(d, v->digest, 20) == 0; +} + /** * Raise privs by field in message */ static int htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m) { + struct htsp_verify_struct vs; const char *username; const void *digest; size_t digestlen; @@ -2923,8 +2943,10 @@ htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m) if(!htsmsg_get_bin(m, "digest", &digest, &digestlen)) { - rights = access_get_hashed(username, digest, htsp->htsp_challenge, - (struct sockaddr *)htsp->htsp_peer); + vs.digest = digest; + vs.challenge = htsp->htsp_challenge; + rights = access_get((struct sockaddr *)htsp->htsp_peer, username, + htsp_verify_callback, &vs); if (rights->aa_rights == 0) { tvhlog(LOG_INFO, "htsp", "%s: Unauthorized access", htsp->htsp_logname); diff --git a/src/http.c b/src/http.c index 2dcc94437..295c5ea32 100644 --- a/src/http.c +++ b/src/http.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "tvheadend.h" #include "tcp.h" @@ -218,6 +219,77 @@ static const char *cachemonths[12] = { "Dec" }; +/** + * Digest authentication helpers + */ +typedef struct http_nonce { + RB_ENTRY(http_nonce) link; + mtimer_t expire; + char nonce[MD5_DIGEST_LENGTH*2+1]; +} http_nonce_t; + +static RB_HEAD(, http_nonce) http_nonces; + +static int +http_nonce_cmp(const void *a, const void *b) +{ + return strcmp(((http_nonce_t *)a)->nonce, ((http_nonce_t *)b)->nonce); +} + +static void +http_nonce_timeout(void *aux) +{ + struct http_nonce *n = aux; + RB_REMOVE(&http_nonces, n, link); + free(n); +} + +static char * +http_get_nonce(void) +{ + struct http_nonce *n = calloc(1, sizeof(*n)); + char stamp[16], *m; + int64_t mono = getmonoclock(); + mono ^= 0xa1687211885fcd30; + snprintf(stamp, sizeof(stamp), "%"PRId64, mono); + m = md5sum(stamp, 1); + strcpy(n->nonce, m); + pthread_mutex_lock(&global_lock); + RB_INSERT_SORTED(&http_nonces, n, link, http_nonce_cmp); + mtimer_arm_rel(&n->expire, http_nonce_timeout, n, sec2mono(10)); + pthread_mutex_unlock(&global_lock); + return m; +} + +static char * +http_find_nonce(const char *nonce) +{ + struct http_nonce *n, tmp; + + if (nonce == NULL) + return NULL; + strcpy(tmp.nonce, nonce); + pthread_mutex_lock(&global_lock); + n = RB_FIND(&http_nonces, &tmp, link, http_nonce_cmp); + pthread_mutex_unlock(&global_lock); + if (n) { + pthread_mutex_lock(&global_lock); + mtimer_arm_rel(&n->expire, http_nonce_timeout, n, sec2mono(2*60)); + pthread_mutex_unlock(&global_lock); + return n->nonce; + } + return NULL; +} + +static char * +http_get_opaque(const char *realm, const char *nonce) +{ + char *a = alloca(strlen(realm) + strlen(nonce) + 1); + strcpy(a, realm); + strcat(a, nonce); + return md5sum(a, 1); +} + /** * Transmit a HTTP reply */ @@ -276,8 +348,28 @@ http_send_header(http_connection_t *hc, int rc, const char *content, htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage); } - if(rc == HTTP_STATUS_UNAUTHORIZED) - htsbuf_append_str(&hdrs, "WWW-Authenticate: Basic realm=\"tvheadend\"\r\n"); + if(rc == HTTP_STATUS_UNAUTHORIZED) { + const char *realm = config.realm; + if (realm == NULL || realm[0] == '\0') + realm = "tvheadend"; + if (config.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, auth-int\", nonce=\""); + htsbuf_append_str(&hdrs, hc->hc_nonce); + htsbuf_append_str(&hdrs, "\", opaque=\""); + htsbuf_append_str(&hdrs, opaque); + htsbuf_append_str(&hdrs, "\"\r\n"); + free(opaque); + } else { + htsbuf_append_str(&hdrs, "WWW-Authenticate: Basic realm=\""); + htsbuf_append_str(&hdrs, realm); + htsbuf_append_str(&hdrs, "\"\r\n"); + } + } if (hc->hc_logout_cookie == 1) { htsbuf_append_str(&hdrs, "Set-Cookie: logout=1; Path=\"/logout\"\r\n"); } else if (hc->hc_logout_cookie == 2) { @@ -366,6 +458,41 @@ http_encoding_valid(http_connection_t *hc, const char *encoding) return 0; } +/** + * + */ +static char * +http_get_header_value(const char *hdr, const char *name) +{ + char *tokbuf, *tok, *saveptr = NULL, *q, *s; + + tokbuf = tvh_strdupa(hdr); + tok = strtok_r(tokbuf, ",", &saveptr); + while (tok) { + while (*tok == ' ') + tok++; + if ((q = strchr(tok, '=')) == NULL) + goto next; + *q = '\0'; + if (strcasecmp(tok, name)) + goto next; + s = q + 1; + if (*s == '"') { + q = strchr(++s, '"'); + if (q) + *q = '\0'; + } else { + q = strchr(s, ' '); + if (q) + *q = '\0'; + } + return strdup(s); +next: + tok = strtok_r(NULL, ",", &saveptr); + } + return NULL; +} + /** * Transmit a HTTP reply */ @@ -548,12 +675,128 @@ http_access_verify_ticket(http_connection_t *hc) hc->hc_peer_ipstr, ticket_id, hc->hc_url); } +/** + * + */ +struct http_verify_structure { + char *password; + char *d_ha1; + char *d_all; + char *d_response; +}; + +static int +http_verify_callback(void *aux, const char *passwd) +{ + struct http_verify_structure *v = aux; + char ha1[512]; + char all[1024]; + char *m; + int res; + + if (v->d_ha1) { + snprintf(ha1, sizeof(ha1), "%s:%s", v->d_ha1, passwd); + m = md5sum(ha1, 1); + snprintf(all, sizeof(all), "%s:%s", m, v->d_all); + free(m); + m = md5sum(all, 1); + res = strcmp(m, v->d_response) == 0; + free(m); + return res; + } + if (v->password == NULL) return 0; + return strcmp(v->password, passwd) == 0; +} + +/** + * + */ +static int +http_verify_prepare(http_connection_t *hc, struct http_verify_structure *v) +{ + memset(v, 0, sizeof(*v)); + if (hc->hc_authhdr) { + + if (hc->hc_nonce == NULL) + return -1; + + const char *method = http_cmd2str(hc->hc_cmd); + 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 *realm = NULL, *nonce_count = NULL, *cnonce = NULL, *m = NULL; + char all[1024]; + int res = -1; + + if (qop == NULL || uri == NULL) + goto end; + + if (strcasecmp(qop, "auth-int") == 0) { + m = md5sum(hc->hc_post_data ?: "", 1); + snprintf(all, sizeof(all), "%s:%s:%s", method, uri, m); + free(m); + m = NULL; + } else if (strcasecmp(qop, "auth") == 0) { + snprintf(all, sizeof(all), "%s:%s", method, uri); + } else { + goto end; + } + + m = md5sum(all, 1); + if (qop == NULL || *qop == '\0') { + snprintf(all, sizeof(all), "%s:%s", hc->hc_nonce, m); + goto set; + } else { + realm = http_get_header_value(hc->hc_authhdr, "realm"); + nonce_count = http_get_header_value(hc->hc_authhdr, "nc"); + cnonce = http_get_header_value(hc->hc_authhdr, "cnonce"); + if (realm == NULL || nonce_count == NULL || cnonce == NULL) { + goto end; + } else { + snprintf(all, sizeof(all), "%s:%s:%s:%s:%s", + hc->hc_nonce, nonce_count, cnonce, qop, m); +set: + v->d_all = strdup(all); + snprintf(all, sizeof(all), "%s:%s", hc->hc_username, realm); + v->d_ha1 = strdup(all); + v->d_response = response; + response = NULL; + } + } + res = 0; +end: + free(response); + free(qop); + free(uri); + free(realm); + free(nonce_count); + free(cnonce); + free(m); + return res; + } else { + v->password = hc->hc_password; + } + return 0; +} + +/* + * + */ +static void +http_verify_free(struct http_verify_structure *v) +{ + free(v->d_ha1); + free(v->d_all); + free(v->d_response); +} + /** * Return non-zero if no access */ int http_access_verify(http_connection_t *hc, int mask) { + struct http_verify_structure v; int res = -1; http_access_verify_ticket(hc); @@ -562,8 +805,11 @@ http_access_verify(http_connection_t *hc, int mask) if (res) { access_destroy(hc->hc_access); - hc->hc_access = access_get(hc->hc_username, hc->hc_password, - (struct sockaddr *)hc->hc_peer); + if (http_verify_prepare(hc, &v)) + return -1; + hc->hc_access = access_get((struct sockaddr *)hc->hc_peer, hc->hc_username, + http_verify_callback, &v); + http_verify_free(&v); if (hc->hc_access) res = access_verify2(hc->hc_access, mask); } @@ -578,6 +824,7 @@ int http_access_verify_channel(http_connection_t *hc, int mask, struct channel *ch) { + struct http_verify_structure v; int res = -1; assert(ch); @@ -592,8 +839,11 @@ http_access_verify_channel(http_connection_t *hc, int mask, if (res) { access_destroy(hc->hc_access); - hc->hc_access = access_get(hc->hc_username, hc->hc_password, - (struct sockaddr *)hc->hc_peer); + if (http_verify_prepare(hc, &v)) + return -1; + hc->hc_access = access_get((struct sockaddr *)hc->hc_peer, hc->hc_username, + http_verify_callback, &v); + http_verify_free(&v); if (hc->hc_access) { res = access_verify2(hc->hc_access, mask); @@ -799,6 +1049,7 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill) hc->hc_representative = hc->hc_peer_ipstr; hc->hc_username = NULL; hc->hc_password = NULL; + hc->hc_authhdr = NULL; hc->hc_session = NULL; /* Set keep-alive status */ @@ -837,14 +1088,37 @@ process_request(http_connection_t *hc, htsbuf_queue_t *spill) /* Extract authorization */ if((v = http_arg_get(&hc->hc_args, "Authorization")) != NULL) { if((n = http_tokenize(v, argv, 2, -1)) == 2) { - n = base64_decode((uint8_t *)authbuf, argv[1], sizeof(authbuf) - 1); - if (n < 0) - n = 0; - authbuf[n] = 0; - if((n = http_tokenize(authbuf, argv, 2, ':')) == 2) { - hc->hc_username = tvh_strdupa(argv[0]); - hc->hc_password = tvh_strdupa(argv[1]); - // No way to actually track this + if (strcasecmp(argv[0], "basic") == 0) { + n = base64_decode((uint8_t *)authbuf, argv[1], sizeof(authbuf) - 1); + if (n < 0) + n = 0; + authbuf[n] = 0; + if((n = http_tokenize(authbuf, argv, 2, ':')) == 2) { + hc->hc_username = tvh_strdupa(argv[0]); + hc->hc_password = tvh_strdupa(argv[1]); + // No way to actually track this + } + } else if (strcasecmp(argv[0], "digest") == 0) { + v = http_get_header_value(argv[1], "nonce"); + if (v == NULL || http_find_nonce(v) == NULL) { + http_error(hc, HTTP_STATUS_UNAUTHORIZED); + free(v); + return -1; + } + if (hc->hc_nonce && strcmp(hc->hc_nonce, v) != 0) { + http_error(hc, HTTP_STATUS_UNAUTHORIZED); + free(v); + return -1; + } + free(hc->hc_nonce); + hc->hc_nonce = v; + v = http_get_header_value(argv[1], "username"); + hc->hc_authhdr = tvh_strdupa(argv[1]); + hc->hc_username = tvh_strdupa(v); + free(v); + } else { + http_error(hc, HTTP_STATUS_BAD_REQUEST); + return -1; } } } @@ -1159,7 +1433,7 @@ http_serve_requests(http_connection_t *hc) free(hc->hc_post_data); hc->hc_post_data = NULL; - + http_arg_flush(&hc->hc_args); http_arg_flush(&hc->hc_req_args); @@ -1175,6 +1449,10 @@ http_serve_requests(http_connection_t *hc) error: free(hdrline); free(cmdline); + + free(hc->hc_nonce); + hc->hc_nonce = NULL; + } @@ -1230,6 +1508,7 @@ http_server_init(const char *bindaddr) .stop = NULL, .cancel = http_cancel }; + RB_INIT(&http_nonces); http_server = tcp_server_create("http", "HTTP", bindaddr, tvheadend_webui_port, &ops, NULL); atomic_set(&http_server_running, 1); } @@ -1244,6 +1523,7 @@ void http_server_done(void) { http_path_t *hp; + http_nonce_t *n; pthread_mutex_lock(&global_lock); atomic_set(&http_server_running, 0); @@ -1257,5 +1537,10 @@ http_server_done(void) free(hp); } pthread_mutex_unlock(&http_paths_mutex); + while ((n = RB_FIRST(&http_nonces)) != NULL) { + mtimer_disarm(&n->expire); + RB_REMOVE(&http_nonces, n, link); + free(n); + } pthread_mutex_unlock(&global_lock); } diff --git a/src/http.h b/src/http.h index aba1343cd..2a8d9b78c 100644 --- a/src/http.h +++ b/src/http.h @@ -149,6 +149,8 @@ typedef struct http_connection { char *hc_username; char *hc_password; + char *hc_authhdr; + char *hc_nonce; access_t *hc_access; struct config_head *hc_user_config; diff --git a/src/tvheadend.h b/src/tvheadend.h index 1f3747e98..62cd39862 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -803,7 +803,7 @@ uint32_t sbuf_peek_u32be(sbuf_t *sb, int off); static inline int32_t sbuf_peek_s32be(sbuf_t *sb, int off) { return sbuf_peek_u32be(sb, off); } static inline uint8_t *sbuf_peek(sbuf_t *sb, int off) { return sb->sb_data + off; } -char *md5sum ( const char *str ); +char *md5sum ( const char *str, int lowercase ); int makedirs ( const char *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 3e558bdf2..c4d8c24ad 100644 --- a/src/utils.c +++ b/src/utils.c @@ -468,15 +468,14 @@ sbuf_read(sbuf_t *sb, int fd) } char * -md5sum ( const char *str ) +md5sum ( const char *str, int lowercase ) { int i; static unsigned char md5[MD5_DIGEST_LENGTH]; char *ret = malloc((MD5_DIGEST_LENGTH * 2) + 1); MD5((const unsigned char*)str, strlen(str), md5); - for ( i = 0; i < MD5_DIGEST_LENGTH; i++ ) { - sprintf(&ret[i*2], "%02X", md5[i]); - } + for (i = 0; i < MD5_DIGEST_LENGTH; i++) + sprintf(&ret[i*2], lowercase ? "%02x" : "%02X", md5[i]); ret[MD5_DIGEST_LENGTH*2] = '\0'; return ret; }