| /* |
| * utils.s -- utilities |
| * |
| * (c) Copyright IBM Corporation 2014, 2015, 2019. |
| * |
| * Author: Stefan Berger <stefanb@us.ibm.com> |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 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. |
| * |
| * Neither the names of the IBM Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "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 COPYRIGHT |
| * HOLDER OR CONTRIBUTORS 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 "config.h" |
| |
| #include <grp.h> |
| #include <pwd.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #if defined __APPLE__ |
| #include <fcntl.h> |
| #include <sys/param.h> |
| #endif |
| |
| #include "utils.h" |
| #include "logging.h" |
| #include "tpmlib.h" |
| #include "swtpm_debug.h" |
| |
| void uninstall_sighandlers() |
| { |
| if (signal(SIGTERM, SIG_DFL) == SIG_ERR) |
| logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGTERM.\n"); |
| |
| if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) |
| logprintf(STDERR_FILENO, "Could not uninstall signal handler for SIGPIPE.\n"); |
| } |
| |
| int install_sighandlers(int pipefd[2], sighandler_t handler) |
| { |
| if (pipe(pipefd) < 0) { |
| logprintf(STDERR_FILENO, "Error: Could not open pipe.\n"); |
| goto err_exit; |
| } |
| |
| if (signal(SIGTERM, handler) == SIG_ERR) { |
| logprintf(STDERR_FILENO, "Could not install signal handler for SIGTERM.\n"); |
| goto err_close_pipe; |
| } |
| |
| if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { |
| logprintf(STDERR_FILENO, "Could not install signal handler for SIGPIPE.\n"); |
| goto err_close_pipe; |
| } |
| |
| return 0; |
| |
| err_close_pipe: |
| close(pipefd[0]); |
| pipefd[0] = -1; |
| close(pipefd[1]); |
| pipefd[1] = -1; |
| |
| err_exit: |
| return -1; |
| } |
| |
| int |
| change_process_owner(const char *user) |
| { |
| struct passwd *passwd; |
| long int uid, gid; |
| char *endptr = NULL; |
| |
| uid = strtoul(user, &endptr, 10); |
| if (*endptr != '\0') { |
| /* a string */ |
| passwd = getpwnam(user); |
| if (!passwd) { |
| logprintf(STDERR_FILENO, |
| "Error: User '%s' does not exist.\n", |
| user); |
| return -14; |
| } |
| |
| if (initgroups(passwd->pw_name, passwd->pw_gid) < 0) { |
| logprintf(STDERR_FILENO, |
| "Error: initgroups(%s, %d) failed.\n", |
| passwd->pw_name, passwd->pw_gid); |
| return -10; |
| } |
| gid = passwd->pw_gid; |
| uid = passwd->pw_uid; |
| } else { |
| /* an integer */ |
| if ((unsigned long int)uid > UINT_MAX) { |
| logprintf(STDERR_FILENO, |
| "Error: uid %s outside valid range.\n", |
| user); |
| return -13; |
| } |
| gid = uid; |
| } |
| |
| if (setgid(gid) < 0) { |
| logprintf(STDERR_FILENO, |
| "Error: setgid(%d) failed.\n", |
| gid); |
| return -11; |
| } |
| if (setuid(uid) < 0) { |
| logprintf(STDERR_FILENO, |
| "Error: setuid(%d) failed.\n", |
| uid); |
| return -12; |
| } |
| return 0; |
| } |
| |
| int |
| do_chroot(const char *path) |
| { |
| if (chroot(path) < 0) { |
| logprintf(STDERR_FILENO, "chroot failed: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| if (chdir("/") < 0) { |
| logprintf(STDERR_FILENO, "chdir failed: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| return 0; |
| } |
| |
| void tpmlib_debug_libtpms_parameters(TPMLIB_TPMVersion tpmversion) |
| { |
| switch (tpmversion) { |
| case TPMLIB_TPM_VERSION_1_2: |
| TPM_DEBUG("TPM 1.2: Compiled for %u auth, %u transport, " |
| "and %u DAA session slots\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_MIN_AUTH_SESSIONS), |
| tpmlib_get_tpm_property(TPMPROP_TPM_MIN_TRANS_SESSIONS), |
| tpmlib_get_tpm_property(TPMPROP_TPM_MIN_DAA_SESSIONS)); |
| TPM_DEBUG("TPM 1.2: Compiled for %u key slots, %u owner evict slots\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_KEY_HANDLES), |
| tpmlib_get_tpm_property(TPMPROP_TPM_OWNER_EVICT_KEY_HANDLES)); |
| TPM_DEBUG("TPM 1.2: Compiled for %u counters, %u saved sessions\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_MIN_COUNTERS), |
| tpmlib_get_tpm_property(TPMPROP_TPM_MIN_SESSION_LIST)); |
| TPM_DEBUG("TPM 1.2: Compiled for %u family, " |
| "%u delegate table entries\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_NUM_FAMILY_TABLE_ENTRY_MIN), |
| tpmlib_get_tpm_property(TPMPROP_TPM_NUM_DELEGATE_TABLE_ENTRY_MIN)); |
| TPM_DEBUG("TPM 1.2: Compiled for %u total NV, %u savestate, " |
| "%u volatile space\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_MAX_NV_SPACE), |
| tpmlib_get_tpm_property(TPMPROP_TPM_MAX_SAVESTATE_SPACE), |
| tpmlib_get_tpm_property(TPMPROP_TPM_MAX_VOLATILESTATE_SPACE)); |
| #if 0 |
| TPM_DEBUG("TPM1.2: Compiled for %u NV defined space\n", |
| tpmlib_get_tpm_property(TPMPROP_TPM_MAX_NV_DEFINED_SIZE)); |
| #endif |
| break; |
| case TPMLIB_TPM_VERSION_2: |
| break; |
| } |
| } |
| |
| char *fd_to_filename(int fd) |
| { |
| #if defined __linux__ |
| |
| char buffer[64]; |
| char *path; |
| |
| snprintf(buffer, sizeof(buffer), "/proc/self/fd/%d", fd); |
| |
| path = realpath(buffer, NULL); |
| if (!path) { |
| logprintf(STDERR_FILENO, "Could not read %s: %s\n", |
| buffer, strerror(errno)); |
| return NULL; |
| } |
| |
| return path; |
| |
| #elif defined __APPLE__ |
| |
| char *path = malloc(MAXPATHLEN); |
| if (!path) { |
| logprintf(STDERR_FILENO, "Out of memory.\n"); |
| return NULL; |
| } |
| if (fcntl(fd, F_GETPATH, path) < 0) { |
| logprintf(STDERR_FILENO, "fcntl for F_GETPATH failed: %\n", |
| strerror(errno)); |
| free(path); |
| return NULL; |
| } |
| return path; |
| |
| #else |
| (void)fd; |
| logprintf(STDERR_FILENO, |
| "Cannot convert file descriptor to filename on this platform.\n"); |
| return NULL; |
| |
| #endif |
| } |
| |
| /* |
| * write_full: Write all bytes of a buffer into the file descriptor |
| * and handle partial writes on the way. |
| * |
| * @fd: file descriptor to write to |
| * @buffer: buffer |
| * @buflen: length of buffer |
| * |
| * Returns -1 in case not all bytes could be transferred, number of |
| * bytes written otherwise (must be equal to buflen). |
| */ |
| ssize_t write_full(int fd, const void *buffer, size_t buflen) |
| { |
| size_t written = 0; |
| ssize_t n; |
| |
| while (written < buflen) { |
| n = write(fd, buffer, buflen - written); |
| if (n == 0) |
| return -1; |
| if (n < 0) { |
| if (errno == EINTR) |
| continue; |
| return -1; |
| } |
| written += n; |
| buffer += n; |
| } |
| return written; |
| } |
| |
| /* |
| * writev_full: Write all bytes of an iovec into the file descriptor |
| * and handle partial writes on the way. |
| * @fd: file descriptor to write to |
| * @iov: pointer to iov |
| * @iovcnt: length of iov array |
| * |
| * Returns -1 in case not all bytes could be transferred, number of |
| * bytes written otherwise (must be equal to buflen). |
| */ |
| ssize_t writev_full(int fd, const struct iovec *iov, int iovcnt) |
| { |
| int i; |
| size_t off; |
| unsigned char *buf; |
| ssize_t n; |
| size_t bytecount = 0; |
| size_t numbufs = 0; |
| size_t lastidx = -1; |
| |
| for (i = 0; i < iovcnt; i++) { |
| if (iov[i].iov_len) { |
| bytecount += iov[i].iov_len; |
| numbufs++; |
| lastidx = i; |
| } |
| } |
| |
| if (numbufs == 1) |
| return write_full(fd, iov[lastidx].iov_base, iov[lastidx].iov_len); |
| |
| buf = malloc(bytecount); |
| if (!buf) { |
| errno = ENOMEM; |
| return -1; |
| } |
| |
| off = 0; |
| for (i = 0; i < iovcnt; i++) { |
| if (!iov[i].iov_len) |
| continue; |
| memcpy(&buf[off], iov[i].iov_base, iov[i].iov_len); |
| off += iov[i].iov_len; |
| } |
| |
| n = write_full(fd, buf, off); |
| |
| free(buf); |
| |
| return n; |
| } |
| |
| /* |
| * read_einter: Read bytes from a file descriptor into a buffer |
| * and handle EINTR. Perform one read(). |
| * |
| * @fd: file descriptor to read from |
| * @buffer: buffer |
| * @buflen: length of buffer |
| * |
| * Returns -1 in case an error occurred, number of bytes read otherwise. |
| */ |
| ssize_t read_eintr(int fd, void *buffer, size_t buflen) |
| { |
| ssize_t n; |
| |
| while (true) { |
| n = read(fd, buffer, buflen); |
| if (n < 0) { |
| if (errno == EINTR) |
| continue; |
| return -1; |
| } |
| return n; |
| } |
| } |