Subversion Repositories oidplus

Rev

Rev 1249 | Rev 1324 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1249 Rev 1284
Line 1086... Line 1086...
1086
     * @var bool
1086
     * @var bool
1087
     */
1087
     */
1088
    private $smartMFA = true;
1088
    private $smartMFA = true;
1089
 
1089
 
1090
    /**
1090
    /**
-
 
1091
     * How many channels are currently opened
-
 
1092
     *
-
 
1093
     * @var int
-
 
1094
     */
-
 
1095
    private $channelCount = 0;
-
 
1096
 
-
 
1097
    /**
-
 
1098
     * Does the server support multiple channels? If not then error out
-
 
1099
     * when multiple channels are attempted to be opened
-
 
1100
     *
-
 
1101
     * @var bool
-
 
1102
     */
-
 
1103
    private $errorOnMultipleChannels;
-
 
1104
 
-
 
1105
    /**
1091
     * Default Constructor.
1106
     * Default Constructor.
1092
     *
1107
     *
1093
     * $host can either be a string, representing the host, or a stream resource.
1108
     * $host can either be a string, representing the host, or a stream resource.
1094
     *
1109
     *
1095
     * @param mixed $host
1110
     * @param mixed $host
Line 1382... Line 1397...
1382
        if (version_compare($matches[3], '1.99', '<')) {
1397
        if (version_compare($matches[3], '1.99', '<')) {
1383
            $this->bitmap = 0;
1398
            $this->bitmap = 0;
1384
            throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
1399
            throw new UnableToConnectException("Cannot connect to SSH $matches[3] servers");
1385
        }
1400
        }
1386
 
1401
 
-
 
1402
        // Ubuntu's OpenSSH from 5.8 to 6.9 didn't work with multiple channels. see
-
 
1403
        // https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/1334916 for more info.
-
 
1404
        // https://lists.ubuntu.com/archives/oneiric-changes/2011-July/005772.html discusses
-
 
1405
        // when consolekit was incorporated.
-
 
1406
        // https://marc.info/?l=openssh-unix-dev&m=163409903417589&w=2 discusses some of the
-
 
1407
        // issues with how Ubuntu incorporated consolekit
-
 
1408
        $pattern = '#^SSH-2\.0-OpenSSH_([\d.]+)[^ ]* Ubuntu-.*$#';
-
 
1409
        $match = preg_match($pattern, $this->server_identifier, $matches);
-
 
1410
        $match = $match && version_compare('5.8', $matches[1], '<=');
-
 
1411
        $match = $match && version_compare('6.9', $matches[1], '>=');
-
 
1412
        $this->errorOnMultipleChannels = $match;
-
 
1413
 
1387
        if (!$this->send_id_string_first) {
1414
        if (!$this->send_id_string_first) {
1388
            fputs($this->fsock, $this->identifier . "\r\n");
1415
            fputs($this->fsock, $this->identifier . "\r\n");
1389
        }
1416
        }
1390
 
1417
 
1391
        if (!$this->send_kex_first) {
1418
        if (!$this->send_kex_first) {
Line 2723... Line 2750...
2723
 
2750
 
2724
        if (!$this->isAuthenticated()) {
2751
        if (!$this->isAuthenticated()) {
2725
            return false;
2752
            return false;
2726
        }
2753
        }
2727
 
2754
 
2728
        if ($this->isPTYOpen()) {
2755
        //if ($this->isPTYOpen()) {
2729
            throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
2756
        //    throw new \RuntimeException('If you want to run multiple exec()\'s you will need to disable (and re-enable if appropriate) a PTY for each one.');
2730
        }
2757
        //}
2731
 
-
 
2732
        // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
-
 
2733
        // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
-
 
2734
        // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
-
 
2735
        // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
-
 
2736
        $this->window_size_server_to_client[self::CHANNEL_EXEC] = $this->window_size;
-
 
2737
        // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
-
 
2738
        // uses 0x4000, that's what will be used here, as well.
-
 
2739
        $packet_size = 0x4000;
-
 
2740
 
-
 
2741
        $packet = Strings::packSSH2(
-
 
2742
            'CsN3',
-
 
2743
            NET_SSH2_MSG_CHANNEL_OPEN,
-
 
2744
            'session',
-
 
2745
            self::CHANNEL_EXEC,
-
 
2746
            $this->window_size_server_to_client[self::CHANNEL_EXEC],
-
 
2747
            $packet_size
-
 
2748
        );
-
 
2749
        $this->send_binary_packet($packet);
-
 
2750
 
-
 
2751
        $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN;
-
 
2752
 
2758
 
2753
        $this->get_channel_packet(self::CHANNEL_EXEC);
2759
        $this->openChannel(self::CHANNEL_EXEC);
2754
 
2760
 
2755
        if ($this->request_pty === true) {
2761
        if ($this->request_pty === true) {
2756
            $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2762
            $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2757
            $packet = Strings::packSSH2(
2763
            $packet = Strings::packSSH2(
2758
                'CNsCsN4s',
2764
                'CNsCsN4s',
Line 2829... Line 2835...
2829
            }
2835
            }
2830
        }
2836
        }
2831
    }
2837
    }
2832
 
2838
 
2833
    /**
2839
    /**
2834
     * Creates an interactive shell
2840
     * How many channels are currently open?
2835
     *
2841
     *
-
 
2842
     * @return int
-
 
2843
     */
2836
     * Returns bool(true) if the shell was opened.
2844
    public function getOpenChannelCount()
-
 
2845
    {
2837
     * Returns bool(false) if the shell was already open.
2846
        return $this->channelCount;
-
 
2847
    }
-
 
2848
 
-
 
2849
    /**
-
 
2850
     * Opens a channel
2838
     *
2851
     *
2839
     * @see self::isShellOpen()
-
 
2840
     * @see self::read()
2852
     * @param string $channel
2841
     * @see self::write()
2853
     * @param bool $skip_extended
2842
     * @return bool
2854
     * @return bool
2843
     * @throws InsufficientSetupException if not authenticated
-
 
2844
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2845
     * @throws \RuntimeException on other errors
-
 
2846
     */
2855
     */
2847
    public function openShell()
2856
    protected function openChannel($channel, $skip_extended = false)
2848
    {
2857
    {
2849
        if ($this->isShellOpen()) {
2858
        if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_CLOSE) {
2850
            return false;
2859
            throw new \RuntimeException('Please close the channel (' . $channel . ') before trying to open it again');
2851
        }
2860
        }
2852
 
2861
 
2853
        if (!$this->isAuthenticated()) {
2862
        $this->channelCount++;
-
 
2863
 
-
 
2864
        if ($this->channelCount > 1 && $this->errorOnMultipleChannels) {
2854
            throw new InsufficientSetupException('Operation disallowed prior to login()');
2865
            throw new \RuntimeException("Ubuntu's OpenSSH from 5.8 to 6.9 doesn't work with multiple channels");
2855
        }
2866
        }
2856
 
2867
 
-
 
2868
        // RFC4254 defines the (client) window size as "bytes the other party can send before it must wait for the window to
-
 
2869
        // be adjusted".  0x7FFFFFFF is, at 2GB, the max size.  technically, it should probably be decremented, but,
-
 
2870
        // honestly, if you're transferring more than 2GB, you probably shouldn't be using phpseclib, anyway.
-
 
2871
        // see http://tools.ietf.org/html/rfc4254#section-5.2 for more info
2857
        $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size;
2872
        $this->window_size_server_to_client[$channel] = $this->window_size;
-
 
2873
        // 0x8000 is the maximum max packet size, per http://tools.ietf.org/html/rfc4253#section-6.1, although since PuTTy
-
 
2874
        // uses 0x4000, that's what will be used here, as well.
2858
        $packet_size = 0x4000;
2875
        $packet_size = 0x4000;
2859
 
2876
 
2860
        $packet = Strings::packSSH2(
2877
        $packet = Strings::packSSH2(
2861
            'CsN3',
2878
            'CsN3',
2862
            NET_SSH2_MSG_CHANNEL_OPEN,
2879
            NET_SSH2_MSG_CHANNEL_OPEN,
2863
            'session',
2880
            'session',
2864
            self::CHANNEL_SHELL,
2881
            $channel,
2865
            $this->window_size_server_to_client[self::CHANNEL_SHELL],
2882
            $this->window_size_server_to_client[$channel],
2866
            $packet_size
2883
            $packet_size
2867
        );
2884
        );
2868
 
2885
 
2869
        $this->send_binary_packet($packet);
2886
        $this->send_binary_packet($packet);
2870
 
2887
 
2871
        $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN;
2888
        $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_OPEN;
2872
 
2889
 
-
 
2890
        return $this->get_channel_packet($channel, $skip_extended);
-
 
2891
    }
-
 
2892
 
-
 
2893
    /**
-
 
2894
     * Creates an interactive shell
-
 
2895
     *
-
 
2896
     * Returns bool(true) if the shell was opened.
-
 
2897
     * Returns bool(false) if the shell was already open.
-
 
2898
     *
-
 
2899
     * @see self::isShellOpen()
-
 
2900
     * @see self::read()
-
 
2901
     * @see self::write()
-
 
2902
     * @return bool
-
 
2903
     * @throws InsufficientSetupException if not authenticated
-
 
2904
     * @throws \UnexpectedValueException on receipt of unexpected packets
-
 
2905
     * @throws \RuntimeException on other errors
-
 
2906
     */
