1 /* 2 * Copyright (c) 2005 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #include "login_locl.h" 35 36 RCSID("$Id: limits_conf.c 19215 2006-12-04 23:41:18Z lha $"); 37 38 #include <errno.h> 39 #include <limits.h> 40 #ifdef HAVE_SYS_RESOURCE_H 41 #include <sys/resource.h> 42 #endif 43 44 struct limit { 45 const char *name; 46 int resource; 47 int scale; 48 int has_limit; 49 struct rlimit limit; 50 } limits[] = { 51 #define LIM(X, S) { #X, RLIMIT_##X, S, 0 } 52 LIM(CORE, 1024), 53 LIM(CPU, 60), 54 LIM(DATA, 1024), 55 LIM(FSIZE, 1024), 56 #ifdef RLIMIT_MEMLOCK 57 LIM(MEMLOCK, 1024), 58 #endif 59 LIM(NOFILE, 1), 60 #ifdef RLIMIT_NPROC 61 LIM(NPROC, 1), 62 #endif 63 #ifdef RLIMIT_RSS 64 LIM(RSS, 1024), 65 #endif 66 LIM(STACK, 1024), 67 68 #ifdef RLIMIT_AS 69 LIM(AS, 1024), 70 #endif 71 #ifdef RLIMIT_LOCKS 72 LIM(LOCKS, 1), 73 #endif 74 /* 75 maxlogins 76 priority 77 */ 78 { NULL, 0 } 79 }; 80 81 static struct limit * 82 find_limit(const char *name) 83 { 84 struct limit *l; 85 for(l = limits; l->name != NULL; l++) 86 if(strcasecmp(name, l->name) == 0) 87 return l; 88 return NULL; 89 } 90 91 /* this function reads limits.conf files similar to pam_limits 92 unimplemented features include: 93 % maxlogins 94 "-" no limits, 95 priorities etc that are not set via setrlimit 96 XXX uses static storage, and clobbers getgr* 97 */ 98 99 int 100 read_limits_conf(const char *file, const struct passwd *pwd) 101 { 102 FILE *f; 103 char *args[4]; 104 int lineno = 0; 105 char buf[1024]; 106 struct limit *l; 107 rlim_t value; 108 109 f = fopen(file, "r"); 110 if(f == NULL) { 111 if(errno != ENOENT && errno != ENOTDIR) 112 syslog(LOG_ERR, "%s: %m", file); 113 return -1; 114 } 115 116 while(fgets(buf, sizeof(buf), f) != NULL) { 117 char *last = NULL; 118 char *end = NULL; 119 int level; 120 121 lineno++; 122 123 if(buf[0] == '\0') { 124 syslog(LOG_ERR, "%s: line %d: NUL character", file, lineno); 125 continue; 126 } 127 if(buf[strlen(buf) - 1] != '\n') { 128 /* file did not end with a newline, figure out if we're at 129 the EOF, or if our buffer was too small */ 130 int eof = 1; 131 int c; 132 while((c = fgetc(f)) != EOF) { 133 eof = 0; 134 if(c == '\n') 135 break; 136 } 137 if(!eof) { 138 syslog(LOG_ERR, "%s: line %d: line too long", file, lineno); 139 continue; 140 } 141 } 142 buf[strcspn(buf, "#\r\n")] = '\0'; 143 if((args[0] = strtok_r(buf, " \t", &last)) == NULL || 144 (args[1] = strtok_r(NULL, " \t", &last)) == NULL || 145 (args[2] = strtok_r(NULL, " \t", &last)) == NULL || 146 (args[3] = strtok_r(NULL, " \t", &last)) == NULL) { 147 if(args[0] != NULL) /* this would include comment lines */ 148 syslog(LOG_ERR, "%s: line %d: malformed line", file, lineno); 149 continue; 150 } 151 152 l = find_limit(args[2]); 153 if(l == NULL) { 154 syslog(LOG_ERR, "%s: line %d: unknown limit %s", file, lineno, args[2]); 155 continue; 156 } 157 if(strcmp(args[3], "-") == 0) { 158 value = RLIM_INFINITY; 159 } else { 160 errno = 0; 161 value = strtol(args[3], &end, 10); 162 if(*end != '\0') { 163 syslog(LOG_ERR, "%s: line %d: bad value %s", file, lineno, args[3]); 164 continue; 165 } 166 if((value == LONG_MIN || value == LONG_MAX) && errno == ERANGE) { 167 syslog(LOG_ERR, "%s: line %d: bad value %s", file, lineno, args[3]); 168 continue; 169 } 170 if(value * l->scale < value) 171 value = RLIM_INFINITY; 172 else 173 value *= l->scale; 174 } 175 level = 0; 176 /* XXX unclear: if you set group hard and user soft limit, 177 should the hard limit still apply? this code doesn't. */ 178 if(strcmp(args[0], pwd->pw_name) == 0) 179 level = 3; 180 if(*args[0] == '@') { 181 struct group *gr; 182 gr = getgrnam(args[0] + 1); 183 if(gr != NULL && gr->gr_gid == pwd->pw_gid) 184 level = 2; 185 } 186 if(strcmp(args[0], "*") == 0) 187 level = 1; 188 if(level == 0 || level < l->has_limit) /* not for us */ 189 continue; 190 if(l->has_limit < level) { 191 if(getrlimit(l->resource, &l->limit) < 0) 192 continue; 193 l->has_limit = level; 194 } 195 196 /* XXX unclear: if you soft to more than default hard, should 197 we set hard to soft? this code doesn't. */ 198 if(strcasecmp(args[1], "soft") == 0 || strcmp(args[1], "-") == 0) 199 l->limit.rlim_cur = value; 200 if(strcasecmp(args[1], "hard") == 0 || strcmp(args[1], "-") == 0) 201 l->limit.rlim_max = value; 202 } 203 fclose(f); 204 for(l = limits; l->name != NULL; l++) { 205 if(l->has_limit) { 206 if(l->limit.rlim_cur > l->limit.rlim_max) 207 l->limit.rlim_cur = l->limit.rlim_max; 208 if(setrlimit(l->resource, &l->limit) != 0) 209 syslog(LOG_ERR, "setrlimit RLIM_%s failed: %m", l->name); 210 } 211 l->has_limit = 0; 212 } 213 return 0; 214 } 215