Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
http: implement digest authorisation per RFC2617
  • Loading branch information
perexg committed Apr 8, 2016
1 parent 95fd947 commit 64ecd9c
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 204 deletions.
185 changes: 14 additions & 171 deletions src/access.c
Expand Up @@ -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);

/**
*
Expand Down Expand Up @@ -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);
}

/*
*
*/
Expand Down Expand Up @@ -624,7 +566,6 @@ access_update(access_t *a, access_entry_t *ae)
}

/**
*
*/
static void
access_set_lang_ui(access_t *a)
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

/**
*
*/
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 3 additions & 11 deletions src/access.h
Expand Up @@ -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) :
Expand All @@ -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);

/**
*
Expand Down
14 changes: 14 additions & 0 deletions src/config.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions src/config.h
Expand Up @@ -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;
Expand Down
26 changes: 24 additions & 2 deletions src/htsp_server.c
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 64ecd9c

Please sign in to comment.