auth.c 18.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"
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. */
184
185
186
static int is_client_connected (client_t *client) {
/* As long as sock_active() is broken we need to disable this:

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

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

200
201
    /* make sure there is still a client at this point, a slow backend request
     * can be avoided if client has disconnected */
Philipp Schafft's avatar
Philipp Schafft committed
202
    if (is_client_connected(client) == 0) {
203
        ICECAST_LOG_DEBUG("listener is no longer connected");
204
        client->respcode = 400;
205
206
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
207
        return AUTH_FAILED;
208
    }
Philipp Schafft's avatar
Philipp Schafft committed
209
210
211
212

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

222

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

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

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

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

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


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

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

258
259
260
261
262
263
264
265
            /* may become NULL before lock taken */
            thread_mutex_lock (&auth->lock);
            auth_user = (auth_client*)auth->head;
            if (auth_user == NULL)
            {
                thread_mutex_unlock (&auth->lock);
                continue;
            }
266
            ICECAST_LOG_DEBUG("%d client(s) pending on %s", auth->pending_count, auth->mount);
267
268
269
270
271
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
            thread_mutex_unlock (&auth->lock);
272
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
273

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

Philipp Schafft's avatar
Philipp Schafft committed
281
282
283
284
285
286
            if (result == AUTH_OK) {
                if (auth_user->client->acl)
                    acl_release(auth_user->client->acl);
                acl_addref(auth_user->client->acl = auth->acl);
                if (auth->role) /* TODO: Handle errors here */
                    auth_user->client->role = strdup(auth->role);
Michael Smith's avatar
Michael Smith committed
287
            }
288

Philipp Schafft's avatar
Philipp Schafft committed
289
290
291
292
            if (result == AUTH_NOMATCH && auth_user->on_no_match) {
                auth_user->on_no_match(auth_user->client, auth_user->on_result, auth_user->userdata);
            } else if (auth_user->on_result) {
                auth_user->on_result(auth_user->client, auth_user->userdata, result);
293
294
            }

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

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


Philipp Schafft's avatar
Philipp Schafft committed
306
/* Add a client.
307
 */
Philipp Schafft's avatar
Philipp Schafft committed
308
309
static void auth_add_client(auth_t *auth, client_t *client, void (*on_no_match)(client_t *client, void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata), void (*on_result)(client_t *client, void *userdata, auth_result result), void *userdata) {
    auth_client *auth_user;
310

Philipp Schafft's avatar
Philipp Schafft committed
311
312
313
314
315
    /* 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;
316
317
    }

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

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

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


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

    acl_release(client->acl);
    client->acl = NULL;
362
363
    return 0;
}
364
365


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

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

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

Karl Heyes's avatar
Karl Heyes committed
408
    return 0;
409
}
410
411


412
413
414
415
416
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
417
418
    char *method;
    size_t i;
419
420
421
422

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
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
470
471
472
    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;
    }

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

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

524
525
    return auth;
}
526
527


Philipp Schafft's avatar
Philipp Schafft committed
528
529
530
/* these are called at server start and termination */

void auth_initialise (void)
531
{
Philipp Schafft's avatar
Philipp Schafft committed
532
}
533

Philipp Schafft's avatar
Philipp Schafft committed
534
535
536
537
538
539
540
541
542
543
544
545
546
547
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);
548
    }
Philipp Schafft's avatar
Philipp Schafft committed
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
598
599
600
}

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;
601
602
603
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
604
605
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
606

Philipp Schafft's avatar
Philipp Schafft committed
607
608
    if (!stack || !auth)
        return -1;
609

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

Philipp Schafft's avatar
Philipp Schafft committed
627
628
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
629

Philipp Schafft's avatar
Philipp Schafft committed
630
631
    if (!stack)
        return -1;
632

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

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

    return 0;
652
}
653

Philipp Schafft's avatar
Philipp Schafft committed
654
655
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
656

Philipp Schafft's avatar
Philipp Schafft committed
657
658
    if (!stack)
        return NULL;
659

Philipp Schafft's avatar
Philipp Schafft committed
660
661
662
663
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
664
665
}

Philipp Schafft's avatar
Philipp Schafft committed
666
667
668
669
670
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
671

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