FIX privsep.c, yes, vulnerability (wapiflapi)..
[s-mailx.git] / privsep.c
blob8084c1bcc5020155e8aa28eec5dadee372a1fc92
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Privilege-separated dot file lock program (WANT_DOTLOCK=yes)
3 *@ that is capable of calling setuid(2) and change its user identity
4 *@ to the configured PRIVSEP_USER (usually "root"), in order to create
5 *@ a dotlock file with the same UID/GID as the mailbox to be locked.
6 *@ It should be started when chdir(2)d to the lock file's directory,
7 *@ and SIGPIPE should be ignored.
9 * Copyright (c) 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
11 * Permission to use, copy, modify, and/or distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 #undef n_FILE
24 #define n_FILE privsep
25 #define n_PRIVSEP_SOURCE
27 #include "nail.h"
29 #include "dotlock.h"
31 static void _ign_signal(int signum);
33 static void
34 _ign_signal(int signum)
36 struct sigaction nact, oact;
38 nact.sa_handler = SIG_IGN;
39 sigemptyset(&nact.sa_mask);
40 nact.sa_flags = 0;
41 sigaction(signum, &nact, &oact);
44 int
45 main(int argc, char **argv)
47 struct dotlock_info di;
48 struct stat stb;
49 sigset_t nset, oset;
50 enum dotlock_state dls;
52 /* We're a dumb helper, ensure as much as we can noone else uses us */
53 if (argc != 12 ||
54 strcmp(argv[ 0], PRIVSEP) ||
55 (argv[1][0] != 'r' && argv[1][0] != 'w') ||
56 strcmp(argv[ 1] + 1, "dotlock") ||
57 strcmp(argv[ 2], "mailbox") ||
58 strcmp(argv[ 4], "name") ||
59 strcmp(argv[ 6], "hostname") ||
60 strcmp(argv[ 8], "randstr") ||
61 strchr(argv[ 9], '/') != NULL /* Seal path injection vector */ ||
62 strcmp(argv[10], "pollmsecs") ||
63 fstat(STDIN_FILENO, &stb) == -1 || !S_ISFIFO(stb.st_mode) ||
64 fstat(STDOUT_FILENO, &stb) == -1 || !S_ISFIFO(stb.st_mode)) {
65 jeuse:
66 fprintf(stderr,
67 "This is a helper program of \"" UAGENT "\" (in " BINDIR ").\n"
68 " It is capable of gaining more privileges than \"" UAGENT "\"\n"
69 " and will be used to create lock files.\n"
70 " It's sole purpose is outsourcing of high privileges into\n"
71 " fewest lines of code in order to reduce attack surface.\n"
72 " It cannot be run by itself.\n");
73 exit(EXIT_USE);
76 di.di_file_name = argv[3];
77 di.di_lock_name = argv[5];
78 di.di_hostname = argv[7];
79 di.di_randstr = argv[9];
80 di.di_pollmsecs = (size_t)strtoul(argv[11], NULL, 10);
82 size_t i = strlen(di.di_file_name);
84 if (i == 0 || strncmp(di.di_file_name, di.di_lock_name, i) ||
85 di.di_lock_name[i] == '\0' || strcmp(di.di_lock_name + i, ".lock"))
86 goto jeuse;
89 close(STDERR_FILENO);
91 /* In order to prevent stale lock files at all cost block any signals until
92 * we have unlinked the lock file.
93 * It is still not safe because we may be SIGKILLed and may linger around
94 * because we have been SIGSTOPped, but unfortunately the standard doesn't
95 * give any option, e.g. atcrash() or open(O_TEMPORARY_KEEP_NAME) or so, ---
96 * and then again we should not unlink(2) the lock file unless our parent
97 * has finalized the synchronization! While at it, let me rant about the
98 * default action of realtime signals, program termination */
99 _ign_signal(SIGPIPE); /* (Inherited, though) */
100 sigfillset(&nset);
101 sigdelset(&nset, SIGCONT); /* (Rather redundant, though) */
102 sigprocmask(SIG_BLOCK, &nset, &oset);
104 dls = DLS_NOPERM | DLS_ABANDON;
106 /* First of all: we only dotlock when the executing user has the necessary
107 * rights to access the mailbox */
108 if (access(di.di_file_name, (argv[1][0] == 'r' ? R_OK : R_OK | W_OK)))
109 goto jmsg;
111 /* We need UID and GID information about the mailbox to lock */
112 if (stat(di.di_file_name, di.di_stb = &stb) == -1)
113 goto jmsg;
115 dls = DLS_PRIVFAILED | DLS_ABANDON;
117 /* This privsep helper only gets executed when needed, it thus doesn't make
118 * sense to try to continue with initial privileges */
119 if (setuid(geteuid()))
120 goto jmsg;
122 dls = _dotlock_create(&di);
124 /* Finally: notify our parent about the actual lock state.. */
125 jmsg:
126 write(STDOUT_FILENO, &dls, sizeof dls);
127 close(STDOUT_FILENO);
129 /* ..then eventually wait until we shall remove the lock again, which will
130 * be notified via the read returning */
131 if (dls == DLS_NONE) {
132 read(STDIN_FILENO, &dls, sizeof dls);
134 unlink(di.di_lock_name);
137 sigprocmask(SIG_SETMASK, &oset, NULL);
138 return (dls == DLS_NONE ? EXIT_OK : EXIT_ERR);
141 /* s-it-mode */