1 /*
2 * Copyright conserver.com, 2000
3 *
4 * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com)
5 *
6 * Copyright GNAC, Inc., 1998
7 */
8
9 /*
10 * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana
11 * 47907. All rights reserved.
12 *
13 * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
14 *
15 * This software is not subject to any license of the American Telephone
16 * and Telegraph Company or the Regents of the University of California.
17 *
18 * Permission is granted to anyone to use this software for any purpose on
19 * any computer system, and to alter it and redistribute it freely, subject
20 * to the following restrictions:
21 *
22 * 1. Neither the authors nor Purdue University are responsible for any
23 * consequences of the use of this software.
24 *
25 * 2. The origin of this software must not be misrepresented, either by
26 * explicit claim or by omission. Credit to the authors and Purdue
27 * University must appear in documentation and sources.
28 *
29 * 3. Altered versions must be plainly marked as such, and must not be
30 * misrepresented as being the original software.
31 *
32 * 4. This notice may not be removed or altered.
33 */
34
35 #include <compat.h>
36
37 #include <cutil.h>
38 #include <consent.h>
39 #include <access.h>
40 #include <client.h>
41 #include <group.h>
42 #include <readcfg.h>
43
44 #if USE_IPV6
45 # include <sys/socket.h>
46 # include <netdb.h>
47 #endif /* USE_IPV6 */
48
49 #if defined(USE_LIBWRAP)
50 # include <syslog.h>
51 # include <tcpd.h>
52 int allow_severity = LOG_INFO;
53 int deny_severity = LOG_WARNING;
54 #endif
55
56
57 /* find the next guy who wants to write on the console (ksb)
58 */
59 void
FindWrite(CONSENT * pCE)60 FindWrite(CONSENT *pCE)
61 {
62 CONSCLIENT *pCLfound = (CONSCLIENT *)0;
63 CONSCLIENT *pCL;
64
65 /* make the first guy (last on the list) to have the `want write' bit set
66 * the writer (tell him of the promotion, too) we could look for the most
67 * recent or some such... I guess it doesn't matter that much.
68 */
69 if (pCE->pCLwr != (CONSCLIENT *)0 || pCE->fronly)
70 return;
71
72 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) {
73 if (pCL->fwantwr && !pCL->fro)
74 pCLfound = pCL;
75 }
76
77 if (pCLfound != (CONSCLIENT *)0) {
78 pCLfound->fwantwr = 0;
79 pCLfound->fwr = 1;
80 if (pCE->nolog) {
81 FileWrite(pCLfound->fd, FLAGFALSE,
82 "\r\n[attached (nologging)]\r\n", -1);
83 } else {
84 FileWrite(pCLfound->fd, FLAGFALSE, "\r\n[attached]\r\n", -1);
85 }
86 TagLogfileAct(pCE, "%s attached", pCLfound->acid->string);
87 pCE->pCLwr = pCLfound;
88 }
89 }
90
91 void
BumpClient(CONSENT * pCE,char * message)92 BumpClient(CONSENT *pCE, char *message)
93 {
94 if ((CONSCLIENT *)0 == pCE->pCLwr)
95 return;
96
97 if ((char *)0 != message)
98 FileWrite(pCE->pCLwr->fd, FLAGFALSE, message, -1);
99 pCE->pCLwr->fwantwr = 0;
100 pCE->pCLwr->fwr = 0;
101 pCE->pCLwr = (CONSCLIENT *)0;
102 }
103
104 /* replay last 'back' lines of the log file upon connect to console (ksb)
105 *
106 * NB: we know the console might be spewing when the replay happens,
107 * we want to just output what is in the log file and get out,
108 * so we don't drop chars...
109 */
110 #define REPLAYBUFFER 4096
111
112 void
Replay(CONSENT * pCE,CONSFILE * fdOut,unsigned short back)113 Replay(CONSENT *pCE, CONSFILE *fdOut, unsigned short back)
114 {
115 CONSFILE *fdLog = (CONSFILE *)0;
116 STRING *line = (STRING *)0;
117 off_t file_pos;
118 off_t buf_pos;
119 char *buf = (char *)0;
120 char *bp = (char *)0;
121 int ch;
122 struct stat stLog;
123 int ln;
124 int was_mark = 0;
125 #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY
126 unsigned long dmallocMarkReplay = 0;
127 #endif
128
129 if (pCE != (CONSENT *)0 && pCE->logfile != (char *)0)
130 fdLog = FileOpen(pCE->logfile, O_RDONLY, 0644);
131
132 if (fdLog == (CONSFILE *)0) {
133 FileWrite(fdOut, FLAGFALSE, "[no log file on this console]\r\n",
134 -1);
135 return;
136 }
137 #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY
138 dmallocMarkReplay = dmalloc_mark();
139 #endif
140
141 /* find the size of the file
142 */
143 if (0 != FileStat(fdLog, &stLog))
144 goto common_exit;
145
146 file_pos = stLog.st_size - 1; /* point at last byte */
147 buf_pos = file_pos + 1;
148
149 if ((char *)0 == (buf = malloc(REPLAYBUFFER)))
150 OutOfMem();
151 bp = buf + 1; /* just give it something - it resets below */
152
153 line = AllocString();
154
155 /* loop as long as there is data in the file or we have not found
156 * the requested number of lines
157 */
158 ln = -1;
159 for (; file_pos >= 0; file_pos--, bp--) {
160 if (file_pos < buf_pos) {
161 int r;
162
163 /* read one buffer worth of data a buffer boundary
164 *
165 * the first read will probably not get a full buffer but
166 * the rest (as we work our way back in the file) should be
167 */
168 buf_pos = (file_pos / REPLAYBUFFER) * REPLAYBUFFER;
169 if (FileSeek(fdLog, buf_pos, SEEK_SET) < 0) {
170 goto common_exit;
171 }
172 if ((r = FileRead(fdLog, buf, REPLAYBUFFER)) < 0) {
173 goto common_exit;
174 }
175 bp = buf + r - 1;
176 }
177
178 /* process the next character
179 */
180 if ((ch = *bp) == '\n') {
181 if (ln >= 0) {
182 int i;
183 int u;
184 int is_mark = 0;
185
186 /* reverse the text to put it in forward order
187 */
188 u = line->used - 1;
189 for (i = 0; i < u / 2; i++) {
190 int temp;
191
192 temp = line->string[i];
193 line->string[i] = line->string[u - i - 1];
194 line->string[u - i - 1] = temp;
195 }
196
197 /* see if this line is a MARK
198 */
199 if (line->used > 0 && line->string[0] == '[') {
200 char dummy[4];
201 int j;
202 i = sscanf(line->string + 1,
203 "-- MARK -- %3c %3c %d %d:%d:%d %d]\r\n",
204 dummy, dummy, &j, &j, &j, &j, &j);
205 is_mark = (i == 7);
206 }
207
208 /* process this line
209 */
210 if (is_mark && was_mark) {
211 /* this is a mark and the previous line is also
212 * a mark, so reduce the line count 'cause it'll
213 * go up by one and we're joining them on output.
214 */
215 ln--;
216 }
217 was_mark = is_mark;
218 }
219
220 /* advance to the next line and break if we have enough
221 */
222 ln++;
223 BuildString((char *)0, line);
224 if (ln >= back) {
225 break;
226 }
227 }
228
229 /* if we have a character but no lines yet, the last text in the
230 * file does not end with a newline, so start the first line anyway
231 */
232 if (ln < 0) {
233 ln = 0;
234 }
235 BuildStringChar(ch, line);
236
237 /* if we've processed "a lot" of data for a line, then bail
238 * why? there must be some very long non-newline terminated
239 * strings and if we just keep going back, we could spew lots
240 * of data and chew up lots of memory
241 */
242 if (line->used > MAXREPLAYLINELEN) {
243 break;
244 }
245 }
246
247 /* move forward. either we hit the beginning of the file and we
248 * move to the first byte, or we hit a \n and we move past it
249 */
250 file_pos++;
251
252 /* Now output the lines, starting from where we stopped */
253 if (FileSeek(fdLog, file_pos, SEEK_SET) >= 0) {
254 int eof = 0;
255 int i = 0;
256 int r = 0;
257 STRING *mark_beg = (STRING *)0;
258 STRING *mark_end = (STRING *)0;
259
260 mark_beg = AllocString();
261 mark_end = AllocString();
262
263 ln = 0; /* number of lines output */
264 BuildString((char *)0, line);
265
266 while (ln < back && !eof) {
267 if (r <= 0) {
268 if ((r = FileRead(fdLog, buf, REPLAYBUFFER)) < 0)
269 eof = 1;
270 i = 0;
271 }
272
273 if (!eof)
274 BuildStringChar(buf[i], line);
275
276 if (buf[i] == '\n' || eof) {
277 int is_mark = 0;
278 if (line->used > 0 && line->string[0] == '[') {
279 char dummy[4];
280 int j;
281 int i;
282 i = sscanf(line->string + 1,
283 "-- MARK -- %3c %3c %d %d:%d:%d %d]\r\n",
284 dummy, dummy, &j, &j, &j, &j, &j);
285 is_mark = (i == 7);
286 }
287 if (is_mark) {
288 if (mark_beg->used > 1) {
289 BuildString((char *)0, mark_end);
290 BuildString(line->string, mark_end);
291 } else
292 BuildString(line->string, mark_beg);
293 } else {
294 if (mark_beg->used > 1) {
295 if (mark_end->used > 1) {
296 char *s;
297
298 /* output the start of the range, stopping at the ']' */
299 s = strrchr(mark_beg->string, ']');
300 if ((char *)0 != s)
301 *s = '\000';
302 FileWrite(fdOut, FLAGTRUE, mark_beg->string,
303 -1);
304 FileWrite(fdOut, FLAGTRUE, " .. ", 4);
305
306 /* build the end string by removing the leading "[-- MARK -- "
307 * and replacing "]\r\n" on the end with " -- MARK --]\r\n"
308 */
309 s = strrchr(mark_end->string, ']');
310 if ((char *)0 != s)
311 *s = '\000';
312 FileWrite(fdOut, FLAGTRUE,
313 mark_end->string +
314 sizeof("[-- MARK -- ") - 1, -1);
315 FileWrite(fdOut, FLAGFALSE, " -- MARK --]\r\n",
316 -1);
317 } else {
318 FileWrite(fdOut, FLAGFALSE, mark_beg->string,
319 mark_beg->used - 1);
320 }
321 BuildString((char *)0, mark_beg);
322 BuildString((char *)0, mark_end);
323 ln++;
324 if (ln >= back)
325 break;
326 }
327 FileWrite(fdOut, FLAGFALSE, line->string,
328 line->used - 1);
329 ln++;
330 }
331 BuildString((char *)0, line);
332 }
333
334 /* move the counters */
335 i++;
336 r--;
337 }
338 DestroyString(mark_end);
339 DestroyString(mark_beg);
340 }
341
342 common_exit:
343
344 if (line != (STRING *)0)
345 DestroyString(line);
346 if (buf != (char *)0)
347 free(buf);
348 if (fdLog != (CONSFILE *)0)
349 FileClose(&fdLog);
350
351 #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY
352 CONDDEBUG((1, "Replay(): dmalloc / MarkReplay"));
353 dmalloc_log_changed(dmallocMarkReplay, 1, 0, 1);
354 #endif
355 }
356
357
358 /* these bit tell us which parts of the Truth to tell the client (ksb)
359 */
360 #define WHEN_SPY 0x01
361 #define WHEN_ATTACH 0x02
362 #define WHEN_EXPERT 0x04 /* ZZZ no way to set his yet */
363 #define WHEN_ALWAYS 0x40
364 #define IS_LIMITED 0x100
365
366 #define HALFLINE 40
367
368 typedef struct HLnode {
369 int iwhen;
370 char *actext;
371 } HELP;
372
373 static HELP aHLTable[] = {
374 {WHEN_ALWAYS, ". disconnect"},
375 {WHEN_ALWAYS | IS_LIMITED, "; move to another console"},
376 {WHEN_ALWAYS, "a attach read/write"},
377 {WHEN_ALWAYS, "b send broadcast message"},
378 {WHEN_ATTACH, "c toggle flow control"},
379 {WHEN_ATTACH, "d down a console"},
380 {WHEN_ALWAYS, "e change escape sequence"},
381 {WHEN_ALWAYS, "f force attach read/write"},
382 {WHEN_ALWAYS, "g group info"},
383 {WHEN_ALWAYS, "i information dump"},
384 {WHEN_ATTACH, "L toggle logging on/off"},
385 {WHEN_ATTACH, "l? break sequence list"},
386 {WHEN_ATTACH, "l0 send break per config file"},
387 {WHEN_ATTACH, "l1-9a-z send specific break sequence"},
388 {WHEN_ALWAYS, "m display message of the day"},
389 {WHEN_ALWAYS, "n write a note to the logfile"},
390 {WHEN_ALWAYS, "o (re)open the tty and log file"},
391 {WHEN_ALWAYS, "p playback the last %hu lines"},
392 {WHEN_ALWAYS, "P set number of playback lines"},
393 {WHEN_ALWAYS, "r replay the last %hu lines"},
394 {WHEN_ALWAYS, "R set number of replay lines"},
395 {WHEN_ATTACH, "s spy mode (read only)"},
396 {WHEN_ALWAYS, "u show host status"},
397 {WHEN_ALWAYS, "v show version info"},
398 {WHEN_ALWAYS, "w who is on this console"},
399 {WHEN_ALWAYS, "x show console baud info"},
400 {WHEN_ALWAYS | IS_LIMITED, "z suspend the connection"},
401 {WHEN_ATTACH, "! invoke task"},
402 {WHEN_ATTACH | IS_LIMITED, "| attach local command"},
403 {WHEN_ALWAYS, "? print this message"},
404 {WHEN_ALWAYS, "<cr> ignore/abort command"},
405 {WHEN_ALWAYS, "^R replay the last line"},
406 {WHEN_ATTACH, "\\ooo send character by octal code"},
407 };
408
409 /* list the commands we know for the user (ksb)
410 */
411 void
HelpUser(CONSCLIENT * pCL)412 HelpUser(CONSCLIENT *pCL)
413 {
414 int i, j, iCmp;
415 static char
416 acH1[] = "help]\r\n", acH2[] = "help spy mode]\r\n", acEoln[] =
417 "\r\n";
418 static STRING *acLine = (STRING *)0;
419
420 if (acLine == (STRING *)0)
421 acLine = AllocString();
422
423 iCmp = WHEN_ALWAYS | WHEN_SPY;
424 if (pCL->fwr) {
425 FileWrite(pCL->fd, FLAGTRUE, acH1, sizeof(acH1) - 1);
426 iCmp |= WHEN_ATTACH;
427 } else {
428 FileWrite(pCL->fd, FLAGTRUE, acH2, sizeof(acH2) - 1);
429 }
430
431 BuildString((char *)0, acLine);
432 for (i = 0; i < sizeof(aHLTable) / sizeof(HELP); ++i) {
433 char *text;
434
435 if (aHLTable[i].iwhen & IS_LIMITED &&
436 ConsentUserOk(pLUList, pCL->username->string) == 1)
437 continue;
438
439 if (0 == (aHLTable[i].iwhen & iCmp))
440 continue;
441
442 text = aHLTable[i].actext;
443 if (text[0] == 'p') {
444 BuildTmpString((char *)0);
445 text = BuildTmpStringPrint(text, pCL->playback);
446 } else if (text[0] == 'r') {
447 BuildTmpString((char *)0);
448 text = BuildTmpStringPrint(text, pCL->replay);
449 }
450
451 if (acLine->used != 0) { /* second part of line */
452 if (strlen(text) < HALFLINE) {
453 for (j = acLine->used; j <= HALFLINE; ++j) {
454 BuildStringChar(' ', acLine);
455 }
456 BuildString(text, acLine);
457 BuildString(acEoln, acLine);
458 FileWrite(pCL->fd, FLAGTRUE, acLine->string,
459 acLine->used - 1);
460 BuildString((char *)0, acLine);
461 continue;
462 } else {
463 BuildString(acEoln, acLine);
464 FileWrite(pCL->fd, FLAGTRUE, acLine->string,
465 acLine->used - 1);
466 BuildString((char *)0, acLine);
467 }
468 }
469 if (acLine->used == 0) { /* at new line */
470 BuildStringChar(' ', acLine);
471 BuildString(text, acLine);
472 if (acLine->used > HALFLINE) {
473 BuildString(acEoln, acLine);
474 FileWrite(pCL->fd, FLAGTRUE, acLine->string,
475 acLine->used - 1);
476 BuildString((char *)0, acLine);
477 }
478 }
479 }
480 if (acLine->used != 0) {
481 BuildString(acEoln, acLine);
482 FileWrite(pCL->fd, FLAGTRUE, acLine->string, acLine->used - 1);
483 }
484 FileWrite(pCL->fd, FLAGFALSE, (char *)0, 0);
485 }
486
487 int
ClientAccessOk(CONSCLIENT * pCL)488 ClientAccessOk(CONSCLIENT *pCL)
489 {
490 char *peername = (char *)0;
491 int retval = 1;
492
493 #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS
494 socklen_t so;
495 int cfd;
496 # if USE_IPV6
497 int error;
498 char addr[NI_MAXHOST];
499 # endif
500 SOCKADDR_STYPE in_port;
501 int getpeer = -1;
502
503 cfd = FileFDNum(pCL->fd);
504 pCL->caccess = 'r';
505 # if defined(USE_LIBWRAP)
506 {
507 struct request_info request;
508 CONDDEBUG((1, "ClientAccessOk(): doing tcpwrappers check"));
509 request_init(&request, RQ_DAEMON, progname, RQ_FILE, cfd, 0);
510 fromhost(&request);
511 if (!hosts_access(&request)) {
512 FileWrite(pCL->fd, FLAGFALSE,
513 "access from your host refused\r\n", -1);
514 retval = 0;
515 goto setpeer;
516 }
517 }
518 # endif
519
520 so = sizeof(in_port);
521 if (-1 ==
522 (getpeer = getpeername(cfd, (struct sockaddr *)&in_port, &so))) {
523 FileWrite(pCL->fd, FLAGFALSE, "getpeername failed\r\n", -1);
524 retval = 0;
525 goto setpeer;
526 }
527 pCL->caccess = AccType(
528 # if USE_IPV6
529 &in_port,
530 # else
531 &in_port.sin_addr,
532 # endif
533 &peername);
534 if (pCL->caccess == 'r') {
535 FileWrite(pCL->fd, FLAGFALSE, "access from your host refused\r\n",
536 -1);
537 retval = 0;
538 }
539 setpeer:
540 #else
541 struct in_addr addr;
542
543 # if HAVE_INET_ATON
544 inet_aton("127.0.0.1", &addr);
545 # else
546 addr.s_addr = inet_addr("127.0.0.1");
547 # endif
548 pCL->caccess = AccType(&addr, &peername);
549 if (pCL->caccess == 'r') {
550 FileWrite(pCL->fd, FLAGFALSE, "access from your host refused\r\n",
551 -1);
552 retval = 0;
553 }
554 #endif
555
556 if (pCL->peername != (STRING *)0) {
557 BuildString((char *)0, pCL->peername);
558 if (peername != (char *)0)
559 BuildString(peername, pCL->peername);
560 #if USE_IPV6
561 else if (getpeer != -1) {
562 error =
563 getnameinfo((struct sockaddr *)&in_port, so, addr,
564 sizeof(addr), NULL, 0, NI_NUMERICHOST);
565 if (error) {
566 FileWrite(pCL->fd, FLAGFALSE, "getnameinfo failed\r\n",
567 -1);
568 Error("ClientAccessOk(): gatenameinfo: %s",
569 gai_strerror(error));
570 retval = 0;
571 }
572
573 BuildString(addr, pCL->peername);
574 } else
575 BuildString("<unknown>", pCL->peername);
576 #elif USE_UNIX_DOMAIN_SOCKETS
577 else
578 BuildString("127.0.0.1", pCL->peername);
579 #else
580 else if (getpeer != -1)
581 BuildString(inet_ntoa(in_port.sin_addr), pCL->peername);
582 else
583 BuildString("<unknown>", pCL->peername);
584 #endif
585 }
586 if (peername != (char *)0)
587 free(peername);
588 return retval;
589 }
590