auth.c 21.1 KB
Newer Older
1
2
3
4
5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7
8
9
10
 *                      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
 */

14
/**
Michael Smith's avatar
Michael Smith committed
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * 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"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
31
#include "stats.h"
Marvin Scholz's avatar
Marvin Scholz committed
32
#include "common/httpp/httpp.h"
33
#include "fserve.h"
34
#include "admin.h"
Philipp Schafft's avatar
Philipp Schafft committed
35
#include "acl.h"
Michael Smith's avatar
Michael Smith committed
36
37
38

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

Philipp Schafft's avatar
Philipp Schafft committed
40
41
42
43
44
45
46
/* data structures */
struct auth_stack_tag {
    size_t refcount;
    auth_t *auth;
    mutex_t lock;
    auth_stack_t *next;
};
47

Philipp Schafft's avatar
Philipp Schafft committed
48
/* code */
Marvin Scholz's avatar
Marvin Scholz committed
49
static void __handle_auth_client(auth_t *auth, auth_client *auth_user);
50

51
52
53
54
55
56
57
58
59
60
61
62
63
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;
}

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
static const char *auth_result2str(auth_result res)
{
    switch (res) {
        case AUTH_UNDEFINED:
            return "undefined";
        break;
        case AUTH_OK:
            return "ok";
        break;
        case AUTH_FAILED:
            return "failed";
        break;
        case AUTH_RELEASED:
            return "released";
        break;
        case AUTH_FORBIDDEN:
            return "forbidden";
        break;
        case AUTH_NOMATCH:
            return "nomatch";
        break;
        case AUTH_USERADDED:
            return "useradded";
        break;
        case AUTH_USEREXISTS:
            return "userexists";
        break;
        case AUTH_USERDELETED:
            return "userdeleted";
        break;
        default:
            return "(unknown)";
        break;
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
100
static auth_client *auth_client_setup (client_t *client)
101
{
102
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
103
    auth_client *auth_user;
104

Philipp Schafft's avatar
Philipp Schafft committed
105
106
107
108
109
110
111
112
113
114
115
    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");

116
117
118
        if (header == NULL)
            break;

Philipp Schafft's avatar
Philipp Schafft committed
119
        if (strncmp(header, "Basic ", 6) == 0) {
120
            userpass = util_base64_decode (header+6);
Philipp Schafft's avatar
Philipp Schafft committed
121
            if (userpass == NULL) {
122
                ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
123
124
125
126
127
                        header+6);
                break;
            }

            tmp = strchr(userpass, ':');
128
            if (tmp == NULL) {
Philipp Schafft's avatar
Philipp Schafft committed
129
                free(userpass);
130
                break;
131
            }
132
133
134
135

            *tmp = 0;
            username = userpass;
            password = tmp+1;
Philipp Schafft's avatar
Philipp Schafft committed
136
137
            client->username = strdup(username);
            client->password = strdup(password);
138
139
            free (userpass);
            break;
140
        }
141
        ICECAST_LOG_INFO("unhandled authorization header: %s", header);
142
    } while (0);
143

Philipp Schafft's avatar
Philipp Schafft committed
144
    auth_user = calloc(1, sizeof(auth_client));
145
146
    auth_user->client = client;
    return auth_user;
147
148
}

Michael Smith's avatar
Michael Smith committed
149

Philipp Schafft's avatar
Philipp Schafft committed
150
static void queue_auth_client (auth_client *auth_user)
151
{
152
153
    auth_t *auth;

154
    if (auth_user == NULL)
155
156
        return;
    auth_user->next = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
157
    if (auth_user->client == NULL || auth_user->client->auth == NULL)
158
    {
Philipp Schafft's avatar
Philipp Schafft committed
159
160
        ICECAST_LOG_WARN("internal state is incorrect for %p", auth_user->client);
        return;
161
    }
Philipp Schafft's avatar
Philipp Schafft committed
162
163
    auth = auth_user->client->auth;
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", auth->mount, (int)auth->refcount);
164
165
166
167
168
169
170
171
172
173
    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);
    }
