mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
raop: add authentication
This commit is contained in:
parent
5d6690ebe7
commit
0573944e59
1 changed files with 131 additions and 4 deletions
|
|
@ -41,6 +41,7 @@
|
||||||
#include <openssl/rsa.h>
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/engine.h>
|
#include <openssl/engine.h>
|
||||||
#include <openssl/aes.h>
|
#include <openssl/aes.h>
|
||||||
|
#include <openssl/md5.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
|
@ -75,7 +76,14 @@ PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME);
|
||||||
#define DEFAULT_UDP_CONTROL_PORT 6001
|
#define DEFAULT_UDP_CONTROL_PORT 6001
|
||||||
#define DEFAULT_UDP_TIMING_PORT 6002
|
#define DEFAULT_UDP_TIMING_PORT 6002
|
||||||
|
|
||||||
#define AES_CHUNK_SIZE 16
|
#define AES_CHUNK_SIZE 16
|
||||||
|
#ifndef MD5_DIGEST_LENGTH
|
||||||
|
#define MD5_DIGEST_LENGTH 16
|
||||||
|
#endif
|
||||||
|
#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
|
||||||
|
|
||||||
|
#define DEFAULT_USER_AGENT "iTunes/11.0.4 (Windows; N)"
|
||||||
|
#define DEFAULT_USER_NAME "iTunes"
|
||||||
|
|
||||||
#define MAX_PORT_RETRY 128
|
#define MAX_PORT_RETRY 128
|
||||||
|
|
||||||
|
|
@ -146,6 +154,7 @@ struct impl {
|
||||||
struct pw_properties *headers;
|
struct pw_properties *headers;
|
||||||
|
|
||||||
char session_id[32];
|
char session_id[32];
|
||||||
|
char *password;
|
||||||
|
|
||||||
unsigned int do_disconnect:1;
|
unsigned int do_disconnect:1;
|
||||||
unsigned int unloading:1;
|
unsigned int unloading:1;
|
||||||
|
|
@ -994,13 +1003,129 @@ static int rtsp_do_announce(struct impl *impl)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *find_attr(char **tokens, const char *key)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *p, *s;
|
||||||
|
for (i = 0; tokens[i]; i++) {
|
||||||
|
if (!spa_strstartswith(tokens[i], key))
|
||||||
|
continue;
|
||||||
|
p = tokens[i] + strlen(key);
|
||||||
|
if ((s = rindex(p, '"')) == NULL)
|
||||||
|
continue;
|
||||||
|
*s = '\0';
|
||||||
|
if ((s = index(p, '"')) == NULL)
|
||||||
|
continue;
|
||||||
|
return s+1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rtsp_auth_reply(void *data, int status, const struct spa_dict *headers)
|
||||||
|
{
|
||||||
|
struct impl *impl = data;
|
||||||
|
pw_log_info("auth %d", status);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 200:
|
||||||
|
rtsp_do_announce(impl);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPA_PRINTF_FUNC(2,3)
|
||||||
|
static int MD5_hash(char hash[MD5_HASH_LENGTH+1], const char *fmt, ...)
|
||||||
|
{
|
||||||
|
unsigned char d[MD5_DIGEST_LENGTH];
|
||||||
|
int i;
|
||||||
|
va_list args;
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
va_start(args, fmt);
|
||||||
|
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
MD5((unsigned char*) buffer, strlen(buffer), d);
|
||||||
|
for (i = 0; i < MD5_DIGEST_LENGTH; i++)
|
||||||
|
sprintf(&hash[2*i], "%02x", (uint8_t) d[i]);
|
||||||
|
hash[MD5_HASH_LENGTH] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rtsp_do_auth(struct impl *impl, const struct spa_dict *headers)
|
||||||
|
{
|
||||||
|
const char *str;
|
||||||
|
char **tokens;
|
||||||
|
int n_tokens;
|
||||||
|
char auth[1024];
|
||||||
|
|
||||||
|
if (impl->password == NULL)
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
if ((str = spa_dict_lookup(headers, "WWW-Authenticate")) == NULL)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
pw_log_info("Auth: %s", str);
|
||||||
|
|
||||||
|
tokens = pw_split_strv(str, " ", INT_MAX, &n_tokens);
|
||||||
|
if (tokens == NULL || tokens[0] == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (spa_streq(tokens[0], "Basic")) {
|
||||||
|
char buf[256];
|
||||||
|
char enc[512];
|
||||||
|
spa_scnprintf(buf, sizeof(buf), "%s:%s", DEFAULT_USER_NAME, impl->password);
|
||||||
|
base64_encode((uint8_t*)buf, strlen(buf), enc, '=');
|
||||||
|
spa_scnprintf(auth, sizeof(auth), "Basic %s", enc);
|
||||||
|
}
|
||||||
|
else if (spa_streq(tokens[0], "Digest")) {
|
||||||
|
const char *realm, *nonce;
|
||||||
|
char h1[MD5_HASH_LENGTH+1];
|
||||||
|
char h2[MD5_HASH_LENGTH+1];
|
||||||
|
char resp[MD5_HASH_LENGTH+1];
|
||||||
|
|
||||||
|
realm = find_attr(tokens, "realm");
|
||||||
|
nonce = find_attr(tokens, "nonce");
|
||||||
|
if (realm == NULL || nonce == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
MD5_hash(h1, "%s:%s:%s", DEFAULT_USER_NAME, realm, impl->password);
|
||||||
|
MD5_hash(h2, "OPTIONS:*");
|
||||||
|
MD5_hash(resp, "%s:%s:%s", h1, nonce, h2);
|
||||||
|
|
||||||
|
spa_scnprintf(auth, sizeof(auth),
|
||||||
|
"Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"*\", response=\"%s\"",
|
||||||
|
DEFAULT_USER_NAME, realm, nonce, resp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pw_properties_setf(impl->headers, "Authorization", "%s %s",
|
||||||
|
tokens[0], auth);
|
||||||
|
pw_free_strv(tokens);
|
||||||
|
|
||||||
|
pw_rtsp_client_send(impl->rtsp, "OPTIONS", &impl->headers->dict,
|
||||||
|
NULL, NULL, rtsp_auth_reply, impl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
pw_free_strv(tokens);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
static void rtsp_options_reply(void *data, int status, const struct spa_dict *headers)
|
static void rtsp_options_reply(void *data, int status, const struct spa_dict *headers)
|
||||||
{
|
{
|
||||||
struct impl *impl = data;
|
struct impl *impl = data;
|
||||||
pw_log_info("options");
|
pw_log_info("options %d", status);
|
||||||
|
|
||||||
rtsp_do_announce(impl);
|
switch (status) {
|
||||||
|
case 401:
|
||||||
|
rtsp_do_auth(impl, headers);
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
rtsp_do_announce(impl);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rtsp_connected(void *data)
|
static void rtsp_connected(void *data)
|
||||||
|
|
@ -1256,7 +1381,7 @@ static void impl_destroy(struct impl *impl)
|
||||||
pw_properties_free(impl->headers);
|
pw_properties_free(impl->headers);
|
||||||
pw_properties_free(impl->stream_props);
|
pw_properties_free(impl->stream_props);
|
||||||
pw_properties_free(impl->props);
|
pw_properties_free(impl->props);
|
||||||
|
free(impl->password);
|
||||||
if (impl->work)
|
if (impl->work)
|
||||||
pw_work_queue_cancel(impl->work, impl, SPA_ID_INVALID);
|
pw_work_queue_cancel(impl->work, impl, SPA_ID_INVALID);
|
||||||
free(impl);
|
free(impl);
|
||||||
|
|
@ -1488,6 +1613,8 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
|
||||||
pw_log_error( "can't handle codec type %s", str);
|
pw_log_error( "can't handle codec type %s", str);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
str = pw_properties_get(props, "raop.password");
|
||||||
|
impl->password = str ? strdup(str) : NULL;
|
||||||
|
|
||||||
impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
|
impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core);
|
||||||
if (impl->core == NULL) {
|
if (impl->core == NULL) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue