9fb40d132b
- curve25519 key exchange fix (-curve25519-6.6.1p1.patch) - patch re-ordering (-audit3-key_auth_usage-fips.patch, -audit4-kex_results-fips.patch) OBS-URL: https://build.opensuse.org/request/show/231427 OBS-URL: https://build.opensuse.org/package/show/network/openssh?expand=0&rev=80
530 lines
13 KiB
Diff
530 lines
13 KiB
Diff
# HG changeset patch
|
|
# Parent 717873621cf4991164c61caafd9ac07473231f10
|
|
# Simple implementation of FIPS 140-2 selfchecks. Use OpenSSL to generate and
|
|
# verify checksums of binaries. Any hash iused in OpenSSH can be used (MD5 would
|
|
# obviously be a poor choice, since OpenSSL would barf and abort immediately in
|
|
# FIPS mode). SHA-2 seems to be a reasonable choice.
|
|
#
|
|
# The logic of the checks is as follows: decide whether FIPS mode is mandated
|
|
# (either by checking /proc/sys/crypto/fips_enabled or envoroinment variable
|
|
# SSH_FORCE_FIPS. In FIPS mode, checksums are required to match (inability to
|
|
# retrieve pre-calculated hash is a fatal error). In non-FIPS mode the checks
|
|
# still must be performed, unless the hashes are not installed. Thus if the hash
|
|
# file is not found (or the hash matches), proceed in non-FIPS mode and abort
|
|
# otherwise.
|
|
|
|
diff --git a/openssh-6.6p1/fips-check.c b/openssh-6.6p1/fips-check.c
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/openssh-6.6p1/fips-check.c
|
|
@@ -0,0 +1,37 @@
|
|
+#include "includes.h"
|
|
+#include <fcntl.h>
|
|
+#include <limits.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include "digest.h"
|
|
+#include "fips.h"
|
|
+
|
|
+#include <openssl/err.h>
|
|
+
|
|
+#define PROC_NAME_LEN 64
|
|
+
|
|
+static const char *argv0;
|
|
+
|
|
+void
|
|
+print_help_exit(int ev)
|
|
+{
|
|
+ fprintf(stderr, "%s <-c|-w> <file> <checksum_file>\n", argv0);
|
|
+ fprintf(stderr, " -c verify hash of 'file' against hash in 'checksum_file'\n");
|
|
+ fprintf(stderr, " -w write hash of 'file' into 'checksum_file'\n");
|
|
+ exit(ev);
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, char **argv)
|
|
+{
|
|
+
|
|
+ fips_ssh_init();
|
|
+// printf("SSL Error: %lx: %s", ERR_get_error(), ERR_get_string(ERR_get_error(), NULL));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/openssh-6.6p1/fips.c b/openssh-6.6p1/fips.c
|
|
--- a/openssh-6.6p1/fips.c
|
|
+++ b/openssh-6.6p1/fips.c
|
|
@@ -24,21 +24,342 @@
|
|
|
|
#include "includes.h"
|
|
|
|
#include "fips.h"
|
|
|
|
#include "digest.h"
|
|
#include "key.h"
|
|
#include "log.h"
|
|
+#include "xmalloc.h"
|
|
+#include <openbsd-compat/openssl-compat.h>
|
|
+
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <string.h>
|
|
+#include <sys/mman.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
|
|
#include <openssl/crypto.h>
|
|
+#include <openssl/err.h>
|
|
+
|
|
+enum fips_checksum_status {
|
|
+ CHECK_OK = 0,
|
|
+ CHECK_FAIL,
|
|
+ CHECK_MISSING
|
|
+};
|
|
|
|
static int fips_state = -1;
|
|
|
|
+static char *
|
|
+hex_fingerprint(u_int raw_len, u_char *raw)
|
|
+{
|
|
+ char *retval;
|
|
+ u_int i;
|
|
+
|
|
+ /* reserve space for both the key hash and the string for the hash type */
|
|
+ retval = malloc(3 * raw_len);
|
|
+ for (i = 0; i < raw_len; i++) {
|
|
+ char hex[4];
|
|
+ snprintf(hex, sizeof(hex), "%02x:", raw[i]);
|
|
+ strlcat(retval, hex, raw_len * 3);
|
|
+ }
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* calculates hash of contents of file given by filename using algorithm alg
|
|
+ * and placing the resukt into newly allacated memory - remember to free it
|
|
+ * when not needed anymore */
|
|
+static int
|
|
+hash_file(const char *filename, int alg, u_char **hash_out)
|
|
+{
|
|
+ int check = -1;
|
|
+ int hash_len;
|
|
+ int fd;
|
|
+ struct stat fs;
|
|
+ void *hmap;
|
|
+ char *hash;
|
|
+
|
|
+ hash_len = ssh_digest_bytes(alg);
|
|
+ hash = xmalloc(hash_len);
|
|
+
|
|
+ fd = open(filename, O_RDONLY);
|
|
+ if (-1 == fd)
|
|
+ goto bail_out;
|
|
+
|
|
+ if (-1 == fstat(fd, &fs))
|
|
+ goto bail_out;
|
|
+
|
|
+ hmap = mmap(NULL, fs.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
|
+
|
|
+ if ((void *)(-1) != hmap) {
|
|
+ check = ssh_digest_memory(alg, hmap, fs.st_size, hash, hash_len);
|
|
+ munmap(hmap, fs.st_size);
|
|
+ }
|
|
+ close(fd);
|
|
+
|
|
+bail_out:
|
|
+ if (0 == check) {
|
|
+ check = CHECK_OK;
|
|
+ *hash_out = hash;
|
|
+ } else {
|
|
+ check = CHECK_FAIL;
|
|
+ *hash_out = NULL;
|
|
+ free(hash);
|
|
+ }
|
|
+ return check;
|
|
+}
|
|
+
|
|
+/* find pathname of binary of process with PID pid. exe is buffer expected to
|
|
+ * be capable of holding at least max_pathlen characters
|
|
+ */
|
|
+static int
|
|
+get_executable_path(pid_t pid, char *exe, int max_pathlen)
|
|
+{
|
|
+ char exe_sl[PROC_EXE_PATH_LEN];
|
|
+ int n;
|
|
+
|
|
+ n = snprintf(exe_sl, sizeof(exe_sl), "/proc/%u/exe", pid);
|
|
+ if ((n <= 10) || (n >= max_pathlen)) {
|
|
+ fatal("error compiling filename of link to executable");
|
|
+ }
|
|
+
|
|
+ n = readlink(exe_sl, exe, max_pathlen);
|
|
+ if (n < max_pathlen) {
|
|
+ exe[n] = 0;
|
|
+ } else {
|
|
+ fatal("error getting executable pathname");
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Read checksum file chk, storing the algorithm used for generating it into
|
|
+ * *alg; allocate enough memory to hold the hash and return it in *hash.
|
|
+ * Remember to free() it when not needed anymore.
|
|
+ */
|
|
+static int
|
|
+read_hash(const char *chk, int *alg, u_char **hash)
|
|
+{
|
|
+ int check = -1;
|
|
+ int hash_len;
|
|
+ int fdh, n;
|
|
+ char alg_c;
|
|
+ char *hash_in;
|
|
+
|
|
+ *hash = NULL;
|
|
+
|
|
+ fdh = open(chk, O_RDONLY);
|
|
+ if (-1 == fdh) {
|
|
+ switch (errno) {
|
|
+ case ENOENT:
|
|
+ check = CHECK_MISSING;
|
|
+ debug("fips: checksum file %s is missing\n", chk);
|
|
+ break;
|
|
+ default:
|
|
+ check = CHECK_FAIL;
|
|
+ debug("fips: ckecksum file %s not accessible\n", chk);
|
|
+ break;
|
|
+
|
|
+ }
|
|
+ goto bail_out;
|
|
+ }
|
|
+
|
|
+ n = read(fdh, &alg_c, 1);
|
|
+ if (1 != n) {
|
|
+ check = CHECK_FAIL;
|
|
+ goto bail_out;
|
|
+ }
|
|
+
|
|
+ *alg = (int)alg_c;
|
|
+ hash_len = ssh_digest_bytes(*alg);
|
|
+ hash_in = xmalloc(hash_len);
|
|
+
|
|
+ n = read(fdh, (void *)hash_in, hash_len);
|
|
+ if (hash_len != n) {
|
|
+ debug("fips: unable to read whole checksum from checksum file\n");
|
|
+ free (hash_in);
|
|
+ check = CHECK_FAIL;
|
|
+ } else {
|
|
+ check = CHECK_OK;
|
|
+ *hash = hash_in;
|
|
+ }
|
|
+bail_out:
|
|
+ return check;
|
|
+}
|
|
+
|
|
+static int
|
|
+fips_hash_self(void)
|
|
+{
|
|
+ int check = -1;
|
|
+ int alg;
|
|
+ u_char *hash, *hash_chk;
|
|
+ char *exe, *chk;
|
|
+
|
|
+ exe = xmalloc(PATH_MAX);
|
|
+ chk = xmalloc(PATH_MAX);
|
|
+
|
|
+ /* we will need to add the ".chk" suffix and the null terminator */
|
|
+ check = get_executable_path(getpid(), exe
|
|
+ , PATH_MAX - strlen(CHECKSUM_SUFFIX) - 1);
|
|
+
|
|
+ strncpy(chk, exe, PATH_MAX);
|
|
+ strlcat(chk, CHECKSUM_SUFFIX, PATH_MAX);
|
|
+
|
|
+ check = read_hash(chk, &alg, &hash_chk);
|
|
+ if (CHECK_OK != check)
|
|
+ goto cleanup_chk;
|
|
+
|
|
+ check = hash_file(exe, alg, &hash);
|
|
+ if (CHECK_OK != check)
|
|
+ goto cleanup;
|
|
+
|
|
+ check = memcmp(hash, hash_chk, ssh_digest_bytes(alg));
|
|
+ if (0 == check) {
|
|
+ check = CHECK_OK;
|
|
+ debug("fips: checksum matches\n");
|
|
+ } else {
|
|
+ check = CHECK_FAIL;
|
|
+ debug("fips: checksum mismatch!\n");
|
|
+ }
|
|
+
|
|
+cleanup:
|
|
+ free(hash);
|
|
+cleanup_chk:
|
|
+ free(hash_chk);
|
|
+ free(chk);
|
|
+ free(exe);
|
|
+
|
|
+ return check;
|
|
+}
|
|
+
|
|
+static int
|
|
+fips_check_required_proc(void)
|
|
+{
|
|
+ int fips_required = 0;
|
|
+ int fips_fd;
|
|
+ char fips_sys = 0;
|
|
+
|
|
+ struct stat dummy;
|
|
+ if (-1 == stat(FIPS_PROC_PATH, &dummy)) {
|
|
+ switch (errno) {
|
|
+ case ENOENT:
|
|
+ case ENOTDIR:
|
|
+ break;
|
|
+ default:
|
|
+ fatal("Check for system-wide FIPS mode is required and %s cannot"
|
|
+ " be accessed for reason other than non-existence - aborting"
|
|
+ , FIPS_PROC_PATH);
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ if (-1 == (fips_fd = open(FIPS_PROC_PATH, O_RDONLY)))
|
|
+ fatal("Check for system-wide FIPS mode is required and %s cannot"
|
|
+ " be opened for reading - aborting"
|
|
+ , FIPS_PROC_PATH);
|
|
+ if (1 > read(fips_fd, &fips_sys, 1))
|
|
+ fatal("Check for system-wide FIPS mode is required and %s doesn't"
|
|
+ " return at least one character - aborting"
|
|
+ , FIPS_PROC_PATH);
|
|
+ close(fips_sys);
|
|
+ switch (fips_sys) {
|
|
+ case '0':
|
|
+ case '1':
|
|
+ fips_required = fips_sys - '0';
|
|
+ break;
|
|
+ default:
|
|
+ fatal("Bogus character %c found in %s - aborting"
|
|
+ , fips_sys, FIPS_PROC_PATH);
|
|
+ }
|
|
+ }
|
|
+ return fips_required;
|
|
+}
|
|
+
|
|
+static int
|
|
+fips_check_required_env(void)
|
|
+{
|
|
+ int fips_required = 0;
|
|
+ char *env = getenv(SSH_FORCE_FIPS_ENV);
|
|
+
|
|
+ if (env) {
|
|
+ errno = 0;
|
|
+ fips_required = strtol(env, NULL, 10);
|
|
+ if (errno) {
|
|
+ debug("bogus value in the %s environment variable, ignoring\n"
|
|
+ , SSH_FORCE_FIPS_ENV);
|
|
+ fips_required = 0;
|
|
+ } else
|
|
+ fips_required = 1;
|
|
+ }
|
|
+ return fips_required;
|
|
+}
|
|
+
|
|
+static int
|
|
+fips_required(void)
|
|
+{
|
|
+ int fips_requests = 0;
|
|
+ fips_requests += fips_check_required_proc();
|
|
+ fips_requests += fips_check_required_env();
|
|
+ return fips_requests;
|
|
+}
|
|
+
|
|
+/* check whether FIPS mode is required and perform selfchecksum/selftest */
|
|
+void
|
|
+fips_ssh_init(void)
|
|
+{
|
|
+ int checksum;
|
|
+
|
|
+ checksum = fips_hash_self();
|
|
+
|
|
+ if (fips_required()) {
|
|
+ switch (checksum) {
|
|
+ case CHECK_OK:
|
|
+ debug("fips: mandatory checksum ok");
|
|
+ break;
|
|
+ case CHECK_FAIL:
|
|
+ fatal("fips: mandatory checksum failed - aborting");
|
|
+ break;
|
|
+ case CHECK_MISSING:
|
|
+ fatal("fips: mandatory checksum data missing - aborting");
|
|
+ break;
|
|
+ default:
|
|
+ fatal("Fatal error: internal error at %s:%u"
|
|
+ , __FILE__, __LINE__);
|
|
+ break;
|
|
+ }
|
|
+ fips_state = FIPS_mode_set(1);
|
|
+ if (1 != fips_state) {
|
|
+ ERR_load_crypto_strings();
|
|
+ u_long err = ERR_get_error();
|
|
+ error("fips: OpenSSL error %lx: %s", err, ERR_error_string(err, NULL));
|
|
+ fatal("fips: unable to set OpenSSL into FIPS mode - aborting"
|
|
+ , fips_state);
|
|
+ }
|
|
+ } else {
|
|
+ switch (checksum) {
|
|
+ case CHECK_OK:
|
|
+ debug("fips: checksum ok");
|
|
+ break;
|
|
+ case CHECK_FAIL:
|
|
+ fatal("fips: checksum failed - aborting");
|
|
+ break;
|
|
+ case CHECK_MISSING:
|
|
+ debug("fips: mandatory checksum data missing, but not required - continuing non-FIPS");
|
|
+ break;
|
|
+ default:
|
|
+ fatal("Fatal error: internal error at %s:%u",
|
|
+ __FILE__, __LINE__);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
int
|
|
fips_mode()
|
|
{
|
|
if (-1 == fips_state) {
|
|
fips_state = FIPS_mode();
|
|
if (fips_state)
|
|
debug("FIPS mode initialized");
|
|
}
|
|
diff --git a/openssh-6.6p1/fips.h b/openssh-6.6p1/fips.h
|
|
--- a/openssh-6.6p1/fips.h
|
|
+++ b/openssh-6.6p1/fips.h
|
|
@@ -1,10 +1,10 @@
|
|
/*
|
|
- * Copyright (c) 2012 Petr Cerny. All rights reserved.
|
|
+ * Copyright (c) 2012-2014 Petr Cerny. 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
|
|
@@ -19,15 +19,22 @@
|
|
* 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.
|
|
*/
|
|
#ifndef FIPS_H
|
|
#define FIPS_H
|
|
|
|
+#define SSH_FORCE_FIPS_ENV "SSH_FORCE_FIPS"
|
|
+#define FIPS_PROC_PATH "/proc/sys/crypto/fips_enabled"
|
|
+
|
|
+#define PROC_EXE_PATH_LEN 64
|
|
+#define CHECKSUM_SUFFIX ".chk"
|
|
+
|
|
+void fips_ssh_init(void);
|
|
int fips_mode(void);
|
|
int fips_correct_dgst(int);
|
|
int fips_dgst_min(void);
|
|
enum fp_type fips_correct_fp_type(enum fp_type);
|
|
|
|
#endif
|
|
|
|
diff --git a/openssh-6.6p1/sftp-server.c b/openssh-6.6p1/sftp-server.c
|
|
--- a/openssh-6.6p1/sftp-server.c
|
|
+++ b/openssh-6.6p1/sftp-server.c
|
|
@@ -47,16 +47,18 @@
|
|
#include "log.h"
|
|
#include "misc.h"
|
|
#include "match.h"
|
|
#include "uidswap.h"
|
|
|
|
#include "sftp.h"
|
|
#include "sftp-common.h"
|
|
|
|
+#include "fips.h"
|
|
+
|
|
/* helper */
|
|
#define get_int64() buffer_get_int64(&iqueue);
|
|
#define get_int() buffer_get_int(&iqueue);
|
|
#define get_string(lenp) buffer_get_string(&iqueue, lenp);
|
|
|
|
/* Our verbosity */
|
|
static LogLevel log_level = SYSLOG_LEVEL_ERROR;
|
|
|
|
@@ -1453,16 +1455,19 @@ sftp_server_main(int argc, char **argv,
|
|
ssize_t len, olen, set_size;
|
|
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
|
|
char *cp, *homedir = NULL, buf[4*4096];
|
|
long mask;
|
|
|
|
extern char *optarg;
|
|
extern char *__progname;
|
|
|
|
+ /* initialize fips */
|
|
+ fips_ssh_init();
|
|
+
|
|
__progname = ssh_get_progname(argv[0]);
|
|
log_init(__progname, log_level, log_facility, log_stderr);
|
|
|
|
pw = pwcopy(user_pw);
|
|
|
|
while (!skipargs && (ch = getopt(argc, argv,
|
|
"d:f:l:P:p:Q:u:m:cehR")) != -1) {
|
|
switch (ch) {
|
|
diff --git a/openssh-6.6p1/ssh.c b/openssh-6.6p1/ssh.c
|
|
--- a/openssh-6.6p1/ssh.c
|
|
+++ b/openssh-6.6p1/ssh.c
|
|
@@ -420,16 +420,19 @@ main(int ac, char **av)
|
|
struct stat st;
|
|
struct passwd *pw;
|
|
int timeout_ms;
|
|
extern int optind, optreset;
|
|
extern char *optarg;
|
|
Forward fwd;
|
|
struct addrinfo *addrs = NULL;
|
|
|
|
+ /* initialize fips */
|
|
+ fips_ssh_init();
|
|
+
|
|
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
|
sanitise_stdfd();
|
|
|
|
__progname = ssh_get_progname(av[0]);
|
|
|
|
#ifndef HAVE_SETPROCTITLE
|
|
/* Prepare for later setproctitle emulation */
|
|
/* Save argv so it isn't clobbered by setproctitle() emulation */
|
|
diff --git a/openssh-6.6p1/sshd.c b/openssh-6.6p1/sshd.c
|
|
--- a/openssh-6.6p1/sshd.c
|
|
+++ b/openssh-6.6p1/sshd.c
|
|
@@ -1466,16 +1466,19 @@ main(int ac, char **av)
|
|
u_int64_t ibytes, obytes;
|
|
mode_t new_umask;
|
|
Key *key;
|
|
Key *pubkey;
|
|
int keytype;
|
|
Authctxt *authctxt;
|
|
struct connection_info *connection_info = get_connection_info(0, 0);
|
|
|
|
+ /* initialize fips */
|
|
+ fips_ssh_init();
|
|
+
|
|
#ifdef HAVE_SECUREWARE
|
|
(void)set_auth_parameters(ac, av);
|
|
#endif
|
|
__progname = ssh_get_progname(av[0]);
|
|
|
|
/* Save argv. Duplicate so setproctitle emulation doesn't clobber it */
|
|
saved_argc = ac;
|
|
rexec_argc = ac;
|