1255767Sdes/* $Id: port-linux.c,v 1.18 2013/06/01 22:07:32 dtucker Exp $ */ 2162852Sdes 3162852Sdes/* 4162852Sdes * Copyright (c) 2005 Daniel Walsh <dwalsh@redhat.com> 5162852Sdes * Copyright (c) 2006 Damien Miller <djm@openbsd.org> 6162852Sdes * 7162852Sdes * Permission to use, copy, modify, and distribute this software for any 8162852Sdes * purpose with or without fee is hereby granted, provided that the above 9162852Sdes * copyright notice and this permission notice appear in all copies. 10162852Sdes * 11162852Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12162852Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13162852Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14162852Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15162852Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16162852Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17162852Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18162852Sdes */ 19162852Sdes 20162852Sdes/* 21162852Sdes * Linux-specific portability code - just SELinux support at present 22162852Sdes */ 23162852Sdes 24162852Sdes#include "includes.h" 25162852Sdes 26204917Sdes#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) 27162852Sdes#include <errno.h> 28162852Sdes#include <stdarg.h> 29162852Sdes#include <string.h> 30204917Sdes#include <stdio.h> 31162852Sdes 32162852Sdes#include "log.h" 33204917Sdes#include "xmalloc.h" 34162852Sdes#include "port-linux.h" 35162852Sdes 36204917Sdes#ifdef WITH_SELINUX 37162852Sdes#include <selinux/selinux.h> 38162852Sdes#include <selinux/flask.h> 39162852Sdes#include <selinux/get_context_list.h> 40162852Sdes 41226046Sdes#ifndef SSH_SELINUX_UNCONFINED_TYPE 42226046Sdes# define SSH_SELINUX_UNCONFINED_TYPE ":unconfined_t:" 43226046Sdes#endif 44226046Sdes 45162852Sdes/* Wrapper around is_selinux_enabled() to log its return value once only */ 46181111Sdesint 47162852Sdesssh_selinux_enabled(void) 48162852Sdes{ 49162852Sdes static int enabled = -1; 50162852Sdes 51162852Sdes if (enabled == -1) { 52221420Sdes enabled = (is_selinux_enabled() == 1); 53162852Sdes debug("SELinux support %s", enabled ? "enabled" : "disabled"); 54162852Sdes } 55162852Sdes 56162852Sdes return (enabled); 57162852Sdes} 58162852Sdes 59162852Sdes/* Return the default security context for the given username */ 60162852Sdesstatic security_context_t 61162852Sdesssh_selinux_getctxbyname(char *pwname) 62162852Sdes{ 63240075Sdes security_context_t sc = NULL; 64162852Sdes char *sename = NULL, *lvl = NULL; 65162852Sdes int r; 66162852Sdes 67162852Sdes#ifdef HAVE_GETSEUSERBYNAME 68162852Sdes if (getseuserbyname(pwname, &sename, &lvl) != 0) 69162852Sdes return NULL; 70162852Sdes#else 71162852Sdes sename = pwname; 72162852Sdes lvl = NULL; 73162852Sdes#endif 74162852Sdes 75162852Sdes#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL 76162852Sdes r = get_default_context_with_level(sename, lvl, NULL, &sc); 77162852Sdes#else 78162852Sdes r = get_default_context(sename, NULL, &sc); 79162852Sdes#endif 80162852Sdes 81162852Sdes if (r != 0) { 82162852Sdes switch (security_getenforce()) { 83162852Sdes case -1: 84162852Sdes fatal("%s: ssh_selinux_getctxbyname: " 85162852Sdes "security_getenforce() failed", __func__); 86162852Sdes case 0: 87162852Sdes error("%s: Failed to get default SELinux security " 88162852Sdes "context for %s", __func__, pwname); 89240075Sdes sc = NULL; 90181111Sdes break; 91162852Sdes default: 92162852Sdes fatal("%s: Failed to get default SELinux security " 93162852Sdes "context for %s (in enforcing mode)", 94162852Sdes __func__, pwname); 95162852Sdes } 96162852Sdes } 97162852Sdes 98162852Sdes#ifdef HAVE_GETSEUSERBYNAME 99255767Sdes free(sename); 100255767Sdes free(lvl); 101162852Sdes#endif 102162852Sdes 103240075Sdes return sc; 104162852Sdes} 105162852Sdes 106162852Sdes/* Set the execution context to the default for the specified user */ 107162852Sdesvoid 108162852Sdesssh_selinux_setup_exec_context(char *pwname) 109162852Sdes{ 110162852Sdes security_context_t user_ctx = NULL; 111162852Sdes 112162852Sdes if (!ssh_selinux_enabled()) 113162852Sdes return; 114162852Sdes 115162852Sdes debug3("%s: setting execution context", __func__); 116162852Sdes 117162852Sdes user_ctx = ssh_selinux_getctxbyname(pwname); 118162852Sdes if (setexeccon(user_ctx) != 0) { 119162852Sdes switch (security_getenforce()) { 120162852Sdes case -1: 121162852Sdes fatal("%s: security_getenforce() failed", __func__); 122162852Sdes case 0: 123162852Sdes error("%s: Failed to set SELinux execution " 124162852Sdes "context for %s", __func__, pwname); 125181111Sdes break; 126162852Sdes default: 127162852Sdes fatal("%s: Failed to set SELinux execution context " 128162852Sdes "for %s (in enforcing mode)", __func__, pwname); 129162852Sdes } 130162852Sdes } 131162852Sdes if (user_ctx != NULL) 132162852Sdes freecon(user_ctx); 133162852Sdes 134162852Sdes debug3("%s: done", __func__); 135162852Sdes} 136162852Sdes 137162852Sdes/* Set the TTY context for the specified user */ 138162852Sdesvoid 139162852Sdesssh_selinux_setup_pty(char *pwname, const char *tty) 140162852Sdes{ 141162852Sdes security_context_t new_tty_ctx = NULL; 142162852Sdes security_context_t user_ctx = NULL; 143162852Sdes security_context_t old_tty_ctx = NULL; 144162852Sdes 145162852Sdes if (!ssh_selinux_enabled()) 146162852Sdes return; 147162852Sdes 148162852Sdes debug3("%s: setting TTY context on %s", __func__, tty); 149162852Sdes 150162852Sdes user_ctx = ssh_selinux_getctxbyname(pwname); 151162852Sdes 152162852Sdes /* XXX: should these calls fatal() upon failure in enforcing mode? */ 153162852Sdes 154162852Sdes if (getfilecon(tty, &old_tty_ctx) == -1) { 155162852Sdes error("%s: getfilecon: %s", __func__, strerror(errno)); 156162852Sdes goto out; 157162852Sdes } 158162852Sdes 159162852Sdes if (security_compute_relabel(user_ctx, old_tty_ctx, 160162852Sdes SECCLASS_CHR_FILE, &new_tty_ctx) != 0) { 161162852Sdes error("%s: security_compute_relabel: %s", 162162852Sdes __func__, strerror(errno)); 163162852Sdes goto out; 164162852Sdes } 165162852Sdes 166162852Sdes if (setfilecon(tty, new_tty_ctx) != 0) 167162852Sdes error("%s: setfilecon: %s", __func__, strerror(errno)); 168162852Sdes out: 169162852Sdes if (new_tty_ctx != NULL) 170162852Sdes freecon(new_tty_ctx); 171162852Sdes if (old_tty_ctx != NULL) 172162852Sdes freecon(old_tty_ctx); 173162852Sdes if (user_ctx != NULL) 174162852Sdes freecon(user_ctx); 175162852Sdes debug3("%s: done", __func__); 176162852Sdes} 177204917Sdes 178204917Sdesvoid 179204917Sdesssh_selinux_change_context(const char *newname) 180204917Sdes{ 181204917Sdes int len, newlen; 182204917Sdes char *oldctx, *newctx, *cx; 183226046Sdes void (*switchlog) (const char *fmt,...) = logit; 184204917Sdes 185204917Sdes if (!ssh_selinux_enabled()) 186204917Sdes return; 187204917Sdes 188204917Sdes if (getcon((security_context_t *)&oldctx) < 0) { 189226046Sdes logit("%s: getcon failed with %s", __func__, strerror(errno)); 190204917Sdes return; 191204917Sdes } 192204917Sdes if ((cx = index(oldctx, ':')) == NULL || (cx = index(cx + 1, ':')) == 193204917Sdes NULL) { 194204917Sdes logit ("%s: unparseable context %s", __func__, oldctx); 195204917Sdes return; 196204917Sdes } 197204917Sdes 198226046Sdes /* 199226046Sdes * Check whether we are attempting to switch away from an unconfined 200226046Sdes * security context. 201226046Sdes */ 202226046Sdes if (strncmp(cx, SSH_SELINUX_UNCONFINED_TYPE, 203226046Sdes sizeof(SSH_SELINUX_UNCONFINED_TYPE) - 1) == 0) 204226046Sdes switchlog = debug3; 205226046Sdes 206204917Sdes newlen = strlen(oldctx) + strlen(newname) + 1; 207204917Sdes newctx = xmalloc(newlen); 208204917Sdes len = cx - oldctx + 1; 209204917Sdes memcpy(newctx, oldctx, len); 210204917Sdes strlcpy(newctx + len, newname, newlen - len); 211204917Sdes if ((cx = index(cx + 1, ':'))) 212204917Sdes strlcat(newctx, cx, newlen); 213226046Sdes debug3("%s: setting context from '%s' to '%s'", __func__, 214226046Sdes oldctx, newctx); 215204917Sdes if (setcon(newctx) < 0) 216226046Sdes switchlog("%s: setcon %s from %s failed with %s", __func__, 217226046Sdes newctx, oldctx, strerror(errno)); 218255767Sdes free(oldctx); 219255767Sdes free(newctx); 220204917Sdes} 221221420Sdes 222221420Sdesvoid 223221420Sdesssh_selinux_setfscreatecon(const char *path) 224221420Sdes{ 225221420Sdes security_context_t context; 226221420Sdes 227221420Sdes if (!ssh_selinux_enabled()) 228221420Sdes return; 229221420Sdes if (path == NULL) { 230221420Sdes setfscreatecon(NULL); 231221420Sdes return; 232221420Sdes } 233221420Sdes if (matchpathcon(path, 0700, &context) == 0) 234221420Sdes setfscreatecon(context); 235221420Sdes} 236221420Sdes 237162852Sdes#endif /* WITH_SELINUX */ 238204917Sdes 239204917Sdes#ifdef LINUX_OOM_ADJUST 240204917Sdes/* 241221420Sdes * The magic "don't kill me" values, old and new, as documented in eg: 242204917Sdes * http://lxr.linux.no/#linux+v2.6.32/Documentation/filesystems/proc.txt 243221420Sdes * http://lxr.linux.no/#linux+v2.6.36/Documentation/filesystems/proc.txt 244204917Sdes */ 245204917Sdes 246204917Sdesstatic int oom_adj_save = INT_MIN; 247221420Sdesstatic char *oom_adj_path = NULL; 248221420Sdesstruct { 249221420Sdes char *path; 250221420Sdes int value; 251221420Sdes} oom_adjust[] = { 252221420Sdes {"/proc/self/oom_score_adj", -1000}, /* kernels >= 2.6.36 */ 253221420Sdes {"/proc/self/oom_adj", -17}, /* kernels <= 2.6.35 */ 254221420Sdes {NULL, 0}, 255221420Sdes}; 256204917Sdes 257204917Sdes/* 258204917Sdes * Tell the kernel's out-of-memory killer to avoid sshd. 259204917Sdes * Returns the previous oom_adj value or zero. 260204917Sdes */ 261204917Sdesvoid 262204917Sdesoom_adjust_setup(void) 263204917Sdes{ 264221420Sdes int i, value; 265204917Sdes FILE *fp; 266204917Sdes 267204917Sdes debug3("%s", __func__); 268221420Sdes for (i = 0; oom_adjust[i].path != NULL; i++) { 269221420Sdes oom_adj_path = oom_adjust[i].path; 270221420Sdes value = oom_adjust[i].value; 271221420Sdes if ((fp = fopen(oom_adj_path, "r+")) != NULL) { 272221420Sdes if (fscanf(fp, "%d", &oom_adj_save) != 1) 273221420Sdes verbose("error reading %s: %s", oom_adj_path, 274221420Sdes strerror(errno)); 275221420Sdes else { 276221420Sdes rewind(fp); 277221420Sdes if (fprintf(fp, "%d\n", value) <= 0) 278221420Sdes verbose("error writing %s: %s", 279221420Sdes oom_adj_path, strerror(errno)); 280221420Sdes else 281295367Sdes debug("Set %s from %d to %d", 282221420Sdes oom_adj_path, oom_adj_save, value); 283221420Sdes } 284221420Sdes fclose(fp); 285221420Sdes return; 286204917Sdes } 287204917Sdes } 288221420Sdes oom_adj_path = NULL; 289204917Sdes} 290204917Sdes 291204917Sdes/* Restore the saved OOM adjustment */ 292204917Sdesvoid 293204917Sdesoom_adjust_restore(void) 294204917Sdes{ 295204917Sdes FILE *fp; 296204917Sdes 297204917Sdes debug3("%s", __func__); 298221420Sdes if (oom_adj_save == INT_MIN || oom_adj_path == NULL || 299221420Sdes (fp = fopen(oom_adj_path, "w")) == NULL) 300204917Sdes return; 301204917Sdes 302204917Sdes if (fprintf(fp, "%d\n", oom_adj_save) <= 0) 303221420Sdes verbose("error writing %s: %s", oom_adj_path, strerror(errno)); 304204917Sdes else 305295367Sdes debug("Set %s to %d", oom_adj_path, oom_adj_save); 306204917Sdes 307204917Sdes fclose(fp); 308204917Sdes return; 309204917Sdes} 310204917Sdes#endif /* LINUX_OOM_ADJUST */ 311204917Sdes#endif /* WITH_SELINUX || LINUX_OOM_ADJUST */ 312