|
|
|
@@ -18,7 +18,7 @@ agent connections.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
|
RCSID("$Id: sshd.c,v 1.1 1999/10/27 03:42:46 damien Exp $");
|
|
|
|
|
RCSID("$Id: sshd.c,v 1.3 1999/10/28 03:20:30 damien Exp $");
|
|
|
|
|
|
|
|
|
|
#include "xmalloc.h"
|
|
|
|
|
#include "rsa.h"
|
|
|
|
@@ -47,14 +47,6 @@ int deny_severity = LOG_WARNING;
|
|
|
|
|
char *ticket = NULL;
|
|
|
|
|
#endif /* KRB4 */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
#include <security/pam_appl.h>
|
|
|
|
|
struct pam_handle_t *pamh=NULL;
|
|
|
|
|
char *pampasswd=NULL;
|
|
|
|
|
int retval;
|
|
|
|
|
int origretval;
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
|
/* Local Xauthority file. */
|
|
|
|
|
char *xauthfile = NULL;
|
|
|
|
|
|
|
|
|
@@ -125,6 +117,7 @@ RSA *public_key;
|
|
|
|
|
/* Prototypes for various functions defined later in this file. */
|
|
|
|
|
void do_connection(int privileged_port);
|
|
|
|
|
void do_authentication(char *user, int privileged_port);
|
|
|
|
|
void eat_packets_and_disconnect(const char *user);
|
|
|
|
|
void do_authenticated(struct passwd *pw);
|
|
|
|
|
void do_exec_pty(const char *command, int ptyfd, int ttyfd,
|
|
|
|
|
const char *ttyname, struct passwd *pw, const char *term,
|
|
|
|
@@ -139,69 +132,98 @@ void do_child(const char *command, struct passwd *pw, const char *term,
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
static int pamconv(int num_msg, const struct pam_message **msg,
|
|
|
|
|
struct pam_response **resp, void *appdata_ptr);
|
|
|
|
|
void do_pam_account_and_session(const char *username, const char *password,
|
|
|
|
|
const char *remote_user, const char *remote_host);
|
|
|
|
|
void pam_cleanup_proc(void *context);
|
|
|
|
|
|
|
|
|
|
static struct pam_conv conv = {
|
|
|
|
|
pamconv,
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
struct pam_handle_t *pamh = NULL;
|
|
|
|
|
const char *pampasswd = NULL;
|
|
|
|
|
|
|
|
|
|
static int pamconv(int num_msg, const struct pam_message **msg,
|
|
|
|
|
struct pam_response **resp, void *appdata_ptr)
|
|
|
|
|
{
|
|
|
|
|
int count = 0;
|
|
|
|
|
int replies = 0;
|
|
|
|
|
struct pam_response *reply = NULL;
|
|
|
|
|
int size = sizeof(struct pam_response);
|
|
|
|
|
|
|
|
|
|
/* PAM will free this later */
|
|
|
|
|
reply = malloc(num_msg * sizeof(*reply));
|
|
|
|
|
if (reply == NULL)
|
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
|
|
|
|
|
for(count = 0; count < num_msg; count++)
|
|
|
|
|
{
|
|
|
|
|
switch (msg[count]->msg_style)
|
|
|
|
|
{
|
|
|
|
|
case PAM_PROMPT_ECHO_ON:
|
|
|
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
|
|
|
if (reply == NULL)
|
|
|
|
|
reply = xmalloc(size);
|
|
|
|
|
else
|
|
|
|
|
reply = realloc(reply, size);
|
|
|
|
|
|
|
|
|
|
if (reply == NULL)
|
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
|
|
|
|
|
size += sizeof(struct pam_response);
|
|
|
|
|
|
|
|
|
|
reply[replies].resp_retcode = PAM_SUCCESS;
|
|
|
|
|
|
|
|
|
|
reply[replies++].resp = xstrdup(pampasswd);
|
|
|
|
|
/* PAM frees resp */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PAM_TEXT_INFO:
|
|
|
|
|
/* ignore it... */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PAM_ERROR_MSG:
|
|
|
|
|
default:
|
|
|
|
|
/* Must be an error of some sort... */
|
|
|
|
|
if (reply != NULL)
|
|
|
|
|
if (pampasswd == NULL)
|
|
|
|
|
{
|
|
|
|
|
free(reply);
|
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
}
|
|
|
|
|
reply[count].resp_retcode = PAM_SUCCESS;
|
|
|
|
|
reply[count].resp = xstrdup(pampasswd);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
}
|
|
|
|
|
case PAM_TEXT_INFO:
|
|
|
|
|
reply[count].resp_retcode = PAM_SUCCESS;
|
|
|
|
|
reply[count].resp = xstrdup("");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PAM_PROMPT_ECHO_ON:
|
|
|
|
|
case PAM_ERROR_MSG:
|
|
|
|
|
default:
|
|
|
|
|
free(reply);
|
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (reply != NULL)
|
|
|
|
|
*resp = reply;
|
|
|
|
|
*resp = reply;
|
|
|
|
|
|
|
|
|
|
return PAM_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pam_cleanup_proc(void *context)
|
|
|
|
|
{
|
|
|
|
|
if (retval == PAM_SUCCESS)
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
if (pamh != NULL)
|
|
|
|
|
{
|
|
|
|
|
retval = pam_close_session((pam_handle_t *)pamh, 0);
|
|
|
|
|
|
|
|
|
|
if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
|
|
|
|
|
log("Cannot release PAM authentication.");
|
|
|
|
|
if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
|
|
|
|
|
log("Cannot release PAM authentication.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void do_pam_account_and_session(const char *username, const char *password, const char *remote_user, const char *remote_host)
|
|
|
|
|
{
|
|
|
|
|
if (remote_host && (PAM_SUCCESS != pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host)))
|
|
|
|
|
{
|
|
|
|
|
log("PAM setup failed.");
|
|
|
|
|
eat_packets_and_disconnect(username);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (remote_user && (PAM_SUCCESS != pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user)))
|
|
|
|
|
{
|
|
|
|
|
log("PAM setup failed.");
|
|
|
|
|
eat_packets_and_disconnect(username);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PAM_SUCCESS != pam_acct_mgmt((pam_handle_t *)pamh, 0))
|
|
|
|
|
{
|
|
|
|
|
log("PAM rejected by account configuration.");
|
|
|
|
|
eat_packets_and_disconnect(username);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PAM_SUCCESS != pam_open_session((pam_handle_t *)pamh, 0))
|
|
|
|
|
{
|
|
|
|
|
log("PAM session setup failed.");
|
|
|
|
|
eat_packets_and_disconnect(username);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
@@ -788,13 +810,19 @@ main(int ac, char **av)
|
|
|
|
|
log("Closing connection to %.100s", inet_ntoa(sin.sin_addr));
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
if (retval == PAM_SUCCESS)
|
|
|
|
|
retval = pam_close_session((pam_handle_t *)pamh, 0);
|
|
|
|
|
{
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
if (pamh != NULL)
|
|
|
|
|
{
|
|
|
|
|
retval = pam_close_session((pam_handle_t *)pamh, 0);
|
|
|
|
|
|
|
|
|
|
if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
|
|
|
|
|
log("Cannot release PAM authentication.");
|
|
|
|
|
if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS)
|
|
|
|
|
log("Cannot release PAM authentication.");
|
|
|
|
|
|
|
|
|
|
fatal_remove_cleanup(&pam_cleanup_proc, NULL);
|
|
|
|
|
fatal_remove_cleanup(&pam_cleanup_proc, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
|
packet_close();
|
|
|
|
@@ -1078,14 +1106,11 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
int type;
|
|
|
|
|
int authenticated = 0;
|
|
|
|
|
int authentication_failures = 0;
|
|
|
|
|
char *password;
|
|
|
|
|
char *password = NULL;
|
|
|
|
|
struct passwd *pw, pwcopy;
|
|
|
|
|
char *client_user;
|
|
|
|
|
char *client_user = NULL;
|
|
|
|
|
unsigned int client_host_key_bits;
|
|
|
|
|
BIGNUM *client_host_key_e, *client_host_key_n;
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
int pam_auth_ok;
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
|
#ifdef AFS
|
|
|
|
|
/* If machine has AFS, set process authentication group. */
|
|
|
|
@@ -1097,63 +1122,9 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
|
|
|
|
|
/* Verify that the user is a valid user. */
|
|
|
|
|
pw = getpwnam(user);
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
if ((pw != NULL) && allowed_user(pw))
|
|
|
|
|
{
|
|
|
|
|
/* Initialise PAM */
|
|
|
|
|
retval = pam_start("ssh", pw->pw_name, &conv, (pam_handle_t **)&pamh);
|
|
|
|
|
fatal_add_cleanup(&pam_cleanup_proc, NULL);
|
|
|
|
|
origretval = retval;
|
|
|
|
|
if (retval == PAM_SUCCESS)
|
|
|
|
|
pam_auth_ok = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pam_auth_ok == 0)
|
|
|
|
|
#else /* HAVE_PAM */
|
|
|
|
|
if (!pw || !allowed_user(pw))
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
{
|
|
|
|
|
/* The user does not exist or access is denied,
|
|
|
|
|
but fake indication that authentication is needed. */
|
|
|
|
|
packet_start(SSH_SMSG_FAILURE);
|
|
|
|
|
packet_send();
|
|
|
|
|
packet_write_wait();
|
|
|
|
|
|
|
|
|
|
/* Keep reading packets, and always respond with a failure. This is to
|
|
|
|
|
avoid disclosing whether such a user really exists. */
|
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
/* Read a packet. This will not return if the client disconnects. */
|
|
|
|
|
int plen;
|
|
|
|
|
int type = packet_read(&plen);
|
|
|
|
|
#ifdef SKEY
|
|
|
|
|
int passw_len;
|
|
|
|
|
char *password, *skeyinfo;
|
|
|
|
|
if (options.password_authentication &&
|
|
|
|
|
options.skey_authentication == 1 &&
|
|
|
|
|
type == SSH_CMSG_AUTH_PASSWORD &&
|
|
|
|
|
(password = packet_get_string(&passw_len)) != NULL &&
|
|
|
|
|
passw_len == 5 &&
|
|
|
|
|
strncasecmp(password, "s/key", 5) == 0 &&
|
|
|
|
|
(skeyinfo = skey_fake_keyinfo(user)) != NULL ){
|
|
|
|
|
/* Send a fake s/key challenge. */
|
|
|
|
|
packet_send_debug(skeyinfo);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
/* Send failure. This should be indistinguishable from a failed
|
|
|
|
|
authentication. */
|
|
|
|
|
packet_start(SSH_SMSG_FAILURE);
|
|
|
|
|
packet_send();
|
|
|
|
|
packet_write_wait();
|
|
|
|
|
if (++authentication_failures >= MAX_AUTH_FAILURES) {
|
|
|
|
|
packet_disconnect("Too many authentication failures for %.100s from %.200s",
|
|
|
|
|
user, get_canonical_hostname());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*NOTREACHED*/
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
eat_packets_and_disconnect(user);
|
|
|
|
|
|
|
|
|
|
/* Take a copy of the returned structure. */
|
|
|
|
|
memset(&pwcopy, 0, sizeof(pwcopy));
|
|
|
|
|
pwcopy.pw_name = xstrdup(pw->pw_name);
|
|
|
|
@@ -1164,6 +1135,18 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
pwcopy.pw_shell = xstrdup(pw->pw_shell);
|
|
|
|
|
pw = &pwcopy;
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
if (PAM_SUCCESS != pam_start("ssh", pw->pw_name, &conv, (pam_handle_t**)&pamh))
|
|
|
|
|
{
|
|
|
|
|
packet_start(SSH_SMSG_FAILURE);
|
|
|
|
|
packet_send();
|
|
|
|
|
packet_write_wait();
|
|
|
|
|
packet_disconnect("PAM initialisation failed.");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
fatal_add_cleanup(&pam_cleanup_proc, NULL);
|
|
|
|
|
|
|
|
|
|
/* If we are not running as root, the user must have the same uid as the
|
|
|
|
|
server. */
|
|
|
|
|
if (getuid() != 0 && pw->pw_uid != getuid())
|
|
|
|
@@ -1306,12 +1289,16 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
log("Rhosts authentication accepted for %.100s, remote %.100s on %.700s.",
|
|
|
|
|
user, client_user, get_canonical_hostname());
|
|
|
|
|
authenticated = 1;
|
|
|
|
|
#ifndef HAVE_PAM
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
log("Rhosts authentication failed for %.100s, remote %.100s.",
|
|
|
|
|
user, client_user);
|
|
|
|
|
#ifndef HAVE_PAM
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SSH_CMSG_AUTH_RHOSTS_RSA:
|
|
|
|
@@ -1354,14 +1341,18 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
{
|
|
|
|
|
/* Authentication accepted. */
|
|
|
|
|
authenticated = 1;
|
|
|
|
|
#ifndef HAVE_PAM
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
BN_clear_free(client_host_key_e);
|
|
|
|
|
BN_clear_free(client_host_key_n);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
log("Rhosts authentication failed for %.100s, remote %.100s.",
|
|
|
|
|
user, client_user);
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
#ifndef HAVE_PAM
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
BN_clear_free(client_host_key_e);
|
|
|
|
|
BN_clear_free(client_host_key_n);
|
|
|
|
|
break;
|
|
|
|
@@ -1412,6 +1403,20 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
packet_integrity_check(plen, 4 + passw_len, type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
pampasswd = password;
|
|
|
|
|
|
|
|
|
|
if (PAM_SUCCESS == pam_authenticate((pam_handle_t *)pamh, 0))
|
|
|
|
|
{
|
|
|
|
|
log("PAM Password authentication accepted for %.100s.", user);
|
|
|
|
|
authenticated = 1;
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
log("PAM Password authentication for %.100s failed.", user);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#else /* HAVE_PAM */
|
|
|
|
|
/* Try authentication with the password. */
|
|
|
|
|
if (auth_password(pw, password))
|
|
|
|
|
{
|
|
|
|
@@ -1427,6 +1432,7 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
memset(password, 0, strlen(password));
|
|
|
|
|
xfree(password);
|
|
|
|
|
break;
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
|
case SSH_CMSG_AUTH_TIS:
|
|
|
|
|
/* TIS Authentication is unsupported */
|
|
|
|
@@ -1464,6 +1470,20 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
get_canonical_hostname());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PAM
|
|
|
|
|
do_pam_account_and_session(pw->pw_name, password, client_user, get_canonical_hostname());
|
|
|
|
|
|
|
|
|
|
/* Clean up */
|
|
|
|
|
if (client_user != NULL)
|
|
|
|
|
xfree(client_user);
|
|
|
|
|
|
|
|
|
|
if (password != NULL)
|
|
|
|
|
{
|
|
|
|
|
memset(password, 0, strlen(password));
|
|
|
|
|
xfree(password);
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_PAM */
|
|
|
|
|
|
|
|
|
|
/* The user has been authenticated and accepted. */
|
|
|
|
|
packet_start(SSH_SMSG_SUCCESS);
|
|
|
|
|
packet_send();
|
|
|
|
@@ -1473,6 +1493,55 @@ do_authentication(char *user, int privileged_port)
|
|
|
|
|
do_authenticated(pw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read authentication messages, but return only failures until */
|
|
|
|
|
/* max auth attempts exceeded, then disconnect */
|
|
|
|
|
void eat_packets_and_disconnect(const char *user)
|
|
|
|
|
{
|
|
|
|
|
int authentication_failures = 0;
|
|
|
|
|
|
|
|
|
|
packet_start(SSH_SMSG_FAILURE);
|
|
|
|
|
packet_send();
|
|
|
|
|
packet_write_wait();
|
|
|
|
|
|
|
|
|
|
/* Keep reading packets, and always respond with a failure. This is to
|
|
|
|
|
avoid disclosing whether such a user really exists. */
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
/* Read a packet. This will not return if the client disconnects. */
|
|
|
|
|
int plen;
|
|
|
|
|
#ifndef SKEY
|
|
|
|
|
(void) packet_read(&plen);
|
|
|
|
|
#else /* SKEY */
|
|
|
|
|
int type = packet_read(&plen);
|
|
|
|
|
int passw_len;
|
|
|
|
|
char *password, *skeyinfo;
|
|
|
|
|
if (options.password_authentication &&
|
|
|
|
|
options.skey_authentication == 1 &&
|
|
|
|
|
type == SSH_CMSG_AUTH_PASSWORD &&
|
|
|
|
|
(password = packet_get_string(&passw_len)) != NULL &&
|
|
|
|
|
passw_len == 5 &&
|
|
|
|
|
strncasecmp(password, "s/key", 5) == 0 &&
|
|
|
|
|
(skeyinfo = skey_fake_keyinfo(user)) != NULL )
|
|
|
|
|
{
|
|
|
|
|
/* Send a fake s/key challenge. */
|
|
|
|
|
packet_send_debug(skeyinfo);
|
|
|
|
|
}
|
|
|
|
|
#endif /* SKEY */
|
|
|
|
|
/* Send failure. This should be indistinguishable from a failed
|
|
|
|
|
authentication. */
|
|
|
|
|
packet_start(SSH_SMSG_FAILURE);
|
|
|
|
|
packet_send();
|
|
|
|
|
packet_write_wait();
|
|
|
|
|
if (++authentication_failures >= MAX_AUTH_FAILURES)
|
|
|
|
|
{
|
|
|
|
|
packet_disconnect("Too many authentication failures for %.100s from %.200s",
|
|
|
|
|
user, get_canonical_hostname());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*NOTREACHED*/
|
|
|
|
|
abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prepares for an interactive session. This is called after the user has
|
|
|
|
|
been successfully authenticated. During this message exchange, pseudo
|
|
|
|
|
terminals are allocated, X11, TCP/IP, and authentication agent forwardings
|
|
|
|
@@ -2151,10 +2220,6 @@ void do_child(const char *command, struct passwd *pw, const char *term,
|
|
|
|
|
exit(254);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set login name in the kernel. */
|
|
|
|
|
if (setlogin(pw->pw_name) < 0)
|
|
|
|
|
error("setlogin failed: %s", strerror(errno));
|
|
|
|
|
|
|
|
|
|
/* Set uid, gid, and groups. */
|
|
|
|
|
/* Login(1) does this as well, and it needs uid 0 for the "-h" switch,
|
|
|
|
|
so we let login(1) to this for us. */
|
|
|
|
|