ACL: Many improvements
- added min-max range for channel numbers for the ACL entry - added channel tag match for the ACL entry - introduced channel_access() routine - moved the username channel tag to ae_rights - add advanced streaming checkbox to control service and mux streaming separately
This commit is contained in:
parent
da10a35ced
commit
3be76456d8
10 changed files with 392 additions and 95 deletions
|
@ -59,11 +59,16 @@ The columns have the following functions:
|
|||
Enables access to streaming function. The 'streaming' access is enough to
|
||||
make Showtime (over HTSP) work.
|
||||
|
||||
<dt>Advanced Streaming
|
||||
<dd>
|
||||
Enables access to advanced streaming function for HTTP - like direct
|
||||
service or whole MPEG-TS stream (mux)..
|
||||
|
||||
<dt>Video Recorder
|
||||
<dd>
|
||||
Enables access to all video recording functions. This also include administration of the auto recordings.
|
||||
|
||||
<dt>User Configs (VR)
|
||||
<dt>Username Configs (VR)
|
||||
<dd>
|
||||
Use a DVR configuration profile matched to the authorized user by name
|
||||
(the configuration profile must have same name as the user). If the DVR
|
||||
|
@ -78,13 +83,28 @@ The columns have the following functions:
|
|||
<dd>
|
||||
Enables access to the Configuration tab.
|
||||
|
||||
<dt>Channel Tag Only
|
||||
<dt>Username Channel Tag Match
|
||||
<dd>
|
||||
If enabled, the user will only be able to access channels with a tag the
|
||||
same name as the username.
|
||||
|
||||
This provides a very rudimentary way of limiting access to certain channels.
|
||||
|
||||
<dt>Min Channel Num
|
||||
<dd>
|
||||
If nonzero, the user will only be able to access channels with
|
||||
a channel number equal or greater to this value.
|
||||
|
||||
<dt>Max Channel Num
|
||||
<dd>
|
||||
If nonzero, the user will only be able to access channels with
|
||||
a channel number equal or lower to this value.
|
||||
|
||||
<dt>Channel Tag
|
||||
<dd>
|
||||
If set, the user will only be able to access channels with
|
||||
a channel tag equal to this value.
|
||||
|
||||
<dt>Comment
|
||||
<dd>
|
||||
Allows the administrator to set a comment only visible in this editor.
|
||||
|
|
175
src/access.c
175
src/access.c
|
@ -37,6 +37,7 @@
|
|||
#include "access.h"
|
||||
#include "dtable.h"
|
||||
#include "settings.h"
|
||||
#include "htsmsg.h"
|
||||
|
||||
struct access_entry_queue access_entries;
|
||||
struct access_ticket_queue access_tickets;
|
||||
|
@ -152,6 +153,16 @@ access_ticket_verify(const char *id, const char *resource)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
access_destroy(access_t *a)
|
||||
{
|
||||
htsmsg_destroy(a->aa_chtags);
|
||||
free(a);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -264,24 +275,96 @@ access_verify(const char *username, const char *password,
|
|||
return (mask & bits) == mask ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static void
|
||||
access_update(access_t *a, access_entry_t *ae)
|
||||
{
|
||||
if(ae->ae_chmin || ae->ae_chmax) {
|
||||
if(a->aa_chmin || a->aa_chmax) {
|
||||
if (a->aa_chmin < ae->ae_chmin)
|
||||
a->aa_chmin = ae->ae_chmin;
|
||||
if (a->aa_chmax > ae->ae_chmax)
|
||||
a->aa_chmax = ae->ae_chmax;
|
||||
} else {
|
||||
a->aa_chmin = ae->ae_chmin;
|
||||
a->aa_chmax = ae->ae_chmax;
|
||||
}
|
||||
}
|
||||
|
||||
if(ae->ae_chtag) {
|
||||
if (a->aa_chtags == NULL)
|
||||
a->aa_chtags = htsmsg_create_list();
|
||||
htsmsg_add_str(a->aa_chtags, NULL, ae->ae_chtag);
|
||||
}
|
||||
|
||||
a->aa_rights |= ae->ae_rights;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t
|
||||
access_get_hashed(const char *username, const uint8_t digest[20],
|
||||
const uint8_t *challenge, struct sockaddr *src,
|
||||
int *entrymatch)
|
||||
access_t *
|
||||
access_get(const char *username, const char *password, struct sockaddr *src)
|
||||
{
|
||||
access_t *a = calloc(1, sizeof(*a));
|
||||
access_entry_t *ae;
|
||||
|
||||
if (access_noacl) {
|
||||
a->aa_rights = ACCESS_FULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
if(username != NULL && superuser_username != NULL &&
|
||||
password != NULL && superuser_password != NULL &&
|
||||
!strcmp(username, superuser_username) &&
|
||||
!strcmp(password, superuser_password)) {
|
||||
a->aa_rights = ACCESS_FULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
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 || password == NULL)
|
||||
continue; /* Didn't get one */
|
||||
|
||||
if(strcmp(ae->ae_username, username) ||
|
||||
strcmp(ae->ae_password, password))
|
||||
continue; /* username/password mismatch */
|
||||
}
|
||||
|
||||
if(!netmask_verify(ae, src))
|
||||
continue; /* IP based access mismatches */
|
||||
|
||||
a->aa_match = 1;
|
||||
access_update(a, ae);
|
||||
}
|
||||
|
||||
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 = calloc(1, sizeof(*a));
|
||||
access_entry_t *ae;
|
||||
SHA_CTX shactx;
|
||||
uint8_t d[20];
|
||||
uint32_t r = 0;
|
||||
int match = 0;
|
||||
|
||||
if(access_noacl)
|
||||
return 0xffffffff;
|
||||
if(access_noacl) {
|
||||
a->aa_rights = ACCESS_FULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
if(superuser_username != NULL && superuser_password != NULL) {
|
||||
|
||||
|
@ -291,8 +374,10 @@ access_get_hashed(const char *username, const uint8_t digest[20],
|
|||
SHA1_Update(&shactx, challenge, 32);
|
||||
SHA1_Final(d, &shactx);
|
||||
|
||||
if(!strcmp(superuser_username, username) && !memcmp(d, digest, 20))
|
||||
return 0xffffffff;
|
||||
if(!strcmp(superuser_username, username) && !memcmp(d, digest, 20)) {
|
||||
a->aa_rights = ACCESS_FULL;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -312,12 +397,12 @@ access_get_hashed(const char *username, const uint8_t digest[20],
|
|||
|
||||
if(strcmp(ae->ae_username, username) || memcmp(d, digest, 20))
|
||||
continue;
|
||||
match = 1;
|
||||
r |= ae->ae_rights;
|
||||
|
||||
a->aa_match = 1;
|
||||
access_update(a, ae);
|
||||
}
|
||||
if(entrymatch != NULL)
|
||||
*entrymatch = match;
|
||||
return r;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
|
@ -325,11 +410,11 @@ access_get_hashed(const char *username, const uint8_t digest[20],
|
|||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t
|
||||
access_t *
|
||||
access_get_by_addr(struct sockaddr *src)
|
||||
{
|
||||
access_t *a = calloc(1, sizeof(*a));
|
||||
access_entry_t *ae;
|
||||
uint32_t r = 0;
|
||||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link) {
|
||||
|
||||
|
@ -342,29 +427,12 @@ access_get_by_addr(struct sockaddr *src)
|
|||
if(!netmask_verify(ae, src))
|
||||
continue; /* IP based access mismatches */
|
||||
|
||||
r |= ae->ae_rights;
|
||||
access_update(a, ae);
|
||||
}
|
||||
return r;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t
|
||||
access_tag_only(const char *username)
|
||||
{
|
||||
access_entry_t *ae;
|
||||
uint32_t r = 0;
|
||||
|
||||
TAILQ_FOREACH(ae, &access_entries, ae_link) {
|
||||
|
||||
if(!strcmp(ae->ae_username, username))
|
||||
return ae->ae_tagonly;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -536,6 +604,7 @@ access_entry_destroy(access_entry_t *ae)
|
|||
free(ae->ae_username);
|
||||
free(ae->ae_password);
|
||||
free(ae->ae_comment);
|
||||
free(ae->ae_chtag);
|
||||
TAILQ_REMOVE(&access_entries, ae, ae_link);
|
||||
free(ae);
|
||||
}
|
||||
|
@ -579,11 +648,15 @@ access_record_build(access_entry_t *ae)
|
|||
htsmsg_add_str(e, "prefix", buf + 1);
|
||||
|
||||
htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0);
|
||||
htsmsg_add_u32(e, "adv_streaming", ae->ae_rights & ACCESS_ADVANCED_STREAMING ? 1 : 0);
|
||||
htsmsg_add_u32(e, "dvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0);
|
||||
htsmsg_add_u32(e, "dvrallcfg", ae->ae_rights & ACCESS_RECORDER_ALL ? 1 : 0);
|
||||
htsmsg_add_u32(e, "webui" , ae->ae_rights & ACCESS_WEB_INTERFACE ? 1 : 0);
|
||||
htsmsg_add_u32(e, "admin" , ae->ae_rights & ACCESS_ADMIN ? 1 : 0);
|
||||
htsmsg_add_u32(e, "tag_only" , ae->ae_tagonly);
|
||||
htsmsg_add_u32(e, "tag_only" , ae->ae_rights & ACCESS_TAG_ONLY ? 1 : 0);
|
||||
htsmsg_add_u32(e, "channel_min" , ae->ae_chmin);
|
||||
htsmsg_add_u32(e, "channel_max" , ae->ae_chmax);
|
||||
htsmsg_add_str(e, "channel_tag", ae->ae_chtag ?: "");
|
||||
|
||||
|
||||
htsmsg_add_str(e, "id", ae->ae_id);
|
||||
|
@ -668,6 +741,12 @@ access_record_update(void *opaque, const char *id, htsmsg_t *values,
|
|||
if(!htsmsg_get_u32(values, "streaming", &u32))
|
||||
access_update_flag(ae, ACCESS_STREAMING, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "adv_streaming", &u32))
|
||||
access_update_flag(ae, ACCESS_ADVANCED_STREAMING, u32);
|
||||
else
|
||||
access_update_flag(ae, ACCESS_ADVANCED_STREAMING,
|
||||
(ae->ae_rights & ACCESS_STREAMING));
|
||||
|
||||
if(!htsmsg_get_u32(values, "dvr", &u32))
|
||||
access_update_flag(ae, ACCESS_RECORDER, u32);
|
||||
|
||||
|
@ -686,7 +765,21 @@ access_record_update(void *opaque, const char *id, htsmsg_t *values,
|
|||
access_update_flag(ae, ACCESS_WEB_INTERFACE, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "tag_only", &u32))
|
||||
ae->ae_tagonly = u32;
|
||||
access_update_flag(ae, ACCESS_TAG_ONLY, u32);
|
||||
|
||||
if(!htsmsg_get_u32(values, "channel_min", &u32))
|
||||
ae->ae_chmin = u32;
|
||||
|
||||
if(!htsmsg_get_u32(values, "channel_max", &u32))
|
||||
ae->ae_chmax = u32;
|
||||
|
||||
if((s = htsmsg_get_str(values, "channel_tag")) != NULL) {
|
||||
free(ae->ae_chtag);
|
||||
if (s[0] == 0)
|
||||
ae->ae_chtag = NULL;
|
||||
else
|
||||
ae->ae_chtag = strdup(s);
|
||||
}
|
||||
|
||||
return access_record_build(ae);
|
||||
}
|
||||
|
@ -738,7 +831,6 @@ access_init(int createdefault, int noacl)
|
|||
struct timeval tv;
|
||||
} randseed;
|
||||
|
||||
access_noacl = noacl;
|
||||
if (noacl)
|
||||
tvhlog(LOG_WARNING, "access", "Access control checking disabled");
|
||||
|
||||
|
@ -759,8 +851,7 @@ access_init(int createdefault, int noacl)
|
|||
ae->ae_comment = strdup("Default access entry");
|
||||
|
||||
ae->ae_enabled = 1;
|
||||
ae->ae_tagonly = 0;
|
||||
ae->ae_rights = 0xffffffff;
|
||||
ae->ae_rights = ACCESS_FULL;
|
||||
|
||||
TAILQ_INIT(&ae->ae_ipmasks);
|
||||
|
||||
|
|
58
src/access.h
58
src/access.h
|
@ -19,6 +19,7 @@
|
|||
#ifndef ACCESS_H_
|
||||
#define ACCESS_H_
|
||||
|
||||
#include "htsmsg.h"
|
||||
|
||||
typedef struct access_ipmask {
|
||||
TAILQ_ENTRY(access_ipmask) ai_link;
|
||||
|
@ -47,7 +48,9 @@ typedef struct access_entry {
|
|||
char *ae_comment;
|
||||
int ae_enabled;
|
||||
int ae_tagonly;
|
||||
|
||||
uint32_t ae_chmin;
|
||||
uint32_t ae_chmax;
|
||||
char *ae_chtag;
|
||||
|
||||
uint32_t ae_rights;
|
||||
|
||||
|
@ -67,13 +70,25 @@ typedef struct access_ticket {
|
|||
char *at_resource;
|
||||
} access_ticket_t;
|
||||
|
||||
#define ACCESS_ANONYMOUS 0x0
|
||||
#define ACCESS_STREAMING 0x1
|
||||
#define ACCESS_WEB_INTERFACE 0x2
|
||||
#define ACCESS_RECORDER 0x4
|
||||
#define ACCESS_RECORDER_ALL 0x8
|
||||
#define ACCESS_ADMIN 0x10
|
||||
#define ACCESS_FULL 0x3f
|
||||
typedef struct access {
|
||||
uint32_t aa_rights;
|
||||
uint32_t aa_chmin;
|
||||
uint32_t aa_chmax;
|
||||
htsmsg_t *aa_chtags;
|
||||
int aa_match;
|
||||
} access_t;
|
||||
|
||||
#define ACCESS_ANONYMOUS 0
|
||||
#define ACCESS_STREAMING (1<<0)
|
||||
#define ACCESS_ADVANCED_STREAMING (1<<1)
|
||||
#define ACCESS_WEB_INTERFACE (1<<2)
|
||||
#define ACCESS_RECORDER (1<<3)
|
||||
#define ACCESS_RECORDER_ALL (1<<4)
|
||||
#define ACCESS_TAG_ONLY (1<<5)
|
||||
#define ACCESS_ADMIN (1<<6)
|
||||
|
||||
#define ACCESS_FULL \
|
||||
(ACCESS_STREAMING | ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
|
||||
|
||||
/**
|
||||
* Create a new ticket for the requested resource and generate a id for it
|
||||
|
@ -86,6 +101,12 @@ const char* access_ticket_create(const char *resource);
|
|||
int access_ticket_verify(const char *id, const char *resource);
|
||||
|
||||
int access_ticket_delete(const char *ticket_id);
|
||||
|
||||
/**
|
||||
* Free the access structure
|
||||
*/
|
||||
void access_destroy(access_t *a);
|
||||
|
||||
/**
|
||||
* Verifies that the given user in combination with the source ip
|
||||
* complies with the requested mask
|
||||
|
@ -96,17 +117,23 @@ int access_verify(const char *username, const char *password,
|
|||
struct sockaddr *src, uint32_t mask);
|
||||
|
||||
/**
|
||||
*
|
||||
* Get the access structure
|
||||
*/
|
||||
uint32_t access_get_hashed(const char *username, const uint8_t digest[20],
|
||||
const uint8_t *challenge, struct sockaddr *src,
|
||||
int *entrymatch);
|
||||
access_t *access_get(const char *username, const char *password,
|
||||
struct sockaddr *src);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t access_get_by_addr(struct sockaddr *src);
|
||||
access_t *
|
||||
access_get_hashed(const char *username, const uint8_t digest[20],
|
||||
const uint8_t *challenge, struct sockaddr *src);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
access_t *
|
||||
access_get_by_addr(struct sockaddr *src);
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -114,9 +141,4 @@ uint32_t access_get_by_addr(struct sockaddr *src);
|
|||
void access_init(int createdefault, int noacl);
|
||||
void access_done(void);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
uint32_t access_tag_only(const char *username);
|
||||
|
||||
#endif /* ACCESS_H_ */
|
||||
|
|
|
@ -413,6 +413,47 @@ channel_find_by_number ( int no )
|
|||
return ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can access the channel
|
||||
*/
|
||||
int
|
||||
channel_access(channel_t *ch, access_t *a, const char *username)
|
||||
{
|
||||
/* Channel number check */
|
||||
if (ch && (a->aa_chmin || a->aa_chmax)) {
|
||||
int chnum = channel_get_number(ch);
|
||||
if (chnum < a->aa_chmin || chnum > a->aa_chmax)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Channel tag check */
|
||||
if (ch && a->aa_chtags) {
|
||||
channel_tag_mapping_t *ctm;
|
||||
htsmsg_field_t *f;
|
||||
HTSMSG_FOREACH(f, a->aa_chtags) {
|
||||
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
|
||||
if (!strcmp(htsmsg_field_get_str(f) ?: "", ctm->ctm_tag->ct_name))
|
||||
goto chtags_ok;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
chtags_ok:
|
||||
|
||||
/* Channel tag <-> user name match */
|
||||
if (ch && (a->aa_rights & ACCESS_TAG_ONLY) != 0) {
|
||||
channel_tag_mapping_t *ctm;
|
||||
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
|
||||
if (!strcmp(username ?: "", ctm->ctm_tag->ct_name))
|
||||
goto tagonly_ok;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
tagonly_ok:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* **************************************************************************
|
||||
* Property updating
|
||||
* *************************************************************************/
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "epg.h"
|
||||
#include "idnode.h"
|
||||
|
||||
struct access;
|
||||
|
||||
RB_HEAD(channel_tree, channel);
|
||||
|
||||
LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
|
||||
|
@ -147,6 +149,8 @@ channel_tag_t *channel_tag_find_by_name(const char *name, int create);
|
|||
|
||||
channel_tag_t *channel_tag_find_by_identifier(uint32_t id);
|
||||
|
||||
int channel_access(channel_t *ch, struct access *a, const char *username);
|
||||
|
||||
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
|
||||
|
||||
void channel_save(channel_t *ch);
|
||||
|
|
|
@ -159,7 +159,7 @@ typedef struct htsp_connection {
|
|||
struct htsp_file_list htsp_files;
|
||||
int htsp_file_id;
|
||||
|
||||
uint32_t htsp_granted_access;
|
||||
access_t *htsp_granted_access;
|
||||
|
||||
uint8_t htsp_challenge[32];
|
||||
|
||||
|
@ -431,19 +431,10 @@ htsp_generate_challenge(htsp_connection_t *htsp)
|
|||
/**
|
||||
* Cehck if user can access the channel
|
||||
*/
|
||||
static int
|
||||
htsp_user_access_channel(htsp_connection_t *htsp, channel_t *ch) {
|
||||
if (!access_tag_only(htsp->htsp_username ?: ""))
|
||||
return 1;
|
||||
else {
|
||||
if (!ch) return 0;
|
||||
channel_tag_mapping_t *ctm;
|
||||
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
|
||||
if (!strcmp(htsp->htsp_username ?: "", ctm->ctm_tag->ct_name))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
static inline int
|
||||
htsp_user_access_channel(htsp_connection_t *htsp, channel_t *ch)
|
||||
{
|
||||
return channel_access(ch, htsp->htsp_granted_access, htsp->htsp_username);
|
||||
}
|
||||
|
||||
#define HTSP_CHECK_CHANNEL_ACCESS(htsp, ch)\
|
||||
|
@ -838,7 +829,7 @@ htsp_method_authenticate(htsp_connection_t *htsp, htsmsg_t *in)
|
|||
{
|
||||
htsmsg_t *r = htsmsg_create_map();
|
||||
|
||||
if(!(htsp->htsp_granted_access & HTSP_PRIV_MASK))
|
||||
if(!(htsp->htsp_granted_access->aa_rights & HTSP_PRIV_MASK))
|
||||
htsmsg_add_u32(r, "noaccess", 1);
|
||||
|
||||
return r;
|
||||
|
@ -1972,9 +1963,8 @@ htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m)
|
|||
const char *username;
|
||||
const void *digest;
|
||||
size_t digestlen;
|
||||
uint32_t access;
|
||||
access_t *rights;
|
||||
int privgain;
|
||||
int match;
|
||||
|
||||
if((username = htsmsg_get_str(m, "username")) == NULL)
|
||||
return;
|
||||
|
@ -1990,15 +1980,18 @@ htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m)
|
|||
if(htsmsg_get_bin(m, "digest", &digest, &digestlen))
|
||||
return;
|
||||
|
||||
access = access_get_hashed(username, digest, htsp->htsp_challenge,
|
||||
(struct sockaddr *)htsp->htsp_peer, &match);
|
||||
rights = access_get_hashed(username, digest, htsp->htsp_challenge,
|
||||
(struct sockaddr *)htsp->htsp_peer);
|
||||
|
||||
privgain = (access | htsp->htsp_granted_access) != htsp->htsp_granted_access;
|
||||
privgain = (rights->aa_rights |
|
||||
htsp->htsp_granted_access->aa_rights) !=
|
||||
htsp->htsp_granted_access->aa_rights;
|
||||
|
||||
if(privgain)
|
||||
tvhlog(LOG_INFO, "htsp", "%s: Privileges raised", htsp->htsp_logname);
|
||||
|
||||
htsp->htsp_granted_access |= access;
|
||||
access_destroy(htsp->htsp_granted_access);
|
||||
htsp->htsp_granted_access = rights;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2081,7 +2074,7 @@ readmsg:
|
|||
for(i = 0; i < NUM_METHODS; i++) {
|
||||
if(!strcmp(method, htsp_methods[i].name)) {
|
||||
|
||||
if((htsp->htsp_granted_access & htsp_methods[i].privmask) !=
|
||||
if((htsp->htsp_granted_access->aa_rights & htsp_methods[i].privmask) !=
|
||||
htsp_methods[i].privmask) {
|
||||
|
||||
pthread_mutex_unlock(&global_lock);
|
||||
|
@ -2281,6 +2274,7 @@ htsp_serve(int fd, void **opaque, struct sockaddr_storage *source,
|
|||
free(htsp.htsp_peername);
|
||||
free(htsp.htsp_username);
|
||||
free(htsp.htsp_clientname);
|
||||
access_destroy(htsp.htsp_granted_access);
|
||||
*opaque = NULL;
|
||||
}
|
||||
|
||||
|
|
43
src/http.c
43
src/http.c
|
@ -35,6 +35,7 @@
|
|||
#include "http.h"
|
||||
#include "access.h"
|
||||
#include "notify.h"
|
||||
#include "channels.h"
|
||||
|
||||
static void *http_server;
|
||||
|
||||
|
@ -395,26 +396,58 @@ http_redirect(http_connection_t *hc, const char *location,
|
|||
}
|
||||
|
||||
/**
|
||||
* Return non-zero if no access
|
||||
*
|
||||
*/
|
||||
int
|
||||
http_access_verify(http_connection_t *hc, int mask)
|
||||
static int http_access_verify_ticket(http_connection_t *hc)
|
||||
{
|
||||
const char *ticket_id = http_arg_get(&hc->hc_req_args, "ticket");
|
||||
|
||||
if(!access_ticket_verify(ticket_id, hc->hc_url))
|
||||
{
|
||||
if(!access_ticket_verify(ticket_id, hc->hc_url)) {
|
||||
char addrstr[50];
|
||||
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
|
||||
tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
|
||||
addrstr, ticket_id, hc->hc_url);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-zero if no access
|
||||
*/
|
||||
int
|
||||
http_access_verify(http_connection_t *hc, int mask)
|
||||
{
|
||||
if (!http_access_verify_ticket(hc))
|
||||
return 0;
|
||||
|
||||
return access_verify(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-zero if no access
|
||||
*/
|
||||
int
|
||||
http_access_verify_channel(http_connection_t *hc, int mask,
|
||||
struct channel *ch)
|
||||
{
|
||||
access_t *a;
|
||||
int res = -1;
|
||||
|
||||
assert(ch);
|
||||
|
||||
if (!http_access_verify_ticket(hc))
|
||||
return 0;
|
||||
|
||||
a = access_get(hc->hc_username, hc->hc_password,
|
||||
(struct sockaddr *)hc->hc_peer);
|
||||
if (channel_access(ch, a, hc->hc_username))
|
||||
res = 0;
|
||||
access_destroy(a);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute url callback
|
||||
*
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "url.h"
|
||||
#include "tvhpoll.h"
|
||||
|
||||
struct channel;
|
||||
|
||||
typedef TAILQ_HEAD(http_arg_list, http_arg) http_arg_list_t;
|
||||
|
||||
typedef RB_HEAD(,http_arg) http_arg_tree_t;
|
||||
|
@ -203,6 +205,7 @@ void http_server_init(const char *bindaddr);
|
|||
void http_server_done(void);
|
||||
|
||||
int http_access_verify(http_connection_t *hc, int mask);
|
||||
int http_access_verify_channel(http_connection_t *hc, int mask, struct channel *ch);
|
||||
|
||||
void http_deescape(char *s);
|
||||
|
||||
|
|
|
@ -34,6 +34,11 @@ tvheadend.acleditor = function() {
|
|||
header: "Streaming",
|
||||
dataIndex: 'streaming',
|
||||
width: 100
|
||||
}, {
|
||||
xtype: 'checkcolumn',
|
||||
header: "Advanced Streaming",
|
||||
dataIndex: 'adv_streaming',
|
||||
width: 100
|
||||
}, {
|
||||
xtype: 'checkcolumn',
|
||||
header: "Video Recorder",
|
||||
|
@ -41,9 +46,9 @@ tvheadend.acleditor = function() {
|
|||
width: 100
|
||||
}, {
|
||||
xtype: 'checkcolumn',
|
||||
header: "User Configs (VR)",
|
||||
header: "Username Configs (VR)",
|
||||
dataIndex: 'dvrallcfg',
|
||||
width: 100
|
||||
width: 150
|
||||
}, {
|
||||
xtype: 'checkcolumn',
|
||||
header: "Web Interface",
|
||||
|
@ -56,9 +61,51 @@ tvheadend.acleditor = function() {
|
|||
width: 100
|
||||
}, {
|
||||
xtype: 'checkcolumn',
|
||||
header: "Channel Tag Only",
|
||||
header: "Username Channel Tag Match",
|
||||
dataIndex: 'tag_only',
|
||||
width: 200
|
||||
width: 150
|
||||
}, {
|
||||
xtype: 'numbercolumn',
|
||||
header: "Min Channel Num",
|
||||
dataIndex: 'channel_min',
|
||||
width: 120,
|
||||
format: '0',
|
||||
editor: {
|
||||
xtype: 'numberfield',
|
||||
allowBlank: 'false',
|
||||
minValue: 0,
|
||||
maxValue: 2147483647,
|
||||
allowNegative: false,
|
||||
allowDecimals: false
|
||||
}
|
||||
}, {
|
||||
xtype: 'numbercolumn',
|
||||
header: "Max Channel Num",
|
||||
dataIndex: 'channel_max',
|
||||
width: 120,
|
||||
format: '0',
|
||||
editor: {
|
||||
xtype: 'numberfield',
|
||||
allowBlank: 'false',
|
||||
minValue: 0,
|
||||
maxValue: 2147483647,
|
||||
allowNegative: false,
|
||||
allowDecimals: false
|
||||
}
|
||||
}, {
|
||||
header: "Channel Tag",
|
||||
dataIndex: 'channel_tag',
|
||||
width: 200,
|
||||
editor: new Ext.form.ComboBox({
|
||||
displayField: 'name',
|
||||
store: tvheadend.channelTags,
|
||||
mode: 'local',
|
||||
editable: true,
|
||||
forceSelection: true,
|
||||
typeAhead: true,
|
||||
triggerAction: 'all',
|
||||
emptyText: 'Match this channel tag...'
|
||||
})
|
||||
}, {
|
||||
header: "Comment",
|
||||
dataIndex: 'comment',
|
||||
|
@ -68,10 +115,30 @@ tvheadend.acleditor = function() {
|
|||
|
||||
var UserRecord = Ext.data.Record.create(
|
||||
['enabled', 'streaming', 'dvr', 'dvrallcfg', 'admin', 'webui', 'username', 'tag_only',
|
||||
'prefix', 'password', 'comment'
|
||||
'prefix', 'password', 'comment', 'channel_min', 'channel_max', 'channel_tag',
|
||||
'adv_streaming'
|
||||
]);
|
||||
|
||||
tvheadend.accesscontrolStore = new Ext.data.JsonStore({
|
||||
root: 'entries',
|
||||
fields: UserRecord,
|
||||
url: "tablemgr",
|
||||
autoLoad: true,
|
||||
id: 'id',
|
||||
baseParams: {
|
||||
table: "accesscontrol",
|
||||
op: "get"
|
||||
}
|
||||
});
|
||||
|
||||
tvheadend.accesscontrolStore.on('update', function (store, record, operation) {
|
||||
if (operation == 'edit') {
|
||||
if (record.isModified('channel_tag') && record.data.channel_tag == '(Clear filter)')
|
||||
record.set('channel_tag',"");
|
||||
}
|
||||
});
|
||||
|
||||
return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
|
||||
UserRecord, [], null, 'config_access.html',
|
||||
UserRecord, [], tvheadend.accesscontrolStore, 'config_access.html',
|
||||
'group');
|
||||
};
|
||||
|
|
|
@ -368,6 +368,9 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
|
|||
const char *host;
|
||||
muxer_container_type_t mc;
|
||||
|
||||
if (http_access_verify_channel(hc, ACCESS_STREAMING, channel))
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
|
||||
mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
|
||||
if(mc == MC_UNKNOWN)
|
||||
mc = dvr_config_find_by_name_default("")->dvr_mc;
|
||||
|
@ -431,6 +434,8 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
|
|||
|
||||
htsbuf_qprintf(hq, "#EXTM3U\n");
|
||||
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
|
||||
if (http_access_verify_channel(hc, ACCESS_STREAMING, ctm->ctm_channel))
|
||||
continue;
|
||||
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ctm->ctm_channel));
|
||||
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ctm->ctm_channel));
|
||||
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
|
||||
|
@ -527,6 +532,10 @@ http_channel_list_playlist(http_connection_t *hc)
|
|||
htsbuf_qprintf(hq, "#EXTM3U\n");
|
||||
for (idx = 0; idx < count; idx++) {
|
||||
ch = chlist[idx];
|
||||
|
||||
if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
|
||||
continue;
|
||||
|
||||
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ch));
|
||||
|
||||
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ch));
|
||||
|
@ -568,6 +577,10 @@ http_dvr_list_playlist(http_connection_t *hc)
|
|||
if(!fsize)
|
||||
continue;
|
||||
|
||||
if (de->de_channel &&
|
||||
http_access_verify_channel(hc, ACCESS_RECORDER, de->de_channel))
|
||||
continue;
|
||||
|
||||
durration = de->de_stop - de->de_start;
|
||||
durration += (de->de_stop_extra + de->de_start_extra)*60;
|
||||
bandwidth = ((8*fsize) / (durration*1024.0));
|
||||
|
@ -721,6 +734,9 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
|
|||
const char *name;
|
||||
char addrbuf[50];
|
||||
|
||||
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
|
||||
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
|
||||
|
@ -788,6 +804,9 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
|
|||
char addrbuf[50];
|
||||
muxer_config_t muxcfg = { 0 };
|
||||
|
||||
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
|
||||
streaming_queue_init(&sq, SMT_PACKET);
|
||||
|
||||
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
|
||||
|
@ -833,6 +852,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight)
|
|||
const char *name;
|
||||
char addrbuf[50];
|
||||
|
||||
if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
|
||||
return HTTP_STATUS_UNAUTHORIZED;
|
||||
|
||||
cfg = dvr_config_find_by_name_default("");
|
||||
|
||||
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
|
||||
|
|
Loading…
Add table
Reference in a new issue