auth.c 20.2 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;

}

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

733
    if (!stack || method < 0 || method > httpp_req_unknown)
Philipp Schafft's avatar
Philipp Schafft committed
734
        return NULL;
735

Philipp Schafft's avatar
Philipp Schafft committed
736
737
738
739
    auth_stack_addref(stack);

    while (!ret && stack) {
        auth_t *auth = auth_stack_get(stack);
740
        if (auth->method[method] && strcmp(auth->type, AUTH_TYPE_ANONYMOUS) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
741
742
743
744
745
746
747
748
749
750
751
752
753
            acl_addref(ret = auth->acl);
        }
        auth_release(auth);

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

    if (stack)
        auth_stack_release(stack);

    return ret;
}