mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-01-03 14:56:34 -05:00
added web based interface to htpasswd client authentication
svn path=/icecast/trunk/icecast/; revision=6610
This commit is contained in:
parent
cd47258bb4
commit
d642846c80
@ -4,5 +4,5 @@ AUTOMAKE_OPTIONS = foreign
|
|||||||
|
|
||||||
admindir = $(pkgdatadir)/admin
|
admindir = $(pkgdatadir)/admin
|
||||||
dist_admin_DATA = listclients.xsl listmounts.xsl moveclients.xsl response.xsl \
|
dist_admin_DATA = listclients.xsl listmounts.xsl moveclients.xsl response.xsl \
|
||||||
stats.xsl
|
stats.xsl manageauth.xsl
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
|
<xsl:variable name = "themount" ><xsl:value-of select="@mount" /></xsl:variable>
|
||||||
<xsl:for-each select="listener">
|
<xsl:for-each select="listener">
|
||||||
<tr>
|
<tr>
|
||||||
<td><xsl:value-of select="IP" /></td>
|
<td><xsl:value-of select="IP" /><xsl:if test="username"> (<xsl:value-of select="username" />)</xsl:if></td>
|
||||||
<td><xsl:value-of select="Connected" /> seconds</td>
|
<td><xsl:value-of select="Connected" /> seconds</td>
|
||||||
<td><xsl:value-of select="UserAgent" /></td>
|
<td><xsl:value-of select="UserAgent" /></td>
|
||||||
<td><a class="nav2" href="killclient.xsl?mount={$themount}&id={ID}">kill</a></td>
|
<td><a class="nav2" href="killclient.xsl?mount={$themount}&id={ID}">kill</a></td>
|
||||||
|
@ -31,13 +31,16 @@
|
|||||||
<xsl:for-each select="source">
|
<xsl:for-each select="source">
|
||||||
<h3>
|
<h3>
|
||||||
<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
|
<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
|
||||||
(<xsl:value-of select="@mount" />)</h3>
|
(<xsl:value-of select="@mount" />)
|
||||||
|
<xsl:if test="authenticator"> <a href="manageauth.xsl?mount={@mount}"><img border="0" src="/key.gif"/></a> </xsl:if>
|
||||||
|
</h3>
|
||||||
<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
|
<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> |
|
<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> |
|
||||||
<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> |
|
<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> |
|
||||||
<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
|
<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
|
||||||
|
<xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
@ -58,13 +58,16 @@
|
|||||||
<xsl:if test = "listeners!=''">
|
<xsl:if test = "listeners!=''">
|
||||||
<h3>
|
<h3>
|
||||||
<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
|
<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
|
||||||
(<xsl:value-of select="@mount" />)</h3>
|
(<xsl:value-of select="@mount" />)
|
||||||
|
<xsl:if test="authenticator"> <a href="manageauth.xsl?mount={@mount}"><img border="0" src="/key.gif"/></a> </xsl:if>
|
||||||
|
</h3>
|
||||||
<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
|
<table border="0" cellpadding="1" cellspacing="5" bgcolor="444444">
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center">
|
<td align="center">
|
||||||
<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> |
|
<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> |
|
||||||
<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a> |
|
<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a> |
|
||||||
<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
|
<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
|
||||||
|
<xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table>
|
</table>
|
||||||
<br></br>
|
<br></br>
|
||||||
|
@ -84,6 +84,9 @@
|
|||||||
<max-listeners>1</max-listeners>
|
<max-listeners>1</max-listeners>
|
||||||
<dump-file>/tmp/dump-example1.ogg</dump-file>
|
<dump-file>/tmp/dump-example1.ogg</dump-file>
|
||||||
<fallback-mount>/example2.ogg</fallback-mount>
|
<fallback-mount>/example2.ogg</fallback-mount>
|
||||||
|
<authentication type="htpasswd">
|
||||||
|
<option name="filename" value="myauth"/>
|
||||||
|
</authentication>
|
||||||
</mount>
|
</mount>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
@ -271,6 +271,10 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
|
|||||||
<max-listeners>1<max-listeners>
|
<max-listeners>1<max-listeners>
|
||||||
<dump-file>/tmp/dump-example1.ogg<dump-file>
|
<dump-file>/tmp/dump-example1.ogg<dump-file>
|
||||||
<fallback-mount>example2.ogg<fallback-mount>
|
<fallback-mount>example2.ogg<fallback-mount>
|
||||||
|
<authentication type="htpasswd">
|
||||||
|
<option name="filename" value="myauth"/>
|
||||||
|
</authentication>
|
||||||
|
|
||||||
<mount>
|
<mount>
|
||||||
</pre>
|
</pre>
|
||||||
<p>This section contains settings which apply only to a specific mountpoint. Within this section you can reserve a specific mountpoint and set a source username/password for that mountpoint (not yet implemented) as well as specify individual settings which will apply only to the supplied mountpoint.
|
<p>This section contains settings which apply only to a specific mountpoint. Within this section you can reserve a specific mountpoint and set a source username/password for that mountpoint (not yet implemented) as well as specify individual settings which will apply only to the supplied mountpoint.
|
||||||
@ -299,6 +303,10 @@ An optional value which will set the filename which will be a dump of the stream
|
|||||||
<div class=indentedbox>
|
<div class=indentedbox>
|
||||||
This specifies a mountpoint that is used in the case of a source disconnect. If listeners are connected to the mount specified by the <mount-name> config value, then if the source is disconnected; all currently connected clients will be moved to the fallback-mount.
|
This specifies a mountpoint that is used in the case of a source disconnect. If listeners are connected to the mount specified by the <mount-name> config value, then if the source is disconnected; all currently connected clients will be moved to the fallback-mount.
|
||||||
</div>
|
</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.
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
85
src/admin.c
85
src/admin.c
@ -37,6 +37,7 @@
|
|||||||
#include "format_mp3.h"
|
#include "format_mp3.h"
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "auth.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#endif
|
#endif
|
||||||
@ -50,10 +51,12 @@
|
|||||||
#define COMMAND_METADATA_UPDATE 2
|
#define COMMAND_METADATA_UPDATE 2
|
||||||
#define COMMAND_RAW_SHOW_LISTENERS 3
|
#define COMMAND_RAW_SHOW_LISTENERS 3
|
||||||
#define COMMAND_RAW_MOVE_CLIENTS 4
|
#define COMMAND_RAW_MOVE_CLIENTS 4
|
||||||
|
#define COMMAND_RAW_MANAGEAUTH 5
|
||||||
|
|
||||||
#define COMMAND_TRANSFORMED_FALLBACK 50
|
#define COMMAND_TRANSFORMED_FALLBACK 50
|
||||||
#define COMMAND_TRANSFORMED_SHOW_LISTENERS 53
|
#define COMMAND_TRANSFORMED_SHOW_LISTENERS 53
|
||||||
#define COMMAND_TRANSFORMED_MOVE_CLIENTS 54
|
#define COMMAND_TRANSFORMED_MOVE_CLIENTS 54
|
||||||
|
#define COMMAND_TRANSFORMED_MANAGEAUTH 55
|
||||||
|
|
||||||
/* Global commands */
|
/* Global commands */
|
||||||
#define COMMAND_RAW_LIST_MOUNTS 101
|
#define COMMAND_RAW_LIST_MOUNTS 101
|
||||||
@ -89,6 +92,8 @@
|
|||||||
#define KILLSOURCE_RAW_REQUEST "killsource"
|
#define KILLSOURCE_RAW_REQUEST "killsource"
|
||||||
#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
|
#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
|
||||||
#define ADMIN_XSL_RESPONSE "response.xsl"
|
#define ADMIN_XSL_RESPONSE "response.xsl"
|
||||||
|
#define MANAGEAUTH_RAW_REQUEST "manageauth"
|
||||||
|
#define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
|
||||||
#define DEFAULT_RAW_REQUEST ""
|
#define DEFAULT_RAW_REQUEST ""
|
||||||
#define DEFAULT_TRANSFORMED_REQUEST ""
|
#define DEFAULT_TRANSFORMED_REQUEST ""
|
||||||
|
|
||||||
@ -133,6 +138,10 @@ int admin_get_command(char *command)
|
|||||||
return COMMAND_RAW_KILL_SOURCE;
|
return COMMAND_RAW_KILL_SOURCE;
|
||||||
else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
|
else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
|
||||||
return COMMAND_TRANSFORMED_KILL_SOURCE;
|
return COMMAND_TRANSFORMED_KILL_SOURCE;
|
||||||
|
else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
|
||||||
|
return COMMAND_RAW_MANAGEAUTH;
|
||||||
|
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
|
||||||
|
return COMMAND_TRANSFORMED_MANAGEAUTH;
|
||||||
else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
|
else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
|
||||||
return COMMAND_TRANSFORMED_STATS;
|
return COMMAND_TRANSFORMED_STATS;
|
||||||
else if(!strcmp(command, DEFAULT_RAW_REQUEST))
|
else if(!strcmp(command, DEFAULT_RAW_REQUEST))
|
||||||
@ -151,6 +160,8 @@ static void command_stats(client_t *client, int response);
|
|||||||
static void command_list_mounts(client_t *client, int response);
|
static void command_list_mounts(client_t *client, int response);
|
||||||
static void command_kill_client(client_t *client, source_t *source,
|
static void command_kill_client(client_t *client, source_t *source,
|
||||||
int response);
|
int response);
|
||||||
|
static void command_manageauth(client_t *client, source_t *source,
|
||||||
|
int response);
|
||||||
static void command_kill_source(client_t *client, source_t *source,
|
static void command_kill_source(client_t *client, source_t *source,
|
||||||
int response);
|
int response);
|
||||||
static void admin_handle_mount_request(client_t *client, source_t *source,
|
static void admin_handle_mount_request(client_t *client, source_t *source,
|
||||||
@ -195,6 +206,10 @@ xmlDocPtr admin_build_sourcelist(char *current_source)
|
|||||||
xmlNewChild(srcnode, NULL, "Connected", buf);
|
xmlNewChild(srcnode, NULL, "Connected", buf);
|
||||||
xmlNewChild(srcnode, NULL, "Format",
|
xmlNewChild(srcnode, NULL, "Format",
|
||||||
source->format->format_description);
|
source->format->format_description);
|
||||||
|
if (source->authenticator) {
|
||||||
|
xmlNewChild(srcnode, NULL, "authenticator",
|
||||||
|
source->authenticator->type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
node = avl_get_next(node);
|
node = avl_get_next(node);
|
||||||
}
|
}
|
||||||
@ -402,6 +417,12 @@ static void admin_handle_mount_request(client_t *client, source_t *source,
|
|||||||
case COMMAND_TRANSFORMED_KILL_SOURCE:
|
case COMMAND_TRANSFORMED_KILL_SOURCE:
|
||||||
command_kill_source(client, source, TRANSFORMED);
|
command_kill_source(client, source, TRANSFORMED);
|
||||||
break;
|
break;
|
||||||
|
case COMMAND_TRANSFORMED_MANAGEAUTH:
|
||||||
|
command_manageauth(client, source, TRANSFORMED);
|
||||||
|
break;
|
||||||
|
case COMMAND_RAW_MANAGEAUTH:
|
||||||
|
command_manageauth(client, source, RAW);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
WARN0("Mount request not recognised");
|
WARN0("Mount request not recognised");
|
||||||
client_send_400(client, "Mount request unknown");
|
client_send_400(client, "Mount request unknown");
|
||||||
@ -547,6 +568,9 @@ static void command_show_listeners(client_t *client, source_t *source,
|
|||||||
memset(buf, '\000', sizeof(buf));
|
memset(buf, '\000', sizeof(buf));
|
||||||
snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
|
snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
|
||||||
xmlNewChild(listenernode, NULL, "ID", buf);
|
xmlNewChild(listenernode, NULL, "ID", buf);
|
||||||
|
if (current->username) {
|
||||||
|
xmlNewChild(listenernode, NULL, "username", current->username);
|
||||||
|
}
|
||||||
client_node = avl_get_next(client_node);
|
client_node = avl_get_next(client_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,6 +581,67 @@ static void command_show_listeners(client_t *client, source_t *source,
|
|||||||
client_destroy(client);
|
client_destroy(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void command_manageauth(client_t *client, source_t *source,
|
||||||
|
int response)
|
||||||
|
{
|
||||||
|
xmlDocPtr doc;
|
||||||
|
xmlNodePtr node, srcnode, msgnode;
|
||||||
|
char *action = NULL;
|
||||||
|
char *username = NULL;
|
||||||
|
char *password = NULL;
|
||||||
|
char *message = NULL;
|
||||||
|
int ret = AUTH_OK;
|
||||||
|
|
||||||
|
if((COMMAND_OPTIONAL(client, "action", action))) {
|
||||||
|
if (!strcmp(action, "add")) {
|
||||||
|
COMMAND_REQUIRE(client, "username", username);
|
||||||
|
COMMAND_REQUIRE(client, "password", password);
|
||||||
|
ret = auth_adduser(source, username, password);
|
||||||
|
if (ret == AUTH_FAILED) {
|
||||||
|
message = strdup("User add failed - check the icecast error log");
|
||||||
|
}
|
||||||
|
if (ret == AUTH_USERADDED) {
|
||||||
|
message = strdup("User added");
|
||||||
|
}
|
||||||
|
if (ret == AUTH_USEREXISTS) {
|
||||||
|
message = strdup("User already exists - not added");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!strcmp(action, "delete")) {
|
||||||
|
COMMAND_REQUIRE(client, "username", username);
|
||||||
|
ret = auth_deleteuser(source, username);
|
||||||
|
if (ret == AUTH_FAILED) {
|
||||||
|
message = strdup("User delete failed - check the icecast error log");
|
||||||
|
}
|
||||||
|
if (ret == AUTH_USERDELETED) {
|
||||||
|
message = strdup("User deleted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = xmlNewDoc("1.0");
|
||||||
|
node = xmlNewDocNode(doc, NULL, "icestats", NULL);
|
||||||
|
srcnode = xmlNewChild(node, NULL, "source", NULL);
|
||||||
|
xmlSetProp(srcnode, "mount", source->mount);
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
msgnode = xmlNewChild(node, NULL, "iceresponse", NULL);
|
||||||
|
xmlNewChild(msgnode, NULL, "message", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlDocSetRootElement(doc, node);
|
||||||
|
|
||||||
|
auth_get_userlist(source, srcnode);
|
||||||
|
|
||||||
|
admin_send_response(doc, client, response,
|
||||||
|
MANAGEAUTH_TRANSFORMED_REQUEST);
|
||||||
|
if (message) {
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
client_destroy(client);
|
||||||
|
}
|
||||||
|
|
||||||
static void command_kill_source(client_t *client, source_t *source,
|
static void command_kill_source(client_t *client, source_t *source,
|
||||||
int response)
|
int response)
|
||||||
{
|
{
|
||||||
|
227
src/auth.c
227
src/auth.c
@ -33,6 +33,7 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#define CATMODULE "auth"
|
#define CATMODULE "auth"
|
||||||
|
|
||||||
|
|
||||||
auth_result auth_check_client(source_t *source, client_t *client)
|
auth_result auth_check_client(source_t *source, client_t *client)
|
||||||
{
|
{
|
||||||
auth_t *authenticator = source->authenticator;
|
auth_t *authenticator = source->authenticator;
|
||||||
@ -90,6 +91,7 @@ auth_t *auth_get_authenticator(char *type, config_options_t *options)
|
|||||||
auth_t *auth = NULL;
|
auth_t *auth = NULL;
|
||||||
if(!strcmp(type, "htpasswd")) {
|
if(!strcmp(type, "htpasswd")) {
|
||||||
auth = auth_get_htpasswd_auth(options);
|
auth = auth_get_htpasswd_auth(options);
|
||||||
|
auth->type = strdup(type);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ERROR1("Unrecognised authenticator type: \"%s\"", type);
|
ERROR1("Unrecognised authenticator type: \"%s\"", type);
|
||||||
@ -104,12 +106,15 @@ auth_t *auth_get_authenticator(char *type, config_options_t *options)
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *filename;
|
char *filename;
|
||||||
|
rwlock_t file_rwlock;
|
||||||
} htpasswd_auth_state;
|
} htpasswd_auth_state;
|
||||||
|
|
||||||
static void htpasswd_clear(auth_t *self) {
|
static void htpasswd_clear(auth_t *self) {
|
||||||
htpasswd_auth_state *state = self->state;
|
htpasswd_auth_state *state = self->state;
|
||||||
free(state->filename);
|
free(state->filename);
|
||||||
|
thread_rwlock_destroy(&state->file_rwlock);
|
||||||
free(state);
|
free(state);
|
||||||
|
free(self->type);
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,9 +157,11 @@ static auth_result htpasswd_auth(auth_t *auth, char *username, char *password)
|
|||||||
char line[MAX_LINE_LEN];
|
char line[MAX_LINE_LEN];
|
||||||
char *sep;
|
char *sep;
|
||||||
|
|
||||||
|
thread_rwlock_rlock(&state->file_rwlock);
|
||||||
if(passwdfile == NULL) {
|
if(passwdfile == NULL) {
|
||||||
WARN2("Failed to open authentication database \"%s\": %s",
|
WARN2("Failed to open authentication database \"%s\": %s",
|
||||||
state->filename, strerror(errno));
|
state->filename, strerror(errno));
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
return AUTH_FAILED;
|
return AUTH_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +183,7 @@ static auth_result htpasswd_auth(auth_t *auth, char *username, char *password)
|
|||||||
if(!strcmp(hash, hashed_password)) {
|
if(!strcmp(hash, hashed_password)) {
|
||||||
fclose(passwdfile);
|
fclose(passwdfile);
|
||||||
free(hashed_password);
|
free(hashed_password);
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
return AUTH_OK;
|
return AUTH_OK;
|
||||||
}
|
}
|
||||||
free(hashed_password);
|
free(hashed_password);
|
||||||
@ -186,6 +194,7 @@ static auth_result htpasswd_auth(auth_t *auth, char *username, char *password)
|
|||||||
|
|
||||||
fclose(passwdfile);
|
fclose(passwdfile);
|
||||||
|
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
return AUTH_FAILED;
|
return AUTH_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,6 +225,224 @@ static auth_t *auth_get_htpasswd_auth(config_options_t *options)
|
|||||||
DEBUG1("Configured htpasswd authentication using password file %s",
|
DEBUG1("Configured htpasswd authentication using password file %s",
|
||||||
state->filename);
|
state->filename);
|
||||||
|
|
||||||
|
thread_rwlock_create(&state->file_rwlock);
|
||||||
|
|
||||||
return authenticator;
|
return authenticator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int auth_htpasswd_existing_user(auth_t *auth, char *username)
|
||||||
|
{
|
||||||
|
FILE *passwdfile;
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
int ret = AUTH_OK;
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
char *sep;
|
||||||
|
|
||||||
|
state = auth->state;
|
||||||
|
passwdfile = fopen(state->filename, "rb");
|
||||||
|
|
||||||
|
if(passwdfile == NULL) {
|
||||||
|
WARN2("Failed to open authentication database \"%s\": %s",
|
||||||
|
state->filename, strerror(errno));
|
||||||
|
return AUTH_FAILED;
|
||||||
|
}
|
||||||
|
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
|
||||||
|
if(!line[0] || line[0] == '#')
|
||||||
|
continue;
|
||||||
|
sep = strchr(line, ':');
|
||||||
|
if(sep == NULL) {
|
||||||
|
DEBUG0("No seperator in line");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*sep = 0;
|
||||||
|
if (!strcmp(username, line)) {
|
||||||
|
/* We found the user, break out of the loop */
|
||||||
|
ret = AUTH_USEREXISTS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(passwdfile);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
int auth_htpasswd_adduser(auth_t *auth, char *username, char *password)
|
||||||
|
{
|
||||||
|
FILE *passwdfile;
|
||||||
|
char *hashed_password = NULL;
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
|
||||||
|
if (auth_htpasswd_existing_user(auth, username) == AUTH_USEREXISTS) {
|
||||||
|
return AUTH_USEREXISTS;
|
||||||
|
}
|
||||||
|
state = auth->state;
|
||||||
|
passwdfile = fopen(state->filename, "ab");
|
||||||
|
|
||||||
|
if(passwdfile == NULL) {
|
||||||
|
WARN2("Failed to open authentication database \"%s\": %s",
|
||||||
|
state->filename, strerror(errno));
|
||||||
|
return AUTH_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashed_password = get_hash(password, strlen(password));
|
||||||
|
if (hashed_password) {
|
||||||
|
fprintf(passwdfile, "%s:%s\n", username, hashed_password);
|
||||||
|
free(hashed_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(passwdfile);
|
||||||
|
return AUTH_USERADDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int auth_adduser(source_t *source, char *username, char *password)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
|
||||||
|
if (source->authenticator) {
|
||||||
|
if (!strcmp(source->authenticator->type, "htpasswd")) {
|
||||||
|
state = source->authenticator->state;
|
||||||
|
thread_rwlock_wlock(&state->file_rwlock);
|
||||||
|
ret = auth_htpasswd_adduser(source->authenticator, username, password);
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int auth_htpasswd_deleteuser(auth_t *auth, char *username)
|
||||||
|
{
|
||||||
|
FILE *passwdfile;
|
||||||
|
FILE *tmp_passwdfile;
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
char *sep;
|
||||||
|
char *tmpfile = NULL;
|
||||||
|
int tmpfile_len = 0;
|
||||||
|
|
||||||
|
state = auth->state;
|
||||||
|
passwdfile = fopen(state->filename, "rb");
|
||||||
|
|
||||||
|
if(passwdfile == NULL) {
|
||||||
|
WARN2("Failed to open authentication database \"%s\": %s",
|
||||||
|
state->filename, strerror(errno));
|
||||||
|
return AUTH_FAILED;
|
||||||
|
}
|
||||||
|
tmpfile_len = strlen(state->filename) + 6;
|
||||||
|
tmpfile = calloc(1, tmpfile_len);
|
||||||
|
sprintf(tmpfile, ".%s.tmp", state->filename);
|
||||||
|
|
||||||
|
tmp_passwdfile = fopen(tmpfile, "wb");
|
||||||
|
|
||||||
|
if(tmp_passwdfile == NULL) {
|
||||||
|
WARN2("Failed to open temporary authentication database \"%s\": %s",
|
||||||
|
tmpfile, strerror(errno));
|
||||||
|
fclose(passwdfile);
|
||||||
|
free(tmpfile);
|
||||||
|
return AUTH_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
|
||||||
|
if(!line[0] || line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sep = strchr(line, ':');
|
||||||
|
if(sep == NULL) {
|
||||||
|
DEBUG0("No seperator in line");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sep = 0;
|
||||||
|
if (strcmp(username, line)) {
|
||||||
|
/* We did not match on the user, so copy it to the temp file */
|
||||||
|
/* and put the : back in */
|
||||||
|
*sep = ':';
|
||||||
|
fprintf(tmp_passwdfile, "%s\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(tmp_passwdfile);
|
||||||
|
fclose(passwdfile);
|
||||||
|
|
||||||
|
/* Now move the contents of the tmp file to the original */
|
||||||
|
if (rename(tmpfile, state->filename) != 0) {
|
||||||
|
ERROR3("Problem moving temp authentication file to original \"%s\" - \"%s\": %s",
|
||||||
|
tmpfile, state->filename, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmpfile);
|
||||||
|
|
||||||
|
return AUTH_USERDELETED;
|
||||||
|
}
|
||||||
|
int auth_deleteuser(source_t *source, char *username)
|
||||||
|
{
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
if (source->authenticator) {
|
||||||
|
if (!strcmp(source->authenticator->type, "htpasswd")) {
|
||||||
|
state = source->authenticator->state;
|
||||||
|
thread_rwlock_wlock(&state->file_rwlock);
|
||||||
|
ret = auth_htpasswd_deleteuser(source->authenticator, username);
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int auth_get_htpasswd_userlist(auth_t *auth, xmlNodePtr srcnode)
|
||||||
|
{
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
FILE *passwdfile;
|
||||||
|
char line[MAX_LINE_LEN];
|
||||||
|
char *sep;
|
||||||
|
char *passwd;
|
||||||
|
xmlNodePtr newnode;
|
||||||
|
|
||||||
|
state = auth->state;
|
||||||
|
|
||||||
|
passwdfile = fopen(state->filename, "rb");
|
||||||
|
|
||||||
|
if(passwdfile == NULL) {
|
||||||
|
WARN2("Failed to open authentication database \"%s\": %s",
|
||||||
|
state->filename, strerror(errno));
|
||||||
|
return AUTH_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(get_line(passwdfile, line, MAX_LINE_LEN)) {
|
||||||
|
if(!line[0] || line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sep = strchr(line, ':');
|
||||||
|
if(sep == NULL) {
|
||||||
|
DEBUG0("No seperator in line");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*sep = 0;
|
||||||
|
newnode = xmlNewChild(srcnode, NULL, "User", NULL);
|
||||||
|
xmlNewChild(newnode, NULL, "username", line);
|
||||||
|
passwd = sep+1;
|
||||||
|
xmlNewChild(newnode, NULL, "password", passwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(passwdfile);
|
||||||
|
return AUTH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int auth_get_userlist(source_t *source, xmlNodePtr srcnode)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
htpasswd_auth_state *state;
|
||||||
|
|
||||||
|
if (source->authenticator) {
|
||||||
|
if (!strcmp(source->authenticator->type, "htpasswd")) {
|
||||||
|
state = source->authenticator->state;
|
||||||
|
thread_rwlock_rlock(&state->file_rwlock);
|
||||||
|
ret = auth_get_htpasswd_userlist(source->authenticator, srcnode);
|
||||||
|
thread_rwlock_unlock(&state->file_rwlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
10
src/auth.h
10
src/auth.h
@ -16,11 +16,17 @@
|
|||||||
#include "source.h"
|
#include "source.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <libxml/xmlmemory.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
AUTH_OK,
|
AUTH_OK,
|
||||||
AUTH_FAILED,
|
AUTH_FAILED,
|
||||||
|
AUTH_USERADDED,
|
||||||
|
AUTH_USEREXISTS,
|
||||||
|
AUTH_USERDELETED,
|
||||||
} auth_result;
|
} auth_result;
|
||||||
|
|
||||||
typedef struct auth_tag
|
typedef struct auth_tag
|
||||||
@ -30,12 +36,16 @@ typedef struct auth_tag
|
|||||||
char *username, char *password);
|
char *username, char *password);
|
||||||
void (*free)(struct auth_tag *self);
|
void (*free)(struct auth_tag *self);
|
||||||
void *state;
|
void *state;
|
||||||
|
void *type;
|
||||||
} auth_t;
|
} auth_t;
|
||||||
|
|
||||||
auth_result auth_check_client(source_t *source, client_t *client);
|
auth_result auth_check_client(source_t *source, client_t *client);
|
||||||
|
|
||||||
auth_t *auth_get_authenticator(char *type, config_options_t *options);
|
auth_t *auth_get_authenticator(char *type, config_options_t *options);
|
||||||
void *auth_clear(auth_t *authenticator);
|
void *auth_clear(auth_t *authenticator);
|
||||||
|
int auth_get_userlist(source_t *source, xmlNodePtr srcnode);
|
||||||
|
int auth_adduser(source_t *source, char *username, char *password);
|
||||||
|
int auth_deleteuser(source_t *source, char *username);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -551,6 +551,7 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
|
|||||||
option = option->next;
|
option = option->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
opt->next = NULL;
|
||||||
|
|
||||||
if(last_option)
|
if(last_option)
|
||||||
last_option->next = opt;
|
last_option->next = opt;
|
||||||
|
@ -978,6 +978,7 @@ void source_apply_mount (source_t *source, mount_proxy *mountinfo)
|
|||||||
{
|
{
|
||||||
source->authenticator = auth_get_authenticator(
|
source->authenticator = auth_get_authenticator(
|
||||||
mountinfo->auth_type, mountinfo->auth_options);
|
mountinfo->auth_type, mountinfo->auth_options);
|
||||||
|
stats_event(source->mount, "authenticator", mountinfo->auth_type);
|
||||||
}
|
}
|
||||||
if (mountinfo->dumpfile)
|
if (mountinfo->dumpfile)
|
||||||
{
|
{
|
||||||
|
@ -10,4 +10,5 @@ dist_web_DATA = status.xsl \
|
|||||||
corner_topleft.jpg \
|
corner_topleft.jpg \
|
||||||
corner_topright.jpg \
|
corner_topright.jpg \
|
||||||
icecast.png \
|
icecast.png \
|
||||||
|
key.gif \
|
||||||
style.css
|
style.css
|
||||||
|
@ -32,13 +32,22 @@ Name: "{app}\logs"
|
|||||||
Source: "Release\Icecast2.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "Release\Icecast2.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "Release\icecast2console.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
|
Source: "..\doc\icecast2.chm"; DestDir: "{app}\doc"; Flags: ignoreversion
|
||||||
Source: "..\web\status.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
|
Source: "..\web\corner_bottomleft.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\corner_bottomright.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\corner_topleft.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\corner_topright.jpg"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\icecast.png"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\key.gif"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
Source: "..\web\status2.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
|
Source: "..\web\status2.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\status.xsl"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
Source: "..\web\style.css"; DestDir: "{app}\web"; Flags: ignoreversion
|
||||||
|
|
||||||
Source: "..\admin\listclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
Source: "..\admin\listclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
Source: "..\admin\listmounts.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
Source: "..\admin\listmounts.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
Source: "..\admin\moveclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
Source: "..\admin\moveclients.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
Source: "..\admin\response.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
Source: "..\admin\response.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
Source: "..\admin\stats.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
Source: "..\admin\stats.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
|
Source: "..\admin\manageauth.xsl"; DestDir: "{app}\admin"; Flags: ignoreversion
|
||||||
Source: "..\..\pthreads\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\..\pthreads\pthreadVSE.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\conf\icecast.xml"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\conf\icecast.xml"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "..\..\iconv\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "..\..\iconv\lib\iconv.dll"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
Loading…
Reference in New Issue
Block a user