auth_url.c 23.4 KB
Newer Older
Karl Heyes's avatar
Karl Heyes committed
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 2011-2012, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
Karl Heyes's avatar
Karl Heyes committed
12 13 14 15 16 17 18 19 20
 */

/* 
 * Client authentication via URL functions
 *
 * authenticate user via a URL, this is done via libcurl so https can also
 * be handled. The request will have POST information about the request in
 * the form of
 *
21
 * action=listener_add&client=1&server=host&port=8000&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&agent=""
Karl Heyes's avatar
Karl Heyes committed
22 23 24 25 26 27
 *
 * For a user to be accecpted the following HTTP header needs
 * to be returned (the actual string can be specified in the xml file)
 *
 * icecast-auth-user: 1
 *
28 29 30 31 32 33
 * A listening client may also be configured as only to stay connected for a
 * certain length of time. eg The auth server may only allow a 15 minute
 * playback by sending back.
 *
 * icecast-auth-timelimit: 900
 *
Karl Heyes's avatar
Karl Heyes committed
34 35 36
 * On client disconnection another request can be sent to a URL with the POST
 * information of
 *
37
 * action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600
Karl Heyes's avatar
Karl Heyes committed
38 39 40 41 42 43 44 45 46 47
 *
 * client refers to the icecast client identification number. mount refers
 * to the mountpoint (beginning with / and may contain query parameters eg ?&
 * encoded) and duration is the amount of time in seconds. user and pass
 * setting can be blank
 *
 * On stream start and end, another url can be issued to help clear any user
 * info stored at the auth server. Useful for abnormal outage/termination
 * cases.
 *
48 49
 * action=mount_add&mount=/live&server=myserver.com&port=8000
 * action=mount_remove&mount=/live&server=myserver.com&port=8000
50 51 52 53 54 55 56 57 58
 *
 * On source client connection, a request can be made to trigger a URL request
 * to verify the details externally. Post info is
 *
 * action=stream_auth&mount=/stream&ip=IP&server=SERVER&port=8000&user=fred&pass=pass
 *
 * As admin requests can come in for a stream (eg metadata update) these requests
 * can be issued while stream is active. For these &admin=1 is added to the POST
 * details.
Karl Heyes's avatar
Karl Heyes committed
59 60 61 62 63 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
 */

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

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef _WIN32
#include <sys/wait.h>
#include <strings.h>
#else
#define snprintf _snprintf
#define strncasecmp strnicmp
#endif

#include <curl/curl.h>

#include "auth.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
#include "httpp/httpp.h"

#include "logging.h"
#define CATMODULE "auth_url"

typedef struct {
89 90
    char *pass_headers; // headers passed from client to addurl.
    char *prefix_headers; // prefix for passed headers.
Karl Heyes's avatar
Karl Heyes committed
91 92 93 94
    char *addurl;
    char *removeurl;
    char *stream_start;
    char *stream_end;
95
    char *stream_auth;
Karl Heyes's avatar
Karl Heyes committed
96 97 98 99
    char *username;
    char *password;
    char *auth_header;
    int  auth_header_len;
100 101
    char *timelimit_header;
    int  timelimit_header_len;
102
    char *userpwd;
Karl Heyes's avatar
Karl Heyes committed
103 104 105 106 107 108
    CURL *handle;
    char errormsg [CURL_ERROR_SIZE];
} auth_url;


static void auth_url_clear(auth_t *self)
109 110
{
    auth_url *url;
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
111

112
    ICECAST_LOG_INFO("Doing auth URL cleanup");
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
113
    url = self->state;
114
    self->state = NULL;
Karl Heyes's avatar
Karl Heyes committed
115 116 117
    curl_easy_cleanup (url->handle);
    free (url->username);
    free (url->password);
118 119
    free (url->pass_headers);
    free (url->prefix_headers);
Karl Heyes's avatar
Karl Heyes committed
120 121 122 123 124
    free (url->removeurl);
    free (url->addurl);
    free (url->stream_start);
    free (url->stream_end);
    free (url->auth_header);
125
    free (url->timelimit_header);
126
    free (url->userpwd);
Karl Heyes's avatar
Karl Heyes committed
127 128 129 130
    free (url);
}


131
#ifdef CURLOPT_PASSWDFUNCTION
132 133 134 135 136 137
/* make sure that prompting at the console does not occur */
static int my_getpass(void *client, char *prompt, char *buffer, int buflen)
{
    buffer[0] = '\0';
    return 0;
}
138
#endif
139 140


141
static size_t handle_returned_header (void *ptr, size_t size, size_t nmemb, void *stream)
Karl Heyes's avatar
Karl Heyes committed
142 143 144 145 146 147 148 149 150 151 152
{
    auth_client *auth_user = stream;
    unsigned bytes = size * nmemb;
    client_t *client = auth_user->client;

    if (client)
    {
        auth_t *auth = client->auth;
        auth_url *url = auth->state;
        if (strncasecmp (ptr, url->auth_header, url->auth_header_len) == 0)
            client->authenticated = 1;
153 154 155 156 157 158
        if (strncasecmp (ptr, url->timelimit_header, url->timelimit_header_len) == 0)
        {
            unsigned int limit = 0;
            sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit);
            client->con->discon_time = time(NULL) + limit;
        }
Karl Heyes's avatar
Karl Heyes committed
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
        if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0)
        {
            char *eol;
            snprintf (url->errormsg, sizeof (url->errormsg), "%s", (char*)ptr+22);
            eol = strchr (url->errormsg, '\r');
            if (eol == NULL)
                eol = strchr (url->errormsg, '\n');
            if (eol)
                *eol = '\0';
        }
    }

    return (int)bytes;
}

/* capture returned data, but don't do anything with it */
175
static size_t handle_returned_data (void *ptr, size_t size, size_t nmemb, void *stream)
Karl Heyes's avatar
Karl Heyes committed
176 177 178 179 180
{
    return (int)(size*nmemb);
}