174
}
Michael Smith's avatar
Michael Smith committed
175
176


177
178
179
/* 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
180
void auth_release (auth_t *authenticator) {
181
182
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
183

Philipp Schafft's avatar
Philipp Schafft committed
184
    thread_mutex_lock(&authenticator->lock);
185
    authenticator->refcount--;
Philipp Schafft's avatar
Philipp Schafft committed
186
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, (int)authenticator->refcount);
187
    if (authenticator->refcount)
188
    {
Philipp Schafft's avatar
Philipp Schafft committed
189
        thread_mutex_unlock(&authenticator->lock);
190
        return;
191
    }
Michael Smith's avatar
Michael Smith committed
192

193
    /* cleanup auth thread attached to this auth */
Philipp Schafft's avatar
Philipp Schafft committed
194
195
196
197
    if (authenticator->running) {
        authenticator->running = 0;
        thread_join(authenticator->thread);
    }
198

199
    if (authenticator->free)
Philipp Schafft's avatar
Philipp Schafft committed
200
201
202
203
204
        authenticator->free(authenticator);
    if (authenticator->type)
        xmlFree (authenticator->type);
    if (authenticator->role)
        xmlFree (authenticator->role);
205
206
    if (authenticator->management_url)
        xmlFree (authenticator->management_url);
Philipp Schafft's avatar
Philipp Schafft committed
207
208
    thread_mutex_unlock(&authenticator->lock);
    thread_mutex_destroy(&authenticator->lock);
209
    if (authenticator->mount)
Philipp Schafft's avatar
Philipp Schafft committed
210
211
212
        free(authenticator->mount);
    acl_release(authenticator->acl);
    free(authenticator);
Michael Smith's avatar
Michael Smith committed
213
214
}

Philipp Schafft's avatar
Philipp Schafft committed
215
216
217
218
219
220
221
222
223
224
225
/* 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
226

227
static void auth_client_free (auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
228
{
229
230
231
    if (auth_user == NULL)
        return;
    free (auth_user);
Michael Smith's avatar
Michael Smith committed
232
233
234
}


235
/* verify that the client is still connected. */
236
237
238
static int is_client_connected (client_t *client) {
/* As long as sock_active() is broken we need to disable this:

239
240
    int ret = 1;
    if (client)
Philipp Schafft's avatar
Philipp Schafft committed
241
        if (sock_active(client->con->sock) == 0)
242
243
            ret = 0;
    return ret;
244
245
*/
    return 1;
246
247
}

Philipp Schafft's avatar
Philipp Schafft committed
248
static auth_result auth_new_client (auth_t *auth, auth_client *auth_user) {
249
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
250
    auth_result ret = AUTH_FAILED;
251

252
253
    /* 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
254
    if (is_client_connected(client) == 0) {
255
        ICECAST_LOG_DEBUG("client is no longer connected");
256
        client->respcode = 400;
257
258
        auth_release (client->auth);
        client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
259
        return AUTH_FAILED;
260
    }
Philipp Schafft's avatar
Philipp Schafft committed
261
262
263
264

    if (auth->authenticate_client) {
        ret = auth->authenticate_client(auth_user);
        if (ret != AUTH_OK)
265
266
267
        {
            auth_release (client->auth);
            client->auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
268
            return ret;
269
        }
Michael Smith's avatar
Michael Smith committed
270
    }
Philipp Schafft's avatar
Philipp Schafft committed
271
    return ret;
Michael Smith's avatar
Michael Smith committed
272
273
}

274

275
/* wrapper function for auth thread to drop client connections
276
 */
Philipp Schafft's avatar
Philipp Schafft committed
277
static auth_result auth_remove_client(auth_t *auth, auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
278
{
279
    client_t *client = auth_user->client;
Philipp Schafft's avatar
Philipp Schafft committed
280
    auth_result ret = AUTH_RELEASED;
281

282
283
    (void)auth;

Philipp Schafft's avatar
Philipp Schafft committed
284
285
    if (client->auth->release_client)
        ret = client->auth->release_client(auth_user);
286

Philipp Schafft's avatar
Philipp Schafft committed
287
    auth_release(client->auth);
288
    client->auth = NULL;
289

Philipp Schafft's avatar
Philipp Schafft committed
290
291
292
    /* client is going, so auth is not an issue at this point */
    acl_release(client->acl);
    client->acl = NULL;
293

Philipp Schafft's avatar
Philipp Schafft committed
294
    return ret;
295
}
Michael Smith's avatar
Michael Smith committed
296

297
298
299
300
301
302
303
304
305
306
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;
    }

307
308
    ICECAST_LOG_DEBUG("client %p on auth %p role %s processed: %s", auth_user->client, auth, auth->role, auth_result2str(result));

309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
    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
325

326
327
328
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
329
330
    auth_t *auth = arg;

331
    ICECAST_LOG_INFO("Authentication thread started");
332
    while (auth->running)
333
    {
334
335
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
336
337
        {
            auth_client *auth_user;
Michael Smith's avatar
Michael Smith committed
338

339
340
341
342
343
344
345
346
            /* 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;
            }
347
            ICECAST_LOG_DEBUG("%d client(s) pending on %s (role %s)", auth->pending_count, auth->mount, auth->role);
348
349
350
351
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
352
            thread_mutex_unlock(&auth->lock);
353
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
354

355
            __handle_auth_client(auth, auth_user);
Philipp Schafft's avatar
Philipp Schafft committed
356

357
358
            continue;
        }
Philipp Schafft's avatar
Philipp Schafft committed
359
        thread_sleep (150000);
Michael Smith's avatar
Michael Smith committed
360
    }
361
    ICECAST_LOG_INFO("Authentication thread shutting down");
Philipp Schafft's avatar
Philipp Schafft committed
362
    return NULL;
363
}
Michael Smith's avatar
Michael Smith committed
364
365


Philipp Schafft's avatar
Philipp Schafft committed
366
/* Add a client.
367
 */
Philipp Schafft's avatar
Philipp Schafft committed
368
369
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;
370

371
372
    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
373
374
    /* TODO: replace that magic number */
    if (auth->pending_count > 100) {
375
        ICECAST_LOG_WARN("too many clients awaiting authentication on auth %p", auth);
Philipp Schafft's avatar
Philipp Schafft committed
376
377
        client_send_error(client, 403, 1, "busy, please try again later");
        return;
378
379
    }

Philipp Schafft's avatar
Philipp Schafft committed
380
381
382
383
384
    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);
385
        }
386
387
388
        return;
    }

Philipp Schafft's avatar
Philipp Schafft committed
389
390
391
392
393
394
395
    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;
396
    ICECAST_LOG_INFO("adding client %p for authentication on %p", client);
Philipp Schafft's avatar
Philipp Schafft committed
397
    queue_auth_client(auth_user);
398
399
}

