July 2023
SSHswitch - an iOS (jailbroken) setuid tool for configuring the OpenSSH server, and starting/stopping it.
SSHswitch
SSHswitch on [port(s) \
a[0|no|1|yes|-] \
w[0|no|1|yes|-] \
u[0|no|1|yes|-] \
g[0|no|1|yes|-] \
k[0|no|1|yes|-] \
l[0|no|1|yes|-]]
SSHswitch off [port(s) \
a[0|no|1|yes|-] \
w[0|no|1|yes|-] \
u[0|no|1|yes|-] \
g[0|no|1|yes|-] \
k[0|no|1|yes|-] \
l[0|no|1|yes|-]]
SSHswitch s | status
SSHswitch p | port [awutglkefrnbv]
SSHswitch a | allowRoot
SSHswitch w | withPassword
SSHswitch u | usingKey
SSHswitch t | twoFactor
SSHswitch g | globalListener
SSHswitch i | info | ij
SSHswitch l | letEmLinger
SSHswitch k | kickEm
SSHswitch e | evenIfLocked
SSHswitch f | forbidIfLocked
SSHswitch r | runOnBoot
SSHswitch n | notOnBoot
SSHswitch b | bootAsToggled
SSHswitch d | defaultState
SSHswitch v | version | -v
SSHswitch -h
SSHswitch h | help | --help
This tool is used internally by the GUI tweaks
- OpenSSH Settings (u.blanxd.OpenSshPort)
- OpenSSH CC Toggle (u.blanxd.OpenSshCC)
- OpenSSH Flipswitch (u.blanxd.OpenSshFlipswitch)
It sets OpenSSH options in /etc/ssh/sshd_config and
/Library/LaunchDaemons/com.openssh.sshd.plist, it uses launchctl to
start/stop the server and read its running status. It keeps the
requested settings (its preferences) in
/var/mobile/Library/Preferences/u.blanxd.sshswitch directory, and the
package also tries to keep a backup of the original OpenSSH config files
in there (before version 1.1.0 the settings were being kept in /etc/ssh
as separate files, and there was a backup of the launchd plist right
next to the original). Some of these settings are also directly read by
the GUI toggle tools. In addition to the OpenSSH server settings these
preferences toggle
- whether it kicks ssh sessions off the server (the iDevice) when it
turns the server off.
- whether using the toggles is allowed while the device is locked.
- how it uses launchctl for starting/stopping the server (ie. whether ON
or OFF are only temporary/runtime or permanent).
com.openssh.sshd.plist is being read and written to as a plain text XML file, so if anyone (any OpenSSH packager) ever makes it a binary or JSON plist then this tool might not be able to report the correct port numbers, nor the local/global listening status (but it wouldn't write to the file then either).
The SSH server itself runs as root, so it wouldn't be possible for the mobile user (the iOS GUI and apps) to start/stop this, that's why this tool uses setuid 0, and is installed as such, so it can be used by any user on the system.
When it successfully turns the server on or off, it emits a system-wide Darwin notification, which may be observed by any process. The name of the notification is “com.openssh.sshd/on” or “com.openssh.sshd/off”. If it’s an internal sshd restart after changing some settings then there will be two of those, there are no exceptions.
If the OpenSSH server package is upgraded or reinstalled (while this tweak is present), it tries to re-backup the new config files and apply the custom settings again. For this it relies on the packaging system (dpkg) to tell when these files are being changed, to the maintenance script of this tweak, so it can rerun the tool using the saved settings.
All subcommands, although representing English words/expressions, are really only read from the beginning until a unique instruction can be determined, which mostly means the 1st letter. Two characters are needed for on, off, ij, -v, -h, --help.
All paths mentioned in this document might be preceded by “/var/jb” or some dynamic JB root path if it’s being run on a rootless jailbreak (the tweak preferences are always kept in /var/mobile/Library, but the backup files’ location depends on the rootlessness of the JB).
SSHswitch
calling it without any args/subcommands simply exits with the return
code 0 if OpenSSH is running, 1 if it isn’t.
status | s
prints whether SSH is on or
off
port | p
[awutglkefrnbv]
prints the port(s) OpenSSH is currently configured to listen on, each
one on a separate line.
It can be followed by any or all of the 5 letters described next
and/or any or all of the tweak settings’ or launchd settings’ letters
(in any order), in which case it also prints `yes’ or `no’ preceded by
the corresponging letter, each on a separate line, and in this case the
exit code reflects the running status of the server. They may be
separated by spaces as separate args but may as well be written as one
argument. This is the API output that the OpenSSH Settings tweak uses.
Neither the ports nor the yes's/no's are guaranteed to be in any
particular order. The corresponding tweak toggle preferences cancel each
other, so it might not be necessary to query eg. both l and
k.
allowRoot | a
prints whether root is allowed to log in (yes or
no)
withPassword | w
prints whether it’s allowed to login with a password
(yes or no)
usingKey | u
prints whether it’s allowed to login with a key (yes
or no)
twoFactor | t
prints whether both the password and a key are required
(yes or no)
globalListener | g
prints whether it’s listening for connections from outside the
localhost (yes or no)
info | i | ij
list active sessions, returns 1 if there aren’t any. Adding `j’ makes
it output the info in JSON format, without a newline.
defaultState | d
prints the startup state: bootAsToggled | runOnBoot |
notOnBoot.
version | v |
-v
prints the version of this tool.
-h
prints a short list of all the possible subcommands.
help | h |
--help
prints a more comprehensive description (still less than in this
manpage).
All configuration concerning OpenSSH itself happens with the on or off subcommands. The optional arguments that follow the on/off determine if anything gets written to /etc/ssh/sshd_config and/or /Library/LaunchDaemons/com.openssh.sshd.plist, and whether it restarts/restops the server service. Their order doesn’t matter and they may be repeated, the last one wins. Each of them, except for the ports, should start with a letter, possibly followed by 1, 0, yes, no, or - (a minus, which skips setting that option). The suffix 1/0/yes/no/- determines which way it gets toggled, any “-” means skip and any “0…” or “n…” means no, anything else is a yes (including omitting the suffix). Before v.1.1.0 these didn’t have the letter prefixes and they were positional (in the order they’re described below), this still works for backward compatibility.
on | off
determines if the server stays on/off after the command has finished,
regardless if any additional options follow.
Optional additional arguments after on/off:
[p]port[,port[,…]] | p-
numeric port number(s) that the server needs to listen on, it may be
prefixed with “p”, but this isn’t required. This tool only saves port 22
(the standard) and/or most numbers from 1001 through 65535 (a couple of
high ports are known to be used by iOS itself which we avoid), there is
no error indication if others are requested, but they don’t get saved.
This is just an arbitrary precaution I took so noone would make it
listen on some port used by other known services. Up to 8 ports may be
specified, separated by commas (no spaces). The port subcommand
reads the result.
a |
a1 | a0 | ayes | ano | a-
determines if root is allowed to log in (PermitRootLogin keyword in
sshd_config). The allowRoot subcommand reads the result.
w |
w1 | w0 | wyes | wno | w-
determines if it's allowed to log in using a password
(PasswordAuthentication and/or KbdInteractiveAuthentication keywords in
sshd_config). The withPassword subcommand reads the
result.
u |
u1 | u0 | uyes | uno | u-
determines if it's allowed to log in using a public key
(PubkeyAuthentication keyword in sshd_config). The usingKey
subcommand reads the result.
g |
g1 | g0 | gyes | gno | g-
determines if connections are being listened for on all addresses
(1/yes/omit) or localhost only (ListenAddress keywords
in sshd_config and SockNodeName key in the launchd plist). The
globalListener subcommand reads the result. This didn’t exist
before v.1.1.0.
k |
k1 | k0 | kyes | kno | k-
l |
l1 | l0 | lyes | lno | l-
this is not an OpenSSH setting. It makes us kick (or not) all possible
(incoming) ssh/sftp/scp sessions off the server, even if the subcommand
is on. They are opposites, k means Kick’Em nad
l means Let’EmLinger. This option also overrides the
letEmLinger/kickEm tweak preference setting (see
below). In v.1.0.x this was the 5th possible toggle after
on/off, only accepting 1/0 (and the 4th in some
earlier versions of SSHonCC).
If both the w (withPassword) and u (usingKey) options
are given 0|no then this doesn't make much sense, either Password or
PubKey authentication should be allowed, else it's a lockout (unless
using some more advanced authentication schemes, which are possible, but
I'm not sure if anyone uses those in iOS). So this tool instead reverts
these to `yes’, and additionally makes the server require both,
twoFactor (using the AuthenticationMethods keyword in
sshd_config). Ie. a user must have their pubkey in the authorized_keys
file, and in addition they must provide the password when logging in.
Ie. Extra Secure :)
Changing these settings may make the tool restart the server if it's issued while it’s running. Definitely if the port(s) or the global/local listener get altered, but also for other settings if sshd isn’t using launchd to listen and runs as a daemon on its own (a few jailbreaks have it like that).
If the OpenSSH server package is reinstalled or upgraded via a package manager (ie. dpkg), the config files /etc/ssh/sshd_config and /Library/LaunchDaemons/com.openssh.sshd.plist historically get overwritten, so the postinst script of this package tries to reapply the settings which this tool has changed.
kickEm | k
makes us kick all possible (incoming) ssh/sftp/scp sessions off the
server whenever an off subcommand is issued. This is the
default when this tweak is installed. Use letEmLinger to revert
this. It can be overridden with the lyes or kno option
to the off subcommand.
letEmLinger | l
the opposite of the above kickEm subcommand, ie. the
off subcommand would simply turn off the server and never mind
if anyone is logged in. It can be overridden with the kyes or
lno option to the off subcommand.
forbidIfLocked | f
this is used by the toggle tweaks. Makes them have no effect when the
device is locked. This is the default for the u.blanxd.OpenSsh… series
of toggles. Use evenIfLocked to revert this.
evenIfLocked | e
this is used by the toggle tweaks. The opposite of the above
forbidIfLocked, makes them work even when the device is
locked.
runOnBoot | r
This is the default when OpenSSH gets installed ie. whenever the
device gets re-jailbroken (or just rebooted in case the jb is
untethered) then the ssh server starts up as well. Internally, the tool
starts the server with `launchctl load -w’ and stops it with `launchctl
unload -F’.
notOnBoot | n
This makes the ssh server not start up when the device is
re-jailbroken or rebooted. Internally, the tool starts the server with
`launchctl load -F’ and stops it with `launchctl unload -w’.
bootAsToggled | b
This makes the ssh server either start or not, based on whichever
state it was in before the restart/re-jailbreaking. Internally, the tool
always starts and stops the server with `launchctl (un)load -w’.
Changing this setting may make the tool restart the server, depending on which way it got turned to while the server is running. Similarly it might get turned on for a millisecond if changing this while the server is off.
This setup might get overridden when the OpenSSH server gets re-installed or upgraded, because often the postinst script in the OpenSSH server package simply uses `launchctl load -w’ to load it up after any installation (but not all OpenSSH packages do that in case of upgrades). The postinst script of this package tries to automatically reapply the setting (if it has been altered).
Any exit status >=10 is an error in any case. One should really always check this, it only prints info to STDERR in case of unrecognized arguments (15) and launchctl errors (11-14,17, these also go to Apple System Log for reading with other tools).
Regular subcommands should return 0.
Without any subcommands, or in case there are extra letters after port, it returns 0 if the server is running, and 1 if it isn’t.
info and ij return 1 if there aren’t any sessions.
This program itself defines the following error codes:
10: setuid error
11-14: posix_spawn and its results errors while executing
launchctl
15: unknown command line argument(s) given
16: failed reading /Library/LaunchDaemons/com.openssh.sshd.plist
17: cannot find launchctl binary
18: failed reading /etc/ssh/sshd_config
19-21: failed writing /etc/ssh/sshd_config
22-29: failed reading tweak prefs
30-40: failed writing tweak prefs
41-44: failed writing /Library/LaunchDaemons/com.openssh.sshd.plist
Most info request commands should be easy, they're just saying
yes or no, and on or off.
Showing some API type subcommands here.
iPhone:~ mobile$ # show the port(s)
iPhone:~ mobile$ SSHswitch port
10222
22
iPhone:~ mobile$ # turn off, save port 22 and allow password auth
iPhone:~ mobile$ SSHswitch off 22 wyes
iPhone:~ mobile$ # report ports and if password auth is allowed
iPhone:~ mobile$ SSHswitch p w
22
wyes
iPhone:~ mobile$ # report all the OpenSSH and tweak settings this tool supports
iPhone:~ mobile$ SSHswitch port awutg ef kl rnb v
eno
fyes
kno
lyes
ryes
nno
bno
v1.1.0
22
gyes
ano
wyes
uyes
tno
iPhone:~ mobile$ # exit status shows if it's running
iPhone:~ mobile$ SSHswitch
iPhone:~ mobile$ echo $? # 1 means it's off, 0=running
1
iPhone:~ mobile$ # turning twoFactor on among other things
iPhone:~ mobile$ SSHswitch on 10222 a1 w0 u0 g1
iPhone:~ mobile$ SSHswitch p awutg
10222
gyes
ayes
wyes
uyes
tyes
iPhone:~ mobile$ # the p(ort) command with extra args returned 0 if it's running
iPhone:~ mobile$ echo $?
0
iPhone:~ mobile$ # kick everyone out without changing anything
iPhone:~ mobile$ SSHswitch on k
Connection to 10.8.9.14 closed.
Copyright (c) 2023-07 Blanxd.H blanxd.h@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.