/* 2stage v0

* ---

* Zachary Hamm

* zsh@imipolexg.org

*

* I often have to ssh into systems from untrusted public computers. The

* possibility of keyloggers makes me nervous (I'm the paranoid sort). I could

* use an ssh key on a usb drive or somesuch but that's often not an option,

* (plus, it's a nuisance to carry a precious usb drive around when I've got

* my passwords all stored up in my noggin). So I use password authentication.

* But those keyloggers torment me... keep me up at night...

*

* Googles two-stage auth gives me the confidence to read my email at a public

* computer. I made this so I could have the same confidence when ssh'ing

* into my servers.

*

* Installation (requires libcurl and libreadline):

* 1. Edit the #defines below for yourself.

* 2. Build with cc 2stage.c -o 2stage -lcurl -lreadline

* 3. Move to /bin or some such.

* 4. Add /bin/2stage to /etc/shells

* 5. Edit /etc/passwd and set your shell to /bin/2stage

* And that's it. Try ssh'ing in.

*

*

* TODO: * Make it configurable by each user via a dotfile in the home dir.

* * Option to remember trusted hosts for 30 days so you don't

* have to do this over and over from your main boxen.

* * Add logging of failed passcode attempts.

* * Does this break X11 forwarding? Must test.

* * Once all that is done, give it a proper autoconf and git host

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <errno.h>

#include <curl/curl.h>

#include <readline/readline.h>

#include <readline/history.h>

#define MSG "From: %s\r

To: %s\r

\r

Your two-stage passcode is %d\r

"

/* Configuration happens here */

/* You */

#define FROM "<zsh@imipolexg.org>"

/* Your SMS gateway or an email address if you don't want to use a phone */

#define TO "<XXXXXXXX@messaging.sprintpcs.com>"

/* Set your preferred shell here */

#define CONCH "/bin/bash"

int executeur ( int argc , char ** argv ) ;

unsigned int stupid_random ( void ) ;

size_t mailbody ( void * ptr , size_t size , size_t nitems , void * strm ) ;

int send_passcode ( void ) ;

static unsigned int passcode ;

int executeur ( int argcount , char ** argvee )

{

char ** args = ( char ** ) malloc ( sizeof ( char * ) * ( argcount + 1 ) ) ;

int i ;

args [ 0 ] = CONCH ;

for ( i = 1 ; i < argcount ; i ++ )

{

args [ i ] = argvee [ i ] ;

}

args [ argcount ] = NULL ;

return execv ( args [ 0 ] , args ) ;

}

unsigned int stupid_random ( void )

{

int urandom_fd ;

unsigned int randy = 0 ;

urandom_fd = open ( "/dev/urandom" , O_RDONLY ) ;

/* We want a number between 10^6 and 10^7 so that we get a 6 digit

number */

while ( ! ( randy > ( 1000000 - 1 ) && randy < ( 10000000 - 1 ) ) )

{

/* read 3 bytes intead of 4 to insure less hits over

the threshold (so we get our random number faster) */

read ( urandom_fd , & randy , sizeof ( unsigned int ) - 1 ) ;

}

close ( urandom_fd ) ;

return ( unsigned int ) randy ;

}

/* Callback for CURLOPT_READFUNCTION */

size_t mailbody ( void * ptr , size_t size , size_t nitems , void * strm )

{

static unsigned int dirty = 0 ;

char * s ;

size_t len = 0 ;

if ( ! dirty )

{

/* 6 is the digit length of the ints from stupid_random()

XXX: we assume malloc succeeds. this is sloppy. */

s = ( char * ) malloc ( strlen ( MSG ) + strlen ( FROM ) + strlen ( TO ) + 6 + 1 ) ;

passcode = stupid_random ( ) ;

sprintf ( s , MSG , FROM , TO , passcode ) ;

len = strlen ( s ) ;

memcpy ( ptr , s , len ) ;

dirty = 1 ;

free ( s ) ;

return len ;

}

return 0 ;

}

int send_passcode ( void )

{

CURL * curl ;

CURLcode res ;

struct curl_slist * targets = NULL ;

curl = curl_easy_init ( ) ;

if ( ! curl ) {

printf ( "Something bad happened to curl_easy_init()!" ) ;

return - 1 ;

}

/* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); */

curl_easy_setopt ( curl , CURLOPT_URL , "smtp://localhost" ) ;

targets = curl_slist_append ( targets , TO ) ;

curl_easy_setopt ( curl , CURLOPT_MAIL_RCPT , targets ) ;

curl_easy_setopt ( curl , CURLOPT_MAIL_FROM , FROM ) ;

curl_easy_setopt ( curl , CURLOPT_READFUNCTION , mailbody ) ;

res = curl_easy_perform ( curl ) ;

if ( res != 0 )

{

printf ( "Something bad happened!

" ) ;

return - 1 ;

}

curl_slist_free_all ( targets ) ;

curl_easy_cleanup ( curl ) ;

return 0 ;

}

int main ( int argc , char * argv [ ] )

{

char * input ;

printf ( "2stage! Sending the passcode to your phone.

" ) ;

if ( send_passcode ( ) != 0 )

{

printf ( "Oh no! Badness! Try again or contact the admin..." ) ;

return - 1 ;

}

input = readline ( "Passcode sent! Have patience, then enter it: " ) ;

if ( strtol ( input , NULL , 10 ) == passcode )

{

printf ( "%s == %d!

Good job!

" , input , passcode ) ;

free ( input ) ;

}

else

{

free ( input ) ;

input = readline ( "No! Bad job! You get one more try: " ) ;

if ( strtol ( input , NULL , 10 ) == passcode )

{

printf ( "Good job!

" ) ;

}

else

{

printf ( "Not this time, bub.

" ) ;

/* XXX: send textmsg about passcode failure. log it

as well */

return - 1 ;

}

free ( input ) ;

}

if ( executeur ( argc , argv ) == - 1 )

{

perror ( "execv" ) ;

return - 1 ;

}

return 0 ;