auth.c 18.3 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"
Michael Smith's avatar
Michael Smith committed
36
#include "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 listener is still connected. */
Philipp Schafft's avatar
Philipp Schafft committed
184
static int is_client_connected (client_t *client)
185 186 187
{
    int ret = 1;
    if (client)
Philipp Schafft's avatar
Philipp Schafft committed
188
        if (sock_active(client->con->sock) == 0)
189 190 191 192
            ret = 0;
    return ret;
}

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

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

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

219

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

Philipp Schafft's avatar
Philipp Schafft committed
227 228
    if (client->auth->release_client)
        ret = client->auth->release_client(auth_user);
229

Philipp Schafft's avatar
Philipp Schafft committed
230
    auth_release(client->auth);
231
    client->auth = NULL;
232

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

Philipp Schafft's avatar
Philipp Schafft committed
237
    return ret;
238
}
Michael Smith's avatar
Michael Smith committed
239 240


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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
278 279 280 281 282 283
            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
284
            }
285

Philipp Schafft's avatar
Philipp Schafft committed
286 287 288 289
            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);
290 291
            }

Philipp Schafft's avatar
Philipp Schafft committed
292 293
            auth_client_free (auth_user);

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


Philipp Schafft's avatar
Philipp Schafft committed
303
/* Add a client.
304
 */
Philipp Schafft's avatar
Philipp Schafft committed
305 306
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;
307

Philipp Schafft's avatar
Philipp Schafft committed
308 309 310 311 312
    /* TODO: replace that magic number */
    if (auth->pending_count > 100) {
        ICECAST_LOG_WARN("too many clients awaiting authentication");
        client_send_error(client, 403, 1, "busy, please try again later");
        return;
313 314
    }

Philipp Schafft's avatar
Philipp Schafft committed
315 316 317 318 319
    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);
320
        }
321 322 323
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
324 325 326 327 328 329 330 331 332
    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);
333 334
}

335 336 337
/* 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
338 339 340
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
341 342


Philipp Schafft's avatar
Philipp Schafft committed
343 344 345 346 347 348 349 350 351 352 353 354
    /* 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;
355
    }
Philipp Schafft's avatar
Philipp Schafft committed
356 357 358

    acl_release(client->acl);
    client->acl = NULL;
359 360
    return 0;
}
361 362


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

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

401
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
402
        return -1;
403 404
    } while (0);

Karl Heyes's avatar
Karl Heyes committed
405
    return 0;
406
}
407 408


409 410 411 412 413
auth_t *auth_get_authenticator (xmlNodePtr node)
{
    auth_t *auth = calloc (1, sizeof (auth_t));
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;
Philipp Schafft's avatar
Philipp Schafft committed
414 415
    char *method;
    size_t i;
416 417 418 419

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
420 421 422 423 424 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
    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;
    }

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

Philipp Schafft's avatar
Philipp Schafft committed
513
    while (options) {
514 515
        config_options_t *opt = options;
        options = opt->next;
Philipp Schafft's avatar
Philipp Schafft committed
516 517
        xmlFree(opt->name);
        xmlFree(opt->value);
518
        free (opt);
519
    }
Philipp Schafft's avatar
Philipp Schafft committed
520

521 522
    return auth;
}
523 524


Philipp Schafft's avatar
Philipp Schafft committed
525 526 527
/* these are called at server start and termination */

void auth_initialise (void)
528
{
Philipp Schafft's avatar
Philipp Schafft committed
529
}
530

Philipp Schafft's avatar
Philipp Schafft committed
531 532 533 534 535 536 537 538 539 540 541 542 543 544
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);
545
    }
Philipp Schafft's avatar
Philipp Schafft committed
546 547 548 549 550 551 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
}

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;
598 599 600
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
601 602
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
603

Philipp Schafft's avatar
Philipp Schafft committed
604 605
    if (!stack || !auth)
        return -1;
606

Philipp Schafft's avatar
Philipp Schafft committed
607 608 609 610 611 612 613 614 615 616 617 618 619 620
    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;
621 622 623
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
624 625
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
626

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

Philipp Schafft's avatar
Philipp Schafft committed
630 631 632 633 634 635 636 637 638 639 640 641
    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);
642
    }
Philipp Schafft's avatar
Philipp Schafft committed
643 644 645 646 647 648

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

    return 0;
649
}
650

Philipp Schafft's avatar
Philipp Schafft committed
651 652
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
653

Philipp Schafft's avatar
Philipp Schafft committed
654 655
    if (!stack)
        return NULL;
656

Philipp Schafft's avatar
Philipp Schafft committed
657 658 659 660
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
661 662
}

Philipp Schafft's avatar
Philipp Schafft committed
663 664 665 666 667
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
668

Philipp Schafft's avatar
Philipp Schafft committed
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    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;
}