181
static auth_result url_remove_listener (auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
182 183 184 185 186 187
{
    client_t *client = auth_user->client;
    auth_t *auth = client->auth;
    auth_url *url = auth->state;
    time_t duration = time(NULL) - client->con->con_time;
    char *username, *password, *mount, *server;
188
    const char *mountreq;
Karl Heyes's avatar
Karl Heyes committed
189 190
    ice_config_t *config;
    int port;
191
    char *userpwd = NULL, post [4096];
192 193
    const char *agent;
    char *user_agent, *ipaddr;
194
    int ret;
Karl Heyes's avatar
Karl Heyes committed
195

196 197
    if (url->removeurl == NULL)
        return AUTH_OK;
198

Karl Heyes's avatar
Karl Heyes committed
199 200 201 202 203
    config = config_get_config ();
    server = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();

204 205 206 207 208 209
    agent = httpp_getvar (client->parser, "user-agent");
    if (agent)
        user_agent = util_url_escape (agent);
    else
        user_agent = strdup ("-");

Karl Heyes's avatar
Karl Heyes committed
210 211 212 213 214 215 216 217 218 219 220
    if (client->username)
        username = util_url_escape (client->username);
    else
        username = strdup ("");

    if (client->password)
        password = util_url_escape (client->password);
    else
        password = strdup ("");

    /* get the full uri (with query params if available) */
221 222 223 224
    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
    if (mountreq == NULL)
        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
    mount = util_url_escape (mountreq);
225
    ipaddr = util_url_escape (client->con->ip);
Karl Heyes's avatar
Karl Heyes committed
226

227 228 229 230 231
    ret = snprintf(post, sizeof(post),
                   "action=listener_remove&server=%s&port=%d&client=%lu&mount=%s"
                   "&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s",
                   server, port, client->con->id, mount, username,
                   password, (long unsigned)duration, ipaddr, user_agent);
Karl Heyes's avatar
Karl Heyes committed
232 233 234 235
    free (server);
    free (mount);
    free (username);
    free (password);
236 237
    free (ipaddr);
    free (user_agent);
Karl Heyes's avatar
Karl Heyes committed
238

239 240 241 242 243
    if (ret <= 0 || ret >= sizeof(post)) {
        ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\" client %p.", auth_user->mount, client);
        return AUTH_FAILED;
    }

244 245 246 247 248 249 250 251 252
    if (strchr (url->removeurl, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
        {
            /* auth'd requests may not have a user/pass, but may use query args */
            if (client->username && client->password)
            {
253
                size_t len = strlen (client->username) + strlen (client->password) + 2;
254 255 256 257 258 259 260 261 262 263 264 265 266
                userpwd = malloc (len);
                snprintf (userpwd, len, "%s:%s", client->username, client->password);
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
            }
            else
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
        }
    }
    else
    {
        /* url has user/pass but libcurl may need to clear any existing settings */
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
Karl Heyes's avatar
Karl Heyes committed
267 268 269 270 271
    curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);

    if (curl_easy_perform (url->handle))
272
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->removeurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
273

274 275
    free (userpwd);

Karl Heyes's avatar
Karl Heyes committed
276 277 278 279
    return AUTH_OK;
}


280
static auth_result url_add_listener (auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
281 282 283 284 285
{
    client_t *client = auth_user->client;
    auth_t *auth = client->auth;
    auth_url *url = auth->state;
    int res = 0, port;
286 287 288
    const char *agent;
    char *user_agent, *username, *password;
    const char *mountreq;
Karl Heyes's avatar
Karl Heyes committed
289 290
    char *mount, *ipaddr, *server;
    ice_config_t *config;
291
    char *userpwd = NULL, post [4096];
292 293 294 295
    ssize_t post_offset;
    char *pass_headers, *cur_header, *next_header;
    const char *header_val;
    char *header_valesc;
Karl Heyes's avatar
Karl Heyes committed
296 297 298 299 300 301 302 303

    if (url->addurl == NULL)
        return AUTH_OK;

    config = config_get_config ();
    server = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();
304

Karl Heyes's avatar
Karl Heyes committed
305
    agent = httpp_getvar (client->parser, "user-agent");
306 307 308 309 310
    if (agent)
        user_agent = util_url_escape (agent);
    else
        user_agent = strdup ("-");

Karl Heyes's avatar
Karl Heyes committed
311
    if (client->username)
312
        username = util_url_escape (client->username);
Karl Heyes's avatar
Karl Heyes committed
313 314
    else
        username = strdup ("");
315

Karl Heyes's avatar
Karl Heyes committed
316
    if (client->password)
317
        password = util_url_escape (client->password);
Karl Heyes's avatar
Karl Heyes committed
318 319 320 321
    else
        password = strdup ("");

    /* get the full uri (with query params if available) */
322 323 324 325
    mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI);
    if (mountreq == NULL)
        mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI);
    mount = util_url_escape (mountreq);
Karl Heyes's avatar
Karl Heyes committed
326 327
    ipaddr = util_url_escape (client->con->ip);

328
    post_offset = snprintf (post, sizeof (post),
329
            "action=listener_add&server=%s&port=%d&client=%lu&mount=%s"
Karl Heyes's avatar
Karl Heyes committed
330 331 332 333 334 335 336 337 338 339
            "&user=%s&pass=%s&ip=%s&agent=%s",
            server, port, client->con->id, mount, username,
            password, ipaddr, user_agent);
    free (server);
    free (mount);
    free (user_agent);
    free (username);
    free (password);
    free (ipaddr);

340 341 342 343 344
    if (post_offset <= 0 || post_offset >= sizeof(post)) {
        ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\" client %p.", auth_user->mount, client);
        return AUTH_FAILED;
    }

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
    pass_headers = NULL;
    if (url->pass_headers)
        pass_headers = strdup (url->pass_headers);
    if (pass_headers)
    {
        cur_header = pass_headers;
        while (cur_header)
        {
	    next_header = strstr (cur_header, ",");
	    if (next_header)
	    {
		*next_header=0;
                next_header++;
	    }

            header_val = httpp_getvar (client->parser, cur_header);
            if (header_val)
            {
363 364
                size_t left = sizeof(post) - post_offset;
                int ret;
365
                header_valesc = util_url_escape (header_val);
366
                ret = snprintf (post+post_offset, left, "&%s%s=%s",
367 368 369
                                         url->prefix_headers ? url->prefix_headers : "",
                                         cur_header, header_valesc);
                free (header_valesc);
370 371 372 373 374 375 376 377

                if (ret <= 0 || ret >= left) {
                    ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\" client %p.", auth_user->mount, client);
                    free(pass_headers);
                    return AUTH_FAILED;
                } else {
                    post_offset += ret;
                }
378 379 380 381
            }

	    cur_header = next_header;
        }
Philipp Schafft's avatar
Philipp Schafft committed
382
        free(pass_headers);
383 384
    }

385 386 387 388 389 390 391 392 393
    if (strchr (url->addurl, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
        {
            /* auth'd requests may not have a user/pass, but may use query args */
            if (client->username && client->password)
            {
394
                size_t len = strlen (client->username) + strlen (client->password) + 2;
395 396 397 398 399 400 401 402 403 404 405 406 407
                userpwd = malloc (len);
                snprintf (userpwd, len, "%s:%s", client->username, client->password);
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd);
            }
            else
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
        }
    }
    else
    {
        /* url has user/pass but libcurl may need to clear any existing settings */
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
Karl Heyes's avatar
Karl Heyes committed
408 409 410 411 412 413 414
    curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
    url->errormsg[0] = '\0';

    res = curl_easy_perform (url->handle);

415 416
    free (userpwd);

Karl Heyes's avatar
Karl Heyes committed
417 418
    if (res)
    {
419
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->addurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
420 421 422 423 424
        return AUTH_FAILED;
    }
    /* we received a response, lets see what it is */
    if (client->authenticated)
        return AUTH_OK;
425
    ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
426 427 428 429 430 431 432 433 434 435 436
    return AUTH_FAILED;
}


/* called by auth thread when a source starts, there is no client_t in
 * this case
 */
static void url_stream_start (auth_client *auth_user)
{
    char *mount, *server;
    ice_config_t *config = config_get_config ();
437
    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
Karl Heyes's avatar
Karl Heyes committed
438 439 440 441 442
    auth_t *auth = mountinfo->auth;
    auth_url *url = auth->state;
    char *stream_start_url;
    int port;
    char post [4096];
443
    int ret;
Karl Heyes's avatar
Karl Heyes committed
444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

    if (url->stream_start == NULL)
    {
        config_release_config ();
        return;
    }
    server = util_url_escape (config->hostname);
    port = config->port;
    stream_start_url = strdup (url->stream_start);
    /* we don't want this auth disappearing from under us while
     * the connection is in progress */
    mountinfo->auth->refcount++;
    config_release_config ();
    mount = util_url_escape (auth_user->mount);

459
    ret = snprintf(post, sizeof(post), "action=mount_add&mount=%s&server=%s&port=%d", mount, server, port);
Karl Heyes's avatar
Karl Heyes committed
460 461 462
    free (server);
    free (mount);

463 464 465 466 467 468 469
    if (ret <= 0 || ret >= sizeof(post)) {
        ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\".", auth_user->mount);
        auth_release(auth);
        free(stream_start_url);
        return;
    }

470 471 472 473 474 475 476 477 478
    if (strchr (url->stream_start, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    else
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
Karl Heyes's avatar
Karl Heyes committed
479 480 481 482 483
    curl_easy_setopt (url->handle, CURLOPT_URL, stream_start_url);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);

    if (curl_easy_perform (url->handle))
484
        ICECAST_LOG_WARN("auth to server %s failed with %s", stream_start_url, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
485 486 487 488 489 490 491 492 493 494 495

    auth_release (auth);
    free (stream_start_url);
    return;
}


static void url_stream_end (auth_client *auth_user)
{
    char *mount, *server;
    ice_config_t *config = config_get_config ();
496
    mount_proxy *mountinfo = config_find_mount (config, auth_user->mount, MOUNT_TYPE_NORMAL);
Karl Heyes's avatar
Karl Heyes committed
497 498 499 500 501
    auth_t *auth = mountinfo->auth;
    auth_url *url = auth->state;
    char *stream_end_url;
    int port;
    char post [4096];
502
    int ret;
Karl Heyes's avatar
Karl Heyes committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

    if (url->stream_end == NULL)
    {
        config_release_config ();
        return;
    }
    server = util_url_escape (config->hostname);
    port = config->port;
    stream_end_url = strdup (url->stream_end);
    /* we don't want this auth disappearing from under us while
     * the connection is in progress */
    mountinfo->auth->refcount++;
    config_release_config ();
    mount = util_url_escape (auth_user->mount);

518
    ret = snprintf(post, sizeof(post), "action=mount_remove&mount=%s&server=%s&port=%d", mount, server, port);
Karl Heyes's avatar
Karl Heyes committed
519 520 521
    free (server);
    free (mount);

522 523 524 525 526 527 528
    if (ret <= 0 || ret >= sizeof(post)) {
        ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\".", auth_user->mount);
        auth_release(auth);
        free(stream_end_url);
        return;
    }

529 530 531 532 533 534 535 536 537 538
    if (strchr (url->stream_end, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    else
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_end);
Karl Heyes's avatar
Karl Heyes committed
539 540 541 542
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);

    if (curl_easy_perform (url->handle))
543
        ICECAST_LOG_WARN("auth to server %s failed with %s", stream_end_url, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
544 545 546 547 548 549

    auth_release (auth);
    free (stream_end_url);
    return;
}

550 551 552 553 554 555 556 557
static void url_stream_auth (auth_client *auth_user)
{
    ice_config_t *config;
    int port;
    client_t *client = auth_user->client;
    auth_url *url = client->auth->state;
    char *mount, *host, *user, *pass, *ipaddr, *admin="";
    char post [4096];
558
    int ret;
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

    if (strchr (url->stream_auth, '@') == NULL)
    {
        if (url->userpwd)
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd);
        else
            curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    }
    else
        curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
    curl_easy_setopt (url->handle, CURLOPT_URL, url->stream_auth);
    curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user);
    if (strcmp (auth_user->mount, httpp_getvar (client->parser, HTTPP_VAR_URI)) != 0)
        admin = "&admin=1";
    mount = util_url_escape (auth_user->mount);
    config = config_get_config ();
    host = util_url_escape (config->hostname);
    port = config->port;
    config_release_config ();
    ipaddr = util_url_escape (client->con->ip);

