auth.c 21.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/* 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).
 */

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

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

40

41
static void auth_postprocess_source (auth_client *auth_user);
42
43


44
static auth_client *auth_client_setup (const char *mount, client_t *client)
45
{
46
    /* This will look something like "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" */
47
    const char *header = httpp_getvar(client->parser, "authorization");
48
49
    char *userpass, *tmp;
    char *username, *password;
50
    auth_client *auth_user;
51
52
53
54
55
56
57
58
59
60
61

    do
    {
        if (header == NULL)
            break;

        if (strncmp(header, "Basic ", 6) == 0)
        {
            userpass = util_base64_decode (header+6);
            if (userpass == NULL)
            {
62
                ICECAST_LOG_WARN("Base64 decode of Authorization header \"%s\" failed",
63
64
65
66
67
68
69
70
71
                        header+6);
                break;
            }

            tmp = strchr(userpass, ':');
            if (tmp == NULL)
            { 
                free (userpass);
                break;
72
            }
73
74
75
76
77
78
79
80

            *tmp = 0;
            username = userpass;
            password = tmp+1;
            client->username = strdup (username);
            client->password = strdup (password);
            free (userpass);
            break;
81
        }
82
        ICECAST_LOG_INFO("unhandled authorization header: %s", header);
83

84
    } while (0);
85

86
87
88
89
    auth_user = calloc (1, sizeof(auth_client));
    auth_user->mount = strdup (mount);
    auth_user->client = client;
    return auth_user;
90
91
}

Michael Smith's avatar
Michael Smith committed
92

93
static void queue_auth_client (auth_client *auth_user, mount_proxy *mountinfo)
94
{
95
96
    auth_t *auth;

97
    if (auth_user == NULL)
98
99
        return;
    auth_user->next = NULL;
100
101
102
103
104
105
106
107
108
109
    if (mountinfo)
    {
        auth = mountinfo->auth;
        thread_mutex_lock (&auth->lock);
        if (auth_user->client)
            auth_user->client->auth = auth;
        auth->refcount++;
    }
    else
    {
110
111
        if (auth_user->client == NULL || auth_user->client->auth == NULL)
        {
112
            ICECAST_LOG_WARN("internal state is incorrect for %p", auth_user->client);
113
114
            return;
        }
115
116
117
        auth = auth_user->client->auth;
        thread_mutex_lock (&auth->lock);
    }
118
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", auth->mount, 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
130
131
132
133
/* release the auth. It is referred to by multiple structures so this is
 * refcounted and only actual freed after the last use
 */
void auth_release (auth_t *authenticator)
{
    if (authenticator == NULL)
        return;
Michael Smith's avatar
Michael Smith committed
134

135
    thread_mutex_lock (&authenticator->lock);
136
    authenticator->refcount--;
137
    ICECAST_LOG_DEBUG("...refcount on auth_t %s is now %d", authenticator->mount, authenticator->refcount);
138
    if (authenticator->refcount)
139
140
    {
        thread_mutex_unlock (&authenticator->lock);
141
        return;
142
    }
Michael Smith's avatar
Michael Smith committed
143

144
145
146
147
    /* cleanup auth thread attached to this auth */
    authenticator->running = 0;
    thread_join (authenticator->thread);

148
149
    if (authenticator->free)
        authenticator->free (authenticator);
150
    xmlFree (authenticator->type);
151
152
    thread_mutex_unlock (&authenticator->lock);
    thread_mutex_destroy (&authenticator->lock);
153
154
    if (authenticator->mount)
        free (authenticator->mount);
155
    free (authenticator);
Michael Smith's avatar
Michael Smith committed
156
157
158
}


159
static void auth_client_free (auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
160
{
161
162
163
164
165
    if (auth_user == NULL)
        return;
    if (auth_user->client)
    {
        client_t *client = auth_user->client;
Michael Smith's avatar
Michael Smith committed
166

167
168
169
        if (client->respcode)
            client_destroy (client);
        else
170
            client_send_error(client, 401, 1, "You need to authenticate\r\n");
171
172
173
174
        auth_user->client = NULL;
    }
    free (auth_user->mount);
    free (auth_user);
Michael Smith's avatar
Michael Smith committed
175
176
177
}


178
179
180
181
182
183
184
185
186
187
188
189
190
/* verify that the listener is still connected. */
static int is_listener_connected (client_t *client)
{
    int ret = 1;
    if (client)
    {
        if (sock_active (client->con->sock) == 0)
            ret = 0;
    }
    return ret;
}


191
192
193
/* wrapper function for auth thread to authenticate new listener
 * connection details
 */
194
static void auth_new_listener (auth_t *auth, auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
195
{
196
197
    client_t *client = auth_user->client;

198
199
200
201
    /* make sure there is still a client at this point, a slow backend request
     * can be avoided if client has disconnected */
    if (is_listener_connected (client) == 0)
    {
202
        ICECAST_LOG_DEBUG("listener is no longer connected");
203
        client->respcode = 400;
204
205
        auth_release (client->auth);
        client->auth = NULL;
206
207
        return;
    }
208
    if (auth->authenticate)
209
    {
210
        if (auth->authenticate (auth_user) != AUTH_OK)
211
212
213
        {
            auth_release (client->auth);
            client->auth = NULL;
214
            return;
215
        }
Michael Smith's avatar
Michael Smith committed
216
    }
217
    if (auth_postprocess_listener (auth_user) < 0)
218
219
220
    {
        auth_release (client->auth);
        client->auth = NULL;
221
        ICECAST_LOG_INFO("client %lu failed", client->con->id);
222
    }
Michael Smith's avatar
Michael Smith committed
223
224
}

225

226
/* wrapper function for auth thread to drop listener connections
227
 */
228
static void auth_remove_listener (auth_t *auth, auth_client *auth_user)
Michael Smith's avatar
Michael Smith committed
229
{
230
231
    client_t *client = auth_user->client;

232
233
    if (client->auth->release_listener)
        client->auth->release_listener (auth_user);
234
235
    auth_release (client->auth);
    client->auth = NULL;
236
237
238
239
240
    /* client is going, so auth is not an issue at this point */
    client->authenticated = 0;
}


241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/* Called from auth thread to process any request for source client
 * authentication. Only applies to source clients, not relays.
 */
static void stream_auth_callback (auth_t *auth, auth_client *auth_user)
{
    client_t *client = auth_user->client;

    if (auth->stream_auth)
        auth->stream_auth (auth_user);

    auth_release (auth);
    client->auth = NULL;
    if (client->authenticated)
        auth_postprocess_source (auth_user);
    else
256
        ICECAST_LOG_WARN("Failed auth for source \"%s\"", auth_user->mount);
257
258
259
}


260
261
262
/* Callback from auth thread to handle a stream start event, this applies
 * to both source clients and relays.
 */
263
static void stream_start_callback (auth_t *auth, auth_client *auth_user)
264
265
266
{
    if (auth->stream_start)
        auth->stream_start (auth_user);
267
    auth_release (auth);
268
269
270
271
272
273
}


/* Callback from auth thread to handle a stream start event, this applies
 * to both source clients and relays.
 */
274
static void stream_end_callback (auth_t *auth, auth_client *auth_user)
275
276
277
{
    if (auth->stream_end)
        auth->stream_end (auth_user);
278
    auth_release (auth);
279
}
Michael Smith's avatar
Michael Smith committed
280
281


282
283
284
/* The auth thread main loop. */
static void *auth_run_thread (void *arg)
{
285
286
    auth_t *auth = arg;

287
    ICECAST_LOG_INFO("Authentication thread started");
288
    while (auth->running)
289
    {
290
291
        /* usually no clients are waiting, so don't bother taking locks */
        if (auth->head)
292
293
        {
            auth_client *auth_user;
Michael Smith's avatar
Michael Smith committed
294

295
296
297
298
299
300
301
302
            /* 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;
            }
303
            ICECAST_LOG_DEBUG("%d client(s) pending on %s", auth->pending_count, auth->mount);
304
305
306
307
308
            auth->head = auth_user->next;
            if (auth->head == NULL)
                auth->tailp = &auth->head;
            auth->pending_count--;
            thread_mutex_unlock (&auth->lock);
309
            auth_user->next = NULL;
Michael Smith's avatar
Michael Smith committed
310

311
            if (auth_user->process)
312
                auth_user->process (auth, auth_user);
313
            else
314
                ICECAST_LOG_ERROR("client auth process not set");
Michael Smith's avatar
Michael Smith committed
315

316
            auth_client_free (auth_user);
Michael Smith's avatar
Michael Smith committed
317

318
            continue;
319
        }
320
        thread_sleep (150000);
321
    }
322
    ICECAST_LOG_INFO("Authenication thread shutting down");
323
324
    return NULL;
}
Michael Smith's avatar
Michael Smith committed
325
326


327
328
329
330
/* Check whether this client is currently on this mount, the client may be
 * on either the active or pending lists.
 * return 1 if ok to add or 0 to prevent
 */
331
static int check_duplicate_logins (source_t *source, client_t *client, auth_t *auth)
332
333
334
335
{
    /* allow multiple authenticated relays */
    if (client->username == NULL)
        return 1;
Michael Smith's avatar
Michael Smith committed
336

337
338
339
340
341
342
343
344
    if (auth && auth->allow_duplicate_users == 0)
    {
        avl_node *node;

        avl_tree_rlock (source->client_tree);
        node = avl_get_first (source->client_tree);
        while (node)
        {   
345
346
347
            client_t *existing_client = (client_t *)node->key;
            if (existing_client->username && 
                    strcmp (existing_client->username, client->username) == 0)
348
349
350
351
352
353
354
355
356
357
358
359
            {
                avl_tree_unlock (source->client_tree);
                return 0;
            }
            node = avl_get_next (node);
        }       
        avl_tree_unlock (source->client_tree);

        avl_tree_rlock (source->pending_tree);
        node = avl_get_first (source->pending_tree);
        while (node)
        {
360
361
362
            client_t *existing_client = (client_t *)node->key;
            if (existing_client->username && 
                    strcmp (existing_client->username, client->username) == 0)
363
364
365
            {
                avl_tree_unlock (source->pending_tree);
                return 0;
Michael Smith's avatar
Michael Smith committed
366
            }
367
            node = avl_get_next (node);
Michael Smith's avatar
Michael Smith committed
368
        }
369
        avl_tree_unlock (source->pending_tree);
Michael Smith's avatar
Michael Smith committed
370
    }
371
    return 1;
Michael Smith's avatar
Michael Smith committed
372
373
}

374
375
376
377

/* if 0 is returned then the client should not be touched, however if -1
 * is returned then the caller is responsible for handling the client
 */
378
static int add_listener_to_source (source_t *source, client_t *client)
Michael Smith's avatar
Michael Smith committed
379
{
380
    int loop = 10;
381
382
    do
    {
383
        ICECAST_LOG_DEBUG("max on %s is %ld (cur %lu)", source->mount,
384
385
386
387
388
                source->max_listeners, source->listeners);
        if (source->max_listeners == -1)
            break;
        if (source->listeners < (unsigned long)source->max_listeners)
            break;
Michael Smith's avatar
Michael Smith committed
389

390
391
392
        if (loop && source->fallback_when_full && source->fallback_mount)
        {
            source_t *next = source_find_mount (source->fallback_mount);
393
            if (!next) {
394
                ICECAST_LOG_ERROR("Fallback '%s' for full source '%s' not found", 
395
396
397
398
                        source->mount, source->fallback_mount);
                return -1;
            }

399
            ICECAST_LOG_INFO("stream full trying %s", next->mount);
400
401
402
403
            source = next;
            loop--;
            continue;
        }
404
405
        /* now we fail the client */
        return -1;
Michael Smith's avatar
Michael Smith committed
406

407
    } while (1);
Michael Smith's avatar
Michael Smith committed
408

409
410
411
412
    client->write_to_client = format_generic_write_to_client;
    client->check_buffer = format_check_http_buffer;
    client->refbuf->len = PER_CLIENT_REFBUF_SIZE;
    memset (client->refbuf->data, 0, PER_CLIENT_REFBUF_SIZE);
Michael Smith's avatar
Michael Smith committed
413

414
415
416
417
418
    /* lets add the client to the active list */
    avl_tree_wlock (source->pending_tree);
    avl_insert (source->pending_tree, client);
    avl_tree_unlock (source->pending_tree);

419
420
421
    if (source->running == 0 && source->on_demand)
    {
        /* enable on-demand relay to start, wake up the slave thread */
422
        ICECAST_LOG_DEBUG("kicking off on-demand relay");
423
        source->on_demand_req = 1;
Michael Smith's avatar
Michael Smith committed
424
    }
425
    ICECAST_LOG_DEBUG("Added client to %s", source->mount);
426
427
    return 0;
}
Michael Smith's avatar
Michael Smith committed
428
429


430
431
432
/* Add listener to the pending lists of either the  source or fserve thread.
 * This can be run from the connection or auth thread context
 */
433
static int add_authenticated_listener (const char *mount, mount_proxy *mountinfo, client_t *client)
434
435
436
{
    int ret = 0;
    source_t *source = NULL;
437

438
439
    client->authenticated = 1;

440
441
442
443
444
445
    /* Here we are parsing the URI request to see if the extension is .xsl, if
     * so, then process this request as an XSLT request
     */
    if (util_check_valid_extension (mount) == XSLT_CONTENT)
    {
        /* If the file exists, then transform it, otherwise, write a 404 */
446
        ICECAST_LOG_DEBUG("Stats request, sending XSL transformed stats");
447
448
449
450
        stats_transform_xslt (client, mount);
        return 0;
    }

451
452
    avl_tree_rlock (global.source_tree);
    source = source_find_mount (mount);
Michael Smith's avatar
Michael Smith committed
453

454
455
    if (source)
    {
456
457
        if (mountinfo)
        {
Karl Heyes's avatar
Karl Heyes committed
458
459
460
461
462
463
            if (check_duplicate_logins (source, client, mountinfo->auth) == 0)
            {
                avl_tree_unlock (global.source_tree);
                return -1;
            }

464
465
466
467
            /* set a per-mount disconnect time if auth hasn't set one already */
            if (mountinfo->max_listener_duration && client->con->discon_time == 0)
                client->con->discon_time = time(NULL) + mountinfo->max_listener_duration;
        }
468

469
        ret = add_listener_to_source (source, client);
470
471
        avl_tree_unlock (global.source_tree);
        if (ret == 0)
472
            ICECAST_LOG_DEBUG("client authenticated, passed to source");
473
    }
474
475
476
477
478
    else
    {
        avl_tree_unlock (global.source_tree);
        fserve_client_create (client, mount);
    }
479
480
    return ret;
}
481
482


483
int auth_postprocess_listener (auth_client *auth_user)
484
{
485
    int ret;
486
    client_t *client = auth_user->client;
487
    ice_config_t *config = config_get_config();
488

489
    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
490

491
    ret = add_authenticated_listener (auth_user->mount, mountinfo, client);
492
    config_release_config();
493

494
    if (ret < 0)
495
        client_send_error(auth_user->client, 401, 1, "You need to authenticate\r\n");
496
    auth_user->client = NULL;
497

498
    return ret;
499
500
}

501

502
503
504
505
506
507
508
509
510
511
512
513
514
/* Decide whether we need to start a source or just process a source
 * admin request.
 */
void auth_postprocess_source (auth_client *auth_user)
{
    client_t *client = auth_user->client;
    const char *mount = auth_user->mount;
    const char *req = httpp_getvar (client->parser, HTTPP_VAR_URI);

    auth_user->client = NULL;
    client->authenticated = 1;
    if (strcmp (req, "/admin.cgi") == 0 || strncmp ("/admin/metadata", req, 15) == 0)
    {
515
        ICECAST_LOG_DEBUG("metadata request (%s, %s)", req, mount);
516
517
518
519
        admin_handle_request (client, "/admin/metadata");
    }
    else
    {
520
        ICECAST_LOG_DEBUG("on mountpoint %s", mount);
521
522
523
524
525
        source_startup (client, mount, 0);
    }
}


526
527
528
/* Add a listener. Check for any mount information that states any
 * authentication to be used.
 */
529
void auth_add_listener (const char *mount, client_t *client)
530
{
531
532
533
    mount_proxy *mountinfo; 
    ice_config_t *config = config_get_config();

534
    mountinfo = config_find_mount (config, mount, MOUNT_TYPE_NORMAL);
535
536
537
    if (mountinfo && mountinfo->no_mount)
    {
        config_release_config ();
538
        client_send_error(client, 403, 1, "mountpoint unavailable");
539
540
        return;
    }
541
    if (mountinfo && mountinfo->auth)
542
543
544
    {
        auth_client *auth_user;

545
        if (mountinfo->auth->pending_count > 100)
546
547
        {
            config_release_config ();
548
            ICECAST_LOG_WARN("too many clients awaiting authentication");
549
            client_send_error(client, 403, 1, "busy, please try again later");
550
551
            return;
        }
552
        auth_user = auth_client_setup (mount, client);
553
        auth_user->process = auth_new_listener;
554
        ICECAST_LOG_INFO("adding client for authentication");
555
556
        queue_auth_client (auth_user, mountinfo);
        config_release_config ();
557
558
559
    }
    else
    {
560
        int ret = add_authenticated_listener (mount, mountinfo, client);
561
562
        config_release_config ();
        if (ret < 0)
563
            client_send_error(client, 403, 1, "max listeners reached");
564
565
566
    }
}

567
568
569
570

/* determine whether we need to process this client further. This
 * involves any auth exit, typically for external auth servers.
 */
571
int auth_release_listener (client_t *client)
572
{
573
    if (client->authenticated)
574
    {
575
        const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI);
576

577
578
579
        /* 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);
580

581
582
583
584
585
586
587
588
        if (mount && client->auth && client->auth->release_listener)
        {
            auth_client *auth_user = auth_client_setup (mount, client);
            auth_user->process = auth_remove_listener;
            queue_auth_client (auth_user, NULL);
            return 1;
        }
        client->authenticated = 0;
589
590
591
    }
    return 0;
}
592
593


Karl Heyes's avatar
Karl Heyes committed
594
static int get_authenticator (auth_t *auth, config_options_t *options)
595
{
596
597
    if (auth->type == NULL)
    {
598
        ICECAST_LOG_WARN("no authentication type defined");
599
600
        return -1;
    }
601
602
    do
    {
603
        ICECAST_LOG_DEBUG("type is %s", auth->type);
604

605
        if (strcmp (auth->type, "url") == 0) {
606
#ifdef HAVE_AUTH_URL
Karl Heyes's avatar
Karl Heyes committed
607
608
            if (auth_get_url_auth (auth, options) < 0)
                return -1;
609
            break;
610
#else
611
            ICECAST_LOG_ERROR("Auth URL disabled");
Karl Heyes's avatar
Karl Heyes committed
612
            return -1;
613
#endif
614
        } else if (strcmp (auth->type, "htpasswd") == 0) {
Karl Heyes's avatar
Karl Heyes committed
615
616
            if (auth_get_htpasswd_auth (auth, options) < 0)
                return -1;
617
            break;
618
        }
Karl Heyes's avatar
Karl Heyes committed
619

620
        ICECAST_LOG_ERROR("Unrecognised authenticator type: \"%s\"", auth->type);
Karl Heyes's avatar
Karl Heyes committed
621
        return -1;
622
623
624
625
    } while (0);

    while (options)
    {
626
627
        if (strcmp (options->name, "allow_duplicate_users") == 0)
            auth->allow_duplicate_users = atoi ((char*)options->value);
628
        options = options->next;
629
    }
Karl Heyes's avatar
Karl Heyes committed
630
    return 0;
631
}
632
633


634
635
636
637
638
639
640
641
642
643
644
645
646
647
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;

    if (auth == NULL)
        return NULL;

    option = node->xmlChildrenNode;
    while (option)
    {
        xmlNodePtr current = option;
        option = option->next;
648
        if (xmlStrcmp (current->name, XMLSTR("option")) == 0)
649
650
        {
            config_options_t *opt = calloc (1, sizeof (config_options_t));
651
            opt->name = (char *)xmlGetProp (current, XMLSTR("name"));
652
653
654
655
656
            if (opt->name == NULL)
            {
                free(opt);
                continue;
            }
657
            opt->value = (char *)xmlGetProp (current, XMLSTR("value"));
658
659
660
661
662
663
664
665
666
667
            if (opt->value == NULL)
            {
                xmlFree (opt->name);
                free (opt);
                continue;
            }
            *next_option = opt;
            next_option = &opt->next;
        }
        else
668
            if (xmlStrcmp (current->name, XMLSTR("text")) != 0)
669
                ICECAST_LOG_WARN("unknown auth setting (%s)", current->name);
670
    }
671
    auth->type = (char*)xmlGetProp (node, XMLSTR("type"));
Karl Heyes's avatar
Karl Heyes committed
672
673
674
675
676
677
678
679
680
681
682
683
684
685
    if (get_authenticator (auth, options) < 0)
    {
        xmlFree (auth->type);
        free (auth);
        auth = NULL;
    }
    else
    {
        auth->tailp = &auth->head;
        thread_mutex_create (&auth->lock);
        auth->refcount = 1;
        auth->running = 1;
        auth->thread = thread_create ("auth thread", auth_run_thread, auth, THREAD_ATTACHED);
    }
686

687
688
689
690
691
692
693
    while (options)
    {
        config_options_t *opt = options;
        options = opt->next;
        xmlFree (opt->name);
        xmlFree (opt->value);
        free (opt);
694
    }
695
696
    return auth;
}
697
698


699
700
701
702
703
704
705
706
707
708
709
/* Called when a source client connects and requires authentication via the
 * authenticator. This is called for both source clients and admin requests
 * that work on a specified mountpoint.
 */
int auth_stream_authenticate (client_t *client, const char *mount, mount_proxy *mountinfo)
{
    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_auth)
    {
        auth_client *auth_user = auth_client_setup (mount, client);

        auth_user->process = stream_auth_callback;
710
        ICECAST_LOG_INFO("request source auth for \"%s\"", mount);
711
712
713
714
715
716
717
        queue_auth_client (auth_user, mountinfo);
        return 1;
    }
    return 0;
}


718
719
720
721
/* called when the stream starts, so that authentication engine can do any
 * cleanup/initialisation.
 */
void auth_stream_start (mount_proxy *mountinfo, const char *mount)
722
{
723
724
725
726
727
728
    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_start)
    {
        auth_client *auth_user = calloc (1, sizeof (auth_client));
        if (auth_user)
        {
            auth_user->mount = strdup (mount);
729
            auth_user->process = stream_start_callback;
730

731
            queue_auth_client (auth_user, mountinfo);
732
733
734
735
736
        }
    }
}


737
738
739
740
741
742
743
744
745
746
747
/* Called when the stream ends so that the authentication engine can do
 * any authentication cleanup
 */
void auth_stream_end (mount_proxy *mountinfo, const char *mount)
{
    if (mountinfo && mountinfo->auth && mountinfo->auth->stream_end)
    {
        auth_client *auth_user = calloc (1, sizeof (auth_client));
        if (auth_user)
        {
            auth_user->mount = strdup (mount);
748
            auth_user->process = stream_end_callback;
749

750
            queue_auth_client (auth_user, mountinfo);
751
        }
752
    }
753
}
754
755


756
/* these are called at server start and termination */
757

758
void auth_initialise (void)
759
{
760
761
}

762
void auth_shutdown (void)
763
{
764
    ICECAST_LOG_INFO("Auth shutdown");
765
}
766