auth.c 20.1 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
static void __handle_auth_client (auth_t *auth, auth_client *auth_user);
54

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

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

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

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

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

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

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

Michael Smith's avatar
Michael Smith committed
117

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

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


145 146 147
/* 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
148
void auth_release (auth_t *authenticator) {
149 150
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
151

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

161
    /* cleanup auth thread attached to this auth */
Philipp Schafft's avatar
Philipp Schafft committed
162 163 164 165
    if (authenticator->running) {
        authenticator->running = 0;
        thread_join(authenticator->thread);
    }
166

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

Philipp Schafft's avatar
Philipp Schafft committed
183 184 185 186 187 188 189 190 191 192 193
/* 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
194

195
static void auth_client_free (auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
196
{
197 198 199
    if (auth_user == NULL)
        return;
    free (auth_user);
Michael Smith's avatar
Michael Smith committed
200 201 202
}


203
/* verify that the client is still connected. */
204 205 206
static int is_client_connected (client_t *client) {
/* As long as sock_active() is broken we need to disable this:

207 208
    int ret = 1;
    if (client)
Philipp Schafft's avatar
Philipp Schafft committed
209
        if (sock_active(client->con->sock) == 0)
210 211
            ret = 0;
    return ret;
212 213
*/
    return 1;
214 215
}

Philipp Schafft's avatar
Philipp Schafft committed
216
static auth_result auth_new_client (auth_t *auth, auth_client *auth_user) {
217
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
218
    auth_result ret = AUTH_FAILED;
219

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

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

242

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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
260
    return ret;
261
}
Michael Smith's avatar
Michael Smith committed
262

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

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

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

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

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

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

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

303 304 305 306 307 308 309 310
            /* 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;
            }
311
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
312 313 314 315
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
316
            thread_mutex_unlock(&auth->lock);
317
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
318

319
            __handle_auth_client(auth, auth_user);
Philipp Schafft's avatar
Philipp Schafft committed
320

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


Philipp Schafft's avatar
Philipp Schafft committed
330
/* Add a client.
331
 */
Philipp Schafft's avatar
Philipp Schafft committed
332 333
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;
334

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

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

Philipp Schafft's avatar
Philipp Schafft committed
353 354 355 356 357 358 359 360 361
    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);
362 363
}

364 365 366
/* 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
367 368 369
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
370 371


Philipp Schafft's avatar
Philipp Schafft committed
372 373 374 375 376 377 378 379 380 381 382 383
    /* 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;
384
    }
Philipp Schafft's avatar
Philipp Schafft committed
385 386 387

    acl_release(client->acl);
    client->acl = NULL;
388 389
    return 0;
}
390 391


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

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

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

Karl Heyes's avatar
Karl Heyes committed
434
    return 0;
435
}
436 437


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

    if (auth == NULL)
        return NULL;

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

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

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

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

555 556 557 558 559 560
    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);
    }

561 562
    return auth;
}
563 564


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

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

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

/* 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);
587
    }
Philipp Schafft's avatar
Philipp Schafft committed
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 629 630 631 632 633 634 635 636 637 638 639
}

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;
640 641 642
    return 0;
}

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

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

Philipp Schafft's avatar
Philipp Schafft committed
649 650 651 652 653 654 655 656 657 658 659 660 661 662
    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;
663 664 665
    }
}

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

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

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

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

    return 0;
691
}
692

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

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

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

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

    if (!stack)
        return NULL;

    auth_stack_addref(stack);

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

    if (stack)
        auth_stack_release(stack);

    return ret;

}

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

    if (!stack)
        return NULL;
735

Philipp Schafft's avatar
Philipp Schafft committed
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753
    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;
}