auth.c 19.4 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
        authenticator->free(authenticator);
    if (authenticator->type)
        xmlFree (authenticator->type);
    if (authenticator->role)
        xmlFree (authenticator->role);
168 169
    if (authenticator->management_url)
        xmlFree (authenticator->management_url);
Philipp Schafft's avatar
Philipp Schafft committed
170 171
    thread_mutex_unlock(&authenticator->lock);
    thread_mutex_destroy(&authenticator->lock);
172
    if (authenticator->mount)
Philipp Schafft's avatar
Philipp Schafft committed
173 174 175
        free(authenticator->mount);
    acl_release(authenticator->acl);
    free(authenticator);
Michael Smith's avatar
Michael Smith committed
176 177
}

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

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


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

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

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

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

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

237

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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
255
    return ret;
256
}
Michael Smith's avatar
Michael Smith committed
257 258


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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
296 297 298 299 300 301
            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
302
            }
303

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

Philipp Schafft's avatar
Philipp Schafft committed
310 311
            auth_client_free (auth_user);

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


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

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

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

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

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


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

    acl_release(client->acl);
    client->acl = NULL;
379 380
    return 0;
}
381 382


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

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

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

Karl Heyes's avatar
Karl Heyes committed
425
    return 0;
426
}
427 428


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

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
440 441
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
442
    auth->id = _next_auth_id();
Philipp Schafft's avatar
Philipp Schafft committed
443 444
    auth->type = (char*)xmlGetProp(node, XMLSTR("type"));
    auth->role = (char*)xmlGetProp(node, XMLSTR("name"));
445
    auth->management_url = (char*)xmlGetProp(node, XMLSTR("management-url"));
Philipp Schafft's avatar
Philipp Schafft committed
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 489 490 491

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

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

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

544 545 546 547 548 549
    if (!auth->management_url && (auth->adduser || auth->deleteuser || auth->listuser)) {
        char url[128];
        snprintf(url, sizeof(url), "/admin/manageauth.xsl?id=%lu", auth->id);
        auth->management_url = (char*)xmlCharStrdup(url);
    }

550 551
    return auth;
}
552 553


Philipp Schafft's avatar
Philipp Schafft committed
554 555 556
/* these are called at server start and termination */

void auth_initialise (void)
557
{
558
    thread_mutex_create(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
559
}
560

Philipp Schafft's avatar
Philipp Schafft committed
561 562 563
void auth_shutdown (void)
{
    ICECAST_LOG_INFO("Auth shutdown");
564
    thread_mutex_destroy(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
565 566 567 568 569 570 571 572 573 574 575
}

/* 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);
576
    }
Philipp Schafft's avatar
Philipp Schafft committed
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 620 621 622 623 624 625 626 627 628
}

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;
629 630 631
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
632 633
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
634

Philipp Schafft's avatar
Philipp Schafft committed
635 636
    if (!stack || !auth)
        return -1;
637

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

Philipp Schafft's avatar
Philipp Schafft committed
655 656
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
657

Philipp Schafft's avatar
Philipp Schafft committed
658 659
    if (!stack)
        return -1;
660

Philipp Schafft's avatar
Philipp Schafft committed
661 662 663 664 665 666 667 668 669 670 671 672
    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);
673
    }
Philipp Schafft's avatar
Philipp Schafft committed
674 675 676 677 678 679

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

    return 0;
680
}
681

Philipp Schafft's avatar
Philipp Schafft committed
682 683
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
684

Philipp Schafft's avatar
Philipp Schafft committed
685 686
    if (!stack)
        return NULL;
687

Philipp Schafft's avatar
Philipp Schafft committed
688 689 690 691
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
692 693
}

Philipp Schafft's avatar
Philipp Schafft committed
694 695 696 697 698
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
699

Philipp Schafft's avatar
Philipp Schafft committed
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
    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;
}