acl.c 9.31 KB
Newer Older
Philipp Schafft's avatar
Philipp Schafft committed
1 2 3 4 5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2014-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
Philipp Schafft's avatar
Philipp Schafft committed
7 8 9 10 11 12 13 14 15 16 17 18
 */

/* -*- 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"
19
#include "util.h"
Philipp Schafft's avatar
Philipp Schafft committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

#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 {
35
        admin_command_id_t command;
Philipp Schafft's avatar
Philipp Schafft committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49
        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 */
Marvin Scholz's avatar
Marvin Scholz committed
50 51
static inline void __skip_spaces(const char **str)
{
Philipp Schafft's avatar
Philipp Schafft committed
52 53 54 55 56 57
 register const char * p;

 for (p = *str; *p == ' '; p++);
 *str = p;
}

Marvin Scholz's avatar
Marvin Scholz committed
58 59 60 61 62 63
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;
Philipp Schafft's avatar
Philipp Schafft committed
64 65 66 67
    size_t len;
    char buf[64];
    int ret;

Marvin Scholz's avatar
Marvin Scholz committed
68
    if ( !acl || !str || !callback || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY) )
Philipp Schafft's avatar
Philipp Schafft committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
        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 */
Marvin Scholz's avatar
Marvin Scholz committed
96 97
acl_t *acl_new(void)
{
Philipp Schafft's avatar
Philipp Schafft committed
98 99 100 101 102 103 104
    acl_t * ret = calloc(1, sizeof(*ret));
    if (!ret)
        return NULL;

    ret->refcount = 1;

    acl_set_method_str(ret, ACL_POLICY_DENY, "*");
105
    acl_set_method_str(ret, ACL_POLICY_ALLOW, "get,options");
Philipp Schafft's avatar
Philipp Schafft committed
106 107 108 109 110 111 112 113 114 115 116 117

    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;
}

Marvin Scholz's avatar
Marvin Scholz committed
118 119
acl_t *acl_new_from_xml_node(xmlNodePtr node)
{
Philipp Schafft's avatar
Philipp Schafft committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
    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) {
135
            /* basic {allow|deny}-* options */
Philipp Schafft's avatar
Philipp Schafft committed
136 137 138 139 140 141 142 143 144
            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) {
145
                if (strstr(tmp, "*") || util_str_to_bool(tmp)) {
Philipp Schafft's avatar
Philipp Schafft committed
146
                    acl_set_web_policy(ret, ACL_POLICY_ALLOW);
147 148 149
                } else {
                    acl_set_web_policy(ret, ACL_POLICY_DENY);
                }
Philipp Schafft's avatar
Philipp Schafft committed
150
            } else if (strcmp((const char*)prop->name, "deny-web") == 0) {
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
                if (strstr(tmp, "*") || util_str_to_bool(tmp)) {
                    acl_set_web_policy(ret, ACL_POLICY_DENY);
                } else {
                    acl_set_web_policy(ret, ACL_POLICY_ALLOW);
                }

            /* wildcard {allow,deny} option */
            } else if (strcmp((const char*)prop->name, "allow-all") == 0) {
                if (strstr(tmp, "*") || util_str_to_bool(tmp)) {
                    acl_set_method_str(ret, ACL_POLICY_ALLOW, "*");
                    acl_set_admin_str(ret,  ACL_POLICY_ALLOW, "*");
                    acl_set_web_policy(ret, ACL_POLICY_ALLOW);
                } else {
                    acl_set_method_str(ret, ACL_POLICY_DENY, "*");
                    acl_set_admin_str(ret,  ACL_POLICY_DENY, "*");
                    acl_set_web_policy(ret, ACL_POLICY_DENY);
                }
            } else if (strcmp((const char*)prop->name, "deny-all") == 0) {
                if (strstr(tmp, "*") || util_str_to_bool(tmp)) {
                    acl_set_method_str(ret, ACL_POLICY_DENY, "*");
171
                    acl_set_admin_str(ret,  ACL_POLICY_DENY, "*");
Philipp Schafft's avatar
Philipp Schafft committed
172
                    acl_set_web_policy(ret, ACL_POLICY_DENY);
173 174 175 176 177 178 179
                } else {
                    acl_set_method_str(ret, ACL_POLICY_ALLOW, "*");
                    acl_set_admin_str(ret,  ACL_POLICY_ALLOW, "*");
                    acl_set_web_policy(ret, ACL_POLICY_ALLOW);
                }

            /* other options */
Philipp Schafft's avatar
Philipp Schafft committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
            } 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;
}

