Commit 56cd1de3 authored by Ed "oddsock" Zaleski's avatar Ed "oddsock" Zaleski

added ability to disallow concurrent connections from the same username if...

added ability to disallow concurrent connections from the same username if using htpasswd listener authentication.

svn path=/icecast/trunk/icecast/; revision=6711
parent 71388d47
......@@ -86,6 +86,7 @@
<fallback-mount>/example2.ogg</fallback-mount>
<authentication type="htpasswd">
<option name="filename" value="myauth"/>
<option name="allow_duplicate_users" value="0"/>
</authentication>
</mount>
-->
......
......@@ -273,6 +273,7 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
&lt;fallback-mount&gt;example2.ogg&lt;fallback-mount&gt;
&lt;authentication type="htpasswd"&gt;
&lt;option name="filename" value="myauth"/&gt;
&lt;option name="allow_duplicate_users" value="0"/&gt;
&lt;/authentication&gt;
&lt;mount&gt;
......@@ -305,7 +306,7 @@ This specifies a mountpoint that is used in the case of a source disconnect. If
</div>
<h4>authentication</h4>
<div class=indentedbox>
This specifies that the named mount point will require listener authentication. Currently, we only support a file-based authentication scheme (type=htpasswd). Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes. These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg). Users and Passwords are maintained via the web admin interface. A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens.
This specifies that the named mount point will require listener authentication. Currently, we only support a file-based authentication scheme (type=htpasswd). Users and encrypted password are placed in this file (separated by a :) and all requests for this mountpoint will require that a user and password be supplied for authentication purposes. These values are passed in via normal HTTP Basic Authentication means (i.e. http://user:password@stream:port/mountpoint.ogg). Users and Passwords are maintained via the web admin interface. A mountpoint configured with an authenticator will display a red key next to the mount point name on the admin screens. You can read more about listener authentication <a href="icecast2_listenerauth.html">here</a>.
</div>
<br>
<br>
......
......@@ -20,10 +20,11 @@
&lt;mount-name&gt;/example-complex.ogg&lt;/mount-name&gt;
&lt;authentication type="htpasswd"&gt;
&lt;option name="filename" value="myauth"/&gt;
&lt;option name="allow_duplicate_users" value="0"/&gt;
&lt;/authentication&gt;
&lt;/mount&gt;
</pre>
<p>To support listener authentication you MUST provide at a minimum &lt;mount-name&gt; and &lt;authentication&gt;. The mount-name is the name of the mountpoint that you will use to connect your source client with and authentication configures what type of icecast2 authenticator to use. Currently, only a single type "htpasswd" is implemented. New authenticators will be added later. Each authenticator has a variable number of options that are required and these are specified as shown in the example. The htpasswd authenticator requires only a single parameter, filename, which specifies the name of the file to use to store users and passwords. Note that this file need not exist (and probably will not exist when you first set it up). Icecast has built-in support for managing users and passwords via the web admin interface. More on this later in this section.</p>
<p>To support listener authentication you MUST provide at a minimum &lt;mount-name&gt; and &lt;authentication&gt;. The mount-name is the name of the mountpoint that you will use to connect your source client with and authentication configures what type of icecast2 authenticator to use. Currently, only a single type "htpasswd" is implemented. New authenticators will be added later. Each authenticator has a variable number of options that are required and these are specified as shown in the example. The htpasswd authenticator requires a few parameters. The first, filename, specifies the name of the file to use to store users and passwords. Note that this file need not exist (and probably will not exist when you first set it up). Icecast has built-in support for managing users and passwords via the web admin interface. More on this later in this section. The second option, allow_duplicate_users, if set to 0, will prevent multiple connections using the same username. Setting this value to 1 will enable mutltiple connections from the same username on a given mountpoint. Note there is no way to specify a "max connections" for a particular user.
<p>Icecast supports a mixture of streams that require listener authentication and those that do not. Only mounts that are named in the config file can be configured for listener authentication.</p>
<br>
......
......@@ -34,6 +34,30 @@
#define CATMODULE "auth"
int auth_is_listener_connected(source_t *source, char *username)
{
client_t *client;
avl_node *client_node;
avl_tree_rlock(source->client_tree);
client_node = avl_get_first(source->client_tree);
while(client_node) {
client = (client_t *)client_node->key;
if (client->username) {
if (!strcmp(client->username, username)) {
avl_tree_unlock(source->client_tree);
return 1;
}
}
client_node = avl_get_next(client_node);
}
avl_tree_unlock(source->client_tree);
return 0;
}
auth_result auth_check_client(source_t *source, client_t *client)
{
auth_t *authenticator = source->authenticator;
......@@ -71,7 +95,7 @@ auth_result auth_check_client(source_t *source, client_t *client)
password = tmp+1;
result = authenticator->authenticate(
authenticator, username, password);
authenticator, source, username, password);
if(result == AUTH_OK)
client->username = strdup(username);
......@@ -106,6 +130,7 @@ auth_t *auth_get_authenticator(char *type, config_options_t *options)
typedef struct {
char *filename;
int allow_duplicate_users;
rwlock_t file_rwlock;
} htpasswd_auth_state;
......@@ -150,14 +175,21 @@ static char *get_hash(char *data, int len)
#define MAX_LINE_LEN 512
/* Not efficient; opens and scans the entire file for every request */
static auth_result htpasswd_auth(auth_t *auth, char *username, char *password)
static auth_result htpasswd_auth(auth_t *auth, source_t *source, char *username, char *password)
{
htpasswd_auth_state *state = auth->state;
FILE *passwdfile = fopen(state->filename, "rb");
FILE *passwdfile = NULL;
char line[MAX_LINE_LEN];
char *sep;
thread_rwlock_rlock(&state->file_rwlock);
if (!state->allow_duplicate_users) {
if (auth_is_listener_connected(source, username)) {
thread_rwlock_unlock(&state->file_rwlock);
return AUTH_FORBIDDEN;
}
}
passwdfile = fopen(state->filename, "rb");
if(passwdfile == NULL) {
WARN2("Failed to open authentication database \"%s\": %s",
state->filename, strerror(errno));
......@@ -208,9 +240,12 @@ static auth_t *auth_get_htpasswd_auth(config_options_t *options)
state = calloc(1, sizeof(htpasswd_auth_state));
state->allow_duplicate_users = 1;
while(options) {
if(!strcmp(options->name, "filename"))
state->filename = strdup(options->value);
if(!strcmp(options->name, "allow_duplicate_users"))
state->allow_duplicate_users = atoi(options->value);
options = options->next;
}
......@@ -458,3 +493,4 @@ int auth_get_userlist(source_t *source, xmlNodePtr srcnode)
}
return ret;
}
......@@ -24,6 +24,7 @@ typedef enum
{
AUTH_OK,
AUTH_FAILED,
AUTH_FORBIDDEN,
AUTH_USERADDED,
AUTH_USEREXISTS,
AUTH_USERDELETED,
......@@ -33,7 +34,7 @@ typedef struct auth_tag
{
/* Authenticate using the given username and password */
auth_result (*authenticate)(struct auth_tag *self,
char *username, char *password);
source_t *source, char *username, char *password);
void (*free)(struct auth_tag *self);
void *state;
void *type;
......
......@@ -115,3 +115,13 @@ void client_send_401(client_t *client) {
client->respcode = 401;
client_destroy(client);
}
void client_send_403(client_t *client) {
int bytes = sock_write(client->con->sock,
"HTTP/1.0 403 Forbidden\r\n"
"\r\n"
"Access restricted.\r\n");
if(bytes > 0) client->con->sent_bytes = bytes;
client->respcode = 403;
client_destroy(client);
}
......@@ -53,6 +53,7 @@ void client_destroy(client_t *client);
void client_send_504(client_t *client, char *message);
void client_send_404(client_t *client, char *message);
void client_send_401(client_t *client);
void client_send_403(client_t *client);
void client_send_400(client_t *client, char *message);
#endif /* __CLIENT_H__ */
......@@ -753,6 +753,7 @@ static void _handle_get_request(connection_t *con,
aliases *alias;
ice_config_t *config;
int client_limit;
int ret;
config = config_get_config();
fileserve = config->fileserve;
......@@ -916,11 +917,20 @@ static void _handle_get_request(connection_t *con,
/* Check for any required authentication first */
if(source->authenticator != NULL) {
if(auth_check_client(source, client) != AUTH_OK) {
ret = auth_check_client(source, client);
if(ret != AUTH_OK) {
avl_tree_unlock(global.source_tree);
INFO1("Client attempted to log in to source (\"%s\")with "
if (ret == AUTH_FORBIDDEN) {
INFO1("Client attempted to log multiple times to source "
"(\"%s\")", uri);
client_send_403(client);
}
else {
/* If not FORBIDDEN, default to 401 */
INFO1("Client attempted to log in to source (\"%s\")with "
"incorrect or missing password", uri);
client_send_401(client);
client_send_401(client);
}
return;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment