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