python/pytest-regressions: update to 2.6.0
[oi-userland.git] / components / network / proftpd / mod_solaris_priv.c
blob1c06f06f1a8bcf968d33c915d23ec5309e6446c7
1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 1997, 1998 Public Flood Software
4 * Copyright (c) 2003-2010 The ProFTPD Project team
5 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 * As a special exemption, the copyright holders give permission to link
22 * this program with OpenSSL and distribute the resulting executable without
23 * including the source code for OpenSSL in the source distribution.
26 /* Use Solaris privileges to severely limit root's access. After user
27 * authentication, this module _completely_ gives up most privileges,
28 * except for the * bare minimum functionality that is required.
29 * VERY highly recommended for security-consious admins.
31 * The concept of this was copied from the Linux mod_cap. Solaris
32 * also has the concept of basic privileges that we can take away to further
33 * restrict a process lower than what a normal user process can do, this
34 * module removes some of those as well.
37 #include <stdio.h>
38 #include <stdlib.h>
40 #include <priv.h>
42 #include "conf.h"
43 #include "privs.h"
45 #define MOD_SOLARIS_PRIV_VERSION "mod_solaris_priv/1.0"
47 /* Configuration handlers
50 #define PRIV_USE_FILE_CHOWN 0x0001
51 #define PRIV_USE_FILE_CHOWN_SELF 0x0002
52 #define PRIV_USE_DAC_READ 0x0004
53 #define PRIV_USE_DAC_WRITE 0x0008
54 #define PRIV_USE_DAC_SEARCH 0x0010
55 #define PRIV_USE_SETID 0x0020
56 #define PRIV_USE_FILE_OWNER 0x0040
57 #define PRIV_DROP_FILE_WRITE 0x0080
59 #define PRIV_SOL_ROOT_PRIVS \
60 (PRIV_USE_FILE_CHOWN | PRIV_USE_FILE_CHOWN_SELF | \
61 PRIV_USE_DAC_READ | PRIV_USE_DAC_WRITE | PRIV_USE_DAC_SEARCH | \
62 PRIV_USE_FILE_OWNER)
64 static unsigned int solaris_priv_flags = 0;
65 static unsigned char use_privs = TRUE;
67 MODRET set_solaris_priv(cmd_rec *cmd) {
68 unsigned int flags = 0;
69 config_rec *c = NULL;
70 register unsigned int i = 0;
72 if (cmd->argc - 1 < 1)
73 CONF_ERROR(cmd, "need at least one parameter");
75 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
77 /* PRIV_CHOWN is enabled by default. */
78 flags |= PRIV_USE_FILE_CHOWN;
80 for (i = 1; i < cmd->argc; i++) {
81 char *cm = cmd->argv[i];
82 char *cp = cmd->argv[i];
83 cp++;
85 if (cm[0] != '+' && cm[0] != '-')
86 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": bad option: '",
87 cm, "'", NULL));
89 if (strcasecmp(cp, "PRIV_USE_FILE_CHOWN") == 0) {
90 if (cm[0] == '-')
91 flags &= ~PRIV_USE_FILE_CHOWN;
93 } else if (strcasecmp(cp, "PRIV_FILE_CHOWN_SELF") == 0) {
94 if (cm[0] == '-')
95 flags &= ~PRIV_USE_FILE_CHOWN_SELF;
97 } else if (strcasecmp(cp, "PRIV_DAC_READ") == 0) {
98 if (cm[0] == '+')
99 flags |= PRIV_USE_DAC_READ;
101 } else if (strcasecmp(cp, "PRIV_DAC_WRITE") == 0) {
102 if (cm[0] == '+')
103 flags |= PRIV_USE_DAC_WRITE;
105 } else if (strcasecmp(cp, "PRIV_DAC_SEARCH") == 0) {
106 if (cm[0] == '+')
107 flags |= PRIV_USE_DAC_SEARCH;
109 } else if (strcasecmp(cp, "PRIV_FILE_OWNER") == 0) {
110 if (cm[0] == '+')
111 flags |= PRIV_USE_FILE_OWNER;
113 } else {
114 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown privilege: '",
115 cp, "'", NULL));
119 c = add_config_param(cmd->argv[0], 1, NULL);
120 c->argv[0] = pcalloc(c->pool, sizeof(unsigned int));
121 *((unsigned int *) c->argv[0]) = flags;
123 return PR_HANDLED(cmd);
127 MODRET set_solaris_priv_engine(cmd_rec *cmd) {
128 int bool = -1;
129 config_rec *c = NULL;
131 CHECK_ARGS(cmd, 1);
132 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
134 bool = get_boolean(cmd, 1);
135 if (bool == -1)
136 CONF_ERROR(cmd, "expecting Boolean parameter");
138 c = add_config_param(cmd->argv[0], 1, NULL);
139 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
140 *((unsigned char *) c->argv[0]) = bool;
142 return PR_HANDLED(cmd);
145 /* Command handlers
148 /* The POST_CMD handler for "PASS" is only called after PASS has
149 * successfully completed, which means authentication is successful,
150 * so we can "tweak" our root access down to almost nothing.
152 MODRET solaris_priv_post_pass(cmd_rec *cmd) {
153 int res = -1;
154 int priv_flags = solaris_priv_flags;
155 priv_set_t *p = NULL;
156 priv_set_t *i = NULL;
158 if (!use_privs)
159 return PR_DECLINED(cmd);
161 /* If we authenticated as root, we get all appropriate privs */
162 if (session.uid == 0) {
163 priv_flags = PRIV_SOL_ROOT_PRIVS;
166 pr_signals_block();
168 /* The only privilege we need is PRIV_NET_PRIVADDR (bind
169 * ports < 1024). Everything else can be discarded. We set this
170 * in the permitted set only, as when we switch away from root
171 * we lose effective anyhow, and must reset it.
173 * We also remove the basic Solaris privileges we know we will
174 * never need.
177 i = priv_allocset();
178 if (i == NULL)
179 goto out;
180 priv_basicset(i);
181 priv_delset(i, PRIV_PROC_EXEC);
182 priv_delset(i, PRIV_PROC_FORK);
183 priv_delset(i, PRIV_PROC_INFO);
184 priv_delset(i, PRIV_PROC_SESSION);
185 setppriv(PRIV_SET, PRIV_INHERITABLE, i);
187 p = priv_allocset();
188 if (p == NULL)
189 goto out;
190 priv_basicset(p);
192 priv_addset(p, PRIV_NET_PRIVADDR);
193 priv_addset(p, PRIV_PROC_AUDIT);
195 priv_delset(p, PRIV_PROC_EXEC);
196 priv_delset(p, PRIV_PROC_FORK);
197 priv_delset(p, PRIV_PROC_INFO);
198 priv_delset(p, PRIV_PROC_SESSION);
200 if (priv_flags & PRIV_USE_SETID)
201 priv_addset(p, PRIV_PROC_SETID);
203 /* Add any of the configurable privileges. */
204 if (priv_flags & PRIV_USE_FILE_CHOWN)
205 priv_addset(p, PRIV_FILE_CHOWN);
207 if (priv_flags & PRIV_USE_FILE_CHOWN_SELF)
208 priv_addset(p, PRIV_FILE_CHOWN_SELF);
210 if (priv_flags & PRIV_USE_DAC_READ)
211 priv_addset(p, PRIV_FILE_DAC_READ);
213 if (priv_flags & PRIV_USE_DAC_WRITE)
214 priv_addset(p, PRIV_FILE_DAC_WRITE);
216 if (priv_flags & PRIV_USE_DAC_SEARCH)
217 priv_addset(p, PRIV_FILE_DAC_SEARCH);
219 if (priv_flags & PRIV_USE_FILE_OWNER)
220 priv_addset(p, PRIV_FILE_OWNER);
222 if (priv_flags & PRIV_DROP_FILE_WRITE)
223 priv_delset(p, PRIV_FILE_WRITE);
225 res = setppriv(PRIV_SET, PRIV_PERMITTED, p);
226 res = setppriv(PRIV_SET, PRIV_EFFECTIVE, p);
228 if (setreuid(session.uid, session.uid) == -1) {
229 pr_log_pri(PR_LOG_ERR, MOD_SOLARIS_PRIV_VERSION ": setreuid: %s",
230 strerror(errno));
231 priv_freeset(i);
232 priv_freeset(p);
233 pr_signals_unblock();
234 end_login(1);
237 out:
238 if (i != NULL)
239 priv_freeset(i);
240 if (p != NULL)
241 priv_freeset(p);
243 pr_signals_unblock();
245 if (res != -1) {
246 /* That's it! Disable all further id switching */
247 session.disable_id_switching = TRUE;
249 } else {
250 pr_log_pri(PR_LOG_NOTICE, MOD_SOLARIS_PRIV_VERSION ": attempt to configure "
251 "privileges failed, reverting to normal operation");
254 return PR_DECLINED(cmd);
257 static void log_err_effective(const char* fn) {
258 pr_log_pri(PR_LOG_ERR, MOD_SOLARIS_PRIV_VERSION ": %s(%s): %s",
259 fn, "effective", strerror(errno));
262 /* Initialization routines
265 static int solaris_priv_sess_init(void) {
266 /* Check to see if the lowering of privileges has been disabled in the
267 * configuration file.
269 if (use_privs) {
270 unsigned char *solaris_priv_engine;
272 solaris_priv_engine = get_param_ptr(main_server->conf, "PrivilegeEngine", FALSE);
273 if (solaris_priv_engine &&
274 *solaris_priv_engine == FALSE) {
275 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
276 ": lowering of privileges disabled");
277 use_privs = FALSE;
281 /* Check for which specific privileges to include/exclude. */
282 if (use_privs) {
283 int use_setuid = FALSE;
284 config_rec *c;
286 c = find_config(main_server->conf, CONF_PARAM, "PrivilegeSet", FALSE);
287 if (c != NULL) {
288 solaris_priv_flags = *((unsigned int *) c->argv[0]);
290 if (!(solaris_priv_flags & PRIV_USE_FILE_CHOWN)) {
291 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
292 ": removing PRIV_CHOWN privilege");
295 if (solaris_priv_flags & PRIV_USE_DAC_READ) {
296 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
297 ": adding PRIV_FILE_DAC_READ privilege");
300 if (solaris_priv_flags & PRIV_USE_DAC_WRITE) {
301 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
302 ": adding PRIV_FILE_DAC_WRITE privilege");
305 if (solaris_priv_flags & PRIV_USE_DAC_SEARCH) {
306 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
307 ": adding PRIV_DAC_SEARCH privilege");
310 if (solaris_priv_flags & PRIV_USE_FILE_OWNER) {
311 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
312 ": adding PRIV_FILE_OWNER privilege");
316 c = find_config(main_server->conf, CONF_PARAM, "AllowOverwrite", FALSE);
317 if (c && *((int *) c->argv[0]) == FALSE) {
318 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
319 ": removing PRIV_FILE_WRITE basic privilege");
320 solaris_priv_flags |= PRIV_DROP_FILE_WRITE;
324 /* We also need to check for things which want to revoke root privs
325 * altogether: mod_exec, mod_sftp, and the RootRevoke directive.
326 * Revoking root privs completely requires the SETUID/SETGID
327 * privileges.
330 if (use_setuid == FALSE &&
331 pr_module_exists("mod_sftp.c")) {
332 c = find_config(main_server->conf, CONF_PARAM, "SFTPEngine", FALSE);
333 if (c &&
334 *((int *) c->argv[0]) == TRUE) {
335 use_setuid = TRUE;
339 if (use_setuid == FALSE &&
340 pr_module_exists("mod_exec.c")) {
341 c = find_config(main_server->conf, CONF_PARAM, "ExecEngine", FALSE);
342 if (c &&
343 *((unsigned char *) c->argv[0]) == TRUE) {
344 use_setuid = TRUE;
348 if (use_setuid == FALSE) {
349 c = find_config(main_server->conf, CONF_PARAM, "RootRevoke", FALSE);
350 if (c &&
351 *((unsigned char *) c->argv[0]) == TRUE) {
352 use_setuid = TRUE;
356 if (use_setuid) {
357 solaris_priv_flags |= PRIV_USE_SETID;
358 pr_log_debug(DEBUG3, MOD_SOLARIS_PRIV_VERSION
359 ": adding PRIV_SETID ");
364 return 0;
367 static int solaris_priv_module_init(void) {
369 return 0;
373 /* Module API tables
376 static conftable solaris_priv_conftab[] = {
377 { "PrivilegeEngine", set_solaris_priv_engine, NULL },
378 { "PrivilegeSet", set_solaris_priv, NULL },
379 { NULL, NULL, NULL }
382 static cmdtable solaris_priv_cmdtab[] = {
383 { POST_CMD, C_PASS, G_NONE, solaris_priv_post_pass, FALSE, FALSE },
384 { 0, NULL }
387 module solaris_priv_module = {
388 NULL, NULL,
390 /* Module API version */
391 0x20,
393 /* Module name */
394 "privileges",
396 /* Module configuration handler table */
397 solaris_priv_conftab,
399 /* Module command handler table */
400 solaris_priv_cmdtab,
402 /* Module authentication handler table */
403 NULL,
405 /* Module initialization */
406 solaris_priv_module_init,
408 /* Session initialization */
409 solaris_priv_sess_init,
411 /* Module version */
412 MOD_SOLARIS_PRIV_VERSION