581 582 583 584 585 586 587 588 589 590 591 592
    if (client->username) {
        user = util_url_escape(client->username);
    } else {
        user = strdup("");
    }

    if (client->password) {
        pass = util_url_escape(client->password);
    } else {
        pass = strdup("");
    }

593 594 595
    ret = snprintf(post, sizeof(post),
                   "action=stream_auth&mount=%s&ip=%s&server=%s&port=%d&user=%s&pass=%s%s",
                   mount, ipaddr, host, port, user, pass, admin);
596 597 598 599 600 601 602
    free (ipaddr);
    free (user);
    free (pass);
    free (mount);
    free (host);

    client->authenticated = 0;
603 604 605 606 607 608

    if (ret <= 0 || ret >= sizeof(post)) {
        ICECAST_LOG_ERROR("POST body too long for buffer on mount point \"%H\" client %p.", auth_user->mount, client);
        return;
    }

609
    if (curl_easy_perform (url->handle))
610
        ICECAST_LOG_WARN("auth to server %s failed with %s", url->stream_auth, url->errormsg);
611 612
}

Karl Heyes's avatar
Karl Heyes committed
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

static auth_result auth_url_adduser(auth_t *auth, const char *username, const char *password)
{
    return AUTH_FAILED;
}

static auth_result auth_url_deleteuser (auth_t *auth, const char *username)
{
    return AUTH_FAILED;
}

static auth_result auth_url_listuser (auth_t *auth, xmlNodePtr srcnode)
{
    return AUTH_FAILED;
}

