auth.c 18.6 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

Philipp Schafft's avatar
Philipp Schafft committed
54
static auth_client *auth_client_setup (client_t *client)
55
{
56
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
57
    auth_client *auth_user;
58

Philipp Schafft's avatar
Philipp Schafft committed
59 60 61 62 63 64 65 66 67 68 69
    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");

70 71 72
        if (header == NULL)
            break;

Philipp Schafft's avatar
Philipp Schafft committed
73
        if (strncmp(header, "Basic ", 6) == 0) {
74
            userpass = util_base64_decode (header+6);
Philipp Schafft's avatar
Philipp Schafft committed
75
            if (userpass == NULL) {
76
                ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
77 78 79 80 81
                        header+6);
                break;
            }

            tmp = strchr(userpass, ':');
Philipp Schafft's avatar
Philipp Schafft committed
82 83
            if (tmp == NULL) { 
                free(userpass);
84
                break;
85
            }
86 87 88 89

            *tmp = 0;
            username = userpass;
            password = tmp+1;
Philipp Schafft's avatar
Philipp Schafft committed
90 91
            client->username = strdup(username);
            client->password = strdup(password);
92 93
            free (userpass);
            break;
94
        }
95
        ICECAST_LOG_INFO("unhandled authorization header: %s", header);
96
    } while (0);
97

Philipp Schafft's avatar
Philipp Schafft committed
98
    auth_user = calloc(1, sizeof(auth_client));
99 100
    auth_user->client = client;
    return auth_user;
101 102
}

Michael Smith's avatar
Michael Smith committed
103

Philipp Schafft's avatar
Philipp Schafft committed
104
static void queue_auth_client (auth_client *auth_user)
105
{
106 107
    auth_t *auth;

108
    if (auth_user == NULL)
109 110
        return;
    auth_user->next = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
111
    if (auth_user->client == NULL || auth_user->client->auth == NULL)
112
    {
Philipp Schafft's avatar
Philipp Schafft committed
113 114
        ICECAST_LOG_WARN("internal state is incorrect for %p", auth_user->client);
        return;
115
    }
Philipp Schafft's avatar
Philipp Schafft committed
116 117 118
    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);
119 120 121
    *auth->tailp = auth_user;
    auth->tailp = &auth_user->next;
    auth->pending_count++;
122
    ICECAST_LOG_INFO("auth on %s has %d pending", auth->mount, auth->pending_count);
123
    thread_mutex_unlock (&auth->lock);
124
}
Michael Smith's avatar
Michael Smith committed
125 126


127 128 129
/* 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
130
void auth_release (auth_t *authenticator) {
131 132
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
133

Philipp Schafft's avatar
Philipp Schafft committed
134
    thread_mutex_lock(&authenticator->lock);
135
    authenticator->refcount--;
Philipp Schafft's avatar
Philipp Schafft committed
136
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, (int)authenticator->refcount);
137
    if (authenticator->refcount)
138
    {
Philipp Schafft's avatar
Philipp Schafft committed
139
        thread_mutex_unlock(&authenticator->lock);
140
        return;
141
    }
Michael Smith's avatar
Michael Smith committed
142

143
    /* cleanup auth thread attached to this auth */
Philipp Schafft's avatar
Philipp Schafft committed
144 145 146 147
    if (authenticator->running) {
        authenticator->running = 0;
        thread_join(authenticator->thread);
    }
148

149
    if (authenticator->free)
Philipp Schafft's avatar
Philipp Schafft committed
150 151 152 153 154 155 156
        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);
157
    if (authenticator->mount)
Philipp Schafft's avatar
Philipp Schafft committed
158 159 160
        free(authenticator->mount);
    acl_release(authenticator->acl);
    free(authenticator);
Michael Smith's avatar
Michael Smith committed
161 162
}

Philipp Schafft's avatar
Philipp Schafft committed
163 164 165 166 167 168 169 170 171 172 173
/* 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
174

175
static void auth_client_free (auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
176
{
177 178 179
    if (auth_user == NULL)
        return;
    free (auth_user);
Michael Smith's avatar
Michael Smith committed
180 181 182
}


183
/* verify that the client is still connected. */
184 185 186
static int is_client_connected (client_t *client) {
/* As long as sock_active() is broken we need to disable this:

187 188
    int ret = 1;
    if (client)
Philipp Schafft's avatar
Philipp Schafft committed
189
        if (sock_active(client->con->sock) == 0)
190 191
            ret = 0;
    return ret;
192 193
*/
    return 1;
194 195
}

