From ab6b35a1d6b61fa8d2bb7336a0ef6fa9378898ef Mon Sep 17 00:00:00 2001 From: green Date: Tue, 5 Dec 2000 02:55:12 +0000 Subject: [PATCH] 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 --- crypto/openssh/auth-krb4.c | 16 +- crypto/openssh/auth-pam.c | 777 +++++++++++++++++++++++++++++++++++ crypto/openssh/auth-pam.h | 38 ++ crypto/openssh/auth-passwd.c | 4 +- crypto/openssh/auth-rh-rsa.c | 6 +- crypto/openssh/auth-rsa.c | 24 +- crypto/openssh/auth-skey.c | 4 +- crypto/openssh/auth.c | 3 +- crypto/openssh/auth1.c | 285 ++++++------- crypto/openssh/auth2-skey.c | 11 +- crypto/openssh/auth2.c | 442 +++++++++++++------- crypto/openssh/authfd.c | 22 +- crypto/openssh/authfile.c | 44 +- crypto/openssh/canohost.c | 4 +- crypto/openssh/cipher.c | 736 ++++++++++++++++++--------------- crypto/openssh/cipher.h | 129 +++--- crypto/openssh/compat.c | 2 + crypto/openssh/readconf.c | 20 +- crypto/openssh/readconf.h | 4 +- crypto/openssh/rsa.c | 2 +- crypto/openssh/servconf.c | 23 +- crypto/openssh/servconf.h | 4 +- crypto/openssh/session.c | 100 ++++- crypto/openssh/ssh-add.c | 3 + crypto/openssh/ssh-agent.c | 18 +- crypto/openssh/ssh.1 | 16 +- crypto/openssh/ssh.c | 48 +-- crypto/openssh/ssh.h | 25 +- crypto/openssh/sshconnect.c | 23 +- crypto/openssh/sshconnect.h | 2 + crypto/openssh/sshconnect1.c | 15 +- crypto/openssh/sshd.8 | 26 +- crypto/openssh/sshd.c | 260 ++++++++++-- crypto/openssh/sshd_config | 10 +- 34 files changed, 2287 insertions(+), 859 deletions(-) create mode 100644 crypto/openssh/auth-pam.c create mode 100644 crypto/openssh/auth-pam.h diff --git a/crypto/openssh/auth-krb4.c b/crypto/openssh/auth-krb4.c index 4d30eef1f09b..a7bce5f7c6cb 100644 --- a/crypto/openssh/auth-krb4.c +++ b/crypto/openssh/auth-krb4.c @@ -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, diff --git a/crypto/openssh/auth-pam.c b/crypto/openssh/auth-pam.c new file mode 100644 index 000000000000..e7403cc03de4 --- /dev/null +++ b/crypto/openssh/auth-pam.c @@ -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 +#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 */ diff --git a/crypto/openssh/auth-pam.h b/crypto/openssh/auth-pam.h new file mode 100644 index 000000000000..194f7e8d9713 --- /dev/null +++ b/crypto/openssh/auth-pam.h @@ -0,0 +1,38 @@ +/* + * OpenSSH PAM authentication support. + * + * $FreeBSD$ + */ +#ifndef AUTH_PAM_H +#define AUTH_PAM_H +#include "includes.h" +#ifdef USE_PAM + +#include /* 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 */ diff --git a/crypto/openssh/auth-passwd.c b/crypto/openssh/auth-passwd.c index 934b6e357def..c579af350291 100644 --- a/crypto/openssh/auth-passwd.c +++ b/crypto/openssh/auth-passwd.c @@ -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) diff --git a/crypto/openssh/auth-rh-rsa.c b/crypto/openssh/auth-rh-rsa.c index 2297b42ead2b..a9dd11762503 100644 --- a/crypto/openssh/auth-rh-rsa.c +++ b/crypto/openssh/auth-rh-rsa.c @@ -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. */ diff --git a/crypto/openssh/auth-rsa.c b/crypto/openssh/auth-rsa.c index f31027a26efd..00d6c9096d3a 100644 --- a/crypto/openssh/auth-rsa.c +++ b/crypto/openssh/auth-rsa.c @@ -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 #include + +/* 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; diff --git a/crypto/openssh/auth-skey.c b/crypto/openssh/auth-skey.c index 32c47df77b0d..0fdfccabf729 100644 --- a/crypto/openssh/auth-skey.c +++ b/crypto/openssh/auth-skey.c @@ -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 @@ -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 && diff --git a/crypto/openssh/auth.c b/crypto/openssh/auth.c index 1baed64b7a8b..2ab6e661125c 100644 --- a/crypto/openssh/auth.c +++ b/crypto/openssh/auth.c @@ -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" diff --git a/crypto/openssh/auth1.c b/crypto/openssh/auth1.c index cfeb0ca2970c..3c50a16c3b01 100644 --- a/crypto/openssh/auth1.c +++ b/crypto/openssh/auth1.c @@ -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 +#include #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); diff --git a/crypto/openssh/auth2-skey.c b/crypto/openssh/auth2-skey.c index 9de08fc09cfd..abfc346670f6 100644 --- a/crypto/openssh/auth2-skey.c +++ b/crypto/openssh/auth2-skey.c @@ -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; diff --git a/crypto/openssh/auth2.c b/crypto/openssh/auth2.c index de61221c79e1..a39b6d742413 100644 --- a/crypto/openssh/auth2.c +++ b/crypto/openssh/auth2.c @@ -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 @@ -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; +} diff --git a/crypto/openssh/authfd.c b/crypto/openssh/authfd.c index dc2f3ceefc59..db0a4e9296ab 100644 --- a/crypto/openssh/authfd.c +++ b/crypto/openssh/authfd.c @@ -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: diff --git a/crypto/openssh/authfile.c b/crypto/openssh/authfile.c index b1426a5bc6c6..a2dc5247e3a8 100644 --- a/crypto/openssh/authfile.c +++ b/crypto/openssh/authfile.c @@ -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 @@ -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); diff --git a/crypto/openssh/canohost.c b/crypto/openssh/canohost.c index 09f62e0bb33b..bc6872eea507 100644 --- a/crypto/openssh/canohost.c +++ b/crypto/openssh/canohost.c @@ -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. */ diff --git a/crypto/openssh/cipher.c b/crypto/openssh/cipher.c index 94d4251b0110..bb91d7ef5c0d 100644 --- a/crypto/openssh/cipher.c +++ b/crypto/openssh/cipher.c @@ -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 + +/* 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) ? "" : 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)); - } -} diff --git a/crypto/openssh/cipher.h b/crypto/openssh/cipher.h index cb2320d54cb1..006fef93be41 100644 --- a/crypto/openssh/cipher.h +++ b/crypto/openssh/cipher.h @@ -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 #include #include - -/* 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 */ diff --git a/crypto/openssh/compat.c b/crypto/openssh/compat.c index a9daabc7379d..f58e9fa4051d 100644 --- a/crypto/openssh/compat.c +++ b/crypto/openssh/compat.c @@ -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| diff --git a/crypto/openssh/readconf.c b/crypto/openssh/readconf.c index 3d3fd15c4eb8..87f5bc98fea2 100644 --- a/crypto/openssh/readconf.c +++ b/crypto/openssh/readconf.c @@ -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) diff --git a/crypto/openssh/readconf.h b/crypto/openssh/readconf.h index 59b1b4cdfcda..770ee535c549 100644 --- a/crypto/openssh/readconf.h +++ b/crypto/openssh/readconf.h @@ -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. */ diff --git a/crypto/openssh/rsa.c b/crypto/openssh/rsa.c index 53d225408d37..21eec8f482d0 100644 --- a/crypto/openssh/rsa.c +++ b/crypto/openssh/rsa.c @@ -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); diff --git a/crypto/openssh/servconf.c b/crypto/openssh/servconf.c index ae0c9cb424ad..ec6682ff7264 100644 --- a/crypto/openssh/servconf.c +++ b/crypto/openssh/servconf.c @@ -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) diff --git a/crypto/openssh/servconf.h b/crypto/openssh/servconf.h index 1abf077bbb8e..79fe5a047433 100644 --- a/crypto/openssh/servconf.h +++ b/crypto/openssh/servconf.h @@ -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; diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c index e96ef15201c8..887195badc33 100644 --- a/crypto/openssh/session.c +++ b/crypto/openssh/session.c @@ -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)); diff --git a/crypto/openssh/ssh-add.c b/crypto/openssh/ssh-add.c index 4b33f965447f..9dde3643e2ae 100644 --- a/crypto/openssh/ssh-add.c +++ b/crypto/openssh/ssh-add.c @@ -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 @@ -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) { diff --git a/crypto/openssh/ssh-agent.c b/crypto/openssh/ssh-agent.c index 653aed787c54..2c089c05c888 100644 --- a/crypto/openssh/ssh-agent.c +++ b/crypto/openssh/ssh-agent.c @@ -1,3 +1,5 @@ +/* $OpenBSD: ssh-agent.c,v 1.37 2000/09/21 11:07:51 markus Exp $ */ + /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , 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); diff --git a/crypto/openssh/ssh.1 b/crypto/openssh/ssh.1 index c9a09e0cabb4..122431d262ea 100644 --- a/crypto/openssh/ssh.1 +++ b/crypto/openssh/ssh.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 diff --git a/crypto/openssh/ssh.c b/crypto/openssh/ssh.c index 22710212b549..c85905665c28 100644 --- a/crypto/openssh/ssh.c +++ b/crypto/openssh/ssh.c @@ -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 @@ -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); diff --git a/crypto/openssh/ssh.h b/crypto/openssh/ssh.h index 5a90d2226443..82e7eb180a80 100644 --- a/crypto/openssh/ssh.h +++ b/crypto/openssh/ssh.h @@ -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 */ diff --git a/crypto/openssh/sshconnect.c b/crypto/openssh/sshconnect.c index 3f263530609d..535ad4101d7e 100644 --- a/crypto/openssh/sshconnect.c +++ b/crypto/openssh/sshconnect.c @@ -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 @@ -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) diff --git a/crypto/openssh/sshconnect.h b/crypto/openssh/sshconnect.h index 146a65baf276..0b0287e7f96b 100644 --- a/crypto/openssh/sshconnect.h +++ b/crypto/openssh/sshconnect.h @@ -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 diff --git a/crypto/openssh/sshconnect1.c b/crypto/openssh/sshconnect1.c index a8c66dbd902c..96439c467695 100644 --- a/crypto/openssh/sshconnect1.c +++ b/crypto/openssh/sshconnect1.c @@ -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 @@ -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))) diff --git a/crypto/openssh/sshd.8 b/crypto/openssh/sshd.8 index 8c79bae8eff1..f2a754444fcc 100644 --- a/crypto/openssh/sshd.8 +++ b/crypto/openssh/sshd.8 @@ -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 diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index dd872c617ed8..671a50678537 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -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 #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); } + diff --git a/crypto/openssh/sshd_config b/crypto/openssh/sshd_config index a76dc5e54640..13fb26504000 100644 --- a/crypto/openssh/sshd_config +++ b/crypto/openssh/sshd_config @@ -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