From d7e1c92052db23adc61a6e57e7ff8e3e8c206a18 Mon Sep 17 00:00:00 2001 From: Fons Rademakers Date: Thu, 11 Sep 2025 17:19:42 +0200 Subject: [PATCH 1/5] Add some more files and directories to be ignored. --- .gitignore | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.gitignore b/.gitignore index 838c9da..7ba8947 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,15 @@ config.status *.o sshpass stamp-h1 +INSTALL +Makefile.in +aclocal.m4 +autom4te.cache/ +compile +config.guess +config.h.in +config.sub +configure +depcomp +install-sh +missing From 1b85978edd43184c140a4cceb17015a4ab163634 Mon Sep 17 00:00:00 2001 From: Fons Rademakers Date: Thu, 11 Sep 2025 17:21:21 +0200 Subject: [PATCH 2/5] Add support for a new option "-x command" to get the password. The command must be a script that returns the password. This is useful if the password can be safely obtained via a password manager like 1Password. --- main.c | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index 158ec89..b7c79cb 100644 --- a/main.c +++ b/main.c @@ -52,6 +52,7 @@ enum program_return_codes { RETURN_HOST_KEY_CHANGED, RETURN_INCORRECT_OTP, RETURN_OTP_COMMAND_ERROR, + RETURN_PWT_COMMAND_ERROR }; // Some systems don't define posix_openpt @@ -63,6 +64,7 @@ posix_openpt(int flags) } #endif +void run_pw_command(); void run_otp_command(); int runprogram( int argc, char *argv[] ); void reliable_write( int fd, const void *data, size_t size ); @@ -75,11 +77,12 @@ void write_pass( int fd ); void write_otp( int fd ); struct { - enum { PWT_STDIN, PWT_FILE, PWT_FD, PWT_PASS } pwtype; + enum { PWT_STDIN, PWT_FILE, PWT_FD, PWT_COMMAND, PWT_PASS } pwtype; union { const char *filename; int fd; const char *password; + const char *pwcommand; } pwsrc; const char *pwprompt; @@ -97,6 +100,7 @@ static void show_help() " -f filename Take password to use from file\n" " -d number Use number as file descriptor for getting password\n" " -p password Provide password as argument (security unwise)\n" + " -x command executable file name printing password\n" " -e Password is passed as env-var \"SSHPASS\"\n" " With no parameters - password will be taken from stdin\n\n" " -P prompt Which string should sshpass search for to detect a password prompt\n" @@ -106,7 +110,7 @@ static void show_help() " -o OTP One time password\n" " -c command executable file name printing one time password\n" " -O OTP prompt Which string should sshpass search for the one time password prompt\n" - "At most one of -f, -d, -p or -e should be used\n"); + "At most one of -f, -d, -p, -x or -e should be used\n"); } // Parse the command line. Fill in the "args" global struct with the results. Return argv offset @@ -132,7 +136,7 @@ static int parse_options( int argc, char *argv[] ) optarg[i]='z'; \ } while(0) - while( (opt=getopt(argc, argv, "+f:d:p:P:o:c:O:heVv"))!=-1 && error==-1 ) { + while( (opt=getopt(argc, argv, "+f:d:p:x:P:o:c:O:heVv"))!=-1 && error==-1 ) { switch( opt ) { case 'f': // Password should come from a file @@ -163,6 +167,13 @@ static int parse_options( int argc, char *argv[] ) for( i=0; optarg[i]!='\0'; ++i ) optarg[i]='z'; } + break; + case 'x': + // Password is provided by command + VIRGIN_PWTYPE; + args.pwtype=PWT_COMMAND; + args.pwsrc.pwcommand=strdup(optarg); + HIDE_OPTARG; break; case 'P': args.pwprompt=optarg; @@ -249,6 +260,10 @@ int main( int argc, char *argv[] ) } } + if( args.pwtype == PWT_COMMAND ) { + run_pw_command(); + } + if( args.otptype == OTP_COMMAND ) { run_otp_command(); } @@ -580,6 +595,7 @@ void write_pass( int fd ) } break; case PWT_PASS: + case PWT_COMMAND: reliable_write( fd, args.pwsrc.password, strlen( args.pwsrc.password ) ); reliable_write( fd, "\n", 1 ); break; @@ -682,3 +698,29 @@ void run_otp_command() pclose( fp ); } + +void run_pw_command() +{ + if( args.verbose ) { + fprintf(stderr, "SSHPASS popen(%s)\n", args.pwsrc.pwcommand); + } + + FILE *fp = popen( args.pwsrc.pwcommand, "r" ); + if( fp == NULL ) { + if( args.verbose ) { + perror( args.pwsrc.pwcommand ); + } + exit(RETURN_PWT_COMMAND_ERROR); + } + + char buf[256]; + if( fgets( buf, sizeof(buf), fp ) != NULL ) { + char *p = strchr(buf, '\n'); + if( p != NULL ) { + *p='\0'; + } + args.pwsrc.password = strdup(buf); + } + + pclose( fp ); +} From cb19a4239eca4979ab67bcf35b995b3b3e18959f Mon Sep 17 00:00:00 2001 From: Fons Rademakers Date: Thu, 11 Sep 2025 17:23:46 +0200 Subject: [PATCH 3/5] Document the new password (-x) option and OTP (-o, -c, -O) options. --- sshpass.1 | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/sshpass.1 b/sshpass.1 index 854c6af..106b178 100644 --- a/sshpass.1 +++ b/sshpass.1 @@ -4,7 +4,7 @@ sshpass \- noninteractive ssh password provider .SH SYNOPSIS .B sshpass -.RB [ -f\fIfilename | -d\fInum | -p\fIpassword | -e ] +.RB [ -f \fIfilename | -d \fInum | -p \fIpassword | -x \fIcommand | -e ] [ -o \fIotp | -c \fIcommand ] .RI [ options ] " command arguments" .br .SH DESCRIPTION @@ -24,27 +24,44 @@ prompt used by ssh is, however, currently hardcoded into sshpass. If no option is given, sshpass reads the password from the standard input. The user may give at most one alternative source for the password: .TP -.B \-p\fIpassword\fP +.B \-p \fIpassword\fP The password is given on the command line. Please note the section titled "\fBSECURITY CONSIDERATIONS\fP". .TP -.B \-f\fIfilename\fP +.B \-f \fIfilename\fP The password is the first line of the file \fIfilename\fP. .TP -.B \-d\fInumber\fP +.B \-x \fIcommand\fP +The password is printed by executing the \fIcommand\fP. This is useful +when the password can be safely retrieved from a password manager (like 1Password). +.TP +.B \-d \fInumber\fP \fInumber\fP is a file descriptor inherited by sshpass from the runner. The password is read from the open file descriptor. .TP .B \-e The password is taken from the environment variable "SSHPASS". .TP -.B \-P +.B \-P \fIprompt\fP Set the password prompt. Sshpass searched for this prompt in the program's output to the TTY as an indication when to send the password. By default sshpass looks for the string "assword:" (which matches both "Password:" and "password:"). If your client's prompt does not fall under either of these, you can override the default with this option. .TP +.B \-o \fIotp\fP +\fIotp\fP is the One Time Password coming from a OTP code generator. +.TP +.B \-c \fIcommand\fP +The OTP (One Time Password) is printed by executing the \fIcommand\fP. This is useful +when the OTP can be safely retrieved from a password manager (like 1Password). +.TP +.B \-O \fIprompt\fP +Set the OTP prompt. Sshpass searched for this prompt in the program's +output to the TTY as an indication when to send the OTP code. By default +sshpass looks for the string "Verification code:". If your client's prompt does +not fall under either of these, you can override the default with this option. +.TP .B \-v Be verbose. sshpass will output to stderr information that should help debug cases where the connection hangs, seemingly for no good reason. From d8a4471a266c42b217b6664361c89d5086c5e21a Mon Sep 17 00:00:00 2001 From: Fons Rademakers Date: Thu, 11 Sep 2025 17:38:40 +0200 Subject: [PATCH 4/5] Document new option -x. --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5b2f090..57055d6 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,24 @@ For ssh servers with 2FA, with a normal password and time-based one time passwor ## Usage -Create a file containing your ssh password. ex: `~/.ssh/pw` +Create a file containing your ssh password, ex: `~/.ssh/pw` or better, create a command that +prints the password after having it safely obtained from a password manager, like 1Password. -Create an shell executable file printing your One time password. ex: `~/.ssh/totp` +Create a command that prints your One Time Password, ex: `~/.ssh/totp` ```shell=1 #!/bin/sh oathtool --totp -b YOUR-SECRET-KEY -``` +``` + +or using 1Password CLI: + +```shell=1 +#!/bin/sh + +op read op://Private/my-account/one-time\ password?attribute=otp` +``` Remember setting executable bit: @@ -51,12 +60,13 @@ ssh beyond Added parameters: ``` +-x command executable file name printing the password -o OTP One time password -c command executable file name printing one time password -O OTP prompt Which string should sshpass search for the one time password prompt ``` --O option's default is `Verification code:`. +`-O` option's default is `Verification code:`. ## Build From e813c1b83a6b368d276a5326d1078f5ac2540511 Mon Sep 17 00:00:00 2001 From: Fons Rademakers Date: Thu, 11 Sep 2025 17:46:44 +0200 Subject: [PATCH 5/5] Some more examples. --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 57055d6..023d0ca 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,18 @@ For ssh servers with 2FA, with a normal password and time-based one time passwor ## Usage -Create a file containing your ssh password, ex: `~/.ssh/pw` or better, create a command that +Create a file containing your ssh password, ex: `~/.ssh/pw` or better, create a command, ex: `~/.ssh/pwd`, that prints the password after having it safely obtained from a password manager, like 1Password. -Create a command that prints your One Time Password, ex: `~/.ssh/totp` +Create a command that prints your password using the 1Password CLI, ex: `~/.ssh/pwd`: + +```shell=1 +#!/bin/sh + +op read op://Private/my-account/password +``` + +Create a command that prints your One Time Password, ex: `~/.ssh/totp`: ```shell=1 #!/bin/sh @@ -21,19 +29,20 @@ or using 1Password CLI: ```shell=1 #!/bin/sh -op read op://Private/my-account/one-time\ password?attribute=otp` +op read op://Private/my-account/one-time\ password?attribute=otp ``` Remember setting executable bit: ``` +chmod +x ~/.ssh/pwd chmod +x ~/.ssh/totp ``` -Run sshpass with -f and -c parameters: +Run sshpass with -x and -c parameters: ``` -sshpass -f ~/.ssh/pw -c ~/.ssh/totp ssh your-ssh-server +sshpass -x ~/.ssh/pwd -c ~/.ssh/totp ssh your-ssh-server ``` You can use -v parameter if something wrong. @@ -45,7 +54,7 @@ You can use sshpass and ssh as a proxy command for connecting beyond severs. Edi ``` Host beyond - ProxyCommand sshpass -f ~/.ssh/pw -c ~/.ssh/totp ssh bastion -qW %h:%p + ProxyCommand sshpass -x ~/.ssh/pwd -c ~/.ssh/totp ssh bastion -qW %h:%p ``` Then run ssh.