pam_sepermit.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /******************************************************************************
  2. * A module for Linux-PAM that allows/denies access based on SELinux state.
  3. *
  4. * Copyright (c) 2007, 2008, 2009 Red Hat, Inc.
  5. * Originally written by Tomas Mraz <tmraz@redhat.com>
  6. * Contributions by Dan Walsh <dwalsh@redhat.com>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, and the entire permission notice in its entirety,
  13. * including the disclaimer of warranties.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. The name of the author may not be used to endorse or promote
  18. * products derived from this software without specific prior
  19. * written permission.
  20. *
  21. * ALTERNATIVELY, this product may be distributed under the terms of
  22. * the GNU Public License, in which case the provisions of the GPL are
  23. * required INSTEAD OF the above restrictions. (This clause is
  24. * necessary due to a potential bad interaction between the GPL and
  25. * the restrictions contained in a BSD-style copyright.)
  26. *
  27. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  28. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  29. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  30. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  31. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  32. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  33. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  34. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  35. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  36. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  37. * OF THE POSSIBILITY OF SUCH DAMAGE.
  38. */
  39. #include "config.h"
  40. #include <errno.h>
  41. #include <pwd.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <syslog.h>
  46. #include <ctype.h>
  47. #include <signal.h>
  48. #include <limits.h>
  49. #include <sys/types.h>
  50. #include <sys/stat.h>
  51. #include <fcntl.h>
  52. #include <unistd.h>
  53. #include <dirent.h>
  54. #include <security/pam_modules.h>
  55. #include <security/_pam_macros.h>
  56. #include <security/pam_modutil.h>
  57. #include <security/pam_ext.h>
  58. #include <selinux/selinux.h>
  59. #define MODULE "pam_sepermit"
  60. #define OPT_DELIM ":"
  61. struct lockfd {
  62. uid_t uid;
  63. int fd;
  64. int debug;
  65. };
  66. #define PROC_BASE "/proc"
  67. #define MAX_NAMES (int)(sizeof(unsigned long)*8)
  68. static int
  69. match_process_uid(pid_t pid, uid_t uid)
  70. {
  71. char buf[128];
  72. uid_t puid;
  73. FILE *f;
  74. int re = 0;
  75. snprintf (buf, sizeof buf, PROC_BASE "/%d/status", pid);
  76. if (!(f = fopen (buf, "r")))
  77. return 0;
  78. while (fgets(buf, sizeof buf, f)) {
  79. if (sscanf (buf, "Uid:\t%d", &puid)) {
  80. re = uid == puid;
  81. break;
  82. }
  83. }
  84. fclose(f);
  85. return re;
  86. }
  87. static int
  88. check_running (pam_handle_t *pamh, uid_t uid, int killall, int debug)
  89. {
  90. DIR *dir;
  91. struct dirent *de;
  92. pid_t *pid_table, pid, self;
  93. int i;
  94. int pids, max_pids;
  95. int running = 0;
  96. self = getpid();
  97. if (!(dir = opendir(PROC_BASE))) {
  98. pam_syslog(pamh, LOG_ERR, "Failed to open proc directory file %s:", PROC_BASE);
  99. return -1;
  100. }
  101. max_pids = 256;
  102. pid_table = malloc(max_pids * sizeof (pid_t));
  103. if (!pid_table) {
  104. (void)closedir(dir);
  105. pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
  106. return -1;
  107. }
  108. pids = 0;
  109. while ((de = readdir (dir)) != NULL) {
  110. if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
  111. continue;
  112. if (pids == max_pids) {
  113. pid_t *npt;
  114. if (!(npt = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
  115. free(pid_table);
  116. (void)closedir(dir);
  117. pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
  118. return -1;
  119. }
  120. pid_table = npt;
  121. max_pids *= 2;
  122. }
  123. pid_table[pids++] = pid;
  124. }
  125. (void)closedir(dir);
  126. for (i = 0; i < pids; i++) {
  127. pid_t id;
  128. if (match_process_uid(pid_table[i], uid) == 0)
  129. continue;
  130. id = pid_table[i];
  131. if (killall) {
  132. if (debug)
  133. pam_syslog(pamh, LOG_NOTICE, "Attempting to kill %d", id);
  134. kill(id, SIGKILL);
  135. }
  136. running++;
  137. }
  138. free(pid_table);
  139. return running;
  140. }
  141. /*
  142. * This function reads the loginuid from the /proc system. It returns
  143. * (uid_t)-1 on failure.
  144. */
  145. static uid_t get_loginuid(pam_handle_t *pamh)
  146. {
  147. int fd, count;
  148. char loginuid[24];
  149. char *eptr;
  150. uid_t rv = (uid_t)-1;
  151. fd = open("/proc/self/loginuid", O_NOFOLLOW|O_RDONLY);
  152. if (fd < 0) {
  153. if (errno != ENOENT) {
  154. pam_syslog(pamh, LOG_ERR,
  155. "Cannot open /proc/self/loginuid: %m");
  156. }
  157. return rv;
  158. }
  159. if ((count = pam_modutil_read(fd, loginuid, sizeof(loginuid)-1)) < 1) {
  160. close(fd);
  161. return rv;
  162. }
  163. loginuid[count] = '\0';
  164. close(fd);
  165. errno = 0;
  166. rv = strtoul(loginuid, &eptr, 10);
  167. if (errno != 0 || eptr == loginuid)
  168. rv = (uid_t) -1;
  169. return rv;
  170. }
  171. static void
  172. sepermit_unlock(pam_handle_t *pamh, void *plockfd, int error_status UNUSED)
  173. {
  174. struct lockfd *lockfd = plockfd;
  175. struct flock fl;
  176. memset(&fl, 0, sizeof(fl));
  177. fl.l_type = F_UNLCK;
  178. fl.l_whence = SEEK_SET;
  179. if (lockfd->debug)
  180. pam_syslog(pamh, LOG_ERR, "Unlocking fd: %d uid: %d", lockfd->fd, lockfd->uid);
  181. /* Don't kill uid==0 */
  182. if (lockfd->uid)
  183. /* This is a DOS but it prevents an app from forking to prevent killing */
  184. while(check_running(pamh, lockfd->uid, 1, lockfd->debug) > 0)
  185. continue;
  186. (void)fcntl(lockfd->fd, F_SETLK, &fl);
  187. (void)close(lockfd->fd);
  188. free(lockfd);
  189. }
  190. static int
  191. sepermit_lock(pam_handle_t *pamh, const char *user, int debug)
  192. {
  193. char buf[PATH_MAX];
  194. struct flock fl;
  195. memset(&fl, 0, sizeof(fl));
  196. fl.l_type = F_WRLCK;
  197. fl.l_whence = SEEK_SET;
  198. struct passwd *pw = pam_modutil_getpwnam( pamh, user );
  199. if (!pw) {
  200. pam_syslog(pamh, LOG_NOTICE, "Unable to find uid for user %s",
  201. user);
  202. return -1;
  203. }
  204. if (check_running(pamh, pw->pw_uid, 0, debug) > 0) {
  205. pam_syslog(pamh, LOG_ERR, "User %s processes are running. Exclusive login not allowed", user);
  206. return -1;
  207. }
  208. snprintf(buf, sizeof(buf), "%s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
  209. int fd = open(buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  210. if (fd < 0) {
  211. pam_syslog(pamh, LOG_ERR, "Unable to open lock file %s/%d.lock", SEPERMIT_LOCKDIR, pw->pw_uid);
  212. return -1;
  213. }
  214. /* Need to close on exec */
  215. fcntl(fd, F_SETFD, FD_CLOEXEC);
  216. if (fcntl(fd, F_SETLK, &fl) == -1) {
  217. pam_syslog(pamh, LOG_ERR, "User %s with exclusive login already logged in", user);
  218. close(fd);
  219. return -1;
  220. }
  221. struct lockfd *lockfd=calloc(1, sizeof(struct lockfd));
  222. if (!lockfd) {
  223. close(fd);
  224. pam_syslog(pamh, LOG_CRIT, "Memory allocation error");
  225. return -1;
  226. }
  227. lockfd->uid = pw->pw_uid;
  228. lockfd->debug = debug;
  229. lockfd->fd=fd;
  230. pam_set_data(pamh, MODULE, lockfd, sepermit_unlock);
  231. return 0;
  232. }
  233. /* return 0 when matched, -1 when unmatched, pam error otherwise */
  234. static int
  235. sepermit_match(pam_handle_t *pamh, const char *cfgfile, const char *user,
  236. const char *seuser, int debug, int *sense)
  237. {
  238. FILE *f;
  239. char *line = NULL;
  240. char *start;
  241. size_t len = 0;
  242. int matched = 0;
  243. int exclusive = 0;
  244. int ignore = 0;
  245. f = fopen(cfgfile, "r");
  246. if (!f) {
  247. pam_syslog(pamh, LOG_ERR, "Failed to open config file %s: %m", cfgfile);
  248. return PAM_SERVICE_ERR;
  249. }
  250. while (!matched && getline(&line, &len, f) != -1) {
  251. size_t n;
  252. char *sptr;
  253. char *opt;
  254. if (line[0] == '#')
  255. continue;
  256. start = line;
  257. while (isspace(*start))
  258. ++start;
  259. n = strlen(start);
  260. while (n > 0 && isspace(start[n-1])) {
  261. --n;
  262. }
  263. if (n == 0)
  264. continue;
  265. start[n] = '\0';
  266. start = strtok_r(start, OPT_DELIM, &sptr);
  267. switch (start[0]) {
  268. case '@':
  269. ++start;
  270. if (debug)
  271. pam_syslog(pamh, LOG_NOTICE, "Matching user %s against group %s", user, start);
  272. if (pam_modutil_user_in_group_nam_nam(pamh, user, start)) {
  273. matched = 1;
  274. }
  275. break;
  276. case '%':
  277. if (seuser == NULL)
  278. break;
  279. ++start;
  280. if (debug)
  281. pam_syslog(pamh, LOG_NOTICE, "Matching seuser %s against seuser %s", seuser, start);
  282. if (strcmp(seuser, start) == 0) {
  283. matched = 1;
  284. }
  285. break;
  286. default:
  287. if (debug)
  288. pam_syslog(pamh, LOG_NOTICE, "Matching user %s against user %s", user, start);
  289. if (strcmp(user, start) == 0) {
  290. matched = 1;
  291. }
  292. }
  293. if (matched)
  294. while ((opt=strtok_r(NULL, OPT_DELIM, &sptr)) != NULL) {
  295. if (strcmp(opt, "exclusive") == 0)
  296. exclusive = 1;
  297. else if (strcmp(opt, "ignore") == 0)
  298. ignore = 1;
  299. else if (debug) {
  300. pam_syslog(pamh, LOG_NOTICE, "Unknown user option: %s", opt);
  301. }
  302. }
  303. }
  304. free(line);
  305. fclose(f);
  306. if (matched) {
  307. if (*sense == PAM_SUCCESS) {
  308. if (ignore)
  309. *sense = PAM_IGNORE;
  310. if (geteuid() == 0 && exclusive && get_loginuid(pamh) == (uid_t)-1)
  311. if (sepermit_lock(pamh, user, debug) < 0)
  312. *sense = PAM_AUTH_ERR;
  313. }
  314. return 0;
  315. }
  316. else
  317. return -1;
  318. }
  319. int
  320. pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
  321. int argc, const char **argv)
  322. {
  323. int i;
  324. int rv;
  325. int debug = 0;
  326. int sense = PAM_AUTH_ERR;
  327. const char *user = NULL;
  328. char *seuser = NULL;
  329. char *level = NULL;
  330. const char *cfgfile = SEPERMIT_CONF_FILE;
  331. /* Parse arguments. */
  332. for (i = 0; i < argc; i++) {
  333. if (strcmp(argv[i], "debug") == 0) {
  334. debug = 1;
  335. }
  336. if (strcmp(argv[i], "conf=") == 0) {
  337. cfgfile = argv[i] + 5;
  338. }
  339. }
  340. if (debug)
  341. pam_syslog(pamh, LOG_NOTICE, "Parsing config file: %s", cfgfile);
  342. if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || *user == '\0') {
  343. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
  344. return PAM_USER_UNKNOWN;
  345. }
  346. if (is_selinux_enabled() > 0) {
  347. if (security_getenforce() == 1) {
  348. if (debug)
  349. pam_syslog(pamh, LOG_NOTICE, "Enforcing mode, access will be allowed on match");
  350. sense = PAM_SUCCESS;
  351. }
  352. }
  353. if (getseuserbyname(user, &seuser, &level) != 0) {
  354. seuser = NULL;
  355. level = NULL;
  356. pam_syslog(pamh, LOG_ERR, "getseuserbyname failed: %m");
  357. }
  358. if (debug && sense != PAM_SUCCESS)
  359. pam_syslog(pamh, LOG_NOTICE, "Access will not be allowed on match");
  360. rv = sepermit_match(pamh, cfgfile, user, seuser, debug, &sense);
  361. if (debug)
  362. pam_syslog(pamh, LOG_NOTICE, "sepermit_match returned: %d", rv);
  363. free(seuser);
  364. free(level);
  365. switch (rv) {
  366. case -1:
  367. return PAM_IGNORE;
  368. case 0:
  369. return sense;
  370. }
  371. return rv;
  372. }
  373. int
  374. pam_sm_setcred (pam_handle_t *pamh UNUSED, int flags UNUSED,
  375. int argc UNUSED, const char **argv UNUSED)
  376. {
  377. return PAM_IGNORE;
  378. }
  379. int
  380. pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
  381. int argc, const char **argv)
  382. {
  383. return pam_sm_authenticate(pamh, flags, argc, argv);
  384. }