Philipp Schafft's avatar
Philipp Schafft committed
196
static auth_result auth_new_client (auth_t *auth, auth_client *auth_user) {
197
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
198
    auth_result ret = AUTH_FAILED;
199

200 201
    /* 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
202
    if (is_client_connected(client) == 0) {
203
        ICECAST_LOG_DEBUG("client is no longer connected");
204
        client->respcode = 400;
205 206
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
207
        return AUTH_FAILED;
208
    }
Philipp Schafft's avatar
Philipp Schafft committed
209 210 211 212

    if (auth->authenticate_client) {
        ret = auth->authenticate_client(auth_user);
        if (ret != AUTH_OK)
213 214 215
        {
            auth_release (client->auth);
            client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
216
            return ret;
217
        }
Michael Smith's avatar
Michael Smith committed
218
    }
Philipp Schafft's avatar
Philipp Schafft committed
219
    return ret;
Michael Smith's avatar
Michael Smith committed
220 221
}

222

223
/* wrapper function for auth thread to drop client connections
224
 */
Philipp Schafft's avatar
Philipp Schafft committed
225
static auth_result auth_remove_client(auth_t *auth, auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
226
{
227
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
228
    auth_result ret = AUTH_RELEASED;
229

Philipp Schafft's avatar
Philipp Schafft committed
230 231
    if (client->auth->release_client)
        ret = client->auth->release_client(auth_user);
232

Philipp Schafft's avatar
Philipp Schafft committed
233
    auth_release(client->auth);
234
    client->auth = NULL;
235

Philipp Schafft's avatar
Philipp Schafft committed
236 237 238
    /* client is going, so auth is not an issue at this point */
    acl_release(client->acl);
    client->acl = NULL;
239

Philipp Schafft's avatar
Philipp Schafft committed
240
    return ret;
241
}
Michael Smith's avatar
Michael Smith committed
242 243


244 245 246
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
247
    auth_t *auth = arg;
Philipp Schafft's avatar
Philipp Schafft committed
248
    auth_result result;
249

250
    ICECAST_LOG_INFO("Authentication thread started");
251
    while (auth->running)
252
    {
253 254
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
255 256
        {
            auth_client *auth_user;
Michael Smith's avatar
Michael Smith committed
257

258 259 260 261 262 263 264 265
            /* 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;
            }
266
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
267 268 269 270
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
271
            thread_mutex_unlock(&auth->lock);
272
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
273

Philipp Schafft's avatar
Philipp Schafft committed
274 275 276
            if (auth_user->process) {
                result = auth_user->process(auth, auth_user);
            } else {
277
                ICECAST_LOG_ERROR("client auth process not set");
Philipp Schafft's avatar
Philipp Schafft committed
278
                result = AUTH_FAILED;
279 280
            }

Philipp Schafft's avatar
Philipp Schafft committed
281 282 283 284 285 286
            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
287
            }
288

Philipp Schafft's avatar
Philipp Schafft committed
289 290 291 292
            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);
293 294
            }

Philipp Schafft's avatar
Philipp Schafft committed
295 296
            auth_client_free (auth_user);

297 298
            continue;
        }
Philipp Schafft's avatar
Philipp Schafft committed
299
        thread_sleep (150000);
Michael Smith's avatar
Michael Smith committed
300
    }
Philipp Schafft's avatar
Philipp Schafft committed
301 302
    ICECAST_LOG_INFO("Authenication thread shutting down");
    return NULL;
303
}
Michael Smith's avatar
Michael Smith committed
304 305


Philipp Schafft's avatar
Philipp Schafft committed
306
/* Add a client.
307
 */
Philipp Schafft's avatar
Philipp Schafft committed
308 309
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;
310

311 312
    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
313 314
    /* TODO: replace that magic number */
    if (auth->pending_count > 100) {
315
        ICECAST_LOG_WARN("too many clients awaiting authentication on auth %p", auth);
Philipp Schafft's avatar
Philipp Schafft committed
316 317
        client_send_error(client, 403, 1, "busy, please try again later");
        return;
318 319
    }

Philipp Schafft's avatar
Philipp Schafft committed
320 321 322 323 324
    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);
325
        }
326 327 328
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
329 330 331 332 333 334 335 336 337
    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);
338 339
}

