auth.c 20.2 KB
Newer Older
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 2000-2004, Jack Moffitt <jack@xiph.org,
7 8 9 10
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
11
 * Copyright 2013-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12 13
 */

14
/**
Michael Smith's avatar
Michael Smith committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * Client authentication functions
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#include "auth.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
31
#include "stats.h"
Marvin Scholz's avatar
Marvin Scholz committed
32
#include "common/httpp/httpp.h"
33
#include "fserve.h"
34
#include "admin.h"
Philipp Schafft's avatar
Philipp Schafft committed
35
#include "acl.h"
Michael Smith's avatar
Michael Smith committed
36 37 38

#include "logging.h"
#define CATMODULE "auth"
39

Philipp Schafft's avatar
Philipp Schafft committed
40 41 42 43 44 45 46
/* data structures */
struct auth_stack_tag {
    size_t refcount;
    auth_t *auth;
    mutex_t lock;
    auth_stack_t *next;
};
47

Philipp Schafft's avatar
Philipp Schafft committed
48
/* code */
Marvin Scholz's avatar
Marvin Scholz committed
49
static void __handle_auth_client(auth_t *auth, auth_client *auth_user);
50

51 52 53 54 55 56 57 58 59 60 61 62 63
static mutex_t _auth_lock; /* protects _current_id */
static volatile unsigned long _current_id = 0;

static unsigned long _next_auth_id(void) {
    unsigned long id;

    thread_mutex_lock(&_auth_lock);
    id = _current_id++;
    thread_mutex_unlock(&_auth_lock);

    return id;
}

Philipp Schafft's avatar
Philipp Schafft committed
64
static auth_client *auth_client_setup (client_t *client)
65
{
66
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
67
    auth_client *auth_user;
68

Philipp Schafft's avatar
Philipp Schafft committed
69 70 71 72 73 74 75 76 77 78 79
    do {
        const char *header;
        char *userpass, *tmp;
        char *username, *password;

        /* check if we already have auth infos */
        if (client->username || client->password)
            break;

        header = httpp_getvar(client->parser, "authorization");

80 81 82
        if (header == NULL)
            break;

Philipp Schafft's avatar
Philipp Schafft committed
83
        if (strncmp(header, "Basic ", 6) == 0) {
84
            userpass = util_base64_decode (header+6);
Philipp Schafft's avatar
Philipp Schafft committed
85
            if (userpass == NULL) {
86
                ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
87 88 89 90 91
                        header+6);
                break;
            }

            tmp = strchr(userpass, ':');
92
            if (tmp == NULL) {
Philipp Schafft's avatar
Philipp Schafft committed
93
                free(userpass);
94
                break;
95
            }
96 97 98 99

            *tmp = 0;
            username = userpass;
            password = tmp+1;
Philipp Schafft's avatar
Philipp Schafft committed
100 101
            client->username = strdup(username);
            client->password = strdup(password);
102 103
            free (userpass);
            break;
104
        }
105
        ICECAST_LOG_INFO("unhandled authorization header: %s", header);
106
    } while (0);
107

Philipp Schafft's avatar
Philipp Schafft committed
108
    auth_user = calloc(1, sizeof(auth_client));
109 110
    auth_user->client = client;
    return auth_user;
111 112
}

Michael Smith's avatar
Michael Smith committed
113

Philipp Schafft's avatar
Philipp Schafft committed
114
static void queue_auth_client (auth_client *auth_user)
115
{
116 117
    auth_t *auth;

118
    if (auth_user == NULL)
119 120
        return;
    auth_user->next = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
121
    if (auth_user->client == NULL || auth_user->client->auth == NULL)
122
    {
Philipp Schafft's avatar
Philipp Schafft committed
123 124
        ICECAST_LOG_WARN("internal state is incorrect for %p", auth_user->client);
        return;
125
    }
Philipp Schafft's avatar
Philipp Schafft committed
126 127
    auth = auth_user->client->auth;
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", auth->mount, (int)auth->refcount);
128 129 130 131 132 133 134 135 136 137
    if (auth->immediate) {
        __handle_auth_client(auth, auth_user);
    } else {
        thread_mutex_lock (&auth->lock);
        *auth->tailp = auth_user;
        auth->tailp = &auth_user->next;
        auth->pending_count++;
        ICECAST_LOG_INFO("auth on %s has %d pending", auth->mount, auth->pending_count);
        thread_mutex_unlock (&auth->lock);
    }
138
}
Michael Smith's avatar
Michael Smith committed
139 140


