spawns FastCGI processes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

488 lines
13 KiB

  1. /*
  2. * pam_limits - impose resource limits when opening a user session
  3. *
  4. * 1.6 - modified for PLD (added process priority settings)
  5. * by Marcin Korzonek <mkorz@shadow.eu.org>
  6. * 1.5 - Elliot Lee's "max system logins patch"
  7. * 1.4 - addressed bug in configuration file parser
  8. * 1.3 - modified the configuration file format
  9. * 1.2 - added 'debug' and 'conf=' arguments
  10. * 1.1 - added @group support
  11. * 1.0 - initial release - Linux ONLY
  12. *
  13. * See end for Copyright information
  14. */
  15. #if !defined(linux) && !defined(__linux)
  16. #error THIS CODE IS KNOWN TO WORK ONLY ON LINUX !!!
  17. #endif
  18. #include <glib.h>
  19. #include <stdio.h>
  20. #include <unistd.h>
  21. #include <string.h>
  22. #include <ctype.h>
  23. #include <stdlib.h>
  24. #include <errno.h>
  25. #include <syslog.h>
  26. #include <stdarg.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <sys/resource.h>
  30. #include <limits.h>
  31. #include <utmp.h>
  32. #ifndef UT_USER /* some systems have ut_name instead of ut_user */
  33. #define UT_USER ut_user
  34. #endif
  35. #include <grp.h>
  36. #include <pwd.h>
  37. /* Module defines */
  38. #ifndef LIMITS_FILE
  39. #define LIMITS_FILE "/etc/security/limits.conf"
  40. #endif
  41. #define LINE_LENGTH 1024
  42. #define LIMITS_DEF_USER 0 /* limit was set by an user entry */
  43. #define LIMITS_DEF_GROUP 1 /* limit was set by a group entry */
  44. #define LIMITS_DEF_ALLGROUP 2 /* limit was set by a group entry */
  45. #define LIMITS_DEF_ALL 3 /* limit was set by an default entry */
  46. #define LIMITS_DEF_DEFAULT 4 /* limit was set by an default entry */
  47. #define LIMITS_DEF_NONE 5 /* this limit was not set yet */
  48. struct user_limits_struct {
  49. int supported;
  50. int src_soft;
  51. int src_hard;
  52. struct rlimit limit;
  53. };
  54. /* internal data */
  55. struct pam_limit_s {
  56. int priority; /* the priority to run user process with */
  57. struct user_limits_struct limits[RLIM_NLIMITS];
  58. struct passwd *pwd;
  59. };
  60. #define LIMIT_PRI RLIM_NLIMITS+1
  61. #define LIMIT_SOFT 1
  62. #define LIMIT_HARD 2
  63. #define LIMITED_OK 0 /* limit setting appeared to work */
  64. #define LIMIT_ERR 1 /* error setting a limit */
  65. #define LOGIN_ERR 2 /* too many logins err */
  66. typedef enum {
  67. LIMHANDLE_OK = 0,
  68. LIMHANDLE_ERROR = -1,
  69. LIMHANDLE_IGNORE = 1
  70. } limhandle_t;
  71. static limhandle_t init_limits(struct pam_limit_s *pl) {
  72. int i, r;
  73. for(i = 0; i < RLIM_NLIMITS; i++) {
  74. errno = 0;
  75. r = getrlimit(i, &pl->limits[i].limit);
  76. if (r == -1) {
  77. pl->limits[i].supported = 0;
  78. if (errno != EINVAL) {
  79. g_printerr("Couldn't getrlimit: %s\n", g_strerror(errno));
  80. return LIMHANDLE_ERROR;
  81. }
  82. } else {
  83. pl->limits[i].supported = 1;
  84. pl->limits[i].src_soft = LIMITS_DEF_NONE;
  85. pl->limits[i].src_hard = LIMITS_DEF_NONE;
  86. }
  87. }
  88. errno = 0;
  89. pl->priority = getpriority (PRIO_PROCESS, 0);
  90. if (pl->priority == -1 && errno != 0) {
  91. g_printerr("Couldn't getpriority: %s\n", g_strerror(errno));
  92. return LIMHANDLE_ERROR;
  93. }
  94. return LIMHANDLE_OK;
  95. }
  96. static void
  97. process_limit (int source, const char *lim_type,
  98. const char *lim_item, const char *lim_value,
  99. struct pam_limit_s *pl) {
  100. int limit_item;
  101. int limit_type = 0;
  102. int int_value = 0;
  103. rlim_t rlimit_value = 0;
  104. char *endptr;
  105. const char *value_orig = lim_value;
  106. /*
  107. if (ctrl & PAM_DEBUG_ARG)
  108. pam_syslog(pamh, LOG_DEBUG, "%s: processing %s %s %s for %s",
  109. __FUNCTION__, lim_type, lim_item, lim_value,
  110. limits_def_names[source]);*/
  111. if (strcmp(lim_item, "cpu") == 0) {
  112. limit_item = RLIMIT_CPU;
  113. } else if (strcmp(lim_item, "fsize") == 0) {
  114. limit_item = RLIMIT_FSIZE;
  115. } else if (strcmp(lim_item, "data") == 0) {
  116. limit_item = RLIMIT_DATA;
  117. } else if (strcmp(lim_item, "stack") == 0) {
  118. limit_item = RLIMIT_STACK;
  119. } else if (strcmp(lim_item, "core") == 0) {
  120. limit_item = RLIMIT_CORE;
  121. } else if (strcmp(lim_item, "rss") == 0) {
  122. limit_item = RLIMIT_RSS;
  123. } else if (strcmp(lim_item, "nproc") == 0) {
  124. limit_item = RLIMIT_NPROC;
  125. } else if (strcmp(lim_item, "nofile") == 0) {
  126. limit_item = RLIMIT_NOFILE;
  127. } else if (strcmp(lim_item, "memlock") == 0) {
  128. limit_item = RLIMIT_MEMLOCK;
  129. } else if (strcmp(lim_item, "as") == 0) {
  130. limit_item = RLIMIT_AS;
  131. #ifdef RLIMIT_LOCKS
  132. } else if (strcmp(lim_item, "locks") == 0) {
  133. limit_item = RLIMIT_LOCKS;
  134. #endif
  135. #ifdef RLIMIT_SIGPENDING
  136. } else if (strcmp(lim_item, "sigpending") == 0) {
  137. limit_item = RLIMIT_SIGPENDING;
  138. #endif
  139. #ifdef RLIMIT_MSGQUEUE
  140. } else if (strcmp(lim_item, "msgqueue") == 0) {
  141. limit_item = RLIMIT_MSGQUEUE;
  142. #endif
  143. #ifdef RLIMIT_NICE
  144. } else if (strcmp(lim_item, "nice") == 0) {
  145. limit_item = RLIMIT_NICE;
  146. #endif
  147. #ifdef RLIMIT_RTPRIO
  148. } else if (strcmp(lim_item, "rtprio") == 0) {
  149. limit_item = RLIMIT_RTPRIO;
  150. #endif
  151. } else if (strcmp(lim_item, "priority") == 0) {
  152. limit_item = LIMIT_PRI;
  153. } else {
  154. g_printerr("unknown limit item '%s'\n", lim_item);
  155. return;
  156. }
  157. if (strcmp(lim_type,"soft")==0) {
  158. limit_type=LIMIT_SOFT;
  159. } else if (strcmp(lim_type, "hard")==0) {
  160. limit_type=LIMIT_HARD;
  161. } else if (strcmp(lim_type,"-")==0) {
  162. limit_type=LIMIT_SOFT | LIMIT_HARD;
  163. } else {
  164. g_printerr("unknown limit type '%s'\n", lim_type);
  165. return;
  166. }
  167. if (limit_item != LIMIT_PRI
  168. #ifdef RLIMIT_NICE
  169. && limit_item != RLIMIT_NICE
  170. #endif
  171. && (strcmp(lim_value, "-1") == 0
  172. || strcmp(lim_value, "-") == 0 || strcmp(lim_value, "unlimited") == 0
  173. || strcmp(lim_value, "infinity") == 0)) {
  174. int_value = -1;
  175. rlimit_value = RLIM_INFINITY;
  176. } else if (limit_item == LIMIT_PRI
  177. #ifdef RLIMIT_NICE
  178. || limit_item == RLIMIT_NICE
  179. #endif
  180. ) {
  181. long temp;
  182. temp = strtol (lim_value, &endptr, 10);
  183. temp = temp < INT_MAX ? temp : INT_MAX;
  184. int_value = temp > INT_MIN ? temp : INT_MIN;
  185. if (int_value == 0 && value_orig == endptr) {
  186. g_printerr("wrong limit value '%s' for limit type '%s'\n",
  187. lim_value, lim_type);
  188. return;
  189. }
  190. } else {
  191. #ifdef __USE_FILE_OFFSET64
  192. rlimit_value = strtoull (lim_value, &endptr, 10);
  193. #else
  194. rlimit_value = strtoul (lim_value, &endptr, 10);
  195. #endif
  196. if (rlimit_value == 0 && value_orig == endptr) {
  197. g_printerr("wrong limit value '%s' for limit type '%s'",
  198. lim_value, lim_type);
  199. return;
  200. }
  201. }
  202. switch(limit_item) {
  203. case RLIMIT_CPU:
  204. if (rlimit_value != RLIM_INFINITY)
  205. rlimit_value *= 60;
  206. break;
  207. case RLIMIT_FSIZE:
  208. case RLIMIT_DATA:
  209. case RLIMIT_STACK:
  210. case RLIMIT_CORE:
  211. case RLIMIT_RSS:
  212. case RLIMIT_MEMLOCK:
  213. case RLIMIT_AS:
  214. if (rlimit_value != RLIM_INFINITY)
  215. rlimit_value *= 1024;
  216. break;
  217. #ifdef RLIMIT_NICE
  218. case RLIMIT_NICE:
  219. if (int_value > 19)
  220. int_value = 19;
  221. rlimit_value = 19 - int_value;
  222. break;
  223. #endif
  224. }
  225. if (limit_item != LIMIT_PRI) {
  226. if (limit_type & LIMIT_SOFT) {
  227. if (pl->limits[limit_item].src_soft < source) {
  228. return;
  229. } else {
  230. pl->limits[limit_item].limit.rlim_cur = rlimit_value;
  231. pl->limits[limit_item].src_soft = source;
  232. }
  233. }
  234. if (limit_type & LIMIT_HARD) {
  235. if (pl->limits[limit_item].src_hard < source) {
  236. return;
  237. } else {
  238. pl->limits[limit_item].limit.rlim_max = rlimit_value;
  239. pl->limits[limit_item].src_hard = source;
  240. }
  241. }
  242. } else {
  243. /* recent kernels support negative priority limits (=raise priority) */
  244. if (limit_item == LIMIT_PRI) {
  245. pl->priority = int_value;
  246. }
  247. }
  248. return;
  249. }
  250. static gboolean pam_modutil_user_in_group_nam_nam(struct passwd *pwd, const char *group) {
  251. struct group *grp;
  252. size_t i;
  253. grp = getgrnam(group);
  254. if (pwd == NULL || grp == NULL) return FALSE;
  255. if (pwd->pw_gid == grp->gr_gid) return TRUE;
  256. for (i = 0; (grp->gr_mem != NULL) && (grp->gr_mem[i] != NULL); i++) {
  257. if (strcmp(pwd->pw_name, grp->gr_mem[i]) == 0) {
  258. return TRUE;
  259. }
  260. }
  261. return FALSE;
  262. }
  263. static int parse_config_file(const char *conf_file, struct passwd *pwd, struct pam_limit_s *pl) {
  264. FILE *fil;
  265. char buf[LINE_LENGTH];
  266. fil = fopen(conf_file, "r");
  267. if (fil == NULL) {
  268. g_printerr("cannot read settings from '%s': %s", conf_file, g_strerror(errno));
  269. return -1;
  270. }
  271. /* init things */
  272. memset(buf, 0, sizeof(buf));
  273. /* start the show */
  274. while (fgets(buf, LINE_LENGTH, fil) != NULL) {
  275. char domain[LINE_LENGTH];
  276. char ltype[LINE_LENGTH];
  277. char item[LINE_LENGTH];
  278. char value[LINE_LENGTH];
  279. int i;
  280. size_t j;
  281. char *tptr;
  282. tptr = buf;
  283. /* skip the leading white space */
  284. while (*tptr && isspace(*tptr))
  285. tptr++;
  286. strncpy(buf, tptr, sizeof(buf)-1);
  287. buf[sizeof(buf)-1] = '\0';
  288. /* Rip off the comments */
  289. tptr = strchr(buf,'#');
  290. if (tptr)
  291. *tptr = '\0';
  292. /* Rip off the newline char */
  293. tptr = strchr(buf,'\n');
  294. if (tptr)
  295. *tptr = '\0';
  296. /* Anything left ? */
  297. if (!strlen(buf)) {
  298. memset(buf, 0, sizeof(buf));
  299. continue;
  300. }
  301. memset(domain, 0, sizeof(domain));
  302. memset(ltype, 0, sizeof(ltype));
  303. memset(item, 0, sizeof(item));
  304. memset(value, 0, sizeof(value));
  305. i = sscanf(buf,"%s%s%s%s", domain, ltype, item, value);
  306. /*
  307. D(("scanned line[%d]: domain[%s], ltype[%s], item[%s], value[%s]",
  308. i, domain, ltype, item, value));
  309. */
  310. for(j=0; j < strlen(ltype); j++)
  311. ltype[j]=tolower(ltype[j]);
  312. for(j=0; j < strlen(item); j++)
  313. item[j]=tolower(item[j]);
  314. for(j=0; j < strlen(value); j++)
  315. value[j]=tolower(value[j]);
  316. if (i == 4) { /* a complete line */
  317. if (strcmp(pwd->pw_name, domain) == 0) /* this user have a limit */
  318. process_limit(LIMITS_DEF_USER, ltype, item, value, pl);
  319. else if (domain[0]=='@') {
  320. if (pam_modutil_user_in_group_nam_nam(pwd, domain+1))
  321. process_limit(LIMITS_DEF_GROUP, ltype, item, value, pl);
  322. } else if (domain[0]=='%') {
  323. if (strcmp(domain,"%") == 0)
  324. process_limit(LIMITS_DEF_ALL, ltype, item, value, pl);
  325. else if (pam_modutil_user_in_group_nam_nam(pwd, domain+1)) {
  326. process_limit(LIMITS_DEF_ALLGROUP, ltype, item, value, pl);
  327. }
  328. } else if (strcmp(domain, "*") == 0) {
  329. process_limit(LIMITS_DEF_DEFAULT, ltype, item, value, pl);
  330. }
  331. } else if (i == 2 && ltype[0] == '-') { /* Probably a no-limit line */
  332. if (strcmp(pwd->pw_name, domain) == 0) {
  333. fclose(fil);
  334. return LIMHANDLE_IGNORE;
  335. } else if (domain[0] == '@' && pam_modutil_user_in_group_nam_nam(pwd, domain+1)) {
  336. fclose(fil);
  337. return LIMHANDLE_IGNORE;
  338. }
  339. } else {
  340. g_printerr("invalid line '%s' - skipped\n", buf);
  341. }
  342. }
  343. fclose(fil);
  344. return LIMHANDLE_OK;
  345. }
  346. static int setup_limits(struct pam_limit_s *pl) {
  347. int i;
  348. int status;
  349. int retval = LIMHANDLE_OK;
  350. for (i=0, status=LIMITED_OK; i<RLIM_NLIMITS; i++) {
  351. if (!pl->limits[i].supported) {
  352. /* skip it if its not known to the system */
  353. continue;
  354. }
  355. if (pl->limits[i].src_soft == LIMITS_DEF_NONE &&
  356. pl->limits[i].src_hard == LIMITS_DEF_NONE) {
  357. /* skip it if its not initialized */
  358. continue;
  359. }
  360. if (pl->limits[i].limit.rlim_cur > pl->limits[i].limit.rlim_max)
  361. pl->limits[i].limit.rlim_cur = pl->limits[i].limit.rlim_max;
  362. status |= setrlimit(i, &pl->limits[i].limit);
  363. }
  364. if (status) {
  365. retval = LIMHANDLE_ERROR;
  366. }
  367. status = setpriority(PRIO_PROCESS, 0, pl->priority);
  368. if (status != 0) {
  369. retval = LIMHANDLE_ERROR;
  370. }
  371. return retval;
  372. }
  373. /* now the session stuff */
  374. int pam_set_limits(const char *conf_file, const char *username);
  375. int pam_set_limits(const char *conf_file, const char *username) {
  376. int retval;
  377. struct pam_limit_s pl;
  378. struct passwd *pwd;
  379. pwd = getpwnam(username);
  380. if (NULL == pwd) return LIMHANDLE_ERROR;
  381. if (!conf_file) conf_file = LIMITS_FILE;
  382. memset(&pl, 0, sizeof(pl));
  383. retval = init_limits(&pl);
  384. if (retval != LIMHANDLE_OK) {
  385. g_printerr("cannot initialize\n");
  386. return retval;
  387. }
  388. retval = parse_config_file(conf_file, pwd, &pl);
  389. if (retval == LIMHANDLE_IGNORE) {
  390. return LIMHANDLE_OK;
  391. }
  392. if (retval != LIMHANDLE_OK) {
  393. g_printerr("error parsing the configuration file\n");
  394. return retval;
  395. }
  396. retval = setup_limits(&pl);
  397. if (retval != LIMHANDLE_OK) {
  398. return retval;
  399. }
  400. return LIMHANDLE_OK;
  401. }
  402. /*
  403. * Copyright (c) Cristian Gafton, 1996-1997, <gafton@redhat.com>
  404. * All rights reserved.
  405. *
  406. * Redistribution and use in source and binary forms, with or without
  407. * modification, are permitted provided that the following conditions
  408. * are met:
  409. * 1. Redistributions of source code must retain the above copyright
  410. * notice, and the entire permission notice in its entirety,
  411. * including the disclaimer of warranties.
  412. * 2. Redistributions in binary form must reproduce the above copyright
  413. * notice, this list of conditions and the following disclaimer in the
  414. * documentation and/or other materials provided with the distribution.
  415. * 3. The name of the author may not be used to endorse or promote
  416. * products derived from this software without specific prior
  417. * written permission.
  418. *
  419. * ALTERNATIVELY, this product may be distributed under the terms of
  420. * the GNU Public License, in which case the provisions of the GPL are
  421. * required INSTEAD OF the above restrictions. (This clause is
  422. * necessary due to a potential bad interaction between the GPL and
  423. * the restrictions contained in a BSD-style copyright.)
  424. *
  425. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  426. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  427. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  428. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  429. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  430. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  431. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  432. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  433. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  434. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  435. * OF THE POSSIBILITY OF SUCH DAMAGE.
  436. */