How to scp with sudo?

0 votes
asked Mar 23 by toby (120 points)

Hi,

I am trying to download a file on a server. I need to login with one set of credentials and then change to another set to access the file I need.

Apparently, this is not supported by SFTP, so I am using SCP instead.

I am creating a SSH session, logging in, executing my 'sudo su USER' command and executing a whoami to confirm the login change. Then I am binding the ssh session to a SCP object and attempting the file transfer. At that point I get a permission error.

var ssh = new Ssh();
ssh.Connect (hostname);
ssh.Login (username, password);
var scripting = ssh.StartScripting("sudo su USER");
scripting.DetectPrompt();
var response = scripting.ReadUntilPrompt();
scripting.SendCommand("whoami");
var response = scripting.ReadUntilPrompt();
Console.WriteLine(response);

// -- All good up to this point --

var scp = new Scp();
scp.Bind(ssh.Session);
scp.GetFile(remotePath, localPath); // Permission failure.

I have played with this for awhile and tried a number of different combinations of the above with the same results. Any ideas what I am doing wrong? Note that I am able to do basically the same thing using WinSCP, so it is not a server configuration / password issue.

Any help would be greatly appreciated. Thanks,

Toby

2 Answers

0 votes
answered Mar 24 by Lukas Pokorny (85,590 points)

Unfortunately, this approach is not going to work. The sudo su USER command only affects the scripting session, not the SCP session you established later. Even though both sessions run over the same SSH connection, they are distinct. To make this work, you would have to start the scp command in the same session where you executed "sudo su USER" (perhaps you are doing something like this in WinSCP), but this is not going to work with Scripting sessions because those have a pseudo-terminal allocated, which would interfere with SCP traffic... Instead, you have to open an SshChannel and use RequestExec, but more on that below. If you can, please send us a communication log from WinScp - if we knew exactly which commands you issue there, we would be able to let you know how to reproduce the same with Rebex libraries.

We already investigated a similar scenario several years ago, so I'm sharing our findings here. It mostly deals with sftp, although the same applies to scp as well.

This is possible with many Unix boxes (just tested it on Debian Linux with OpenSSH), but requires some changes to "sudo" configuration files. A long explanation follows…
 
