>From 000c5e7faaa233e0adf1ee550237bc79b706a6aa Mon Sep 17 00:00:00 2001
From: Yclept Nemo <pscjtwjdjtAhnbjm/dpn>
Date: Mon, 18 May 2015 14:07:13 -0400
Subject: [PATCH 2/2] smbclient auth support
There is now a backend block which in turns provides several password
configuration backends:
Configuration file:
backend {
name "smbclient"
password_backend "config"
username "ExampleName"
password "ExamplePassword"
}
Libsecret (keyring):
backend {
name "smbclient"
password_backend "libsecret"
username "ExampleName"
}
Some values are optional:
backend {
name "smbclient"
workgroup "FANCYWORKGROUP"
username "ExampleName"
}
Defaults:
workgroup "WORKGROUP"
username ""
password ""
password_backend "config"
Use scripts/mpd.secret.py to store a secret (password) and optionally
create a new collection (keyring).
---
INSTALL | 3 +
Makefile.am | 18 ++++-
configure.ac | 5 ++
doc/user.xml | 133 +++++++++++++++++++++++++++++++
scripts/mpd.secret.py | 111 ++++++++++++++++++++++++++
src/config/ConfigOption.hxx | 1 +
src/config/ConfigTemplates.cxx | 1 +
src/lib/smbclient/Init.cxx | 176
++++++++++++++++++++++++++++++++++++-----
src/lib/smbclient/Init.hxx | 89 ++++++++++++++++++++-
9 files changed, 513 insertions(+), 24 deletions(-)
create mode 100755 scripts/mpd.secret.py
diff --git a/INSTALL b/INSTALL
index 792bbd7..8de660d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -138,6 +138,9 @@ For playing audio CDs.
libsystemd-daemon - http://freedesktop.org/wiki/Software/systemd/
For systemd activation.
+libsecret - https://wiki.gnome.org/Projects/Libsecret
+For smbclient libsecret authentication.
+
pkg-config
----------
diff --git a/Makefile.am b/Makefile.am
index c6bcfa1..fc8cc51 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -611,12 +611,14 @@ libstorage_a_SOURCES = \
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(NFS_CFLAGS) \
- $(SMBCLIENT_CFLAGS)
+ $(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS)
STORAGE_LIBS = \
libstorage.a \
$(NFS_LIBS) \
- $(SMBCLIENT_LIBS)
+ $(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS)
if ENABLE_SMBCLIENT
libstorage_a_SOURCES += \
@@ -651,7 +653,8 @@ libneighbor_a_SOURCES = \
src/neighbor/NeighborPlugin.hxx
libneighbor_a_CPPFLAGS = $(AM_CPPFLAGS) \
- $(SMBCLIENT_CFLAGS)
+ $(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS)
if ENABLE_SMBCLIENT
libneighbor_a_SOURCES += \
@@ -661,6 +664,7 @@ endif
NEIGHBOR_LIBS = \
$(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS) \
libneighbor.a
if ENABLE_UPNP
@@ -1165,6 +1169,7 @@ libinput_a_SOURCES = \
libinput_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(CURL_CFLAGS) \
$(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS) \
$(NFS_CFLAGS) \
$(CDIO_PARANOIA_CFLAGS) \
$(FFMPEG_CFLAGS) \
@@ -1174,6 +1179,7 @@ INPUT_LIBS = \
libinput.a \
$(CURL_LIBS) \
$(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS) \
$(NFS_LIBS) \
$(CDIO_PARANOIA_LIBS) \
$(FFMPEG_LIBS2) \
@@ -2194,6 +2200,11 @@ endif
man_MANS = doc/mpd.1 doc/mpd.conf.5
doc_DATA = AUTHORS COPYING NEWS README doc/mpdconf.example
+if ENABLE_LIBSECRET
+docscriptsdir = $(docdir)/scripts
+docscripts_SCRIPTS = scripts/mpd.secret.py
+endif
+
DOCBOOK_FILES = doc/protocol.xml doc/user.xml doc/developer.xml
if ENABLE_DOCUMENTATION
@@ -2251,6 +2262,7 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
test/test_archive_zzip.sh \
$(wildcard scripts/*.sh) \
$(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \
+ scripts/mpd.secret.py
systemd/mpd.socket \
android/AndroidManifest.xml \
android/build.py \
diff --git a/configure.ac b/configure.ac
index dfd208a..82f814d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -709,6 +709,10 @@ dnl ----------------------------------- CURL
----------------------------------
MPD_ENABLE_AUTO_PKG(curl, CURL, [libcurl >= 7.18],
[libcurl HTTP streaming], [libcurl not found])
+dnl --------------------------------- LIBSECRET
-------------------------------
+MPD_ENABLE_AUTO_PKG(libsecret, LIBSECRET, [libsecret-1],
+ [libsecret password support], [libsecret not found])
+
dnl ----------------------------------- smbclient
-----------------------------
MPD_ENABLE_AUTO_PKG_LIB(smbclient, SMBCLIENT, [smbclient >= 0.2],
[smbclient], [smbc_init], [-lsmbclient], [],
@@ -1421,6 +1425,7 @@ results(soxr, [libsoxr])
results(libmpdclient, [libmpdclient])
results(inotify, [inotify])
results(sqlite, [SQLite])
+results(libsecret, [LIBSECRET])
printf '\nMetadata support:\n\t'
results(id3,[ID3])
diff --git a/doc/user.xml b/doc/user.xml
index 7a60b04..1838485 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -1025,6 +1025,64 @@ database {
plugin).
</para>
</section>
+
+ <section id="smbclient_auth">
+ <title>Smbclient Authentication</title>
+
+ <para>
+ <application>MPD</application> can can be configured to
+ authenticate itself to SMB/CIFS servers. This section
+ illustrates several setups.
+ </para>
+
+ <para>
+ Both of the following configurations use the
+ <filename>mpd.conf</filename> configuration file to store the
+ authentication information. Notice some values have been omitted
from
+ the second section. See <link
+
linkend="smbclient_backend"><varname>smbclient</varname></link> for
+ default values:
+ </para>
+
+ <programlisting>
+backend {
+ name "smbclient"
+ password_backend "config"
+ username "ExampleName"
+ password "ExamplePassword"
+}
+
+backend {
+ name "smbclient"
+ workgroup "FANCYWORKGROUP"
+ username "ExampleName"
+}
+ </programlisting>
+
+ <para>
+ This configuration queries the "Secret Service" via DBus. This can
+ obtain the password from multiple implementations, including
+ <application>gnome-keyring</application> and
+ <application>ksecretservice</application>.
+ </para>
+ <programlisting>
+backend {
+ name "smbclient"
+ password_backend "libsecret"
+ username "ExampleName"
+}
+ </programlisting>
+
+ <para>
+ The provided <filename>python3</filename> script,
+ <filename>mpd.secret.py</filename>, aids in creating both the
+ collection (keyring) and secret (password). It is only installed if
+ <application>MPD</application> has been compiled with
+ <filename>libsecret</filename>, and requires the
+ <filename>gobject-introspection</filename> bindings to
+ <filename>libsecret</filename>.
+ </para>
+ </section>
</chapter>
<chapter id="use">
@@ -3427,4 +3485,79 @@ buffer_size: 16384</programlisting>
</section>
</section>
</chapter>
+
+ <chapter id="backend_reference">
+ <title>Backend reference</title>
+
+ <para>
+ The <varname>backend</varname> block configures various backends
+ through the <varname>name</varname> setting. At the moment, only
+ <parameter>smbclient</parameter> can be configured in this
+ manner.
+ </para>
+
+ <section id="smbclient_backend">
+ <title><parameter>smbclient</parameter></title>
+
+ <para>
+ The <varname>smbclient</varname> backend supports
+ authentication if configured.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>password_backend</varname>
+ <parameter>config|libsecret</parameter>
+ </entry>
+ <entry>
+ Source from which to read the password. When set to
+ <parameter>config</parameter>, the password will be
+ read from <varname>password</varname>. When set to
+ <parameter>libsecret</parameter>, the "Secret Service"
+ will be queried over DBus. Defaults to
+ <parameter>config</parameter>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>username</varname>
+ <parameter>NAME</parameter>
+ </entry>
+ <entry>
+ Username with which to authenticate. Defaults to "".
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>password</varname>
+ <parameter>WORD</parameter>
+ </entry>
+ <entry>
+ Password with which to authenticate. Defaults to "".
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>workgroup</varname>
+ <parameter>GROUP</parameter>
+ </entry>
+ <entry>
+ Workgroup with which to authenticate. Defaults to
+ "WORKGROUP".
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+ </chapter>
</book>
diff --git a/scripts/mpd.secret.py b/scripts/mpd.secret.py
new file mode 100755
index 0000000..30cb497
--- /dev/null
+++ b/scripts/mpd.secret.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2003-2015 The Music Player Daemon Project
+# http://www.musicpd.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+
+import getpass
+from gi.repository import Secret
+
+
+mpd_schema_attributes =\
+ { "server" : Secret.SchemaAttributeType.STRING
+ , "share" : Secret.SchemaAttributeType.STRING
+ , "workgroup" : Secret.SchemaAttributeType.STRING
+ , "username" : Secret.SchemaAttributeType.STRING
+ }
+
+mpd_schema = Secret.Schema.new\
+ ( "org.mpd.smbclient.password"
+ , Secret.SchemaFlags.NONE
+ , mpd_schema_attributes
+ )
+
+mpd_default_input_data =\
+ { "collection" : { "default": "MusicPD" }
+ , "password" : { "secret": True }
+ , "server" : { }
+ , "share" : { }
+ , "workgroup" : { "default": "WORKGROUP" }
+ , "username" : { }
+ }
+
+
+def mpd_prompt(prompt, default=None, secret=False):
+ if default is None:
+ prompt = "{}: ".format(prompt)
+ else:
+ prompt = "{} [{}]: ".format(prompt, default)
+ string = ""
+ while not string:
+ if secret:
+ string = getpass.getpass(prompt)
+ else:
+ string = input(prompt)
+ if not string and default is not None:
+ string = default
+ break
+ return string
+
+def mpd_main():
+ input_data = mpd_input(mpd_default_input_data)
+ mpd_collection_ensure(input_data["collection"])
+ mpd_password_store(input_data)
+
+def mpd_input(input_data_arguments):
+ input_data = {}
+ print("<Enter> for default values (in parentheses)")
+ for attribute, arguments in sorted(input_data_arguments.items()):
+ input_data[attribute] = mpd_prompt(attribute, **arguments)
+ return input_data
+
+def mpd_collection_ensure(label):
+ service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
+ collections = Secret.Service.get_collections(service)
+ labels = [collection.get_label() for collection in collections]
+
+ if label not in labels:
+ Secret.Collection.create_sync\
+ ( service
+ , label
+ , None
+ , Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE
+ , None
+ )
+
+def mpd_input_to_attributes(d):
+ return {k:v for k,v in d.items() if k in mpd_schema_attributes}
+
+def mpd_input_get_collection_path(input_data):
+ return
"/org/freedesktop/secrets/collection/{}".format(input_data["collection"])
+
+def mpd_input_get_password_label(input_data):
+ return "Smbclient Password: {}".format(input_data["server"])
+
+def mpd_password_store(input_data):
+ Secret.password_store_sync\
+ ( mpd_schema
+ , mpd_input_to_attributes(input_data)
+ , mpd_input_get_collection_path(input_data)
+ , mpd_input_get_password_label(input_data)
+ , input_data["password"]
+ , None
+ )
+
+if __name__ == "__main__":
+ mpd_main()
diff --git a/src/config/ConfigOption.hxx b/src/config/ConfigOption.hxx
index 5acd6fd..8d11a3f 100644
--- a/src/config/ConfigOption.hxx
+++ b/src/config/ConfigOption.hxx
@@ -90,6 +90,7 @@ enum class ConfigBlockOption {
AUDIO_FILTER,
DATABASE,
NEIGHBORS,
+ BACKEND,
MAX
};
diff --git a/src/config/ConfigTemplates.cxx b/src/config/ConfigTemplates.cxx
index 6fbf025..d283e29 100644
--- a/src/config/ConfigTemplates.cxx
+++ b/src/config/ConfigTemplates.cxx
@@ -90,6 +90,7 @@ const ConfigTemplate config_block_templates[] = {
{ "filter", true },
{ "database", false },
{ "neighbors", true },
+ { "backend", "true" },
};
static constexpr unsigned n_config_block_templates =
diff --git a/src/lib/smbclient/Init.cxx b/src/lib/smbclient/Init.cxx
index 999e60f..cf5299b 100644
--- a/src/lib/smbclient/Init.cxx
+++ b/src/lib/smbclient/Init.cxx
@@ -22,34 +22,172 @@
#include "Mutex.hxx"
#include "thread/Mutex.hxx"
#include "util/Error.hxx"
+#include "util/Alloc.hxx"
+#include "config/ConfigGlobal.hxx"
#include <libsmbclient.h>
+#ifdef ENABLE_LIBSECRET
+#include <libsecret/secret.h>
+#endif
+
#include <string.h>
-static void
-mpd_smbc_get_auth_data(gcc_unused const char *srv,
- gcc_unused const char *shr,
- char *wg, gcc_unused int wglen,
- char *un, gcc_unused int unlen,
- char *pw, gcc_unused int pwlen)
-{
- // TODO: implement
- strcpy(wg, "WORKGROUP");
- strcpy(un, "");
- strcpy(pw, "");
+
+SmbAuthData smb_auth_data;
+SmbState smb_state = { false };
+
+
+SmbAuthData::~SmbAuthData()
+{ free(password);
+}
+
+void
+SmbAuthData::set_password(const char* pw)
+{ free(password);
+ if (pw == nullptr)
+ password = xstrdup("");
+ else
+ password = xstrdup(pw);
+}
+
+void
+SmbAuthData::initialize(const char* svr, const char* shr)
+{ if (initialized)
+ return;
+ initialized = true;
+ server = svr;
+ share = shr;
+
+ const ConfigBlock* block =
config_get_block(ConfigBlockOption::BACKEND);
+
+ if (block == nullptr)
+ return;
+
+ const char* backend_name = block->GetBlockValue("name", "");
+
+ if (strcmp(backend_name, "smbclient") != 0)
+ return;
+
+ workgroup = block->GetBlockValue("workgroup", "WORKGROUP");
+ username = block->GetBlockValue("username", "");
+
+ initialize_password(block);
+}
+
+void
+SmbAuthData::initialize_password
+ ( const ConfigBlock* block
+ )
+{ const char* password_backend =
block->GetBlockValue("password_backend", "config");
+
+ if (strcmp(password_backend, "config") == 0)
+ initialize_password_config(block);
+#ifdef ENABLE_LIBSECRET
+ else if (strcmp(password_backend, "libsecret") == 0)
+ initialize_password_secret();
+#endif
+}
+
+void
+SmbAuthData::initialize_password_config
+ ( const ConfigBlock* block
+ )
+{ set_password(block->GetBlockValue("password", ""));
+}
+
+#ifdef ENABLE_LIBSECRET
+void
+SmbAuthData::initialize_password_secret()
+{ gchar* pw = nullptr;
+ GError *error = nullptr;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+ const SecretSchema mpd_schema =
+ { "org.mpd.smbclient.password"
+ , SECRET_SCHEMA_NONE
+ , { { "server"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "share"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "workgroup"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "username"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "NULL"
+ , (SecretSchemaAttributeType)0
+ }
+ }
+ };
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+ pw = secret_password_lookup_sync
+ ( &mpd_schema , nullptr , &error
+ , "server", server
+ , "share", share
+ , "workgroup", workgroup
+ , "username", username
+ , nullptr
+ );
+
+ if (error != nullptr)
+ g_error_free(error);
+
+ set_password(pw);
+ secret_password_free(pw);
+}
+#endif
+
+
+void
+mpd_smbc_set_auth_data
+ ( char* destination
+ , const char* source
+ , size_t num
+ )
+{ size_t source_len = strlen(source) + 1;
+ memcpy(destination, source, std::min(source_len, num));
+ if (source_len > num)
+ destination[num] = '\0';
+}
+
+void
+mpd_smbc_get_auth_data
+ ( const char* server
+ , const char* share
+ , char* workgroup, int workgrouplen
+ , char* username, int usernamelen
+ , char* password, int passwordlen
+ )
+{ smb_auth_data.initialize(server, share);
+
+ mpd_smbc_set_auth_data(workgroup, smb_auth_data.workgroup,
workgrouplen);
+ mpd_smbc_set_auth_data(username, smb_auth_data.username, usernamelen);
+ mpd_smbc_set_auth_data(password, smb_auth_data.password, passwordlen);
}
bool
SmbclientInit(Error &error)
-{
- const ScopeLock protect(smbclient_mutex);
+{ const ScopeLock protect(smbclient_mutex);
- constexpr int debug = 0;
- if (smbc_init(mpd_smbc_get_auth_data, debug) < 0) {
- error.SetErrno("smbc_init() failed");
- return false;
- }
+ if (!smb_state.initialized) {
+ constexpr int debug = 0;
+ if (smbc_init(mpd_smbc_get_auth_data, debug) < 0) {
+ error.SetErrno("smbc_init() failed");
+ return false;
+ }
+ else {
+ smb_state.initialized = false;
+ }
+ }
- return true;
+ return true;
}
diff --git a/src/lib/smbclient/Init.hxx b/src/lib/smbclient/Init.hxx
index 1ccaec0..f2bf2dc 100644
--- a/src/lib/smbclient/Init.hxx
+++ b/src/lib/smbclient/Init.hxx
@@ -22,12 +22,97 @@
#include "check.h"
+#include "config/Block.hxx"
+#include "util/Alloc.hxx"
+
+
class Error;
-/**
+struct SmbAuthData
+ { const char* server;
+ const char* share;
+ const char* workgroup;
+ const char* username;
+ char* password;
+ bool initialized;
+
+ SmbAuthData &operator=
+ ( const SmbAuthData &
+ ) = delete;
+
+ SmbAuthData
+ ()
+ : server("")
+ , share("")
+ , workgroup("WORKGROUP")
+ , username("")
+ , password(xstrdup(""))
+ , initialized(false)
+ {}
+
+ ~SmbAuthData
+ ();
+
+ void
+ initialize
+ ( const char*
+ , const char*
+ );
+
+ private:
+ void
+ set_password
+ ( const char*
+ );
+
+ void
+ initialize_password
+ ( const ConfigBlock*
+ );
+
+ void
+ initialize_password_config
+ ( const ConfigBlock*
+ );
+
+#ifdef ENABLE_LIBSECRET
+ void
+ initialize_password_secret
+ ();
+#endif
+ };
+
+struct SmbState
+ { bool initialized;
+ };
+
+
+extern SmbAuthData smb_auth_data;
+extern SmbState smb_state;
+
+
+void
+mpd_smbc_set_auth_data
+ ( char*
+ , const char*
+ , size_t
+ );
+
+void
+mpd_smbc_get_auth_data
+ ( const char*
+ , const char*
+ , char*, int
+ , char*, int
+ , char*, int
+ );
+
+/*
* Initialize libsmbclient.
*/
bool
-SmbclientInit(Error &error);
+SmbclientInit
+ (Error&
+ );
#endif
--
2.1.4
From 000c5e7faaa233e0adf1ee550237bc79b706a6aa Mon Sep 17 00:00:00 2001
From: Yclept Nemo <pscjtwjdjtAhnbjm/dpn>
Date: Mon, 18 May 2015 14:07:13 -0400
Subject: [PATCH 2/2] smbclient auth support
There is now a backend block which in turns provides several password
configuration backends:
Configuration file:
backend {
name "smbclient"
password_backend "config"
username "ExampleName"
password "ExamplePassword"
}
Libsecret (keyring):
backend {
name "smbclient"
password_backend "libsecret"
username "ExampleName"
}
Some values are optional:
backend {
name "smbclient"
workgroup "FANCYWORKGROUP"
username "ExampleName"
}
Defaults:
workgroup "WORKGROUP"
username ""
password ""
password_backend "config"
Use scripts/mpd.secret.py to store a secret (password) and optionally
create a new collection (keyring).
---
INSTALL | 3 +
Makefile.am | 18 ++++-
configure.ac | 5 ++
doc/user.xml | 133 +++++++++++++++++++++++++++++++
scripts/mpd.secret.py | 111 ++++++++++++++++++++++++++
src/config/ConfigOption.hxx | 1 +
src/config/ConfigTemplates.cxx | 1 +
src/lib/smbclient/Init.cxx | 176 ++++++++++++++++++++++++++++++++++++-----
src/lib/smbclient/Init.hxx | 89 ++++++++++++++++++++-
9 files changed, 513 insertions(+), 24 deletions(-)
create mode 100755 scripts/mpd.secret.py
diff --git a/INSTALL b/INSTALL
index 792bbd7..8de660d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -138,6 +138,9 @@ For playing audio CDs.
libsystemd-daemon - http://freedesktop.org/wiki/Software/systemd/
For systemd activation.
+libsecret - https://wiki.gnome.org/Projects/Libsecret
+For smbclient libsecret authentication.
+
pkg-config
----------
diff --git a/Makefile.am b/Makefile.am
index c6bcfa1..fc8cc51 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -611,12 +611,14 @@ libstorage_a_SOURCES = \
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(NFS_CFLAGS) \
- $(SMBCLIENT_CFLAGS)
+ $(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS)
STORAGE_LIBS = \
libstorage.a \
$(NFS_LIBS) \
- $(SMBCLIENT_LIBS)
+ $(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS)
if ENABLE_SMBCLIENT
libstorage_a_SOURCES += \
@@ -651,7 +653,8 @@ libneighbor_a_SOURCES = \
src/neighbor/NeighborPlugin.hxx
libneighbor_a_CPPFLAGS = $(AM_CPPFLAGS) \
- $(SMBCLIENT_CFLAGS)
+ $(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS)
if ENABLE_SMBCLIENT
libneighbor_a_SOURCES += \
@@ -661,6 +664,7 @@ endif
NEIGHBOR_LIBS = \
$(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS) \
libneighbor.a
if ENABLE_UPNP
@@ -1165,6 +1169,7 @@ libinput_a_SOURCES = \
libinput_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(CURL_CFLAGS) \
$(SMBCLIENT_CFLAGS) \
+ $(LIBSECRET_CFLAGS) \
$(NFS_CFLAGS) \
$(CDIO_PARANOIA_CFLAGS) \
$(FFMPEG_CFLAGS) \
@@ -1174,6 +1179,7 @@ INPUT_LIBS = \
libinput.a \
$(CURL_LIBS) \
$(SMBCLIENT_LIBS) \
+ $(LIBSECRET_LIBS) \
$(NFS_LIBS) \
$(CDIO_PARANOIA_LIBS) \
$(FFMPEG_LIBS2) \
@@ -2194,6 +2200,11 @@ endif
man_MANS = doc/mpd.1 doc/mpd.conf.5
doc_DATA = AUTHORS COPYING NEWS README doc/mpdconf.example
+if ENABLE_LIBSECRET
+docscriptsdir = $(docdir)/scripts
+docscripts_SCRIPTS = scripts/mpd.secret.py
+endif
+
DOCBOOK_FILES = doc/protocol.xml doc/user.xml doc/developer.xml
if ENABLE_DOCUMENTATION
@@ -2251,6 +2262,7 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \
test/test_archive_zzip.sh \
$(wildcard scripts/*.sh) \
$(man_MANS) $(DOCBOOK_FILES) doc/mpdconf.example doc/doxygen.conf \
+ scripts/mpd.secret.py
systemd/mpd.socket \
android/AndroidManifest.xml \
android/build.py \
diff --git a/configure.ac b/configure.ac
index dfd208a..82f814d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -709,6 +709,10 @@ dnl ----------------------------------- CURL ----------------------------------
MPD_ENABLE_AUTO_PKG(curl, CURL, [libcurl >= 7.18],
[libcurl HTTP streaming], [libcurl not found])
+dnl --------------------------------- LIBSECRET -------------------------------
+MPD_ENABLE_AUTO_PKG(libsecret, LIBSECRET, [libsecret-1],
+ [libsecret password support], [libsecret not found])
+
dnl ----------------------------------- smbclient -----------------------------
MPD_ENABLE_AUTO_PKG_LIB(smbclient, SMBCLIENT, [smbclient >= 0.2],
[smbclient], [smbc_init], [-lsmbclient], [],
@@ -1421,6 +1425,7 @@ results(soxr, [libsoxr])
results(libmpdclient, [libmpdclient])
results(inotify, [inotify])
results(sqlite, [SQLite])
+results(libsecret, [LIBSECRET])
printf '\nMetadata support:\n\t'
results(id3,[ID3])
diff --git a/doc/user.xml b/doc/user.xml
index 7a60b04..1838485 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -1025,6 +1025,64 @@ database {
plugin).
</para>
</section>
+
+ <section id="smbclient_auth">
+ <title>Smbclient Authentication</title>
+
+ <para>
+ <application>MPD</application> can can be configured to
+ authenticate itself to SMB/CIFS servers. This section
+ illustrates several setups.
+ </para>
+
+ <para>
+ Both of the following configurations use the
+ <filename>mpd.conf</filename> configuration file to store the
+ authentication information. Notice some values have been omitted from
+ the second section. See <link
+ linkend="smbclient_backend"><varname>smbclient</varname></link> for
+ default values:
+ </para>
+
+ <programlisting>
+backend {
+ name "smbclient"
+ password_backend "config"
+ username "ExampleName"
+ password "ExamplePassword"
+}
+
+backend {
+ name "smbclient"
+ workgroup "FANCYWORKGROUP"
+ username "ExampleName"
+}
+ </programlisting>
+
+ <para>
+ This configuration queries the "Secret Service" via DBus. This can
+ obtain the password from multiple implementations, including
+ <application>gnome-keyring</application> and
+ <application>ksecretservice</application>.
+ </para>
+ <programlisting>
+backend {
+ name "smbclient"
+ password_backend "libsecret"
+ username "ExampleName"
+}
+ </programlisting>
+
+ <para>
+ The provided <filename>python3</filename> script,
+ <filename>mpd.secret.py</filename>, aids in creating both the
+ collection (keyring) and secret (password). It is only installed if
+ <application>MPD</application> has been compiled with
+ <filename>libsecret</filename>, and requires the
+ <filename>gobject-introspection</filename> bindings to
+ <filename>libsecret</filename>.
+ </para>
+ </section>
</chapter>
<chapter id="use">
@@ -3427,4 +3485,79 @@ buffer_size: 16384</programlisting>
</section>
</section>
</chapter>
+
+ <chapter id="backend_reference">
+ <title>Backend reference</title>
+
+ <para>
+ The <varname>backend</varname> block configures various backends
+ through the <varname>name</varname> setting. At the moment, only
+ <parameter>smbclient</parameter> can be configured in this
+ manner.
+ </para>
+
+ <section id="smbclient_backend">
+ <title><parameter>smbclient</parameter></title>
+
+ <para>
+ The <varname>smbclient</varname> backend supports
+ authentication if configured.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>password_backend</varname>
+ <parameter>config|libsecret</parameter>
+ </entry>
+ <entry>
+ Source from which to read the password. When set to
+ <parameter>config</parameter>, the password will be
+ read from <varname>password</varname>. When set to
+ <parameter>libsecret</parameter>, the "Secret Service"
+ will be queried over DBus. Defaults to
+ <parameter>config</parameter>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>username</varname>
+ <parameter>NAME</parameter>
+ </entry>
+ <entry>
+ Username with which to authenticate. Defaults to "".
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>password</varname>
+ <parameter>WORD</parameter>
+ </entry>
+ <entry>
+ Password with which to authenticate. Defaults to "".
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>workgroup</varname>
+ <parameter>GROUP</parameter>
+ </entry>
+ <entry>
+ Workgroup with which to authenticate. Defaults to
+ "WORKGROUP".
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+ </chapter>
</book>
diff --git a/scripts/mpd.secret.py b/scripts/mpd.secret.py
new file mode 100755
index 0000000..30cb497
--- /dev/null
+++ b/scripts/mpd.secret.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2003-2015 The Music Player Daemon Project
+# http://www.musicpd.org
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+
+import getpass
+from gi.repository import Secret
+
+
+mpd_schema_attributes =\
+ { "server" : Secret.SchemaAttributeType.STRING
+ , "share" : Secret.SchemaAttributeType.STRING
+ , "workgroup" : Secret.SchemaAttributeType.STRING
+ , "username" : Secret.SchemaAttributeType.STRING
+ }
+
+mpd_schema = Secret.Schema.new\
+ ( "org.mpd.smbclient.password"
+ , Secret.SchemaFlags.NONE
+ , mpd_schema_attributes
+ )
+
+mpd_default_input_data =\
+ { "collection" : { "default": "MusicPD" }
+ , "password" : { "secret": True }
+ , "server" : { }
+ , "share" : { }
+ , "workgroup" : { "default": "WORKGROUP" }
+ , "username" : { }
+ }
+
+
+def mpd_prompt(prompt, default=None, secret=False):
+ if default is None:
+ prompt = "{}: ".format(prompt)
+ else:
+ prompt = "{} [{}]: ".format(prompt, default)
+ string = ""
+ while not string:
+ if secret:
+ string = getpass.getpass(prompt)
+ else:
+ string = input(prompt)
+ if not string and default is not None:
+ string = default
+ break
+ return string
+
+def mpd_main():
+ input_data = mpd_input(mpd_default_input_data)
+ mpd_collection_ensure(input_data["collection"])
+ mpd_password_store(input_data)
+
+def mpd_input(input_data_arguments):
+ input_data = {}
+ print("<Enter> for default values (in parentheses)")
+ for attribute, arguments in sorted(input_data_arguments.items()):
+ input_data[attribute] = mpd_prompt(attribute, **arguments)
+ return input_data
+
+def mpd_collection_ensure(label):
+ service = Secret.Service.get_sync(Secret.ServiceFlags.LOAD_COLLECTIONS)
+ collections = Secret.Service.get_collections(service)
+ labels = [collection.get_label() for collection in collections]
+
+ if label not in labels:
+ Secret.Collection.create_sync\
+ ( service
+ , label
+ , None
+ , Secret.CollectionCreateFlags.COLLECTION_CREATE_NONE
+ , None
+ )
+
+def mpd_input_to_attributes(d):
+ return {k:v for k,v in d.items() if k in mpd_schema_attributes}
+
+def mpd_input_get_collection_path(input_data):
+ return "/org/freedesktop/secrets/collection/{}".format(input_data["collection"])
+
+def mpd_input_get_password_label(input_data):
+ return "Smbclient Password: {}".format(input_data["server"])
+
+def mpd_password_store(input_data):
+ Secret.password_store_sync\
+ ( mpd_schema
+ , mpd_input_to_attributes(input_data)
+ , mpd_input_get_collection_path(input_data)
+ , mpd_input_get_password_label(input_data)
+ , input_data["password"]
+ , None
+ )
+
+if __name__ == "__main__":
+ mpd_main()
diff --git a/src/config/ConfigOption.hxx b/src/config/ConfigOption.hxx
index 5acd6fd..8d11a3f 100644
--- a/src/config/ConfigOption.hxx
+++ b/src/config/ConfigOption.hxx
@@ -90,6 +90,7 @@ enum class ConfigBlockOption {
AUDIO_FILTER,
DATABASE,
NEIGHBORS,
+ BACKEND,
MAX
};
diff --git a/src/config/ConfigTemplates.cxx b/src/config/ConfigTemplates.cxx
index 6fbf025..d283e29 100644
--- a/src/config/ConfigTemplates.cxx
+++ b/src/config/ConfigTemplates.cxx
@@ -90,6 +90,7 @@ const ConfigTemplate config_block_templates[] = {
{ "filter", true },
{ "database", false },
{ "neighbors", true },
+ { "backend", "true" },
};
static constexpr unsigned n_config_block_templates =
diff --git a/src/lib/smbclient/Init.cxx b/src/lib/smbclient/Init.cxx
index 999e60f..cf5299b 100644
--- a/src/lib/smbclient/Init.cxx
+++ b/src/lib/smbclient/Init.cxx
@@ -22,34 +22,172 @@
#include "Mutex.hxx"
#include "thread/Mutex.hxx"
#include "util/Error.hxx"
+#include "util/Alloc.hxx"
+#include "config/ConfigGlobal.hxx"
#include <libsmbclient.h>
+#ifdef ENABLE_LIBSECRET
+#include <libsecret/secret.h>
+#endif
+
#include <string.h>
-static void
-mpd_smbc_get_auth_data(gcc_unused const char *srv,
- gcc_unused const char *shr,
- char *wg, gcc_unused int wglen,
- char *un, gcc_unused int unlen,
- char *pw, gcc_unused int pwlen)
-{
- // TODO: implement
- strcpy(wg, "WORKGROUP");
- strcpy(un, "");
- strcpy(pw, "");
+
+SmbAuthData smb_auth_data;
+SmbState smb_state = { false };
+
+
+SmbAuthData::~SmbAuthData()
+{ free(password);
+}
+
+void
+SmbAuthData::set_password(const char* pw)
+{ free(password);
+ if (pw == nullptr)
+ password = xstrdup("");
+ else
+ password = xstrdup(pw);
+}
+
+void
+SmbAuthData::initialize(const char* svr, const char* shr)
+{ if (initialized)
+ return;
+ initialized = true;
+ server = svr;
+ share = shr;
+
+ const ConfigBlock* block = config_get_block(ConfigBlockOption::BACKEND);
+
+ if (block == nullptr)
+ return;
+
+ const char* backend_name = block->GetBlockValue("name", "");
+
+ if (strcmp(backend_name, "smbclient") != 0)
+ return;
+
+ workgroup = block->GetBlockValue("workgroup", "WORKGROUP");
+ username = block->GetBlockValue("username", "");
+
+ initialize_password(block);
+}
+
+void
+SmbAuthData::initialize_password
+ ( const ConfigBlock* block
+ )
+{ const char* password_backend = block->GetBlockValue("password_backend", "config");
+
+ if (strcmp(password_backend, "config") == 0)
+ initialize_password_config(block);
+#ifdef ENABLE_LIBSECRET
+ else if (strcmp(password_backend, "libsecret") == 0)
+ initialize_password_secret();
+#endif
+}
+
+void
+SmbAuthData::initialize_password_config
+ ( const ConfigBlock* block
+ )
+{ set_password(block->GetBlockValue("password", ""));
+}
+
+#ifdef ENABLE_LIBSECRET
+void
+SmbAuthData::initialize_password_secret()
+{ gchar* pw = nullptr;
+ GError *error = nullptr;
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+ const SecretSchema mpd_schema =
+ { "org.mpd.smbclient.password"
+ , SECRET_SCHEMA_NONE
+ , { { "server"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "share"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "workgroup"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "username"
+ , SECRET_SCHEMA_ATTRIBUTE_STRING
+ }
+ , { "NULL"
+ , (SecretSchemaAttributeType)0
+ }
+ }
+ };
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
+ pw = secret_password_lookup_sync
+ ( &mpd_schema , nullptr , &error
+ , "server", server
+ , "share", share
+ , "workgroup", workgroup
+ , "username", username
+ , nullptr
+ );
+
+ if (error != nullptr)
+ g_error_free(error);
+
+ set_password(pw);
+ secret_password_free(pw);
+}
+#endif
+
+
+void
+mpd_smbc_set_auth_data
+ ( char* destination
+ , const char* source
+ , size_t num
+ )
+{ size_t source_len = strlen(source) + 1;
+ memcpy(destination, source, std::min(source_len, num));
+ if (source_len > num)
+ destination[num] = '\0';
+}
+
+void
+mpd_smbc_get_auth_data
+ ( const char* server
+ , const char* share
+ , char* workgroup, int workgrouplen
+ , char* username, int usernamelen
+ , char* password, int passwordlen
+ )
+{ smb_auth_data.initialize(server, share);
+
+ mpd_smbc_set_auth_data(workgroup, smb_auth_data.workgroup, workgrouplen);
+ mpd_smbc_set_auth_data(username, smb_auth_data.username, usernamelen);
+ mpd_smbc_set_auth_data(password, smb_auth_data.password, passwordlen);
}
bool
SmbclientInit(Error &error)
-{
- const ScopeLock protect(smbclient_mutex);
+{ const ScopeLock protect(smbclient_mutex);
- constexpr int debug = 0;
- if (smbc_init(mpd_smbc_get_auth_data, debug) < 0) {
- error.SetErrno("smbc_init() failed");
- return false;
- }
+ if (!smb_state.initialized) {
+ constexpr int debug = 0;
+ if (smbc_init(mpd_smbc_get_auth_data, debug) < 0) {
+ error.SetErrno("smbc_init() failed");
+ return false;
+ }
+ else {
+ smb_state.initialized = false;
+ }
+ }
- return true;
+ return true;
}
diff --git a/src/lib/smbclient/Init.hxx b/src/lib/smbclient/Init.hxx
index 1ccaec0..f2bf2dc 100644
--- a/src/lib/smbclient/Init.hxx
+++ b/src/lib/smbclient/Init.hxx
@@ -22,12 +22,97 @@
#include "check.h"
+#include "config/Block.hxx"
+#include "util/Alloc.hxx"
+
+
class Error;
-/**
+struct SmbAuthData
+ { const char* server;
+ const char* share;
+ const char* workgroup;
+ const char* username;
+ char* password;
+ bool initialized;
+
+ SmbAuthData &operator=
+ ( const SmbAuthData &
+ ) = delete;
+
+ SmbAuthData
+ ()
+ : server("")
+ , share("")
+ , workgroup("WORKGROUP")
+ , username("")
+ , password(xstrdup(""))
+ , initialized(false)
+ {}
+
+ ~SmbAuthData
+ ();
+
+ void
+ initialize
+ ( const char*
+ , const char*
+ );
+
+ private:
+ void
+ set_password
+ ( const char*
+ );
+
+ void
+ initialize_password
+ ( const ConfigBlock*
+ );
+
+ void
+ initialize_password_config
+ ( const ConfigBlock*
+ );
+
+#ifdef ENABLE_LIBSECRET
+ void
+ initialize_password_secret
+ ();
+#endif
+ };
+
+struct SmbState
+ { bool initialized;
+ };
+
+
+extern SmbAuthData smb_auth_data;
+extern SmbState smb_state;
+
+
+void
+mpd_smbc_set_auth_data
+ ( char*
+ , const char*
+ , size_t
+ );
+
+void
+mpd_smbc_get_auth_data
+ ( const char*
+ , const char*
+ , char*, int
+ , char*, int
+ , char*, int
+ );
+
+/*
* Initialize libsmbclient.
*/
bool
-SmbclientInit(Error &error);
+SmbclientInit
+ (Error&
+ );
#endif
--
2.1.4
_______________________________________________
mpd-devel mailing list
[email protected]
http://mailman.blarg.de/listinfo/mpd-devel