Blame src/getpass.c

Packit 6c0a39
/*
Packit 6c0a39
 * getpass.c - platform independent getpass function.
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2011-2013    by Andreas Schneider <mail@cryptomilk.org>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
#include <string.h>
Packit 6c0a39
#include <stdlib.h>
Packit 6c0a39
Packit 6c0a39
#include <libssh/priv.h>
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Get the password from the console.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  prompt   The prompt to display.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  buf      The buffer to fill.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  len      The length of the buffer.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  verify   Should the password be verified?
Packit 6c0a39
 *
Packit 6c0a39
 * @return              1 on success, 0 on error.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) {
Packit 6c0a39
    char *tmp;
Packit 6c0a39
    char *ptr = NULL;
Packit 6c0a39
    int ok = 0;
Packit 6c0a39
Packit 6c0a39
    tmp = calloc(1, len);
Packit 6c0a39
    if (tmp == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* read the password */
Packit 6c0a39
    while (!ok) {
Packit 6c0a39
        if (buf[0] != '\0') {
Packit 6c0a39
            fprintf(stdout, "%s[%s] ", prompt, buf);
Packit 6c0a39
        } else {
Packit 6c0a39
            fprintf(stdout, "%s", prompt);
Packit 6c0a39
        }
Packit 6c0a39
        fflush(stdout);
Packit 6c0a39
        if (fgets(tmp, len, stdin) == NULL) {
Packit 6c0a39
            free(tmp);
Packit 6c0a39
            return 0;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if ((ptr = strchr(tmp, '\n'))) {
Packit 6c0a39
            *ptr = '\0';
Packit 6c0a39
        }
Packit 6c0a39
        fprintf(stdout, "\n");
Packit 6c0a39
Packit 6c0a39
        if (*tmp) {
Packit 6c0a39
            strncpy(buf, tmp, len);
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if (verify) {
Packit 6c0a39
            char *key_string;
Packit 6c0a39
Packit 6c0a39
            key_string = calloc(1, len);
Packit 6c0a39
            if (key_string == NULL) {
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
Packit 6c0a39
            fprintf(stdout, "\nVerifying, please re-enter. %s", prompt);
Packit 6c0a39
            fflush(stdout);
Packit 6c0a39
            if (! fgets(key_string, len, stdin)) {
Packit 6c0a39
                explicit_bzero(key_string, len);
Packit 6c0a39
                SAFE_FREE(key_string);
Packit 6c0a39
                clearerr(stdin);
Packit 6c0a39
                continue;
Packit 6c0a39
            }
Packit 6c0a39
            if ((ptr = strchr(key_string, '\n'))) {
Packit 6c0a39
                *ptr = '\0';
Packit 6c0a39
            }
Packit 6c0a39
            fprintf(stdout, "\n");
Packit 6c0a39
            if (strcmp(buf, key_string)) {
Packit 6c0a39
                printf("\n\07\07Mismatch - try again\n");
Packit 6c0a39
                explicit_bzero(key_string, len);
Packit 6c0a39
                SAFE_FREE(key_string);
Packit 6c0a39
                fflush(stdout);
Packit 6c0a39
                continue;
Packit 6c0a39
            }
Packit 6c0a39
            explicit_bzero(key_string, len);
Packit 6c0a39
            SAFE_FREE(key_string);
Packit 6c0a39
        }
Packit 6c0a39
        ok = 1;
Packit 6c0a39
    }
Packit 6c0a39
    explicit_bzero(tmp, len);
Packit 6c0a39
    free(tmp);
Packit 6c0a39
Packit 6c0a39
    return ok;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#ifdef _WIN32
Packit 6c0a39
#include <windows.h>
Packit 6c0a39
Packit 6c0a39
int ssh_getpass(const char *prompt,
Packit 6c0a39
                char *buf,
Packit 6c0a39
                size_t len,
Packit 6c0a39
                int echo,
Packit 6c0a39
                int verify) {
Packit 6c0a39
    HANDLE h;
Packit 6c0a39
    DWORD mode = 0;
Packit 6c0a39
    int ok;
Packit 6c0a39
Packit 6c0a39
    /* fgets needs at least len - 1 */
Packit 6c0a39
    if (prompt == NULL || buf == NULL || len < 2) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* get stdin and mode */
Packit 6c0a39
    h = GetStdHandle(STD_INPUT_HANDLE);
Packit 6c0a39
    if (!GetConsoleMode(h, &mode)) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* disable echo */
Packit 6c0a39
    if (!echo) {
Packit 6c0a39
        if (!SetConsoleMode(h, mode & ~ENABLE_ECHO_INPUT)) {
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    ok = ssh_gets(prompt, buf, len, verify);
Packit 6c0a39
Packit 6c0a39
    /* reset echo */
Packit 6c0a39
    SetConsoleMode(h, mode);
Packit 6c0a39
Packit 6c0a39
    if (!ok) {
Packit 6c0a39
        explicit_bzero(buf, len);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* force termination */
Packit 6c0a39
    buf[len - 1] = '\0';
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#else
Packit 6c0a39
Packit 6c0a39
#include <fcntl.h>
Packit 6c0a39
#ifdef HAVE_TERMIOS_H
Packit 6c0a39
#include <termios.h>
Packit 6c0a39
#endif
Packit 6c0a39
#ifdef HAVE_UNISTD_H
Packit 6c0a39
#include <unistd.h>
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @ingroup libssh_misc
Packit 6c0a39
 *
Packit 6c0a39
 * @brief Get a password from the console.
Packit 6c0a39
 *
Packit 6c0a39
 * You should make sure that the buffer is an empty string!
Packit 6c0a39
 *
Packit 6c0a39
 * You can also use this function to ask for a username. Then you can fill the
Packit 6c0a39
 * buffer with the username and it is shows to the users. If the users just
Packit 6c0a39
 * presses enter the buffer will be untouched.
Packit 6c0a39
 *
Packit 6c0a39
 * @code
Packit 6c0a39
 *   char username[128];
Packit 6c0a39
 *
Packit 6c0a39
 *   snprintf(username, sizeof(username), "john");
Packit 6c0a39
 *
Packit 6c0a39
 *   ssh_getpass("Username:", username, sizeof(username), 1, 0);
Packit 6c0a39
 * @endcode
Packit 6c0a39
 *
Packit 6c0a39
 * The prompt will look like this:
Packit 6c0a39
 *
Packit 6c0a39
 *   Username: [john]
Packit 6c0a39
 *
Packit 6c0a39
 * If you press enter then john is used as the username, or you can type it in
Packit 6c0a39
 * to change it.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  prompt   The prompt to show to ask for the password.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[out] buf    The buffer the password should be stored. It NEEDS to be
Packit 6c0a39
 *                      empty or filled out.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  len      The length of the buffer.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  echo     Should we echo what you type.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  verify   Should we ask for the password twice.
Packit 6c0a39
 *
Packit 6c0a39
 * @return              0 on success, -1 on error.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_getpass(const char *prompt,
Packit 6c0a39
                char *buf,
Packit 6c0a39
                size_t len,
Packit 6c0a39
                int echo,
Packit 6c0a39
                int verify) {
Packit 6c0a39
    struct termios attr;
Packit 6c0a39
    struct termios old_attr;
Packit 6c0a39
    int ok = 0;
Packit 6c0a39
    int fd = -1;
Packit 6c0a39
Packit 6c0a39
    /* fgets needs at least len - 1 */
Packit 6c0a39
    if (prompt == NULL || buf == NULL || len < 2) {
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (isatty(STDIN_FILENO)) {
Packit 6c0a39
        ZERO_STRUCT(attr);
Packit 6c0a39
        ZERO_STRUCT(old_attr);
Packit 6c0a39
Packit 6c0a39
        /* get local terminal attributes */
Packit 6c0a39
        if (tcgetattr(STDIN_FILENO, &attr) < 0) {
Packit 6c0a39
            perror("tcgetattr");
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* save terminal attributes */
Packit 6c0a39
        memcpy(&old_attr, &attr, sizeof(attr));
Packit 6c0a39
        if((fd = fcntl(0, F_GETFL, 0)) < 0) {
Packit 6c0a39
            perror("fcntl");
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* disable echo */
Packit 6c0a39
        if (!echo) {
Packit 6c0a39
            attr.c_lflag &= ~(ECHO);
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* write attributes to terminal */
Packit 6c0a39
        if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) {
Packit 6c0a39
            perror("tcsetattr");
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* disable nonblocking I/O */
Packit 6c0a39
    if (fd & O_NDELAY) {
Packit 6c0a39
        fcntl(0, F_SETFL, fd & ~O_NDELAY);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    ok = ssh_gets(prompt, buf, len, verify);
Packit 6c0a39
Packit 6c0a39
    if (isatty(STDIN_FILENO)) {
Packit 6c0a39
        /* reset terminal */
Packit 6c0a39
        tcsetattr(STDIN_FILENO, TCSANOW, &old_attr);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* close fd */
Packit 6c0a39
    if (fd & O_NDELAY) {
Packit 6c0a39
        fcntl(0, F_SETFL, fd);
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (!ok) {
Packit 6c0a39
        explicit_bzero(buf, len);
Packit 6c0a39
        return -1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* force termination */
Packit 6c0a39
    buf[len - 1] = '\0';
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
#endif