mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-29 04:25:55 -04:00
298 lines
7.5 KiB
C
298 lines
7.5 KiB
C
|
/* Icecast
|
||
|
*
|
||
|
* This program is distributed under the GNU General Public License, version 2.
|
||
|
* A copy of this license is included with this source.
|
||
|
*
|
||
|
* Copyright 2014, Philipp Schafft <lion@lion.leolix.org>
|
||
|
*/
|
||
|
|
||
|
/* -*- c-basic-offset: 4; indent-tabs-mode: nil; -*- */
|
||
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include "acl.h"
|
||
|
#include "admin.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#define MAX_ADMIN_COMMANDS 32
|
||
|
|
||
|
/* define internal structure */
|
||
|
struct acl_tag {
|
||
|
/* reference counter */
|
||
|
size_t refcount;
|
||
|
|
||
|
/* allowed methods */
|
||
|
acl_policy_t method[httpp_req_unknown+1];
|
||
|
|
||
|
/* admin/ interface */
|
||
|
struct {
|
||
|
int command;
|
||
|
acl_policy_t policy;
|
||
|
} admin_commands[MAX_ADMIN_COMMANDS];
|
||
|
size_t admin_commands_len;
|
||
|
acl_policy_t admin_command_policy;
|
||
|
|
||
|
/* web/ interface */
|
||
|
acl_policy_t web_policy;
|
||
|
|
||
|
/* mount specific functons */
|
||
|
time_t max_connection_duration;
|
||
|
size_t max_connections_per_user;
|
||
|
};
|
||
|
|
||
|
/* some string util functions */
|
||
|
static inline void __skip_spaces(const char **str) {
|
||
|
register const char * p;
|
||
|
|
||
|
for (p = *str; *p == ' '; p++);
|
||
|
|
||
|
*str = p;
|
||
|
}
|
||
|
|
||
|
int acl_set_ANY_str(acl_t * acl, acl_policy_t policy, const char * str, int (*callback)(acl_t *, acl_policy_t, const char *)) {
|
||
|
const char * end;
|
||
|
size_t len;
|
||
|
char buf[64];
|
||
|
int ret;
|
||
|
|
||
|
if (!acl || !str || !callback || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY))
|
||
|
return -1;
|
||
|
|
||
|
do {
|
||
|
__skip_spaces(&str);
|
||
|
end = strstr(str, ",");
|
||
|
if (end) {
|
||
|
len = end - str;
|
||
|
} else {
|
||
|
len = strlen(str);
|
||
|
}
|
||
|
|
||
|
if (len > (sizeof(buf) - 1))
|
||
|
return -1;
|
||
|
memcpy(buf, str, len);
|
||
|
buf[len] = 0;
|
||
|
|
||
|
ret = callback(acl, policy, buf);
|
||
|
if (ret)
|
||
|
return ret;
|
||
|
|
||
|
str += len + 1;
|
||
|
} while (end);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* basic functions to work with ACLs */
|
||
|
acl_t * acl_new(void) {
|
||
|
acl_t * ret = calloc(1, sizeof(*ret));
|
||
|
if (!ret)
|
||
|
return NULL;
|
||
|
|
||
|
ret->refcount = 1;
|
||
|
|
||
|
acl_set_method_str(ret, ACL_POLICY_DENY, "*");
|
||
|
acl_set_method_str(ret, ACL_POLICY_ALLOW, "get");
|
||
|
|
||
|
acl_set_admin_str(ret, ACL_POLICY_DENY, "*");
|
||
|
acl_set_admin_str(ret, ACL_POLICY_ALLOW, "buildm3u");
|
||
|
|
||
|
acl_set_web_policy(ret, ACL_POLICY_ALLOW);
|
||
|
|
||
|
acl_set_max_connection_duration(ret, -1);
|
||
|
acl_set_max_connections_per_user(ret, 0);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
acl_t * acl_new_from_xml_node(xmlNodePtr node) {
|
||
|
acl_t * ret;
|
||
|
char * tmp;
|
||
|
xmlAttrPtr prop;
|
||
|
|
||
|
if (!node)
|
||
|
return NULL;
|
||
|
|
||
|
ret = acl_new();
|
||
|
if (!ret)
|
||
|
return NULL;
|
||
|
|
||
|
prop = node->properties;
|
||
|
while (prop) {
|
||
|
tmp = (char*)xmlGetProp(node, prop->name);
|
||
|
if (tmp) {
|
||
|
if (strcmp((const char*)prop->name, "allow-method") == 0) {
|
||
|
acl_set_method_str(ret, ACL_POLICY_ALLOW, tmp);
|
||
|
} else if (strcmp((const char*)prop->name, "deny-method") == 0) {
|
||
|
acl_set_method_str(ret, ACL_POLICY_DENY, tmp);
|
||
|
} else if (strcmp((const char*)prop->name, "allow-admin") == 0) {
|
||
|
acl_set_admin_str(ret, ACL_POLICY_ALLOW, tmp);
|
||
|
} else if (strcmp((const char*)prop->name, "deny-admin") == 0) {
|
||
|
acl_set_admin_str(ret, ACL_POLICY_DENY, tmp);
|
||
|
} else if (strcmp((const char*)prop->name, "allow-web") == 0) {
|
||
|
if (strstr(tmp, "*"))
|
||
|
acl_set_web_policy(ret, ACL_POLICY_ALLOW);
|
||
|
} else if (strcmp((const char*)prop->name, "deny-web") == 0) {
|
||
|
if (strstr(tmp, "*"))
|
||
|
acl_set_web_policy(ret, ACL_POLICY_DENY);
|
||
|
} else if (strcmp((const char*)prop->name, "connections-per-user") == 0) {
|
||
|
if (strcmp(tmp, "*") == 0 || strcmp(tmp, "unlimited") == 0) {
|
||
|
acl_set_max_connections_per_user(ret, 0);
|
||
|
} else {
|
||
|
acl_set_max_connections_per_user(ret, atoi(tmp));
|
||
|
}
|
||
|
} else if (strcmp((const char*)prop->name, "connection-duration") == 0) {
|
||
|
if (strcmp(tmp, "*") == 0 || strcmp(tmp, "unlimited") == 0) {
|
||
|
acl_set_max_connection_duration(ret, 0);
|
||
|
} else {
|
||
|
acl_set_max_connection_duration(ret, atoi(tmp));
|
||
|
}
|
||
|
}
|
||
|
xmlFree(tmp);
|
||
|
}
|
||
|
prop = prop->next;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void acl_addref(acl_t * acl) {
|
||
|
if (!acl)
|
||
|
return;
|
||
|
|
||
|
acl->refcount++;
|
||
|
}
|
||
|
|
||
|
void acl_release(acl_t * acl) {
|
||
|
if (!acl)
|
||
|
return;
|
||
|
|
||
|
acl->refcount--;
|
||
|
if (acl->refcount)
|
||
|
return;
|
||
|
|
||
|
free(acl);
|
||
|
}
|
||
|
|
||
|
/* HTTP Method specific functions */
|
||
|
int acl_set_method_str__callback(acl_t * acl, acl_policy_t policy, const char * str) {
|
||
|
httpp_request_type_e method;
|
||
|
size_t i;
|
||
|
|
||
|
if (strcmp(str, "*") == 0) {
|
||
|
for (i = 0; i < (sizeof(acl->method)/sizeof(*acl->method)); i++)
|
||
|
acl->method[i] = policy;
|
||
|
} else {
|
||
|
method = httpp_str_to_method(str);
|
||
|
if (method == httpp_req_unknown)
|
||
|
return -1;
|
||
|
|
||
|
acl->method[method] = policy;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
acl_policy_t acl_test_method(acl_t * acl, httpp_request_type_e method) {
|
||
|
if (!acl || method < httpp_req_none || method > httpp_req_unknown)
|
||
|
return ACL_POLICY_ERROR;
|
||
|
|
||
|
return acl->method[method];
|
||
|
}
|
||
|
|
||
|
/* admin/ interface specific functions */
|
||
|
int acl_set_admin_str__callbck(acl_t * acl, acl_policy_t policy, const char * str) {
|
||
|
size_t read_i, write_i;
|
||
|
int command = admin_get_command(str);
|
||
|
|
||
|
if (command == ADMIN_COMMAND_ERROR)
|
||
|
return -1;
|
||
|
|
||
|
if (command == ADMIN_COMMAND_ANY) {
|
||
|
acl->admin_command_policy = policy;
|
||
|
for (read_i = write_i = 0; read_i < acl->admin_commands_len; read_i++) {
|
||
|
if (acl->admin_commands[read_i].policy == policy)
|
||
|
continue;
|
||
|
acl->admin_commands[write_i] = acl->admin_commands[read_i];
|
||
|
write_i++; /* no need to check bounds here as this loop can only compress the array */
|
||
|
}
|
||
|
acl->admin_commands_len = write_i;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (acl->admin_commands_len == MAX_ADMIN_COMMANDS)
|
||
|
return -1;
|
||
|
|
||
|
acl->admin_commands[acl->admin_commands_len].command = command;
|
||
|
acl->admin_commands[acl->admin_commands_len].policy = policy;
|
||
|
acl->admin_commands_len++;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
acl_policy_t acl_test_admin(acl_t * acl, int command) {
|
||
|
size_t i;
|
||
|
|
||
|
if (!acl)
|
||
|
return ACL_POLICY_ERROR;
|
||
|
|
||
|
for (i = 0; i < acl->admin_commands_len; i++)
|
||
|
if (acl->admin_commands[i].command == command)
|
||
|
return acl->admin_commands[i].policy;
|
||
|
|
||
|
return acl->admin_command_policy;
|
||
|
}
|
||
|
|
||
|
/* web/ interface specific functions */
|
||
|
int acl_set_web_policy(acl_t * acl, acl_policy_t policy) {
|
||
|
if (!acl || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY))
|
||
|
return -1;
|
||
|
|
||
|
acl->web_policy = policy;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
acl_policy_t acl_test_web(acl_t * acl) {
|
||
|
if (!acl)
|
||
|
return ACL_POLICY_ERROR;
|
||
|
|
||
|
return acl->web_policy;
|
||
|
}
|
||
|
|
||
|
/* mount specific functons */
|
||
|
int acl_set_max_connection_duration(acl_t * acl, time_t duration) {
|
||
|
if (!acl)
|
||
|
return -1;
|
||
|
|
||
|
acl->max_connection_duration = duration;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
time_t acl_get_max_connection_duration(acl_t * acl) {
|
||
|
if (!acl)
|
||
|
return -1;
|
||
|
|
||
|
return acl->max_connection_duration;
|
||
|
}
|
||
|
|
||
|
int acl_set_max_connections_per_user(acl_t * acl, size_t limit) {
|
||
|
if (!acl)
|
||
|
return -1;
|
||
|
|
||
|
acl->max_connections_per_user = limit;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ssize_t acl_get_max_connections_per_user(acl_t * acl) {
|
||
|
if (!acl)
|
||
|
return -1;
|
||
|
|
||
|
return acl->max_connections_per_user;
|
||
|
}
|