pam_xauth.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. /*
  2. * pam_xauth module
  3. *
  4. * Copyright 2001-2003 Red Hat, Inc.
  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. * 1. Redistributions of source code must retain the above copyright
  10. * notice, and the entire permission notice in its entirety,
  11. * including the disclaimer of warranties.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. The name of the author may not be used to endorse or promote
  16. * products derived from this software without specific prior
  17. * written permission.
  18. *
  19. * ALTERNATIVELY, this product may be distributed under the terms of
  20. * the GNU Public License, in which case the provisions of the GPL are
  21. * required INSTEAD OF the above restrictions. (This clause is
  22. * necessary due to a potential bad interaction between the GPL and
  23. * the restrictions contained in a BSD-style copyright.)
  24. *
  25. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
  26. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  27. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  28. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
  29. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  35. * OF THE POSSIBILITY OF SUCH DAMAGE.
  36. */
  37. #include "config.h"
  38. #include <sys/types.h>
  39. #include <sys/wait.h>
  40. #include <sys/stat.h>
  41. #include <fcntl.h>
  42. #include <unistd.h>
  43. #include <errno.h>
  44. #include <fnmatch.h>
  45. #include <grp.h>
  46. #include <limits.h>
  47. #include <netdb.h>
  48. #include <pwd.h>
  49. #include <stdarg.h>
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <string.h>
  53. #include <syslog.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. #ifdef WITH_SELINUX
  59. #include <selinux/selinux.h>
  60. #include <selinux/label.h>
  61. #endif
  62. #include "pam_cc_compat.h"
  63. #include "pam_inline.h"
  64. #define DATANAME "pam_xauth_cookie_file"
  65. #define XAUTHENV "XAUTHORITY"
  66. #define HOMEENV "HOME"
  67. #define XAUTHDEF ".Xauthority"
  68. #define XAUTHTMP ".xauthXXXXXX"
  69. /* Hurd compatibility */
  70. #ifndef PATH_MAX
  71. #define PATH_MAX 4096
  72. #endif
  73. /* Possible paths to xauth executable */
  74. static const char * const xauthpaths[] = {
  75. #ifdef PAM_PATH_XAUTH
  76. PAM_PATH_XAUTH,
  77. #endif
  78. "/usr/X11R6/bin/xauth",
  79. "/usr/bin/xauth",
  80. "/usr/bin/X11/xauth"
  81. };
  82. /* Run a given command (with a NULL-terminated argument list), feeding it the
  83. * given input on stdin, and storing any output it generates. */
  84. static int
  85. run_coprocess(pam_handle_t *pamh, const char *input, char **output,
  86. uid_t uid, gid_t gid, const char *command, ...)
  87. {
  88. int ipipe[2], opipe[2], i;
  89. char buf[LINE_MAX];
  90. pid_t child;
  91. char *buffer = NULL;
  92. size_t buffer_size = 0;
  93. va_list ap;
  94. *output = NULL;
  95. /* Create stdio pipery. */
  96. if (pipe(ipipe) == -1) {
  97. pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
  98. return -1;
  99. }
  100. if (pipe(opipe) == -1) {
  101. pam_syslog(pamh, LOG_ERR, "Could not create pipe: %m");
  102. close(ipipe[0]);
  103. close(ipipe[1]);
  104. return -1;
  105. }
  106. /* Fork off a child. */
  107. child = fork();
  108. if (child == -1) {
  109. pam_syslog(pamh, LOG_ERR, "Could not fork: %m");
  110. close(ipipe[0]);
  111. close(ipipe[1]);
  112. close(opipe[0]);
  113. close(opipe[1]);
  114. return -1;
  115. }
  116. if (child == 0) {
  117. /* We're the child. */
  118. size_t j;
  119. const char *args[10];
  120. /* Drop privileges. */
  121. if (setgid(gid) == -1)
  122. {
  123. int err = errno;
  124. pam_syslog (pamh, LOG_ERR, "setgid(%lu) failed: %m",
  125. (unsigned long) getegid ());
  126. _exit (err);
  127. }
  128. if (setgroups(0, NULL) == -1)
  129. {
  130. int err = errno;
  131. pam_syslog (pamh, LOG_ERR, "setgroups() failed: %m");
  132. _exit (err);
  133. }
  134. if (setuid(uid) == -1)
  135. {
  136. int err = errno;
  137. pam_syslog (pamh, LOG_ERR, "setuid(%lu) failed: %m",
  138. (unsigned long) geteuid ());
  139. _exit (err);
  140. }
  141. /* Set the pipe descriptors up as stdin and stdout, and close
  142. * everything else, including the original values for the
  143. * descriptors. */
  144. if (dup2(ipipe[0], STDIN_FILENO) != STDIN_FILENO) {
  145. int err = errno;
  146. pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdin");
  147. _exit(err);
  148. }
  149. if (dup2(opipe[1], STDOUT_FILENO) != STDOUT_FILENO) {
  150. int err = errno;
  151. pam_syslog(pamh, LOG_ERR, "dup2 of %s failed: %m", "stdout");
  152. _exit(err);
  153. }
  154. if (pam_modutil_sanitize_helper_fds(pamh, PAM_MODUTIL_IGNORE_FD,
  155. PAM_MODUTIL_IGNORE_FD,
  156. PAM_MODUTIL_NULL_FD) < 0) {
  157. _exit(1);
  158. }
  159. /* Initialize the argument list. */
  160. memset(args, 0, sizeof(args));
  161. /* Convert the varargs list into a regular array of strings. */
  162. va_start(ap, command);
  163. args[0] = command;
  164. for (j = 1; j < PAM_ARRAY_SIZE(args) - 1; j++) {
  165. args[j] = va_arg(ap, const char*);
  166. if (args[j] == NULL) {
  167. break;
  168. }
  169. }
  170. /* Run the command. */
  171. DIAG_PUSH_IGNORE_CAST_QUAL;
  172. execv(command, (char *const *) args);
  173. DIAG_POP_IGNORE_CAST_QUAL;
  174. /* Never reached. */
  175. _exit(1);
  176. }
  177. /* We're the parent, so close the other ends of the pipes. */
  178. close(opipe[1]);
  179. /* Send input to the process (if we have any), then send an EOF. */
  180. if (input) {
  181. (void)pam_modutil_write(ipipe[1], input, strlen(input));
  182. }
  183. close(ipipe[0]); /* close here to avoid possible SIGPIPE above */
  184. close(ipipe[1]);
  185. /* Read data output until we run out of stuff to read. */
  186. i = pam_modutil_read(opipe[0], buf, sizeof(buf));
  187. while ((i != 0) && (i != -1)) {
  188. char *tmp;
  189. /* Resize the buffer to hold the data. */
  190. tmp = realloc(buffer, buffer_size + i + 1);
  191. if (tmp == NULL) {
  192. /* Uh-oh, bail. */
  193. if (buffer != NULL) {
  194. free(buffer);
  195. }
  196. close(opipe[0]);
  197. waitpid(child, NULL, 0);
  198. return -1;
  199. }
  200. /* Save the new buffer location, copy the newly-read data into
  201. * the buffer, and make sure the result will be
  202. * nul-terminated. */
  203. buffer = tmp;
  204. memcpy(buffer + buffer_size, buf, i);
  205. buffer[buffer_size + i] = '\0';
  206. buffer_size += i;
  207. /* Try to read again. */
  208. i = pam_modutil_read(opipe[0], buf, sizeof(buf));
  209. }
  210. /* No more data. Clean up and return data. */
  211. close(opipe[0]);
  212. *output = buffer;
  213. waitpid(child, NULL, 0);
  214. return 0;
  215. }
  216. /* Free a data item. */
  217. static void
  218. cleanup (pam_handle_t *pamh UNUSED, void *data, int err UNUSED)
  219. {
  220. free (data);
  221. }
  222. /* Check if we want to allow export to the other user, or import from the
  223. * other user. */
  224. static int
  225. check_acl(pam_handle_t *pamh,
  226. const char *sense, const char *this_user, const char *other_user,
  227. int noent_code, int debug)
  228. {
  229. char path[PATH_MAX];
  230. struct passwd *pwd;
  231. FILE *fp = NULL;
  232. int i, fd = -1, save_errno;
  233. struct stat st;
  234. PAM_MODUTIL_DEF_PRIVS(privs);
  235. /* Check this user's <sense> file. */
  236. pwd = pam_modutil_getpwnam(pamh, this_user);
  237. if (pwd == NULL) {
  238. pam_syslog(pamh, LOG_ERR,
  239. "error determining home directory for '%s'",
  240. this_user);
  241. return PAM_SESSION_ERR;
  242. }
  243. /* Figure out what that file is really named. */
  244. i = snprintf(path, sizeof(path), "%s/.xauth/%s", pwd->pw_dir, sense);
  245. if ((i >= (int)sizeof(path)) || (i < 0)) {
  246. pam_syslog(pamh, LOG_ERR,
  247. "name of user's home directory is too long");
  248. return PAM_SESSION_ERR;
  249. }
  250. if (pam_modutil_drop_priv(pamh, &privs, pwd))
  251. return PAM_SESSION_ERR;
  252. if (!stat(path, &st)) {
  253. if (!S_ISREG(st.st_mode))
  254. errno = EINVAL;
  255. else
  256. fd = open(path, O_RDONLY | O_NOCTTY);
  257. }
  258. save_errno = errno;
  259. if (pam_modutil_regain_priv(pamh, &privs)) {
  260. if (fd >= 0)
  261. close(fd);
  262. return PAM_SESSION_ERR;
  263. }
  264. if (fd >= 0) {
  265. if (!fstat(fd, &st)) {
  266. if (!S_ISREG(st.st_mode))
  267. errno = EINVAL;
  268. else
  269. fp = fdopen(fd, "r");
  270. }
  271. if (!fp) {
  272. save_errno = errno;
  273. close(fd);
  274. }
  275. }
  276. if (fp) {
  277. char buf[LINE_MAX], *tmp;
  278. /* Scan the file for a list of specs of users to "trust". */
  279. while (fgets(buf, sizeof(buf), fp) != NULL) {
  280. tmp = memchr(buf, '\r', sizeof(buf));
  281. if (tmp != NULL) {
  282. *tmp = '\0';
  283. }
  284. tmp = memchr(buf, '\n', sizeof(buf));
  285. if (tmp != NULL) {
  286. *tmp = '\0';
  287. }
  288. if (fnmatch(buf, other_user, 0) == 0) {
  289. if (debug) {
  290. pam_syslog(pamh, LOG_DEBUG,
  291. "%s %s allowed by %s",
  292. other_user, sense, path);
  293. }
  294. fclose(fp);
  295. return PAM_SUCCESS;
  296. }
  297. }
  298. /* If there's no match in the file, we fail. */
  299. if (debug) {
  300. pam_syslog(pamh, LOG_DEBUG, "%s not listed in %s",
  301. other_user, path);
  302. }
  303. fclose(fp);
  304. return PAM_PERM_DENIED;
  305. } else {
  306. /* Default to okay if the file doesn't exist. */
  307. errno = save_errno;
  308. switch (errno) {
  309. case ENOENT:
  310. if (noent_code == PAM_SUCCESS) {
  311. if (debug) {
  312. pam_syslog(pamh, LOG_DEBUG,
  313. "%s does not exist, ignoring",
  314. path);
  315. }
  316. } else {
  317. if (debug) {
  318. pam_syslog(pamh, LOG_DEBUG,
  319. "%s does not exist, failing",
  320. path);
  321. }
  322. }
  323. return noent_code;
  324. default:
  325. if (debug) {
  326. pam_syslog(pamh, LOG_DEBUG,
  327. "error opening %s: %m", path);
  328. }
  329. return PAM_PERM_DENIED;
  330. }
  331. }
  332. }
  333. int
  334. pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
  335. int argc, const char **argv)
  336. {
  337. char *cookiefile = NULL, *xauthority = NULL,
  338. *cookie = NULL, *display = NULL, *tmp = NULL,
  339. *xauthlocalhostname = NULL;
  340. const char *user, *xauth = NULL;
  341. struct passwd *tpwd, *rpwd;
  342. int fd, i, debug = 0;
  343. int retval = PAM_SUCCESS;
  344. uid_t systemuser = 499, targetuser = 0;
  345. /* Parse arguments. We don't understand many, so no sense in breaking
  346. * this into a separate function. */
  347. for (i = 0; i < argc; i++) {
  348. const char *str;
  349. if (strcmp(argv[i], "debug") == 0) {
  350. debug = 1;
  351. continue;
  352. }
  353. if ((str = pam_str_skip_prefix(argv[i], "xauthpath=")) != NULL) {
  354. xauth = str;
  355. continue;
  356. }
  357. if ((str = pam_str_skip_prefix(argv[i], "targetuser=")) != NULL) {
  358. long l = strtol(str, &tmp, 10);
  359. if ((*str != '\0') && (*tmp == '\0')) {
  360. targetuser = l;
  361. } else {
  362. pam_syslog(pamh, LOG_WARNING,
  363. "invalid value for targetuser (`%s')",
  364. argv[i] + 11);
  365. }
  366. continue;
  367. }
  368. if ((str = pam_str_skip_prefix(argv[i], "systemuser=")) != NULL) {
  369. long l = strtol(str, &tmp, 10);
  370. if ((*str != '\0') && (*tmp == '\0')) {
  371. systemuser = l;
  372. } else {
  373. pam_syslog(pamh, LOG_WARNING,
  374. "invalid value for systemuser (`%s')",
  375. argv[i] + 11);
  376. }
  377. continue;
  378. }
  379. pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'",
  380. argv[i]);
  381. }
  382. if (xauth == NULL) {
  383. size_t j;
  384. for (j = 0; j < PAM_ARRAY_SIZE(xauthpaths); j++) {
  385. if (access(xauthpaths[j], X_OK) == 0) {
  386. xauth = xauthpaths[j];
  387. break;
  388. }
  389. }
  390. if (xauth == NULL) {
  391. /* xauth executable not found - nothing to do */
  392. return PAM_SUCCESS;
  393. }
  394. }
  395. /* If DISPLAY isn't set, we don't really care, now do we? */
  396. if ((display = getenv("DISPLAY")) == NULL) {
  397. if (debug) {
  398. pam_syslog(pamh, LOG_DEBUG,
  399. "user has no DISPLAY, doing nothing");
  400. }
  401. return PAM_SUCCESS;
  402. }
  403. /* Read the target user's name. */
  404. if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
  405. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
  406. retval = PAM_SESSION_ERR;
  407. goto cleanup;
  408. }
  409. rpwd = pam_modutil_getpwuid(pamh, getuid());
  410. if (rpwd == NULL) {
  411. pam_syslog(pamh, LOG_ERR,
  412. "error determining invoking user's name");
  413. retval = PAM_SESSION_ERR;
  414. goto cleanup;
  415. }
  416. /* Get the target user's UID and primary GID, which we'll need to set
  417. * on the xauthority file we create later on. */
  418. tpwd = pam_modutil_getpwnam(pamh, user);
  419. if (tpwd == NULL) {
  420. pam_syslog(pamh, LOG_NOTICE,
  421. "error determining target user's UID");
  422. retval = PAM_SESSION_ERR;
  423. goto cleanup;
  424. }
  425. if (debug) {
  426. pam_syslog(pamh, LOG_DEBUG,
  427. "requesting user %lu/%lu, target user %lu/%lu",
  428. (unsigned long) rpwd->pw_uid,
  429. (unsigned long) rpwd->pw_gid,
  430. (unsigned long) tpwd->pw_uid,
  431. (unsigned long) tpwd->pw_gid);
  432. }
  433. /* If the UID is a system account (and not the superuser), forget
  434. * about forwarding keys. */
  435. if ((tpwd->pw_uid != 0) &&
  436. (tpwd->pw_uid != targetuser) &&
  437. (tpwd->pw_uid <= systemuser)) {
  438. if (debug) {
  439. pam_syslog(pamh, LOG_DEBUG,
  440. "not forwarding cookies to user ID %lu",
  441. (unsigned long) tpwd->pw_uid);
  442. }
  443. retval = PAM_SESSION_ERR;
  444. goto cleanup;
  445. }
  446. /* If current user and the target user are the same, don't
  447. check the ACL list, but forward X11 */
  448. if (strcmp (rpwd->pw_name, tpwd->pw_name) != 0) {
  449. /* Check that both users are amenable to this. By default, this
  450. * boils down to this policy:
  451. * export(ruser=root): only if <user> is listed in .xauth/export
  452. * export(ruser=*) if <user> is listed in .xauth/export, or
  453. * if .xauth/export does not exist
  454. * import(user=*): if <ruser> is listed in .xauth/import, or
  455. * if .xauth/import does not exist */
  456. i = (getuid() != 0 || tpwd->pw_uid == 0) ? PAM_SUCCESS : PAM_PERM_DENIED;
  457. i = check_acl(pamh, "export", rpwd->pw_name, user, i, debug);
  458. if (i != PAM_SUCCESS) {
  459. retval = PAM_SESSION_ERR;
  460. goto cleanup;
  461. }
  462. i = PAM_SUCCESS;
  463. i = check_acl(pamh, "import", user, rpwd->pw_name, i, debug);
  464. if (i != PAM_SUCCESS) {
  465. retval = PAM_SESSION_ERR;
  466. goto cleanup;
  467. }
  468. } else {
  469. if (debug)
  470. pam_syslog (pamh, LOG_DEBUG, "current and target user are the same, forward X11");
  471. }
  472. /* Figure out where the source user's .Xauthority file is. */
  473. if (getenv(XAUTHENV) != NULL) {
  474. cookiefile = strdup(getenv(XAUTHENV));
  475. } else {
  476. cookiefile = malloc(strlen(rpwd->pw_dir) + 1 +
  477. strlen(XAUTHDEF) + 1);
  478. if (cookiefile == NULL) {
  479. retval = PAM_SESSION_ERR;
  480. goto cleanup;
  481. }
  482. strcpy(cookiefile, rpwd->pw_dir);
  483. strcat(cookiefile, "/");
  484. strcat(cookiefile, XAUTHDEF);
  485. }
  486. if (debug) {
  487. pam_syslog(pamh, LOG_DEBUG, "reading keys from `%s'",
  488. cookiefile);
  489. }
  490. /* Read the user's .Xauthority file. Because the current UID is
  491. * the original user's UID, this will only fail if something has
  492. * gone wrong, or we have no cookies. */
  493. if (debug) {
  494. pam_syslog(pamh, LOG_DEBUG,
  495. "running \"%s %s %s %s %s\" as %lu/%lu",
  496. xauth, "-f", cookiefile, "nlist", display,
  497. (unsigned long) getuid(), (unsigned long) getgid());
  498. }
  499. if (run_coprocess(pamh, NULL, &cookie,
  500. getuid(), getgid(),
  501. xauth, "-f", cookiefile, "nlist", display,
  502. NULL) == 0) {
  503. #ifdef WITH_SELINUX
  504. char *context_raw = NULL;
  505. #endif
  506. PAM_MODUTIL_DEF_PRIVS(privs);
  507. /* Check that we got a cookie. If not, we get creative. */
  508. if (((cookie == NULL) || (strlen(cookie) == 0)) &&
  509. (pam_str_skip_prefix(display, "localhost:") != NULL ||
  510. pam_str_skip_prefix(display, "localhost/unix:") != NULL)) {
  511. char *t, *screen;
  512. size_t tlen, slen;
  513. /* Free the useless cookie string. */
  514. if (cookie != NULL) {
  515. free(cookie);
  516. cookie = NULL;
  517. }
  518. /* Allocate enough space to hold an adjusted name. */
  519. tlen = strlen(display) + LINE_MAX + 1;
  520. t = malloc(tlen);
  521. if (t != NULL) {
  522. memset(t, 0, tlen);
  523. if (gethostname(t, tlen - 1) != -1) {
  524. /* Append the protocol and then the
  525. * screen number. */
  526. if (strlen(t) < tlen - 6) {
  527. strcat(t, "/unix:");
  528. }
  529. screen = strchr(display, ':');
  530. if (screen != NULL) {
  531. screen++;
  532. slen = strlen(screen);
  533. if (strlen(t) + slen < tlen) {
  534. strcat(t, screen);
  535. }
  536. }
  537. if (debug) {
  538. pam_syslog(pamh, LOG_DEBUG,
  539. "no key for `%s', "
  540. "trying `%s'",
  541. display, t);
  542. }
  543. /* Read the cookie for this display. */
  544. if (debug) {
  545. pam_syslog(pamh, LOG_DEBUG,
  546. "running "
  547. "\"%s %s %s %s %s\" as "
  548. "%lu/%lu",
  549. xauth,
  550. "-f",
  551. cookiefile,
  552. "nlist",
  553. t,
  554. (unsigned long) getuid(),
  555. (unsigned long) getgid());
  556. }
  557. run_coprocess(pamh, NULL, &cookie,
  558. getuid(), getgid(),
  559. xauth, "-f", cookiefile,
  560. "nlist", t, NULL);
  561. }
  562. free(t);
  563. t = NULL;
  564. }
  565. }
  566. /* Check that we got a cookie, this time for real. */
  567. if ((cookie == NULL) || (strlen(cookie) == 0)) {
  568. if (debug) {
  569. pam_syslog(pamh, LOG_DEBUG, "no key");
  570. }
  571. retval = PAM_SESSION_ERR;
  572. goto cleanup;
  573. }
  574. /* Generate the environment variable
  575. * "XAUTHORITY=<homedir>/filename". */
  576. if (asprintf(&xauthority, "%s=%s/%s",
  577. XAUTHENV, tpwd->pw_dir, XAUTHTMP) < 0) {
  578. xauthority = NULL;
  579. if (debug) {
  580. pam_syslog(pamh, LOG_DEBUG, "out of memory");
  581. }
  582. retval = PAM_SESSION_ERR;
  583. goto cleanup;
  584. }
  585. /* Generate a new file to hold the data. */
  586. if (pam_modutil_drop_priv(pamh, &privs, tpwd)) {
  587. retval = PAM_SESSION_ERR;
  588. goto cleanup;
  589. }
  590. #ifdef WITH_SELINUX
  591. if (is_selinux_enabled() > 0) {
  592. struct selabel_handle *ctx = selabel_open(SELABEL_CTX_FILE, NULL, 0);
  593. if (ctx != NULL) {
  594. if (selabel_lookup_raw(ctx, &context_raw,
  595. xauthority + sizeof(XAUTHENV), S_IFREG) != 0) {
  596. pam_syslog(pamh, LOG_WARNING,
  597. "could not get SELinux label for '%s'",
  598. xauthority + sizeof(XAUTHENV));
  599. }
  600. selabel_close(ctx);
  601. if (setfscreatecon_raw(context_raw)) {
  602. pam_syslog(pamh, LOG_WARNING,
  603. "setfscreatecon_raw(%s) failed: %m", context_raw);
  604. }
  605. }
  606. }
  607. #endif /* WITH_SELINUX */
  608. fd = mkstemp(xauthority + sizeof(XAUTHENV));
  609. if (fd < 0)
  610. pam_syslog(pamh, LOG_ERR,
  611. "error creating temporary file `%s': %m",
  612. xauthority + sizeof(XAUTHENV));
  613. #ifdef WITH_SELINUX
  614. if (context_raw != NULL) {
  615. free(context_raw);
  616. setfscreatecon_raw(NULL);
  617. }
  618. #endif /* WITH_SELINUX */
  619. if (fd >= 0)
  620. close(fd);
  621. if (pam_modutil_regain_priv(pamh, &privs) || fd < 0) {
  622. retval = PAM_SESSION_ERR;
  623. goto cleanup;
  624. }
  625. /* Get a copy of the filename to save as a data item for
  626. * removal at session-close time. */
  627. free(cookiefile);
  628. cookiefile = strdup(xauthority + sizeof(XAUTHENV));
  629. /* Save the filename. */
  630. if (pam_set_data(pamh, DATANAME, cookiefile, cleanup) != PAM_SUCCESS) {
  631. pam_syslog(pamh, LOG_ERR,
  632. "error saving name of temporary file `%s'",
  633. cookiefile);
  634. unlink(cookiefile);
  635. retval = PAM_SESSION_ERR;
  636. goto cleanup;
  637. }
  638. /* Set the new variable in the environment. */
  639. if (pam_putenv (pamh, xauthority) != PAM_SUCCESS)
  640. pam_syslog(pamh, LOG_ERR,
  641. "can't set environment variable '%s'",
  642. xauthority);
  643. putenv (xauthority); /* The environment owns this string now. */
  644. xauthority = NULL; /* Don't free environment variables. */
  645. /* set $DISPLAY in pam handle to make su - work */
  646. {
  647. char *d;
  648. if (asprintf(&d, "DISPLAY=%s", display) < 0)
  649. {
  650. pam_syslog(pamh, LOG_CRIT, "out of memory");
  651. cookiefile = NULL;
  652. retval = PAM_SESSION_ERR;
  653. goto cleanup;
  654. }
  655. if (pam_putenv (pamh, d) != PAM_SUCCESS)
  656. pam_syslog (pamh, LOG_ERR,
  657. "can't set environment variable '%s'", d);
  658. free (d);
  659. }
  660. /* set XAUTHLOCALHOSTNAME to make sure that su - work under gnome */
  661. if ((xauthlocalhostname = getenv("XAUTHLOCALHOSTNAME")) != NULL) {
  662. char *d;
  663. if (asprintf(&d, "XAUTHLOCALHOSTNAME=%s", xauthlocalhostname) < 0) {
  664. pam_syslog(pamh, LOG_CRIT, "out of memory");
  665. retval = PAM_SESSION_ERR;
  666. goto cleanup;
  667. }
  668. if (pam_putenv (pamh, d) != PAM_SUCCESS)
  669. pam_syslog (pamh, LOG_ERR,
  670. "can't set environment variable '%s'", d);
  671. free (d);
  672. }
  673. /* Merge the cookie we read before into the new file. */
  674. if (debug) {
  675. pam_syslog(pamh, LOG_DEBUG,
  676. "writing key `%s' to temporary file `%s'",
  677. cookie, cookiefile);
  678. }
  679. if (debug) {
  680. pam_syslog(pamh, LOG_DEBUG,
  681. "running \"%s %s %s %s %s\" as %lu/%lu",
  682. xauth, "-f", cookiefile, "nmerge", "-",
  683. (unsigned long) tpwd->pw_uid,
  684. (unsigned long) tpwd->pw_gid);
  685. }
  686. run_coprocess(pamh, cookie, &tmp,
  687. tpwd->pw_uid, tpwd->pw_gid,
  688. xauth, "-f", cookiefile, "nmerge", "-", NULL);
  689. /* We don't need to keep a copy of these around any more. */
  690. cookiefile = NULL;
  691. free(tmp);
  692. }
  693. cleanup:
  694. /* Unset any old XAUTHORITY variable in the environment. */
  695. if (retval != PAM_SUCCESS && getenv (XAUTHENV))
  696. unsetenv (XAUTHENV);
  697. free(cookiefile);
  698. free(cookie);
  699. free(xauthority);
  700. return retval;
  701. }
  702. int
  703. pam_sm_close_session (pam_handle_t *pamh, int flags UNUSED,
  704. int argc, const char **argv)
  705. {
  706. int i, debug = 0;
  707. const char *user;
  708. const void *data;
  709. const char *cookiefile;
  710. struct passwd *tpwd;
  711. PAM_MODUTIL_DEF_PRIVS(privs);
  712. /* Try to retrieve the name of a file we created when
  713. * the session was opened. */
  714. if (pam_get_data(pamh, DATANAME, &data) != PAM_SUCCESS)
  715. return PAM_SUCCESS;
  716. cookiefile = data;
  717. /* Parse arguments. We don't understand many, so
  718. * no sense in breaking this into a separate function. */
  719. for (i = 0; i < argc; i++) {
  720. if (strcmp(argv[i], "debug") == 0) {
  721. debug = 1;
  722. continue;
  723. }
  724. if (pam_str_skip_prefix(argv[i], "xauthpath=") != NULL)
  725. continue;
  726. if (pam_str_skip_prefix(argv[i], "systemuser=") != NULL)
  727. continue;
  728. if (pam_str_skip_prefix(argv[i], "targetuser=") != NULL)
  729. continue;
  730. pam_syslog(pamh, LOG_WARNING, "unrecognized option `%s'",
  731. argv[i]);
  732. }
  733. if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS) {
  734. pam_syslog(pamh, LOG_NOTICE, "cannot determine user name");
  735. return PAM_SESSION_ERR;
  736. }
  737. if (!(tpwd = pam_modutil_getpwnam(pamh, user))) {
  738. pam_syslog(pamh, LOG_NOTICE,
  739. "error determining target user's UID");
  740. return PAM_SESSION_ERR;
  741. }
  742. if (debug)
  743. pam_syslog(pamh, LOG_DEBUG, "removing `%s'", cookiefile);
  744. if (pam_modutil_drop_priv(pamh, &privs, tpwd))
  745. return PAM_SESSION_ERR;
  746. if (unlink(cookiefile) == -1 && errno != ENOENT)
  747. pam_syslog(pamh, LOG_WARNING, "Couldn't remove `%s': %m", cookiefile);
  748. if (pam_modutil_regain_priv(pamh, &privs))
  749. return PAM_SESSION_ERR;
  750. return PAM_SUCCESS;
  751. }