Marvin Scholz's avatar
Marvin Scholz committed
201 202
void acl_addref(acl_t * acl)
{
Philipp Schafft's avatar
Philipp Schafft committed
203 204 205 206 207 208
    if (!acl)
        return;

    acl->refcount++;
}

Marvin Scholz's avatar
Marvin Scholz committed
209 210
void acl_release(acl_t * acl)
{
Philipp Schafft's avatar
Philipp Schafft committed
211 212 213 214 215 216 217 218 219 220 221
    if (!acl)
        return;

    acl->refcount--;
    if (acl->refcount)
        return;

    free(acl);
}

/* HTTP Method specific functions */
Marvin Scholz's avatar
Marvin Scholz committed
222 223 224 225
int acl_set_method_str__callback(acl_t          *acl,
                                 acl_policy_t   policy,
                                 const char     *str)
{
Philipp Schafft's avatar
Philipp Schafft committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    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;
}

Marvin Scholz's avatar
Marvin Scholz committed
243 244
acl_policy_t acl_test_method(acl_t * acl, httpp_request_type_e method)
{
Philipp Schafft's avatar
Philipp Schafft committed
245 246 247 248 249 250 251
    if (!acl || method < httpp_req_none || method > httpp_req_unknown)
        return ACL_POLICY_ERROR;

    return acl->method[method];
}

/* admin/ interface specific functions */
Marvin Scholz's avatar
Marvin Scholz committed
252 253 254 255
int acl_set_admin_str__callbck(acl_t        *acl,
                               acl_policy_t policy,
                               const char   *str)
{
Philipp Schafft's avatar
Philipp Schafft committed
256
    size_t read_i, write_i;
257
    admin_command_id_t command = admin_get_command(str);
Philipp Schafft's avatar
Philipp Schafft committed
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

   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;
}

283
acl_policy_t acl_test_admin(acl_t *acl, admin_command_id_t command)
Marvin Scholz's avatar
Marvin Scholz committed
284
{
Philipp Schafft's avatar
Philipp Schafft committed
285 286 287 288 289 290 291 292 293 294 295 296 297
    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 */
Marvin Scholz's avatar
Marvin Scholz committed
298 299
int acl_set_web_policy(acl_t *acl, acl_policy_t policy)
{
Philipp Schafft's avatar
Philipp Schafft committed
300 301 302 303 304 305 306 307
    if (!acl || (policy != ACL_POLICY_ALLOW && policy != ACL_POLICY_DENY))
        return -1;

    acl->web_policy = policy;

    return 0;
}

Marvin Scholz's avatar
Marvin Scholz committed
308 309
acl_policy_t acl_test_web(acl_t *acl)
{
Philipp Schafft's avatar
Philipp Schafft committed
310 311 312 313 314 315 316
    if (!acl)
        return ACL_POLICY_ERROR;

    return acl->web_policy;
}

/* mount specific functons */
Marvin Scholz's avatar
Marvin Scholz committed
317 318
int acl_set_max_connection_duration(acl_t *acl, time_t duration)
{
Philipp Schafft's avatar
Philipp Schafft committed
319 320 321 322 323 324 325 326
    if (!acl)
        return -1;

    acl->max_connection_duration = duration;

    return 0;
}

Marvin Scholz's avatar
Marvin Scholz committed
327 328
time_t acl_get_max_connection_duration(acl_t *acl)
{
Philipp Schafft's avatar
Philipp Schafft committed
329 330
    if (!acl)
        return -1;
331

Philipp Schafft's avatar
Philipp Schafft committed
332 333 334
    return acl->max_connection_duration;
}

Marvin Scholz's avatar
Marvin Scholz committed
335 336
int acl_set_max_connections_per_user(acl_t *acl, size_t limit)
{
Philipp Schafft's avatar
Philipp Schafft committed
337 338 339 340 341 342 343 344
    if (!acl)
        return -1;

    acl->max_connections_per_user = limit;

    return 0;
}

Marvin Scholz's avatar
Marvin Scholz committed
345 346
ssize_t acl_get_max_connections_per_user(acl_t *acl)
{
Philipp Schafft's avatar
Philipp Schafft committed
347 348
    if (!acl)
        return -1;
349

Philipp Schafft's avatar
Philipp Schafft committed
350 351
    return acl->max_connections_per_user;
}