summarylogtreecommitdiffstats
path: root/xscreensaver-oom-protect.c
blob: 6a4e4b28f71567564fd49b3705a0f8d1a1321464 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* protect xscreensaver from the OOM killer via a small
 * setuid-ed "wrapper".
 *
 * An attempt to patch the security hole described in
 *   https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562884
 * without globally disabling the magic SysRq functionality
 * and/or OOM killer, without patching xscreensaver directly.
 *
 * Hardcoded to check whether the given pid actually is a process for
 * /usr/bin/xscreensaver to avoid malicious users protecting arbitrary
 * other processes.
 *
 * If you think you want to consider other screensavers, read
 *   https://www.jwz.org/xscreensaver/faq.html
 * - in particular, #20 and #28 -
 *   https://www.jwz.org/xscreensaver/faq.html#no-ctl-alt-bs
 *   https://www.jwz.org/xscreensaver/faq.html#gnome-screensaver
 * and
 *   https://www.jwz.org/xscreensaver/toolkits.html
 * and think again. */

/* (c) 2018 Alexander Kobel <a-kobel@a-kobel.de> */
/* Published under the WTFPL; see LICENSE. */

#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/oom.h>
#include <sys/resource.h>

int usage (char **argv) {
  fprintf (stderr,
           "Protect process <pid> from the OOM killer by setting its "
           "oom_score_adj to %d, its\n"
           "nice value to -20, and its priority to realtime.\n"
           "(TL;DR: try to avoid that an attacker types Alt+SysRq+F "
           "repeatedly and ends up\n"
           "with your logged-in session without the xscreensaver lock, "
           "and give screen locking\n"
           "utmost urgency.)\n"
           "Will fail with error code %d if the process does not run "
           "/usr/bin/xscreensaver.\n"
           "For more information and background, see\n"
           "\tman xscreensaver(1) \"Magic Backdoor Keystrokes\"\n"
           "\tman proc(5)\n"
           "\thttps://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562884\n"
           "\n"
           "(c) 2018 Alexander Kobel <a-kobel@a-kobel.de>\n"
           "\n"
           "Usage:\t%s <pid>\n"
           "\n"
           "  <pid>\n"
           "\tprocess id of a /usr/bin/xscreensaver instance\n"
           "\n"
           "Example:\n"
           "\tfor pid in $(pgrep xscreensaver); do\n"
           "\t\t%s $pid\n"
           "\tdone\n"
           "\n"
           "Preferably called from a supervisor like systemd.\n"
           "\n"
           "WARNING: The OOM adjustment is not locked, and may be changed or "
           "reset again.\n"
           "As far as the author is aware, the necessary permissions are those "
           "of the\n"
           "xscreensaver process itself; and with the same permissions, the "
           "OOM score\n"
           "adjustment can be lowered again. Thus, for additional security "
           "against\n"
           "*non-malicious* resets, call the equivalent of\n"
           "\techo %d > /proc/<pid>/oom_score_adj\n"
           "on each activation of the screensaver (which should be possible "
           "as non-root).\n"
           "All bets are off against an attacker that already aquired the "
           "rights of the\n"
           "owner of the original xscreensaver process (and, most probably, "
           "there's no need\n"
           "to work around that).\n",
           OOM_SCORE_ADJ_MIN, EINVAL, argv[0], argv[0], OOM_SCORE_ADJ_MIN);
  return EINVAL;
}

int main (int argc, char **argv) {
  char *endptr;
  long pid;
  char fn[PATH_MAX];
  char xscreensaver_exe[PATH_MAX];
  int n;
  FILE *f;
  struct sched_param sparam;

  if (argc != 2)
    return usage (argv);

  pid = strtol (argv[1], &endptr, 10);
  if (errno == ERANGE || *endptr != 0 || pid < 1)
    return usage (argv);

  n = snprintf (fn, PATH_MAX, "/proc/%ld/exe", pid);
  if (n < 0 && n > PATH_MAX)
    return ENAMETOOLONG;
  if (realpath (fn, xscreensaver_exe) == NULL)
    return errno;
  if (strcmp (xscreensaver_exe, "/usr/bin/xscreensaver") != 0)
    return EINVAL;

  n = snprintf (fn, PATH_MAX, "/proc/%ld/oom_score_adj", pid);
  if (n < 0 && n > PATH_MAX)
    return ENAMETOOLONG;
  f = fopen (fn, "w");
  if (f == NULL)
    return EPERM;
  fprintf (f, "%d", OOM_SCORE_ADJ_MIN);
  fclose (f);

  if (setpriority (PRIO_PROCESS, pid, INT_MIN) != 0)
    return errno;

  sparam.sched_priority = 99;
  if (sched_setscheduler (pid, SCHED_FIFO, &sparam) != 0)
    return errno;

  return 0;
}