Update to OpenSSH 2.3.0 with FreeBSD modifications. OpenSSH 2.3.0
new features description elided in favor of checking out their website. Important new FreeBSD-version stuff: PAM support has been worked in, partially from the "Unix" OpenSSH version, and a lot due to the work of Eivind Eklend, too. This requires at least the following in pam.conf: sshd auth sufficient pam_skey.so sshd auth required pam_unix.so try_first_pass sshd session required pam_permit.so Parts by: Eivind Eklend <eivind@FreeBSD.org>
This commit is contained in:
parent
386879a128
commit
099584266b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=69591
@ -28,7 +28,7 @@
|
||||
#include "ssh.h"
|
||||
#include "servconf.h"
|
||||
|
||||
RCSID("$OpenBSD: auth-krb4.c,v 1.18 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: auth-krb4.c,v 1.19 2000/10/03 18:03:02 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#ifdef KRB4
|
||||
@ -281,6 +281,8 @@ auth_krb4_tgt(struct passwd *pw, const char *string)
|
||||
{
|
||||
CREDENTIALS creds;
|
||||
|
||||
if (pw == NULL)
|
||||
goto auth_kerberos_tgt_failure;
|
||||
if (!radix_to_creds(string, &creds)) {
|
||||
log("Protocol error decoding Kerberos V4 tgt");
|
||||
packet_send_debug("Protocol error decoding Kerberos V4 tgt");
|
||||
@ -335,8 +337,16 @@ int
|
||||
auth_afs_token(struct passwd *pw, const char *token_string)
|
||||
{
|
||||
CREDENTIALS creds;
|
||||
uid_t uid = pw->pw_uid;
|
||||
uid_t uid;
|
||||
|
||||
if (pw == NULL) {
|
||||
/* XXX fake protocol error */
|
||||
packet_send_debug("Protocol error decoding AFS token");
|
||||
packet_start(SSH_SMSG_FAILURE);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
return 0;
|
||||
}
|
||||
if (!radix_to_creds(token_string, &creds)) {
|
||||
log("Protocol error decoding AFS token");
|
||||
packet_send_debug("Protocol error decoding AFS token");
|
||||
@ -350,6 +360,8 @@ auth_afs_token(struct passwd *pw, const char *token_string)
|
||||
|
||||
if (strncmp(creds.pname, "AFS ID ", 7) == 0)
|
||||
uid = atoi(creds.pname + 7);
|
||||
else
|
||||
uid = pw->pw_uid;
|
||||
|
||||
if (kafs_settoken(creds.realm, uid, &creds)) {
|
||||
log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm,
|
||||
|
777
crypto/openssh/auth-pam.c
Normal file
777
crypto/openssh/auth-pam.c
Normal file
@ -0,0 +1,777 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Damien Miller. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#ifdef USE_PAM
|
||||
#include <security/pam_appl.h>
|
||||
#include "ssh.h"
|
||||
#include "xmalloc.h"
|
||||
#include "servconf.h"
|
||||
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#define NEW_AUTHTOK_MSG \
|
||||
"Warning: Your password has expired, please change it now"
|
||||
|
||||
#define SSHD_PAM_SERVICE "sshd"
|
||||
#define PAM_STRERROR(a, b) pam_strerror((a), (b))
|
||||
|
||||
/* Callbacks */
|
||||
static int pamconv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *appdata_ptr);
|
||||
void pam_cleanup_proc(void *context);
|
||||
void pam_msg_cat(const char *msg);
|
||||
|
||||
/* module-local variables */
|
||||
static struct pam_conv conv = {
|
||||
pamconv,
|
||||
NULL
|
||||
};
|
||||
static pam_handle_t *pamh = NULL;
|
||||
static const char *pampasswd = NULL;
|
||||
static char *pam_msg = NULL;
|
||||
|
||||
/* states for pamconv() */
|
||||
typedef enum { INITIAL_LOGIN, OTHER } pamstates;
|
||||
static pamstates pamstate = INITIAL_LOGIN;
|
||||
/* remember whether pam_acct_mgmt() returned PAM_NEWAUTHTOK_REQD */
|
||||
static int password_change_required = 0;
|
||||
|
||||
/*
|
||||
* PAM conversation function.
|
||||
* There are two states this can run in.
|
||||
*
|
||||
* INITIAL_LOGIN mode simply feeds the password from the client into
|
||||
* PAM in response to PAM_PROMPT_ECHO_OFF, and collects output
|
||||
* messages with pam_msg_cat(). This is used during initial
|
||||
* authentication to bypass the normal PAM password prompt.
|
||||
*
|
||||
* OTHER mode handles PAM_PROMPT_ECHO_OFF with read_passphrase(prompt, 1)
|
||||
* and outputs messages to stderr. This mode is used if pam_chauthtok()
|
||||
* is called to update expired passwords.
|
||||
*/
|
||||
static int pamconv(int num_msg, const struct pam_message **msg,
|
||||
struct pam_response **resp, void *appdata_ptr)
|
||||
{
|
||||
struct pam_response *reply;
|
||||
int count;
|
||||
char buf[1024];
|
||||
|
||||
/* 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:
|
||||
if (pamstate == INITIAL_LOGIN) {
|
||||
free(reply);
|
||||
return PAM_CONV_ERR;
|
||||
} else {
|
||||
fputs((*msg)[count].msg, stderr);
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
reply[count].resp = xstrdup(buf);
|
||||
reply[count].resp_retcode = PAM_SUCCESS;
|
||||
break;
|
||||
}
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
if (pamstate == INITIAL_LOGIN) {
|
||||
if (pampasswd == NULL) {
|
||||
free(reply);
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
reply[count].resp = xstrdup(pampasswd);
|
||||
} else {
|
||||
reply[count].resp =
|
||||
xstrdup(read_passphrase((*msg)[count].msg, 1));
|
||||
}
|
||||
reply[count].resp_retcode = PAM_SUCCESS;
|
||||
break;
|
||||
case PAM_ERROR_MSG:
|
||||
case PAM_TEXT_INFO:
|
||||
if ((*msg)[count].msg != NULL) {
|
||||
if (pamstate == INITIAL_LOGIN)
|
||||
pam_msg_cat((*msg)[count].msg);
|
||||
else {
|
||||
fputs((*msg)[count].msg, stderr);
|
||||
fputs("\n", stderr);
|
||||
}
|
||||
}
|
||||
reply[count].resp = xstrdup("");
|
||||
reply[count].resp_retcode = PAM_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
free(reply);
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
*resp = reply;
|
||||
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/* Called at exit to cleanly shutdown PAM */
|
||||
void pam_cleanup_proc(void *context)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
if (pamh != NULL)
|
||||
{
|
||||
pam_retval = pam_close_session(pamh, 0);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
log("Cannot close PAM session[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
|
||||
pam_retval = pam_setcred(pamh, PAM_DELETE_CRED);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
debug("Cannot delete credentials[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
|
||||
pam_retval = pam_end(pamh, pam_retval);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
log("Cannot release PAM authentication[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Attempt password authentation using PAM */
|
||||
int auth_pam_password(struct passwd *pw, const char *password)
|
||||
{
|
||||
extern ServerOptions options;
|
||||
int pam_retval;
|
||||
|
||||
/* deny if no user. */
|
||||
if (pw == NULL)
|
||||
return 0;
|
||||
if (pw->pw_uid == 0 && options.permit_root_login == 2)
|
||||
return 0;
|
||||
if (*password == '\0' && options.permit_empty_passwd == 0)
|
||||
return 0;
|
||||
|
||||
pampasswd = password;
|
||||
|
||||
pamstate = INITIAL_LOGIN;
|
||||
pam_retval = pam_authenticate(pamh, 0);
|
||||
if (pam_retval == PAM_SUCCESS) {
|
||||
debug("PAM Password authentication accepted for user \"%.100s\"",
|
||||
pw->pw_name);
|
||||
return 1;
|
||||
} else {
|
||||
debug("PAM Password authentication for \"%.100s\" failed[%d]: %s",
|
||||
pw->pw_name, pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do account management using PAM */
|
||||
int do_pam_account(char *username, char *remote_user)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname());
|
||||
pam_retval = pam_set_item(pamh, PAM_RHOST,
|
||||
get_canonical_hostname());
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM set rhost failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
|
||||
if (remote_user != NULL) {
|
||||
debug("PAM setting ruser to \"%.200s\"", remote_user);
|
||||
pam_retval = pam_set_item(pamh, PAM_RUSER, remote_user);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM set ruser failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
}
|
||||
|
||||
pam_retval = pam_acct_mgmt(pamh, 0);
|
||||
switch (pam_retval) {
|
||||
case PAM_SUCCESS:
|
||||
/* This is what we want */
|
||||
break;
|
||||
case PAM_NEW_AUTHTOK_REQD:
|
||||
pam_msg_cat(NEW_AUTHTOK_MSG);
|
||||
/* flag that password change is necessary */
|
||||
password_change_required = 1;
|
||||
break;
|
||||
default:
|
||||
log("PAM rejected by account configuration[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
return(0);
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* Do PAM-specific session initialisation */
|
||||
void do_pam_session(char *username, const char *ttyname)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
if (ttyname != NULL) {
|
||||
debug("PAM setting tty to \"%.200s\"", ttyname);
|
||||
pam_retval = pam_set_item(pamh, PAM_TTY, ttyname);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM set tty failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
}
|
||||
|
||||
debug("do_pam_session: euid %u, uid %u", geteuid(), getuid());
|
||||
pam_retval = pam_open_session(pamh, 0);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM session setup failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
}
|
||||
|
||||
/* Set PAM credentials */
|
||||
void do_pam_setcred(void)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
debug("PAM establishing creds");
|
||||
pam_retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM setcred failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
}
|
||||
|
||||
/* accessor function for file scope static variable */
|
||||
int pam_password_change_required(void)
|
||||
{
|
||||
return password_change_required;
|
||||
}
|
||||
|
||||
/*
|
||||
* Have user change authentication token if pam_acct_mgmt() indicated
|
||||
* it was expired. This needs to be called after an interactive
|
||||
* session is established and the user's pty is connected to
|
||||
* stdin/stout/stderr.
|
||||
*/
|
||||
void do_pam_chauthtok(void)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
if (password_change_required) {
|
||||
pamstate = OTHER;
|
||||
/*
|
||||
* XXX: should we really loop forever?
|
||||
*/
|
||||
do {
|
||||
pam_retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
log("PAM pam_chauthtok failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
} while (pam_retval != PAM_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanly shutdown PAM */
|
||||
void finish_pam(void)
|
||||
{
|
||||
pam_cleanup_proc(NULL);
|
||||
fatal_remove_cleanup(&pam_cleanup_proc, NULL);
|
||||
}
|
||||
|
||||
/* Start PAM authentication for specified account */
|
||||
void start_pam(struct passwd *pw)
|
||||
{
|
||||
int pam_retval;
|
||||
|
||||
debug("Starting up PAM with username \"%.200s\"", pw->pw_name);
|
||||
|
||||
pam_retval = pam_start(SSHD_PAM_SERVICE, pw->pw_name, &conv, &pamh);
|
||||
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM initialisation failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
|
||||
#ifdef PAM_TTY_KLUDGE
|
||||
/*
|
||||
* Some PAM modules (e.g. pam_time) require a TTY to operate,
|
||||
* and will fail in various stupid ways if they don't get one.
|
||||
* sshd doesn't set the tty until too late in the auth process and may
|
||||
* not even need one (for tty-less connections)
|
||||
* Kludge: Set a fake PAM_TTY
|
||||
*/
|
||||
pam_retval = pam_set_item(pamh, PAM_TTY, "ssh");
|
||||
if (pam_retval != PAM_SUCCESS) {
|
||||
fatal("PAM set tty failed[%d]: %.200s",
|
||||
pam_retval, PAM_STRERROR(pamh, pam_retval));
|
||||
}
|
||||
#endif /* PAM_TTY_KLUDGE */
|
||||
|
||||
fatal_add_cleanup(&pam_cleanup_proc, NULL);
|
||||
}
|
||||
|
||||
/* Return list of PAM enviornment strings */
|
||||
char **fetch_pam_environment(void)
|
||||
{
|
||||
#ifdef HAVE_PAM_GETENVLIST
|
||||
return(pam_getenvlist(pamh));
|
||||
#else /* HAVE_PAM_GETENVLIST */
|
||||
return(NULL);
|
||||
#endif /* HAVE_PAM_GETENVLIST */
|
||||
}
|
||||
|
||||
/* Print any messages that have been generated during authentication */
|
||||
/* or account checking to stderr */
|
||||
void print_pam_messages(void)
|
||||
{
|
||||
if (pam_msg != NULL)
|
||||
fputs(pam_msg, stderr);
|
||||
}
|
||||
|
||||
/* Append a message to the PAM message buffer */
|
||||
void pam_msg_cat(const char *msg)
|
||||
{
|
||||
char *p;
|
||||
size_t new_msg_len;
|
||||
size_t pam_msg_len;
|
||||
|
||||
new_msg_len = strlen(msg);
|
||||
|
||||
if (pam_msg) {
|
||||
pam_msg_len = strlen(pam_msg);
|
||||
pam_msg = xrealloc(pam_msg, new_msg_len + pam_msg_len + 2);
|
||||
p = pam_msg + pam_msg_len;
|
||||
} else {
|
||||
pam_msg = p = xmalloc(new_msg_len + 2);
|
||||
}
|
||||
|
||||
memcpy(p, msg, new_msg_len);
|
||||
p[new_msg_len] = '\n';
|
||||
p[new_msg_len + 1] = '\0';
|
||||
}
|
||||
|
||||
struct inverted_pam_userdata {
|
||||
/*
|
||||
* Pipe for telling whether we are doing conversation or sending
|
||||
* authentication results.
|
||||
*/
|
||||
int statefd[2];
|
||||
int challengefd[2];
|
||||
int responsefd[2];
|
||||
|
||||
/* Whether we have sent off our challenge */
|
||||
int state;
|
||||
};
|
||||
|
||||
#define STATE_CONV 1
|
||||
#define STATE_AUTH_OK 2
|
||||
#define STATE_AUTH_FAIL 3
|
||||
|
||||
int
|
||||
ssh_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp,
|
||||
void *userdata) {
|
||||
int i;
|
||||
FILE *reader;
|
||||
char buf[1024];
|
||||
struct pam_response *reply = NULL;
|
||||
char state_to_write = STATE_CONV; /* One char to write */
|
||||
struct inverted_pam_userdata *ud = userdata;
|
||||
char *response = NULL;
|
||||
|
||||
/* The stdio functions are more convenient for the read half */
|
||||
reader = fdopen(ud->responsefd[0], "rb");
|
||||
if (reader == NULL)
|
||||
goto protocol_failure;
|
||||
|
||||
reply = malloc(num_msg * sizeof(struct pam_response));
|
||||
if (reply == NULL)
|
||||
return PAM_CONV_ERR;
|
||||
|
||||
if (write(ud->statefd[1], &state_to_write, 1) != 1)
|
||||
goto protocol_failure;
|
||||
|
||||
/*
|
||||
* Re-package our data and send it off to our better half (the actual SSH
|
||||
* process)
|
||||
*/
|
||||
if (write(ud->challengefd[1], buf,
|
||||
sprintf(buf, "%d\n", num_msg)) == -1)
|
||||
goto protocol_failure;
|
||||
for (i = 0; i < num_msg; i++) {
|
||||
if (write(ud->challengefd[1], buf,
|
||||
sprintf(buf, "%d\n", msg[i]->msg_style)) == -1)
|
||||
goto protocol_failure;
|
||||
if (write(ud->challengefd[1], buf,
|
||||
sprintf(buf, "%d\n", strlen(msg[i]->msg))) == -1)
|
||||
goto protocol_failure;
|
||||
if (write(ud->challengefd[1], msg[i]->msg,
|
||||
strlen(msg[i]->msg)) == -1)
|
||||
goto protocol_failure;
|
||||
}
|
||||
/*
|
||||
* Read back responses. These may not be as nice as we want, as the SSH
|
||||
* protocol isn't exactly a perfect fit with PAM.
|
||||
*/
|
||||
|
||||
for (i = 0; i < num_msg; i++) {
|
||||
char buf[1024];
|
||||
char *endptr;
|
||||
size_t len; /* Length of the response */
|
||||
|
||||
switch (msg[i]->msg_style) {
|
||||
case PAM_PROMPT_ECHO_OFF:
|
||||
case PAM_PROMPT_ECHO_ON:
|
||||
if (fgets(buf, sizeof(buf), reader) == NULL)
|
||||
goto protocol_failure;
|
||||
len = (size_t)strtoul(buf, &endptr, 10);
|
||||
/* The length is supposed to stand on a line by itself */
|
||||
if (endptr == NULL || *endptr != '\n')
|
||||
goto protocol_failure;
|
||||
response = malloc(len+1);
|
||||
if (response == NULL)
|
||||
goto protocol_failure;
|
||||
if (fread(response, len, 1, reader) != 1)
|
||||
goto protocol_failure;
|
||||
response[len] = '\0';
|
||||
reply[i].resp = response;
|
||||
response = NULL;
|
||||
break;
|
||||
default:
|
||||
reply[i].resp = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*resp = reply;
|
||||
return PAM_SUCCESS;
|
||||
protocol_failure:
|
||||
free(reply);
|
||||
return PAM_CONV_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
ipam_free_cookie(struct inverted_pam_cookie *cookie) {
|
||||
struct inverted_pam_userdata *ud;
|
||||
int i;
|
||||
|
||||
if (cookie == NULL)
|
||||
return;
|
||||
ud = cookie->userdata;
|
||||
cookie->userdata = NULL;
|
||||
/* Free userdata if allocated */
|
||||
if (ud) {
|
||||
/* Close any opened file descriptors */
|
||||
if (ud->statefd[0] != -1)
|
||||
close(ud->statefd[0]);
|
||||
if (ud->statefd[1] != -1)
|
||||
close(ud->statefd[1]);
|
||||
if (ud->challengefd[0] != -1)
|
||||
close(ud->challengefd[0]);
|
||||
if (ud->challengefd[1] != -1)
|
||||
close(ud->challengefd[1]);
|
||||
if (ud->responsefd[0] != -1)
|
||||
close(ud->responsefd[0]);
|
||||
if (ud->responsefd[1] != -1)
|
||||
close(ud->responsefd[1]);
|
||||
free(ud);
|
||||
ud = NULL;
|
||||
}
|
||||
/* Now free the normal cookie */
|
||||
if (cookie->pid != 0 && cookie->pid != -1) {
|
||||
int status;
|
||||
|
||||
/* XXX Use different signal? */
|
||||
kill(cookie->pid, SIGKILL);
|
||||
waitpid(cookie->pid, &status, 0);
|
||||
}
|
||||
for (i = 0; i < cookie->num_msg; i++) {
|
||||
if (cookie->resp && cookie->resp[i]) {
|
||||
free(cookie->resp[i]->resp);
|
||||
free(cookie->resp[i]);
|
||||
}
|
||||
if (cookie->msg && cookie->msg[i]) {
|
||||
free((void *)cookie->msg[i]->msg);
|
||||
free(cookie->msg[i]);
|
||||
}
|
||||
}
|
||||
free(cookie->msg);
|
||||
free(cookie->resp);
|
||||
free(cookie);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do first half of PAM authentication - this comes to the point where
|
||||
* you get a message to send to the user.
|
||||
*/
|
||||
struct inverted_pam_cookie *
|
||||
ipam_start_auth(const char *service, const char *username) {
|
||||
struct inverted_pam_cookie *cookie;
|
||||
struct inverted_pam_userdata *ud;
|
||||
static struct pam_conv conv = {
|
||||
ssh_conv,
|
||||
NULL
|
||||
};
|
||||
|
||||
cookie = malloc(sizeof(*cookie));
|
||||
if (cookie == NULL)
|
||||
return NULL;
|
||||
cookie->state = 0;
|
||||
/* Set up the cookie so ipam_freecookie can be used on it */
|
||||
cookie->num_msg = 0;
|
||||
cookie->msg = NULL;
|
||||
cookie->resp = NULL;
|
||||
cookie->pid = -1;
|
||||
|
||||
ud = calloc(sizeof(*ud), 1);
|
||||
if (ud == NULL) {
|
||||
free(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->userdata = ud;
|
||||
ud->statefd[0] = ud->statefd[1] = -1;
|
||||
ud->challengefd[0] = ud->challengefd[1] = -1;
|
||||
ud->responsefd[0] = ud->responsefd[1] = -1;
|
||||
|
||||
if (pipe(ud->statefd) != 0) {
|
||||
ud->statefd[0] = ud->statefd[1] = -1;
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
if (pipe(ud->challengefd) != 0) {
|
||||
ud->challengefd[0] = ud->challengefd[1] = -1;
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
if (pipe(ud->responsefd) != 0) {
|
||||
ud->responsefd[0] = ud->responsefd[1] = -1;
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->pid = fork();
|
||||
if (cookie->pid == -1) {
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
} else if (cookie->pid != 0) {
|
||||
int num_msgs; /* Number of messages from PAM */
|
||||
char *endptr;
|
||||
char buf[1024];
|
||||
FILE *reader;
|
||||
size_t num_msg;
|
||||
int i;
|
||||
char state; /* Which state did the connection just enter? */
|
||||
|
||||
/* We are the parent - wait for a call to the communications
|
||||
function to turn up, or the challenge to be finished */
|
||||
if (read(ud->statefd[0], &state, 1) != 1) {
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->state = state;
|
||||
switch (state) {
|
||||
case STATE_CONV:
|
||||
/* We are running the conversation function */
|
||||
/* The stdio functions are more convenient for read */
|
||||
reader = fdopen(ud->challengefd[0], "r");
|
||||
if (reader == NULL) {
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
if (fgets(buf, 4, reader) == NULL) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
num_msg = (size_t)strtoul(buf, &endptr, 10);
|
||||
/* The length is supposed to stand on a line by itself */
|
||||
if (endptr == NULL || *endptr != '\n') {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->msg =
|
||||
malloc(sizeof(struct pam_message *) * num_msg);
|
||||
cookie->resp =
|
||||
malloc(sizeof(struct pam_response *) * num_msg);
|
||||
if (cookie->msg == NULL || cookie->resp == NULL) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < num_msg; i++) {
|
||||
cookie->msg[i] =
|
||||
malloc(sizeof(struct pam_message));
|
||||
cookie->resp[i] =
|
||||
malloc(sizeof(struct pam_response));
|
||||
if (cookie->msg[i] == NULL ||
|
||||
cookie->resp[i] == NULL) {
|
||||
for (;;) {
|
||||
free(cookie->msg[i]);
|
||||
free(cookie->resp[i]);
|
||||
if (i == 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->msg[i]->msg = NULL;
|
||||
cookie->resp[i]->resp = NULL;
|
||||
cookie->resp[i]->resp_retcode = 0;
|
||||
}
|
||||
/* Set up so the above will be freed on failure */
|
||||
cookie->num_msg = num_msg;
|
||||
/*
|
||||
* We have a an allocated response and message for
|
||||
* each of the entries in the PAM structure - transfer
|
||||
* the data sent to the conversation function over.
|
||||
*/
|
||||
for (i = 0; i < num_msg; i++) {
|
||||
size_t len;
|
||||
|
||||
if (fgets(buf, sizeof(buf), reader) == NULL) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->msg[i]->msg_style =
|
||||
(size_t)strtoul(buf, &endptr, 10);
|
||||
if (endptr == NULL || *endptr != '\n') {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
if (fgets(buf, sizeof(buf), reader) == NULL) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
len = (size_t)strtoul(buf, &endptr, 10);
|
||||
if (endptr == NULL || *endptr != '\n') {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
cookie->msg[i]->msg = malloc(len + 1);
|
||||
if (cookie->msg[i]->msg == NULL) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
if (fread((char *)cookie->msg[i]->msg, len, 1, reader) !=
|
||||
1) {
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
*(char *)&(cookie->msg[i]->msg[len]) = '\0';
|
||||
}
|
||||
break;
|
||||
case STATE_AUTH_OK:
|
||||
case STATE_AUTH_FAIL:
|
||||
break;
|
||||
default:
|
||||
/* Internal failure, somehow */
|
||||
fclose(reader);
|
||||
ipam_free_cookie(cookie);
|
||||
return NULL;
|
||||
}
|
||||
return cookie;
|
||||
} else {
|
||||
/* We are the child */
|
||||
pam_handle_t *pamh=NULL;
|
||||
int retval;
|
||||
char state;
|
||||
|
||||
conv.appdata_ptr = ud;
|
||||
retval = pam_start(service, username, &conv, &pamh);
|
||||
/* Is user really user? */
|
||||
if (retval == PAM_SUCCESS)
|
||||
retval = pam_authenticate(pamh, 0);
|
||||
/* permitted access? */
|
||||
if (retval == PAM_SUCCESS)
|
||||
retval = pam_acct_mgmt(pamh, 0);
|
||||
/* This is where we have been authorized or not. */
|
||||
|
||||
/* Be conservative - flag as auth failure if we can't close */
|
||||
/*
|
||||
* XXX This is based on example code from Linux-PAM -
|
||||
* but can it really be correct to pam_end if
|
||||
* pam_start failed?
|
||||
*/
|
||||
if (pam_end(pamh, retval) != PAM_SUCCESS)
|
||||
retval = PAM_AUTH_ERR;
|
||||
|
||||
/* Message to parent */
|
||||
state = retval == PAM_SUCCESS ? STATE_AUTH_OK : STATE_AUTH_FAIL;
|
||||
if (write(ud->statefd[1], &state, 1) != 1) {
|
||||
_exit(1);
|
||||
}
|
||||
/* FDs will be closed, so further communication will stop */
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do second half of PAM authentication - cookie should now be filled
|
||||
* in with the response to the challenge.
|
||||
*/
|
||||
|
||||
int
|
||||
ipam_complete_auth(struct inverted_pam_cookie *cookie) {
|
||||
int i;
|
||||
char buf[1024];
|
||||
struct inverted_pam_userdata *ud = cookie->userdata;
|
||||
char state;
|
||||
|
||||
/* Send over our responses */
|
||||
for (i = 0; i < cookie->num_msg; i++) {
|
||||
if (cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_ON &&
|
||||
cookie->msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
|
||||
continue;
|
||||
if (write(ud->responsefd[1], buf,
|
||||
sprintf(buf, "%d\n", strlen(cookie->resp[i]->resp))) == -1) {
|
||||
ipam_free_cookie(cookie);
|
||||
return 0;
|
||||
}
|
||||
if (write(ud->responsefd[1], cookie->resp[i]->resp,
|
||||
strlen(cookie->resp[i]->resp)) == -1) {
|
||||
ipam_free_cookie(cookie);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* Find out what state we are changing to */
|
||||
if (read(ud->statefd[0], &state, 1) != 1) {
|
||||
ipam_free_cookie(cookie);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return state == STATE_AUTH_OK ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif /* USE_PAM */
|
38
crypto/openssh/auth-pam.h
Normal file
38
crypto/openssh/auth-pam.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* OpenSSH PAM authentication support.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef AUTH_PAM_H
|
||||
#define AUTH_PAM_H
|
||||
#include "includes.h"
|
||||
#ifdef USE_PAM
|
||||
|
||||
#include <pwd.h> /* For struct passwd */
|
||||
|
||||
void start_pam(struct passwd *pw);
|
||||
void finish_pam(void);
|
||||
int auth_pam_password(struct passwd *pw, const char *password);
|
||||
char **fetch_pam_environment(void);
|
||||
int do_pam_account(char *username, char *remote_user);
|
||||
void do_pam_session(char *username, const char *ttyname);
|
||||
void do_pam_setcred(void);
|
||||
void print_pam_messages(void);
|
||||
int pam_password_change_required(void);
|
||||
void do_pam_chauthtok(void);
|
||||
|
||||
struct inverted_pam_cookie {
|
||||
int state; /* Which state have we reached? */
|
||||
pid_t pid; /* PID of child process */
|
||||
|
||||
/* Only valid in state STATE_CONV */
|
||||
int num_msg; /* Number of messages */
|
||||
struct pam_message **msg; /* Message structures */
|
||||
struct pam_response **resp; /* Response structures */
|
||||
struct inverted_pam_userdata *userdata;
|
||||
};
|
||||
void ipam_free_cookie(struct inverted_pam_cookie *cookie);
|
||||
struct inverted_pam_cookie *ipam_start_auth(const char *, const char *);
|
||||
|
||||
#endif /* USE_PAM */
|
||||
#endif /* AUTH_PAM_H */
|
@ -59,7 +59,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth-passwd.c,v 1.17 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: auth-passwd.c,v 1.18 2000/10/03 18:03:03 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "packet.h"
|
||||
@ -85,7 +85,7 @@ auth_password(struct passwd * pw, const char *password)
|
||||
if (*password == '\0' && options.permit_empty_passwd == 0)
|
||||
return 0;
|
||||
|
||||
#ifdef SKEY
|
||||
#ifdef SKEY_VIA_PASSWD_IS_DISABLED
|
||||
if (options.skey_authentication == 1) {
|
||||
int ret = auth_skey_password(pw, password);
|
||||
if (ret == 1 || ret == 0)
|
||||
|
@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth-rh-rsa.c,v 1.16 2000/09/07 21:13:36 markus Exp $");
|
||||
RCSID("$OpenBSD: auth-rh-rsa.c,v 1.17 2000/10/03 18:03:03 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "packet.h"
|
||||
@ -40,9 +40,9 @@ auth_rhosts_rsa(struct passwd *pw, const char *client_user, RSA *client_host_key
|
||||
HostStatus host_status;
|
||||
Key *client_key, *found;
|
||||
|
||||
debug("Trying rhosts with RSA host authentication for %.100s", client_user);
|
||||
debug("Trying rhosts with RSA host authentication for client user %.100s", client_user);
|
||||
|
||||
if (client_host_key == NULL)
|
||||
if (pw == NULL || client_host_key == NULL)
|
||||
return 0;
|
||||
|
||||
/* Check if we would accept it using rhosts authentication. */
|
||||
|
@ -14,7 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth-rsa.c,v 1.29 2000/09/07 21:13:36 markus Exp $");
|
||||
RCSID("$OpenBSD: auth-rsa.c,v 1.32 2000/10/14 12:19:45 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "rsa.h"
|
||||
@ -30,6 +30,10 @@ RCSID("$FreeBSD$");
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/md5.h>
|
||||
|
||||
|
||||
/* import */
|
||||
extern ServerOptions options;
|
||||
|
||||
/*
|
||||
* Session identifier that is used to bind key exchange and authentication
|
||||
* responses to a particular session.
|
||||
@ -117,7 +121,6 @@ auth_rsa_challenge_dialog(RSA *pk)
|
||||
int
|
||||
auth_rsa(struct passwd *pw, BIGNUM *client_n)
|
||||
{
|
||||
extern ServerOptions options;
|
||||
char line[8192], file[1024];
|
||||
int authenticated;
|
||||
unsigned int bits;
|
||||
@ -126,6 +129,10 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
|
||||
struct stat st;
|
||||
RSA *pk;
|
||||
|
||||
/* no user given */
|
||||
if (pw == NULL)
|
||||
return 0;
|
||||
|
||||
/* Temporarily use the user's uid. */
|
||||
temporarily_use_uid(pw->pw_uid);
|
||||
|
||||
@ -225,6 +232,12 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
|
||||
}
|
||||
} else
|
||||
options = NULL;
|
||||
/*
|
||||
* If our options do not allow this key to be used,
|
||||
* do not send challenge.
|
||||
*/
|
||||
if (!auth_parse_options(pw, options, linenum))
|
||||
continue;
|
||||
|
||||
/* Parse the key from the line. */
|
||||
if (!auth_rsa_read_key(&cp, &bits, pk->e, pk->n)) {
|
||||
@ -263,9 +276,8 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
|
||||
* Break out of the loop if authentication was successful;
|
||||
* otherwise continue searching.
|
||||
*/
|
||||
authenticated = auth_parse_options(pw, options, linenum);
|
||||
if (authenticated)
|
||||
break;
|
||||
authenticated = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Restore the privileged uid. */
|
||||
@ -278,6 +290,8 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n)
|
||||
|
||||
if (authenticated)
|
||||
packet_send_debug("RSA authentication accepted.");
|
||||
else
|
||||
auth_clear_options();
|
||||
|
||||
/* Return authentication result. */
|
||||
return authenticated;
|
||||
|
@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth-skey.c,v 1.8 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: auth-skey.c,v 1.9 2000/10/19 16:41:13 deraadt Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
@ -47,7 +47,7 @@ auth_skey_password(struct passwd * pw, const char *password)
|
||||
skeyinfo = skey_fake_keyinfo(pw->pw_name);
|
||||
}
|
||||
if (skeyinfo != NULL)
|
||||
packet_send_debug(skeyinfo);
|
||||
packet_send_debug("%s", skeyinfo);
|
||||
/* Try again. */
|
||||
return 0;
|
||||
} else if (opie_haskey(pw->pw_name) == 0 &&
|
||||
|
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth.c,v 1.10 2000/09/07 21:13:36 markus Exp $");
|
||||
RCSID("$OpenBSD: auth.c,v 1.11 2000/10/11 20:27:23 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "xmalloc.h"
|
||||
@ -42,7 +42,6 @@ RCSID("$FreeBSD$");
|
||||
#include "pty.h"
|
||||
#include "packet.h"
|
||||
#include "buffer.h"
|
||||
#include "cipher.h"
|
||||
#include "mpaux.h"
|
||||
#include "servconf.h"
|
||||
#include "compat.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth1.c,v 1.4 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: auth1.c,v 1.6 2000/10/11 20:27:23 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "xmalloc.h"
|
||||
@ -18,13 +18,13 @@ RCSID("$FreeBSD$");
|
||||
#include "ssh.h"
|
||||
#include "packet.h"
|
||||
#include "buffer.h"
|
||||
#include "cipher.h"
|
||||
#include "mpaux.h"
|
||||
#include "servconf.h"
|
||||
#include "compat.h"
|
||||
#include "auth.h"
|
||||
#include "session.h"
|
||||
#include <login_cap.h>
|
||||
#include <security/pam_appl.h>
|
||||
|
||||
#ifdef KRB5
|
||||
extern krb5_context ssh_context;
|
||||
@ -70,78 +70,16 @@ get_authname(int type)
|
||||
}
|
||||
|
||||
/*
|
||||
* The user does not exist or access is denied,
|
||||
* but fake indication that authentication is needed.
|
||||
* read packets and try to authenticate local user 'luser'.
|
||||
* return if authentication is successfull. not that pw == NULL
|
||||
* if the user does not exists or is not allowed to login.
|
||||
* each auth method has to 'fake' authentication for nonexisting
|
||||
* users.
|
||||
*/
|
||||
void
|
||||
do_fake_authloop1(char *user)
|
||||
{
|
||||
int attempt = 0;
|
||||
|
||||
log("Faking authloop for illegal user %.200s from %.200s port %d",
|
||||
user,
|
||||
get_remote_ipaddr(),
|
||||
get_remote_port());
|
||||
|
||||
/* Indicate 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 (attempt = 1;; attempt++) {
|
||||
/* Read a packet. This will not return if the client disconnects. */
|
||||
int plen;
|
||||
int type = packet_read(&plen);
|
||||
#ifdef SKEY
|
||||
unsigned int dlen;
|
||||
char *password, *skeyinfo;
|
||||
password = NULL;
|
||||
/* Try to send a fake s/key challenge. */
|
||||
if (options.skey_authentication == 1 &&
|
||||
(skeyinfo = skey_fake_keyinfo(user)) != NULL) {
|
||||
if (type == SSH_CMSG_AUTH_TIS) {
|
||||
packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
|
||||
packet_put_string(skeyinfo, strlen(skeyinfo));
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
continue;
|
||||
} else if (type == SSH_CMSG_AUTH_PASSWORD &&
|
||||
options.password_authentication &&
|
||||
(password = packet_get_string(&dlen)) != NULL &&
|
||||
dlen == 5 &&
|
||||
strncasecmp(password, "s/key", 5) == 0 ) {
|
||||
packet_send_debug(skeyinfo);
|
||||
}
|
||||
}
|
||||
if (password != NULL)
|
||||
xfree(password);
|
||||
#endif
|
||||
if (attempt > AUTH_FAIL_MAX)
|
||||
packet_disconnect(AUTH_FAIL_MSG, user);
|
||||
|
||||
/*
|
||||
* Send failure. This should be indistinguishable from a
|
||||
* failed authentication.
|
||||
*/
|
||||
packet_start(SSH_SMSG_FAILURE);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
}
|
||||
/* NOTREACHED */
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* read packets and try to authenticate local user *pw.
|
||||
* return if authentication is successfull
|
||||
*/
|
||||
void
|
||||
do_authloop(struct passwd * pw)
|
||||
do_authloop(struct passwd * pw, char *luser)
|
||||
{
|
||||
int authenticated = 0;
|
||||
int attempt = 0;
|
||||
unsigned int bits;
|
||||
RSA *client_host_key;
|
||||
@ -156,15 +94,15 @@ do_authloop(struct passwd * pw)
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_cap_t *lc;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifdef USE_PAM
|
||||
struct inverted_pam_cookie *pam_cookie;
|
||||
#endif /* USE_PAM */
|
||||
#if defined(HAVE_LOGIN_CAP) || defined(LOGIN_ACCESS)
|
||||
const char *from_host, *from_ip;
|
||||
|
||||
from_host = get_canonical_hostname();
|
||||
from_ip = get_remote_ipaddr();
|
||||
#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
|
||||
#ifdef HAVE_LIBPAM
|
||||
int pam_retval;
|
||||
#endif /* HAVE_LIBPAM */
|
||||
#if 0
|
||||
#ifdef KRB5
|
||||
{
|
||||
@ -184,8 +122,12 @@ do_authloop(struct passwd * pw)
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
|
||||
client_user = NULL;
|
||||
|
||||
for (attempt = 1;; attempt++) {
|
||||
int authenticated = 0;
|
||||
/* default to fail */
|
||||
authenticated = 0;
|
||||
|
||||
strlcpy(user, "", sizeof user);
|
||||
|
||||
/* Get a packet from the client. */
|
||||
@ -204,14 +146,13 @@ do_authloop(struct passwd * pw)
|
||||
char *tgt = packet_get_string(&dlen);
|
||||
packet_integrity_check(plen, 4 + dlen, type);
|
||||
if (!auth_krb4_tgt(pw, tgt))
|
||||
verbose("Kerberos v4 tgt REFUSED for %s", pw->pw_name);
|
||||
verbose("Kerberos v4 tgt REFUSED for %s", luser);
|
||||
xfree(tgt);
|
||||
}
|
||||
continue;
|
||||
|
||||
case SSH_CMSG_HAVE_AFS_TOKEN:
|
||||
if (!options.afs_token_passing || !k_hasafs()) {
|
||||
/* packet_get_all(); */
|
||||
verbose("AFS token passing disabled.");
|
||||
break;
|
||||
} else {
|
||||
@ -219,7 +160,7 @@ do_authloop(struct passwd * pw)
|
||||
char *token_string = packet_get_string(&dlen);
|
||||
packet_integrity_check(plen, 4 + dlen, type);
|
||||
if (!auth_afs_token(pw, token_string))
|
||||
verbose("AFS token REFUSED for %s", pw->pw_name);
|
||||
verbose("AFS token REFUSED for %.100s", luser);
|
||||
xfree(token_string);
|
||||
}
|
||||
continue;
|
||||
@ -241,11 +182,12 @@ do_authloop(struct passwd * pw)
|
||||
memcpy(auth.dat, kdata, auth.length);
|
||||
xfree(kdata);
|
||||
|
||||
authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
|
||||
|
||||
if (authenticated) {
|
||||
snprintf(user, sizeof user, " tktuser %s", tkt_user);
|
||||
xfree(tkt_user);
|
||||
if (pw != NULL) {
|
||||
authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
|
||||
if (authenticated) {
|
||||
snprintf(user, sizeof user, " tktuser %s", tkt_user);
|
||||
xfree(tkt_user);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -267,13 +209,13 @@ do_authloop(struct passwd * pw)
|
||||
|
||||
k5data.data = packet_get_string(&k5data.length);
|
||||
packet_integrity_check(plen, 4 + k5data.length, type);
|
||||
if (auth_krb5(pw->pw_name, &k5data, &tkt_client)) {
|
||||
/* pw->name is passed just for logging purposes
|
||||
if (auth_krb5(luser, &k5data, &tkt_client)) {
|
||||
/* "luser" is passed just for logging purposes
|
||||
* */
|
||||
/* authorize client against .k5login */
|
||||
if (krb5_kuserok(ssh_context,
|
||||
tkt_client,
|
||||
pw->pw_name))
|
||||
luser))
|
||||
authenticated = 1;
|
||||
}
|
||||
xfree(k5data.data);
|
||||
@ -295,12 +237,10 @@ do_authloop(struct passwd * pw)
|
||||
client_user = packet_get_string(&ulen);
|
||||
packet_integrity_check(plen, 4 + ulen, type);
|
||||
|
||||
/* Try to authenticate using /etc/hosts.equiv and
|
||||
.rhosts. */
|
||||
/* Try to authenticate using /etc/hosts.equiv and .rhosts. */
|
||||
authenticated = auth_rhosts(pw, client_user);
|
||||
|
||||
snprintf(user, sizeof user, " ruser %s", client_user);
|
||||
xfree(client_user);
|
||||
break;
|
||||
|
||||
case SSH_CMSG_AUTH_RHOSTS_RSA:
|
||||
@ -328,7 +268,7 @@ do_authloop(struct passwd * pw)
|
||||
packet_get_bignum(client_host_key->n, &nlen);
|
||||
|
||||
if (bits != BN_num_bits(client_host_key->n))
|
||||
log("Warning: keysize mismatch for client_host_key: "
|
||||
verbose("Warning: keysize mismatch for client_host_key: "
|
||||
"actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
|
||||
packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
|
||||
|
||||
@ -336,7 +276,6 @@ do_authloop(struct passwd * pw)
|
||||
RSA_free(client_host_key);
|
||||
|
||||
snprintf(user, sizeof user, " ruser %s", client_user);
|
||||
xfree(client_user);
|
||||
break;
|
||||
|
||||
case SSH_CMSG_AUTH_RSA:
|
||||
@ -365,27 +304,66 @@ do_authloop(struct passwd * pw)
|
||||
password = packet_get_string(&dlen);
|
||||
packet_integrity_check(plen, 4 + dlen, type);
|
||||
|
||||
/* Try authentication with the password. */
|
||||
#ifdef USE_PAM
|
||||
/* Do PAM auth with password */
|
||||
authenticated = auth_pam_password(pw, password);
|
||||
#else /* !USE_PAM */
|
||||
/* Try authentication with the password. */
|
||||
authenticated = auth_password(pw, password);
|
||||
#endif /* USE_PAM */
|
||||
|
||||
memset(password, 0, strlen(password));
|
||||
xfree(password);
|
||||
break;
|
||||
|
||||
#ifdef SKEY
|
||||
#ifdef USE_PAM
|
||||
case SSH_CMSG_AUTH_TIS:
|
||||
debug("rcvd SSH_CMSG_AUTH_TIS: Trying PAM");
|
||||
pam_cookie = ipam_start_auth("csshd", pw->pw_name);
|
||||
/* We now have data available to send as a challenge */
|
||||
if (pam_cookie->num_msg != 1 ||
|
||||
(pam_cookie->msg[0]->msg_style != PAM_PROMPT_ECHO_OFF &&
|
||||
pam_cookie->msg[0]->msg_style != PAM_PROMPT_ECHO_ON)) {
|
||||
/* We got several challenges or an unknown challenge type */
|
||||
ipam_free_cookie(pam_cookie);
|
||||
pam_cookie = NULL;
|
||||
break;
|
||||
}
|
||||
packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
|
||||
packet_put_string(pam_cookie->msg[0]->msg, strlen(pam_cookie->msg[0]->msg));
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
continue;
|
||||
case SSH_CMSG_AUTH_TIS_RESPONSE:
|
||||
debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
|
||||
if (pam_cookie == NULL)
|
||||
break;
|
||||
{
|
||||
char *response = packet_get_string(&dlen);
|
||||
|
||||
packet_integrity_check(plen, 4 + dlen, type);
|
||||
pam_cookie->resp[0]->resp = strdup(response);
|
||||
xfree(response);
|
||||
authenticated = ipam_complete_auth(pam_cookie);
|
||||
ipam_free_cookie(pam_cookie);
|
||||
pam_cookie = NULL;
|
||||
}
|
||||
break;
|
||||
#elif defined(SKEY)
|
||||
case SSH_CMSG_AUTH_TIS:
|
||||
debug("rcvd SSH_CMSG_AUTH_TIS");
|
||||
if (options.skey_authentication == 1) {
|
||||
char *skeyinfo = opie_keyinfo(pw->pw_name);
|
||||
char *skeyinfo = pw ? opie_keyinfo(pw->pw_name) :
|
||||
NULL;
|
||||
if (skeyinfo == NULL) {
|
||||
debug("generating fake skeyinfo for %.100s.", pw->pw_name);
|
||||
skeyinfo = skey_fake_keyinfo(pw->pw_name);
|
||||
debug("generating fake skeyinfo for %.100s.", luser);
|
||||
skeyinfo = skey_fake_keyinfo(luser);
|
||||
}
|
||||
if (skeyinfo != NULL) {
|
||||
/* we send our s/key- in tis-challenge messages */
|
||||
debug("sending challenge '%s'", skeyinfo);
|
||||
packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
|
||||
packet_put_string(skeyinfo, strlen(skeyinfo));
|
||||
packet_put_cstring(skeyinfo);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
continue;
|
||||
@ -398,8 +376,9 @@ do_authloop(struct passwd * pw)
|
||||
char *response = packet_get_string(&dlen);
|
||||
debug("skey response == '%s'", response);
|
||||
packet_integrity_check(plen, 4 + dlen, type);
|
||||
authenticated = (opie_haskey(pw->pw_name) == 0 &&
|
||||
opie_passverify(pw->pw_name, response) != -1);
|
||||
authenticated = (pw != NULL &&
|
||||
opie_haskey(pw->pw_name) == 0 &&
|
||||
opie_passverify(pw->pw_name, response) != -1);
|
||||
xfree(response);
|
||||
}
|
||||
break;
|
||||
@ -425,8 +404,8 @@ do_authloop(struct passwd * pw)
|
||||
krb5_data tgt;
|
||||
tgt.data = packet_get_string(&tgt.length);
|
||||
|
||||
if (!auth_krb5_tgt(pw->pw_name, &tgt, tkt_client))
|
||||
verbose ("Kerberos V5 TGT refused for %.100s", pw->pw_name);
|
||||
if (!auth_krb5_tgt(luser, &tgt, tkt_client))
|
||||
verbose ("Kerberos V5 TGT refused for %.100s", luser);
|
||||
xfree(tgt.data);
|
||||
|
||||
break;
|
||||
@ -441,13 +420,15 @@ do_authloop(struct passwd * pw)
|
||||
log("Unknown message during authentication: type %d", type);
|
||||
break;
|
||||
}
|
||||
if (authenticated && pw == NULL)
|
||||
fatal("internal error: authenticated for pw == NULL");
|
||||
|
||||
/*
|
||||
* Check if the user is logging in as root and root logins
|
||||
* are disallowed.
|
||||
* Note that root login is allowed for forced commands.
|
||||
*/
|
||||
if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) {
|
||||
if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
|
||||
if (forced_command) {
|
||||
log("Root login accepted for forced command.");
|
||||
} else {
|
||||
@ -458,30 +439,33 @@ do_authloop(struct passwd * pw)
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
lc = login_getpwclass(pw);
|
||||
if (lc == NULL)
|
||||
lc = login_getclassbyname(NULL, pw);
|
||||
if (!auth_hostok(lc, from_host, from_ip)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
if (pw != NULL) {
|
||||
lc = login_getpwclass(pw);
|
||||
if (lc == NULL)
|
||||
lc = login_getclassbyname(NULL, pw);
|
||||
if (!auth_hostok(lc, from_host, from_ip)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
pw->pw_name, from_host, from_ip);
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
}
|
||||
if (!auth_timeok(lc, time(NULL))) {
|
||||
log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
}
|
||||
if (!auth_timeok(lc, time(NULL))) {
|
||||
log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
|
||||
pw->pw_name, from_host);
|
||||
packet_disconnect("Logins not available right now.");
|
||||
packet_disconnect("Logins not available right now.");
|
||||
}
|
||||
login_close(lc);
|
||||
lc = NULL;
|
||||
}
|
||||
login_close(lc);
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifdef LOGIN_ACCESS
|
||||
if (!login_access(pw->pw_name, from_host)) {
|
||||
if (pw != NULL && !login_access(pw->pw_name, from_host)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
pw->pw_name, from_host, from_ip);
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
}
|
||||
#endif /* LOGIN_ACCESS */
|
||||
|
||||
if (pw->pw_uid == 0)
|
||||
if (pw != NULL && pw->pw_uid == 0)
|
||||
log("ROOT LOGIN as '%.100s' from %.100s",
|
||||
pw->pw_name, get_canonical_hostname());
|
||||
|
||||
@ -491,10 +475,11 @@ do_authloop(struct passwd * pw)
|
||||
type == SSH_CMSG_AUTH_PASSWORD)
|
||||
authlog = log;
|
||||
|
||||
authlog("%s %s for %.200s from %.200s port %d%s",
|
||||
authlog("%s %s for %s%.100s from %.200s port %d%s",
|
||||
authenticated ? "Accepted" : "Failed",
|
||||
get_authname(type),
|
||||
pw->pw_uid == 0 ? "ROOT" : pw->pw_name,
|
||||
pw ? "" : "illegal user ",
|
||||
pw && pw->pw_uid == 0 ? "ROOT" : luser,
|
||||
get_remote_ipaddr(),
|
||||
get_remote_port(),
|
||||
user);
|
||||
@ -502,8 +487,18 @@ do_authloop(struct passwd * pw)
|
||||
if (authenticated)
|
||||
return;
|
||||
|
||||
#ifdef USE_PAM
|
||||
if (authenticated && !do_pam_account(pw->pw_name, client_user))
|
||||
authenticated = 0;
|
||||
#endif
|
||||
|
||||
if (client_user != NULL) {
|
||||
xfree(client_user);
|
||||
client_user = NULL;
|
||||
}
|
||||
|
||||
if (attempt > AUTH_FAIL_MAX)
|
||||
packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
|
||||
packet_disconnect(AUTH_FAIL_MSG, luser);
|
||||
|
||||
/* Send a message indicating that the authentication attempt failed. */
|
||||
packet_start(SSH_SMSG_FAILURE);
|
||||
@ -543,32 +538,35 @@ do_authentication()
|
||||
|
||||
/* Verify that the user is a valid user. */
|
||||
pw = getpwnam(user);
|
||||
if (!pw || !allowed_user(pw))
|
||||
do_fake_authloop1(user);
|
||||
xfree(user);
|
||||
|
||||
/* Take a copy of the returned structure. */
|
||||
memset(&pwcopy, 0, sizeof(pwcopy));
|
||||
pwcopy.pw_name = xstrdup(pw->pw_name);
|
||||
pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
|
||||
pwcopy.pw_uid = pw->pw_uid;
|
||||
pwcopy.pw_gid = pw->pw_gid;
|
||||
pwcopy.pw_class = xstrdup(pw->pw_class);
|
||||
pwcopy.pw_dir = xstrdup(pw->pw_dir);
|
||||
pwcopy.pw_shell = xstrdup(pw->pw_shell);
|
||||
pwcopy.pw_class = xstrdup(pw->pw_class);
|
||||
pwcopy.pw_expire = pw->pw_expire;
|
||||
pwcopy.pw_change = pw->pw_change;
|
||||
pw = &pwcopy;
|
||||
if (pw && allowed_user(pw)) {
|
||||
/* Take a copy of the returned structure. */
|
||||
memset(&pwcopy, 0, sizeof(pwcopy));
|
||||
pwcopy.pw_name = xstrdup(pw->pw_name);
|
||||
pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
|
||||
pwcopy.pw_uid = pw->pw_uid;
|
||||
pwcopy.pw_gid = pw->pw_gid;
|
||||
pwcopy.pw_class = xstrdup(pw->pw_class);
|
||||
pwcopy.pw_dir = xstrdup(pw->pw_dir);
|
||||
pwcopy.pw_shell = xstrdup(pw->pw_shell);
|
||||
pwcopy.pw_expire = pw->pw_expire;
|
||||
pwcopy.pw_change = pw->pw_change;
|
||||
pw = &pwcopy;
|
||||
} else {
|
||||
pw = NULL;
|
||||
}
|
||||
|
||||
#ifdef USE_PAM
|
||||
if (pw != NULL)
|
||||
start_pam(pw);
|
||||
#endif
|
||||
/*
|
||||
* If we are not running as root, the user must have the same uid as
|
||||
* the server.
|
||||
*/
|
||||
if (getuid() != 0 && pw->pw_uid != getuid())
|
||||
if (getuid() != 0 && pw && pw->pw_uid != getuid())
|
||||
packet_disconnect("Cannot change user when server not running as root.");
|
||||
|
||||
debug("Attempting authentication for %.100s.", pw->pw_name);
|
||||
debug("Attempting authentication for %s%.100s.", pw ? "" : "illegal user ", user);
|
||||
|
||||
/* If the user has no password, accept authentication immediately. */
|
||||
if (options.password_authentication &&
|
||||
@ -578,16 +576,23 @@ do_authentication()
|
||||
#ifdef KRB4
|
||||
(!options.krb4_authentication || options.krb4_or_local_passwd) &&
|
||||
#endif /* KRB4 */
|
||||
auth_password(pw, "")) {
|
||||
#ifdef USE_PAM
|
||||
auth_pam_password(pw, "")
|
||||
#else /* !USE_PAM */
|
||||
auth_password(pw, "")
|
||||
#endif /* USE_PAM */
|
||||
) {
|
||||
/* Authentication with empty password succeeded. */
|
||||
log("Login for user %s from %.100s, accepted without authentication.",
|
||||
pw->pw_name, get_remote_ipaddr());
|
||||
user, get_remote_ipaddr());
|
||||
} else {
|
||||
/* Loop until the user has been authenticated or the
|
||||
connection is closed, do_authloop() returns only if
|
||||
authentication is successfull */
|
||||
do_authloop(pw);
|
||||
do_authloop(pw, user);
|
||||
}
|
||||
if (pw == NULL)
|
||||
fatal("internal error, authentication successfull for user '%.100s'", user);
|
||||
|
||||
/* The user has been authenticated and accepted. */
|
||||
packet_start(SSH_SMSG_SUCCESS);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "includes.h"
|
||||
RCSID("$FreeBSD$");
|
||||
RCSID("$OpenBSD: auth2-skey.c,v 1.1 2000/10/11 20:14:38 markus Exp $");
|
||||
|
||||
#include "ssh.h"
|
||||
@ -27,8 +28,8 @@ void
|
||||
send_userauth_into_request(Authctxt *authctxt, int echo)
|
||||
{
|
||||
int retval = -1;
|
||||
struct skey skey;
|
||||
char challenge[SKEY_MAX_CHALLENGE];
|
||||
struct opie skey;
|
||||
char challenge[OPIE_CHALLENGE_MAX + 1];
|
||||
char *fake;
|
||||
|
||||
if (authctxt->user == NULL)
|
||||
@ -36,7 +37,7 @@ send_userauth_into_request(Authctxt *authctxt, int echo)
|
||||
|
||||
/* get skey challenge */
|
||||
if (authctxt->valid)
|
||||
retval = skeychallenge(&skey, authctxt->user, challenge);
|
||||
retval = opiechallenge(&skey, authctxt->user, challenge);
|
||||
|
||||
if (retval == -1) {
|
||||
fake = skey_fake_keyinfo(authctxt->user);
|
||||
@ -87,8 +88,8 @@ input_userauth_info_response(int type, int plen, void *ctxt)
|
||||
} else {
|
||||
/* verify skey response */
|
||||
if (authctxt->valid &&
|
||||
skey_haskey(authctxt->pw->pw_name) == 0 &&
|
||||
skey_passcheck(authctxt->pw->pw_name, resp) != -1) {
|
||||
opie_haskey(authctxt->pw->pw_name) == 0 &&
|
||||
opie_passverify(authctxt->pw->pw_name, resp) != -1) {
|
||||
authenticated = 1;
|
||||
} else {
|
||||
authenticated = 0;
|
||||
|
@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: auth2.c,v 1.14 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: auth2.c,v 1.20 2000/10/14 12:16:56 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <openssl/dsa.h>
|
||||
@ -36,7 +36,6 @@ RCSID("$FreeBSD$");
|
||||
#include "pty.h"
|
||||
#include "packet.h"
|
||||
#include "buffer.h"
|
||||
#include "cipher.h"
|
||||
#include "servconf.h"
|
||||
#include "compat.h"
|
||||
#include "channels.h"
|
||||
@ -62,54 +61,77 @@ extern ServerOptions options;
|
||||
extern unsigned char *session_id2;
|
||||
extern int session_id2_len;
|
||||
|
||||
static Authctxt *x_authctxt = NULL;
|
||||
static int one = 1;
|
||||
|
||||
typedef struct Authmethod Authmethod;
|
||||
struct Authmethod {
|
||||
char *name;
|
||||
int (*userauth)(Authctxt *authctxt);
|
||||
int *enabled;
|
||||
};
|
||||
|
||||
/* protocol */
|
||||
|
||||
void input_service_request(int type, int plen);
|
||||
void input_userauth_request(int type, int plen);
|
||||
void protocol_error(int type, int plen);
|
||||
void input_service_request(int type, int plen, void *ctxt);
|
||||
void input_userauth_request(int type, int plen, void *ctxt);
|
||||
void protocol_error(int type, int plen, void *ctxt);
|
||||
|
||||
/* auth */
|
||||
int ssh2_auth_none(struct passwd *pw);
|
||||
int ssh2_auth_password(struct passwd *pw);
|
||||
int ssh2_auth_pubkey(struct passwd *pw, char *service);
|
||||
|
||||
/* helper */
|
||||
struct passwd* auth_set_user(char *u, char *s);
|
||||
Authmethod *authmethod_lookup(const char *name);
|
||||
struct passwd *pwcopy(struct passwd *pw);
|
||||
int user_dsa_key_allowed(struct passwd *pw, Key *key);
|
||||
char *authmethods_get(void);
|
||||
|
||||
typedef struct Authctxt Authctxt;
|
||||
struct Authctxt {
|
||||
char *user;
|
||||
char *service;
|
||||
struct passwd pw;
|
||||
int valid;
|
||||
/* auth */
|
||||
int userauth_none(Authctxt *authctxt);
|
||||
int userauth_passwd(Authctxt *authctxt);
|
||||
int userauth_pubkey(Authctxt *authctxt);
|
||||
int userauth_kbdint(Authctxt *authctxt);
|
||||
|
||||
Authmethod authmethods[] = {
|
||||
{"none",
|
||||
userauth_none,
|
||||
&one},
|
||||
{"publickey",
|
||||
userauth_pubkey,
|
||||
&options.dsa_authentication},
|
||||
{"keyboard-interactive",
|
||||
userauth_kbdint,
|
||||
&options.kbd_interactive_authentication},
|
||||
{"password",
|
||||
userauth_passwd,
|
||||
&options.password_authentication},
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
static Authctxt *authctxt = NULL;
|
||||
static int userauth_success = 0;
|
||||
|
||||
/*
|
||||
* loop until userauth_success == TRUE
|
||||
* loop until authctxt->success == TRUE
|
||||
*/
|
||||
|
||||
void
|
||||
do_authentication2()
|
||||
{
|
||||
/* turn off skey/kerberos, not supported by SSH2 */
|
||||
#ifdef SKEY
|
||||
options.skey_authentication = 0;
|
||||
#endif
|
||||
Authctxt *authctxt = xmalloc(sizeof(*authctxt));
|
||||
memset(authctxt, 'a', sizeof(*authctxt));
|
||||
authctxt->valid = 0;
|
||||
authctxt->attempt = 0;
|
||||
authctxt->success = 0;
|
||||
x_authctxt = authctxt; /*XXX*/
|
||||
|
||||
#ifdef KRB4
|
||||
/* turn off kerberos, not supported by SSH2 */
|
||||
options.krb4_authentication = 0;
|
||||
#endif
|
||||
|
||||
dispatch_init(&protocol_error);
|
||||
dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request);
|
||||
dispatch_run(DISPATCH_BLOCK, &userauth_success);
|
||||
dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt);
|
||||
do_authenticated2();
|
||||
}
|
||||
|
||||
void
|
||||
protocol_error(int type, int plen)
|
||||
protocol_error(int type, int plen, void *ctxt)
|
||||
{
|
||||
log("auth: protocol error: type %d plen %d", type, plen);
|
||||
packet_start(SSH2_MSG_UNIMPLEMENTED);
|
||||
@ -119,15 +141,19 @@ protocol_error(int type, int plen)
|
||||
}
|
||||
|
||||
void
|
||||
input_service_request(int type, int plen)
|
||||
input_service_request(int type, int plen, void *ctxt)
|
||||
{
|
||||
Authctxt *authctxt = ctxt;
|
||||
unsigned int len;
|
||||
int accept = 0;
|
||||
char *service = packet_get_string(&len);
|
||||
packet_done();
|
||||
|
||||
if (authctxt == NULL)
|
||||
fatal("input_service_request: no authctxt");
|
||||
|
||||
if (strcmp(service, "ssh-userauth") == 0) {
|
||||
if (!userauth_success) {
|
||||
if (!authctxt->success) {
|
||||
accept = 1;
|
||||
/* now we can handle user-auth requests */
|
||||
dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request);
|
||||
@ -148,14 +174,12 @@ input_service_request(int type, int plen)
|
||||
}
|
||||
|
||||
void
|
||||
input_userauth_request(int type, int plen)
|
||||
input_userauth_request(int type, int plen, void *ctxt)
|
||||
{
|
||||
static void (*authlog) (const char *fmt,...) = verbose;
|
||||
static int attempt = 0;
|
||||
unsigned int len;
|
||||
Authctxt *authctxt = ctxt;
|
||||
Authmethod *m = NULL;
|
||||
int authenticated = 0;
|
||||
char *user, *service, *method, *authmsg = NULL;
|
||||
struct passwd *pw;
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_cap_t *lc;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
@ -166,62 +190,118 @@ input_userauth_request(int type, int plen)
|
||||
from_ip = get_remote_ipaddr();
|
||||
#endif /* HAVE_LOGIN_CAP || LOGIN_ACCESS */
|
||||
|
||||
if (++attempt == AUTH_FAIL_MAX)
|
||||
if (authctxt == NULL)
|
||||
fatal("input_userauth_request: no authctxt");
|
||||
if (authctxt->attempt++ >= AUTH_FAIL_MAX)
|
||||
packet_disconnect("too many failed userauth_requests");
|
||||
|
||||
user = packet_get_string(&len);
|
||||
service = packet_get_string(&len);
|
||||
method = packet_get_string(&len);
|
||||
user = packet_get_string(NULL);
|
||||
service = packet_get_string(NULL);
|
||||
method = packet_get_string(NULL);
|
||||
debug("userauth-request for user %s service %s method %s", user, service, method);
|
||||
debug("attempt #%d", authctxt->attempt);
|
||||
|
||||
/* XXX we only allow the ssh-connection service */
|
||||
pw = auth_set_user(user, service);
|
||||
if (pw && strcmp(service, "ssh-connection")==0) {
|
||||
if (strcmp(method, "none") == 0) {
|
||||
authenticated = ssh2_auth_none(pw);
|
||||
} else if (strcmp(method, "password") == 0) {
|
||||
authenticated = ssh2_auth_password(pw);
|
||||
} else if (strcmp(method, "publickey") == 0) {
|
||||
authenticated = ssh2_auth_pubkey(pw, service);
|
||||
if (authctxt->attempt == 1) {
|
||||
/* setup auth context */
|
||||
struct passwd *pw = NULL;
|
||||
setproctitle("%s", user);
|
||||
pw = getpwnam(user);
|
||||
if (pw && allowed_user(pw) && strcmp(service, "ssh-connection")==0) {
|
||||
authctxt->pw = pwcopy(pw);
|
||||
authctxt->valid = 1;
|
||||
debug2("input_userauth_request: setting up authctxt for %s", user);
|
||||
#ifdef USE_PAM
|
||||
start_pam(pw);
|
||||
#endif
|
||||
} else {
|
||||
log("input_userauth_request: illegal user %s", user);
|
||||
}
|
||||
authctxt->user = xstrdup(user);
|
||||
authctxt->service = xstrdup(service);
|
||||
} else if (authctxt->valid) {
|
||||
if (strcmp(user, authctxt->user) != 0 ||
|
||||
strcmp(service, authctxt->service) != 0) {
|
||||
log("input_userauth_request: missmatch: (%s,%s)!=(%s,%s)",
|
||||
user, service, authctxt->user, authctxt->service);
|
||||
authctxt->valid = 0;
|
||||
}
|
||||
}
|
||||
if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) {
|
||||
authenticated = 0;
|
||||
log("ROOT LOGIN REFUSED FROM %.200s",
|
||||
get_canonical_hostname());
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
lc = login_getpwclass(pw);
|
||||
if (lc == NULL)
|
||||
lc = login_getclassbyname(NULL, pw);
|
||||
if (!auth_hostok(lc, from_host, from_ip)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
pw->pw_name, from_host, from_ip);
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
if (authctxt->pw != NULL) {
|
||||
lc = login_getpwclass(authctxt->pw);
|
||||
if (lc == NULL)
|
||||
lc = login_getclassbyname(NULL, authctxt->pw);
|
||||
if (!auth_hostok(lc, from_host, from_ip)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
authctxt->pw->pw_name, from_host, from_ip);
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
}
|
||||
if (!auth_timeok(lc, time(NULL))) {
|
||||
log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
|
||||
authctxt->pw->pw_name, from_host);
|
||||
packet_disconnect("Logins not available right now.");
|
||||
}
|
||||
login_close(lc);
|
||||
lc = NULL;
|
||||
}
|
||||
if (!auth_timeok(lc, time(NULL))) {
|
||||
log("LOGIN %.200s REFUSED (TIME) FROM %.200s",
|
||||
pw->pw_name, from_host);
|
||||
packet_disconnect("Logins not available right now.");
|
||||
}
|
||||
login_close(lc);
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifdef LOGIN_ACCESS
|
||||
if (!login_access(pw->pw_name, from_host)) {
|
||||
if (authctxt->pw != NULL &&
|
||||
!login_access(authctxt->pw->pw_name, from_host)) {
|
||||
log("Denied connection for %.200s from %.200s [%.200s].",
|
||||
pw->pw_name, from_host, from_ip);
|
||||
authctxt->pw->pw_name, from_host, from_ip);
|
||||
packet_disconnect("Sorry, you are not allowed to connect.");
|
||||
}
|
||||
#endif /* LOGIN_ACCESS */
|
||||
|
||||
m = authmethod_lookup(method);
|
||||
if (m != NULL) {
|
||||
debug2("input_userauth_request: try method %s", method);
|
||||
authenticated = m->userauth(authctxt);
|
||||
} else {
|
||||
debug2("input_userauth_request: unsupported method %s", method);
|
||||
}
|
||||
if (!authctxt->valid && authenticated == 1) {
|
||||
log("input_userauth_request: INTERNAL ERROR: authenticated invalid user %s service %s", user, method);
|
||||
authenticated = 0;
|
||||
}
|
||||
|
||||
/* Special handling for root */
|
||||
if (authenticated == 1 &&
|
||||
authctxt->valid && authctxt->pw->pw_uid == 0 && !options.permit_root_login) {
|
||||
authenticated = 0;
|
||||
log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname());
|
||||
}
|
||||
|
||||
#ifdef USE_PAM
|
||||
if (authenticated && authctxt->user && !do_pam_account(authctxt->user, NULL))
|
||||
authenticated = 0;
|
||||
#endif /* USE_PAM */
|
||||
|
||||
/* Log before sending the reply */
|
||||
userauth_log(authctxt, authenticated, method);
|
||||
userauth_reply(authctxt, authenticated);
|
||||
|
||||
xfree(service);
|
||||
xfree(user);
|
||||
xfree(method);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
userauth_log(Authctxt *authctxt, int authenticated, char *method)
|
||||
{
|
||||
void (*authlog) (const char *fmt,...) = verbose;
|
||||
char *user = NULL, *authmsg = NULL;
|
||||
|
||||
/* Raise logging level */
|
||||
if (authenticated == 1 ||
|
||||
attempt == AUTH_FAIL_LOG ||
|
||||
!authctxt->valid ||
|
||||
authctxt->attempt >= AUTH_FAIL_LOG ||
|
||||
strcmp(method, "password") == 0)
|
||||
authlog = log;
|
||||
|
||||
/* Log before sending the reply */
|
||||
if (authenticated == 1) {
|
||||
authmsg = "Accepted";
|
||||
} else if (authenticated == 0) {
|
||||
@ -229,13 +309,24 @@ input_userauth_request(int type, int plen)
|
||||
} else {
|
||||
authmsg = "Postponed";
|
||||
}
|
||||
authlog("%s %s for %.200s from %.200s port %d ssh2",
|
||||
authmsg,
|
||||
method,
|
||||
pw && pw->pw_uid == 0 ? "ROOT" : user,
|
||||
get_remote_ipaddr(),
|
||||
get_remote_port());
|
||||
|
||||
if (authctxt->valid) {
|
||||
user = authctxt->pw->pw_uid == 0 ? "ROOT" : authctxt->user;
|
||||
} else {
|
||||
user = "NOUSER";
|
||||
}
|
||||
|
||||
authlog("%s %s for %.200s from %.200s port %d ssh2",
|
||||
authmsg,
|
||||
method,
|
||||
user,
|
||||
get_remote_ipaddr(),
|
||||
get_remote_port());
|
||||
}
|
||||
|
||||
void
|
||||
userauth_reply(Authctxt *authctxt, int authenticated)
|
||||
{
|
||||
/* XXX todo: check if multiple auth methods are needed */
|
||||
if (authenticated == 1) {
|
||||
/* turn off userauth */
|
||||
@ -244,28 +335,37 @@ input_userauth_request(int type, int plen)
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
/* now we can break out */
|
||||
userauth_success = 1;
|
||||
authctxt->success = 1;
|
||||
} else if (authenticated == 0) {
|
||||
char *methods = authmethods_get();
|
||||
packet_start(SSH2_MSG_USERAUTH_FAILURE);
|
||||
packet_put_cstring("publickey,password"); /* XXX dynamic */
|
||||
packet_put_char(0); /* XXX partial success, unused */
|
||||
packet_put_cstring(methods);
|
||||
packet_put_char(0); /* XXX partial success, unused */
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
xfree(methods);
|
||||
} else {
|
||||
/* do nothing, we did already send a reply */
|
||||
}
|
||||
|
||||
xfree(service);
|
||||
xfree(user);
|
||||
xfree(method);
|
||||
}
|
||||
|
||||
int
|
||||
ssh2_auth_none(struct passwd *pw)
|
||||
userauth_none(Authctxt *authctxt)
|
||||
{
|
||||
/* disable method "none", only allowed one time */
|
||||
Authmethod *m = authmethod_lookup("none");
|
||||
if (m != NULL)
|
||||
m->enabled = NULL;
|
||||
packet_done();
|
||||
return auth_password(pw, "");
|
||||
#ifdef USE_PAM
|
||||
return authctxt->valid ? auth_pam_password(authctxt->pw, "") : 0;
|
||||
#else /* !USE_PAM */
|
||||
return authctxt->valid ? auth_password(authctxt->pw, "") : 0;
|
||||
#endif /* USE_PAM */
|
||||
}
|
||||
|
||||
int
|
||||
ssh2_auth_password(struct passwd *pw)
|
||||
userauth_passwd(Authctxt *authctxt)
|
||||
{
|
||||
char *password;
|
||||
int authenticated = 0;
|
||||
@ -276,15 +376,43 @@ ssh2_auth_password(struct passwd *pw)
|
||||
log("password change not supported");
|
||||
password = packet_get_string(&len);
|
||||
packet_done();
|
||||
if (options.password_authentication &&
|
||||
auth_password(pw, password) == 1)
|
||||
if (authctxt->valid &&
|
||||
#ifdef USE_PAM
|
||||
auth_pam_password(authctxt->pw, password) == 1
|
||||
#else
|
||||
auth_password(authctxt->pw, password) == 1
|
||||
#endif
|
||||
)
|
||||
authenticated = 1;
|
||||
memset(password, 0, len);
|
||||
xfree(password);
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
int
|
||||
ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
userauth_kbdint(Authctxt *authctxt)
|
||||
{
|
||||
int authenticated = 0;
|
||||
char *lang = NULL;
|
||||
char *devs = NULL;
|
||||
|
||||
lang = packet_get_string(NULL);
|
||||
devs = packet_get_string(NULL);
|
||||
packet_done();
|
||||
|
||||
debug("keyboard-interactive language %s devs %s", lang, devs);
|
||||
#ifdef SKEY
|
||||
/* XXX hardcoded, we should look at devs */
|
||||
if (options.skey_authentication != 0)
|
||||
authenticated = auth2_skey(authctxt);
|
||||
#endif
|
||||
xfree(lang);
|
||||
xfree(devs);
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
int
|
||||
userauth_pubkey(Authctxt *authctxt)
|
||||
{
|
||||
Buffer b;
|
||||
Key *key;
|
||||
@ -293,15 +421,15 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
int have_sig;
|
||||
int authenticated = 0;
|
||||
|
||||
if (options.dsa_authentication == 0) {
|
||||
debug("pubkey auth disabled");
|
||||
if (!authctxt->valid) {
|
||||
debug2("userauth_pubkey: disabled because of invalid user");
|
||||
return 0;
|
||||
}
|
||||
have_sig = packet_get_char();
|
||||
pkalg = packet_get_string(&alen);
|
||||
if (strcmp(pkalg, KEX_DSS) != 0) {
|
||||
xfree(pkalg);
|
||||
log("bad pkalg %s", pkalg); /*XXX*/
|
||||
xfree(pkalg);
|
||||
return 0;
|
||||
}
|
||||
pkblob = packet_get_string(&blen);
|
||||
@ -311,18 +439,18 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
sig = packet_get_string(&slen);
|
||||
packet_done();
|
||||
buffer_init(&b);
|
||||
if (datafellows & SSH_COMPAT_SESSIONID_ENCODING) {
|
||||
buffer_put_string(&b, session_id2, session_id2_len);
|
||||
} else {
|
||||
if (datafellows & SSH_OLD_SESSIONID) {
|
||||
buffer_append(&b, session_id2, session_id2_len);
|
||||
} else {
|
||||
buffer_put_string(&b, session_id2, session_id2_len);
|
||||
}
|
||||
/* reconstruct packet */
|
||||
buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST);
|
||||
buffer_put_cstring(&b, pw->pw_name);
|
||||
buffer_put_cstring(&b, authctxt->user);
|
||||
buffer_put_cstring(&b,
|
||||
datafellows & SSH_BUG_PUBKEYAUTH ?
|
||||
"ssh-userauth" :
|
||||
service);
|
||||
authctxt->service);
|
||||
buffer_put_cstring(&b, "publickey");
|
||||
buffer_put_char(&b, have_sig);
|
||||
buffer_put_cstring(&b, KEX_DSS);
|
||||
@ -331,15 +459,15 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
buffer_dump(&b);
|
||||
#endif
|
||||
/* test for correct signature */
|
||||
if (user_dsa_key_allowed(pw, key) &&
|
||||
if (user_dsa_key_allowed(authctxt->pw, key) &&
|
||||
dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1)
|
||||
authenticated = 1;
|
||||
buffer_clear(&b);
|
||||
xfree(sig);
|
||||
} else {
|
||||
debug("test whether pkalg/pkblob are acceptable");
|
||||
packet_done();
|
||||
debug("test key...");
|
||||
/* test whether pkalg/pkblob are acceptable */
|
||||
|
||||
/* XXX fake reply and always send PK_OK ? */
|
||||
/*
|
||||
* XXX this allows testing whether a user is allowed
|
||||
@ -348,7 +476,7 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
* if a user is not allowed to login. is this an
|
||||
* issue? -markus
|
||||
*/
|
||||
if (user_dsa_key_allowed(pw, key)) {
|
||||
if (user_dsa_key_allowed(authctxt->pw, key)) {
|
||||
packet_start(SSH2_MSG_USERAUTH_PK_OK);
|
||||
packet_put_string(pkalg, alen);
|
||||
packet_put_string(pkblob, blen);
|
||||
@ -357,6 +485,8 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
authenticated = -1;
|
||||
}
|
||||
}
|
||||
if (authenticated != 1)
|
||||
auth_clear_options();
|
||||
key_free(key);
|
||||
}
|
||||
xfree(pkalg);
|
||||
@ -364,52 +494,60 @@ ssh2_auth_pubkey(struct passwd *pw, char *service)
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
/* set and get current user */
|
||||
/* get current user */
|
||||
|
||||
struct passwd*
|
||||
auth_get_user(void)
|
||||
{
|
||||
return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL;
|
||||
return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL;
|
||||
}
|
||||
|
||||
struct passwd*
|
||||
auth_set_user(char *u, char *s)
|
||||
{
|
||||
struct passwd *pw, *copy;
|
||||
#define DELIM ","
|
||||
|
||||
if (authctxt == NULL) {
|
||||
authctxt = xmalloc(sizeof(*authctxt));
|
||||
authctxt->valid = 0;
|
||||
authctxt->user = xstrdup(u);
|
||||
authctxt->service = xstrdup(s);
|
||||
setproctitle("%s", u);
|
||||
pw = getpwnam(u);
|
||||
if (!pw || !allowed_user(pw)) {
|
||||
log("auth_set_user: illegal user %s", u);
|
||||
return NULL;
|
||||
}
|
||||
copy = &authctxt->pw;
|
||||
memset(copy, 0, sizeof(*copy));
|
||||
copy->pw_name = xstrdup(pw->pw_name);
|
||||
copy->pw_passwd = xstrdup(pw->pw_passwd);
|
||||
copy->pw_uid = pw->pw_uid;
|
||||
copy->pw_gid = pw->pw_gid;
|
||||
copy->pw_class = xstrdup(pw->pw_class);
|
||||
copy->pw_dir = xstrdup(pw->pw_dir);
|
||||
copy->pw_shell = xstrdup(pw->pw_shell);
|
||||
copy->pw_class = xstrdup(pw->pw_class);
|
||||
copy->pw_expire = pw->pw_expire;
|
||||
copy->pw_change = pw->pw_change;
|
||||
authctxt->valid = 1;
|
||||
} else {
|
||||
if (strcmp(u, authctxt->user) != 0 ||
|
||||
strcmp(s, authctxt->service) != 0) {
|
||||
log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)",
|
||||
u, s, authctxt->user, authctxt->service);
|
||||
return NULL;
|
||||
char *
|
||||
authmethods_get(void)
|
||||
{
|
||||
Authmethod *method = NULL;
|
||||
unsigned int size = 0;
|
||||
char *list;
|
||||
|
||||
for (method = authmethods; method->name != NULL; method++) {
|
||||
if (strcmp(method->name, "none") == 0)
|
||||
continue;
|
||||
if (method->enabled != NULL && *(method->enabled) != 0) {
|
||||
if (size != 0)
|
||||
size += strlen(DELIM);
|
||||
size += strlen(method->name);
|
||||
}
|
||||
}
|
||||
return auth_get_user();
|
||||
size++; /* trailing '\0' */
|
||||
list = xmalloc(size);
|
||||
list[0] = '\0';
|
||||
|
||||
for (method = authmethods; method->name != NULL; method++) {
|
||||
if (strcmp(method->name, "none") == 0)
|
||||
continue;
|
||||
if (method->enabled != NULL && *(method->enabled) != 0) {
|
||||
if (list[0] != '\0')
|
||||
strlcat(list, DELIM, size);
|
||||
strlcat(list, method->name, size);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Authmethod *
|
||||
authmethod_lookup(const char *name)
|
||||
{
|
||||
Authmethod *method = NULL;
|
||||
if (name != NULL)
|
||||
for (method = authmethods; method->name != NULL; method++)
|
||||
if (method->enabled != NULL &&
|
||||
*(method->enabled) != 0 &&
|
||||
strcmp(name, method->name) == 0)
|
||||
return method;
|
||||
debug2("Unrecognized authentication method name: %s", name ? name : "NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return 1 if user allows given key */
|
||||
@ -424,6 +562,9 @@ user_dsa_key_allowed(struct passwd *pw, Key *key)
|
||||
struct stat st;
|
||||
Key *found;
|
||||
|
||||
if (pw == NULL)
|
||||
return 0;
|
||||
|
||||
/* Temporarily use the user's uid. */
|
||||
temporarily_use_uid(pw->pw_uid);
|
||||
|
||||
@ -451,8 +592,10 @@ user_dsa_key_allowed(struct passwd *pw, Key *key)
|
||||
if (fstat(fileno(f), &st) < 0 ||
|
||||
(st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
|
||||
(st.st_mode & 022) != 0) {
|
||||
snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: "
|
||||
"bad ownership or modes for '%s'.", pw->pw_name, file);
|
||||
snprintf(buf, sizeof buf,
|
||||
"%s authentication refused for %.100s: "
|
||||
"bad ownership or modes for '%s'.",
|
||||
key_type(key), pw->pw_name, file);
|
||||
fail = 1;
|
||||
} else {
|
||||
/* Check path to SSH_USER_PERMITTED_KEYS */
|
||||
@ -467,9 +610,9 @@ user_dsa_key_allowed(struct passwd *pw, Key *key)
|
||||
(st.st_uid != 0 && st.st_uid != pw->pw_uid) ||
|
||||
(st.st_mode & 022) != 0) {
|
||||
snprintf(buf, sizeof buf,
|
||||
"DSA authentication refused for %.100s: "
|
||||
"%s authentication refused for %.100s: "
|
||||
"bad ownership or modes for '%s'.",
|
||||
pw->pw_name, line);
|
||||
key_type(key), pw->pw_name, line);
|
||||
fail = 1;
|
||||
break;
|
||||
}
|
||||
@ -483,7 +626,7 @@ user_dsa_key_allowed(struct passwd *pw, Key *key)
|
||||
}
|
||||
}
|
||||
found_key = 0;
|
||||
found = key_new(KEY_DSA);
|
||||
found = key_new(key->type);
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
char *cp, *options = NULL;
|
||||
@ -527,3 +670,20 @@ user_dsa_key_allowed(struct passwd *pw, Key *key)
|
||||
key_free(found);
|
||||
return found_key;
|
||||
}
|
||||
|
||||
struct passwd *
|
||||
pwcopy(struct passwd *pw)
|
||||
{
|
||||
struct passwd *copy = xmalloc(sizeof(*copy));
|
||||
memset(copy, 0, sizeof(*copy));
|
||||
copy->pw_name = xstrdup(pw->pw_name);
|
||||
copy->pw_passwd = xstrdup(pw->pw_passwd);
|
||||
copy->pw_uid = pw->pw_uid;
|
||||
copy->pw_gid = pw->pw_gid;
|
||||
copy->pw_class = xstrdup(pw->pw_class);
|
||||
copy->pw_dir = xstrdup(pw->pw_dir);
|
||||
copy->pw_shell = xstrdup(pw->pw_shell);
|
||||
copy->pw_expire = pw->pw_expire;
|
||||
copy->pw_change = pw->pw_change;
|
||||
return copy;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: authfd.c,v 1.27 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: authfd.c,v 1.29 2000/10/09 21:51:00 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "ssh.h"
|
||||
@ -52,10 +52,15 @@ RCSID("$FreeBSD$");
|
||||
#include "authfd.h"
|
||||
#include "kex.h"
|
||||
#include "dsa.h"
|
||||
#include "compat.h"
|
||||
|
||||
/* helper */
|
||||
int decode_reply(int type);
|
||||
|
||||
/* macro to check for "agent failure" message */
|
||||
#define agent_failed(x) \
|
||||
((x == SSH_AGENT_FAILURE) || (x == SSH_COM_AGENT2_FAILURE))
|
||||
|
||||
/* Returns the number of the authentication fd, or -1 if there is none. */
|
||||
|
||||
int
|
||||
@ -238,7 +243,7 @@ ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int versi
|
||||
|
||||
/* Get message type, and verify that we got a proper answer. */
|
||||
type = buffer_get_char(&auth->identities);
|
||||
if (type == SSH_AGENT_FAILURE) {
|
||||
if (agent_failed(type)) {
|
||||
return NULL;
|
||||
} else if (type != code2) {
|
||||
fatal("Bad authentication reply message type: %d", type);
|
||||
@ -337,7 +342,7 @@ ssh_decrypt_challenge(AuthenticationConnection *auth,
|
||||
}
|
||||
type = buffer_get_char(&buffer);
|
||||
|
||||
if (type == SSH_AGENT_FAILURE) {
|
||||
if (agent_failed(type)) {
|
||||
log("Agent admitted failure to authenticate using the key.");
|
||||
} else if (type != SSH_AGENT_RSA_RESPONSE) {
|
||||
fatal("Bad authentication response: %d", type);
|
||||
@ -361,20 +366,24 @@ ssh_agent_sign(AuthenticationConnection *auth,
|
||||
unsigned char **sigp, int *lenp,
|
||||
unsigned char *data, int datalen)
|
||||
{
|
||||
extern int datafellows;
|
||||
Buffer msg;
|
||||
unsigned char *blob;
|
||||
unsigned int blen;
|
||||
int type;
|
||||
int type, flags = 0;
|
||||
int ret = -1;
|
||||
|
||||
if (dsa_make_key_blob(key, &blob, &blen) == 0)
|
||||
return -1;
|
||||
|
||||
if (datafellows & SSH_BUG_SIGBLOB)
|
||||
flags = SSH_AGENT_OLD_SIGNATURE;
|
||||
|
||||
buffer_init(&msg);
|
||||
buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
|
||||
buffer_put_string(&msg, blob, blen);
|
||||
buffer_put_string(&msg, data, datalen);
|
||||
buffer_put_int(&msg, 0); /* flags, unused */
|
||||
buffer_put_int(&msg, flags);
|
||||
xfree(blob);
|
||||
|
||||
if (ssh_request_reply(auth, &msg, &msg) == 0) {
|
||||
@ -382,7 +391,7 @@ ssh_agent_sign(AuthenticationConnection *auth,
|
||||
return -1;
|
||||
}
|
||||
type = buffer_get_char(&msg);
|
||||
if (type == SSH_AGENT_FAILURE) {
|
||||
if (agent_failed(type)) {
|
||||
log("Agent admitted failure to sign using the key.");
|
||||
} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
|
||||
fatal("Bad authentication response: %d", type);
|
||||
@ -529,6 +538,7 @@ decode_reply(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case SSH_AGENT_FAILURE:
|
||||
case SSH_COM_AGENT2_FAILURE:
|
||||
log("SSH_AGENT_FAILURE");
|
||||
return 0;
|
||||
case SSH_AGENT_SUCCESS:
|
||||
|
@ -36,7 +36,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: authfile.c,v 1.19 2000/09/07 20:27:49 deraadt Exp $");
|
||||
RCSID("$OpenBSD: authfile.c,v 1.20 2000/10/11 20:27:23 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <openssl/bn.h>
|
||||
@ -48,7 +48,6 @@ RCSID("$FreeBSD$");
|
||||
#include "xmalloc.h"
|
||||
#include "buffer.h"
|
||||
#include "bufaux.h"
|
||||
#include "cipher.h"
|
||||
#include "ssh.h"
|
||||
#include "key.h"
|
||||
|
||||
@ -69,8 +68,8 @@ save_private_key_rsa(const char *filename, const char *passphrase,
|
||||
Buffer buffer, encrypted;
|
||||
char buf[100], *cp;
|
||||
int fd, i;
|
||||
CipherContext cipher;
|
||||
int cipher_type;
|
||||
CipherContext ciphercontext;
|
||||
Cipher *cipher;
|
||||
u_int32_t rand;
|
||||
|
||||
/*
|
||||
@ -78,9 +77,11 @@ save_private_key_rsa(const char *filename, const char *passphrase,
|
||||
* to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
|
||||
*/
|
||||
if (strcmp(passphrase, "") == 0)
|
||||
cipher_type = SSH_CIPHER_NONE;
|
||||
cipher = cipher_by_number(SSH_CIPHER_NONE);
|
||||
else
|
||||
cipher_type = SSH_AUTHFILE_CIPHER;
|
||||
cipher = cipher_by_number(SSH_AUTHFILE_CIPHER);
|
||||
if (cipher == NULL)
|
||||
fatal("save_private_key_rsa: bad cipher");
|
||||
|
||||
/* This buffer is used to built the secret part of the private key. */
|
||||
buffer_init(&buffer);
|
||||
@ -117,7 +118,7 @@ save_private_key_rsa(const char *filename, const char *passphrase,
|
||||
buffer_put_char(&encrypted, 0);
|
||||
|
||||
/* Store cipher type. */
|
||||
buffer_put_char(&encrypted, cipher_type);
|
||||
buffer_put_char(&encrypted, cipher->number);
|
||||
buffer_put_int(&encrypted, 0); /* For future extension */
|
||||
|
||||
/* Store public key. This will be in plain text. */
|
||||
@ -129,11 +130,10 @@ save_private_key_rsa(const char *filename, const char *passphrase,
|
||||
/* Allocate space for the private part of the key in the buffer. */
|
||||
buffer_append_space(&encrypted, &cp, buffer_len(&buffer));
|
||||
|
||||
cipher_set_key_string(&cipher, cipher_type, passphrase);
|
||||
cipher_encrypt(&cipher, (unsigned char *) cp,
|
||||
(unsigned char *) buffer_ptr(&buffer),
|
||||
buffer_len(&buffer));
|
||||
memset(&cipher, 0, sizeof(cipher));
|
||||
cipher_set_key_string(&ciphercontext, cipher, passphrase);
|
||||
cipher_encrypt(&ciphercontext, (unsigned char *) cp,
|
||||
(unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer));
|
||||
memset(&ciphercontext, 0, sizeof(ciphercontext));
|
||||
|
||||
/* Destroy temporary data. */
|
||||
memset(buf, 0, sizeof(buf));
|
||||
@ -314,7 +314,8 @@ load_private_key_rsa(int fd, const char *filename,
|
||||
off_t len;
|
||||
Buffer buffer, decrypted;
|
||||
char *cp;
|
||||
CipherContext cipher;
|
||||
CipherContext ciphercontext;
|
||||
Cipher *cipher;
|
||||
BN_CTX *ctx;
|
||||
BIGNUM *aux;
|
||||
|
||||
@ -365,10 +366,10 @@ load_private_key_rsa(int fd, const char *filename,
|
||||
xfree(buffer_get_string(&buffer, NULL));
|
||||
|
||||
/* Check that it is a supported cipher. */
|
||||
if (((cipher_mask1() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) &
|
||||
(1 << cipher_type)) == 0) {
|
||||
debug("Unsupported cipher %.100s used in key file %.200s.",
|
||||
cipher_name(cipher_type), filename);
|
||||
cipher = cipher_by_number(cipher_type);
|
||||
if (cipher == NULL) {
|
||||
debug("Unsupported cipher %d used in key file %.200s.",
|
||||
cipher_type, filename);
|
||||
buffer_free(&buffer);
|
||||
goto fail;
|
||||
}
|
||||
@ -377,11 +378,10 @@ load_private_key_rsa(int fd, const char *filename,
|
||||
buffer_append_space(&decrypted, &cp, buffer_len(&buffer));
|
||||
|
||||
/* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
|
||||
cipher_set_key_string(&cipher, cipher_type, passphrase);
|
||||
cipher_decrypt(&cipher, (unsigned char *) cp,
|
||||
(unsigned char *) buffer_ptr(&buffer),
|
||||
buffer_len(&buffer));
|
||||
|
||||
cipher_set_key_string(&ciphercontext, cipher, passphrase);
|
||||
cipher_decrypt(&ciphercontext, (unsigned char *) cp,
|
||||
(unsigned char *) buffer_ptr(&buffer), buffer_len(&buffer));
|
||||
memset(&ciphercontext, 0, sizeof(ciphercontext));
|
||||
buffer_free(&buffer);
|
||||
|
||||
check1 = buffer_get_char(&decrypted);
|
||||
|
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: canohost.c,v 1.15 2000/09/07 21:13:37 markus Exp $");
|
||||
RCSID("$OpenBSD: canohost.c,v 1.16 2000/10/21 17:04:22 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "packet.h"
|
||||
@ -124,7 +124,7 @@ get_remote_hostname(int socket)
|
||||
else
|
||||
ipproto = IPPROTO_IP;
|
||||
option_size = sizeof(options);
|
||||
if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options,
|
||||
if (getsockopt(socket, ipproto, IP_OPTIONS, (char *) options,
|
||||
&option_size) >= 0 && option_size != 0) {
|
||||
cp = text;
|
||||
/* Note: "text" buffer must be at least 3x as big as options. */
|
||||
|
@ -35,15 +35,92 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: cipher.c,v 1.30 2000/09/07 20:27:50 deraadt Exp $");
|
||||
RCSID("$OpenBSD: cipher.c,v 1.37 2000/10/23 19:31:54 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "ssh.h"
|
||||
#include "cipher.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
#include <openssl/md5.h>
|
||||
|
||||
|
||||
/* no encryption */
|
||||
void
|
||||
none_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
}
|
||||
void
|
||||
none_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
}
|
||||
void
|
||||
none_crypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
memcpy(dest, src, len);
|
||||
}
|
||||
|
||||
/* DES */
|
||||
void
|
||||
des_ssh1_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
static int dowarn = 1;
|
||||
if (dowarn) {
|
||||
error("Warning: use of DES is strongly discouraged "
|
||||
"due to cryptographic weaknesses");
|
||||
dowarn = 0;
|
||||
}
|
||||
des_set_key((void *)key, cc->u.des.key);
|
||||
}
|
||||
void
|
||||
des_ssh1_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
memset(cc->u.des.iv, 0, sizeof(cc->u.des.iv));
|
||||
}
|
||||
void
|
||||
des_ssh1_encrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
des_ncbc_encrypt(src, dest, len, cc->u.des.key, &cc->u.des.iv,
|
||||
DES_ENCRYPT);
|
||||
}
|
||||
void
|
||||
des_ssh1_decrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
des_ncbc_encrypt(src, dest, len, cc->u.des.key, &cc->u.des.iv,
|
||||
DES_DECRYPT);
|
||||
}
|
||||
|
||||
/* 3DES */
|
||||
void
|
||||
des3_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
des_set_key((void *) key, cc->u.des3.key1);
|
||||
des_set_key((void *) (key+8), cc->u.des3.key2);
|
||||
des_set_key((void *) (key+16), cc->u.des3.key3);
|
||||
}
|
||||
void
|
||||
des3_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
memset(cc->u.des3.iv2, 0, sizeof(cc->u.des3.iv2));
|
||||
memset(cc->u.des3.iv3, 0, sizeof(cc->u.des3.iv3));
|
||||
if (iv == NULL)
|
||||
return;
|
||||
memcpy(cc->u.des3.iv3, (char *)iv, 8);
|
||||
}
|
||||
void
|
||||
des3_cbc_encrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
des_ede3_cbc_encrypt(src, dest, len,
|
||||
cc->u.des3.key1, cc->u.des3.key2, cc->u.des3.key3,
|
||||
&cc->u.des3.iv3, DES_ENCRYPT);
|
||||
}
|
||||
void
|
||||
des3_cbc_decrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
des_ede3_cbc_encrypt(src, dest, len,
|
||||
cc->u.des3.key1, cc->u.des3.key2, cc->u.des3.key3,
|
||||
&cc->u.des3.iv3, DES_DECRYPT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used by SSH1:
|
||||
*
|
||||
@ -59,160 +136,356 @@ RCSID("$FreeBSD$");
|
||||
* choosing the X block.
|
||||
*/
|
||||
void
|
||||
SSH_3CBC_ENCRYPT(des_key_schedule ks1,
|
||||
des_key_schedule ks2, des_cblock * iv2,
|
||||
des_key_schedule ks3, des_cblock * iv3,
|
||||
unsigned char *dest, unsigned char *src,
|
||||
unsigned int len)
|
||||
des3_ssh1_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
des_set_key((void *) key, cc->u.des3.key1);
|
||||
des_set_key((void *) (key+8), cc->u.des3.key2);
|
||||
if (keylen <= 16)
|
||||
des_set_key((void *) key, cc->u.des3.key3);
|
||||
else
|
||||
des_set_key((void *) (key+16), cc->u.des3.key3);
|
||||
}
|
||||
void
|
||||
des3_ssh1_encrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
des_cblock iv1;
|
||||
des_cblock *iv2 = &cc->u.des3.iv2;
|
||||
des_cblock *iv3 = &cc->u.des3.iv3;
|
||||
|
||||
memcpy(&iv1, iv2, 8);
|
||||
|
||||
des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT);
|
||||
des_cbc_encrypt(src, dest, len, cc->u.des3.key1, &iv1, DES_ENCRYPT);
|
||||
memcpy(&iv1, dest + len - 8, 8);
|
||||
|
||||
des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT);
|
||||
des_cbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_DECRYPT);
|
||||
memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */
|
||||
|
||||
des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT);
|
||||
des_cbc_encrypt(dest, dest, len, cc->u.des3.key3, iv3, DES_ENCRYPT);
|
||||
memcpy(iv3, dest + len - 8, 8);
|
||||
}
|
||||
|
||||
void
|
||||
SSH_3CBC_DECRYPT(des_key_schedule ks1,
|
||||
des_key_schedule ks2, des_cblock * iv2,
|
||||
des_key_schedule ks3, des_cblock * iv3,
|
||||
unsigned char *dest, unsigned char *src,
|
||||
unsigned int len)
|
||||
des3_ssh1_decrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
des_cblock iv1;
|
||||
des_cblock *iv2 = &cc->u.des3.iv2;
|
||||
des_cblock *iv3 = &cc->u.des3.iv3;
|
||||
|
||||
memcpy(&iv1, iv2, 8);
|
||||
|
||||
des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT);
|
||||
des_cbc_encrypt(src, dest, len, cc->u.des3.key3, iv3, DES_DECRYPT);
|
||||
memcpy(iv3, src + len - 8, 8);
|
||||
|
||||
des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT);
|
||||
des_cbc_encrypt(dest, dest, len, cc->u.des3.key2, iv2, DES_ENCRYPT);
|
||||
memcpy(iv2, dest + len - 8, 8);
|
||||
|
||||
des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT);
|
||||
des_cbc_encrypt(dest, dest, len, cc->u.des3.key1, &iv1, DES_DECRYPT);
|
||||
/* memcpy(&iv1, iv2, 8); */
|
||||
/* Note how iv1 == iv2 on entry and exit. */
|
||||
}
|
||||
|
||||
/* Blowfish */
|
||||
void
|
||||
blowfish_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
BF_set_key(&cc->u.bf.key, keylen, (unsigned char *)key);
|
||||
}
|
||||
void
|
||||
blowfish_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
if (iv == NULL)
|
||||
memset(cc->u.bf.iv, 0, 8);
|
||||
else
|
||||
memcpy(cc->u.bf.iv, (char *)iv, 8);
|
||||
}
|
||||
void
|
||||
blowfish_cbc_encrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
BF_cbc_encrypt((void *)src, dest, len, &cc->u.bf.key, cc->u.bf.iv,
|
||||
BF_ENCRYPT);
|
||||
}
|
||||
void
|
||||
blowfish_cbc_decrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
BF_cbc_encrypt((void *)src, dest, len, &cc->u.bf.key, cc->u.bf.iv,
|
||||
BF_DECRYPT);
|
||||
}
|
||||
|
||||
/*
|
||||
* SSH1 uses a variation on Blowfish, all bytes must be swapped before
|
||||
* and after encryption/decryption. Thus the swap_bytes stuff (yuk).
|
||||
*/
|
||||
static void
|
||||
swap_bytes(const unsigned char *src, unsigned char *dst_, int n)
|
||||
swap_bytes(const unsigned char *src, unsigned char *dst, int n)
|
||||
{
|
||||
/* dst must be properly aligned. */
|
||||
u_int32_t *dst = (u_int32_t *) dst_;
|
||||
union {
|
||||
u_int32_t i;
|
||||
char c[4];
|
||||
} t;
|
||||
char c[4];
|
||||
|
||||
/* Process 8 bytes every lap. */
|
||||
for (n = n / 8; n > 0; n--) {
|
||||
t.c[3] = *src++;
|
||||
t.c[2] = *src++;
|
||||
t.c[1] = *src++;
|
||||
t.c[0] = *src++;
|
||||
*dst++ = t.i;
|
||||
/* Process 4 bytes every lap. */
|
||||
for (n = n / 4; n > 0; n--) {
|
||||
c[3] = *src++;
|
||||
c[2] = *src++;
|
||||
c[1] = *src++;
|
||||
c[0] = *src++;
|
||||
|
||||
t.c[3] = *src++;
|
||||
t.c[2] = *src++;
|
||||
t.c[1] = *src++;
|
||||
t.c[0] = *src++;
|
||||
*dst++ = t.i;
|
||||
*dst++ = c[0];
|
||||
*dst++ = c[1];
|
||||
*dst++ = c[2];
|
||||
*dst++ = c[3];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Names of all encryption algorithms.
|
||||
* These must match the numbers defined in cipher.h.
|
||||
*/
|
||||
static char *cipher_names[] =
|
||||
void
|
||||
blowfish_ssh1_encrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
"none",
|
||||
"idea",
|
||||
"des",
|
||||
"3des",
|
||||
"tss",
|
||||
"rc4",
|
||||
"blowfish",
|
||||
"reserved",
|
||||
"blowfish-cbc",
|
||||
"3des-cbc",
|
||||
"arcfour",
|
||||
"cast128-cbc"
|
||||
swap_bytes(src, dest, len);
|
||||
BF_cbc_encrypt((void *)dest, dest, len, &cc->u.bf.key, cc->u.bf.iv,
|
||||
BF_ENCRYPT);
|
||||
swap_bytes(dest, dest, len);
|
||||
}
|
||||
void
|
||||
blowfish_ssh1_decrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
swap_bytes(src, dest, len);
|
||||
BF_cbc_encrypt((void *)dest, dest, len, &cc->u.bf.key, cc->u.bf.iv,
|
||||
BF_DECRYPT);
|
||||
swap_bytes(dest, dest, len);
|
||||
}
|
||||
|
||||
/* alleged rc4 */
|
||||
void
|
||||
arcfour_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
RC4_set_key(&cc->u.rc4, keylen, (u_char *)key);
|
||||
}
|
||||
void
|
||||
arcfour_crypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
RC4(&cc->u.rc4, len, (u_char *)src, dest);
|
||||
}
|
||||
|
||||
/* CAST */
|
||||
void
|
||||
cast_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
CAST_set_key(&cc->u.cast.key, keylen, (unsigned char *) key);
|
||||
}
|
||||
void
|
||||
cast_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
if (iv == NULL)
|
||||
fatal("no IV for %s.", cc->cipher->name);
|
||||
memcpy(cc->u.cast.iv, (char *)iv, 8);
|
||||
}
|
||||
void
|
||||
cast_cbc_encrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
CAST_cbc_encrypt(src, dest, len, &cc->u.cast.key, cc->u.cast.iv,
|
||||
CAST_ENCRYPT);
|
||||
}
|
||||
void
|
||||
cast_cbc_decrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
CAST_cbc_encrypt(src, dest, len, &cc->u.cast.key, cc->u.cast.iv,
|
||||
CAST_DECRYPT);
|
||||
}
|
||||
|
||||
/* RIJNDAEL */
|
||||
|
||||
#define RIJNDAEL_BLOCKSIZE 16
|
||||
void
|
||||
rijndael_setkey(CipherContext *cc, const u_char *key, u_int keylen)
|
||||
{
|
||||
rijndael_set_key(&cc->u.rijndael.enc, (u4byte *)key, 8*keylen, 1);
|
||||
rijndael_set_key(&cc->u.rijndael.dec, (u4byte *)key, 8*keylen, 0);
|
||||
}
|
||||
void
|
||||
rijndael_setiv(CipherContext *cc, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
if (iv == NULL)
|
||||
fatal("no IV for %s.", cc->cipher->name);
|
||||
memcpy((u_char *)cc->u.rijndael.iv, iv, RIJNDAEL_BLOCKSIZE);
|
||||
}
|
||||
void
|
||||
rijndael_cbc_encrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
rijndael_ctx *ctx = &cc->u.rijndael.enc;
|
||||
u4byte *iv = cc->u.rijndael.iv;
|
||||
u4byte in[4];
|
||||
u4byte *cprev, *cnow, *plain;
|
||||
int i, blocks = len / RIJNDAEL_BLOCKSIZE;
|
||||
if (len == 0)
|
||||
return;
|
||||
if (len % RIJNDAEL_BLOCKSIZE)
|
||||
fatal("rijndael_cbc_encrypt: bad len %d", len);
|
||||
cnow = (u4byte*) dest;
|
||||
plain = (u4byte*) src;
|
||||
cprev = iv;
|
||||
for(i = 0; i < blocks; i++, plain+=4, cnow+=4) {
|
||||
in[0] = plain[0] ^ cprev[0];
|
||||
in[1] = plain[1] ^ cprev[1];
|
||||
in[2] = plain[2] ^ cprev[2];
|
||||
in[3] = plain[3] ^ cprev[3];
|
||||
rijndael_encrypt(ctx, in, cnow);
|
||||
cprev = cnow;
|
||||
}
|
||||
memcpy(iv, cprev, RIJNDAEL_BLOCKSIZE);
|
||||
}
|
||||
|
||||
void
|
||||
rijndael_cbc_decrypt(CipherContext *cc, u_char *dest, const u_char *src,
|
||||
u_int len)
|
||||
{
|
||||
rijndael_ctx *ctx = &cc->u.rijndael.dec;
|
||||
u4byte *iv = cc->u.rijndael.iv;
|
||||
u4byte ivsaved[4];
|
||||
u4byte *cnow = (u4byte*) (src+len-RIJNDAEL_BLOCKSIZE);
|
||||
u4byte *plain = (u4byte*) (dest+len-RIJNDAEL_BLOCKSIZE);
|
||||
u4byte *ivp;
|
||||
int i, blocks = len / RIJNDAEL_BLOCKSIZE;
|
||||
if (len == 0)
|
||||
return;
|
||||
if (len % RIJNDAEL_BLOCKSIZE)
|
||||
fatal("rijndael_cbc_decrypt: bad len %d", len);
|
||||
memcpy(ivsaved, cnow, RIJNDAEL_BLOCKSIZE);
|
||||
for(i = blocks; i > 0; i--, cnow-=4, plain-=4) {
|
||||
rijndael_decrypt(ctx, cnow, plain);
|
||||
ivp = (i == 1) ? iv : cnow-4;
|
||||
plain[0] ^= ivp[0];
|
||||
plain[1] ^= ivp[1];
|
||||
plain[2] ^= ivp[2];
|
||||
plain[3] ^= ivp[3];
|
||||
}
|
||||
memcpy(iv, ivsaved, RIJNDAEL_BLOCKSIZE);
|
||||
}
|
||||
|
||||
Cipher ciphers[] = {
|
||||
{ "none",
|
||||
SSH_CIPHER_NONE, 8, 0,
|
||||
none_setkey, none_setiv,
|
||||
none_crypt, none_crypt },
|
||||
{ "des",
|
||||
SSH_CIPHER_DES, 8, 8,
|
||||
des_ssh1_setkey, des_ssh1_setiv,
|
||||
des_ssh1_encrypt, des_ssh1_decrypt },
|
||||
{ "3des",
|
||||
SSH_CIPHER_3DES, 8, 16,
|
||||
des3_ssh1_setkey, des3_setiv,
|
||||
des3_ssh1_encrypt, des3_ssh1_decrypt },
|
||||
{ "blowfish",
|
||||
SSH_CIPHER_BLOWFISH, 8, 16,
|
||||
blowfish_setkey, blowfish_setiv,
|
||||
blowfish_ssh1_encrypt, blowfish_ssh1_decrypt },
|
||||
|
||||
{ "3des-cbc",
|
||||
SSH_CIPHER_SSH2, 8, 24,
|
||||
des3_setkey, des3_setiv,
|
||||
des3_cbc_encrypt, des3_cbc_decrypt },
|
||||
{ "blowfish-cbc",
|
||||
SSH_CIPHER_SSH2, 8, 16,
|
||||
blowfish_setkey, blowfish_setiv,
|
||||
blowfish_cbc_encrypt, blowfish_cbc_decrypt },
|
||||
{ "cast128-cbc",
|
||||
SSH_CIPHER_SSH2, 8, 16,
|
||||
cast_setkey, cast_setiv,
|
||||
cast_cbc_encrypt, cast_cbc_decrypt },
|
||||
{ "arcfour",
|
||||
SSH_CIPHER_SSH2, 8, 16,
|
||||
arcfour_setkey, none_setiv,
|
||||
arcfour_crypt, arcfour_crypt },
|
||||
{ "aes128-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 16,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "aes192-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 24,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "aes256-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 32,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "rijndael128-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 16,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "rijndael192-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 24,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "rijndael256-cbc",
|
||||
SSH_CIPHER_SSH2, 16, 32,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ "rijndael-cbc@lysator.liu.se",
|
||||
SSH_CIPHER_SSH2, 16, 32,
|
||||
rijndael_setkey, rijndael_setiv,
|
||||
rijndael_cbc_encrypt, rijndael_cbc_decrypt },
|
||||
{ NULL, SSH_CIPHER_ILLEGAL, 0, 0, NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns a bit mask indicating which ciphers are supported by this
|
||||
* implementation. The bit mask has the corresponding bit set of each
|
||||
* supported cipher.
|
||||
*/
|
||||
/*--*/
|
||||
|
||||
unsigned int
|
||||
cipher_mask1()
|
||||
cipher_mask_ssh1(int client)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */
|
||||
mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */
|
||||
mask |= 1 << SSH_CIPHER_BLOWFISH;
|
||||
if (client) {
|
||||
mask |= 1 << SSH_CIPHER_DES;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
unsigned int
|
||||
cipher_mask2()
|
||||
|
||||
Cipher *
|
||||
cipher_by_name(const char *name)
|
||||
{
|
||||
unsigned int mask = 0;
|
||||
mask |= 1 << SSH_CIPHER_BLOWFISH_CBC;
|
||||
mask |= 1 << SSH_CIPHER_3DES_CBC;
|
||||
mask |= 1 << SSH_CIPHER_ARCFOUR;
|
||||
mask |= 1 << SSH_CIPHER_CAST128_CBC;
|
||||
return mask;
|
||||
}
|
||||
unsigned int
|
||||
cipher_mask()
|
||||
{
|
||||
return cipher_mask1() | cipher_mask2();
|
||||
Cipher *c;
|
||||
for (c = ciphers; c->name != NULL; c++)
|
||||
if (strcasecmp(c->name, name) == 0)
|
||||
return c;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns the name of the cipher. */
|
||||
|
||||
const char *
|
||||
cipher_name(int cipher)
|
||||
Cipher *
|
||||
cipher_by_number(int id)
|
||||
{
|
||||
if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) ||
|
||||
cipher_names[cipher] == NULL)
|
||||
fatal("cipher_name: bad cipher name: %d", cipher);
|
||||
return cipher_names[cipher];
|
||||
Cipher *c;
|
||||
for (c = ciphers; c->name != NULL; c++)
|
||||
if (c->number == id)
|
||||
return c;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns 1 if the name of the ciphers are valid. */
|
||||
|
||||
#define CIPHER_SEP ","
|
||||
int
|
||||
ciphers_valid(const char *names)
|
||||
{
|
||||
Cipher *c;
|
||||
char *ciphers, *cp;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (names == NULL || strcmp(names, "") == 0)
|
||||
return 0;
|
||||
ciphers = cp = xstrdup(names);
|
||||
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
|
||||
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
|
||||
(p = strsep(&cp, CIPHER_SEP))) {
|
||||
i = cipher_number(p);
|
||||
if (i == -1 || !(cipher_mask2() & (1 << i))) {
|
||||
c = cipher_by_name(p);
|
||||
if (c == NULL || c->number != SSH_CIPHER_SSH2) {
|
||||
debug("bad cipher %s [%s]", p, names);
|
||||
xfree(ciphers);
|
||||
return 0;
|
||||
} else {
|
||||
debug3("cipher ok: %s [%s]", p, names);
|
||||
}
|
||||
}
|
||||
debug3("ciphers ok: [%s]", names);
|
||||
xfree(ciphers);
|
||||
return 1;
|
||||
}
|
||||
@ -225,14 +498,49 @@ ciphers_valid(const char *names)
|
||||
int
|
||||
cipher_number(const char *name)
|
||||
{
|
||||
int i;
|
||||
Cipher *c;
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++)
|
||||
if (strcmp(cipher_names[i], name) == 0 &&
|
||||
(cipher_mask() & (1 << i)))
|
||||
return i;
|
||||
return -1;
|
||||
c = cipher_by_name(name);
|
||||
return (c==NULL) ? -1 : c->number;
|
||||
}
|
||||
|
||||
char *
|
||||
cipher_name(int id)
|
||||
{
|
||||
Cipher *c = cipher_by_number(id);
|
||||
return (c==NULL) ? "<unknown>" : c->name;
|
||||
}
|
||||
|
||||
void
|
||||
cipher_init(CipherContext *cc, Cipher *cipher,
|
||||
const u_char *key, u_int keylen, const u_char *iv, u_int ivlen)
|
||||
{
|
||||
if (keylen < cipher->key_len)
|
||||
fatal("cipher_init: key length %d is insufficient for %s.",
|
||||
keylen, cipher->name);
|
||||
if (iv != NULL && ivlen < cipher->block_size)
|
||||
fatal("cipher_init: iv length %d is insufficient for %s.",
|
||||
ivlen, cipher->name);
|
||||
cc->cipher = cipher;
|
||||
cipher->setkey(cc, key, keylen);
|
||||
cipher->setiv(cc, iv, ivlen);
|
||||
}
|
||||
|
||||
void
|
||||
cipher_encrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
if (len % cc->cipher->block_size)
|
||||
fatal("cipher_encrypt: bad plaintext length %d", len);
|
||||
cc->cipher->encrypt(cc, dest, src, len);
|
||||
}
|
||||
|
||||
void
|
||||
cipher_decrypt(CipherContext *cc, u_char *dest, const u_char *src, u_int len)
|
||||
{
|
||||
if (len % cc->cipher->block_size)
|
||||
fatal("cipher_decrypt: bad ciphertext length %d", len);
|
||||
cc->cipher->decrypt(cc, dest, src, len);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -241,248 +549,18 @@ cipher_number(const char *name)
|
||||
*/
|
||||
|
||||
void
|
||||
cipher_set_key_string(CipherContext *context, int cipher, const char *passphrase)
|
||||
cipher_set_key_string(CipherContext *cc, Cipher *cipher,
|
||||
const char *passphrase)
|
||||
{
|
||||
MD5_CTX md;
|
||||
unsigned char digest[16];
|
||||
|
||||
MD5_Init(&md);
|
||||
MD5_Update(&md, (const unsigned char *) passphrase, strlen(passphrase));
|
||||
MD5_Update(&md, (const u_char *)passphrase, strlen(passphrase));
|
||||
MD5_Final(digest, &md);
|
||||
|
||||
cipher_set_key(context, cipher, digest, 16);
|
||||
cipher_init(cc, cipher, digest, 16, NULL, 0);
|
||||
|
||||
memset(digest, 0, sizeof(digest));
|
||||
memset(&md, 0, sizeof(md));
|
||||
}
|
||||
|
||||
/* Selects the cipher to use and sets the key. */
|
||||
|
||||
void
|
||||
cipher_set_key(CipherContext *context, int cipher, const unsigned char *key,
|
||||
int keylen)
|
||||
{
|
||||
unsigned char padded[32];
|
||||
|
||||
/* Set cipher type. */
|
||||
context->type = cipher;
|
||||
|
||||
/* Get 32 bytes of key data. Pad if necessary. (So that code
|
||||
below does not need to worry about key size). */
|
||||
memset(padded, 0, sizeof(padded));
|
||||
memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded));
|
||||
|
||||
/* Initialize the initialization vector. */
|
||||
switch (cipher) {
|
||||
case SSH_CIPHER_NONE:
|
||||
/*
|
||||
* Has to stay for authfile saving of private key with no
|
||||
* passphrase
|
||||
*/
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES:
|
||||
/*
|
||||
* Note: the least significant bit of each byte of key is
|
||||
* parity, and must be ignored by the implementation. 16
|
||||
* bytes of key are used (first and last keys are the same).
|
||||
*/
|
||||
if (keylen < 16)
|
||||
error("Key length %d is insufficient for 3DES.", keylen);
|
||||
des_set_key((void *) padded, context->u.des3.key1);
|
||||
des_set_key((void *) (padded + 8), context->u.des3.key2);
|
||||
if (keylen <= 16)
|
||||
des_set_key((void *) padded, context->u.des3.key3);
|
||||
else
|
||||
des_set_key((void *) (padded + 16), context->u.des3.key3);
|
||||
memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2));
|
||||
memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3));
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
if (keylen < 16)
|
||||
error("Key length %d is insufficient for blowfish.", keylen);
|
||||
BF_set_key(&context->u.bf.key, keylen, padded);
|
||||
memset(context->u.bf.iv, 0, 8);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES_CBC:
|
||||
case SSH_CIPHER_BLOWFISH_CBC:
|
||||
case SSH_CIPHER_ARCFOUR:
|
||||
case SSH_CIPHER_CAST128_CBC:
|
||||
fatal("cipher_set_key: illegal cipher: %s", cipher_name(cipher));
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
|
||||
}
|
||||
memset(padded, 0, sizeof(padded));
|
||||
}
|
||||
|
||||
void
|
||||
cipher_set_key_iv(CipherContext * context, int cipher,
|
||||
const unsigned char *key, int keylen,
|
||||
const unsigned char *iv, int ivlen)
|
||||
{
|
||||
/* Set cipher type. */
|
||||
context->type = cipher;
|
||||
|
||||
/* Initialize the initialization vector. */
|
||||
switch (cipher) {
|
||||
case SSH_CIPHER_NONE:
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES:
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
fatal("cipher_set_key_iv: illegal cipher: %s", cipher_name(cipher));
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES_CBC:
|
||||
if (keylen < 24)
|
||||
error("Key length %d is insufficient for 3des-cbc.", keylen);
|
||||
des_set_key((void *) key, context->u.des3.key1);
|
||||
des_set_key((void *) (key+8), context->u.des3.key2);
|
||||
des_set_key((void *) (key+16), context->u.des3.key3);
|
||||
if (ivlen < 8)
|
||||
error("IV length %d is insufficient for 3des-cbc.", ivlen);
|
||||
memcpy(context->u.des3.iv3, (char *)iv, 8);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH_CBC:
|
||||
if (keylen < 16)
|
||||
error("Key length %d is insufficient for blowfish.", keylen);
|
||||
if (ivlen < 8)
|
||||
error("IV length %d is insufficient for blowfish.", ivlen);
|
||||
BF_set_key(&context->u.bf.key, keylen, (unsigned char *)key);
|
||||
memcpy(context->u.bf.iv, (char *)iv, 8);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_ARCFOUR:
|
||||
if (keylen < 16)
|
||||
error("Key length %d is insufficient for arcfour.", keylen);
|
||||
RC4_set_key(&context->u.rc4, keylen, (unsigned char *)key);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_CAST128_CBC:
|
||||
if (keylen < 16)
|
||||
error("Key length %d is insufficient for cast128.", keylen);
|
||||
if (ivlen < 8)
|
||||
error("IV length %d is insufficient for cast128.", ivlen);
|
||||
CAST_set_key(&context->u.cast.key, keylen, (unsigned char *) key);
|
||||
memcpy(context->u.cast.iv, (char *)iv, 8);
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
|
||||
}
|
||||
}
|
||||
|
||||
/* Encrypts data using the cipher. */
|
||||
|
||||
void
|
||||
cipher_encrypt(CipherContext *context, unsigned char *dest,
|
||||
const unsigned char *src, unsigned int len)
|
||||
{
|
||||
if ((len & 7) != 0)
|
||||
fatal("cipher_encrypt: bad plaintext length %d", len);
|
||||
|
||||
switch (context->type) {
|
||||
case SSH_CIPHER_NONE:
|
||||
memcpy(dest, src, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES:
|
||||
SSH_3CBC_ENCRYPT(context->u.des3.key1,
|
||||
context->u.des3.key2, &context->u.des3.iv2,
|
||||
context->u.des3.key3, &context->u.des3.iv3,
|
||||
dest, (unsigned char *) src, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
swap_bytes(src, dest, len);
|
||||
BF_cbc_encrypt(dest, dest, len,
|
||||
&context->u.bf.key, context->u.bf.iv,
|
||||
BF_ENCRYPT);
|
||||
swap_bytes(dest, dest, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH_CBC:
|
||||
BF_cbc_encrypt((void *)src, dest, len,
|
||||
&context->u.bf.key, context->u.bf.iv,
|
||||
BF_ENCRYPT);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES_CBC:
|
||||
des_ede3_cbc_encrypt(src, dest, len,
|
||||
context->u.des3.key1, context->u.des3.key2,
|
||||
context->u.des3.key3, &context->u.des3.iv3, DES_ENCRYPT);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_ARCFOUR:
|
||||
RC4(&context->u.rc4, len, (unsigned char *)src, dest);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_CAST128_CBC:
|
||||
CAST_cbc_encrypt(src, dest, len,
|
||||
&context->u.cast.key, context->u.cast.iv, CAST_ENCRYPT);
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type));
|
||||
}
|
||||
}
|
||||
|
||||
/* Decrypts data using the cipher. */
|
||||
|
||||
void
|
||||
cipher_decrypt(CipherContext *context, unsigned char *dest,
|
||||
const unsigned char *src, unsigned int len)
|
||||
{
|
||||
if ((len & 7) != 0)
|
||||
fatal("cipher_decrypt: bad ciphertext length %d", len);
|
||||
|
||||
switch (context->type) {
|
||||
case SSH_CIPHER_NONE:
|
||||
memcpy(dest, src, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES:
|
||||
SSH_3CBC_DECRYPT(context->u.des3.key1,
|
||||
context->u.des3.key2, &context->u.des3.iv2,
|
||||
context->u.des3.key3, &context->u.des3.iv3,
|
||||
dest, (unsigned char *) src, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
swap_bytes(src, dest, len);
|
||||
BF_cbc_encrypt((void *) dest, dest, len,
|
||||
&context->u.bf.key, context->u.bf.iv,
|
||||
BF_DECRYPT);
|
||||
swap_bytes(dest, dest, len);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_BLOWFISH_CBC:
|
||||
BF_cbc_encrypt((void *) src, dest, len,
|
||||
&context->u.bf.key, context->u.bf.iv,
|
||||
BF_DECRYPT);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_3DES_CBC:
|
||||
des_ede3_cbc_encrypt(src, dest, len,
|
||||
context->u.des3.key1, context->u.des3.key2,
|
||||
context->u.des3.key3, &context->u.des3.iv3, DES_DECRYPT);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_ARCFOUR:
|
||||
RC4(&context->u.rc4, len, (unsigned char *)src, dest);
|
||||
break;
|
||||
|
||||
case SSH_CIPHER_CAST128_CBC:
|
||||
CAST_cbc_encrypt(src, dest, len,
|
||||
&context->u.cast.key, context->u.cast.iv, CAST_DECRYPT);
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type));
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,31 @@
|
||||
* software must be clearly marked as such, and if the derived work is
|
||||
* incompatible with the protocol description in the RFC file, it must be
|
||||
* called by a name other than "ssh" or "Secure Shell".
|
||||
*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* RCSID("$OpenBSD: cipher.h,v 1.19 2000/09/07 20:27:50 deraadt Exp $"); */
|
||||
/* RCSID("$OpenBSD: cipher.h,v 1.22 2000/10/13 18:59:14 markus Exp $"); */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#ifndef CIPHER_H
|
||||
@ -20,9 +42,12 @@
|
||||
#include <openssl/blowfish.h>
|
||||
#include <openssl/rc4.h>
|
||||
#include <openssl/cast.h>
|
||||
|
||||
/* Cipher types. New types can be added, but old types should not be removed
|
||||
for compatibility. The maximum allowed value is 31. */
|
||||
#include "rijndael.h"
|
||||
/*
|
||||
* Cipher types for SSH-1. New types can be added, but old types should not
|
||||
* be removed for compatibility. The maximum allowed value is 31.
|
||||
*/
|
||||
#define SSH_CIPHER_SSH2 -3
|
||||
#define SSH_CIPHER_ILLEGAL -2 /* No valid cipher selected. */
|
||||
#define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */
|
||||
#define SSH_CIPHER_NONE 0 /* no encryption */
|
||||
@ -33,16 +58,17 @@
|
||||
#define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */
|
||||
#define SSH_CIPHER_BLOWFISH 6
|
||||
#define SSH_CIPHER_RESERVED 7
|
||||
#define SSH_CIPHER_MAX 31
|
||||
|
||||
/* these ciphers are used in SSH2: */
|
||||
#define SSH_CIPHER_BLOWFISH_CBC 8
|
||||
#define SSH_CIPHER_3DES_CBC 9
|
||||
#define SSH_CIPHER_ARCFOUR 10 /* Alleged RC4 */
|
||||
#define SSH_CIPHER_CAST128_CBC 11
|
||||
typedef struct Cipher Cipher;
|
||||
typedef struct CipherContext CipherContext;
|
||||
|
||||
typedef struct {
|
||||
unsigned int type;
|
||||
struct CipherContext {
|
||||
union {
|
||||
struct {
|
||||
des_key_schedule key;
|
||||
des_cblock iv;
|
||||
} des;
|
||||
struct {
|
||||
des_key_schedule key1;
|
||||
des_key_schedule key2;
|
||||
@ -52,64 +78,41 @@ typedef struct {
|
||||
} des3;
|
||||
struct {
|
||||
struct bf_key_st key;
|
||||
unsigned char iv[8];
|
||||
u_char iv[8];
|
||||
} bf;
|
||||
struct {
|
||||
CAST_KEY key;
|
||||
unsigned char iv[8];
|
||||
u_char iv[8];
|
||||
} cast;
|
||||
struct {
|
||||
u4byte iv[4];
|
||||
rijndael_ctx enc;
|
||||
rijndael_ctx dec;
|
||||
} rijndael;
|
||||
RC4_KEY rc4;
|
||||
} u;
|
||||
} CipherContext;
|
||||
/*
|
||||
* Returns a bit mask indicating which ciphers are supported by this
|
||||
* implementation. The bit mask has the corresponding bit set of each
|
||||
* supported cipher.
|
||||
*/
|
||||
unsigned int cipher_mask();
|
||||
unsigned int cipher_mask1();
|
||||
unsigned int cipher_mask2();
|
||||
Cipher *cipher;
|
||||
};
|
||||
struct Cipher {
|
||||
char *name;
|
||||
int number; /* for ssh1 only */
|
||||
u_int block_size;
|
||||
u_int key_len;
|
||||
void (*setkey)(CipherContext *, const u_char *, u_int);
|
||||
void (*setiv)(CipherContext *, const u_char *, u_int);
|
||||
void (*encrypt)(CipherContext *, u_char *, const u_char *, u_int);
|
||||
void (*decrypt)(CipherContext *, u_char *, const u_char *, u_int);
|
||||
};
|
||||
|
||||
/* Returns the name of the cipher. */
|
||||
const char *cipher_name(int cipher);
|
||||
|
||||
/*
|
||||
* Parses the name of the cipher. Returns the number of the corresponding
|
||||
* cipher, or -1 on error.
|
||||
*/
|
||||
int cipher_number(const char *name);
|
||||
|
||||
/* returns 1 if all ciphers are supported (ssh2 only) */
|
||||
int ciphers_valid(const char *names);
|
||||
|
||||
/*
|
||||
* Selects the cipher to use and sets the key. If for_encryption is true,
|
||||
* the key is setup for encryption; otherwise it is setup for decryption.
|
||||
*/
|
||||
void
|
||||
cipher_set_key(CipherContext * context, int cipher,
|
||||
const unsigned char *key, int keylen);
|
||||
void
|
||||
cipher_set_key_iv(CipherContext * context, int cipher,
|
||||
const unsigned char *key, int keylen,
|
||||
const unsigned char *iv, int ivlen);
|
||||
|
||||
/*
|
||||
* Sets key for the cipher by computing the MD5 checksum of the passphrase,
|
||||
* and using the resulting 16 bytes as the key.
|
||||
*/
|
||||
void
|
||||
cipher_set_key_string(CipherContext * context, int cipher,
|
||||
const char *passphrase);
|
||||
|
||||
/* Encrypts data using the cipher. */
|
||||
void
|
||||
cipher_encrypt(CipherContext * context, unsigned char *dest,
|
||||
const unsigned char *src, unsigned int len);
|
||||
|
||||
/* Decrypts data using the cipher. */
|
||||
void
|
||||
cipher_decrypt(CipherContext * context, unsigned char *dest,
|
||||
const unsigned char *src, unsigned int len);
|
||||
unsigned int cipher_mask_ssh1(int client);
|
||||
Cipher *cipher_by_name(const char *name);
|
||||
Cipher *cipher_by_number(int id);
|
||||
int cipher_number(const char *name);
|
||||
char *cipher_name(int id);
|
||||
int ciphers_valid(const char *names);
|
||||
void cipher_init(CipherContext *, Cipher *, const u_char *, u_int, const u_char *, u_int);
|
||||
void cipher_encrypt(CipherContext *context, u_char *dest, const u_char *src, u_int len);
|
||||
void cipher_decrypt(CipherContext *context, u_char *dest, const u_char *src, u_int len);
|
||||
void cipher_set_key_string(CipherContext *context, Cipher *cipher, const char *passphrase);
|
||||
|
||||
#endif /* CIPHER_H */
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$FreeBSD$");
|
||||
RCSID("$OpenBSD: compat.c,v 1.27 2000/10/31 09:31:58 markus Exp $");
|
||||
|
||||
#include "ssh.h"
|
||||
@ -58,6 +59,7 @@ compat_datafellows(const char *version)
|
||||
char *pat;
|
||||
int bugs;
|
||||
} check[] = {
|
||||
{ "^OpenSSH[-_]2\\.3", 0 },
|
||||
{ "^OpenSSH[-_]2\\.[012]", SSH_OLD_SESSIONID },
|
||||
{ "MindTerm", 0 },
|
||||
{ "^2\\.1\\.0 ", SSH_BUG_SIGBLOB|SSH_BUG_HMAC|
|
||||
|
@ -12,11 +12,10 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: readconf.c,v 1.47 2000/09/07 21:13:37 markus Exp $");
|
||||
RCSID("$OpenBSD: readconf.c,v 1.49 2000/10/11 20:27:23 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "ssh.h"
|
||||
#include "cipher.h"
|
||||
#include "readconf.h"
|
||||
#include "match.h"
|
||||
#include "xmalloc.h"
|
||||
@ -107,7 +106,8 @@ typedef enum {
|
||||
oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression,
|
||||
oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication,
|
||||
oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2,
|
||||
oGlobalKnownHostsFile2, oUserKnownHostsFile2, oDSAAuthentication
|
||||
oGlobalKnownHostsFile2, oUserKnownHostsFile2, oDSAAuthentication,
|
||||
oKbdInteractiveAuthentication, oKbdInteractiveDevices
|
||||
} OpCodes;
|
||||
|
||||
/* Textual representations of the tokens. */
|
||||
@ -123,6 +123,8 @@ static struct {
|
||||
{ "useprivilegedport", oUsePrivilegedPort },
|
||||
{ "rhostsauthentication", oRhostsAuthentication },
|
||||
{ "passwordauthentication", oPasswordAuthentication },
|
||||
{ "kbdinteractiveauthentication", oKbdInteractiveAuthentication },
|
||||
{ "kbdinteractivedevices", oKbdInteractiveDevices },
|
||||
{ "rsaauthentication", oRSAAuthentication },
|
||||
{ "dsaauthentication", oDSAAuthentication },
|
||||
{ "skeyauthentication", oSkeyAuthentication },
|
||||
@ -296,6 +298,14 @@ process_config_line(Options *options, const char *host,
|
||||
intptr = &options->password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case oKbdInteractiveAuthentication:
|
||||
intptr = &options->kbd_interactive_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case oKbdInteractiveDevices:
|
||||
charptr = &options->kbd_interactive_devices;
|
||||
goto parse_string;
|
||||
|
||||
case oDSAAuthentication:
|
||||
intptr = &options->dsa_authentication;
|
||||
goto parse_flag;
|
||||
@ -684,6 +694,8 @@ initialize_options(Options * options)
|
||||
options->afs_token_passing = -1;
|
||||
#endif
|
||||
options->password_authentication = -1;
|
||||
options->kbd_interactive_authentication = -1;
|
||||
options->kbd_interactive_devices = NULL;
|
||||
options->rhosts_rsa_authentication = -1;
|
||||
options->fallback_to_rsh = -1;
|
||||
options->use_rsh = -1;
|
||||
@ -760,6 +772,8 @@ fill_default_options(Options * options)
|
||||
#endif /* AFS */
|
||||
if (options->password_authentication == -1)
|
||||
options->password_authentication = 1;
|
||||
if (options->kbd_interactive_authentication == -1)
|
||||
options->kbd_interactive_authentication = 0;
|
||||
if (options->rhosts_rsa_authentication == -1)
|
||||
options->rhosts_rsa_authentication = 1;
|
||||
if (options->fallback_to_rsh == -1)
|
||||
|
@ -11,7 +11,7 @@
|
||||
* called by a name other than "ssh" or "Secure Shell".
|
||||
*/
|
||||
|
||||
/* RCSID("$OpenBSD: readconf.h,v 1.21 2000/09/07 20:27:53 deraadt Exp $"); */
|
||||
/* RCSID("$OpenBSD: readconf.h,v 1.22 2000/10/11 20:14:39 markus Exp $"); */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#ifndef READCONF_H
|
||||
@ -54,6 +54,8 @@ typedef struct {
|
||||
#endif
|
||||
int password_authentication; /* Try password
|
||||
* authentication. */
|
||||
int kbd_interactive_authentication; /* Try keyboard-interactive auth. */
|
||||
char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */
|
||||
int fallback_to_rsh;/* Use rsh if cannot connect with ssh. */
|
||||
int use_rsh; /* Always use rsh (don\'t try ssh). */
|
||||
int batch_mode; /* Batch mode: do not ask for passwords. */
|
||||
|
@ -151,7 +151,7 @@ rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key)
|
||||
|
||||
if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key,
|
||||
RSA_PKCS1_PADDING)) <= 0)
|
||||
fatal("rsa_private_encrypt() failed.");
|
||||
fatal("rsa_public_encrypt() failed.");
|
||||
|
||||
BN_bin2bn(outbuf, len, out);
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: servconf.c,v 1.51 2000/09/07 20:27:53 deraadt Exp $");
|
||||
RCSID("$OpenBSD: servconf.c,v 1.53 2000/10/14 12:12:09 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "ssh.h"
|
||||
@ -66,11 +66,13 @@ initialize_server_options(ServerOptions *options)
|
||||
options->afs_token_passing = -1;
|
||||
#endif
|
||||
options->password_authentication = -1;
|
||||
options->kbd_interactive_authentication = -1;
|
||||
#ifdef SKEY
|
||||
options->skey_authentication = -1;
|
||||
#endif
|
||||
options->permit_empty_passwd = -1;
|
||||
options->use_login = -1;
|
||||
options->allow_tcp_forwarding = -1;
|
||||
options->num_allow_users = 0;
|
||||
options->num_deny_users = 0;
|
||||
options->num_allow_groups = 0;
|
||||
@ -161,6 +163,8 @@ fill_default_server_options(ServerOptions *options)
|
||||
#endif /* AFS */
|
||||
if (options->password_authentication == -1)
|
||||
options->password_authentication = 1;
|
||||
if (options->kbd_interactive_authentication == -1)
|
||||
options->kbd_interactive_authentication = 0;
|
||||
#ifdef SKEY
|
||||
if (options->skey_authentication == -1)
|
||||
options->skey_authentication = 1;
|
||||
@ -169,6 +173,8 @@ fill_default_server_options(ServerOptions *options)
|
||||
options->permit_empty_passwd = 0;
|
||||
if (options->use_login == -1)
|
||||
options->use_login = 0;
|
||||
if (options->allow_tcp_forwarding == -1)
|
||||
options->allow_tcp_forwarding = 1;
|
||||
if (options->protocol == SSH_PROTO_UNKNOWN)
|
||||
options->protocol = SSH_PROTO_1|SSH_PROTO_2;
|
||||
if (options->gateway_ports == -1)
|
||||
@ -199,10 +205,11 @@ typedef enum {
|
||||
#ifdef SKEY
|
||||
sSkeyAuthentication,
|
||||
#endif
|
||||
sPasswordAuthentication, sListenAddress,
|
||||
sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress,
|
||||
sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset,
|
||||
sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail,
|
||||
sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
|
||||
sUseLogin, sAllowTcpForwarding,
|
||||
sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
|
||||
sIgnoreUserKnownHosts, sHostDSAKeyFile, sCiphers, sProtocol, sPidFile,
|
||||
sGatewayPorts, sDSAAuthentication, sConnectionsPerPeriod, sXAuthLocation,
|
||||
sSubsystem, sMaxStartups
|
||||
@ -241,6 +248,7 @@ static struct {
|
||||
{ "afstokenpassing", sAFSTokenPassing },
|
||||
#endif
|
||||
{ "passwordauthentication", sPasswordAuthentication },
|
||||
{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
|
||||
#ifdef SKEY
|
||||
{ "skeyauthentication", sSkeyAuthentication },
|
||||
#endif
|
||||
@ -257,6 +265,7 @@ static struct {
|
||||
{ "uselogin", sUseLogin },
|
||||
{ "randomseed", sRandomSeedFile },
|
||||
{ "keepalive", sKeepAlives },
|
||||
{ "allowtcpforwarding", sAllowTcpForwarding },
|
||||
{ "allowusers", sAllowUsers },
|
||||
{ "denyusers", sDenyUsers },
|
||||
{ "allowgroups", sAllowGroups },
|
||||
@ -534,6 +543,10 @@ read_server_config(ServerOptions *options, const char *filename)
|
||||
intptr = &options->password_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case sKbdInteractiveAuthentication:
|
||||
intptr = &options->kbd_interactive_authentication;
|
||||
goto parse_flag;
|
||||
|
||||
case sCheckMail:
|
||||
intptr = &options->check_mail;
|
||||
goto parse_flag;
|
||||
@ -602,6 +615,10 @@ read_server_config(ServerOptions *options, const char *filename)
|
||||
*intptr = (LogLevel) value;
|
||||
break;
|
||||
|
||||
case sAllowTcpForwarding:
|
||||
intptr = &options->allow_tcp_forwarding;
|
||||
goto parse_flag;
|
||||
|
||||
case sAllowUsers:
|
||||
while ((arg = strdelim(&cp)) && *arg != '\0') {
|
||||
if (options->num_allow_users >= MAX_ALLOW_USERS)
|
||||
|
@ -11,7 +11,7 @@
|
||||
* called by a name other than "ssh" or "Secure Shell".
|
||||
*/
|
||||
|
||||
/* RCSID("$OpenBSD: servconf.h,v 1.28 2000/09/07 20:27:53 deraadt Exp $"); */
|
||||
/* RCSID("$OpenBSD: servconf.h,v 1.30 2000/10/14 12:12:09 markus Exp $"); */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#ifndef SERVCONF_H
|
||||
@ -84,6 +84,7 @@ typedef struct {
|
||||
#endif
|
||||
int password_authentication; /* If true, permit password
|
||||
* authentication. */
|
||||
int kbd_interactive_authentication; /* If true, permit */
|
||||
#ifdef SKEY
|
||||
int skey_authentication; /* If true, permit s/key
|
||||
* authentication. */
|
||||
@ -91,6 +92,7 @@ typedef struct {
|
||||
int permit_empty_passwd; /* If false, do not permit empty
|
||||
* passwords. */
|
||||
int use_login; /* If true, login(1) is used */
|
||||
int allow_tcp_forwarding;
|
||||
unsigned int num_allow_users;
|
||||
char *allow_users[MAX_ALLOW_USERS];
|
||||
unsigned int num_deny_users;
|
||||
|
@ -33,7 +33,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: session.c,v 1.37 2000/09/07 20:27:53 deraadt Exp $");
|
||||
RCSID("$OpenBSD: session.c,v 1.42 2000/10/27 07:32:18 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "xmalloc.h"
|
||||
@ -41,7 +41,6 @@ RCSID("$FreeBSD$");
|
||||
#include "pty.h"
|
||||
#include "packet.h"
|
||||
#include "buffer.h"
|
||||
#include "cipher.h"
|
||||
#include "mpaux.h"
|
||||
#include "servconf.h"
|
||||
#include "uidswap.h"
|
||||
@ -206,7 +205,7 @@ do_authenticated(struct passwd * pw)
|
||||
* by the client telling us, so we can equally well trust the client
|
||||
* not to request anything bogus.)
|
||||
*/
|
||||
if (!no_port_forwarding_flag)
|
||||
if (!no_port_forwarding_flag && options.allow_tcp_forwarding)
|
||||
channel_permit_all_opens();
|
||||
|
||||
s = session_new();
|
||||
@ -358,6 +357,10 @@ do_authenticated(struct passwd * pw)
|
||||
debug("Port forwarding not permitted for this authentication.");
|
||||
break;
|
||||
}
|
||||
if (!options.allow_tcp_forwarding) {
|
||||
debug("Port forwarding not permitted.");
|
||||
break;
|
||||
}
|
||||
debug("Received TCP/IP port forwarding request.");
|
||||
channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports);
|
||||
success = 1;
|
||||
@ -445,8 +448,14 @@ do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
|
||||
if (s == NULL)
|
||||
fatal("do_exec_no_pty: no session");
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
session_proctitle(s);
|
||||
|
||||
#ifdef USE_PAM
|
||||
do_pam_setcred();
|
||||
#endif /* USE_PAM */
|
||||
|
||||
/* Fork the child. */
|
||||
if ((pid = fork()) == 0) {
|
||||
/* Child. Reinitialize the log since the pid has changed. */
|
||||
@ -551,6 +560,11 @@ do_exec_pty(Session *s, const char *command, struct passwd * pw)
|
||||
ptyfd = s->ptyfd;
|
||||
ttyfd = s->ttyfd;
|
||||
|
||||
#ifdef USE_PAM
|
||||
do_pam_session(pw->pw_name, s->tty);
|
||||
do_pam_setcred();
|
||||
#endif /* USE_PAM */
|
||||
|
||||
/* Fork the child. */
|
||||
if ((pid = fork()) == 0) {
|
||||
/* Child. Reinitialize the log because the pid has changed. */
|
||||
@ -645,7 +659,6 @@ do_login(Session *s, const char *command)
|
||||
struct passwd * pw = s->pw;
|
||||
pid_t pid = getpid();
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_cap_t *lc;
|
||||
char *fname;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifdef __FreeBSD__
|
||||
@ -679,7 +692,20 @@ do_login(Session *s, const char *command)
|
||||
record_login(pid, s->tty, pw->pw_name, pw->pw_uid,
|
||||
get_remote_name_or_ip(), (struct sockaddr *)&from);
|
||||
|
||||
/* Done if .hushlogin exists. */
|
||||
#ifdef USE_PAM
|
||||
/*
|
||||
* If password change is needed, do it now.
|
||||
* This needs to occur before the ~/.hushlogin check.
|
||||
*/
|
||||
if (pam_password_change_required()) {
|
||||
print_pam_messages();
|
||||
do_pam_chauthtok();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Done if .hushlogin exists or a command given. */
|
||||
if (command != NULL)
|
||||
return;
|
||||
snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
lc = login_getpwclass(pw);
|
||||
@ -687,8 +713,12 @@ do_login(Session *s, const char *command)
|
||||
lc = login_getclassbyname(NULL, pw);
|
||||
quiet_login = login_getcapbool(lc, "hushlogin", quiet_login) || stat(buf, &st) >= 0;
|
||||
#else
|
||||
quiet_login = stat(line, &st) >= 0;
|
||||
quiet_login = stat(buf, &st) >= 0;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifdef USE_PAM
|
||||
if (!quiet_login && !pam_password_change_required())
|
||||
print_pam_messages();
|
||||
#endif /* USE_PAM */
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
if (pw->pw_change || pw->pw_expire)
|
||||
@ -707,7 +737,9 @@ do_login(Session *s, const char *command)
|
||||
"Sorry -- your password has expired.\n");
|
||||
log("%s Password expired - forcing change",
|
||||
pw->pw_name);
|
||||
newcommand = _PATH_CHPASS;
|
||||
if (newcommand != NULL)
|
||||
xfree(newcommand);
|
||||
newcommand = xstrdup(_PATH_CHPASS);
|
||||
} else if (pw->pw_change - tv.tv_sec < warntime &&
|
||||
!quiet_login)
|
||||
(void)printf(
|
||||
@ -718,6 +750,7 @@ do_login(Session *s, const char *command)
|
||||
warntime = login_getcaptime(lc, "warnexpire",
|
||||
DEFAULT_WARN, DEFAULT_WARN);
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
#ifndef USE_PAM
|
||||
if (pw->pw_expire) {
|
||||
if (tv.tv_sec >= pw->pw_expire) {
|
||||
(void)printf(
|
||||
@ -732,6 +765,7 @@ do_login(Session *s, const char *command)
|
||||
"Warning: your account expires on %s",
|
||||
ctime(&pw->pw_expire));
|
||||
}
|
||||
#endif /* !USE_PAM */
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
@ -759,9 +793,7 @@ do_login(Session *s, const char *command)
|
||||
/* Remove the trailing newline. */
|
||||
if (strchr(time_string, '\n'))
|
||||
*strchr(time_string, '\n') = 0;
|
||||
/* Display the last login time. Host if displayed
|
||||
if known. */
|
||||
if (strcmp(buf, "") == 0)
|
||||
if (strcmp(hostname, "") == 0)
|
||||
printf("Last login: %s\r\n", time_string);
|
||||
else
|
||||
printf("Last login: %s from %s\r\n", time_string, hostname);
|
||||
@ -805,6 +837,7 @@ do_login(Session *s, const char *command)
|
||||
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_close(lc);
|
||||
lc = NULL;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
return newcommand;
|
||||
}
|
||||
@ -889,6 +922,37 @@ read_environment_file(char ***env, unsigned int *envsize,
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
#ifdef USE_PAM
|
||||
/*
|
||||
* Sets any environment variables which have been specified by PAM
|
||||
*/
|
||||
void do_pam_environment(char ***env, int *envsize)
|
||||
{
|
||||
char *equals, var_name[512], var_val[512];
|
||||
char **pam_env;
|
||||
int i;
|
||||
|
||||
if ((pam_env = fetch_pam_environment()) == NULL)
|
||||
return;
|
||||
|
||||
for(i = 0; pam_env[i] != NULL; i++) {
|
||||
if ((equals = strstr(pam_env[i], "=")) == NULL)
|
||||
continue;
|
||||
|
||||
if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
|
||||
memset(var_name, '\0', sizeof(var_name));
|
||||
memset(var_val, '\0', sizeof(var_val));
|
||||
|
||||
strncpy(var_name, pam_env[i], equals - pam_env[i]);
|
||||
strcpy(var_val, equals + 1);
|
||||
|
||||
child_set_env(env, envsize, var_name, var_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* USE_PAM */
|
||||
|
||||
|
||||
/*
|
||||
* Performs common processing for the child, such as setting up the
|
||||
* environment, closing extra file descriptors, setting the user and group
|
||||
@ -908,14 +972,12 @@ do_child(const char *command, struct passwd * pw, const char *term,
|
||||
extern char **environ;
|
||||
struct stat st;
|
||||
char *argv[10];
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_cap_t *lc;
|
||||
#endif
|
||||
|
||||
/* login(1) is only called if we execute the login shell */
|
||||
if (options.use_login && command != NULL)
|
||||
options.use_login = 0;
|
||||
|
||||
#ifndef USE_PAM
|
||||
if (!options.use_login) {
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
lc = login_getpwclass(pw);
|
||||
@ -937,6 +999,7 @@ do_child(const char *command, struct passwd * pw, const char *term,
|
||||
exit(254);
|
||||
}
|
||||
}
|
||||
#endif /* !USE_PAM */
|
||||
/* Set login name, 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. */
|
||||
@ -1119,6 +1182,11 @@ do_child(const char *command, struct passwd * pw, const char *term,
|
||||
}
|
||||
#endif /* KRB5 */
|
||||
|
||||
#ifdef USE_PAM
|
||||
/* Pull in any environment variables that may have been set by PAM. */
|
||||
do_pam_environment(&env, &envsize);
|
||||
#endif /* USE_PAM */
|
||||
|
||||
if (xauthfile)
|
||||
child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
|
||||
if (auth_get_socket_name() != NULL)
|
||||
@ -1215,6 +1283,7 @@ do_child(const char *command, struct passwd * pw, const char *term,
|
||||
}
|
||||
#ifdef HAVE_LOGIN_CAP
|
||||
login_close(lc);
|
||||
lc = NULL;
|
||||
#endif /* HAVE_LOGIN_CAP */
|
||||
|
||||
/*
|
||||
@ -1702,7 +1771,8 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr)
|
||||
fatal("no channel for session %d", s->self);
|
||||
channel_set_fds(s->chanid,
|
||||
fdout, fdin, fderr,
|
||||
fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ);
|
||||
fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
|
||||
1);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1834,6 +1904,8 @@ session_close_by_channel(int id, void *arg)
|
||||
session_close(s);
|
||||
} else {
|
||||
/* notify child, delay session cleanup */
|
||||
if (s->pid <= 1)
|
||||
fatal("session_close_by_channel: Unsafe s->pid = %d", s->pid);
|
||||
if (kill(s->pid, (s->ttyfd == -1) ? SIGTERM : SIGHUP) < 0)
|
||||
error("session_close_by_channel: kill %d: %s",
|
||||
s->pid, strerror(errno));
|
||||
|
@ -35,6 +35,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$FreeBSD$");
|
||||
RCSID("$OpenBSD: ssh-add.c,v 1.22 2000/09/07 20:27:54 deraadt Exp $");
|
||||
|
||||
#include <openssl/evp.h>
|
||||
@ -101,6 +102,8 @@ ssh_askpass(char *askpass, char *msg)
|
||||
fatal("internal error: askpass undefined");
|
||||
if (pipe(p) < 0)
|
||||
fatal("ssh_askpass: pipe: %s", strerror(errno));
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
if ((pid = fork()) < 0)
|
||||
fatal("ssh_askpass: fork: %s", strerror(errno));
|
||||
if (pid == 0) {
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* $OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -35,7 +37,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: ssh-agent.c,v 1.35 2000/09/07 20:27:54 deraadt Exp $");
|
||||
RCSID("$OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "ssh.h"
|
||||
@ -55,6 +57,7 @@ RCSID("$FreeBSD$");
|
||||
#include "authfd.h"
|
||||
#include "dsa.h"
|
||||
#include "kex.h"
|
||||
#include "compat.h"
|
||||
|
||||
typedef struct {
|
||||
int fd;
|
||||
@ -232,6 +235,7 @@ process_sign_request2(SocketEntry *e)
|
||||
Key *key, *private;
|
||||
unsigned char *blob, *data, *signature = NULL;
|
||||
unsigned int blen, dlen, slen = 0;
|
||||
int flags;
|
||||
Buffer msg;
|
||||
int ok = -1;
|
||||
|
||||
@ -239,7 +243,10 @@ process_sign_request2(SocketEntry *e)
|
||||
|
||||
blob = buffer_get_string(&e->input, &blen);
|
||||
data = buffer_get_string(&e->input, &dlen);
|
||||
buffer_get_int(&e->input); /* flags, unused */
|
||||
|
||||
flags = buffer_get_int(&e->input);
|
||||
if (flags & SSH_AGENT_OLD_SIGNATURE)
|
||||
datafellows = SSH_BUG_SIGBLOB;
|
||||
|
||||
key = dsa_key_from_blob(blob, blen);
|
||||
if (key != NULL) {
|
||||
@ -770,8 +777,11 @@ main(int ac, char **av)
|
||||
printf("echo Agent pid %d;\n", pid);
|
||||
exit(0);
|
||||
}
|
||||
setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
|
||||
setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
|
||||
if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
|
||||
setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
|
||||
perror("setenv");
|
||||
exit(1);
|
||||
}
|
||||
execvp(av[0], av);
|
||||
perror(av[0]);
|
||||
exit(1);
|
||||
|
@ -370,15 +370,16 @@ It is believed to be secure.
|
||||
(triple-des) is an encrypt-decrypt-encrypt triple with three different keys.
|
||||
It is presumably more secure than the
|
||||
.Ar des
|
||||
cipher which is no longer supported in
|
||||
cipher which is no longer fully supported in
|
||||
.Nm ssh .
|
||||
.Ar blowfish
|
||||
is a fast block cipher, it appears very secure and is much faster than
|
||||
.Ar 3des .
|
||||
.It Fl c Ar "3des-cbc,blowfish-cbc,arcfour,cast128-cbc"
|
||||
Additionally, for protocol version 2 a comma-separated list of ciphers can
|
||||
be specified in order of preference. Protocol version 2 supports
|
||||
3DES, Blowfish and CAST128 in CBC mode and Arcfour.
|
||||
be specified in order of preference.
|
||||
Protocol version 2 supports 3DES, Blowfish, and CAST128 in CBC mode
|
||||
and Arcfour.
|
||||
.It Fl e Ar ch|^ch|none
|
||||
Sets the escape character for sessions with a pty (default:
|
||||
.Ql ~ ) .
|
||||
@ -486,6 +487,8 @@ debugging connection, authentication, and configuration problems.
|
||||
The verbose mode is also used to display
|
||||
.Xr skey 1
|
||||
challenges, if the user entered "s/key" as password.
|
||||
Multiple -v options increases the verbosity.
|
||||
Maximum is 3.
|
||||
.It Fl x
|
||||
Disables X11 forwarding.
|
||||
.It Fl X
|
||||
@ -627,9 +630,10 @@ If the option is set to
|
||||
.Dq no ,
|
||||
the check will not be executed.
|
||||
.It Cm Cipher
|
||||
Specifies the cipher to use for encrypting the session.
|
||||
Specifies the cipher to use for encrypting the session
|
||||
in protocol version 1.
|
||||
Currently,
|
||||
.Dq blowfish ,
|
||||
.Dq blowfish
|
||||
and
|
||||
.Dq 3des
|
||||
are supported.
|
||||
@ -640,7 +644,7 @@ Specifies the ciphers allowed for protocol version 2
|
||||
in order of preference.
|
||||
Multiple ciphers must be comma-separated.
|
||||
The default is
|
||||
.Dq 3des-cbc,blowfish-cbc,arcfour,cast128-cbc .
|
||||
.Dq 3des-cbc,blowfish-cbc,cast128-cbc,arcfour .
|
||||
.It Cm Compression
|
||||
Specifies whether to use compression.
|
||||
The argument must be
|
||||
|
@ -39,7 +39,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: ssh.c,v 1.65 2000/09/07 20:40:30 markus Exp $");
|
||||
RCSID("$OpenBSD: ssh.c,v 1.69 2000/10/27 07:32:19 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <openssl/evp.h>
|
||||
@ -148,6 +148,7 @@ usage()
|
||||
fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n");
|
||||
fprintf(stderr, " -T Do not allocate a tty.\n");
|
||||
fprintf(stderr, " -v Verbose; display verbose debugging messages.\n");
|
||||
fprintf(stderr, " Multiple -v increases verbosity.\n");
|
||||
fprintf(stderr, " -V Display version number only.\n");
|
||||
fprintf(stderr, " -P Don't allocate a privileged port.\n");
|
||||
fprintf(stderr, " -q Quiet; don't display any warning messages.\n");
|
||||
@ -366,6 +367,16 @@ main(int ac, char **av)
|
||||
tty_flag = 1;
|
||||
break;
|
||||
case 'v':
|
||||
if (0 == debug_flag) {
|
||||
debug_flag = 1;
|
||||
options.log_level = SYSLOG_LEVEL_DEBUG1;
|
||||
} else if (options.log_level < SYSLOG_LEVEL_DEBUG3) {
|
||||
options.log_level++;
|
||||
break;
|
||||
} else {
|
||||
fatal("Too high debugging level.\n");
|
||||
}
|
||||
/* fallthrough */
|
||||
case 'V':
|
||||
fprintf(stderr, "SSH Version %s, protocol versions %d.%d/%d.%d.\n",
|
||||
SSH_VERSION,
|
||||
@ -374,8 +385,6 @@ main(int ac, char **av)
|
||||
fprintf(stderr, "Compiled with SSL (0x%8.8lx).\n", SSLeay());
|
||||
if (opt == 'V')
|
||||
exit(0);
|
||||
debug_flag = 1;
|
||||
options.log_level = SYSLOG_LEVEL_DEBUG;
|
||||
break;
|
||||
case 'q':
|
||||
options.log_level = SYSLOG_LEVEL_QUIET;
|
||||
@ -400,11 +409,12 @@ main(int ac, char **av)
|
||||
options.cipher = SSH_CIPHER_ILLEGAL;
|
||||
} else {
|
||||
/* SSH1 only */
|
||||
options.cipher = cipher_number(optarg);
|
||||
if (options.cipher == -1) {
|
||||
Cipher *c = cipher_by_name(optarg);
|
||||
if (c == NULL || c->number < 0) {
|
||||
fprintf(stderr, "Unknown cipher type '%s'\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
options.cipher = c->number;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
@ -557,22 +567,6 @@ main(int ac, char **av)
|
||||
if (options.hostname != NULL)
|
||||
host = options.hostname;
|
||||
|
||||
/* Find canonic host name. */
|
||||
if (strchr(host, '.') == 0) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *ai = NULL;
|
||||
int errgai;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = IPv4or6;
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
errgai = getaddrinfo(host, NULL, &hints, &ai);
|
||||
if (errgai == 0) {
|
||||
if (ai->ai_canonname != NULL)
|
||||
host = xstrdup(ai->ai_canonname);
|
||||
freeaddrinfo(ai);
|
||||
}
|
||||
}
|
||||
/* Disable rhosts authentication if not running as root. */
|
||||
if (original_effective_uid != 0 || !options.use_privileged_port) {
|
||||
options.rhosts_authentication = 0;
|
||||
@ -604,7 +598,7 @@ main(int ac, char **av)
|
||||
* if rhosts_{rsa_}authentication is enabled.
|
||||
*/
|
||||
|
||||
ok = ssh_connect(host, &hostaddr, options.port,
|
||||
ok = ssh_connect(&host, &hostaddr, options.port,
|
||||
options.connection_attempts,
|
||||
!options.rhosts_authentication &&
|
||||
!options.rhosts_rsa_authentication,
|
||||
@ -993,6 +987,14 @@ ssh_session2(void)
|
||||
if (in < 0 || out < 0 || err < 0)
|
||||
fatal("dup() in/out/err failed");
|
||||
|
||||
/* enable nonblocking unless tty */
|
||||
if (!isatty(in))
|
||||
set_nonblock(in);
|
||||
if (!isatty(out))
|
||||
set_nonblock(out);
|
||||
if (!isatty(err))
|
||||
set_nonblock(err);
|
||||
|
||||
/* should be pre-session */
|
||||
init_local_fwd();
|
||||
|
||||
@ -1010,7 +1012,7 @@ ssh_session2(void)
|
||||
id = channel_new(
|
||||
"session", SSH_CHANNEL_OPENING, in, out, err,
|
||||
window, packetmax, CHAN_EXTENDED_WRITE,
|
||||
xstrdup("client-session"));
|
||||
xstrdup("client-session"), /*nonblock*/0);
|
||||
|
||||
channel_open(id);
|
||||
channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, client_init, (void *)0);
|
||||
|
@ -12,7 +12,7 @@
|
||||
* called by a name other than "ssh" or "Secure Shell".
|
||||
*/
|
||||
|
||||
/* RCSID("$OpenBSD: ssh.h,v 1.50 2000/09/07 20:27:54 deraadt Exp $"); */
|
||||
/* RCSID("$OpenBSD: ssh.h,v 1.54 2000/10/11 20:27:24 markus Exp $"); */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
#ifndef SSH_H
|
||||
@ -21,14 +21,6 @@
|
||||
#include "rsa.h"
|
||||
#include "cipher.h"
|
||||
|
||||
/*
|
||||
* XXX
|
||||
* The default cipher used if IDEA is not supported by the remote host. It is
|
||||
* recommended that this be one of the mandatory ciphers (DES, 3DES), though
|
||||
* that is not required.
|
||||
*/
|
||||
#define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES
|
||||
|
||||
/* Cipher used for encrypting authentication files. */
|
||||
#define SSH_AUTHFILE_CIPHER SSH_CIPHER_3DES
|
||||
|
||||
@ -82,6 +74,7 @@
|
||||
#define SERVER_CONFIG_FILE ETCDIR "/sshd_config"
|
||||
#define HOST_CONFIG_FILE ETCDIR "/ssh_config"
|
||||
#define HOST_DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key"
|
||||
#define DH_PRIMES ETCDIR "/primes"
|
||||
|
||||
#define SSH_PROGRAM "/usr/bin/ssh"
|
||||
|
||||
@ -296,7 +289,7 @@ void record_logout(pid_t pid, const char *ttyname);
|
||||
* packet_set_connection for the connection.
|
||||
*/
|
||||
int
|
||||
ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
ssh_connect(char **host, struct sockaddr_storage * hostaddr,
|
||||
u_short port, int connection_attempts,
|
||||
int anonymous, uid_t original_real_uid,
|
||||
const char *proxy_command);
|
||||
@ -393,7 +386,7 @@ int auth_rsa_challenge_dialog(RSA *pk);
|
||||
* passphrase (allocated with xmalloc). Exits if EOF is encountered. If
|
||||
* from_stdin is true, the passphrase will be read from stdin instead.
|
||||
*/
|
||||
char *read_passphrase(const char *prompt, int from_stdin);
|
||||
char *read_passphrase(char *prompt, int from_stdin);
|
||||
|
||||
|
||||
/*------------ Definitions for logging. -----------------------*/
|
||||
@ -419,7 +412,9 @@ typedef enum {
|
||||
SYSLOG_LEVEL_ERROR,
|
||||
SYSLOG_LEVEL_INFO,
|
||||
SYSLOG_LEVEL_VERBOSE,
|
||||
SYSLOG_LEVEL_DEBUG
|
||||
SYSLOG_LEVEL_DEBUG1,
|
||||
SYSLOG_LEVEL_DEBUG2,
|
||||
SYSLOG_LEVEL_DEBUG3
|
||||
} LogLevel;
|
||||
/* Initializes logging. */
|
||||
void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr);
|
||||
@ -437,6 +432,8 @@ void error(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
void log(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
void verbose(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
void debug(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
void debug2(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
void debug3(const char *fmt,...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
/* same as fatal() but w/o logging */
|
||||
void fatal_cleanup(void);
|
||||
@ -535,4 +532,8 @@ int auth_skey_password(struct passwd * pw, const char *password);
|
||||
/* AF_UNSPEC or AF_INET or AF_INET6 */
|
||||
extern int IPv4or6;
|
||||
|
||||
#ifdef USE_PAM
|
||||
#include "auth-pam.h"
|
||||
#endif /* USE_PAM */
|
||||
|
||||
#endif /* SSH_H */
|
||||
|
@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: sshconnect.c,v 1.78 2000/09/07 20:27:54 deraadt Exp $");
|
||||
RCSID("$OpenBSD: sshconnect.c,v 1.79 2000/09/17 15:52:51 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <openssl/bn.h>
|
||||
@ -173,6 +173,7 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family)
|
||||
|
||||
/*
|
||||
* Opens a TCP/IP connection to the remote server on the given host.
|
||||
* The canonical host name used to connect will be returned in *host.
|
||||
* The address of the remote host will be returned in hostaddr.
|
||||
* If port is 0, the default port will be used. If anonymous is zero,
|
||||
* a privileged port will be allocated to make the connection.
|
||||
@ -183,7 +184,7 @@ ssh_create_socket(uid_t original_real_uid, int privileged, int family)
|
||||
* the daemon.
|
||||
*/
|
||||
int
|
||||
ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
ssh_connect(char **host, struct sockaddr_storage * hostaddr,
|
||||
u_short port, int connection_attempts,
|
||||
int anonymous, uid_t original_real_uid,
|
||||
const char *proxy_command)
|
||||
@ -208,16 +209,17 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
}
|
||||
/* If a proxy command is given, connect using it. */
|
||||
if (proxy_command != NULL)
|
||||
return ssh_proxy_connect(host, port, original_real_uid, proxy_command);
|
||||
return ssh_proxy_connect(*host, port, original_real_uid, proxy_command);
|
||||
|
||||
/* No proxy command. */
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = IPv4or6;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
snprintf(strport, sizeof strport, "%d", port);
|
||||
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
|
||||
fatal("%s: %.100s: %s", __progname, host,
|
||||
if ((gaierr = getaddrinfo(*host, strport, &hints, &aitop)) != 0)
|
||||
fatal("%s: %.100s: %s", __progname, *host,
|
||||
gai_strerror(gaierr));
|
||||
|
||||
/*
|
||||
@ -241,7 +243,7 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
continue;
|
||||
}
|
||||
debug("Connecting to %.200s [%.100s] port %s.",
|
||||
host, ntop, strport);
|
||||
ai->ai_canonname, ntop, strport);
|
||||
|
||||
/* Create a socket for connecting. */
|
||||
sock = ssh_create_socket(original_real_uid,
|
||||
@ -273,8 +275,11 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
|
||||
close(sock);
|
||||
}
|
||||
}
|
||||
if (ai)
|
||||
if (ai) {
|
||||
if (ai->ai_canonname != NULL)
|
||||
*host = xstrdup(ai->ai_canonname);
|
||||
break; /* Successful connection. */
|
||||
}
|
||||
|
||||
/* Sleep a moment before retrying. */
|
||||
sleep(1);
|
||||
@ -437,8 +442,10 @@ read_yes_or_no(const char *prompt, int defval)
|
||||
retval = defval;
|
||||
if (strcmp(buf, "yes") == 0)
|
||||
retval = 1;
|
||||
if (strcmp(buf, "no") == 0)
|
||||
else if (strcmp(buf, "no") == 0)
|
||||
retval = 0;
|
||||
else
|
||||
fprintf(stderr, "Please type 'yes' or 'no'.\n");
|
||||
|
||||
if (retval != -1) {
|
||||
if (f != stdin)
|
||||
|
@ -20,6 +20,8 @@
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
#ifndef SSHCONNECT_H
|
||||
#define SSHCONNECT_H
|
||||
|
@ -13,7 +13,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: sshconnect1.c,v 1.6 2000/09/07 20:27:54 deraadt Exp $");
|
||||
RCSID("$OpenBSD: sshconnect1.c,v 1.8 2000/10/12 09:59:19 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include <openssl/bn.h>
|
||||
@ -26,7 +26,6 @@ RCSID("$FreeBSD$");
|
||||
#include "ssh.h"
|
||||
#include "buffer.h"
|
||||
#include "packet.h"
|
||||
#include "cipher.h"
|
||||
#include "mpaux.h"
|
||||
#include "uidswap.h"
|
||||
#include "readconf.h"
|
||||
@ -837,17 +836,11 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
|
||||
|
||||
if (options.cipher == SSH_CIPHER_ILLEGAL) {
|
||||
log("No valid SSH1 cipher, using %.100s instead.",
|
||||
cipher_name(SSH_FALLBACK_CIPHER));
|
||||
options.cipher = SSH_FALLBACK_CIPHER;
|
||||
cipher_name(ssh_cipher_default));
|
||||
options.cipher = ssh_cipher_default;
|
||||
} else if (options.cipher == SSH_CIPHER_NOT_SET) {
|
||||
if (cipher_mask1() & supported_ciphers & (1 << ssh_cipher_default))
|
||||
if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default))
|
||||
options.cipher = ssh_cipher_default;
|
||||
else {
|
||||
debug("Cipher %s not supported, using %.100s instead.",
|
||||
cipher_name(ssh_cipher_default),
|
||||
cipher_name(SSH_FALLBACK_CIPHER));
|
||||
options.cipher = SSH_FALLBACK_CIPHER;
|
||||
}
|
||||
}
|
||||
/* Check that the selected cipher is supported. */
|
||||
if (!(supported_ciphers & (1 << options.cipher)))
|
||||
|
@ -188,6 +188,8 @@ The server sends verbose debug output to the system
|
||||
log, and does not put itself in the background.
|
||||
The server also will not fork and will only process one connection.
|
||||
This option is only intended for debugging for the server.
|
||||
Multiple -d options increases the debugging level.
|
||||
Maximum is 3.
|
||||
.It Fl f Ar configuration_file
|
||||
Specifies the name of the configuration file.
|
||||
The default is
|
||||
@ -256,12 +258,13 @@ file.
|
||||
.It Fl Q
|
||||
Do not print an error message if RSA support is missing.
|
||||
.It Fl V Ar client_protocol_id
|
||||
SSH2 compatibility mode.
|
||||
SSH-2 compatibility mode.
|
||||
When this option is specified
|
||||
.Nm
|
||||
assumes the client has sent the supplied version string
|
||||
and skips the
|
||||
Protocol Version Identification Exchange.
|
||||
This option is not intended to be called directly.
|
||||
.It Fl 4
|
||||
Forces
|
||||
.Nm
|
||||
@ -302,6 +305,14 @@ wildcards in the patterns.
|
||||
Only group names are valid; a numerical group ID isn't recognized.
|
||||
By default login is allowed regardless of the primary group.
|
||||
.Pp
|
||||
.It Cm AllowTcpForwarding
|
||||
Specifies whether TCP forwarding is permitted.
|
||||
The default is
|
||||
.Dq yes .
|
||||
Note that disabling TCP forwarding does not improve security unless
|
||||
users are also denied shell access, as they can always install their
|
||||
own forwarders.
|
||||
.Pp
|
||||
.It Cm AllowUsers
|
||||
This keyword can be followed by a number of user names, separated
|
||||
by spaces.
|
||||
@ -450,7 +461,8 @@ Specifies whether Kerberos authentication is allowed.
|
||||
This can be in the form of a Kerberos ticket, or if
|
||||
.Cm PasswordAuthentication
|
||||
is yes, the password provided by the user will be validated through
|
||||
the Kerberos KDC. To use this option, the server needs a
|
||||
the Kerberos KDC.
|
||||
To use this option, the server needs a
|
||||
Kerberos servtab which allows the verification of the KDC's identity.
|
||||
Default is
|
||||
.Dq yes .
|
||||
@ -458,8 +470,7 @@ Default is
|
||||
If set then if password authentication through Kerberos fails then
|
||||
the password will be validated via any additional local mechanism
|
||||
such as
|
||||
.Pa /etc/passwd
|
||||
or SecurID.
|
||||
.Pa /etc/passwd .
|
||||
Default is
|
||||
.Dq yes .
|
||||
.It Cm KerberosTgtPassing
|
||||
@ -515,7 +526,7 @@ The default is 10.
|
||||
Alternatively, random early drop can be enabled by specifying
|
||||
the three colon separated values
|
||||
.Dq start:rate:full
|
||||
(e.g. "10:30:60").
|
||||
(e.g., "10:30:60").
|
||||
.Nm
|
||||
will refuse connection attempts with a probabillity of
|
||||
.Dq rate/100
|
||||
@ -642,8 +653,9 @@ directory or files world-writable.
|
||||
The default is
|
||||
.Dq yes .
|
||||
.It Cm Subsystem
|
||||
Configures an external subsystem (e.g. file transfer daemon).
|
||||
Arguments should be a subsystem name and a command to execute upon subsystem request.
|
||||
Configures an external subsystem (e.g., file transfer daemon).
|
||||
Arguments should be a subsystem name and a command to execute upon subsystem
|
||||
request.
|
||||
The command
|
||||
.Xr sftp-server 8
|
||||
implements the
|
||||
|
@ -40,7 +40,7 @@
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
RCSID("$OpenBSD: sshd.c,v 1.126 2000/09/07 20:27:55 deraadt Exp $");
|
||||
RCSID("$OpenBSD: sshd.c,v 1.132 2000/10/13 18:34:46 markus Exp $");
|
||||
RCSID("$FreeBSD$");
|
||||
|
||||
#include "xmalloc.h"
|
||||
@ -48,7 +48,6 @@ RCSID("$FreeBSD$");
|
||||
#include "ssh.h"
|
||||
#include "pty.h"
|
||||
#include "packet.h"
|
||||
#include "cipher.h"
|
||||
#include "mpaux.h"
|
||||
#include "servconf.h"
|
||||
#include "uidswap.h"
|
||||
@ -66,6 +65,7 @@ RCSID("$FreeBSD$");
|
||||
#include <openssl/rsa.h>
|
||||
#include "key.h"
|
||||
#include "dsa.h"
|
||||
#include "dh.h"
|
||||
|
||||
#include "auth.h"
|
||||
#include "myproposal.h"
|
||||
@ -200,6 +200,9 @@ unsigned int utmp_len = MAXHOSTNAMELEN;
|
||||
void do_ssh1_kex();
|
||||
void do_ssh2_kex();
|
||||
|
||||
void ssh_dh1_server(Kex *, Buffer *_kexinit, Buffer *);
|
||||
void ssh_dhgex_server(Kex *, Buffer *_kexinit, Buffer *);
|
||||
|
||||
/*
|
||||
* Close all listening sockets
|
||||
*/
|
||||
@ -362,6 +365,10 @@ sshd_exchange_identification(int sock_in, int sock_out)
|
||||
if (buf[i] == '\r') {
|
||||
buf[i] = '\n';
|
||||
buf[i + 1] = 0;
|
||||
/* Kludge for F-Secure Macintosh < 1.0.2 */
|
||||
if (i == 12 &&
|
||||
strncmp(buf, "SSH-1.5-W1.0", 12) == 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
if (buf[i] == '\n') {
|
||||
@ -538,8 +545,15 @@ main(int ac, char **av)
|
||||
config_file_name = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debug_flag = 1;
|
||||
options.log_level = SYSLOG_LEVEL_DEBUG;
|
||||
if (0 == debug_flag) {
|
||||
debug_flag = 1;
|
||||
options.log_level = SYSLOG_LEVEL_DEBUG1;
|
||||
} else if (options.log_level < SYSLOG_LEVEL_DEBUG3) {
|
||||
options.log_level++;
|
||||
} else {
|
||||
fprintf(stderr, "Too high debugging level.\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'i':
|
||||
inetd_flag = 1;
|
||||
@ -555,8 +569,10 @@ main(int ac, char **av)
|
||||
break;
|
||||
case 'p':
|
||||
options.ports_from_cmdline = 1;
|
||||
if (options.num_ports >= MAX_PORTS)
|
||||
fatal("too many ports.\n");
|
||||
if (options.num_ports >= MAX_PORTS) {
|
||||
fprintf(stderr, "too many ports.\n");
|
||||
exit(1);
|
||||
}
|
||||
options.ports[options.num_ports++] = atoi(optarg);
|
||||
break;
|
||||
case 'g':
|
||||
@ -582,7 +598,7 @@ main(int ac, char **av)
|
||||
fprintf(stderr, "Usage: %s [options]\n", av0);
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, " -f file Configuration file (default %s)\n", SERVER_CONFIG_FILE);
|
||||
fprintf(stderr, " -d Debugging mode\n");
|
||||
fprintf(stderr, " -d Debugging mode (multiple -d means more debugging)\n");
|
||||
fprintf(stderr, " -i Started from inetd\n");
|
||||
fprintf(stderr, " -q Quiet (no logging)\n");
|
||||
fprintf(stderr, " -p port Listen on the specified port (default: 22)\n");
|
||||
@ -1137,6 +1153,11 @@ main(int ac, char **av)
|
||||
|
||||
/* The connection has been terminated. */
|
||||
verbose("Closing connection to %.100s", remote_ip);
|
||||
|
||||
#ifdef USE_PAM
|
||||
finish_pam();
|
||||
#endif /* USE_PAM */
|
||||
|
||||
packet_close();
|
||||
exit(0);
|
||||
}
|
||||
@ -1194,7 +1215,7 @@ do_ssh1_kex()
|
||||
packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN);
|
||||
|
||||
/* Declare which ciphers we support. */
|
||||
packet_put_int(cipher_mask1());
|
||||
packet_put_int(cipher_mask_ssh1(0));
|
||||
|
||||
/* Declare supported authentication types. */
|
||||
auth_mask = 0;
|
||||
@ -1245,7 +1266,7 @@ do_ssh1_kex()
|
||||
/* Get cipher type and check whether we accept this. */
|
||||
cipher_type = packet_get_char();
|
||||
|
||||
if (!(cipher_mask() & (1 << cipher_type)))
|
||||
if (!(cipher_mask_ssh1(0) & (1 << cipher_type)))
|
||||
packet_disconnect("Warning: client selects unsupported cipher.");
|
||||
|
||||
/* Get check bytes from the packet. These must match those we
|
||||
@ -1349,18 +1370,8 @@ do_ssh2_kex()
|
||||
{
|
||||
Buffer *server_kexinit;
|
||||
Buffer *client_kexinit;
|
||||
int payload_len, dlen;
|
||||
int slen;
|
||||
unsigned int klen, kout;
|
||||
unsigned char *signature = NULL;
|
||||
unsigned char *server_host_key_blob = NULL;
|
||||
unsigned int sbloblen;
|
||||
DH *dh;
|
||||
BIGNUM *dh_client_pub = 0;
|
||||
BIGNUM *shared_secret = 0;
|
||||
int payload_len;
|
||||
int i;
|
||||
unsigned char *kbuf;
|
||||
unsigned char *hash;
|
||||
Kex *kex;
|
||||
char *cprop[PROPOSAL_MAX];
|
||||
|
||||
@ -1380,8 +1391,63 @@ do_ssh2_kex()
|
||||
for (i = 0; i < PROPOSAL_MAX; i++)
|
||||
xfree(cprop[i]);
|
||||
|
||||
/* KEXDH */
|
||||
switch (kex->kex_type) {
|
||||
case DH_GRP1_SHA1:
|
||||
ssh_dh1_server(kex, client_kexinit, server_kexinit);
|
||||
break;
|
||||
case DH_GEX_SHA1:
|
||||
ssh_dhgex_server(kex, client_kexinit, server_kexinit);
|
||||
break;
|
||||
default:
|
||||
fatal("Unsupported key exchange %d", kex->kex_type);
|
||||
}
|
||||
|
||||
debug("send SSH2_MSG_NEWKEYS.");
|
||||
packet_start(SSH2_MSG_NEWKEYS);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
debug("done: send SSH2_MSG_NEWKEYS.");
|
||||
|
||||
debug("Wait SSH2_MSG_NEWKEYS.");
|
||||
packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
|
||||
debug("GOT SSH2_MSG_NEWKEYS.");
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
/* send 1st encrypted/maced/compressed message */
|
||||
packet_start(SSH2_MSG_IGNORE);
|
||||
packet_put_cstring("markus");
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
#endif
|
||||
|
||||
debug("done: KEX2.");
|
||||
}
|
||||
|
||||
/*
|
||||
* SSH2 key exchange
|
||||
*/
|
||||
|
||||
/* diffie-hellman-group1-sha1 */
|
||||
|
||||
void
|
||||
ssh_dh1_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit)
|
||||
{
|
||||
#ifdef DEBUG_KEXDH
|
||||
int i;
|
||||
#endif
|
||||
int payload_len, dlen;
|
||||
int slen;
|
||||
unsigned char *signature = NULL;
|
||||
unsigned char *server_host_key_blob = NULL;
|
||||
unsigned int sbloblen;
|
||||
unsigned int klen, kout;
|
||||
unsigned char *kbuf;
|
||||
unsigned char *hash;
|
||||
BIGNUM *shared_secret = 0;
|
||||
DH *dh;
|
||||
BIGNUM *dh_client_pub = 0;
|
||||
|
||||
/* KEXDH */
|
||||
debug("Wait SSH2_MSG_KEXDH_INIT.");
|
||||
packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT);
|
||||
|
||||
@ -1393,7 +1459,7 @@ do_ssh2_kex()
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
fprintf(stderr, "\ndh_client_pub= ");
|
||||
bignum_print(dh_client_pub);
|
||||
BN_print_fp(stderr, dh_client_pub);
|
||||
fprintf(stderr, "\n");
|
||||
debug("bits %d", BN_num_bits(dh_client_pub));
|
||||
#endif
|
||||
@ -1403,12 +1469,13 @@ do_ssh2_kex()
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
fprintf(stderr, "\np= ");
|
||||
bignum_print(dh->p);
|
||||
BN_print_fp(stderr, dh->p);
|
||||
fprintf(stderr, "\ng= ");
|
||||
bignum_print(dh->g);
|
||||
bn_print(dh->g);
|
||||
fprintf(stderr, "\npub= ");
|
||||
bignum_print(dh->pub_key);
|
||||
BN_print_fp(stderr, dh->pub_key);
|
||||
fprintf(stderr, "\n");
|
||||
DHparams_print_fp(stderr, dh);
|
||||
#endif
|
||||
if (!dh_pub_is_valid(dh, dh_client_pub))
|
||||
packet_disconnect("bad client public DH value");
|
||||
@ -1431,7 +1498,8 @@ do_ssh2_kex()
|
||||
xfree(kbuf);
|
||||
|
||||
/* XXX precompute? */
|
||||
dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen);
|
||||
dsa_make_key_blob(sensitive_data.dsa_host_key,
|
||||
&server_host_key_blob, &sbloblen);
|
||||
|
||||
/* calc H */ /* XXX depends on 'kex' */
|
||||
hash = kex_hash(
|
||||
@ -1481,23 +1549,139 @@ do_ssh2_kex()
|
||||
|
||||
/* have keys, free DH */
|
||||
DH_free(dh);
|
||||
}
|
||||
|
||||
debug("send SSH2_MSG_NEWKEYS.");
|
||||
packet_start(SSH2_MSG_NEWKEYS);
|
||||
/* diffie-hellman-group-exchange-sha1 */
|
||||
|
||||
void
|
||||
ssh_dhgex_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit)
|
||||
{
|
||||
#ifdef DEBUG_KEXDH
|
||||
int i;
|
||||
#endif
|
||||
int payload_len, dlen;
|
||||
int slen, nbits;
|
||||
unsigned char *signature = NULL;
|
||||
unsigned char *server_host_key_blob = NULL;
|
||||
unsigned int sbloblen;
|
||||
unsigned int klen, kout;
|
||||
unsigned char *kbuf;
|
||||
unsigned char *hash;
|
||||
BIGNUM *shared_secret = 0;
|
||||
DH *dh;
|
||||
BIGNUM *dh_client_pub = 0;
|
||||
|
||||
/* KEXDHGEX */
|
||||
debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST.");
|
||||
packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_REQUEST);
|
||||
nbits = packet_get_int();
|
||||
dh = choose_dh(nbits);
|
||||
|
||||
debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP.");
|
||||
packet_start(SSH2_MSG_KEX_DH_GEX_GROUP);
|
||||
packet_put_bignum2(dh->p);
|
||||
packet_put_bignum2(dh->g);
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
debug("done: send SSH2_MSG_NEWKEYS.");
|
||||
|
||||
debug("Wait SSH2_MSG_NEWKEYS.");
|
||||
packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
|
||||
debug("GOT SSH2_MSG_NEWKEYS.");
|
||||
debug("Wait SSH2_MSG_KEX_DH_GEX_INIT.");
|
||||
packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_INIT);
|
||||
|
||||
/* key, cert */
|
||||
dh_client_pub = BN_new();
|
||||
if (dh_client_pub == NULL)
|
||||
fatal("dh_client_pub == NULL");
|
||||
packet_get_bignum2(dh_client_pub, &dlen);
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
/* send 1st encrypted/maced/compressed message */
|
||||
packet_start(SSH2_MSG_IGNORE);
|
||||
packet_put_cstring("markus");
|
||||
packet_send();
|
||||
packet_write_wait();
|
||||
fprintf(stderr, "\ndh_client_pub= ");
|
||||
BN_print_fp(stderr, dh_client_pub);
|
||||
fprintf(stderr, "\n");
|
||||
debug("bits %d", BN_num_bits(dh_client_pub));
|
||||
#endif
|
||||
debug("done: KEX2.");
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
fprintf(stderr, "\np= ");
|
||||
BN_print_fp(stderr, dh->p);
|
||||
fprintf(stderr, "\ng= ");
|
||||
bn_print(dh->g);
|
||||
fprintf(stderr, "\npub= ");
|
||||
BN_print_fp(stderr, dh->pub_key);
|
||||
fprintf(stderr, "\n");
|
||||
DHparams_print_fp(stderr, dh);
|
||||
#endif
|
||||
if (!dh_pub_is_valid(dh, dh_client_pub))
|
||||
packet_disconnect("bad client public DH value");
|
||||
|
||||
klen = DH_size(dh);
|
||||
kbuf = xmalloc(klen);
|
||||
kout = DH_compute_key(kbuf, dh_client_pub, dh);
|
||||
|
||||
#ifdef DEBUG_KEXDH
|
||||
debug("shared secret: len %d/%d", klen, kout);
|
||||
fprintf(stderr, "shared secret == ");
|
||||
for (i = 0; i< kout; i++)
|
||||
fprintf(stderr, "%02x", (kbuf[i])&0xff);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
shared_secret = BN_new();
|
||||
|
||||
BN_bin2bn(kbuf, kout, shared_secret);
|
||||
memset(kbuf, 0, klen);
|
||||
xfree(kbuf);
|
||||
|
||||
/* XXX precompute? */
|
||||
dsa_make_key_blob(sensitive_data.dsa_host_key,
|
||||
&server_host_key_blob, &sbloblen);
|
||||
|
||||
/* calc H */ /* XXX depends on 'kex' */
|
||||
hash = kex_hash_gex(
|
||||
client_version_string,
|
||||
server_version_string,
|
||||
buffer_ptr(client_kexinit), buffer_len(client_kexinit),
|
||||
buffer_ptr(server_kexinit), buffer_len(server_kexinit),
|
||||
(char *)server_host_key_blob, sbloblen,
|
||||
nbits, dh->p, dh->g,
|
||||
dh_client_pub,
|
||||
dh->pub_key,
|
||||
shared_secret
|
||||
);
|
||||
buffer_free(client_kexinit);
|
||||
buffer_free(server_kexinit);
|
||||
xfree(client_kexinit);
|
||||
xfree(server_kexinit);
|
||||
#ifdef DEBUG_KEXDH
|
||||
fprintf(stderr, "hash == ");
|
||||
for (i = 0; i< 20; i++)
|
||||
fprintf(stderr, "%02x", (hash[i])&0xff);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
/* save session id := H */
|
||||
/* XXX hashlen depends on KEX */
|
||||
session_id2_len = 20;
|
||||
session_id2 = xmalloc(session_id2_len);
|
||||
memcpy(session_id2, hash, session_id2_len);
|
||||
|
||||
/* sign H */
|
||||
/* XXX hashlen depends on KEX */
|
||||
dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20);
|
||||
|
||||
destroy_sensitive_data();
|
||||
|
||||
/* send server hostkey, DH pubkey 'f' and singed H */
|
||||
packet_start(SSH2_MSG_KEX_DH_GEX_REPLY);
|
||||
packet_put_string((char *)server_host_key_blob, sbloblen);
|
||||
packet_put_bignum2(dh->pub_key); /* f */
|
||||
packet_put_string((char *)signature, slen);
|
||||
packet_send();
|
||||
xfree(signature);
|
||||
xfree(server_host_key_blob);
|
||||
packet_write_wait();
|
||||
|
||||
kex_derive_keys(kex, hash, shared_secret);
|
||||
packet_set_kex(kex);
|
||||
|
||||
/* have keys, free DH */
|
||||
DH_free(dh);
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,12 @@ ServerKeyBits 768
|
||||
LoginGraceTime 120
|
||||
KeyRegenerationInterval 3600
|
||||
PermitRootLogin no
|
||||
# Rate-limit sshd connections to 5 connections per 10 seconds
|
||||
ConnectionsPerPeriod 5/10
|
||||
# Deprecated: rate-limit sshd connections to 5 connections per 10 seconds
|
||||
# ConnectionsPerPeriod 5/10
|
||||
|
||||
# After 10 unauthenticated connections, refuse 30% of the new ones, and
|
||||
# refuse any more than 60 total.
|
||||
MaxStartups 10:30:60
|
||||
# Don't read ~/.rhosts and ~/.shosts files
|
||||
IgnoreRhosts yes
|
||||
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
|
||||
@ -41,6 +45,7 @@ PasswordAuthentication yes
|
||||
PermitEmptyPasswords no
|
||||
# Uncomment to disable s/key passwords
|
||||
#SkeyAuthentication no
|
||||
#KbdInteractiveAuthentication yes
|
||||
|
||||
# To change Kerberos options
|
||||
#KerberosAuthentication no
|
||||
@ -56,4 +61,3 @@ CheckMail yes
|
||||
|
||||
# Uncomment if you want to enable sftp
|
||||
#Subsystem sftp /usr/libexec/sftp-server
|
||||
#MaxStartups 10:30:60
|
||||
|
Loading…
Reference in New Issue
Block a user