auth.c 19 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
11
 * Copyright 2013-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12
13
 */

Michael Smith's avatar
Michael Smith committed
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/** 
 * Client authentication functions
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#include "auth.h"
28
#include "auth_htpasswd.h"
Karl Heyes's avatar
Karl Heyes committed
29
#include "auth_url.h"
Philipp Schafft's avatar
Philipp Schafft committed
30
31
#include "auth_anonymous.h"
#include "auth_static.h"
Michael Smith's avatar
Michael Smith committed
32
33
34
#include "source.h"
#include "client.h"
#include "cfgfile.h"
35
#include "stats.h"
Marvin Scholz's avatar
Marvin Scholz committed
36
#include "common/httpp/httpp.h"
37
#include "fserve.h"
38
#include "admin.h"
Philipp Schafft's avatar
Philipp Schafft committed
39
#include "acl.h"
Michael Smith's avatar
Michael Smith committed
40
41
42

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

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

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

54
55
56
57
58
59
60
61
62
63
64
65
66
static mutex_t _auth_lock; /* protects _current_id */
static volatile unsigned long _current_id = 0;

static unsigned long _next_auth_id(void) {
    unsigned long id;

    thread_mutex_lock(&_auth_lock);
    id = _current_id++;
    thread_mutex_unlock(&_auth_lock);

    return id;
}

Philipp Schafft's avatar
Philipp Schafft committed
67
static auth_client *auth_client_setup (client_t *client)
68
{
69
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
70
    auth_client *auth_user;
71

Philipp Schafft's avatar
Philipp Schafft committed
72
73
74
75
76
77
78
79
80
81
82
    do {
        const char *header;
        char *userpass, *tmp;
        char *username, *password;

        /* check if we already have auth infos */
        if (client->username || client->password)
            break;

        header = httpp_getvar(client->parser, "authorization");

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

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

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

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

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

Michael Smith's avatar
Michael Smith committed
116

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

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


140
141
142
/* release the auth. It is referred to by multiple structures so this is
 * refcounted and only actual freed after the last use
 */
Philipp Schafft's avatar
Philipp Schafft committed
143
void auth_release (auth_t *authenticator) {
144
145
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
146

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
176
177
178
179
180
181
182
183
184
185
186
/* increment refcount on the auth.
 */
void    auth_addref (auth_t *authenticator) {
    if (authenticator == NULL)
        return;

    thread_mutex_lock (&authenticator->lock);
    authenticator->refcount++;
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, (int)authenticator->refcount);
    thread_mutex_unlock (&authenticator->lock);
}
Michael Smith's avatar
Michael Smith committed
187

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


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

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

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

213
214
    /* make sure there is still a client at this point, a slow backend request
     * can be avoided if client has disconnected */
Philipp Schafft's avatar
Philipp Schafft committed
215
    if (is_client_connected(client) == 0) {
216
        ICECAST_LOG_DEBUG("client is no longer connected");
217
        client->respcode = 400;
218
219
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
220
        return AUTH_FAILED;
221
    }
Philipp Schafft's avatar
Philipp Schafft committed
222
223
224
225

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

235

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

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

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

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

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


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

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

271
272
273
274
275
276
277
278
            /* may become NULL before lock taken */
            thread_mutex_lock (&auth->lock);
            auth_user = (auth_client*)auth->head;
            if (auth_user == NULL)
            {
                thread_mutex_unlock (&auth->lock);
                continue;
            }
279
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
280
281
282
283
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
284
            thread_mutex_unlock(&auth->lock);
285
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
286

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

Philipp Schafft's avatar
Philipp Schafft committed
294
295
296
297
298
299
            if (result == AUTH_OK) {
                if (auth_user->client->acl)
                    acl_release(auth_user->client->acl);
                acl_addref(auth_user->client->acl = auth->acl);
                if (auth->role) /* TODO: Handle errors here */
                    auth_user->client->role = strdup(auth->role);
Michael Smith's avatar
Michael Smith committed
300
            }
301

Philipp Schafft's avatar
Philipp Schafft committed
302
303
304
305
            if (result == AUTH_NOMATCH && auth_user->on_no_match) {
                auth_user->on_no_match(auth_user->client, auth_user->on_result, auth_user->userdata);
            } else if (auth_user->on_result) {
                auth_user->on_result(auth_user->client, auth_user->userdata, result);
306
307
            }

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

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


Philipp Schafft's avatar
Philipp Schafft committed
319
/* Add a client.
320
 */
Philipp Schafft's avatar
Philipp Schafft committed
321
322
static void auth_add_client(auth_t *auth, client_t *client, void (*on_no_match)(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata), void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_client *auth_user;
323

324
325
    ICECAST_LOG_DEBUG("Trying to add client %p to auth %p's (role %s) queue.", client, auth, auth->role);

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

Philipp Schafft's avatar
Philipp Schafft committed
333
334
335
336
337
    if (!auth->method[client->parser->req_type]) {
        if (on_no_match) {
           on_no_match(client, on_result, userdata);
        } else if (on_result) {
           on_result(client, userdata, AUTH_NOMATCH);
338
        }
339
340
341
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
342
343
344
345
346
347
348
349
350
    auth_release(client->auth);
    auth_addref(client->auth = auth);
    auth_user = auth_client_setup(client);
    auth_user->process = auth_new_client;
    auth_user->on_no_match = on_no_match;
    auth_user->on_result = on_result;
    auth_user->userdata = userdata;
    ICECAST_LOG_INFO("adding client for authentication");
    queue_auth_client(auth_user);
351
352
}

353
354
355
/* determine whether we need to process this client further. This
 * involves any auth exit, typically for external auth servers.
 */
Philipp Schafft's avatar
Philipp Schafft committed
356
357
358
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
359
360


Philipp Schafft's avatar
Philipp Schafft committed
361
362
363
364
365
366
367
368
369
370
371
372
    /* drop any queue reference here, we do not want a race between the source thread
     * and the auth/fserve thread */
    client_set_queue (client, NULL);

    if (client->auth && client->auth->release_client) {
        auth_client *auth_user = auth_client_setup(client);
        auth_user->process = auth_remove_client;
        queue_auth_client(auth_user);
        return 1;
    } else if (client->auth) {
        auth_release(client->auth);
        client->auth = NULL;
373
    }
Philipp Schafft's avatar
Philipp Schafft committed
374
375
376

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


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

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

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

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


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

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
438
439
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
440
    auth->id = _next_auth_id();
Philipp Schafft's avatar
Philipp Schafft committed
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
    auth->type = (char*)xmlGetProp(node, XMLSTR("type"));
    auth->role = (char*)xmlGetProp(node, XMLSTR("name"));

    if (!auth->type) {
        auth_release(auth);
        return NULL;
    }

    method = (char*)xmlGetProp(node, XMLSTR("method"));
    if (method) {
        char *cur = method;
        char *next;

        for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
            auth->method[i] = 0;

        while (cur) {
            httpp_request_type_e idx;

            next = strstr(cur, ",");
            if (next) {
                *next = 0;
                next++;
                for (; *next == ' '; next++);
            }

            if (strcmp(cur, "*") == 0) {
                for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
                    auth->method[i] = 1;
                break;
            }

            idx = httpp_str_to_method(cur);
            if (idx == httpp_req_unknown) {
                auth_release(auth);
                return NULL;
            }
            auth->method[idx] = 1;

            cur = next;
        }

        xmlFree(method);
    } else {
        for (i = 0; i < (sizeof(auth->method)/sizeof(*auth->method)); i++)
            auth->method[i] = 1;
    }

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

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

541
542
    return auth;
}
543
544


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

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

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

/* authstack functions */

static void __move_client_forward_in_auth_stack(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_stack_next(&client->authstack);
    if (client->authstack) {
        auth_stack_add_client(client->authstack, client, on_result, userdata);
    } else {
        if (on_result)
            on_result(client, userdata, AUTH_NOMATCH);
567
    }
Philipp Schafft's avatar
Philipp Schafft committed
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
}

void          auth_stack_add_client(auth_stack_t *stack, client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_t *auth;

    if (!stack || !client || (client->authstack && client->authstack != stack))
        return;

    if (!client->authstack)
        auth_stack_addref(stack);
    client->authstack = stack;
    auth = auth_stack_get(stack);
    auth_add_client(auth, client, __move_client_forward_in_auth_stack, on_result, userdata);
    auth_release(auth);
}

void          auth_stack_release(auth_stack_t *stack) {
    if (!stack)
        return;

    thread_mutex_lock(&stack->lock);
    stack->refcount--;
    thread_mutex_unlock(&stack->lock);

    if (stack->refcount)
        return;

    auth_release(stack->auth);
    thread_mutex_destroy(&stack->lock);
    free(stack);
}

void          auth_stack_addref(auth_stack_t *stack) {
    if (!stack)
        return;
    thread_mutex_lock(&stack->lock);
    stack->refcount++;
    thread_mutex_unlock(&stack->lock);
}

int           auth_stack_next(auth_stack_t **stack) {
    auth_stack_t *next;
    if (!stack || !*stack)
        return -1;
    thread_mutex_lock(&(*stack)->lock);
    next = (*stack)->next;
    thread_mutex_unlock(&(*stack)->lock);
    auth_stack_release(*stack);
    auth_stack_addref(next);
    *stack = next;
    if (!next)
        return 1;
620
621
622
    return 0;
}

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

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

Philipp Schafft's avatar
Philipp Schafft committed
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    next = calloc(1, sizeof(*next));
    if (!next) {
        return -1;
    }
    thread_mutex_create(&next->lock);
    next->refcount = 1;
    next->auth = auth;
    auth_addref(auth);

    if (*stack) {
        return auth_stack_append(*stack, next);
    } else {
        *stack = next;
        return 0;
643
644
645
    }
}

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

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

Philipp Schafft's avatar
Philipp Schafft committed
652
653
654
655
656
657
658
659
660
661
662
663
    auth_stack_addref(cur = stack);
    thread_mutex_lock(&cur->lock);
    while (1) {
        next = cur->next;
        if (!cur->next)
            break;

        auth_stack_addref(next);
        thread_mutex_unlock(&cur->lock);
        auth_stack_release(cur);
        cur = next;
        thread_mutex_lock(&cur->lock);
664
    }
Philipp Schafft's avatar
Philipp Schafft committed
665
666
667
668
669
670

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

    return 0;
671
}
672

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

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

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

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

    if (!stack)
        return NULL;
690

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

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

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

    if (stack)
        auth_stack_release(stack);

    return ret;
}