Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2022-01-20 23:25:58 +0300
committerGitHub <noreply@github.com>2022-01-20 23:25:58 +0300
commitc0b03000a507f7bdce57eb91deaf4e7d2d67a3b4 (patch)
tree86019acc692b5e207a2c2efa226620e611b0dfa1
parent4466eb1f42c037ed3a71e4a0d549fbe7c7295703 (diff)
parent13b8179912630acede43aff844f2d302f552cb80 (diff)
Merge pull request #30782 from nextcloud/backport/29349/stable23v23.0.1rc3
-rw-r--r--.github/workflows/smb-kerberos.yml78
-rw-r--r--apps/files_external/3rdparty/.gitignore2
-rw-r--r--apps/files_external/3rdparty/composer.json2
-rw-r--r--apps/files_external/3rdparty/composer.lock16
-rw-r--r--apps/files_external/3rdparty/composer/ClassLoader.php113
-rw-r--r--apps/files_external/3rdparty/composer/InstalledVersions.php25
-rw-r--r--apps/files_external/3rdparty/composer/autoload_classmap.php1
-rw-r--r--apps/files_external/3rdparty/composer/autoload_static.php1
-rw-r--r--apps/files_external/3rdparty/composer/installed.json14
-rw-r--r--apps/files_external/3rdparty/composer/installed.php10
-rw-r--r--apps/files_external/3rdparty/icewind/smb/README.md31
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/IShare.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php136
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php12
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php13
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/System.php2
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php43
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php14
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php34
-rw-r--r--apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php14
-rw-r--r--apps/files_external/lib/AppInfo/Application.php60
-rw-r--r--apps/files_external/lib/Lib/Auth/SMB/KerberosApacheAuth.php53
-rw-r--r--apps/files_external/lib/Lib/Backend/SMB.php48
-rwxr-xr-xapps/files_external/tests/setup-krb.sh33
25 files changed, 633 insertions, 126 deletions
diff --git a/.github/workflows/smb-kerberos.yml b/.github/workflows/smb-kerberos.yml
new file mode 100644
index 00000000000..2875a7f4fbd
--- /dev/null
+++ b/.github/workflows/smb-kerberos.yml
@@ -0,0 +1,78 @@
+name: Samba Kerberos SSO
+on:
+ push:
+ branches:
+ - master
+ - stable*
+ paths:
+ - 'apps/files_external/**'
+ pull_request:
+ paths:
+ - 'apps/files_external/**'
+
+jobs:
+ smb-kerberos-tests:
+ runs-on: ubuntu-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ php-versions: ['7.4', '8.0']
+
+ name: php${{ matrix.php-versions }}-${{ matrix.ftpd }}
+
+ steps:
+ - name: Checkout server
+ uses: actions/checkout@v2
+ with:
+ submodules: true
+ - name: Pull images
+ run: |
+ docker pull icewind1991/samba-krb-test-dc
+ docker pull icewind1991/samba-krb-test-apache
+ docker pull icewind1991/samba-krb-test-client
+ - name: Setup AD-DC
+ run: |
+ mkdir data
+ sudo chown -R 33 data apps config
+ apps/files_external/tests/setup-krb.sh
+ - name: Set up Nextcloud
+ run: |
+ docker exec --user 33 apache ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password
+ docker exec --user 33 apache ./occ config:system:set trusted_domains 1 --value 'httpd.domain.test'
+
+ # setup user_saml
+ docker exec --user 33 apache ./occ app:enable user_saml --force
+ docker exec --user 33 apache ./occ config:app:set user_saml type --value 'environment-variable'
+ docker exec --user 33 apache ./occ config:app:set user_saml general-uid_mapping --value REMOTE_USER
+
+ # setup external storage
+ docker exec --user 33 apache ./occ app:enable files_external --force
+ docker exec --user 33 apache ./occ files_external:create smb smb smb::kerberosapache
+ docker exec --user 33 apache ./occ files_external:config 1 host krb.domain.test
+ docker exec --user 33 apache ./occ files_external:config 1 share netlogon
+ docker exec --user 33 apache ./occ files_external:list
+ - name: Test SSO
+ run: |
+ mkdir cookies
+ chmod 0777 cookies
+
+ DC_IP=$(docker inspect dc --format '{{.NetworkSettings.IPAddress}}')
+ docker run --rm --name client -v $PWD/cookies:/cookies -v /tmp/shared:/shared --dns $DC_IP --hostname client.domain.test icewind1991/samba-krb-test-client \
+ curl -c /cookies/jar -s --negotiate -u testuser@DOMAIN.TEST: --delegation always http://httpd.domain.test/index.php/apps/user_saml/saml/login
+ CONTENT=$(docker run --rm --name client -v $PWD/cookies:/cookies -v /tmp/shared:/shared --dns $DC_IP --hostname client.domain.test icewind1991/samba-krb-test-client \
+ curl -b /cookies/jar -s --negotiate -u testuser@DOMAIN.TEST: --delegation always http://httpd.domain.test/remote.php/webdav/smb/test.txt)
+ echo $CONTENT
+ CONTENT=$(echo $CONTENT | tr -d '[:space:]')
+ [[ $CONTENT == "testfile" ]]
+
+
+ smb-kerberos-summary:
+ runs-on: ubuntu-latest
+ needs: smb-kerberos-tests
+
+ if: always()
+
+ steps:
+ - name: Summary status
+ run: if ${{ needs.smb-kerberos-tests.result != 'success' }}; then exit 1; fi
diff --git a/apps/files_external/3rdparty/.gitignore b/apps/files_external/3rdparty/.gitignore
index 651eb60572d..e787d39fca6 100644
--- a/apps/files_external/3rdparty/.gitignore
+++ b/apps/files_external/3rdparty/.gitignore
@@ -5,6 +5,8 @@ icewind/smb/install_libsmbclient.sh
icewind/smb/Makefile
icewind/smb/.travis.yml
icewind/smb/.scrutinizer.yml
+icewind/smb/example-apache-kerberos.php
+icewind/smb/codecov.yml
icewind/streams/tests
.github
.php_cs*
diff --git a/apps/files_external/3rdparty/composer.json b/apps/files_external/3rdparty/composer.json
index d8854aa976a..e343521add3 100644
--- a/apps/files_external/3rdparty/composer.json
+++ b/apps/files_external/3rdparty/composer.json
@@ -9,6 +9,6 @@
},
"require": {
"icewind/streams": "0.7.4",
- "icewind/smb": "3.4.1"
+ "icewind/smb": "3.5.2"
}
}
diff --git a/apps/files_external/3rdparty/composer.lock b/apps/files_external/3rdparty/composer.lock
index 05de684a017..918dd7da08d 100644
--- a/apps/files_external/3rdparty/composer.lock
+++ b/apps/files_external/3rdparty/composer.lock
@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "0ffc772b2aaaaffe52decb8d13361976",
+ "content-hash": "524c99fd87297e01d004eb5a4e53b04c",
"packages": [
{
"name": "icewind/smb",
- "version": "v3.4.1",
+ "version": "v3.5.2",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
- "reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3"
+ "reference": "0a425bd21acf7ae112b135dca34640e1b1a825c3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/icewind1991/SMB/zipball/9dba42ab2a3990de29e18cc62b0a8270aceb74e3",
- "reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3",
+ "url": "https://api.github.com/repos/icewind1991/SMB/zipball/0a425bd21acf7ae112b135dca34640e1b1a825c3",
+ "reference": "0a425bd21acf7ae112b135dca34640e1b1a825c3",
"shasum": ""
},
"require": {
@@ -49,9 +49,9 @@
"description": "php wrapper for smbclient and libsmbclient-php",
"support": {
"issues": "https://github.com/icewind1991/SMB/issues",
- "source": "https://github.com/icewind1991/SMB/tree/v3.4.1"
+ "source": "https://github.com/icewind1991/SMB/tree/v3.5.2"
},
- "time": "2021-04-19T13:53:08+00:00"
+ "time": "2022-01-20T14:51:51+00:00"
},
{
"name": "icewind/streams",
@@ -107,5 +107,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
- "plugin-api-version": "2.1.0"
+ "plugin-api-version": "2.2.0"
}
diff --git a/apps/files_external/3rdparty/composer/ClassLoader.php b/apps/files_external/3rdparty/composer/ClassLoader.php
index 6d0c3f2d001..afef3fa2ad8 100644
--- a/apps/files_external/3rdparty/composer/ClassLoader.php
+++ b/apps/files_external/3rdparty/composer/ClassLoader.php
@@ -42,30 +42,75 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
+ /** @var ?string */
private $vendorDir;
// PSR-4
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<string, int>>
+ */
private $prefixLengthsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<int, string>>
+ */
private $prefixDirsPsr4 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, string>
+ */
private $fallbackDirsPsr4 = array();
// PSR-0
+ /**
+ * @var array[]
+ * @psalm-var array<string, array<string, string[]>>
+ */
private $prefixesPsr0 = array();
+ /**
+ * @var array[]
+ * @psalm-var array<string, string>
+ */
private $fallbackDirsPsr0 = array();
+ /** @var bool */
private $useIncludePath = false;
+
+ /**
+ * @var string[]
+ * @psalm-var array<string, string>
+ */
private $classMap = array();
+
+ /** @var bool */
private $classMapAuthoritative = false;
+
+ /**
+ * @var bool[]
+ * @psalm-var array<string, bool>
+ */
private $missingClasses = array();
+
+ /** @var ?string */
private $apcuPrefix;
+ /**
+ * @var self[]
+ */
private static $registeredLoaders = array();
+ /**
+ * @param ?string $vendorDir
+ */
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
+ /**
+ * @return string[]
+ */
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
@@ -75,28 +120,47 @@ class ClassLoader
return array();
}
+ /**
+ * @return array[]
+ * @psalm-return array<string, array<int, string>>
+ */
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
+ /**
+ * @return array[]
+ * @psalm-return array<string, string>
+ */
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
+ /**
+ * @return array[]
+ * @psalm-return array<string, string>
+ */
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
+ /**
+ * @return string[] Array of classname => path
+ * @psalm-return array<string, string>
+ */
public function getClassMap()
{
return $this->classMap;
}
/**
- * @param array $classMap Class to filename map
+ * @param string[] $classMap Class to filename map
+ * @psalm-param array<string, string> $classMap
+ *
+ * @return void
*/
public function addClassMap(array $classMap)
{
@@ -111,9 +175,11 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param array|string $paths The PSR-0 root directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
*/
public function add($prefix, $paths, $prepend = false)
{
@@ -156,11 +222,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param array|string $paths The PSR-4 base directories
- * @param bool $prepend Whether to prepend the directories
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
+ *
+ * @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@@ -204,8 +272,10 @@ class ClassLoader
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
- * @param string $prefix The prefix
- * @param array|string $paths The PSR-0 base directories
+ * @param string $prefix The prefix
+ * @param string[]|string $paths The PSR-0 base directories
+ *
+ * @return void
*/
public function set($prefix, $paths)
{
@@ -220,10 +290,12 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
- * @param string $prefix The prefix/namespace, with trailing '\\'
- * @param array|string $paths The PSR-4 base directories
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
+ *
+ * @return void
*/
public function setPsr4($prefix, $paths)
{
@@ -243,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
+ *
+ * @return void
*/
public function setUseIncludePath($useIncludePath)
{
@@ -265,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
+ *
+ * @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
@@ -285,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
+ *
+ * @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@@ -305,6 +383,8 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
*/
public function register($prepend = false)
{
@@ -324,6 +404,8 @@ class ClassLoader
/**
* Unregisters this instance as an autoloader.
+ *
+ * @return void
*/
public function unregister()
{
@@ -403,6 +485,11 @@ class ClassLoader
return self::$registeredLoaders;
}
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
@@ -474,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ * @private
*/
function includeFile($file)
{
diff --git a/apps/files_external/3rdparty/composer/InstalledVersions.php b/apps/files_external/3rdparty/composer/InstalledVersions.php
index b3a4e1611e6..d50e0c9fcc4 100644
--- a/apps/files_external/3rdparty/composer/InstalledVersions.php
+++ b/apps/files_external/3rdparty/composer/InstalledVersions.php
@@ -20,12 +20,25 @@ use Composer\Semver\VersionParser;
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
- * To require it's presence, you can require `composer-runtime-api ^2.0`
+ * To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
+ /**
+ * @var mixed[]|null
+ * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
+ */
private static $installed;
+
+ /**
+ * @var bool|null
+ */
private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
+ */
private static $installedByVendor = array();
/**
@@ -228,7 +241,7 @@ class InstalledVersions
/**
* @return array
- * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
@@ -242,7 +255,7 @@ class InstalledVersions
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
- * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@@ -265,7 +278,7 @@ class InstalledVersions
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
- * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
@@ -288,7 +301,7 @@ class InstalledVersions
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
- * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
@@ -298,7 +311,7 @@ class InstalledVersions
/**
* @return array[]
- * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
diff --git a/apps/files_external/3rdparty/composer/autoload_classmap.php b/apps/files_external/3rdparty/composer/autoload_classmap.php
index d0f82994f29..17b94af0e99 100644
--- a/apps/files_external/3rdparty/composer/autoload_classmap.php
+++ b/apps/files_external/3rdparty/composer/autoload_classmap.php
@@ -48,6 +48,7 @@ return array(
'Icewind\\SMB\\IShare' => $vendorDir . '/icewind/smb/src/IShare.php',
'Icewind\\SMB\\ISystem' => $vendorDir . '/icewind/smb/src/ISystem.php',
'Icewind\\SMB\\ITimeZoneProvider' => $vendorDir . '/icewind/smb/src/ITimeZoneProvider.php',
+ 'Icewind\\SMB\\KerberosApacheAuth' => $vendorDir . '/icewind/smb/src/KerberosApacheAuth.php',
'Icewind\\SMB\\KerberosAuth' => $vendorDir . '/icewind/smb/src/KerberosAuth.php',
'Icewind\\SMB\\Native\\NativeFileInfo' => $vendorDir . '/icewind/smb/src/Native/NativeFileInfo.php',
'Icewind\\SMB\\Native\\NativeReadStream' => $vendorDir . '/icewind/smb/src/Native/NativeReadStream.php',
diff --git a/apps/files_external/3rdparty/composer/autoload_static.php b/apps/files_external/3rdparty/composer/autoload_static.php
index 899982f2a67..1d309dcd6f1 100644
--- a/apps/files_external/3rdparty/composer/autoload_static.php
+++ b/apps/files_external/3rdparty/composer/autoload_static.php
@@ -68,6 +68,7 @@ class ComposerStaticInit98fe9b281934250b3a93f69a5ce843b3
'Icewind\\SMB\\IShare' => __DIR__ . '/..' . '/icewind/smb/src/IShare.php',
'Icewind\\SMB\\ISystem' => __DIR__ . '/..' . '/icewind/smb/src/ISystem.php',
'Icewind\\SMB\\ITimeZoneProvider' => __DIR__ . '/..' . '/icewind/smb/src/ITimeZoneProvider.php',
+ 'Icewind\\SMB\\KerberosApacheAuth' => __DIR__ . '/..' . '/icewind/smb/src/KerberosApacheAuth.php',
'Icewind\\SMB\\KerberosAuth' => __DIR__ . '/..' . '/icewind/smb/src/KerberosAuth.php',
'Icewind\\SMB\\Native\\NativeFileInfo' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeFileInfo.php',
'Icewind\\SMB\\Native\\NativeReadStream' => __DIR__ . '/..' . '/icewind/smb/src/Native/NativeReadStream.php',
diff --git a/apps/files_external/3rdparty/composer/installed.json b/apps/files_external/3rdparty/composer/installed.json
index 7405962c70c..2c9b1c087df 100644
--- a/apps/files_external/3rdparty/composer/installed.json
+++ b/apps/files_external/3rdparty/composer/installed.json
@@ -2,17 +2,17 @@
"packages": [
{
"name": "icewind/smb",
- "version": "v3.4.1",
- "version_normalized": "3.4.1.0",
+ "version": "v3.5.2",
+ "version_normalized": "3.5.2.0",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
- "reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3"
+ "reference": "0a425bd21acf7ae112b135dca34640e1b1a825c3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/icewind1991/SMB/zipball/9dba42ab2a3990de29e18cc62b0a8270aceb74e3",
- "reference": "9dba42ab2a3990de29e18cc62b0a8270aceb74e3",
+ "url": "https://api.github.com/repos/icewind1991/SMB/zipball/0a425bd21acf7ae112b135dca34640e1b1a825c3",
+ "reference": "0a425bd21acf7ae112b135dca34640e1b1a825c3",
"shasum": ""
},
"require": {
@@ -25,7 +25,7 @@
"phpunit/phpunit": "^8.5|^9.3.8",
"psalm/phar": "^4.3"
},
- "time": "2021-04-19T13:53:08+00:00",
+ "time": "2022-01-20T14:51:51+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -46,7 +46,7 @@
"description": "php wrapper for smbclient and libsmbclient-php",
"support": {
"issues": "https://github.com/icewind1991/SMB/issues",
- "source": "https://github.com/icewind1991/SMB/tree/v3.4.1"
+ "source": "https://github.com/icewind1991/SMB/tree/v3.5.2"
},
"install-path": "../icewind/smb"
},
diff --git a/apps/files_external/3rdparty/composer/installed.php b/apps/files_external/3rdparty/composer/installed.php
index 7dfd1c420f7..255420e1003 100644
--- a/apps/files_external/3rdparty/composer/installed.php
+++ b/apps/files_external/3rdparty/composer/installed.php
@@ -5,7 +5,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
- 'reference' => '70483a16a3a232758979bb6fa363629b5a16b6a4',
+ 'reference' => '0bed61f949bc7a8c69cd154919e78b704e28c99e',
'name' => 'files_external/3rdparty',
'dev' => true,
),
@@ -16,16 +16,16 @@
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
- 'reference' => '70483a16a3a232758979bb6fa363629b5a16b6a4',
+ 'reference' => '0bed61f949bc7a8c69cd154919e78b704e28c99e',
'dev_requirement' => false,
),
'icewind/smb' => array(
- 'pretty_version' => 'v3.4.1',
- 'version' => '3.4.1.0',
+ 'pretty_version' => 'v3.5.2',
+ 'version' => '3.5.2.0',
'type' => 'library',
'install_path' => __DIR__ . '/../icewind/smb',
'aliases' => array(),
- 'reference' => '9dba42ab2a3990de29e18cc62b0a8270aceb74e3',
+ 'reference' => '0a425bd21acf7ae112b135dca34640e1b1a825c3',
'dev_requirement' => false,
),
'icewind/streams' => array(
diff --git a/apps/files_external/3rdparty/icewind/smb/README.md b/apps/files_external/3rdparty/icewind/smb/README.md
index 272c4ebedcd..fec1faefbad 100644
--- a/apps/files_external/3rdparty/icewind/smb/README.md
+++ b/apps/files_external/3rdparty/icewind/smb/README.md
@@ -44,13 +44,42 @@ $server = $serverFactory->createServer('localhost', $auth);
### Using kerberos authentication ###
+There are two ways of using kerberos to authenticate against the smb server:
+
+- Using a ticket from the php server
+- Re-using a ticket send by the client
+
+### Using a server ticket
+
+Using a server ticket allows the web server to authenticate against the smb server using an existing machine account.
+
+The ticket needs to be available in the environment of the php process.
+
```php
$serverFactory = new ServerFactory();
$auth = new KerberosAuth();
$server = $serverFactory->createServer('localhost', $auth);
```
-Note that this requires a valid kerberos ticket to already be available for php
+### Re-using a client ticket
+
+By re-using a client ticket you can create a single sign-on setup where the user authenticates against
+the web service using kerberos. And the web server can forward that ticket to the smb server, allowing it
+to act on the behalf of the user without requiring the user to enter his passord.
+
+The setup for such a system is fairly involved and requires roughly the following this
+
+- The web server is authenticated against kerberos with a machine account
+- Delegation is enabled for the web server's machine account
+- Apache is setup to perform kerberos authentication and save the ticket in it's environment
+- Php has the krb5 extension installed
+- The client authenticates using a ticket with forwarding enabled
+
+```php
+$serverFactory = new ServerFactory();
+$auth = new KerberosApacheAuth();
+$server = $serverFactory->createServer('localhost', $auth);
+```
### Upload a file ###
diff --git a/apps/files_external/3rdparty/icewind/smb/src/IShare.php b/apps/files_external/3rdparty/icewind/smb/src/IShare.php
index 6ac6e0d2d15..40213b93a99 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/IShare.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/IShare.php
@@ -45,7 +45,7 @@ interface IShare {
public function put(string $source, string $target): bool;
/**
- * Open a readable stream top a remote file
+ * Open a readable stream to a remote file
*
* @param string $source
* @return resource a read only stream with the contents of the remote file
diff --git a/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php b/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php
new file mode 100644
index 00000000000..c49918be114
--- /dev/null
+++ b/apps/files_external/3rdparty/icewind/smb/src/KerberosApacheAuth.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Icewind\SMB;
+
+use Icewind\SMB\Exception\DependencyException;
+use Icewind\SMB\Exception\Exception;
+
+/**
+ * Use existing kerberos ticket to authenticate and reuse the apache ticket cache (mod_auth_kerb)
+ */
+class KerberosApacheAuth extends KerberosAuth implements IAuth {
+ /** @var string */
+ private $ticketPath = "";
+
+ /** @var bool */
+ private $init = false;
+
+ /** @var string|false */
+ private $ticketName;
+
+ public function __construct() {
+ $this->ticketName = getenv("KRB5CCNAME");
+ }
+
+
+ /**
+ * Copy the ticket to a temporary location and use that ticket for authentication
+ *
+ * @return void
+ */
+ public function copyTicket(): void {
+ if (!$this->checkTicket()) {
+ return;
+ }
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ $tmpFilename = tempnam("/tmp", "krb5cc_php_");
+ $tmpCacheFile = "FILE:" . $tmpFilename;
+ $krb5->save($tmpCacheFile);
+ $this->ticketPath = $tmpFilename;
+ $this->ticketName = $tmpCacheFile;
+ }
+
+ /**
+ * Pass the ticket to smbclient by memory instead of path
+ *
+ * @return void
+ */
+ public function passTicketFromMemory(): void {
+ if (!$this->checkTicket()) {
+ return;
+ }
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ $this->ticketName = (string)$krb5->getName();
+ }
+
+ /**
+ * Check if a valid kerberos ticket is present
+ *
+ * @return bool
+ * @psalm-assert-if-true string $this->ticketName
+ */
+ public function checkTicket(): bool {
+ //read apache kerberos ticket cache
+ if (!$this->ticketName) {
+ return false;
+ }
+
+ $krb5 = new \KRB5CCache();
+ $krb5->open($this->ticketName);
+ /** @psalm-suppress MixedArgument */
+ return count($krb5->getEntries()) > 0;
+ }
+
+ private function init(): void {
+ if ($this->init) {
+ return;
+ }
+ $this->init = true;
+ // inspired by https://git.typo3.org/TYPO3CMS/Extensions/fal_cifs.git
+
+ if (!extension_loaded("krb5")) {
+ // https://pecl.php.net/package/krb5
+ throw new DependencyException('Ensure php-krb5 is installed.');
+ }
+
+ //read apache kerberos ticket cache
+ if (!$this->checkTicket()) {
+ throw new Exception('No kerberos ticket cache environment variable (KRB5CCNAME) found.');
+ }
+
+ // note that even if the ticketname is the value we got from `getenv("KRB5CCNAME")` we still need to set the env variable ourselves
+ // this is because `getenv` also reads the variables passed from the SAPI (apache-php) and we need to set the variable in the OS's env
+ putenv("KRB5CCNAME=" . $this->ticketName);
+ }
+
+ public function getExtraCommandLineArguments(): string {
+ $this->init();
+ return parent::getExtraCommandLineArguments();
+ }
+
+ public function setExtraSmbClientOptions($smbClientState): void {
+ $this->init();
+ try {
+ parent::setExtraSmbClientOptions($smbClientState);
+ } catch (Exception $e) {
+ // suppress
+ }
+ }
+
+ public function __destruct() {
+ if (!empty($this->ticketPath) && file_exists($this->ticketPath) && is_file($this->ticketPath)) {
+ unlink($this->ticketPath);
+ }
+ }
+}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
index 539bb728426..85fb0274ac1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeFileInfo.php
@@ -99,7 +99,7 @@ class NativeFileInfo implements IFileInfo {
public function isDirectory(): bool {
$mode = $this->getMode();
if ($mode > 0x1000) {
- return (bool)($mode & 0x4000); // 0x4000: unix directory flag
+ return ($mode & 0x4000 && !($mode & 0x8000)); // 0x4000: unix directory flag shares bits with 0xC000: socket
} else {
return (bool)($mode & IFileInfo::MODE_DIRECTORY);
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
index 03ec501b830..8c4eab2a60f 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeShare.php
@@ -267,14 +267,14 @@ class NativeShare extends AbstractShare {
* Open a writeable stream to a remote file
* Note: This method will truncate the file to 0bytes first
*
- * @param string $source
+ * @param string $target
* @return resource a writeable stream
*
* @throws NotFoundException
* @throws InvalidTypeException
*/
- public function write(string $source) {
- $url = $this->buildUrl($source);
+ public function write(string $target) {
+ $url = $this->buildUrl($target);
$handle = $this->getState()->create($url);
return NativeWriteStream::wrap($this->getState(), $handle, 'w', $url);
}
@@ -282,14 +282,14 @@ class NativeShare extends AbstractShare {
/**
* Open a writeable stream and set the cursor to the end of the stream
*
- * @param string $source
+ * @param string $target
* @return resource a writeable stream
*
* @throws NotFoundException
* @throws InvalidTypeException
*/
- public function append(string $source) {
- $url = $this->buildUrl($source);
+ public function append(string $target) {
+ $url = $this->buildUrl($target);
$handle = $this->getState()->open($url, "a+");
return NativeWriteStream::wrap($this->getState(), $handle, "a", $url);
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
index e1a13ce3e72..088e6f733d1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Native/NativeState.php
@@ -38,6 +38,14 @@ class NativeState {
/** @var bool */
protected $connected = false;
+ /**
+ * sync the garbage collection cycle
+ * __deconstruct() of KerberosAuth should not called too soon
+ *
+ * @var IAuth|null $auth
+ */
+ protected $auth = null;
+
// see error.h
const EXCEPTION_MAP = [
1 => ForbiddenException::class,
@@ -107,6 +115,11 @@ class NativeState {
}
$auth->setExtraSmbClientOptions($this->state);
+
+ // sync the garbage collection cycle
+ // __deconstruct() of KerberosAuth should not caled too soon
+ $this->auth = $auth;
+
/** @var bool $result */
$result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword());
diff --git a/apps/files_external/3rdparty/icewind/smb/src/System.php b/apps/files_external/3rdparty/icewind/smb/src/System.php
index 919907477ab..d3475e7a5cb 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/System.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/System.php
@@ -62,7 +62,7 @@ class System implements ISystem {
$result = null;
$output = [];
exec("which $binary 2>&1", $output, $result);
- $this->paths[$binary] = $result === 0 ? trim(implode('', $output)) : null;
+ $this->paths[$binary] = $result === 0 && isset($output[0]) ? (string)$output[0] : null;
}
return $this->paths[$binary];
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
index 31b72b05d97..cc73ac1ad14 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Connection.php
@@ -47,7 +47,7 @@ class Connection extends RawConnection {
public function clearTillPrompt(): void {
$this->write('');
do {
- $promptLine = $this->readLine();
+ $promptLine = $this->readTillPrompt();
if ($promptLine === false) {
break;
}
@@ -56,13 +56,12 @@ class Connection extends RawConnection {
if ($this->write('') === false) {
throw new ConnectionRefusedException();
}
- $this->readLine();
+ $this->readTillPrompt();
}
/**
* get all unprocessed output from smbclient until the next prompt
*
- * @param (callable(string):bool)|null $callback (optional) callback to call for every line read
* @return string[]
* @throws AuthenticationException
* @throws ConnectException
@@ -71,42 +70,22 @@ class Connection extends RawConnection {
* @throws NoLoginServerException
* @throws AccessDeniedException
*/
- public function read(callable $callback = null): array {
+ public function read(): array {
if (!$this->isValid()) {
throw new ConnectionException('Connection not valid');
}
- $promptLine = $this->readLine(); //first line is prompt
- if ($promptLine === false) {
- $this->unknownError($promptLine);
- }
- $this->parser->checkConnectionError($promptLine);
-
- $output = [];
- if (!$this->isPrompt($promptLine)) {
- $line = $promptLine;
- } else {
- $line = $this->readLine();
- }
- if ($line === false) {
- $this->unknownError($promptLine);
- }
- while ($line !== false && !$this->isPrompt($line)) { //next prompt functions as delimiter
- if (is_callable($callback)) {
- $result = $callback($line);
- if ($result === false) { // allow the callback to close the connection for infinite running commands
- $this->close(true);
- break;
- }
- } else {
- $output[] = $line;
- }
- $line = $this->readLine();
+ $output = $this->readTillPrompt();
+ if ($output === false) {
+ $this->unknownError(false);
}
+ $output = explode("\n", $output);
+ // last line contains the prompt
+ array_pop($output);
return $output;
}
private function isPrompt(string $line): bool {
- return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER;
+ return substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER;
}
/**
@@ -132,6 +111,6 @@ class Connection extends RawConnection {
// ignore any errors while trying to send the close command, the process might already be dead
@$this->write('close' . PHP_EOL);
}
- parent::close($terminate);
+ $this->close_process($terminate);
}
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
index 18451f4daa6..ecb5bb1e3c1 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/NotifyHandler.php
@@ -65,16 +65,20 @@ class NotifyHandler implements INotifyHandler {
*/
public function listen(callable $callback): void {
if ($this->listening) {
- $this->connection->read(function (string $line) use ($callback): bool {
+ while (true) {
+ $line = $this->connection->readLine();
+ if ($line === false) {
+ break;
+ }
$this->checkForError($line);
$change = $this->parseChangeLine($line);
if ($change) {
$result = $callback($change);
- return $result === false ? false : true;
- } else {
- return true;
+ if ($result === false) {
+ break;
+ }
}
- });
+ };
}
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
index 26a17cc584b..4aec674c3da 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/RawConnection.php
@@ -71,7 +71,8 @@ class RawConnection {
setlocale(LC_ALL, Server::LOCALE);
$env = array_merge($this->env, [
- 'CLI_FORCE_INTERACTIVE' => 'y', // Needed or the prompt isn't displayed!!
+ 'CLI_FORCE_INTERACTIVE' => 'y', // Make sure the prompt is displayed
+ 'CLI_NO_READLINE' => 1, // Not all distros build smbclient with readline, disable it to get consistent behaviour
'LC_ALL' => Server::LOCALE,
'LANG' => Server::LOCALE,
'COLUMNS' => 8192 // prevent smbclient from line-wrapping it's output
@@ -91,7 +92,7 @@ class RawConnection {
public function isValid(): bool {
if (is_resource($this->process)) {
$status = proc_get_status($this->process);
- return (bool)$status['running'];
+ return $status['running'];
} else {
return false;
}
@@ -110,12 +111,29 @@ class RawConnection {
}
/**
+ * read output till the next prompt
+ *
+ * @return string|false
+ */
+ public function readTillPrompt() {
+ $output = "";
+ do {
+ $chunk = $this->readLine('\> ');
+ if ($chunk === false) {
+ return false;
+ }
+ $output .= $chunk;
+ } while (strlen($chunk) == 4096 && strpos($chunk, "smb:") === false);
+ return $output;
+ }
+
+ /**
* read a line of output
*
* @return string|false
*/
- public function readLine() {
- return stream_get_line($this->getOutputStream(), 4086, "\n");
+ public function readLine(string $end = "\n") {
+ return stream_get_line($this->getOutputStream(), 4096, $end);
}
/**
@@ -202,6 +220,14 @@ class RawConnection {
* @psalm-assert null $this->process
*/
public function close(bool $terminate = true): void {
+ $this->close_process($terminate);
+ }
+
+ /**
+ * @param bool $terminate
+ * @psalm-assert null $this->process
+ */
+ protected function close_process(bool $terminate = true): void {
if (!is_resource($this->process)) {
return;
}
diff --git a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
index 68446d380e0..eb68d3800b3 100644
--- a/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
+++ b/apps/files_external/3rdparty/icewind/smb/src/Wrapped/Share.php
@@ -345,11 +345,17 @@ class Share extends AbstractShare {
// since returned stream is closed by the caller we need to create a new instance
// since we can't re-use the same file descriptor over multiple calls
$connection = $this->getConnection();
+ stream_set_blocking($connection->getOutputStream(), false);
$connection->write('get ' . $source . ' ' . $this->system->getFD(5));
$connection->write('exit');
$fh = $connection->getFileOutputStream();
- stream_context_set_option($fh, 'file', 'connection', $connection);
+ $fh = CallbackWrapper::wrap($fh, function() use ($connection) {
+ $connection->write('');
+ });
+ if (!is_resource($fh)) {
+ throw new Exception("Failed to wrap file output");
+ }
return $fh;
}
@@ -374,7 +380,9 @@ class Share extends AbstractShare {
// use a close callback to ensure the upload is finished before continuing
// this also serves as a way to keep the connection in scope
- $stream = CallbackWrapper::wrap($fh, null, null, function () use ($connection) {
+ $stream = CallbackWrapper::wrap($fh, function() use ($connection) {
+ $connection->write('');
+ }, null, function () use ($connection) {
$connection->close(false); // dont terminate, give the upload some time
});
if (is_resource($stream)) {
@@ -446,7 +454,7 @@ class Share extends AbstractShare {
* @return string[]
*/
protected function execute(string $command): array {
- $this->connect()->write($command . PHP_EOL);
+ $this->connect()->write($command);
return $this->connect()->read();
}
diff --git a/apps/files_external/lib/AppInfo/Application.php b/apps/files_external/lib/AppInfo/Application.php
index 7f6d8863350..6f8018746b3 100644
--- a/apps/files_external/lib/AppInfo/Application.php
+++ b/apps/files_external/lib/AppInfo/Application.php
@@ -31,8 +31,6 @@ namespace OCA\Files_External\AppInfo;
use OCA\Files_External\Config\ConfigAdapter;
use OCA\Files_External\Config\UserPlaceholderHandler;
-use OCA\Files_External\Listener\GroupDeletedListener;
-use OCA\Files_External\Listener\UserDeletedListener;
use OCA\Files_External\Lib\Auth\AmazonS3\AccessKey;
use OCA\Files_External\Lib\Auth\Builtin;
use OCA\Files_External\Lib\Auth\NullMechanism;
@@ -49,6 +47,7 @@ use OCA\Files_External\Lib\Auth\Password\UserGlobalAuth;
use OCA\Files_External\Lib\Auth\Password\UserProvided;
use OCA\Files_External\Lib\Auth\PublicKey\RSA;
use OCA\Files_External\Lib\Auth\PublicKey\RSAPrivateKey;
+use OCA\Files_External\Lib\Auth\SMB\KerberosApacheAuth;
use OCA\Files_External\Lib\Auth\SMB\KerberosAuth;
use OCA\Files_External\Lib\Backend\AmazonS3;
use OCA\Files_External\Lib\Backend\DAV;
@@ -62,6 +61,8 @@ use OCA\Files_External\Lib\Backend\SMB_OC;
use OCA\Files_External\Lib\Backend\Swift;
use OCA\Files_External\Lib\Config\IAuthMechanismProvider;
use OCA\Files_External\Lib\Config\IBackendProvider;
+use OCA\Files_External\Listener\GroupDeletedListener;
+use OCA\Files_External\Listener\UserDeletedListener;
use OCA\Files_External\Service\BackendService;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
@@ -126,16 +127,16 @@ class Application extends App implements IBackendProvider, IAuthMechanismProvide
$container = $this->getContainer();
$backends = [
- $container->query(Local::class),
- $container->query(FTP::class),
- $container->query(DAV::class),
- $container->query(OwnCloud::class),
- $container->query(SFTP::class),
- $container->query(AmazonS3::class),
- $container->query(Swift::class),
- $container->query(SFTP_Key::class),
- $container->query(SMB::class),
- $container->query(SMB_OC::class),
+ $container->get(Local::class),
+ $container->get(FTP::class),
+ $container->get(DAV::class),
+ $container->get(OwnCloud::class),
+ $container->get(SFTP::class),
+ $container->get(AmazonS3::class),
+ $container->get(Swift::class),
+ $container->get(SFTP_Key::class),
+ $container->get(SMB::class),
+ $container->get(SMB_OC::class),
];
return $backends;
@@ -149,37 +150,38 @@ class Application extends App implements IBackendProvider, IAuthMechanismProvide
return [
// AuthMechanism::SCHEME_NULL mechanism
- $container->query(NullMechanism::class),
+ $container->get(NullMechanism::class),
// AuthMechanism::SCHEME_BUILTIN mechanism
- $container->query(Builtin::class),
+ $container->get(Builtin::class),
// AuthMechanism::SCHEME_PASSWORD mechanisms
- $container->query(Password::class),
- $container->query(SessionCredentials::class),
- $container->query(LoginCredentials::class),
- $container->query(UserProvided::class),
- $container->query(GlobalAuth::class),
- $container->query(UserGlobalAuth::class),
+ $container->get(Password::class),
+ $container->get(SessionCredentials::class),
+ $container->get(LoginCredentials::class),
+ $container->get(UserProvided::class),
+ $container->get(GlobalAuth::class),
+ $container->get(UserGlobalAuth::class),
// AuthMechanism::SCHEME_OAUTH1 mechanisms
- $container->query(OAuth1::class),
+ $container->get(OAuth1::class),
// AuthMechanism::SCHEME_OAUTH2 mechanisms
- $container->query(OAuth2::class),
+ $container->get(OAuth2::class),
// AuthMechanism::SCHEME_PUBLICKEY mechanisms
- $container->query(RSA::class),
- $container->query(RSAPrivateKey::class),
+ $container->get(RSA::class),
+ $container->get(RSAPrivateKey::class),
// AuthMechanism::SCHEME_OPENSTACK mechanisms
- $container->query(OpenStackV2::class),
- $container->query(OpenStackV3::class),
- $container->query(Rackspace::class),
+ $container->get(OpenStackV2::class),
+ $container->get(OpenStackV3::class),
+ $container->get(Rackspace::class),
// Specialized mechanisms
- $container->query(AccessKey::class),
- $container->query(KerberosAuth::class),
+ $container->get(AccessKey::class),
+ $container->get(KerberosAuth::class),
+ $container->get(KerberosApacheAuth::class),
];
}
}
diff --git a/apps/files_external/lib/Lib/Auth/SMB/KerberosApacheAuth.php b/apps/files_external/lib/Lib/Auth/SMB/KerberosApacheAuth.php
new file mode 100644
index 00000000000..88aaa417a87
--- /dev/null
+++ b/apps/files_external/lib/Lib/Auth/SMB/KerberosApacheAuth.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_External\Lib\Auth\SMB;
+
+use OCA\Files_External\Lib\Auth\AuthMechanism;
+use OCA\Files_External\Lib\DefinitionParameter;
+use OCP\Authentication\LoginCredentials\IStore;
+use OCP\IL10N;
+
+class KerberosApacheAuth extends AuthMechanism {
+ /** @var IStore */
+ private $credentialsStore;
+
+ public function __construct(IL10N $l, IStore $credentialsStore) {
+ $realm = new DefinitionParameter('default_realm', 'Default realm');
+ $realm
+ ->setType(DefinitionParameter::VALUE_TEXT)
+ ->setFlag(DefinitionParameter::FLAG_OPTIONAL)
+ ->setTooltip($l->t('Kerberos default realm, defaults to "WORKGROUP"'));
+ $this
+ ->setIdentifier('smb::kerberosapache')
+ ->setScheme(self::SCHEME_SMB)
+ ->setText($l->t('Kerberos ticket apache mode'))
+ ->addParameter($realm);
+ $this->credentialsStore = $credentialsStore;
+ }
+
+ public function getCredentialsStore(): IStore {
+ return $this->credentialsStore;
+ }
+}
diff --git a/apps/files_external/lib/Lib/Backend/SMB.php b/apps/files_external/lib/Lib/Backend/SMB.php
index 867648824ac..57ee866f3c7 100644
--- a/apps/files_external/lib/Lib/Backend/SMB.php
+++ b/apps/files_external/lib/Lib/Backend/SMB.php
@@ -24,16 +24,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OCA\Files_External\Lib\Backend;
use Icewind\SMB\BasicAuth;
+use Icewind\SMB\KerberosApacheAuth;
use Icewind\SMB\KerberosAuth;
use OCA\Files_External\Lib\Auth\AuthMechanism;
use OCA\Files_External\Lib\Auth\Password\Password;
+use OCA\Files_External\Lib\Auth\SMB\KerberosApacheAuth as KerberosApacheAuthMechanism;
use OCA\Files_External\Lib\DefinitionParameter;
+use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
use OCA\Files_External\Lib\LegacyDependencyCheckPolyfill;
use OCA\Files_External\Lib\StorageConfig;
-
use OCP\IL10N;
use OCP\IUser;
@@ -69,10 +72,6 @@ class SMB extends Backend {
->setLegacyAuthMechanism($legacyAuth);
}
- /**
- * @param StorageConfig $storage
- * @param IUser $user
- */
public function manipulateStorageConfig(StorageConfig &$storage, IUser $user = null) {
$auth = $storage->getAuthMechanism();
if ($auth->getScheme() === AuthMechanism::SCHEME_PASSWORD) {
@@ -90,6 +89,45 @@ class SMB extends Backend {
case 'smb::kerberos':
$smbAuth = new KerberosAuth();
break;
+ case 'smb::kerberosapache':
+ if (!$auth instanceof KerberosApacheAuthMechanism) {
+ throw new \InvalidArgumentException('invalid authentication backend');
+ }
+ $credentialsStore = $auth->getCredentialsStore();
+ $kerbAuth = new KerberosApacheAuth();
+ // check if a kerberos ticket is available, else fallback to session credentials
+ if ($kerbAuth->checkTicket()) {
+ $smbAuth = $kerbAuth;
+ } else {
+ try {
+ $credentials = $credentialsStore->getLoginCredentials();
+ $user = $credentials->getLoginName();
+ $pass = $credentials->getPassword();
+ preg_match('/(.*)@(.*)/', $user, $matches);
+ $realm = $storage->getBackendOption('default_realm');
+ if (empty($realm)) {
+ $realm = 'WORKGROUP';
+ }
+ $userPart = $matches[1];
+ $domainPart = $matches[2];
+ if (count($matches) === 0) {
+ $username = $user;
+ $workgroup = $realm;
+ } else {
+ $username = $userPart;
+ $workgroup = $domainPart;
+ }
+ $smbAuth = new BasicAuth(
+ $username,
+ $workgroup,
+ $pass
+ );
+ } catch (\Exception $e) {
+ throw new InsufficientDataForMeaningfulAnswerException('No session credentials saved');
+ }
+ }
+
+ break;
default:
throw new \InvalidArgumentException('unknown authentication backend');
}
diff --git a/apps/files_external/tests/setup-krb.sh b/apps/files_external/tests/setup-krb.sh
new file mode 100755
index 00000000000..968ba80529e
--- /dev/null
+++ b/apps/files_external/tests/setup-krb.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+function getContainerHealth {
+ docker inspect --format "{{.State.Health.Status}}" $1
+}
+
+function waitContainer {
+ while STATUS=$(getContainerHealth $1); [ $STATUS != "healthy" ]; do
+ if [ $STATUS == "unhealthy" ]; then
+ echo "Failed!"
+ exit -1
+ fi
+ printf .
+ lf=$'\n'
+ sleep 1
+ done
+ printf "$lf"
+}
+
+mkdir /tmp/shared
+
+# start the dc
+docker run -dit --name dc -v /tmp/shared:/shared --hostname krb.domain.test --cap-add SYS_ADMIN icewind1991/samba-krb-test-dc
+DC_IP=$(docker inspect dc --format '{{.NetworkSettings.IPAddress}}')
+
+waitContainer dc
+
+# start apache
+docker run -d --name apache -v $PWD:/var/www/html -v /tmp/shared:/shared --dns $DC_IP --hostname httpd.domain.test icewind1991/samba-krb-test-apache
+APACHE_IP=$(docker inspect apache --format '{{.NetworkSettings.IPAddress}}')
+
+# add the dns record for apache
+docker exec dc samba-tool dns add krb.domain.test domain.test httpd A $APACHE_IP -U administrator --password=passwOrd1