int auth_get_url_auth (auth_t *authenticator, config_options_t *options)
{
    auth_url *url_info;

    authenticator->free = auth_url_clear;
    authenticator->adduser = auth_url_adduser;
    authenticator->deleteuser = auth_url_deleteuser;
    authenticator->listuser = auth_url_listuser;

    url_info = calloc(1, sizeof(auth_url));
639
    authenticator->state = url_info;
640 641

    /* default headers */
Karl Heyes's avatar
Karl Heyes committed
642
    url_info->auth_header = strdup ("icecast-auth-user: 1\r\n");
643
    url_info->timelimit_header = strdup ("icecast-auth-timelimit:");
Karl Heyes's avatar
Karl Heyes committed
644

645 646 647
    /* force auth thread to call function. this makes sure the auth_t is attached to client */
    authenticator->authenticate = url_add_listener;

Karl Heyes's avatar
Karl Heyes committed
648 649
    while(options) {
        if(!strcmp(options->name, "username"))
650 651
        {
            free (url_info->username);
Karl Heyes's avatar
Karl Heyes committed
652
            url_info->username = strdup (options->value);
653
        }
Karl Heyes's avatar
Karl Heyes committed
654
        if(!strcmp(options->name, "password"))
655 656
        {
            free (url_info->password);
Karl Heyes's avatar
Karl Heyes committed
657
            url_info->password = strdup (options->value);
658
        }
659 660 661 662 663 664 665 666 667 668
        if(!strcmp(options->name, "headers"))
        {
            free (url_info->pass_headers);
            url_info->pass_headers = strdup (options->value);
        }
        if(!strcmp(options->name, "header_prefix"))
        {
            free (url_info->prefix_headers);
            url_info->prefix_headers = strdup (options->value);
        }
669
        if(!strcmp(options->name, "listener_add"))
670 671
        {
            free (url_info->addurl);
Karl Heyes's avatar
Karl Heyes committed
672
            url_info->addurl = strdup (options->value);
673
        }
674
        if(!strcmp(options->name, "listener_remove"))
675 676 677
        {
            authenticator->release_listener = url_remove_listener;
            free (url_info->removeurl);
Karl Heyes's avatar
Karl Heyes committed
678
            url_info->removeurl = strdup (options->value);
679
        }
680
        if(!strcmp(options->name, "mount_add"))
681 682 683
        {
            authenticator->stream_start = url_stream_start;
            free (url_info->stream_start);
Karl Heyes's avatar
Karl Heyes committed
684
            url_info->stream_start = strdup (options->value);
685
        }
686
        if(!strcmp(options->name, "mount_remove"))
687 688 689
        {
            authenticator->stream_end = url_stream_end;
            free (url_info->stream_end);
Karl Heyes's avatar
Karl Heyes committed
690
            url_info->stream_end = strdup (options->value);
691
        }
692 693 694 695 696 697
        if(!strcmp(options->name, "stream_auth"))
        {
            authenticator->stream_auth = url_stream_auth;
            free (url_info->stream_auth);
            url_info->stream_auth = strdup (options->value);
        }
Karl Heyes's avatar
Karl Heyes committed
698 699 700 701 702
        if(!strcmp(options->name, "auth_header"))
        {
            free (url_info->auth_header);
            url_info->auth_header = strdup (options->value);
        }
703
        if (strcmp(options->name, "timelimit_header") == 0)
704 705 706 707
        {
            free (url_info->timelimit_header);
            url_info->timelimit_header = strdup (options->value);
        }
Karl Heyes's avatar
Karl Heyes committed
708 709 710 711 712
        options = options->next;
    }
    url_info->handle = curl_easy_init ();
    if (url_info->handle == NULL)
    {
713
        auth_url_clear (authenticator);
Karl Heyes's avatar
Karl Heyes committed
714 715 716 717
        return -1;
    }
    if (url_info->auth_header)
        url_info->auth_header_len = strlen (url_info->auth_header);
718 719
    if (url_info->timelimit_header)
        url_info->timelimit_header_len = strlen (url_info->timelimit_header);
Karl Heyes's avatar
Karl Heyes committed
720 721 722 723 724 725 726

    curl_easy_setopt (url_info->handle, CURLOPT_USERAGENT, ICECAST_VERSION_STRING);
    curl_easy_setopt (url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
    curl_easy_setopt (url_info->handle, CURLOPT_WRITEFUNCTION, handle_returned_data);
    curl_easy_setopt (url_info->handle, CURLOPT_WRITEDATA, url_info->handle);
    curl_easy_setopt (url_info->handle, CURLOPT_NOSIGNAL, 1L);
    curl_easy_setopt (url_info->handle, CURLOPT_TIMEOUT, 15L);
727
#ifdef CURLOPT_PASSWDFUNCTION
728
    curl_easy_setopt (url_info->handle, CURLOPT_PASSWDFUNCTION, my_getpass);
729
#endif
Karl Heyes's avatar
Karl Heyes committed
730 731
    curl_easy_setopt (url_info->handle, CURLOPT_ERRORBUFFER, &url_info->errormsg[0]);

732 733 734 735 736 737 738
    if (url_info->username && url_info->password)
    {
        int len = strlen (url_info->username) + strlen (url_info->password) + 2;
        url_info->userpwd = malloc (len);
        snprintf (url_info->userpwd, len, "%s:%s", url_info->username, url_info->password);
    }

739
    ICECAST_LOG_INFO("URL based authentication setup");
Karl Heyes's avatar
Karl Heyes committed
740 741 742
    return 0;
}