auth_url.c 21.8 KB
Newer Older
Karl Heyes's avatar
Karl Heyes committed
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>,
Karl Heyes's avatar
Karl Heyes committed
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 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
Karl Heyes's avatar
Karl Heyes committed
12 13
 */

14
/*
Karl Heyes's avatar
Karl Heyes committed
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
 *
 * 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
 *
44 45 46 47 48 49 50 51
 * 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
52 53 54 55 56 57 58 59 60 61 62
 */

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

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef _WIN32
63 64
#   include <sys/wait.h>
#   include <strings.h>
Karl Heyes's avatar
Karl Heyes committed
65
#else
66 67
#   define snprintf _snprintf
#   define strncasecmp strnicmp
Karl Heyes's avatar
Karl Heyes committed
68 69
#endif

70
#include "util.h"
71
#include "curl.h"
Karl Heyes's avatar
Karl Heyes committed
72 73 74 75
#include "auth.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
76
#include "connection.h"
77
#include "common/httpp/httpp.h"
Karl Heyes's avatar
Karl Heyes committed
78 79 80 81

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

82 83 84 85 86 87 88 89
/* Default headers */
#define DEFAULT_HEADER_OLD_RESULT       "icecast-auth-user: 1\r\n"
#define DEFAULT_HEADER_OLD_TIMELIMIT    "icecast-auth-timelimit:"
#define DEFAULT_HEADER_OLD_MESSAGE      "icecast-auth-message"
#define DEFAULT_HEADER_NEW_RESULT       "x-icecast-auth-result"
#define DEFAULT_HEADER_NEW_TIMELIMIT    "x-icecast-auth-timelimit"
#define DEFAULT_HEADER_NEW_MESSAGE      "x-icecast-auth-message"

Karl Heyes's avatar
Karl Heyes committed
90
typedef struct {
Marvin Scholz's avatar
Marvin Scholz committed
91 92 93 94 95 96 97 98
    char       *pass_headers; // headers passed from client to addurl.
    char       *prefix_headers; // prefix for passed headers.
    char       *addurl;
    char       *removeurl;
    char       *addaction;
    char       *removeaction;
    char       *username;
    char       *password;
99 100

    /* old style */
Marvin Scholz's avatar
Marvin Scholz committed
101
    char       *auth_header;
102
    size_t      auth_header_len;
Marvin Scholz's avatar
Marvin Scholz committed
103
    char       *timelimit_header;
104 105 106 107
    size_t      timelimit_header_len;
    /* new style */
    char       *header_auth;
    char       *header_timelimit;
108
    char       *header_message;
109

Marvin Scholz's avatar
Marvin Scholz committed
110 111 112
    char       *userpwd;
    CURL       *handle;
    char        errormsg[CURL_ERROR_SIZE];
Philipp Schafft's avatar
Philipp Schafft committed
113
    auth_result result;
Karl Heyes's avatar
Karl Heyes committed
114 115
} auth_url;

116 117 118 119 120
typedef struct {
    char *all_headers;
    size_t all_headers_len;
    http_parser_t *parser;
} auth_user_url_t;
Karl Heyes's avatar
Karl Heyes committed
121 122

