/* 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\nTo: %s\r\n\r\nYour two-stage passcode is %d\r\n"
/* 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!\n");
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.\n");
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!\nGood job!\n", 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!\n");
}
else
{
printf("Not this time, bub.\n");
/* 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;
}