]>
Commit | Line | Data |
---|---|---|
ef4d1846 | 1 | /* $Id$ */ |
2 | ||
3 | /* | |
4 | * Copyright (c) 2005 Daniel Walsh <dwalsh@redhat.com> | |
5 | * Copyright (c) 2006 Damien Miller <djm@openbsd.org> | |
6 | * | |
7 | * Permission to use, copy, modify, and distribute this software for any | |
8 | * purpose with or without fee is hereby granted, provided that the above | |
9 | * copyright notice and this permission notice appear in all copies. | |
10 | * | |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | */ | |
19 | ||
20 | /* | |
21 | * Linux-specific portability code - just SELinux support at present | |
22 | */ | |
23 | ||
24 | #include "includes.h" | |
25 | ||
fd2d830a | 26 | #if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) |
28cb0a43 | 27 | #include <errno.h> |
24436b92 | 28 | #include <stdarg.h> |
28cb0a43 | 29 | #include <string.h> |
fd2d830a | 30 | #include <stdio.h> |
28cb0a43 | 31 | |
ef4d1846 | 32 | #include "log.h" |
51fa929a | 33 | #include "xmalloc.h" |
ef4d1846 | 34 | #include "port-linux.h" |
35 | ||
fd2d830a | 36 | #ifdef WITH_SELINUX |
ef4d1846 | 37 | #include <selinux/selinux.h> |
38 | #include <selinux/flask.h> | |
39 | #include <selinux/get_context_list.h> | |
40 | ||
41 | /* Wrapper around is_selinux_enabled() to log its return value once only */ | |
0b639bf6 | 42 | int |
ef4d1846 | 43 | ssh_selinux_enabled(void) |
44 | { | |
45 | static int enabled = -1; | |
46 | ||
47 | if (enabled == -1) { | |
48 | enabled = is_selinux_enabled(); | |
49 | debug("SELinux support %s", enabled ? "enabled" : "disabled"); | |
50 | } | |
51 | ||
52 | return (enabled); | |
53 | } | |
54 | ||
55 | /* Return the default security context for the given username */ | |
56 | static security_context_t | |
57 | ssh_selinux_getctxbyname(char *pwname) | |
58 | { | |
59 | security_context_t sc; | |
60 | char *sename = NULL, *lvl = NULL; | |
61 | int r; | |
62 | ||
63 | #ifdef HAVE_GETSEUSERBYNAME | |
64 | if (getseuserbyname(pwname, &sename, &lvl) != 0) | |
65 | return NULL; | |
66 | #else | |
67 | sename = pwname; | |
68 | lvl = NULL; | |
69 | #endif | |
70 | ||
71 | #ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL | |
72 | r = get_default_context_with_level(sename, lvl, NULL, &sc); | |
73 | #else | |
74 | r = get_default_context(sename, NULL, &sc); | |
75 | #endif | |
76 | ||
77 | if (r != 0) { | |
78 | switch (security_getenforce()) { | |
79 | case -1: | |
80 | fatal("%s: ssh_selinux_getctxbyname: " | |
81 | "security_getenforce() failed", __func__); | |
82 | case 0: | |
83 | error("%s: Failed to get default SELinux security " | |
84 | "context for %s", __func__, pwname); | |
665ca996 | 85 | break; |
ef4d1846 | 86 | default: |
87 | fatal("%s: Failed to get default SELinux security " | |
88 | "context for %s (in enforcing mode)", | |
89 | __func__, pwname); | |
90 | } | |
91 | } | |
92 | ||
93 | #ifdef HAVE_GETSEUSERBYNAME | |
94 | if (sename != NULL) | |
95 | xfree(sename); | |
96 | if (lvl != NULL) | |
97 | xfree(lvl); | |
98 | #endif | |
99 | ||
100 | return (sc); | |
101 | } | |
102 | ||
103 | /* Set the execution context to the default for the specified user */ | |
104 | void | |
105 | ssh_selinux_setup_exec_context(char *pwname) | |
106 | { | |
107 | security_context_t user_ctx = NULL; | |
108 | ||
109 | if (!ssh_selinux_enabled()) | |
110 | return; | |
111 | ||
112 | debug3("%s: setting execution context", __func__); | |
113 | ||
114 | user_ctx = ssh_selinux_getctxbyname(pwname); | |
115 | if (setexeccon(user_ctx) != 0) { | |
116 | switch (security_getenforce()) { | |
117 | case -1: | |
118 | fatal("%s: security_getenforce() failed", __func__); | |
119 | case 0: | |
120 | error("%s: Failed to set SELinux execution " | |
121 | "context for %s", __func__, pwname); | |
665ca996 | 122 | break; |
ef4d1846 | 123 | default: |
124 | fatal("%s: Failed to set SELinux execution context " | |
125 | "for %s (in enforcing mode)", __func__, pwname); | |
126 | } | |
127 | } | |
128 | if (user_ctx != NULL) | |
129 | freecon(user_ctx); | |
130 | ||
131 | debug3("%s: done", __func__); | |
132 | } | |
133 | ||
134 | /* Set the TTY context for the specified user */ | |
135 | void | |
136 | ssh_selinux_setup_pty(char *pwname, const char *tty) | |
137 | { | |
138 | security_context_t new_tty_ctx = NULL; | |
139 | security_context_t user_ctx = NULL; | |
140 | security_context_t old_tty_ctx = NULL; | |
141 | ||
142 | if (!ssh_selinux_enabled()) | |
143 | return; | |
144 | ||
145 | debug3("%s: setting TTY context on %s", __func__, tty); | |
146 | ||
147 | user_ctx = ssh_selinux_getctxbyname(pwname); | |
148 | ||
149 | /* XXX: should these calls fatal() upon failure in enforcing mode? */ | |
150 | ||
151 | if (getfilecon(tty, &old_tty_ctx) == -1) { | |
152 | error("%s: getfilecon: %s", __func__, strerror(errno)); | |
153 | goto out; | |
154 | } | |
155 | ||
156 | if (security_compute_relabel(user_ctx, old_tty_ctx, | |
157 | SECCLASS_CHR_FILE, &new_tty_ctx) != 0) { | |
158 | error("%s: security_compute_relabel: %s", | |
159 | __func__, strerror(errno)); | |
160 | goto out; | |
161 | } | |
162 | ||
163 | if (setfilecon(tty, new_tty_ctx) != 0) | |
164 | error("%s: setfilecon: %s", __func__, strerror(errno)); | |
165 | out: | |
166 | if (new_tty_ctx != NULL) | |
167 | freecon(new_tty_ctx); | |
168 | if (old_tty_ctx != NULL) | |
169 | freecon(old_tty_ctx); | |
170 | if (user_ctx != NULL) | |
171 | freecon(user_ctx); | |
172 | debug3("%s: done", __func__); | |
173 | } | |
51fa929a | 174 | |
175 | void | |
176 | ssh_selinux_change_context(const char *newname) | |
177 | { | |
178 | int len, newlen; | |
179 | char *oldctx, *newctx, *cx; | |
180 | ||
181 | if (!ssh_selinux_enabled()) | |
182 | return; | |
183 | ||
184 | if (getcon((security_context_t *)&oldctx) < 0) { | |
185 | logit("%s: getcon failed with %s", __func__, strerror (errno)); | |
186 | return; | |
187 | } | |
188 | if ((cx = index(oldctx, ':')) == NULL || (cx = index(cx + 1, ':')) == | |
189 | NULL) { | |
190 | logit ("%s: unparseable context %s", __func__, oldctx); | |
191 | return; | |
192 | } | |
193 | ||
194 | newlen = strlen(oldctx) + strlen(newname) + 1; | |
195 | newctx = xmalloc(newlen); | |
196 | len = cx - oldctx + 1; | |
197 | memcpy(newctx, oldctx, len); | |
198 | strlcpy(newctx + len, newname, newlen - len); | |
199 | if ((cx = index(cx + 1, ':'))) | |
200 | strlcat(newctx, cx, newlen); | |
201 | debug3("%s: setting context from '%s' to '%s'", __func__, oldctx, | |
202 | newctx); | |
203 | if (setcon(newctx) < 0) | |
204 | logit("%s: setcon failed with %s", __func__, strerror (errno)); | |
205 | xfree(oldctx); | |
206 | xfree(newctx); | |
207 | } | |
ef4d1846 | 208 | #endif /* WITH_SELINUX */ |
fd2d830a | 209 | |
210 | #ifdef LINUX_OOM_ADJUST | |
211 | #define OOM_ADJ_PATH "/proc/self/oom_adj" | |
212 | /* | |
213 | * The magic "don't kill me", as documented in eg: | |
214 | * http://lxr.linux.no/#linux+v2.6.32/Documentation/filesystems/proc.txt | |
215 | */ | |
216 | #define OOM_ADJ_NOKILL -17 | |
217 | ||
218 | static int oom_adj_save = INT_MIN; | |
219 | ||
220 | /* | |
221 | * Tell the kernel's out-of-memory killer to avoid sshd. | |
222 | * Returns the previous oom_adj value or zero. | |
223 | */ | |
224 | void | |
225 | oom_adjust_setup(void) | |
226 | { | |
227 | FILE *fp; | |
228 | ||
229 | debug3("%s", __func__); | |
230 | if ((fp = fopen(OOM_ADJ_PATH, "r+")) != NULL) { | |
231 | if (fscanf(fp, "%d", &oom_adj_save) != 1) | |
232 | logit("error reading %s: %s", OOM_ADJ_PATH, strerror(errno)); | |
233 | else { | |
234 | rewind(fp); | |
235 | if (fprintf(fp, "%d\n", OOM_ADJ_NOKILL) <= 0) | |
236 | logit("error writing %s: %s", | |
237 | OOM_ADJ_PATH, strerror(errno)); | |
238 | else | |
239 | verbose("Set %s from %d to %d", | |
240 | OOM_ADJ_PATH, oom_adj_save, OOM_ADJ_NOKILL); | |
241 | } | |
242 | fclose(fp); | |
243 | } | |
244 | } | |
245 | ||
246 | /* Restore the saved OOM adjustment */ | |
247 | void | |
248 | oom_adjust_restore(void) | |
249 | { | |
250 | FILE *fp; | |
251 | ||
252 | debug3("%s", __func__); | |
253 | if (oom_adj_save == INT_MIN || (fp = fopen(OOM_ADJ_PATH, "w")) == NULL) | |
254 | return; | |
255 | ||
256 | if (fprintf(fp, "%d\n", oom_adj_save) <= 0) | |
257 | logit("error writing %s: %s", OOM_ADJ_PATH, strerror(errno)); | |
258 | else | |
259 | verbose("Set %s to %d", OOM_ADJ_PATH, oom_adj_save); | |
260 | ||
261 | fclose(fp); | |
262 | return; | |
263 | } | |
264 | #endif /* LINUX_OOM_ADJUST */ | |
265 | #endif /* WITH_SELINUX || LINUX_OOM_ADJUST */ |