The SFTP protocol is actually one of the subsystem (sub-protocols) of SSH (other subsystems are 'remote exec' and 'remote shell'), and authentication occurs at the SSH level. Unfortunately, SSH protocol itself doesn't support any additional su-like authentication, although it's of course possible to execute these commands inside the 'remote exec' or 'remote shell' subsystems. Unfortunately, you can't execute these commands in the SFTP subsystem and it doesn't offer any similar functionality either. (The "sftp-su@vandyke.com" protocol extension makes this possible, but it's not widely supported.)
 
However, some SSH/SFTP implementations such as OpenSSH make it possible to "execute" SFTP server using the 'remote exec' subsystem – this works on most Linux boxes, for example. To take advantage of this using Rebex SFTP, you would use the following code:

 

// connect and authenticate to the SSH/SFTP server                         
var ssh = new SshSession();
ssh.Connect("server01");
ssh.Authenticate("userA", "password");

// open an SSH channel
SshChannel channel = ssh.OpenSession();

// execute the SFTP server (works on Debian Linux with OpenSSH)
channel.RequestExec("/usr/lib/sftp-server");

// bind Sftp object to the channel
Sftp sftp = new Sftp();
sftp.Bind(channel.ToSocket());

// try listing files using the Sftp object
Console.WriteLine(string.Join("\n", sftp.GetRawList()));

 

This executes an SFTP session under the logged-on user account.
 
Unfortunately, although it seems quite straightforward to simply execute something like su userB -c /usr/lib/sftp-server instead (and send the password and receive response if needed before proceeding to SFTP), this usually does not work. The su command fails with an error message of su: must be run from a terminal error message (which can be read using channel.Receive method). The error is reported because su is supposed to only be run by users connected through a terminal… Although it's possible to request a terminal by calling channel.RequestPseudoTerminal() before RequestExec(), making it possible to actually authenticate to su or sudo, this has a nasty side effect of making the SFTP session unusable (because pseud-terminals cannot handle binary input/output streams).
 
The same applies to sudo as well by default, but fortunately, sudo is very configurable and the requirement can even be disabled for a single command (even for a single user) – see this forum post for details. By adding the following lines to /etc/sudoers file, I was able to run SFTP as root simply by logging in as userA and calling channel.RequestExec("sudo /usr/lib/sftp-server") – the !requiretty option disables the terminal requirement, while NOPASSWD disables sudo's password prompt for the sftp-server executable:

 
userA    ALL=(ALL)    NOPASSWD:/usr/lib/sftp-server
Defaults:userA        !requiretty
 
Although we are not very familiar with sudo's configuration options, it looks like it is possible to configure it to execute a command as non-root user as well.

commented Mar 24 by toby (120 points)
Hi Lukas - many thanks for the quick and detailed response!

I want to stay focused on the SCP command as I think this has the best chance of success. I am not able to modify the server configuration as this is outside of my control.

Looking at WinSCP I am using the SHELL option described here :
https://winscp.net/eng/docs/ui_login_scp

"You can also make use of the option to alter session startup. For example to change the user after login (known as su)"

My shell is defined as "sudo su USER".

This seems to coincide with your comment that "you would have to start the scp command in the same session where you executed"

How can I do that with Rebex?

Many thanks,

Toby
commented Mar 24 by toby (120 points)
Just to follow up on the approach provided... The SFTP code works as long as I do not Sudo prior to the SFTP call.  So this will not work for me.

I tried the same approach with SCP instead.  This fails because SCP cannot bind to an ISocket. ... Which makes sense.

The SshSession object seems more low level than the Ssh object.  I will keep playing with it, but any help would be greatly appreciated.

Thanks,

Toby
commented Mar 24 by Lukas Pokorny (85,590 points)
Well, getting SCP to work this way is actually more complicated than SFTP. With SFTP, if you use OpenSSH at the server, you just have to start the `/usr/lib/sftp-server` as described above, and if you can start it through su/sudo, it would just work. However, SCP is different because the command to start server-side SCP depends on what you intend to transfer. That's actually why it cannot be bound to ISocket.

In order to do what WinSCP does, we would have issue "sudo su USER" before executing SCP, which cannot be achieved with our current API. Fortunately, this should be quite simple to add. We'll look into this next week.
commented Mar 24 by toby (120 points)
The SFTP approach is preferable, but I can't seem to get it to work in combination with a sudo command.  The problem seems to be multiple RequestExec calls as if I follow the sudo command with a whoami, I also run into problems.  Maybe I need to reset something after each RequestExec call?

I would love to see Rebex support for the SCP approach.  Even if I had the SFTP approach working, the SCP is a great fallback.  Please do let me know if this is something you can add.

Thanks again,

Toby
commented Mar 27 by Lukas Pokorny (85,590 points)
The RequestExec method can only be called once per SshChannel. This is the limitation of the protocol itself. To behave exactly like WinSCP, you would have to call RequestShell instead of RequestExec and perform subsequent commands using Send/Receive methods only.
However, for SFTP, perhaps RequestExec("sudo su -c /usr/lib/sftp-server USER") would do the trick. Have you tried that?
commented Mar 27 by Lukas Pokorny (85,590 points)
I sent a link to a new build of Rebex SFTP to your email address. It should make it possible to launch SCP using "sudo su" - please give it a try and let me know whether it works.
0 votes
answered Mar 27 by Lukas Pokorny (85,590 points)

In the current development build of Rebex SFTP, there is a new setting that makes it possible to process and modify the scp command and arguments before it is sent to the server. The following code works fine with Debian Linux that has been configured not to require a pseudoterminal or ask for password when executing "sudo su":

var client = new Scp();
client.Settings.ProcessCommand = (command) =>
{
    // prepare escapes and quotes for additional quoting
    command = command.Replace("\\", "\\\\").Replace("\"", "\\\"");

    // construct the actual command to issue
    return "sudo su " + realUser + " -c \"" + command + "\"";
};

...

This uses a slightly different approach than WinSCP (unlike WinSCP, the Scp object doesn't establish a shell session). Due to this, we had to modify your original sudo su USER command accordingly by adding the -c ... part.

commented Mar 28 by toby (120 points)
I still have a few things to test, but both solutions above appear to be working.  Certainly, they are working for the problem described.

Any idea when this fix will be 'officially' released?

Many thanks for the quick turn around on this.  Ignoring the weekend, it was probably 36 hours between me reporting the problem and Rebex providing a fix - very impressive!

Thanks,

Toby
commented Mar 28 by Lukas Pokorny (85,590 points)
Thanks to you as well for testing this! Fortunately, this was a simple change and it will be included in the next release for sure - hopefully in about a month. However, the "beta" was based on the recent 2017 R2 release and the only other change is support for underline cursor in TerminalControl, which means it's almost certainly just as good as the current official release.
commented Aug 10 by Lukas Pokorny (85,590 points)
This has been officially released with Rebex SFTP 2017 R3: https://www.rebex.net/sftp.net/history.aspx#2017R3

Welcome to Q&A forum for C# and VB.NET developers working with following .NET components:

If you need immediate assistance, please contact us directly.

...