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 (c) 1990 The Ohio State University.
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms are permitted
14 * provided that: (1) source distributions retain this entire copyright
15 * notice and comment, and (2) distributions including binaries display
16 * the following acknowledgement: ``This product includes software
17 * developed by The Ohio State University and its contributors''
18 * in the documentation or other materials provided with the distribution
19 * and in all advertising materials mentioning features or use of this
20 * software. Neither the name of the University nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
26 *
27 *
28 * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana
29 * 47907. All rights reserved.
30 *
31 * Recoded by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
32 *
33 * This software is not subject to any license of the American Telephone
34 * and Telegraph Company or the Regents of the University of California.
35 *
36 * Permission is granted to anyone to use this software for any purpose on
37 * any computer system, and to alter it and redistribute it freely, subject
38 * to the following restrictions:
39 *
40 * 1. Neither the authors nor Purdue University are responsible for any
41 * consequences of the use of this software.
42 *
43 * 2. The origin of this software must not be misrepresented, either by
44 * explicit claim or by omission. Credit to the authors and Purdue
45 * University must appear in documentation and sources.
46 *
47 * 3. Altered versions must be plainly marked as such, and must not be
48 * misrepresented as being the original software.
49 *
50 * 4. This notice may not be removed or altered.
51 */
52
53 #include <compat.h>
54
55 #include <pwd.h>
56 #include <grp.h>
57 #include <stdarg.h>
58 #include <arpa/telnet.h>
59
60 #include <cutil.h>
61 #include <consent.h>
62 #include <client.h>
63 #include <access.h>
64 #include <group.h>
65 #include <readcfg.h>
66 #include <master.h>
67 #include <main.h>
68 #include <version.h>
69 #include <stdio.h>
70
71 #if HAVE_PAM
72 # include <security/pam_appl.h>
73 #endif
74
75
76 /* flags that a signal has occurred */
77 static sig_atomic_t fSawChldHUP = 0, fSawReUp = 0, fSawGoAway =
78 0, fSawReapVirt = 0, fSawChldUSR2 = 0;
79
80 /* timers */
81 time_t timers[T_MAX];
82
83 #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION
84 static unsigned long dmallocMarkClientConnection = 0;
85 #endif
86
87 void
SendIWaitClientsMsg(CONSENT * pCE,char * message)88 SendIWaitClientsMsg(CONSENT *pCE, char *message)
89 {
90 CONSCLIENT *pCL;
91
92 if ((CONSENT *)0 == pCE) {
93 return;
94 }
95
96 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) {
97 if (pCL->fcon && pCL->fiwait) {
98 pCL->fiwait = 0;
99 FileWrite(pCL->fd, FLAGFALSE, message, -1);
100 }
101 }
102 }
103
104 void
SendClientsMsg(CONSENT * pCE,char * message)105 SendClientsMsg(CONSENT *pCE, char *message)
106 {
107 CONSCLIENT *pCL;
108
109 if ((CONSENT *)0 == pCE) {
110 return;
111 }
112
113 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) {
114 if (pCL->fcon) {
115 FileWrite(pCL->fd, FLAGFALSE, message, -1);
116 }
117 }
118 }
119
120 void
SendCertainClientsMsg(GRPENT * pGE,char * who,char * message)121 SendCertainClientsMsg(GRPENT *pGE, char *who, char *message)
122 {
123 CONSCLIENT *pCL;
124 char *console = (char *)0;
125
126 if ((GRPENT *)0 == pGE || who == (char *)0 || message == (char *)0) {
127 return;
128 }
129
130 if ((console = strrchr(who, '@')) != (char *)0) {
131 *console++ = '\000';
132 if (*console == '\000')
133 console = (char *)0;
134 }
135
136 for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) {
137 if (pCL->fcon) {
138 if (*who != '\000' && strcmp(pCL->username->string, who) != 0)
139 continue;
140 if (console != (char *)0 &&
141 strcmp(pCL->pCEto->server, console) != 0)
142 continue;
143 FileWrite(pCL->fd, FLAGFALSE, message, -1);
144 }
145 }
146 }
147
148 void
SendAllClientsMsg(GRPENT * pGE,char * message)149 SendAllClientsMsg(GRPENT *pGE, char *message)
150 {
151 CONSCLIENT *pCL;
152
153 if ((GRPENT *)0 == pGE) {
154 return;
155 }
156
157 for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) {
158 if (pCL->fcon) {
159 FileWrite(pCL->fd, FLAGFALSE, message, -1);
160 }
161 }
162 }
163
164 void
AbortAnyClientExec(CONSCLIENT * pCL)165 AbortAnyClientExec(CONSCLIENT *pCL)
166 {
167 if (pCL->iState == S_CEXEC) {
168 FileSetQuoteIAC(pCL->fd, FLAGFALSE);
169 FilePrint(pCL->fd, FLAGTRUE, "%c%c", OB_IAC, OB_ABRT);
170 FileSetQuoteIAC(pCL->fd, FLAGTRUE);
171 pCL->fcon = 1;
172 pCL->iState = S_NORMAL;
173 }
174 }
175
176 void
StopTask(CONSENT * pCE)177 StopTask(CONSENT *pCE)
178 {
179 if (pCE->taskpid != 0) {
180 kill(pCE->taskpid, SIGHUP);
181 CONDDEBUG((1, "StopTask(): sending task pid %lu signal %d",
182 (unsigned long)pCE->taskpid, SIGHUP));
183 }
184
185 if (pCE->taskfile != (CONSFILE *)0) {
186 int taskfile = FileFDNum(pCE->taskfile);
187 FD_CLR(taskfile, &rinit);
188 FileClose(&pCE->taskfile);
189 pCE->taskfile = (CONSFILE *)0;
190 }
191 }
192
193 void
ClientWantsWrite(CONSCLIENT * pCL)194 ClientWantsWrite(CONSCLIENT *pCL)
195 {
196 CONSENT *pCE;
197
198 if ((CONSCLIENT *)0 == pCL)
199 return;
200 if (pCL->fwr)
201 return;
202
203 pCL->fwr = 0;
204 pCL->fwantwr = 1;
205 pCE = pCL->pCEto;
206 if ((CONSENT *)0 == pCE)
207 return;
208
209 /* promote the client to the top of the list
210 * (which allows them to be picked first for
211 * aquiring read-write access)
212 * first by extracting...
213 */
214 if ((CONSCLIENT *)0 != pCL->pCLnext) {
215 pCL->pCLnext->ppCLbnext = pCL->ppCLbnext;
216 }
217 *(pCL->ppCLbnext) = pCL->pCLnext;
218
219 /* now by inserting...
220 */
221 pCL->pCLnext = pCE->pCLon;
222 pCL->ppCLbnext = &pCE->pCLon;
223 if ((CONSCLIENT *)0 != pCL->pCLnext) {
224 pCL->pCLnext->ppCLbnext = &pCL->pCLnext;
225 }
226 pCE->pCLon = pCL;
227 }
228
229 void
DisconnectClient(GRPENT * pGE,CONSCLIENT * pCL,char * message,FLAG force)230 DisconnectClient(GRPENT *pGE, CONSCLIENT *pCL, char *message, FLAG force)
231 {
232 CONSENT *pCEServing;
233
234 if (pGE == (GRPENT *)0 || pCL == (CONSCLIENT *)0) {
235 return;
236 }
237
238 AbortAnyClientExec(pCL);
239
240 if (pCL->fcon) {
241 FileWrite(pCL->fd, FLAGFALSE, message, -1);
242 }
243
244 if (force != FLAGTRUE && !FileBufEmpty(pCL->fd)) {
245 pCL->ioState = ISFLUSHING;
246 return;
247 }
248
249 /* log it, drop from select list,
250 * close gap in table, etc, etc...
251 */
252 pCEServing = pCL->pCEto;
253
254 if (pGE->pCEctl != pCEServing) {
255 Msg("[%s] logout %s", pCEServing->server, pCL->acid->string);
256 } else if (pCL->iState == S_NORMAL)
257 Verbose("<group> logout %s", pCL->acid->string);
258
259 if (pCEServing->ondemand == FLAGTRUE &&
260 pCEServing->pCLon->pCLnext == (CONSCLIENT *)0)
261 ConsDown(pCEServing, FLAGFALSE, FLAGFALSE);
262
263 FD_CLR(FileFDNum(pCL->fd), &rinit);
264 FD_CLR(FileFDNum(pCL->fd), &winit);
265 FileClose(&pCL->fd);
266
267 /* mark as not writer, if he is
268 * and turn logging back on...
269 */
270 if (pCL->fwr) {
271 BumpClient(pCEServing, (char *)0);
272 TagLogfileAct(pCEServing, "%s detached", pCL->acid->string);
273 if (pCEServing->nolog) {
274 pCEServing->nolog = 0;
275 TagLogfile(pCEServing, "Console logging restored (logout)");
276 }
277 FindWrite(pCEServing);
278 }
279
280 /* mark as unconnected and remove from both
281 * lists (all clients, and this console)
282 */
283 pCL->fcon = 0;
284 if ((CONSCLIENT *)0 != pCL->pCLnext) {
285 pCL->pCLnext->ppCLbnext = pCL->ppCLbnext;
286 }
287 *(pCL->ppCLbnext) = pCL->pCLnext;
288 if ((CONSCLIENT *)0 != pCL->pCLscan) {
289 pCL->pCLscan->ppCLbscan = pCL->ppCLbscan;
290 }
291 *(pCL->ppCLbscan) = pCL->pCLscan;
292
293 /* the continue below will advance to a (ksb)
294 * legal client, even though we are now closed
295 * and in the fre list becasue pCLscan is used
296 * for the free list
297 */
298 pCL->pCLnext = pGE->pCLfree;
299 pGE->pCLfree = pCL;
300 #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION
301 CONDDEBUG((1, "DisconnectClient(): dmalloc / MarkClientConnection"));
302 dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1);
303 #endif
304 }
305
306 int
DisconnectCertainClients(GRPENT * pGE,char * admin,char * who)307 DisconnectCertainClients(GRPENT *pGE, char *admin, char *who)
308 {
309 CONSCLIENT *pCL;
310 char *console = (char *)0;
311 int count = 0;
312 char *msg = (char *)0;
313
314 if ((GRPENT *)0 == pGE || who == (char *)0) {
315 return 0;
316 }
317
318 if ((console = strrchr(who, '@')) != (char *)0) {
319 *console++ = '\000';
320 if (*console == '\000')
321 console = (char *)0;
322 }
323
324 for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) {
325 /* skip folks not connected to a console
326 * we check for 'fcon' in other code, but that skips users
327 * that are suspended as well, we don't want that here
328 */
329 if (pGE->pCEctl == pCL->pCEto)
330 continue;
331 if (*who != '\000' && strcmp(pCL->username->string, who) != 0)
332 continue;
333 if (console != (char *)0 &&
334 strcmp(pCL->pCEto->server, console) != 0)
335 continue;
336 if (msg == (char *)0) {
337 BuildTmpString((char *)0);
338 BuildTmpString("[-- Disconnected by admin `");
339 BuildTmpString(admin);
340 msg = BuildTmpString("' --]\r\n");
341 }
342 DisconnectClient(pGE, pCL, msg, FLAGFALSE);
343 count++;
344 }
345
346 return count;
347 }
348
349 void
DisconnectAllClients(GRPENT * pGE,char * message)350 DisconnectAllClients(GRPENT *pGE, char *message)
351 {
352 CONSCLIENT *pCL;
353
354 if ((GRPENT *)0 == pGE) {
355 return;
356 }
357
358 for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) {
359 DisconnectClient(pGE, pCL, message, FLAGTRUE);
360 }
361 }
362
363 void
DestroyClient(CONSCLIENT * pCL)364 DestroyClient(CONSCLIENT *pCL)
365 {
366 if (pCL == (CONSCLIENT *)0)
367 return;
368 if (pCL->acid != (STRING *)0)
369 DestroyString(pCL->acid);
370 if (pCL->peername != (STRING *)0)
371 DestroyString(pCL->peername);
372 if (pCL->accmd != (STRING *)0)
373 DestroyString(pCL->accmd);
374 if (pCL->username != (STRING *)0)
375 DestroyString(pCL->username);
376 FileClose(&pCL->fd);
377 free(pCL);
378 }
379
380 void
DestroyConsentUsers(CONSENTUSERS ** cu)381 DestroyConsentUsers(CONSENTUSERS **cu)
382 {
383 CONSENTUSERS *n = (CONSENTUSERS *)0;
384
385 if (cu == (CONSENTUSERS **)0)
386 return;
387
388 while ((*cu) != (CONSENTUSERS *)0) {
389 n = (*cu)->next;
390 free(*cu);
391 (*cu) = n;
392 }
393 }
394
395 CONSENTUSERS *
ConsentFindUser(CONSENTUSERS * pCU,char * id)396 ConsentFindUser(CONSENTUSERS *pCU, char *id)
397 {
398 short close = 0;
399 struct group *g = (struct group *)0;
400 struct passwd *pwd = (struct passwd *)0;
401
402 for (; pCU != (CONSENTUSERS *)0; pCU = pCU->next) {
403 if (pCU->user->name[0] == '@' && pCU->user->name[1] != '\000') {
404 if (close == 0) {
405 close = 1;
406 /* try to grab the primary group */
407 pwd = getpwnam(id);
408 }
409
410 /* grab the group info */
411 if ((g = getgrnam(pCU->user->name + 1)) == (struct group *)0) {
412 Error("ConsentFindUser(): unknown group name `%s'",
413 pCU->user->name + 1);
414 } else if (pwd != (struct passwd *)0 &&
415 pwd->pw_gid == g->gr_gid) {
416 goto donehunting;
417 } else if (g->gr_mem != (char **)0) {
418 char **m;
419 for (m = g->gr_mem; *m != (char *)0; m++)
420 if (strcmp(*m, id) == 0)
421 goto donehunting;
422 }
423 } else if (strcmp(pCU->user->name, id) == 0) {
424 goto donehunting;
425 }
426 }
427 donehunting:
428 if (close) {
429 endgrent();
430 endpwent();
431 }
432 return pCU;
433 }
434
435 int
ConsentUserOk(CONSENTUSERS * pCU,char * id)436 ConsentUserOk(CONSENTUSERS *pCU, char *id)
437 {
438 CONSENTUSERS *c;
439
440 if ((c = ConsentFindUser(pCU, id)) != (CONSENTUSERS *)0)
441 return !c->not;
442 if ((c = ConsentFindUser(pCU, "*")) != (CONSENTUSERS *)0)
443 return !c->not;
444 return -1;
445 }
446
447 /* check user permissions. return 0 for r/w, 1 for r/o, -1 for none */
448 int
ClientAccess(CONSENT * pCE,char * user)449 ClientAccess(CONSENT *pCE, char *user)
450 {
451 CONDDEBUG((1, "ClientAccess(): Authenticating user %s", user));
452
453 if (ConsentUserOk(pCE->rw, user) == 1)
454 return 0;
455 if (ConsentUserOk(pCE->ro, user) == 1)
456 return 1;
457 #if STRIP_REALM
458 {
459 /* try the username without @REALM against the ACL
460 * this allows for falling back to PAM from kerberos5/gssapi
461 * as the latter uses 'user@REALM' and the former only 'user'
462 */
463 char *at;
464 char *shortname;
465 int ret = -1;
466
467 if ((at = strrchr(user, '@')) != (char *)0) {
468 shortname = StrDup(user);
469 *(shortname + (at - user)) = '\000';
470
471 CONDDEBUG((1,
472 "ClientAccess(): Shortname computed from %s is %s",
473 user, shortname));
474 if (ConsentUserOk(pCE->rw, shortname) == 1) {
475 ret = 0;
476 } else if (ConsentUserOk(pCE->ro, shortname) == 1) {
477 ret = 1;
478 }
479 free(shortname);
480 return ret;
481 }
482 }
483 #endif
484 return -1;
485 }
486
487 void
DestroyConsent(GRPENT * pGE,CONSENT * pCE)488 DestroyConsent(GRPENT *pGE, CONSENT *pCE)
489 {
490 CONSCLIENT *pCL;
491 CONSENT **ppCE;
492 NAMES *name;
493
494 if (pCE == (CONSENT *)0 || pGE == (GRPENT *)0)
495 return;
496
497 CONDDEBUG((1, "DestroyConsent(): destroying `%s'", pCE->server));
498
499 /* must loop using pCLall and pCLscan for the same reason as the
500 * drop: code. this is basically the same set of code, but modified
501 * since we know we're going to nuke the console itself.
502 */
503 for (pCL = pGE->pCLall; pCL != (CONSCLIENT *)0; pCL = pCL->pCLscan) {
504 if (pCL->pCEto != pCE)
505 continue;
506 AbortAnyClientExec(pCL);
507 if (pCL->fcon) {
508 FileWrite(pCL->fd, FLAGFALSE,
509 "[-- Conserver reconfigured - console has been (re)moved --]\r\n",
510 -1);
511 }
512 Msg("[%s] logout %s", pCE->server, pCL->acid->string);
513 FD_CLR(FileFDNum(pCL->fd), &rinit);
514 FD_CLR(FileFDNum(pCL->fd), &winit);
515 FileClose(&pCL->fd);
516 if (pCL->fwr) {
517 BumpClient(pCE, (char *)0);
518 TagLogfileAct(pCE, "%s detached", pCL->acid->string);
519 if (pCE->nolog) {
520 pCE->nolog = 0;
521 TagLogfile(pCE, "Console logging restored (logout)");
522 }
523 }
524 /* mark as unconnected and remove from both
525 * lists (all clients, and this console)
526 */
527 if ((CONSCLIENT *)0 != pCL->pCLnext) {
528 pCL->pCLnext->ppCLbnext = pCL->ppCLbnext;
529 }
530 *(pCL->ppCLbnext) = pCL->pCLnext;
531 if ((CONSCLIENT *)0 != pCL->pCLscan) {
532 pCL->pCLscan->ppCLbscan = pCL->ppCLbscan;
533 }
534 *(pCL->ppCLbscan) = pCL->pCLscan;
535
536 pCL->pCLnext = pGE->pCLfree;
537 pGE->pCLfree = pCL;
538 }
539
540 StopTask(pCE);
541 ConsDown(pCE, FLAGFALSE, FLAGTRUE);
542
543 for (ppCE = &(pGE->pCElist); *ppCE != (CONSENT *)0;
544 ppCE = &((*ppCE)->pCEnext)) {
545 if (*ppCE == pCE) {
546 *ppCE = pCE->pCEnext;
547 break;
548 }
549 }
550
551 DestroyConsentUsers(&(pCE->ro));
552 DestroyConsentUsers(&(pCE->rw));
553
554 if (pCE->server != (char *)0)
555 free(pCE->server);
556 if (pCE->host != (char *)0)
557 free(pCE->host);
558 if (pCE->master != (char *)0)
559 free(pCE->master);
560 if (pCE->exec != (char *)0)
561 free(pCE->exec);
562 if (pCE->device != (char *)0)
563 free(pCE->device);
564 if (pCE->devicesubst != (char *)0)
565 free(pCE->devicesubst);
566 if (pCE->execsubst != (char *)0)
567 free(pCE->execsubst);
568 if (pCE->initsubst != (char *)0)
569 free(pCE->initsubst);
570 if (pCE->logfile != (char *)0)
571 free(pCE->logfile);
572 if (pCE->initcmd != (char *)0)
573 free(pCE->initcmd);
574 if (pCE->motd != (char *)0)
575 free(pCE->motd);
576 if (pCE->idlestring != (char *)0)
577 free(pCE->idlestring);
578 if (pCE->replstring != (char *)0)
579 free(pCE->replstring);
580 if (pCE->tasklist != (char *)0)
581 free(pCE->tasklist);
582 if (pCE->breaklist != (char *)0)
583 free(pCE->breaklist);
584 if (pCE->execSlave != (char *)0)
585 free(pCE->execSlave);
586 while (pCE->aliases != (NAMES *)0) {
587 name = pCE->aliases->next;
588 if (pCE->aliases->name != (char *)0)
589 free(pCE->aliases->name);
590 free(pCE->aliases);
591 pCE->aliases = name;
592 }
593 FileClose(&pCE->fdlog);
594 if (pCE->wbuf != (STRING *)0)
595 DestroyString(pCE->wbuf);
596 free(pCE);
597
598 pGE->imembers--;
599 }
600
601 void
DestroyGroup(GRPENT * pGE)602 DestroyGroup(GRPENT *pGE)
603 {
604 CONSENT *pCEtmp, *pCE;
605 CONSCLIENT *pCLtmp, *pCL;
606
607 if (pGE == (GRPENT *)0)
608 return;
609
610 CONDDEBUG((1, "DestroyGroup(): destroying group #%d (%d members)",
611 pGE->id, pGE->imembers));
612
613 /* nuke each console (which kicks off clients) */
614 DestroyConsent(pGE, pGE->pCEctl);
615 pCE = pGE->pCElist;
616 while (pCE != (CONSENT *)0) {
617 pCEtmp = pCE->pCEnext;
618 DestroyConsent(pGE, pCE);
619 pCE = pCEtmp;
620 }
621
622 /* now we can nuke the client structures */
623 pCL = pGE->pCLall;
624 while (pCL != (CONSCLIENT *)0) {
625 pCLtmp = pCL->pCLscan;
626 DestroyClient(pCL);
627 pCL = pCLtmp;
628 }
629 pCL = pGE->pCLfree;
630 while (pCL != (CONSCLIENT *)0) {
631 pCLtmp = pCL->pCLnext;
632 DestroyClient(pCL);
633 pCL = pCLtmp;
634 }
635
636 free(pGE);
637 }
638
639 #if HAVE_PAM
640 int
QuietConv(int num_msg,struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)641 QuietConv(int num_msg, struct pam_message **msg,
642 struct pam_response **resp, void *appdata_ptr)
643 {
644 int i;
645 struct pam_response *response = NULL;
646 char *pcUser;
647 char *pcWord;
648 pcUser = ((char **)appdata_ptr)[0];
649 pcWord = ((char **)appdata_ptr)[1];
650
651 if (num_msg <= 0)
652 return PAM_CONV_ERR;
653
654 response =
655 (struct pam_response *)calloc(num_msg,
656 sizeof(struct pam_response));
657
658 if (response == (struct pam_response *)0)
659 return PAM_CONV_ERR;
660
661 for (i = 0; i < num_msg; i++) {
662 response[i].resp_retcode = PAM_SUCCESS;
663 switch (msg[i]->msg_style) {
664 case PAM_PROMPT_ECHO_ON:
665 response[i].resp =
666 (pcUser != (char *)0 ? StrDup(pcUser) : (char *)0);
667 break;
668
669 case PAM_PROMPT_ECHO_OFF:
670 response[i].resp =
671 (pcWord != (char *)0 ? StrDup(pcWord) : (char *)0);
672 break;
673
674 case PAM_TEXT_INFO:
675 case PAM_ERROR_MSG:
676 /* Ignore it... */
677 response[i].resp = NULL;
678 break;
679
680 default:
681 /* Must be an error of some sort... */
682 free(response);
683 return PAM_CONV_ERR;
684 }
685 }
686
687 *resp = response;
688 return PAM_SUCCESS;
689 }
690 #endif
691
692 /* Is this passwd a match for this user's passwd? (gregf/ksb)
693 * look up passwd in shadow file if we have to, if we are
694 * given a special epass try it first.
695 */
696 int
CheckPass(char * pcUser,char * pcWord,FLAG empty_check)697 CheckPass(char *pcUser, char *pcWord, FLAG empty_check)
698 {
699 if (pcWord == (char *)0) {
700 pcWord = "";
701 }
702 #if HAVE_PAM
703 if (empty_check == FLAGTRUE)
704 return AUTH_INVALID;
705 int pam_error;
706 char *appdata[2];
707 static pam_handle_t *pamh = (pam_handle_t *)0;
708 struct pam_conv conv;
709 appdata[0] = pcUser;
710 appdata[1] = pcWord;
711 conv.conv = &QuietConv;
712 conv.appdata_ptr = (void *)&appdata;
713
714 CONDDEBUG((1, "CheckPass(): pam_start(conserver,%s,...)", pcUser));
715 pam_error = pam_start("conserver", pcUser, &conv, &pamh);
716
717 if (pam_error == PAM_SUCCESS) {
718 pam_set_item(pamh, PAM_RHOST, "IHaveNoIdeaHowIGotHere");
719 CONDDEBUG((1, "CheckPass(): pam_authenticate(%s)", pcUser));
720 pam_error = pam_authenticate(pamh, PAM_SILENT);
721 if (pam_error == PAM_SUCCESS) {
722 CONDDEBUG((1, "CheckPass(): pam_acct_mgmt(%s)", pcUser));
723 pam_error = pam_acct_mgmt(pamh, PAM_SILENT);
724 if (pam_error != PAM_SUCCESS) {
725 Error("CheckPass(): PAM failure(%s): %s", pcUser,
726 pam_strerror(pamh, pam_error));
727 }
728 } else if (pam_error != PAM_AUTH_ERR) {
729 Error("CheckPass(): PAM failure(%s): %s", pcUser,
730 pam_strerror(pamh, pam_error));
731 }
732 CONDDEBUG((1, "CheckPass(): pam_end(%s)", pcUser));
733 pam_end(pamh, pam_error);
734 if (pam_error == PAM_ABORT) /* things just got real bad */
735 fSawGoAway = 1;
736 } else {
737 Error("CheckPass(): PAM failure(%s): %s", pcUser,
738 pam_strerror(pamh, pam_error));
739 }
740 if (pam_error == PAM_SUCCESS)
741 return AUTH_SUCCESS;
742 if (pam_error == PAM_USER_UNKNOWN)
743 return AUTH_NOUSER;
744 return AUTH_INVALID;
745 #else /* getpw*() */
746 struct passwd *pwd;
747 int retval = AUTH_SUCCESS;
748 char *pass;
749 # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM
750 struct pr_passwd *prpwd;
751 # endif
752 # if HAVE_GETSPNAM
753 struct spwd *spwd;
754 # endif
755
756 if ((pwd = getpwnam(pcUser)) == (struct passwd *)0) {
757 CONDDEBUG((1, "CheckPass(): getpwnam(%s): %s", pcUser,
758 strerror(errno)));
759 retval = AUTH_NOUSER;
760 goto finished_pass;
761 }
762 pass = pwd->pw_passwd;
763
764 # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM
765 if (iscomsec()) {
766 CONDDEBUG((1, "CheckPass(): trusted password check"));
767 if ((prpwd = getprpwnam(pcUser)) == (struct pr_passwd *)0) {
768 CONDDEBUG((1, "CheckPass(): getprpwnam(%s): %s", pcUser,
769 strerror(errno)));
770 retval = AUTH_NOUSER;
771 goto finished_pass;
772 }
773 pass = prpwd->ufld.fd_encrypt;
774 }
775 # endif
776
777 # if HAVE_GETSPNAM
778 if ('x' == pass[0] && '\000' == pass[1]) {
779 CONDDEBUG((1, "CheckPass(): shadow password check"));
780 if ((spwd = getspnam(pcUser)) == (struct spwd *)0) {
781 CONDDEBUG((1, "CheckPass(): getspnam(%s): %s", pcUser,
782 strerror(errno)));
783 retval = AUTH_NOUSER;
784 goto finished_pass;
785 }
786 pass = spwd->sp_pwdp;
787 }
788 # endif
789
790 if (pass[0] == '\000' && pcWord[0] == '\000') {
791 retval = AUTH_SUCCESS; /* let empty password match */
792 } else {
793 char *encrypted;
794 char *salt;
795
796 if (pass[0] == '\000')
797 salt = "XX";
798 else
799 salt = pass;
800
801 # if HAVE_ISCOMSEC && HAVE_BIGCRYPT
802 if (iscomsec())
803 encrypted = bigcrypt(pcWord, salt);
804 else
805 # endif
806 encrypted = crypt(pcWord, salt);
807 if ((strcmp(pass, encrypted) != 0)) {
808 CONDDEBUG((1, "CheckPass(): password check failed (%s)",
809 pass));
810 retval = AUTH_INVALID;
811 }
812 }
813
814 finished_pass:
815 endpwent();
816 # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM
817 if (iscomsec())
818 endprpwent();
819 # endif
820 # if HAVE_GETSPNAM
821 endspent();
822 # endif
823 return retval;
824 #endif /* getpw*() */
825 }
826
827 /* on an HUP close and re-open log files so lop can trim them (ksb)
828 * and reread the configuration file
829 * lucky for us: log file fd's can change async from the group driver!
830 */
831 static RETSIGTYPE
FlagSawChldHUP(int sig)832 FlagSawChldHUP(int sig)
833 {
834 fSawChldHUP = 1;
835 #if !HAVE_SIGACTION
836 SimpleSignal(SIGHUP, FlagSawChldHUP);
837 #endif
838 }
839
840 /* on an USR2 close and re-open log files so lop can trim them (ksb)
841 * lucky for us: log file fd's can change async from the group driver!
842 */
843 static RETSIGTYPE
FlagSawChldUSR2(int sig)844 FlagSawChldUSR2(int sig)
845 {
846 fSawChldUSR2 = 1;
847 #if !HAVE_SIGACTION
848 SimpleSignal(SIGUSR2, FlagSawChldUSR2);
849 #endif
850 }
851
852 void
ConsoleError(CONSENT * pCE)853 ConsoleError(CONSENT *pCE)
854 {
855 if (pCE->autoreinit != FLAGTRUE) {
856 ConsDown(pCE, FLAGTRUE, FLAGTRUE);
857 } else {
858 /* Try an initial reconnect */
859 Msg("[%s] automatic reinitialization", pCE->server);
860 ConsInit(pCE);
861
862 /* If we didn't succeed, try again later */
863 if (!pCE->fup)
864 pCE->autoReUp = 1;
865 }
866 }
867
868 static void
ReOpen(GRPENT * pGE)869 ReOpen(GRPENT *pGE)
870 {
871 CONSENT *pCE;
872
873 if ((GRPENT *)0 == pGE) {
874 return;
875 }
876
877 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
878 if ((CONSFILE *)0 == pCE->fdlog) {
879 continue;
880 }
881 FileClose(&pCE->fdlog);
882 if ((CONSFILE *)0 ==
883 (pCE->fdlog =
884 FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, 0644))) {
885 Error("[%s] FileOpen(%s): %s: forcing down", pCE->server,
886 pCE->logfile, strerror(errno));
887 ConsDown(pCE, FLAGTRUE, FLAGTRUE);
888 continue;
889 }
890 }
891 }
892
893 static RETSIGTYPE
FlagReUp(int sig)894 FlagReUp(int sig)
895 {
896 fSawReUp = 1;
897 #if !HAVE_SIGACTION
898 SimpleSignal(SIGUSR1, FlagReUp);
899 #endif
900 }
901
902 static struct delay {
903 char *host;
904 time_t last;
905 struct delay *next;
906 } *delays = (struct delay *)0;
907
908 /* returns zero if the delay has been reached, otherwise returns
909 * the time when the next init should happen
910 */
911 static time_t
InitDelay(CONSENT * pCE)912 InitDelay(CONSENT *pCE)
913 {
914 char *l;
915 struct delay *d;
916
917 if (pCE->host != (char *)0)
918 l = pCE->host;
919 else
920 l = "";
921
922 for (d = delays; d != (struct delay *)0; d = d->next) {
923 if (strcmp(l, d->host) == 0) {
924 if ((time((time_t *)0) - d->last) >= config->initdelay) {
925 return (time_t)0;
926 } else
927 return d->last + config->initdelay;
928 }
929 }
930 return (time_t)0;
931 }
932
933 static void
UpdateDelay(CONSENT * pCE)934 UpdateDelay(CONSENT *pCE)
935 {
936 char *l;
937 struct delay *d;
938
939 if (pCE->host != (char *)0)
940 l = pCE->host;
941 else
942 l = "";
943
944 for (d = delays; d != (struct delay *)0; d = d->next) {
945 if (strcmp(l, d->host) == 0) {
946 d->last = time((time_t *)0);
947 return;
948 }
949 }
950 if ((d =
951 (struct delay *)malloc(sizeof(struct delay))) ==
952 (struct delay *)0)
953 OutOfMem();
954 if ((d->host = StrDup(l)) == (char *)0)
955 OutOfMem();
956 d->last = time((time_t *)0);
957 d->next = delays;
958 delays = d;
959 }
960
961 static void
ReUp(GRPENT * pGE,short automatic)962 ReUp(GRPENT *pGE, short automatic)
963 {
964 CONSENT *pCE;
965 int autoReUp;
966 time_t tyme;
967 short retry;
968 static short autoup = 0;
969 short wasAuto = 0;
970
971 if ((GRPENT *)0 == pGE)
972 return;
973
974 tyme = time((time_t *)0);
975 if ((automatic == 1) && (tyme < timers[T_AUTOUP]))
976 return;
977 if ((automatic == 2) &&
978 (!config->reinitcheck || (tyme < timers[T_REINIT])))
979 return;
980
981 if (automatic == -1)
982 wasAuto = autoup;
983 autoup = 0;
984
985 /* we loop here 'cause the init process could take a bit of time
986 * (depending on how many things we init in the run through the
987 * consoles) and we might be able to then initialize more stuff.
988 * we'll eventually run through too fast, run out of consoles, or
989 * have a big enough delay to go back to the main loop.
990 */
991 do {
992 retry = 0;
993 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
994 short updateDelay = 0;
995
996 if (pCE->fup || pCE->ondemand == FLAGTRUE ||
997 (automatic == 1 && !pCE->autoReUp))
998 continue;
999 if (config->initdelay > 0) {
1000 time_t t;
1001 if ((t = InitDelay(pCE)) > 0) {
1002 if (timers[T_INITDELAY] == (time_t)0 ||
1003 timers[T_INITDELAY] > t)
1004 timers[T_INITDELAY] = t;
1005 continue;
1006 } else {
1007 updateDelay = retry = 1;
1008 }
1009 }
1010 autoReUp = pCE->autoReUp;
1011 if (automatic > 0 || wasAuto) {
1012 Msg("[%s] automatic reinitialization", pCE->server);
1013 autoup = 1;
1014 }
1015 ConsInit(pCE);
1016 if (updateDelay)
1017 UpdateDelay(pCE);
1018 if (!pCE->fup && automatic > 0)
1019 pCE->autoReUp = autoReUp;
1020 }
1021 } while (retry);
1022
1023 /* update all the timers */
1024 if (automatic == 0 || automatic == 2) {
1025 if (config->reinitcheck)
1026 timers[T_REINIT] = tyme + (config->reinitcheck * 60);
1027 }
1028 if (!fNoautoreup)
1029 timers[T_AUTOUP] = tyme + 60;
1030 }
1031
1032 void
TagLogfile(const CONSENT * pCE,char * fmt,...)1033 TagLogfile(const CONSENT *pCE, char *fmt, ...)
1034 {
1035 va_list ap;
1036 va_start(ap, fmt);
1037
1038 if ((pCE == (CONSENT *)0) || (pCE->fdlog == (CONSFILE *)0))
1039 return;
1040
1041 FileWrite(pCE->fdlog, FLAGTRUE, "[-- ", -1);
1042 FileVWrite(pCE->fdlog, FLAGTRUE, fmt, ap);
1043 FilePrint(pCE->fdlog, FLAGFALSE, " -- %s]\r\n", StrTime((time_t *)0));
1044 va_end(ap);
1045 }
1046
1047 void
TagLogfileAct(const CONSENT * pCE,char * fmt,...)1048 TagLogfileAct(const CONSENT *pCE, char *fmt, ...)
1049 {
1050 va_list ap;
1051 va_start(ap, fmt);
1052
1053 if ((pCE == (CONSENT *)0) || (pCE->fdlog == (CONSFILE *)0) ||
1054 (pCE->activitylog != FLAGTRUE))
1055 return;
1056
1057 FileWrite(pCE->fdlog, FLAGTRUE, "[-- ", -1);
1058 FileVWrite(pCE->fdlog, FLAGTRUE, fmt, ap);
1059 FilePrint(pCE->fdlog, FLAGFALSE, " -- %s]\r\n", StrTime((time_t *)0));
1060 va_end(ap);
1061 }
1062
1063 static void
RollLogs(GRPENT * pGE)1064 RollLogs(GRPENT *pGE)
1065 {
1066 CONSENT *pCE;
1067 struct stat stLog;
1068 char *t = (char *)0;
1069 char timestr[40];
1070 time_t tyme = (time_t)0;
1071 short maxset = 0;
1072 char buf[4096];
1073 int roll = 0;
1074 int r = 0;
1075 CONSFILE *old;
1076
1077 if ((GRPENT *)0 == pGE)
1078 return;
1079
1080 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
1081 if (pCE->logfilemax == 0)
1082 continue;
1083 maxset = 1;
1084 if (pCE->fdlog == (CONSFILE *)0)
1085 continue;
1086 if (FileStat(pCE->fdlog, &stLog) != 0) {
1087 CONDDEBUG((1, "RollLogs(): FileStat(%d) failed",
1088 FileFDNum(pCE->fdlog)));
1089 continue;
1090 }
1091 if (stLog.st_size < pCE->logfilemax)
1092 continue;
1093 if (pCE->logfilemax > 1024) {
1094 if (pCE->logfilemax > 0x100000)
1095 Msg("[%s] logfile exceeds %dMB: rolling", pCE->server,
1096 pCE->logfilemax / 0x100000);
1097 else
1098 Msg("[%s] logfile exceeds %dKB: rolling", pCE->server,
1099 pCE->logfilemax / 1024);
1100 }
1101
1102 if (pCE->logfilemax < 4000)
1103 roll = 100;
1104 else if (pCE->logfilemax > 160000)
1105 roll = 4000;
1106 else
1107 roll = pCE->logfilemax * 0.025;
1108
1109 r = 0;
1110
1111 if (FileSeek(pCE->fdlog, stLog.st_size - roll, SEEK_SET) > 0) {
1112 if ((r = FileRead(pCE->fdlog, buf, 4096)) > 0) {
1113 if (r == roll) {
1114 for (; r != 0 && buf[roll - r] != '\n'; r--);
1115 r--; /* go beyond \n */
1116 } else
1117 r = 0;
1118 }
1119 }
1120
1121 if (tyme == (time_t)0) {
1122 tyme = time((time_t *)0);
1123 strftime(timestr, sizeof(timestr), "-%Y%m%d-%H%M%S",
1124 gmtime(&tyme));
1125 }
1126 BuildTmpString((char *)0);
1127 t = BuildTmpStringPrint("%s%s", pCE->logfile, timestr);
1128
1129 if (rename(pCE->logfile, t) != 0) {
1130 Error("[%s] RollLogs(): rename(%s,%s) failed: %s", pCE->server,
1131 pCE->logfile, t, strerror(errno));
1132 continue;
1133 }
1134
1135 old = pCE->fdlog;
1136
1137 if ((pCE->fdlog =
1138 FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND,
1139 0644)) == (CONSFILE *)0) {
1140 FileClose(&old);
1141 Error("[%s] RollLogs(): open(%s): %s: forcing down",
1142 pCE->server, pCE->logfile, strerror(errno));
1143 ConsDown(pCE, FLAGTRUE, FLAGTRUE);
1144 continue;
1145 }
1146 if (r > 0) {
1147 FileWrite(pCE->fdlog, FLAGFALSE, buf + roll - r, r);
1148 ftruncate(FileFDNum(old), stLog.st_size - r);
1149 }
1150
1151 FileClose(&old);
1152 }
1153
1154 if (tyme != (time_t)0)
1155 BuildTmpString((char *)0);
1156
1157 if (maxset == 0)
1158 timers[T_ROLL] = (time_t)0;
1159 else {
1160 if (timers[T_ROLL] == (time_t)0)
1161 /* try and spread processes out a bit */
1162 timers[T_ROLL] = time((time_t *)0) + 300 + (pGE->id * 7) % 60;
1163 else
1164 timers[T_ROLL] = time((time_t *)0) + 300;
1165 }
1166 }
1167
1168 static void
Mark(GRPENT * pGE)1169 Mark(GRPENT *pGE)
1170 {
1171 time_t tyme;
1172 CONSENT *pCE;
1173 static STRING *out = (STRING *)0;
1174
1175 if ((GRPENT *)0 == pGE)
1176 return;
1177
1178 if (out == (STRING *)0)
1179 out = AllocString();
1180
1181 BuildString((char *)0, out);
1182
1183 /* [-- MARK -- `date`] */
1184 BuildStringPrint(out, "[-- MARK -- %s]\r\n", StrTime(&tyme));
1185
1186 timers[T_MARK] = (time_t)0;
1187
1188 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
1189 if (pCE->nextMark > 0) {
1190 if (tyme >= pCE->nextMark) {
1191 if ((CONSFILE *)0 != pCE->fdlog) {
1192 CONDDEBUG((1, "Mark(): [-- MARK --] stamp added to %s",
1193 pCE->logfile));
1194 FileWrite(pCE->fdlog, FLAGFALSE, out->string,
1195 out->used - 1);
1196 }
1197 /* Add as many pCE->mark values as necessary so that we move
1198 * beyond the current time.
1199 */
1200 pCE->nextMark +=
1201 (((tyme - pCE->nextMark) / pCE->mark) + 1) * pCE->mark;
1202 }
1203 if (timers[T_MARK] == (time_t)0 ||
1204 timers[T_MARK] > pCE->nextMark)
1205 timers[T_MARK] = pCE->nextMark;
1206 }
1207 }
1208 }
1209
1210 void
WriteLog(CONSENT * pCE,char * s,int len)1211 WriteLog(CONSENT *pCE, char *s, int len)
1212 {
1213 int i = 0;
1214 int j;
1215 static STRING *buf = (STRING *)0;
1216
1217 if ((CONSFILE *)0 == pCE->fdlog) {
1218 return;
1219 }
1220 if (pCE->mark >= 0) { /* no line marking */
1221 FileWrite(pCE->fdlog, FLAGFALSE, s, len);
1222 return;
1223 }
1224
1225 if (buf == (STRING *)0)
1226 buf = AllocString();
1227 BuildString((char *)0, buf);
1228
1229 for (j = 0; j < len; j++) {
1230 if (pCE->nextMark == 0) {
1231 FileWrite(pCE->fdlog, FLAGTRUE, s + i, j - i);
1232 i = j;
1233
1234 if (buf->used <= 1)
1235 BuildStringPrint(buf, "[%s]", StrTime((time_t *)0));
1236
1237 FileWrite(pCE->fdlog, FLAGTRUE, buf->string, buf->used - 1);
1238 pCE->nextMark = pCE->mark;
1239 }
1240 if (s[j] == '\n') {
1241 CONDDEBUG((1,
1242 "WriteLog(): [%s] found newline (nextMark=%d, mark=%d)",
1243 pCE->server, pCE->nextMark, pCE->mark));
1244 pCE->nextMark++;
1245 }
1246 }
1247 if (i < j) {
1248 FileWrite(pCE->fdlog, FLAGTRUE, s + i, j - i);
1249 }
1250 FileWrite(pCE->fdlog, FLAGFALSE, (char *)0, 0);
1251 }
1252
1253 static RETSIGTYPE
FlagGoAway(int sig)1254 FlagGoAway(int sig)
1255 {
1256 fSawGoAway = 1;
1257 #if !HAVE_SIGACTION
1258 SimpleSignal(SIGTERM, FlagGoAway);
1259 #endif
1260 }
1261
1262 /* yep, basically the same...ah well, maybe someday */
1263 static RETSIGTYPE
FlagGoAwayAlso(int sig)1264 FlagGoAwayAlso(int sig)
1265 {
1266 fSawGoAway = 1;
1267 #if !HAVE_SIGACTION
1268 SimpleSignal(SIGINT, FlagGoAwayAlso);
1269 #endif
1270 }
1271
1272 static RETSIGTYPE
FlagReapVirt(int sig)1273 FlagReapVirt(int sig)
1274 {
1275 fSawReapVirt = 1;
1276 #if !HAVE_SIGACTION
1277 SimpleSignal(SIGCHLD, FlagReapVirt);
1278 #endif
1279 }
1280
1281 /* on a TERM we have to cleanup utmp entries (ask ptyd to do it) (ksb)
1282 */
1283 void
DeUtmp(GRPENT * pGE,int sfd)1284 DeUtmp(GRPENT *pGE, int sfd)
1285 {
1286 CONSENT *pCE;
1287 #if USE_UNIX_DOMAIN_SOCKETS
1288 struct sockaddr_un lstn_port;
1289 socklen_t so;
1290
1291 so = sizeof(lstn_port);
1292 if (getsockname(sfd, (struct sockaddr *)&lstn_port, &so) != -1)
1293 unlink(lstn_port.sun_path);
1294 #endif
1295 /* shut down the socket */
1296 close(sfd);
1297
1298 /* say Bye to all connections */
1299 if ((GRPENT *)0 != pGE) {
1300 DisconnectAllClients(pGE,
1301 "[-- Console server shutting down --]\r\n");
1302
1303 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
1304 ConsDown(pCE, FLAGFALSE, FLAGTRUE);
1305 }
1306 }
1307
1308 if (unifiedlog != (CONSFILE *)0)
1309 FileClose(&unifiedlog);
1310
1311 DumpDataStructures();
1312
1313 endpwent();
1314 Bye(EX_OK);
1315 }
1316
1317 /* virtual console procs are our kids, when they die we get a CHLD (ksb)
1318 * which will send us here to clean up the exit code. The lack of a
1319 * reader on the pseudo will cause us to notice the death in Kiddie...
1320 */
1321 static void
ReapVirt(GRPENT * pGE)1322 ReapVirt(GRPENT *pGE)
1323 {
1324 pid_t pid;
1325 int UWbuf;
1326 CONSENT *pCE;
1327
1328 while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) {
1329 if (0 == pid) {
1330 break;
1331 }
1332 /* stopped child is just continued
1333 */
1334 if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) {
1335 Msg("child pid %lu: stopped, sending SIGCONT",
1336 (unsigned long)pid);
1337 continue;
1338 }
1339
1340 if ((GRPENT *)0 == pGE) {
1341 continue;
1342 }
1343
1344 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
1345 if (pid == pCE->initpid) {
1346 if (WIFEXITED(UWbuf))
1347 Msg("[%s] initcmd terminated: pid %lu: exit(%d)",
1348 pCE->server, pid, WEXITSTATUS(UWbuf));
1349 if (WIFSIGNALED(UWbuf))
1350 Msg("[%s] initcmd terminated: pid %lu: signal(%d)",
1351 pCE->server, pid, WTERMSIG(UWbuf));
1352 TagLogfileAct(pCE, "initcmd terminated");
1353 pCE->initpid = 0;
1354 StopInit(pCE);
1355 break;
1356 }
1357 if (pid == pCE->taskpid) {
1358 CONSCLIENT *pCL;
1359 if (WIFEXITED(UWbuf)) {
1360 Msg("[%s] task terminated: pid %lu: exit(%d)",
1361 pCE->server, pid, WEXITSTATUS(UWbuf));
1362 if (pCE->tasklog == FLAGTRUE)
1363 TagLogfile(pCE,
1364 "task terminated: pid %lu: exit(%d)",
1365 pid, WEXITSTATUS(UWbuf));
1366 /* tell all how it ended */
1367 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL;
1368 pCL = pCL->pCLnext) {
1369 if (pCL->fcon) {
1370 FilePrint(pCL->fd, FLAGFALSE,
1371 "[task terminated: exit(%d)]\r\n",
1372 WEXITSTATUS(UWbuf));
1373 }
1374 }
1375 }
1376 if (WIFSIGNALED(UWbuf)) {
1377 Msg("[%s] task terminated: pid %lu: signal(%d)",
1378 pCE->server, pid, WTERMSIG(UWbuf));
1379 if (pCE->tasklog == FLAGTRUE)
1380 TagLogfile(pCE,
1381 "task terminated: pid %lu: signal(%d)",
1382 pid, WTERMSIG(UWbuf));
1383 /* tell all how it ended */
1384 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL;
1385 pCL = pCL->pCLnext) {
1386 if (pCL->fcon) {
1387 FilePrint(pCL->fd, FLAGFALSE,
1388 "[task terminated: signal(%d)]\r\n",
1389 WTERMSIG(UWbuf));
1390 }
1391 }
1392 }
1393 pCE->taskpid = 0;
1394 StopTask(pCE);
1395 break;
1396 }
1397
1398 if (pid != pCE->ipid)
1399 continue;
1400
1401 if (WIFEXITED(UWbuf))
1402 Msg("[%s] exit(%d)", pCE->server, WEXITSTATUS(UWbuf));
1403 if (WIFSIGNALED(UWbuf))
1404 Msg("[%s] signal(%d)", pCE->server, WTERMSIG(UWbuf));
1405
1406 if (pCE->autoreinit != FLAGTRUE &&
1407 !(WIFEXITED(UWbuf) && WEXITSTATUS(UWbuf) == 0)) {
1408 ConsDown(pCE, FLAGTRUE, FLAGFALSE);
1409 } else {
1410 /* Try an initial reconnect */
1411 Msg("[%s] automatic reinitialization", pCE->server);
1412 ConsInit(pCE);
1413
1414 /* If we didn't succeed, try again later */
1415 if (!pCE->fup)
1416 pCE->autoReUp = 1;
1417 }
1418 break;
1419 }
1420 }
1421 }
1422
1423 int
CheckPasswd(CONSCLIENT * pCL,char * pw_string,FLAG empty_check)1424 CheckPasswd(CONSCLIENT *pCL, char *pw_string, FLAG empty_check)
1425 {
1426 FILE *fp;
1427 int iLine = 0;
1428 char *this_pw, *user;
1429
1430 #if USE_UNIX_DOMAIN_SOCKETS
1431 # if TRUST_UDS_CRED
1432 struct UDS_CRED_STYPE client_cred;
1433 socklen_t client_cred_len = sizeof(client_cred);
1434 memset(&client_cred, 0, sizeof(client_cred));
1435 int err;
1436
1437 if ((err =
1438 getsockopt(pCL->fd->fd, SOL_SOCKET, UDS_CRED_SO, &client_cred,
1439 &client_cred_len)) == -1) {
1440 Error("CheckPasswd(): getsockopt(CREDENTIALS) failed: %s",
1441 strerror(err));
1442 } else if (client_cred.pid != 0) {
1443 /* Does pCL->username->string and client_cred.uid match ? */
1444 struct passwd *pwd = (struct passwd *)0;
1445
1446 /* we allow root to impersonate anyone */
1447 if (client_cred.UDS_CRED_UID == 0)
1448 return AUTH_SUCCESS;
1449
1450 if ((pwd = getpwnam(pCL->username->string)) != NULL) {
1451 if (pwd->pw_uid == client_cred.UDS_CRED_UID) {
1452 Verbose("user %s authenticated", pCL->username->string);
1453 return AUTH_SUCCESS;
1454 }
1455 }
1456 }
1457 # endif
1458 #endif
1459
1460 if ((fp = fopen(config->passwdfile, "r")) == (FILE *)0) {
1461 if (CheckPass(pCL->username->string, pw_string, empty_check) ==
1462 AUTH_SUCCESS) {
1463 Verbose("user %s authenticated", pCL->acid->string);
1464 return AUTH_SUCCESS;
1465 }
1466 } else {
1467 char *wholeLine;
1468 static STRING *saveLine = (STRING *)0;
1469
1470 if (saveLine == (STRING *)0)
1471 saveLine = AllocString();
1472 BuildString((char *)0, saveLine);
1473
1474 while ((wholeLine = ReadLine(fp, saveLine, &iLine)) != (char *)0) {
1475 PruneSpace(wholeLine);
1476 /*printf("whole=<%s>\n", wholeLine); */
1477 if (wholeLine[0] == '\000')
1478 continue;
1479
1480 if ((char *)0 == (this_pw = strchr(wholeLine, ':'))) {
1481 Error("CheckPasswd(): %s(%d) bad password line `%s'",
1482 config->passwdfile, iLine, wholeLine);
1483 continue;
1484 }
1485 *this_pw++ = '\000';
1486 user = PruneSpace(wholeLine);
1487 this_pw = PruneSpace(this_pw);
1488
1489 if (strcmp(user, "*any*") != 0 &&
1490 strcmp(user, pCL->username->string) != 0)
1491 continue;
1492
1493 /* If one is empty and the other isn't, instant failure */
1494 if ((*this_pw == '\000' && *pw_string != '\000') ||
1495 (*this_pw != '\000' && *pw_string == '\000')) {
1496 break;
1497 }
1498
1499 if ((*this_pw == '\000' && *pw_string == '\000') ||
1500 ((strcmp(this_pw, "*passwd*") ==
1501 0) ? (CheckPass(pCL->username->string, pw_string,
1502 empty_check) ==
1503 AUTH_SUCCESS) : (strcmp(this_pw,
1504 crypt(pw_string,
1505 this_pw)) == 0))) {
1506 Verbose("user %s authenticated", pCL->acid->string);
1507 fclose(fp);
1508 return AUTH_SUCCESS;
1509 }
1510 break;
1511 }
1512 fclose(fp);
1513 }
1514
1515 return AUTH_INVALID;
1516 }
1517
1518 static char *
IdleTyme(long tyme)1519 IdleTyme(long tyme)
1520 {
1521 long hours, minutes;
1522 static STRING *timestr = (STRING *)0;
1523
1524 if (timestr == (STRING *)0)
1525 timestr = AllocString();
1526
1527 minutes = tyme / 60;
1528 hours = minutes / 60;
1529 minutes = minutes % 60;
1530
1531 BuildString((char *)0, timestr);
1532 if (hours < 24)
1533 BuildStringPrint(timestr, " %2ld:%02ld", hours, minutes);
1534 else if (hours < 24 * 2)
1535 BuildStringPrint(timestr, " 1 day");
1536 else if (hours < 24 * 10)
1537 BuildStringPrint(timestr, "%1ld days", hours / 24);
1538 else
1539 BuildStringPrint(timestr, "%2lddays", hours / 24);
1540
1541 return timestr->string;
1542 }
1543
1544 void
PutConsole(CONSENT * pCEServing,unsigned char c,int quote)1545 PutConsole(CONSENT *pCEServing, unsigned char c, int quote)
1546 {
1547 /* if we need to send an IAC char to a telnet-based port, quote
1548 * the thing (which means send two to the port). but, since we're
1549 * using IAC as a trigger for breaks and pauses, we have to load
1550 * two into the buffer here and two below (so two get sent).
1551 *
1552 * if we're just sending a IAC character in the raw, the 'quote'
1553 * flag will not be set and we'll put only two IAC chars into
1554 * the buffer (below)...which will result in one to the port.
1555 *
1556 * we're also tracking the first IAC character in the buffer with
1557 * the wbufIAC variable...that way we don't have to do a string
1558 * search every time we flush this thing ('cause it should be
1559 * rather infrequent to have an IAC char).
1560 *
1561 * quote == 0, raw - processed by conserver
1562 * quote == 1, console - processed by console
1563 * quote == 2, telnet - processed by telnet protocol
1564 * if console != telnet, 1 == 2
1565 */
1566 if (quote == 1 && pCEServing->type == HOST &&
1567 pCEServing->raw != FLAGTRUE && c == IAC) {
1568 BuildStringChar((char)c, pCEServing->wbuf);
1569 if (pCEServing->wbufIAC == 0)
1570 pCEServing->wbufIAC = pCEServing->wbuf->used;
1571 BuildStringChar((char)c, pCEServing->wbuf);
1572 }
1573 /* if we're trying to send an IAC char, quote it in the buffer */
1574 if (quote && c == IAC) {
1575 BuildStringChar((char)c, pCEServing->wbuf);
1576 if (pCEServing->wbufIAC == 0)
1577 pCEServing->wbufIAC = pCEServing->wbuf->used;
1578 }
1579 BuildStringChar((char)c, pCEServing->wbuf);
1580 if (c == IAC && pCEServing->wbufIAC == 0)
1581 pCEServing->wbufIAC = pCEServing->wbuf->used;
1582
1583 CONDDEBUG((1, "PutConsole(): queued byte to console %s",
1584 pCEServing->server));
1585 }
1586
1587 void
ExpandString(char * str,CONSENT * pCE,short breaknum)1588 ExpandString(char *str, CONSENT *pCE, short breaknum)
1589 {
1590 char s;
1591 short backslash = 0;
1592 short cntrl = 0;
1593 char oct = '\000';
1594 short octs = 0;
1595
1596 if (str == (char *)0 || pCE == (CONSENT *)0)
1597 return;
1598
1599 backslash = 0;
1600 cntrl = 0;
1601 while ((s = (*str++)) != '\000') {
1602 if (octs > 0 && octs < 3 && s >= '0' && s <= '7') {
1603 ++octs;
1604 oct = oct * 8 + (s - '0');
1605 continue;
1606 }
1607 if (octs != 0) {
1608 PutConsole(pCE, oct, 1);
1609 octs = 0;
1610 oct = '\000';
1611 }
1612 if (backslash) {
1613 backslash = 0;
1614 if (s == 'a')
1615 s = '\a';
1616 else if (s == 'b')
1617 s = '\b';
1618 else if (s == 'f')
1619 s = '\f';
1620 else if (s == 'n')
1621 s = '\n';
1622 else if (s == 'r')
1623 s = '\r';
1624 else if (s == 't')
1625 s = '\t';
1626 else if (s == 'v')
1627 s = '\v';
1628 else if (s == '^')
1629 s = '^';
1630 else if (s >= '0' && s <= '7') {
1631 ++octs;
1632 oct = oct * 8 + (s - '0');
1633 continue;
1634 } else if (s == 'd') {
1635 PutConsole(pCE, IAC, 0);
1636 PutConsole(pCE,
1637 '0' + breaknum + (breaknum >
1638 9 ? BREAKALPHAOFFSET : 0), 0);
1639 continue;
1640 } else if (s == 'z') {
1641 PutConsole(pCE, IAC, 0);
1642 PutConsole(pCE, BREAK, 0);
1643 continue;
1644 }
1645 PutConsole(pCE, s, 1);
1646 continue;
1647 }
1648 if (cntrl) {
1649 cntrl = 0;
1650 if (s == '?')
1651 s = 0x7f; /* delete */
1652 else
1653 s = s & 0x1f;
1654 PutConsole(pCE, s, 1);
1655 continue;
1656 }
1657 if (s == '\\') {
1658 backslash = 1;
1659 continue;
1660 }
1661 if (s == '^') {
1662 cntrl = 1;
1663 continue;
1664 }
1665 PutConsole(pCE, s, 1);
1666 }
1667
1668 if (octs != 0)
1669 PutConsole(pCE, oct, 1);
1670
1671 if (backslash)
1672 PutConsole(pCE, '\\', 1);
1673
1674 if (cntrl)
1675 PutConsole(pCE, '^', 1);
1676 }
1677
1678 void
SendBreak(CONSCLIENT * pCLServing,CONSENT * pCEServing,short bt)1679 SendBreak(CONSCLIENT *pCLServing, CONSENT *pCEServing, short bt)
1680 {
1681 CONSCLIENT *pCL;
1682
1683 short waszero = 0;
1684 if (bt < 0 || bt > BREAKLISTSIZE) {
1685 FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1);
1686 return;
1687 }
1688 if (bt == 0) {
1689 bt = pCEServing->breakNum;
1690 waszero = 1;
1691 }
1692 if (bt == 0 || breakList[bt - 1].seq->used <= 1) {
1693 FileWrite(pCLServing->fd, FLAGFALSE, "undefined]\r\n", -1);
1694 return;
1695 }
1696
1697 if (breakList[bt - 1].confirm == FLAGTRUE) {
1698 switch (pCLServing->confirmed) {
1699 case FLAGUNKNOWN:
1700 FileWrite(pCLServing->fd, FLAGFALSE, "confirm? [y/N] ",
1701 -1);
1702 pCLServing->confirmed = FLAGFALSE;
1703 pCLServing->cState = S_HALT1;
1704 pCLServing->iState = S_CONFIRM;
1705 pCLServing->cOption = '0' + bt;
1706 return;
1707 case FLAGFALSE:
1708 FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1);
1709 pCLServing->confirmed = FLAGUNKNOWN;
1710 return;
1711 case FLAGTRUE:
1712 pCLServing->confirmed = FLAGUNKNOWN;
1713 }
1714 }
1715
1716
1717 ExpandString(breakList[bt - 1].seq->string, pCEServing, bt);
1718
1719 FileWrite(pCLServing->fd, FLAGFALSE, "sent]\r\n", -1);
1720
1721 /* tell all who sent it */
1722 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
1723 pCL = pCL->pCLnext) {
1724 if (pCL == pCLServing)
1725 continue;
1726 if (pCL->fcon) {
1727 FilePrint(pCL->fd, FLAGFALSE, "[break sent by %s]\r\n",
1728 pCLServing->acid->string);
1729 }
1730 }
1731
1732 if (pCEServing->breaklog == FLAGTRUE) {
1733 if (waszero) {
1734 TagLogfile(pCEServing, "break #0(%c) sent -- `%s'",
1735 '0' + bt + (bt > 9 ? BREAKALPHAOFFSET : 0),
1736 breakList[bt - 1].seq->string);
1737 } else {
1738 TagLogfile(pCEServing, "break #%c sent -- `%s'",
1739 '0' + bt + (bt > 9 ? BREAKALPHAOFFSET : 0),
1740 breakList[bt - 1].seq->string);
1741 }
1742 }
1743 }
1744
1745 static int
StartTask(CONSENT * pCE,char * cmd,uid_t uid,gid_t gid)1746 StartTask(CONSENT *pCE, char *cmd, uid_t uid, gid_t gid)
1747 {
1748 int i;
1749 extern char **environ;
1750 char *pcShell, **ppcArgv;
1751 extern int FallBack(char **, int *);
1752 char *execSlave = (char *)0; /* pseudo-device slave side */
1753 int execSlaveFD; /* fd of slave side */
1754 int cofile;
1755
1756 if ((cofile = FallBack(&execSlave, &execSlaveFD)) == -1) {
1757 Error("[%s] StartTask(): failed to allocate pseudo-tty: %s",
1758 pCE->server, strerror(errno));
1759 return -1;
1760 }
1761 if (execSlave != (char *)0)
1762 free(execSlave);
1763
1764 fflush(stdout);
1765 fflush(stderr);
1766
1767 switch (pCE->taskpid = fork()) {
1768 case -1:
1769 pCE->taskpid = 0;
1770 return -1;
1771 case 0:
1772 break;
1773 default: /* server */
1774 close(execSlaveFD);
1775 if ((pCE->taskfile =
1776 FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) {
1777 close(cofile);
1778 Error("[%s] FileOpenFD(%d,simpleFile) failed", pCE->server,
1779 cofile);
1780 return -1;
1781 }
1782 Msg("[%s] task started: pid %lu", pCE->server,
1783 (unsigned long)pCE->taskpid);
1784 FD_SET(cofile, &rinit);
1785 if (maxfd < cofile + 1)
1786 maxfd = cofile + 1;
1787 fflush(stderr);
1788 return 0;
1789 }
1790
1791 close(cofile);
1792
1793 #if HAVE_SETSID
1794 setsid();
1795 #else
1796 tcsetpgrp(0, getpid());
1797 #endif
1798
1799 close(1);
1800 if (dup(execSlaveFD) != 1) {
1801 Error("[%s] StartTask(): fd 1 sync error", pCE->server);
1802 exit(EX_OSERR);
1803 }
1804 close(2);
1805 if (dup(execSlaveFD) != 2) {
1806 exit(EX_OSERR);
1807 }
1808 /* no stdin */
1809 close(0);
1810 close(execSlaveFD);
1811
1812 /* setup new process with clean file descriptors
1813 */
1814 #if HAVE_CLOSEFROM
1815 closefrom(3);
1816 #else
1817 i = GetMaxFiles();
1818 for ( /* i above */ ; --i > 2;) {
1819 close(i);
1820 }
1821 #endif
1822
1823 if (geteuid() == 0) {
1824 if (gid != 0)
1825 setgid(gid);
1826 if (uid != 0)
1827 setuid(uid);
1828 }
1829
1830 SetupTty(pCE, 1);
1831
1832 pcShell = "/bin/sh";
1833 static char *apcArgv[] = {
1834 "/bin/sh", "-ce", (char *)0, (char *)0
1835 };
1836
1837 apcArgv[2] = cmd;
1838 ppcArgv = apcArgv;
1839
1840 execve(pcShell, ppcArgv, environ);
1841 Error("[%s] execve(): %s", pCE->server, strerror(errno));
1842 exit(EX_OSERR);
1843 return -1;
1844 }
1845
1846 void
InvokeTask(CONSCLIENT * pCLServing,CONSENT * pCEServing,char id)1847 InvokeTask(CONSCLIENT *pCLServing, CONSENT *pCEServing, char id)
1848 {
1849 TASKS *t = (TASKS *)0;
1850 char *cmd;
1851
1852 if ((id < '0' || id > '9') && (id < 'a' || id > 'z')) {
1853 FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1);
1854 return;
1855 }
1856
1857 if (pCEServing->taskpid != 0) {
1858 FileWrite(pCLServing->fd, FLAGFALSE, "aborted - task running]\r\n",
1859 -1);
1860 return;
1861 }
1862
1863 if ((char *)0 != strchr(pCEServing->tasklist, id) ||
1864 (char *)0 != strchr(pCEServing->tasklist, '*')) {
1865 for (t = taskList; t != (TASKS *)0; t = t->next) {
1866 if (t->id == id)
1867 break;
1868 }
1869 }
1870
1871 if (t == (TASKS *)0) {
1872 FileWrite(pCLServing->fd, FLAGFALSE, "undefined]\r\n", -1);
1873 return;
1874 }
1875
1876 if (t->confirm == FLAGTRUE) {
1877 switch (pCLServing->confirmed) {
1878 case FLAGUNKNOWN:
1879 FileWrite(pCLServing->fd, FLAGFALSE, "confirm? [y/N] ",
1880 -1);
1881 pCLServing->confirmed = FLAGFALSE;
1882 pCLServing->cState = S_TASK;
1883 pCLServing->iState = S_CONFIRM;
1884 pCLServing->cOption = id;
1885 return;
1886 case FLAGFALSE:
1887 FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1);
1888 pCLServing->confirmed = FLAGUNKNOWN;
1889 return;
1890 case FLAGTRUE:
1891 pCLServing->confirmed = FLAGUNKNOWN;
1892 }
1893 }
1894
1895 if ((cmd = StrDup(t->cmd->string)) == (char *)0)
1896 OutOfMem();
1897
1898 if (t->subst != (char *)0) {
1899 substData->data = (void *)pCEServing;
1900 ProcessSubst(substData, &cmd, (char **)0, (char *)0, t->subst);
1901 }
1902
1903 if (StartTask(pCEServing, cmd, t->uid, t->gid) == 0) {
1904 CONSCLIENT *pCL;
1905 char *detail;
1906
1907 FilePrint(pCLServing->fd, FLAGFALSE, "`%c' started]\r\n", id);
1908 detail = t->descr->used > 1 ? t->descr->string : cmd;
1909
1910 /* tell all who started it */
1911 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
1912 pCL = pCL->pCLnext) {
1913 if (pCL == pCLServing)
1914 continue;
1915 if (pCL->fcon) {
1916 FilePrint(pCL->fd, FLAGFALSE,
1917 "[task `%s' started by %s]\r\n", detail,
1918 pCLServing->acid->string);
1919 }
1920 }
1921
1922 if (pCEServing->tasklog == FLAGTRUE) {
1923 TagLogfile(pCEServing, "task started: pid %lu -- `%s'",
1924 pCEServing->taskpid, cmd);
1925 }
1926 } else {
1927 FileWrite(pCLServing->fd, FLAGFALSE, "failure]\r\n", -1);
1928 }
1929 free(cmd);
1930 }
1931
1932 #if HAVE_OPENSSL
1933 int
AttemptSSL(CONSCLIENT * pCL)1934 AttemptSSL(CONSCLIENT *pCL)
1935 {
1936 int fdnum;
1937 SSL *ssl;
1938 int retval;
1939
1940 fdnum = FileFDNum(pCL->fd);
1941 if (ctx == (SSL_CTX *)0) {
1942 Error("AttemptSSL(): WTF? The SSL context disappeared?!?!?");
1943 Bye(EX_SOFTWARE);
1944 }
1945 if (!(ssl = SSL_new(ctx))) {
1946 Error("AttemptSSL(): SSL_new() failed for fd %d", fdnum);
1947 return 0;
1948 }
1949 FileSetSSL(pCL->fd, ssl);
1950 SSL_set_accept_state(ssl);
1951 SSL_set_fd(ssl, fdnum);
1952
1953 if ((retval = FileSSLAccept(pCL->fd)) < 0) {
1954 Error("AttemptSSL(): FileSSLAccept() failed for fd %d", fdnum);
1955 return 0;
1956 } else if (retval == 0)
1957 pCL->ioState = INSSLACCEPT;
1958 return 1;
1959 }
1960 #endif
1961
1962 #if HAVE_GSSAPI
1963 int
AttemptGSSAPI(CONSCLIENT * pCL)1964 AttemptGSSAPI(CONSCLIENT *pCL)
1965 {
1966 int nr, ret = 0;
1967 char buf[1024];
1968 gss_buffer_desc sendtok, recvtok, dbuf;
1969 gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT;
1970 OM_uint32 stmaj, stmin, mctx, dmin;
1971 gss_name_t user = 0;
1972
1973 if ((nr = FileRead(pCL->fd, buf, sizeof(buf))) <= 0) {
1974 return nr;
1975 }
1976 recvtok.value = buf;
1977 recvtok.length = nr;
1978
1979 stmaj =
1980 gss_accept_sec_context(&stmin, &gssctx, gss_mycreds, &recvtok,
1981 NULL, &user, NULL, &sendtok, NULL, NULL,
1982 NULL);
1983 switch (stmaj) {
1984 case GSS_S_COMPLETE:
1985 FileSetQuoteIAC(pCL->fd, FLAGFALSE);
1986 FileWrite(pCL->fd, FLAGFALSE, sendtok.value, sendtok.length);
1987 FileSetQuoteIAC(pCL->fd, FLAGTRUE);
1988 pCL->iState = S_NORMAL;
1989 gss_release_buffer(&stmin, &sendtok);
1990 BuildString((char *)0, pCL->username);
1991 BuildString((char *)0, pCL->acid);
1992 stmaj = gss_display_name(&stmin, user, &dbuf, NULL);
1993
1994 BuildStringN(dbuf.value, dbuf.length, pCL->username);
1995 BuildStringN(dbuf.value, dbuf.length, pCL->acid);
1996 BuildStringChar('@', pCL->acid);
1997 BuildString(pCL->peername->string, pCL->acid);
1998 gss_release_name(&stmin, &user);
1999 gss_release_buffer(&stmin, &dbuf);
2000 ret = 1;
2001 break;
2002 case GSS_S_CREDENTIALS_EXPIRED:
2003 /* reacquire creds and try again */
2004 Error("Credentials expired");
2005 break;
2006 default:
2007 gss_display_status(&dmin, stmaj, GSS_C_GSS_CODE,
2008 GSS_C_NULL_OID, &mctx, &dbuf);
2009 Error("GSSAPI didn't work, %*s", dbuf.length, dbuf.value);
2010 ret = -1;
2011 }
2012 return ret;
2013 }
2014 #endif
2015
2016 CONSENT *
HuntForConsole(GRPENT * pGE,char * name)2017 HuntForConsole(GRPENT *pGE, char *name)
2018 {
2019 /* try to find a given console
2020 * we assume all the right checks for ambiguity
2021 * were already done by the master process, so
2022 * the first match should be what the user wants
2023 */
2024 CONSENT *pCE = (CONSENT *)0;
2025
2026 if (name == (char *)0)
2027 return pCE;
2028
2029 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
2030 NAMES *n = (NAMES *)0;
2031 if (strcasecmp(name, pCE->server) == 0)
2032 break;
2033 for (n = pCE->aliases; n != (NAMES *)0; n = n->next) {
2034 if (strcasecmp(name, n->name) == 0)
2035 break;
2036 }
2037 if (n != (NAMES *)0)
2038 break;
2039 }
2040 if (pCE == (CONSENT *)0 && config->autocomplete == FLAGTRUE) {
2041 NAMES *n = (NAMES *)0;
2042 int len = strlen(name);
2043 for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
2044 if (strncasecmp(name, pCE->server, len) == 0)
2045 break;
2046 for (n = pCE->aliases; n != (NAMES *)0; n = n->next) {
2047 if (strncasecmp(name, n->name, len) == 0)
2048 break;
2049 }
2050 if (n != (NAMES *)0)
2051 break;
2052 }
2053 }
2054 return pCE;
2055 }
2056
2057 void
CommandAttach(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2058 CommandAttach(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2059 long tyme)
2060 {
2061 CONSCLIENT *pCL;
2062
2063 ClientWantsWrite(pCLServing);
2064
2065 if (pCEServing->fronly) {
2066 FileWrite(pCLServing->fd, FLAGFALSE, "console is read-only]\r\n",
2067 -1);
2068 } else if (pCLServing->fro) {
2069 FileWrite(pCLServing->fd, FLAGFALSE, "read-only]\r\n", -1);
2070 } else if ((CONSCLIENT *)0 == (pCL = pCEServing->pCLwr)) {
2071 pCEServing->pCLwr = pCLServing;
2072 pCLServing->fwr = 1;
2073 if (pCEServing->nolog) {
2074 FileWrite(pCLServing->fd, FLAGFALSE,
2075 "attached (nologging)]\r\n", -1);
2076 } else {
2077 FileWrite(pCLServing->fd, FLAGFALSE, "attached]\r\n", -1);
2078 }
2079 TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string);
2080 } else if (pCL == pCLServing) {
2081 if (pCEServing->nolog) {
2082 FileWrite(pCLServing->fd, FLAGFALSE, "ok (nologging)]\r\n",
2083 -1);
2084 } else {
2085 FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1);
2086 }
2087 } else {
2088 FilePrint(pCLServing->fd, FLAGFALSE, "no, %s is attached]\r\n",
2089 pCL->acid->string);
2090 }
2091 }
2092
2093 void
CommandChangeFlow(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2094 CommandChangeFlow(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2095 long tyme)
2096 {
2097 struct termios sbuf;
2098 int cofile;
2099
2100 if (!pCLServing->fwr) {
2101 FileWrite(pCLServing->fd, FLAGFALSE, "attach to change flow]\r\n",
2102 -1);
2103 return;
2104 }
2105 if (pCEServing->type != DEVICE && pCEServing->type != EXEC) {
2106 FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1);
2107 return;
2108 }
2109 cofile = FileFDNum(pCEServing->cofile);
2110 if (-1 == tcgetattr(cofile, &sbuf)) {
2111 FileWrite(pCLServing->fd, FLAGFALSE, "failed]\r\n", -1);
2112 return;
2113 }
2114 if (0 != (sbuf.c_iflag & IXON)) {
2115 sbuf.c_iflag &= ~(IXON);
2116 } else {
2117 sbuf.c_iflag |= IXON;
2118 }
2119 if (-1 == tcsetattr(cofile, TCSANOW, &sbuf)) {
2120 FileWrite(pCLServing->fd, FLAGFALSE, "failed]\r\n", -1);
2121 return;
2122 }
2123 if ((sbuf.c_iflag & IXON) == 0) {
2124 FileWrite(pCLServing->fd, FLAGFALSE, "ixon OFF]\r\n", -1);
2125 } else {
2126 FileWrite(pCLServing->fd, FLAGFALSE, "ixon ON]\r\n", -1);
2127 }
2128 }
2129
2130 void
CommandDown(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2131 CommandDown(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2132 long tyme)
2133 {
2134 CONSCLIENT *pCL;
2135
2136 if (!pCLServing->fwr) {
2137 FileWrite(pCLServing->fd, FLAGFALSE, "attach to down line]\r\n",
2138 -1);
2139 return;
2140 }
2141 if (!pCEServing->fup) {
2142 FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1);
2143 return;
2144 }
2145
2146 ConsDown(pCEServing, FLAGFALSE, FLAGFALSE);
2147 FileWrite(pCLServing->fd, FLAGFALSE, "line down]\r\n", -1);
2148
2149 /* tell all who closed it */
2150 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
2151 pCL = pCL->pCLnext) {
2152 if (pCL == pCLServing)
2153 continue;
2154 if (pCL->fcon) {
2155 FilePrint(pCL->fd, FLAGFALSE, "[line down by %s]\r\n",
2156 pCLServing->acid->string);
2157 }
2158 }
2159 }
2160
2161 void
CommandExamine(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme,char * args)2162 CommandExamine(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2163 long tyme, char *args)
2164 {
2165 CONSENT *pCE;
2166
2167 if (args == (char *)0)
2168 pCE = pGE->pCElist;
2169 else
2170 pCE = HuntForConsole(pGE, args);
2171
2172 for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
2173 char *d = (char *)0;
2174 char *b = (char *)0;
2175 char p = '\000';
2176 switch (pCE->type) {
2177 case EXEC:
2178 d = pCE->execSlave;
2179 b = "Local";
2180 p = ' ';
2181 break;
2182 case DEVICE:
2183 d = BuildTmpStringPrint("%s@%s", pCE->device, pCE->master);
2184 b = pCE->baud->acrate;
2185 p = pCE->parity->key[0];
2186 break;
2187 #if HAVE_FREEIPMI
2188 case IPMI:
2189 d = BuildTmpStringPrint("%s", pCE->host);
2190 b = "IPMI";
2191 p = ' ';
2192 break;
2193 #endif
2194 case HOST:
2195 d = BuildTmpStringPrint("%s/%hu", pCE->host, pCE->netport);
2196 b = "Netwk";
2197 p = ' ';
2198 break;
2199 case NOOP:
2200 d = "NOOP";
2201 b = "NOOP";
2202 p = ' ';
2203 break;
2204 case UDS:
2205 d = pCE->uds;
2206 b = "UDS";
2207 p = ' ';
2208 break;
2209 case UNKNOWNTYPE: /* shut up gcc */
2210 break;
2211 }
2212 FilePrint(pCLServing->fd, FLAGFALSE,
2213 " %-24.24s on %-32.32s at %6.6s%c\r\n", pCE->server, d,
2214 b, p);
2215 if (args != (char *)0)
2216 break;
2217 }
2218 }
2219
2220 void
CommandForce(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2221 CommandForce(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2222 long tyme)
2223 {
2224 CONSCLIENT *pCL;
2225
2226 ClientWantsWrite(pCLServing);
2227
2228 if (pCLServing->fro) {
2229 FileWrite(pCLServing->fd, FLAGFALSE, "read-only]\r\n", -1);
2230 return;
2231 } else if (pCEServing->fronly) {
2232 FileWrite(pCLServing->fd, FLAGFALSE, "console is read-only]\r\n",
2233 -1);
2234 return;
2235 }
2236 if ((CONSCLIENT *)0 != (pCL = pCEServing->pCLwr)) {
2237 if (pCL == pCLServing) {
2238 if (pCEServing->nolog) {
2239 FileWrite(pCLServing->fd, FLAGFALSE, "ok (nologging)]\r\n",
2240 -1);
2241 } else {
2242 FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1);
2243 }
2244 return;
2245 }
2246 if (pCEServing->nolog) {
2247 FilePrint(pCLServing->fd, FLAGFALSE,
2248 "bumped %s (nologging)]\r\n", pCL->acid->string);
2249 } else {
2250 FilePrint(pCLServing->fd, FLAGFALSE, "bumped %s]\r\n",
2251 pCL->acid->string);
2252 }
2253 AbortAnyClientExec(pCL);
2254 BumpClient(pCEServing, (char *)0);
2255 ClientWantsWrite(pCL);
2256 if (pCL->fcon)
2257 FilePrint(pCL->fd, FLAGFALSE,
2258 "\r\n[forced to `spy' mode by %s]\r\n",
2259 pCLServing->acid->string);
2260 TagLogfileAct(pCEServing, "%s bumped %s", pCLServing->acid->string,
2261 pCL->acid->string);
2262 } else {
2263 if (pCEServing->nolog) {
2264 FileWrite(pCLServing->fd, FLAGFALSE,
2265 "attached (nologging)]\r\n", -1);
2266 } else {
2267 FileWrite(pCLServing->fd, FLAGFALSE, "attached]\r\n", -1);
2268 }
2269 TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string);
2270 }
2271 pCEServing->pCLwr = pCLServing;
2272 pCLServing->fwr = 1;
2273 }
2274
2275 void
CommandGroup(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme,char * args)2276 CommandGroup(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2277 long tyme, char *args)
2278 {
2279 CONSCLIENT *pCL;
2280 CONSENT *pCE;
2281
2282 pCE = HuntForConsole(pGE, args);
2283
2284 /* we do not show the ctl console
2285 * else we'd get the client always
2286 */
2287 for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) {
2288 if (pGE->pCEctl == pCL->pCEto)
2289 continue;
2290 if (pCE != (CONSENT *)0 && pCL->pCEto != pCE)
2291 continue;
2292 FilePrint(pCLServing->fd, FLAGFALSE,
2293 " %-32.32s %c %-7.7s %6s %s\r\n", pCL->acid->string,
2294 pCL == pCLServing ? '*' : ' ',
2295 pCL->fcon ? (pCL->fwr ? "attach" : "spy") : "stopped",
2296 IdleTyme(tyme - pCL->typetym), pCL->pCEto->server);
2297 }
2298 }
2299
2300 void
CommandHosts(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme,char * args)2301 CommandHosts(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2302 long tyme, char *args)
2303 {
2304 CONSENT *pCE;
2305
2306 if (args == (char *)0)
2307 pCE = pGE->pCElist;
2308 else
2309 pCE = HuntForConsole(pGE, args);
2310
2311 for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
2312 FilePrint(pCLServing->fd, FLAGFALSE,
2313 " %-24.24s %c %-4.4s %-.40s\r\n", pCE->server,
2314 pCE == pCEServing ? '*' : ' ', (pCE->fup &&
2315 pCE->ioState == ISNORMAL)
2316 ? (pCE->initfile == (CONSFILE *)
2317 0 ? "up" : "init") : "down",
2318 pCE->pCLwr ? pCE->pCLwr->acid->
2319 string : pCE->pCLon ? "<spies>" : "<none>");
2320 if (args != (char *)0)
2321 break;
2322 }
2323 }
2324
2325 void
CommandInfo(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme,char * args)2326 CommandInfo(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2327 long tyme, char *args)
2328 {
2329 CONSENT *pCE;
2330 CONSCLIENT *pCL;
2331
2332 if (args == (char *)0)
2333 pCE = pGE->pCElist;
2334 else
2335 pCE = HuntForConsole(pGE, args);
2336
2337 for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) {
2338 int comma = 0;
2339 char *s = (char *)0;
2340 FilePrint(pCLServing->fd, FLAGTRUE, "%s:%s,%lu,%hu:", pCE->server,
2341 myHostname, (unsigned long)thepid, pGE->port);
2342 switch (pCE->type) {
2343 case EXEC:
2344 FilePrint(pCLServing->fd, FLAGTRUE, "|:%s,%lu,%s,%d:",
2345 (pCE->exec != (char *)0 ? pCE->exec : "/bin/sh"),
2346 (unsigned long)pCE->ipid, pCE->execSlave,
2347 FileFDNum(pCE->cofile));
2348 break;
2349 #if HAVE_FREEIPMI
2350 case IPMI:
2351 FilePrint(pCLServing->fd, FLAGTRUE, "@:%s,%d:", pCE->host,
2352 FileFDNum(pCE->cofile));
2353 break;
2354 #endif
2355 case HOST:
2356 FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu,%s,%d:",
2357 pCE->host, pCE->netport,
2358 (pCE->raw == FLAGTRUE ? "raw" : "telnet"),
2359 FileFDNum(pCE->cofile));
2360 break;
2361 case NOOP:
2362 FileWrite(pCLServing->fd, FLAGTRUE, "#::", 3);
2363 break;
2364 case UDS:
2365 FilePrint(pCLServing->fd, FLAGTRUE, "%%:%s,%d:", pCE->uds,
2366 FileFDNum(pCE->cofile));
2367 break;
2368 case DEVICE:
2369 FilePrint(pCLServing->fd, FLAGTRUE, "/:%s,%s%c,%d:",
2370 pCE->device,
2371 (pCE->baud ? pCE->baud->acrate : ""),
2372 (pCE->parity ? pCE->parity->key[0] : ' '),
2373 FileFDNum(pCE->cofile));
2374 break;
2375 case UNKNOWNTYPE: /* shut up gcc */
2376 break;
2377 }
2378 if (pCE->pCLwr) {
2379 FilePrint(pCLServing->fd, FLAGTRUE, "w@%s@%ld",
2380 pCE->pCLwr->acid->string,
2381 tyme - pCE->pCLwr->typetym);
2382 comma = 1;
2383 }
2384
2385 for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) {
2386 if (pCL == pCE->pCLwr)
2387 continue;
2388 if (comma)
2389 FilePrint(pCLServing->fd, FLAGTRUE, ",");
2390 if (pCL->fcon)
2391 FilePrint(pCLServing->fd, FLAGTRUE, "r@%s@%ld@%s",
2392 pCL->acid->string, tyme - pCL->typetym,
2393 (pCL->fwantwr && !pCL->fro) ? "rw" : "ro");
2394 else
2395 FilePrint(pCLServing->fd, FLAGTRUE, "s@%s@%ld@%s",
2396 pCL->acid->string, tyme - pCL->typetym,
2397 (pCL->fwantwr && !pCL->fro) ? "rw" : "ro");
2398 comma = 1;
2399 }
2400
2401 FilePrint(pCLServing->fd, FLAGTRUE,
2402 ":%s:%s:%s,%s,%s,%s,%s,%d,%d:%d:%s:",
2403 ((pCE->fup &&
2404 pCE->ioState == ISNORMAL) ? (pCE->initfile ==
2405 (CONSFILE *)0 ? "up" :
2406 "init")
2407 : "down"), (pCE->fronly ? "ro" : "rw"),
2408 (pCE->logfile == (char *)0 ? "" : pCE->logfile),
2409 (pCE->nolog ? "nolog" : "log"),
2410 (pCE->activitylog == FLAGTRUE ? "act" : "noact"),
2411 (pCE->breaklog == FLAGTRUE ? "brk" : "nobrk"),
2412 (pCE->tasklog == FLAGTRUE ? "task" : "notask"),
2413 pCE->mark, (pCE->fdlog ? pCE->fdlog->fd : -1),
2414 pCE->breakNum, (pCE->autoReUp ? "autoup" : "noautoup"));
2415 if (pCE->aliases != (NAMES *)0) {
2416 NAMES *n;
2417 comma = 0;
2418 for (n = pCE->aliases; n != (NAMES *)0; n = n->next) {
2419 if (comma)
2420 FilePrint(pCLServing->fd, FLAGTRUE, ",");
2421 FilePrint(pCLServing->fd, FLAGTRUE, "%s", n->name);
2422 comma = 1;
2423 }
2424 }
2425 BuildTmpString((char *)0);
2426 s = (char *)0;
2427 if (pCE->type == DEVICE) {
2428 if (pCE->hupcl == FLAGTRUE)
2429 s = BuildTmpString(",hupcl");
2430 if (pCE->cstopb == FLAGTRUE)
2431 s = BuildTmpString(",cstopb");
2432 #if defined(CRTSCTS)
2433 if (pCE->crtscts == FLAGTRUE)
2434 s = BuildTmpString(",crtscts");
2435 #endif
2436 }
2437 if (pCE->type == DEVICE || pCE->type == EXEC) {
2438 if (pCE->ixon == FLAGTRUE)
2439 s = BuildTmpString(",ixon");
2440 if (pCE->ixany == FLAGTRUE)
2441 s = BuildTmpString(",ixany");
2442 if (pCE->ixoff == FLAGTRUE)
2443 s = BuildTmpString(",ixoff");
2444 }
2445 if (pCE->ondemand == FLAGTRUE)
2446 s = BuildTmpString(",ondemand");
2447 if (pCE->reinitoncc == FLAGTRUE)
2448 s = BuildTmpString(",reinitoncc");
2449 if (pCE->striphigh == FLAGTRUE)
2450 s = BuildTmpString(",striphigh");
2451 if (pCE->autoreinit == FLAGTRUE)
2452 s = BuildTmpString(",autoreinit");
2453 if (pCE->unloved == FLAGTRUE)
2454 s = BuildTmpString(",unloved");
2455 if (pCE->login == FLAGTRUE)
2456 s = BuildTmpString(",login");
2457 FilePrint(pCLServing->fd, FLAGFALSE, ":%s:%s:%d:%s\r\n",
2458 (s == (char *)0 ? "" : s + 1),
2459 (pCE->initcmd == (char *)0 ? "" : pCE->initcmd),
2460 pCE->idletimeout,
2461 (pCE->idlestring == (char *)0 ? "" : pCE->idlestring));
2462 BuildTmpString((char *)0);
2463 if (args != (char *)0)
2464 break;
2465 }
2466 }
2467
2468 void
CommandLogging(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2469 CommandLogging(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2470 long tyme)
2471 {
2472 if (pCLServing->fwr) {
2473 pCEServing->nolog = !pCEServing->nolog;
2474 if (pCEServing->nolog) {
2475 FileWrite(pCLServing->fd, FLAGFALSE, "logging off]\r\n", -1);
2476 TagLogfile(pCEServing, "Console logging disabled by %s",
2477 pCLServing->acid->string);
2478 } else {
2479 FileWrite(pCLServing->fd, FLAGFALSE, "logging on]\r\n", -1);
2480 TagLogfile(pCEServing, "Console logging restored by %s",
2481 pCLServing->acid->string);
2482 }
2483 } else {
2484 FilePrint(pCLServing->fd, FLAGFALSE,
2485 "attach to toggle logging]\r\n");
2486 }
2487 }
2488
2489 void
CommandOpen(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2490 CommandOpen(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2491 long tyme)
2492 {
2493 CONSCLIENT *pCL;
2494
2495 if (!pCLServing->fwr) {
2496 FileWrite(pCLServing->fd, FLAGFALSE, "attach to reopen]\r\n", -1);
2497 return;
2498 }
2499 /* with a close/re-open we might
2500 * change fd's
2501 */
2502 ConsInit(pCEServing);
2503 if (pCEServing->fup &&
2504 (pCEServing->initfile != (CONSFILE *)0 ||
2505 pCEServing->ioState == INCONNECT)) {
2506 FileWrite(pCLServing->fd, FLAGFALSE, "connecting...", -1);
2507 pCLServing->fiwait = 1;
2508 } else if (pCEServing->fronly) {
2509 FilePrint(pCLServing->fd, FLAGFALSE, "%s -- read-only]\r\n",
2510 pCEServing->fup ? "up" : "down");
2511 } else if ((CONSCLIENT *)0 == (pCL = pCEServing->pCLwr)) {
2512 pCEServing->pCLwr = pCLServing;
2513 pCLServing->fwr = 1;
2514 FilePrint(pCLServing->fd, FLAGFALSE, "%s -- attached]\r\n",
2515 pCEServing->fup ? "up" : "down");
2516 TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string);
2517 } else if (pCL == pCLServing) {
2518 FilePrint(pCLServing->fd, FLAGFALSE, "%s]\r\n",
2519 pCEServing->fup ? "up" : "down");
2520 TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string);
2521 } else {
2522 FilePrint(pCLServing->fd, FLAGFALSE, "%s, %s is attached]\r\n",
2523 pCEServing->fup ? "up" : "down", pCL->acid->string);
2524 }
2525 }
2526
2527 void
CommandWho(GRPENT * pGE,CONSCLIENT * pCLServing,CONSENT * pCEServing,long tyme)2528 CommandWho(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing,
2529 long tyme)
2530 {
2531 CONSCLIENT *pCL;
2532
2533 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
2534 pCL = pCL->pCLnext) {
2535 FilePrint(pCLServing->fd, FLAGFALSE,
2536 " %-32.32s %c %-7.7s %6s %s\r\n", pCL->acid->string,
2537 pCL == pCLServing ? '*' : ' ',
2538 pCL->fcon ? (pCL->fwr ? "attach" : "spy") : "stopped",
2539 IdleTyme(tyme - pCL->typetym), pCL->actym);
2540 }
2541 }
2542
2543 char *
TelOpt(int o)2544 TelOpt(int o)
2545 {
2546 static char opt[128];
2547 char *telopts[] = {
2548 "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME",
2549 "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP",
2550 "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS",
2551 "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
2552 "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT",
2553 "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
2554 "TACACS UID", "OUTPUT MARKING", "TTYLOC",
2555 "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW",
2556 "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION",
2557 "ENCRYPT", "NEW-ENVIRON"
2558 };
2559
2560 if (o < sizeof(telopts) / sizeof(char *))
2561 return telopts[o];
2562 else {
2563 sprintf(opt, "%d", o);
2564 return opt;
2565 }
2566 }
2567
2568 void
DoConsoleRead(CONSENT * pCEServing)2569 DoConsoleRead(CONSENT *pCEServing)
2570 {
2571 unsigned char acIn[BUFSIZ], acInOrig[BUFSIZ];
2572 int nr, i;
2573 CONSCLIENT *pCL;
2574
2575 int cofile = FileFDNum(pCEServing->cofile);
2576
2577 if (!pCEServing->fup) {
2578 FD_CLR(cofile, &rinit);
2579 FD_CLR(cofile, &winit);
2580 return;
2581 }
2582
2583 /* read terminal line */
2584 if ((nr =
2585 FileRead(pCEServing->cofile, acInOrig, sizeof(acInOrig))) < 0) {
2586 Error("[%s] read failure: unexpected EOF", pCEServing->server);
2587 ConsoleError(pCEServing);
2588 return;
2589 }
2590 CONDDEBUG((1, "DoConsoleRead(): read %d bytes from fd %d", nr,
2591 cofile));
2592
2593 if (nr > 0) {
2594 pCEServing->lastWrite = time((time_t *)0);
2595 if (pCEServing->idletimeout != (time_t)0 &&
2596 (timers[T_CIDLE] == (time_t)0 ||
2597 timers[T_CIDLE] >
2598 pCEServing->lastWrite + pCEServing->idletimeout))
2599 timers[T_CIDLE] =
2600 pCEServing->lastWrite + pCEServing->idletimeout;
2601 }
2602
2603 if (pCEServing->type == HOST && pCEServing->raw != FLAGTRUE) {
2604 /* Do a little Telnet Protocol interpretation
2605 * state = 0: normal
2606 * = 1: Saw a IAC char
2607 * = 2: Saw a DONT/WONT command
2608 * = 3: Saw a WILL command
2609 * = 4: Saw a DO command
2610 * = 5: Saw a \r
2611 */
2612 int new = 0, state;
2613 state = pCEServing->telnetState;
2614 for (i = 0; i < nr; ++i) {
2615 if (state == 0 && acInOrig[i] == IAC) {
2616 CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet `IAC'",
2617 pCEServing->server));
2618 state = 1;
2619 } else if (state == 1 && acInOrig[i] != IAC) {
2620 if (acInOrig[i] == WILL) {
2621 state = 3;
2622 CONDDEBUG((1,
2623 "DoConsoleRead(): [%s] got telnet cmd `WILL'",
2624 pCEServing->server));
2625 } else if (acInOrig[i] == DO) {
2626 state = 4;
2627 CONDDEBUG((1,
2628 "DoConsoleRead(): [%s] got telnet cmd `DO'",
2629 pCEServing->server));
2630 } else if (acInOrig[i] == DONT) {
2631 state = 2;
2632 CONDDEBUG((1,
2633 "DoConsoleRead(): [%s] got telnet cmd `DONT'",
2634 pCEServing->server));
2635 } else if (acInOrig[i] == WONT) {
2636 state = 2;
2637 CONDDEBUG((1,
2638 "DoConsoleRead(): [%s] got telnet cmd `WONT'",
2639 pCEServing->server));
2640 } else {
2641 CONDDEBUG((1,
2642 "DoConsoleRead(): [%s] got telnet cmd `%u'",
2643 pCEServing->server, acInOrig[i]));
2644 state = 0;
2645 }
2646 } else if (state == 2) {
2647 CONDDEBUG((1,
2648 "DoConsoleRead(): [%s] got telnet option `%s'",
2649 pCEServing->server, TelOpt(acInOrig[i])));
2650 state = 0;
2651 } else if (state == 3) {
2652 CONDDEBUG((1,
2653 "DoConsoleRead(): [%s] got telnet option `%s'",
2654 pCEServing->server, TelOpt(acInOrig[i])));
2655 if ((acInOrig[i] == TELOPT_ECHO &&
2656 pCEServing->sentDoEcho != FLAGTRUE) ||
2657 (acInOrig[i] == TELOPT_SGA &&
2658 pCEServing->sentDoSGA != FLAGTRUE)) {
2659 PutConsole(pCEServing, IAC, 2);
2660 PutConsole(pCEServing, DO, 2);
2661 PutConsole(pCEServing, acInOrig[i], 2);
2662 CONDDEBUG((1,
2663 "DoConsoleRead(): [%s] sent telnet DO `%s'",
2664 pCEServing->server, TelOpt(acInOrig[i])));
2665 if (acInOrig[i] == TELOPT_ECHO) {
2666 pCEServing->sentDoEcho = FLAGTRUE;
2667 } else {
2668 pCEServing->sentDoSGA = FLAGTRUE;
2669 }
2670 }
2671 state = 0;
2672 } else if (state == 4) {
2673 CONDDEBUG((1,
2674 "DoConsoleRead(): [%s] got telnet option `%s'",
2675 pCEServing->server, TelOpt(acInOrig[i])));
2676 PutConsole(pCEServing, IAC, 2);
2677 PutConsole(pCEServing, WONT, 2);
2678 PutConsole(pCEServing, acInOrig[i], 2);
2679 CONDDEBUG((1,
2680 "DoConsoleRead(): [%s] sent telnet WONT `%s'",
2681 pCEServing->server, TelOpt(acInOrig[i])));
2682 state = 0;
2683 } else {
2684 if (state == 5) {
2685 state = 0;
2686 if (acInOrig[i] == '\000')
2687 continue;
2688 }
2689 if (acInOrig[i] == IAC)
2690 CONDDEBUG((1, "DoConsoleRead(): [%s] quoted `IAC'",
2691 pCEServing->server));
2692 if (pCEServing->striphigh == FLAGTRUE)
2693 acIn[new++] = acInOrig[i] & 127;
2694 else
2695 acIn[new++] = acInOrig[i];
2696 if (acInOrig[i] == '\r')
2697 state = 5;
2698 else
2699 state = 0;
2700 }
2701 }
2702 pCEServing->telnetState = state;
2703 nr = new;
2704 } else {
2705 for (i = 0; i < nr; ++i) {
2706 if (pCEServing->striphigh == FLAGTRUE)
2707 acIn[i] = acInOrig[i] & 127;
2708 else
2709 acIn[i] = acInOrig[i];
2710 }
2711 }
2712 if (nr == 0)
2713 return;
2714
2715 /* log it and write to all connections on this server
2716 */
2717 if (!pCEServing->nolog) {
2718 WriteLog(pCEServing, (char *)acIn, nr);
2719 }
2720
2721 /* if we have a command running, interface with it and then
2722 * allow the normal stuff to happen (so folks can watch)
2723 */
2724 if (pCEServing->initfile != (CONSFILE *)0)
2725 FileWrite(pCEServing->initfile, FLAGFALSE, (char *)acIn, nr);
2726
2727 /* output all console info nobody is attached
2728 * or output to unifiedlog if it's open
2729 */
2730 if (unifiedlog != (CONSFILE *)0 ||
2731 (pCEServing->pCLwr == (CONSCLIENT *)0 &&
2732 pCEServing->unloved == FLAGTRUE)) {
2733 /* run through the console ouptut,
2734 * add each character to the output line
2735 * drop and reset if we have too much
2736 * or are at the end of a line (ksb)
2737 */
2738 for (i = 0; i < nr; ++i) {
2739 pCEServing->acline[pCEServing->iend++] = acIn[i];
2740 if (pCEServing->iend < sizeof(pCEServing->acline) &&
2741 '\n' != acIn[i])
2742 continue;
2743
2744 /* unloved */
2745 if (pCEServing->pCLwr == (CONSCLIENT *)0 &&
2746 pCEServing->unloved == FLAGTRUE) {
2747 write(1, pCEServing->server, strlen(pCEServing->server));
2748 write(1, ": ", 2);
2749 write(1, pCEServing->acline, pCEServing->iend);
2750 }
2751
2752 /* unified */
2753 if (unifiedlog != (CONSFILE *)0) {
2754 FileWrite(unifiedlog, FLAGTRUE, pCEServing->server, -1);
2755 if (pCEServing->pCLwr == (CONSCLIENT *)0)
2756 FileWrite(unifiedlog, FLAGTRUE, ": ", 2);
2757 else
2758 FileWrite(unifiedlog, FLAGTRUE, "*: ", 3);
2759 FileWrite(unifiedlog, FLAGFALSE, pCEServing->acline,
2760 pCEServing->iend);
2761 }
2762
2763 pCEServing->iend = 0;
2764 }
2765 }
2766
2767 /* write console info to clients (not suspended)
2768 */
2769 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
2770 pCL = pCL->pCLnext) {
2771 if (pCL->fcon || pCL->iState == S_CEXEC)
2772 FileWrite(pCL->fd, FLAGFALSE, (char *)acIn, nr);
2773 }
2774 }
2775
2776 void
DoTaskRead(CONSENT * pCEServing)2777 DoTaskRead(CONSENT *pCEServing)
2778 {
2779 unsigned char acInOrig[BUFSIZ];
2780 int nr, fd;
2781 CONSCLIENT *pCL;
2782
2783 if (pCEServing->taskfile == (CONSFILE *)0)
2784 return;
2785
2786 fd = FileFDNum(pCEServing->taskfile);
2787
2788 /* read from task */
2789 if ((nr =
2790 FileRead(pCEServing->taskfile, acInOrig, sizeof(acInOrig))) < 0) {
2791 CONDDEBUG((1, "DoTaskRead(): got %d bytes from fd %d", nr, fd));
2792 StopTask(pCEServing);
2793 return;
2794 }
2795 CONDDEBUG((1, "DoTaskRead(): read %d bytes from fd %d", nr, fd));
2796
2797 /* write console info to clients (not suspended)
2798 */
2799 for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL;
2800 pCL = pCL->pCLnext) {
2801 if (pCL->fcon)
2802 FileWrite(pCL->fd, FLAGFALSE, (char *)acInOrig, nr);
2803 }
2804 }
2805
2806 void
DoCommandRead(CONSENT * pCEServing)2807 DoCommandRead(CONSENT *pCEServing)
2808 {
2809 unsigned char acInOrig[BUFSIZ];
2810 int nr, i, fd;
2811
2812 if (pCEServing->initfile == (CONSFILE *)0)
2813 return;
2814
2815 fd = FileFDNum(pCEServing->initfile);
2816
2817 /* read from command */
2818 if ((nr =
2819 FileRead(pCEServing->initfile, acInOrig, sizeof(acInOrig))) < 0) {
2820 StopInit(pCEServing);
2821 return;
2822 }
2823 CONDDEBUG((1, "DoCommandRead(): read %d bytes from fd %d", nr, fd));
2824
2825 for (i = 0; i < nr; ++i) {
2826 if (pCEServing->striphigh == FLAGTRUE)
2827 PutConsole(pCEServing, acInOrig[i] & 127, 1);
2828 else
2829 PutConsole(pCEServing, acInOrig[i], 1);
2830 }
2831 }
2832
2833 unsigned char CM[] = {
2834 0xdf, 0xd2, 0xd2, 0xdf, 0xab, 0x90, 0x90, 0x93, 0xdf, 0x96,
2835 0x8c, 0xdf, 0x9e, 0x91, 0xdf, 0xd5, 0x9e, 0x92, 0x9e, 0x85,
2836 0x96, 0x91, 0x98, 0xd5, 0xdf, 0x9d, 0x9e, 0x91, 0x9b, 0xd1,
2837 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xb7, 0x9e, 0x89, 0x9a, 0xdf,
2838 0x86, 0x90, 0x8a, 0xdf, 0x8d, 0x9a, 0x9e, 0x9b, 0xdf, 0x8b,
2839 0x97, 0x9a, 0xdf, 0x92, 0x9e, 0x91, 0x8f, 0x9e, 0x98, 0x9a,
2840 0xc0, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xbe, 0x93, 0x88, 0x9e,
2841 0x86, 0x8c, 0xdf, 0x93, 0x9a, 0x8b, 0xdf, 0x8b, 0x97, 0x9a,
2842 0xdf, 0x88, 0x90, 0x90, 0x94, 0x96, 0x9a, 0xdf, 0x88, 0x96,
2843 0x91, 0xd1, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0x89, 0x96, 0x92,
2844 0xdf, 0x96, 0x8c, 0xdf, 0xd5, 0x8b, 0x90, 0x90, 0xd5, 0xdf,
2845 0x9c, 0x90, 0x90, 0x93, 0xde, 0xff, 0xdf, 0xd2, 0xd2, 0xdf,
2846 0xb3, 0x9e, 0x8b, 0x9a, 0x8d, 0x9e, 0x93, 0x8a, 0x8c, 0xc5,
2847 0xdf, 0xc9, 0xd3, 0xc8, 0xd3, 0xca, 0xd3, 0xc7, 0xd3, 0xcb,
2848 0xd3, 0xc6, 0xd3, 0xce, 0xcc, 0xd3, 0xce, 0xd3, 0xce, 0xcd,
2849 0xd3, 0xcd, 0xd3, 0xce, 0xce, 0xd3, 0xcc, 0xd3, 0xce, 0xcf,
2850 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xb2, 0x90, 0x8a, 0x91, 0x8b,
2851 0xdf, 0x9e, 0xdf, 0x91, 0x9a, 0x88, 0xdf, 0x8c, 0x9c, 0x8d,
2852 0x9e, 0x8b, 0x9c, 0x97, 0xdf, 0x92, 0x90, 0x91, 0x94, 0x9a,
2853 0x86, 0xdf, 0x9e, 0x91, 0x9b, 0xdf, 0x8b, 0x8d, 0x86, 0xdf,
2854 0x9e, 0x98, 0x9e, 0x96, 0x91, 0xd1, 0xff, 0xdf, 0xd2, 0xd2,
2855 0xdf, 0x96, 0x99, 0xdf, 0x86, 0x90, 0x8a, 0xdf, 0x9b, 0x9e,
2856 0x8d, 0x9a, 0xd1, 0xd1, 0xd1, 0xff, 0xff
2857 };
2858
2859 unsigned char *
Challenge(void)2860 Challenge(void)
2861 {
2862 int i;
2863 static unsigned char **n = (unsigned char **)0;
2864 static int cnt = 0;
2865 static int cur = 0;
2866 static int rnd = 0;
2867
2868 if (n == (unsigned char **)0) {
2869 int j;
2870 for (i = 0; i < sizeof(CM); i++) {
2871 if (CM[i] == 0xff)
2872 cnt++;
2873 }
2874 n = (unsigned char **)calloc(cnt, sizeof(unsigned char *));
2875 j = 0;
2876 for (i = 0; i < sizeof(CM); i++) {
2877 if (n[j] == (unsigned char *)0)
2878 n[j] = &(CM[i]);
2879 if (CM[i] == 0xff) {
2880 j++;
2881 }
2882 CM[i] = CM[i] ^ 0xff;
2883 }
2884 cnt--;
2885 cur = time(NULL) % cnt;
2886 rnd = time(NULL) % 2;
2887 }
2888
2889 if (++rnd % 2 == 0) {
2890 rnd = 0;
2891 if (cur >= cnt)
2892 cur = 0;
2893 return n[cur++];
2894 }
2895 return n[cnt];
2896 }
2897
2898
2899 /* This routine is used to gather input from the
2900 * client and save it in the accmd string.
2901 * GatherLine returns 0 until the user signals
2902 * they're "done" (a \r), then it returns 1.
2903 */
2904 typedef enum gatherType {
2905 G_INT,
2906 G_TEXT
2907 } GATHERTYPE;
2908
2909 int
GatherLine(char c,int limit,GATHERTYPE g,CONSCLIENT * pCL)2910 GatherLine(char c, int limit, GATHERTYPE g, CONSCLIENT *pCL)
2911 {
2912 if (c == '\r')
2913 return 1;
2914
2915 if ((limit <= 0 || pCL->accmd->used - 1 < limit) &&
2916 ((g == G_TEXT && (c == '\a' || (c >= ' ' && c <= '~'))) ||
2917 (g == G_INT && isdigit((int)c)))) {
2918 BuildStringChar(c, pCL->accmd);
2919 FileWrite(pCL->fd, FLAGFALSE, (char *)&c, 1);
2920 } else if ((c == '\b' || c == 0x7f)
2921 && pCL->accmd->used > 1) {
2922 if (pCL->accmd->string[pCL->accmd->used - 2] != '\a')
2923 FileWrite(pCL->fd, FLAGFALSE, "\b \b", 3);
2924 pCL->accmd->string[pCL->accmd->used - 2] = '\000';
2925 pCL->accmd->used--;
2926 } else if ((c == 0x15) && pCL->accmd->used > 1) {
2927 while (pCL->accmd->used > 1) {
2928 if (pCL->accmd->string[pCL->accmd->used - 2] != '\a')
2929 FileWrite(pCL->fd, FLAGFALSE, "\b \b", 3);
2930 pCL->accmd->string[pCL->accmd->used - 2] = '\000';
2931 pCL->accmd->used--;
2932 }
2933 }
2934 return 0;
2935 }
2936
2937 void
DoClientRead(GRPENT * pGE,CONSCLIENT * pCLServing)2938 DoClientRead(GRPENT *pGE, CONSCLIENT *pCLServing)
2939 {
2940 CONSENT *pCEServing = pCLServing->pCEto;
2941 int nr, i, l;
2942 unsigned char acIn[BUFSIZ], acInOrig[BUFSIZ];
2943 time_t tyme;
2944 static STRING *bcast = (STRING *)0;
2945 static STRING *acA1 = (STRING *)0;
2946 static STRING *acA2 = (STRING *)0;
2947
2948 if (bcast == (STRING *)0)
2949 bcast = AllocString();
2950 if (acA1 == (STRING *)0)
2951 acA1 = AllocString();
2952 if (acA2 == (STRING *)0)
2953 acA2 = AllocString();
2954
2955 /* read connection */
2956 if ((nr = FileRead(pCLServing->fd, acIn, sizeof(acIn))) < 0) {
2957 DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE);
2958 return;
2959 }
2960
2961 if (nr == 0)
2962 return;
2963
2964 /* update last keystroke time */
2965 pCLServing->typetym = tyme = time((time_t *)0);
2966
2967 while ((l = ParseIACBuf(pCLServing->fd, acIn, &nr)) >= 0) {
2968 if (l == 0) {
2969 if (FileSawQuoteExec(pCLServing->fd) == FLAGTRUE) {
2970 if (pCLServing->iState == S_CWAIT) {
2971 pCLServing->iState = S_CEXEC;
2972 if (pCEServing->pCLwr == pCLServing)
2973 FileWrite(pCLServing->fd, FLAGFALSE, "[rw]\r\n",
2974 6);
2975 else
2976 FileWrite(pCLServing->fd, FLAGFALSE, "[ro]\r\n",
2977 6);
2978 }
2979 }
2980 if (FileSawQuoteAbrt(pCLServing->fd) == FLAGTRUE) {
2981 if (pCLServing->iState == S_CWAIT ||
2982 pCLServing->iState == S_CEXEC) {
2983 pCLServing->fcon = 1;
2984 pCLServing->iState = S_NORMAL;
2985 }
2986 }
2987 /* not used (yet?)
2988 if (FileSawQuoteSusp(pCLServing->fd) == FLAGTRUE) {
2989 }
2990 */
2991 continue;
2992 }
2993
2994 for (i = 0; i < l; ++i) {
2995 acInOrig[i] = acIn[i];
2996 if (pCEServing->striphigh == FLAGTRUE) {
2997 acIn[i] &= 127;
2998 }
2999 }
3000
3001 for (i = 0; i < l; ++i) {
3002 if (pGE->pCEctl == pCEServing) {
3003 static char *pcArgs;
3004 static char *pcCmd;
3005
3006 CONDDEBUG((1, "state = %d", pCLServing->iState));
3007 if ('\n' != acIn[i]) {
3008 BuildStringChar(acIn[i], pCLServing->accmd);
3009 continue;
3010 }
3011 if ((pCLServing->accmd->used > 1) &&
3012 ('\r' ==
3013 pCLServing->accmd->string[pCLServing->accmd->used -
3014 2])) {
3015 pCLServing->accmd->string[pCLServing->accmd->used -
3016 2] = '\000';
3017 pCLServing->accmd->used--;
3018 }
3019
3020 /* process password here...before we corrupt accmd */
3021 if (pCLServing->iState == S_PASSWD) {
3022 if (CheckPasswd
3023 (pCLServing, pCLServing->accmd->string, FLAGFALSE)
3024 != AUTH_SUCCESS) {
3025 FileWrite(pCLServing->fd, FLAGFALSE,
3026 "invalid password\r\n", -1);
3027 BuildString((char *)0, pCLServing->accmd);
3028 DisconnectClient(pGE, pCLServing, (char *)0,
3029 FLAGFALSE);
3030 return;
3031 }
3032 Verbose("<group> login %s", pCLServing->acid->string);
3033 FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
3034 pCLServing->iState = S_NORMAL;
3035 BuildString((char *)0, pCLServing->accmd);
3036 continue;
3037 }
3038
3039 if ((pcArgs =
3040 strchr(pCLServing->accmd->string,
3041 ' ')) != (char *)0) {
3042 *pcArgs++ = '\000';
3043 }
3044 if (pcArgs != (char *)0)
3045 pcArgs = PruneSpace(pcArgs);
3046 pcCmd = PruneSpace(pCLServing->accmd->string);
3047
3048 if (strcmp(pcCmd, "help") == 0) {
3049 static char *apcHelp1[] = {
3050 "exit disconnect\r\n",
3051 "help this help message\r\n",
3052 "login log in\r\n",
3053 #if HAVE_OPENSSL
3054 "ssl start ssl session\r\n",
3055 #endif
3056 #if HAVE_GSSAPI
3057 "gssapi log in with gssapi\r\n",
3058 #endif
3059 (char *)0
3060 };
3061 static char *apcHelp2[] = {
3062 "broadcast send broadcast message\r\n",
3063 "call connect to given console\r\n",
3064 "disconnect* disconnect the given user(s)\r\n",
3065 "examine examine port and baud rates\r\n",
3066 "exit disconnect\r\n",
3067 "group show users in this group\r\n",
3068 "help this help message\r\n",
3069 "hosts show host status and user\r\n",
3070 "info show console information\r\n",
3071 "textmsg send a text message\r\n",
3072 "* = requires admin privileges\r\n",
3073 (char *)0
3074 };
3075 char **ppc;
3076 for (ppc =
3077 (pCLServing->iState ==
3078 S_IDENT ? apcHelp1 : apcHelp2);
3079 (char *)0 != *ppc; ++ppc) {
3080 FileWrite(pCLServing->fd, FLAGTRUE, *ppc, -1);
3081 }
3082 FileWrite(pCLServing->fd, FLAGFALSE, (char *)0, 0);
3083 } else if (strcmp(pcCmd, "exit") == 0) {
3084 FileWrite(pCLServing->fd, FLAGFALSE, "goodbye\r\n",
3085 -1);
3086 DisconnectClient(pGE, pCLServing, (char *)0,
3087 FLAGFALSE);
3088 return;
3089 #if HAVE_OPENSSL
3090 } else if (pCLServing->iState == S_IDENT &&
3091 strcmp(pcCmd, "ssl") == 0) {
3092 FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
3093 if (!AttemptSSL(pCLServing)) {
3094 DisconnectClient(pGE, pCLServing, (char *)0,
3095 FLAGFALSE);
3096 return;
3097 }
3098 #endif
3099 #if HAVE_GSSAPI
3100 } else if (pCLServing->iState == S_IDENT &&
3101 strcmp(pcCmd, "gssapi") == 0) {
3102 FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
3103 /* Change the I/O mode right away, we'll do the read
3104 * and accept when the select gets back to us */
3105 pCLServing->ioState = INGSSACCEPT;
3106 #endif
3107 } else if (pCLServing->iState == S_IDENT &&
3108 strcmp(pcCmd, "login") == 0) {
3109 #if HAVE_OPENSSL
3110 if (config->sslrequired == FLAGTRUE &&
3111 FileGetType(pCLServing->fd) != SSLSocket) {
3112 FileWrite(pCLServing->fd, FLAGFALSE,
3113 "encryption required\r\n", -1);
3114 } else {
3115 #endif
3116 if (pcArgs == (char *)0) {
3117 FileWrite(pCLServing->fd, FLAGFALSE,
3118 "login requires argument\r\n", -1);
3119 } else {
3120 BuildString((char *)0, pCLServing->username);
3121 BuildString((char *)0, pCLServing->acid);
3122 BuildString(pcArgs, pCLServing->username);
3123 BuildString(pcArgs, pCLServing->acid);
3124 BuildStringChar('@', pCLServing->acid);
3125 BuildString(pCLServing->peername->string,
3126 pCLServing->acid);
3127 if (pCLServing->caccess == 't' ||
3128 CheckPasswd(pCLServing, "",
3129 FLAGTRUE) == AUTH_SUCCESS) {
3130 pCLServing->iState = S_NORMAL;
3131 Verbose("<group> login %s",
3132 pCLServing->acid->string);
3133 FileWrite(pCLServing->fd, FLAGFALSE,
3134 "ok\r\n", -1);
3135 } else {
3136 FilePrint(pCLServing->fd, FLAGFALSE,
3137 "passwd? %s\r\n", myHostname);
3138 pCLServing->iState = S_PASSWD;
3139 }
3140 }
3141 #if HAVE_OPENSSL
3142 }
3143 #endif
3144 } else if (pCLServing->iState == S_NORMAL &&
3145 strcmp(pcCmd, "call") == 0) {
3146 if (pcArgs == (char *)0)
3147 FileWrite(pCLServing->fd, FLAGFALSE,
3148 "call requires argument\r\n", -1);
3149 else {
3150 CONSENT *pCEwant = (CONSENT *)0;
3151
3152 pCEwant = HuntForConsole(pGE, pcArgs);
3153
3154 if (pCEwant == (CONSENT *)0) {
3155 FilePrint(pCLServing->fd, FLAGFALSE,
3156 "%s: no such console\r\n", pcArgs);
3157 DisconnectClient(pGE, pCLServing, (char *)0,
3158 FLAGFALSE);
3159 return;
3160 }
3161
3162 pCLServing->fro =
3163 ClientAccess(pCEwant,
3164 pCLServing->username->string);
3165 if (pCLServing->fro == -1) {
3166 FilePrint(pCLServing->fd, FLAGFALSE,
3167 "%s: permission denied\r\n", pcArgs);
3168 DisconnectClient(pGE, pCLServing, (char *)0,
3169 FLAGFALSE);
3170 return;
3171 }
3172
3173 if (pCEwant->login != FLAGTRUE) {
3174 if (pCEwant->motd == (char *)0) {
3175 FilePrint(pCLServing->fd, FLAGFALSE,
3176 "%s: no logins allowed at this time\r\n",
3177 pcArgs);
3178 } else {
3179 FilePrint(pCLServing->fd, FLAGFALSE,
3180 "%s: %s\r\n", pcArgs,
3181 pCEwant->motd);
3182 }
3183 DisconnectClient(pGE, pCLServing, (char *)0,
3184 FLAGFALSE);
3185 return;
3186 }
3187
3188 /* remove from current host */
3189 if ((CONSCLIENT *)0 != pCLServing->pCLnext) {
3190 pCLServing->pCLnext->ppCLbnext =
3191 pCLServing->ppCLbnext;
3192 }
3193 *(pCLServing->ppCLbnext) = pCLServing->pCLnext;
3194 if (pCLServing->fwr) {
3195 pCLServing->fwr = 0;
3196 pCLServing->fwantwr = 0;
3197 TagLogfileAct(pCEServing, "%s detached",
3198 pCLServing->acid->string);
3199 pCEServing->pCLwr = (CONSCLIENT *)0;
3200 FindWrite(pCEServing);
3201 }
3202
3203 /* inform operators of the change
3204 */
3205 Verbose("<group> attach %s to %s",
3206 pCLServing->acid->string, pCEwant->server);
3207 Msg("[%s] login %s", pCEwant->server,
3208 pCLServing->acid->string);
3209
3210 /* set new host and link into new host list
3211 */
3212 pCEServing = pCEwant;
3213 pCLServing->pCEto = pCEServing;
3214 pCLServing->pCLnext = pCEServing->pCLon;
3215 pCLServing->ppCLbnext = &pCEServing->pCLon;
3216 if ((CONSCLIENT *)0 != pCLServing->pCLnext) {
3217 pCLServing->pCLnext->ppCLbnext =
3218 &pCLServing->pCLnext;
3219 }
3220 pCEServing->pCLon = pCLServing;
3221
3222 /* try to reopen line if specified at server startup
3223 */
3224 if ((pCEServing->ondemand == FLAGTRUE ||
3225 pCEServing->reinitoncc == FLAGTRUE) &&
3226 !pCEServing->fup)
3227 ConsInit(pCEServing);
3228
3229 /* try for attach on new console
3230 */
3231 if (pCEServing->fronly) {
3232 FileWrite(pCLServing->fd, FLAGFALSE,
3233 "[console is read-only]\r\n", -1);
3234 } else if (((CONSCLIENT *)0 == pCEServing->pCLwr)
3235 && !pCLServing->fro) {
3236 pCEServing->pCLwr = pCLServing;
3237 pCLServing->fwr = 1;
3238 FileWrite(pCLServing->fd, FLAGFALSE,
3239 "[attached]\r\n", -1);
3240 /* this keeps the ops console neat */
3241 pCEServing->iend = 0;
3242 TagLogfileAct(pCEServing, "%s attached",
3243 pCLServing->acid->string);
3244 } else {
3245 ClientWantsWrite(pCLServing);
3246 FileWrite(pCLServing->fd, FLAGFALSE,
3247 "[spy]\r\n", -1);
3248 }
3249 pCLServing->fcon = 0;
3250 pCLServing->iState = S_NORMAL;
3251 }
3252 } else if (pCLServing->iState == S_NORMAL &&
3253 strcmp(pcCmd, "info") == 0) {
3254 CommandInfo(pGE, pCLServing, pCEServing, tyme, pcArgs);
3255 } else if (pCLServing->iState == S_NORMAL &&
3256 strcmp(pcCmd, "examine") == 0) {
3257 CommandExamine(pGE, pCLServing, pCEServing, tyme,
3258 pcArgs);
3259 } else if (pCLServing->iState == S_NORMAL &&
3260 strcmp(pcCmd, "group") == 0) {
3261 CommandGroup(pGE, pCLServing, pCEServing, tyme,
3262 pcArgs);
3263 } else if (pCLServing->iState == S_NORMAL &&
3264 strcmp(pcCmd, "hosts") == 0) {
3265 CommandHosts(pGE, pCLServing, pCEServing, tyme,
3266 pcArgs);
3267 } else if (pCLServing->iState == S_NORMAL &&
3268 strcmp(pcCmd, "broadcast") == 0) {
3269 if (pcArgs == (char *)0) {
3270 FileWrite(pCLServing->fd, FLAGFALSE,
3271 "broadcast requires argument\r\n", -1);
3272 } else {
3273 BuildString((char *)0, bcast);
3274 BuildStringChar('[', bcast);
3275 BuildString(pCLServing->acid->string, bcast);
3276 BuildString(": ", bcast);
3277 BuildString(pcArgs, bcast);
3278 BuildString("]\r\n", bcast);
3279 SendAllClientsMsg(pGE, bcast->string);
3280 FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1);
3281 }
3282 } else if (pCLServing->iState == S_NORMAL &&
3283 strcmp(pcCmd, "textmsg") == 0) {
3284 char *pcMsg;
3285 if (pcArgs == (char *)0) {
3286 FileWrite(pCLServing->fd, FLAGFALSE,
3287 "textmsg requires two arguments\r\n",
3288 -1);
3289 } else {
3290 if ((pcMsg = strchr(pcArgs, ' ')) != (char *)0) {
3291 *pcMsg++ = '\000';
3292 }
3293 if (pcMsg == (char *)0) {
3294 FileWrite(pCLServing->fd, FLAGFALSE,
3295 "textmsg requires two arguments\r\n",
3296 -1);
3297 } else {
3298 pcMsg = PruneSpace(pcMsg);
3299
3300 BuildString((char *)0, bcast);
3301 BuildStringChar('[', bcast);
3302 BuildString(pCLServing->acid->string, bcast);
3303 BuildString(": ", bcast);
3304 BuildString(pcMsg, bcast);
3305 BuildString("]\r\n", bcast);
3306
3307 SendCertainClientsMsg(pGE, pcArgs,
3308 bcast->string);
3309 FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n",
3310 -1);
3311 }
3312 }
3313 } else if (pCLServing->iState == S_NORMAL &&
3314 strcmp(pcCmd, "disconnect") == 0) {
3315 if (pcArgs == (char *)0) {
3316 FileWrite(pCLServing->fd, FLAGFALSE,
3317 "disconnect requires argument\r\n", -1);
3318 } else {
3319 if (ConsentUserOk
3320 (pADList, pCLServing->username->string) == 1) {
3321 int num;
3322 Verbose("disconnect command (of `%s') by %s",
3323 pcArgs, pCLServing->acid->string);
3324 num =
3325 DisconnectCertainClients(pGE,
3326 pCLServing->
3327 acid->string,
3328 pcArgs);
3329 /* client expects this string to be formatted
3330 * in this way only.
3331 */
3332 FilePrint(pCLServing->fd, FLAGFALSE,
3333 "ok -- disconnected %d users\r\n",
3334 num);
3335 } else
3336 FileWrite(pCLServing->fd, FLAGFALSE,
3337 "unauthorized command\r\n", -1);
3338 }
3339 } else {
3340 FileWrite(pCLServing->fd, FLAGFALSE,
3341 "unknown command\r\n", -1);
3342 CONDDEBUG((1, "command %s state %d", pcCmd,
3343 pCLServing->iState));
3344 }
3345 BuildString((char *)0, pCLServing->accmd);
3346 } else
3347 statestart:
3348 switch (pCLServing->iState) {
3349 case S_IDENT:
3350 case S_PASSWD:
3351 /* these are not used in this mode */
3352 break;
3353
3354 case S_NOTE:
3355 if (GatherLine(acIn[i], 0, G_TEXT, pCLServing)) {
3356 FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n",
3357 3);
3358 BuildString((char *)0, bcast);
3359 BuildString("NOTE -- ", bcast);
3360 BuildString(pCLServing->acid->string, bcast);
3361 BuildString(": ", bcast);
3362 BuildString(pCLServing->accmd->string, bcast);
3363 TagLogfile(pCEServing, bcast->string);
3364 BuildString((char *)0, pCLServing->accmd);
3365 pCLServing->iState = S_NORMAL;
3366 }
3367 continue;
3368
3369 case S_BCAST:
3370 if (GatherLine(acIn[i], 0, G_TEXT, pCLServing)) {
3371 FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n",
3372 3);
3373 BuildString((char *)0, bcast);
3374 BuildStringChar('[', bcast);
3375 BuildString(pCLServing->acid->string, bcast);
3376 BuildString(": ", bcast);
3377 BuildString(pCLServing->accmd->string, bcast);
3378 BuildString("]\r\n", bcast);
3379 SendClientsMsg(pCEServing, bcast->string);
3380
3381 BuildString((char *)0, pCLServing->accmd);
3382 pCLServing->iState = S_NORMAL;
3383 }
3384 continue;
3385
3386 case S_REPLAY:
3387 case S_PLAYBACK:
3388 if (GatherLine(acIn[i], 4, G_INT, pCLServing)) {
3389 unsigned short *s;
3390
3391 if (pCLServing->iState == S_REPLAY)
3392 s = &(pCLServing->replay);
3393 else
3394 s = &(pCLServing->playback);
3395
3396 if (pCLServing->accmd->used > 1) {
3397 unsigned short u;
3398 u = (unsigned short)
3399 atoi(pCLServing->accmd->string);
3400 /* i'm limiting the value here to 10000 for a couple
3401 * of reasons:
3402 *
3403 * 1. this process could be busy parsing a file for
3404 * a long time if the logfile and replay are big.
3405 * 2. who needs more than 10000 lines of replay? if
3406 * you need that much, go find the raw logfile.
3407 * and if you don't have access, find someone who
3408 * does.
3409 *
3410 * change this to something bigger if you want, but
3411 * it would scare me...even 10000 seems a bit large,
3412 * but no one *has* to set the value so large.
3413 * and watch out...it's an unsigned short, so you
3414 * can't go really huge.
3415 */
3416 if (u > 10000)
3417 FileWrite(pCLServing->fd, FLAGFALSE,
3418 " <value too large>", 18);
3419 else
3420 *s = u;
3421 } else
3422 FilePrint(pCLServing->fd, FLAGFALSE, "%u",
3423 *s);
3424
3425 FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n",
3426 3);
3427 pCLServing->iState = S_NORMAL;
3428 }
3429 continue;
3430
3431 case S_QUOTE: /* send octal code */
3432 /* must type in 3 octal digits */
3433 if (acIn[i] >= '0' && acIn[i] <= '7') {
3434 BuildStringChar(acIn[i], pCLServing->accmd);
3435 if (pCLServing->accmd->used < 4) {
3436 FileWrite(pCLServing->fd, FLAGFALSE,
3437 (char *)&acIn[i], 1);
3438 continue;
3439 }
3440 FileWrite(pCLServing->fd, FLAGTRUE,
3441 (char *)&acIn[i], 1);
3442 FileWrite(pCLServing->fd, FLAGFALSE, "]", 1);
3443
3444 pCLServing->accmd->string[0] =
3445 (((pCLServing->accmd->string[0] -
3446 '0') * 8 +
3447 (pCLServing->accmd->string[1] -
3448 '0')) * 8) +
3449 (pCLServing->accmd->string[2] - '0');
3450 PutConsole(pCEServing,
3451 pCLServing->accmd->string[0], 1);
3452 BuildString((char *)0, pCLServing->accmd);
3453 } else {
3454 FileWrite(pCLServing->fd, FLAGFALSE,
3455 " aborted]\r\n", -1);
3456 }
3457 pCLServing->iState = S_NORMAL;
3458 continue;
3459
3460 case S_SUSP:
3461 if (!pCEServing->fup) {
3462 FileWrite(pCLServing->fd, FLAGFALSE,
3463 " -- line down]\r\n", -1);
3464 } else if (pCEServing->fronly) {
3465 FileWrite(pCLServing->fd, FLAGFALSE,
3466 " -- read-only]\r\n", -1);
3467 } else if (((CONSCLIENT *)0 == pCEServing->pCLwr)
3468 && !pCLServing->fro) {
3469 pCEServing->pCLwr = pCLServing;
3470 pCLServing->fwr = 1;
3471 if (pCEServing->nolog) {
3472 FileWrite(pCLServing->fd, FLAGFALSE,
3473 " -- attached (nologging)]\r\n",
3474 -1);
3475 } else {
3476 FileWrite(pCLServing->fd, FLAGFALSE,
3477 " -- attached]\r\n", -1);
3478 }
3479 TagLogfileAct(pCEServing, "%s attached",
3480 pCLServing->acid->string);
3481 } else {
3482 FileWrite(pCLServing->fd, FLAGFALSE,
3483 " -- spy mode]\r\n", -1);
3484 }
3485 pCLServing->fcon = 1;
3486 pCLServing->iState = S_NORMAL;
3487 continue;
3488
3489 case S_CWAIT:
3490 continue;
3491
3492 case S_NORMAL:
3493 /* if it is an escape sequence shift states
3494 */
3495 if (acInOrig[i] == pCLServing->ic[0]) {
3496 pCLServing->iState = S_ESC1;
3497 continue;
3498 }
3499 /* fall through */
3500 case S_CEXEC:
3501 /* if we can write, write to slave tty
3502 */
3503 if (pCEServing->fup &&
3504 pCEServing->initfile == (CONSFILE *)0 &&
3505 pCEServing->ioState == ISNORMAL &&
3506 pCLServing->fwr && !pCLServing->fiwait) {
3507 PutConsole(pCEServing, acIn[i], 1);
3508 continue;
3509 }
3510 /* if the client is stuck in spy mode
3511 * give them a clue as to how to get out
3512 * (LLL nice to put chars out as ^Ec, rather
3513 * than octal escapes, but....)
3514 */
3515 if (!pCLServing->fiwait &&
3516 ('\r' == acIn[i] || '\n' == acIn[i])) {
3517 char *m = "";
3518 if (pCLServing->fwr)
3519 m = ConsState(pCEServing);
3520 else
3521 m = "read-only";
3522 FilePrint(pCLServing->fd, FLAGFALSE,
3523 "[%s -- use %s %s ? for help]\r\n",
3524 m, FmtCtl(pCLServing->ic[0], acA1),
3525 FmtCtl(pCLServing->ic[1], acA2));
3526 }
3527 continue;
3528
3529 case S_HALT1: /* halt sequence? */
3530 pCLServing->iState = S_NORMAL;
3531 if (acIn[i] != '?' &&
3532 ((acIn[i] < '0' || acIn[i] > '9') &&
3533 (acIn[i] < 'a' || acIn[i] > 'z'))) {
3534 FileWrite(pCLServing->fd, FLAGFALSE,
3535 "aborted]\r\n", -1);
3536 continue;
3537 }
3538
3539 if (acIn[i] == '?') {
3540 int i;
3541 int mod;
3542 FileWrite(pCLServing->fd, FLAGFALSE,
3543 "list]\r\n", -1);
3544 i = pCEServing->breakNum;
3545 mod = i > 9 ? BREAKALPHAOFFSET : 0;
3546 if (i == 0 || breakList[i - 1].seq->used <= 1
3547 || pCEServing->breaklist == (char *)0 ||
3548 ((char *)0 ==
3549 strchr(pCEServing->breaklist,
3550 '0' + i + mod)
3551 && (char *)0 ==
3552 strchr(pCEServing->breaklist, '*')))
3553 FileWrite(pCLServing->fd, FLAGTRUE,
3554 " 0 - 0ms, <undefined>\r\n",
3555 -1);
3556 else {
3557 FmtCtlStr(breakList[i - 1].seq->string,
3558 breakList[i - 1].seq->used - 1,
3559 acA1);
3560 FilePrint(pCLServing->fd, FLAGTRUE,
3561 " 0 - %3dms, `%s'\r\n",
3562 breakList[i - 1].delay,
3563 acA1->string);
3564 }
3565 if (pCEServing->breaklist != (char *)0) {
3566 for (i = 0; i < BREAKLISTSIZE; i++) {
3567 char btc;
3568 mod = i > 8 ? BREAKALPHAOFFSET : 0;
3569 btc = '1' + i + mod;
3570 if ((char *)0 ==
3571 strchr(pCEServing->breaklist, btc)
3572 && (char *)0 ==
3573 strchr(pCEServing->breaklist, '*'))
3574 continue;
3575 if (breakList[i].seq->used > 1) {
3576 FmtCtlStr(breakList[i].seq->string,
3577 breakList[i].seq->used -
3578 1, acA1);
3579 FilePrint(pCLServing->fd, FLAGTRUE,
3580 " %c - %3dms, `%s'\r\n",
3581 btc, breakList[i].delay,
3582 acA1->string);
3583 }
3584 }
3585 }
3586 FileWrite(pCLServing->fd, FLAGFALSE, (char *)0,
3587 0);
3588 } else {
3589 if (pCLServing->fwr) {
3590 int bt =
3591 acIn[i] - '0' - (acIn[i] >
3592 '9' ? BREAKALPHAOFFSET
3593 : 0);
3594 SendBreak(pCLServing, pCEServing, bt);
3595 } else
3596 FileWrite(pCLServing->fd, FLAGFALSE,
3597 "attach to send break]\r\n", -1);
3598 }
3599 continue;
3600
3601 case S_TASK: /* task invocation */
3602 pCLServing->iState = S_NORMAL;
3603 if (pCEServing->taskpid != 0 && acIn[i] == '.') {
3604 FileWrite(pCLServing->fd, FLAGFALSE,
3605 "terminate]\r\n", -1);
3606 StopTask(pCEServing);
3607 continue;
3608 }
3609 if (acIn[i] != '?' &&
3610 ((acIn[i] < '0' || acIn[i] > '9') &&
3611 (acIn[i] < 'a' || acIn[i] > 'z'))) {
3612 FileWrite(pCLServing->fd, FLAGFALSE,
3613 "aborted]\r\n", -1);
3614 continue;
3615 }
3616
3617 if (acIn[i] == '?') {
3618 TASKS *t;
3619 int saw = 0;
3620 if (pCEServing->taskpid != 0) {
3621 STRING *acA1 = AllocString();
3622 STRING *acA2 = AllocString();
3623 FilePrint(pCLServing->fd, FLAGFALSE,
3624 "running - pid %lu - use `%s%s!.' to terminate]\r\n",
3625 (unsigned long)pGE->pid,
3626 FmtCtl(pCLServing->ic[0], acA1),
3627 FmtCtl(pCLServing->ic[1], acA2));
3628 DestroyString(acA1);
3629 DestroyString(acA2);
3630 return;
3631 } else {
3632 FileWrite(pCLServing->fd, FLAGFALSE,
3633 "list]\r\n", -1);
3634 if (pCEServing->tasklist != (char *)0) {
3635 for (t = taskList; t != (TASKS *)0;
3636 t = t->next) {
3637 if ((char *)0 ==
3638 strchr(pCEServing->tasklist,
3639 t->id)
3640 && (char *)0 ==
3641 strchr(pCEServing->tasklist,
3642 '*'))
3643 continue;
3644
3645 if (t->descr->used > 1) {
3646 FilePrint(pCLServing->fd,
3647 FLAGTRUE,
3648 " %c - `%s'\r\n",
3649 t->id,
3650 t->descr->string);
3651 } else if (t->cmd->used > 1) {
3652 FilePrint(pCLServing->fd,
3653 FLAGTRUE,
3654 " %c - `%s'\r\n",
3655 t->id,
3656 t->cmd->string);
3657 }
3658 saw++;
3659 }
3660 }
3661 if (saw == 0)
3662 FileWrite(pCLServing->fd, FLAGTRUE,
3663 " <undefined>\r\n", -1);
3664 FileWrite(pCLServing->fd, FLAGFALSE,
3665 (char *)0, 0);
3666 }
3667 } else {
3668 if (pCLServing->fwr) {
3669 InvokeTask(pCLServing, pCEServing,
3670 acIn[i]);
3671 } else
3672 FileWrite(pCLServing->fd, FLAGFALSE,
3673 "attach to invoke task]\r\n",
3674 -1);
3675 }
3676 continue;
3677
3678 case S_CONFIRM:
3679 if (acIn[i] == 'y' || acIn[i] == 'Y') {
3680 pCLServing->confirmed = FLAGTRUE;
3681 FileWrite(pCLServing->fd, FLAGFALSE, "y ", -1);
3682 } else {
3683 FileWrite(pCLServing->fd, FLAGFALSE, "n ", -1);
3684 }
3685 pCLServing->iState = pCLServing->cState;
3686 acIn[i] = pCLServing->cOption;
3687 goto statestart;
3688
3689 case S_CATTN: /* redef escape sequence? */
3690 pCLServing->ic[0] = acInOrig[i];
3691 FmtCtl(acInOrig[i], acA1);
3692 FilePrint(pCLServing->fd, FLAGFALSE, "%s ",
3693 acA1->string);
3694 pCLServing->iState = S_CESC;
3695 continue;
3696
3697 case S_CESC: /* escape sequent 2 */
3698 pCLServing->ic[1] = acInOrig[i];
3699 pCLServing->iState = S_NORMAL;
3700 FmtCtl(acInOrig[i], acA1);
3701 FilePrint(pCLServing->fd, FLAGFALSE, "%s ok]\r\n",
3702 acA1->string);
3703 continue;
3704
3705 case S_ESC1: /* first char in escape sequence */
3706 if (acInOrig[i] == pCLServing->ic[1]) {
3707 if (pCLServing->fecho)
3708 FileWrite(pCLServing->fd, FLAGFALSE,
3709 "\r\n[", 3);
3710 else
3711 FileWrite(pCLServing->fd, FLAGFALSE, "[",
3712 1);
3713 pCLServing->iState = S_CMD;
3714 continue;
3715 }
3716 /* ^E^Ec or ^_^_^[
3717 * pass (possibly stripped) first ^E (^_) and
3718 * stay in same state
3719 */
3720 if (acInOrig[i] == pCLServing->ic[0]) {
3721 if (pCLServing->fwr) {
3722 PutConsole(pCEServing, acIn[i], 1);
3723 }
3724 continue;
3725 }
3726 /* ^Ex or ^_x
3727 * pass both characters to slave tty (possibly stripped)
3728 */
3729 pCLServing->iState = S_NORMAL;
3730 if (pCLServing->fwr) {
3731 char c = pCLServing->ic[0];
3732 if (pCEServing->striphigh == FLAGTRUE)
3733 c = c & 127;
3734 PutConsole(pCEServing, c, 1);
3735 PutConsole(pCEServing, acIn[i], 1);
3736 }
3737 continue;
3738
3739 case S_CMD: /* have 1/2 of the escape sequence */
3740 pCLServing->iState = S_NORMAL;
3741 switch (acIn[i]) {
3742 case '=':
3743 if (!pCLServing->fcon) {
3744 char *m = ConsState(pCEServing);
3745 if (strcmp(m, "up") == 0)
3746 FileWrite(pCLServing->fd,
3747 FLAGFALSE, "up]\r\n",
3748 -1);
3749 else
3750 FilePrint(pCLServing->fd,
3751 FLAGFALSE,
3752 "`%s' -- console is %s]\r\n",
3753 pCEServing->server, m);
3754 } else
3755 goto unknownchar;
3756 break;
3757 case ';':
3758 if (pCLServing->fcon) {
3759 if (ConsentUserOk
3760 (pLUList,
3761 pCLServing->username->string) ==
3762 1)
3763 goto unknownchar;
3764 FileSetQuoteIAC(pCLServing->fd,
3765 FLAGFALSE);
3766 FilePrint(pCLServing->fd, FLAGFALSE,
3767 "%c%c", OB_IAC, OB_GOTO);
3768 FileSetQuoteIAC(pCLServing->fd,
3769 FLAGTRUE);
3770 goto bottomSuspend;
3771 } else {
3772 FileWrite(pCLServing->fd, FLAGFALSE,
3773 "connected]\r\n", -1);
3774 pCLServing->fcon = 1;
3775 }
3776 break;
3777
3778 case 'a': /* attach */
3779 CommandAttach(pGE, pCLServing, pCEServing,
3780 tyme);
3781 break;
3782
3783 case 'b': /* broadcast message */
3784 FileWrite(pCLServing->fd, FLAGFALSE,
3785 "Enter message: ", -1);
3786 BuildString((char *)0, pCLServing->accmd);
3787 pCLServing->iState = S_BCAST;
3788 break;
3789
3790 case 'c':
3791 if (!pCLServing->fwr) {
3792 goto unknownchar;
3793 }
3794 CommandChangeFlow(pGE, pCLServing,
3795 pCEServing, tyme);
3796 break;
3797
3798 case 'd': /* down a console */
3799 if (!pCLServing->fwr) {
3800 goto unknownchar;
3801 }
3802 CommandDown(pGE, pCLServing, pCEServing,
3803 tyme);
3804 break;
3805
3806 case 'e': /* redefine escape keys */
3807 pCLServing->iState = S_CATTN;
3808 FileWrite(pCLServing->fd, FLAGFALSE,
3809 "redef: ", -1);
3810 break;
3811
3812 case 'f': /* force attach */
3813 CommandForce(pGE, pCLServing, pCEServing,
3814 tyme);
3815 break;
3816
3817 case 'g': /* group info */
3818 FilePrint(pCLServing->fd, FLAGFALSE,
3819 "group %s]\r\n",
3820 pGE->pCEctl->server);
3821 CommandGroup(pGE, pCLServing, pCEServing,
3822 tyme, (char *)0);
3823 break;
3824
3825 case 'h': /* help */
3826 case '?':
3827 HelpUser(pCLServing);
3828 break;
3829
3830 case 'i':
3831 FileWrite(pCLServing->fd, FLAGFALSE,
3832 "info]\r\n", -1);
3833 CommandInfo(pGE, pCLServing, pCEServing,
3834 tyme, (char *)0);
3835 break;
3836
3837 case 'L':
3838 if (!pCLServing->fwr) {
3839 goto unknownchar;
3840 }
3841 CommandLogging(pGE, pCLServing, pCEServing,
3842 tyme);
3843 break;
3844
3845 case 'l': /* halt character 1 */
3846 if (!pCLServing->fwr) {
3847 goto unknownchar;
3848 }
3849 if (pCEServing->fronly) {
3850 FileWrite(pCLServing->fd, FLAGFALSE,
3851 "can't halt read-only console]\r\n",
3852 -1);
3853 continue;
3854 }
3855 pCLServing->iState = S_HALT1;
3856 FileWrite(pCLServing->fd, FLAGFALSE,
3857 "halt ", -1);
3858 break;
3859
3860 case 'm': /* message of the day */
3861 if (pCEServing->motd == (char *)0)
3862 FileWrite(pCLServing->fd, FLAGFALSE,
3863 "-- MOTD --]\r\n", -1);
3864 else
3865 FilePrint(pCLServing->fd, FLAGFALSE,
3866 "-- MOTD -- %s]\r\n",
3867 pCEServing->motd);
3868 break;
3869
3870 case 'n': /* note message to log file */
3871 if (pCEServing->fdlog == (CONSFILE *)0) {
3872 FileWrite(pCLServing->fd, FLAGFALSE,
3873 "no log file on this console]\r\n",
3874 -1);
3875 } else {
3876 FileWrite(pCLServing->fd, FLAGFALSE,
3877 "Enter note: ", -1);
3878 BuildString((char *)0,
3879 pCLServing->accmd);
3880 pCLServing->iState = S_NOTE;
3881 }
3882 break;
3883
3884 case 'o': /* close and re-open line */
3885 CommandOpen(pGE, pCLServing, pCEServing,
3886 tyme);
3887 break;
3888
3889 case 'P': /* broadcast message */
3890 FilePrint(pCLServing->fd, FLAGFALSE,
3891 "set playback (%d): ",
3892 pCLServing->playback);
3893 BuildString((char *)0, pCLServing->accmd);
3894 pCLServing->iState = S_PLAYBACK;
3895 break;
3896
3897 case 'p': /* replay lines (longer, in theory) */
3898 FileWrite(pCLServing->fd, FLAGFALSE,
3899 "playback]\r\n", -1);
3900 Replay(pCEServing, pCLServing->fd,
3901 pCLServing->playback);
3902 break;
3903
3904 case '\022': /* ^R */
3905 FileWrite(pCLServing->fd, FLAGFALSE,
3906 "^R]\r\n", -1);
3907 Replay(pCEServing, pCLServing->fd, 1);
3908 break;
3909
3910 case 'R': /* broadcast message */
3911 FilePrint(pCLServing->fd, FLAGFALSE,
3912 "set replay (%d): ",
3913 pCLServing->replay);
3914 BuildString((char *)0, pCLServing->accmd);
3915 pCLServing->iState = S_REPLAY;
3916 break;
3917
3918 case 'r': /* replay lines */
3919 FileWrite(pCLServing->fd, FLAGFALSE,
3920 "replay]\r\n", -1);
3921 Replay(pCEServing, pCLServing->fd,
3922 pCLServing->replay);
3923 break;
3924
3925 case 's': /* spy mode */
3926 if (!pCLServing->fwr) {
3927 goto unknownchar;
3928 }
3929 pCLServing->fwantwr = 0;
3930 BumpClient(pCEServing, (char *)0);
3931 TagLogfileAct(pCEServing, "%s detached",
3932 pCLServing->acid->string);
3933 FindWrite(pCEServing);
3934 FileWrite(pCLServing->fd, FLAGFALSE,
3935 "spying]\r\n", -1);
3936 break;
3937
3938 case 'u': /* hosts on server this */
3939 FileWrite(pCLServing->fd, FLAGFALSE,
3940 "hosts]\r\n", -1);
3941 CommandHosts(pGE, pCLServing, pCEServing,
3942 tyme, (char *)0);
3943 break;
3944
3945 case 'v': /* version */
3946 FilePrint(pCLServing->fd, FLAGFALSE,
3947 "version `%s']\r\n",
3948 MyVersion());
3949 break;
3950
3951 case 'w': /* who */
3952 FilePrint(pCLServing->fd, FLAGFALSE,
3953 "who %s]\r\n",
3954 pCEServing->server);
3955 CommandWho(pGE, pCLServing, pCEServing,
3956 tyme);
3957 break;
3958
3959 case 'x':
3960 FileWrite(pCLServing->fd, FLAGFALSE,
3961 "examine]\r\n", -1);
3962 CommandExamine(pGE, pCLServing, pCEServing,
3963 tyme, (char *)0);
3964 break;
3965
3966 case 'z': /* suspend the client */
3967 case '\032':
3968 if (ConsentUserOk
3969 (pLUList,
3970 pCLServing->username->string) == 1)
3971 goto unknownchar;
3972 FileSetQuoteIAC(pCLServing->fd, FLAGFALSE);
3973 FilePrint(pCLServing->fd, FLAGFALSE,
3974 "%c%c", OB_IAC, OB_SUSP);
3975 FileSetQuoteIAC(pCLServing->fd, FLAGTRUE);
3976 bottomSuspend:
3977 pCLServing->fcon = 0;
3978 pCLServing->iState = S_SUSP;
3979 if (pCEServing->pCLwr == pCLServing) {
3980 BumpClient(pCEServing, (char *)0);
3981 TagLogfileAct(pCEServing,
3982 "%s detached",
3983 pCLServing->
3984 acid->string);
3985 FindWrite(pCEServing);
3986 }
3987 break;
3988
3989 case '!': /* invoke a task */
3990 if (!pCLServing->fwr) {
3991 goto unknownchar;
3992 }
3993 pCLServing->iState = S_TASK;
3994 FileWrite(pCLServing->fd, FLAGFALSE,
3995 "task ", -1);
3996 break;
3997
3998 case '|': /* wait for client */
3999 if (!pCLServing->fwr ||
4000 ConsentUserOk(pLUList,
4001 pCLServing->
4002 username->string) == 1)
4003 goto unknownchar;
4004 FileSetQuoteIAC(pCLServing->fd, FLAGFALSE);
4005 FilePrint(pCLServing->fd, FLAGFALSE,
4006 "%c%c", OB_IAC, OB_EXEC);
4007 FileSetQuoteIAC(pCLServing->fd, FLAGTRUE);
4008 pCLServing->fcon = 0;
4009 pCLServing->iState = S_CWAIT;
4010 break;
4011
4012 case '.': /* disconnect */
4013 case '\004':
4014 case '\003':
4015 FileWrite(pCLServing->fd, FLAGFALSE,
4016 "disconnect]\r\n", -1);
4017 DisconnectClient(pGE, pCLServing,
4018 (char *)0, FLAGFALSE);
4019 return;
4020
4021 case ' ': /* abort escape sequence */
4022 case '\n':
4023 case '\r':
4024 FileWrite(pCLServing->fd, FLAGFALSE,
4025 "ignored]\r\n", -1);
4026 break;
4027
4028 case '\\': /* quote mode (send ^Q,^S) */
4029 if (pCEServing->fronly) {
4030 FileWrite(pCLServing->fd, FLAGFALSE,
4031 "can't write to read-only console]\r\n",
4032 -1);
4033 continue;
4034 }
4035 if (!pCLServing->fwr) {
4036 FileWrite(pCLServing->fd, FLAGFALSE,
4037 "attach to send character]\r\n",
4038 -1);
4039 continue;
4040 }
4041 BuildString((char *)0, pCLServing->accmd);
4042 pCLServing->iState = S_QUOTE;
4043 FileWrite(pCLServing->fd, FLAGFALSE,
4044 "quote \\", -1);
4045 break;
4046
4047 case 0xD6: /* 'v' with high bit set */
4048 /* this is really just used "behind the scenes",
4049 * but of someone wants to type it, fine...we
4050 * just need a way to query the server for what
4051 * version it is so the client can determine
4052 * functionality
4053 */
4054 FilePrint(pCLServing->fd, FLAGFALSE,
4055 "%u]\r\n", VERSION_UINT);
4056 break;
4057
4058 default: /* unknown sequence */
4059 unknownchar:
4060 #if USE_EXTENDED_MESSAGES
4061 FilePrint(pCLServing->fd, FLAGFALSE,
4062 "unknown -- use `?'%s]\r\n",
4063 Challenge());
4064 #else
4065 FileWrite(pCLServing->fd, FLAGFALSE,
4066 "unknown -- use `?']\r\n", -1);
4067 #endif
4068 break;
4069 }
4070 continue;
4071 }
4072 }
4073 nr -= l;
4074 MemMove(acIn, acIn + l, nr);
4075 }
4076 }
4077
4078 void
FlushConsole(CONSENT * pCEServing)4079 FlushConsole(CONSENT *pCEServing)
4080 {
4081 static STRING *buf = (STRING *)0;
4082 int offset = 0;
4083 /* we buffered console data in PutConsole() so that we can
4084 * send more than 1-byte payloads, if we get more than 1-byte
4085 * of data from a client connection. here we flush that buffer,
4086 * possibly putting it into the write buffer (but we don't really
4087 * need to worry about that here.
4088 */
4089 if (pCEServing->wbuf->used <= 1) {
4090 return;
4091 }
4092 if (!(pCEServing->fup && pCEServing->ioState == ISNORMAL)) {
4093 /* if we have data but aren't up, drop it */
4094 BuildString((char *)0, pCEServing->wbuf);
4095 pCEServing->wbufIAC = 0;
4096 return;
4097 }
4098
4099 if (buf == (STRING *)0)
4100 buf = AllocString();
4101 BuildString((char *)0, buf);
4102
4103 /* while wbuf
4104 * if wbufIAC == 1, yikes
4105 * else if wbufIAC == 0, buffer all data, move offset
4106 * else if wbufIAC > 2, buffer data, wbufIAC = 2, move offset
4107 * else if wbufIAC == 2, then
4108 * if heavy
4109 * write buffer
4110 * if flushed, do heavy, else break
4111 * break
4112 * else if light
4113 * buffer data
4114 * search for new wbufIAC
4115 */
4116 {
4117 static STRING *s;
4118 if (s == (STRING *)0)
4119 s = AllocString();
4120 BuildString((char *)0, s);
4121 FmtCtlStr(pCEServing->wbuf->string, pCEServing->wbuf->used, s);
4122 CONDDEBUG((1, "Kiddie(): wbuf=%s", s->string));
4123 }
4124
4125 while (pCEServing->wbuf->used > 1 &&
4126 offset < pCEServing->wbuf->used - 1) {
4127 CONDDEBUG((1, "Kiddie(): wbuf->used=%d, offset=%d, wbufIAC=%d",
4128 pCEServing->wbuf->used, offset, pCEServing->wbufIAC));
4129 if (pCEServing->wbufIAC >= pCEServing->wbuf->used) {
4130 /* this should never really happen...but in case it
4131 * does, just reset wbufIAC and try again.
4132 */
4133 CONDDEBUG((1, "Kiddie(): invalid wbufIAC setting for [%s]",
4134 pCEServing->server));
4135 } else if (pCEServing->wbufIAC == 1) {
4136 Error("[%s] internal failure: wbufIAC==1", pCEServing->server);
4137 offset = pCEServing->wbuf->used - 1; /* bail */
4138 } else if (pCEServing->wbufIAC == 0) {
4139 CONDDEBUG((1,
4140 "Kiddie(): flushing final %d non-IAC bytes to [%s]",
4141 pCEServing->wbuf->used - 1 - offset,
4142 pCEServing->server));
4143 BuildStringN(pCEServing->wbuf->string + offset,
4144 pCEServing->wbuf->used - 1 - offset, buf);
4145 offset = pCEServing->wbuf->used - 1;
4146 } else if (pCEServing->wbufIAC > 2) {
4147 CONDDEBUG((1, "Kiddie(): flushing %d non-IAC bytes to [%s]",
4148 pCEServing->wbufIAC - 2, pCEServing->server));
4149 BuildStringN(pCEServing->wbuf->string + offset,
4150 pCEServing->wbufIAC - 2, buf);
4151 offset += pCEServing->wbufIAC - 2;
4152 pCEServing->wbufIAC = 2;
4153 continue;
4154 } else { /* wbufIAC == 2 */
4155 unsigned char next =
4156 (unsigned char)pCEServing->wbuf->string[offset + 1];
4157 if ((next >= '0' && next <= '9') ||
4158 (next >= 'a' && next <= 'z')
4159 || (next == BREAK && pCEServing->type != HOST)) {
4160 CONDDEBUG((1, "Kiddie(): heavy IAC for [%s]",
4161 pCEServing->server));
4162 offset += 2;
4163 /* if we have buffered data, send it */
4164 if (buf->used > 1) {
4165 CONDDEBUG((1,
4166 "Kiddie(): heavy IAC flushing %d leading bytes for [%s]",
4167 buf->used - 1, pCEServing->server));
4168 if (FileWrite
4169 (pCEServing->cofile, FLAGFALSE, buf->string,
4170 buf->used - 1) < 0) {
4171 Error("[%s] write failure", pCEServing->server);
4172 ConsoleError(pCEServing);
4173 BuildString((char *)0, buf);
4174 break;
4175 }
4176 BuildString((char *)0, buf);
4177 }
4178 /* if we didn't flush everything, bail and get
4179 * it the next time around (hopefully it'll have
4180 * cleared...or will soon.
4181 */
4182 if (!FileBufEmpty(pCEServing->cofile)) {
4183 CONDDEBUG((1,
4184 "Kiddie(): heavy IAC (wait for flush) for [%s]",
4185 pCEServing->server));
4186 break;
4187 }
4188
4189 /* Do the operation */
4190 if ((next >= '0' && next <= '9') ||
4191 (next >= 'a' && next <= 'z')) {
4192 int delay = BREAKDELAYDEFAULT;
4193 int bnum =
4194 next - '1' - (next > '9' ? BREAKALPHAOFFSET : 0);
4195 if (next != '0')
4196 delay = breakList[bnum].delay;
4197 /* in theory this sets the break length to whatever
4198 * the "default" break sequence is for the console.
4199 * but, i think it would be better to just use the
4200 * global default (250ms right now) so that you
4201 * don't have to change things or get anything
4202 * unexpected. remember, this is really just for
4203 * idle strings...
4204 else {
4205 if (pCEServing->breakNum != 0 &&
4206 breakList[pCEServbing->breakNum -
4207 1].seq->used <= 1)
4208 delay =
4209 breakList[pCEServbing->breakNum -
4210 1].delay;
4211 }
4212 */
4213 CONDDEBUG((1,
4214 "Kiddie(): heavy IAC - doing usleep() for [%s] (break #%c - delay %dms)",
4215 pCEServing->server, next, delay));
4216 if (delay != 0)
4217 usleep(delay * 1000);
4218 } else if (next == BREAK) {
4219 CONDDEBUG((1,
4220 "Kiddie(): heavy IAC - sending break for [%s]",
4221 pCEServing->server));
4222 #if HAVE_FREEIPMI
4223 if (pCEServing->type == IPMI) {
4224 if (ipmiconsole_ctx_generate_break
4225 (pCEServing->ipmictx) == -1) {
4226 if (pCEServing->pCLwr != (CONSCLIENT *)0)
4227 FilePrint(pCEServing->pCLwr->fd, FLAGFALSE,
4228 "[ipmiconsole_ctx_generate_break() failed: %s]\r\n",
4229 ipmiconsole_ctx_errormsg
4230 (pCEServing->ipmictx));
4231
4232 }
4233 } else {
4234 #endif
4235
4236 if (tcsendbreak(FileFDNum(pCEServing->cofile), 0)
4237 == -1) {
4238 if (pCEServing->pCLwr != (CONSCLIENT *)0)
4239 FileWrite(pCEServing->pCLwr->fd, FLAGFALSE,
4240 "[tcsendbreak() failed]\r\n",
4241 -1);
4242 }
4243 #if HAVE_FREEIPMI
4244 }
4245 #endif
4246 }
4247 /* we do this 'cause we just potentially paused for
4248 * a half-second doing a break...or even the
4249 * intentional usleep(). we could take out the
4250 * justHadDelay bits and continue with the stream,
4251 * but this allows us to process other consoles and
4252 * then come around and do more on this one. you
4253 * see, someone could have a '\d\z\d\z\d\z' sequence
4254 * as a break string and we'd have about a 2 second
4255 * delay added up if we process it all at once.
4256 * we're just trying to be nice here.
4257 */
4258 break;
4259 } else {
4260 CONDDEBUG((1, "Kiddie(): soft IAC for fd [%s]",
4261 pCEServing->server));
4262 offset += 2;
4263 if (next == IAC) {
4264 CONDDEBUG((1,
4265 "Kiddie(): soft IAC processing IAC for [%s]",
4266 pCEServing->server));
4267 BuildStringChar((char)IAC, buf);
4268 } else if (next == BREAK && pCEServing->type == HOST) {
4269 CONDDEBUG((1,
4270 "Kiddie(): soft IAC processing HOST BREAK for [%s]",
4271 pCEServing->server));
4272 BuildStringChar((char)IAC, buf);
4273 BuildStringChar((char)BREAK, buf);
4274 } else {
4275 CONDDEBUG((1,
4276 "Kiddie(): soft IAC unprocessable IAC for [%s]",
4277 pCEServing->server));
4278 }
4279 }
4280 }
4281
4282 /* hunt for a new IAC position */
4283 if (offset < pCEServing->wbuf->used - 1) {
4284 char *iac = StringChar(pCEServing->wbuf, offset, (char)IAC);
4285 CONDDEBUG((1, "Kiddie(): hunting for new IAC for [%s]",
4286 pCEServing->server));
4287 if (iac == (char *)0)
4288 pCEServing->wbufIAC = 0;
4289 else
4290 pCEServing->wbufIAC =
4291 (iac - pCEServing->wbuf->string - offset) + 2;
4292 } else
4293 pCEServing->wbufIAC = 0;
4294 }
4295
4296 if (buf->used > 1) {
4297 CONDDEBUG((1, "Kiddie(): flushing buffer of %d bytes for [%s]",
4298 buf->used - 1, pCEServing->server));
4299 if (FileWrite
4300 (pCEServing->cofile, FLAGFALSE, buf->string,
4301 buf->used - 1) < 0) {
4302 Error("[%s] write failure", pCEServing->server);
4303 ConsoleError(pCEServing);
4304 return;
4305 }
4306 BuildString((char *)0, buf);
4307 }
4308
4309 /* nuke the data alread sent */
4310 if (offset >= pCEServing->wbuf->used - 1) {
4311 BuildString((char *)0, pCEServing->wbuf);
4312 } else if (offset > 0) {
4313 ShiftString(pCEServing->wbuf, offset);
4314 }
4315
4316 if (pCEServing->wbuf->used > 1) {
4317 char *iac = StringChar(pCEServing->wbuf, 0, (char)IAC);
4318 CONDDEBUG((1, "Kiddie(): hunting for new IAC for [%s]",
4319 pCEServing->server));
4320 if (iac == (char *)0)
4321 pCEServing->wbufIAC = 0;
4322 else
4323 pCEServing->wbufIAC = (iac - pCEServing->wbuf->string) + 2;
4324 CONDDEBUG((1,
4325 "Kiddie(): watching writability for fd %d 'cause we have buffered data",
4326 FileFDNum(pCEServing->cofile)));
4327 FD_SET(FileFDNum(pCEServing->cofile), &winit);
4328 } else {
4329 pCEServing->wbufIAC = 0;
4330 if (FileBufEmpty(pCEServing->cofile)) {
4331 CONDDEBUG((1,
4332 "Kiddie(): removing writability for fd %d 'cause we don't have buffered data",
4333 FileFDNum(pCEServing->cofile)));
4334 FD_CLR(FileFDNum(pCEServing->cofile), &winit);
4335 }
4336 }
4337 pCEServing->lastWrite = time((time_t *)0);
4338 if (pCEServing->idletimeout != (time_t)0 &&
4339 (timers[T_CIDLE] == (time_t)0 ||
4340 timers[T_CIDLE] >
4341 pCEServing->lastWrite + pCEServing->idletimeout))
4342 timers[T_CIDLE] = pCEServing->lastWrite + pCEServing->idletimeout;
4343 }
4344
4345 /* routine used by the child processes. (ksb/fine)
4346 * Most of it is escape sequence parsing.
4347 * fine:
4348 * All of it is squirrely code, for which I most humbly apologize
4349 * ksb:
4350 * Note the states a client can be in, all of the client processing
4351 * is done one character at a time, we buffer and shift a lot -- this
4352 * stops the denial of services attack where a user telnets to the
4353 * group port and just hangs it (by not following the protocol). I've
4354 * repaired this by letting all new clients on a bogus console that is
4355 * a kinda control line for the group. They have to use the `;'
4356 * command to shift to a real console before they can get any (real)
4357 * thrills.
4358 *
4359 * If you were not awake in your finite state machine course this code
4360 * should scare the shit out of you; but there are a few invarients:
4361 * - the fwr (I can write) bit always is set *after* the
4362 * notification that to the console (and reset before)
4363 * - we never look at more than one character at a time, even
4364 * when we read a hunk from the MUX we string it out in a loop
4365 * - look at the output (x, u, w) and attach (a, f, ;) commands
4366 * for more clues
4367 *
4368 * NB: the ZZZ markers below indicate places where I didn't have time
4369 * (machine?) to test some missing bit of tty diddling, I'd love
4370 * patches for other ioctl/termio/termios stuff -- ksb
4371 *
4372 */
4373 static void
Kiddie(GRPENT * pGE,int sfd)4374 Kiddie(GRPENT *pGE, int sfd)
4375 {
4376 CONSCLIENT *pCL, /* console we must scan/notify */
4377 *pCLServing; /* client we are serving */
4378 CONSENT *pCEServing; /* console we are talking to */
4379 GRPENT *pGEtmp;
4380 REMOTE *pRCtmp;
4381 int ret;
4382 time_t tyme;
4383 time_t tymer;
4384 int fd;
4385 socklen_t so;
4386 fd_set rmask;
4387 fd_set wmask;
4388 struct timeval tv;
4389 struct timeval *tvp;
4390
4391
4392 /* nuke the other group lists - of no use in the child */
4393 while (pGroups != (GRPENT *)0) {
4394 pGEtmp = pGroups->pGEnext;
4395 if (pGroups != pGE)
4396 DestroyGroup(pGroups);
4397 pGroups = pGEtmp;
4398 }
4399 pGroups = pGE;
4400 pGE->pGEnext = (GRPENT *)0;
4401
4402 /* nuke the remote consoles - of no use in the child */
4403 while (pRCList != (REMOTE *)0) {
4404 pRCtmp = pRCList->pRCnext;
4405 DestroyRemoteConsole(pRCList);
4406 pRCList = pRCtmp;
4407 }
4408
4409 if ((pGE->pCEctl = (CONSENT *)calloc(1, sizeof(CONSENT)))
4410 == (CONSENT *)0)
4411 OutOfMem();
4412
4413 /* turn off signals that master() might have turned on
4414 * (only matters if respawned)
4415 */
4416 SimpleSignal(SIGQUIT, SIG_IGN);
4417 SimpleSignal(SIGPIPE, SIG_IGN);
4418 #if defined(SIGTTOU)
4419 SimpleSignal(SIGTTOU, SIG_IGN);
4420 #endif
4421 #if defined(SIGTTIN)
4422 SimpleSignal(SIGTTIN, SIG_IGN);
4423 #endif
4424 #if defined(SIGPOLL)
4425 SimpleSignal(SIGPOLL, SIG_IGN);
4426 #endif
4427 #if defined(SIGXFSZ)
4428 SimpleSignal(SIGXFSZ, SIG_IGN);
4429 #endif
4430 SimpleSignal(SIGTERM, FlagGoAway);
4431 SimpleSignal(SIGCHLD, FlagReapVirt);
4432 SimpleSignal(SIGINT, FlagGoAwayAlso);
4433
4434 BuildTmpString((char *)0);
4435 if ((pGE->pCEctl->server =
4436 StrDup(BuildTmpStringPrint("ctl_%hu", pGE->port)))
4437 == (char *)0)
4438 OutOfMem();
4439
4440 /* set up stuff for the select() call once, then just copy it
4441 * rinit is all the fd's we might get data on, we copy it
4442 * to rmask before we call select, this saves lots of prep work
4443 * we used to do in the loop, but we have to mod rinit whenever
4444 * we add a connection or drop one... (ksb)
4445 */
4446 /*maxfd = GetMaxFiles(); */
4447 FD_ZERO(&rinit);
4448 FD_ZERO(&winit);
4449 FD_SET(sfd, &rinit);
4450 if (maxfd < sfd + 1)
4451 maxfd = sfd + 1;
4452 /* open all the files we need for the consoles in our group
4453 * if we can't get one (bitch and) flag as down
4454 */
4455 ReUp(pGE, 0);
4456
4457 /* prime the list of free connection slots
4458 */
4459 if ((pGE->pCLfree = (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT)))
4460 == (CONSCLIENT *)0)
4461 OutOfMem();
4462 pGE->pCLfree->acid = AllocString();
4463 pGE->pCLfree->username = AllocString();
4464 pGE->pCLfree->peername = AllocString();
4465 pGE->pCLfree->accmd = AllocString();
4466
4467 /* on a SIGHUP we should close and reopen our log files and
4468 * reread the config file
4469 */
4470 SimpleSignal(SIGHUP, FlagSawChldHUP);
4471
4472 /* on a SIGUSR2 we should close and reopen our log files
4473 */
4474 SimpleSignal(SIGUSR2, FlagSawChldUSR2);
4475
4476 /* on a SIGUSR1 we try to bring up all downed consoles */
4477 SimpleSignal(SIGUSR1, FlagReUp);
4478
4479 /* prime the pump */
4480 RollLogs(pGE);
4481 Mark(pGE);
4482
4483 /* the MAIN loop a group server
4484 */
4485 pGE->pCLall = (CONSCLIENT *)0;
4486 while (1) {
4487 /* check signal flags */
4488 if (fSawGoAway) {
4489 fSawGoAway = 0;
4490 DeUtmp(pGE, sfd);
4491 }
4492 if (fSawReapVirt) {
4493 fSawReapVirt = 0;
4494 ReapVirt(pGE);
4495 }
4496 if (fSawChldHUP) {
4497 fSawChldHUP = 0;
4498 ReopenLogfile();
4499 ReopenUnifiedlog();
4500 ReReadCfg(sfd, -1);
4501 pGE = pGroups;
4502 ReOpen(pGE);
4503 ReUp(pGE, 0);
4504 }
4505 if (fSawChldUSR2) {
4506 fSawChldUSR2 = 0;
4507 ReopenLogfile();
4508 ReopenUnifiedlog();
4509 ReOpen(pGE);
4510 ReUp(pGE, 0);
4511 }
4512 if (fSawReUp) {
4513 fSawReUp = 0;
4514 ReUp(pGE, 0);
4515 }
4516
4517 /* check for timeouts with consoles */
4518 tymer = (time_t)0;
4519 tyme = time((time_t *)0);
4520
4521 /* check for state timeouts (currently connect() timeouts) */
4522 if (timers[T_STATE] != (time_t)0 && tyme >= timers[T_STATE]) {
4523 timers[T_STATE] = (time_t)0;
4524 for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0;
4525 pCEServing = pCEServing->pCEnext) {
4526 if (pCEServing->stateTimer == (time_t)0)
4527 continue;
4528 if (pCEServing->stateTimer > tyme) {
4529 if (timers[T_STATE] == (time_t)0 ||
4530 timers[T_STATE] > pCEServing->stateTimer)
4531 timers[T_STATE] = pCEServing->stateTimer;
4532 continue;
4533 }
4534 pCEServing->stateTimer = (time_t)0;
4535 if (pCEServing->ioState != INCONNECT)
4536 continue;
4537 SendIWaitClientsMsg(pCEServing, "down]\r\n");
4538 Error("[%s] connect timeout: forcing down",
4539 pCEServing->server);
4540 /* can't use ConsoleError() here otherwise we could reinit
4541 * the console repeatedly (immediately). we know there are
4542 * no clients attached, so it's basically the same.
4543 */
4544 ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
4545 }
4546 }
4547
4548 /* process any idle timeouts */
4549 if (timers[T_CIDLE] != (time_t)0 && tyme >= timers[T_CIDLE]) {
4550 timers[T_CIDLE] = (time_t)0;
4551 for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0;
4552 pCEServing = pCEServing->pCEnext) {
4553 /* if we aren't in a normal state, skip it */
4554 if (!(pCEServing->fup && pCEServing->ioState == ISNORMAL))
4555 continue;
4556 /* should we check for a r/w user too and only idle when
4557 * they aren't connected? right now, i think we want to
4558 * do the idle stuff even if we have users
4559 */
4560 if (pCEServing->idletimeout != 0) {
4561 time_t chime =
4562 pCEServing->lastWrite + pCEServing->idletimeout;
4563 if (tyme < chime) {
4564 if (timers[T_CIDLE] == (time_t)0 ||
4565 timers[T_CIDLE] > chime)
4566 timers[T_CIDLE] = chime;
4567 continue;
4568 }
4569 ExpandString(pCEServing->idlestring, pCEServing, 0);
4570 TagLogfileAct(pCEServing, "idle timeout");
4571 FlushConsole(pCEServing);
4572 SendClientsMsg(pCEServing, "[-- idle timeout --]\r\n");
4573 /* we're not technically correct here in saying the write
4574 * happened, but we don't want to accidentally trigger
4575 * another idle action, so we lie...when the buffer gets
4576 * flushed, this will be updated and correct.
4577 */
4578 pCEServing->lastWrite = tyme;
4579 chime = tyme + pCEServing->idletimeout;
4580 if (timers[T_CIDLE] == (time_t)0 ||
4581 timers[T_CIDLE] > chime)
4582 timers[T_CIDLE] = chime;
4583 }
4584 }
4585 }
4586
4587 /* see if we need to bring things back up or mark logfiles
4588 * or do other such events here. we call time() each time
4589 * in case one of the subroutines actually takes a long time
4590 * to complete */
4591 if (timers[T_MARK] != (time_t)0 &&
4592 time((time_t *)0) >= timers[T_MARK])
4593 Mark(pGE);
4594
4595 if (timers[T_INITDELAY] != (time_t)0 &&
4596 time((time_t *)0) >= timers[T_INITDELAY]) {
4597 timers[T_INITDELAY] = (time_t)0;
4598 ReUp(pGE, -1);
4599 }
4600
4601 if (timers[T_REINIT] != (time_t)0 &&
4602 time((time_t *)0) >= timers[T_REINIT])
4603 ReUp(pGE, 2);
4604
4605 /* must do ReUp(,1) last for timers to work right */
4606 if (timers[T_AUTOUP] != (time_t)0 &&
4607 time((time_t *)0) >= timers[T_AUTOUP])
4608 ReUp(pGE, 1);
4609
4610 /* do we need to check on log file sizes? */
4611 if (timers[T_ROLL] != (time_t)0 &&
4612 time((time_t *)0) >= timers[T_ROLL])
4613 RollLogs(pGE);
4614
4615 /* check on various timers and set the appropriate timeout */
4616 /* all this so we don't have to use alarm() any more... */
4617
4618 /* look for the next nearest timeout */
4619 for (ret = 0; ret < T_MAX; ret++) {
4620 if (timers[ret] != (time_t)0 &&
4621 (tymer == (time_t)0 || tymer > timers[ret]))
4622 tymer = timers[ret];
4623 }
4624
4625 /* if we have a timer, figure out the delay left */
4626 if (tymer != (time_t)0) {
4627 tyme = time((time_t *)0);
4628 if (tymer > tyme) /* in the future */
4629 tv.tv_sec = tymer - tyme;
4630 else /* now or in the past */
4631 tv.tv_sec = 1;
4632 tv.tv_usec = 0;
4633 tvp = &tv;
4634 } else /* no timeout */
4635 tvp = (struct timeval *)0;
4636 if (tvp == (struct timeval *)0) {
4637 CONDDEBUG((1, "Kiddie(): no select timeout"));
4638 } else {
4639 CONDDEBUG((1, "Kiddie(): select timeout of %d seconds",
4640 tv.tv_sec));
4641 }
4642
4643 rmask = rinit;
4644 wmask = winit;
4645
4646 if ((ret = select(maxfd, &rmask, &wmask, (fd_set *)0, tvp)) == -1) {
4647 if (errno != EINTR) {
4648 Error("Kiddie(): select(): %s", strerror(errno));
4649 break;
4650 }
4651 continue;
4652 }
4653
4654 if (ret == 0) /* timeout -- loop back up and handle it */
4655 continue;
4656
4657 /* anything on a console? */
4658 for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0;
4659 pCEServing = pCEServing->pCEnext) {
4660 if (!pCEServing->fup || pCEServing->type == NOOP)
4661 continue;
4662 switch (pCEServing->ioState) {
4663 case INCONNECT:
4664 #if HAVE_FREEIPMI
4665 if (pCEServing->type == IPMI) {
4666 if (FileCanRead
4667 (pCEServing->cofile, &rmask, &wmask)) {
4668 if (IPMICONSOLE_CTX_STATUS_SOL_ESTABLISHED ==
4669 ipmiconsole_ctx_status
4670 (pCEServing->ipmictx)) {
4671 /* Read in the NULL from OUTPUT_ON_SOL_ESTABLISHED flag */
4672 char b[1];
4673 FileRead(pCEServing->cofile, b, 1); /* trust it's NULL */
4674 } else {
4675 Error("[%s] IPMI error: %s: forcing down",
4676 pCEServing->server,
4677 ipmiconsole_ctx_errormsg(pCEServing->
4678 ipmictx));
4679 /* no ConsoleError() for same reason as above */
4680 SendIWaitClientsMsg(pCEServing,
4681 "down]\r\n");
4682 ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
4683 break;
4684 }
4685 } else
4686 break;
4687 } else {
4688 #endif /* freeipmi */
4689 /* deal with this state above as well */
4690 if (FileCanWrite
4691 (pCEServing->cofile, &rmask, &wmask)) {
4692 socklen_t slen;
4693 int flags = 0;
4694 int cofile = FileFDNum(pCEServing->cofile);
4695 slen = sizeof(flags);
4696 /* So, getsockopt seems to return -1 if there is
4697 * something interesting in SO_ERROR under
4698 * solaris...sheesh. So, the error message has
4699 * the small change it's not accurate.
4700 */
4701 if (getsockopt
4702 (cofile, SOL_SOCKET, SO_ERROR,
4703 (char *)&flags, &slen) < 0) {
4704 Error
4705 ("[%s] getsockopt(%u,SO_ERROR): %s: forcing down",
4706 pCEServing->server, cofile,
4707 strerror(errno));
4708 /* no ConsoleError() for same reason as above */
4709 SendIWaitClientsMsg(pCEServing,
4710 "down]\r\n");
4711 ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
4712 break;
4713 }
4714 if (flags != 0) {
4715 Error("[%s] connect(%u): %s: forcing down",
4716 pCEServing->server, cofile,
4717 strerror(flags));
4718 /* no ConsoleError() for same reason as above */
4719 SendIWaitClientsMsg(pCEServing,
4720 "down]\r\n");
4721 ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
4722 break;
4723 }
4724
4725 /* waiting for a connect(), we watch the write bit,
4726 * so switch around and now watch for the read and
4727 * start gathering data
4728 */
4729 FD_SET(cofile, &rinit);
4730 FD_CLR(cofile, &winit);
4731 } else
4732 break;
4733 #if HAVE_FREEIPMI
4734 }
4735 #endif
4736
4737 pCEServing->ioState = ISNORMAL;
4738 pCEServing->lastWrite = time((time_t *)0);
4739 #if HAVE_GETTIMEOFDAY
4740 if (gettimeofday(&tv, (void *)0) == 0)
4741 pCEServing->lastInit = tv;
4742 #else
4743 if ((tv = time((time_t *)0)) != (time_t)-1)
4744 pCEServing->lastInit = tv;
4745 #endif
4746 if (pCEServing->idletimeout != (time_t)0 &&
4747 (timers[T_CIDLE] == (time_t)0 ||
4748 timers[T_CIDLE] >
4749 pCEServing->lastWrite + pCEServing->idletimeout))
4750 timers[T_CIDLE] =
4751 pCEServing->lastWrite +
4752 pCEServing->idletimeout;
4753 if (pCEServing->downHard == FLAGTRUE) {
4754 Msg("[%s] console up", pCEServing->server);
4755 pCEServing->downHard = FLAGFALSE;
4756 }
4757 SendIWaitClientsMsg(pCEServing, "up]\r\n");
4758 StartInit(pCEServing);
4759 break;
4760 case ISNORMAL:
4761 if (FileCanRead(pCEServing->cofile, &rmask, &wmask))
4762 DoConsoleRead(pCEServing);
4763 if (FileCanRead(pCEServing->initfile, &rmask, &wmask))
4764 DoCommandRead(pCEServing);
4765 if (FileCanRead(pCEServing->taskfile, &rmask, &wmask))
4766 DoTaskRead(pCEServing);
4767 /* fall through to ISFLUSHING for buffered data */
4768 case ISFLUSHING:
4769 /* write cofile data */
4770 if (!FileBufEmpty(pCEServing->cofile) &&
4771 FileCanWrite(pCEServing->cofile, &rmask, &wmask)) {
4772 CONDDEBUG((1, "Master(): flushing fd %d",
4773 FileFDNum(pCEServing->cofile)));
4774 if (FileWrite
4775 (pCEServing->cofile, FLAGFALSE, (char *)0,
4776 0) < 0) {
4777 Error("[%s] write failure",
4778 pCEServing->server);
4779 ConsoleError(pCEServing);
4780 break;
4781 }
4782 }
4783 /* write fdlog data */
4784 if (!FileBufEmpty(pCEServing->fdlog) &&
4785 FileCanWrite(pCEServing->fdlog, &rmask, &wmask)) {
4786 CONDDEBUG((1, "Kiddie(): flushing fd %d",
4787 FileFDNum(pCEServing->fdlog)));
4788 if (FileWrite
4789 (pCEServing->fdlog, FLAGFALSE, (char *)0,
4790 0) < 0) {
4791 Error("[%s] write failure",
4792 pCEServing->server);
4793 ConsoleError(pCEServing);
4794 break;
4795 }
4796 }
4797 /* write initfile data */
4798 if (!FileBufEmpty(pCEServing->initfile) &&
4799 FileCanWrite(pCEServing->initfile, &rmask,
4800 &wmask)) {
4801 CONDDEBUG((1, "Kiddie(): flushing fd %d",
4802 FileFDNum(pCEServing->initfile)));
4803 if (FileWrite
4804 (pCEServing->initfile, FLAGFALSE, (char *)0,
4805 0) < 0) {
4806 Error("[%s] write failure",
4807 pCEServing->server);
4808 ConsoleError(pCEServing);
4809 break;
4810 }
4811 }
4812 /* stop if we're in ISFLUSHING state and out of data */
4813 if ((pCEServing->ioState == ISFLUSHING) &&
4814 FileBufEmpty(pCEServing->cofile) &&
4815 FileBufEmpty(pCEServing->fdlog) &&
4816 FileBufEmpty(pCEServing->initfile))
4817 /* no ConsoleError() for same reason as above */
4818 ConsDown(pCEServing, FLAGFALSE, FLAGTRUE);
4819 break;
4820 default:
4821 /* this really can't ever happen */
4822 Error
4823 ("Kiddie(): console socket state == %d -- THIS IS A BUG",
4824 pCEServing->ioState);
4825 /* no ConsoleError() for same reason as above */
4826 ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
4827 break;
4828 }
4829 }
4830
4831 /* anything on a client? */
4832 for (pCLServing = pGE->pCLall; (CONSCLIENT *)0 != pCLServing;
4833 pCLServing = pCLServing->pCLscan) {
4834 switch (pCLServing->ioState) {
4835 #if HAVE_OPENSSL
4836 case INSSLACCEPT:
4837 if (FileCanSSLAccept(pCLServing->fd, &rmask, &wmask)) {
4838 int r;
4839 if ((r = FileSSLAccept(pCLServing->fd)) < 0)
4840 DisconnectClient(pGE, pCLServing, (char *)0,
4841 FLAGFALSE);
4842 else if (r == 1)
4843 pCLServing->ioState = ISNORMAL;
4844 }
4845 break;
4846 #endif
4847 #if HAVE_GSSAPI
4848 case INGSSACCEPT:
4849 {
4850 int r;
4851 if ((r = AttemptGSSAPI(pCLServing)) < 0)
4852 DisconnectClient(pGE, pCLServing, (char *)0,
4853 FLAGFALSE);
4854 else if (r == 1)
4855 pCLServing->ioState = ISNORMAL;
4856 }
4857 break;
4858 #endif
4859 case ISNORMAL:
4860 if (FileCanRead(pCLServing->fd, &rmask, &wmask))
4861 DoClientRead(pGE, pCLServing);
4862 /* fall through to ISFLUSHING for buffered data */
4863 case ISFLUSHING:
4864 if (!FileBufEmpty(pCLServing->fd) &&
4865 FileCanWrite(pCLServing->fd, &rmask, &wmask)) {
4866 CONDDEBUG((1, "Kiddie(): flushing fd %d",
4867 FileFDNum(pCLServing->fd)));
4868 if (FileWrite
4869 (pCLServing->fd, FLAGFALSE, (char *)0, 0)
4870 < 0) {
4871 DisconnectClient(pGE, pCLServing, (char *)0,
4872 FLAGTRUE);
4873 break;
4874 }
4875 }
4876 if ((pCLServing->ioState == ISFLUSHING) &&
4877 FileBufEmpty(pCLServing->fd))
4878 DisconnectClient(pGE, pCLServing, (char *)0,
4879 FLAGFALSE);
4880 break;
4881 default:
4882 /* this really can't ever happen */
4883 Error
4884 ("Kiddie(): client socket state == %d -- THIS IS A BUG",
4885 pCLServing->ioState);
4886 DisconnectClient(pGE, pCLServing, (char *)0,
4887 FLAGFALSE);
4888 break;
4889 }
4890 }
4891
4892 /* we buffered console data in PutConsole() so that we can
4893 * send more than 1-byte payloads, if we get more than 1-byte
4894 * of data from a client connection. here we flush that buffer,
4895 * possibly putting it into the write buffer (but we don't really
4896 * need to worry about that here).
4897 */
4898 for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0;
4899 pCEServing = pCEServing->pCEnext)
4900 FlushConsole(pCEServing);
4901
4902 /* if nothing on control line, get more
4903 */
4904 if (!FD_ISSET(sfd, &rmask)) {
4905 continue;
4906 }
4907
4908 /* accept new connections and deal with them
4909 */
4910 #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION
4911 dmallocMarkClientConnection = dmalloc_mark();
4912 #endif
4913 so = sizeof(INADDR_STYPE);
4914 fd = accept(sfd, (struct sockaddr *)&pGE->pCLfree->cnct_port, &so);
4915 if (fd < 0) {
4916 Error("Kiddie(): accept(): %s", strerror(errno));
4917 #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION
4918 CONDDEBUG((1, "Kiddie(): dmalloc / MarkClientConnection"));
4919 dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1);
4920 #endif
4921 continue;
4922 }
4923
4924 if (SetFlags(fd, O_NONBLOCK, 0)) {
4925 pGE->pCLfree->fd = FileOpenFD(fd, simpleSocket);
4926 FileSetQuoteIAC(pGE->pCLfree->fd, FLAGTRUE);
4927 } else
4928 pGE->pCLfree->fd = (CONSFILE *)0;
4929
4930 if ((CONSFILE *)0 == pGE->pCLfree->fd) {
4931 Error("Kiddie(): FileOpenFD(): %s", strerror(errno));
4932 close(fd);
4933 #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION
4934 CONDDEBUG((1, "Kiddie(): dmalloc / MarkClientConnection"));
4935 dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1);
4936 #endif
4937 continue;
4938 }
4939
4940 /* save pCL so we can advance to the next free one
4941 */
4942 pCL = pGE->pCLfree;
4943 pGE->pCLfree = pCL->pCLnext;
4944
4945 /* init the identification stuff
4946 */
4947 BuildString((char *)0, pCL->peername);
4948 BuildString((char *)0, pCL->acid);
4949 BuildString("<unknown>@", pCL->acid);
4950 BuildString((char *)0, pCL->username);
4951 BuildString("<unknown>", pCL->username);
4952 StrCpy(pCL->actym, StrTime(&(pCL->tym)), sizeof(pCL->actym));
4953 pCL->typetym = pCL->tym;
4954
4955 /* link into the control list for the dummy console
4956 */
4957 pCL->pCEto = pGE->pCEctl;
4958 pCL->pCLnext = pGE->pCEctl->pCLon;
4959 pCL->ppCLbnext = &pGE->pCEctl->pCLon;
4960 if ((CONSCLIENT *)0 != pCL->pCLnext) {
4961 pCL->pCLnext->ppCLbnext = &pCL->pCLnext;
4962 }
4963 pGE->pCEctl->pCLon = pCL;
4964
4965 /* link into all clients list
4966 */
4967 pCL->pCLscan = pGE->pCLall;
4968 pCL->ppCLbscan = &pGE->pCLall;
4969 if ((CONSCLIENT *)0 != pCL->pCLscan) {
4970 pCL->pCLscan->ppCLbscan = &pCL->pCLscan;
4971 }
4972 pGE->pCLall = pCL;
4973
4974 FD_SET(FileFDNum(pCL->fd), &rinit);
4975 if (maxfd < FileFDNum(pCL->fd) + 1)
4976 maxfd = FileFDNum(pCL->fd) + 1;
4977
4978 /* init the fsm
4979 */
4980 pCL->fecho = 0;
4981 pCL->iState = S_IDENT;
4982 pCL->ic[0] = DEFATTN;
4983 pCL->ic[1] = DEFESC;
4984 pCL->replay = DEFREPLAY;
4985 pCL->playback = DEFPLAYBACK;
4986 BuildString((char *)0, pCL->accmd);
4987
4988 /* mark as stopped (no output from console)
4989 * and spy only (on chars to console)
4990 */
4991 pCL->fcon = 0;
4992 pCL->fwr = 0;
4993 pCL->fwantwr = 0;
4994
4995 /* remove from the free list
4996 * if we ran out of free slots, calloc one...
4997 */
4998 if ((CONSCLIENT *)0 == pGE->pCLfree) {
4999 if ((pGE->pCLfree =
5000 (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT)))
5001 == (CONSCLIENT *)0)
5002 OutOfMem();
5003 pGE->pCLfree->acid = AllocString();
5004 pGE->pCLfree->username = AllocString();
5005 pGE->pCLfree->peername = AllocString();
5006 pGE->pCLfree->accmd = AllocString();
5007 }
5008
5009 if (ClientAccessOk(pCL)) {
5010 pCL->ioState = ISNORMAL;
5011 /* say hi to start */
5012 FileWrite(pCL->fd, FLAGFALSE, "ok\r\n", -1);
5013 BuildString(pCL->peername->string, pCL->acid);
5014 CONDDEBUG((1, "Kiddie(): client acid initialized to `%s'",
5015 pCL->acid->string));
5016 } else
5017 DisconnectClient(pGE, pCL, (char *)0, FLAGFALSE);
5018 }
5019 }
5020
5021 /* create a child process: (fine)
5022 * fork off a process for each group with an open socket for connections
5023 */
5024 void
Spawn(GRPENT * pGE,int msfd)5025 Spawn(GRPENT *pGE, int msfd)
5026 {
5027 pid_t pid;
5028 int sfd;
5029 #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS
5030 # if USE_IPV6
5031 int error;
5032 struct addrinfo *rp, hints, *res;
5033 char host[NI_MAXHOST];
5034 char serv[NI_MAXSERV];
5035 unsigned short bindBasePort;
5036 # else
5037 socklen_t so;
5038 struct sockaddr_in lstn_port;
5039 # endif
5040 # if HAVE_SETSOCKOPT
5041 int true = 1;
5042 # endif
5043 unsigned short portInc = 0;
5044 #else
5045 struct sockaddr_un lstn_port;
5046 static STRING *portPath = (STRING *)0;
5047 #endif
5048
5049 #if !USE_IPV6
5050 /* get a socket for listening */
5051 # if HAVE_MEMSET
5052 memset((void *)&lstn_port, 0, sizeof(lstn_port));
5053 # else
5054 bzero((char *)&lstn_port, sizeof(lstn_port));
5055 # endif
5056 #endif
5057
5058 #if USE_IPV6
5059 for (rp = bindBaseAddr; rp != NULL; rp = rp->ai_next) {
5060 if ((sfd =
5061 socket(rp->ai_family, rp->ai_socktype,
5062 rp->ai_protocol)) < 0) {
5063 Error("Spawn(): socket(): %s", strerror(errno));
5064 continue;
5065 }
5066 # if HAVE_SETSOCKOPT
5067 if (setsockopt
5068 (sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true,
5069 sizeof(true)) < 0) {
5070 Error("Spawn(): setsockopt(%u,SO_REUSEADDR): %s", sfd,
5071 strerror(errno));
5072 return;
5073 }
5074 # endif
5075 if (!SetFlags(sfd, O_NONBLOCK, 0))
5076 return;
5077
5078 error =
5079 getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host),
5080 serv, sizeof(serv),
5081 NI_NUMERICHOST | NI_NUMERICSERV);
5082 if (error) {
5083 Error("Spawn(): getnameinfo failed: %s", gai_strerror(error));
5084 return;
5085 }
5086 bindBasePort = (unsigned short)strtol(serv, NULL, 10);
5087
5088 while (bind(sfd, rp->ai_addr, rp->ai_addrlen) < 0) {
5089 if (bindBasePort && (
5090 # if defined(EADDRINUSE)
5091 (errno == EADDRINUSE) ||
5092 # endif
5093 (errno == EACCES)) && ++portInc) {
5094 /*
5095 * instead of checking for ai_family and modifying
5096 * the structure directly we will generate new addrinfo
5097 * structure, copy over first entry and release it.
5098 */
5099 memset(&hints, 0, sizeof(hints));
5100 hints.ai_family = AF_UNSPEC;
5101 hints.ai_socktype = SOCK_STREAM;
5102 hints.ai_flags =
5103 AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
5104 snprintf(serv, sizeof(serv), "%hu",
5105 bindBasePort + portInc);
5106 error = getaddrinfo(host, serv, &hints, &res);
5107 if (error)
5108 goto OUT;
5109
5110 memcpy(rp->ai_addr, res->ai_addr, rp->ai_addrlen);
5111 freeaddrinfo(res);
5112 continue;
5113 } else
5114 goto OUT;
5115 }
5116 break; /* if we're here bind was succesful so get out */
5117
5118 OUT:
5119 portInc = 0;
5120 close(sfd);
5121 }
5122
5123 /* if rp is null we did not bind */
5124 if (rp == NULL) {
5125 Error("Spawn(): could not bind");
5126 Bye(EX_OSERR);
5127 }
5128
5129 if (-1 == getsockname(sfd, rp->ai_addr, &rp->ai_addrlen)) {
5130 Error("Spawn(): getsockname(%u): %s", sfd, strerror(errno));
5131 Bye(EX_OSERR);
5132 }
5133
5134 /*
5135 * if bindBasePort is 0 we did auto-assign our port. to find out
5136 * what we bind to we need to call getsockname (above) and call
5137 * getnameinfo again. otherwise we have the port info in
5138 * bindBasePort + portInc
5139 */
5140 if (!bindBasePort) {
5141 error =
5142 getnameinfo(rp->ai_addr, rp->ai_addrlen, NULL, 0, serv,
5143 sizeof(serv), NI_NUMERICSERV);
5144 if (error) {
5145 Error("Spawn(): getnameinfo failed: %s", gai_strerror(error));
5146 Bye(EX_OSERR);
5147 }
5148 pGE->port = (unsigned short)strtol(serv, NULL, 10);
5149 } else
5150 pGE->port = bindBasePort + portInc;
5151 #elif USE_UNIX_DOMAIN_SOCKETS
5152 lstn_port.sun_family = AF_UNIX;
5153
5154 if (portPath == (STRING *)0)
5155 portPath = AllocString();
5156 BuildStringPrint(portPath, "%s/%u", interface, pGE->id);
5157 if (portPath->used > sizeof(lstn_port.sun_path)) {
5158 Error("Spawn(): path to socket too long: %s", portPath->string);
5159 Bye(EX_OSERR);
5160 }
5161 StrCpy(lstn_port.sun_path, portPath->string,
5162 sizeof(lstn_port.sun_path));
5163
5164 /* create a socket to listen on
5165 * (prepared by master so he can see the port number of the kid)
5166 */
5167 if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
5168 Error("Spawn(): socket(): %s", strerror(errno));
5169 Bye(EX_OSERR);
5170 }
5171
5172 if (!SetFlags(sfd, O_NONBLOCK, 0))
5173 Bye(EX_OSERR);
5174
5175 if (bind(sfd, (struct sockaddr *)&lstn_port, sizeof(lstn_port)) < 0) {
5176 Error("Spawn(): bind(%s): %s", lstn_port.sun_path,
5177 strerror(errno));
5178 Bye(EX_OSERR);
5179 }
5180 # ifdef TRUST_UDS_CRED
5181 /* Allow everyone to connect, but we later auth them via SO_PEERCRED */
5182 chmod(lstn_port.sun_path, 0666);
5183 # endif
5184
5185 pGE->port = pGE->id;
5186 #else
5187 lstn_port.sin_family = AF_INET;
5188 lstn_port.sin_addr.s_addr = bindAddr;
5189 lstn_port.sin_port = htons(bindBasePort);
5190
5191 /* create a socket to listen on
5192 * (prepared by master so he can see the port number of the kid)
5193 */
5194 if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
5195 Error("Spawn(): socket(): %s", strerror(errno));
5196 Bye(EX_OSERR);
5197 }
5198 # if HAVE_SETSOCKOPT
5199 if (setsockopt
5200 (sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true, sizeof(true))
5201 < 0) {
5202 Error("Spawn(): setsockopt(%u,SO_REUSEADDR): %s", sfd,
5203 strerror(errno));
5204 Bye(EX_OSERR);
5205 }
5206 # endif
5207
5208 if (!SetFlags(sfd, O_NONBLOCK, 0))
5209 Bye(EX_OSERR);
5210
5211 while (bind(sfd, (struct sockaddr *)&lstn_port, sizeof(lstn_port))
5212 < 0) {
5213 if (bindBasePort && (
5214 # if defined(EADDRINUSE)
5215 (errno == EADDRINUSE) ||
5216 # endif
5217 (errno == EACCES)) && ++portInc) {
5218 lstn_port.sin_port = htons(bindBasePort + portInc);
5219 } else {
5220 Error("Spawn(): bind(%hu): %s", ntohs(lstn_port.sin_port),
5221 strerror(errno));
5222 Bye(EX_OSERR);
5223 }
5224 }
5225 so = sizeof(lstn_port);
5226
5227 if (-1 == getsockname(sfd, (struct sockaddr *)&lstn_port, &so)) {
5228 Error("Spawn(): getsockname(%u): %s", sfd, strerror(errno));
5229 Bye(EX_OSERR);
5230 }
5231 pGE->port = ntohs(lstn_port.sin_port);
5232 #endif
5233
5234 fflush(stderr);
5235 fflush(stdout);
5236 switch (pid = fork()) {
5237 case -1:
5238 Error("Spawn(): fork(): %s", strerror(errno));
5239 Bye(EX_OSERR);
5240 default:
5241 close(sfd);
5242 /* hmm...there seems to be a potential linux bug here as well.
5243 * if you have a parent and child both sharing a socket and the
5244 * parent is able to close it and create a new socket (same port
5245 * request) before the child is able to listen() and you have
5246 * been using SO_REUSEADDR, then you get two processes listening
5247 * to the same port - only one appears to get the connections.
5248 * sleeping a bit not only throttles startup impact (a bit) but
5249 * it gives the child a chance to listen() before the parent
5250 * possibly opens another socket to the port. this really is only
5251 * an issue if you use the same port with -p and -b, i think.
5252 */
5253 usleep(750000); /* pause 0.75 sec to throttle startup a bit */
5254 pGE->pid = pid;
5255 return;
5256 case 0:
5257 pGE->pid = thepid = getpid();
5258 isMaster = 0;
5259 break;
5260 }
5261
5262 #if HAVE_SETPROCTITLE
5263 if (config->setproctitle == FLAGTRUE)
5264 setproctitle("group %u: port %hu, %d %s", pGE->id, pGE->port,
5265 pGE->imembers,
5266 pGE->imembers == 1 ? "console" : "consoles");
5267 #endif
5268
5269 /* close the master fd - which is there *except* on startup */
5270 if (msfd != -1)
5271 close(msfd);
5272
5273 /* clean out the master client lists - they aren't useful here and just
5274 * cause extra file descriptors and memory allocation to lie around,
5275 * not a very good thing!
5276 */
5277 while (pCLmall != (CONSCLIENT *)0) {
5278 CONSCLIENT *pCL;
5279 if (pCLmall->fd != (CONSFILE *)0) {
5280 int fd;
5281 fd = FileUnopen(pCLmall->fd);
5282 pCLmall->fd = (CONSFILE *)0;
5283 CONDDEBUG((1, "Spawn(): closing Master() client fd %d", fd));
5284 close(fd);
5285 FD_CLR(fd, &rinit);
5286 FD_CLR(fd, &winit);
5287 }
5288 pCL = pCLmall->pCLscan;
5289 DestroyClient(pCLmall);
5290 pCLmall = pCL;
5291 }
5292 while (pCLmfree != (CONSCLIENT *)0) {
5293 CONSCLIENT *pCL;
5294 pCL = pCLmfree->pCLnext;
5295 DestroyClient(pCLmfree);
5296 pCLmfree = pCL;
5297 }
5298
5299 if (listen(sfd, SOMAXCONN) < 0) {
5300 #if USE_UNIX_DOMAIN_SOCKETS
5301 Error("Spawn(): listen(%s): %s", lstn_port.sun_path,
5302 strerror(errno));
5303 #else
5304 Error("Spawn(): listen(%hu): %s", pGE->port, strerror(errno));
5305 #endif
5306 Bye(EX_OSERR);
5307 }
5308 Kiddie(pGE, sfd);
5309
5310 /* should never get here...but on errors we could */
5311 close(sfd);
5312 #if USE_UNIX_DOMAIN_SOCKETS
5313 unlink(lstn_port.sun_path);
5314 #endif
5315 Bye(EX_SOFTWARE);
5316 }
5317