340 341 342
/* 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
343 344 345
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
346 347


Philipp Schafft's avatar
Philipp Schafft committed
348 349 350 351 352 353 354 355 356 357 358 359
    /* 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;
360
    }
Philipp Schafft's avatar
Philipp Schafft committed
361 362 363

    acl_release(client->acl);
    client->acl = NULL;
364 365
    return 0;
}
366 367


Karl Heyes's avatar
Karl Heyes committed
368
static int get_authenticator (auth_t *auth, config_options_t *options)
369
{
370 371
    if (auth->type == NULL)
    {
372
        ICECAST_LOG_WARN("no authentication type defined");
373 374
        return -1;
    }
375 376
    do
    {
377
        ICECAST_LOG_DEBUG("type is %s", auth->type);
378

Philipp Schafft's avatar
Philipp Schafft committed
379
        if (strcmp(auth->type, AUTH_TYPE_URL) == 0) {
380
#ifdef HAVE_AUTH_URL
Philipp Schafft's avatar
Philipp Schafft committed
381
            if (auth_get_url_auth(auth, options) < 0)
Karl Heyes's avatar
Karl Heyes committed
382
                return -1;
383
            break;
384
#else
385
            ICECAST_LOG_ERROR("Auth URL disabled");
Karl Heyes's avatar
Karl Heyes committed
386
            return -1;
387
#endif
Philipp Schafft's avatar
Philipp Schafft committed
388 389 390 391 392 393 394 395 396 397 398 399 400 401
        } 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
402
                return -1;
403
            break;
404
        }
Karl Heyes's avatar
Karl Heyes committed
405

406
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
407
        return -1;
408 409
    } while (0);

Karl Heyes's avatar
Karl Heyes committed
410
    return 0;
411
}
412 413


414
auth_t *auth_get_authenticator(xmlNodePtr node)
415
{
416
    auth_t *auth = calloc(1, sizeof(auth_t));
417 418
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;
Philipp Schafft's avatar
Philipp Schafft committed
419 420
    char *method;
    size_t i;
421 422 423 424

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 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
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
    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;
    }

475
    /* BEFORE RELEASE 2.4.2 TODO: Migrate this to config_parse_options(). */
476 477 478 479 480
    option = node->xmlChildrenNode;
    while (option)
    {
        xmlNodePtr current = option;
        option = option->next;
481
        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
482
        {
Philipp Schafft's avatar
Philipp Schafft committed
483 484
            config_options_t *opt = calloc(1, sizeof (config_options_t));
            opt->name = (char *)xmlGetProp(current, XMLSTR("name"));
485 486 487 488 489
            if (opt->name == NULL)
            {
                free(opt);
                continue;
            }
Philipp Schafft's avatar
Philipp Schafft committed
490
            opt->value = (char *)xmlGetProp(current, XMLSTR("value"));
491 492
            if (opt->value == NULL)
            {
Philipp Schafft's avatar
Philipp Schafft committed
493 494
                xmlFree(opt->name);
                free(opt);
495 496 497 498 499 500
                continue;
            }
            *next_option = opt;
            next_option = &opt->next;
        }
        else
501
            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
502
                ICECAST_LOG_WARN("unknown auth setting (%s)", current->name);
503
    }
Philipp Schafft's avatar
Philipp Schafft committed
504 505 506
    auth->acl  = acl_new_from_xml_node(node);
    if (!auth->acl) {
        auth_release(auth);
Karl Heyes's avatar
Karl Heyes committed
507
        auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
508 509 510 511 512 513 514 515 516
    } 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
517
    }
518

Philipp Schafft's avatar
Philipp Schafft committed
519
    while (options) {
520 521
        config_options_t *opt = options;
        options = opt->next;
Philipp Schafft's avatar
Philipp Schafft committed
522 523
        xmlFree(opt->name);
        xmlFree(opt->value);
524
        free (opt);
525
    }
Philipp Schafft's avatar
Philipp Schafft committed
526

527 528
    return auth;
}
529 530


Philipp Schafft's avatar
Philipp Schafft committed
531 532 533
/* these are called at server start and termination */

void auth_initialise (void)
534
{
Philipp Schafft's avatar
Philipp Schafft committed
535
}
536

Philipp Schafft's avatar
Philipp Schafft committed
537 538 539 540 541 542 543 544 545 546 547 548 549 550
void auth_shutdown (void)
{
    ICECAST_LOG_INFO("Auth shutdown");
}

/* 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);
551
    }
Philipp Schafft's avatar
Philipp Schafft committed
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 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
}

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;
604 605 606
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
607 608
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
609

Philipp Schafft's avatar
Philipp Schafft committed
610 611
    if (!stack || !auth)
        return -1;
612

Philipp Schafft's avatar
Philipp Schafft committed
613 614 615 616 617 618 619 620 621 622 623 624 625 626
    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;
627 628 629
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
630 631
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
632

Philipp Schafft's avatar
Philipp Schafft committed
633 634
    if (!stack)
        return -1;
635

Philipp Schafft's avatar
Philipp Schafft committed
636 637 638 639 640 641 642 643 644 645 646 647
    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);
648
    }
Philipp Schafft's avatar
Philipp Schafft committed
649 650 651 652 653 654

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

    return 0;
655
}
656

Philipp Schafft's avatar
Philipp Schafft committed
657 658
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
659

Philipp Schafft's avatar
Philipp Schafft committed
660 661
    if (!stack)
        return NULL;
662

Philipp Schafft's avatar
Philipp Schafft committed
663 664 665 666
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
667 668
}

Philipp Schafft's avatar
Philipp Schafft committed
669 670 671 672 673
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
674

Philipp Schafft's avatar
Philipp Schafft committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    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;
}