Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP1:Update
openssh.3552
openssh-6.6p1-fips-checks.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File openssh-6.6p1-fips-checks.patch of Package openssh.3552
# HG changeset patch # Parent e265bbfd09491209436a3bffb1dfead25ee11556 # # 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,34 @@ +#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(); + 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 @@ -26,40 +26,331 @@ #include "fips.h" #include "dh.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> +#include <openssl/hmac.h> + +enum fips_checksum_status { + CHECK_OK = 0, + CHECK_FAIL, + CHECK_MISSING +}; /* import from dh.c */ extern int dh_grp_min; 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 HMAC of contents of a file given by filename using the hash + * algorithm specified by FIPS_HMAC_EVP in fips.h and placing the result into + * newly allacated memory - remember to free it when not needed anymore */ +static int +hmac_file(const char *filename, u_char **hmac_out) +{ + int check = -1; + int fd; + struct stat fs; + void *hmap; + unsigned char *hmac; + unsigned char *hmac_rv = NULL; + + hmac = xmalloc(FIPS_HMAC_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) { + hmac_rv = HMAC(FIPS_HMAC_EVP(), FIPS_HMAC_KEY + , strlen(FIPS_HMAC_KEY), hmap, fs.st_size, hmac, NULL); + check = CHECK_OK; + munmap(hmap, fs.st_size); + } + close(fd); + +bail_out: + if (hmac_rv) { + check = CHECK_OK; + *hmac_out = hmac; + } else { + check = CHECK_FAIL; + *hmac_out = NULL; + free(hmac); + } + 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; + int rv = -1; + + 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"); + } + + exe[0] = 0; + n = readlink(exe_sl, exe, max_pathlen); + /* the file doesn't need to exist - procfs might not be mounted in + * chroot */ + if (n == -1) { + rv = CHECK_MISSING; + } else { + if (n < max_pathlen) { + exe[n] = 0; + rv = CHECK_OK; + } else { + rv = CHECK_FAIL; + } + } + return rv; +} + +/* Read HMAC from file chk, allocating enough memory to hold the HMAC and + * return it in *hmac. + * Remember to free() it when it's not needed anymore. + */ +static int +read_hmac(const char *chk, u_char **hmac) +{ + int check = -1; + int fdh, n; + u_char *hmac_in; + + *hmac = 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; + } + + hmac_in = xmalloc(FIPS_HMAC_LEN); + + n = read(fdh, (void *)hmac_in, FIPS_HMAC_LEN); + if (FIPS_HMAC_LEN != n) { + debug("fips: unable to read whole checksum from checksum file\n"); + free (hmac_in); + check = CHECK_FAIL; + } else { + check = CHECK_OK; + *hmac = hmac_in; + } +bail_out: + return check; +} + +static int +fips_hmac_self(void) +{ + int check = -1; + u_char *hmac = NULL, *hmac_chk = NULL; + char *exe, *chk; + + exe = xmalloc(PATH_MAX); + chk = xmalloc(PATH_MAX); + + /* we will need to add the suffix and the null terminator */ + check = get_executable_path(getpid(), exe + , PATH_MAX - strlen(CHECKSUM_SUFFIX) - 1); + if (CHECK_OK != check) + goto cleanup; + + strncpy(chk, exe, PATH_MAX); + strlcat(chk, CHECKSUM_SUFFIX, PATH_MAX); + + check = read_hmac(chk, &hmac_chk); + if (CHECK_OK != check) + goto cleanup; + + check = hmac_file(exe, &hmac); + if (CHECK_OK != check) + goto cleanup; + + check = memcmp(hmac, hmac_chk, FIPS_HMAC_LEN); + if (0 == check) { + check = CHECK_OK; + debug("fips: checksum matches\n"); + } else { + check = CHECK_FAIL; + debug("fips: checksum mismatch!\n"); + } + +cleanup: + free(hmac); + free(hmac_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); + return (NULL != 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_hmac_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"); + } + } 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: 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(void) { if (-1 == fips_state) { fips_state = FIPS_mode(); if (fips_state) 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 @@ -22,17 +22,25 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FIPS_H #define FIPS_H #include "key.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 ".hmac" +#define FIPS_HMAC_KEY "HMAC_KEY:OpenSSH-FIPS@SLE" +#define FIPS_HMAC_EVP EVP_sha256 +#define FIPS_HMAC_LEN 32 +void fips_ssh_init(void); int fips_mode(void); int fips_correct_dgst(int); int fips_dgst_min(void); int fips_dh_grp_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; @@ -1444,16 +1446,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: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 @@ -1391,16 +1391,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;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor