
4 changed files with 543 additions and 8 deletions
-
20CMakeLists.txt
-
486pam_limits.c
-
23spawn-fcgi.c
-
22wscript
@ -0,0 +1,486 @@ |
|||
/* |
|||
* pam_limits - impose resource limits when opening a user session |
|||
* |
|||
* 1.6 - modified for PLD (added process priority settings) |
|||
* by Marcin Korzonek <mkorz@shadow.eu.org> |
|||
* 1.5 - Elliot Lee's "max system logins patch" |
|||
* 1.4 - addressed bug in configuration file parser |
|||
* 1.3 - modified the configuration file format |
|||
* 1.2 - added 'debug' and 'conf=' arguments |
|||
* 1.1 - added @group support |
|||
* 1.0 - initial release - Linux ONLY |
|||
* |
|||
* See end for Copyright information |
|||
*/ |
|||
|
|||
#if !defined(linux) && !defined(__linux) |
|||
#error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!! |
|||
#endif |
|||
|
|||
#include <glib.h> |
|||
|
|||
#include <stdio.h> |
|||
#include <unistd.h> |
|||
#include <string.h> |
|||
#include <ctype.h> |
|||
#include <stdlib.h> |
|||
#include <errno.h> |
|||
#include <syslog.h> |
|||
#include <stdarg.h> |
|||
#include <sys/types.h> |
|||
#include <sys/stat.h> |
|||
#include <sys/resource.h> |
|||
#include <limits.h> |
|||
|
|||
#include <utmp.h> |
|||
#ifndef UT_USER /* some systems have ut_name instead of ut_user */ |
|||
#define UT_USER ut_user |
|||
#endif |
|||
|
|||
#include <grp.h> |
|||
#include <pwd.h> |
|||
|
|||
/* Module defines */ |
|||
#ifndef LIMITS_FILE |
|||
#define LIMITS_FILE "/etc/security/limits.conf" |
|||
#endif |
|||
|
|||
#define LINE_LENGTH 1024 |
|||
|
|||
#define LIMITS_DEF_USER 0 /* limit was set by an user entry */ |
|||
#define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */ |
|||
#define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */ |
|||
#define LIMITS_DEF_ALL 3 /* limit was set by an default entry */ |
|||
#define LIMITS_DEF_DEFAULT 4 /* limit was set by an default entry */ |
|||
#define LIMITS_DEF_NONE 5 /* this limit was not set yet */ |
|||
|
|||
struct user_limits_struct { |
|||
int supported; |
|||
int src_soft; |
|||
int src_hard; |
|||
struct rlimit limit; |
|||
}; |
|||
|
|||
/* internal data */ |
|||
struct pam_limit_s { |
|||
int priority; /* the priority to run user process with */ |
|||
struct user_limits_struct limits[RLIM_NLIMITS]; |
|||
struct passwd *pwd; |
|||
}; |
|||
|
|||
#define LIMIT_PRI RLIM_NLIMITS+1 |
|||
|
|||
#define LIMIT_SOFT 1 |
|||
#define LIMIT_HARD 2 |
|||
|
|||
#define LIMITED_OK 0 /* limit setting appeared to work */ |
|||
#define LIMIT_ERR 1 /* error setting a limit */ |
|||
#define LOGIN_ERR 2 /* too many logins err */ |
|||
|
|||
typedef enum { |
|||
LIMHANDLE_OK = 0, |
|||
LIMHANDLE_ERROR = -1, |
|||
LIMHANDLE_IGNORE = 1 |
|||
} limhandle_t; |
|||
|
|||
static limhandle_t init_limits(struct pam_limit_s *pl) { |
|||
int i, r; |
|||
|
|||
for(i = 0; i < RLIM_NLIMITS; i++) { |
|||
errno = 0; |
|||
r = getrlimit(i, &pl->limits[i].limit); |
|||
if (r == -1) { |
|||
pl->limits[i].supported = 0; |
|||
if (errno != EINVAL) { |
|||
g_printerr("Couldn't getrlimit: %s\n", g_strerror(errno)); |
|||
return LIMHANDLE_ERROR; |
|||
} |
|||
} else { |
|||
pl->limits[i].supported = 1; |
|||
pl->limits[i].src_soft = LIMITS_DEF_NONE; |
|||
pl->limits[i].src_hard = LIMITS_DEF_NONE; |
|||
} |
|||
} |
|||
|
|||
errno = 0; |
|||
pl->priority = getpriority (PRIO_PROCESS, 0); |
|||
if (pl->priority == -1 && errno != 0) { |
|||
g_printerr("Couldn't getpriority: %s\n", g_strerror(errno)); |
|||
return LIMHANDLE_ERROR; |
|||
} |
|||
|
|||
return LIMHANDLE_OK; |
|||
} |
|||
|
|||
static void |
|||
process_limit (int source, const char *lim_type, |
|||
const char *lim_item, const char *lim_value, |
|||
struct pam_limit_s *pl) { |
|||
int limit_item; |
|||
int limit_type = 0; |
|||
int int_value = 0; |
|||
rlim_t rlimit_value = 0; |
|||
char *endptr; |
|||
const char *value_orig = lim_value; |
|||
|
|||
/* |
|||
if (ctrl & PAM_DEBUG_ARG) |
|||
pam_syslog(pamh, LOG_DEBUG, "%s: processing %s %s %s for %s", |
|||
__FUNCTION__, lim_type, lim_item, lim_value, |
|||
limits_def_names[source]);*/ |
|||
|
|||
if (strcmp(lim_item, "cpu") == 0) { |
|||
limit_item = RLIMIT_CPU; |
|||
} else if (strcmp(lim_item, "fsize") == 0) { |
|||
limit_item = RLIMIT_FSIZE; |
|||
} else if (strcmp(lim_item, "data") == 0) { |
|||
limit_item = RLIMIT_DATA; |
|||
} else if (strcmp(lim_item, "stack") == 0) { |
|||
limit_item = RLIMIT_STACK; |
|||
} else if (strcmp(lim_item, "core") == 0) { |
|||
limit_item = RLIMIT_CORE; |
|||
} else if (strcmp(lim_item, "rss") == 0) { |
|||
limit_item = RLIMIT_RSS; |
|||
} else if (strcmp(lim_item, "nproc") == 0) { |
|||
limit_item = RLIMIT_NPROC; |
|||
} else if (strcmp(lim_item, "nofile") == 0) { |
|||
limit_item = RLIMIT_NOFILE; |
|||
} else if (strcmp(lim_item, "memlock") == 0) { |
|||
limit_item = RLIMIT_MEMLOCK; |
|||
} else if (strcmp(lim_item, "as") == 0) { |
|||
limit_item = RLIMIT_AS; |
|||
#ifdef RLIMIT_LOCKS |
|||
} else if (strcmp(lim_item, "locks") == 0) { |
|||
limit_item = RLIMIT_LOCKS; |
|||
#endif |
|||
#ifdef RLIMIT_SIGPENDING |
|||
} else if (strcmp(lim_item, "sigpending") == 0) { |
|||
limit_item = RLIMIT_SIGPENDING; |
|||
#endif |
|||
#ifdef RLIMIT_MSGQUEUE |
|||
} else if (strcmp(lim_item, "msgqueue") == 0) { |
|||
limit_item = RLIMIT_MSGQUEUE; |
|||
#endif |
|||
#ifdef RLIMIT_NICE |
|||
} else if (strcmp(lim_item, "nice") == 0) { |
|||
limit_item = RLIMIT_NICE; |
|||
#endif |
|||
#ifdef RLIMIT_RTPRIO |
|||
} else if (strcmp(lim_item, "rtprio") == 0) { |
|||
limit_item = RLIMIT_RTPRIO; |
|||
#endif |
|||
} else if (strcmp(lim_item, "priority") == 0) { |
|||
limit_item = LIMIT_PRI; |
|||
} else { |
|||
g_printerr("unknown limit item '%s'\n", lim_item); |
|||
return; |
|||
} |
|||
|
|||
if (strcmp(lim_type,"soft")==0) { |
|||
limit_type=LIMIT_SOFT; |
|||
} else if (strcmp(lim_type, "hard")==0) { |
|||
limit_type=LIMIT_HARD; |
|||
} else if (strcmp(lim_type,"-")==0) { |
|||
limit_type=LIMIT_SOFT | LIMIT_HARD; |
|||
} else { |
|||
g_printerr("unknown limit type '%s'\n", lim_type); |
|||
return; |
|||
} |
|||
if (limit_item != LIMIT_PRI |
|||
#ifdef RLIMIT_NICE |
|||
&& limit_item != RLIMIT_NICE |
|||
#endif |
|||
&& (strcmp(lim_value, "-1") == 0 |
|||
|| strcmp(lim_value, "-") == 0 || strcmp(lim_value, "unlimited") == 0 |
|||
|| strcmp(lim_value, "infinity") == 0)) { |
|||
int_value = -1; |
|||
rlimit_value = RLIM_INFINITY; |
|||
} else if (limit_item == LIMIT_PRI |
|||
#ifdef RLIMIT_NICE |
|||
|| limit_item == RLIMIT_NICE |
|||
#endif |
|||
) { |
|||
long temp; |
|||
temp = strtol (lim_value, &endptr, 10); |
|||
temp = temp < INT_MAX ? temp : INT_MAX; |
|||
int_value = temp > INT_MIN ? temp : INT_MIN; |
|||
if (int_value == 0 && value_orig == endptr) { |
|||
g_printerr("wrong limit value '%s' for limit type '%s'\n", |
|||
lim_value, lim_type); |
|||
return; |
|||
} |
|||
} else { |
|||
#ifdef __USE_FILE_OFFSET64 |
|||
rlimit_value = strtoull (lim_value, &endptr, 10); |
|||
#else |
|||
rlimit_value = strtoul (lim_value, &endptr, 10); |
|||
#endif |
|||
if (rlimit_value == 0 && value_orig == endptr) { |
|||
g_printerr("wrong limit value '%s' for limit type '%s'", |
|||
lim_value, lim_type); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
switch(limit_item) { |
|||
case RLIMIT_CPU: |
|||
if (rlimit_value != RLIM_INFINITY) |
|||
rlimit_value *= 60; |
|||
break; |
|||
case RLIMIT_FSIZE: |
|||
case RLIMIT_DATA: |
|||
case RLIMIT_STACK: |
|||
case RLIMIT_CORE: |
|||
case RLIMIT_RSS: |
|||
case RLIMIT_MEMLOCK: |
|||
case RLIMIT_AS: |
|||
if (rlimit_value != RLIM_INFINITY) |
|||
rlimit_value *= 1024; |
|||
break; |
|||
#ifdef RLIMIT_NICE |
|||
case RLIMIT_NICE: |
|||
if (int_value > 19) |
|||
int_value = 19; |
|||
rlimit_value = 19 - int_value; |
|||
break; |
|||
#endif |
|||
} |
|||
|
|||
if (limit_item != LIMIT_PRI) { |
|||
if (limit_type & LIMIT_SOFT) { |
|||
if (pl->limits[limit_item].src_soft < source) { |
|||
return; |
|||
} else { |
|||
pl->limits[limit_item].limit.rlim_cur = rlimit_value; |
|||
pl->limits[limit_item].src_soft = source; |
|||
} |
|||
} |
|||
if (limit_type & LIMIT_HARD) { |
|||
if (pl->limits[limit_item].src_hard < source) { |
|||
return; |
|||
} else { |
|||
pl->limits[limit_item].limit.rlim_max = rlimit_value; |
|||
pl->limits[limit_item].src_hard = source; |
|||
} |
|||
} |
|||
} else { |
|||
/* recent kernels support negative priority limits (=raise priority) */ |
|||
|
|||
if (limit_item == LIMIT_PRI) { |
|||
pl->priority = int_value; |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
|
|||
static gboolean pam_modutil_user_in_group_nam_nam(struct passwd *pwd, const char *group) { |
|||
struct group *grp; |
|||
grp = getgrnam(group); |
|||
size_t i; |
|||
|
|||
if (pwd == NULL || grp == NULL) return FALSE; |
|||
if (pwd->pw_gid == grp->gr_gid) return TRUE; |
|||
|
|||
for (i = 0; (grp->gr_mem != NULL) && (grp->gr_mem[i] != NULL); i++) { |
|||
if (strcmp(pwd->pw_name, grp->gr_mem[i]) == 0) { |
|||
return TRUE; |
|||
} |
|||
} |
|||
|
|||
return FALSE; |
|||
} |
|||
|
|||
static int parse_config_file(const char *conf_file, struct passwd *pwd, struct pam_limit_s *pl) { |
|||
FILE *fil; |
|||
char buf[LINE_LENGTH]; |
|||
|
|||
fil = fopen(conf_file, "r"); |
|||
if (fil == NULL) { |
|||
g_printerr("cannot read settings from '%s': %s", conf_file, g_strerror(errno)); |
|||
return -1; |
|||
} |
|||
|
|||
/* init things */ |
|||
memset(buf, 0, sizeof(buf)); |
|||
/* start the show */ |
|||
while (fgets(buf, LINE_LENGTH, fil) != NULL) { |
|||
char domain[LINE_LENGTH]; |
|||
char ltype[LINE_LENGTH]; |
|||
char item[LINE_LENGTH]; |
|||
char value[LINE_LENGTH]; |
|||
int i; |
|||
size_t j; |
|||
char *tptr; |
|||
|
|||
tptr = buf; |
|||
/* skip the leading white space */ |
|||
while (*tptr && isspace(*tptr)) |
|||
tptr++; |
|||
strncpy(buf, tptr, sizeof(buf)-1); |
|||
buf[sizeof(buf)-1] = '\0'; |
|||
|
|||
/* Rip off the comments */ |
|||
tptr = strchr(buf,'#'); |
|||
if (tptr) |
|||
*tptr = '\0'; |
|||
/* Rip off the newline char */ |
|||
tptr = strchr(buf,'\n'); |
|||
if (tptr) |
|||
*tptr = '\0'; |
|||
/* Anything left ? */ |
|||
if (!strlen(buf)) { |
|||
memset(buf, 0, sizeof(buf)); |
|||
continue; |
|||
} |
|||
|
|||
memset(domain, 0, sizeof(domain)); |
|||
memset(ltype, 0, sizeof(ltype)); |
|||
memset(item, 0, sizeof(item)); |
|||
memset(value, 0, sizeof(value)); |
|||
|
|||
i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value); |
|||
/* |
|||
D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]", |
|||
i, domain, ltype, item, value)); |
|||
*/ |
|||
|
|||
for(j=0; j < strlen(ltype); j++) |
|||
ltype[j]=tolower(ltype[j]); |
|||
for(j=0; j < strlen(item); j++) |
|||
item[j]=tolower(item[j]); |
|||
for(j=0; j < strlen(value); j++) |
|||
value[j]=tolower(value[j]); |
|||
|
|||
if (i == 4) { /* a complete line */ |
|||
if (strcmp(pwd->pw_name, domain) == 0) /* this user have a limit */ |
|||
process_limit(LIMITS_DEF_USER, ltype, item, value, pl); |
|||
else if (domain[0]=='@') { |
|||
if (pam_modutil_user_in_group_nam_nam(pwd, domain+1)) |
|||
process_limit(LIMITS_DEF_GROUP, ltype, item, value, pl); |
|||
} else if (domain[0]=='%') { |
|||
if (strcmp(domain,"%") == 0) |
|||
process_limit(LIMITS_DEF_ALL, ltype, item, value, pl); |
|||
else if (pam_modutil_user_in_group_nam_nam(pwd, domain+1)) { |
|||
process_limit(LIMITS_DEF_ALLGROUP, ltype, item, value, pl); |
|||
} |
|||
} else if (strcmp(domain, "*") == 0) { |
|||
process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, pl); |
|||
} |
|||
} else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */ |
|||
if (strcmp(pwd->pw_name, domain) == 0) { |
|||
fclose(fil); |
|||
return LIMHANDLE_IGNORE; |
|||
} else if (domain[0] == '@' && pam_modutil_user_in_group_nam_nam(pwd, domain+1)) { |
|||
fclose(fil); |
|||
return LIMHANDLE_IGNORE; |
|||
} |
|||
} else { |
|||
g_printerr("invalid line '%s' - skipped\n", buf); |
|||
} |
|||
} |
|||
fclose(fil); |
|||
return LIMHANDLE_OK; |
|||
} |
|||
|
|||
static int setup_limits(struct pam_limit_s *pl) { |
|||
int i; |
|||
int status; |
|||
int retval = LIMHANDLE_OK; |
|||
|
|||
for (i=0, status=LIMITED_OK; i<RLIM_NLIMITS; i++) { |
|||
if (!pl->limits[i].supported) { |
|||
/* skip it if its not known to the system */ |
|||
continue; |
|||
} |
|||
if (pl->limits[i].src_soft == LIMITS_DEF_NONE && |
|||
pl->limits[i].src_hard == LIMITS_DEF_NONE) { |
|||
/* skip it if its not initialized */ |
|||
continue; |
|||
} |
|||
if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max) |
|||
pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max; |
|||
status |= setrlimit(i, &pl->limits[i].limit); |
|||
} |
|||
|
|||
if (status) { |
|||
retval = LIMHANDLE_ERROR; |
|||
} |
|||
|
|||
status = setpriority(PRIO_PROCESS, 0, pl->priority); |
|||
if (status != 0) { |
|||
retval = LIMHANDLE_ERROR; |
|||
} |
|||
|
|||
return retval; |
|||
} |
|||
|
|||
/* now the session stuff */ |
|||
int pam_set_limits(const char *conf_file, const char *username) { |
|||
int retval; |
|||
struct pam_limit_s pl; |
|||
struct passwd *pwd; |
|||
|
|||
pwd = getpwnam(username); |
|||
if (NULL == pwd) return LIMHANDLE_ERROR; |
|||
|
|||
if (!conf_file) conf_file = LIMITS_FILE; |
|||
memset(&pl, 0, sizeof(pl)); |
|||
|
|||
retval = init_limits(&pl); |
|||
if (retval != LIMHANDLE_OK) { |
|||
g_printerr("cannot initialize\n"); |
|||
return retval; |
|||
} |
|||
|
|||
retval = parse_config_file(conf_file, pwd, &pl); |
|||
if (retval == LIMHANDLE_IGNORE) { |
|||
return LIMHANDLE_OK; |
|||
} |
|||
if (retval != LIMHANDLE_OK) { |
|||
g_printerr("error parsing the configuration file\n"); |
|||
return retval; |
|||
} |
|||
|
|||
retval = setup_limits(&pl); |
|||
if (retval != LIMHANDLE_OK) { |
|||
return retval; |
|||
} |
|||
|
|||
return LIMHANDLE_OK; |
|||
} |
|||
|
|||
/* |
|||
* Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.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: |
|||
* 1. Redistributions of source code must retain the above copyright |
|||
* notice, and the entire permission notice in its entirety, |
|||
* including the disclaimer of warranties. |
|||
* 2. 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. |
|||
* 3. The name of the author may not be used to endorse or promote |
|||
* products derived from this software without specific prior |
|||
* written permission. |
|||
* |
|||
* ALTERNATIVELY, this product may be distributed under the terms of |
|||
* the GNU Public License, in which case the provisions of the GPL are |
|||
* required INSTEAD OF the above restrictions. (This clause is |
|||
* necessary due to a potential bad interaction between the GPL and |
|||
* the restrictions contained in a BSD-style copyright.) |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. |
|||
*/ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue