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

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

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

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

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

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

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

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

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

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

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

    return id;
}

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

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

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

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

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

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

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

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

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

Michael Smith's avatar
Michael Smith committed
116

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

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


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

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

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

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

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

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

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


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

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

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

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

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

237

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

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

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

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

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


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

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

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

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

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

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

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

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


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

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

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

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

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

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


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

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

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


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

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

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

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


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

    if (auth == NULL)
        return NULL;

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

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

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

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

        while (cur) {
            httpp_request_type_e idx;

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

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

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

            cur = next;
        }

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

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

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

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

550
551
    return auth;
}
552
553


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

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

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

/* authstack functions */

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

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

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

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

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

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

    if (stack->refcount)
        return;

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

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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
638
639
640
641
642
643
644
645
646
647
648
649
650
651
    next = calloc(1, sizeof(*next));
    if (!next) {
        return -1;
    }
    thread_mutex_create(&next->lock);
    next->refcount = 1;
    next->auth = auth;
    auth_addref(auth);

    if (*stack) {
        return auth_stack_append(*stack, next);
    } else {
        *stack = next;
        return 0;
652
653
654
    }
}

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

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

Philipp Schafft's avatar
Philipp Schafft committed
661
662
663
664
665
666
667
668
669
670
671
672
    auth_stack_addref(cur = stack);
    thread_mutex_lock(&cur->lock);
    while (1) {
        next = cur->next;
        if (!cur->next)
            break;

        auth_stack_addref(next);
        thread_mutex_unlock(&cur->lock);
        auth_stack_release(cur);
        cur = next;
        thread_mutex_lock(&cur->lock);
673
    }
Philipp Schafft's avatar
Philipp Schafft committed
674
675
676
677
678
679

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

    return 0;
680
}
681

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

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

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

694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
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
719
720
721
722
723
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack) {
    acl_t *ret = NULL;

    if (!stack)
        return NULL;
724

Philipp Schafft's avatar
Philipp Schafft committed
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
    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;
}