]> git.pond.sub.org Git - empserver/blob - src/client/servcmd.c
client: Simplify rogue redirection and execute protection
[empserver] / src / client / servcmd.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2015, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  servercmd.c: Change the state depending on the command from the server.
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1989
31  *     Steve McClure, 1998
32  *     Ron Koenderink, 2005
33  *     Markus Armbruster, 2005-2015
34  */
35
36 #include <config.h>
37
38 #include <assert.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include "misc.h"
45 #include "proto.h"
46 #include "secure.h"
47
48 int eight_bit_clean;
49 FILE *auxfp;
50 int restricted;
51
52 static FILE *redir_fp;
53 static int redir_is_pipe;
54 static int executing;
55
56 static void prompt(int, char *, char *);
57 static void doredir(char *p);
58 static void dopipe(char *p);
59 static int doexecute(char *p);
60
61 void
62 servercmd(int code, char *arg, int len)
63 {
64     static int nmin, nbtu, fd;
65     static char the_prompt[1024];
66     static char teles[64];
67
68     switch (code) {
69     case C_PROMPT:
70         if (sscanf(arg, "%d %d", &nmin, &nbtu) != 2) {
71             fprintf(stderr, "Warning: server sent malformed prompt %s",
72                     arg);
73         }
74         snprintf(the_prompt, sizeof(the_prompt), "[%d:%d] Command : ",
75                  nmin, nbtu);
76         if (redir_fp) {
77             if (redir_is_pipe)
78                 (void)pclose(redir_fp);
79             else
80                 (void)fclose(redir_fp);
81             redir_fp = NULL;
82         }
83         prompt(code, the_prompt, teles);
84         executing = 0;
85         break;
86     case C_FLUSH:
87         snprintf(the_prompt, sizeof(the_prompt), "%.*s", len - 1, arg);
88         prompt(code, the_prompt, teles);
89         break;
90     case C_EXECUTE:
91         fd = doexecute(arg);
92         if (fd < 0)
93             send_eof++;
94         else {
95             input_fd = fd;
96             executing = 1;
97         }
98         break;
99     case C_EXIT:
100         printf("Exit: %s", arg);
101         if (auxfp)
102             fprintf(auxfp, "Exit: %s", arg);
103         break;
104     case C_FLASH:
105         printf("\n%s", arg);
106         if (auxfp)
107             fprintf(auxfp, "\n%s", arg);
108         break;
109     case C_INFORM:
110         if (arg[0] != '\n') {
111             snprintf(teles, sizeof(teles), "(%.*s) ", len - 1, arg);
112             if (!redir_fp) {
113                 putchar('\07');
114                 prompt(code, the_prompt, teles);
115             }
116         } else
117             teles[0] = 0;
118         break;
119     case C_PIPE:
120         dopipe(arg);
121         break;
122     case C_REDIR:
123         doredir(arg);
124         break;
125     default:
126         assert(0);
127         break;
128     }
129 }
130
131 static void
132 prompt(int code, char *prompt, char *teles)
133 {
134     char *nl;
135
136     nl = code == C_PROMPT || code == C_INFORM ? "\n" : "";
137     printf("%s%s%s", nl, teles, prompt);
138     fflush(stdout);
139     if (auxfp) {
140         fprintf(auxfp, "%s%s%s", nl, teles, prompt);
141         fflush(auxfp);
142     }
143 }
144
145 static char *
146 fname(char *s)
147 {
148     char *beg, *end;
149
150     for (beg = s; isspace(*(unsigned char *)beg); beg++) ;
151     for (end = beg; !isspace(*(unsigned char *)end); end++) ;
152     *end = 0;
153     return beg;
154 }
155
156 static int
157 redir_authorized(char *arg, char *attempt, int expected)
158 {
159     if (!expected) {
160         fprintf(stderr, "Warning: dropped conflicting %s %s",
161                 attempt, arg);
162         return 0;
163     }
164
165     if (!seen_input(arg)) {
166         fprintf(stderr, "Warning: server attempted to %s %s",
167                 attempt, arg);
168         return 0;
169     }
170
171     if (restricted) {
172         fprintf(stderr, "Can't %s in restricted mode\n", attempt);
173         return 0;
174     }
175
176     if (executing) {
177         fprintf(stderr, "Can't %s in a batch file\n", attempt);
178         return 0;
179     }
180     return 1;
181 }
182
183 static void
184 doredir(char *p)
185 {
186     int mode;
187     int fd;
188
189     if (!redir_authorized(p, "redirect to file", !redir_fp))
190         return;
191     if (*p++ != '>') {
192         fprintf(stderr, "Warning: dropped weird redirection %s", p);
193         return;
194     }
195
196     mode = O_WRONLY | O_CREAT;
197     if (*p == '>') {
198         mode |= O_APPEND;
199         p++;
200     } else if (*p == '!') {
201         mode |= O_TRUNC;
202         p++;
203     } else
204         mode |= O_EXCL;
205
206     p = fname(p);
207     if (*p == 0) {
208         fprintf(stderr, "Redirection lacks a file name\n");
209         return;
210     }
211
212     redir_is_pipe = 0;
213     fd = open(p, mode, 0666);
214     redir_fp = fd < 0 ? NULL : fdopen(fd, "w");
215     if (!redir_fp) {
216         fprintf(stderr, "Can't redirect to %s: %s\n",
217                 p, strerror(errno));
218     }
219 }
220
221 static void
222 dopipe(char *p)
223 {
224     if (!redir_authorized(p, "pipe to shell command", !redir_fp))
225         return;
226     if (*p++ != '|') {
227         fprintf(stderr, "Warning: dropped weird pipe %s", p);
228         return;
229     }
230
231     for (; *p && isspace(*p); p++) ;
232     if (*p == 0) {
233         fprintf(stderr, "Redirection lacks a command\n");
234         return;
235     }
236
237     /* strip newline */
238     p[strlen(p) - 1] = 0;
239
240     redir_is_pipe = 1;
241     errno = 0;
242     if ((redir_fp = popen(p, "w")) == NULL) {
243         fprintf(stderr, "Can't redirect to pipe %s%s%s\n",
244                 p, errno ? ": " : "", errno ? strerror(errno) : "");
245     }
246 }
247
248 static int
249 doexecute(char *p)
250 {
251     int fd;
252
253     if (!redir_authorized(p, "execute batch file", 1))
254         return -1;
255
256     p = fname(p);
257     if (*p == 0) {
258         fprintf(stderr, "Need a file to execute\n");
259         return -1;
260     }
261
262     if ((fd = open(p, O_RDONLY)) < 0) {
263         fprintf(stderr, "Can't open batch file %s: %s\n",
264                 p, strerror(errno));
265         return -1;
266     }
267
268     return fd;
269 }
270
271 void
272 outch(char c)
273 {
274     if (auxfp)
275         putc(c, auxfp);
276     if (redir_fp)
277         putc(c, redir_fp);
278     else if (eight_bit_clean) {
279         if (c == 14)
280             putso();
281         else if (c == 15)
282             putse();
283         else
284             putchar(c);
285     } else if (c & 0x80) {
286         putso();
287         putchar(c & 0x7f);
288         putse();
289     } else
290         putchar(c);
291 }