141 142 143
/* release the auth. It is referred to by multiple structures so this is
 * refcounted and only actual freed after the last use
 */
Philipp Schafft's avatar
Philipp Schafft committed
144
void auth_release (auth_t *authenticator) {
145 146
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
147

Philipp Schafft's avatar
Philipp Schafft committed
148
    thread_mutex_lock(&authenticator->lock);
149
    authenticator->refcount--;
Philipp Schafft's avatar
Philipp Schafft committed
150
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, (int)authenticator->refcount);
151
    if (authenticator->refcount)
152
    {
Philipp Schafft's avatar
Philipp Schafft committed
153
        thread_mutex_unlock(&authenticator->lock);
154
        return;
155
    }
Michael Smith's avatar
Michael Smith committed
156

157
    /* cleanup auth thread attached to this auth */
Philipp Schafft's avatar
Philipp Schafft committed
158 159 160 161
    if (authenticator->running) {
        authenticator->running = 0;
        thread_join(authenticator->thread);
    }
162

163
    if (authenticator->free)
Philipp Schafft's avatar
Philipp Schafft committed
164 165 166 167 168
        authenticator->free(authenticator);
    if (authenticator->type)
        xmlFree (authenticator->type);
    if (authenticator->role)
        xmlFree (authenticator->role);
169 170
    if (authenticator->management_url)
        xmlFree (authenticator->management_url);
Philipp Schafft's avatar
Philipp Schafft committed
171 172
    thread_mutex_unlock(&authenticator->lock);
    thread_mutex_destroy(&authenticator->lock);
173
    if (authenticator->mount)
Philipp Schafft's avatar
Philipp Schafft committed
174 175 176
        free(authenticator->mount);
    acl_release(authenticator->acl);
    free(authenticator);
Michael Smith's avatar
Michael Smith committed
177 178
}

Philipp Schafft's avatar
Philipp Schafft committed
179 180 181 182 183 184 185 186 187 188 189
/* increment refcount on the auth.
 */
void    auth_addref (auth_t *authenticator) {
    if (authenticator == NULL)
        return;

    thread_mutex_lock (&authenticator->lock);
    authenticator->refcount++;
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, (int)authenticator->refcount);
    thread_mutex_unlock (&authenticator->lock);
}
Michael Smith's avatar
Michael Smith committed
190

191
static void auth_client_free (auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
192
{
193 194 195
    if (auth_user == NULL)
        return;
    free (auth_user);
Michael Smith's avatar
Michael Smith committed
196 197 198
}


199
/* verify that the client is still connected. */
200 201 202
static int is_client_connected (client_t *client) {
/* As long as sock_active() is broken we need to disable this:

203 204
    int ret = 1;
    if (client)
Philipp Schafft's avatar
Philipp Schafft committed
205
        if (sock_active(client->con->sock) == 0)
206 207
            ret = 0;
    return ret;
208 209
*/
    return 1;
210 211
}

Philipp Schafft's avatar
Philipp Schafft committed
212
static auth_result auth_new_client (auth_t *auth, auth_client *auth_user) {
213
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
214
    auth_result ret = AUTH_FAILED;
215

216 217
    /* make sure there is still a client at this point, a slow backend request
     * can be avoided if client has disconnected */
Philipp Schafft's avatar
Philipp Schafft committed
218
    if (is_client_connected(client) == 0) {
219
        ICECAST_LOG_DEBUG("client is no longer connected");
220
        client->respcode = 400;
221 222
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
223
        return AUTH_FAILED;
224
    }
Philipp Schafft's avatar
Philipp Schafft committed
225 226 227 228

    if (auth->authenticate_client) {
        ret = auth->authenticate_client(auth_user);
        if (ret != AUTH_OK)
229 230 231
        {
            auth_release (client->auth);
            client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
232
            return ret;
233
        }
Michael Smith's avatar
Michael Smith committed
234
    }
Philipp Schafft's avatar
Philipp Schafft committed
235
    return ret;
Michael Smith's avatar
Michael Smith committed
236 237
}

238

239
/* wrapper function for auth thread to drop client connections
240
 */
Philipp Schafft's avatar
Philipp Schafft committed
241
static auth_result auth_remove_client(auth_t *auth, auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
242
{
243
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
244
    auth_result ret = AUTH_RELEASED;
245

246 247
    (void)auth;

Philipp Schafft's avatar
Philipp Schafft committed
248 249
    if (client->auth->release_client)
        ret = client->auth->release_client(auth_user);
250

Philipp Schafft's avatar
Philipp Schafft committed
251
    auth_release(client->auth);
252
    client->auth = NULL;
253

Philipp Schafft's avatar
Philipp Schafft committed
254 255 256
    /* client is going, so auth is not an issue at this point */
    acl_release(client->acl);
    client->acl = NULL;
257

Philipp Schafft's avatar
Philipp Schafft committed
258
    return ret;
259
}
Michael Smith's avatar
Michael Smith committed
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
static void __handle_auth_client (auth_t *auth, auth_client *auth_user) {
    auth_result result;

    if (auth_user->process) {
        result = auth_user->process(auth, auth_user);
    } else {
        ICECAST_LOG_ERROR("client auth process not set");
        result = AUTH_FAILED;
    }

    if (result == AUTH_OK) {
        if (auth_user->client->acl)
            acl_release(auth_user->client->acl);
        acl_addref(auth_user->client->acl = auth->acl);
        if (auth->role) /* TODO: Handle errors here */
            auth_user->client->role = strdup(auth->role);
    }

    if (result == AUTH_NOMATCH && auth_user->on_no_match) {
        auth_user->on_no_match(auth_user->client, auth_user->on_result, auth_user->userdata);
    } else if (auth_user->on_result) {
        auth_user->on_result(auth_user->client, auth_user->userdata, result);
    }

    auth_client_free (auth_user);
}
Michael Smith's avatar
Michael Smith committed
287

288 289 290
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
291 292
    auth_t *auth = arg;

293
    ICECAST_LOG_INFO("Authentication thread started");
294
    while (auth->running)
295
    {
296 297
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
298 299
        {
            auth_client *auth_user;
Michael Smith's avatar
Michael Smith committed
300

301 302 303 304 305 306 307 308
            /* may become NULL before lock taken */
            thread_mutex_lock (&auth->lock);
            auth_user = (auth_client*)auth->head;
            if (auth_user == NULL)
            {
                thread_mutex_unlock (&auth->lock);
                continue;
            }
309
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
310 311 312 313
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
314
            thread_mutex_unlock(&auth->lock);
315
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
316

317
            __handle_auth_client(auth, auth_user);
Philipp Schafft's avatar
Philipp Schafft committed
318

319 320
            continue;
        }
Philipp Schafft's avatar
Philipp Schafft committed
321
        thread_sleep (150000);
Michael Smith's avatar
Michael Smith committed
322
    }
323
    ICECAST_LOG_INFO("Authentication thread shutting down");
Philipp Schafft's avatar
Philipp Schafft committed
324
    return NULL;
325
}
Michael Smith's avatar
Michael Smith committed
326 327


Philipp Schafft's avatar
Philipp Schafft committed
328
/* Add a client.
329
 */
Philipp Schafft's avatar
Philipp Schafft committed
330 331
static void auth_add_client(auth_t *auth, client_t *client, void (*on_no_match)(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata), void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_client *auth_user;
332

333 334
    ICECAST_LOG_DEBUG("Trying to add client %p to auth %p's (role %s) queue.", client, auth, auth->role);

Philipp Schafft's avatar
Philipp Schafft committed
335 336
    /* TODO: replace that magic number */
    if (auth->pending_count > 100) {
337
        ICECAST_LOG_WARN("too many clients awaiting authentication on auth %p", auth);
Philipp Schafft's avatar
Philipp Schafft committed
338 339
        client_send_error(client, 403, 1, "busy, please try again later");
        return;
340 341
    }

Philipp Schafft's avatar
Philipp Schafft committed
342 343 344 345 346
    if (!auth->method[client->parser->req_type]) {
        if (on_no_match) {
           on_no_match(client, on_result, userdata);
        } else if (on_result) {
           on_result(client, userdata, AUTH_NOMATCH);
347
        }
348 349 350
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
351 352 353 354 355 356 357 358 359
    auth_release(client->auth);
    auth_addref(client->auth = auth);
    auth_user = auth_client_setup(client);
    auth_user->process = auth_new_client;
    auth_user->on_no_match = on_no_match;
    auth_user->on_result = on_result;
    auth_user->userdata = userdata;
    ICECAST_LOG_INFO("adding client for authentication");
    queue_auth_client(auth_user);
360 361
}

362 363 364
/* determine whether we need to process this client further. This
 * involves any auth exit, typically for external auth servers.
 */
Philipp Schafft's avatar
Philipp Schafft committed
365 366 367
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
368 369


Philipp Schafft's avatar
Philipp Schafft committed
370 371 372 373 374 375 376 377 378 379 380 381
    /* drop any queue reference here, we do not want a race between the source thread
     * and the auth/fserve thread */
    client_set_queue (client, NULL);

    if (client->auth && client->auth->release_client) {
        auth_client *auth_user = auth_client_setup(client);
        auth_user->process = auth_remove_client;
        queue_auth_client(auth_user);
        return 1;
    } else if (client->auth) {
        auth_release(client->auth);
        client->auth = NULL;
382
    }
Philipp Schafft's avatar
Philipp Schafft committed
383 384 385

    acl_release(client->acl);
    client->acl = NULL;
386 387
    return 0;
}
388 389


Karl Heyes's avatar
Karl Heyes committed
390
static int get_authenticator (auth_t *auth, config_options_t *options)
391
{
392 393
    if (auth->type == NULL)
    {
394
        ICECAST_LOG_WARN("no authentication type defined");
395 396
        return -1;
    }
397 398
    do
    {
399
        ICECAST_LOG_DEBUG("type is %s", auth->type);
400

Philipp Schafft's avatar
Philipp Schafft committed
401
        if (strcmp(auth->type, AUTH_TYPE_URL) == 0) {
402
#ifdef HAVE_AUTH_URL
Philipp Schafft's avatar
Philipp Schafft committed
403
            if (auth_get_url_auth(auth, options) < 0)
Karl Heyes's avatar
Karl Heyes committed
404
                return -1;
405
            break;
406
#else
407
            ICECAST_LOG_ERROR("Auth URL disabled");
Karl Heyes's avatar
Karl Heyes committed
408
            return -1;
409
#endif
Philipp Schafft's avatar
Philipp Schafft committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423
        } else if (strcmp(auth->type, AUTH_TYPE_HTPASSWD) == 0) {
            if (auth_get_htpasswd_auth(auth, options) < 0)
                return -1;
            break;
        } else if (strcmp(auth->type, AUTH_TYPE_ANONYMOUS) == 0) {
            if (auth_get_anonymous_auth(auth, options) < 0)
                return -1;
            break;
        } else if (strcmp(auth->type, AUTH_TYPE_STATIC) == 0) {
            if (auth_get_static_auth(auth, options) < 0)
                return -1;
            break;
        } else if (strcmp(auth->type, AUTH_TYPE_LEGACY_PASSWORD) == 0) {
            if (auth_get_static_auth(auth, options) < 0)
Karl Heyes's avatar
Karl Heyes committed
424
                return -1;
425
            break;
426
        }
Karl Heyes's avatar
Karl Heyes committed
427

428
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
429
        return -1;
430 431
    } while (0);

Karl Heyes's avatar
Karl Heyes committed
432
    return 0;
433
}
434 435


436
auth_t *auth_get_authenticator(xmlNodePtr node)
437
{
438
    auth_t *auth = calloc(1, sizeof(auth_t));
439 440
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;
Philipp Schafft's avatar
Philipp Schafft committed
441 442
    char *method;
    size_t i;
443 444 445 446

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
447 448
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
449
    auth->id = _next_auth_id();
Philipp Schafft's avatar
Philipp Schafft committed
450 451
    auth->type = (char*)xmlGetProp(node, XMLSTR("type"));
    auth->role = (char*)xmlGetProp(node, XMLSTR("name"));
452
    auth->management_url = (char*)xmlGetProp(node, XMLSTR("management-url"));
Philipp Schafft's avatar
Philipp Schafft committed
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498

    if (!auth->type) {
        auth_release(auth);
        return NULL;
    }

    method = (char*)xmlGetProp(node, XMLSTR("method"));
    if (method) {
        char *cur = method;
        char *next;

        for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
            auth->method[i] = 0;

        while (cur) {
            httpp_request_type_e idx;

            next = strstr(cur, ",");
            if (next) {
                *next = 0;
                next++;
                for (; *next == ' '; next++);
            }

            if (strcmp(cur, "*") == 0) {
                for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
                    auth->method[i] = 1;
                break;
            }

            idx = httpp_str_to_method(cur);
            if (idx == httpp_req_unknown) {
                auth_release(auth);
                return NULL;
            }
            auth->method[idx] = 1;

            cur = next;
        }

        xmlFree(method);
    } else {
        for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
            auth->method[i] = 1;
    }

499
    /* BEFORE RELEASE 2.5.0 TODO: Migrate this to config_parse_options(). */
500 501 502 503 504
    option = node->xmlChildrenNode;
    while (option)
    {
        xmlNodePtr current = option;
        option = option->next;
505
        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
506
        {
Philipp Schafft's avatar
Philipp Schafft committed
507 508
            config_options_t *opt = calloc(1, sizeof (config_options_t));
            opt->name = (char *)xmlGetProp(current, XMLSTR("name"));
509 510 511 512 513
            if (opt->name == NULL)
            {
                free(opt);
                continue;
            }
Philipp Schafft's avatar
Philipp Schafft committed
514
            opt->value = (char *)xmlGetProp(current, XMLSTR("value"));
515 516
            if (opt->value == NULL)
            {
Philipp Schafft's avatar
Philipp Schafft committed
517 518
                xmlFree(opt->name);
                free(opt);
519 520 521 522 523 524
                continue;
            }
            *next_option = opt;
            next_option = &opt->next;
        }
        else
525
            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
526
                ICECAST_LOG_WARN("unknown auth setting (%s)", current->name);
527
    }
Philipp Schafft's avatar
Philipp Schafft committed
528 529 530
    auth->acl  = acl_new_from_xml_node(node);
    if (!auth->acl) {
        auth_release(auth);
Karl Heyes's avatar
Karl Heyes committed
531
        auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
532 533 534 535 536 537
    } else {
        if (get_authenticator (auth, options) < 0) {
            auth_release(auth);
            auth = NULL;
        } else {
            auth->tailp = &auth->head;
538 539 540 541
            if (!auth->immediate) {
                auth->running = 1;
                auth->thread = thread_create("auth thread", auth_run_thread, auth, THREAD_ATTACHED);
            }
Philipp Schafft's avatar
Philipp Schafft committed
542
        }
Karl Heyes's avatar
Karl Heyes committed
543
    }
544

Philipp Schafft's avatar
Philipp Schafft committed
545
    while (options) {
546 547
        config_options_t *opt = options;
        options = opt->next;
Philipp Schafft's avatar
Philipp Schafft committed
548 549
        xmlFree(opt->name);
        xmlFree(opt->value);
550
        free (opt);
551
    }
Philipp Schafft's avatar
Philipp Schafft committed
552

553
    if (auth && !auth->management_url && (auth->adduser || auth->deleteuser || auth->listuser)) {
554 555 556 557 558
        char url[128];
        snprintf(url, sizeof(url), "/admin/manageauth.xsl?id=%lu", auth->id);
        auth->management_url = (char*)xmlCharStrdup(url);
    }

559 560
    return auth;
}
561 562


Philipp Schafft's avatar
Philipp Schafft committed
563 564 565
/* these are called at server start and termination */

void auth_initialise (void)
566
{
567
    thread_mutex_create(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
568
}
569

Philipp Schafft's avatar
Philipp Schafft committed
570 571 572
void auth_shutdown (void)
{
    ICECAST_LOG_INFO("Auth shutdown");
573
    thread_mutex_destroy(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
574 575 576 577 578 579 580 581 582 583 584
}

/* authstack functions */

static void __move_client_forward_in_auth_stack(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_stack_next(&client->authstack);
    if (client->authstack) {
        auth_stack_add_client(client->authstack, client, on_result, userdata);
    } else {
        if (on_result)
            on_result(client, userdata, AUTH_NOMATCH);
585
    }
Philipp Schafft's avatar
Philipp Schafft committed
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
}

void          auth_stack_add_client(auth_stack_t *stack, client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_t *auth;

    if (!stack || !client || (client->authstack && client->authstack != stack))
        return;

    if (!client->authstack)
        auth_stack_addref(stack);
    client->authstack = stack;
    auth = auth_stack_get(stack);
    auth_add_client(auth, client, __move_client_forward_in_auth_stack, on_result, userdata);
    auth_release(auth);
}

void          auth_stack_release(auth_stack_t *stack) {
    if (!stack)
        return;

    thread_mutex_lock(&stack->lock);
    stack->refcount--;
    thread_mutex_unlock(&stack->lock);

    if (stack->refcount)
        return;

    auth_release(stack->auth);
614
    auth_stack_release(stack->next);
Philipp Schafft's avatar
Philipp Schafft committed
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
    thread_mutex_destroy(&stack->lock);
    free(stack);
}

void          auth_stack_addref(auth_stack_t *stack) {
    if (!stack)
        return;
    thread_mutex_lock(&stack->lock);
    stack->refcount++;
    thread_mutex_unlock(&stack->lock);
}

int           auth_stack_next(auth_stack_t **stack) {
    auth_stack_t *next;
    if (!stack || !*stack)
        return -1;
    thread_mutex_lock(&(*stack)->lock);
    next = (*stack)->next;
633
    auth_stack_addref(next);
Philipp Schafft's avatar
Philipp Schafft committed
634 635 636 637 638
    thread_mutex_unlock(&(*stack)->lock);
    auth_stack_release(*stack);
    *stack = next;
    if (!next)
        return 1;
639 640 641
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
642 643
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
644

Philipp Schafft's avatar
Philipp Schafft committed
645 646
    if (!stack || !auth)
        return -1;
647

Philipp Schafft's avatar
Philipp Schafft committed
648 649 650 651 652 653 654 655 656 657
    next = calloc(1, sizeof(*next));
    if (!next) {
        return -1;
    }
    thread_mutex_create(&next->lock);
    next->refcount = 1;
    next->auth = auth;
    auth_addref(auth);

    if (*stack) {
658 659 660
        auth_stack_append(*stack, next);
        auth_stack_release(next);
        return 0;
Philipp Schafft's avatar
Philipp Schafft committed
661 662 663
    } else {
        *stack = next;
        return 0;
664 665 666
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
667 668
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
669

Philipp Schafft's avatar
Philipp Schafft committed
670 671
    if (!stack)
        return -1;
672

Philipp Schafft's avatar
Philipp Schafft committed
673 674 675 676 677 678 679 680 681 682 683 684
    auth_stack_addref(cur = stack);
    thread_mutex_lock(&cur->lock);
    while (1) {
        next = cur->next;
        if (!cur->next)
            break;

        auth_stack_addref(next);
        thread_mutex_unlock(&cur->lock);
        auth_stack_release(cur);
        cur = next;
        thread_mutex_lock(&cur->lock);
685
    }
Philipp Schafft's avatar
Philipp Schafft committed
686 687 688 689 690 691

    auth_stack_addref(cur->next = tail);
    thread_mutex_unlock(&cur->lock);
    auth_stack_release(cur);

    return 0;
692
}
693

Philipp Schafft's avatar
Philipp Schafft committed
694 695
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
696

Philipp Schafft's avatar
Philipp Schafft committed
697 698
    if (!stack)
        return NULL;
699

Philipp Schafft's avatar
Philipp Schafft committed
700 701 702 703
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
704 705
}

706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
auth_t       *auth_stack_getbyid(auth_stack_t *stack, unsigned long id) {
    auth_t *ret = NULL;

    if (!stack)
        return NULL;

    auth_stack_addref(stack);

    while (!ret && stack) {
        auth_t *auth = auth_stack_get(stack);
        if (auth->id == id) {
            ret = auth;
            break;
        }
        auth_release(auth);
        auth_stack_next(&stack);
    }

    if (stack)
        auth_stack_release(stack);

    return ret;

}

731
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack, httpp_request_type_e method) {
Philipp Schafft's avatar
Philipp Schafft committed
732 733
    acl_t *ret = NULL;

734
    if (!stack || method < 0 || method > httpp_req_unknown)
Philipp Schafft's avatar
Philipp Schafft committed
735
        return NULL;
736

Philipp Schafft's avatar
Philipp Schafft committed
737 738 739 740
    auth_stack_addref(stack);

    while (!ret && stack) {
        auth_t *auth = auth_stack_get(stack);
741
        if (auth->method[method] && strcmp(auth->type, AUTH_TYPE_ANONYMOUS) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
742 743 744 745 746 747 748 749 750 751 752 753 754
            acl_addref(ret = auth->acl);
        }
        auth_release(auth);

        if (!ret)
            auth_stack_next(&stack);
    }

    if (stack)
        auth_stack_release(stack);

    return ret;
}