static void auth_url_clear(auth_t *self)
123 124
{
    auth_url *url;
125

126
    ICECAST_LOG_INFO("Doing auth URL cleanup");
127
    url = self->state;
128
    self->state = NULL;
129
    icecast_curl_free(url->handle);
Philipp Schafft's avatar
Philipp Schafft committed
130 131 132 133 134 135 136 137 138 139
    free(url->username);
    free(url->password);
    free(url->pass_headers);
    free(url->prefix_headers);
    free(url->removeurl);
    free(url->addurl);
    free(url->addaction);
    free(url->removeaction);
    free(url->auth_header);
    free(url->timelimit_header);
140 141
    free(url->header_auth);
    free(url->header_timelimit);
142
    free(url->header_message);
Philipp Schafft's avatar
Philipp Schafft committed
143 144
    free(url->userpwd);
    free(url);
Karl Heyes's avatar
Karl Heyes committed
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
static void auth_user_url_clear(auth_client *auth_user)
{
    auth_user_url_t *au_url = auth_user->authbackend_userdata;

    if (!au_url)
        return;

    free(au_url->all_headers);
    if (au_url->parser)
        httpp_destroy(au_url->parser);

    free(au_url);
    auth_user->authbackend_userdata = NULL;
}

static void handle_returned_header__complete(auth_client *auth_user)
{
    auth_user_url_t *au_url = auth_user->authbackend_userdata;
    const char *tmp;
    auth_url *url = auth_user->client->auth->state;

    if (!au_url)
        return;

    if (au_url->parser)
        return;

    au_url->parser = httpp_create_parser();
    httpp_initialize(au_url->parser, NULL);

    if (!httpp_parse_response(au_url->parser, au_url->all_headers, au_url->all_headers_len, NULL)) {
        ICECAST_LOG_ERROR("Can not parse auth backend reply.");
        return;
    }

    tmp = httpp_getvar(au_url->parser, HTTPP_VAR_ERROR_CODE);
    if (tmp[0] == '2') {
        ICECAST_LOG_DEBUG("Got final status: %#H", tmp);
    } else {
        ICECAST_LOG_DEBUG("Got non-final status: %#H", tmp);
        httpp_destroy(au_url->parser);
        au_url->parser = NULL;
        au_url->all_headers_len = 0;
        return;
    }

    if (url->header_auth) {
        tmp = httpp_getvar(au_url->parser, url->header_auth);
        if (tmp) {
            url->result = auth_str2result(tmp);
        }
    }

    if (url->header_timelimit) {
        tmp = httpp_getvar(au_url->parser, url->header_timelimit);
        if (tmp) {
            long long int ret;
            char *endptr;

            errno = 0;
            ret = strtoll(tmp, &endptr, 0);
            if (endptr != tmp && errno == 0) {
                auth_user->client->con->discon_time = time(NULL) + (time_t)ret;
            } else {
                ICECAST_LOG_ERROR("Auth backend returned invalid new style timelimit header: % #H", tmp);
            }
        }
    }

216 217 218 219 220 221 222
    if (url->header_message) {
        tmp = httpp_getvar(au_url->parser, url->header_message);
    } else {
        tmp = httpp_getvar(au_url->parser, DEFAULT_HEADER_NEW_MESSAGE);
        if (!tmp)
            tmp = httpp_getvar(au_url->parser, DEFAULT_HEADER_OLD_MESSAGE);
    }
223 224 225 226 227
    if (tmp) {
        snprintf(url->errormsg, sizeof(url->errormsg), "%s", tmp);
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
228 229 230 231
static size_t handle_returned_header(void      *ptr,
                                     size_t    size,
                                     size_t    nmemb,
                                     void      *stream)
Karl Heyes's avatar
Karl Heyes committed
232
{
233
    size_t len = size * nmemb;
Karl Heyes's avatar
Karl Heyes committed
234 235
    auth_client *auth_user = stream;
    client_t *client = auth_user->client;
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    auth_t *auth;
    auth_url *url;

    if (!client)
        return len;

    auth = client->auth;
    url = auth->state;

    if (!auth_user->authbackend_userdata) {
        auth_user->authbackend_userdata = calloc(1, sizeof(auth_user_url_t));
    }

    if (auth_user->authbackend_userdata) {
        auth_user_url_t *au_url = auth_user->authbackend_userdata;
        char *n = realloc(au_url->all_headers, au_url->all_headers_len + len);
        if (n) {
            au_url->all_headers = n;
            memcpy(n + au_url->all_headers_len, ptr, len);
            au_url->all_headers_len += len;
        } else {
            ICECAST_LOG_ERROR("Can not allocate buffer for auth backend reply headers. BAD.");
        }
    } else {
        ICECAST_LOG_ERROR("Can not allocate authbackend_userdata. BAD.");
    }
Karl Heyes's avatar
Karl Heyes committed
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    ICECAST_LOG_DEBUG("Got header: %* #H", (int)(size * nmemb + 2), ptr);

    if (url->auth_header && len >= url->auth_header_len && strncasecmp(ptr, url->auth_header, url->auth_header_len) == 0) {
        url->result = AUTH_OK;
    }

    if (url->timelimit_header && len > url->timelimit_header_len && strncasecmp(ptr, url->timelimit_header, url->timelimit_header_len) == 0) {
        const char *input = ptr;
        unsigned int limit = 0;

        if (len >= 2 && input[len - 2] == '\r' && input[len - 1] == '\n') {
            input += url->timelimit_header_len;

            if (sscanf(input, "%u\r\n", &limit) == 1) {
                client->con->discon_time = time(NULL) + limit;
            } else {
                ICECAST_LOG_ERROR("Auth backend returned invalid timeline header: Can not parse limit");
            }
        } else {
            ICECAST_LOG_ERROR("Auth backend returned invalid timelimit header.");
        }
    }

    if (len == 1) {
        const char *c = ptr;
        if (c[0] == '\r' || c[0] == '\n') {
            handle_returned_header__complete(auth_user);
290
        }
291 292 293 294
    } else if (len == 2) {
        const char *c = ptr;
        if ((c[0] == '\r' || c[0] == '\n') && (c[1] == '\r' || c[1] == '\n')) {
            handle_returned_header__complete(auth_user);
Karl Heyes's avatar
Karl Heyes committed
295 296 297
        }
    }

298 299

    return len;
Karl Heyes's avatar
Karl Heyes committed
300 301
}

Marvin Scholz's avatar
Marvin Scholz committed
302
static auth_result url_remove_client(auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
303
{
Marvin Scholz's avatar
Marvin Scholz committed
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    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;
    const char     *mountreq;
    ice_config_t   *config;
    int             port;
    char           *userpwd     = NULL,
                    post[4096];
    const char     *agent;
    char           *user_agent,
                   *ipaddr;
Karl Heyes's avatar
Karl Heyes committed
320

321 322
    if (url->removeurl == NULL)
        return AUTH_OK;
323

Marvin Scholz's avatar
Marvin Scholz committed
324 325
    config = config_get_config();
    server = util_url_escape(config->hostname);
Karl Heyes's avatar
Karl Heyes committed
326
    port = config->port;
Marvin Scholz's avatar
Marvin Scholz committed
327
    config_release_config();
Karl Heyes's avatar
Karl Heyes committed
328

Marvin Scholz's avatar
Marvin Scholz committed
329 330 331 332 333 334
    agent = httpp_getvar(client->parser, "user-agent");
    if (agent) {
        user_agent = util_url_escape(agent);
    } else {
        user_agent = strdup("-");
    }
335

Marvin Scholz's avatar
Marvin Scholz committed
336 337 338 339 340
    if (client->username) {
        username = util_url_escape(client->username);
    } else {
        username = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
341

Marvin Scholz's avatar
Marvin Scholz committed
342 343 344 345 346
    if (client->password) {
        password = util_url_escape(client->password);
    } else {
        password = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
347 348

    /* get the full uri (with query params if available) */
Marvin Scholz's avatar
Marvin Scholz committed
349
    mountreq = httpp_getvar(client->parser, HTTPP_VAR_RAWURI);
350
    if (mountreq == NULL)
Marvin Scholz's avatar
Marvin Scholz committed
351 352 353
        mountreq = httpp_getvar(client->parser, HTTPP_VAR_URI);
    mount = util_url_escape(mountreq);
    ipaddr = util_url_escape(client->con->ip);
Karl Heyes's avatar
Karl Heyes committed
354

Marvin Scholz's avatar
Marvin Scholz committed
355
    snprintf(post, sizeof (post),
Philipp Schafft's avatar
Philipp Schafft committed
356
            "action=%s&server=%s&port=%d&client=%lu&mount=%s"
357
            "&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s",
Philipp Schafft's avatar
Philipp Schafft committed
358
            url->removeaction, /* already escaped */
Karl Heyes's avatar
Karl Heyes committed
359
            server, port, client->con->id, mount, username,
360
            password, (long unsigned)duration, ipaddr, user_agent);
Marvin Scholz's avatar
Marvin Scholz committed
361 362 363 364 365 366 367 368 369 370 371 372

    free(server);
    free(mount);
    free(username);
    free(password);
    free(ipaddr);
    free(user_agent);

    if (strchr (url->removeurl, '@') == NULL) {
        if (url->userpwd) {
            curl_easy_setopt(url->handle, CURLOPT_USERPWD, url->userpwd);
        } else {
373
            /* auth'd requests may not have a user/pass, but may use query args */
Marvin Scholz's avatar
Marvin Scholz committed
374 375 376 377 378 379 380 381 382
            if (client->username && client->password) {
                size_t len = strlen(client->username) +
                    strlen(client->password) + 2;
                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, "");
383 384
            }
        }
Marvin Scholz's avatar
Marvin Scholz committed
385
    } else {
386
        /* url has user/pass but libcurl may need to clear any existing settings */
Marvin Scholz's avatar
Marvin Scholz committed
387
        curl_easy_setopt(url->handle, CURLOPT_USERPWD, "");
388
    }
Marvin Scholz's avatar
Marvin Scholz committed
389 390 391
    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);
Karl Heyes's avatar
Karl Heyes committed
392 393

    if (curl_easy_perform (url->handle))
Marvin Scholz's avatar
Marvin Scholz committed
394 395
        ICECAST_LOG_WARN("auth to server %s failed with %s",
            url->removeurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
396

Marvin Scholz's avatar
Marvin Scholz committed
397
    free(userpwd);
398
    auth_user_url_clear(auth_user);
399

Karl Heyes's avatar
Karl Heyes committed
400 401 402 403
    return AUTH_OK;
}


Marvin Scholz's avatar
Marvin Scholz committed
404
static auth_result url_add_client(auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
405
{
Marvin Scholz's avatar
Marvin Scholz committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
    client_t       *client      = auth_user->client;
    auth_t         *auth        = client->auth;
    auth_url       *url         = auth->state;
    int             res         = 0,
                    port;
    const char     *agent;
    char           *user_agent,
                   *username,
                   *password;
    const char     *mountreq;
    char           *mount,
                   *ipaddr,
                   *server;
    ice_config_t   *config;
    char           *userpwd    = NULL, post [4096];
    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
427 428 429 430

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

Marvin Scholz's avatar
Marvin Scholz committed
431 432
    config = config_get_config();
    server = util_url_escape(config->hostname);
Karl Heyes's avatar
Karl Heyes committed
433
    port = config->port;
Marvin Scholz's avatar
Marvin Scholz committed
434
    config_release_config();
435

Marvin Scholz's avatar
Marvin Scholz committed
436 437 438 439 440 441
    agent = httpp_getvar(client->parser, "user-agent");
    if (agent) {
        user_agent = util_url_escape(agent);
    } else {
        user_agent = strdup("-");
    }
442

Marvin Scholz's avatar
Marvin Scholz committed
443 444 445 446 447
    if (client->username) {
        username = util_url_escape(client->username);
    } else {
        username = strdup("");
    }
448

Marvin Scholz's avatar
Marvin Scholz committed
449 450 451 452 453
    if (client->password) {
        password = util_url_escape(client->password);
    } else {
        password = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
454 455

    /* get the full uri (with query params if available) */
Marvin Scholz's avatar
Marvin Scholz committed
456
    mountreq = httpp_getvar(client->parser, HTTPP_VAR_RAWURI);
457
    if (mountreq == NULL)
Marvin Scholz's avatar
Marvin Scholz committed
458 459 460
        mountreq = httpp_getvar(client->parser, HTTPP_VAR_URI);
    mount = util_url_escape(mountreq);
    ipaddr = util_url_escape(client->con->ip);
Karl Heyes's avatar
Karl Heyes committed
461

Marvin Scholz's avatar
Marvin Scholz committed
462
    post_offset = snprintf(post, sizeof (post),
Philipp Schafft's avatar
Philipp Schafft committed
463
            "action=%s&server=%s&port=%d&client=%lu&mount=%s"
Karl Heyes's avatar
Karl Heyes committed
464
            "&user=%s&pass=%s&ip=%s&agent=%s",
Philipp Schafft's avatar
Philipp Schafft committed
465
            url->addaction, /* already escaped */
Karl Heyes's avatar
Karl Heyes committed
466 467
            server, port, client->con->id, mount, username,
            password, ipaddr, user_agent);
Marvin Scholz's avatar
Marvin Scholz committed
468 469 470 471 472 473 474

    free(server);
    free(mount);
    free(user_agent);
    free(username);
    free(password);
    free(ipaddr);
Karl Heyes's avatar
Karl Heyes committed
475

476 477
    pass_headers = NULL;
    if (url->pass_headers)
Marvin Scholz's avatar
Marvin Scholz committed
478 479
        pass_headers = strdup(url->pass_headers);
    if (pass_headers) {
480
        cur_header = pass_headers;
Marvin Scholz's avatar
Marvin Scholz committed
481 482 483
        while (cur_header) {
            next_header = strstr(cur_header, ",");
            if (next_header) {
484
                *next_header=0;
485
                next_header++;
486
            }
487 488

            header_val = httpp_getvar (client->parser, cur_header);
Marvin Scholz's avatar
Marvin Scholz committed
489
            if (header_val) {
490
                header_valesc = util_url_escape (header_val);
Marvin Scholz's avatar
Marvin Scholz committed
491 492 493 494 495 496
                post_offset += snprintf(post + post_offset,
                                        sizeof(post) - post_offset,
                                        "&%s%s=%s",
                                        url->prefix_headers ? url->prefix_headers : "",
                                        cur_header, header_valesc);
                free(header_valesc);
497 498
            }

499
            cur_header = next_header;
500 501 502
        }
    }

Marvin Scholz's avatar
Marvin Scholz committed
503 504 505 506
    if (strchr(url->addurl, '@') == NULL) {
        if (url->userpwd) {
            curl_easy_setopt(url->handle, CURLOPT_USERPWD, url->userpwd);
        } else {
507
            /* auth'd requests may not have a user/pass, but may use query args */
Marvin Scholz's avatar
Marvin Scholz committed
508 509
            if (client->username && client->password) {
                size_t len = strlen(client->username) + strlen(client->password) + 2;
510
                userpwd = malloc (len);
Marvin Scholz's avatar
Marvin Scholz committed
511 512 513 514
                snprintf(userpwd, len, "%s:%s",
                    client->username, client->password);
                curl_easy_setopt(url->handle, CURLOPT_USERPWD, userpwd);
            } else {
515
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
Marvin Scholz's avatar
Marvin Scholz committed
516
            }
517
        }
Marvin Scholz's avatar
Marvin Scholz committed
518
    } else {
519
        /* url has user/pass but libcurl may need to clear any existing settings */
Marvin Scholz's avatar
Marvin Scholz committed
520
        curl_easy_setopt(url->handle, CURLOPT_USERPWD, "");
521
    }
Marvin Scholz's avatar
Marvin Scholz committed
522 523 524
    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);
Karl Heyes's avatar
Karl Heyes committed
525 526
    url->errormsg[0] = '\0';

Philipp Schafft's avatar
Philipp Schafft committed
527
    url->result = AUTH_FAILED;
Marvin Scholz's avatar
Marvin Scholz committed
528
    res = curl_easy_perform(url->handle);
Karl Heyes's avatar
Karl Heyes committed
529

Marvin Scholz's avatar
Marvin Scholz committed
530
    free(userpwd);
531
    auth_user_url_clear(auth_user);
532

Marvin Scholz's avatar
Marvin Scholz committed
533 534 535
    if (res) {
        ICECAST_LOG_WARN("auth to server %s failed with %s",
            url->addurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
536 537 538
        return AUTH_FAILED;
    }
    /* we received a response, lets see what it is */
Philipp Schafft's avatar
Philipp Schafft committed
539
    if (url->result == AUTH_FAILED) {
Marvin Scholz's avatar
Marvin Scholz committed
540 541
        ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"",
            url->addurl, url->errormsg);
542
    }
Philipp Schafft's avatar
Philipp Schafft committed
543
    return url->result;
544 545
}

Marvin Scholz's avatar
Marvin Scholz committed
546 547 548
static auth_result auth_url_adduser(auth_t      *auth,
                                    const char  *username,
                                    const char  *password)
Karl Heyes's avatar
Karl Heyes committed
549 550 551 552
{
    return AUTH_FAILED;
}

Marvin Scholz's avatar
Marvin Scholz committed
553
static auth_result auth_url_deleteuser(auth_t *auth, const char *username)
Karl Heyes's avatar
Karl Heyes committed
554 555 556 557
{
    return AUTH_FAILED;
}

Marvin Scholz's avatar
Marvin Scholz committed
558
static auth_result auth_url_listuser(auth_t *auth, xmlNodePtr srcnode)
Karl Heyes's avatar
Karl Heyes committed
559 560 561 562
{
    return AUTH_FAILED;
}

563
int auth_get_url_auth(auth_t *authenticator, config_options_t *options)
Karl Heyes's avatar
Karl Heyes committed
564
{
Marvin Scholz's avatar
Marvin Scholz committed
565 566 567
    auth_url    *url_info;
    const char  *addaction      = "listener_add";
    const char  *removeaction   = "listener_remove";
Karl Heyes's avatar
Karl Heyes committed
568

Marvin Scholz's avatar
Marvin Scholz committed
569 570 571 572
    authenticator->free         = auth_url_clear;
    authenticator->adduser      = auth_url_adduser;
    authenticator->deleteuser   = auth_url_deleteuser;
    authenticator->listuser     = auth_url_listuser;
Karl Heyes's avatar
Karl Heyes committed
573

Marvin Scholz's avatar
Marvin Scholz committed
574 575
    url_info                    = calloc(1, sizeof(auth_url));
    authenticator->state        = url_info;
576

577
    /* force auth thread to call function. this makes sure the auth_t is attached to client */
Philipp Schafft's avatar
Philipp Schafft committed
578
    authenticator->authenticate_client = url_add_client;
579

Karl Heyes's avatar
Karl Heyes committed
580
    while(options) {
Philipp Schafft's avatar
Philipp Schafft committed
581
        if(strcmp(options->name, "username") == 0) {
582
            replace_string(&(url_info->username), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
583
        } else if(strcmp(options->name, "password") == 0) {
584
            replace_string(&(url_info->password), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
585
        } else if(strcmp(options->name, "headers") == 0) {
586
            replace_string(&(url_info->pass_headers), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
587
        } else if(strcmp(options->name, "header_prefix") == 0) {
588
            replace_string(&(url_info->prefix_headers), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
589
        } else if(strcmp(options->name, "client_add") == 0) {
590
            replace_string(&(url_info->addurl), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
591 592
        } else if(strcmp(options->name, "client_remove") == 0) {
            authenticator->release_client = url_remove_client;
593
            replace_string(&(url_info->removeurl), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
594 595 596 597 598
        } else if(strcmp(options->name, "action_add") == 0) {
            addaction = options->value;
        } else if(strcmp(options->name, "action_remove") == 0) {
            removeaction = options->value;
        } else if(strcmp(options->name, "auth_header") == 0) {
599
            replace_string(&(url_info->auth_header), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
600
        } else if (strcmp(options->name, "timelimit_header") == 0) {
601
            replace_string(&(url_info->timelimit_header), options->value);
602 603 604 605
        } else if (strcmp(options->name, "header_auth") == 0) {
            replace_string(&(url_info->header_auth), options->value);
        } else if (strcmp(options->name, "header_timelimit") == 0) {
            replace_string(&(url_info->header_timelimit), options->value);
606 607
        } else if (strcmp(options->name, "header_message") == 0) {
            replace_string(&(url_info->header_message), options->value);
Philipp Schafft's avatar
Philipp Schafft committed
608 609
        } else {
            ICECAST_LOG_ERROR("Unknown option: %s", options->name);
610
        }
Karl Heyes's avatar
Karl Heyes committed
611 612
        options = options->next;
    }
Philipp Schafft's avatar
Philipp Schafft committed
613 614 615 616

    url_info->addaction = util_url_escape(addaction);
    url_info->removeaction = util_url_escape(removeaction);

617
    url_info->handle = icecast_curl_new(NULL, &url_info->errormsg[0]);
Marvin Scholz's avatar
Marvin Scholz committed
618 619
    if (url_info->handle == NULL) {
        auth_url_clear(authenticator);
Karl Heyes's avatar
Karl Heyes committed
620 621
        return -1;
    }
Philipp Schafft's avatar
Philipp Schafft committed
622

623
    /* default headers */
624 625 626 627 628 629 630 631 632 633 634 635 636 637
    if (url_info->auth_header) {
        ICECAST_LOG_WARN("You use old style auth option \"auth_header\". Please switch to new style option \"header_auth\".");
    } else if (!url_info->header_auth && !url_info->auth_header) {
        ICECAST_LOG_WARN("You do not have enabled old or new style auth option for auth status header. I will enable both. Please set \"header_auth\".");
        url_info->auth_header = strdup(DEFAULT_HEADER_OLD_RESULT);
        url_info->header_auth = strdup(DEFAULT_HEADER_NEW_RESULT);
    }
    if (url_info->timelimit_header) {
        ICECAST_LOG_WARN("You use old style auth option \"timelimit_header\". Please switch to new style option \"header_timelimit\".");
    } else if (!url_info->header_timelimit && !url_info->timelimit_header) {
        ICECAST_LOG_WARN("You do not have enabled old or new style auth option for auth timelimit header. I will enable both. Please set \"header_timelimit\".");
        url_info->timelimit_header = strdup(DEFAULT_HEADER_OLD_TIMELIMIT);
        url_info->timelimit_header = strdup(DEFAULT_HEADER_NEW_TIMELIMIT);
    }
638

Karl Heyes's avatar
Karl Heyes committed
639 640
    if (url_info->auth_header)
        url_info->auth_header_len = strlen (url_info->auth_header);
641 642
    if (url_info->timelimit_header)
        url_info->timelimit_header_len = strlen (url_info->timelimit_header);
Karl Heyes's avatar
Karl Heyes committed
643

Marvin Scholz's avatar
Marvin Scholz committed
644
    curl_easy_setopt(url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
Karl Heyes's avatar
Karl Heyes committed
645

Marvin Scholz's avatar
Marvin Scholz committed
646 647 648 649 650
    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);
651 652
    }

653
    ICECAST_LOG_INFO("URL based authentication setup");
Karl Heyes's avatar
Karl Heyes committed
654 655
    return 0;
}