/* 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 #include #include #include #include #include #include #include #include #include #define MSG "From: %s\r\nTo: %s\r\n\r\nYour two-stage passcode is %d\r\n" /* Configuration happens here */ /* You */ #define FROM "" /* Your SMS gateway or an email address if you don't want to use a phone */ #define TO "" /* 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; }