Commit d642846c authored by Ed "oddsock" Zaleski's avatar Ed "oddsock" Zaleski

added web based interface to htpasswd client authentication

svn path=/icecast/trunk/icecast/; revision=6610
parent cd47258b
...@@ -4,5 +4,5 @@ AUTOMAKE_OPTIONS = foreign ...@@ -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 @@ ...@@ -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}&amp;id={ID}">kill</a></td> <td><a class="nav2" href="killclient.xsl?mount={$themount}&amp;id={ID}">kill</a></td>
......
...@@ -31,13 +31,16 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 ...@@ -271,6 +271,10 @@ If you are relaying a Shoutcast stream, you need to specify this indicator to al
&lt;max-listeners&gt;1&lt;max-listeners&gt; &lt;max-listeners&gt;1&lt;max-listeners&gt;
&lt;dump-file&gt;/tmp/dump-example1.ogg&lt;dump-file&gt; &lt;dump-file&gt;/tmp/dump-example1.ogg&lt;dump-file&gt;
&lt;fallback-mount&gt;example2.ogg&lt;fallback-mount&gt; &lt;fallback-mount&gt;example2.ogg&lt;fallback-mount&gt;
&lt;authentication type="htpasswd"&gt;
&lt;option name="filename" value="myauth"/&gt;
&lt;/authentication&gt;
&lt;mount&gt; &lt;mount&gt;
</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 ...@@ -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 &lt;mount-name&gt; 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 &lt;mount-name&gt; 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>
......
...@@ -37,6 +37,7 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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); ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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)
{ {
......
...@@ -33,6 +33,7 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
}
...@@ -16,11 +16,17 @@ ...@@ -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 ...@@ -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
......