auth.c 19 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      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
 */

Michael Smith's avatar
Michael Smith committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27
/** 
 * 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"
28
#include "auth_htpasswd.h"
Karl Heyes's avatar
Karl Heyes committed
29
#include "auth_url.h"
Philipp Schafft's avatar
Philipp Schafft committed
30 31
#include "auth_anonymous.h"
#include "auth_static.h"
Michael Smith's avatar
Michael Smith committed
32 33 34
#include "source.h"
#include "client.h"
#include "cfgfile.h"
35
#include "stats.h"
Marvin Scholz's avatar
Marvin Scholz committed
36
#include "common/httpp/httpp.h"
37
#include "fserve.h"
38
#include "admin.h"
Philipp Schafft's avatar
Philipp Schafft committed
39
#include "acl.h"
Michael Smith's avatar
Michael Smith committed
40 41 42

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

Philipp Schafft's avatar
Philipp Schafft committed
44 45 46 47 48 49 50
/* data structures */
struct auth_stack_tag {
    size_t refcount;
    auth_t *auth;
    mutex_t lock;
    auth_stack_t *next;
};
51

Philipp Schafft's avatar
Philipp Schafft committed
52
/* code */
53

54 55 56 57 58 59 60 61 62 63 64 65 66
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
67
static auth_client *auth_client_setup (client_t *client)
68
{
69
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
70
    auth_client *auth_user;
71

Philipp Schafft's avatar
Philipp Schafft committed
72 73 74 75 76 77 78 79 80 81 82
    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");

83 84 85
        if (header == NULL)
            break;

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
111
    auth_user = calloc(1, sizeof(auth_client));
112 113
    auth_user->client = client;
    return auth_user;
114 115
}

Michael Smith's avatar
Michael Smith committed
116

Philipp Schafft's avatar
Philipp Schafft committed
117
static void queue_auth_client (auth_client *auth_user)
118
{
119 120
    auth_t *auth;

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


140 141 142
/* 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
143
void auth_release (auth_t *authenticator) {
144 145
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
146

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
176 177 178 179 180 181 182 183 184 185 186
/* 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
187

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


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

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

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

213 214
    /* 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
215
    if (is_client_connected(client) == 0) {
216
        ICECAST_LOG_DEBUG("client is no longer connected");
217
        client->respcode = 400;
218 219
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
220
        return AUTH_FAILED;
221
    }
Philipp Schafft's avatar
Philipp Schafft committed
222 223 224 225

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

235

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

Philipp Schafft's avatar
Philipp Schafft committed
243 244
    if (client->auth->release_client)
        ret = client->auth->release_client(auth_user);
245

Philipp Schafft's avatar
Philipp Schafft committed
246
    auth_release(client->auth);
247
    client->auth = NULL;
248

Philipp Schafft's avatar
Philipp Schafft committed
249 250 251
    /* client is going, so auth is not an issue at this point */
    acl_release(client->acl);
    client->acl = NULL;
252

Philipp Schafft's avatar
Philipp Schafft committed
253
    return ret;
254
}
Michael Smith's avatar
Michael Smith committed
255 256


257 258 259
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
260
    auth_t *auth = arg;
Philipp Schafft's avatar
Philipp Schafft committed
261
    auth_result result;
262

263
    ICECAST_LOG_INFO("Authentication thread started");
264
    while (auth->running)
265
    {
266 267
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
268 269
        {
            auth_client *auth_user;
Michael Smith's avatar
Michael Smith committed
270

271 272 273 274 275 276 277 278
            /* 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;
            }
279
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
280 281 282 283
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
284
            thread_mutex_unlock(&auth->lock);
285
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
286

Philipp Schafft's avatar
Philipp Schafft committed
287 288 289
            if (auth_user->process) {
                result = auth_user->process(auth, auth_user);
            } else {
290
                ICECAST_LOG_ERROR("client auth process not set");
Philipp Schafft's avatar
Philipp Schafft committed
291
                result = AUTH_FAILED;
292 293
            }

Philipp Schafft's avatar
Philipp Schafft committed
294 295 296 297 298 299
            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);
Michael Smith's avatar
Michael Smith committed
300
            }
301

Philipp Schafft's avatar
Philipp Schafft committed
302 303 304 305
            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);
306 307
            }

Philipp Schafft's avatar
Philipp Schafft committed
308 309
            auth_client_free (auth_user);

310 311
            continue;
        }
Philipp Schafft's avatar
Philipp Schafft committed
312
        thread_sleep (150000);
Michael Smith's avatar
Michael Smith committed
313
    }
Philipp Schafft's avatar
Philipp Schafft committed
314 315
    ICECAST_LOG_INFO("Authenication thread shutting down");
    return NULL;
316
}
Michael Smith's avatar
Michael Smith committed
317 318


Philipp Schafft's avatar
Philipp Schafft committed
319
/* Add a client.
320
 */
Philipp Schafft's avatar
Philipp Schafft committed
321 322
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;
323

324 325
    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
326 327
    /* TODO: replace that magic number */
    if (auth->pending_count > 100) {
328
        ICECAST_LOG_WARN("too many clients awaiting authentication on auth %p", auth);
Philipp Schafft's avatar
Philipp Schafft committed
329 330
        client_send_error(client, 403, 1, "busy, please try again later");
        return;
331 332
    }

Philipp Schafft's avatar
Philipp Schafft committed
333 334 335 336 337
    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);
338
        }