-
 
2907
    public function openShell()
-
 
2908
    {
-
 
2909
        if (!$this->isAuthenticated()) {
-
 
2910
            throw new InsufficientSetupException('Operation disallowed prior to login()');
-
 
2911
        }
-
 
2912
 
2873
        $this->get_channel_packet(self::CHANNEL_SHELL);
2913
        $this->openChannel(self::CHANNEL_SHELL);
2874
 
2914
 
2875
        $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2915
        $terminal_modes = pack('C', NET_SSH2_TTY_OP_END);
2876
        $packet = Strings::packSSH2(
2916
        $packet = Strings::packSSH2(
2877
            'CNsbsN4s',
2917
            'CNsbsN4s',
2878
            NET_SSH2_MSG_CHANNEL_REQUEST,
2918
            NET_SSH2_MSG_CHANNEL_REQUEST,
Line 3021... Line 3061...
3021
     * @throws \RuntimeException on connection error
3061
     * @throws \RuntimeException on connection error
3022
     * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
3062
     * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
3023
     */
3063
     */
3024
    public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null)
3064
    public function read($expect = '', $mode = self::READ_SIMPLE, $channel = null)
3025
    {
3065
    {
-
 
3066
        if (!$this->isAuthenticated()) {
-
 
3067
            throw new InsufficientSetupException('Operation disallowed prior to login()');
-
 
3068
        }
-
 
3069
 
3026
        $this->curTimeout = $this->timeout;
3070
        $this->curTimeout = $this->timeout;
3027
        $this->is_timeout = false;
3071
        $this->is_timeout = false;
3028
 
3072
 
3029
        if ($channel === null) {
3073
        if ($channel === null) {
3030
            $channel = $this->get_interactive_channel();
3074
            $channel = $this->get_interactive_channel();
3031
        }
3075
        }
3032
 
3076
 
3033
        if (!$this->isInteractiveChannelOpen($channel)) {
3077
        if (!$this->is_channel_status_data($channel) && empty($this->channel_buffers[$channel])) {
3034
            if ($channel != self::CHANNEL_SHELL) {
3078
            if ($channel != self::CHANNEL_SHELL) {
3035
                throw new InsufficientSetupException('Data is not available on channel');
3079
                throw new InsufficientSetupException('Data is not available on channel');
3036
            } elseif (!$this->openShell()) {
3080
            } elseif (!$this->openShell()) {
3037
                throw new \RuntimeException('Unable to initiate an interactive shell session');
3081
                throw new \RuntimeException('Unable to initiate an interactive shell session');
3038
            }
3082
            }
Line 3078... Line 3122...
3078
     * @throws \RuntimeException on connection error
3122
     * @throws \RuntimeException on connection error
3079
     * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
3123
     * @throws InsufficientSetupException on unexpected channel status, possibly due to closure
3080
     */
3124
     */
3081
    public function write($cmd, $channel = null)
3125
    public function write($cmd, $channel = null)
3082
    {
3126
    {
-
 
3127
        if (!$this->isAuthenticated()) {
-
 
3128
            throw new InsufficientSetupException('Operation disallowed prior to login()');
-
 
3129
        }
-
 
3130
 
3083
        if ($channel === null) {
3131
        if ($channel === null) {
3084
            $channel = $this->get_interactive_channel();
3132
            $channel = $this->get_interactive_channel();
3085
        }
3133
        }
3086
 
3134
 
3087
        if (!$this->isInteractiveChannelOpen($channel)) {
3135
        if (!$this->is_channel_status_data($channel)) {
3088
            if ($channel != self::CHANNEL_SHELL) {
3136
            if ($channel != self::CHANNEL_SHELL) {
3089
                throw new InsufficientSetupException('Data is not available on channel');
3137
                throw new InsufficientSetupException('Data is not available on channel');
3090
            } elseif (!$this->openShell()) {
3138
            } elseif (!$this->openShell()) {
3091
                throw new \RuntimeException('Unable to initiate an interactive shell session');
3139
                throw new \RuntimeException('Unable to initiate an interactive shell session');
3092
            }
3140
            }
Line 3108... Line 3156...
3108
     * @param string $subsystem
3156
     * @param string $subsystem
3109
     * @return bool
3157
     * @return bool
3110
     */
3158
     */
3111
    public function startSubsystem($subsystem)
3159
    public function startSubsystem($subsystem)
3112
    {
3160
    {
3113
        $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size;
-
 
3114
 
-
 
3115
        $packet = Strings::packSSH2(
-
 
3116
            'CsN3',
-
 
3117
            NET_SSH2_MSG_CHANNEL_OPEN,
-
 
3118
            'session',
-
 
3119
            self::CHANNEL_SUBSYSTEM,
-
 
3120
            $this->window_size,
-
 
3121
            0x4000
-
 
3122
        );
-
 
3123
 
-
 
3124
        $this->send_binary_packet($packet);
-
 
3125
 
-
 
3126
        $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN;
-
 
3127
 
-
 
3128
        $this->get_channel_packet(self::CHANNEL_SUBSYSTEM);
3161
        $this->openChannel(self::CHANNEL_SUBSYSTEM);
3129
 
3162
 
3130
        $packet = Strings::packSSH2(
3163
        $packet = Strings::packSSH2(
3131
            'CNsCs',
3164
            'CNsCs',
3132
            NET_SSH2_MSG_CHANNEL_REQUEST,
3165
            NET_SSH2_MSG_CHANNEL_REQUEST,
3133
            $this->server_channels[self::CHANNEL_SUBSYSTEM],
3166
            $this->server_channels[self::CHANNEL_SUBSYSTEM],
Line 3301... Line 3334...
3301
                return $this->reconnect();
3334
                return $this->reconnect();
3302
            }
3335
            }
3303
            return false;
3336
            return false;
3304
        }
3337
        }
3305
 
3338
 
3306
        $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size;
-
 
3307
        $packet_size = 0x4000;
-
 
3308
        $packet = Strings::packSSH2(
-
 
3309
            'CsN3',
-
 
3310
            NET_SSH2_MSG_CHANNEL_OPEN,
-
 
3311
            'session',
-
 
3312
            self::CHANNEL_KEEP_ALIVE,
-
 
3313
            $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE],
-
 
3314
            $packet_size
-
 
3315
        );
-
 
3316
 
-
 
3317
        try {
3339
        try {
3318
            $this->send_binary_packet($packet);
-
 
3319
 
-
 
3320
            $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN;
-
 
3321
 
-
 
3322
            $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE);
3340
            $this->openChannel(self::CHANNEL_KEEP_ALIVE);
3323
        } catch (\RuntimeException $e) {
3341
        } catch (\RuntimeException $e) {
3324
            return $this->reconnect();
3342
            return $this->reconnect();
3325
        }
3343
        }
3326
 
3344
 
3327
        $this->close_channel(self::CHANNEL_KEEP_ALIVE);
3345
        $this->close_channel(self::CHANNEL_KEEP_ALIVE);
Line 4109... Line 4127...
4109
                    if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
4127
                    if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) {
4110
                        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
4128
                        $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel]));
4111
                    }
4129
                    }
4112
 
4130
 
4113
                    $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4131
                    $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
-
 
4132
                    $this->channelCount--;
-
 
4133
 
4114
                    if ($client_channel == $channel) {
4134
                    if ($client_channel == $channel) {
4115
                        return true;
4135
                        return true;
4116
                    }
4136
                    }
4117
                    // fall-through
4137
                    // fall-through
4118
                case NET_SSH2_MSG_CHANNEL_EOF:
4138
                case NET_SSH2_MSG_CHANNEL_EOF:
Line 4443... Line 4463...
4443
        if (!$want_reply) {
4463
        if (!$want_reply) {
4444
            $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4464
            $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel]));
4445
        }
4465
        }
4446
 
4466
 
4447
        $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
4467
        $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE;
-
 
4468
        $this->channelCount--;
4448
 
4469
 
4449
        $this->curTimeout = 5;
4470
        $this->curTimeout = 5;
4450
 
4471
 
4451
        while (!is_bool($this->get_channel_packet($client_channel))) {
4472
        while (!is_bool($this->get_channel_packet($client_channel))) {
4452
        }
4473
        }
Line 4915... Line 4936...
4915
            ]
4936
            ]
4916
        ];
4937
        ];
4917
    }
4938
    }
4918
 
4939
 
4919
    /**
4940
    /**
-
 
4941
     * Force multiple channels (even if phpseclib has decided to disable them)
-
 
4942
     */
-
 
4943
    public function forceMultipleChannels()
-
 
4944
    {
-
 
4945
        $this->errorOnMultipleChannels = false;
-
 
4946
    }
-
 
4947
 
-
 
4948
    /**
4920
     * Allows you to set the terminal
4949
     * Allows you to set the terminal
4921
     *
4950
     *
4922
     * @param string $term
4951
     * @param string $term
4923
     */
4952
     */
4924
    public function setTerminal($term)
4953
    public function setTerminal($term)