readpass.c revision 113908
157429Smarkm/*
292555Sdes * Copyright (c) 2001 Markus Friedl.  All rights reserved.
357429Smarkm *
457429Smarkm * Redistribution and use in source and binary forms, with or without
557429Smarkm * modification, are permitted provided that the following conditions
657429Smarkm * are met:
757429Smarkm * 1. Redistributions of source code must retain the above copyright
857429Smarkm *    notice, this list of conditions and the following disclaimer.
957429Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1057429Smarkm *    notice, this list of conditions and the following disclaimer in the
1157429Smarkm *    documentation and/or other materials provided with the distribution.
1257429Smarkm *
1392555Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1492555Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1592555Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1692555Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1792555Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1892555Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1992555Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2092555Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2192555Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2292555Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2357429Smarkm */
2457429Smarkm
2557429Smarkm#include "includes.h"
26113908SdesRCSID("$OpenBSD: readpass.c,v 1.28 2003/01/23 13:50:27 markus Exp $");
2757429Smarkm
2857429Smarkm#include "xmalloc.h"
2976259Sgreen#include "readpass.h"
3076259Sgreen#include "pathnames.h"
3176259Sgreen#include "log.h"
3257429Smarkm#include "ssh.h"
3357429Smarkm
3492555Sdesstatic char *
3592555Sdesssh_askpass(char *askpass, const char *msg)
3676259Sgreen{
3776259Sgreen	pid_t pid;
3876259Sgreen	size_t len;
3992555Sdes	char *pass;
4092555Sdes	int p[2], status, ret;
4176259Sgreen	char buf[1024];
4276259Sgreen
4376259Sgreen	if (fflush(stdout) != 0)
4476259Sgreen		error("ssh_askpass: fflush: %s", strerror(errno));
4576259Sgreen	if (askpass == NULL)
4676259Sgreen		fatal("internal error: askpass undefined");
4792555Sdes	if (pipe(p) < 0) {
4892555Sdes		error("ssh_askpass: pipe: %s", strerror(errno));
49113908Sdes		return NULL;
5092555Sdes	}
5192555Sdes	if ((pid = fork()) < 0) {
5292555Sdes		error("ssh_askpass: fork: %s", strerror(errno));
53113908Sdes		return NULL;
5492555Sdes	}
5576259Sgreen	if (pid == 0) {
5676259Sgreen		seteuid(getuid());
5776259Sgreen		setuid(getuid());
5876259Sgreen		close(p[0]);
5976259Sgreen		if (dup2(p[1], STDOUT_FILENO) < 0)
6076259Sgreen			fatal("ssh_askpass: dup2: %s", strerror(errno));
6176259Sgreen		execlp(askpass, askpass, msg, (char *) 0);
6276259Sgreen		fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno));
6376259Sgreen	}
6476259Sgreen	close(p[1]);
6592555Sdes
6692555Sdes	len = ret = 0;
6792555Sdes	do {
6892555Sdes		ret = read(p[0], buf + len, sizeof(buf) - 1 - len);
6992555Sdes		if (ret == -1 && errno == EINTR)
7092555Sdes			continue;
7192555Sdes		if (ret <= 0)
7292555Sdes			break;
7392555Sdes		len += ret;
7492555Sdes	} while (sizeof(buf) - 1 - len > 0);
7592555Sdes	buf[len] = '\0';
7692555Sdes
7776259Sgreen	close(p[0]);
7876259Sgreen	while (waitpid(pid, &status, 0) < 0)
7976259Sgreen		if (errno != EINTR)
8076259Sgreen			break;
8192555Sdes
82113908Sdes	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
83113908Sdes		memset(buf, 0, sizeof(buf));
84113908Sdes		return NULL;
85113908Sdes	}
86113908Sdes
8792555Sdes	buf[strcspn(buf, "\r\n")] = '\0';
8876259Sgreen	pass = xstrdup(buf);
8976259Sgreen	memset(buf, 0, sizeof(buf));
9076259Sgreen	return pass;
9176259Sgreen}
9276259Sgreen
9357429Smarkm/*
9492555Sdes * Reads a passphrase from /dev/tty with echo turned off/on.  Returns the
9592555Sdes * passphrase (allocated with xmalloc).  Exits if EOF is encountered. If
9692555Sdes * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no
9792555Sdes * tty is available
9857429Smarkm */
9957429Smarkmchar *
10092555Sdesread_passphrase(const char *prompt, int flags)
10157429Smarkm{
10292555Sdes	char *askpass = NULL, *ret, buf[1024];
10392555Sdes	int rppflags, use_askpass = 0, ttyfd;
10476259Sgreen
10592555Sdes	rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF;
10692555Sdes	if (flags & RP_ALLOW_STDIN) {
10776259Sgreen		if (!isatty(STDIN_FILENO))
10876259Sgreen			use_askpass = 1;
10976259Sgreen	} else {
11092555Sdes		rppflags |= RPP_REQUIRE_TTY;
11192555Sdes		ttyfd = open(_PATH_TTY, O_RDWR);
11276259Sgreen		if (ttyfd >= 0)
11376259Sgreen			close(ttyfd);
11476259Sgreen		else
11576259Sgreen			use_askpass = 1;
11676259Sgreen	}
11776259Sgreen
11876259Sgreen	if (use_askpass && getenv("DISPLAY")) {
11976259Sgreen		if (getenv(SSH_ASKPASS_ENV))
12076259Sgreen			askpass = getenv(SSH_ASKPASS_ENV);
12176259Sgreen		else
12276259Sgreen			askpass = _PATH_SSH_ASKPASS_DEFAULT;
123113908Sdes		if ((ret = ssh_askpass(askpass, prompt)) == NULL)
124113908Sdes			if (!(flags & RP_ALLOW_EOF))
125113908Sdes				return xstrdup("");
126113908Sdes		return ret;
12776259Sgreen	}
12876259Sgreen
12998675Sdes	if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
13098675Sdes		if (flags & RP_ALLOW_EOF)
13198675Sdes			return NULL;
13292555Sdes		return xstrdup("");
13398675Sdes	}
13492555Sdes
13592555Sdes	ret = xstrdup(buf);
13692555Sdes	memset(buf, 'x', sizeof buf);
13792555Sdes	return ret;
13857429Smarkm}
139