SCP launches the SSH sub process with "-oPermitLocalCommand=no" - how to change this?

I have in my SSH ~/.ssh/config the following configuration:

Host *
     # note: this needs in /etc/ssh/ssh_config:  PermitLocalCommand yes
     LocalCommand gpgconf --reload scdaemon

The reason for this is, the private RSA key for the SSH connection comes from an OpenPGP smartcard which is requesting a PIN to give access and the card should be resetted with the above command after providing the key. This works fine for SSH, but not for SCP. SCP launches the required SSH command with an option "-oPermitLocalCommand=no"which disables this feature. One can see this nicely with:

strace -f -o scp "-oPermitLocalCommand=yes" foo
grep execv
10205 execve("/usr/bin/scp", ["scp", "-oPermitLocalCommand=yes", "foo",
""...], 0xffffdf2147a0 /* 32 vars */) = 0
10206 execve("/usr/bin/ssh", ["/usr/bin/ssh", "-x", "-oPermitLocalCommand=no",
"-oClearAllForwardings=yes", "-oRemoteCommand=none", "-oRequestTTY=no", "-o",
"PermitLocalCommand=yes", "-oForwardAgent=no", "-l", "XXXXXXXXXXXXXXXX", "--",
"", "scp -t ."], 0xffffe38c6780 /* 32 vars */) = 0

and so the card remains active and provides keys without PIN entry.


I got some hint in SCP launches the SSH sub process with "-oPermitLocalCommand=no" - how to change this? - Unix & Linux Stack Exchange that it is hardcoded in the source that way
at in OpenSSH_8.9p1 Ubuntu-3ubuntu0.7

Seems as well in Debian:

purism@pureos:~$ strings --all /usr/bin/scp | grep -- "-oPermitLocalCommand=no"

What could be the reason for this?


Maybe if you look at the source, it will explain the reason.

PermitLocalCommand appears to be problematic anyway because it controls two otherwise unrelated pieces of functionality: LocalCommand and local commands that you type during an ssh session. The latter doesn’t really make sense with scp.

Regardless of the reason, I would be concerned about a race condition between multiple concurrent scp commands (or for that matter also ssh commands).

1 Like

LocalCommand runs on the host where the sshis started. Can you give an example for your local commands you type in the session, please.

I’m more concerned about that the OpenPGP card remains in state “no further PIN needed” and we’re talking here about a host only one person at the time will run ssh or scp , me. There will be no raise condition.

1 Like

man ssh

Go to the section on ESCAPE CHARACTERS and in particular ~C! but bear in mind that PermitLocalCommand is off by default (I think). To be honest, it’s not functionality that I have ever used.

1 Like

This is not an example (that could interfere with my local command)

1 Like

I didn’t mean that it could interfere with anything, only that it is poor design that one option controls two unrelated pieces of functionality. In other words, if you had a critical need to use LocalCommand (as you appear to do) - so you turned the relevant option on - then you would also be turning on ~C! which might not be what you want (although likely to be harmless).

I understood that you can manage that (avoid that) as it is a single-user computer - although I suppose things could get hairy if you have scp being used in a shell script.

As a theoretical alternative, maybe you can wrap the needed commands in shell scripts, then the shell script can do: “open”, actual ssh or scp command, “close” - and then you can also serialise that if you want to guarantee that there are no race conditions.

1 Like

As for scp I couldn’t manage this with LocalCommand configuration, I’m using a ~/.bashrc function:

function scp {
   $(which scp) $@
   # lock the OpenPGP card again
   echo gpgconf --reload scdaemon
   gpgconf --reload scdaemon

Works fine.


I was lazy and pulled the sources. The flag is added without any comment to the command line:

grep -n -B3 -A3 -- '-oPermitLocalCommand=no' openssh-8.4p1/scp.c
425-	args.list = remote_remote_args.list = NULL;
426-	addargs(&args, "%s", ssh_program);
427-	addargs(&args, "-x");
428:	addargs(&args, "-oPermitLocalCommand=no");
429-	addargs(&args, "-oClearAllForwardings=yes");
430-	addargs(&args, "-oRemoteCommand=none");
431-	addargs(&args, "-oRequestTTY=no");

I like such programming without comments :frowning:


I’m guessing it shows the challenges of such a powerful capability like SSH. scp is trying to activate SSH, so as to copy files, not actually connect to a remote secure shell, while minimising the amount of random SSH functionality that goes along for the ride. I suppose that the philosophy is sound i.e. don’t do things unless intentionally - although that happens to be inconvenient for you in this particular case.

SSH is a protocol that is a bit like … it has everything except the kitchen sink (unless you specify
--kitchen-sink … just kidding.)

Can you clarify … if you specify -o PermitLocalCommand=yes … so that that particular option is present twice in the command arguments, does it work? In other words, when it says both “no” and “yes”, which one has precedence?

1 Like

If I do:

purism@pureos:~$ strace -f -e trace=execve /usr/bin/scp -oPermitLocalCommand=yes foo $ua:.:.
execve("/usr/bin/scp", ["/usr/bin/scp", "-oPermitLocalCommand=yes", "foo", ""...], 0xffffddbba1a0 /* 32 vars */) = 0
strace: Process 40236 attached
[pid 40236] execve("/usr/bin/ssh", ["/usr/bin/ssh", "-x", "-oPermitLocalCommand=no", "-oClearAllForwardings=yes", "-oRemoteCommand=none", "-oRequestTTY=no", "-o", "PermitLocalCommand=yes", "-oForwardAgent=no", "-l", "XXXXXXXX", "--", "", "scp -t .:."], 0xffffd5d61e00 /* 32 vars */) = 0
foo                                                 100%    0     0.0KB/s   00:00
[pid 40236] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=40236, si_uid=1000, si_status=0, si_utime=9, si_stime=8} ---
+++ exited with 0 +++

the LocalCommand from ~/.ssh/config is not executed, the card remains active.

1 Like

I compiled openssh-8.4p1 from the source with this small change:

diff scp.c scp.c.orig
< 	/* DON'T set "-oPermitLocalCommand=no" to be able to
< 	 * use LocalComman from ~/.ssh/config
< 	 *, May 2024
< 	 *
< 	 * addargs(&args, "-oPermitLocalCommand=no");
< 	 *
< 	 */
> 	addargs(&args, "-oPermitLocalCommand=no");

sudo apt install libssl-dev.  # brings
./configure --prefix /usr

The resulting ./scp works fine, the OpenPGP card is locked afterwards from the LocalCommand, and I will copy this (and only this) to /usr/bin.


Yes, it looks to be the case that in the complicated dance to get a value for an option … the first specification of a value for a given option takes precedence. So by virtue of the fact that scp hardcodes PermitLocalCommand at the early part of the argument list that it builds, there is no way to override that, not from a later argument, and not from the per-user config file, and not from a system config file.

Rather than just comment out the explicit setting to no, maybe if you moved that line of code so that it is after all arguments that the user explicitly specifies, you could override it when you need to while still forcing it to no by default for all uses of scp. In your use case though maybe you don’t want that and you just specify it in the per-user config file.

In the very specific case of ClearAllForwardings this behaviour is actually documented:

man ssh_config

and that gives an insight into the rationale.

It could be argued that the documentation just fails to mention the other 4 options that scp forces irrevocably to specific values.

Just for fun … you wouldn’t want LocalCommand to invoke scp or ssh or sftp carelessly. :wink:

1 Like

Final (?) observation on this topic …

Just because you can copy a file to a remote computer (where obviously you have credentials for access) does not mean that you either have administrative control over the remote computer or that you trust the administrator of the remote computer.

As far as I know, SSH has no easy way of understanding the trust and security relationship between the local computer and the remote computer.

So, for example, if scp did not force -x then there is a risk that in the period of time that you copy a file to the remote computer, the remote computer could run an application on the remote computer but with the X display set to your computer which, I suppose, could be used to trick you in some way - and the man page even says that the remote computer could engage in keystroke monitoring.

I think the motivation for this behaviour of scp is sound but obviously the documentation is lacking and some of the details are open to debate (for example, when there is trust between the local and remote computers there is no way to override the behaviour).

In my case, 100% of the time when I am transferring files over SSH, I am the administrator of both local and remote, there is trust, and it would be safe to override. However I am not myself affected by the restrictions.

1 Like

To explain again the problem I am in: To use the OpenPGP card (for decryption or for the RSA secret key) you must provide a 6-digit PIN. Then the card is unlocked and free for doing the next SSH session with your RSA key or decrypting the next file, both without to have to enter the PIN. When you not do this reset of the card via LocalCommand, but with some scripting after the SSH-session end or the file transfer by SCP is done, you are in the risk that all the time (in the case of SSH maybe for hours) the card stays nlocked.

That’s why I prefer use LocalCommand (with any theoretical problems which this could have, but in my case on the L5 never will occur). I checked with strace and the command configured in LocalCommand is directly executed after the SSH-session is established.

1 Like