339 340 341
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
342 343 344 345 346 347 348 349 350
    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);
351 352
}

353 354 355
/* 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
356 357 358
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
359 360


Philipp Schafft's avatar
Philipp Schafft committed
361 362 363 364 365 366 367 368 369 370 371 372
    /* 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;
373
    }
Philipp Schafft's avatar
Philipp Schafft committed
374 375 376

    acl_release(client->acl);
    client->acl = NULL;
377 378
    return 0;
}
379 380


Karl Heyes's avatar
Karl Heyes committed
381
static int get_authenticator (auth_t *auth, config_options_t *options)
382
{
383 384
    if (auth->type == NULL)
    {
385
        ICECAST_LOG_WARN("no authentication type defined");
386 387
        return -1;
    }
388 389
    do
    {
390
        ICECAST_LOG_DEBUG("type is %s", auth->type);
391

Philipp Schafft's avatar
Philipp Schafft committed
392
        if (strcmp(auth->type, AUTH_TYPE_URL) == 0) {
393
#ifdef HAVE_AUTH_URL
Philipp Schafft's avatar
Philipp Schafft committed
394
            if (auth_get_url_auth(auth, options) < 0)
Karl Heyes's avatar
Karl Heyes committed
395
                return -1;
396
            break;
397
#else
398
            ICECAST_LOG_ERROR("Auth URL disabled");
Karl Heyes's avatar
Karl Heyes committed
399
            return -1;
400
#endif
Philipp Schafft's avatar
Philipp Schafft committed
401 402 403 404 405 406 407 408 409 410 411 412 413 414
        } 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
415
                return -1;
416
            break;
417
        }
Karl Heyes's avatar
Karl Heyes committed
418

419
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
420
        return -1;
421 422
    } while (0);

Karl Heyes's avatar
Karl Heyes committed
423
    return 0;
424
}
425 426


427
auth_t *auth_get_authenticator(xmlNodePtr node)
428
{
429
    auth_t *auth = calloc(1, sizeof(auth_t));
430 431
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;
Philipp Schafft's avatar
Philipp Schafft committed
432 433
    char *method;
    size_t i;
434 435 436 437

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
438 439
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
440
    auth->id = _next_auth_id();
Philipp Schafft's avatar
Philipp Schafft committed
441 442 443 444 445 446 447 448 449 450 451 452 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
    auth->type = (char*)xmlGetProp(node, XMLSTR("type"));
    auth->role = (char*)xmlGetProp(node, XMLSTR("name"));

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

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

Philipp Schafft's avatar
Philipp Schafft committed
533
    while (options) {
534 535
        config_options_t *opt = options;
        options = opt->next;
Philipp Schafft's avatar
Philipp Schafft committed
536 537
        xmlFree(opt->name);
        xmlFree(opt->value);
538
        free (opt);
539
    }
Philipp Schafft's avatar
Philipp Schafft committed
540

541 542
    return auth;
}
543 544


Philipp Schafft's avatar
Philipp Schafft committed
545 546 547
/* these are called at server start and termination */

void auth_initialise (void)
548
{
549
    thread_mutex_create(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
550
}
551

Philipp Schafft's avatar
Philipp Schafft committed
552 553 554
void auth_shutdown (void)
{
    ICECAST_LOG_INFO("Auth shutdown");
555
    thread_mutex_destroy(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
556 557 558 559 560 561 562 563 564 565 566
}

/* 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);
567
    }
Philipp Schafft's avatar
Philipp Schafft committed
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 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 614 615 616 617 618 619
}

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);
    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;
    thread_mutex_unlock(&(*stack)->lock);
    auth_stack_release(*stack);
    auth_stack_addref(next);
    *stack = next;
    if (!next)
        return 1;
620 621 622
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
623 624
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
625

Philipp Schafft's avatar
Philipp Schafft committed
626 627
    if (!stack || !auth)
        return -1;
628

Philipp Schafft's avatar
Philipp Schafft committed
629 630 631 632 633 634 635 636 637 638 639 640 641 642
    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) {
        return auth_stack_append(*stack, next);
    } else {
        *stack = next;
        return 0;
643 644 645
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
646 647
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
648

Philipp Schafft's avatar
Philipp Schafft committed
649 650
    if (!stack)
        return -1;
651

Philipp Schafft's avatar
Philipp Schafft committed
652 653 654 655 656 657 658 659 660 661 662 663
    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);
664
    }
Philipp Schafft's avatar
Philipp Schafft committed
665 666 667 668 669 670

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

    return 0;
671
}
672

Philipp Schafft's avatar
Philipp Schafft committed
673 674
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
675

Philipp Schafft's avatar
Philipp Schafft committed
676 677
    if (!stack)
        return NULL;
678

Philipp Schafft's avatar
Philipp Schafft committed
679 680 681 682
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
683 684
}

Philipp Schafft's avatar
Philipp Schafft committed
685 686 687 688 689
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
690

Philipp Schafft's avatar
Philipp Schafft committed
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
    auth_stack_addref(stack);

    while (!ret && stack) {
        auth_t *auth = auth_stack_get(stack);
        if (strcmp(auth->type, AUTH_TYPE_ANONYMOUS) == 0) {
            acl_addref(ret = auth->acl);
        }
        auth_release(auth);

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

    if (stack)
        auth_stack_release(stack);

    return ret;
}