#include #include #include #include #include #include #include #include #include #define BUFFER_LENGTH 50 #define OTPCHALL_LENGTH 10 #define OTPRESP_LENGTH 30 #define OTPCOMM_LENGTH 70 #define NULL_CHAR_SIZE 1 /* number of file descriptors to poll */ #define POLL_FDS 2 #define RESTORE_TERMIOS 1 #define DONT_RESTORE_TERMIOS 0 struct termios termios_stdin_initial, termios_stdin_new; void restore_termios_stdin(void) { if (tcsetattr(STDIN_FILENO, TCSANOW, &termios_stdin_initial) != 0) perror("Error: restore termios - type 'stty echo'\n"); } void exit_failure(char *mess, char restore) { perror(mess); if (restore) restore_termios_stdin(); exit(EXIT_FAILURE); } void printhelp(void) { printf("antikos 0.1a, 17 July 2002\n" "This program comes with ABSOLUTELY NO WARRANTY.\n" "This is free software, and you are welcome to redistribute " "it\nunder the GNU General Public Licence, version 2.\n" "See the http://www.gnu.org for licence details.\n\n" "Antikos attempts to login to the remote host via telnet " "periodically.\n" "It uses otp-md5 authentication via the Java OTP Calculator.\n\n" "Project homepage: http://verunek.host.sk\n" "Author: Jiri VERUNEK " "\n\n"); printf("Usage: antikos [-j path] [-m e-mail] [-l loginname] " "-p password host\n" "Options:\n" " -j path Path to the Java OTP Calculator. You can " "download that\n" " from http://www.cs.umd.edu/users/harry/jotp/\n" " This option overrides CLASSPATH environment " "variable.\n" " -l loginname Login using this username.\n" " -p password Login using this password.\n" " -h Print this help message.\n" " host Login to this host by telnet.\n"); } int main(int argc, char *argv[]) { int opt, pid_stat; char *jpath, *loginname, *pass, *host; char buffer[BUFFER_LENGTH]; char *ptrchar; pid_t pid; char otpchall[OTPCHALL_LENGTH + 1]; /* OTP challenge buffer */ char *otpchall_ptr; char chars_to_otpchall; char otpresp[OTPRESP_LENGTH]; /* buffer for jOTP output */ FILE *otpresp_fd; char otpcomm[OTPCOMM_LENGTH]; int child_to_parent[2], parent_to_child[2]; /* pipes */ struct pollfd mypoll[POLL_FDS]; char poll_break, find_challs; ssize_t bytesread, otpbytesread; char *challenges[] = {"login:", "OTP Challenge: otp-md5", \ "Response:", "Stisknete klavesu ", NULL}; int chall_index; char *chall_ptr, *buffer_ptr; jpath = loginname = pass = host = (char *)0; while ((opt = getopt(argc, argv, "j:l:p:h")) != -1) { switch (opt) { case 'j': if ((jpath = malloc(strlen(optarg) + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strcpy(jpath, optarg); break; case 'l': if ((loginname = malloc(strlen(optarg) + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strcpy(loginname, optarg); break; case 'p': if ((pass = malloc(strlen(optarg) + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strcpy(pass, optarg); break; case ':': fprintf(stderr, "Option %d needs argument\n", opt); case '?': case 'h': printhelp(); exit(EXIT_FAILURE); } } if ((jpath == NULL) && (getenv("CLASSPATH") == NULL)) { fprintf(stderr, "Please specify path to the Java OTP Calculator " "by -j option.\n"); exit(EXIT_FAILURE); } if ((argc - optind) > 1) { fprintf(stderr, "Too many arguments.\n"); printhelp(); exit(EXIT_FAILURE); } if (optind < argc) { if ((host = malloc(strlen(argv[optind]) + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strcpy(host, argv[optind]); } else { /* scanf() has/had a lot of bugs */ printf("Enter hostname: "); fgets(buffer, BUFFER_LENGTH, stdin); ptrchar = strchr(buffer, '\n'); if ((ptrchar - buffer) > BUFFER_LENGTH) { fprintf(stderr, "Error: Hostname is too long. Please specify that as " "command line parameter.\n"); printhelp(); exit(EXIT_FAILURE); } if ((host = malloc(ptrchar - buffer + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strncpy(host, buffer, ptrchar - buffer); *(ptrchar - buffer + host) = '\0'; } if (loginname == NULL) { if ((loginname = getenv("LOGNAME")) == NULL) { /* scanf() has/had a lot of bugs */ printf("Enter loginname: "); fgets(buffer, BUFFER_LENGTH, stdin); ptrchar = strchr(buffer, '\n'); if ((ptrchar - buffer) > BUFFER_LENGTH) { fprintf(stderr, "Error: Loginname is too long. Please use the -l " "option.\n"); printhelp(); exit(EXIT_FAILURE); } if ((loginname = malloc(ptrchar - buffer + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strncpy(loginname, buffer, ptrchar - buffer); *(ptrchar - buffer + loginname) = '\0'; } } if (tcgetattr(STDIN_FILENO, &termios_stdin_initial) != 0) perror("Error: tcgetattr(stdin)\n"); if (pass == NULL) { termios_stdin_new = termios_stdin_initial; termios_stdin_new.c_lflag &= ~ECHO; termios_stdin_new.c_cc[VMIN] = 1; termios_stdin_new.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW,&termios_stdin_new) != 0) perror("Error: tcsetattr(stdin)\n"); /* scanf() has/had a lot of bugs */ printf("Enter password: "); fgets(buffer, BUFFER_LENGTH, stdin); ptrchar = strchr(buffer, '\n'); if ((ptrchar - buffer) > BUFFER_LENGTH) { fprintf(stderr, "Password is too long. Please use the -p option.\n"); printhelp(); restore_termios_stdin(); exit(EXIT_FAILURE); } if ((pass = malloc(ptrchar - buffer + 1)) == NULL) exit_failure("Error: allocating memory", DONT_RESTORE_TERMIOS); strncpy(pass, buffer, ptrchar - buffer); *(ptrchar - buffer + pass) = '\0'; restore_termios_stdin(); } while(1) { pipe(child_to_parent); pipe(parent_to_child); switch (pid = fork()) { case (pid_t)-1: exit_failure("Error: forking process", DONT_RESTORE_TERMIOS); case (pid_t)0: /* redirect stdin to the output of the parent_to_child pipe */ if (dup2(parent_to_child[0], STDIN_FILENO) == -1) exit_failure("Error: dup2()", DONT_RESTORE_TERMIOS); close(parent_to_child[0]); /* redirect stdout to the input of the child_to_parent pipe */ dup2(child_to_parent[1], STDOUT_FILENO); close(child_to_parent[1]); /* close the remaining child pipes descriptors */ close(parent_to_child[1]); close(child_to_parent[0]); execlp("telnet", "telnet", host, (char *)0); break; default: close(parent_to_child[0]); close(child_to_parent[1]); /* suppress printing chars of pressed keys, turn off canonical mode */ termios_stdin_new = termios_stdin_initial; termios_stdin_new.c_lflag &= ~ICANON; termios_stdin_new.c_lflag &= ~ECHO; /* read(stdin) will not freeze when having nothing in stdin */ termios_stdin_new.c_cc[VMIN] = 0; termios_stdin_new.c_cc[VTIME] = 0; if (tcsetattr(STDIN_FILENO, TCSANOW, &termios_stdin_new) != 0) exit_failure("Error: tcsetattr(stdin)\n", DONT_RESTORE_TERMIOS); mypoll[0].fd = STDIN_FILENO; mypoll[0].events = POLLIN; mypoll[1].fd = child_to_parent[0]; mypoll[1].events = POLLIN; chall_ptr = challenges[0]; chall_index = 0; otpchall_ptr = otpchall; chars_to_otpchall = 0; find_challs = 1; poll_break = 0; do { /* wait for data to read from stdin or child_to_parent[0] */ if (poll(mypoll, POLL_FDS, -1) > 0) { if (mypoll[1].revents > 0) { if (mypoll[1].revents != POLLIN) { poll_break = 1; break; } else { bytesread = read(child_to_parent[0], &buffer, BUFFER_LENGTH); write(STDOUT_FILENO, &buffer, bytesread); if (find_challs) { buffer_ptr = buffer; for (; (((buffer_ptr - buffer) < bytesread) && (find_challs)) ; ) { if (chars_to_otpchall) { *otpchall_ptr++ = *buffer_ptr; chars_to_otpchall--; } if (*chall_ptr == '\0') { if (chall_index == 0) { /* Child has written challenge, but still not reading */ /* Send me e-mail if you know the better sollution. */ usleep(200000); write(parent_to_child[1], loginname, strlen(loginname)); write(parent_to_child[1], "\n", strlen("\n")); } else if (chall_index == 1) { chars_to_otpchall = OTPCHALL_LENGTH; otpchall[OTPCHALL_LENGTH] = '\0'; buffer_ptr++; } else if (chall_index == 2) { strcpy(otpcomm, "java"); if (jpath != NULL) { strcat(otpcomm, " -cp "); strcat(otpcomm, jpath); } strcat(otpcomm, " jotp "); strcat(otpcomm, otpchall); strcat(otpcomm, " "); strcat(otpcomm, pass); strcat(otpcomm, " md5 |tail -1"); if ((otpresp_fd = popen(otpcomm, "r")) == NULL) exit_failure("Error: popen()", RESTORE_TERMIOS); otpbytesread = fread(otpresp, sizeof(char), OTPRESP_LENGTH, otpresp_fd); pclose(otpresp_fd); /* usleep(200000); Java is slow enough. ;-) */ write(parent_to_child[1], otpresp, otpbytesread); } else if (chall_index == 3) { /* Child has written challenge, but still not reading */ /* Send me e-mail if you know the better sollution. */ usleep(200000); write(parent_to_child[1], "\n", strlen("\n")); } chall_index++; chall_ptr = challenges[chall_index]; if (chall_ptr == NULL) { find_challs = 0; break; } } else if (*chall_ptr == *buffer_ptr) { chall_ptr++; buffer_ptr++; } else { chall_ptr = challenges[chall_index]; buffer_ptr++; } } } } } else if (mypoll[0].revents > 0) { if (mypoll[0].revents != POLLIN) { exit_failure("Error: polling stdin", RESTORE_TERMIOS); } bytesread = read(STDIN_FILENO, &buffer, BUFFER_LENGTH); write(parent_to_child[1], &buffer, bytesread); } } else { exit_failure("Error: poll()", RESTORE_TERMIOS); } } while (! poll_break); wait(&pid_stat); } close(parent_to_child[1]); close(child_to_parent[0]); } /* These lines are not executed in this version. */ restore_termios_stdin(); return(EXIT_SUCCESS); }