allow setting protocol levels

This commit is contained in:
Robin Appelman 2021-03-02 21:20:17 +01:00
commit 61012e6196
9 changed files with 149 additions and 60 deletions

View file

@ -125,6 +125,17 @@ $options->setTimeout(5);
$serverFactory = new ServerFactory($options); $serverFactory = new ServerFactory($options);
``` ```
### Setting protocol version
```php
$options = new Options();
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
$options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
$serverFactory = new ServerFactory($options);
```
Note, setting the protocol version is not supported with php-smbclient version 1.0.1 or lower.
### Customizing system integration ### Customizing system integration
The `smbclient` backend needs to get various information about the system it's running on to function The `smbclient` backend needs to get various information about the system it's running on to function

View file

@ -22,8 +22,20 @@
namespace Icewind\SMB; namespace Icewind\SMB;
interface IOptions { interface IOptions {
/** const PROTOCOL_NT1 = 'NT1';
* @return int const PROTOCOL_SMB2 = 'SMB2';
*/ const PROTOCOL_SMB2_02 = 'SMB2_02';
public function getTimeout(); const PROTOCOL_SMB2_22 = 'SMB2_22';
const PROTOCOL_SMB2_24 = 'SMB2_24';
const PROTOCOL_SMB3 = 'SMB3';
const PROTOCOL_SMB3_00 = 'SMB3_00';
const PROTOCOL_SMB3_02 = 'SMB3_02';
const PROTOCOL_SMB3_10 = 'SMB3_10';
const PROTOCOL_SMB3_11 = 'SMB3_11';
public function getTimeout(): int;
public function getMinProtocol(): ?string;
public function getMaxProtocol(): ?string;
} }

View file

@ -91,6 +91,14 @@ class NativeState {
$this->state = smbclient_state_new(); $this->state = smbclient_state_new();
smbclient_option_set($this->state, SMBCLIENT_OPT_AUTO_ANONYMOUS_LOGIN, false); smbclient_option_set($this->state, SMBCLIENT_OPT_AUTO_ANONYMOUS_LOGIN, false);
smbclient_option_set($this->state, SMBCLIENT_OPT_TIMEOUT, $options->getTimeout() * 1000); smbclient_option_set($this->state, SMBCLIENT_OPT_TIMEOUT, $options->getTimeout() * 1000);
if (function_exists('smbclient_client_protocols')) {
$maxProtocol = $options->getMaxProtocol();
$minProtocol = $options->getMinProtocol();
smbclient_client_protocols($this->state, $minProtocol, $maxProtocol);
}
$auth->setExtraSmbClientOptions($this->state); $auth->setExtraSmbClientOptions($this->state);
$result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword()); $result = @smbclient_state_init($this->state, $auth->getWorkgroup(), $auth->getUsername(), $auth->getPassword());

View file

@ -25,11 +25,32 @@ class Options implements IOptions {
/** @var int */ /** @var int */
private $timeout = 20; private $timeout = 20;
public function getTimeout() { /** @var string|null */
private $minProtocol;
/** @var string|null */
private $maxProtocol;
public function getTimeout(): int {
return $this->timeout; return $this->timeout;
} }
public function setTimeout($timeout) { public function setTimeout(int $timeout) {
$this->timeout = $timeout; $this->timeout = $timeout;
} }
public function getMinProtocol(): ?string {
return $this->minProtocol;
}
public function setMinProtocol(?string $minProtocol): void {
$this->minProtocol = $minProtocol;
}
public function getMaxProtocol(): ?string {
return $this->maxProtocol;
}
public function setMaxProtocol(?string $maxProtocol): void {
$this->maxProtocol = $maxProtocol;
}
} }

View file

@ -1,50 +0,0 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 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;
class ProtocolLevel {
/** @var string */
private $level;
private function __construct(string $level) {
$this->level = $level;
}
public function getLevel(): string {
return $this->level;
}
public static function NT1(): self {
return new ProtocolLevel("NT1");
}
public static function SMB2(): self {
return new ProtocolLevel("SMB2");
}
public static function SMB3(): self {
return new ProtocolLevel("SMB3");
}
}

View file

@ -43,11 +43,15 @@ class Server extends AbstractServer {
* @throws ConnectException * @throws ConnectException
*/ */
public function listShares() { public function listShares() {
$maxProtocol = $this->options->getMaxProtocol();
$minProtocol = $this->options->getMinProtocol();
$command = sprintf( $command = sprintf(
'%s %s %s -L %s', '%s %s %s %s %s -L %s',
$this->system->getSmbclientPath(), $this->system->getSmbclientPath(),
$this->getAuthFileArgument(), $this->getAuthFileArgument(),
$this->getAuth()->getExtraCommandLineArguments(), $this->getAuth()->getExtraCommandLineArguments(),
$maxProtocol ? "--option='client max protocol=" . $maxProtocol . "'" : "",
$minProtocol ? "--option='client min protocol=" . $minProtocol . "'" : "",
escapeshellarg('//' . $this->getHost()) escapeshellarg('//' . $this->getHost())
); );
$connection = new RawConnection($command); $connection = new RawConnection($command);

View file

@ -80,14 +80,18 @@ class Share extends AbstractShare {
} }
protected function getConnection() { protected function getConnection() {
$maxProtocol = $this->server->getOptions()->getMaxProtocol();
$minProtocol = $this->server->getOptions()->getMinProtocol();
$command = sprintf( $command = sprintf(
'%s %s%s -t %s %s %s %s', '%s %s%s -t %s %s %s %s %s %s',
self::EXEC_CMD, self::EXEC_CMD,
$this->system->getStdBufPath() ? $this->system->getStdBufPath() . ' -o0 ' : '', $this->system->getStdBufPath() ? $this->system->getStdBufPath() . ' -o0 ' : '',
$this->system->getSmbclientPath(), $this->system->getSmbclientPath(),
$this->server->getOptions()->getTimeout(), $this->server->getOptions()->getTimeout(),
$this->getAuthFileArgument(), $this->getAuthFileArgument(),
$this->server->getAuth()->getExtraCommandLineArguments(), $this->server->getAuth()->getExtraCommandLineArguments(),
$maxProtocol ? "--option='client max protocol=" . $maxProtocol . "'" : "",
$minProtocol ? "--option='client min protocol=" . $minProtocol . "'" : "",
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name) escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
); );
$connection = new Connection($command, $this->parser); $connection = new Connection($command, $this->parser);

View file

@ -8,6 +8,8 @@
namespace Icewind\SMB\Test; namespace Icewind\SMB\Test;
use Icewind\SMB\BasicAuth; use Icewind\SMB\BasicAuth;
use Icewind\SMB\Exception\InvalidArgumentException;
use Icewind\SMB\IOptions;
use Icewind\SMB\Native\NativeServer; use Icewind\SMB\Native\NativeServer;
use Icewind\SMB\Options; use Icewind\SMB\Options;
use Icewind\SMB\System; use Icewind\SMB\System;
@ -20,6 +22,9 @@ class NativeShareTest extends AbstractShareTest {
$this->markTestSkipped('libsmbclient php extension not installed'); $this->markTestSkipped('libsmbclient php extension not installed');
} }
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json')); $this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$options = new Options();
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
$options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
$this->server = new NativeServer( $this->server = new NativeServer(
$this->config->host, $this->config->host,
new BasicAuth( new BasicAuth(
@ -29,7 +34,7 @@ class NativeShareTest extends AbstractShareTest {
), ),
new System(), new System(),
new TimeZoneProvider(new System()), new TimeZoneProvider(new System()),
new Options() $options
); );
$this->share = $this->server->getShare($this->config->share); $this->share = $this->server->getShare($this->config->share);
if ($this->config->root) { if ($this->config->root) {
@ -39,4 +44,41 @@ class NativeShareTest extends AbstractShareTest {
} }
$this->share->mkdir($this->root); $this->share->mkdir($this->root);
} }
public function testProtocolMatch() {
$options = new Options();
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
$options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
$server = new NativeServer(
$this->config->host,
new BasicAuth(
$this->config->user,
'test',
$this->config->password
),
new System(),
new TimeZoneProvider(new System()),
$options
);
$server->listShares();
$this->assertTrue(true);
}
public function testToLowMaxProtocol() {
$this->expectException(InvalidArgumentException::class);
$options = new Options();
$options->setMaxProtocol(IOptions::PROTOCOL_NT1);
$server = new NativeServer(
$this->config->host,
new BasicAuth(
$this->config->user,
'test',
$this->config->password
),
new System(),
new TimeZoneProvider(new System()),
$options
);
$server->listShares();
}
} }

View file

@ -10,7 +10,7 @@ namespace Icewind\SMB\Test;
use Icewind\SMB\BasicAuth; use Icewind\SMB\BasicAuth;
use Icewind\SMB\Exception\AuthenticationException; use Icewind\SMB\Exception\AuthenticationException;
use Icewind\SMB\Exception\ConnectionRefusedException; use Icewind\SMB\Exception\ConnectionRefusedException;
use Icewind\SMB\Exception\InvalidHostException; use Icewind\SMB\IOptions;
use Icewind\SMB\IShare; use Icewind\SMB\IShare;
use Icewind\SMB\Options; use Icewind\SMB\Options;
use Icewind\SMB\System; use Icewind\SMB\System;
@ -114,4 +114,41 @@ class ServerTest extends TestCase {
); );
$server->listShares(); $server->listShares();
} }
public function testProtocolMatch() {
$options = new Options();
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
$options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
$server = new Server(
$this->config->host,
new BasicAuth(
$this->config->user,
'test',
$this->config->password
),
new System(),
new TimeZoneProvider(new System()),
$options
);
$server->listShares();
$this->assertTrue(true);
}
public function testToLowMaxProtocol() {
$this->expectException(ConnectionRefusedException::class);
$options = new Options();
$options->setMaxProtocol(IOptions::PROTOCOL_NT1);
$server = new Server(
$this->config->host,
new BasicAuth(
$this->config->user,
'test',
$this->config->password
),
new System(),
new TimeZoneProvider(new System()),
$options
);
$server->listShares();
}
} }