400
401
402
/* 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
403
404
405
int auth_release_client (client_t *client) {
    if (!client->acl)
        return 0;
406
407


Philipp Schafft's avatar
Philipp Schafft committed
408
409
410
411
412
413
414
415
416
417
418
419
    /* 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;
420
    }
Philipp Schafft's avatar
Philipp Schafft committed
421
422
423

    acl_release(client->acl);
    client->acl = NULL;
424
425
    return 0;
}
426
427


Karl Heyes's avatar
Karl Heyes committed
428
static int get_authenticator (auth_t *auth, config_options_t *options)
429
{
430
431
    if (auth->type == NULL)
    {
432
        ICECAST_LOG_WARN("no authentication type defined");
433
434
        return -1;
    }
435
436
    do
    {
437
        ICECAST_LOG_DEBUG("type is %s", auth->type);
438

Philipp Schafft's avatar
Philipp Schafft committed
439
        if (strcmp(auth->type, AUTH_TYPE_URL) == 0) {
440
#ifdef HAVE_AUTH_URL
Philipp Schafft's avatar
Philipp Schafft committed
441
            if (auth_get_url_auth(auth, options) < 0)
Karl Heyes's avatar
Karl Heyes committed
442
                return -1;
443
            break;
444
#else
445
            ICECAST_LOG_ERROR("Auth URL disabled");
Karl Heyes's avatar
Karl Heyes committed
446
            return -1;
447
#endif
Philipp Schafft's avatar
Philipp Schafft committed
448
449
450
451
452
453
454
455
456
457
458
459
460
461
        } 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
462
                return -1;
463
            break;
464
        }
Karl Heyes's avatar
Karl Heyes committed
465

466
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
467
        return -1;
468
469
    } while (0);

Karl Heyes's avatar
Karl Heyes committed
470
    return 0;
471
}
472
473


474
auth_t *auth_get_authenticator(xmlNodePtr node)
475
{
476
    auth_t *auth = calloc(1, sizeof(auth_t));
477
478
    config_options_t *options = NULL, **next_option = &options;
    xmlNodePtr option;
Philipp Schafft's avatar
Philipp Schafft committed
479
480
    char *method;
    size_t i;
481
482
483
484

    if (auth == NULL)
        return NULL;

Philipp Schafft's avatar
Philipp Schafft committed
485
486
    thread_mutex_create(&auth->lock);
    auth->refcount = 1;
487
    auth->id = _next_auth_id();
Philipp Schafft's avatar
Philipp Schafft committed
488
489
    auth->type = (char*)xmlGetProp(node, XMLSTR("type"));
    auth->role = (char*)xmlGetProp(node, XMLSTR("name"));
490
    auth->management_url = (char*)xmlGetProp(node, XMLSTR("management-url"));
Philipp Schafft's avatar
Philipp Schafft committed
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536

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

537
    /* BEFORE RELEASE 2.5.0 TODO: Migrate this to config_parse_options(). */
538
539
540
541
542
    option = node->xmlChildrenNode;
    while (option)
    {
        xmlNodePtr current = option;
        option = option->next;
543
        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
544
        {
Philipp Schafft's avatar
Philipp Schafft committed
545
546
            config_options_t *opt = calloc(1, sizeof (config_options_t));
            opt->name = (char *)xmlGetProp(current, XMLSTR("name"));
547
548
549
550
551
            if (opt->name == NULL)
            {
                free(opt);
                continue;
            }
Philipp Schafft's avatar
Philipp Schafft committed
552
            opt->value = (char *)xmlGetProp(current, XMLSTR("value"));
553
554
            if (opt->value == NULL)
            {
Philipp Schafft's avatar
Philipp Schafft committed
555
556
                xmlFree(opt->name);
                free(opt);
557
558
559
560
561
562
                continue;
            }
            *next_option = opt;
            next_option = &opt->next;
        }
        else
563
            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
564
                ICECAST_LOG_WARN("unknown auth setting (%s)", current->name);
565
    }
Philipp Schafft's avatar
Philipp Schafft committed
566
567
568
    auth->acl  = acl_new_from_xml_node(node);
    if (!auth->acl) {
        auth_release(auth);
Karl Heyes's avatar
Karl Heyes committed
569
        auth = NULL;
Philipp Schafft's avatar
Philipp Schafft committed
570
571
572
573
574
575
    } else {
        if (get_authenticator (auth, options) < 0) {
            auth_release(auth);
            auth = NULL;
        } else {
            auth->tailp = &auth->head;
576
577
578
579
            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
580
        }
Karl Heyes's avatar
Karl Heyes committed
581
    }
582

Philipp Schafft's avatar
Philipp Schafft committed
583
    while (options) {
584
585
        config_options_t *opt = options;
        options = opt->next;
Philipp Schafft's avatar
Philipp Schafft committed
586
587
        xmlFree(opt->name);
        xmlFree(opt->value);
588
        free (opt);
589
    }
Philipp Schafft's avatar
Philipp Schafft committed
590

591
    if (auth && !auth->management_url && (auth->adduser || auth->deleteuser || auth->listuser)) {
592
593
594
595
596
        char url[128];
        snprintf(url, sizeof(url), "/admin/manageauth.xsl?id=%lu", auth->id);
        auth->management_url = (char*)xmlCharStrdup(url);
    }

597
598
    return auth;
}
599
600


Philipp Schafft's avatar
Philipp Schafft committed
601
602
603
/* these are called at server start and termination */

void auth_initialise (void)
604
{
605
    thread_mutex_create(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
606
}
607

Philipp Schafft's avatar
Philipp Schafft committed
608
609
610
void auth_shutdown (void)
{
    ICECAST_LOG_INFO("Auth shutdown");
611
    thread_mutex_destroy(&_auth_lock);
Philipp Schafft's avatar
Philipp Schafft committed
612
613
614
615
616
617
618
619
620
621
622
}

/* 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);
623
    }
Philipp Schafft's avatar
Philipp Schafft committed
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
}

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);
652
    auth_stack_release(stack->next);
Philipp Schafft's avatar
Philipp Schafft committed
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
    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;
671
    auth_stack_addref(next);
Philipp Schafft's avatar
Philipp Schafft committed
672
673
674
675
676
    thread_mutex_unlock(&(*stack)->lock);
    auth_stack_release(*stack);
    *stack = next;
    if (!next)
        return 1;
677
678
679
    return 0;
}

Philipp Schafft's avatar
Philipp Schafft committed
680
681
int           auth_stack_push(auth_stack_t **stack, auth_t *auth) {
    auth_stack_t *next;
682

Philipp Schafft's avatar
Philipp Schafft committed
683
684
    if (!stack || !auth)
        return -1;
685

Philipp Schafft's avatar
Philipp Schafft committed
686
687
688
689
690
691
692
693
694
695
    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) {
696
697
698
        auth_stack_append(*stack, next);
        auth_stack_release(next);
        return 0;
Philipp Schafft's avatar
Philipp Schafft committed
699
700
701
    } else {
        *stack = next;
        return 0;
702
703
704
    }
}

Philipp Schafft's avatar
Philipp Schafft committed
705
706
int           auth_stack_append(auth_stack_t *stack, auth_stack_t *tail) {
    auth_stack_t *next, *cur;
707

Philipp Schafft's avatar
Philipp Schafft committed
708
709
    if (!stack)
        return -1;
710

Philipp Schafft's avatar
Philipp Schafft committed
711
712
713
714
715
716
717
718
719
720
721
722
    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);
723
    }
Philipp Schafft's avatar
Philipp Schafft committed
724
725
726
727
728
729

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

    return 0;
730
}
731

Philipp Schafft's avatar
Philipp Schafft committed
732
733
auth_t       *auth_stack_get(auth_stack_t *stack) {
    auth_t *auth;
734

Philipp Schafft's avatar
Philipp Schafft committed
735
736
    if (!stack)
        return NULL;
737

Philipp Schafft's avatar
Philipp Schafft committed
738
739
740
741
    thread_mutex_unlock(&stack->lock);
    auth_addref(auth = stack->auth);
    thread_mutex_unlock(&stack->lock);
    return auth;
742
743
}

744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
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;

}

769
acl_t        *auth_stack_get_anonymous_acl(auth_stack_t *stack, httpp_request_type_e method) {
Philipp Schafft's avatar
Philipp Schafft committed
770
771
    acl_t *ret = NULL;

772
    if (!stack || method < 0 || method > httpp_req_unknown)
Philipp Schafft's avatar
Philipp Schafft committed
773
        return NULL;
774

Philipp Schafft's avatar
Philipp Schafft committed
775
776
777
778
    auth_stack_addref(stack);

    while (!ret && stack) {
        auth_t *auth = auth_stack_get(stack);
779
        if (auth->method[method] && strcmp(auth->type, AUTH_TYPE_ANONYMOUS) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
780
781
782
783
784
785
786
787
788
789
790
791
792
            acl_addref(ret = auth->acl);
        }
        auth_release(auth);

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

    if (stack)
        auth_stack_release(stack);

    return ret;
}