pax_global_header00006660000000000000000000000064134466052040014516gustar00rootroot0000000000000052 comment=e289ba408f848860f45c68ad074871c133e79202 conserver-8.2.4/000077500000000000000000000000001344660520400135375ustar00rootroot00000000000000conserver-8.2.4/.cirrus.yml000066400000000000000000000013501344660520400156460ustar00rootroot00000000000000env: CIRRUS_CLONE_DEPTH: 1 freebsd_12_task: freebsd_instance: image: freebsd-12-0-release-amd64 install_script: - pkg install -y autoconf automake build_script: - ./package/setup-configure - ./configure || { cat config.log; exit 1; } - make test_script: - make test linux_gcc_task: container: image: gcc:latest build_script: - ./package/setup-configure - ./configure || { cat config.log; exit 1; } - make test_script: - make test macos_task: osx_instance: image: mojave-xcode-10.1 install_script: - brew install autoconf automake build_script: - ./package/setup-configure - ./configure || { cat config.log; exit 1; } - make test_script: - make test conserver-8.2.4/.gitignore000066400000000000000000000000101344660520400155160ustar00rootroot00000000000000*.[ch]~ conserver-8.2.4/CHANGES000066400000000000000000001464251344660520400145460ustar00rootroot00000000000000 CHANGES ======= version 8.2.4 (March 26, 2019): - Correct man page typo (Ed Maste ) - Remove autotools generated files from repo and create with release - Better integration of Cirrus CI - FreeBSD, Linux, and MacOS - Moving README to markdown - Fix #12 - Remote infomation flags (i.e. "-x") cannot be filtered by console - Fix #8 - defaultaccess appears broken - Rename configure.in and use autoreconf - Better use of version.h and letting configure build things with versions version 8.2.3 (March 17, 2019): - Correct 'impi' typo (Ed Maste ) - Correct argument type passed to time() (Ed Maste ) - Fix compilation without deprecated OpenSSL APIs (Rosen Penev ) - Fix compilation without deprecated OpenSSL 1.1 APIs (Rosen Penev ) - Fix #6 - clang "-Wstring-plus-int" warning (Bryan Stansell ) - configure.in: Add test for closefrom (Ed Maste ) - regenerate autoconf files (Ed Maste ) - Use closefrom if available (Ed Maste ) - Correct typo (Ed Maste ) - Add Cirrus-CI FreeBSD CI build config (Ed Maste ) - off by one found by Ed Maste (Bryan Stansell ) version 8.2.2 (May 28, 2018): - fixes for OpenSSL 1.1+ - patch by Eneas U de Queiroz - adjustments to documentation after move to github - removal of old RCS/CVS tags since we have git version 8.2.1 (Jun 2, 2015): - added TCP keepalives between client and server - TCP-based consoles already had the code - this was mostly an oversight - patch for SEGV and task execution - patch by Artem Savkov - expanded break sequences from [1-9] to [1-9a-z] - based on patch by Artem Savkov version 8.2.0 (Apr 20, 2014): - added --with-trust-uds-cred which uses getsockopt() to fetch and trust the client uid, bypassing password lookups - patch by Anton Lundin - missing closedir() causing memory leak - patch by Anton Lundin - sending a break signal over IPMI was broken - based on patch by Alexander Y. Fomichev - IPv6 support (marked as experimental at this point because it's untested (except by the author), there's a lack of documentation, and I'm hoping for non-getifaddrs() system support) - patch by Milos Vyletel - no more K&R compiler support version 8.1.20 (Apr 4, 2014): - IPMI serial over LAN support via FreeIPMI - based on patch by Anton D. Kachalov - minor cleanup of code, removal of gcc warnings and such that should have no fuctional change version 8.1.19 (Sep 26, 2013): - prevent select/read loop when EOF on non-pty input (console) - reported by Chris Marget - "!" syntax prefixing use of group names not honored - reported by Zonker - fixed memory leak using timestamps - patch by Karvendhan M. - deprecated --with-cycladests (noop now) - cross-compilation should work without it as autologin now expects setpgrp() to take two arugments instead of testing for it - no automatic checks for an empty password when using PAM authentication - based on discussion with Ryan Kirkpatrick - added 'sslcacertificatefile' and 'sslcacertificatepath' client configuration options - based on patch by Aki Tuomi - added 'sslcacertificatefile' and 'sslreqclientcert' server configuration options - added --with-req-server-cert to force clients to require a certificate from the server when using SSL - based on emails with Thor Simon - added server-side tasks (see conserver.cf man page) that are invoked by the client (useful for things like IPMI-based power control of servers, invoking resets of terminal server ports, or anything else that requires scripting) - ideas from patch by Anton Lundin and discussion on mailing list (2011) - added 'confirm' option to break sequences - added 'breaklist' option to limit exposure of break sequences to consoles - sending of break signals is now announced to all attached clients version 8.1.18 (Nov 11, 2010): - install man pages read-only and improved the contributed redhat init script - patches by Eric Biederman - spec file improvements in contrib/redhat-rpm - patch by Jodok Ole Muellers - GSS-API patch for client code - patch by Andras Horvath version 8.1.17 (Sep 29, 2009): - fix for interface detection when HAVE_SA_LEN is defined - first detected on NetBSD 5.0 and patched by Chris Ross - first person to connect to a console wanting read/write now gets it once the active user drops read/write - suggested by Thomas Gardner - fix typo when setting nonblocking socket for client connections, fixing stall issues - patch by Eric Biederman - GSS-API patch (--with-gssapi) to help with Kerberos tokens - patch by Nate Straz - authenticate username without @REALM when using GSS-API (--with-striprealm) - based on patch by Andras Horvath - various contrib/redhat-rpm fixes - patch by Fabien Wernli - fix handling of read(stdin) returning -1 in console client - patch by Ed Swierk version 8.1.16 (Apr 10, 2007): - added 'replstring' substitution option - inspired by conversation with Owen DeLong - added '^Ecn' option for writing a note to the logfile - patch by Bryan Schmersal - fixed leaking file descriptors when 'host' and 'uds' consoles fail to connect - based on patch by Michael Heironimus version 8.1.15 (Dec 31, 2006): - protection again telnet option negotation loops - patch by Robby Griffin - console now prints the attach/detach sequences when suspended - added --with-cycladests configure option to allow cross-compiling on a cyclades ts - patch by Matt Johnson - added master conserver host to 'console -x' output for 'device' consoles - patch by Matt Johnson - fixed parsing error when dealing with encapsulated client/server options version 8.1.14 (Apr 9, 2006): - fixed rpm conserver.spec file - based on patch by Martin Evans - added 'uds' console type for unix domain socket connections - based on patch by DJ Gregor - probing of interfaces under cygwin (and possibly others?) now skips unconfigured interfaces (even if flagged as up!) - reported by Chris Riddoch - added the '!login' console option to prevent clients from connecting to a console - suggested by Greg Tillman - added a 'noop' console type for consoles you'd like to name, but have no connection to - suggested by Greg Tillman - deprecated escape commands removed from the code - added '^EcP' and '^EcR' sequences to set the playback and replay line lengths - new console config options 'playback' and 'replay' to let the client set prefered output lengths (with a special feature for a size of zero) - prefer strlcpy() over strcpy() - based on patch by Peter Valchev - fixed bug where '^Eco' by user on an 'exec' console with an 'initcmd' causes input by user to be ignored (could be other console types as well) - reported by Mark Wedel - made POSIX termios code in autologin a requirement (since conserver requires it) and cleaned up a few other issues - based on reports by Arthur Clune version 8.1.13 (Jan 15, 2006): - use SIOCGIFNUM for interface count (if available) and catch EINVAL on Solaris - patch by Peter Jeremy - console output now resets idle timer - suggested by Peter Saunders - bug fix for conserver process running out of control and using up cpu - debugged with Alexandra N. Kossovsky version 8.1.12 (Sep 5, 2005): - printf() fix for autologin - patch by Menno Duursma - newly spawned (from SIGHUP) processes didn't properly close primary socket - SSL certificates now work again (anonymous ciphers are not allowed if a certificate is used) - client options -x, -u, -w, and -i can now take a console name to restrict output - suggested by Evan McClure - convert program now installed in $libdir/conserver - patch by Petter Reinholdtsen - we now ignore SIGXFSZ, if it exists - added 'limited' access option to remove certain functionality from users - suggested by Sven Michels - added client option -z/-Z for sending commands to servers (reload, quit, etc) - based on suggestion by Joshua Pincus - added 'execrunas' and 'initrunas' console options to allow 'exec' and 'initcmd' execution as another user and/or group - based on patch by Gary Mills - the east coast mirror had to shut down (for now, at least) - crash from bad pointer manipulation during log replay - reported by Ryan Kirkpatrick version 8.1.11 (Nov 9, 2004): - fixed array bounds and stack tromping - reported by Emmett Hogan - most recent client to ask for read-write now gets it (used to be first client to connect) - client read-write mode now separate from console up/down state - 'make autologin.install' now compiles autologin - reported by Graham Merrill - new 'autocomplete' option for controlling shortest-prefix console name matching (which has always been on) - client now blocks on ^Eco, waiting for status of connection - added 'a' and 'A' for upper and lowercase alphanumeric (0-9a-z) conversions to the *subst config items - based on patch by Jonathan Chen version 8.1.10 (Sep 21, 2004): - fix "forwarding level too deep" issue with '^Ec;' - reported by Han Pilmeyer version 8.1.9 (Jul 14, 2004): - fixed bug processing access lists when duplicates exist in the list - reported by Phil Dibowitz - fixed bug where 'idletimeout' was not recognized in default blocks - reported by John Cagle - added an 'autocomplete' config option to set the console name autocompletion behavior - suggested by John Stoffel version 8.1.8 (Jun 9, 2004): - added 'initspinmax' and 'initspintimer' console options to help calm console initialization "spinning" - fixed setsockopt() error on 64bit solaris - reported by Trond Hagen version 8.1.7 (May 28, 2004): - cleaned up the manpages a bit to make things clearer and more standard - reported by Dave Stuit - added an east coast mirror! http://conserver.syr.edu/ - thanks to Christopher T. Beers - primary group of users not included in '@group' syntax - reported by Phil Dibowitz - changed '@group' behavior such that groups are checked when needed, instead of cached at startup, which is more logical - missing variables when building convert with tcp_wrappers - reported by Nikolaos Papavassiliou - added --with-rpath option - suggested by Phil Dibowitz version 8.1.6 (May 25, 2004): - added ability to configure client via system-wide console.cf file and per-user .consolerc - suggested (independently) by Erik Sjolund and Trevor Fiatal - fixed bug where break strings were not properly sent - reported by Tim Small - fixed bug in config file 'protocol' value handling - reported by Kees Cook - conserver no longer uses the local domain name in the default access list (the default list is only created if no access list is specified in the configuration file) - inspired by William P LePera - added a 'terminal' console configuration block for printing strings when attaching and detaching from consoles - suggested by Richard Threadgill version 8.1.5 (May 7, 2004): - changed remaining O_NDELAY flags to O_NONBLOCK - added PROTOCOLS file to describe the client/server protocol - added '#include' capability to conserver.cf file - added '@group' syntax to conserver.cf file to support use of system groups - added -U client option to allow client to ask for encryption but fall back to non-encrypted connections otherwise - suggested by Mike Hendon - fixed bug where socket not properly deleted on exit - reported by William P LePera - added 'initdelay' option for throttling startup of consoles - suggested by Jay McCanta version 8.1.4 (Apr 13, 2004): - fixed macro use in manpages to hopefully be more compatible - removed extra newline of --MARK-- range output - fixed bug where server -b option wasn't working - reported by Nathan R. Hruby - fixed client segfault when using -R, -t, -d, and -b options - added a --with-uds configure option to have all client/server communication happen via unix domain sockets - suggested by William P LePera version 8.1.3 (Mar 22, 2004): - fixed small memory and file descriptor leak in client when using '^Ec;' - '^Ec;' now only disconnects from the previous console only after a successfully move to a new console, allowing you to abort the move - suggested by Christopher T. Beers version 8.1.2 (Mar 11, 2004): - better handling of client command (^Ec|) when user is bumped, conserver is reconfigured, etc - added 'initsubst' option for 'initcmd' substitutions like 'devicesubst' and 'execsubst' - based on patch by Bill Sommerfeld - modified and added to *subst (initsubst, etc) syntax to allow for flexibility and future enhancement - changed 'port' and 'portinc' minimums from 1 to 0 - it allows more flexibility and helps with 0-based counting - removed unportable sys/cdefs.h from contrib/chat/chat.c - patch by Bill Sommerfeld - added --with-extmsgs configure switch to enable entertaining messages - marked various undocumented client commands as deprecated so that they can be removed in a future version - added ability to "move" to a new console via '^Ec;' - suggested by Christopher T. Beers - added a dump of console information when -S is used twice - suggested by Todd Stansell version 8.1.1 (Feb 10, 2004): - fixed mistake in Makefiles where rpmbuild fails - reported by Martin Evans - fixed a couple typos - reported by Matt Selsky version 8.1.0 (Jan 18, 2004): - fixes for HP-UX compilation - patch by Petter Reinholdtsen - fixes for compilation under various operating systems - patch by Albert Chin - added a 'protocol' option for either 'telnet' or 'raw' socket communication - also reflected in -i output - changed the client/server protocol to use 0xff as a command character for sending control commands - similiar to the telnet protocol - improves the ^Ec| interaction - client -E option now disables ssl encryption attempts, useful for broken SSL environents - suggested by Graydon Dodson - bad error message connecting non-ssl client with ssl-required server - reported by Graydon Dodson - added note about pam configuration to conserver.passwd.man - suggested by Erik Sjolund - improved telnet protocol option handling such that connections to standard telnet ports should work properly version 8.0.9 (Dec 10, 2003): - fixed major bug in connect() handling which causes most socket-based consoles to timeout after 10 seconds - reported by Tom Rataski - added a couple details to the 'logfilemax' manpage entry version 8.0.8 (Dec 2, 2003): - added client ^Ec| sequence for running a command on the client and having it's I/O directed to the console - inspired by discussions with David Williamson years ago ;-) - touched up Makefiles to test against .h files - fixed inability to replay the console log while the console is down - reported by Matt Selsky - added a console 'logfilemax' option for rotating the console logfile once the file is greater than the specified size - added sample configuration files to conserver.cf/samples - tweaked some failure messages and initcmd notifications version 8.0.7 (Nov 20, 2003): - renamed util.[ch] to cutil.[ch] to prevent name conflict with system util.h, reordered some #includes, and fixed a configure test for sys/proc.h to build on OpenBSD 3.4 (and probably others) - reported by Kurt Raschke - fixed missing semi-colon for cygwin build - reported by Raymond Richmond version 8.0.6 (Nov 16, 2003): - code was missing regarding the 'setproctitle' option - patch by Dmitry Morozovsky - fixed the order of access list checking when --with-trustrevdns is used - fixed various spelling errors - patches by Matt Selsky - added console 'idletimeout' and 'idlestring' options to trigger the sending of 'idlestring' after a lack of activity for 'idletimeout' seconds - suggested by Ian Potts - added console 'portbase' and 'portinc' options to allow specifying a formula for referencing ports - inspired by Todd Stansell - fixed problem where console 'port' option didn't take port names (manpage said it did) - added server -U option and configuration option 'unifiedlog' to allow a copy of all console activity to go to a single file - removed all sprintf() calls - added 'devicesubst' and 'execsubst' console options for doing replacements with calculated port numbers on the 'device' and 'exec' values - NULL characters in console data caused data loss because of strlen() usage - reported by Toby Gerhart - fixed a couple errors in contrib/redhat-rpm/conserver.spec - reported by Martin Evans - added capability to use '!' in user access lists to prevent a user from having access - suggested by Matt Selsky version 8.0.5 (Oct 31, 2003): - added 'loghostnames' config option (default is on) to log client hostnames instead of ip addresses (like pre-8.0.0) - suggested by Han Pilmeyer - fixed bug where 'daemonmode' config file option wasn't being used - fixed potential logfile (-L) opening problem processing SIGHUP - fixed various potential conserver.cf issues with 'config' block values processing SIGHUP - added 'setproctitle' config option (default is off) to enable changing the process title to contain runtime information - patch by Dmitry Morozovsky version 8.0.4 (Oct 10, 2003): - fixed client rejection bug that can nearly never happen - reported by Han Pilmeyer - fixed bug where client is reconnected to the previous console when non-exact console names are used - reported by Rolf Petter Halle - fixed bug where exact matchs on a remote console name never happened - reported by Toby Gerhart - fixed bug where SIGHUP fails to update console aliases - reported by Han Pilmeyer version 8.0.3 (Oct 6, 2003): - the SIGHUP process fails to pick up changes to certain fields because of a horribly broken SwapStr() function [broken in all previous 8.0.x versions] - reported by Toby Gerhart version 8.0.2 (Oct 5, 2003): - reworked the i/o calls to better buffer data - added console 'motd' option for holding a "message of the day", displayed to the client on attachment to console - suggested by Toby Gerhart - added ^Ecm client command for displaying MOTD and integrated it into the client console attachment sequence - now unallocate client lists when forking new child conserver processes - changed strdup() to local StrDup() so dmalloc can gracefully track changes - added a ^Ec; sequence to let client signal server when it's ready to see console data, otherwise a chatty console can cause the login sequence to fail and you can never attach to the console version 8.0.1 (Sep 29, 2003): - fixed bug in access list parsing where multiple addresses per line can cause errors - reported by Jay McCanta - changed client password prompt to show hostname passed down by the server - suggested by Toby Gerhart - fixed bug where remote console names were only search for substring matches - reported by Toby Gerhart - the server -M option wasn't being used properly to limit the consoles managed by the host - added 'initcmd' console option which allows a command to interact with a console right after a console is opened - suggested by Greg Woods - added the chat program contributed by Greg Woods to the contrib/chat directory - added WUNTRACED to waitpid() for catching suspended processes - reworded some client/server messages to be clearer - embedded non-printable characters in break lists now display correctly when '^Ecl?' is used - in case client aborts unexpectedly, terminal state should now be restored to normal version 8.0.0 (Sep 22, 2003): - better error messages and management of the user's password - 8.0.0-beta4 mistakenly lost conserver.passwd usage - empty passwords now don't trigger a passwd prompt (like 7.2.7) - upgraded to autoconf-2.57 and use recent config.guess/sub files - suggested by Jorgen Hagg - we now install the conserver.rc file as well as sample conserver.cf and conserver.passwd files in $(prefix)/share/examples/conserver - suggested by Hubert Feyrer version 8.0.0-beta4 (Aug 24, 2003): - totally rewrote the client/server communication, allowing SSL connections to occur first, protecting *all* information - added 'admin' keyword to the 'access' portion of the config file for specifying users able to issue the 'quit' command - removed client -G option since it's not really useful any more - added client -t option for sending "text messages" to users, which is similar to broadcast messages, but you can specify the user and/or console - suggested by Trevor Fiatal - added client -d option for disconnecting users specified by username and/or console - suggested by Trevor Fiatal - removed --with-64bit configure option as 64bit operation is reported to work just fine - break strings with '\d' are interpreted as a delay, which can be specified in the config file (default 250ms) - removed 'reset -x' portion of default break sequence #3 - remote conserver hostnames now properly match - had to be a character string match previously - hostname aliases now checked against access lists and the matched name is used for logging - added --with-trustrevdns to enable the use of reverse DNS information for access list checks [not recommended] - see the INSTALL file for full details on who should actually need this Many thanks to Chuck Rouzer for all the help with FreeBSD support and the following issues... - fixed 'make test' problem on hosts where 'localhost' doesn't resolve to 127.0.0.1 - fixed interface probe problem under *BSD - added openpty() interface for pty allocation version 8.0.0-beta3 (Aug 8, 2003): - master process no longer forks on client requests - handles them with select() like child process - alarm()/SIGALRM usage removed and replaced with counters and timer on select() call - removed caching of timeouts to terminal servers - each socket connection now has a proper timeout (and can happen simultaniously) - partial write()s are properly buffered and retried - made all sockets (including SSL) non-buffered - client now supports piping data to it and properly printing all server data ("echo '^Ecr^Ec.' | console universe") version 8.0.0-beta2 (Jul 17, 2003): - console aliases added with 'aliases' console keyword - two stop bit support for serial devices - requested by Kelly Setzer - added support for inet_aton() over inet_addr() - all server interfaces now used to identify console management - server interfaces probed with SIOCGIFCONF ioctl, if available - added flow control options 'ixon', 'ixany', 'ixoff', and 'crtscts' - added info to console client -i output - man pages updated, however the wording needs work version 8.0.0-beta1 (Jul 4, 2003): - ***NOTICE*** the format of conserver.cf and conserver.passwd has completely changed. see the INSTALL file for upgrade instructions (it should be "fairly painless"). some documentation has been updated to reflect the new world, some hasn't - my goal for beta2 is to have all the docs updated - many command-line options now also conserver.cf options options - POSIX termios interface now required for compilation - some POSIX requirements were already in the code and others will undoubtedly creep in as time goes by - configure --with-regex option removed because of conserver.cf and conserver.passwd changes - added -S option to server for syntax checking of the configuration file - suggested by Dave Stuit - authorized users now either have r/w or r/o access to consoles - getpassword.o replaces getpass() and getpassphrase() so we can get any string length - fixed rm commands in test script - patch by Petter Reinholdtsen version 7.2.7 (Apr 9, 2003): - added test suite ('make test') for basic client/server communication tests - changed configure script to provide better checking of options, hp-ux specifics, and functions - console names are no longer case-sensitive - fixed case-sensitive DNS name comparisons - reported by Peter Chubb via Jorgen Hagg - added dmalloc and openssl version numbers to -V output - reordered includes for openssl-0.9.7a compatibility - conserver.rc looks at pidfile to find the master pid - suggested by Petter Reinholdtsen - misplaced code regarding --with-regex - patch by Andreas Wrede - added password support for HP-UX trusted systems - immense help by Greg Brown - potential SIGUSR1 bug where cached terminal server availability wasn't being cleared correctly - reported by Dave Stuit - server is now more forgiving about errors instead of just giving up and shutting everything down - consoles that have trouble coming up (and are forced down) now log when they come back up - suggested by Dave Stuit version 7.2.6 (Mar 10, 2003): - the -b conserver option was mostly broken (since 7.2.0) - changed logging format so that all messages are of a similar form - fixed bad port number in 'cached previous timeout' message - reported by Dave Stuit - now using pid_t type for better compatibility - fixed the RPM and Solaris package to include the man page for conserver.passwd - suggested by R P Herrold - added restart option (-HUP) to conserver startup scripts - added -B option to client for sending messages to a single server - suggested by Dave Stuit - added --with-dmalloc for memory usage debugging - bug with multiple interfaces and -M option not maching hostname in configuration file - patch by Igor Sviridov - memory leak using openssl library plugged - automatic reinitialization of failed consoles now retries every minute like the manpage says it does - reported by Chris Fowler - when -R option is used, substring matches on console names from clients only match local console names (and if that fails just remote console names) but ambiguous name list returns both local and remote consoles - suggested by Todd Stansell version 7.2.5 (Jan 27, 2003): - fixed many documentation bugs - reported by Dave Stuit - added -I option to client which operates like -i, but on the primary conserver only - suggested by Dave Stuit - added SIGUSR2, which does not reread the configuration file, but does everything else SIGHUP does - suggested at LISA 2002 - fixed bug where LOGDIR setting gets used even if no logfile is wanted - added -R option to server to prevent client redirection to other conserver hosts - suggested by Todd Stansell version 7.2.4 (Oct 14, 2002): - added --with-openssl for some client/server encryption - added -E option to client and server to allow for non-encrypted connections (encryption is the default if compiled in) - added -c option so credentials (certificate and key) can be exchanged between client and server - expanded -V output to show what optional bits actually got compiled into the code (libwrap, regex, etc) - compilation errors on non-shadow file systems without using --with-pam - reported by Jesper Frank Nemholt - client now prefers $LOGNAME, then $USER, then the current uid for its -l default - suggested by Dave Stuit - putting back socklen_t usage - it's the right thing to do, so tell me where it breaks things - configure options --with-cffile and --with-pwdfile now recognize fully-qualified pathnames - suggested by Kjell Andresen version 7.2.3 (Sep 23, 2002): - checking for duplicate console names got lost in the major 7.2.0 rewrite of that code. it's back now, with a couple of other edge-case fixes i noticed while scanning the code - reported by Dave Stuit - added --with-pam and PAM authentication support - suggested by Stu May - added -F server option to prevent automatic reinitialization of failed consoles ('|' syntax consoles which exit with a zero status are still reinitialized) - requested by William P LePera and Malcolm Gibbs - successful automatic reinitialization of consoles now attaches a client that wants read-write mode - added read-only client wish to become read-write in -i output - moved to autoconf-2.54 and fixed some small configure.in bugs Many thanks to John R. Jackson for the following fixes, cleanups, and enhancements... - "lost timestamps" bug (SIGALRM/sleep()/usleep()/tcp_wrapper interaction) - compiler warnings, bad fileOpenFD tests, and ability to use a colon after an equal in the config file (LOGDIR=C:\Logs) - autologin fix for Solaris BSM support version 7.2.2 (Jun 05, 2002): - added 'n' as token for no-parity - patch by Greg A. Woods - extra timestamps occur when consoles come back up after being down longer than their timestamp period - reported by Dave Stuit - AIX 5.1 pseudo-terminal support broken - patch by William P LePera - PID file overwritten and not unlinked on exit - reported by William P LePera - signals cleaned up in master, child, and sub-proc sections - SIGPIPE now ignored - reported by Greg A. Woods version 7.2.1 (Mar 25, 2002): - bug regarding timestamp/break reading - patch by Benn Oshrin - console names in conserver.passwd can be regular expressions if enabled using --with-regex - based on code from Vladislav Patenko - breaks recorded to console logs if 'b' option used in timestamp-spec - suggested by Dave Stuit version 7.2.0 (Mar 12, 2002): - check for valid baud rates (invalid resulted in '0') - reported by Andrew Gallatin - a broadcast messages to others on your console can now be sent via ^Ecb version 7.2.0-beta3 (Feb 25, 2002): - allow for 57600 and 115200 baud rates - patch by Andreas Wrede - TCP traffic now escaped according to Telnet protocol - the highly desired dynamic reconfiguration of conserver is here! the HUP signal now triggers a read of the configuration file and adjustment of consoles - configure flags shown in -V output version 7.2.0-beta2 (Feb 14, 2002): - new -W client option for showing who's attached to a single conserver master - suggested by Dave Stuit - maxfiles() clashes with HP-UX 11 'maxfiles' variable - patch by Adam Morris - removed use of socklen_t type for better compatibility - removed TELCMD/TELOPT use for compatibility version 7.2.0-beta1 (Jan 29, 2002): - static structures and strings are now dynamic in server - MAXGRP (--with-maxgrp) has been removed as it's now dynamic - new -m server option for setting the maximum consoles per process - the default is still set with --with-maxmemb - new -i client option (and ^Eci) that displays console information in a machine-parseable format - two debug levels (second level by using two -D options) - ANSI prototypes and definitions (when available) version 7.1.4 (Jan 21, 2002): - console -[PqQ] didn't work - patch by Han Pilmeyer - maxfiles() didn't check FD_SETSIZE - patch by Justin Grudzien - New -o and -O server flags for automatically reconnecting downed consoles - patch by Benn Oshrin - Automatic reconnection of consoles on read failures, retried every minute - Up to nine break sequences can be defined in the configuration file and assigned to consoles individually, accessed via new ^ecl[?0-9] escape sequences - console logs are marked with "up" and "down" timestamps The following based on code by John R. Jackson - sequential timestamps merged into one range during playback - timestamps done on "nice" boundaries (hour, minute, etc.) - lots of code cleanup, optimizations, etc. version 7.1.3 (Oct 16, 2001): - NetBSD 1.5 termios bug/compatibility - patch by Andreas Wrede - Missing quotes broke ^ecl1 code - reported by William Charles - Alternate break code didn't exist for local ports version 7.1.2 (Oct 15, 2001): - fixed line-based timestamp code - reported by Benn Oshrin - tcp_wrappers support (--with-libwrap) - CLOCAL bit set for local ports - patch by Egan Ford - timestamp added to 'lost carrier' error - suggested by Todd Stansell - Alternate break sequence for Solaris 8 is available as ^ecl2 escape sequence - patch by William Charles The following suggested by Trevor Fiatal - Widened username field of 'console -w' output - Added server hostname to password entry prompt - AC_CHECK_LIB replaced with AC_SEARCH_LIBS in configure.in so that irrelevant (and sometimes incompatible) libraries aren't linked in version 7.1.1 (Aug 4, 2001): - Now using getlogin() for real username info - suggested by Dave Stuit - gethostbyname() failure for a console now doesn't cause a shutdown - reported by Todd Stansell - Shutdown via client -q/-Q fixed (broken in 7.1.0 because of master process fork() code) - Password file parsing fixed to allow for empty passwords - allowing users to access conserver without a password prompt - Both the password and configuration files no longer have line length limitations, they now support comments (`#' lines), and lines with leading whitespace are continuations of previous lines - Client hostname/ip sometimes not printed - reported by Todd Stansell version 7.1.0 (Jul 26, 2001): - Hostname in access list that began with a digit was treated as an IP address - only strings using [0-9./] are considered CIDR notation and they must be of the form a.b.c.d[/n] - Fixed SIGHUP always opening logfile(-L) - shouldn't without -d - Fixed 'make install' problem under MacOS X and cygwin - Client -l option totally broken in 7.0.3 - patch by Daniel E. Singer - Client now accepts IP addresses from server and server now sends the IP of the socket instead of its hostname to the client, fixing part of the multi-interface problem - Client no longer passes hostname to server along with username - Client now turns off IEXTEN so stuff like ^V passes through - Server now pauses one second when reopening a TCP-based console to give the terminal server a chance to clean up - Master server process now fork()s when accepting clients - timestamp-spec can be numeric only ('m' default) - Ambiguous console name error now shows ambiguous list - Console list in conserver.passwd can now have whitespace - Access lists in conserver.cf can now use ',' as a separator - Added special '*any*' username in conserver.passwd - Username match (real or '*any*') now stops conserver.passwd file processing - allowing you to lock out users - Added -u flag to server to enable "unloved" output - this is the opposite of the old -n flag, which now does nothing - Added -7 flag to client and server for stripping the high bit of data on either side (--disable-8bit removed) - Added -b to server to specify the base port number for the secondary communication channel - based on code from Enrik Berkhan - Changed -d and -D flags to -r and -R in client - now -D enables debugging mode in the client (and -d is unused) - Changed -r to -G in client so -r could be used for the above - Client now uses getpassphrase() (if available) for > 8 char passwords - pointed out by Gregory Bond - Improved signal handling - Improved process handling (POSIX waitpid() now necessary) - Significant rework of STREAMS-based pseudo-tty code - TCP connections ('!' config syntax) now have some Telnet protocol knowledge, removing the "noise" seen when connecting to a terminal server - reported by Todd Stansell - Various code and message "pretty-printing" done - Client and server both run in Windows (tested with Windows 2000) under the cygwin environment thanks to a debug-fest with Emmett Hogan - Using autoconf version 2.50 - Man pages reworked and conserver.passwd page created by Dave Stuit - the pain he suffered documenting the program (and pointing out many of the bugs fixed above) should help endless others version 7.0.3 (Jul 5, 2001): - "Off by 1" bug in 7.0.2 line-oriented timestamp code fixed (extra chars in logfile) reported by Matthew Cheek - TODO file added to distribution - 'attached', 'detached', and 'bumped' actions now written to console logs that have 'a' in timestamp-spec field - Default timestamp specification with TIMESTAMP= in .cf file - Config file much more forgiving about whitespace in fields - gethostbyaddr() failures are no longer fatal - Added -M to server to specify the address to listen on based on code from Enrik Berkhan - Added -p option to client and server to specify the port to connect to and listen on (--with-port still sets the default) - Added logfile output when going into daemon mode - Added --with-logfile to configure - Added -L to override compile-time logfile location - SIGHUP reopens this logfile as well as previous behavior - Run as root only restriction removed - warning now (if necessary) - More #defines for FreeBSD compatibility - Using inet_ntoa() now instead of peeking at bytes - Fixed up -V (and -Vv) output for client and server - Increased conserver.passwd read buffer (CheckPasswd()) - Removed DO_VIRTUAL #if's - always build that code now - Removed ORIGINAL_CODE #if's (old non-CIDR access list parsing) - Changed undocumented -p option to -P in console client - man pages updated to reflect a little more reality version 7.0.2 (Jun 15, 2001): - Bogus "ambiguous server abbreviation" with distributed server setup fixed - Hostname not passed with username from console client - Fixed pseudo-terminal ('|' syntax in .cf file) bug with Solaris and other STREAMS-based systems (actually works now in 7.X) - Line-oriented timestamps in logfiles (see 'l' mark specification) based on patch from Benn Oshrin - Generalized debugging output, info messages and error messages - Moved library search up in configure.in so function calls are detected properly - Permissions of install-sh were wrong - Trimmed the README file and now distribute conserver.html - Redhat RPM bundle (contrib/redhat-rpm) contributed by Paul Heinlein - Cleaned up gcc warnings (for Solaris 7, at least) - Makefiles now use .c.o: specification instead of %.o: version 7.0.1 (May 3, 2001): - 8bit on by default now (use --disable-8bit for old behavior) - FreeBSD patches by Bill Fenner (hopefully *BSD systems will compile cleaner now) - Bad error reporting on getsockopt() found by Bill Fenner - PID file patch by Martin Andrews version 7.0.0 (Feb 18, 2001): - GNU configure-based packaging!!! All work done by Mark D. Roth . Thank you! - Removed conserver/cons.h and moved options into --with and --enable configure arguments - ioctl() bug found by Ross Parker - Removed last of the "loopback preference" code in console/console.c - should have been removed a long time ago version 6.1.7 (Dec 14, 2000): - Documented no parity option in conserver.cf.man - Lowered default CONNECTTIMEOUT to 10 seconds - Changed more internal references to GNAC - Shell command support just plain broken (tracked down and slain by Rob Joyce ) - Added a -P option for specifying the password file - CPARITY setting reflected in -V output - New version numbering scheme and packaging version 6.16 (Sep 8, 2000): - Changed the main GNAC references to Certainty Solutions - Made the comment blocks in conserver/cons.h a little less confusing - There are *NO* code changes to this version. If you have version 6.15, you're good to go. version 6.15 (Mar 6, 2000): - Added debug flag (-D) and the beginning of debug output - A timeout can now be set for TCP connect() calls. The default is 30 seconds. Hosts not responding are only tried once at startup (per child), lessening the impact of a down terminal server. See CONNECTTIMEOUT in conserver/cons.h. version 6.14 (Jan 5, 2000): - Determining if the local conserver controls a port (with @conserver spec) we now compare ip addresses instead of hostnames - Invalid argument now shows -h output version 6.13 (Dec 1, 1999): - High-bit always stripped from network - now follows CPARITY setting in cons.h (found by Daniel Andersson ) - New conserver -i flag for on-demand connects/disconnects to console ports (contributed by Thomas E. Knowles ) - Solaris, Linux, and others now use tcsendbreak() version 6.12 (Aug 24, 1999): - Blank line bug after '%%' in .cf file (found by Miss Himali Patel ) Special thanks to Michael Sullivan for the following improvements and fixes... - Read result bug in group.c caused lock up - Signal handling fixes and cleanup - Several minor spelling errors in strings and comments - Solaris package creation scripts (contrib/solaris-package) - Extended syntax of the access restrictions to understand network numbers with optional netmasks version 6.11 (May 14, 1999): - Added broadcast capability in client (-b option) - Protected certain escape sequences from end-user use - Added "console server shutting down" broadcast - Improved local tty break code (thanks Craig and Brian!) version 6.10 (Jan 26, 1999): - Documentation fixes - Web page up and running version 6.09: - Added more documentation (look for README and INSTALL files). - Fixed up man pages so they are more accurate. version 6.08: - Added support for Linux 2.X and IRIX 6.X - Fixed "suspend" code in client - it shouldn't have worked before, but slow connections could explain it. version 6.07: - Porting infrastructure has been set up in ./port. Solaris 2.5.1 and BSD/OS 3.1 successfully built. - Added CHANGES and README files. Wow, starting to document! version 6.06: - SIGUSR1 now tells the console server to try and reopen all currently downed consoles. - Cleaned up configuration file (cons.h) and Makefiles. version 6.05: - conserver.cf change: groups and their passwords have been removed and timestamp specifications have been added. before version 6.05: - Many, many variations. Nothing was tracked well. Bug fixes and enhancements of various types were applied. conserver-8.2.4/FAQ000066400000000000000000000220521344660520400140720ustar00rootroot00000000000000 Conserver FAQ ============= This is the Conserver FAQ. Any suggestions/corrections/etc should be directed to faq@conserver.com. The FAQ answers the following questions: 1) What is conserver? 2) Where can I find the software? 3) How do I deal with these serial ports? 4) How can I tell what compile-time defaults were used? 5) What does "conserver: getservbyname: conserver: No such file or directory" mean (or something close to that)? 6) What does "console: gethostbyname: console: host lookup error" mean (or something close to that)? 7) How do I set up a serial port for no parity? 8) Is "Conserver" a Trademark or Registered Trademark? 9) When I connect to a console, it says it is down. Why? 10) Is there a technical reason why --with-maxmemb's default is 16? I've changed mine to 96. 99) OK, things just don't seem to work. Help?!? 1) What is conserver? From an email I quickly wrote to a potential user (I'll try and clean it up and make it a little clearer sometime soon): Conserver is an application that allows multiple users to watch a serial console at the same time. It can log the data, allows users to take write-access of a console (one at a time), and has a variety of bells and whistles to accentuate that basic functionality. The idea is that conserver will log all your serial traffic so you can go back and review why something crashed, look at changes (if done on the console), or tie the console logs into a monitoring system (just watch the logfiles it creates). With multi-user capabilities you can work on equipment with others, mentor, train, etc. It also does all that client-server stuff so that, assuming you have a network connection, you can interact with any of the equipment from home or wherever. 2) Where can I find the software? The latest version can be found at http://www.conserver.com/ 3) How do I wire/hook up serial ports? David "Zonker" Harris has a wonderful set of pages that talk about hooking up many types of terminal servers, equipment, etc. It also provides links to other serial port references. You can find his pages at http://www.conserver.com/consoles/. Celeste Stokely also has a wealth of references at http://www.stokely.com/. Just look around and be amazed at what you'll find! 4) How can I tell what compile-time defaults were used? The compile-time defaults can be found by running conserver and console with the -V flag. Simple as that. 5) What does "conserver: getservbyname: conserver: No such file or directory" mean (or something close to that)? When conserver was compiled, it was told to use the /etc/services entry of "conserver" (what came after getservbyname:). You'll need to either recompile conserver and hard-code a port number (using --with-port=) or enter "conserver" in /etc/services. 6) What does "console: gethostbyname: console: host lookup error" mean (or something close to that)? When the console command was compiled, it was told to use the hostname "console" (what came after gethostbyname:) as the master conserver host. You'll need to either reconfigure with the appropriate name of your conserver host (--with-master=) or add an alias of "console". In most cases, adding an alias is my suggestion. 7) How do I set up a local serial port for no parity? The manpage has the answer to this question. For those that don't want to read it, here are some guidelines. For pre-7.2.2, you'd want to use a 'p' after the baud rate ("9600p", for example). For 7.2.2 thru 7.2.7, you can use an 'n'. For 8.0.0 and beyond, you use 'parity none;'. 8) Is "Conserver" a trademark or registered trademark? The best answer I can give is "not as far as I know". A couple of quick searches through the source code doesn't find any claim of a trademark. I've never done a registered trademark search, but if it had been registered (by a previous author), I'm sure it would be mentioned. But I'm no lawyer and don't deal with these types of things, so I'm not exactly sure what I'm taking about and my answer becomes a very vague "not as far as I know". 9) When I connect to a console, it says it is down. Why? There are multiple reasons why this might happen. First, see if it's just a remnant of some other temporary problem. Try and bring the console up by doing a '^Eco' from the client. If that doesn't work, there's a more serious problem which, hopefully, the conserver logfile will explain. Check the log for any permission problems, connection refused messages, etc. You might get more useful information in the log by using the -v option or even by enabling debugging with -D (ideally that shouldn't be necessary). Depending on the type of console, your system might be out of pseudo-terminals, another process might have a terminal server port occupied (another console server or telnet session), or there was an unseen typo in a path or hostname. The logfile should show hints of things like this and other issues. 10) Is there a technical reason why --with-maxmemb's default is 16? I've changed mine to 96. The following is an array of things you need to think about when adjusting --with-maxmemb. It's a bit long, but it's an important question. The big reason (and the main reason for conserver spawning multiple processes) is the maximum number of open files a process can have. Each console can have a few file descriptors associated with it (device, logfile, connected users, and listening socket). So, each process will have about ( 2 * consoles + users + 1 ) open files (--with-maxmemb sets the maximum number of consoles per process in the equation). Although most current operating systems allow a large number of open files per process, the general assumption is it's still pretty low. You also have the speed of your conserver host vs the rates at which data could be streaming to it. Go back a decade and this was probably more of an issue than today, but it's still something to think about. And then you have the problem of delays. If any of the 96 console connections "lock up", it'll delay all activity on the 96 consoles. With 16, there's less of an impact. This can be an issue once the server is up or during startup. Also, with 16 consoles per process, you get a bit more parallelization during startup. So, is there any reason not to up the number to 96? No. Assuming you know the risks and weigh things appropriately. If I remember right, I've upped the number to 48 at some sites. But that was mainly to reduce the memory footprint in older versions of the code which had statically allocated buffers. No need to worry about that with the current code. Personally, I wouldn't change from 16 unless there was a really good reason (like wanting to only have one child process for firewall rules or some such reason). 99) OK, things just don't seem to work. Help?!? Yes, this is a pretty vague question, but here are a few tips that might help. - Is your low-level serial connection correct? Incorrect cables, adapters, wiring, etc. could be the issue. Using a signal tracer or attaching other equipment that's known to work (like a laptop) might be enlightening. Check out http://www.conserver.com/consoles/msock.html for basic serial information (or http://www.conserver.com/consoles/ for even more info). - Can you talk to the serial port with different software? Try using tip or minicom or another application to make sure you can interact with the port. If you're not seeing the right info here, there may be a baud rate issue, a lack of a getty (or equivalent) running on the host or...*shrug*. But, if you've determined that you already have a valid low-level signal connection, you shouldn't have to worry about that level of problem. - So, you're getting the proper interaction from other applications, but not conserver? Are the port name and baud rate correct in the conserver.cf file? When you start conserver (adding -v doesn't hurt), are there any warnings/errors? Is the port in the "up" state when you use "console -u"? If not, what happens when you connect and then do a "^eco" escape sequence to bring it up? What does the conserver process say when you do this? These are the types of things I look at first. If that doesn't help you determine the problem, others will want to see this info (and possibly the same steps with both the client and server using the -D option) to be able to help. Posting your questions to the users mailing list is probably your next step. - Have you tried a search on the conserver site (it searches mailing list traffic as well) to see if someone else has gone through the same problem? conserver-8.2.4/INSTALL000066400000000000000000000306471344660520400146020ustar00rootroot00000000000000 INSTALL ======= Upgrading? Whenever you upgrade I suggest you upgrade both the client and server. Most times, however, you can get away without upgrading the client (it's usually a fairly static piece of code). I'll document any dependencies here, but check the CHANGES file for any new features added to the client if you're considering *not* upgrading. Version 8.1.3 - The '^Ec;' sequence won't work correctly with 8.1.2 (where it was introduced). Version 8.1.2 - The 'devicesubst' and 'execsubst' formats have changed from 8.1.1. It's fairly simple to update your config file to the new format...just check the conserver.cf manpage. Sorry for having to change things, but it's for a good reason (I should have though ahead when designing the original format). Version 8.1.0 - The client/server protocol has changed to better protect 8-bit data and to allow programs invoked with '^Ec|' not have to worry about accidentally sending the escape sequence to the server. Though it will look like things are mostly backward-compatible, don't count on it and just upgrade. Version 8.0.2 - I've added a '^Ec;' sequence to allow the client to signal the server as to when it's ready to see console data. Without this, verbose consoles will prevent clients from attaching (the client sees unexpected data). An 8.0.2 client should be compatible with an 8.0.1 server, but an 8.0.1 client is not compatible with an 8.0.2 server. Version 8.0.1 - There's a slight client/server protocol change to implement the new 'initcmd' console option. If you use this functionality with an 8.0.0 client, you'll run into a compatibility problem while the 'initcmd' command is running. Version 8.0.0 - The client/server protocol has been rearchitected. You *MUST* use an 8.0.0 client with an 8.0.0 server. No combination of client/server will work with pre-8.0.0 code. - Upgrading from pre-8.0.0 code to 8.0.0 and beyond requires you to change your conserver.cf and conserver.passwd files because both of the file formats have changed. The conserver.cf file changes are so major that there is a convert program available in the conserver subdirectory. Just run './conserver/convert ' and it will attempt a conversion to the new format, sending it to stdout. Any errors will be printed to stderr. There are a couple of things you might need to adjust. First are the user access lists. If you are restricting users to certain consoles in your old conserver.passwd file, you'll need to move those restrictions into the new conserver.cf file. Restrictions are set with the 'ro' and 'rw' tags in the configuration file. Second are the 'access' blocks. What get produced by the convert program will be functionally equivalent to the old behavior, but you may be able to tune things to better suit your environment. The conserver.passwd file's console restrictions have moved, as described above. So to convert the conserver.passwd file, all you really need to do is something like: awk -F: '{print $1 ":" $2}' If you have comments or continuation lines in your file, you'll have to do a bit more cleanup to strip out the third field (which is what the awk command is intending to do). - Conserver no longer trusts reverse DNS information by default. If you use the --with-trustrevdns configure flag, you can re-enable the use of gethostbyaddr() [I don't recommended it, however]. If you are using domain names in access lists, you'll either need to change those to use hostnames and/or ip addresses/ranges or use the --with-trustrevdns flag. For example, if you have (in the 8.0.0 format): allowed conserver.com; # allow *.conserver.com then you'll need to worry about this change. If you only use full hostnames, you shouldn't have to do anything. Version 7.2.4 - If SSL support is compiled into the code, older versions of the client and server are, by default, incompatible because encrypted connections are a requirement. Use of the -E flag in the client and/or server can work around this (but I discourage this - please upgrade the clients and servers instead). Version 7.2.0 - The code related to broadcast messages in the client (-b) has changed. If you want the username to come across properly in the broadcast message, you'll need to make sure you upgrade to the 7.2.0 client. Version 7.1.1 - Both conserver.passwd and conserver.cf file parsing behaves the same now. Both use leading whitespace as a continuation line indicator - if you have leading whitespace on a line (aside from comments) you probably should remove it. Version 7.1.0 - The client/server protocol has changed. You *MUST* use a 7.1.0 client with a 7.1.0 and above server. A 7.1.0 client is *not* backward compatible with a pre-7.1.0 server. - Some of the flags in the client (-d, -D, and -r) and server (-n) have been given new identities to make the client and server flags more uniform. - The conserver.passwd file now uses the first username match to determine access rights - if you have multiple instances of a username in an existing password file, they must be combined into one to continue to work. Quickie Instructions - Download conserver (http://www.conserver.com/) and unpack - Run './configure' - Run 'make' - Run 'make test' - If all is well, run 'make install' - Now set up config files, etc. (see below) Detailed Instructions - First thing to do is determine if you want different defaults. A './configure --help' will show you the basics. If you like all the defaults shown, you're set. If not, here are the conserver unique options: --with-port=PORT Specify port number [conserver] --with-base=PORT Base port for secondary channel [0] --with-master=MASTER Specify master server hostname [console] --with-ccffile=CFFILE Specify client config filename [SYSCONFDIR/console.cf] --with-cffile=CFFILE Specify config filename [SYSCONFDIR/conserver.cf] --with-pwdfile=PWDFILE Specify password filename [SYSCONFDIR/conserver.passwd] --with-logfile=LOGFILE Specify log filename [/var/log/conserver] --with-pidfile=PIDFILE Specify PID filepath [/var/run/conserver.pid] --with-maxmemb=MAXMEMB Specify maximum consoles per process [16] --with-timeout=TIMEOUT Specify connect() timeout in seconds [10] --with-trustrevdns Trust reverse DNS information --with-extmsgs Produce extended messages --with-rpath Use -R as well as -L for libraries --with-cycladests (deprecated - noop) Build for a Cyclades TS --with-uds[=DIR] Use Unix domain sockets for client/server communication [/tmp/conserver] --with-trust-uds-cred Trust UDS credentials obtained via socket --with-libwrap[=PATH] Compile in libwrap (tcp_wrappers) support --with-openssl[=PATH] Compile in OpenSSL support --with-req-server-cert Require server SSL certificate by client --with-gssapi[=PATH] Compile in GSS-API support --with-striprealm retry username without @REALM with gss-api authentication --with-freeipmi[=PATH] Compile in FreeIPMI support --with-dmalloc[=PATH] Compile in dmalloc support --with-pam Enable PAM support --with-ipv6 (experimental) Use IPv6 for client/server communication Not surprisingly, some match the old conserver/cons.h items...here they are for reference: PORT or SERVICE - Socket used to communicate HOST - Hostname of console server CONFIG - Config file path PASSWD_FILE - Password file path MAXMEMB - Number of consoles per child process A couple of notes. First, --with-libwrap will add tcp_wrappers lookups to all socket connections in the server. --with-openssl will add encryption between the client and server when you connect to a console. --with-uds will cause the client and server to use unix domain sockets for their communication, eliminating the tcp communication they normally do (which means --with-master and --with-port are not used). --with-dmalloc should only be used to do memory allocation debugging and not used in production. - Run './configure'. This will detect system specific information. The --prefix option will redirect where things are installed. Other options are available as well...try './configure --help'. - Now run 'make'. Hopefully things will compile. - To test your binaries, run 'make test'. If there are problems, it should mean something is wrong, but check the output differences to make sure it wasn't a temporary failure. I tried to make the tests generic, but I may have missed something. - Once things build, you can run 'make install'. - If you'd like to build the autologin application, you'll need to run 'make autologin'. If you'd like it installed, use 'make autologin.install'. - Now that the binaries are in place, we need to set up the configuration files and such. + Does your conserver master hostname exist? This is the hostname specified with the --with-master option. By default the hostname is "console", so make sure it's in DNS, hosts files, or whatever. + If you used a symbolic name for the --with-port option (by default it uses "conserver", so the answer would be yes), you'll need to enter a definition in your services file (directly, via NIS, or whatever). Here's what we use: console 782/tcp conserver # console server If you used a number, you shouldn't have to worry about this step. + Next, make sure conserver runs during boot. The init script we use under Solaris is installed in /examples/conserver/conserver.rc. Use that or some form of it for your own /etc/init.d script or an entry in startup files (/etc/rc, /etc/rc.local, or whatever). + Now for the fun stuff. You need to create a conserver.cf and conserver.passwd file. Those are defined with the --with-cffile and --with-pwdfile settings. If you ever need to know what values were compiled into conserver, run 'conserver -V'. See the conserver.cf/INSTALL file for instructions on setup of these files. - That's it! Just start up the console server and enjoy! Other Information And Gotchas - Debian Linux Distribution The Debian folks have conserver distributed with the package names of conserver-client and conserver-server. They are in the distribution "sid" and the "non-free" part (because the Ohio State license doesn't explicitly allow for modification to the code, even though it's totally implied and the intention of the author - I've even got proof in email! Oh well, can't blame the Debian folks for being cautious - they've been burned before, apparently). - Potential GCC bug Adam Morris reported a problem with the following line in console/console.c: if ((in_addr_t) (-1) == pPort->sin_addr.s_addr) { This tickles a GCC bug under HP-UX 11.11 using GCC 3.0.2 in 64-bit mode with optimization enabled (-O). The bug could possibly be provoked in other combinations as well. His fix is to change the line to: if ((in_addr_t) (-1) == inet_addr(pcToHost)) { It's also reported that newer versions of the compiler fix the issue, so if you happen to have problems with the client connecting to servers, you might be tickling this bug and you can upgrade the compiler, turn off the optimization, or apply this code change. conserver-8.2.4/LICENSE000066400000000000000000000027511344660520400145510ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2000, conserver.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. conserver-8.2.4/LICENSES000066400000000000000000000125151344660520400146730ustar00rootroot00000000000000Since this piece of software has had many contiributors, there is a "chain" of licensing information embedded in the files. I've copied what I could find here so that it's easy to reference. The entire bundle of software is guided by these licensing statements. ---------------------------------------------------------------------------- Copyright (c) 2000, conserver.com All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of conserver.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- Copyright (c) 1998, GNAC, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of GNAC, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana 47907. All rights reserved. This software is not subject to any license of the American Telephone and Telegraph Company or the Regents of the University of California. Permission is granted to anyone to use this software for any purpose on any computer system, and to alter it and redistribute it freely, subject to the following restrictions: 1. Neither the authors nor Purdue University are responsible for any consequences of the use of this software. 2. The origin of this software must not be misrepresented, either by explicit claim or by omission. Credit to the authors and Purdue University must appear in documentation and sources. 3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. This notice may not be removed or altered. ---------------------------------------------------------------------------- Copyright (c) 1990 The Ohio State University. All rights reserved. Redistribution and use in source and binary forms are permitted provided that: (1) source distributions retain this entire copyright notice and comment, and (2) distributions including binaries display the following acknowledgement: ``This product includes software developed by The Ohio State University and its contributors'' in the documentation or other materials provided with the distribution and in all advertising materials mentioning features or use of this software. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ---------------------------------------------------------------------------- conserver-8.2.4/Makefile.in000066400000000000000000000022131344660520400156020ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ mandir = @mandir@ libdir = @libdir@ includedir = @includedir@ sysconfdir = @sysconfdir@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below SUBDIRS = conserver console conserver.cf all: for n in $(SUBDIRS); do \ ( cd $$n && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; \ done autologin: ( cd autologin && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; autologin.install: ( cd autologin && $(MAKE) install $(MAKE_FLAGS) ) || exit 1; chat: ( cd contrib/chat && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; chat.install: ( cd contrib/chat && $(MAKE) install $(MAKE_FLAGS) ) || exit 1; test: ( cd test && ./dotest ) || exit 1 install: for n in $(SUBDIRS); do \ ( cd $$n && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; \ done clean: for n in $(SUBDIRS); do \ ( cd $$n && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; \ done distclean: for n in $(SUBDIRS); do \ ( cd $$n && $(MAKE) $@ $(MAKE_FLAGS) ) || exit 1; \ done rm -f config.cache config.log config.status config.h Makefile .PHONY: autologin autologin.install clean distclean install all test conserver-8.2.4/PROTOCOL000066400000000000000000000310101344660520400147160ustar00rootroot00000000000000 Conserver Protocol ================== What Is This? ------------- The following is an attempt to describe the client/server protocol used between the server (conserver) and the client (console). This document bases its information on conserver version 8.1.4, as it's the release currently available. If there are changes to the client/server protocol, the INSTALL file should reference them and, ideally, this document will be updated. The information is looked at from the point of the server, since it's the server that controls all information and triggers actions on the client (like a suspend). The client's perspective should be obvious from this information. SSL --- The client and server can negotiate an SSL connection. As far as the code is concerned, the SSL "layer" is transparent. Data is sent and received just as if it was unencrypted. Therefore, aside bringing up the SSL connection, the SSL bits are unimportant from a protocol standpoint. The client and server still send and receive the same information - it just happens to be encrypted to everyone else. "On-The-Wire" Data ------------------ The low-level, "on-the-wire" data is encapsulated similar to the telnet protocol. All data is sent "as-is" with the exception of 0xFF. 0xFF is used as a "command character" and both the client and server expect to see a predefined option after it. The possible options are: 0xFF, 'E', 'G', 'Z', and '.'. The 0xFF option says to use the literal character 0xFF. So, if there is a 0xFF character in the data stream to be sent, the code will send two 0xFF characters (it's similar to using '\\' in C strings to embed a '\'). The other options are used in various contexts, which will be described in detail below. Life As A Server ---------------- There are three different interfaces presented to clients by the server. I'm going to name the three modes "master", "group", and "console". The first two are line-based, and the third is character-based. To understand the differences, I must outline how conserver manages consoles. When conserver starts, it reads the configuration file, listens on the master socket, and, for each group of consoles it must manage (where the group size is set by -m), it forks off a copy of itself. Those child processes are what actually connect to the consoles and they each listen on a new socket for client connections. So, you end up with a parent process (that knows about all consoles) that manages the child processes (that know only about consoles it manages), and everyone is listening on an individual socket for connections from clients. The parent process interacts with clients in "master" mode. That mode expects line-based commands and responds similarly. Because it's the master, it understands a certain set of commands that are different than in "group" mode. The child processes interact with clients in "group" mode first, and negotiate a change to "console" mode when a client requests a connection to a specific console. "master" Mode ------------- When parent process gets a connection from a client, it either sends an "ok" string to signal it's ready or an error message (like "access from your host is refused") and the connection is dropped. At this point, there are a small number of commands recognized by the server, since most are restricted to "logged in" clients. Here's the list of available commands: exit disconnect help this help message login log in ssl start ssl session An "exit" is sent a "goodbye" response and the connection is dropped. A "help" is sent the list above. A "ssl" is sent an "ok" response and then the server expects the client to negotiate an ssl connection. A "login" requires one argument (the username) and is either sent an "ok", meaning the client is logged in, or a "passwd?" followed by the local hostname, asking for the user's password, which it expects next. If the client sends a valid password, an "ok" is sent, otherwise an error message and the connection is dropped. Upon successful login, the commands available are: call provide port for given console exit disconnect groups provide ports for group leaders help this help message master provide a list of master servers newlogs* close and open all logfiles (SIGUSR2) pid provide pid of master process quit* terminate conserver (SIGTERM) restart* restart conserver (SIGHUP) - deprecated reconfig* reread config file (SIGHUP) version provide version info for server up* bring up all downed consoles (SIGUSR1) * = requires admin privileges "exit" and "help" are the same as before the client logged login. The "call" command expects one argument, the console name to connect to. The server will respond with either a port number (if it's a locally managed console), an "@hostname" where hostname is the name of the remote conserver host managing the console (if it's a remotely managed console), or an error message (possibly multi-line). The client is not disconnected, whatever the response. The "groups" command responds with a colon-separated list of port numbers, which correspond to each of the child processes running on the local host. The client is not disconnected. The "master" command responds with a colon-separated list of "@hostname" names. The list includes any hosts (including the possibility of the local host) which have locally managed consoles. The client is not disconnected. The "newlogs" command reopens all logfiles used by conserver, assuming the user has administrative access. It responds with a message starting with "ok" if successful and an error message otherwise (like "unauthorized command"). The client is disconnected if it's successful. The "pid" command responds with the pid of the master process (in this case, the one the client is talking to). The client is not disconnected. The "quit" command will shut down conserver, assuming the user has administrative access. It responds with a message starting with "ok" if successful and an error message otherwise (like "unauthorized command"). The client is disconnected if it's successful. The "restart" command has been deprecated. You should use "reconfig". The "reconfig" command will cause conserver to reread the configuration file and apply any changes, assuming the user has administrative access. It responds with a message starting with "ok" if successful and an error message otherwise (like "unauthorized command"). The client is not disconnected. The "version" command responds with the version string. The client is not disconnected. The "up" command tries to "bring up" all disconnected consoles, assuming the user has administrative access. It responds with a message starting with "ok" if successful and an error message otherwise (like "unauthorized command"). The client is disconnected if it's successful. "group" Mode ------------ When a child process gets a connection from a client, it either sends an "ok" string to signal it's ready or an error message (like "access from your host is refused") and the connection is dropped. At this point, "group" mode acts just like "master" mode. Once the client successfully logs in, however, "group" mode has the recognizes the following commands: broadcast send broadcast message call connect to given console disconnect* disconnect the given user(s) examine examine port and baud rates exit disconnect group show users in this group help this help message hosts show host status and user info show console information textmsg send a text message * = requires admin privileges The "exit" and "help" commands are like the others documented above. The "broadcast" command expects a text string of the message to be sent to all users connected to this process. An "ok" is sent as a response. The "call" command expects one argument, the console name to connect to, just like in "master" mode. The difference here is that this requests the server to attach the client to the console and go into "console" mode. If the attachment is successful, the response will begin with a '[' character. If not, an error message is returned. The success responses are: [console is read-only] - console is read only [read-only -- initializing] - console is initializing, and read-only for the time being [line to console is down] - console is down [attached] - attached read-write [spy] - attached read-only The "disconnect" command expects an argument of the form "user@console" where either the "user" or "@console" part may be omitted. Upon success, a response of the form "ok -- disconnected X users" is sent, where X is the number of users disconnected. If a user is unauthorized or some other problem occurs, an error message (like "unauthorized command") is sent. The "examine" command returns a list of console information of the form that 'console -x' shows. The "group" command returns a list of console information of the form that 'console -w' shows. The "hosts" command returns a list of console information of the form that 'console -u' shows. The "info" command returns a list of console information of the form that 'console -i' shows. The "textmsg" command expects two arguments, the first being the recipient of the message in the form "user@console" (again, where the "user" or "@console" portion may be omitted) and the second being the string, like the "broadcast" command. The server returns "ok". "console" Mode -------------- As mentioned above, "console" mode is obtained by using the "call" command when connected to a child processes operating in "group" mode. "console" mode should look very familiar to a user of conserver, as it's what the user interacts with when connected to a console. There's really nothings special here. Each character received from the client is compared to the escape sequence, and if it matches, an action occurs on the server side. If it doesn't match the escape sequence, the data is sent on to the console. All data received from the console is sent to the client(s). Of course, there are certain exceptions to these rules, based on the state of the console and the state of the client. And, certain escape sequences cause special behaviors to occur. Most escape sequences cause the server to send information back to the user. Stuff like "^Ecw", "^Eci", and "^Ecu" are examples. The escape sequence is absorbed by the server, the server sends the client a variety of information, and things continue as before. The more "interesting" escape sequences are the following. "^Ec;" The server sends a 0xFF,'G' command sequence to the client, to signal a wish to move to a new console. The client then gets put into the same state as the "^Ecz" sequence (paused), which gives the client a chance to either resume the connection or disconnect. "^Ec|" The server sends a 0xFF,'E' command sequence to the client, to signal a wish to have the client program interact with a program, as opposed to the user. The server discards all data until it receives one of the following command sequences from the client: 0xFF,'E' Signals successful redirection of interaction to a program. The server then responds with "[rw]" or "[ro]" to tell the client whether or not they have read-write access. If not, the client should abort the program and send the abort command sequence below, as other data received by the server will just get dropped. 0xFF,'.' Abort the operation. The server assumes the redirection didn't happen and returns the client to it's normal mode. The server keeps the client in the "redirected" state until it receives a 0xFF,'.' command sequence from the client (which usually occurs when the client command terminates). If the client is "bumped" from read-write to read-only by another user, the server will send the client a 0xFF,'.' command sequence to tell it to abort the redirection and return control back to the user. "^Ecz" The server sends a 0xFF,'Z' command sequence to the client, to signal a wish to suspend to client process. The client is then put into a "paused" state where it receives no more data from the server. When the client is ready to resume receiving data, it sends a character of data to the server, at which point the server discards the character and sends back a status message of the form " -- MSG]". The current set of possible messages are: " -- line down]" " -- read-only]" " -- attached (nologging)]" " -- attached]" " -- spy mode]" conserver-8.2.4/README.md000066400000000000000000000023301344660520400150140ustar00rootroot00000000000000Conserver ========= [![Build Status](https://api.cirrus-ci.com/github/conserver/conserver.svg)](https://cirrus-ci.com/github/conserver/conserver) Conserver is an application that allows multiple users to watch a serial console at the same time. It can log the data, allows users to take write-access of a console (one at a time), and has a variety of bells and whistles to accentuate that basic functionality. The idea is that conserver will log all your serial traffic so you can go back and review why something crashed, look at changes (if done on the console), or tie the console logs into a monitoring system (just watch the logfiles it creates). With multi-user capabilities you can work on equipment with others, mentor, train, etc. It also does all that client-server stuff so that, assuming you have a network connection, you can interact with any of the equipment from home or wherever. Documentation ------------- See the `INSTALL` file for installation and the man pages for specifics. Downloading ----------- The latest version can be found on [GitHub](https://github.com/conserver/conserver/releases). Contributions ------------- Contributions distributed with the code can be found in the `contrib` subdirectory. conserver-8.2.4/TODO000066400000000000000000000100321344660520400142230ustar00rootroot00000000000000 TODO ==== Warning to those who aren't me: this is an unorganized list of things that might be classified as bugs, improvements, random thoughts or suggestions. Hopefully it will get cleaned up over time (yeah, right!). Bryan Stansell --------------------------------------------------------------------------- - Telnet protocol should be improved - Not even RFC 854 compliant...or maybe it is (as of 8.1.0) - Option negotiation semi-ignored - should we negotiate anything more? - Others? - syslog? Daniel E. Singer would like to see it - especially in regards to --use-libwrap code - alternate (md5) password encryption support in conserver.passwd - actually happens if the crypt() call supports it, like under linux - hpux has bigcrypt() also, which we support, so maybe we're covered - config file examples for various configurations - sample conserver.cf has some...but it's not explained well - per-line timestamps - only when not connected? - pipe input/output (console <-> program) via 'console' - some apps (net-ups thing, gdb) might need to talk to user - ^Ec| does this, but the interact with user bits might not work - actually, ^Ec| does work right with 8.1.0...one change that might be nice is the ability to NOT watch the i/o pass to the local command - try sending a big file to the local host with xmodem. - autologout? setting per console? gack, would have to interpret data. - this will never happen...i don't want to interpret data - "listen" capability (watch all/multiple consoles) - send data to multiple consoles (carbon copy) - Steve Lammert - authentication to terminal servers (ssh, passphrase, whatever) - ssh should probably just be handled by invoking the ssh command. so, that's really already covered, no? - passphrase...hmmm..could really use some sort of send/expect thing here. you could write a wrapper script of sorts, but it really would be nice to have a raw socket and do the right thing. - this does work, using the 'initcmd' option, so, all done? - cyclades ts1000/2000 port : "Moses, Joel" - strftime() idea for logfile names : Lars Kellogg-Stedman - 9600baud log replay? - server -M flag should accept multiple addresses (comma separated) - should client as well? - this may never happen...does anyone really need it? - automatic log rotation in general : Egan Ford - website docs on serial port configs - PCs (solaris x86, linux, *bsd, etc) - lilo - bios support - suggestions by Trevor Fiatal - include server hostname on 'console -x' output - i think the -i output covers it, but maybe not - ability to configure strings to be sent to a console periodically : Greg A. Woods - show attach/detach events to/of spy console clients : Greg A. Woods - redefine client escape sequence in conserver.cf : Toby Gerhart - not even sure if this is possible w/o confusing the client, but maybe with the new 8.1.0 client-server protocol, we can! - log rotation by date : Tom Pachla - strict file permission checks on conserver.passwd/conserver.cf : Erik Sjolund - netgroup support? : Nikolaos Papavassiliou and Phil Dibowitz - send sequences to console on client connect? (to repaint screen, for example) : John Cagle - uucp locks : Sebastian Zagrodzki - support more than 9 break sequences : Danish Mirza thought it was easy, but adding more than could break things with current encoding. doable, will have to think harder about it. - reintroduce console grouping : Martin Turba - quick-recheck of down consoles (for uds) and possibly only log state changes (instead of each try) : DJ Gregor conserver-8.2.4/_config.yml000066400000000000000000000000321344660520400156610ustar00rootroot00000000000000theme: jekyll-theme-caymanconserver-8.2.4/autologin/000077500000000000000000000000001344660520400155405ustar00rootroot00000000000000conserver-8.2.4/autologin/INSTALL.old000066400000000000000000000040521344660520400173470ustar00rootroot00000000000000To install this program you need root access and access to the physical console of the machine (either through the console server or via the physical world). First compile this program and install it as /etc/autologin or /usr/local/etc/autologin. If `/' is the only file system mounted sometimes you'll have to use the /etc directory. OK. Then login on the console as root and run # exec /etc/autologin -l root if you get a prompt back with no warnings things look good. Next try to force *your* account on the console (replace `ME' with your account): # finger -s ... # exec /etc/autologin -l ME $ finger -s ... You should have seen root on the console the first time you ran finger and `ME' on the console the second. If you got this far we are ready to make the leap. Exit the autologin shell as `ME' and login as root *on another terminal*. Edit /etc/inittab on a System V machine cons2:2:respawn:/usr/local/etc/autologin -t/dev/tty0 -lroot >/dev/console 2>&1 or co:2:respawn:/usr/local/etc/autologin -t/dev/console -lroot or on an RIOS run commands like: mkitab "cons0:013456789:respawn:/etc/getty /dev/console" mkitab "cons2:2:respawn:/usr/local/etc/autologin -t/dev/tty0 -lroot -gsystem >/dev/console 2>&1" chitab "cons:0123456789:off:/etc/getty /dev/console" chcons -a login=disable if you are running SunOS edit /etc/ttytab and comment out the line for the console and add a new one for autologin: #console "/usr/etc/getty cons8" unknown on local console "/usr/local/etc/autologin -lroot -t" xterm on local secure In either case restart init on the port now kill -1 1 or telinit 2 or what ever local custom sez. One of these will look right to you. You should get a root shell on the console that you can exit and it will just come back. When the machine reboots it should stick the same shell up. You can make the login name here be `ops' or some other `operator' account to run backups. We use `root'. -- "We've got all your slack" -- net.flamer kayessbee, Kevin Braunsdorf, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb conserver-8.2.4/autologin/Makefile.in000066400000000000000000000021241344660520400176040ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sysconfdir = @sysconfdir@ ### Installation programs and flags INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s LN_S = @LN_S@ MKDIR = @MKDIR@ ### Compiler and link options CC = @CC@ CFLAGS = @CFLAGS@ # -DPUCC -DSUN5 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" CPPFLAGS = -I.. -I$(top_srcdir) -I$(srcdir) $(DEFS) @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below AUTOLOGIN_OBJS = main.o autologin.o AUTOLOGIN_HDRS = ../config.h $(top_srcdir)/compat.h $(srcdir)/main.h ALL = autologin all: $(ALL) $(AUTOLOGIN_OBJS): $(AUTOLOGIN_HDRS) autologin: $(AUTOLOGIN_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o autologin $(AUTOLOGIN_OBJS) $(LIBS) .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< clean: rm -f *~ *.o $(ALL) core distclean: clean rm -f Makefile install: autologin $(MKDIR) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) autologin $(DESTDIR)$(bindir) .PHONY: clean distclean install conserver-8.2.4/autologin/README000066400000000000000000000013701344660520400164210ustar00rootroot00000000000000 This file used to say "I have not touched the autologin directory." That's no longer true. I've applied patches submitted by the user community (see the CHANGES file for details). I still cannot guarantee anything, but it sounds like at least one person out there is successfully using the code. And now for my original hand-waving... See the README.old and INSTALL.old files if you interested in the program. I cannot guarantee it will compile, install, or run. It is definately not integrated with ../Makefile or any of the porting support. If you have patches that make it work, please send them to me and I'll be more than happy to incorporate them. No one I've ever talked to found a need for something like autologin. Good luck. Bryan Stansell conserver-8.2.4/autologin/README.old000066400000000000000000000004031344660520400171720ustar00rootroot00000000000000This program can be used to put a root shell on the console at boot time. See the manual page. ksb Ports to: HOST OS CDEFS SUN3 4.1.1 -DSUNOS SUN4 4.1.2 -DSUNOS V386 ? -DSRM EPIX ? -DEPIX -systype posix -I/usr/include (+ extra .o files) IBMR2 -DIBMR2 conserver-8.2.4/autologin/autologin.c000066400000000000000000000332301344660520400177060ustar00rootroot00000000000000/* * Perform an auto-login on a specified tty port. * * autologin [-u] [-c] [-e] [-g] -l -t * * Jeff W. Stewart - Purdue University Computing Center * * some of the ideas in this code are based on the Ohio State * console server as re-coded by Kevin Braunsdorf (PUCC) * * This program was written to be run out of inittab. */ #include #include #include #include #include #include #include #include #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) /* * There is no official registry of non-vendor audit event numbers, * but the following should be OK. * * You need to add a line by hand to /etc/security/audit_event to make * praudit(1) look pretty: * * 32900:AUE_autologin:autologin:lo * * If you have to change the value for AUE_autologin, you'll also need * to change the /etc/security/audit_event line. */ # define AUE_autologin 32900 # include # include # include # include # include #endif #include #include #define TTYMODE 0600 #ifndef O_RDWR # define O_RDWR 2 #endif #ifndef UTMP_FILE # if defined(_PATH_UTMP) # define UTMP_FILE _PATH_UTMP # else # define UTMP_FILE "/etc/utmp" # endif #endif #define PATH_SU "/bin/su" /* * Global variables */ extern char *progname; gid_t awGrps[NGROUPS_MAX]; int iGrps = 0; /* * External variables */ extern int optind; extern char *optarg; void make_utmp(); void usage(); int Process(void) { int iErrs = 0; int i, iNewGrp; gid_t wGid; uid_t wUid; char *pcCmd = (char *)0, *pcDevTty = (char *)0; #ifdef HAVE_GETUSERATTR char *pcGrps; #endif struct passwd *pwd; struct stat st; struct termios n_tio; #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) char my_hostname[MAXHOSTNAMELEN]; #endif #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) if (0 != gethostname(my_hostname, sizeof(my_hostname))) { (void)fprintf(stderr, "%s: gethostname: %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } #endif if ((char *)0 != pcCommand) { if ((char *)0 == (pcCmd = (char *)malloc(strlen(pcCommand) + 4))) { (void)fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } (void)strcpy(pcCmd, "-c "); (void)strcat(pcCmd, pcCommand); } if ((char *)0 != pcGroup) { iErrs += addgroup(pcGroup); } if ((char *)0 == pcLogin) { static char acLogin[17]; if ((struct passwd *)0 == (pwd = getpwuid(geteuid()))) { (void)fprintf(stderr, "%s: %d: uid unknown\n", progname, geteuid()); exit(1); /* NOTREACHED */ } pcLogin = strcpy(acLogin, pwd->pw_name); } else if ((struct passwd *)0 == (pwd = getpwnam(pcLogin))) { (void)fprintf(stderr, "%s: %s: login name unknown\n", progname, pcLogin); exit(1); /* NOTREACHED */ } wUid = pwd->pw_uid; wGid = pwd->pw_gid; (void)endpwent(); #ifdef HAVE_GETUSERATTR /* getuserattr() returns a funny list of groups: * "grp1\0grp2\0grp3\0\0" */ if (0 == getuserattr(pcLogin, S_SUGROUPS, &pcGrps, SEC_LIST)) { while ('\000' != *pcGrps) { /* ignore "ALL" and any group beginning with '!' */ if ('!' == *pcGrps || 0 != strcmp(pcGrps, "ALL")) { iErrs += addgroup(pcGrps); } pcGrps = pcGrps + strlen(pcGrps) + 1; } } #endif /* HAVE_GETUSERATTR */ (void)endgrent(); if ((char *)0 != pcTty) { if ('/' == *pcTty) { pcDevTty = pcTty; } else { if ((char *)0 == (pcDevTty = (char *)malloc(strlen(pcTty) + 5 + 1))) { (void)fprintf(stderr, "%s: malloc: %s\n", progname, strerror(errno)); exit(1); } sprintf(pcDevTty, "/dev/%s", pcTty); } if (0 != stat(pcDevTty, &st)) { (void)fprintf(stderr, "%s: Can't stat %s: %s\n", progname, pcDevTty, strerror(errno)); ++iErrs; #if defined(VCHR) && defined(VMPC) } else if (VCHR != st.st_type && VMPC != st.st_type) { (void)fprintf(stderr, "%s: %s is not a character device\n", progname, pcDevTty); ++iErrs; #endif } } else { pcDevTty = (char *)0; } if (iErrs) { usage(); exit(1); /* NOTREACHED */ } if (0 != geteuid()) { (void)fprintf(stderr, "%s: Must be root!!!\n", progname); exit(1); /* NOTREACHED */ } if (iGrps && 0 < setgroups(iGrps, awGrps)) { (void)fprintf(stderr, "%s: Can't setgroups(): %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } /* Close open files */ #if HAVE_CLOSEFROM closefrom((char *)0 == pcTty ? 3 : 0); #else for (i = (char *)0 == pcTty ? 3 : 0; i < getdtablesize(); ++i) { (void)close(i); } #endif /* Make us a session leader so that when we open /dev/tty * it will become our controlling terminal. */ if (-1 == (iNewGrp = getsid(getpid()))) { if (-1 == (iNewGrp = setsid())) { (void)fprintf(stderr, "%s: setsid: %d: %s\n", progname, iNewGrp, strerror(errno)); iNewGrp = getpid(); } } #if defined(HAVE_BSM_AUDIT_H) && defined(HAVE_LIBBSM) if (!cannot_audit(0)) { # if defined(HAVE_GETAUDIT_ADDR) struct auditinfo_addr audit_info; # else struct auditinfo audit_info; # endif au_mask_t audit_mask; # if !defined(HAVE_GETAUDIT_ADDR) struct hostent *hp; # endif int iAuditFile; int fShowEvent = 1; token_t *ptAuditToken; (void)memset(&audit_info, 0, sizeof(audit_info)); audit_info.ai_auid = wUid; audit_info.ai_asid = getpid(); audit_mask.am_success = audit_mask.am_failure = 0; (void)au_user_mask(pcLogin, &audit_mask); audit_info.ai_mask.am_success = audit_mask.am_success; audit_info.ai_mask.am_failure = audit_mask.am_failure; # if defined(HAVE_GETAUDIT_ADDR) (void)aug_get_machine(my_hostname, &audit_info.ai_termid.at_addr[0], &audit_info.ai_termid.at_type); # else if ((char *)0 != (hp = gethostbyname(my_hostname)) && AF_INET == hp->h_addrtype) { (void)memcpy(&audit_info.ai_termid.machine, hp->h_addr, sizeof(audit_info.ai_termid.machine)); } # endif # if defined(HAVE_GETAUDIT_ADDR) if (0 > setaudit_addr(&audit_info, sizeof(audit_info))) # else if (0 > setaudit(&audit_info)) # endif { fprintf(stderr, "%s: setaudit failed: %s\n", progname, strerror(errno)); fShowEvent = 0; } if (fShowEvent) { fShowEvent = au_preselect(AUE_autologin, &audit_mask, AU_PRS_SUCCESS, AU_PRS_REREAD); } if (fShowEvent) { iAuditFile = au_open(); # if defined(HAVE_GETAUDIT_ADDR) ptAuditToken = au_to_subject_ex(wUid, wUid, wGid, wUid, wGid, audit_info.ai_asid, audit_info.ai_asid, &audit_info.ai_termid), # else ptAuditToken = au_to_subject(wUid, wUid, wGid, wUid, wGid, audit_info.ai_asid, audit_info.ai_asid, &audit_info.ai_termid), # endif (void)au_write(iAuditFile, ptAuditToken); ptAuditToken = au_to_text(gettext("successful login")); (void)au_write(iAuditFile, ptAuditToken); if ((char *)0 != pcCmd) { ptAuditToken = au_to_text(pcCmd); (void)au_write(iAuditFile, ptAuditToken); } # if defined(HAVE_GETAUDIT_ADDR) ptAuditToken = au_to_return32(0, 0); # else ptAuditToken = au_to_return(0, 0); # endif (void)au_write(iAuditFile, ptAuditToken); if (0 > au_close(iAuditFile, AU_TO_WRITE, AUE_autologin)) { fprintf(stderr, "%s: audit write failed: %s", progname, strerror(errno)); } } } #endif /* Open the TTY for stdin, stdout and stderr */ if ((char *)0 != pcDevTty) { #ifdef TIOCNOTTY if (-1 != (i = open("/dev/tty", 2, 0))) { if (ioctl(i, TIOCNOTTY, (char *)0)) (void)fprintf(stderr, "%s: ioctl(%d, TIOCNOTTY, (char *)0): %s\n", progname, i, strerror(errno)); (void)close(i); } #endif if (0 != open(pcDevTty, O_RDWR, 0666)) { exit(1); /* NOTREACHED */ } dup(0); dup(0); } /* put the tty in out process group */ #ifdef HAVE_TCGETPGRP if (-1 >= (i = tcgetpgrp(0))) { (void)fprintf(stderr, "%s: tcgetpgrp: %s\n", progname, strerror(errno)); } #endif if (-1 != i && setpgrp(0, i)) { (void)fprintf(stderr, "%s: setpgrp: %s, i = %d\n", progname, strerror(errno), i); } #ifdef HAVE_TCSETPGRP if (tcsetpgrp(0, iNewGrp)) { (void)fprintf(stderr, "%s: tcsetpgrp: %s\n", progname, strerror(errno)); } #endif if (-1 != iNewGrp && setpgrp(0, iNewGrp)) { (void)fprintf(stderr, "%s: setpgrp: %s, iNewGrp = %d\n", progname, strerror(errno), iNewGrp); } /* put the tty in the correct mode */ #ifdef HAVE_TCGETATTR if (0 != tcgetattr(0, &n_tio)) { (void)fprintf(stderr, "%s: tcgetattr: %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } #else if (0 != ioctl(0, TCGETS, &n_tio)) { (void)fprintf(stderr, "%s: iotcl: TCGETS: %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } #endif n_tio.c_iflag &= ~(IGNCR | IUCLC); n_tio.c_iflag |= ICRNL | IXON | IXANY; n_tio.c_oflag &= ~(OLCUC | ONOCR | ONLRET | OFILL | NLDLY | CRDLY | TABDLY | BSDLY); n_tio.c_oflag |= OPOST | ONLCR | TAB3; n_tio.c_lflag &= ~(XCASE | NOFLSH | ECHOK | ECHONL); n_tio.c_lflag |= ISIG | ICANON | ECHO; n_tio.c_cc[VEOF] = '\004'; /* ^D */ n_tio.c_cc[VEOL] = '\000'; /* EOL */ n_tio.c_cc[VERASE] = '\010'; /* ^H */ n_tio.c_cc[VINTR] = '\003'; /* ^C */ n_tio.c_cc[VKILL] = '\025'; /* ^U */ /* MIN */ n_tio.c_cc[VQUIT] = '\034'; /* ^\ */ n_tio.c_cc[VSTART] = '\021'; /* ^Q */ n_tio.c_cc[VSTOP] = '\023'; /* ^S */ n_tio.c_cc[VSUSP] = '\032'; /* ^Z */ #ifdef HAVE_TCSETATTR if (0 != tcsetattr(0, TCSANOW, &n_tio)) { (void)fprintf(stderr, "%s: tcsetattr: %s\n", progname, strerror(errno)); exit(1); /* NOTREACHED */ } #endif if (fMakeUtmp) { extern char *ttyname(); make_utmp(pcLogin, (char *)0 != pcTty ? pcTty : ttyname(0)); } /* Change ownership and modes on the tty. */ if ((char *)0 != pcDevTty) { (void)chown(pcDevTty, wUid, wGid); (void)chmod(pcDevTty, (mode_t) TTYMODE); } if ((char *)0 != pcCmd) { execl(PATH_SU, "su", "-", pcLogin, pcCmd, (char *)0); } else { execl(PATH_SU, "su", "-", pcLogin, (char *)0); } } #ifndef HAVE_PUTENV int putenv(char *pcAssign) { register char *pcEq; if ((char *)0 != (pcEq = strchr(pcAssign, '='))) { *pcEq++ = '\000'; (void)setenv(pcAssign, pcEq, 1); *--pcEq = '='; } else { unsetenv(pcAssign); } } #endif int addgroup(char *pcGrp) { struct group *grp; grp = getgrnam(pcGrp); if ((struct group *)0 == grp) { (void)fprintf(stderr, "%s: Unknown group: %s\n", progname, pcGrp); return (1); } if (iGrps >= NGROUPS_MAX) { (void)fprintf(stderr, "%s: Too many groups specified with \"%s\".\n", progname, pcGrp); return (1); } awGrps[iGrps++] = grp->gr_gid; return (0); } /* install a utmp entry to show the use we know is here is here (ksb) */ void make_utmp(char *pclogin, char *pctty) { register int iFound, iPos; register int fdUtmp; register char *pcDev; register struct utmp *up; auto struct utmp utmp; if ((char *)0 == pctty) { return; } if ((fdUtmp = open(UTMP_FILE, O_RDWR, 0664)) < 0) { return; } /* create empty utmp entry */ (void)memset(&utmp, 0, sizeof(struct utmp)); /* Only the last portion of the tty is saved, unless it's * all digits. Then back up and include the previous part * /dev/pty/02 -> pty/02 (not just 02) */ if ((char *)0 != (pcDev = strrchr(pctty, '/'))) { if (!*(pcDev + strspn(pcDev, "/0123456789"))) { while (pcDev != pctty && *--pcDev != '/') { } } if (*pcDev == '/') { ++pcDev; } } else { pcDev = pctty; } #ifdef HAVE_GETUTENT /* look through getutent's by pid */ (void)setutent(); utmp.ut_pid = getpid(); iFound = iPos = 0; while ((up = getutent()) != NULL) { if (up->ut_pid == utmp.ut_pid) { utmp = *up; ++iFound; break; } iPos++; } (void)endutent(); /* we were an initprocess, now we are a login shell */ utmp.ut_type = USER_PROCESS; (void)strncpy(utmp.ut_user, pclogin, sizeof(utmp.ut_user)); if ('\000' == utmp.ut_line[0]) { (void)strncpy(utmp.ut_line, pcDev, sizeof(utmp.ut_line)); } #else # ifdef HAVE_SETTTYENT { register struct ttyent *ty; /* look through ttyslots by line? */ (void)setttyent(); iFound = iPos = 0; while ((ty = getttyent()) != NULL) { if (strcmp(ty->ty_name, pcDev) == 0) { ++iFound; break; } iPos++; } /* fill in utmp from ty ZZZ */ (void)endttyent(); } (void)strncpy(utmp.ut_line, pcDev, sizeof(utmp.ut_line)); (void)strncpy(utmp.ut_name, pclogin, sizeof(utmp.ut_name)); (void)strncpy(utmp.ut_host, "(autologin)", sizeof(utmp.ut_host)); # else /* look through /etc/utmp by hand (sigh) */ iFound = iPos = 0; while (sizeof(utmp) == read(fdUtmp, &utmp, sizeof(utmp))) { if (0 == strncmp(utmp.ut_line, pcDev, sizeof(utmp.ut_line))) { ++iFound; break; } iPos++; } (void)strncpy(utmp.ut_name, pclogin, sizeof(utmp.ut_name)); # endif #endif utmp.ut_time = time((time_t *)0); if (0 == iFound) { fprintf(stderr, "%s: %s: no ttyslot\n", progname, pctty); } else if (-1 == lseek(fdUtmp, (off_t) (iPos * sizeof(utmp)), 0)) { fprintf(stderr, "%s: lseek: %s\n", progname, strerror(errno)); } else { (void)write(fdUtmp, (char *)&utmp, sizeof(utmp)); } (void)close(fdUtmp); } void usage(void) { char *u_pch; int u_loop; for (u_loop = 0; (char *)0 != (u_pch = au_terse[u_loop]); ++u_loop) { fprintf(stdout, "%s: usage%s\n", progname, u_pch); } for (u_loop = 0; (char *)0 != (u_pch = u_help[u_loop]); ++u_loop) { fprintf(stdout, "%s\n", u_pch); } } conserver-8.2.4/autologin/autologin.m000066400000000000000000000016601344660520400177220ustar00rootroot00000000000000# mkcmd parser for autologin program %% %% integer variable "iErrs" { init "0" } char* 'c' { named "pcCommand" param "cmd" init '(char *)0' help "command to run" } function 'e' { named "putenv" param "env=value" update "if (%n(%N) != 0) { (void) fprintf(stderr, \"%%s: putenv(\\\"%%s\\\"): failed\\n\", %b, %N);exit(1);}" help "environment variable to set" } char* 'g' { named "pcGroup" param "group" init '(char *)0' help "initial group" } char* 'l' { named "pcLogin" param "login" init '(char *)0' help "login name" } char* 't' { named "pcTty" param "tty" init '(char *)0' help "attach to this terminal" } boolean 'u' { named "fMakeUtmp" init "1" update "%run = 0;" help "do no make utmp entry" } exit { named "Process" update "%n();" aborts "exit(iErrs);" } conserver-8.2.4/autologin/autologin.man000066400000000000000000000054411344660520400202420ustar00rootroot00000000000000.TH AUTOLOGIN 8L PUCC .SH NAME autologin \- create an automatic login session from /etc/inittab .SH SYNOPSIS .B /usr/local/etc/autologin [ .B \-u ] [ .B \-c .I command ] [ .B \-e .IB env = val ] [ .B \-g .I group ] [ .B \-l .I login ] [ .B \-t .I tty ] .SH DESCRIPTION .I Autologin creates a login session for .I login by running an .RB ` "su \- .IR login ' on the specified device .RI ( tty ). If a .I command is given, that command is executed via .RB ` "su \- .IB login " \-c .IR command .' .PP .I Autologin also changes the ownership of the tty port to the user and sets the mode to 0600. .PP On AIX, .I autologin uses .IR getuserattr ( 3 ) to determine which groups are required to su to .I login and sets those groups for the process before executing the .IR su ( 1 ) command. .SH OPTIONS .TP \w'command'u+4 .BI \-c command Execute the command .IR command . The default action is to create a login shell. .TP .BI \-e env = val Add the evironment variable assignment .IB env = val to the environment. .TP .BI \-g group Add .I group to current process group set before running .IR su ( 1 ). This option probably isn't necessary since the group set should be properly handled through the use of .IR getuserattr ( 3 ). .TP .BI \-l login Create the login process for the user .IR login . If none is given the effective uid is used. .TP .BI \-t tty .I tty is the name of the character-special file that corresponds to the terminal to be logged in. If none is given the current controlling terminal is used. .TP .B \-u Don't create a utmp entry. Normally, an entry is written to .I /etc/utmp to maintain a record of users logged into the system. .SH EXAMPLES Adding the following line to .I /etc/inittab on an AIX machine establishes a root login on the console terminal .RI ( /dev/tty0 ) with any error messages directed to .IR /dev/console : .br .na cons2:2:respawn:/usr/local/etc/autologin \-t/dev/tty0 \-lroot > /dev/console 2>&1 .ad .PP Adding the following line to .I /etc/inittab on an AIX machine causes ssinfo to be logged in on .I /dev/tty10 with the .B TERM environment variable set to .IR reg20 : .br .na ss10:2:respawn:/usr/local/etc/autologin \-e TERM=reg20 \-t/dev/tty10 \-lssinfo .ad .PP Adding the following line to .I /etc/ttytab on a Sun .RI 4.1. x machine establishes a root login on the console device: .br .na console "/usr/local/etc/autologin \-lroot \-t" xterm on local secure .ad .PP Note that .I init provides the .I tty argument on the end of the command. .SH FILES /bin/su .br /etc/inittab .br /etc/passwd .br /etc/utmp .SH "SEE ALSO" su(1), getuserattr(3), inittab(5), init(8). .SH AUTHOR Jeff W\. Stewart \- Purdue University Computing Center .SH BUGS Doesn't add entries to /usr/adm/wtmp?? .br Doesn't add utmp entry unless it's been setup by init(8). .br Only runs on SUN4, EPIX, SUN3, IBMR2 (currently). conserver-8.2.4/autologin/main.c000066400000000000000000000101321344660520400166250ustar00rootroot00000000000000/* * machine generated cmd line parser * built by mkcmd version 7.6 Gamma */ #include #include #include #include #ifndef HAVE_GETOPT static int optopt; /* character checked for validity */ /* get option letter from argument vector, also does -number correctly * for nice, xargs, and stuff (these extras by ksb) * does +arg if you give a last argument of "+", else give (char *)0 */ static int getopt(int nargc, char **nargv, char *ostr) { register char *oli; /* option letter list index */ static char EMSG[] = ""; /* just a null place */ static char *place = EMSG; /* option letter processing */ if ('\000' == *place) { /* update scanning pointer */ if (optind >= nargc) return EOF; if (nargv[optind][0] != '-') { register int iLen; return EOF; } place = nargv[optind]; if ('\000' == *++place) /* "-" (stdin) */ return EOF; if (*place == '-' && '\000' == place[1]) { /* found "--" */ ++optind; return EOF; } } /* option letter okay? */ /* if we find the letter, (not a `:') * or a digit to match a # in the list */ if ((optopt = *place++) == ':' || ((char *)0 == (oli = strchr(ostr, optopt)) && (!(isdigit(optopt) || '-' == optopt) || (char *)0 == (oli = strchr(ostr, '#'))))) { if (!*place) ++optind; return ('?'); } if ('#' == *oli) { /* accept as -digits */ optarg = place - 1; ++optind; place = EMSG; return '#'; } if (*++oli != ':') { /* don't need argument */ optarg = NULL; if ('\000' == *place) ++optind; } else { /* need an argument */ if (*place) { /* no white space */ optarg = place; } else if (nargc <= ++optind) { /* no arg!! */ place = EMSG; return '*'; } else { optarg = nargv[optind]; /* white space */ } place = EMSG; ++optind; } return optopt; /* dump back option letter */ } #endif /* ! HAVE_GETOPT */ char *progname = "", *au_terse[] = { " [-u] [-c cmd] [-e env=value] [-g group] [-l login] [-t tty]", " -h", " -V", (char *)0 }, *u_help[] = { "c cmd command to run", "e env=value environment variable to set", "g group initial group", "h print this help message", "l login login name", "t tty attach to this terminal", "u do no make utmp entry", "V show version information", (char *)0 }, *pcCommand = (char *)0, *pcGroup = (char *)0, *pcLogin = (char *)0, *pcTty = (char *)0; int fMakeUtmp = 1, iErrs = 0; #ifndef u_terse # define u_terse (au_terse[0]) #endif /* * parser */ int main(int argc, char **argv) { static char sbOpt[] = "c:e:g:hl:t:uV", *u_pch = (char *)0; static int u_loop = 0; register int u_curopt; extern int atoi(); progname = strrchr(argv[0], '/'); if ((char *)0 == progname) progname = argv[0]; else ++progname; while (EOF != (u_curopt = getopt(argc, argv, sbOpt))) { switch (u_curopt) { case '*': fprintf(stderr, "%s: option `-%c\' needs a parameter\n", progname, optopt); exit(1); case '?': fprintf(stderr, "%s: unknown option `-%c\', use `-h\' for help\n", progname, optopt); exit(1); case 'c': pcCommand = optarg; continue; case 'e': if (putenv(optarg) != 0) { (void)fprintf(stderr, "%s: putenv(\"%s\"): failed\n", progname, optarg); exit(1); } continue; case 'g': pcGroup = optarg; continue; case 'h': for (u_loop = 0; (char *)0 != (u_pch = au_terse[u_loop]); ++u_loop) { if ('\000' == *u_pch) { fprintf(stdout, "%s: with no parameters\n", progname); continue; } fprintf(stdout, "%s: usage%s\n", progname, u_pch); } for (u_loop = 0; (char *)0 != (u_pch = u_help[u_loop]); ++u_loop) { fprintf(stdout, "%s\n", u_pch); } exit(0); case 'l': pcLogin = optarg; continue; case 't': pcTty = optarg; continue; case 'u': fMakeUtmp = 0; continue; case 'V': printf("%s\n", progname); exit(0); } break; } Process(); exit(iErrs); } conserver-8.2.4/autologin/main.h000066400000000000000000000004521344660520400166360ustar00rootroot00000000000000/* * parse options */ extern char *progname, *au_terse[4], *u_help[9]; #ifndef u_terse # define u_terse (au_terse[0]) #endif extern int main(); extern int fMakeUtmp, iErrs; extern char *pcCommand, *pcGroup, *pcLogin, *pcTty; /* from std_help.m */ /* from std_version.m */ /* from autologin.m */ conserver-8.2.4/compat.h000066400000000000000000000135461344660520400152040ustar00rootroot00000000000000#include /* things everything seems to need */ #include #include #include #include #include #include #include #include #include #include #include #include /* If, when processing a logfile for replaying the last N lines, * we end up seeing more than MAXREPLAYLINELEN characters in a line, * abort processing and display the data. Why? There could be some * very large logfiles and very long lines and we'd chew up lots of * memory and send a LOT of data down to the client - all potentially * bad. If there's a line over this in size, would you really want to * see the whole thing (and possibly others)? */ #if !defined(MAXREPLAYLINELEN) # define MAXREPLAYLINELEN 10000 #endif /* the default escape sequence used to give meta commands */ #if !defined(DEFATTN) # define DEFATTN '\005' #endif #if !defined(DEFESC) # define DEFESC 'c' #endif /* set the default length of the replay functions * DEFREPLAY for 'r' * DEFPLAYBACK for 'p' */ #if !defined(DEFREPLAY) # define DEFREPLAY 20 #endif #if !defined(PLAYBACK) # define DEFPLAYBACK 60 #endif /* For legacy compile-time setting of the port... */ #if ! defined(DEFPORT) # if defined(SERVICENAME) # define DEFPORT SERVICENAME # else # if defined(PORTNUMBER) # define DEFPORT PORTNUMBER # else # define DEFPORT "conserver" # endif # endif #endif #if STDC_HEADERS # include # include #else # include # ifndef HAVE_STRCHR # define strchr index # define strrchr rindex # endif #endif #if !HAVE_STRCASECMP && HAVE_STRICMP # define strcasecmp stricmp # define strncasecmp strnicmp #endif #ifdef HAVE_UNISTD_H # include #endif /* if you do not have fd_set's here is a possible emulation */ #ifdef HAVE_SYS_SELECT_H # include #endif #ifndef FD_ZERO typedef long fd_set; # define FD_ZERO(a) {*(a)=0;} # define FD_SET(d,a) {*(a) |= (1 << (d));} # define FD_CLR(d,a) {*(a) &= ~(1 << (d));} # define FD_ISSET(d,a) (*(a) & (1 << (d))) #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_IOCTL_COMPAT_H # include #endif #include #ifndef TAB3 # ifdef OXTABS # define TAB3 OXTABS # else # ifdef XTABS # define TAB3 XTABS # else # define TAB3 0 # endif # endif #endif #ifdef HAVE_STROPTS_H # include #endif #ifdef HAVE_TTYENT_H # include #endif #ifdef HAVE_SYS_TTOLD_H # include #endif #if HAVE_TYPES_H # include #endif #if HAVE_SYS_WAIT_H # include #endif #define LO(s) ((unsigned)((s) & 0377)) #define HI(s) ((unsigned)(((s) >> 8) & 0377)) #if !defined(WIFEXITED) # define WIFEXITED(s) (LO(s)==0) #endif #if !defined(WEXITSTATUS) # define WEXITSTATUS(s) HI(s) #endif #if !defined(WIFSIGNALED) # define WIFSIGNALED(s) ((LO(s)>0)&&(HI(s)==0)) #endif #if !defined(WTERMSIG) # define WTERMSIG(s) (LO(s)&0177) #endif #if !defined(WIFSTOPPED) # define WIFSTOPPED(s) ((LO(s)==0177)&&(HI(s)!=0)) #endif #if !defined(WSTOPSIG) # define WSTOPSIG(s) HI(s) #endif #if HAVE_SYSEXITS_H # include #else # define EX_OK 0 # define EX_UNAVAILABLE 69 # define EX_TEMPFAIL 75 #endif #include #if !defined(HAVE_STRERROR) extern int errno; extern char *sys_errlist[]; # define strerror(Me) (sys_errlist[Me]) #endif #if HAVE_H_ERRLIST extern int h_errno; extern char *h_errlist[]; # define hstrerror(Me) (h_errlist[Me]) #else # define hstrerror(Me) "host lookup error" #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SHADOW_H # include #endif #ifdef HAVE_CRYPT_H # include #endif #ifdef HAVE_HPSECURITY_H # include #endif #ifdef HAVE_PROT_H # include #endif #ifdef HAVE_GETOPT_H # include #endif #ifdef HAVE_SYS_VLIMIT_H # include #else # include #endif #ifdef HAVE_SYS_RESOURCE_H # include #endif #ifdef HAVE_SYS_UIO_H # include #endif #ifdef HAVE_SYS_PROC_H # include #endif #ifdef HAVE_SYS_AUDIT_H # include #endif #ifdef HAVE_USERSEC_H # include #endif #ifdef HAVE_PTY_H # include #endif #ifdef HAVE_LIBUTIL_H # include #endif #ifdef HAVE_UTIL_H # include #endif #ifndef NGROUPS_MAX # define NGROUPS_MAX 8 #endif #ifndef HAVE_GETSID # define getsid(Mp) (Mp) #endif #ifndef HAVE_SETSID # define setsid() getpid() #endif #ifndef HAVE_SETGROUPS # define setgroups(x, y) 0 #endif #ifndef HAVE_IN_ADDR_T typedef unsigned long in_addr_t; #endif #ifndef HAVE_SOCKLEN_T typedef int socklen_t; #endif /* * IUCLC, OLCUC and XCASE were removed from IEEE Std 1003.1-200x * as legacy definitions. */ #ifndef IUCLC # define IUCLC 0 #endif #ifndef OLCUC # define OLCUC 0 #endif #ifndef XCASE # define XCASE 0 #endif /* Some systems don't have OFILL or *DLY. */ #ifndef OFILL # define OFILL 0 #endif #ifndef NLDLY # define NLDLY 0 #endif #ifndef CRDLY # define CRDLY 0 #endif #ifndef TABDLY # define TABDLY 0 #endif #ifndef BSDLY # define BSDLY 0 #endif #ifndef ONOCR # define ONOCR 0 #endif #ifndef ONLRET # define ONLRET 0 #endif #ifndef SEEK_SET # define SEEK_SET L_SET #endif /* setup a conditional debugging line */ #ifndef CONDDEBUG # define CONDDEBUG(line) if (fDebug) {debugFileName=__FILE__; debugLineNo=__LINE__; Debug line;} #endif #if HAVE_DMALLOC # include #endif #if HAVE_FREEIPMI # include #endif #ifndef INADDR_STYPE # if USE_IPV6 # define INADDR_STYPE struct sockaddr_storage # else # define INADDR_STYPE struct in_addr # endif #endif #ifndef SOCKADDR_STYPE # if USE_IPV6 # define SOCKADDR_STYPE struct sockaddr_storage # else # define SOCKADDR_STYPE struct sockaddr_in # endif #endif conserver-8.2.4/configure.ac000066400000000000000000000626441344660520400160410ustar00rootroot00000000000000dnl ### autoheader templates ######################################## AH_TEMPLATE([CONFIGINVOCATION], [./configure invocation]) AH_TEMPLATE([DEFPORT], [Socket used to communicate]) AH_TEMPLATE([DEFBASEPORT], [Base socket used for secondary channel]) AH_TEMPLATE([MASTERHOST], [Hostname of console server]) AH_TEMPLATE([CONFIGFILE], [Config file path]) AH_TEMPLATE([CLIENTCONFIGFILE], [Client config file path]) AH_TEMPLATE([PASSWDFILE], [Password file path]) AH_TEMPLATE([LOGFILEPATH], [Logfile path]) AH_TEMPLATE([MAXMEMB], [Number of consoles per child process]) AH_TEMPLATE([CONNECTTIMEOUT], [TCP connection timeout]) AH_TEMPLATE([PIDFILE], [pidfile to write to]) AH_TEMPLATE([USE_LIBWRAP], [use tcp_wrappers libwrap]) dnl AH_TEMPLATE([HAVE_POSIX_REGCOMP], [have POSIX regcomp]) AH_TEMPLATE([HAVE_PAM], [have PAM support]) AH_TEMPLATE([HAVE_OPENSSL], [have openssl support]) AH_TEMPLATE([HAVE_GSSAPI], [have gss-api support]) AH_TEMPLATE([HAVE_FREEIPMI], [have freeipmi support]) AH_TEMPLATE([STRIP_REALM], [retry username without @REALM with gss-api authentication]) AH_TEMPLATE([HAVE_DMALLOC], [have dmalloc support]) AH_TEMPLATE([HAVE_SA_LEN],[Defined if sa_len member exists in struct sockaddr]) AH_TEMPLATE([TRUST_REVERSE_DNS],[Defined if we trust reverse DNS]) AH_TEMPLATE([USE_EXTENDED_MESSAGES],[Defined if we produce extended messages]) AH_TEMPLATE([USE_UNIX_DOMAIN_SOCKETS],[Defined if we use Unix domain sockets]) AH_TEMPLATE([USE_IPV6], [Defined if building with IPv6 support]) AH_TEMPLATE([UDSDIR], [Directory for Unix domain sockets]) AH_TEMPLATE([FOR_CYCLADES_TS], [Defined if building for a Cyclades TS]) AH_TEMPLATE([REQ_SERVER_CERT], [Defined if client requires server SSL certificate]) AH_TEMPLATE([TRUST_UDS_CRED], [Defined if we trust credentials from UDS client]) dnl ### Normal initialization. ###################################### AC_INIT([conserver],m4_esyscmd_s([./package/get-version number])) AC_PREREQ(2.59) AC_CONFIG_SRCDIR([conserver/main.c]) AC_CONFIG_HEADER(config.h) AC_DEFINE_UNQUOTED(CONFIGINVOCATION, "$0 $@") dnl ### Set some option defaults. ################################### if test -z "$CFLAGS"; then CFLAGS="-O" fi MKDIR="mkdir -p -m 755" AC_SUBST(MKDIR) AC_SUBST(CONSERVER_VERSION, m4_esyscmd_s([./package/get-version number])) AC_SUBST(CONSERVER_DATE, m4_esyscmd_s([./package/get-version date])) dnl ### Custom settings. ############################################ dnl AC_MSG_CHECKING(whether to allow 64bit compilation) dnl AC_ARG_WITH(64bit, dnl AC_HELP_STRING([--with-64bit],[Allow 64bit compilation]), dnl [case "$withval" in dnl yes) dnl with_64bit=yes dnl ;; dnl *) dnl with_64bit=no dnl ;; dnl esac], [with_64bit=no]) dnl AC_MSG_RESULT($with_64bit) AC_MSG_CHECKING(for port number specification) AC_ARG_WITH(port, AS_HELP_STRING([--with-port=PORT],[Specify port number @<:@conserver@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(DEFPORT, "conserver") AC_MSG_RESULT(port 'conserver') ;; *) AC_DEFINE_UNQUOTED(DEFPORT, "$withval") AC_MSG_RESULT(port '$withval') ;; esac], [AC_DEFINE_UNQUOTED(DEFPORT, "conserver") AC_MSG_RESULT(port 'conserver')]) AC_MSG_CHECKING(for secondary channel base port) AC_ARG_WITH(base, AS_HELP_STRING([--with-base=PORT], [Base port for secondary channel @<:@0@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(DEFBASEPORT, "0") AC_MSG_RESULT(port '0') ;; *) AC_DEFINE_UNQUOTED(DEFBASEPORT, "$withval") AC_MSG_RESULT(port '$withval') ;; esac], [AC_DEFINE_UNQUOTED(DEFBASEPORT, "0") AC_MSG_RESULT(port '0')]) AC_MSG_CHECKING(for master conserver hostname) AC_ARG_WITH(master, AS_HELP_STRING([--with-master=MASTER],[Specify master server hostname @<:@console@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(MASTERHOST, "console") AC_MSG_RESULT('console') ;; *) AC_DEFINE_UNQUOTED(MASTERHOST, "$withval") AC_MSG_RESULT('$withval') ;; esac], [AC_DEFINE_UNQUOTED(MASTERHOST, "console") AC_MSG_RESULT('console')]) AC_MSG_CHECKING(for client configuration filename) AC_ARG_WITH(ccffile, AS_HELP_STRING([--with-ccffile=CFFILE],[Specify client config filename @<:@SYSCONFDIR/console.cf@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(CLIENTCONFIGFILE, [SYSCONFDIR "/console.cf"]) AC_MSG_RESULT('$sysconfdir/console.cf') ;; [[\\/]]* | ?:[[\\/]]* ) AC_DEFINE_UNQUOTED(CLIENTCONFIGFILE, ["$withval"]) AC_MSG_RESULT('$withval') ;; *) AC_DEFINE_UNQUOTED(CLIENTCONFIGFILE, [SYSCONFDIR "/$withval"]) AC_MSG_RESULT('$sysconfdir/$withval') ;; esac], [AC_DEFINE_UNQUOTED(CLIENTCONFIGFILE, [SYSCONFDIR "/console.cf"]) AC_MSG_RESULT('$sysconfdir/console.cf')]) AC_MSG_CHECKING(for configuration filename) AC_ARG_WITH(cffile, AS_HELP_STRING([--with-cffile=CFFILE],[Specify config filename @<:@SYSCONFDIR/conserver.cf@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(CONFIGFILE, [SYSCONFDIR "/conserver.cf"]) AC_MSG_RESULT('$sysconfdir/conserver.cf') ;; [[\\/]]* | ?:[[\\/]]* ) AC_DEFINE_UNQUOTED(CONFIGFILE, ["$withval"]) AC_MSG_RESULT('$withval') ;; *) AC_DEFINE_UNQUOTED(CONFIGFILE, [SYSCONFDIR "/$withval"]) AC_MSG_RESULT('$sysconfdir/$withval') ;; esac], [AC_DEFINE_UNQUOTED(CONFIGFILE, [SYSCONFDIR "/conserver.cf"]) AC_MSG_RESULT('$sysconfdir/conserver.cf')]) AC_MSG_CHECKING(for password filename) AC_ARG_WITH(pwdfile, AS_HELP_STRING([--with-pwdfile=PWDFILE],[Specify password filename @<:@SYSCONFDIR/conserver.passwd@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(PASSWDFILE, [SYSCONFDIR "/conserver.passwd"]) AC_MSG_RESULT('$sysconfdir/conserver.passwd') ;; [[\\/]]* | ?:[[\\/]]* ) AC_DEFINE_UNQUOTED(PASSWDFILE, ["$withval"]) AC_MSG_RESULT('$withval') ;; *) AC_DEFINE_UNQUOTED(PASSWDFILE, [SYSCONFDIR "/$withval"]) AC_MSG_RESULT('$sysconfdir/$withval') ;; esac], [AC_DEFINE_UNQUOTED(PASSWDFILE, [SYSCONFDIR "/conserver.passwd"]) AC_MSG_RESULT('$sysconfdir/conserver.passwd')]) AC_MSG_CHECKING(for log filename) AC_ARG_WITH(logfile, AS_HELP_STRING([--with-logfile=LOGFILE],[Specify log filename @<:@/var/log/conserver@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(LOGFILEPATH, "/var/log/conserver") AC_MSG_RESULT('/var/log/conserver') ;; *) AC_DEFINE_UNQUOTED(LOGFILEPATH, "$withval") AC_MSG_RESULT('$withval') ;; esac], [AC_DEFINE_UNQUOTED(LOGFILEPATH, "/var/log/conserver") AC_MSG_RESULT('/var/log/conserver')]) AC_SUBST(PIDFILE) AC_MSG_CHECKING(for PID filename) AC_ARG_WITH(pidfile, AS_HELP_STRING([--with-pidfile=PIDFILE],[Specify PID filepath @<:@/var/run/conserver.pid@:>@]), [case "$withval" in yes|no) PIDFILE="/var/run/conserver.pid" ;; *) PIDFILE="$withval" ;; esac], [PIDFILE="/var/run/conserver.pid"]) AC_DEFINE_UNQUOTED(PIDFILE, "$PIDFILE") AC_MSG_RESULT('$PIDFILE') AC_MSG_CHECKING(for MAXMEMB setting) AC_ARG_WITH(maxmemb, AS_HELP_STRING([--with-maxmemb=MAXMEMB],[Specify maximum consoles per process @<:@16@:>@]), [case "$withval" in yes|no) AC_DEFINE_UNQUOTED(MAXMEMB, 16) AC_MSG_RESULT(16) ;; *) if expr "$withval" : '[[0-9]]*$' >/dev/null 2>&1 && test "$withval" -gt 0 -a "$withval" -lt 256; then AC_DEFINE_UNQUOTED(MAXMEMB, $withval) AC_MSG_RESULT($withval) else AC_DEFINE_UNQUOTED(MAXMEMB, 16) AC_MSG_RESULT([value out of bounds (0@]), [if expr "$withval" : '[[0-9]]*$' >/dev/null 2>&1 && test "$withval" -gt 0 -a "$withval" -lt 300; then AC_DEFINE_UNQUOTED(CONNECTTIMEOUT, $withval) AC_MSG_RESULT($withval) else AC_DEFINE_UNQUOTED(CONNECTTIMEOUT, 10) AC_MSG_RESULT([value out of bounds (032bit systems (to override use --with-64bit)]) dnl else dnl AC_MSG_WARN([building a 64bit version of conserver - good luck!]) dnl fi dnl fi dnl ### Checks for header files. ################################### AC_HEADER_STDC AC_CHECK_HEADERS(sys/ioctl.h) AC_SYS_POSIX_TERMIOS if test "$ac_cv_sys_posix_termios" != "yes"; then AC_MSG_ERROR([POSIX termios interface required]) fi AC_CHECK_HEADERS(unistd.h getopt.h sys/vlimit.h sys/resource.h ttyent.h sys/ttold.h sys/uio.h sys/ioctl_compat.h usersec.h sys/select.h stropts.h sys/audit.h shadow.h sys/time.h crypt.h sysexits.h types.h sys/sockio.h sys/param.h sys/un.h) dnl sys/proc.h needs sys/param.h on openbsd, apparently AC_CHECK_HEADERS(sys/proc.h, [], [], [#if HAVE_SYS_PARAM_H #include #endif ]) AC_HEADER_TIME AC_HEADER_SYS_WAIT AC_TYPE_MODE_T AC_TYPE_SIGNAL AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_UID_T AC_CHECK_TYPE([sig_atomic_t],, AC_DEFINE(sig_atomic_t, volatile int, [Define if does not define sig_atomic_t]), [#include ]) AC_CHECK_TYPE([in_addr_t],[AC_DEFINE(HAVE_IN_ADDR_T,1, [Defined if in_addr_t exists])],,[$ac_includes_default #include ]) AC_CHECK_TYPE([socklen_t],[AC_DEFINE(HAVE_SOCKLEN_T,1, [Defined if socklen_t exists])],,[$ac_includes_default #include ]) AC_MSG_CHECKING(for sa_len in struct sockaddr) AC_TRY_COMPILE([#include #include ], [struct sockaddr s; s.sa_len=0;], [AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SA_LEN)], [AC_MSG_RESULT(no)]) dnl ### Host specific checks. ###################################### AC_CANONICAL_HOST case "$host" in *-*-hpux*) CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE_EXTENDED=1" AC_CHECK_LIB(xnet,t_error,,AC_MSG_ERROR([-lxnet needed on HP-UX])) AC_CHECK_LIB(sec,getspnam) AC_CHECK_HEADERS(hpsecurity.h prot.h) AC_CHECK_FUNCS(bigcrypt iscomsec getprpwnam) ;; esac dnl ### Check for libraries. ####################################### AC_SEARCH_LIBS(socket,socket) AC_SEARCH_LIBS(gethostbyname,nsl) AC_SEARCH_LIBS(crypt,crypt) AC_SUBST(CONSLIBS) AC_SUBST(CONSCPPFLAGS) AC_SUBST(CONSLDFLAGS) AC_MSG_CHECKING(whether to use Unix domain sockets) cons_with_uds="NO" AC_ARG_WITH(uds, AS_HELP_STRING([--with-uds@<:@=DIR@:>@ ], [Use Unix domain sockets for client/server communication @<:@/tmp/conserver@:>@]), [case "$withval" in yes) AC_DEFINE_UNQUOTED(UDSDIR, "/tmp/conserver") AC_DEFINE(USE_UNIX_DOMAIN_SOCKETS) AC_MSG_RESULT([/tmp/conserver]) cons_with_uds="YES" ;; no) AC_MSG_RESULT(no) ;; *) AC_DEFINE_UNQUOTED(UDSDIR, "$withval") AC_DEFINE(USE_UNIX_DOMAIN_SOCKETS) AC_MSG_RESULT('$withval') cons_with_uds="YES" if expr "$withval" : '/' >/dev/null 2>&1; then : else echo "*** WARNING *** you may have better success using a fully-qualified path" echo "*** WARNING *** instead of '$withval'" fi ;; esac if test $cons_with_uds = YES; then AC_MSG_CHECKING(whether to trust UDS credentials) AC_ARG_WITH(trust-uds-cred, AS_HELP_STRING([--with-trust-uds-cred],[Trust UDS credentials obtained via socket]), [case "$withval" in yes) AC_TRY_COMPILE([#include #include ], [ struct ucred u; u.uid = 0; #if !defined(SO_PEERCRED) #error "no SO_PEERCRED defined" #endif ], [AC_MSG_RESULT(yes) AC_DEFINE(UDS_CRED_STYPE, ucred, [Defined to UDS credential structure name]) AC_DEFINE(UDS_CRED_UID, uid, [Defined to UDS credential structure uid field]) AC_DEFINE(UDS_CRED_SO, SO_PEERCRED, [Defined to UDS credential socket option]) AC_DEFINE(TRUST_UDS_CRED)], [ AC_TRY_COMPILE([#include #include ], [ struct peercred_struct u; u.euid = 0; #if !defined(SO_PEERID) #error "no SO_PEERID defined" #endif ], [AC_MSG_RESULT(yes) AC_DEFINE(UDS_CRED_STYPE, peercred_struct, [Defined to UDS credential structure name]) AC_DEFINE(UDS_CRED_UID, euid, [Defined to UDS credential structure uid field]) AC_DEFINE(UDS_CRED_SO, SO_PEERID, [Defined to UDS credential socket option]) AC_DEFINE(TRUST_UDS_CRED)], [AC_MSG_RESULT(no)]) ]) ;; *) AC_MSG_RESULT(no) ;; esac],[AC_MSG_RESULT(no)]) fi ],[AC_MSG_RESULT(no)]) cons_with_libwrap="NO" AC_ARG_WITH(libwrap, AS_HELP_STRING([--with-libwrap@<:@=PATH@:>@], [Compile in libwrap (tcp_wrappers) support]), [if test "$withval" != "no"; then if test "$withval" != "yes"; then WRAPCPPFLAGS="-I$withval/include" if test "$use_dash_r" != "yes"; then WRAPLDFLAGS="-L$withval/lib" else WRAPLDFLAGS="-L$withval/lib -R$withval/lib" fi else WRAPCPPFLAGS="" WRAPLDFLAGS="" fi oCPPFLAGS="$CPPFLAGS" oLDFLAGS="$LDFLAGS" oLIBS="$LIBS" CPPFLAGS="$CPPFLAGS $WRAPCPPFLAGS" LDFLAGS="$LDFLAGS $WRAPLDFLAGS" AC_CHECK_HEADER(tcpd.h, [LIBS="$LIBS -lwrap" AC_MSG_CHECKING(for TCP wrappers library -lwrap) AC_TRY_LINK([#include int allow_severity = 0; int deny_severity = 0; ],[hosts_access((void *)0)], [AC_MSG_RESULT(yes) cons_with_libwrap="YES" AC_DEFINE(USE_LIBWRAP) CONSLIBS="$CONSLIBS -lwrap" CONSLDFLAGS="$CONSLDFLAGS $WRAPLDFLAGS" CONSCPPFLAGS="$CONSCPPFLAGS $WRAPCPPFLAGS"], [AC_MSG_RESULT(no) LIBS="$LIBS -lnsl" AC_MSG_CHECKING(for TCP wrappers library -lwrap with -lnsl) AC_TRY_LINK([#include int allow_severity = 0; int deny_severity = 0; ],[hosts_access((void *)0)], [AC_MSG_RESULT(yes) cons_with_libwrap="YES" AC_DEFINE(USE_LIBWRAP) CONSLIBS="$CONSLIBS -lwrap -lnsl" CONSLDFLAGS="$CONSLDFLAGS $WRAPLDFLAGS" CONSCPPFLAGS="$CONSCPPFLAGS $WRAPCPPFLAGS"], [AC_MSG_RESULT(no)])])],) LIBS="$oLIBS" CPPFLAGS="$oCPPFLAGS" LDFLAGS="$oLDFLAGS" fi] ) cons_with_openssl="NO" AC_ARG_WITH(openssl, AS_HELP_STRING([--with-openssl@<:@=PATH@:>@], [Compile in OpenSSL support]), [if test "$withval" != "no"; then if test "$withval" != "yes"; then OPENSSLCPPFLAGS="-I$withval/include" if test "$use_dash_r" != "yes"; then OPENSSLLDFLAGS="-L$withval/lib" else OPENSSLLDFLAGS="-L$withval/lib -R$withval/lib" fi else OPENSSLCPPFLAGS="" OPENSSLLDFLAGS="" fi oCPPFLAGS="$CPPFLAGS" oLDFLAGS="$LDFLAGS" oLIBS="$LIBS" have_openssl=no CPPFLAGS="$CPPFLAGS $OPENSSLCPPFLAGS" LDFLAGS="$LDFLAGS $OPENSSLLDFLAGS" AC_CHECK_HEADER([openssl/ssl.h], [LIBS="$LIBS -lssl -lcrypto" AC_MSG_CHECKING(for openssl libraries -lssl and -lcrypto) AC_TRY_LINK([#include ],[SSL_CTX_new(NULL)], [AC_MSG_RESULT(yes) cons_with_openssl="YES" AC_DEFINE(HAVE_OPENSSL) have_openssl=yes], [AC_MSG_RESULT(no)])],) if test $have_openssl = no; then LIBS="$oLIBS" CPPFLAGS="$oCPPFLAGS" LDFLAGS="$oLDFLAGS" fi AC_MSG_CHECKING(whether to require server cert) AC_ARG_WITH(req-server-cert, AS_HELP_STRING([--with-req-server-cert],[Require server SSL certificate by client]), [case "$withval" in yes) AC_DEFINE(REQ_SERVER_CERT) AC_MSG_RESULT(yes) ;; *) AC_MSG_RESULT(no) ;; esac],[AC_MSG_RESULT(no)]) fi] ) cons_with_gssapi="NO" cons_strip_realm="NO" AC_ARG_WITH(gssapi, AS_HELP_STRING([--with-gssapi@<:@=PATH@:>@], [Compile in GSS-API support]), [if test "$withval" != "no"; then if test "$withval" != "yes"; then GSSAPICPPFLAGS="-I$withval/include" if test "$use_dash_r" != "yes"; then GSSAPILDFLAGS="-L$withval/lib" else GSSAPILDFLAGS="-L$withval/lib -R$withval/lib" fi else GSSAPICPPFLAGS="" GSSAPILDFLAGS="" fi oCPPFLAGS="$CPPFLAGS" oLDFLAGS="$LDFLAGS" oLIBS="$LIBS" have_gssapi=no CPPFLAGS="$CPPFLAGS $GSSAPICPPFLAGS" LDFLAGS="$LDFLAGS $GSSAPILDFLAGS" AC_CHECK_HEADER([gssapi/gssapi.h], [LIBS="$oLIBS -lgssapi" AC_MSG_CHECKING(for gssapi library -lgssapi) AC_TRY_LINK([#include ],[gss_create_empty_oid_set(NULL, NULL)], [AC_MSG_RESULT(yes) cons_with_gssapi="YES" AC_DEFINE(HAVE_GSSAPI) have_gssapi=yes], [AC_MSG_RESULT(no) LIBS="$oLIBS -lgssglue" AC_MSG_CHECKING(for gssapi library -lgssglue) AC_TRY_LINK([#include ],[gss_create_empty_oid_set(NULL, NULL)], [AC_MSG_RESULT(yes) cons_with_gssapi="YES" AC_DEFINE(HAVE_GSSAPI) have_gssapi=yes], [AC_MSG_RESULT(no) LIBS="$oLIBS -lgss" AC_MSG_CHECKING(for gssapi library -lgss) AC_TRY_LINK([#include ],[gss_create_empty_oid_set(NULL, NULL)], [AC_MSG_RESULT(yes) cons_with_gssapi="YES" AC_DEFINE(HAVE_GSSAPI) have_gssapi=yes], [AC_MSG_RESULT(no)])])])],) if test $have_gssapi = no; then LIBS="$oLIBS" CPPFLAGS="$oCPPFLAGS" LDFLAGS="$oLDFLAGS" else AC_MSG_CHECKING(whether to fallback to username without @REALM) AC_ARG_WITH(striprealm, AS_HELP_STRING([--with-striprealm],[retry username without @REALM with gss-api authentication]), [case "$withval" in yes) AC_DEFINE(STRIP_REALM) AC_MSG_RESULT(yes) cons_strip_realm="YES" ;; *) AC_MSG_RESULT(no) ;; esac],[AC_MSG_RESULT(no)]) fi fi] ) cons_with_freeipmi="NO" AC_ARG_WITH(freeipmi, AS_HELP_STRING([--with-freeipmi@<:@=PATH@:>@], [Compile in FreeIPMI support]), [if test "$withval" != "no"; then if test "$withval" != "yes"; then FREEIPMICPPFLAGS="-I$withval/include" if test "$use_dash_r" != "yes"; then FREEIPMILDFLAGS="-L$withval/lib" else FREEIPMILDFLAGS="-L$withval/lib -R$withval/lib" fi else FREEIPMICPPFLAGS="" FREEIPMILDFLAGS="" fi oCPPFLAGS="$CPPFLAGS" oLDFLAGS="$LDFLAGS" oLIBS="$LIBS" have_freeipmi=no CPPFLAGS="$CPPFLAGS $FREEIPMICPPFLAGS" LDFLAGS="$LDFLAGS $FREEIPMILDFLAGS" AC_CHECK_HEADER([ipmiconsole.h], [LIBS="$LIBS -lipmiconsole" AC_MSG_CHECKING(for freeipmi libraries -lipmiconsole) AC_TRY_LINK([#include ],[ipmiconsole_ctx_fd(0)], [AC_MSG_RESULT(yes) cons_with_freeipmi="YES" AC_DEFINE(HAVE_FREEIPMI) CONSLIBS="$CONSLIBS -lipmiconsole" have_freeipmi=yes], [AC_MSG_RESULT(no)])],) LIBS="$oLIBS" if test $have_freeipmi = no; then CPPFLAGS="$oCPPFLAGS" LDFLAGS="$oLDFLAGS" fi fi] ) cons_with_dmalloc="NO" AC_ARG_WITH(dmalloc, AS_HELP_STRING([--with-dmalloc@<:@=PATH@:>@], [Compile in dmalloc support]), [if test "$withval" != "no"; then if test "$withval" != "yes"; then DMALLOCCPPFLAGS="-I$withval/include" if test "$use_dash_r" != "yes"; then DMALLOCLDFLAGS="-L$withval/lib" else DMALLOCLDFLAGS="-L$withval/lib -R$withval/lib" fi else DMALLOCCPPFLAGS="" DMALLOCLDFLAGS="" fi oCPPFLAGS="$CPPFLAGS" oLDFLAGS="$LDFLAGS" oLIBS="$LIBS" have_dmalloc=no CPPFLAGS="$CPPFLAGS $DMALLOCCPPFLAGS" LDFLAGS="$LDFLAGS $DMALLOCLDFLAGS" AC_CHECK_HEADER([dmalloc.h], [LIBS="$LIBS -ldmalloc" AC_MSG_CHECKING(for dmalloc libraries -ldmalloc) AC_TRY_LINK([#include ],[dmalloc_debug(0)], [AC_MSG_RESULT(yes) cons_with_dmalloc="YES" AC_DEFINE(HAVE_DMALLOC) have_dmalloc=yes], [AC_MSG_RESULT(no)])],) if test $have_dmalloc = no; then LIBS="$oLIBS" CPPFLAGS="$oCPPFLAGS" LDFLAGS="$oLDFLAGS" fi fi] ) dnl ### Check for needed functions. ################################ dnl dnl The following basically stollen from the less-358 distribution, but dnl dnl modified for my own purposes dnl AC_MSG_CHECKING(for POSIX regex) dnl AC_ARG_WITH(regex, dnl AC_HELP_STRING([--with-regex], dnl [Use regular expressions in conserver.passwd]), dnl [if test "$withval" = yes; then dnl AC_TRY_RUN([ dnl #include dnl #include dnl main() { regex_t r; regmatch_t rm; char *text = "xabcy"; dnl if (regcomp(&r, "abc", 0)) exit(1); dnl if (regexec(&r, text, 1, &rm, 0)) exit(1); dnl if (rm.rm_so != 1) exit(1); /* check for correct offset */ dnl exit(0); } dnl ],have_posix_regex=yes,have_posix_regex=no,have_posix_regex=unknown) dnl if test $have_posix_regex = yes; then dnl AC_MSG_RESULT(yes) dnl AC_DEFINE(HAVE_POSIX_REGCOMP) dnl elif test $have_posix_regex = unknown; then dnl AC_TRY_LINK([ dnl #include dnl #include ], dnl [regex_t *r; regfree(r);], dnl AC_MSG_RESULT(yes) dnl AC_DEFINE(HAVE_POSIX_REGCOMP)) dnl else dnl AC_MSG_RESULT(no) dnl fi dnl else dnl AC_MSG_RESULT(no) dnl fi],[AC_MSG_RESULT(no)]) cons_with_pam="NO" AC_MSG_CHECKING(for PAM support) AC_ARG_WITH(pam, AS_HELP_STRING([--with-pam], [Enable PAM support]), [if test "$withval" = yes; then oLIBS="$LIBS" AC_CHECK_HEADER(security/pam_appl.h, [LIBS="$LIBS -lpam" AC_MSG_CHECKING(for PAM library -lpam) AC_TRY_LINK_FUNC([pam_start], [AC_MSG_RESULT(yes) cons_with_pam="YES" AC_DEFINE(HAVE_PAM) CONSLIBS="$CONSLIBS -lpam"], [LIBS="$LIBS -ldl" AC_MSG_RESULT(no) AC_MSG_CHECKING(for PAM library -lpam with -ldl) AC_TRY_LINK_FUNC([pam_end], [AC_MSG_RESULT(yes) cons_with_pam="YES" AC_DEFINE(HAVE_PAM) CONSLIBS="$CONSLIBS -lpam -ldl"], [AC_MSG_RESULT(no)])])],) LIBS="$oLIBS" else AC_MSG_RESULT(no) fi],[AC_MSG_RESULT(no)]) cons_with_ipv6="NO" AC_MSG_CHECKING(whether to support IPv6) AC_ARG_WITH(ipv6, AS_HELP_STRING([--with-ipv6], [(experimental) Use IPv6 for client/server communication]), [case "$withval" in yes) AC_DEFINE(USE_IPV6) AC_MSG_RESULT(yes) cons_with_ipv6="YES" ;; *) AC_MSG_RESULT(no) ;; esac],[AC_MSG_RESULT(no)]) dnl Checks for pty allocation... dnl According to the xemacs distribution: dnl getpt() is the preferred pty allocation method on glibc systems. dnl _getpty() is the preferred pty allocation method on SGI systems. dnl grantpt(), unlockpt(), ptsname() are defined by Unix98. dnl openpty() is the preferred pty allocation method on BSD and Tru64 systems. dnl openpty() might be declared in: dnl - pty.h (Tru64 or Linux) dnl - libutil.h (FreeBSD) dnl - util.h (NetBSD) dnl Conserver doesn't support getpt() or _getpt() yet. AC_CHECK_HEADERS(pty.h libutil.h util.h) AC_CHECK_LIB(util, openpty) AC_CHECK_FUNCS(openpty) AC_CHECK_FUNCS(getopt strerror getrlimit getsid setsid getuserattr setgroups tcgetpgrp tcsetpgrp tcgetattr tcsetattr tcsendbreak setpgrp getutent setttyent getspnam setlinebuf setvbuf ptsname grantpt unlockpt sigaction setsockopt getdtablesize putenv memset memcpy memcmp memmove sysconf getlogin inet_aton setproctitle gettimeofday strlcpy closefrom) AC_CHECK_FUNC(strcasecmp, [AC_DEFINE(HAVE_STRCASECMP, 1, [Define if strcasecmp is available])], [AC_CHECK_FUNC(stricmp, [AC_DEFINE(HAVE_STRICMP, 1, [Define if stricmp is available])], [AC_MSG_ERROR([strcasecmp or stricmp must be available])])]) dnl Checks for libbsm functions AC_CHECK_HEADERS(bsm/audit.h) AC_CHECK_LIB(bsm, getaudit) AC_CHECK_FUNCS(getaudit getaudit_addr) dnl ### Create output files. ####################################### AC_CONFIG_FILES([Makefile conserver/Makefile conserver.cf/Makefile console/Makefile autologin/Makefile contrib/chat/Makefile]) AC_CONFIG_FILES([conserver/conserver.rc], [chmod +x conserver/conserver.rc]) AC_CONFIG_FILES([conserver.cf/conserver.cf.man conserver.cf/conserver.passwd.man conserver/conserver.man console/console.man]) AC_OUTPUT [ echo "==============================================================" echo " Feature Summary" echo "" echo " Unix domain sockets (--with-uds) : $cons_with_uds" echo " TCP wrappers (--with-libwrap) : $cons_with_libwrap" echo " OpenSSL (--with-openssl) : $cons_with_openssl" echo " GSS-API (--with-gssapi) : $cons_with_gssapi" echo " FreeIPMI (--with-freeipmi) : $cons_with_freeipmi" if [ $cons_with_gssapi = "YES" ]; then echo " strip @REALM (--with-striprealm): $cons_strip_realm" fi echo " dmalloc (--with-dmalloc) : $cons_with_dmalloc" echo " PAM support (--with-pam) : $cons_with_pam" echo " IPv6 support (--with-ipv6) : $cons_with_ipv6" echo "" echo "==============================================================" ] conserver-8.2.4/conserver.cf/000077500000000000000000000000001344660520400161345ustar00rootroot00000000000000conserver-8.2.4/conserver.cf/INSTALL000066400000000000000000000006131344660520400171650ustar00rootroot00000000000000The two files you need to set up are the conserver.cf and conserver.passwd files. See the sample conserver.cf and conserver.passwd files for examples (installed in /examples/conserver). You can start with those and then modify extensively. The man page for conserver.cf and conserver.passwd should explain the files with enough detail to get you going. That's about it. Good luck. conserver-8.2.4/conserver.cf/Makefile.in000066400000000000000000000014311344660520400202000ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ srcdir = @srcdir@ prefix = @prefix@ mandir = @mandir@ sysconfdir = @sysconfdir@ datadir = @datadir@ exampledir = $(datadir)/examples/conserver ### Installation programs and flags INSTALL = @INSTALL@ MKDIR = @MKDIR@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below all: clean: rm -f *~ *.o $(ALL) core distclean: clean rm -f Makefile install: $(MKDIR) $(DESTDIR)$(mandir)/man5 $(INSTALL) -m 0644 conserver.cf.man $(DESTDIR)$(mandir)/man5/conserver.cf.5 $(INSTALL) -m 0644 conserver.passwd.man $(DESTDIR)$(mandir)/man5/conserver.passwd.5 $(MKDIR) $(DESTDIR)$(exampledir) $(INSTALL) -m 0644 conserver.cf $(DESTDIR)$(exampledir) $(INSTALL) -m 0644 conserver.passwd $(DESTDIR)$(exampledir) .PHONY: clean distclean install conserver-8.2.4/conserver.cf/conserver.cf000066400000000000000000000063151344660520400204610ustar00rootroot00000000000000# # Sample conserver.cf file, to give you ideas of what you can do with # the various configuration items. # ### set up global access default full { rw *; } ### define some terminal server specifics # we set portbase and portinc so we can reference the ports in a # physical representation and let conserver do the math to figure # out the actual socket address default cisco { type host; portbase 2000; portinc 1; } default xyplex { type host; portbase 2000; portinc 100; } default iolan { type host; portbase 10000; portinc 1; } ### set up some custom break sequences break 4 { string "+\d+\d+"; delay 300; } break 5 { string "\033c"; } ### set the defaults for all the consoles # these get applied before anything else default * { # The '&' character is substituted with the console name logfile /var/consoles/&; # timestamps every hour with activity and break logging timestamp 1hab; # include the 'full' default include full; # master server is localhost master localhost; } ### define the first terminal server default ts1.conserver.com { # use the xyplex defaults include xyplex; # host to connect to is ts1.conserver.com host ts1.conserver.com; # run login-xyplex when connecting to the term server initcmd /usr/local/sbin/login-xyplex; } # now define the consoles on ts1.conserver.com # bryan isn't allowed on web1.conserver.com console web1.conserver.com { include ts1.conserver.com; port 2; rw !bryan; } console ns1.conserver.com { include ts1.conserver.com; port 10; } console ns2.conserver.com { include ts1.conserver.com; port 8; } ### define the second terminal server # this one is a cisco, with simple socket connections default ts2.conserver.com { include cisco; host ts2.conserver.com; } # and the consoles on ts2.conserver.com console ldap1.conserver.com { include ts2.conserver.com; port 7; } ### and now some one-off consoles # we still inherit the '*' default set # a simple ssh invocation console ssh { type exec; exec ssh localhost; # provide a 'message-of-the-day' motd "just a simple ssh to localhost"; } # connect to /dev/ttya console ttya { type device; device /dev/ttya; parity none; baud 9600; idlestring "#"; idletimeout 5m; # send a '#' every 5 minutes of idle timestamp ""; # no timestamps on this console } ### define a group of users group sysadmin { users bryan, todd; users dave; } ### reset the defaults for the next set of consoles # again, these get applied before anything else default * { # The '&' character is substituted with the console name logfile /var/consoles/&; timestamp 5m; rw sysadmin; # use the group defined above master localhost; } default cyclades { # sets up /dev/ttyC0 through /dev/ttyC31, for a 32 port card # referenced as ports 1 through 32 type device; device /dev/ttyC.; devicesubst .=Pd; portbase -1; portinc 1; host none; # not really used, since devicesubst doesn't use it baud 9600; parity none; } console modem1.conserver.com { include cyclades; port 2; break 4; } # todd isn't allowed on modem2.conserver.com console modem2.conserver.com { include cyclades; port 6; rw !todd; } ### list of clients we allow access * { allowed 10.0.0.0/8 192.168.0.0/16; allowed cs1.conserver.com cs2.conserver.com cs3.conserver.com; trusted 127.0.0.1; } conserver-8.2.4/conserver.cf/conserver.cf.man.in000066400000000000000000001050101344660520400216300ustar00rootroot00000000000000.TH CONSERVER.CF 5 "@CONSERVER_DATE@" "conserver-@CONSERVER_VERSION@" "conserver" .SH NAME conserver.cf \- console configuration file for .BR conserver (8) .SH DESCRIPTION The format of the conserver.cf file is made up of named blocks of keyword/value pairs, comments, and optional whitespace for formatting flexibility. The block types as well as the keywords are pre-defined and explained in the .B \s-1BLOCKS\s0 section. A comment is an unquoted pound-sign to a newline. See the .B \s-1PARSER\s0 section for full details on whitespace and quoting. .PP Let me first show you a sample block with a couple of keyword/value pairs to make the description a bit simpler to understand. .IP .ft CR .nf console simple { master localhost; type exec; rw *; } .fi .ft .PP This is actually a fully functional conserver.cf file (if certain conditions are met...and if you can list those conditions, you can probably can skip to the .B \s-1BLOCKS\s0 section). .PP Our example is made of up of a console-block named ``simple'' with three keyword/value pairs. What this does is define a console named ``simple'', makes the master of that console the host ``localhost'', makes the type an exec-style console, and gives every user read/write permission. This is the generic format of the file: .IP .ft CR .nf block-type block-name { keyword value; ... } .fi .ft .PP To show the addition of comments and whitespace, here is the example reformatted (but functionally equivalent): .IP .ft CR .nf # define a console named "simple" console simple { # setting all required values... master localhost; type exec; # exec-style console rw *; # allow any username } .fi .ft .SH PARSER .PP The parser has six characters that it considers special. These are: ``{'', ``}'', ``;'', ``#'', ``\e'', and ``"''. The first three (hereby called tokens) define the format of the configuration blocks and are used as word separators, the next is the comment character, and the last two are quoting characters. .PP Word separation occurs when the parser encounters an unquoted token and, in certain cases, whitespace. Whitespace is only used as a word separator when the parser is looking for a block-type or keyword. When it's looking for a block-name or value, whitespace is like any other character, which allows you to embed whitespace in a block-name or value without having to quote it. Here is an example: .IP .ft CR .nf default my defs { rw *; include other defs ; } .fi .ft .PP The block-type is ``default'', the block-name is ``my defs'', and the value for the keyword ``include'' is ``other defs''. Whitespace around tokens are ignored so you get ``other defs'' instead of ``other defs '' as the value. .PP The only way to use one of the special characters as part of a block-name or value is to quote it. .PP Quoting is a simple matter of prefixing a character with a backslash or surrounding a group of characters with double-quotes. If a character is prefixed by a backslash, the next character is a literal (so ``\e\e'' produces a ``\e'', ``\e"'' produces ``"'', ``\e{'' produces a ``{'', etc.). For double-quoted strings, all characters are literal except for ``\e"'', which embeds a double-quote. .PP Adding a variety of quotes to our example without changing the meaning of things, we have: .IP .ft CR .nf "defa"ult my\e defs { rw *; in\eclude "other defs" ; } .fi .ft .PP There is one special line the parser recognizes: a ``#include'' statement. It is of the form: .IP .B #include .I filename .PP Any whitespace around .I filename is ignored, but whitespace embedded inside is preserved. Everything in .I filename is taken literally, so none of the normal parser quoting applies. The .B #include must begin in ``column 0'' - no whitespace is allowed between it and the start of the physical line. There is an include file depth limit of 10 to prevent infinite recursion. .SH BLOCKS .TP \f3access\fP \f2hostname\fP|\f2ipaddr\fP .br Define an access block for the host named .I hostname or using the address .IR ipaddr . If the value of ``*'' is used, the access block will be applied to all conserver hosts. Access lists are used in a first match fashion (top down), so order is important. .RS .TP \f3admin\fP [\f3!\fP]\f2username\fP[\f3,\fP...]|\f3""\fP .br Define a list of users making up the admin list for the console server. If .I username matches a previously defined group name, all members of the previous group are applied to the admin list (with access reversed if prefixed with a `!'). If .I username doesn't match a previously defined group and .I username begins with `@', the name (minus the `@') is checked against the host's group database. All users found in the group will be granted (or denied, if prefixed with `!') access. If .I username doesn't match a previous group and doesn't begin with `@', the users will be granted (or denied, if prefixed with `!') access. If the null string (``\f3""\fP'') is used, any users previously defined for the console servers's admin list are removed. .TP \f3allowed\fP \f2hostname\fP[\f3,\fP...] .br The list of hostnames are added to the ``allowed'' list, which grants connections from the hosts but requires username authentication. .TP \f3include\fP \f2accessgroup\fP .br The access lists defined using the name .I accessgroup are applied to the current access block. The included access block must be previously defined. .TP \f3limited\fP [\f3!\fP]\f2username\fP[\f3,\fP...]|\f3""\fP .br Define a list of users with limited functionality on the console server. These users will not be allowed to suspend their connection, shift to another console, or attach to a local command. If .I username matches a previously defined group name, all members of the previous group are applied to the admin list (with access reversed if prefixed with a `!'). If .I username doesn't match a previously defined group and .I username begins with `@', the name (minus the `@') is checked against the host's group database. All users found in the group will be granted (or denied, if prefixed with `!') access. If .I username doesn't match a previous group and doesn't begin with `@', the users will be granted (or denied, if prefixed with `!') access. If the null string (``\f3""\fP'') is used, any users previously defined for the console server's limited list are removed. .TP \f3rejected\fP \f2hostname\fP[\f3,\fP...] .br The list of hostnames are added to the ``rejected'' list, which rejects connections from the hosts. .TP \f3trusted\fP \f2hostname\fP[\f3,\fP...] .br The list of hostnames are added to the ``trusted'' list, which grants connections from the hosts without username authentication. .RE .TP \f3break\fP \f2n\fP .br Define a break sequence where (1 <= .I n <= 9) or (a <= .I n <= z). Break sequences are accessed via the .RI ``^Ecl n '' client escape sequence. .RS .TP \f3confirm\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not to ask the client for confirmation before sending the break sequence. The default is ``no''. .TP \f3delay\fP \f2n\fP .br Set the time delay for the .B \ed sequence to .I n milliseconds. The default time delay is 250ms. .TP \f3string\fP \f2breakseq\fP .br Assign the string .IR breakseq to the specified slot .IR n . A break sequence is a simple character string with the exception of `\e' and `^': .RS .RS .sp .PD 0 .TP 6 .B \ea alert .TP .B \eb backspace .TP .B \ed delay specified by the .B delay option. .TP .B \ef form-feed .TP .B \en newline .TP .B \er carriage-return .TP .B \et tab .TP .B \ev vertical-tab .TP .B \ez serial break .TP .B \e\e backslash .TP .B \e^ circumflex .TP .BI \e ooo octal representation of a character (where .I ooo is one to three octal digits) .TP .BI \e c character .I c .TP .B ^? delete .TP .BI ^ c control character .RI ( c is ``and''ed with 0x1f) .PD .RE .RE .RE .TP \f3config\fP \f2hostname\fP|\f2ipaddr\fP .br Define a configuration block for the host named .I hostname or using the address .IR ipaddr . If the value of ``*'' is used, the configuration block will be applied to all conserver hosts. .RS .TP \f3autocomplete\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Turn the console name autocompletion feature on or off. If autocompletion is on, a client can use any unique leading portion of a console name when connecting to a console. Autocompletion is on by default. .TP \f3defaultaccess\fP \f3rejected\fP|\f3trusted\fP|\f3allowed\fP .br Set the default access permission for all hosts not matched by an access list (see the .B \-a command-line flag). .TP \f3daemonmode\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not to become a daemon when run (see the .B \-d command-line flag). .TP \f3initdelay\fP \f2number\fP .br Set the number of seconds between console initializations. All consoles with the same .B host value will be throttled as a group (those without a .B host value are their own group). In other words, each console within a group will only be initialized after .I number seconds passes from the previous initialization of a console in that group. Different throttle groups are initialized simultaneously. One warning: since consoles are split up and managed by seperate conserver processes, it's possible for more than one conserver process to have a throttle group based on a particular .B host value. If this happens, each conserver process will throttle their groups independently of the other conserver processes, which results in a more rapid initialization (per .B host value) than one might otherwise expect. If .I number is zero, all consoles are initialized without delay. .TP \f3logfile\fP \f2filename\fP .br Set the logfile to write to when in daemon mode (see the .B \-L command-line flag). .TP \f3passwdfile\fP \f2filename\fP .br Set the password file location used for authentication (see the .B \-P command-line flag). .TP \f3primaryport\fP \f2number\fP|\f2name\fP .br Set the port used by the master conserver process (see the .B \-p command-line flag). .TP \f3redirect\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Turn redirection on or off (see the .B \-R command-line flag). .TP \f3reinitcheck\fP \f2number\fP .br Set the number of minutes used between reinitialization checks (see the .B \-O command-line flag). .TP \f3secondaryport\fP \f2number\fP|\f2name\fP .br Set the base port number used by child processes (see the .B \-b command-line flag). .TP \f3setproctitle\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not the process title shows master/group functionality as well as the port number the process is listening on and how many consoles it is managing. The operating system must support the .BR setproctitle () call. .TP \f3sslcredentials\fP \f2filename\fP .br Set the .SM SSL credentials file location (see the .B \-c command-line flag). .TP \f3sslcacertificatefile\fP \f2filename\fP .br Load the valid CA certificates for the .SM SSL connection from the PEM encoded file. This option overrides the global CA list. .TP \f3sslreqclientcert\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not a certificate is required by the client to connect. The default is ``no''. .TP \f3sslrequired\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not encryption is required when talking to clients (see the .B \-E command-line flag). .TP \f3unifiedlog\fP \f2filename\fP .br Set the location of the unified log to .IR filename . See the .B \-U command-line flag for details. .RE .TP \f3console\fP \f2name\fP .br Define a console identified as .IR name . The keywords are the same as the .B default block with the following addition. .RS .TP \f3aliases\fP \f2name\fP[\f3,\fP...]|\f3""\fP .br Define a list of console aliases. If the null string (``\f3""\fP'') is used, any aliases previously defined for the console are removed. .RE .TP \f3default\fP \f2name\fP .br Define a block of defaults identified as .IR name . If .I name is ``*'', the automatically applied default block is defined (basically all consoles have an implicit ``include "*";'' at the beginning of their definition). .RS .TP \f3baud\fP \f3300\fP|\f3600\fP|\f31800\fP|\f32400\fP|\f34800\fP|\f39600\fP|\f319200\fP|\f338400\fP|\f357600\fP|\f3115200\fP .br Assign the baud rate to the console. Only consoles of type ``device'' will use this value. .TP \f3break\fP \f2n\fP .br Assign the break sequence .I n as the default for the console, which is used by the ``^Ecl0'' client escape sequence. .TP \f3breaklist\fP \f2n\fP[\f3,\fP...]|\f3""\fP Associate a list of break sequences referenced by .I n with the console. If ``*'' is used (the default), all defined break sequences will be available. If the null string (``\f3""\fP'') is used, no sequences will be available. .TP \f3device\fP \f2filename\fP .br Assign the serial device .I filename as the path to the console. Only consoles of type ``device'' will use this value. .TP \f3devicesubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B device value. A series of replacements can be defined by specifying a comma-separated list of \f2c\fP=\f2t\fP[\f2n\fP]\f2f\fP sequences where .I c is any printable character, .I t specifies the replacement value, .I n is a field length (optional), and .I f is the format string. .I t can be one of the characters below, catagorized as a string replacement or a numeric replacement, which dictates the use of the .I n and .I f fields. .RS .RS .sp .PD 0 .TP String Replacement .TP .B c console name .TP .B h .B host value .TP .B r .B replstring value .sp .PP Numeric Replacement .TP .B p config .B port value .TP .B P calculated port value .PD .RE .RE .IP For string replacements, if the replacement isn't at least .I n characters, it will be padded with space characters on the left. .I f must be `s'. For numeric replacements, the value will be formatted to at least .I n characters, padded with 0s if .I n begins with a 0, and space characters otherwise. .I f must be either `d', `x', `X', `a', or `A', specifying a decimal, lowercase hexadecimal (0-9a-f), uppercase hexadecimal (0-9A-F), lowercase alphanumeric (0-9a-z), or uppercase alphanumeric (0-9A-Z) conversion. If the null string (``\f3""\fP'') is used, no replacements will be done. .TP \f3exec\fP \f2command|\f3""\fP .br Assign the string .I command as the command to access the console. Conserver will run the command by invoking ``/bin/sh -ce "\f2command\fP"''. If the null string (``\f3""\fP'') is used or no .B exec keyword is specified, conserver will use the command ``/bin/sh -i''. Only consoles of type ``exec'' will use this value. .TP \f3execrunas\fP [\f2user\fP][:\f2group\fP]|\f3""\fP .br By default, the command invoked by .B exec is run with the same privileges as the server. If the server is running with root privileges, this option resets the user and/or group of the invoked process to .I user and .I group respectively. .I user may be a username or numeric uid and .I group may be a group name or numeric gid. Either one is optional. If the server is not running with root privileges, these values are not used. If the null string (``\f3""\fP'') is specified, the default of running with the same privileges as the server is restored. .TP \f3execsubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B exec value. See the .B devicesubst option for an explanation of the format string. If the null string (``\f3""\fP'') is used, no replacements will be done. .TP \f3host\fP \f2hostname\fP .br Assign .I hostname as the host to connect to for accessing the console. You must also set the .B port option for consoles of type ``host''. Normally, only consoles of type ``host'' and ``ipmi'' will use this value, however if the .BR devicesubst , .BR execsubst , or .B initsubst keywords are used in any console type, this value is used. .TP \f3idlestring\fP \f2string\fP|\f3""\fP .br Assign the .I string that is sent to the console once the console is idle for an .I idletimeout amount of time. If the null string (``\f3""\fP'') is used, the string is unset and the default is used. The string is interpreted just as a .B break string is interpreted (see the .B break configuration items for details) where all delays specified (via ``\ed'') use the default delay time. The default string is ``\en''. .TP \f3idletimeout\fP \f2number\fP[\f3s\fP|\f3m\fP|\f3h\fP] .br Set the idle timeout of the console to .I number seconds. If an `s', `m', or `h' is used after .IR number , the specified time is interpreted as seconds, minutes, or hours. Set the timeout to zero to disable the idle timeout (the default). .TP \f3ipmiciphersuite\fP \f2number\fP .br Set the IPMI cipher suite. Syntactically valid values are -1 (the default) and greater. Check the FreeIPMI documentation for usable values. .TP \f3ipmikg\fP \f2string\fP|\f3""\fP Set the BMC authentication key K_g to .IR string . A K_g value is a simple character string with the exception of `\e': .RS .RS .sp .PD 0 .TP 6 .B \e\e backslash .TP .BI \e ooo octal representation of a character (where .I ooo is one to three octal digits) .TP .BI \e c character .I c .PD .RE .RE .IP The resulting value must be no more than 20 characters. The null string (``\f3""\fP'') is the default. .TP \f3ipmiworkaround\fP [\f3!\fP]option[\f3,\fP...]|\f3""\fP .br You can turn off a workaround by prefixing it with a .RB `` ! '' character. So, to turn off the .B integrity workaround, you would use .BR !integrity . The following are valid .IR option s and their mapping to FreeIPMI settings: .RS .sp .PD 0 .TP 21 .B activation-status .SM SKIP_SOL_ACTIVATION_STATUS .TP .B auth-capabilites .SM AUTHENTICATION_CAPABILITIES .TP .B channel-payload .SM SKIP_CHANNEL_PAYLOAD_SUPPORT .TP .B checksum .SM NO_CHECKSUM_CHECK .TP .B default .SM DEFAULT .TP .B ignore-payload-size .SM IGNORE_SOL_PAYLOAD_SIZE .TP .B ignore-port .SM IGNORE_SOL_PORT .TP .B integrity .SM NON_EMPTY_INTEGRITY_CHECK_VALUE .TP .B intel-session .SM INTEL_2_0_SESSION .TP .B packet-sequence .SM INCREMENT_SOL_PACKET_SEQUENCE .TP .B privilege .SM OPEN_SESSION_PRIVILEGE .TP .B serial-alerts .SM SERIAL_ALERTS_DEFERRED .TP .B sun-session .SM SUN_2_0_SESSION .TP .B supermicro-session .SM SUPERMICRO_2_0_SESSION .PD .RE .IP If no .B ipmiworkaround is specified, the ``\f3default\fP'' workaround will be used. The null string (``\f3""\fP'') unsets all workarounds, including ``\f3default\fP''. See the FreeIPMI documentation for details on what workarounds affect. .TP \f3ipmiprivlevel\fP \f2user\fP|\f2operator\fP|\f2admin\fP .br Set the privilege level for the username used during IPMI authentication. The default privilege level is ``\f2admin\fP''. .TP \f3include\fP \f2default\fP .br The default block defined using the name .I default is applied to the current console or default block. The included default block must be previously defined. .TP \f3initcmd\fP \f2command\fP|\f3""\fP .br Invoke .I command as soon as the console is brought up, redirecting the console to stdin, stdout, and stderr of .IR command . The .I command is passed as an argument to ``/bin/sh -ce''. If the null string (``\f3""\fP'') is used, the command is unset and nothing is invoked. .TP \f3initrunas\fP [\f2user\fP][:\f2group\fP]|\f3""\fP .br By default, the command invoked by .B initcmd is run with the same privileges as the server. If the server is running with root privileges, this option resets the user and/or group of the invoked process to .I user and .I group respectively. .I user may be a username or numeric uid and .I group may be a group name or numeric gid. Either one is optional. If the server is not running with root privileges, these values are not used. If the null string (``\f3""\fP'') is specified, the default of running with the same privileges as the server is restored. .TP \f3initspinmax\fP \f2n\fP|\f3""\fP .br Set the maximum number of ``spins'' allowed for the console to .IR n , where 0 <= .I n <= 254. A console is determined to be ``spinning'' if an attempt to initialize the console occurs in under .B initspintimer seconds from its previous initialization and this quick initialization occurs .B initspinmax times in a row. If, at any point, the time between initializations is greater than .BR initspintimer , the counter for reaching .B initspinmax resets to zero. When a console is determined to be ``spinning'' it is forced down. If the null string (``\f3""\fP'') is specified, the default of .B 5 is used. .TP \f3initspintimer\fP \f2t\fP|\f3""\fP .br Set the number of seconds a console must be ``up'' to not be considered ``spinning'' to .IR t , where 0 <= .I t <= 254. See .B initspinmax for a full description of console ``spinning.'' If the null string (``\f3""\fP'') is specified, the default of .B 1 is used. .TP \f3initsubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B initcmd value. See the .B devicesubst option for an explanation of the format string. If the null string (``\f3""\fP'') is used, no replacements will be done. .TP \f3logfile\fP \f2filename\fP|\f3""\fP .br Assign the logfile specified by .I filename to the console. Any occurrence of ``&'' in .I filename will be replaced with the name of the console. If the null string (``\f3""\fP'') is used, the logfile name is unset and no logging will occur. .TP \f3logfilemax\fP \f2number\fP[\f3k\fP|\f3m\fP] .br Enable automatic rotation of .B logfile once its size exceeds .I number bytes. Specifying .B k or .B m interpret .I number as kilobytes and megabytes. .I number must be at least 2048 bytes. A value of zero will turn off automatic rotation of .BR logfile . The .B logfile .I filename will be renamed .IR filename -\s-1YYYYMMDD\s0-\s-1HHMMSS\s0, where the extension is the current GMT year, month, day, hour, minute, and second (to prevent issues with clock rollbacks). File sizes are checked every 5 minutes with an additional initial pseudo-random delay of up to one minute (to help prevent all processes checking all consoles simultaneously). 2.5% (minimum 100 bytes, maximum 4000 bytes) of the old logfile is read from the end of the file. All data past the first newline is moved (not copied) to the new logfile so that a replay of the console works and starts on a line boundary. .TP \f3master\fP \f2hostname\fP|\f2ipaddr\fP .br Define which conserver host manages the console. The host may be specified by .I hostname or using the address .IR ipaddr . .TP \f3motd\fP \f2message\fP|\f3""\fP .br Set the "message of the day" for the console to .IR message , which gets displayed when a client attaches to the console. If the null string (``\f3""\fP'') is used, the MOTD is unset and no message will occur. .TP \f3options\fP [\f3!\fP]option[\f3,\fP...]|\f3""\fP .br You can negate the option by prefixing it with a .RB `` ! '' character. So, to turn off the .B hupcl flag, you would use .BR !hupcl . The following are valid .IR option s: .RS .sp .PD 0 .TP 12 .B ixon Enable .SM XON/XOFF flow control on output. Only consoles of type ``device'' or ``exec'' will use this value. Default is .BR ixon . .TP .B ixany Enable any character to restart output. Only consoles of type ``device'' or ``exec'' will use this value. Default is .BR !ixany . .TP .B ixoff Enable .SM XON/XOFF flow control on input. Only consoles of type ``device'' or ``exec'' will use this value. Default is .B ixoff for consoles of type ``device'' and .B !ixoff for consoles of type ``exec''. .TP .B crtscts Enable .SM RTS/CTS (hardware) flow control. Only consoles of type ``device'' will use this value. Default is .BR !crtscts . .TP .B cstopb Set two stop bits, rather than one. Only consoles of type ``device'' will use this value. Default is .BR !cstopb . .TP .B hupcl Lower modem control lines after last process closes the device (hang up). Only consoles of type ``device'' will use this value. Default is .BR !hupcl . .TP .B ondemand Initialize the console when a client requests a connection to the console. When no clients are connected, bring the console down. The conserver option .B \-i will set this flag for all consoles. Default is .BR !ondemand . .TP .B striphigh Strip the high bit off all data coming from this console and all clients connected to this console before processing occurs. The conserver option .B \-7 will set this flag for all consoles. Default is .BR !striphigh . .TP .B reinitoncc Automatically reinitialize (``bring up'') a downed console when a client connects. Without this option, a client will be attached to the downed console and will need to manually reinitialize the console with an escape sequence. The conserver option .B \-o will set this flag for all consoles. Default is .BR !reinitoncc . .TP .B autoreinit Allow this console to be automatically reinitialized if it unexpectedly goes down. If the console doesn't come back up, it is retried every minute. A console of type ``exec'' that exits with a zero exit status is automatically reinitialized regardless of this setting. The conserver option .B \-F will .B unset this flag for all consoles. Default is .BR autoreinit . .TP .B unloved Enable the sending of this console's output (prefixed with its name) to the daemon's stdout (or the logfile if in daemon mode) when no clients are connected to the console. The conserver option .B \-u will set this flag for all consoles. Default is .BR !unloved . .TP .B login Allow users to log into this console. If logins are not allowed, conserver will send a generic message to the client saying so and terminate the connection. You can override the generic message by setting the .B motd message. Default is .BR login . .PD .RE .TP \f3parity\fP \f3even\fP|\f3mark\fP|\f3none\fP|\f3odd\fP|\f3space\fP .br Set the parity option for the console. Only consoles of type ``device'' will use this value. .TP \f3password\fP \f2password\fP|\f3""\fP .br Use .I password during IPMI authentication. If the null string (``\f3""\fP'') is used (the default), no password will be used. .TP \f3port\fP \f2number\fP|\f2name\fP .br Set the port used to access the console. The port may be specified as a .I number or a .IR name . A .I name will cause a .BR getservbyname (3) call to look up the port number. The .BR port , .BR portbase , and .B portinc values are all used to calculate the final port number to connect to. The formula used is .IR finalport " = " .BR portbase " + " .BR portinc " * " port . By using proper values in the formula, you can reference ports on a terminal server by their physical numbering of .RI 0.. n or .RI 1.. n (depending on if you like zero-based or one-based numbering). Warning: you can generate a -1 value with this formula, which will become a very high numbered positive value (since things are stored unsigned). You must also set the .B host option as well. Normally, only consoles of type ``host'' will use this value, however if the .BR devicesubst , .BR execsubst , or .B initsubst keywords are used in any console type, this value is used. .TP \f3portbase\fP \f2number\fP .br Set the base value for the port calculation formula. .I number must be 0 or greater. The default is zero. See .B port for the details of the formula. .TP \f3portinc\fP \f2number\fP .br Set the increment value for the port calculation formula. .I number must be 0 or greater. The default is one. See .B port for the details of the formula. .TP \f3protocol\fP \f3telnet\fP|\f3raw\fP .br Set the protocol used to send and receive data from the console. If .B raw is used, all data is sent ``as is'', unprotected by any protocol specification. If .B telnet is used (which is the default), data is encapsulated in the telnet protocol. The .B striphigh console option still applies when data is read by the server, and if enabled, can impact the encapsulation process. .TP \f3replstring\fP \f2string\fP .br A generic replacement string that can be used by the .BR devicesubst , .BR execsubst , and .B initsubst keywords. .TP \f3ro\fP [\f3!\fP]\f2username\fP[\f3,\fP...]|\f3""\fP .br Define a list of users making up the read-only access list for the console. If .I username matches a previously defined group name, all members of the previous group are applied to the read-only access list (with access reversed if prefixed with a `!'). If .I username doesn't match a previously defined group and .I username begins with `@', the name (minus the `@') is checked against the host's group database. All users found in the group will be granted (or denied, if prefixed with `!') read-only access. If .I username doesn't match a previous group and doesn't begin with `@', the users will be granted (or denied, if prefixed with `!') read-only access. If the null string (``\f3""\fP'') is used, any users previously defined for the console's read-only list are removed. .TP \f3rw\fP [\f3!\fP]\f2username\fP[\f3,\fP...]|\f3""\fP .br Define a list of users making up the read-write access list for the console. If .I username matches a previously defined group name, all members of the previous group are applied to the read-write access list (with access reversed if prefixed with a `!'). If .I username doesn't match a previously defined group and .I username begins with `@', the name (minus the `@') is checked against the host's group database. All users found in the group will be granted (or denied, if prefixed with `!') read-write access. If .I username doesn't match a previous group and doesn't begin with `@', the users will be granted (or denied, if prefixed with `!') read-write access. If the null string (``\f3""\fP'') is used, any users previously defined for the console's read-write list are removed. .TP \f3tasklist\fP \f2c\fP[\f3,\fP...]|\f3""\fP Associate a list of tasks referenced by .I c with the console. If ``*'' is used (the default), all defined tasks will be available. If the null string (``\f3""\fP'') is used, no tasks will be available. .TP \f3timestamp\fP [\f2number\fP[\f3m\fP|\f3h\fP|\f3d\fP|\f3l\fP]][\f3a\fP][\f3b\fP]|\f3""\fP .br Specifies the time between timestamps applied to the console log file and whether to log read/write connection actions. The timestamps look like ``[-- MARK -- Mon Jan 25 14:46:56 1999]''. The .RB ` m ', .RB ` h ', and .RB ` d ' tags specify ``minutes'' (the default), ``hours'', and ``days''. The .RB ` l ' tag specifies ``lines'' and will cause timestamps of the form ``[Mon Jan 25 14:46:56 PST 1999]'' to be placed every .I number lines (a newline character signifies a new line). So, ``5h'' specifies every five hours and ``2l'' specifies every two lines. An .RB ` a ' can be specified to add logs of ``attached'', ``detached'', and ``bumped'' actions, including the user's name and the host from which the client connection was made. A .RB ` b ' can be specified to add logging of break sequences sent to the console. .TP \f3type\fP \f3device\fP|\f3ipmi\fP|\f3exec\fP|\f3host\fP|\f3noop\fP|\f3uds\fP .br Set the type of console. A type of .RB `` device '' should be used for local serial ports (also set the .B device value). A type of .RB `` ipmi '' should be used for IPMI serial over LAN consoles (also set the .B host value and possibly the .BR username , .BR password , and .BR ipmi * values). A type of .RB `` exec '' should be used for command invocations (perhaps also set the .B exec value). A type of .RB `` host '' should be used for terminal servers and other TCP socket-based interaction (also set the .B host and .B port values). A type of .RB `` noop '' should be used as a placeholder - it does nothing, ignores any .B logfile value and forces the .B !nologin option (so you might want to set the .B motd value). A type of .RB `` uds '' should be used for Unix domain sockets (also set the .B uds option). .TP \f3uds\fP \f2filename\fP .br Assign the Unix domain socket .I filename as the path to the console. Only consoles of type ``uds'' will use this value. .TP \f3udssubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B uds value. See the .B devicesubst option for an explanation of the format string. If the null string (``\f3""\fP'') is used, no replacements will be done. .TP \f3username\fP \f2username\fP|\f3""\fP .br Use .I username during IPMI authentication. If the null string (``\f3""\fP'') is used (the default), the ``null'' user will be used. .RE .TP \f3group\fP \f2name\fP .br Define a user group identified as .IR name . .RS .TP \f3users\fP [\f3!\fP]\f2username\fP[\f3,\fP...]|\f3""\fP .br Define a list of users making up the group .IR name . If .I username matches a previously defined group name, all members of the previous group are applied to the current group (with access reversed if prefixed with a `!'). If .I username doesn't match a previously defined group and .I username begins with `@', the name (minus the `@') is checked against the host's group database. All users found in the group will be recorded with (or without, if prefixed with `!') access. If .I username doesn't match a previous group and doesn't begin with `@', the users will be recorded with (or without, if prefixed with `!') access. If the null string (``\f3""\fP'') is used, any users previously defined for this group are removed. .RE .TP \f3task\fP \f2c\fP .br Define a task where .I c is a lowercase alphanumeric (0-9a-z). Tasks are invoked via the .RI ``^Ec! c '' client escape sequence. .RS .TP \f3cmd\fP \f2command\fP|\f3""\fP .br Invoke .I command on the server when instructed by the client. All file descriptors are closed, except for stderr (which is inherited from the server). The .I command is passed as an argument to ``/bin/sh -ce'' and is a ``fire and forget'' methodology (you need to check logs for any issues). If the null string (``\f3""\fP'') is used, the entire task definition is ignored. .TP \f3confirm\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not to ask the client for confirmation before invoking the task. The default is ``no''. .TP \f3description\fP \f2string\fP .br Set a description for the task. When a client lists tasks, .I string will be printed instead of the command defined above. If the null string (``\f3""\fP'') is used, the command defined above will be printed. .TP \f3runas\fP [\f2user\fP][:\f2group\fP]|\f3""\fP .br By default, the command invoked by .B cmd is run with the same privileges as the server. If the server is running with root privileges, this option resets the user and/or group of the invoked process to .I user and .I group respectively. .I user may be a username or numeric uid and .I group may be a group name or numeric gid. Either one is optional. If the server is not running with root privileges, these values are not used. If the null string (``\f3""\fP'') is specified, the default of running with the same privileges as the server is restored. .TP \f3subst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B cmd value. See the .B devicesubst option for an explanation of the format string. If the null string (``\f3""\fP'') is used, no replacements will be done. .RE .SH AUTHORS Bryan Stansell, conserver.com .SH "SEE ALSO" .BR console (1), .BR conserver.passwd (5), .BR conserver (8) conserver-8.2.4/conserver.cf/conserver.passwd000066400000000000000000000000571344660520400213670ustar00rootroot00000000000000bryan:td1AgneGE3RsU djs:*passwd* todd:*passwd* conserver-8.2.4/conserver.cf/conserver.passwd.man.in000066400000000000000000000050251344660520400225460ustar00rootroot00000000000000.TH CONSERVER.PASSWD 5 "@CONSERVER_DATE@" "conserver-@CONSERVER_VERSION@" "conserver" .SH NAME conserver.passwd \- user access information for .BR conserver (8) .SH SYNOPSIS .IB username : password .SH DESCRIPTION The .B conserver.passwd file is the user authentication and authorization file for .BR conserver (8). Upon each incoming client connection, .B conserver opens and reads the .B conserver.passwd file, so edits to the file take effect immediately. It reads only until the first .I username match. .PP Blank lines and comment lines (those beginning with a ``#'' and optional leading whitespace) are ignored. Non-ignored lines beginning with whitespace are considered continuations of the previous line. This allows you to span one logical line over many physical lines and insert comments wherever appropriate. .PP Each logical line consists of two colon-separated fields. Leading and trailing white space in each field is ignored. .TP .I username the login name of the authorized user, or the string .RB `` *any* '' to match any user. This is compared against the name sent by the .B console client, based either on the user's identity or on the .B \-l option. Since .B conserver only uses the first .I username match, a .RB `` *any* '' entry will apply to any user without an entry earlier in the file. .TP .I password the encrypted password, or the string .RB `` *passwd* '' to indicate that .B conserver should look up the user's password in the system .BR passwd " (or " shadow ") database." If PAM support has been enabled .RB ( --with-pam ), PAM lookups will be done instead of .BR passwd " (or " shadow ") lookups" (you may need to edit /etc/pam.conf or create /etc/pam.d/conserver). If this field is empty, password checking is bypassed for this user. .SH EXAMPLE .TP 24 .B mary:r71mXjfALB5Ak Mary uses the password specified above; it does not matter whether she has a login on the conserver host. .TP .B fred:*passwd* Fred may connect only with his regular login password on the conserver host. .TP .B bozo:* Bozo is only allowed to access a console if his password isn't used (since it's invalid) which means he needs to come from a .B trusted host. .TP .B *any*:*passwd* Anyone not listed above uses their regular login and password. .SH "SEE ALSO" .BR console (1), .BR conserver.cf (5), .BR conserver (8) .SH BUGS .PP There is currently no way provided by the conserver package to generate the encrypted password strings besides copying them from the system .B passwd database or running .BR crypt (3) via C or perl or some other language that supports it. conserver-8.2.4/conserver.cf/label.ps000066400000000000000000000070601344660520400175620ustar00rootroot00000000000000%!PS-Adobe-2.0 EPSF-1.2 %%Title: RJ-11 %%Creator: A Braunsdorf %%CreationDate: %%For: ab %%BoundingBox: 0 0 243 148.5 %%EndComments % Add new hosts in the parens that match the port number /hosts [ (icd96) % C0 (fleet) (mentor) (icd84) (icd92) % C4 (pop.stat) (rapid) (tyro) (mace) % C8 (sage) (lively) (sentinel) (speedy) % C12 (nis35) (expert) (quick) (feserve) % C16 (curator) (franklin) (icd82) (icd94) % C20 (oasis) (staff) (swift) (flash) % C24 (labwatch) (ipscgate) (snap.stat) (_28) % C28 (keep) (tag) (assist) ] def % The relative co-ords of the various RJ11 jacks /coords [ % j# x y jack number, +x, +y [ 13 0.5 1.25 ] [ 11 1.0 1.25 ] [ 8 1.5 1.25 ] [ 14 0.5 0.625 ] [ 10 1.0 0.625 ] [ 9 1.5 0.625 ] [ 15 0.5 0 ] [ 12 1.0 0 ] [ 7 2.1875 1.25 ] [ 5 2.6875 1.25 ] [ 3 3.1875 1.25 ] [ 6 2.1875 0.625 ] [ 4 2.6875 0.625 ] [ 0 3.1875 0.625 ] [ 2 2.6875 0 ] [ 1 3.1875 0 ] ] def /str 20 string def % how to build an RJ11 connector diagram /rj11 { gsave 1 150 div setlinewidth 0 0 moveto 1 2 div 0 lineto 0 1 2 div rlineto 1 2 div neg 0 rlineto closepath stroke 1 16 div 1 8 div moveto 0 1 4 div rlineto 1 16 div 0 rlineto 0 1 16 div rlineto 3 16 div 0 rlineto 0 2 32 div neg rlineto 1 16 div 0 rlineto 0 1 16 div neg rlineto 1 16 div 0 rlineto 0 2 16 div neg rlineto 1 16 div neg 0 rlineto 0 1 16 div neg rlineto 1 16 div neg 0 rlineto 0 1 16 div neg rlineto 3 16 div neg 0 rlineto 0 1 16 div rlineto closepath stroke 0 1 7 { 36 div 9 64 div add 1 16 div exch moveto 0 1 72 div rlineto 1 72 div 0 rlineto 0 1 72 div neg rlineto closepath fill } for grestore /Courier findfont 1.5 8 div scalefont setfont 2 16 div 3 16 div moveto str cvs dup length 1 eq { ( )show } if show } def % Page layout stuff 72 72 scale -90 rotate -11 0 translate /jack 28 def /fudge 1 32 div def /Courier findfont 10 72 div scalefont setfont 0 setlinewidth % label the lines on the left 1 1 8 { 9 div 6 mul 0.25 exch moveto gsave fudge dup rmoveto jack str cvs show ( ) show hosts jack get show grestore /jack jack 1 add def gsave 15 16 div 0 rlineto stroke grestore 1 0 rmoveto gsave fudge dup rmoveto jack str cvs show ( ) show hosts jack get show grestore /jack jack 1 add def gsave 15 16 div 0 rlineto stroke grestore 1 0 rmoveto gsave fudge dup rmoveto jack str cvs show ( ) show hosts jack get show grestore /jack jack 1 add def gsave 15 16 div 0 rlineto stroke grestore 1 0 rmoveto gsave fudge dup rmoveto jack str cvs show ( ) show hosts jack get show grestore /jack jack 1 add def gsave 15 16 div 0 rlineto stroke grestore 1 0 rmoveto /jack jack 8 sub def } for % Diddle the page and layout the RJ11s 4.5 0.75 translate 9 8 div dup scale 0 1 coords length 1 sub { gsave coords exch get aload pop translate rj11 grestore } for 0 1 coords length 1 sub { gsave coords exch get aload pop 2.5 add % for the second back up 2.5 inches translate 16 add rj11 % ... add 16 to the RJ11 number grestore } for showpage conserver-8.2.4/conserver.cf/samples/000077500000000000000000000000001344660520400176005ustar00rootroot00000000000000conserver-8.2.4/conserver.cf/samples/README000066400000000000000000000015221344660520400204600ustar00rootroot00000000000000I put together the sample configuration files in this directory in hopes that it would help folks see some of the possibilities of the configuration file format. Each of the files are syntatically correct, but have never actually been used. Each file is basically built upon the previous...theoretically, if not actually. Hopefully they'll help show some of the cool things you can do with the configuration file and help those trying to figure out how they should even start. simple.cf - A very simple, one console config file basic.cf - A config with a couple consoles, mostly using defaults average.cf - A config for many consoles, using breaks, user lists, etc...bascially customizing each area average-distributed.cf - Taking average.cf to multiple conserver hosts with overrides on those hosts Bryan Stansell conserver-8.2.4/conserver.cf/samples/average-distributed.cf000066400000000000000000000075141344660520400240530ustar00rootroot00000000000000# # I took the average.cf file and expanded it to use a distributed # conserver setup...two conserver hosts (conserver1 and conserver2), but # the basic philosophy would hold for many more console and/or conserver # hosts. # # ------ define a user group ------ group sysadmin { users bryan, todd, dave; } # helpers is everyone but the sysadmin group group helpers { users *, !sysadmin; } # ------ make sure breaks are the way we want -------- break 1 { string "\z"; } break 2 { string "\r\d~\d^b"; delay 600; } break 3 { string "#."; } # ----- define some console types ------ # yeah, just setting a break doesn't quite seem worth it, but perhaps, # some day, there will be more host-specific stuff. default sun-std { break 1; } default sun-alt { break 2; } default sun-lom { break 3; } # ------ defaults ------ # we set a 'global' default so we can reuse the bits below. we're going # to set the '*' default, then define consoles, reset the '*' default, # define more consoles, etc. default global { logfile /var/consoles/&; # '&' is replaced with console name timestamp 1hab; # write timestamps rw sysadmin; # allow sysadmins full access ro helpers; # allow helpers to watch include sun-std; } # --------- define our terminal attributes ---------- # simple tcp connections are "easy" default cisco { type host; portbase 2000; portinc 1; } default xyplex { type host; portbase 2000; portinc 100; } # this is a cyclades card referenced with /dev/ttyC0 through /dev/ttyC31 # (referenced as ports 1 through 32 in conserver.cf) # we set the various port calculation bits and pattern substitution to # come up with a generic definition default cyclades { type device; device /dev/ttyC&; baud 9600; parity none; devicesubst &=Pd; portbase -1; portinc 1; host unused; } ## this is a term server accessed with an ssh command # it too uses pattern substitution and such to get the job done default ciscossh { type exec; portbase 2000; portinc 1; exec /usr/local/bin/ssh -p P -l tsuser H; execsubst H=hs,P=Pd; } # ------- set the global default for the first conserver host ------- # the consoles below (until the default is reset) are managed # by conserver1.conserver.com default * { include global; master conserver1.conserver.com; } # ------- define the consoles on ts1.conserver.com -------- default ts1.conserver.com { include cisco; host ts1.conserver.com; } console web1.conserver.com { include ts1.conserver.com; port 2; } console ns1.conserver.com { include ts1.conserver.com; port 10; } # ------- define the consoles on ts2.conserver.com -------- default ts2.conserver.com { include xyplex; host ts2.conserver.com; } console web2.conserver.com { include ts2.conserver.com; port 4; } console ns2.conserver.com { include ts2.conserver.com; port 22; } # ------- set the global default for the second conserver host ------- # the following consoles are managed by conserver2.conserver.com default * { include global; master conserver2.conserver.com; } # ------- define the consoles on ts3.conserver.com -------- default ts3.conserver.com { include ciscossh; host ts3.conserver.com; } console ftp1.conserver.com { include ts3.conserver.com; include sun-lom; port 7; } # ------- set up the an access list to avoid the default ------- # anything *not* matched here will fallback to the default access mode access * { trusted 127.0.0.1; allowed 10.0.0.0/8; } # conserver2 has an extra leg that is trusted access conserver2.conserver.com { trusted 192.168.0.0/16; } # ------- do some server configuration --------- # both conserver1.conserver.com and conserver2.conserver.com use the same # set of defaults config * { defaultaccess rejected; daemonmode on; logfile /var/log/conserver; } # we're going to set the default access on conserver2 to allowed, because # it's in a higher-trust network config conserver2.conserver.com { defaultaccess allowed; } conserver-8.2.4/conserver.cf/samples/average.cf000066400000000000000000000056771344660520400215430ustar00rootroot00000000000000# # This would be what i'd expect a more common configuration file would # look like. There are consoles attached to multiple devices, simple # access lists, etc. # # ------ define a user group ------ group sysadmin { users bryan, todd, dave; } # helpers is everyone but the sysadmin group group helpers { users *, !sysadmin; } # ------ make sure breaks are the way we want -------- break 1 { string "\z"; } break 2 { string "\r\d~\d^b"; delay 600; } break 3 { string "#."; } # ----- define some console types ------ # yeah, just setting a break doesn't quite seem worth it, but perhaps, # some day, there will be more host-specific stuff. default sun-std { break 1; } default sun-alt { break 2; } default sun-lom { break 3; } # ------ defaults ------ # now for some generic console defaults so that we don't have to # duplicate them for each console. default * { logfile /var/consoles/&; # '&' is replaced with console name timestamp 1hab; # write timestamps rw sysadmin; # allow sysadmins full access ro helpers; # allow helpers to watch master localhost; include sun-std; } # --------- define our terminal attributes ---------- # simple tcp connections are "easy" default cisco { type host; portbase 2000; portinc 1; } default xyplex { type host; portbase 2000; portinc 100; } # this is a cyclades card referenced with /dev/ttyC0 through /dev/ttyC31 # (referenced as ports 1 through 32 in conserver.cf) # we set the various port calculation bits and pattern substitution to # come up with a generic definition default cyclades { type device; device /dev/ttyC&; baud 9600; parity none; devicesubst &=Pd; portbase -1; portinc 1; host unused; } ## this is a term server accessed with an ssh command # it too uses pattern substitution and such to get the job done default ciscossh { type exec; portbase 2000; portinc 1; exec /usr/local/bin/ssh -p P -l tsuser H; execsubst H=hs,P=Pd; } # ------- define the consoles on ts1.conserver.com -------- default ts1.conserver.com { include cisco; host ts1.conserver.com; } console web1.conserver.com { include ts1.conserver.com; port 2; } console ns1.conserver.com { include ts1.conserver.com; port 10; } # ------- define the consoles on ts2.conserver.com -------- default ts2.conserver.com { include xyplex; host ts2.conserver.com; } console web2.conserver.com { include ts2.conserver.com; port 4; } console ns2.conserver.com { include ts2.conserver.com; port 22; } # ------- define the consoles on ts3.conserver.com -------- default ts3.conserver.com { include ciscossh; host ts3.conserver.com; } console ftp1.conserver.com { include ts3.conserver.com; include sun-lom; port 7; } # ------- set up the an access list to avoid the default ------- # anything *not* matched here will fallback to the default access mode access * { trusted 127.0.0.1; allowed 10.0.0.0/8; } # ------- do some server configuration --------- config * { defaultaccess rejected; daemonmode on; logfile /var/log/conserver; } conserver-8.2.4/conserver.cf/samples/basic.cf000066400000000000000000000015661344660520400212030ustar00rootroot00000000000000# # This is a fairly basic configuration file that interacts with one # terminal server. # # first, we're going to set some generic console defaults so that we # don't have to duplicate them for each console. default * { logfile /var/consoles/&; # '&' is replaced with console name timestamp 1hab; # write timestamps rw *; # allow all users master localhost; type host; host ts1.conserver.com; # consoles on ts1.conserver.co portbase 2000; # port numbers start at 2001 and portinc 1; # go up by 1 (port #1 == 2001, etc) } # define two consoles on the terminal server console web1.conserver.com { port 2; } # calculates to tcp port 2002 console ns1.conserver.com { port 10; } # calculates to tcp port 2010 # set up the an access list to avoid the default # anything *not* matched here will fallback to the default access (-a) # mode access * { trusted 127.0.0.1; } conserver-8.2.4/conserver.cf/samples/simple.cf000066400000000000000000000004121344660520400214000ustar00rootroot00000000000000# # I believe this is the smallest configuration file that is also fully # functional. You have to be happy with the default access type (-a) # as well as the default access list that gets used. # console simple { master localhost; type exec; rw *; } conserver-8.2.4/conserver.cf/test.cf000066400000000000000000000010751344660520400174300ustar00rootroot00000000000000# dummy conserver config file # default full { rw *; } default * { logfile /tmp/&; timestamp ""; include full; } break 5 { string "\rtest\r"; } # list of consoles we serve console shell { master localhost; timestamp 5; type exec; exec ""; } console bash { master localhost; timestamp 2; type exec; exec /usr/local/bin/bash; } console web { master localhost; type host; host www.conserver.com; port 80; } console b { master localhost; type device; device /dev/ttyb; baud 9600; parity none; } # list of clients we allow access * { trusted 127.0.0.1; } conserver-8.2.4/conserver/000077500000000000000000000000001344660520400155455ustar00rootroot00000000000000conserver-8.2.4/conserver/Makefile.in000066400000000000000000000035421344660520400176160ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ mandir = @mandir@ datadir = @datadir@ libdir = @libdir@ pkglibdir = $(libdir)/conserver exampledir = $(datadir)/examples/conserver ### Installation programs and flags INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s LN_S = @LN_S@ MKDIR = @MKDIR@ ### Compiler and link options CC = @CC@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" CPPFLAGS = -I.. -I$(top_srcdir) -I$(srcdir) $(DEFS) @CPPFLAGS@ @CONSCPPFLAGS@ LDFLAGS = @LDFLAGS@ @CONSLDFLAGS@ LIBS = @LIBS@ @CONSLIBS@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below CONSERVER_OBJS = access.o client.o consent.o group.o main.o master.o \ readcfg.o fallback.o cutil.o CONSERVER_HDRS = ../config.h $(top_srcdir)/compat.h $(srcdir)/access.h \ $(srcdir)/client.h $(srcdir)/consent.h $(srcdir)/cutil.h \ $(srcdir)/group.h $(srcdir)/main.h $(srcdir)/master.h \ $(srcdir)/readcfg.h $(srcdir)/version.h ALL = conserver convert all: $(ALL) $(CONSERVER_OBJS): $(CONSERVER_HDRS) conserver: $(CONSERVER_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o conserver $(CONSERVER_OBJS) $(LIBS) convert: convert.o cutil.o $(CC) $(CFLAGS) $(LDFLAGS) -o convert convert.o cutil.o $(LIBS) .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< clean: rm -f *~ *.o $(ALL) core distclean: clean rm -f Makefile conserver.rc install: conserver $(MKDIR) $(DESTDIR)$(sbindir) $(INSTALL_PROGRAM) conserver $(DESTDIR)$(sbindir) $(MKDIR) $(DESTDIR)$(mandir)/man8 $(INSTALL) -m 0644 conserver.man $(DESTDIR)$(mandir)/man8/conserver.8 $(MKDIR) $(DESTDIR)$(exampledir) $(INSTALL) conserver.rc $(DESTDIR)$(exampledir) $(MKDIR) $(DESTDIR)$(pkglibdir) $(INSTALL) convert $(DESTDIR)$(pkglibdir)/convert .PHONY: clean distclean install conserver-8.2.4/conserver/Sun-serial000066400000000000000000000033721344660520400175170ustar00rootroot00000000000000If you are going to be hooking Sun consoles to your console server, you will run into a problem: The sun will halt whenever the cable is unplugged. It will also halt when the the console server is powered off and on. To prevent this we modified the wiring of our serial cables at the end that attaches to the suns. The server can still be halted by generating a software line break, which is can be done by sending an escape sequence to the console server. WE WILL NOT BE HELD RESPONSIBLE FOR ANY DAMAGES CAUSED BY ATTEMPTING A PARTIAL OR COMPLETE IMPLEMENTATION OF THIS MODIFICATION. "WE" refers to The Ohio State University and specifically, the employees of CIS Department of Ohio State. This is a diagram of the cable we use. Sun file Console Server Server Side Side 2 Tx ------------------------- ------------------------- 2 Tx \/ /\ 3 Rx ------+------------------ ------------------------- 3 Rx | Z 4.7K resistor | 25 -5v ------+ 25 -5v 7 GND ---------------------------------------------------- 7 GND 4 RTS --+ 4 RTS | 5 CTS --+ 5 CTS 6 DSR --+ 6 DSR | 20 DTR --+ 20 DTR I'm not a hardware person, but I think the important part is adding the resistor. The rest is icing, more or less. conserver-8.2.4/conserver/access.c000066400000000000000000000254741344660520400171660ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #include #include #include #include #include #include #include #include #if USE_IPV6 # include # include # include # include #endif #if !USE_IPV6 /* Compare an Internet address (IPv4 expected), with an address pattern * passed as a character string representing an address in the Internet * standard `.' notation, optionally followed by a slash and an integer * specifying the number of bits in the network portion of the address * (the netmask size). If not specified explicitly, the netmask size used * is that implied by the address class. If either the netmask is specified * explicitly, or the local network address part of the pattern is zero, * then only the network number parts of the addresses are compared; * otherwise the entire addresses are compared. * * Returns 0 if the addresses match, else returns 1. */ int AddrCmp(struct in_addr *addr, char *pattern) { in_addr_t hostaddr, pattern_addr, netmask; char *p, *slash_posn; static STRING *buf = (STRING *)0; # if HAVE_INET_ATON struct in_addr inetaddr; # endif if (buf == (STRING *)0) buf = AllocString(); slash_posn = strchr(pattern, '/'); if (slash_posn != NULL) { BuildString((char *)0, buf); BuildString(pattern, buf); buf->string[slash_posn - pattern] = '\0'; /* isolate the address */ p = buf->string; } else p = pattern; # if HAVE_INET_ATON if (inet_aton(p, &inetaddr) == 0) return 1; pattern_addr = inetaddr.s_addr; # else pattern_addr = inet_addr(p); if (pattern_addr == (in_addr_t) (-1)) return 1; /* malformed address */ # endif if (slash_posn) { /* convert explicit netmask */ int mask_bits = atoi(slash_posn + 1); for (netmask = 0; mask_bits > 0; --mask_bits) netmask = 0x80000000 | (netmask >> 1); } else { /* netmask implied by address class */ in_addr_t ia = ntohl(pattern_addr); if (IN_CLASSA(ia)) netmask = IN_CLASSA_NET; else if (IN_CLASSB(ia)) netmask = IN_CLASSB_NET; else if (IN_CLASSC(ia)) netmask = IN_CLASSC_NET; else return 1; /* unsupported address class */ } netmask = htonl(netmask); if (~netmask & pattern_addr) netmask = 0xffffffff; /* compare entire addresses */ hostaddr = addr->s_addr; CONDDEBUG((1, "AddrCmp(): host=%lx(%lx/%lx) acl=%lx(%lx/%lx)", hostaddr & netmask, hostaddr, netmask, pattern_addr & netmask, pattern_addr, netmask)); return (hostaddr & netmask) != (pattern_addr & netmask); } #endif /* USE_IPV6 */ /* return the access type for a given host entry (ksb) */ char AccType(INADDR_STYPE *addr, char **peername) { ACCESS *pACtmp; socklen_t so; char ret; #if USE_IPV6 int error; char host[NI_MAXHOST]; char ipaddr[NI_MAXHOST]; #else struct hostent *he = (struct hostent *)0; int a; # if TRUST_REVERSE_DNS char **revNames = (char **)0; # endif CONDDEBUG((1, "AccType(): ip=%s", inet_ntoa(*addr))); #endif /* USE_IPV6 */ ret = config->defaultaccess; so = sizeof(*addr); #if USE_IPV6 error = getnameinfo((struct sockaddr *)addr, so, ipaddr, sizeof(ipaddr), NULL, 0, NI_NUMERICHOST); if (error) { Error("AccType(): getnameinfo failed: %s", gai_strerror(error)); goto common_ret; } CONDDEBUG((1, "AccType(): ip=%s", ipaddr)); error = getnameinfo((struct sockaddr *)addr, so, host, sizeof(host), NULL, 0, 0); if (!error) CONDDEBUG((1, "AccType(): host=%s", host)); for (pACtmp = pACList; pACtmp != (ACCESS *)0; pACtmp = pACtmp->pACnext) { CONDDEBUG((1, "AccType(): who=%s, trust=%c", pACtmp->pcwho, pACtmp->ctrust)); if (strstr(ipaddr, pACtmp->pcwho) != NULL) { CONDDEBUG((1, "AccType(): match for ip=%s", ipaddr)); ret = pACtmp->ctrust; goto common_ret; } if (!error && strstr(host, pACtmp->pcwho) != NULL) { CONDDEBUG((1, "AccType(): match for host=%s", host)); ret = pACtmp->ctrust; goto common_ret; } } common_ret: if (config->loghostnames == FLAGTRUE && !error) *peername = StrDup(host); #else # if TRUST_REVERSE_DNS /* if we trust reverse dns, we get the names associated with * the address we're checking and then check each of those * against the access list entries (below). */ if ((he = gethostbyaddr((char *)addr, so, AF_INET)) == (struct hostent *)0) { Error("AccType(): gethostbyaddr(%s): %s", inet_ntoa(*addr), hstrerror(h_errno)); } else { char *hname; if (he->h_name != (char *)0) { /* count up the number of names */ for (a = 0, hname = he->h_aliases[a]; hname != (char *)0; hname = he->h_aliases[++a]); a += 2; /* h_name + (char *)0 */ /* now duplicate them */ if ((revNames = (char **)calloc(a, sizeof(char *))) != (char **)0) { for (hname = he->h_name, a = 0; hname != (char *)0; hname = he->h_aliases[a++]) { if ((revNames[a] = StrDup(hname)) == (char *)0) break; CONDDEBUG((1, "AccType(): revNames[%d]='%s'", a, hname)); } } } } # endif for (pACtmp = pACList; pACtmp != (ACCESS *)0; pACtmp = pACtmp->pACnext) { CONDDEBUG((1, "AccType(): who=%s, trust=%c", pACtmp->pcwho, pACtmp->ctrust)); if (pACtmp->isCIDR != 0) { if (AddrCmp(addr, pACtmp->pcwho) == 0) { ret = pACtmp->ctrust; goto common_ret; } continue; } if ((he = gethostbyname(pACtmp->pcwho)) == (struct hostent *)0) { Error("AccType(): gethostbyname(%s): %s", pACtmp->pcwho, hstrerror(h_errno)); } else if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("AccType(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)", pACtmp->pcwho, he->h_length, AF_INET, he->h_addrtype); } else { for (a = 0; he->h_addr_list[a] != (char *)0; a++) { CONDDEBUG((1, "AccType(): addr=%s", inet_ntoa(*(struct in_addr *) (he->h_addr_list[a])))); if ( # if HAVE_MEMCMP memcmp(&(addr->s_addr), he->h_addr_list[a], he->h_length) # else bcmp(&(addr->s_addr), he->h_addr_list[a], he->h_length) # endif == 0) { ret = pACtmp->ctrust; goto common_ret; } } } # if TRUST_REVERSE_DNS /* we chop bits off client names so that we can put domain * names in access lists or even top-level domains. * allowed conserver.com, net; * this allows anything from conserver.com and anything in * the .net top-level. without TRUST_REVERSE_DNS, those names * better map to ip addresses for them to take effect. */ if (revNames != (char **)0) { char *pcName; int wlen; int len; wlen = strlen(pACtmp->pcwho); for (a = 0; revNames[a] != (char *)0; a++) { for (pcName = revNames[a], len = strlen(pcName); len >= wlen; len = strlen(++pcName)) { CONDDEBUG((1, "AccType(): name=%s", pcName)); if (strcasecmp(pcName, pACtmp->pcwho) == 0) { if (peername != (char **)0) *peername = StrDup(revNames[a]); ret = pACtmp->ctrust; goto common_ret2; } pcName = strchr(pcName, '.'); if (pcName == (char *)0) break; } } } # endif } common_ret: if (config->loghostnames == FLAGTRUE && peername != (char **)0) { # if TRUST_REVERSE_DNS if (revNames != (char **)0 && revNames[0] != (char *)0) *peername = StrDup(revNames[0]); # else if ((he = gethostbyaddr((char *)addr, so, AF_INET)) != (struct hostent *)0) { *peername = StrDup(he->h_name); } # endif } # if TRUST_REVERSE_DNS common_ret2: if (revNames != (char **)0) { for (a = 0; revNames[a] != (char *)0; a++) free(revNames[a]); free(revNames); } # endif #endif /* USE_IPV6 */ return ret; } void SetDefAccess( #if USE_IPV6 void #else struct in_addr *pAddr, char *pHost #endif ) { ACCESS *a; #if USE_IPV6 int error; char addr[NI_MAXHOST]; struct ifaddrs *myAddrs, *ifa; #endif /* USE_IPV6 */ while (pACList != (ACCESS *)0) { a = pACList->pACnext; DestroyAccessList(pACList); pACList = a; } #if USE_IPV6 /* get list of all addresses on system */ error = getifaddrs(&myAddrs); if (error) { Error("SetDefAccess(): getifaddrs: %s", strerror(errno)); return; } for (ifa = myAddrs; ifa != NULL; ifa = ifa->ifa_next) { /* skip interfaces without address or in down state */ if (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP)) continue; error = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_storage), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); if (error) continue; if ((a = (ACCESS *)calloc(1, sizeof(ACCESS))) == (ACCESS *)0) OutOfMem(); if ((a->pcwho = StrDup(addr)) == (char *)0) OutOfMem(); a->ctrust = config->defaultaccess; a->pACnext = pACList; pACList = a; CONDDEBUG((1, "SetDefAccess(): trust=%c, who=%s", pACList->ctrust, pACList->pcwho)); } freeifaddrs(myAddrs); #elif USE_UNIX_DOMAIN_SOCKETS if ((pACList = (ACCESS *)calloc(1, sizeof(ACCESS))) == (ACCESS *)0) OutOfMem(); if ((pACList->pcwho = StrDup("127.0.0.1")) == (char *)0) OutOfMem(); pACList->ctrust = config->defaultaccess; CONDDEBUG((1, "SetDefAccess(): trust=%c, who=%s", pACList->ctrust, pACList->pcwho)); #else while (pAddr->s_addr != (in_addr_t) 0) { char *addr; addr = inet_ntoa(*pAddr); if ((a = (ACCESS *)calloc(1, sizeof(ACCESS))) == (ACCESS *)0) OutOfMem(); if ((a->pcwho = StrDup(addr)) == (char *)0) OutOfMem(); a->ctrust = config->defaultaccess; a->pACnext = pACList; pACList = a; CONDDEBUG((1, "SetDefAccess(): trust=%c, who=%s", pACList->ctrust, pACList->pcwho)); pAddr++; } #endif } void DestroyAccessList(ACCESS *pACList) { if (pACList == (ACCESS *)0) return; if (pACList->pcwho != (char *)0) free(pACList->pcwho); free(pACList); } conserver-8.2.4/conserver/access.h000066400000000000000000000032601344660520400171600ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* * keep track of network access and peer console servers (ksb) */ typedef struct access { char ctrust; /* how much do we trust the host */ char *pcwho; /* what is the hosts name/ip number */ int isCIDR; /* is this a CIDR addr (or hostname?) */ struct access *pACnext; /* next access list */ } ACCESS; extern char AccType(INADDR_STYPE *, char **); extern void SetDefAccess( #if USE_IPV6 void #else struct in_addr *, char * #endif ); extern void DestroyAccessList(ACCESS *); conserver-8.2.4/conserver/client.c000066400000000000000000000363201344660520400171730ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #include #include #include #include #include #include #include #if USE_IPV6 # include # include #endif /* USE_IPV6 */ #if defined(USE_LIBWRAP) # include # include int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* find the next guy who wants to write on the console (ksb) */ void FindWrite(CONSENT *pCE) { CONSCLIENT *pCLfound = (CONSCLIENT *)0; CONSCLIENT *pCL; /* make the first guy (last on the list) to have the `want write' bit set * the writer (tell him of the promotion, too) we could look for the most * recent or some such... I guess it doesn't matter that much. */ if (pCE->pCLwr != (CONSCLIENT *)0 || pCE->fronly) return; for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fwantwr && !pCL->fro) pCLfound = pCL; } if (pCLfound != (CONSCLIENT *)0) { pCLfound->fwantwr = 0; pCLfound->fwr = 1; if (pCE->nolog) { FileWrite(pCLfound->fd, FLAGFALSE, "\r\n[attached (nologging)]\r\n", -1); } else { FileWrite(pCLfound->fd, FLAGFALSE, "\r\n[attached]\r\n", -1); } TagLogfileAct(pCE, "%s attached", pCLfound->acid->string); pCE->pCLwr = pCLfound; } } void BumpClient(CONSENT *pCE, char *message) { if ((CONSCLIENT *)0 == pCE->pCLwr) return; if ((char *)0 != message) FileWrite(pCE->pCLwr->fd, FLAGFALSE, message, -1); pCE->pCLwr->fwantwr = 0; pCE->pCLwr->fwr = 0; pCE->pCLwr = (CONSCLIENT *)0; } /* replay last 'back' lines of the log file upon connect to console (ksb) * * NB: we know the console might be spewing when the replay happens, * we want to just output what is in the log file and get out, * so we don't drop chars... */ #define REPLAYBUFFER 4096 void Replay(CONSENT *pCE, CONSFILE *fdOut, unsigned short back) { CONSFILE *fdLog = (CONSFILE *)0; STRING *line = (STRING *)0; off_t file_pos; off_t buf_pos; char *buf = (char *)0; char *bp = (char *)0; int ch; struct stat stLog; int ln; int was_mark = 0; #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY unsigned long dmallocMarkReplay = 0; #endif if (pCE != (CONSENT *)0 && pCE->logfile != (char *)0) fdLog = FileOpen(pCE->logfile, O_RDONLY, 0644); if (fdLog == (CONSFILE *)0) { FileWrite(fdOut, FLAGFALSE, "[no log file on this console]\r\n", -1); return; } #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY dmallocMarkReplay = dmalloc_mark(); #endif /* find the size of the file */ if (0 != FileStat(fdLog, &stLog)) goto common_exit; file_pos = stLog.st_size - 1; /* point at last byte */ buf_pos = file_pos + 1; if ((char *)0 == (buf = malloc(REPLAYBUFFER))) OutOfMem(); bp = buf + 1; /* just give it something - it resets below */ line = AllocString(); /* loop as long as there is data in the file or we have not found * the requested number of lines */ ln = -1; for (; file_pos >= 0; file_pos--, bp--) { if (file_pos < buf_pos) { int r; /* read one buffer worth of data a buffer boundary * * the first read will probably not get a full buffer but * the rest (as we work our way back in the file) should be */ buf_pos = (file_pos / REPLAYBUFFER) * REPLAYBUFFER; if (FileSeek(fdLog, buf_pos, SEEK_SET) < 0) { goto common_exit; } if ((r = FileRead(fdLog, buf, REPLAYBUFFER)) < 0) { goto common_exit; } bp = buf + r - 1; } /* process the next character */ if ((ch = *bp) == '\n') { if (ln >= 0) { int i; int u; int is_mark = 0; /* reverse the text to put it in forward order */ u = line->used - 1; for (i = 0; i < u / 2; i++) { int temp; temp = line->string[i]; line->string[i] = line->string[u - i - 1]; line->string[u - i - 1] = temp; } /* see if this line is a MARK */ if (line->used > 0 && line->string[0] == '[') { char dummy[4]; int j; i = sscanf(line->string + 1, "-- MARK -- %3c %3c %d %d:%d:%d %d]\r\n", dummy, dummy, &j, &j, &j, &j, &j); is_mark = (i == 7); } /* process this line */ if (is_mark && was_mark) { /* this is a mark and the previous line is also * a mark, so reduce the line count 'cause it'll * go up by one and we're joining them on output. */ ln--; } was_mark = is_mark; } /* advance to the next line and break if we have enough */ ln++; BuildString((char *)0, line); if (ln >= back) { break; } } /* if we have a character but no lines yet, the last text in the * file does not end with a newline, so start the first line anyway */ if (ln < 0) { ln = 0; } BuildStringChar(ch, line); /* if we've processed "a lot" of data for a line, then bail * why? there must be some very long non-newline terminated * strings and if we just keep going back, we could spew lots * of data and chew up lots of memory */ if (line->used > MAXREPLAYLINELEN) { break; } } /* move forward. either we hit the beginning of the file and we * move to the first byte, or we hit a \n and we move past it */ file_pos++; /* Now output the lines, starting from where we stopped */ if (FileSeek(fdLog, file_pos, SEEK_SET) >= 0) { int eof = 0; int i = 0; int r = 0; STRING *mark_beg = (STRING *)0; STRING *mark_end = (STRING *)0; mark_beg = AllocString(); mark_end = AllocString(); ln = 0; /* number of lines output */ BuildString((char *)0, line); while (ln < back && !eof) { if (r <= 0) { if ((r = FileRead(fdLog, buf, REPLAYBUFFER)) < 0) eof = 1; i = 0; } if (!eof) BuildStringChar(buf[i], line); if (buf[i] == '\n' || eof) { int is_mark = 0; if (line->used > 0 && line->string[0] == '[') { char dummy[4]; int j; int i; i = sscanf(line->string + 1, "-- MARK -- %3c %3c %d %d:%d:%d %d]\r\n", dummy, dummy, &j, &j, &j, &j, &j); is_mark = (i == 7); } if (is_mark) { if (mark_beg->used > 1) { BuildString((char *)0, mark_end); BuildString(line->string, mark_end); } else BuildString(line->string, mark_beg); } else { if (mark_beg->used > 1) { if (mark_end->used > 1) { char *s; /* output the start of the range, stopping at the ']' */ s = strrchr(mark_beg->string, ']'); if ((char *)0 != s) *s = '\000'; FileWrite(fdOut, FLAGTRUE, mark_beg->string, -1); FileWrite(fdOut, FLAGTRUE, " .. ", 4); /* build the end string by removing the leading "[-- MARK -- " * and replacing "]\r\n" on the end with " -- MARK --]\r\n" */ s = strrchr(mark_end->string, ']'); if ((char *)0 != s) *s = '\000'; FileWrite(fdOut, FLAGTRUE, mark_end->string + sizeof("[-- MARK -- ") - 1, -1); FileWrite(fdOut, FLAGFALSE, " -- MARK --]\r\n", -1); } else { FileWrite(fdOut, FLAGFALSE, mark_beg->string, mark_beg->used - 1); } BuildString((char *)0, mark_beg); BuildString((char *)0, mark_end); ln++; if (ln >= back) break; } FileWrite(fdOut, FLAGFALSE, line->string, line->used - 1); ln++; } BuildString((char *)0, line); } /* move the counters */ i++; r--; } DestroyString(mark_end); DestroyString(mark_beg); } common_exit: if (line != (STRING *)0) DestroyString(line); if (buf != (char *)0) free(buf); if (fdLog != (CONSFILE *)0) FileClose(&fdLog); #if HAVE_DMALLOC && DMALLOC_MARK_REPLAY CONDDEBUG((1, "Replay(): dmalloc / MarkReplay")); dmalloc_log_changed(dmallocMarkReplay, 1, 0, 1); #endif } /* these bit tell us which parts of the Truth to tell the client (ksb) */ #define WHEN_SPY 0x01 #define WHEN_ATTACH 0x02 #define WHEN_EXPERT 0x04 /* ZZZ no way to set his yet */ #define WHEN_ALWAYS 0x40 #define IS_LIMITED 0x100 #define HALFLINE 40 typedef struct HLnode { int iwhen; char *actext; } HELP; static HELP aHLTable[] = { {WHEN_ALWAYS, ". disconnect"}, {WHEN_ALWAYS | IS_LIMITED, "; move to another console"}, {WHEN_ALWAYS, "a attach read/write"}, {WHEN_ALWAYS, "b send broadcast message"}, {WHEN_ATTACH, "c toggle flow control"}, {WHEN_ATTACH, "d down a console"}, {WHEN_ALWAYS, "e change escape sequence"}, {WHEN_ALWAYS, "f force attach read/write"}, {WHEN_ALWAYS, "g group info"}, {WHEN_ALWAYS, "i information dump"}, {WHEN_ATTACH, "L toggle logging on/off"}, {WHEN_ATTACH, "l? break sequence list"}, {WHEN_ATTACH, "l0 send break per config file"}, {WHEN_ATTACH, "l1-9a-z send specific break sequence"}, {WHEN_ALWAYS, "m display message of the day"}, {WHEN_ALWAYS, "n write a note to the logfile"}, {WHEN_ALWAYS, "o (re)open the tty and log file"}, {WHEN_ALWAYS, "p playback the last %hu lines"}, {WHEN_ALWAYS, "P set number of playback lines"}, {WHEN_ALWAYS, "r replay the last %hu lines"}, {WHEN_ALWAYS, "R set number of replay lines"}, {WHEN_ATTACH, "s spy mode (read only)"}, {WHEN_ALWAYS, "u show host status"}, {WHEN_ALWAYS, "v show version info"}, {WHEN_ALWAYS, "w who is on this console"}, {WHEN_ALWAYS, "x show console baud info"}, {WHEN_ALWAYS | IS_LIMITED, "z suspend the connection"}, {WHEN_ATTACH, "! invoke task"}, {WHEN_ATTACH | IS_LIMITED, "| attach local command"}, {WHEN_ALWAYS, "? print this message"}, {WHEN_ALWAYS, " ignore/abort command"}, {WHEN_ALWAYS, "^R replay the last line"}, {WHEN_ATTACH, "\\ooo send character by octal code"}, }; /* list the commands we know for the user (ksb) */ void HelpUser(CONSCLIENT *pCL) { int i, j, iCmp; static char acH1[] = "help]\r\n", acH2[] = "help spy mode]\r\n", acEoln[] = "\r\n"; static STRING *acLine = (STRING *)0; if (acLine == (STRING *)0) acLine = AllocString(); iCmp = WHEN_ALWAYS | WHEN_SPY; if (pCL->fwr) { FileWrite(pCL->fd, FLAGTRUE, acH1, sizeof(acH1) - 1); iCmp |= WHEN_ATTACH; } else { FileWrite(pCL->fd, FLAGTRUE, acH2, sizeof(acH2) - 1); } BuildString((char *)0, acLine); for (i = 0; i < sizeof(aHLTable) / sizeof(HELP); ++i) { char *text; if (aHLTable[i].iwhen & IS_LIMITED && ConsentUserOk(pLUList, pCL->username->string) == 1) continue; if (0 == (aHLTable[i].iwhen & iCmp)) continue; text = aHLTable[i].actext; if (text[0] == 'p') { BuildTmpString((char *)0); text = BuildTmpStringPrint(text, pCL->playback); } else if (text[0] == 'r') { BuildTmpString((char *)0); text = BuildTmpStringPrint(text, pCL->replay); } if (acLine->used != 0) { /* second part of line */ if (strlen(text) < HALFLINE) { for (j = acLine->used; j <= HALFLINE; ++j) { BuildStringChar(' ', acLine); } BuildString(text, acLine); BuildString(acEoln, acLine); FileWrite(pCL->fd, FLAGTRUE, acLine->string, acLine->used - 1); BuildString((char *)0, acLine); continue; } else { BuildString(acEoln, acLine); FileWrite(pCL->fd, FLAGTRUE, acLine->string, acLine->used - 1); BuildString((char *)0, acLine); } } if (acLine->used == 0) { /* at new line */ BuildStringChar(' ', acLine); BuildString(text, acLine); if (acLine->used > HALFLINE) { BuildString(acEoln, acLine); FileWrite(pCL->fd, FLAGTRUE, acLine->string, acLine->used - 1); BuildString((char *)0, acLine); } } } if (acLine->used != 0) { BuildString(acEoln, acLine); FileWrite(pCL->fd, FLAGTRUE, acLine->string, acLine->used - 1); } FileWrite(pCL->fd, FLAGFALSE, (char *)0, 0); } int ClientAccessOk(CONSCLIENT *pCL) { char *peername = (char *)0; int retval = 1; #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS socklen_t so; int cfd; # if USE_IPV6 int error; char addr[NI_MAXHOST]; # endif SOCKADDR_STYPE in_port; int getpeer = -1; cfd = FileFDNum(pCL->fd); pCL->caccess = 'r'; # if defined(USE_LIBWRAP) { struct request_info request; CONDDEBUG((1, "ClientAccessOk(): doing tcpwrappers check")); request_init(&request, RQ_DAEMON, progname, RQ_FILE, cfd, 0); fromhost(&request); if (!hosts_access(&request)) { FileWrite(pCL->fd, FLAGFALSE, "access from your host refused\r\n", -1); retval = 0; goto setpeer; } } # endif so = sizeof(in_port); if (-1 == (getpeer = getpeername(cfd, (struct sockaddr *)&in_port, &so))) { FileWrite(pCL->fd, FLAGFALSE, "getpeername failed\r\n", -1); retval = 0; goto setpeer; } pCL->caccess = AccType( # if USE_IPV6 &in_port, # else &in_port.sin_addr, # endif &peername); if (pCL->caccess == 'r') { FileWrite(pCL->fd, FLAGFALSE, "access from your host refused\r\n", -1); retval = 0; } setpeer: #else struct in_addr addr; # if HAVE_INET_ATON inet_aton("127.0.0.1", &addr); # else addr.s_addr = inet_addr("127.0.0.1"); # endif pCL->caccess = AccType(&addr, &peername); if (pCL->caccess == 'r') { FileWrite(pCL->fd, FLAGFALSE, "access from your host refused\r\n", -1); retval = 0; } #endif if (pCL->peername != (STRING *)0) { BuildString((char *)0, pCL->peername); if (peername != (char *)0) BuildString(peername, pCL->peername); #if USE_IPV6 else if (getpeer != -1) { error = getnameinfo((struct sockaddr *)&in_port, so, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); if (error) { FileWrite(pCL->fd, FLAGFALSE, "getnameinfo failed\r\n", -1); Error("ClientAccessOk(): gatenameinfo: %s", gai_strerror(error)); retval = 0; } BuildString(addr, pCL->peername); } else BuildString("", pCL->peername); #elif USE_UNIX_DOMAIN_SOCKETS else BuildString("127.0.0.1", pCL->peername); #else else if (getpeer != -1) BuildString(inet_ntoa(in_port.sin_addr), pCL->peername); else BuildString("", pCL->peername); #endif } if (peername != (char *)0) free(peername); return retval; } conserver-8.2.4/conserver/client.h000066400000000000000000000110621344660520400171740ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* states for a server fsm */ typedef enum clientState { S_NORMAL, /* just pass character */ S_ESC1, /* first escape character received */ S_CMD, /* second interrupt character received */ S_CATTN, /* change 1 escape char to next input char */ S_CESC, /* change 2 escape char to next input char */ S_HALT1, /* we have a halt sequence in progress */ S_SUSP, /* we are suspened, first char wakes us up */ S_IDENT, /* probational connection (who is this) */ S_PASSWD, /* still needs a passwd to connect */ S_QUOTE, /* send any character we can spell */ S_BCAST, /* send a broadcast message to all clients */ S_CWAIT, /* wait for client */ S_CEXEC, /* client execing a program */ S_REPLAY, /* set replay length for 'r' */ S_PLAYBACK, /* set replay length for 'p' */ S_NOTE, /* send a note to the logfile */ S_TASK, /* invoke a task on the server side */ S_CONFIRM /* confirm input */ } CLIENTSTATE; typedef struct client { /* Connection Information: */ CONSFILE *fd; /* file descriptor */ short fcon; /* currently connect or not */ short fwr; /* (client) write enable flag */ short fwantwr; /* (client) wants to write */ short fro; /* read-only permission */ short fecho; /* echo commands (not set by machines) */ short fiwait; /* client wanting for console init */ STRING *acid; /* login and location of client */ STRING *peername; /* location of client */ STRING *username; /* login of client */ time_t tym; /* time of connect */ time_t typetym; /* time of last keystroke */ char actym[32]; /* pre-formatted time */ struct consent *pCEto; /* host a client gets output from */ struct client **ppCLbscan, /* back link for scan ptr */ *pCLscan, /* next client fd to scan after select */ /* scan lists link ALL clients together */ **ppCLbnext, /* back link for next ptr */ *pCLnext; /* next person on this list */ /* next lists link clients on a console */ char ic[2]; /* two character escape sequence */ unsigned short replay; /* lines to replay for 'r' */ unsigned short playback; /* lines to replay for 'p' */ CLIENTSTATE iState; /* state for fsm in server */ char caccess; /* did we trust the remote machine */ IOSTATE ioState; /* state of the socket */ time_t stateTimer; /* timer for various ioState states */ STRING *accmd; /* the command the user issued */ INADDR_STYPE cnct_port; /* where from */ FLAG confirmed; /* confirm state */ CLIENTSTATE cState; /* state needing confirmation */ char cOption; /* option initiating the confirmation */ } CONSCLIENT; extern void Replay(CONSENT *, CONSFILE *, unsigned short); extern void HelpUser(CONSCLIENT *); extern void FindWrite(CONSENT *); extern int ClientAccessOk(CONSCLIENT *); extern void BumpClient(CONSENT *, char *); conserver-8.2.4/conserver/consent.c000066400000000000000000001076511344660520400173740ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* * Network console modifications by Robert Olson, olson@mcs.anl.gov. */ #include #include #include #include #include #include #include #include #include BAUD baud[] = { #if defined(FOR_CYCLADES_TS) {"0", 0}, {"50", 1}, {"75", 2}, {"110", 3}, {"134", 4}, {"150", 5}, {"200", 6}, {"300", 7}, {"600", 8}, {"1200", 9}, {"1800", 10}, {"2400", 11}, {"4800", 12}, {"9600", 13}, {"14400", 14}, {"19200", 15}, {"28800", 16}, {"38400", 17}, {"57600", 18}, {"76800", 19}, {"115200", 20}, {"230400", 21}, {"460800", 22}, {"500000", 23}, {"576000", 24}, {"921600", 25}, {"1000000", 26}, {"1152000", 27}, {"1500000", 28}, {"2000000", 29}, {"2500000", 30}, {"3000000", 31}, {"3500000", 32}, {"4000000", 33}, #else /* FOR_CYCLADES_TS */ # if defined(B115200) {"115200", B115200}, # endif # if defined(B57600) {"57600", B57600}, # endif # if defined(B38400) {"38400", B38400}, # endif # if defined(B19200) {"19200", B19200}, # endif # if defined(B9600) {"9600", B9600}, # endif # if defined(B4800) {"4800", B4800}, # endif # if defined(B2400) {"2400", B2400}, # endif # if defined(B1800) {"1800", B1800}, # endif {"1200", B1200}, # if defined(B600) {"600", B600}, # endif # if defined(B300) {"300", B300}, # endif #endif /* FOR_CYCLADES_TS */ }; /* find a baud rate for the string "9600x" -> B9600 (ksb) */ BAUD * FindBaud(char *pcMode) { int i; for (i = 0; i < sizeof(baud) / sizeof(struct baud); ++i) { if (strcmp(pcMode, baud[i].acrate) == 0) return baud + i; } return (BAUD *)0; } #if !defined(PAREXT) # define PAREXT 0 #endif struct parity parity[] = { {"even", PARENB | CS7, 0}, {"mark", PARENB | CS7 | PARODD | PAREXT, 0}, {"none", CS8, 0}, {"odd", PARENB | CS7 | PARODD, 0}, {"space", PARENB | CS7 | PAREXT, 0}, }; /* find a parity "even" or "E" or "ev" -> EVEN */ PARITY * FindParity(char *pcMode) { int i; for (i = 0; i < sizeof(parity) / sizeof(struct parity); ++i) { if (strcasecmp(pcMode, parity[i].key) == 0) return parity + i; } return (PARITY *)0; } /* setup a tty device (ksb) */ static int TtyDev(CONSENT *pCE) { struct termios termp; struct stat stPerm; int cofile; cofile = FileFDNum(pCE->cofile); /* here we should fstat for `read-only' checks */ if (-1 == fstat(cofile, &stPerm)) { Error("[%s] fstat(%s(%d)): %s: forcing down", pCE->server, pCE->device, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return -1; } else if (0 == (stPerm.st_mode & 0222)) { /* any device that is read-only we won't write to */ pCE->fronly = 1; } /* * Get terminal attributes */ if (-1 == tcgetattr(cofile, &termp)) { Error("[%s] tcgetattr(%s(%d)): %s: forcing down", pCE->server, pCE->device, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return -1; } /* * Turn off: echo * icrnl * opost No post processing * icanon No line editing * isig No signal generation * Turn on: ixoff */ termp.c_iflag = BRKINT; if (pCE->ixon == FLAGTRUE) termp.c_iflag |= IXON; if (pCE->ixany == FLAGTRUE) termp.c_iflag |= IXANY; if (pCE->ixoff == FLAGTRUE) termp.c_iflag |= IXOFF; termp.c_oflag = 0; /* CLOCAL suggested by egan@us.ibm.com * carrier transitions result in dropped consoles otherwise */ termp.c_cflag = CREAD | CLOCAL; if (pCE->hupcl == FLAGTRUE) termp.c_cflag |= HUPCL; if (pCE->cstopb == FLAGTRUE) termp.c_cflag |= CSTOPB; #if defined(CRTSCTS) if (pCE->crtscts == FLAGTRUE) termp.c_cflag |= CRTSCTS; #endif termp.c_cflag |= pCE->parity->iset; termp.c_lflag = 0; /* * Set the VMIN == 1 * Set the VTIME == 1 (0.1 sec) * Don't bother with the control characters as they are not used */ termp.c_cc[VMIN] = 1; termp.c_cc[VTIME] = 1; if (-1 == cfsetospeed(&termp, pCE->baud->irate)) { Error("[%s] cfsetospeed(%s(%d)): %s: forcing down", pCE->server, pCE->device, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return -1; } if (-1 == cfsetispeed(&termp, pCE->baud->irate)) { Error("[%s] cfsetispeed(%s(%d)): %s: forcing down", pCE->server, pCE->device, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return -1; } /* * Set terminal attributes */ if (-1 == tcsetattr(cofile, TCSADRAIN, &termp)) { Error("[%s] tcsetattr(%s(%d),TCSADRAIN): %s: forcing down", pCE->server, pCE->device, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return -1; } if (fDebug >= 2) { int i; Debug(2, "TtyDev(): [%s] termp.c_iflag=%lu", pCE->server, (unsigned long)termp.c_iflag); Debug(2, "TtyDev(): [%s] termp.c_oflag=%lu", pCE->server, (unsigned long)termp.c_oflag); Debug(2, "TtyDev(): [%s] termp.c_cflag=%lu", pCE->server, (unsigned long)termp.c_cflag); Debug(2, "TtyDev(): [%s] termp.c_lflag=%lu", pCE->server, (unsigned long)termp.c_lflag); #if defined(NCCS) for (i = 0; i < NCCS; i++) { Debug(2, "TtyDev(): [%s] termp.c_cc[%d]=%lu", pCE->server, i, (unsigned long)termp.c_cc[i]); } #endif } #if HAVE_STROPTS_H /* * eat all the streams modules upto and including ttcompat */ while (ioctl(cofile, I_FIND, "ttcompat") == 1) { ioctl(cofile, I_POP, 0); } #endif pCE->fup = 1; return 0; } void StopInit(CONSENT *pCE) { if (pCE->initcmd == (char *)0) return; if (pCE->initpid != 0 || pCE->initfile != (CONSFILE *)0) SendIWaitClientsMsg(pCE, (pCE->fup && pCE->ioState == ISNORMAL) ? "up]\r\n" : "down]\r\n"); if (pCE->initpid != 0) { kill(pCE->initpid, SIGHUP); CONDDEBUG((1, "StopInit(): sending initcmd pid %lu signal %d", (unsigned long)pCE->initpid, SIGHUP)); Msg("[%s] initcmd terminated: pid %lu", pCE->server, (unsigned long)pCE->initpid); TagLogfileAct(pCE, "initcmd terminated"); pCE->initpid = 0; } if (pCE->initfile != (CONSFILE *)0) { int initfile = FileFDNum(pCE->initfile); FD_CLR(initfile, &rinit); initfile = FileFDOutNum(pCE->initfile); FD_CLR(initfile, &winit); FileClose(&pCE->initfile); pCE->initfile = (CONSFILE *)0; } } #if HAVE_FREEIPMI ipmiconsole_ctx_t IpmiSOLCreate(CONSENT *pCE) { ipmiconsole_ctx_t ctx; struct ipmiconsole_ipmi_config ipmi; struct ipmiconsole_protocol_config protocol; struct ipmiconsole_engine_config engine; if (ipmiconsole_engine_init(1, 0) < 0) return 0; ipmi.username = pCE->username; ipmi.password = pCE->password; if (pCE->ipmikg->used <= 1) { /* 1 == NULL only */ ipmi.k_g = NULL; ipmi.k_g_len = 0; } else { ipmi.k_g = (unsigned char *)pCE->ipmikg->string; ipmi.k_g_len = pCE->ipmikg->used - 1; } ipmi.privilege_level = pCE->ipmiprivlevel; ipmi.cipher_suite_id = pCE->ipmiciphersuite; ipmi.workaround_flags = pCE->ipmiworkaround; protocol.session_timeout_len = -1; protocol.retransmission_timeout_len = -1; protocol.retransmission_backoff_count = -1; protocol.keepalive_timeout_len = -1; protocol.retransmission_keepalive_timeout_len = -1; protocol.acceptable_packet_errors_count = -1; protocol.maximum_retransmission_count = -1; engine.engine_flags = IPMICONSOLE_ENGINE_OUTPUT_ON_SOL_ESTABLISHED; engine.behavior_flags = 0; engine.debug_flags = 0; ctx = ipmiconsole_ctx_create(pCE->host, &ipmi, &protocol, &engine); return ctx; } #endif /* invoke the initcmd command */ void StartInit(CONSENT *pCE) { int i; pid_t iNewGrp; extern char **environ; int pin[2]; int pout[2]; static char *apcArgv[] = { "/bin/sh", "-ce", (char *)0, (char *)0 }; if (pCE->initcmd == (char *)0) return; /* this should never happen, but hey, just in case */ if (pCE->initfile != (CONSFILE *)0 || pCE->initpid != 0) { Error("[%s] StartInit(): initpid/initfile sync error", pCE->server); StopInit(pCE); } /* pin[0] = parent read, pin[1] = child write */ if (pipe(pin) != 0) { Error("[%s] StartInit(): pipe(): %s", pCE->server, strerror(errno)); return; } /* pout[0] = child read, pout[l] = parent write */ if (pipe(pout) != 0) { close(pin[0]); close(pin[1]); Error("[%s] StartInit(): pipe(): %s", pCE->server, strerror(errno)); return; } fflush(stdout); fflush(stderr); switch (pCE->initpid = fork()) { case -1: pCE->initpid = 0; return; case 0: thepid = getpid(); break; default: close(pout[0]); close(pin[1]); if ((pCE->initfile = FileOpenPipe(pin[0], pout[1])) == (CONSFILE *)0) { Error("[%s] FileOpenPipe(%d,%d) failed: forcing down", pCE->server, pin[0], pout[1]); close(pin[0]); close(pout[1]); kill(pCE->initpid, SIGHUP); pCE->initpid = 0; return; } Msg("[%s] initcmd started: pid %lu", pCE->server, (unsigned long)pCE->initpid); TagLogfileAct(pCE, "initcmd started"); FD_SET(pin[0], &rinit); if (maxfd < pin[0] + 1) maxfd = pin[0] + 1; fflush(stderr); return; } close(pin[0]); close(pout[1]); /* put the signals back that we ignore (trapped auto-reset to default) */ SimpleSignal(SIGQUIT, SIG_DFL); SimpleSignal(SIGINT, SIG_DFL); SimpleSignal(SIGPIPE, SIG_DFL); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_DFL); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_DFL); #endif #if defined(SIGTSTP) SimpleSignal(SIGTSTP, SIG_DFL); #endif #if defined(SIGPOLL) SimpleSignal(SIGPOLL, SIG_DFL); #endif /* setup new process with clean file descriptors */ #if HAVE_CLOSEFROM for (i = 3; i <= pout[0] || i <= pin[1]; i++) { if (i != pout[0] && i != pin[1]) close(i); } closefrom(i); #else i = GetMaxFiles(); for ( /* i above */ ; --i > 2;) { if (i != pout[0] && i != pin[1]) close(i); } #endif /* leave 2 until we have to close it */ close(1); close(0); #if HAVE_SETSID iNewGrp = setsid(); if (-1 == iNewGrp) { Error("[%s] setsid(): %s", pCE->server, strerror(errno)); iNewGrp = getpid(); } #else iNewGrp = getpid(); #endif if (dup(pout[0]) != 0 || dup(pin[1]) != 1) { Error("[%s] StartInit(): fd sync error", pCE->server); exit(EX_OSERR); } close(pout[0]); close(pin[1]); if (geteuid() == 0) { if (pCE->initgid != 0) setgid(pCE->initgid); if (pCE->inituid != 0) setuid(pCE->inituid); } tcsetpgrp(0, iNewGrp); apcArgv[2] = pCE->initcmd; close(2); dup(1); /* better be 2, but it is too late now */ execve(apcArgv[0], apcArgv, environ); Error("[%s] execve(%s): %s", pCE->server, apcArgv[2], strerror(errno)); exit(EX_OSERR); return; } /* We exit() here, so only call this in a child process before an exec() */ void SetupTty(CONSENT *pCE, int fd) { struct termios n_tio; #if HAVE_STROPTS_H && !defined(_AIX) /* SYSVr4 semantics for opening stream ptys (gregf) * under PTX (others?) we have to push the compatibility * streams modules `ptem', `ld', and `ttcompat' */ ioctl(1, I_PUSH, "ptem"); ioctl(1, I_PUSH, "ldterm"); ioctl(1, I_PUSH, "ttcompat"); #endif if (0 != tcgetattr(1, &n_tio)) { exit(EX_OSERR); } n_tio.c_iflag &= ~(IGNCR | IUCLC); n_tio.c_iflag |= ICRNL; if (pCE->ixon == FLAGTRUE) n_tio.c_iflag |= IXON; if (pCE->ixany == FLAGTRUE) n_tio.c_iflag |= IXANY; if (pCE->ixoff == FLAGTRUE) n_tio.c_iflag |= IXOFF; n_tio.c_oflag &= ~(OLCUC | ONOCR | ONLRET | OFILL | NLDLY | CRDLY | TABDLY | BSDLY); n_tio.c_oflag |= OPOST | ONLCR; n_tio.c_lflag &= ~(XCASE | NOFLSH | ECHOK | ECHONL); n_tio.c_lflag |= ISIG | ICANON | ECHO; n_tio.c_cc[VEOF] = '\004'; n_tio.c_cc[VEOL] = '\000'; n_tio.c_cc[VERASE] = '\010'; n_tio.c_cc[VINTR] = '\003'; n_tio.c_cc[VKILL] = '@'; /* MIN */ n_tio.c_cc[VQUIT] = '\034'; n_tio.c_cc[VSTART] = '\021'; n_tio.c_cc[VSTOP] = '\023'; n_tio.c_cc[VSUSP] = '\032'; if (0 != tcsetattr(1, TCSANOW, &n_tio)) exit(EX_OSERR); } /* setup a virtual device (ksb) */ static int VirtDev(CONSENT *pCE) { int i; pid_t iNewGrp; extern char **environ; char *pcShell, **ppcArgv; fflush(stdout); fflush(stderr); switch (pCE->ipid = fork()) { case -1: pCE->ipid = 0; return -1; case 0: thepid = getpid(); break; default: fflush(stderr); pCE->fup = 1; return 0; } /* put the signals back that we ignore (trapped auto-reset to default) */ SimpleSignal(SIGQUIT, SIG_DFL); SimpleSignal(SIGINT, SIG_DFL); SimpleSignal(SIGPIPE, SIG_DFL); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_DFL); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_DFL); #endif #if defined(SIGTSTP) SimpleSignal(SIGTSTP, SIG_DFL); #endif #if defined(SIGPOLL) SimpleSignal(SIGPOLL, SIG_DFL); #endif /* setup new process with clean filew descriptors */ #if HAVE_CLOSEFROM for (i = 3; i < pCE->execSlaveFD; i++) close(i); i++; closefrom(i); #else i = GetMaxFiles(); for ( /* i above */ ; --i > 2;) { if (i != pCE->execSlaveFD) close(i); } #endif /* leave 2 until we *have to close it* */ close(1); close(0); #if HAVE_SETSID iNewGrp = setsid(); if (-1 == iNewGrp) { Error("[%s] setsid(): %s", pCE->server, strerror(errno)); iNewGrp = getpid(); } #else iNewGrp = getpid(); #endif if (dup(pCE->execSlaveFD) != 0 || dup(pCE->execSlaveFD) != 1) { Error("[%s] fd sync error", pCE->server); exit(EX_OSERR); } if (geteuid() == 0) { if (pCE->execgid != 0) setgid(pCE->execgid); if (pCE->execuid != 0) { fchown(0, pCE->execuid, -1); setuid(pCE->execuid); } } SetupTty(pCE, 0); tcsetpgrp(0, iNewGrp); /* if the command is null we should run root's shell, directly * if we can't find root's shell run /bin/sh */ pcShell = "/bin/sh"; if (pCE->exec == (char *)0) { static char *apcArgv[] = { "-shell", "-i", (char *)0 }; struct passwd *pwd; if ((struct passwd *)0 != (pwd = getpwuid(0)) && '\000' != pwd->pw_shell[0]) { pcShell = pwd->pw_shell; } ppcArgv = apcArgv; } else { static char *apcArgv[] = { "/bin/sh", "-ce", (char *)0, (char *)0 }; apcArgv[2] = pCE->exec; ppcArgv = apcArgv; } close(2); dup(1); /* better be 2, but it is too late now */ execve(pcShell, ppcArgv, environ); Error("[%s] execve(): %s", pCE->server, strerror(errno)); exit(EX_OSERR); return -1; } char * ConsState(CONSENT *pCE) { if (!pCE->fup) return "down"; if (pCE->initfile != (CONSFILE *)0) return "initializing"; switch (pCE->ioState) { case ISNORMAL: return "up"; case INCONNECT: return "connecting"; case ISDISCONNECTED: return "disconnected"; #if HAVE_OPENSSL case INSSLACCEPT: return "SSL_accept"; case INSSLSHUTDOWN: return "SSL_shutdown"; #endif case ISFLUSHING: return "flushing"; } return "in unknown state"; } /* down a console, virtual or real (ksb) * * this should be kept pretty simple, 'cause the config file reading code * can come along and reconfigure a console out from under this - and it * expects to be able to call ConsDown() to shut it down. so, only mess * with the "runtime" members of the structure here. */ void ConsDown(CONSENT *pCE, FLAG downHard, FLAG force) { if (force != FLAGTRUE && !(FileBufEmpty(pCE->fdlog) && FileBufEmpty(pCE->cofile) && FileBufEmpty(pCE->initfile))) { pCE->ioState = ISFLUSHING; return; } StopInit(pCE); if (pCE->ipid != 0) { CONDDEBUG((1, "ConsDown(): sending pid %lu signal %d", (unsigned long)pCE->ipid, SIGHUP)); kill(pCE->ipid, SIGHUP); pCE->ipid = 0; } if (pCE->cofile != (CONSFILE *)0) { int cofile = FileFDNum(pCE->cofile); FD_CLR(cofile, &rinit); FD_CLR(cofile, &winit); FileClose(&pCE->cofile); } #if HAVE_FREEIPMI /* need to do this after cofile close above as * ipmiconsole_ctx_destroy will close the fd */ if (pCE->ipmictx != (ipmiconsole_ctx_t) 0) { ipmiconsole_ctx_destroy(pCE->ipmictx); pCE->ipmictx = (ipmiconsole_ctx_t) 0; } #endif if (pCE->fdlog != (CONSFILE *)0) { if (pCE->nolog) { TagLogfile(pCE, "Console logging restored"); } TagLogfile(pCE, "Console down"); FD_CLR(FileFDNum(pCE->fdlog), &winit); FileClose(&pCE->fdlog); } if (pCE->type == EXEC && pCE->execSlaveFD != 0) { close(pCE->execSlaveFD); pCE->execSlaveFD = 0; } pCE->fup = 0; pCE->nolog = 0; pCE->autoReUp = 0; pCE->downHard = downHard; pCE->ioState = ISDISCONNECTED; pCE->telnetState = 0; pCE->sentDoEcho = FLAGFALSE; pCE->sentDoSGA = FLAGFALSE; } /* set up a console the way it should be for use to work with it (ksb) * also, recover from silo over runs by dropping the line and re-opening * We also maintian the select set for the caller. */ void ConsInit(CONSENT *pCE) { time_t tyme; extern int FallBack(char **, int *); int cofile = -1; int ret; #if HAVE_GETTIMEOFDAY struct timeval tv; #else time_t tv; #endif if (pCE->spintimer > 0 && pCE->spinmax > 0) { #if HAVE_GETTIMEOFDAY if (gettimeofday(&tv, (void *)0) == 0) { /* less than pCE->spintimer seconds gone by? */ if ((tv.tv_sec <= pCE->lastInit.tv_sec + pCE->spintimer - 1) || (tv.tv_sec == pCE->lastInit.tv_sec + 1 && tv.tv_usec <= pCE->lastInit.tv_usec)) { #else if ((tv = time((time_t *)0)) != (time_t)-1) { /* less than pCE->spintimer seconds gone by? (approx) */ if (tv <= pCE->lastInit + pCE->spintimer) { #endif pCE->spincount++; if (pCE->spincount >= pCE->spinmax) { pCE->spincount = 0; pCE->lastInit = tv; Error ("[%s] initialization rate exceeded: forcing down", pCE->server); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } } else pCE->spincount = 0; pCE->lastInit = tv; } else pCE->spincount = 0; } /* clean up old stuff */ if (pCE->fup) { ConsDown(pCE, FLAGFALSE, FLAGTRUE); usleep(250000); /* pause 0.25 sec to let things settle a bit */ } pCE->autoReUp = 0; pCE->fronly = 0; pCE->nolog = 0; pCE->iend = 0; /* try to open them again */ if (pCE->logfile != (char *)0) { if ((CONSFILE *)0 == (pCE->fdlog = FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, 0644))) { Error("[%s] FileOpen(%s): %s: forcing down", pCE->server, pCE->logfile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } } TagLogfile(pCE, "Console up"); switch (pCE->type) { case UNKNOWNTYPE: /* shut up gcc */ break; case NOOP: pCE->fup = 1; pCE->ioState = ISNORMAL; break; case EXEC: if ((cofile = FallBack(&pCE->execSlave, &pCE->execSlaveFD)) == -1) { Error ("[%s] failed to allocate pseudo-tty: %s: forcing down", pCE->server, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if ((pCE->cofile = FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) { Error ("[%s] FileOpenFD(%d,simpleFile) failed: forcing down", pCE->server, cofile); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } VirtDev(pCE); pCE->ioState = ISNORMAL; break; case HOST: { #if USE_IPV6 int error; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; struct addrinfo *ai, *rp, hints; #else struct sockaddr_in port; struct hostent *hp; #endif /* USE_IPV6 */ #if HAVE_SETSOCKOPT int one = 1; #endif usleep(100000); /* Not all terminal servers can keep up */ #if USE_IPV6 # if HAVE_MEMSET memset(&hints, 0, sizeof(hints)); # else bzero(&hints, sizeof(hints)); # endif hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; snprintf(serv, sizeof(serv), "%hu", pCE->netport); error = getaddrinfo(pCE->host, serv, &hints, &ai); if (error) { Error("[%s] getaddrinfo(%s): %s: forcing down", pCE->server, pCE->host, gai_strerror(error)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } rp = ai; while (rp) { error = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); if (error) continue; CONDDEBUG((1, "[%s]: trying hostname=%s, ip=%s, port=%s", pCE->server, pCE->host, host, serv)); cofile = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (cofile != -1) { # if HAVE_SETSOCKOPT if (setsockopt (cofile, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) goto fail; # endif if (!SetFlags(cofile, O_NONBLOCK, 0)) goto fail; if ((ret = connect(cofile, rp->ai_addr, rp->ai_addrlen)) == 0) goto success; fail: close(cofile); } rp = rp->ai_next; } Error("[%s]: Unable to connect to %s:%s", pCE->server, host, serv); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; success: freeaddrinfo(ai); #else # if HAVE_MEMSET memset((void *)&port, 0, sizeof(port)); # else bzero((char *)&port, sizeof(port)); # endif if ((hp = gethostbyname(pCE->host)) == NULL) { Error("[%s] gethostbyname(%s): %s: forcing down", pCE->server, pCE->host, hstrerror(h_errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } # if HAVE_MEMCPY memcpy(&port.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); # else bcopy(hp->h_addr_list[0], &port.sin_addr.s_addr, hp->h_length); # endif port.sin_family = hp->h_addrtype; port.sin_port = htons(pCE->netport); if ((cofile = socket(AF_INET, SOCK_STREAM, 0)) < 0) { Error ("[%s] socket(AF_INET,SOCK_STREAM): %s: forcing down", pCE->server, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } # if HAVE_SETSOCKOPT if (setsockopt (cofile, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) { Error ("[%s] setsockopt(%u,SO_KEEPALIVE): %s: forcing down", pCE->server, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } # endif if (!SetFlags(cofile, O_NONBLOCK, 0)) { ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } if ((ret = connect(cofile, (struct sockaddr *)&port, sizeof(port))) < 0) { if (errno != EINPROGRESS) { Error("[%s] connect(%u): %s: forcing down", pCE->server, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } } #endif /* USE_IPV6 */ } if ((pCE->cofile = FileOpenFD(cofile, simpleSocket)) == (CONSFILE *)0) { Error ("[%s] FileOpenFD(%d,simpleSocket) failed: forcing down", pCE->server, cofile); ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } if (ret == 0) { pCE->ioState = ISNORMAL; pCE->stateTimer = 0; } else { pCE->ioState = INCONNECT; pCE->stateTimer = time((time_t *)0) + CONNECTTIMEOUT; if (timers[T_STATE] == (time_t)0 || timers[T_STATE] > pCE->stateTimer) timers[T_STATE] = pCE->stateTimer; } pCE->fup = 1; break; case UDS: { struct sockaddr_un port; #if HAVE_MEMSET memset((void *)&port, 0, sizeof(port)); #else bzero((char *)&port, sizeof(port)); #endif /* we ensure that pCE->uds exists and fits inside port.sun_path * in readcfg.c, so we can just defend ourselves here (which * should never trigger). */ if (strlen(pCE->uds) >= sizeof(port.sun_path)) { Error ("[%s] strlen(uds path) > sizeof(port.sun_path): forcing down", pCE->server); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } StrCpy(port.sun_path, pCE->uds, sizeof(port.sun_path)); port.sun_family = AF_UNIX; if ((cofile = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { Error ("[%s] socket(AF_UNIX,SOCK_STREAM): %s: forcing down", pCE->server, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if (!SetFlags(cofile, O_NONBLOCK, 0)) { ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } if ((ret = connect(cofile, (struct sockaddr *)&port, sizeof(port))) < 0) { if (errno != EINPROGRESS) { Error("[%s] connect(%u): %s: forcing down", pCE->server, cofile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } } } if ((pCE->cofile = FileOpenFD(cofile, simpleSocket)) == (CONSFILE *)0) { Error ("[%s] FileOpenFD(%d,simpleSocket) failed: forcing down", pCE->server, cofile); ConsDown(pCE, FLAGTRUE, FLAGTRUE); close(cofile); return; } if (ret == 0) { pCE->ioState = ISNORMAL; pCE->stateTimer = 0; } else { pCE->ioState = INCONNECT; pCE->stateTimer = time((time_t *)0) + CONNECTTIMEOUT; if (timers[T_STATE] == (time_t)0 || timers[T_STATE] > pCE->stateTimer) timers[T_STATE] = pCE->stateTimer; } pCE->fup = 1; break; case DEVICE: if (-1 == (cofile = open(pCE->device, O_RDWR | O_NONBLOCK, 0600))) { Error("[%s] open(%s): %s: forcing down", pCE->server, pCE->device, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if ((pCE->cofile = FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) { Error ("[%s] FileOpenFD(%d,simpleFile) failed: forcing down", pCE->server, cofile); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } TtyDev(pCE); pCE->ioState = ISNORMAL; break; #if HAVE_FREEIPMI case IPMI: if (!(pCE->ipmictx = IpmiSOLCreate(pCE))) { Error("[%s] Could not create IPMI context: forcing down", pCE->server); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if (ipmiconsole_engine_submit(pCE->ipmictx, NULL, NULL) < 0) { Error ("[%s] Could not connect to IPMI host `%s': forcing down", pCE->server, pCE->host); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } cofile = ipmiconsole_ctx_fd(pCE->ipmictx); if (!SetFlags(cofile, O_NONBLOCK, 0)) { ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if ((pCE->cofile = FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) { Error("[%s] FileOpenFD(simpleFile) failed: forcing down", pCE->server); ConsDown(pCE, FLAGTRUE, FLAGTRUE); return; } if (ipmiconsole_ctx_status(pCE->ipmictx) == IPMICONSOLE_CTX_STATUS_SOL_ESTABLISHED) { /* Read in the NULL from OUTPUT_ON_SOL_ESTABLISHED flag */ char b[1]; FileRead(pCE->cofile, b, 1); /* trust it's NULL */ pCE->ioState = ISNORMAL; pCE->stateTimer = 0; } else { /* Error status cases will be handled in Kiddie() */ pCE->ioState = INCONNECT; pCE->stateTimer = time((time_t *)0) + CONNECTTIMEOUT; if (timers[T_STATE] == (time_t)0 || timers[T_STATE] > pCE->stateTimer) timers[T_STATE] = pCE->stateTimer; } pCE->fup = 1; break; #endif } if (!pCE->fup) { pCE->ioState = ISDISCONNECTED; return; } switch (pCE->type) { case UNKNOWNTYPE: /* shut up gcc */ break; case EXEC: Verbose("[%s] pid %lu on %s", pCE->server, pCE->ipid, pCE->execSlave); break; case HOST: Verbose("[%s] port %hu on %s", pCE->server, pCE->netport, pCE->host); break; #if HAVE_FREEIPMI case IPMI: Verbose("[%s] on %s", pCE->server); break; #endif case NOOP: Verbose("[%s] noop", pCE->server); break; case UDS: Verbose("[%s] uds %s", pCE->server, pCE->uds); break; case DEVICE: Verbose("[%s] at %s%c on %s", pCE->server, pCE->baud->acrate, pCE->parity->key[0], pCE->device); break; } if (cofile != -1) { /* if we're waiting for connect() to finish, watch the * write bit, otherwise watch for the read bit */ if (pCE->ioState == INCONNECT #if HAVE_FREEIPMI /* We wait for read() with the libipmiconsole */ && pCE->type != IPMI #endif ) FD_SET(cofile, &winit); else FD_SET(cofile, &rinit); if (maxfd < cofile + 1) maxfd = cofile + 1; } tyme = time((time_t *)0); if (pCE->ioState == ISNORMAL) { pCE->lastWrite = tyme; if (pCE->idletimeout != (time_t)0 && (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > pCE->lastWrite + pCE->idletimeout)) timers[T_CIDLE] = pCE->lastWrite + pCE->idletimeout; } /* If we have marks, adjust the next one so that it's in the future */ if (pCE->nextMark > 0) { if (tyme >= pCE->nextMark) { /* Add as many pCE->mark values as necessary so that we move * beyond the current time. */ pCE->nextMark += (((tyme - pCE->nextMark) / pCE->mark) + 1) * pCE->mark; } } if (pCE->downHard == FLAGTRUE) { if (pCE->ioState == ISNORMAL) { Msg("[%s] console up", pCE->server); pCE->downHard = FLAGFALSE; } else Msg("[%s] console initializing", pCE->server); } #if HAVE_GETTIMEOFDAY if (gettimeofday(&tv, (void *)0) == 0) pCE->lastInit = tv; #else if ((tv = time((time_t *)0)) != (time_t)-1) pCE->lastInit = tv; #endif if (pCE->ioState == ISNORMAL) StartInit(pCE); } int AddrsMatch(char *addr1, char *addr2) { #if USE_IPV6 int error, ret = 0; struct addrinfo *ai1, *ai2, hints; #else /* so, since we might use inet_addr, we're going to use * (in_addr_t)(-1) as a sign of an invalid ip address. * sad, but true. */ in_addr_t inAddr1 = (in_addr_t) (-1); in_addr_t inAddr2 = (in_addr_t) (-1); # if HAVE_INET_ATON struct in_addr inetAddr1; struct in_addr inetAddr2; # endif #endif /* USE_IPV6 */ /* first try simple character string match */ if (strcasecmp(addr1, addr2) == 0) return 1; #if USE_IPV6 # if HAVE_MEMSET memset(&hints, 0, sizeof(hints)); # else bzero(&hints, sizeof(hints)); # endif hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(addr1, NULL, &hints, &ai1); if (error) { Error("getaddrinfo(%s): %s", addr1, gai_strerror(error)); goto done; } error = getaddrinfo(addr2, NULL, &hints, &ai2); if (error) { Error("getaddrinfo(%s): %s", addr2, gai_strerror(error)); goto done; } for (; ai1 != NULL; ai1 = ai1->ai_next) { for (; ai2 != NULL; ai2 = ai2->ai_next) { if (ai1->ai_addr->sa_family != ai2->ai_addr->sa_family) continue; if ( # if HAVE_MEMCMP memcmp(&ai1->ai_addr, &ai2->ai_addr, sizeof(struct sockaddr_storage)) # else bcmp(&ai1->ai_addr, &ai2->ai_addr, sizeof(struct sockaddr_storage)) # endif == 0) { ret = 1; goto done; } } } done: freeaddrinfo(ai1); freeaddrinfo(ai2); Msg("compare %s and %s returns %d", addr1, addr2, ret); return ret; #else /* now try ip address match (could have leading zeros or something) */ # if HAVE_INET_ATON if (inet_aton(addr1, &inetAddr1) != 0) inAddr1 = inetAddr1.s_addr; if (inet_aton(addr2, &inetAddr2) != 0) inAddr2 = inetAddr2.s_addr; # else inAddr1 = inet_addr(addr1); inAddr2 = inet_addr(addr2); # endif /* if both are ip addresses, we just match */ if (inAddr1 != (in_addr_t) (-1) && inAddr2 != (in_addr_t) (-1)) return ! # if HAVE_MEMCMP memcmp(&inAddr1, &inAddr2, sizeof(inAddr1)) # else bcmp(&inAddr1, &inAddr2, sizeof(inAddr1)) # endif ; /* both are hostnames...this sucks 'cause we have to copy one * list and compare it to the other */ if (inAddr1 == (in_addr_t) (-1) && inAddr2 == (in_addr_t) (-1)) { struct hostent *he; int i, j, c; in_addr_t *addrs; if ((he = gethostbyname(addr1)) == (struct hostent *)0) { Error("AddrsMatch(): gethostbyname(%s): %s", addr1, hstrerror(h_errno)); return 0; } if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("AddrsMatch(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)", addr1, he->h_length, AF_INET, he->h_addrtype); return 0; } for (i = 0; he->h_addr_list[i] != (char *)0; i++); c = i; addrs = (in_addr_t *) calloc(i, sizeof(in_addr_t)); if (addrs == (in_addr_t *) 0) OutOfMem(); for (i = 0; i < c; i++) { # if HAVE_MEMCPY memcpy(&(addrs[i]), he->h_addr_list[i], he->h_length); # else bcopy(he->h_addr_list[i], &(addrs[i]), he->h_length); # endif } /* now process the second hostname */ if ((he = gethostbyname(addr2)) == (struct hostent *)0) { Error("AddrsMatch(): gethostbyname(%s): %s", addr2, hstrerror(h_errno)); free(addrs); return 0; } if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("AddrsMatch(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)", addr2, he->h_length, AF_INET, he->h_addrtype); free(addrs); return 0; } for (j = 0; he->h_addr_list[j] != (char *)0; j++) { for (i = 0; i < c; i++) { if ( # if HAVE_MEMCMP memcmp(&(addrs[i]), he->h_addr_list[j], he->h_length) # else bcmp(&(addrs[i]), he->h_addr_list[j], he->h_length) # endif == 0) { free(addrs); return 1; } } } free(addrs); } else { /* one hostname, one ip addr */ in_addr_t *iaddr; char *addr; struct hostent *he; int i; if (inAddr1 == (in_addr_t) (-1)) { addr = addr1; iaddr = &inAddr2; } else { addr = addr2; iaddr = &inAddr1; } if ((he = gethostbyname(addr)) == (struct hostent *)0) { Error("AddrsMatch(): gethostbyname(%s): %s", addr, hstrerror(h_errno)); return 0; } if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("AddrsMatch(): wrong address size (4 != %d) or address family (%d != %d)", he->h_length, AF_INET, he->h_addrtype); return 0; } for (i = 0; he->h_addr_list[i] != (char *)0; i++) { if ( # if HAVE_MEMCMP memcmp(iaddr, he->h_addr_list[i], he->h_length) # else bcmp(iaddr, he->h_addr_list[i], he->h_length) # endif == 0) return 1; } } return 0; #endif /* USE_IPV6 */ } /* thread ther list of uniq console server machines, aliases for (ksb) * machines will screw us up */ REMOTE * FindUniq(REMOTE *pRCAll) { REMOTE *pRC; /* INV: tail of the list we are building always contains only * uniq hosts, or the empty list. */ if (pRCAll == (REMOTE *)0) return (REMOTE *)0; pRCAll->pRCuniq = FindUniq(pRCAll->pRCnext); /* if it is in the returned list of uniq hosts, return that list * else add us by returning our node */ for (pRC = pRCAll->pRCuniq; (REMOTE *)0 != pRC; pRC = pRC->pRCuniq) { if (AddrsMatch(pRC->rhost, pRCAll->rhost)) return pRCAll->pRCuniq; } return pRCAll; } void DestroyRemoteConsole(REMOTE *pRCList) { NAMES *name = (NAMES *)0; if (pRCList == (REMOTE *)0) return; if (pRCList->rserver != (char *)0) free(pRCList->rserver); if (pRCList->rhost != (char *)0) free(pRCList->rhost); while (pRCList->aliases != (NAMES *)0) { name = pRCList->aliases->next; if (pRCList->aliases->name != (char *)0) free(pRCList->aliases->name); free(pRCList->aliases); pRCList->aliases = name; } free(pRCList); } conserver-8.2.4/conserver/consent.h000066400000000000000000000223261344660520400173740ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* * Network console modifications by Robert Olson, olson@mcs.anl.gov. */ /* stuff to keep track of a console entry */ typedef struct baud { /* a baud rate table */ char acrate[8]; int irate; } BAUD; typedef struct parity { /* a parity bits table */ char *key; int iset; int iclr; } PARITY; typedef enum consType { UNKNOWNTYPE = 0, DEVICE, EXEC, HOST, NOOP, UDS, #if HAVE_FREEIPMI IPMI, #endif } CONSTYPE; #if HAVE_FREEIPMI # define IPMIL_UNKNOWN (0) # define IPMIL_USER (IPMICONSOLE_PRIVILEGE_USER+1) # define IPMIL_OPERATOR (IPMICONSOLE_PRIVILEGE_OPERATOR+1) # define IPMIL_ADMIN (IPMICONSOLE_PRIVILEGE_ADMIN+1) #endif typedef struct names { char *name; struct names *next; } NAMES; typedef struct consentUsers { NAMES *user; short not; struct consentUsers *next; } CONSENTUSERS; /* we calloc() these things, so we're trying to make everything be * "empty" when it's got a zero value */ typedef struct consent { /* console information */ /*** config file settings ***/ char *server; /* server name */ CONSTYPE type; /* console type */ NAMES *aliases; /* aliases for server name */ /* type == DEVICE */ char *device; /* device file */ char *devicesubst; /* device substitution pattern */ BAUD *baud; /* the baud on this console port */ PARITY *parity; /* the parity on this line */ FLAG hupcl; /* use HUPCL */ FLAG cstopb; /* use two stop bits */ FLAG ixon; /* XON/XOFF flow control on output */ FLAG ixany; /* any character to restart output */ FLAG ixoff; /* XON/XOFF flow control on input */ #if defined(CRTSCTS) FLAG crtscts; /* use hardware flow control */ #endif #if HAVE_FREEIPMI /* type == IPMI */ int ipmiprivlevel; /* IPMI authentication level */ ipmiconsole_ctx_t ipmictx; /* IPMI ctx */ unsigned int ipmiworkaround; /* IPMI workaround flags */ short ipmiwrkset; /* workaround flags set in config */ int ipmiciphersuite; /* IPMI cipher suite */ char *username; /* Username to log as */ char *password; /* Login Password */ STRING *ipmikg; /* IPMI k_g auth key */ #endif /* type == HOST */ char *host; /* hostname */ unsigned short netport; /* final port | netport = portbase + */ unsigned short port; /* port number | portinc * port */ unsigned short portbase; /* port base */ unsigned short portinc; /* port increment */ FLAG raw; /* raw or telnet protocol? */ /* type == EXEC */ char *exec; /* exec command */ char *execsubst; /* exec substitution pattern */ uid_t execuid; /* user to run exec as */ gid_t execgid; /* group to run exec as */ /* type == UDS */ char *uds; /* socket file */ char *udssubst; /* socket file substitution pattern */ /* global stuff */ char *master; /* master hostname */ unsigned short breakNum; /* break type [1-35] */ char *logfile; /* logfile */ off_t logfilemax; /* size limit for rolling logfile */ char *initcmd; /* initcmd command */ char *initsubst; /* initcmd substitution pattern */ uid_t inituid; /* user to run initcmd as */ gid_t initgid; /* group to run initcmd as */ char *motd; /* motd */ time_t idletimeout; /* idle timeout */ char *idlestring; /* string to print when idle */ unsigned short spinmax; /* initialization spin maximum */ unsigned short spintimer; /* initialization spin timer */ char *replstring; /* generic string for replacements */ char *tasklist; /* list of valid tasks */ char *breaklist; /* list of valid break sequences */ /* timestamp stuff */ int mark; /* Mark (chime) interval */ long nextMark; /* Next mark (chime) time */ FLAG activitylog; /* log attach/detach/bump */ FLAG breaklog; /* log breaks sent */ FLAG tasklog; /* log tasks invoked */ /* options */ FLAG ondemand; /* bring up on-demand */ FLAG reinitoncc; /* open if down on client connect */ FLAG striphigh; /* strip high-bit of console data */ FLAG autoreinit; /* auto-reinitialize if failed */ FLAG unloved; /* copy "unloved" data to stdout */ FLAG login; /* allow logins to the console */ /*** runtime settings ***/ CONSFILE *fdlog; /* the local log file */ CONSFILE *cofile; /* the port to talk to machine on */ char *execSlave; /* pseudo-device slave side */ int execSlaveFD; /* fd of slave side */ pid_t ipid; /* pid of virtual command */ pid_t initpid; /* pid of initcmd command */ CONSFILE *initfile; /* the command run on init */ pid_t taskpid; /* pid of task running */ CONSFILE *taskfile; /* the output from the task (read-only) */ STRING *wbuf; /* write() buffer */ int wbufIAC; /* next IAC location in wbuf */ IOSTATE ioState; /* state of the socket */ time_t stateTimer; /* timer for ioState states */ time_t lastWrite; /* time of last data sent to console */ #if HAVE_GETTIMEOFDAY struct timeval lastInit; /* time of last initialization */ #else time_t lastInit; /* time of last initialization */ #endif unsigned short spincount; /* initialization spin counter */ /*** state information ***/ char acline[132 * 2 + 2]; /* max chars we will call a line */ int iend; /* length of data stored in acline */ int telnetState; /* state for telnet negotiations */ FLAG sentDoEcho; /* have we sent telnet DO ECHO cmd? */ FLAG sentDoSGA; /* have we sent telnet DO SGA cmd? */ unsigned short autoReUp; /* is it coming back up automatically? */ FLAG downHard; /* did it go down unexpectedly? */ unsigned short nolog; /* don't log output */ unsigned short fup; /* we setup this line? */ unsigned short fronly; /* we can only read this console */ /*** list management ***/ struct client *pCLon; /* clients on this console */ struct client *pCLwr; /* client that is writting on console */ CONSENTUSERS *rw; /* rw users */ CONSENTUSERS *ro; /* ro users */ struct consent *pCEnext; /* next console entry */ } CONSENT; typedef struct remote { /* console at another host */ struct remote *pRCnext; /* next remote console we know about */ struct remote *pRCuniq; /* list of uniq remote servers */ char *rserver; /* remote server name */ char *rhost; /* remote host to call to get it */ NAMES *aliases; /* aliases for remote server name */ } REMOTE; extern PARITY *FindParity(char *); extern BAUD *FindBaud(char *); extern void ConsInit(CONSENT *); extern void ConsDown(CONSENT *, FLAG, FLAG); extern REMOTE *FindUniq(REMOTE *); extern void DestroyRemoteConsole(REMOTE *); extern void StartInit(CONSENT *); extern void StopInit(CONSENT *); extern char *ConsState(CONSENT *); extern void SetupTty(CONSENT *, int); conserver-8.2.4/conserver/conserver.man.in000066400000000000000000000352751344660520400206710ustar00rootroot00000000000000.TH CONSERVER 8 "@CONSERVER_DATE@" "conserver-@CONSERVER_VERSION@" "conserver" .SH NAME conserver \- console server daemon .SH SYNOPSIS .B conserver .RB [ \-7dDEFhinoRSuvV ] .RB [ \-a .IR type ] .RB [ \-m .IR max ] .RB [ \-M .IR master ] .RB [ \-p .IR port ] .RB [ \-b .IR port ] .RB [ \-c .IR cred ] .RB [ \-C .IR config ] .RB [ \-P .IR passwd ] .RB [ \-L .IR logfile ] .RB [ \-O .IR min ] .RB [ \-U .IR logfile ] .SH DESCRIPTION .B Conserver is the daemon that manages remote access to system consoles by multiple users via the .BR console (1) client program and (optionally) log the console output. It can connect to consoles via local serial ports, Unix domain sockets, TCP sockets (for terminal servers and the like), or any external program. .PP When started, .B conserver reads the .BR conserver.cf (5) file for details of each console. The console type, logging options, serial or network parameters, and user access levels are just a few of the things that can be specified. Command-line options are then applied, possibly overriding .BR conserver.cf (5) settings. .B Conserver categorizes consoles into two types: those it should actively manage, and those it should just know about, so it can refer clients to other .B conserver instances. If the .B master value of a console matches the hostname or ip address of the local machine, .B conserver will actively manage the console. Otherwise, it's considered a ``remote'' console and managed by a different server. .B Conserver forks a child for each group of consoles it must manage and assigns each process a port number to listen on. The maximum number of consoles managed by each child process is set using the .B \-m option. The .BR console (1) client program communicates with the master console server process to find the port (and host, in a multi-server configuration) on which the appropriate child is listening. .B Conserver restricts connections from clients based on the host access section of its .BR conserver.cf (5) file, restricts users based on the console access lists of the .BR conserver.cf (5) file, and authenticates users against its .BR conserver.passwd (5) file. .B Conserver can also restrict clients using the tcp-wrappers package (enabled using .BR --with-libwrap ). This authentication is done before consulting the .BR conserver.cf (5) access list. .PP When Unix domain sockets are used between the client and server (enabled using .BR --with-uds ), authentication checks are done on the hardcoded address ``127.0.0.1''. Automatic client redirection is also disabled (as if the .B \-R option was used) since the client cannot communicate with remote servers. The directory used to hold the sockets is checked to make sure it's empty when the server starts. The server will .B not remove any files in the directory itself, just in case the directory is accidentally specified as ``/etc'' or some other critical location. The server will do its best to remove all the sockets when it shuts down, but it could stop ungracefully (crash, ``kill -9'', etc) and leave files behind. It would then be up to the admin (or a creative startup script) to clean up the directory before the server will start again. .PP .B Conserver completely controls any connection to a console. All escape sequences given by the user to .B console are passed to the server without interpretation. The server recognizes and processes all escape sequences. .PP The .B conserver parent process will automatically respawn any child process that dies. The following signals are propagated by the parent process to its children. .TP 10 SIGTERM Close all connections and exit. .TP SIGHUP Reread the configuration file. New consoles are managed by forking off new children, deleted consoles (and their clients) are dropped, and changes to consoles are done "in place", resetting the console port (bringing it down and up) only when necessary. The console name is used to determine when consoles have been added/removed/changed. All actions performed by SIGUSR2 are also performed. .TP SIGUSR1 Try to connect to any consoles marked as down. This can come in handy if you had a terminal server (or more) that wasn't accepting connections at startup and you want .B conserver to try to reconnect to all those downed ports. .TP SIGUSR2 Close and reopen all console logfiles and, if in daemon mode .RB ( \-d option), the error logfile (see the .BR \-L option). All actions performed by SIGUSR1 are also performed. .PP Consoles which have no current client connection might produce important error messages. With the .B \-u option, these ``unloved'' errors are labeled with a machine name and output on stdout (or, in daemon mode, to the logfile). This allows a live operator or an automated log scanner to find otherwise unseen errors by watching in a single location. .PP .B Conserver must be run as root if it is to bind to a port under 1024 or if it must read protected password files (like shadow passwords) for authentication (see .BR conserver.passwd (5)). Otherwise, it may be run by any user, with .B \-p used to specify a port above 1024. .PP If encryption has been built into the code .RB ( --with-openssl ), encrypted client connections (without certificate exchanges) happen by default. To add certificate exchanges, use the .B \-c option with the client and server. For authentication of the certificates to work, the signing certificate must be properly trusted, which usually means the public portion is in .IB OPENSSL_ROOT /ssl/certs (on both the client and server sides). See the sample self-signing certificate making script .B contrib/maketestcerts for further clues. To allow non-encrypted client connections (in addition to encrypted client connections), use the .B \-E option. .SH OPTIONS .PP Options may be given as separate arguments (e.g., .B \-n .BR \-d ) or clustered (e.g., .BR \-nd ). Options and their arguments may be separated by optional white space. Option arguments containing spaces or other characters special to the shell must be quoted. .TP 12 .B \-7 Strip the high bit off of all data received, whether from the .B console client or from the console device, before any processing occurs. .TP .BI \-a type Set the default access type for incoming connections from .B console clients: .RB ` r ' for refused (the default), .RB ` a ' for allowed, or .RB ` t ' for trusted. This applies to hosts for which no matching entry is found in the access section of .BR conserver.cf (5). .TP .BI \-b port Set the base port for children to listen on. Each child starts looking for free ports at .I port and working upward, trying a maximum number of ports equal to twice the maximum number of groups. If no free ports are available in that range, .B conserver exits. By default, .B conserver lets the operating system choose a free port. .TP .BI \-c cred Load an SSL certificate and key from the PEM encoded file .IR cred . .TP .BI \-C config Read configuration information from the file .IR config . The default .I config may be changed at compile time using the .B --with-cffile option. .TP .B \-d Become a daemon. Disconnects from the controlling terminal and sends all output (including any debug output) to the logfile (see .BR \-L ). .TP .B \-D Enable debugging output, sent to stderr. Multiple .B \-D options increases debug output. .TP .B \-E If encryption has been built into the code .RB ( --with-openssl ), encrypted client connections are a requirement. This option allows non-encrypted clients (as well as encrypted clients) to connect to consoles. .TP .B \-F Do not automatically reinitialize failed (unexpectedly closed) consoles. If the console is a program (`|' syntax) and it closes with a zero exit status, the console is reinitialized regardless of this option. Without this option, a console is immediately reopened, and if that fails, retried every minute until successful. This option has no effect on the .B \-o and .B \-O options. .TP .B \-h Output a brief help message. .TP .B \-i Initiate console connections on demand (and close them when not used). .TP .BI \-L logfile Log errors and informational messages to .I logfile after startup in daemon mode .RB ( \-d ). This option does not apply when not running in daemon mode. The default .I logfile may be changed at compile time using the .B --with-logfile option. .TP .BI \-m max Set the maximum consoles managed per process. The default .I max may be changed at compile time using the .B --with-maxmemb option. .TP .BI \-M master Normally, this allows conserver to bind to a particular IP address (like `127.0.0.1') instead of all interfaces. The default is to bind to all addresses. However, if .B --with-uds was used to enable Unix domain sockets for client/server communication, this points conserver to the directory where it should store the sockets. The default .I master directory .RB (`` /tmp/conserver '') may be changed at compile time using the .B --with-uds option. .TP .B \-n Obsolete (now a no-op); see .BR \-u . .TP .B \-o Normally, a client connecting to a ``downed'' console does just that. Using this option, the server will automatically attempt to open (``bring up'') the console when the client connects. .TP .BI \-O min Enable periodic attempts (every .I min minutes) to open (``bring up'') all downed consoles (similar to sending a SIGUSR1). Without this option, or if .I min is zero, no periodic attempts occur. .TP .BI \-p port Set the TCP port for the master process to listen on. This may be either a port number or a service name. The default .IR port , ``conserver'' (typically 782), may be changed at compile time using the .B --with-port option. If the .B --with-uds option was used, this option is ignored. .TP .BI \-P passwd Read the table of authorized user data from the file .IR passwd . The default .I passwd may be changed at compile time using the .B --with-pwdfile option. .TP .B \-R Disable automatic client redirection to other conserver hosts. This means informational commands like .B \-w and .B \-i will only show the status of the local conserver host and attempts to connect to remote consoles will result in an informative message to the user. .TP .B \-S Do not run the server, just perform a syntax check of configuration file and exit with a non-zero value if there is an error. Using more than one .B \-S will cause conserver to output various information about each console in 5 colon-separated fields, enclosed in curly-braces. The philosophy behind the output is to provide information to allow external detection of multiple consoles access the same physical port. Since this is .I highly environment-specific, conserver cannot do the check internally. .RS .TP 9 .I name The name of the console. .TP .I master The hostname of the master conserver host for the console. .TP .I aliases The console aliases in a comma-separated list. .TP .I type The type of console. Values will be a `/' for a local device, `|' for a command, `!' for a remote port, `%' for a Unix domain socket, and `#' for a noop console. .TP .I details Multiple values are comma-separated and depend on the type of the console. Local devices will have the values of the device file and baud rate/parity. Commands will have string to invoke. Remote ports will have the values of the remote hostname and port number. Unix domain sockets will have the path to the socket. Noop consoles will have nothing. .RE .TP .B \-u Send unloved console output to .BR conserver 's stdout (which, in daemon mode, is redirected to the logfile). This applies to all consoles to which no user is attached, independent of whether logging of individual consoles is enabled via .B conserver.cf entries. .TP .BI \-U logfile Copy all console data to the ``unified'' .IR logfile . The output is the same as the .B \-u output, but all consoles, not just those without a user, are logged. Each line of output is prefixed with the console name. If a user is attached read/write, a `*' is appended to the console name, to allow log watching utilities to ignore potential user-introduced alarms. .TP .B \-v Echo the configuration as it is being read (be verbose). .TP .B \-V Output the version number and settings of the .B conserver program and then exit. .SH PROTOCOL .PP The protocol used to interact with the .B conserver daemon has two basic styles. The first style is the initial line-based mode, which occurs before connecting to a console. The second style is the character-based, escape-sequence mode, while connected to a console. .PP The initial line-based mode begins the same for both the master process and its children. Upon a successful (non-rejected) client connection, an ``ok'' is sent. The client then issues a command and the server responds to it with a result string (``ok'' being the sign of success for most commands). The commands available are ``help'', ``ssl'' (if SSL was built into the code), ``login'', and ``exit''. Using the ``login'' command, the client authenticates and gains access to the extended command set. This is where the master process and its children differ. The master process gives the client access to global commands, and the child provides commands for interacting with the consoles it manages. The ``help'' command, in both cases, will provide a complete list of commands and a short description of what they do. .PP The second, character-based, style of interaction occurs when the client issues the ``call'' command with a child process. This command connects the client to a console and, at that point, relays all traffic between the client and the console. There is no more command-based interaction between the client and the server, any interaction with the server is done with the default escape sequence. .PP This is, by no means, a complete description of the entire client/server interaction. It is, however, a brief explanation in order to give a idea of what the program does. See the .B \s-1PROTOCOL\s0 file in the distribution for further details. .SH FILES .PP The following default file locations may be overridden at compile time or by the command-line options described above. Run .B conserver \-V to see the defaults set at compile time. .PP .PD 0 .TP 25 .B /etc/conserver.cf description of console terminal lines and client host access levels; see .BR conserver.cf (5). .TP .B /etc/conserver.passwd users allowed to access consoles; see .BR conserver.passwd (5). .TP .B /var/run/conserver.pid the master conserver process ID .TP .B /var/log/conserver log of errors and informational messages .TP .B /tmp/conserver directory to hold Unix domain sockets (if enabled) .PD .PP Additionally, output from individual consoles may be logged to separate files specified in .BR conserver.cf (5). .SH BUGS I'm sure there are bugs, I just don't know where they are. Please let me know if you find any. .SH AUTHORS Thomas A. Fine, Ohio State Computer Science .br Kevin S Braunsdorf, Purdue University Computing Center .br Bryan Stansell, conserver.com .SH "SEE ALSO" .BR console (1), .BR conserver.cf (5), .BR conserver.passwd (5) conserver-8.2.4/conserver/conserver.rc.in000066400000000000000000000011351344660520400205060ustar00rootroot00000000000000#!/bin/sh # # Startup for conserver # PATH=/usr/bin:/bin:/usr/local/bin PIDFILE="@PIDFILE@" signalmaster() { sig=$1 if [ -f "$PIDFILE" ]; then master=`cat "$PIDFILE"` else master=`ps -ef | grep conserver | awk '$3 == "1"{print $2}'` fi [ "$master" ] && kill -$sig $master } case "$1" in 'start') echo "Starting console server daemon" conserver -d ;; 'stop') echo "Stopping console server daemon" signalmaster TERM ;; 'restart') echo "Restarting console server daemon" signalmaster HUP ;; *) echo "Usage: $0 { start | stop | restart }" ;; esac exit 0 conserver-8.2.4/conserver/convert.c000066400000000000000000000277701344660520400174060ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ /* * Network console modifications by Robert Olson, olson@mcs.anl.gov. */ #include #include #include #include #include #include #include #include #include #if defined(USE_LIBWRAP) /* we don't use it...but we link to it */ int allow_severity; int deny_severity; #endif SECTION sections[] = { {(char *)0, (void *)0, (void *)0, (void *)0, (void *)0} }; void DestroyDataStructures() { } char * ReadLine2(FILE *fp, STRING *save, int *iLine) { static char buf[1024]; char *wholeline = (char *)0; char *ret = (char *)0; int i, buflen, peek, commentCheck = 1; static STRING *bufstr = (STRING *)0; static STRING *wholestr = (STRING *)0; if (bufstr == (STRING *)0) bufstr = AllocString(); if (wholestr == (STRING *)0) wholestr = AllocString(); peek = 0; wholeline = (char *)0; BuildString((char *)0, bufstr); BuildString((char *)0, wholestr); while (save->used || ((ret = fgets(buf, sizeof(buf), fp)) != (char *)0) || peek) { /* If we have a previously saved line, use it instead */ if (save->used) { StrCpy(buf, save->string, sizeof(buf)); BuildString((char *)0, save); } if (peek) { /* End of file? Never mind. */ if (ret == (char *)0) break; /* If we don't have a line continuation and we've seen * some worthy data */ if (!isspace((int)buf[0]) && (wholeline != (char *)0)) { BuildString((char *)0, save); BuildString(buf, save); break; } peek = 0; } if (commentCheck) { for (i = 0; buf[i] != '\000'; i++) if (!isspace((int)buf[i])) break; if (buf[i] == '#') { commentCheck = 0; } else if (buf[i] != '\000') { commentCheck = 0; } } /* Check for EOL */ buflen = strlen(buf); if ((buflen >= 1) && (buf[buflen - 1] == '\n')) { (*iLine)++; /* Finally have a whole line */ /* Finish off the chunk without the \n */ buf[buflen - 1] = '\000'; BuildString(buf, bufstr); wholeline = BuildString(bufstr->string, wholestr); peek = 1; commentCheck = 1; BuildString((char *)0, bufstr); } else { /* Save off the partial chunk */ BuildString(buf, bufstr); } } /* If we hit the EOF and weren't peeking ahead * and it's not a comment */ if (!peek && (ret == (char *)0)) { (*iLine)++; wholeline = BuildString(bufstr->string, wholestr); if (wholeline != (char *)0 && wholeline[0] == '\000') wholeline = (char *)0; } CONDDEBUG((1, "ReadLine2(): returning <%s>", (wholeline != (char *)0) ? wholeline : "")); return wholeline; } /* read in the configuration file, fill in all the structs we use (ksb) * to manage the consoles */ void ReadCfg(char *pcFile, FILE *fp) { int iLine; unsigned char *acIn; static STRING *acInSave = (STRING *)0; char *acStart; static STRING *logDirectory = (STRING *)0; static STRING *defMark = (STRING *)0; int sawACL = 0; int printedFull = 0; if (defMark == (STRING *)0) defMark = AllocString(); if (logDirectory == (STRING *)0) logDirectory = AllocString(); if (acInSave == (STRING *)0) acInSave = AllocString(); BuildString((char *)0, defMark); BuildString((char *)0, acInSave); BuildString((char *)0, logDirectory); iLine = 0; while ((acIn = (unsigned char *)ReadLine2(fp, acInSave, &iLine)) != (unsigned char *)0) { char *pcLine, *pcMode, *pcLog, *pcRem, *pcStart, *pcMark, *pcBreak; char *pcColon; acStart = PruneSpace((char *)acIn); if (acStart[0] == '#') { printf("%s\n", acStart); continue; } if (printedFull == 0) { printf("default full {\n\trw *;\n}\n"); printedFull = 1; } if ('%' == acStart[0] && '%' == acStart[1] && '\000' == acStart[2]) { break; } if ((char *)0 != (pcLine = strchr(acStart, '=')) && ((char *)0 == (pcColon = strchr(acStart, ':')) || pcColon > pcLine)) { *pcLine++ = '\000'; acStart = PruneSpace(acStart); pcLine = PruneSpace(pcLine); if (0 == strcmp(acStart, "LOGDIR")) { BuildString((char *)0, logDirectory); BuildString(pcLine, logDirectory); printf("default * {\n"); if (logDirectory->used > 1) printf("\tlogfile %s/&;\n", logDirectory->string); else printf("\tlogfile \"\";\n"); if (defMark->used > 1) printf("\ttimestamp %s;\n", defMark->string); else printf("\ttimestamp \"\";\n"); printf("\tinclude full;\n}\n"); } else if (0 == strcmp(acStart, "TIMESTAMP")) { BuildString((char *)0, defMark); BuildString(pcLine, defMark); printf("default * {\n"); if (logDirectory->used > 1) printf("\tlogfile %s/&;\n", logDirectory->string); else printf("\tlogfile \"\";\n"); if (defMark->used > 1) printf("\ttimestamp %s;\n", defMark->string); else printf("\ttimestamp \"\";\n"); printf("\tinclude full;\n}\n"); } else if (0 == strcmp(acStart, "DOMAINHACK")) { } else if (0 == strncmp(acStart, "BREAK", 5) && acStart[5] >= '1' && acStart[5] <= '9' && acStart[6] == '\000') { CONDDEBUG((1, "ReadCfg(): BREAK%c found with `%s'", acStart[5], pcLine)); if (pcLine[0] == '\000') { printf("break %c {\n\tstring \"\";\n}\n", acStart[5]); } else { char *q, *p; p = pcLine; BuildTmpString((char *)0); while ((q = strchr(p, '"')) != (char *)0) { *q = '\000'; BuildTmpString(p); BuildTmpString("\\\""); p = q + 1; *q = '"'; } q = BuildTmpString(p); printf("break %c {\n\tstring \"%s\";\n}\n", acStart[5], q); } } else { Error("%s(%d) unknown variable `%s'", pcFile, iLine, acStart); } continue; } if ((char *)0 == (pcLine = strchr(acStart, ':')) || (char *)0 == (pcMode = strchr(pcLine + 1, ':')) || (char *)0 == (pcLog = strchr(pcMode + 1, ':'))) { Error("%s(%d) bad config line `%s'", pcFile, iLine, acIn); continue; } *pcLine++ = '\000'; *pcMode++ = '\000'; *pcLog++ = '\000'; acStart = PruneSpace(acStart); pcLine = PruneSpace(pcLine); pcMode = PruneSpace(pcMode); pcLog = PruneSpace(pcLog); if ((char *)0 != (pcMark = strchr(pcLog, ':'))) { *pcMark++ = '\000'; pcLog = PruneSpace(pcLog); pcMark = PruneSpace(pcMark); /* Skip null intervals */ if (pcMark[0] == '\000') pcMark = (char *)0; } if ((char *)0 == pcMark) { pcBreak = (char *)0; } else { if ((char *)0 != (pcBreak = strchr(pcMark, ':'))) { *pcBreak++ = '\000'; pcMark = PruneSpace(pcMark); pcBreak = PruneSpace(pcBreak); /* Ignore null specs */ if (pcMark[0] == '\000') pcMark = (char *)0; if (pcBreak[0] == '\000') pcBreak = (char *)0; } } if ((char *)0 != (pcRem = strchr(pcLine, '@'))) { *pcRem++ = '\000'; pcLine = PruneSpace(pcLine); pcRem = PruneSpace(pcRem); } printf("console %s {\n", acStart); if (pcRem == (char *)0) { printf("\tmaster localhost;\n"); } else { printf("\tmaster %s;\n", pcRem); } /* * Here we substitute the console name for any '&' character in the * logfile name. That way you can just have something like * "/var/console/&" for each of the conserver.cf entries. */ if (pcLog[0] == '&' && pcLog[1] == '\000' && logDirectory->used > 1) { /* special case where logfile name is '&' and the LOGDIR was * seen above. in this case we just allow inheritance to * work it's magic. */ } else if (pcLog[0] == '\000') { printf("\tlogfile \"\";\n"); } else { STRING *lfile; lfile = AllocString(); BuildString((char *)0, lfile); pcStart = pcLog; BuildString(pcStart, lfile); if (logDirectory->used > 1 && lfile->used > 1 && lfile->string[0] != '/') { char *p; BuildTmpString((char *)0); p = BuildTmpString(lfile->string); BuildString((char *)0, lfile); BuildString(logDirectory->string, lfile); BuildStringChar('/', lfile); BuildString(p, lfile); BuildTmpString((char *)0); } printf("\tlogfile %s;\n", lfile->string); DestroyString(lfile); } if (pcMark) { printf("\ttimestamp %s;\n", pcMark); } if (pcBreak) { int bt; bt = atoi(pcBreak); if (bt > 9 || bt < 0) { Error("%s(%d) bad break spec `%d'", pcFile, iLine, bt); } else { printf("\tbreak %d;\n", bt); } } if (pcLine[0] == '!') { pcLine = PruneSpace(pcLine + 1); printf("\ttype host;\n"); printf("\thost %s;\n", pcLine); printf("\tport %s;\n", pcMode); } else if ('|' == pcLine[0]) { pcLine = PruneSpace(pcLine + 1); printf("\ttype exec;\n"); if (pcLine == (char *)0 || pcLine[0] == '\000') printf("\texec \"\";\n"); else printf("\texec %s;\n", pcLine); } else { char p, *t; printf("\ttype device;\n"); printf("\tdevice %s;\n", pcLine); t = pcMode; while (isdigit((int)(*t))) { ++t; } p = *t; *t = '\000'; printf("\tbaud %s;\n", pcMode); switch (p) { case 'E': case 'e': t = "even"; break; case 'M': case 'm': t = "mark"; break; case 'N': case 'n': case 'P': case 'p': t = "none"; break; case 'O': case 'o': t = "odd"; break; case 'S': case 's': t = "space"; break; default: Error ("%s(%d) unknown parity type `%c' - assuming `none'", pcFile, iLine, p); t = "none"; break; } printf("\tparity %s;\n", t); } printf("}\n"); } while ((acIn = (unsigned char *)ReadLine2(fp, acInSave, &iLine)) != (unsigned char *)0) { char *pcNext; acStart = PruneSpace((char *)acIn); if (acStart[0] == '#') { printf("%s\n", acStart); continue; } if ('%' == acStart[0] && '%' == acStart[1] && '\000' == acStart[2]) { break; } if ((char *)0 == (pcNext = strchr(acStart, ':'))) { Error("%s(%d) missing colon?", pcFile, iLine); continue; } do { *pcNext++ = '\000'; } while (isspace((int)(*pcNext))); switch (acStart[0]) { case 'a': /* allowed, allow, allows */ case 'A': if (!sawACL) { sawACL = 1; printf("access * {\n"); } printf("\tallowed %s;\n", pcNext); break; case 'r': /* rejected, refused, refuse */ case 'R': if (!sawACL) { sawACL = 1; printf("access * {\n"); } printf("\trejected %s;\n", pcNext); break; case 't': /* trust, trusted, trusts */ case 'T': if (!sawACL) { sawACL = 1; printf("access * {\n"); } printf("\ttrusted %s;\n", pcNext); break; default: Error("%s(%d) unknown access key `%s'", pcFile, iLine, acStart); break; } } if (sawACL) { printf("}\n"); } } int main(int argc, char **argv) { char *pcFile; FILE *fp; progname = "convert"; fDebug = 0; if (argc != 2) { Error("Usage: convert old-conserver.cf"); return 1; } pcFile = argv[1]; if ((fp = fopen(pcFile, "r")) == (FILE *)0) { Error("fopen(%s): %s", pcFile, strerror(errno)); return 1; } ReadCfg(pcFile, fp); return 0; } conserver-8.2.4/conserver/cutil.c000066400000000000000000002127611344660520400170420ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ #include #include #include #include #if USE_IPV6 # include #endif #if HAVE_SYS_SOCKIO_H # include #endif #if HAVE_OPENSSL # include #endif int fVerbose = 0, fErrorPrinted = 0; int isMultiProc = 0; char *progname = "conserver package"; pid_t thepid = 0; int fDebug = 0; STRING *allStrings = (STRING *)0; int stringCount = 0; /* count of allStrings list */ #if !USE_IPV6 struct in_addr *myAddrs = (struct in_addr *)0; #endif char myHostname[MAXHOSTNAME]; /* staff.cc.purdue.edu */ fd_set rinit; fd_set winit; int maxfd = 0; int debugLineNo = 0; char *debugFileName = (char *)0; int isMaster = 1; /* in the routines below (the init code) we can bomb if malloc fails (ksb) */ void OutOfMem(void) { static char acNoMem[] = ": out of memory\n"; write(2, progname, strlen(progname)); write(2, acNoMem, sizeof(acNoMem) - 1); exit(EX_UNAVAILABLE); } /* do a general cleanup and exit */ void Bye(int status) { DestroyDataStructures(); #if HAVE_OPENSSL # if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_free_strings(); # endif #endif exit(status); } /* This returns a string with the current time in ascii form. * (same as ctime() but without the \n) * optionally returns the time in time_t form (pass in NULL if you don't care). * It's overwritten each time, so use it and forget it. */ const char * StrTime(time_t *ltime) { static char curtime[40]; /* just in case ctime() varies */ time_t tyme; tyme = time((time_t *)0); StrCpy(curtime, ctime(&tyme), sizeof(curtime)); curtime[24] = '\000'; /* might need to adjust this at some point */ if (ltime != NULL) *ltime = tyme; return (const char *)curtime; } #define STRING_ALLOC_SIZE 64 char * BuildStringChar(const char ch, STRING *msg) { if (msg->used + 1 >= msg->allocated) { if (0 == msg->allocated) { msg->allocated = STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)calloc(1, msg->allocated); } else { msg->allocated += STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)realloc(msg->string, msg->allocated); } CONDDEBUG((3, "BuildStringChar(): 0x%lx tried allocating %lu bytes", (void *)msg, msg->allocated)); if (msg->string == (char *)0) OutOfMem(); } if (msg->used) { msg->string[msg->used - 1] = ch; /* overwrite NULL and */ msg->string[msg->used++] = '\000'; /* increment by one */ CONDDEBUG((3, "BuildStringChar(): 0x%lx added 1 char (%d/%d now)", (void *)msg, msg->used, msg->allocated)); } else { msg->string[msg->used++] = ch; /* no NULL, so store stuff */ msg->string[msg->used++] = '\000'; /* and increment by two */ CONDDEBUG((3, "BuildStringChar(): 0x%lx added 2 chars (%d/%d now)", (void *)msg, msg->used, msg->allocated)); } return msg->string; } char * BuildString(const char *str, STRING *msg) { int len; if ((char *)0 == str) { msg->used = 0; if (msg->string != (char *)0) msg->string[0] = '\000'; CONDDEBUG((3, "BuildString(): 0x%lx reset", (void *)msg)); return msg->string; } if (msg->used) /* string or string + null? */ len = strlen(str); else len = strlen(str) + 1; if (msg->used + len >= msg->allocated) { if (0 == msg->allocated) { msg->allocated = (len / STRING_ALLOC_SIZE + 1) * STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)calloc(1, msg->allocated); } else { msg->allocated += ((msg->used + len - msg->allocated) / STRING_ALLOC_SIZE + 1) * STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)realloc(msg->string, msg->allocated); } CONDDEBUG((3, "BuildString(): 0x%lx tried allocating %lu bytes", (void *)msg, msg->allocated)); if (msg->string == (char *)0) OutOfMem(); } /* if msg->used, then len = strlen(), so we need to copy len + 1 to * get the NULL which we overwrote with the copy */ #if HAVE_MEMCPY if (msg->used) memcpy(msg->string + msg->used - 1, str, len + 1); else memcpy(msg->string, str, len); #else if (msg->used) bcopy(str, msg->string + msg->used - 1, len + 1); else bcopy(str, msg->string, len); #endif msg->used += len; CONDDEBUG((3, "BuildString(): 0x%lx added %d chars (%d/%d now)", (void *)msg, len, msg->used, msg->allocated)); return msg->string; } char * BuildStringN(const char *str, int n, STRING *msg) { int len; if ((char *)0 == str) { msg->used = 0; if (msg->string != (char *)0) msg->string[0] = '\000'; CONDDEBUG((3, "BuildStringN(): 0x%lx reset", (void *)msg)); return msg->string; } if (n <= 0) return msg->string; if (msg->used) len = n; else len = n + 1; if (msg->used + len >= msg->allocated) { if (0 == msg->allocated) { msg->allocated = (len / STRING_ALLOC_SIZE + 1) * STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)calloc(1, msg->allocated); } else { msg->allocated += ((msg->used + len - msg->allocated) / STRING_ALLOC_SIZE + 1) * STRING_ALLOC_SIZE * sizeof(char); msg->string = (char *)realloc(msg->string, msg->allocated); } CONDDEBUG((3, "BuildStringN(): 0x%lx tried allocating %lu bytes", (void *)msg, msg->allocated)); if (msg->string == (char *)0) OutOfMem(); } #if HAVE_MEMCPY memcpy(msg->string + (msg->used ? msg->used - 1 : 0), str, n); #else bcopy(str, msg->string + (msg->used ? msg->used - 1 : 0), n); #endif /* add a NULL */ msg->string[(msg->used ? msg->used - 1 : 0) + n] = '\000'; msg->used += len; CONDDEBUG((3, "BuildStringN(): 0x%lx added %d chars (%d/%d now)", (void *)msg, len, msg->used, msg->allocated)); return msg->string; } void * MemMove(void *dest, void *src, size_t n) { #if HAVE_MEMMOVE return memmove(dest, src, n); #else char *s = src; char *d = dest; if (s < d) { /* Moving from low mem to hi mem; start at end. */ for (s += n, d += n; n > 0; --n) *--d = *--s; } else if (s != d) { /* Moving from hi mem to low mem; start at beginning. */ for (; n > 0; --n) *d++ = *s++; } return dest; #endif } char * ShiftString(STRING *msg, int n) { if (msg == (STRING *)0 || n <= 0 || n > msg->used - 1) return (char *)0; MemMove(msg->string, msg->string + n, msg->used - n); msg->used -= n; return msg->string; } void InitString(STRING *msg) { msg->string = (char *)0; msg->used = msg->allocated = 0; } void DestroyString(STRING *msg) { if (msg->prev == (STRING *)0 && msg->next == (STRING *)0 && allStrings != msg) { CONDDEBUG((1, "DestroyString(): 0x%lx non-pooled string destroyed", (void *)msg, stringCount)); } else { if (msg->prev != (STRING *)0) msg->prev->next = msg->next; if (msg->next != (STRING *)0) msg->next->prev = msg->prev; if (msg == allStrings) { allStrings = msg->next; } stringCount--; CONDDEBUG((1, "DestroyString(): 0x%lx string destroyed (count==%d)", (void *)msg, stringCount)); } if (msg->allocated) free(msg->string); free(msg); } STRING * AllocString(void) { STRING *s; if ((s = (STRING *)calloc(1, sizeof(STRING))) == (STRING *)0) OutOfMem(); if (allStrings != (STRING *)0) { allStrings->prev = s; s->next = allStrings; } allStrings = s; InitString(s); stringCount++; CONDDEBUG((1, "AllocString(): 0x%lx created string #%d", (void *)s, stringCount)); return s; } void DestroyStrings(void) { while (allStrings != (STRING *)0) { DestroyString(allStrings); } } static STRING *mymsg = (STRING *)0; char * BuildTmpString(const char *str) { if (mymsg == (STRING *)0) mymsg = AllocString(); return BuildString(str, mymsg); } char * BuildTmpStringChar(const char c) { if (mymsg == (STRING *)0) mymsg = AllocString(); return BuildStringChar(c, mymsg); } char * ReadLine(FILE *fp, STRING *save, int *iLine) { static char buf[1024]; char *wholeline = (char *)0; char *ret = (char *)0; int i, buflen, peek, commentCheck = 1, comment = 0; static STRING *bufstr = (STRING *)0; static STRING *wholestr = (STRING *)0; if (bufstr == (STRING *)0) bufstr = AllocString(); if (wholestr == (STRING *)0) wholestr = AllocString(); peek = 0; wholeline = (char *)0; BuildString((char *)0, bufstr); BuildString((char *)0, wholestr); while (save->used || ((ret = fgets(buf, sizeof(buf), fp)) != (char *)0) || peek) { /* If we have a previously saved line, use it instead */ if (save->used) { StrCpy(buf, save->string, sizeof(buf)); BuildString((char *)0, save); } if (peek) { /* End of file? Never mind. */ if (ret == (char *)0) break; /* If we don't have a line continuation and we've seen * some worthy data */ if (!isspace((int)buf[0]) && (wholeline != (char *)0)) { BuildString((char *)0, save); BuildString(buf, save); break; } peek = 0; } if (commentCheck) { for (i = 0; buf[i] != '\000'; i++) if (!isspace((int)buf[i])) break; if (buf[i] == '#') { comment = 1; commentCheck = 0; } else if (buf[i] != '\000') { commentCheck = 0; } } /* Check for EOL */ buflen = strlen(buf); if ((buflen >= 1) && (buf[buflen - 1] == '\n')) { (*iLine)++; /* Finally have a whole line */ if (comment == 0 && commentCheck == 0) { /* Finish off the chunk without the \n */ buf[buflen - 1] = '\000'; BuildString(buf, bufstr); wholeline = BuildString(bufstr->string, wholestr); } peek = 1; comment = 0; commentCheck = 1; BuildString((char *)0, bufstr); } else { /* Save off the partial chunk */ BuildString(buf, bufstr); } } /* If we hit the EOF and weren't peeking ahead * and it's not a comment */ if (!peek && (ret == (char *)0) && (comment == 0) && (commentCheck == 0)) { (*iLine)++; wholeline = BuildString(bufstr->string, wholestr); } CONDDEBUG((1, "ReadLine(): returning <%s>", (wholeline != (char *)0) ? wholeline : "")); return wholeline; } /* show a character as a string so the user cannot mistake it for (ksb) * another */ char * FmtCtl(int ci, STRING *pcIn) { unsigned char c; BuildString((char *)0, pcIn); c = ci & 0xff; if (c > 127) { c -= 128; BuildString("M-", pcIn); } if (c < ' ' || c == '\177') { BuildStringChar('^', pcIn); BuildStringChar(c ^ 0100, pcIn); } else if (c == ' ') { BuildString("", pcIn); } else if (c == '^') { BuildString("", pcIn); } else if (c == '\\') { BuildString("", pcIn); } else { BuildStringChar(c, pcIn); } return pcIn->string; } void FmtCtlStr(char *pcIn, int len, STRING *pcOut) { unsigned char c; BuildString((char *)0, pcOut); if (pcIn == (char *)0) return; if (len < 0) len = strlen(pcIn); for (; len; len--, pcIn++) { c = *pcIn & 0xff; if (c > 127) { c -= 128; BuildString("M-", pcOut); } if (c < ' ' || c == '\177') { BuildStringChar('^', pcOut); BuildStringChar(c ^ 0100, pcOut); } else { BuildStringChar(c, pcOut); } } } void Debug(int level, char *fmt, ...) { va_list ap; if (fDebug < level) return; va_start(ap, fmt); if (isMultiProc) fprintf(stderr, "[%s] %s (%lu): DEBUG: [%s:%d] ", StrTime((time_t *)0), progname, (unsigned long)thepid, debugFileName, debugLineNo); else fprintf(stderr, "%s: DEBUG: [%s:%d] ", progname, debugFileName, debugLineNo); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } void Error(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (isMultiProc) fprintf(stderr, "[%s] %s (%lu): ERROR: ", StrTime((time_t *)0), progname, (unsigned long)thepid); else fprintf(stderr, "%s: ", progname); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); fErrorPrinted = 1; } void Msg(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (isMultiProc) fprintf(stdout, "[%s] %s (%lu): ", StrTime((time_t *)0), progname, (unsigned long)thepid); else fprintf(stdout, "%s: ", progname); vfprintf(stdout, fmt, ap); fprintf(stdout, "\n"); va_end(ap); } void Verbose(char *fmt, ...) { va_list ap; if (!fVerbose) return; va_start(ap, fmt); if (isMultiProc) fprintf(stdout, "[%s] %s (%lu): INFO: ", StrTime((time_t *)0), progname, (unsigned long)thepid); else fprintf(stdout, "%s: ", progname); vfprintf(stdout, fmt, ap); fprintf(stdout, "\n"); va_end(ap); } void SimpleSignal(int sig, RETSIGTYPE(*disp) (int)) { #if HAVE_SIGACTION struct sigaction sa; sa.sa_handler = disp; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sigaction(sig, &sa, NULL); #else signal(sig, disp); #endif } int GetMaxFiles(void) { int mf; #if HAVE_SYSCONF mf = sysconf(_SC_OPEN_MAX); #else # if HAVE_GETRLIMIT struct rlimit rl; getrlimit(RLIMIT_NOFILE, &rl); mf = rl.rlim_cur; # else # if HAVE_GETDTABLESIZE mf = getdtablesize(); # else # ifndef OPEN_MAX # define OPEN_MAX 64 # endif /* !OPEN_MAX */ mf = OPEN_MAX; # endif /* HAVE_GETDTABLESIZE */ # endif/* HAVE_GETRLIMIT */ #endif /* HAVE_SYSCONF */ #ifdef FD_SETSIZE if (FD_SETSIZE <= mf) { mf = (FD_SETSIZE - 1); } #endif CONDDEBUG((1, "GetMaxFiles(): maxfiles=%d", mf)); return mf; } /* Routines for the generic I/O stuff for conserver. This will handle * all open(), close(), read(), and write() calls. */ /* This encapsulates a regular file descriptor in a CONSFILE * object. Returns a CONSFILE pointer to that object. */ CONSFILE * FileOpenFD(int fd, enum consFileType type) { CONSFILE *cfp; if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE))) == (CONSFILE *)0) OutOfMem(); cfp->ftype = type; cfp->fd = fd; cfp->wbuf = AllocString(); #if HAVE_OPENSSL cfp->ssl = (SSL *)0; cfp->waitForRead = cfp->waitForWrite = FLAGFALSE; #endif #if DEBUG_CONSFILE_IO { char buf[1024]; sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname, (unsigned long)thepid, fd); if ((cfp->debugwfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugwfd, buf, strlen(buf)); } sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname, (unsigned long)thepid, fd); if ((cfp->debugrfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugrfd, buf, strlen(buf)); } } #endif CONDDEBUG((2, "FileOpenFD(): encapsulated fd %d type %d", fd, type)); return cfp; } /* This encapsulates a pipe pair in a CONSFILE * object. Returns a CONSFILE pointer to that object. */ CONSFILE * FileOpenPipe(int fd, int fdout) { CONSFILE *cfp; if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE))) == (CONSFILE *)0) OutOfMem(); cfp->ftype = simplePipe; cfp->fd = fd; cfp->fdout = fdout; cfp->wbuf = AllocString(); #if HAVE_OPENSSL cfp->ssl = (SSL *)0; cfp->waitForRead = cfp->waitForWrite = FLAGFALSE; #endif #if DEBUG_CONSFILE_IO { char buf[1024]; sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname, (unsigned long)thepid, fdout); if ((cfp->debugwfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugwfd, buf, strlen(buf)); } sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname, (unsigned long)thepid, fd); if ((cfp->debugrfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugrfd, buf, strlen(buf)); } } #endif CONDDEBUG((2, "FileOpenPipe(): encapsulated pipe pair fd %d and fd %d", fd, fdout)); return cfp; } /* This is to "unencapsulate" the file descriptor */ int FileUnopen(CONSFILE *cfp) { int retval = 0; if (cfp == (CONSFILE *)0) return 0; switch (cfp->ftype) { case simpleFile: retval = cfp->fd; break; case simplePipe: retval = cfp->fd; break; case simpleSocket: retval = cfp->fd; break; #if HAVE_OPENSSL case SSLSocket: retval = -1; break; #endif default: retval = -1; break; } CONDDEBUG((2, "FileUnopen(): unopened fd %d", cfp->fd)); DestroyString(cfp->wbuf); #if DEBUG_CONSFILE_IO if (cfp->debugwfd != -1) close(cfp->debugwfd); if (cfp->debugrfd != -1) close(cfp->debugrfd); #endif free(cfp); return retval; } /* This opens a file like open(2). Returns a CONSFILE pointer * or a (CONSFILE *)0 on error */ CONSFILE * FileOpen(const char *path, int flag, int mode) { CONSFILE *cfp; int fd; if (-1 == (fd = open(path, flag, mode))) { CONDDEBUG((2, "FileOpen(): failed to open `%s'", path)); return (CONSFILE *)0; } if ((cfp = (CONSFILE *)calloc(1, sizeof(CONSFILE))) == (CONSFILE *)0) OutOfMem(); cfp->ftype = simpleFile; cfp->fd = fd; cfp->wbuf = AllocString(); #if HAVE_OPENSSL cfp->ssl = (SSL *)0; cfp->waitForRead = cfp->waitForWrite = FLAGFALSE; #endif #if DEBUG_CONSFILE_IO { char buf[1024]; sprintf(buf, "CONSFILE-%s-%lu-%d.w", progname, (unsigned long)thepid, fd); if ((cfp->debugwfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugwfd, buf, strlen(buf)); } sprintf(buf, "CONSFILE-%s-%lu-%d.r", progname, (unsigned long)thepid, fd); if ((cfp->debugrfd = open(buf, O_WRONLY | O_CREAT | O_APPEND, 0644)) != -1) { sprintf(buf, "[---- STARTED - %s ----]\n", StrTime((time_t *)0)); write(cfp->debugrfd, buf, strlen(buf)); } } #endif CONDDEBUG((2, "FileOpen(): opened `%s' as fd %d", path, fd)); return cfp; } /* Unless otherwise stated, returns the same values as close(2). * The CONSFILE object passed in *CANNOT* be used once calling * this function - even if there was an error. */ int FileClose(CONSFILE **pcfp) { CONSFILE *cfp; int retval = 0; #if defined(__CYGWIN__) struct linger lingeropt; #endif cfp = *pcfp; if (cfp == (CONSFILE *)0) return 0; switch (cfp->ftype) { case simpleFile: do { retval = close(cfp->fd); } while (retval == -1 && errno == EINTR); break; case simplePipe: do { retval = close(cfp->fd); } while (retval == -1 && errno == EINTR); do { retval = close(cfp->fdout); } while (retval == -1 && errno == EINTR); break; case simpleSocket: #if defined(__CYGWIN__) /* flush out the client socket - set it to blocking, * then write to it */ SetFlags(cfp->fd, 0, O_NONBLOCK); /* sent it a byte - guaranteed to block - ensure delivery * of prior data yeah - this is a bit paranoid - try * without this at first */ /* write(cfp->fd, "\n", 1); */ /* this is the guts of the workaround for Winsock close bug */ shutdown(cfp->fd, 1); /* enable lingering */ lingeropt.l_onoff = 1; lingeropt.l_linger = 15; setsockopt(cfp->fd, SOL_SOCKET, SO_LINGER, &lingeropt, sizeof(lingeropt)); #endif do { retval = close(cfp->fd); } while (retval == -1 && errno == EINTR); break; #if HAVE_OPENSSL case SSLSocket: CONDDEBUG((2, "FileClose(): performing a SSL_shutdown() on fd %d", cfp->fd)); SSL_shutdown(cfp->ssl); CONDDEBUG((2, "FileClose(): performing a SSL_free() on fd %d", cfp->fd)); SSL_free(cfp->ssl); /* set the sucker back to a simpleSocket and recall so we * do all that special stuff we oh so love...and make sure * we return so we don't try and free(0). -bryan */ cfp->ftype = simpleSocket; return FileClose(pcfp); #endif default: retval = -1; break; } if (cfp->ftype == simplePipe) { CONDDEBUG((2, "FileClose(): closed fd %d/%d", cfp->fd, cfp->fdout)); } else { CONDDEBUG((2, "FileClose(): closed fd %d", cfp->fd)); } DestroyString(cfp->wbuf); #if DEBUG_CONSFILE_IO if (cfp->debugwfd != -1) close(cfp->debugwfd); if (cfp->debugrfd != -1) close(cfp->debugrfd); #endif free(cfp); *pcfp = (CONSFILE *)0; return retval; } /* returns: -1 on error or eof, >= 0 for valid reads */ int FileRead(CONSFILE *cfp, void *buf, int len) { int retval = -1; if (cfp->errored == FLAGTRUE) return -1; switch (cfp->ftype) { case simpleFile: case simplePipe: case simpleSocket: while (retval < 0) { if ((retval = read(cfp->fd, buf, len)) <= 0) { if (retval == 0) { retval = -1; break; } if (errno == EINTR) continue; if (errno == EAGAIN) { /* must be non-blocking - so stop */ retval = 0; break; } Error("FileRead(): fd %d: %s", cfp->fd, strerror(errno)); retval = -1; break; } #if DEBUG_CONSFILE_IO if (cfp->debugrfd != -1) write(cfp->debugrfd, buf, retval); #endif } break; #if HAVE_OPENSSL case SSLSocket: if (cfp->waitForWrite == FLAGTRUE) { cfp->waitForWrite = FLAGFALSE; if (cfp->wbuf->used <= 1) FD_CLR(cfp->fd, &winit); } retval = SSL_read(cfp->ssl, buf, len); switch (SSL_get_error(cfp->ssl, retval)) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: retval = 0; break; case SSL_ERROR_WANT_WRITE: cfp->waitForWrite = FLAGTRUE; FD_SET(cfp->fd, &winit); retval = 0; break; default: Error("FileRead(): SSL error on fd %d", cfp->fd); /* fall through */ case SSL_ERROR_ZERO_RETURN: retval = -1; CONDDEBUG((2, "FileRead(): performing a SSL_shutdown() on fd %d", cfp->fd)); SSL_shutdown(cfp->ssl); CONDDEBUG((2, "FileRead(): performing a SSL_free() on fd %d", cfp->fd)); SSL_free(cfp->ssl); cfp->ssl = (SSL *)0; cfp->ftype = simpleSocket; break; } # if DEBUG_CONSFILE_IO if (cfp->debugrfd != -1) write(cfp->debugrfd, buf, retval); # endif break; #endif default: retval = -1; break; } if (retval >= 0) { CONDDEBUG((2, "FileRead(): read %d byte%s from fd %d", retval, (retval == 1) ? "" : "s", cfp->fd)); if (fDebug && buf != (char *)0) { static STRING *tmpString = (STRING *)0; if (tmpString == (STRING *)0) tmpString = AllocString(); BuildString((char *)0, tmpString); if (retval > 30) { FmtCtlStr(buf, 30, tmpString); CONDDEBUG((2, "FileRead(): read `%s'... from fd %d", tmpString->string, cfp->fd)); } else { FmtCtlStr(buf, retval, tmpString); CONDDEBUG((2, "FileRead(): read `%s' from fd %d", tmpString->string, cfp->fd)); } } } else { CONDDEBUG((2, "FileRead(): failed attempted read of %d byte%s from fd %d", len, (len == 1) ? "" : "s", cfp->fd)); } if (retval < 0) cfp->errored = FLAGTRUE; return retval; } /* returns: -1 on error or eof, >= 0 for valid reads */ int FileWrite(CONSFILE *cfp, FLAG bufferonly, char *buf, int len) { int len_orig = len; int len_out = 0; int retval = 0; int fdout = 0; if (cfp->ftype == simplePipe) fdout = cfp->fdout; else fdout = cfp->fd; if (cfp->errored == FLAGTRUE) { if (cfp->wbuf->used > 1) BuildString((char *)0, cfp->wbuf); FD_CLR(fdout, &winit); return -1; } if (len < 0 && buf != (char *)0) len = strlen(buf); if (fDebug && len > 0 && buf != (char *)0) { static STRING *tmpString = (STRING *)0; if (tmpString == (STRING *)0) tmpString = AllocString(); BuildString((char *)0, tmpString); if (len > 30) { FmtCtlStr(buf, 30, tmpString); CONDDEBUG((2, "FileWrite(): sending `%s'... to fd %d", tmpString->string, fdout)); } else { FmtCtlStr(buf, len, tmpString); CONDDEBUG((2, "FileWrite(): sending `%s' to fd %d", tmpString->string, fdout)); } } /* save the data */ if (len > 0 && buf != (char *)0) { if (cfp->quoteiac == FLAGTRUE) { int l, o; for (o = l = 0; l < len; l++) { if (buf[l] == (char)OB_IAC) { BuildStringN(buf + o, l + 1 - o, cfp->wbuf); BuildStringChar((char)OB_IAC, cfp->wbuf); o = l + 1; } } if (o < len) BuildStringN(buf + o, len - o, cfp->wbuf); } else BuildStringN(buf, len, cfp->wbuf); } if (bufferonly == FLAGTRUE) return 0; /* point at the local data */ buf = cfp->wbuf->string; len = cfp->wbuf->used - 1; /* if we don't have any, forget it */ if (buf == (char *)0 || len <= 0) return 0; /* so, we could be blocking or non-blocking. since we may be able * to block, we'll just keep trying to write while we have data and * stop when we hit an error or flush all the data. */ switch (cfp->ftype) { case simplePipe: case simpleFile: case simpleSocket: while (len > 0) { if ((retval = write(fdout, buf, len)) < 0) { if (errno == EINTR) continue; if (errno == EAGAIN) { /* must be non-blocking - so stop */ retval = 0; break; } retval = -1; /* i believe, as of 8.0.8, we need to just ignore * this and actually produce the error message * below. perhaps we'll have a lot of extra * FileWrite() errors, perhaps not. things shouldn't * just close down and cause errors in normal cases, * right?!? -bryan * maybe not right now, actually. i'm going to check * the return code of FileWrite() on the "important" * things and let the others silently fail and have * the FileRead() catch problems - like it has been * doing. i really should be checking all the return * codes...and i'm sure i'll get there eventually. */ if (errno == EPIPE) break; Error("FileWrite(): fd %d: %s", fdout, strerror(errno)); break; } #if DEBUG_CONSFILE_IO if (cfp->debugwfd != -1) write(cfp->debugwfd, buf, retval); #endif buf += retval; len -= retval; len_out += retval; } break; #if HAVE_OPENSSL case SSLSocket: if (cfp->waitForRead == FLAGTRUE) cfp->waitForRead = FLAGFALSE; while (len > 0) { /* in theory, SSL_write always returns 'len' on success * so the while() loop is a noop. but, just in case i * read something wrong, we treat SSL_write like write(). */ retval = SSL_write(cfp->ssl, buf, len); switch (SSL_get_error(cfp->ssl, retval)) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: cfp->waitForRead = FLAGTRUE; retval = len_out = 0; break; case SSL_ERROR_WANT_WRITE: retval = len_out = 0; break; default: Error("FileWrite(): SSL error on fd %d", cfp->fd); /* fall through */ case SSL_ERROR_ZERO_RETURN: retval = -1; CONDDEBUG((2, "FileWrite(): performing a SSL_shutdown() on fd %d", cfp->fd)); SSL_shutdown(cfp->ssl); CONDDEBUG((2, "FileWrite(): performing a SSL_free() on fd %d", cfp->fd)); SSL_free(cfp->ssl); cfp->ssl = (SSL *)0; cfp->ftype = simpleSocket; break; } if (retval <= 0) break; # if DEBUG_CONSFILE_IO if (cfp->debugwfd != -1) write(cfp->debugwfd, buf, retval); # endif buf += retval; len -= retval; len_out += retval; } break; #endif default: retval = -1; break; } /* so, if we saw an error, just bail...all is done anyway */ if (retval >= 0) { if (len_out > 0) { /* save the rest for later */ if (len > 0) { ShiftString(cfp->wbuf, len_out); } else { BuildString((char *)0, cfp->wbuf); } } retval = len_out; } if (retval < 0) { if (cfp->wbuf->used > 1) BuildString((char *)0, cfp->wbuf); cfp->errored = FLAGTRUE; } if (cfp->wbuf->used <= 1) FD_CLR(fdout, &winit); else { FD_SET(fdout, &winit); CONDDEBUG((2, "FileWrite(): buffered %d byte%s for fd %d", (cfp->wbuf->used <= 1) ? 0 : cfp->wbuf->used, (cfp->wbuf->used <= 1) ? "" : "s", fdout)); } if (retval >= 0) { CONDDEBUG((2, "FileWrite(): wrote %d byte%s to fd %d", retval, (retval == 1) ? "" : "s", fdout)); } else { CONDDEBUG((2, "FileWrite(): write of %d byte%s to fd %d: %s", len_orig, (retval == -1) ? "" : "s", fdout, strerror(errno))); } return retval; } int FileCanRead(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd) { #if HAVE_OPENSSL int fdout; #endif if (cfp == (CONSFILE *)0) return 0; #if HAVE_OPENSSL if (cfp->ftype == simplePipe) fdout = cfp->fdout; else fdout = cfp->fd; #endif return ((FD_ISSET(cfp->fd, prfd) #if HAVE_OPENSSL && cfp->waitForRead != FLAGTRUE) || (fdout >= 0 && FD_ISSET(fdout, pwfd) && cfp->waitForWrite == FLAGTRUE #endif )); } int FileCanWrite(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd) { int fdout; if (cfp == (CONSFILE *)0) return 0; if (cfp->ftype == simplePipe) fdout = cfp->fdout; else fdout = cfp->fd; return ((FD_ISSET(fdout, pwfd) #if HAVE_OPENSSL && cfp->waitForWrite != FLAGTRUE) || (FD_ISSET(cfp->fd, prfd) && cfp->waitForRead == FLAGTRUE #endif )); } int FileBufEmpty(CONSFILE *cfp) { if (cfp == (CONSFILE *)0) return 1; return (cfp->wbuf->used <= 1); } void VWrite(CONSFILE *cfp, FLAG bufferonly, STRING *str, char *fmt, va_list ap) { int s, l, e; char c; int fmtlen = 0; int fmtpre = 0; short padzero = 0; short sawdot = 0; static STRING *msg = (STRING *)0; static STRING *output = (STRING *)0; short flong = 0, fneg = 0, fminus = 0; if (fmt == (char *)0 || (cfp == (CONSFILE *)0 && str == (STRING *)0)) return; if (msg == (STRING *)0) msg = AllocString(); if (output == (STRING *)0) output = AllocString(); BuildString((char *)0, output); for (e = s = l = 0; (c = fmt[s + l]) != '\000'; l++) { if (e == 0 && c == '%') { e = 1; BuildStringN(fmt + s, l, output); s += l; l = 0; continue; } if (e) { unsigned long i; int u; char *p; char cc; if (c >= '0' && c <= '9') { if (sawdot == 0) { if (c == '0' && fmtlen == 0) padzero = 1; fmtlen = fmtlen * 10 + (c - '0'); } else { fmtpre = fmtpre * 10 + (c - '0'); } } else { switch (c) { case '.': sawdot = 1; continue; case '-': fminus = 1; continue; case 'h': /* noop since shorts are promoted to int in va_arg */ continue; case 'l': flong = 1; continue; case '%': BuildStringChar('%', output); break; case 'c': cc = (char)va_arg(ap, int); BuildStringChar(cc, output); break; case 's': p = va_arg(ap, char *); if (p == (char *)0) p = "(null)"; { int l = strlen(p); int c; if (fmtpre > 0 && fmtpre < l) l = fmtpre; if (fminus != 0) BuildStringN(p, l, output); for (c = l; c < fmtlen; c++) BuildStringChar(' ', output); if (fminus == 0) BuildStringN(p, l, output); } break; case 'd': i = (flong ? va_arg(ap, long) : (long) va_arg(ap, int)); if ((long)i < 0) { fneg = 1; i = -i; } goto number; case 'u': i = (flong ? va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int)); number: BuildString((char *)0, msg); while (i >= 10) { BuildStringChar((i % 10) + '0', msg); i /= 10; } BuildStringChar(i + '0', msg); if (fneg) BuildStringChar('-', msg); if (fmtpre > 0) { padzero = 0; if (fmtpre > fmtlen) fmtlen = fmtpre; while (msg->used - 1 < fmtpre) BuildStringChar('0', msg); } /* reverse the text to put it in forward order */ u = msg->used - 1; for (i = 0; i < u / 2; i++) { char temp; temp = msg->string[i]; msg->string[i] = msg->string[u - i - 1]; msg->string[u - i - 1] = temp; } { int l = msg->used - 1; if (fminus != 0) BuildString(msg->string, output); for (; l < fmtlen; l++) { if (padzero == 0 || fminus != 0) BuildStringChar(' ', output); else BuildStringChar('0', output); } if (fminus == 0) BuildString(msg->string, output); } break; case 'X': case 'x': i = (flong ? va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int)); BuildString((char *)0, msg); while (i >= 16) { if (i % 16 >= 10) BuildStringChar((i % 16) - 10 + (c == 'x' ? 'a' : 'A'), msg); else BuildStringChar((i % 16) + '0', msg); i /= 16; } if (i >= 10) BuildStringChar(i - 10 + (c == 'x' ? 'a' : 'A'), msg); else BuildStringChar(i + '0', msg); if (fmtpre > 0) { padzero = 0; if (fmtpre > fmtlen) fmtlen = fmtpre; while (msg->used - 1 < fmtpre) BuildStringChar('0', msg); } /* reverse the text to put it in forward order */ u = msg->used - 1; for (i = 0; i < u / 2; i++) { char temp; temp = msg->string[i]; msg->string[i] = msg->string[u - i - 1]; msg->string[u - i - 1] = temp; } { int l = msg->used - 1; if (fminus != 0) BuildString(msg->string, output); for (; l < fmtlen; l++) { if (padzero == 0 || fminus != 0) BuildStringChar(' ', output); else BuildStringChar('0', output); } if (fminus == 0) BuildString(msg->string, output); } break; default: Error ("VWrite(): unknown conversion character `%c' in `%s'", c, fmt); break; } s += l + 1; l = -1; e = flong = fneg = fminus = 0; fmtlen = fmtpre = sawdot = padzero = 0; } } } if (l) BuildStringN(fmt + s, l, output); if (str != (STRING *)0) BuildString((char *)0, str); if (output->used > 1) { if (str != (STRING *)0) BuildStringN(output->string, output->used - 1, str); if (cfp != (CONSFILE *)0) FileWrite(cfp, bufferonly, output->string, output->used - 1); } } char * BuildStringPrint(STRING *str, char *fmt, ...) { va_list ap; va_start(ap, fmt); VWrite((CONSFILE *)0, FLAGFALSE, str, fmt, ap); va_end(ap); if (str == (STRING *)0) return (char *)0; else return str->string; } char * BuildTmpStringPrint(char *fmt, ...) { va_list ap; va_start(ap, fmt); if (mymsg == (STRING *)0) mymsg = AllocString(); VWrite((CONSFILE *)0, FLAGFALSE, mymsg, fmt, ap); va_end(ap); return mymsg->string; } void FileVWrite(CONSFILE *cfp, FLAG bufferonly, char *fmt, va_list ap) { VWrite(cfp, bufferonly, (STRING *)0, fmt, ap); } void FilePrint(CONSFILE *cfp, FLAG bufferonly, char *fmt, ...) { va_list ap; va_start(ap, fmt); FileVWrite(cfp, bufferonly, fmt, ap); va_end(ap); } /* Unless otherwise stated, returns the same values as fstat(2) */ int FileStat(CONSFILE *cfp, struct stat *buf) { int retval = 0; if (cfp->errored == FLAGTRUE) return -1; switch (cfp->ftype) { case simpleFile: retval = fstat(cfp->fd, buf); break; case simplePipe: retval = -1; break; case simpleSocket: retval = fstat(cfp->fd, buf); break; #if HAVE_OPENSSL case SSLSocket: retval = -1; break; #endif default: retval = -1; break; } if (retval < 0) cfp->errored = FLAGTRUE; return retval; } /* Unless otherwise stated, returns the same values as lseek(2) */ int FileSeek(CONSFILE *cfp, off_t offset, int whence) { int retval = 0; if (cfp->errored == FLAGTRUE) return -1; switch (cfp->ftype) { case simpleFile: retval = lseek(cfp->fd, offset, whence); break; case simplePipe: retval = -1; break; case simpleSocket: retval = lseek(cfp->fd, offset, whence); break; #if HAVE_OPENSSL case SSLSocket: retval = -1; break; #endif default: retval = -1; break; } if (retval < 0) cfp->errored = FLAGTRUE; return retval; } /* Returns the file descriptor number of the underlying file */ int FileFDNum(CONSFILE *cfp) { int retval = 0; if (cfp == (CONSFILE *)0) return -1; switch (cfp->ftype) { case simpleFile: retval = cfp->fd; break; case simplePipe: retval = cfp->fd; break; case simpleSocket: retval = cfp->fd; break; #if HAVE_OPENSSL case SSLSocket: retval = cfp->fd; break; #endif default: retval = cfp->fd; break; } return retval; } /* Returns the file descriptor number of the underlying file */ int FileFDOutNum(CONSFILE *cfp) { if (cfp == (CONSFILE *)0 || cfp->ftype != simplePipe) return -1; return cfp->fdout; } /* Returns the file type */ enum consFileType FileGetType(CONSFILE *cfp) { switch (cfp->ftype) { case simpleFile: return simpleFile; case simplePipe: return simplePipe; case simpleSocket: return simpleSocket; #if HAVE_OPENSSL case SSLSocket: return SSLSocket; #endif default: return nothing; } } /* Sets the file type */ void FileSetType(CONSFILE *cfp, enum consFileType type) { cfp->ftype = type; } /* Sets the file quoting method */ void FileSetQuoteIAC(CONSFILE *cfp, FLAG flag) { cfp->quoteiac = flag; } FLAG FileSawQuoteSusp(CONSFILE *cfp) { FLAG r = cfp->sawiacsusp; cfp->sawiacsusp = FLAGFALSE; return r; } FLAG FileSawQuoteExec(CONSFILE *cfp) { FLAG r = cfp->sawiacexec; cfp->sawiacexec = FLAGFALSE; return r; } FLAG FileSawQuoteAbrt(CONSFILE *cfp) { FLAG r = cfp->sawiacabrt; cfp->sawiacabrt = FLAGFALSE; return r; } FLAG FileSawQuoteGoto(CONSFILE *cfp) { FLAG r = cfp->sawiacgoto; cfp->sawiacgoto = FLAGFALSE; return r; } #if HAVE_OPENSSL /* Get the SSL instance */ SSL * FileGetSSL(CONSFILE *cfp) { return cfp->ssl; } /* Sets the SSL instance */ void FileSetSSL(CONSFILE *cfp, SSL *ssl) { cfp->ssl = ssl; } /* return -1 on error, 0 for "wait" state, 1 for success */ int FileCanSSLAccept(CONSFILE *cfp, fd_set *prfd, fd_set *pwfd) { if (cfp == (CONSFILE *)0) return 0; return ((FD_ISSET(cfp->fd, prfd) && cfp->waitForRead == FLAGTRUE) || (FD_ISSET(cfp->fd, pwfd) && cfp->waitForWrite == FLAGTRUE) || (cfp->waitForRead != FLAGTRUE && cfp->waitForWrite != FLAGTRUE)); } /* return -1 on error, 0 for "wait" state, 1 for success */ int FileSSLAccept(CONSFILE *cfp) { int retval; if (cfp->waitForWrite == FLAGTRUE) { cfp->waitForWrite = FLAGFALSE; if (cfp->wbuf->used <= 1) FD_CLR(cfp->fd, &winit); } cfp->waitForRead = FLAGFALSE; CONDDEBUG((1, "FileSSLAccept(): about to SSL_accept() for fd %d", cfp->fd)); retval = SSL_accept(cfp->ssl); switch (SSL_get_error(cfp->ssl, retval)) { case SSL_ERROR_NONE: break; case SSL_ERROR_WANT_READ: cfp->waitForRead = FLAGTRUE; return 0; case SSL_ERROR_WANT_WRITE: cfp->waitForWrite = FLAGTRUE; FD_SET(cfp->fd, &winit); return 0; default: Error("FileSSLAccept(): SSL error on fd %d", cfp->fd); /* fall through */ case SSL_ERROR_ZERO_RETURN: SSL_free(cfp->ssl); cfp->ssl = (SSL *)0; cfp->ftype = simpleSocket; return -1; } cfp->ftype = SSLSocket; CONDDEBUG((1, "FileSSLAccept(): SSL Connection: %s :: %s", SSL_get_cipher_version(cfp->ssl), SSL_get_cipher_name(cfp->ssl))); return 1; } #endif /* Unless otherwise stated, returns the same values as send(2) */ int FileSend(CONSFILE *cfp, const void *msg, size_t len, int flags) { int retval = 0; int fdout; if (cfp->ftype == simplePipe) fdout = cfp->fdout; else fdout = cfp->fd; if (cfp->errored == FLAGTRUE) { FD_CLR(fdout, &winit); return -1; } switch (cfp->ftype) { case simpleFile: retval = send(fdout, msg, len, flags); break; case simplePipe: retval = send(fdout, msg, len, flags); break; case simpleSocket: retval = send(fdout, msg, len, flags); break; #if HAVE_OPENSSL case SSLSocket: retval = send(fdout, msg, len, flags); break; #endif default: retval = -1; break; } if (retval < 0) { cfp->errored = FLAGTRUE; FD_CLR(fdout, &winit); } return retval; } /* replace trailing space with '\000' in a string and return * a pointer to the start of the non-space part */ char * PruneSpace(char *string) { char *p; char *head = (char *)0; char *tail = (char *)0; /* Don't do much if it's crap */ if (string == (char *)0 || *string == '\000') return string; /* Now for the tricky part - search the string */ for (p = string; *p != '\000'; p++) { if (isspace((int)(*p))) { if (tail == (char *)0) tail = p; /* possible end of string */ } else { if (head == (char *)0) head = p; /* found the start */ tail = (char *)0; /* reset tail */ } } if (tail != (char *)0) *tail = '\000'; if (head != (char *)0) return head; else return string; } #if !USE_IPV6 /* fills the myAddrs array with host interface addresses */ void ProbeInterfaces(in_addr_t bindAddr) { # ifdef SIOCGIFCONF struct ifconf ifc; struct ifreq *ifr; # ifdef SIOCGIFFLAGS struct ifreq ifrcopy; # endif # ifdef SIOCGIFNUM int nifr; # endif int sock; int r = 0, m = 0; int bufsize = 2048; int count = 0; /* if we use -M, just fill the array with that interface */ if (bindAddr != INADDR_ANY) { myAddrs = (struct in_addr *)calloc(2, sizeof(struct in_addr)); if (myAddrs == (struct in_addr *)0) OutOfMem(); # if HAVE_MEMCPY memcpy(&(myAddrs[0].s_addr), &bindAddr, sizeof(in_addr_t)); # else bcopy(&bindAddr, &(myAddrs[0].s_addr), sizeof(in_addr_t)); # endif Verbose("interface address %s (-M option)", inet_ntoa(myAddrs[0])); return; } if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { Error("ProbeInterfaces(): socket(): %s", strerror(errno)); Bye(EX_OSERR); } # ifdef SIOCGIFNUM if (ioctl(sock, SIOCGIFNUM, &nifr) == 0) bufsize = nifr * sizeof(struct ifreq) + 512; # endif while (bufsize) { ifc.ifc_len = bufsize; ifc.ifc_req = (struct ifreq *)malloc(ifc.ifc_len); if (ifc.ifc_req == (struct ifreq *)0) OutOfMem(); if (ioctl(sock, SIOCGIFCONF, &ifc) != 0 && errno != EINVAL) { free(ifc.ifc_req); close(sock); Error("ProbeInterfaces(): ioctl(SIOCGIFCONF): %s", strerror(errno)); Bye(EX_OSERR); } /* if the return size plus a 512 byte "buffer zone" is less than * the buffer we passed in (bufsize), we're done. otherwise * allocate a bigger buffer and try again. with a too-small * buffer, some implementations (freebsd) will fill the buffer * best it can (leaving a gap - returning <=bufsize) and others * (linux) will return a buffer length the same size as passed * in (==bufsize). so, we'll assume a 512 byte gap would have * been big enough to put one more record and as long as we have * that "buffer zone", we should have all the interfaces. * so, solaris returns EINVAL if it's too small, so we catch that * above and since if_len is bufsize, it'll loop again. */ if (ifc.ifc_len + 512 < bufsize) break; free(ifc.ifc_req); bufsize += 2048; } /* this is probably way overkill, but better to kill a few bytes * than loop through looking for valid interfaces that are up * twice, huh? */ count = ifc.ifc_len / sizeof(*ifr); CONDDEBUG((1, "ProbeInterfaces(): ifc_len==%d max_count==%d", ifc.ifc_len, count)); /* set up myAddrs array */ if (myAddrs != (struct in_addr *)0) free(myAddrs); myAddrs = (struct in_addr *)0; if (count == 0) { free(ifc.ifc_req); close(sock); return; } myAddrs = (struct in_addr *)calloc(count + 1, sizeof(struct in_addr)); if (myAddrs == (struct in_addr *)0) OutOfMem(); for (m = r = 0; r < ifc.ifc_len;) { struct sockaddr *sa; ifr = (struct ifreq *)&ifc.ifc_buf[r]; sa = (struct sockaddr *)&ifr->ifr_addr; /* don't use less than a ifreq sized chunk */ if ((ifc.ifc_len - r) < sizeof(*ifr)) break; # ifdef HAVE_SA_LEN if (sa->sa_len > sizeof(ifr->ifr_ifru)) r += sizeof(ifr->ifr_name) + sa->sa_len; else # endif r += sizeof(*ifr); if (sa->sa_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)sa; /* make sure the address isn't 0.0.0.0, which is how we * signal the end of our list */ if ( # if HAVE_MEMCMP memcmp(&(myAddrs[m]), &(sin->sin_addr), sizeof(struct in_addr)) # else bcmp(&(myAddrs[m]), &(sin->sin_addr), sizeof(struct in_addr)) # endif == 0) continue; # ifdef SIOCGIFFLAGS /* make sure the interface is up */ ifrcopy = *ifr; if ((ioctl(sock, SIOCGIFFLAGS, &ifrcopy) == 0) && ((ifrcopy.ifr_flags & IFF_UP) == 0)) continue; # endif CONDDEBUG((1, "ProbeInterfaces(): name=%s addr=%s", ifr->ifr_name, inet_ntoa(sin->sin_addr))); # if HAVE_MEMCPY memcpy(&myAddrs[m], &(sin->sin_addr), sizeof(struct in_addr)); # else bcopy(&(sin->sin_addr), &myAddrs[m], sizeof(struct in_addr)); # endif Verbose("interface address %s (%s)", inet_ntoa(myAddrs[m]), ifr->ifr_name); m++; } } if (m == 0) { free(myAddrs); myAddrs = (struct in_addr *)0; } close(sock); free(ifc.ifc_req); # else/* use the hostname like the old code did (but use all addresses!) */ int count; struct hostent *he; /* if we use -M, just fill the array with that interface */ if (bindAddr != INADDR_ANY) { myAddrs = (struct in_addr *)calloc(2, sizeof(struct in_addr)); if (myAddrs == (struct in_addr *)0) OutOfMem(); # if HAVE_MEMCPY memcpy(&(myAddrs[0].s_addr), &bindAddr, sizeof(in_addr_t)); # else bcopy(&bindAddr, &(myAddrs[0].s_addr), sizeof(in_addr_t)); # endif Verbose("interface address %s (-M option)", inet_ntoa(myAddrs[0])); return; } Verbose("using hostname for interface addresses"); if ((struct hostent *)0 == (he = gethostbyname(myHostname))) { Error("ProbeInterfaces(): gethostbyname(%s): %s", myHostname, hstrerror(h_errno)); return; } if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("ProbeInterfaces(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)", myHostname, he->h_length, AF_INET, he->h_addrtype); return; } for (count = 0; he->h_addr_list[count] != (char *)0; count++); if (myAddrs != (struct in_addr *)0) free(myAddrs); myAddrs = (struct in_addr *)0; if (count == 0) return; myAddrs = (struct in_addr *)calloc(count + 1, sizeof(struct in_addr)); if (myAddrs == (struct in_addr *)0) OutOfMem(); for (count--; count >= 0; count--) { # if HAVE_MEMCPY memcpy(&(myAddrs[count].s_addr), he->h_addr_list[count], he->h_length); # else bcopy(he->h_addr_list[count], &(myAddrs[count].s_addr), he->h_length); # endif Verbose("interface address %s (hostname address)", inet_ntoa(myAddrs[count])); } # endif } #endif /* USE_IPV6 */ int IsMe(char *id) { #if USE_IPV6 int ret = 0; int error; struct addrinfo hints; struct addrinfo *res, *rp; struct ifaddrs *myAddrs, *ifa; void *a, *b; size_t len; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; /* get IP based on hostname */ error = getaddrinfo(id, NULL, &hints, &res); if (error) { perror(gai_strerror(error)); return 0; } /* get list of all addresses on system */ error = getifaddrs(&myAddrs); if (error) { perror("getifaddrs failed"); return 0; } /* try to find a match */ for (ifa = myAddrs; ifa != NULL; ifa = ifa->ifa_next) { /* skip interfaces without address or in down state */ if (ifa->ifa_addr == NULL || !(ifa->ifa_flags & IFF_UP)) continue; for (rp = res; rp != NULL; rp = rp->ai_next) { if (ifa->ifa_addr->sa_family == rp->ai_addr->sa_family) { /* I really don't like to hardcode it but we have to */ if (ifa->ifa_addr->sa_family == AF_INET) { /* IPv4 */ a = &(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr); b = &(((struct sockaddr_in *)rp->ai_addr)->sin_addr); len = sizeof(struct in_addr); } else { /* IPv6 */ a = &(((struct sockaddr_in6 *)ifa-> ifa_addr)->sin6_addr); b = &(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr); len = sizeof(struct in6_addr); } if ( # if HAVE_MEMCMP memcmp(a, b, len) # else bcmp(a, b, len) # endif == 0) { ret = 1; goto done; } } } } done: freeaddrinfo(res); freeifaddrs(myAddrs); CONDDEBUG((1, "IsMe: ret %d id %s", ret, id)); return ret; #else int j, i; struct hostent *he; in_addr_t addr; # if HAVE_INET_ATON struct in_addr inetaddr; # endif /* check for ip address match */ # if HAVE_INET_ATON if (inet_aton(id, &inetaddr) != 0) { addr = inetaddr.s_addr; # else addr = inet_addr(id); if (addr != (in_addr_t) (-1)) { # endif for (i = 0; myAddrs != (struct in_addr *)0 && myAddrs[i].s_addr != (in_addr_t) 0; i++) { if ( # if HAVE_MEMCMP memcmp(&(myAddrs[i].s_addr), &addr, sizeof(addr)) # else bcmp(&(myAddrs[i].s_addr), &addr, sizeof(addr)) # endif == 0) return 1; } return 0; } /* check for ip match of hostname */ if ((struct hostent *)0 == (he = gethostbyname(id))) { Error("IsMe(): gethostbyname(%s): %s", id, hstrerror(h_errno)); return 0; } if (4 != he->h_length || AF_INET != he->h_addrtype) { Error ("IsMe(): gethostbyname(%s): wrong address size (4 != %d) or address family (%d != %d)", id, he->h_length, AF_INET, he->h_addrtype); return 0; } for (j = 0; he->h_addr_list[j] != (char *)0; j++) { for (i = 0; myAddrs != (struct in_addr *)0 && myAddrs[i].s_addr != (in_addr_t) 0; i++) { if ( # if HAVE_MEMCMP memcmp(&(myAddrs[i].s_addr), he->h_addr_list[j], he->h_length) # else bcmp(&(myAddrs[i].s_addr), he->h_addr_list[j], he->h_length) # endif == 0) return 1; } } return 0; #endif /* USE_IPV6 */ } #if HAVE_OPENSSL /* Unless otherwise stated, returns the same values as send(2) */ int SSLVerifyCallback(int ok, X509_STORE_CTX *store) { char data[256]; if (ok) { if (fDebug) { X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); CONDDEBUG((1, "SSLVerifyCallback(): info of certificate at depth: %d", depth)); X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); CONDDEBUG((1, "SSLVerifyCallback(): issuer = %s", data)); X509_NAME_oneline(X509_get_subject_name(cert), data, 256); CONDDEBUG((1, "SSLVerifyCallback(): subject = %s", data)); } } else { X509 *cert = X509_STORE_CTX_get_current_cert(store); int depth = X509_STORE_CTX_get_error_depth(store); int err = X509_STORE_CTX_get_error(store); Error("SSLVerifyCallback(): error with certificate at depth: %d", depth); X509_NAME_oneline(X509_get_issuer_name(cert), data, 256); Error("SSLVerifyCallback(): issuer = %s", data); X509_NAME_oneline(X509_get_subject_name(cert), data, 256); Error("SSLVerifyCallback(): subject = %s", data); Error("SSLVerifyCallback(): error #%d: %s", err, X509_verify_cert_error_string(err)); } return ok; } #endif int SetFlags(int fd, int s, int c) { int flags; if ((flags = fcntl(fd, F_GETFL)) >= 0) { flags |= s; flags &= ~c; if (fcntl(fd, F_SETFL, flags) < 0) { Error("SetFlags(): fcntl(%u,F_SETFL): %s", fd, strerror(errno)); return 0; } } else { Error("SetFlags(): fcntl(%u,F_GETFL): %s", fd, strerror(errno)); return 0; } return 1; } char * StrDup(const char *msg) { int len; char *buf; if (msg == (char *)0) return (char *)0; len = strlen(msg) + 1; buf = malloc(len); if (buf == (char *)0) return (char *)0; #if HAVE_MEMCPY memcpy(buf, msg, len); #else bcopy(msg, buf, len); #endif return buf; } char * StringChar(STRING *msg, int offset, char c) { int o; if (msg == (STRING *)0 || msg->used <= 1 || offset < 0 || offset > msg->used) return (char *)0; for (o = offset; o != msg->used; o++) { if (msg->string[o] == c) return &(msg->string[o]); } return (char *)0; } /* this takes a buffer, and returns the number of characters to use, * which goes up to the first OB_IAC character sequence (that isn't * OB_IAC/OB_IAC). if it is an OB_IAC sequence, it sets the flag and * returns zero. if it's invalid args, we return -1. * so <0 == no data, 0 == check flags, >0 number of chars to use * this *WILL* modify the buffer (OB_IAC sequences get extracted/shrunk) */ int ParseIACBuf(CONSFILE *cfp, void *msg, int *len) { int l = 0; unsigned char *b = msg; if (*len <= 0) return -1; if (cfp->quoteiac != FLAGTRUE) return *len; /* split OB_IAC/char pair OR OB_IAC at start */ if (cfp->sawiac == FLAGTRUE || b[0] == OB_IAC) { int i = 1; if (cfp->sawiac == FLAGTRUE) { i = 0; cfp->sawiac = FLAGFALSE; } if (i == *len) { /* only thing is OB_IAC */ cfp->sawiac = FLAGTRUE; return -1; } if (b[i] == OB_SUSP) cfp->sawiacsusp = FLAGTRUE; else if (b[i] == OB_EXEC) cfp->sawiacexec = FLAGTRUE; else if (b[i] == OB_ABRT) cfp->sawiacabrt = FLAGTRUE; else if (b[i] == OB_GOTO) cfp->sawiacgoto = FLAGTRUE; else { if (b[i] != OB_IAC) Error ("ParseIACBuf(): fd %d: unrecognized quoted-OB_IAC char", cfp->fd, strerror(errno)); l = 1; } *len = *len - i - 1 + l; MemMove(b, b + i + 1 - l, *len); if (l == 0) return 0; } for (; l < *len; l++) { if (b[l] == OB_IAC) { if (l + 1 == *len) return l; if (b[l + 1] == OB_IAC) { --(*len); MemMove(b + l, b + l + 1, *len - l); } else { return l; } } } return l; } /* the format of the file should be as follows * *
[section name] { * [item value]; * . * . * } * * whitespace gets retained in [section name], and [item value] * values. for example, * * users bryan todd ; * * will give users the value of 'bryan todd'. the leading and * trailing whitespace is nuked, but the middle stuff isn't. * * a little note about the 'state' var... * START = before
* NAME = before [section name] * LEFTB = before left curly brace * KEY = before * VALUE = before [item value] * SEMI = before semi-colon */ typedef enum states { START, NAME, LEFTB, KEY, VALUE, SEMI } STATES; typedef enum tokens { DONE, LEFTBRACE, RIGHTBRACE, SEMICOLON, WORD, INCLUDE } TOKEN; int line = 1; /* current line number */ char *file = (char *)0; TOKEN GetWord(FILE *fp, int *line, short spaceok, STRING *word) { int c; short backslash = 0; short quote = 0; short comment = 0; short sawQuote = 0; short quotedBackslash = 0; char *include = "include"; short checkInc = -1; /* checkInc == -3, saw #include * == -2, saw nothin' * == -1, saw \n or start of file * == 0, saw "\n#" */ BuildString((char *)0, word); while ((c = fgetc(fp)) != EOF) { if (c == '\n') { (*line)++; if (checkInc == -2) checkInc = -1; } if (comment) { if (c == '\n') comment = 0; if (checkInc >= 0) { if (include[checkInc] == '\000') { if (isspace(c)) checkInc = -3; } else if (c == include[checkInc]) checkInc++; else checkInc = -2; } else if (checkInc == -3) { static STRING *fname = (STRING *)0; if (fname == (STRING *)0) fname = AllocString(); if (fname->used != 0 || !isspace(c)) { if (c == '\n') { if (fname->used > 0) { while (fname->used > 1 && isspace((int) (fname-> string [fname-> used - 2]))) fname->used--; if (fname->used > 0) fname->string[fname->used - 1] = '\000'; } checkInc = -2; if (fname->used > 0) { BuildString((char *)0, word); BuildString(fname->string, word); BuildString((char *)0, fname); return INCLUDE; } } else BuildStringChar(c, fname); } } continue; } if (backslash) { BuildStringChar(c, word); backslash = 0; continue; } if (quote) { if (c == '"') { if (quotedBackslash) { BuildStringChar(c, word); quotedBackslash = 0; } else quote = 0; } else { if (quotedBackslash) { BuildStringChar('\\', word); quotedBackslash = 0; } if (c == '\\') quotedBackslash = 1; else BuildStringChar(c, word); } continue; } if (c == '\\') { backslash = 1; } else if (c == '#') { comment = 1; if (checkInc == -1) checkInc = 0; } else if (c == '"') { quote = 1; sawQuote = 1; } else if (isspace(c)) { if (word->used <= 1) continue; if (spaceok) { BuildStringChar(c, word); continue; } gotword: while (word->used > 1 && isspace((int)(word->string[word->used - 2]))) word->used--; if (word->used > 0) word->string[word->used - 1] = '\000'; return WORD; } else if (c == '{') { if (word->used <= 1 && !sawQuote) { BuildStringChar(c, word); return LEFTBRACE; } else { ungetc(c, fp); goto gotword; } } else if (c == '}') { if (word->used <= 1 && !sawQuote) { BuildStringChar(c, word); return RIGHTBRACE; } else { ungetc(c, fp); goto gotword; } } else if (c == ';') { if (word->used <= 1 && !sawQuote) { BuildStringChar(c, word); return SEMICOLON; } else { ungetc(c, fp); goto gotword; } } else { BuildStringChar(c, word); } } /* this should only happen in rare cases */ if (quotedBackslash) { BuildStringChar('\\', word); quotedBackslash = 0; } /* if we saw "valid" data, it's a word */ if (word->used > 1 || sawQuote) goto gotword; return DONE; } void ParseFile(char *filename, FILE *fp, int level) { /* things that should be used between recursions */ static STATES state = START; static STRING *word = (STRING *)0; static short spaceok = 0; static int secIndex = 0; static int keyIndex = 0; /* other stuff that's local to each recursion */ char *p; TOKEN token = DONE; int nextline = 1; /* "next" line number */ if (level >= 10) { if (isMaster) Error("ParseFile(): nesting too deep, not parsing `%s'", filename); return; } /* set some globals */ line = 1; file = filename; /* if we're parsing the base file, set static vars */ if (level == 0) { state = START; spaceok = 0; secIndex = 0; keyIndex = 0; } /* initialize local things */ if (word == (STRING *)0) word = AllocString(); while ((token = GetWord(fp, &nextline, spaceok, word)) != DONE) { if (token == INCLUDE) { FILE *lfp; if ((FILE *)0 == (lfp = fopen(word->string, "r"))) { if (isMaster) Error("ParseFile(): fopen(%s): %s", word->string, strerror(errno)); } else { char *fname; /* word gets destroyed, so save the name */ fname = StrDup(word->string); ParseFile(fname, lfp, level + 1); fclose(lfp); free(fname); } } else { switch (state) { case START: switch (token) { case WORD: for (secIndex = 0; (p = sections[secIndex].id) != (char *)0; secIndex++) { if (strcasecmp(word->string, p) == 0) { CONDDEBUG((1, "ReadCfg(): got keyword '%s' [%s:%d]", word->string, file, line)); state = NAME; break; } } if (state == START) { if (isMaster) Error("invalid keyword '%s' [%s:%d]", word->string, file, line); } break; case LEFTBRACE: case RIGHTBRACE: case SEMICOLON: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; case NAME: switch (token) { case WORD: (*sections[secIndex].begin) (word->string); state = LEFTB; break; case RIGHTBRACE: if (isMaster) Error("premature token '%s' [%s:%d]", word->string, file, line); state = START; break; case LEFTBRACE: case SEMICOLON: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; case LEFTB: switch (token) { case LEFTBRACE: state = KEY; break; case RIGHTBRACE: if (isMaster) Error("premature token '%s' [%s:%d]", word->string, file, line); (*sections[secIndex].abort) (); state = START; break; case SEMICOLON: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case WORD: if (isMaster) Error("invalid word '%s' [%s:%d]", word->string, file, line); break; case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; case KEY: switch (token) { case WORD: for (keyIndex = 0; (p = sections[secIndex].items[keyIndex].id) != (char *)0; keyIndex++) { if (strcasecmp(word->string, p) == 0) { CONDDEBUG((1, "got keyword '%s' [%s:%d]", word->string, file, line)); state = VALUE; break; } } if (state == KEY) { if (isMaster) Error("invalid keyword '%s' [%s:%d]", word->string, file, line); } break; case RIGHTBRACE: (*sections[secIndex].end) (); state = START; break; case LEFTBRACE: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case SEMICOLON: if (isMaster) Error("premature token '%s' [%s:%d]", word->string, file, line); case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; case VALUE: switch (token) { case WORD: (*sections[secIndex]. items[keyIndex].reg) (word->string); state = SEMI; break; case SEMICOLON: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); state = KEY; break; case RIGHTBRACE: if (isMaster) Error("premature token '%s' [%s:%d]", word->string, file, line); (*sections[secIndex].abort) (); state = START; break; case LEFTBRACE: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; case SEMI: switch (token) { case SEMICOLON: state = KEY; break; case RIGHTBRACE: if (isMaster) Error("premature token '%s' [%s:%d]", word->string, file, line); (*sections[secIndex].abort) (); state = START; break; case LEFTBRACE: if (isMaster) Error("invalid token '%s' [%s:%d]", word->string, file, line); break; case WORD: if (isMaster) Error("invalid word '%s' [%s:%d]", word->string, file, line); break; case DONE: /* just shutting up gcc */ case INCLUDE: /* just shutting up gcc */ break; } break; } switch (state) { case NAME: case VALUE: spaceok = 1; break; case KEY: case LEFTB: case START: case SEMI: spaceok = 0; break; } } line = nextline; } if (level == 0) { int i; /* check for proper ending of file and do any cleanup */ switch (state) { case START: break; case KEY: case LEFTB: case VALUE: case SEMI: (*sections[secIndex].abort) (); /* fall through */ case NAME: if (isMaster) Error("premature EOF seen [%s:%d]", file, line); break; } /* now clean up all the temporary space used */ for (i = 0; sections[i].id != (char *)0; i++) { (*sections[i].destroy) (); } } } void ProcessSubst(SUBST *s, char **repl, char **str, char *name, char *id) { /* * (CONSENT *pCE) and (char **repl) are used when a replacement is to * actually happen...repl is the string to munch, pCE holds the data. * * (char **str) is used to store a copy of (char *id), if it passes * the format check. * * the idea is that this is first called when the config file is read, * putting the result in (char **str). then we call it again, near * the end, permuting (char **repl) with values from (CONSENT *pCE) with * the saved string now coming in as (char *id). got it? * * you could pass all arguments in...then both types of actions occur. */ char *p; char *repfmt[256]; unsigned short repnum; int i; enum repstate { REP_BEGIN, REP_LTR, REP_EQ, REP_INT, REP_END } state; if (s == (SUBST *)0) { Error("ProcessSubst(): WTF? No substitute support structure?!?!"); Bye(EX_SOFTWARE); } if (str != (char **)0) { if (*str != (char *)0) { free(*str); *str = (char *)0; } } if ((id == (char *)0) || (*id == '\000')) return; repnum = 0; state = REP_BEGIN; for (i = 0; i < 256; i++) repfmt[i] = (char *)0; for (p = id; *p != '\000'; p++) { switch (state) { case REP_BEGIN: /* must be printable */ if (*p == ',' || !isgraph((int)(*p))) goto subst_err; /* make sure we haven't seen this replacement char yet */ repnum = (unsigned short)(*p); if (repfmt[repnum] != (char *)0) { if (isMaster) Error ("substitution characters of `%s' option are the same [%s:%d]", name, file, line); return; } state = REP_LTR; break; case REP_LTR: if (*p != '=') goto subst_err; state = REP_EQ; break; case REP_EQ: repfmt[repnum] = p; if (s->token(*(repfmt[repnum])) != ISNOTHING) state = REP_INT; else goto subst_err; break; case REP_INT: if (*p == 'd' || *p == 'x' || *p == 'X' || *p == 'a' || *p == 'A') { if (s->token(*(repfmt[repnum])) != ISNUMBER) goto subst_err; state = REP_END; } else if (*p == 's') { if (s->token(*(repfmt[repnum])) != ISSTRING) goto subst_err; state = REP_END; } else if (!isdigit((int)(*p))) goto subst_err; break; case REP_END: if (*p != ',') goto subst_err; state = REP_BEGIN; break; } } if (state != REP_END) { subst_err: if (isMaster) Error ("invalid `%s' specification `%s' (char #%d: `%c') [%s:%d]", name, id, (p - id) + 1, *p, file, line); return; } if (str != (char **)0) { if ((*str = StrDup(id)) == (char *)0) OutOfMem(); } if (s != (SUBST *)0 && repl != (char **)0 && *repl != (char *)0) { static STRING *result = (STRING *)0; if (result == (STRING *)0) result = AllocString(); BuildString((char *)0, result); for (p = *repl; *p != '\000'; p++) { if (repfmt[(unsigned short)(*p)] != (char *)0) { char *r = repfmt[(unsigned short)(*p)]; int plen = 0; char *c = (char *)0; int o = 0; if (s->token(*r) == ISSTRING) { /* check the pattern for a length */ if (isdigit((int)(*(r + 1)))) plen = atoi(r + 1); /* this should never return zero, but just in case */ if ((*s->value) (*r, &c, (int *)0) == 0) c = ""; plen -= strlen(c); /* pad it out, if necessary */ for (i = 0; i < plen; i++) BuildStringChar(' ', result); /* throw in the string */ BuildString(c, result); } else { int i = 0; unsigned short port = 0; unsigned short base = 0; int padzero = 0; static STRING *num = (STRING *)0; if (num == (STRING *)0) num = AllocString(); BuildString((char *)0, num); /* this should never return zero, but just in case */ if ((*s->value) (*r, (char **)0, &i) == 0) port = 0; else port = (unsigned short)i; /* check the pattern for a length and padding */ for (c = r + 1; *c != '\000'; c++) if (!isdigit((int)(*c))) break; if (c != r + 1) { plen = atoi(r + 1); padzero = (r[1] == '0'); } /* check for base */ switch (*c) { case 'd': base = 10; break; case 'x': case 'X': base = 16; break; case 'a': case 'A': base = 36; break; default: return; } while (port >= base) { if (port % base >= 10) BuildStringChar((port % base) - 10 + ((*c == 'x' || *c == 'a') ? 'a' : 'A'), num); else BuildStringChar((port % base) + '0', num); port /= base; } if (port >= 10) BuildStringChar(port - 10 + ((*c == 'x' || *c == 'a') ? 'a' : 'A'), num); else BuildStringChar(port + '0', num); /* if we're supposed to be a certain length, pad it */ while (num->used - 1 < plen) { if (padzero == 0) BuildStringChar(' ', num); else BuildStringChar('0', num); } /* reverse the text to put it in forward order */ o = num->used - 1; for (i = 0; i < o / 2; i++) { char temp; temp = num->string[i]; num->string[i] = num->string[o - i - 1]; num->string[o - i - 1] = temp; } BuildStringN(num->string, o, result); } } else BuildStringChar(*p, result); } free(*repl); if ((*repl = StrDup(result->string)) == (char *)0) OutOfMem(); } return; } char * MyVersion(void) { static STRING *version = (STRING *)0; if (version != (STRING *)0) return version->string; version = AllocString(); BuildStringPrint(version, "%s %d.%d.%d", VERSION_TEXT, VERSION_MAJOR, VERSION_MINOR, VERSION_REV); return version->string; } unsigned int AtoU(char *str) { unsigned int v; int i; v = 0; for (i = 0; isdigit((int)str[i]); i++) { v *= 10; v += str[i] - '0'; } return v; } void StrCpy(char *dst, const char *src, unsigned int size) { #ifdef HAVE_STRLCPY strlcpy(dst, src, size); #else strcpy(dst, src); #endif } conserver-8.2.4/conserver/cutil.h000066400000000000000000000137751344660520400170530ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ #include #if HAVE_OPENSSL # include # include # include # include # if OPENSSL_VERSION_NUMBER < 0x10100000L # define TLS_method SSLv23_method # define CIPHER_SEC0 # else # define CIPHER_SEC0 ":@SECLEVEL=0" # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ #endif #if HAVE_GSSAPI # include #endif /* communication constants */ #define OB_IAC 0xff /* quote char */ #define OB_EXEC 'E' /* exec a command on the client */ #define OB_GOTO 'G' /* goto next console */ #define OB_SUSP 'Z' /* suspended by server */ #define OB_ABRT '.' /* abort */ /* Struct to wrap information about a "file"... * This can be a socket, local file, whatever. We do this so * we can add encryption to sockets (and generalize I/O). */ enum consFileType { simpleFile, simpleSocket, simplePipe, #if HAVE_OPENSSL SSLSocket, #endif nothing }; typedef enum IOState { ISDISCONNECTED = 0, INCONNECT, ISNORMAL, #if HAVE_OPENSSL INSSLACCEPT, INSSLSHUTDOWN, #endif #if HAVE_GSSAPI INGSSACCEPT, #endif ISFLUSHING } IOSTATE; typedef enum flag { FLAGUNKNOWN = 0, FLAGTRUE, FLAGFALSE } FLAG; typedef struct dynamicString { char *string; int used; int allocated; struct dynamicString *next; struct dynamicString *prev; } STRING; typedef struct consFile { /* Standard socket type stuff */ enum consFileType ftype; int fd; int fdout; /* only used when a simplePipe */ STRING *wbuf; FLAG errored; FLAG quoteiac; FLAG sawiac; FLAG sawiacsusp; FLAG sawiacexec; FLAG sawiacabrt; FLAG sawiacgoto; #if HAVE_OPENSSL /* SSL stuff */ SSL *ssl; FLAG waitForWrite; FLAG waitForRead; #endif /* Add crypto stuff to suit */ #if DEBUG_CONSFILE_IO int debugrfd; int debugwfd; #endif } CONSFILE; typedef struct item { char *id; void (*reg)(char *); } ITEM; typedef struct section { char *id; void (*begin)(char *); void (*end)(void); void (*abort)(void); void (*destroy)(void); ITEM *items; } SECTION; typedef enum substToken { ISNOTHING = 0, ISNUMBER, ISSTRING } SUBSTTOKEN; typedef struct subst { /* function to retrieve a token type based on a character */ SUBSTTOKEN (*token)(char); /* data for callback function */ void *data; /* function to retrieve a value (as a char* or int or both) for * a substitution */ int (*value)(char, char **, int *); } SUBST; extern int isMultiProc, fDebug, fVerbose, fErrorPrinted; extern char *progname; extern pid_t thepid; #define MAXHOSTNAME 1024 extern char myHostname[]; #if !USE_IPV6 extern struct in_addr *myAddrs; #endif extern fd_set rinit; extern fd_set winit; extern int maxfd; extern int debugLineNo; extern char *debugFileName; extern int line; /* used by ParseFile */ extern char *file; /* used by ParseFile */ extern SECTION sections[]; /* used by ParseFile */ extern int isMaster; extern const char *StrTime(time_t *); extern void Debug(int, char *, ...); extern void Error(char *, ...); extern void Msg(char *, ...); extern void Verbose(char *, ...); extern void SimpleSignal(int, RETSIGTYPE(*)(int)); extern int GetMaxFiles(); extern char *FmtCtl(int, STRING *); extern void FmtCtlStr(char *, int, STRING *); extern CONSFILE *FileOpenFD(int, enum consFileType); extern CONSFILE *FileOpenPipe(int, int); extern CONSFILE *FileOpen(const char *, int, int); extern int FileClose(CONSFILE **); extern int FileRead(CONSFILE *, void *, int); extern int FileWrite(CONSFILE *, FLAG, char *, int); extern void FileVWrite(CONSFILE *, FLAG, char *, va_list); extern void FilePrint(CONSFILE *, FLAG, char *, ...); extern int FileStat(CONSFILE *, struct stat *); extern int FileSeek(CONSFILE *, off_t, int); extern int FileSend(CONSFILE *, const void *, size_t, int); extern int FileFDNum(CONSFILE *); extern int FileFDOutNum(CONSFILE *); extern int FileUnopen(CONSFILE *); extern void OutOfMem(); extern char *BuildTmpString(const char *); extern char *BuildTmpStringChar(const char); extern char *BuildTmpStringPrint(char *, ...); extern char *BuildString(const char *, STRING *); extern char *BuildStringChar(const char, STRING *); extern char *BuildStringPrint(STRING *, char *, ...); extern char *BuildStringN(const char *, int, STRING *); extern char *ShiftString(STRING *, int); extern void InitString(STRING *); extern void DestroyString(STRING *); extern void DestroyStrings(void); extern STRING *AllocString(void); extern char *ReadLine(FILE *, STRING *, int *); extern enum consFileType FileGetType(CONSFILE *); extern void FileSetType(CONSFILE *, enum consFileType); extern void FileSetQuoteIAC(CONSFILE *, FLAG); extern FLAG FileSawQuoteSusp(CONSFILE *); extern FLAG FileSawQuoteExec(CONSFILE *); extern FLAG FileSawQuoteAbrt(CONSFILE *); extern FLAG FileSawQuoteGoto(CONSFILE *); extern void Bye(int); extern void DestroyDataStructures(void); extern int IsMe(char *); extern char *PruneSpace(char *); extern int FileCanRead(CONSFILE *, fd_set *, fd_set *); extern int FileCanWrite(CONSFILE *, fd_set *, fd_set *); extern int FileBufEmpty(CONSFILE *); extern int SetFlags(int, int, int); extern char *StrDup(const char *); extern int ParseIACBuf(CONSFILE *, void *, int *); extern void *MemMove(void *, void *, size_t); extern char *StringChar(STRING *, int, char); extern void ParseFile(char *, FILE *, int); #if !USE_IPV6 extern void ProbeInterfaces(in_addr_t); #endif extern void ProcessSubst(SUBST *, char **, char **, char *, char *); extern char *MyVersion(void); extern unsigned int AtoU(char *); extern void StrCpy(char *, const char *, unsigned int); #if HAVE_OPENSSL extern SSL *FileGetSSL(CONSFILE *); extern void FileSetSSL(CONSFILE *, SSL *); extern int SSLVerifyCallback(int, X509_STORE_CTX *); extern int FileSSLAccept(CONSFILE *); extern int FileCanSSLAccept(CONSFILE *, fd_set *, fd_set *); #endif conserver-8.2.4/conserver/fallback.c000066400000000000000000000133031344660520400174500ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * This is a fake library interface to ptyd (mtr&ksb) * * Mike Rowan (mtr@mace.cc.purdue.edu) */ #include #include /* * get a pty for the user * * this has been revamped rather heavily for 8.0.0. i've taken ideas * from the xemacs and openssh distributions to get code that *should* * work on systems i have no access to. thanks to those reference * packages, i think things are ok...hopefully it's true! */ static int GetPseudoTTY(STRING *slave, int *slaveFD) { #if HAVE_OPENPTY int fd = -1; int sfd = -1; int opty = 0; char *pcName; # if HAVE_SIGACTION sigset_t oldmask, newmask; # else extern RETSIGTYPE FlagReapVirt(int); # endif # if HAVE_SIGACTION sigemptyset(&newmask); sigaddset(&newmask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) Error("GetPseudoTTY(): sigprocmask(SIG_BLOCK): %s", strerror(errno)); # else SimpleSignal(SIGCHLD, SIG_DFL); # endif opty = openpty(&fd, &sfd, NULL, NULL, NULL); # if HAVE_SIGACTION if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) Error("GetPseudoTTY(): sigprocmask(SIG_SETMASK): %s", strerror(errno)); # else SimpleSignal(SIGCHLD, FlagReapVirt); # endif if (opty != 0) { if (fd >= 0) close(fd); if (sfd >= 0) close(sfd); return -1; } if ((char *)0 == (pcName = ttyname(sfd))) { close(fd); close(sfd); return -1; } BuildString((char *)0, slave); BuildString(pcName, slave); *slaveFD = sfd; return fd; #else # if (HAVE_PTSNAME && HAVE_GRANTPT && HAVE_UNLOCKPT) || defined(_AIX) int fd = -1; int sfd = -1; char *pcName; # if HAVE_SIGACTION sigset_t oldmask, newmask; # else extern RETSIGTYPE FlagReapVirt(int); # endif int c; /* clone list and idea stolen from xemacs distribution */ static char *clones[] = { "/dev/ptmx", /* Various systems */ "/dev/ptm/clone", /* HPUX */ "/dev/ptc", /* AIX */ "/dev/ptmx_bsd", /* Tru64 */ (char *)0 }; /* try to find the pty allocator */ for (c = 0; clones[c] != (char *)0; c++) { if ((fd = open(clones[c], O_RDWR, 0)) >= 0) break; } if (fd < 0) return -1; # if HAVE_SIGACTION sigemptyset(&newmask); sigaddset(&newmask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) Error("GetPseudoTTY(): sigprocmask(SIG_BLOCK): %s", strerror(errno)); # else SimpleSignal(SIGCHLD, SIG_DFL); # endif # if HAVE_GRANTPT grantpt(fd); /* change permission of slave */ # endif # if HAVE_SIGACTION if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) Error("GetPseudoTTY(): sigprocmask(SIG_SETMASK): %s", strerror(errno)); # else SimpleSignal(SIGCHLD, FlagReapVirt); # endif # if HAVE_UNLOCKPT unlockpt(fd); /* unlock slave */ # endif # if defined(_AIX) if ((pcName = ttyname(fd)) == (char *)0) { close(fd); return -1; } # else # if HAVE_PTSNAME if ((pcName = ptsname(fd)) == (char *)0) { close(fd); return -1; } # else close(fd); return -1; # endif # endif /* go ahead and open the slave */ if ((sfd = open(pcName, O_RDWR, 0)) < 0) { Error("GetPseudoTTY(): open(%s): %s", pcName, strerror(errno)); close(fd); return -1; } BuildString((char *)0, slave); BuildString(pcName, slave); *slaveFD = sfd; return fd; # else /* * Below is the string for finding /dev/ptyXX. For each architecture we * leave some pty's world writable because we don't have source for * everything that uses pty's. For the most part, we'll be trying to * make /dev/ptyq* the "free" pty's. */ /* all the world's a vax ;-) */ static char charone[] = "prstuvwxyzPQRSTUVWq"; static char chartwo[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static char acMaster[] = "/dev/ptyXX"; static char acSlave[] = "/dev/ttyXX"; static char *pcOne = charone, *pcTwo = chartwo; int fd, sfd, iLoop, iIndex = sizeof("/dev/pty") - 1; char *pcOld1; struct stat statBuf; iLoop = 0; pcOld1 = pcOne; for (;;) { if ('\000' == *++pcTwo) { pcTwo = chartwo; if ('\000' == *++pcOne) { pcOne = charone; if ((pcOld1 == pcOne && ++iLoop > 1) || (iLoop > 32)) return -1; } } acMaster[iIndex] = *pcOne; acMaster[iIndex + 1] = *pcTwo; /* * Remeber we are root - stat the file * to see if it exists before we open it * for read/write - if it doesn't we don't * have any pty's left in the row */ if (-1 == stat(acMaster, &statBuf) || S_IFCHR != (statBuf.st_mode & S_IFMT)) { pcTwo = "l"; continue; } if (0 > (fd = open(acMaster, O_RDWR | O_NONBLOCK, 0))) { continue; } acSlave[iIndex] = *pcOne; acSlave[iIndex + 1] = *pcTwo; if (-1 == access(acSlave, F_OK)) { close(fd); continue; } break; } /* go ahead and open the slave */ if ((sfd = open(acSlave, O_RDWR, 0)) < 0) { Error("GetPseudoTTY(): open(%s): %s", acSlave, strerror(errno)); close(fd); return -1; } BuildString((char *)0, slave); BuildString(acSlave, slave); *slaveFD = sfd; return fd; # endif/* (HAVE_PTSNAME && HAVE_GRANTPT && HAVE_UNLOCKPT) || defined(_AIX) */ #endif /* HAVE_OPENPTY */ } /* * get a pty using the GetPseudoTTY code above */ int FallBack(char **slave, int *sfd) { int fd; static STRING *pcTSlave = (STRING *)0; if (pcTSlave == (STRING *)0) pcTSlave = AllocString(); if ((fd = GetPseudoTTY(pcTSlave, sfd)) == -1) { return -1; } if ((*slave) != (char *)0) free(*slave); if (((*slave) = StrDup(pcTSlave->string)) == (char *)0) OutOfMem(); return fd; } conserver-8.2.4/conserver/group.c000066400000000000000000004322601344660520400170540ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Recoded by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_PAM # include #endif /* flags that a signal has occurred */ static sig_atomic_t fSawChldHUP = 0, fSawReUp = 0, fSawGoAway = 0, fSawReapVirt = 0, fSawChldUSR2 = 0; /* timers */ time_t timers[T_MAX]; #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION static unsigned long dmallocMarkClientConnection = 0; #endif void SendIWaitClientsMsg(CONSENT *pCE, char *message) { CONSCLIENT *pCL; if ((CONSENT *)0 == pCE) { return; } for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon && pCL->fiwait) { pCL->fiwait = 0; FileWrite(pCL->fd, FLAGFALSE, message, -1); } } } void SendClientsMsg(CONSENT *pCE, char *message) { CONSCLIENT *pCL; if ((CONSENT *)0 == pCE) { return; } for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon) { FileWrite(pCL->fd, FLAGFALSE, message, -1); } } } void SendCertainClientsMsg(GRPENT *pGE, char *who, char *message) { CONSCLIENT *pCL; char *console = (char *)0; if ((GRPENT *)0 == pGE || who == (char *)0 || message == (char *)0) { return; } if ((console = strrchr(who, '@')) != (char *)0) { *console++ = '\000'; if (*console == '\000') console = (char *)0; } for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) { if (pCL->fcon) { if (*who != '\000' && strcmp(pCL->username->string, who) != 0) continue; if (console != (char *)0 && strcmp(pCL->pCEto->server, console) != 0) continue; FileWrite(pCL->fd, FLAGFALSE, message, -1); } } } void SendAllClientsMsg(GRPENT *pGE, char *message) { CONSCLIENT *pCL; if ((GRPENT *)0 == pGE) { return; } for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) { if (pCL->fcon) { FileWrite(pCL->fd, FLAGFALSE, message, -1); } } } void AbortAnyClientExec(CONSCLIENT *pCL) { if (pCL->iState == S_CEXEC) { FileSetQuoteIAC(pCL->fd, FLAGFALSE); FilePrint(pCL->fd, FLAGTRUE, "%c%c", OB_IAC, OB_ABRT); FileSetQuoteIAC(pCL->fd, FLAGTRUE); pCL->fcon = 1; pCL->iState = S_NORMAL; } } void StopTask(CONSENT *pCE) { if (pCE->taskpid != 0) { kill(pCE->taskpid, SIGHUP); CONDDEBUG((1, "StopTask(): sending task pid %lu signal %d", (unsigned long)pCE->taskpid, SIGHUP)); } if (pCE->taskfile != (CONSFILE *)0) { int taskfile = FileFDNum(pCE->taskfile); FD_CLR(taskfile, &rinit); FileClose(&pCE->taskfile); pCE->taskfile = (CONSFILE *)0; } } void ClientWantsWrite(CONSCLIENT *pCL) { CONSENT *pCE; if ((CONSCLIENT *)0 == pCL) return; if (pCL->fwr) return; pCL->fwr = 0; pCL->fwantwr = 1; pCE = pCL->pCEto; if ((CONSENT *)0 == pCE) return; /* promote the client to the top of the list * (which allows them to be picked first for * aquiring read-write access) * first by extracting... */ if ((CONSCLIENT *)0 != pCL->pCLnext) { pCL->pCLnext->ppCLbnext = pCL->ppCLbnext; } *(pCL->ppCLbnext) = pCL->pCLnext; /* now by inserting... */ pCL->pCLnext = pCE->pCLon; pCL->ppCLbnext = &pCE->pCLon; if ((CONSCLIENT *)0 != pCL->pCLnext) { pCL->pCLnext->ppCLbnext = &pCL->pCLnext; } pCE->pCLon = pCL; } void DisconnectClient(GRPENT *pGE, CONSCLIENT *pCL, char *message, FLAG force) { CONSENT *pCEServing; if (pGE == (GRPENT *)0 || pCL == (CONSCLIENT *)0) { return; } AbortAnyClientExec(pCL); if (pCL->fcon) { FileWrite(pCL->fd, FLAGFALSE, message, -1); } if (force != FLAGTRUE && !FileBufEmpty(pCL->fd)) { pCL->ioState = ISFLUSHING; return; } /* log it, drop from select list, * close gap in table, etc, etc... */ pCEServing = pCL->pCEto; if (pGE->pCEctl != pCEServing) { Msg("[%s] logout %s", pCEServing->server, pCL->acid->string); } else if (pCL->iState == S_NORMAL) Verbose(" logout %s", pCL->acid->string); if (pCEServing->ondemand == FLAGTRUE && pCEServing->pCLon->pCLnext == (CONSCLIENT *)0) ConsDown(pCEServing, FLAGFALSE, FLAGFALSE); FD_CLR(FileFDNum(pCL->fd), &rinit); FD_CLR(FileFDNum(pCL->fd), &winit); FileClose(&pCL->fd); /* mark as not writer, if he is * and turn logging back on... */ if (pCL->fwr) { BumpClient(pCEServing, (char *)0); TagLogfileAct(pCEServing, "%s detached", pCL->acid->string); if (pCEServing->nolog) { pCEServing->nolog = 0; TagLogfile(pCEServing, "Console logging restored (logout)"); } FindWrite(pCEServing); } /* mark as unconnected and remove from both * lists (all clients, and this console) */ pCL->fcon = 0; if ((CONSCLIENT *)0 != pCL->pCLnext) { pCL->pCLnext->ppCLbnext = pCL->ppCLbnext; } *(pCL->ppCLbnext) = pCL->pCLnext; if ((CONSCLIENT *)0 != pCL->pCLscan) { pCL->pCLscan->ppCLbscan = pCL->ppCLbscan; } *(pCL->ppCLbscan) = pCL->pCLscan; /* the continue below will advance to a (ksb) * legal client, even though we are now closed * and in the fre list becasue pCLscan is used * for the free list */ pCL->pCLnext = pGE->pCLfree; pGE->pCLfree = pCL; #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "DisconnectClient(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif } int DisconnectCertainClients(GRPENT *pGE, char *admin, char *who) { CONSCLIENT *pCL; char *console = (char *)0; int count = 0; char *msg = (char *)0; if ((GRPENT *)0 == pGE || who == (char *)0) { return 0; } if ((console = strrchr(who, '@')) != (char *)0) { *console++ = '\000'; if (*console == '\000') console = (char *)0; } for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) { /* skip folks not connected to a console * we check for 'fcon' in other code, but that skips users * that are suspended as well, we don't want that here */ if (pGE->pCEctl == pCL->pCEto) continue; if (*who != '\000' && strcmp(pCL->username->string, who) != 0) continue; if (console != (char *)0 && strcmp(pCL->pCEto->server, console) != 0) continue; if (msg == (char *)0) { BuildTmpString((char *)0); BuildTmpString("[-- Disconnected by admin `"); BuildTmpString(admin); msg = BuildTmpString("' --]\r\n"); } DisconnectClient(pGE, pCL, msg, FLAGFALSE); count++; } return count; } void DisconnectAllClients(GRPENT *pGE, char *message) { CONSCLIENT *pCL; if ((GRPENT *)0 == pGE) { return; } for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) { DisconnectClient(pGE, pCL, message, FLAGTRUE); } } void DestroyClient(CONSCLIENT *pCL) { if (pCL == (CONSCLIENT *)0) return; if (pCL->acid != (STRING *)0) DestroyString(pCL->acid); if (pCL->peername != (STRING *)0) DestroyString(pCL->peername); if (pCL->accmd != (STRING *)0) DestroyString(pCL->accmd); if (pCL->username != (STRING *)0) DestroyString(pCL->username); FileClose(&pCL->fd); free(pCL); } void DestroyConsentUsers(CONSENTUSERS **cu) { CONSENTUSERS *n = (CONSENTUSERS *)0; if (cu == (CONSENTUSERS **)0) return; while ((*cu) != (CONSENTUSERS *)0) { n = (*cu)->next; free(*cu); (*cu) = n; } } CONSENTUSERS * ConsentFindUser(CONSENTUSERS *pCU, char *id) { short close = 0; struct group *g = (struct group *)0; struct passwd *pwd = (struct passwd *)0; for (; pCU != (CONSENTUSERS *)0; pCU = pCU->next) { if (pCU->user->name[0] == '@' && pCU->user->name[1] != '\000') { if (close == 0) { close = 1; /* try to grab the primary group */ pwd = getpwnam(id); } /* grab the group info */ if ((g = getgrnam(pCU->user->name + 1)) == (struct group *)0) { Error("ConsentFindUser(): unknown group name `%s'", pCU->user->name + 1); } else if (pwd != (struct passwd *)0 && pwd->pw_gid == g->gr_gid) { goto donehunting; } else if (g->gr_mem != (char **)0) { char **m; for (m = g->gr_mem; *m != (char *)0; m++) if (strcmp(*m, id) == 0) goto donehunting; } } else if (strcmp(pCU->user->name, id) == 0) { goto donehunting; } } donehunting: if (close) { endgrent(); endpwent(); } return pCU; } int ConsentUserOk(CONSENTUSERS *pCU, char *id) { CONSENTUSERS *c; if ((c = ConsentFindUser(pCU, id)) != (CONSENTUSERS *)0) return !c->not; if ((c = ConsentFindUser(pCU, "*")) != (CONSENTUSERS *)0) return !c->not; return -1; } /* check user permissions. return 0 for r/w, 1 for r/o, -1 for none */ int ClientAccess(CONSENT *pCE, char *user) { CONDDEBUG((1, "ClientAccess(): Authenticating user %s", user)); if (ConsentUserOk(pCE->rw, user) == 1) return 0; if (ConsentUserOk(pCE->ro, user) == 1) return 1; #if STRIP_REALM { /* try the username without @REALM against the ACL * this allows for falling back to PAM from kerberos5/gssapi * as the latter uses 'user@REALM' and the former only 'user' */ char *at; char *shortname; int ret = -1; if ((at = strrchr(user, '@')) != (char *)0) { shortname = StrDup(user); *(shortname + (at - user)) = '\000'; CONDDEBUG((1, "ClientAccess(): Shortname computed from %s is %s", user, shortname)); if (ConsentUserOk(pCE->rw, shortname) == 1) { ret = 0; } else if (ConsentUserOk(pCE->ro, shortname) == 1) { ret = 1; } free(shortname); return ret; } } #endif return -1; } void DestroyConsent(GRPENT *pGE, CONSENT *pCE) { CONSCLIENT *pCL; CONSENT **ppCE; NAMES *name; if (pCE == (CONSENT *)0 || pGE == (GRPENT *)0) return; CONDDEBUG((1, "DestroyConsent(): destroying `%s'", pCE->server)); /* must loop using pCLall and pCLscan for the same reason as the * drop: code. this is basically the same set of code, but modified * since we know we're going to nuke the console itself. */ for (pCL = pGE->pCLall; pCL != (CONSCLIENT *)0; pCL = pCL->pCLscan) { if (pCL->pCEto != pCE) continue; AbortAnyClientExec(pCL); if (pCL->fcon) { FileWrite(pCL->fd, FLAGFALSE, "[-- Conserver reconfigured - console has been (re)moved --]\r\n", -1); } Msg("[%s] logout %s", pCE->server, pCL->acid->string); FD_CLR(FileFDNum(pCL->fd), &rinit); FD_CLR(FileFDNum(pCL->fd), &winit); FileClose(&pCL->fd); if (pCL->fwr) { BumpClient(pCE, (char *)0); TagLogfileAct(pCE, "%s detached", pCL->acid->string); if (pCE->nolog) { pCE->nolog = 0; TagLogfile(pCE, "Console logging restored (logout)"); } } /* mark as unconnected and remove from both * lists (all clients, and this console) */ if ((CONSCLIENT *)0 != pCL->pCLnext) { pCL->pCLnext->ppCLbnext = pCL->ppCLbnext; } *(pCL->ppCLbnext) = pCL->pCLnext; if ((CONSCLIENT *)0 != pCL->pCLscan) { pCL->pCLscan->ppCLbscan = pCL->ppCLbscan; } *(pCL->ppCLbscan) = pCL->pCLscan; pCL->pCLnext = pGE->pCLfree; pGE->pCLfree = pCL; } StopTask(pCE); ConsDown(pCE, FLAGFALSE, FLAGTRUE); for (ppCE = &(pGE->pCElist); *ppCE != (CONSENT *)0; ppCE = &((*ppCE)->pCEnext)) { if (*ppCE == pCE) { *ppCE = pCE->pCEnext; break; } } DestroyConsentUsers(&(pCE->ro)); DestroyConsentUsers(&(pCE->rw)); if (pCE->server != (char *)0) free(pCE->server); if (pCE->host != (char *)0) free(pCE->host); if (pCE->master != (char *)0) free(pCE->master); if (pCE->exec != (char *)0) free(pCE->exec); if (pCE->device != (char *)0) free(pCE->device); if (pCE->devicesubst != (char *)0) free(pCE->devicesubst); if (pCE->execsubst != (char *)0) free(pCE->execsubst); if (pCE->initsubst != (char *)0) free(pCE->initsubst); if (pCE->logfile != (char *)0) free(pCE->logfile); if (pCE->initcmd != (char *)0) free(pCE->initcmd); if (pCE->motd != (char *)0) free(pCE->motd); if (pCE->idlestring != (char *)0) free(pCE->idlestring); if (pCE->replstring != (char *)0) free(pCE->replstring); if (pCE->tasklist != (char *)0) free(pCE->tasklist); if (pCE->breaklist != (char *)0) free(pCE->breaklist); if (pCE->execSlave != (char *)0) free(pCE->execSlave); while (pCE->aliases != (NAMES *)0) { name = pCE->aliases->next; if (pCE->aliases->name != (char *)0) free(pCE->aliases->name); free(pCE->aliases); pCE->aliases = name; } FileClose(&pCE->fdlog); if (pCE->wbuf != (STRING *)0) DestroyString(pCE->wbuf); free(pCE); pGE->imembers--; } void DestroyGroup(GRPENT *pGE) { CONSENT *pCEtmp, *pCE; CONSCLIENT *pCLtmp, *pCL; if (pGE == (GRPENT *)0) return; CONDDEBUG((1, "DestroyGroup(): destroying group #%d (%d members)", pGE->id, pGE->imembers)); /* nuke each console (which kicks off clients) */ DestroyConsent(pGE, pGE->pCEctl); pCE = pGE->pCElist; while (pCE != (CONSENT *)0) { pCEtmp = pCE->pCEnext; DestroyConsent(pGE, pCE); pCE = pCEtmp; } /* now we can nuke the client structures */ pCL = pGE->pCLall; while (pCL != (CONSCLIENT *)0) { pCLtmp = pCL->pCLscan; DestroyClient(pCL); pCL = pCLtmp; } pCL = pGE->pCLfree; while (pCL != (CONSCLIENT *)0) { pCLtmp = pCL->pCLnext; DestroyClient(pCL); pCL = pCLtmp; } free(pGE); } #if HAVE_PAM int QuietConv(int num_msg, struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int i; struct pam_response *response = NULL; char *pcUser; char *pcWord; pcUser = ((char **)appdata_ptr)[0]; pcWord = ((char **)appdata_ptr)[1]; if (num_msg <= 0) return PAM_CONV_ERR; response = (struct pam_response *)calloc(num_msg, sizeof(struct pam_response)); if (response == (struct pam_response *)0) return PAM_CONV_ERR; for (i = 0; i < num_msg; i++) { response[i].resp_retcode = PAM_SUCCESS; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: response[i].resp = (pcUser != (char *)0 ? StrDup(pcUser) : (char *)0); break; case PAM_PROMPT_ECHO_OFF: response[i].resp = (pcWord != (char *)0 ? StrDup(pcWord) : (char *)0); break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: /* Ignore it... */ response[i].resp = NULL; break; default: /* Must be an error of some sort... */ free(response); return PAM_CONV_ERR; } } *resp = response; return PAM_SUCCESS; } #endif /* Is this passwd a match for this user's passwd? (gregf/ksb) * look up passwd in shadow file if we have to, if we are * given a special epass try it first. */ int CheckPass(char *pcUser, char *pcWord, FLAG empty_check) { if (pcWord == (char *)0) { pcWord = ""; } #if HAVE_PAM if (empty_check == FLAGTRUE) return AUTH_INVALID; int pam_error; char *appdata[2]; static pam_handle_t *pamh = (pam_handle_t *)0; struct pam_conv conv; appdata[0] = pcUser; appdata[1] = pcWord; conv.conv = &QuietConv; conv.appdata_ptr = (void *)&appdata; CONDDEBUG((1, "CheckPass(): pam_start(conserver,%s,...)", pcUser)); pam_error = pam_start("conserver", pcUser, &conv, &pamh); if (pam_error == PAM_SUCCESS) { pam_set_item(pamh, PAM_RHOST, "IHaveNoIdeaHowIGotHere"); CONDDEBUG((1, "CheckPass(): pam_authenticate(%s)", pcUser)); pam_error = pam_authenticate(pamh, PAM_SILENT); if (pam_error == PAM_SUCCESS) { CONDDEBUG((1, "CheckPass(): pam_acct_mgmt(%s)", pcUser)); pam_error = pam_acct_mgmt(pamh, PAM_SILENT); if (pam_error != PAM_SUCCESS) { Error("CheckPass(): PAM failure(%s): %s", pcUser, pam_strerror(pamh, pam_error)); } } else if (pam_error != PAM_AUTH_ERR) { Error("CheckPass(): PAM failure(%s): %s", pcUser, pam_strerror(pamh, pam_error)); } CONDDEBUG((1, "CheckPass(): pam_end(%s)", pcUser)); pam_end(pamh, pam_error); if (pam_error == PAM_ABORT) /* things just got real bad */ fSawGoAway = 1; } else { Error("CheckPass(): PAM failure(%s): %s", pcUser, pam_strerror(pamh, pam_error)); } if (pam_error == PAM_SUCCESS) return AUTH_SUCCESS; if (pam_error == PAM_USER_UNKNOWN) return AUTH_NOUSER; return AUTH_INVALID; #else /* getpw*() */ struct passwd *pwd; int retval = AUTH_SUCCESS; char *pass; # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM struct pr_passwd *prpwd; # endif # if HAVE_GETSPNAM struct spwd *spwd; # endif if ((pwd = getpwnam(pcUser)) == (struct passwd *)0) { CONDDEBUG((1, "CheckPass(): getpwnam(%s): %s", pcUser, strerror(errno))); retval = AUTH_NOUSER; goto finished_pass; } pass = pwd->pw_passwd; # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM if (iscomsec()) { CONDDEBUG((1, "CheckPass(): trusted password check")); if ((prpwd = getprpwnam(pcUser)) == (struct pr_passwd *)0) { CONDDEBUG((1, "CheckPass(): getprpwnam(%s): %s", pcUser, strerror(errno))); retval = AUTH_NOUSER; goto finished_pass; } pass = prpwd->ufld.fd_encrypt; } # endif # if HAVE_GETSPNAM if ('x' == pass[0] && '\000' == pass[1]) { CONDDEBUG((1, "CheckPass(): shadow password check")); if ((spwd = getspnam(pcUser)) == (struct spwd *)0) { CONDDEBUG((1, "CheckPass(): getspnam(%s): %s", pcUser, strerror(errno))); retval = AUTH_NOUSER; goto finished_pass; } pass = spwd->sp_pwdp; } # endif if (pass[0] == '\000' && pcWord[0] == '\000') { retval = AUTH_SUCCESS; /* let empty password match */ } else { char *encrypted; char *salt; if (pass[0] == '\000') salt = "XX"; else salt = pass; # if HAVE_ISCOMSEC && HAVE_BIGCRYPT if (iscomsec()) encrypted = bigcrypt(pcWord, salt); else # endif encrypted = crypt(pcWord, salt); if ((strcmp(pass, encrypted) != 0)) { CONDDEBUG((1, "CheckPass(): password check failed (%s)", pass)); retval = AUTH_INVALID; } } finished_pass: endpwent(); # if HAVE_ISCOMSEC && HAVE_GETPRPWNAM if (iscomsec()) endprpwent(); # endif # if HAVE_GETSPNAM endspent(); # endif return retval; #endif /* getpw*() */ } /* on an HUP close and re-open log files so lop can trim them (ksb) * and reread the configuration file * lucky for us: log file fd's can change async from the group driver! */ static RETSIGTYPE FlagSawChldHUP(int sig) { fSawChldHUP = 1; #if !HAVE_SIGACTION SimpleSignal(SIGHUP, FlagSawChldHUP); #endif } /* on an USR2 close and re-open log files so lop can trim them (ksb) * lucky for us: log file fd's can change async from the group driver! */ static RETSIGTYPE FlagSawChldUSR2(int sig) { fSawChldUSR2 = 1; #if !HAVE_SIGACTION SimpleSignal(SIGUSR2, FlagSawChldUSR2); #endif } void ConsoleError(CONSENT *pCE) { if (pCE->autoreinit != FLAGTRUE) { ConsDown(pCE, FLAGTRUE, FLAGTRUE); } else { /* Try an initial reconnect */ Msg("[%s] automatic reinitialization", pCE->server); ConsInit(pCE); /* If we didn't succeed, try again later */ if (!pCE->fup) pCE->autoReUp = 1; } } static void ReOpen(GRPENT *pGE) { CONSENT *pCE; if ((GRPENT *)0 == pGE) { return; } for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if ((CONSFILE *)0 == pCE->fdlog) { continue; } FileClose(&pCE->fdlog); if ((CONSFILE *)0 == (pCE->fdlog = FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, 0644))) { Error("[%s] FileOpen(%s): %s: forcing down", pCE->server, pCE->logfile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); continue; } } } static RETSIGTYPE FlagReUp(int sig) { fSawReUp = 1; #if !HAVE_SIGACTION SimpleSignal(SIGUSR1, FlagReUp); #endif } static struct delay { char *host; time_t last; struct delay *next; } *delays = (struct delay *)0; /* returns zero if the delay has been reached, otherwise returns * the time when the next init should happen */ static time_t InitDelay(CONSENT *pCE) { char *l; struct delay *d; if (pCE->host != (char *)0) l = pCE->host; else l = ""; for (d = delays; d != (struct delay *)0; d = d->next) { if (strcmp(l, d->host) == 0) { if ((time((time_t *)0) - d->last) >= config->initdelay) { return (time_t)0; } else return d->last + config->initdelay; } } return (time_t)0; } static void UpdateDelay(CONSENT *pCE) { char *l; struct delay *d; if (pCE->host != (char *)0) l = pCE->host; else l = ""; for (d = delays; d != (struct delay *)0; d = d->next) { if (strcmp(l, d->host) == 0) { d->last = time((time_t *)0); return; } } if ((d = (struct delay *)malloc(sizeof(struct delay))) == (struct delay *)0) OutOfMem(); if ((d->host = StrDup(l)) == (char *)0) OutOfMem(); d->last = time((time_t *)0); d->next = delays; delays = d; } static void ReUp(GRPENT *pGE, short automatic) { CONSENT *pCE; int autoReUp; time_t tyme; short retry; static short autoup = 0; short wasAuto = 0; if ((GRPENT *)0 == pGE) return; tyme = time((time_t *)0); if ((automatic == 1) && (tyme < timers[T_AUTOUP])) return; if ((automatic == 2) && (!config->reinitcheck || (tyme < timers[T_REINIT]))) return; if (automatic == -1) wasAuto = autoup; autoup = 0; /* we loop here 'cause the init process could take a bit of time * (depending on how many things we init in the run through the * consoles) and we might be able to then initialize more stuff. * we'll eventually run through too fast, run out of consoles, or * have a big enough delay to go back to the main loop. */ do { retry = 0; for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { short updateDelay = 0; if (pCE->fup || pCE->ondemand == FLAGTRUE || (automatic == 1 && !pCE->autoReUp)) continue; if (config->initdelay > 0) { time_t t; if ((t = InitDelay(pCE)) > 0) { if (timers[T_INITDELAY] == (time_t)0 || timers[T_INITDELAY] > t) timers[T_INITDELAY] = t; continue; } else { updateDelay = retry = 1; } } autoReUp = pCE->autoReUp; if (automatic > 0 || wasAuto) { Msg("[%s] automatic reinitialization", pCE->server); autoup = 1; } ConsInit(pCE); if (updateDelay) UpdateDelay(pCE); if (!pCE->fup && automatic > 0) pCE->autoReUp = autoReUp; } } while (retry); /* update all the timers */ if (automatic == 0 || automatic == 2) { if (config->reinitcheck) timers[T_REINIT] = tyme + (config->reinitcheck * 60); } if (!fNoautoreup) timers[T_AUTOUP] = tyme + 60; } void TagLogfile(const CONSENT *pCE, char *fmt, ...) { va_list ap; va_start(ap, fmt); if ((pCE == (CONSENT *)0) || (pCE->fdlog == (CONSFILE *)0)) return; FileWrite(pCE->fdlog, FLAGTRUE, "[-- ", -1); FileVWrite(pCE->fdlog, FLAGTRUE, fmt, ap); FilePrint(pCE->fdlog, FLAGFALSE, " -- %s]\r\n", StrTime((time_t *)0)); va_end(ap); } void TagLogfileAct(const CONSENT *pCE, char *fmt, ...) { va_list ap; va_start(ap, fmt); if ((pCE == (CONSENT *)0) || (pCE->fdlog == (CONSFILE *)0) || (pCE->activitylog != FLAGTRUE)) return; FileWrite(pCE->fdlog, FLAGTRUE, "[-- ", -1); FileVWrite(pCE->fdlog, FLAGTRUE, fmt, ap); FilePrint(pCE->fdlog, FLAGFALSE, " -- %s]\r\n", StrTime((time_t *)0)); va_end(ap); } static void RollLogs(GRPENT *pGE) { CONSENT *pCE; struct stat stLog; char *t = (char *)0; char timestr[40]; time_t tyme = (time_t)0; short maxset = 0; char buf[4096]; int roll = 0; int r = 0; CONSFILE *old; if ((GRPENT *)0 == pGE) return; for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if (pCE->logfilemax == 0) continue; maxset = 1; if (pCE->fdlog == (CONSFILE *)0) continue; if (FileStat(pCE->fdlog, &stLog) != 0) { CONDDEBUG((1, "RollLogs(): FileStat(%d) failed", FileFDNum(pCE->fdlog))); continue; } if (stLog.st_size < pCE->logfilemax) continue; if (pCE->logfilemax > 1024) { if (pCE->logfilemax > 0x100000) Msg("[%s] logfile exceeds %dMB: rolling", pCE->server, pCE->logfilemax / 0x100000); else Msg("[%s] logfile exceeds %dKB: rolling", pCE->server, pCE->logfilemax / 1024); } if (pCE->logfilemax < 4000) roll = 100; else if (pCE->logfilemax > 160000) roll = 4000; else roll = pCE->logfilemax * 0.025; r = 0; if (FileSeek(pCE->fdlog, stLog.st_size - roll, SEEK_SET) > 0) { if ((r = FileRead(pCE->fdlog, buf, 4096)) > 0) { if (r == roll) { for (; r != 0 && buf[roll - r] != '\n'; r--); r--; /* go beyond \n */ } else r = 0; } } if (tyme == (time_t)0) { tyme = time((time_t *)0); strftime(timestr, sizeof(timestr), "-%Y%m%d-%H%M%S", gmtime(&tyme)); } BuildTmpString((char *)0); t = BuildTmpStringPrint("%s%s", pCE->logfile, timestr); if (rename(pCE->logfile, t) != 0) { Error("[%s] RollLogs(): rename(%s,%s) failed: %s", pCE->server, pCE->logfile, t, strerror(errno)); continue; } old = pCE->fdlog; if ((pCE->fdlog = FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, 0644)) == (CONSFILE *)0) { FileClose(&old); Error("[%s] RollLogs(): open(%s): %s: forcing down", pCE->server, pCE->logfile, strerror(errno)); ConsDown(pCE, FLAGTRUE, FLAGTRUE); continue; } if (r > 0) { FileWrite(pCE->fdlog, FLAGFALSE, buf + roll - r, r); ftruncate(FileFDNum(old), stLog.st_size - r); } FileClose(&old); } if (tyme != (time_t)0) BuildTmpString((char *)0); if (maxset == 0) timers[T_ROLL] = (time_t)0; else { if (timers[T_ROLL] == (time_t)0) /* try and spread processes out a bit */ timers[T_ROLL] = time((time_t *)0) + 300 + (pGE->id * 7) % 60; else timers[T_ROLL] = time((time_t *)0) + 300; } } static void Mark(GRPENT *pGE) { time_t tyme; CONSENT *pCE; static STRING *out = (STRING *)0; if ((GRPENT *)0 == pGE) return; if (out == (STRING *)0) out = AllocString(); BuildString((char *)0, out); /* [-- MARK -- `date`] */ BuildStringPrint(out, "[-- MARK -- %s]\r\n", StrTime(&tyme)); timers[T_MARK] = (time_t)0; for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if (pCE->nextMark > 0) { if (tyme >= pCE->nextMark) { if ((CONSFILE *)0 != pCE->fdlog) { CONDDEBUG((1, "Mark(): [-- MARK --] stamp added to %s", pCE->logfile)); FileWrite(pCE->fdlog, FLAGFALSE, out->string, out->used - 1); } /* Add as many pCE->mark values as necessary so that we move * beyond the current time. */ pCE->nextMark += (((tyme - pCE->nextMark) / pCE->mark) + 1) * pCE->mark; } if (timers[T_MARK] == (time_t)0 || timers[T_MARK] > pCE->nextMark) timers[T_MARK] = pCE->nextMark; } } } void WriteLog(CONSENT *pCE, char *s, int len) { int i = 0; int j; static STRING *buf = (STRING *)0; if ((CONSFILE *)0 == pCE->fdlog) { return; } if (pCE->mark >= 0) { /* no line marking */ FileWrite(pCE->fdlog, FLAGFALSE, s, len); return; } if (buf == (STRING *)0) buf = AllocString(); BuildString((char *)0, buf); for (j = 0; j < len; j++) { if (pCE->nextMark == 0) { FileWrite(pCE->fdlog, FLAGTRUE, s + i, j - i); i = j; if (buf->used <= 1) BuildStringPrint(buf, "[%s]", StrTime((time_t *)0)); FileWrite(pCE->fdlog, FLAGTRUE, buf->string, buf->used - 1); pCE->nextMark = pCE->mark; } if (s[j] == '\n') { CONDDEBUG((1, "WriteLog(): [%s] found newline (nextMark=%d, mark=%d)", pCE->server, pCE->nextMark, pCE->mark)); pCE->nextMark++; } } if (i < j) { FileWrite(pCE->fdlog, FLAGTRUE, s + i, j - i); } FileWrite(pCE->fdlog, FLAGFALSE, (char *)0, 0); } static RETSIGTYPE FlagGoAway(int sig) { fSawGoAway = 1; #if !HAVE_SIGACTION SimpleSignal(SIGTERM, FlagGoAway); #endif } /* yep, basically the same...ah well, maybe someday */ static RETSIGTYPE FlagGoAwayAlso(int sig) { fSawGoAway = 1; #if !HAVE_SIGACTION SimpleSignal(SIGINT, FlagGoAwayAlso); #endif } static RETSIGTYPE FlagReapVirt(int sig) { fSawReapVirt = 1; #if !HAVE_SIGACTION SimpleSignal(SIGCHLD, FlagReapVirt); #endif } /* on a TERM we have to cleanup utmp entries (ask ptyd to do it) (ksb) */ void DeUtmp(GRPENT *pGE, int sfd) { CONSENT *pCE; #if USE_UNIX_DOMAIN_SOCKETS struct sockaddr_un lstn_port; socklen_t so; so = sizeof(lstn_port); if (getsockname(sfd, (struct sockaddr *)&lstn_port, &so) != -1) unlink(lstn_port.sun_path); #endif /* shut down the socket */ close(sfd); /* say Bye to all connections */ if ((GRPENT *)0 != pGE) { DisconnectAllClients(pGE, "[-- Console server shutting down --]\r\n"); for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { ConsDown(pCE, FLAGFALSE, FLAGTRUE); } } if (unifiedlog != (CONSFILE *)0) FileClose(&unifiedlog); DumpDataStructures(); endpwent(); Bye(EX_OK); } /* virtual console procs are our kids, when they die we get a CHLD (ksb) * which will send us here to clean up the exit code. The lack of a * reader on the pseudo will cause us to notice the death in Kiddie... */ static void ReapVirt(GRPENT *pGE) { pid_t pid; int UWbuf; CONSENT *pCE; while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) { if (0 == pid) { break; } /* stopped child is just continued */ if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) { Msg("child pid %lu: stopped, sending SIGCONT", (unsigned long)pid); continue; } if ((GRPENT *)0 == pGE) { continue; } for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if (pid == pCE->initpid) { if (WIFEXITED(UWbuf)) Msg("[%s] initcmd terminated: pid %lu: exit(%d)", pCE->server, pid, WEXITSTATUS(UWbuf)); if (WIFSIGNALED(UWbuf)) Msg("[%s] initcmd terminated: pid %lu: signal(%d)", pCE->server, pid, WTERMSIG(UWbuf)); TagLogfileAct(pCE, "initcmd terminated"); pCE->initpid = 0; StopInit(pCE); break; } if (pid == pCE->taskpid) { CONSCLIENT *pCL; if (WIFEXITED(UWbuf)) { Msg("[%s] task terminated: pid %lu: exit(%d)", pCE->server, pid, WEXITSTATUS(UWbuf)); if (pCE->tasklog == FLAGTRUE) TagLogfile(pCE, "task terminated: pid %lu: exit(%d)", pid, WEXITSTATUS(UWbuf)); /* tell all how it ended */ for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon) { FilePrint(pCL->fd, FLAGFALSE, "[task terminated: exit(%d)]\r\n", WEXITSTATUS(UWbuf)); } } } if (WIFSIGNALED(UWbuf)) { Msg("[%s] task terminated: pid %lu: signal(%d)", pCE->server, pid, WTERMSIG(UWbuf)); if (pCE->tasklog == FLAGTRUE) TagLogfile(pCE, "task terminated: pid %lu: signal(%d)", pid, WTERMSIG(UWbuf)); /* tell all how it ended */ for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon) { FilePrint(pCL->fd, FLAGFALSE, "[task terminated: signal(%d)]\r\n", WTERMSIG(UWbuf)); } } } pCE->taskpid = 0; StopTask(pCE); break; } if (pid != pCE->ipid) continue; if (WIFEXITED(UWbuf)) Msg("[%s] exit(%d)", pCE->server, WEXITSTATUS(UWbuf)); if (WIFSIGNALED(UWbuf)) Msg("[%s] signal(%d)", pCE->server, WTERMSIG(UWbuf)); if (pCE->autoreinit != FLAGTRUE && !(WIFEXITED(UWbuf) && WEXITSTATUS(UWbuf) == 0)) { ConsDown(pCE, FLAGTRUE, FLAGFALSE); } else { /* Try an initial reconnect */ Msg("[%s] automatic reinitialization", pCE->server); ConsInit(pCE); /* If we didn't succeed, try again later */ if (!pCE->fup) pCE->autoReUp = 1; } break; } } } int CheckPasswd(CONSCLIENT *pCL, char *pw_string, FLAG empty_check) { FILE *fp; int iLine = 0; char *this_pw, *user; #if USE_UNIX_DOMAIN_SOCKETS # if TRUST_UDS_CRED struct UDS_CRED_STYPE client_cred; socklen_t client_cred_len = sizeof(client_cred); memset(&client_cred, 0, sizeof(client_cred)); int err; if ((err = getsockopt(pCL->fd->fd, SOL_SOCKET, UDS_CRED_SO, &client_cred, &client_cred_len)) == -1) { Error("CheckPasswd(): getsockopt(CREDENTIALS) failed: %s", strerror(err)); } else if (client_cred.pid != 0) { /* Does pCL->username->string and client_cred.uid match ? */ struct passwd *pwd = (struct passwd *)0; /* we allow root to impersonate anyone */ if (client_cred.UDS_CRED_UID == 0) return AUTH_SUCCESS; if ((pwd = getpwnam(pCL->username->string)) != NULL) { if (pwd->pw_uid == client_cred.UDS_CRED_UID) { Verbose("user %s authenticated", pCL->username->string); return AUTH_SUCCESS; } } } # endif #endif if ((fp = fopen(config->passwdfile, "r")) == (FILE *)0) { if (CheckPass(pCL->username->string, pw_string, empty_check) == AUTH_SUCCESS) { Verbose("user %s authenticated", pCL->acid->string); return AUTH_SUCCESS; } } else { char *wholeLine; static STRING *saveLine = (STRING *)0; if (saveLine == (STRING *)0) saveLine = AllocString(); BuildString((char *)0, saveLine); while ((wholeLine = ReadLine(fp, saveLine, &iLine)) != (char *)0) { PruneSpace(wholeLine); /*printf("whole=<%s>\n", wholeLine); */ if (wholeLine[0] == '\000') continue; if ((char *)0 == (this_pw = strchr(wholeLine, ':'))) { Error("CheckPasswd(): %s(%d) bad password line `%s'", config->passwdfile, iLine, wholeLine); continue; } *this_pw++ = '\000'; user = PruneSpace(wholeLine); this_pw = PruneSpace(this_pw); if (strcmp(user, "*any*") != 0 && strcmp(user, pCL->username->string) != 0) continue; /* If one is empty and the other isn't, instant failure */ if ((*this_pw == '\000' && *pw_string != '\000') || (*this_pw != '\000' && *pw_string == '\000')) { break; } if ((*this_pw == '\000' && *pw_string == '\000') || ((strcmp(this_pw, "*passwd*") == 0) ? (CheckPass(pCL->username->string, pw_string, empty_check) == AUTH_SUCCESS) : (strcmp(this_pw, crypt(pw_string, this_pw)) == 0))) { Verbose("user %s authenticated", pCL->acid->string); fclose(fp); return AUTH_SUCCESS; } break; } fclose(fp); } return AUTH_INVALID; } static char * IdleTyme(long tyme) { long hours, minutes; static STRING *timestr = (STRING *)0; if (timestr == (STRING *)0) timestr = AllocString(); minutes = tyme / 60; hours = minutes / 60; minutes = minutes % 60; BuildString((char *)0, timestr); if (hours < 24) BuildStringPrint(timestr, " %2ld:%02ld", hours, minutes); else if (hours < 24 * 2) BuildStringPrint(timestr, " 1 day"); else if (hours < 24 * 10) BuildStringPrint(timestr, "%1ld days", hours / 24); else BuildStringPrint(timestr, "%2lddays", hours / 24); return timestr->string; } void PutConsole(CONSENT *pCEServing, unsigned char c, int quote) { /* if we need to send an IAC char to a telnet-based port, quote * the thing (which means send two to the port). but, since we're * using IAC as a trigger for breaks and pauses, we have to load * two into the buffer here and two below (so two get sent). * * if we're just sending a IAC character in the raw, the 'quote' * flag will not be set and we'll put only two IAC chars into * the buffer (below)...which will result in one to the port. * * we're also tracking the first IAC character in the buffer with * the wbufIAC variable...that way we don't have to do a string * search every time we flush this thing ('cause it should be * rather infrequent to have an IAC char). * * quote == 0, raw - processed by conserver * quote == 1, console - processed by console * quote == 2, telnet - processed by telnet protocol * if console != telnet, 1 == 2 */ if (quote == 1 && pCEServing->type == HOST && pCEServing->raw != FLAGTRUE && c == IAC) { BuildStringChar((char)c, pCEServing->wbuf); if (pCEServing->wbufIAC == 0) pCEServing->wbufIAC = pCEServing->wbuf->used; BuildStringChar((char)c, pCEServing->wbuf); } /* if we're trying to send an IAC char, quote it in the buffer */ if (quote && c == IAC) { BuildStringChar((char)c, pCEServing->wbuf); if (pCEServing->wbufIAC == 0) pCEServing->wbufIAC = pCEServing->wbuf->used; } BuildStringChar((char)c, pCEServing->wbuf); if (c == IAC && pCEServing->wbufIAC == 0) pCEServing->wbufIAC = pCEServing->wbuf->used; CONDDEBUG((1, "PutConsole(): queued byte to console %s", pCEServing->server)); } void ExpandString(char *str, CONSENT *pCE, short breaknum) { char s; short backslash = 0; short cntrl = 0; char oct = '\000'; short octs = 0; if (str == (char *)0 || pCE == (CONSENT *)0) return; backslash = 0; cntrl = 0; while ((s = (*str++)) != '\000') { if (octs > 0 && octs < 3 && s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } if (octs != 0) { PutConsole(pCE, oct, 1); octs = 0; oct = '\000'; } if (backslash) { backslash = 0; if (s == 'a') s = '\a'; else if (s == 'b') s = '\b'; else if (s == 'f') s = '\f'; else if (s == 'n') s = '\n'; else if (s == 'r') s = '\r'; else if (s == 't') s = '\t'; else if (s == 'v') s = '\v'; else if (s == '^') s = '^'; else if (s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } else if (s == 'd') { PutConsole(pCE, IAC, 0); PutConsole(pCE, '0' + breaknum + (breaknum > 9 ? BREAKALPHAOFFSET : 0), 0); continue; } else if (s == 'z') { PutConsole(pCE, IAC, 0); PutConsole(pCE, BREAK, 0); continue; } PutConsole(pCE, s, 1); continue; } if (cntrl) { cntrl = 0; if (s == '?') s = 0x7f; /* delete */ else s = s & 0x1f; PutConsole(pCE, s, 1); continue; } if (s == '\\') { backslash = 1; continue; } if (s == '^') { cntrl = 1; continue; } PutConsole(pCE, s, 1); } if (octs != 0) PutConsole(pCE, oct, 1); if (backslash) PutConsole(pCE, '\\', 1); if (cntrl) PutConsole(pCE, '^', 1); } void SendBreak(CONSCLIENT *pCLServing, CONSENT *pCEServing, short bt) { CONSCLIENT *pCL; short waszero = 0; if (bt < 0 || bt > BREAKLISTSIZE) { FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); return; } if (bt == 0) { bt = pCEServing->breakNum; waszero = 1; } if (bt == 0 || breakList[bt - 1].seq->used <= 1) { FileWrite(pCLServing->fd, FLAGFALSE, "undefined]\r\n", -1); return; } if (breakList[bt - 1].confirm == FLAGTRUE) { switch (pCLServing->confirmed) { case FLAGUNKNOWN: FileWrite(pCLServing->fd, FLAGFALSE, "confirm? [y/N] ", -1); pCLServing->confirmed = FLAGFALSE; pCLServing->cState = S_HALT1; pCLServing->iState = S_CONFIRM; pCLServing->cOption = '0' + bt; return; case FLAGFALSE: FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); pCLServing->confirmed = FLAGUNKNOWN; return; case FLAGTRUE: pCLServing->confirmed = FLAGUNKNOWN; } } ExpandString(breakList[bt - 1].seq->string, pCEServing, bt); FileWrite(pCLServing->fd, FLAGFALSE, "sent]\r\n", -1); /* tell all who sent it */ for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL == pCLServing) continue; if (pCL->fcon) { FilePrint(pCL->fd, FLAGFALSE, "[break sent by %s]\r\n", pCLServing->acid->string); } } if (pCEServing->breaklog == FLAGTRUE) { if (waszero) { TagLogfile(pCEServing, "break #0(%c) sent -- `%s'", '0' + bt + (bt > 9 ? BREAKALPHAOFFSET : 0), breakList[bt - 1].seq->string); } else { TagLogfile(pCEServing, "break #%c sent -- `%s'", '0' + bt + (bt > 9 ? BREAKALPHAOFFSET : 0), breakList[bt - 1].seq->string); } } } static int StartTask(CONSENT *pCE, char *cmd, uid_t uid, gid_t gid) { int i; extern char **environ; char *pcShell, **ppcArgv; extern int FallBack(char **, int *); char *execSlave = (char *)0; /* pseudo-device slave side */ int execSlaveFD; /* fd of slave side */ int cofile; if ((cofile = FallBack(&execSlave, &execSlaveFD)) == -1) { Error("[%s] StartTask(): failed to allocate pseudo-tty: %s", pCE->server, strerror(errno)); return -1; } if (execSlave != (char *)0) free(execSlave); fflush(stdout); fflush(stderr); switch (pCE->taskpid = fork()) { case -1: pCE->taskpid = 0; return -1; case 0: break; default: /* server */ close(execSlaveFD); if ((pCE->taskfile = FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) { close(cofile); Error("[%s] FileOpenFD(%d,simpleFile) failed", pCE->server, cofile); return -1; } Msg("[%s] task started: pid %lu", pCE->server, (unsigned long)pCE->taskpid); FD_SET(cofile, &rinit); if (maxfd < cofile + 1) maxfd = cofile + 1; fflush(stderr); return 0; } close(cofile); #if HAVE_SETSID setsid(); #else tcsetpgrp(0, getpid()); #endif close(1); if (dup(execSlaveFD) != 1) { Error("[%s] StartTask(): fd 1 sync error", pCE->server); exit(EX_OSERR); } close(2); if (dup(execSlaveFD) != 2) { exit(EX_OSERR); } /* no stdin */ close(0); close(execSlaveFD); /* setup new process with clean file descriptors */ #if HAVE_CLOSEFROM closefrom(3); #else i = GetMaxFiles(); for ( /* i above */ ; --i > 2;) { close(i); } #endif if (geteuid() == 0) { if (gid != 0) setgid(gid); if (uid != 0) setuid(uid); } SetupTty(pCE, 1); pcShell = "/bin/sh"; static char *apcArgv[] = { "/bin/sh", "-ce", (char *)0, (char *)0 }; apcArgv[2] = cmd; ppcArgv = apcArgv; execve(pcShell, ppcArgv, environ); Error("[%s] execve(): %s", pCE->server, strerror(errno)); exit(EX_OSERR); return -1; } void InvokeTask(CONSCLIENT *pCLServing, CONSENT *pCEServing, char id) { TASKS *t = (TASKS *)0; char *cmd; if ((id < '0' || id > '9') && (id < 'a' || id > 'z')) { FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); return; } if (pCEServing->taskpid != 0) { FileWrite(pCLServing->fd, FLAGFALSE, "aborted - task running]\r\n", -1); return; } if ((char *)0 != strchr(pCEServing->tasklist, id) || (char *)0 != strchr(pCEServing->tasklist, '*')) { for (t = taskList; t != (TASKS *)0; t = t->next) { if (t->id == id) break; } } if (t == (TASKS *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "undefined]\r\n", -1); return; } if (t->confirm == FLAGTRUE) { switch (pCLServing->confirmed) { case FLAGUNKNOWN: FileWrite(pCLServing->fd, FLAGFALSE, "confirm? [y/N] ", -1); pCLServing->confirmed = FLAGFALSE; pCLServing->cState = S_TASK; pCLServing->iState = S_CONFIRM; pCLServing->cOption = id; return; case FLAGFALSE: FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); pCLServing->confirmed = FLAGUNKNOWN; return; case FLAGTRUE: pCLServing->confirmed = FLAGUNKNOWN; } } if ((cmd = StrDup(t->cmd->string)) == (char *)0) OutOfMem(); if (t->subst != (char *)0) { substData->data = (void *)pCEServing; ProcessSubst(substData, &cmd, (char **)0, (char *)0, t->subst); } if (StartTask(pCEServing, cmd, t->uid, t->gid) == 0) { CONSCLIENT *pCL; char *detail; FilePrint(pCLServing->fd, FLAGFALSE, "`%c' started]\r\n", id); detail = t->descr->used > 1 ? t->descr->string : cmd; /* tell all who started it */ for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL == pCLServing) continue; if (pCL->fcon) { FilePrint(pCL->fd, FLAGFALSE, "[task `%s' started by %s]\r\n", detail, pCLServing->acid->string); } } if (pCEServing->tasklog == FLAGTRUE) { TagLogfile(pCEServing, "task started: pid %lu -- `%s'", pCEServing->taskpid, cmd); } } else { FileWrite(pCLServing->fd, FLAGFALSE, "failure]\r\n", -1); } free(cmd); } #if HAVE_OPENSSL int AttemptSSL(CONSCLIENT *pCL) { int fdnum; SSL *ssl; int retval; fdnum = FileFDNum(pCL->fd); if (ctx == (SSL_CTX *)0) { Error("AttemptSSL(): WTF? The SSL context disappeared?!?!?"); Bye(EX_SOFTWARE); } if (!(ssl = SSL_new(ctx))) { Error("AttemptSSL(): SSL_new() failed for fd %d", fdnum); return 0; } FileSetSSL(pCL->fd, ssl); SSL_set_accept_state(ssl); SSL_set_fd(ssl, fdnum); if ((retval = FileSSLAccept(pCL->fd)) < 0) { Error("AttemptSSL(): FileSSLAccept() failed for fd %d", fdnum); return 0; } else if (retval == 0) pCL->ioState = INSSLACCEPT; return 1; } #endif #if HAVE_GSSAPI int AttemptGSSAPI(CONSCLIENT *pCL) { int nr, ret = 0; char buf[1024]; gss_buffer_desc sendtok, recvtok, dbuf; gss_ctx_id_t gssctx = GSS_C_NO_CONTEXT; OM_uint32 stmaj, stmin, mctx, dmin; gss_name_t user = 0; if ((nr = FileRead(pCL->fd, buf, sizeof(buf))) <= 0) { return nr; } recvtok.value = buf; recvtok.length = nr; stmaj = gss_accept_sec_context(&stmin, &gssctx, gss_mycreds, &recvtok, NULL, &user, NULL, &sendtok, NULL, NULL, NULL); switch (stmaj) { case GSS_S_COMPLETE: FileSetQuoteIAC(pCL->fd, FLAGFALSE); FileWrite(pCL->fd, FLAGFALSE, sendtok.value, sendtok.length); FileSetQuoteIAC(pCL->fd, FLAGTRUE); pCL->iState = S_NORMAL; gss_release_buffer(NULL, &sendtok); BuildString((char *)0, pCL->username); BuildString((char *)0, pCL->acid); stmaj = gss_display_name(&stmin, user, &dbuf, NULL); BuildStringN(dbuf.value, dbuf.length, pCL->username); BuildStringN(dbuf.value, dbuf.length, pCL->acid); BuildStringChar('@', pCL->acid); BuildString(pCL->peername->string, pCL->acid); gss_release_name(&stmin, &user); gss_release_buffer(NULL, &dbuf); ret = 1; break; case GSS_S_CREDENTIALS_EXPIRED: /* reacquire creds and try again */ Error("Credentials expired"); break; default: gss_display_status(&dmin, stmaj, GSS_C_GSS_CODE, GSS_C_NULL_OID, &mctx, &dbuf); Error("GSSAPI didn't work, %*s", dbuf.length, dbuf.value); ret = -1; } return ret; } #endif CONSENT * HuntForConsole(GRPENT *pGE, char *name) { /* try to find a given console * we assume all the right checks for ambiguity * were already done by the master process, so * the first match should be what the user wants */ CONSENT *pCE = (CONSENT *)0; if (name == (char *)0) return pCE; for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { NAMES *n = (NAMES *)0; if (strcasecmp(name, pCE->server) == 0) break; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { if (strcasecmp(name, n->name) == 0) break; } if (n != (NAMES *)0) break; } if (pCE == (CONSENT *)0 && config->autocomplete == FLAGTRUE) { NAMES *n = (NAMES *)0; int len = strlen(name); for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if (strncasecmp(name, pCE->server, len) == 0) break; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { if (strncasecmp(name, n->name, len) == 0) break; } if (n != (NAMES *)0) break; } } return pCE; } void CommandAttach(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { CONSCLIENT *pCL; ClientWantsWrite(pCLServing); if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, "console is read-only]\r\n", -1); } else if (pCLServing->fro) { FileWrite(pCLServing->fd, FLAGFALSE, "read-only]\r\n", -1); } else if ((CONSCLIENT *)0 == (pCL = pCEServing->pCLwr)) { pCEServing->pCLwr = pCLServing; pCLServing->fwr = 1; if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, "attached (nologging)]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "attached]\r\n", -1); } TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } else if (pCL == pCLServing) { if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, "ok (nologging)]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1); } } else { FilePrint(pCLServing->fd, FLAGFALSE, "no, %s is attached]\r\n", pCL->acid->string); } } void CommandChangeFlow(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { struct termios sbuf; int cofile; if (!pCLServing->fwr) { FileWrite(pCLServing->fd, FLAGFALSE, "attach to change flow]\r\n", -1); return; } if (pCEServing->type != DEVICE && pCEServing->type != EXEC) { FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1); return; } cofile = FileFDNum(pCEServing->cofile); if (-1 == tcgetattr(cofile, &sbuf)) { FileWrite(pCLServing->fd, FLAGFALSE, "failed]\r\n", -1); return; } if (0 != (sbuf.c_iflag & IXON)) { sbuf.c_iflag &= ~(IXON); } else { sbuf.c_iflag |= IXON; } if (-1 == tcsetattr(cofile, TCSANOW, &sbuf)) { FileWrite(pCLServing->fd, FLAGFALSE, "failed]\r\n", -1); return; } if ((sbuf.c_iflag & IXON) == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "ixon OFF]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "ixon ON]\r\n", -1); } } void CommandDown(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { CONSCLIENT *pCL; if (!pCLServing->fwr) { FileWrite(pCLServing->fd, FLAGFALSE, "attach to down line]\r\n", -1); return; } if (!pCEServing->fup) { FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1); return; } ConsDown(pCEServing, FLAGFALSE, FLAGFALSE); FileWrite(pCLServing->fd, FLAGFALSE, "line down]\r\n", -1); /* tell all who closed it */ for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL == pCLServing) continue; if (pCL->fcon) { FilePrint(pCL->fd, FLAGFALSE, "[line down by %s]\r\n", pCLServing->acid->string); } } } void CommandExamine(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme, char *args) { CONSENT *pCE; if (args == (char *)0) pCE = pGE->pCElist; else pCE = HuntForConsole(pGE, args); for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { char *d = (char *)0; char *b = (char *)0; char p = '\000'; switch (pCE->type) { case EXEC: d = pCE->execSlave; b = "Local"; p = ' '; break; case DEVICE: d = BuildTmpStringPrint("%s@%s", pCE->device, pCE->master); b = pCE->baud->acrate; p = pCE->parity->key[0]; break; #if HAVE_FREEIPMI case IPMI: d = BuildTmpStringPrint("%s", pCE->host); b = "IPMI"; p = ' '; break; #endif case HOST: d = BuildTmpStringPrint("%s/%hu", pCE->host, pCE->netport); b = "Netwk"; p = ' '; break; case NOOP: d = "NOOP"; b = "NOOP"; p = ' '; break; case UDS: d = pCE->uds; b = "UDS"; p = ' '; break; case UNKNOWNTYPE: /* shut up gcc */ break; } FilePrint(pCLServing->fd, FLAGFALSE, " %-24.24s on %-32.32s at %6.6s%c\r\n", pCE->server, d, b, p); if (args != (char *)0) break; } } void CommandForce(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { CONSCLIENT *pCL; ClientWantsWrite(pCLServing); if (pCLServing->fro) { FileWrite(pCLServing->fd, FLAGFALSE, "read-only]\r\n", -1); return; } else if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, "console is read-only]\r\n", -1); return; } if ((CONSCLIENT *)0 != (pCL = pCEServing->pCLwr)) { if (pCL == pCLServing) { if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, "ok (nologging)]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "ok]\r\n", -1); } return; } if (pCEServing->nolog) { FilePrint(pCLServing->fd, FLAGFALSE, "bumped %s (nologging)]\r\n", pCL->acid->string); } else { FilePrint(pCLServing->fd, FLAGFALSE, "bumped %s]\r\n", pCL->acid->string); } AbortAnyClientExec(pCL); BumpClient(pCEServing, (char *)0); ClientWantsWrite(pCL); if (pCL->fcon) FilePrint(pCL->fd, FLAGFALSE, "\r\n[forced to `spy' mode by %s]\r\n", pCLServing->acid->string); TagLogfileAct(pCEServing, "%s bumped %s", pCLServing->acid->string, pCL->acid->string); } else { if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, "attached (nologging)]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "attached]\r\n", -1); } TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } pCEServing->pCLwr = pCLServing; pCLServing->fwr = 1; } void CommandGroup(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme, char *args) { CONSCLIENT *pCL; CONSENT *pCE; pCE = HuntForConsole(pGE, args); /* we do not show the ctl console * else we'd get the client always */ for (pCL = pGE->pCLall; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLscan) { if (pGE->pCEctl == pCL->pCEto) continue; if (pCE != (CONSENT *)0 && pCL->pCEto != pCE) continue; FilePrint(pCLServing->fd, FLAGFALSE, " %-32.32s %c %-7.7s %6s %s\r\n", pCL->acid->string, pCL == pCLServing ? '*' : ' ', pCL->fcon ? (pCL->fwr ? "attach" : "spy") : "stopped", IdleTyme(tyme - pCL->typetym), pCL->pCEto->server); } } void CommandHosts(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme, char *args) { CONSENT *pCE; if (args == (char *)0) pCE = pGE->pCElist; else pCE = HuntForConsole(pGE, args); for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { FilePrint(pCLServing->fd, FLAGFALSE, " %-24.24s %c %-4.4s %-.40s\r\n", pCE->server, pCE == pCEServing ? '*' : ' ', (pCE->fup && pCE->ioState == ISNORMAL) ? (pCE->initfile == (CONSFILE *) 0 ? "up" : "init") : "down", pCE->pCLwr ? pCE->pCLwr->acid-> string : pCE->pCLon ? "" : ""); if (args != (char *)0) break; } } void CommandInfo(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme, char *args) { CONSENT *pCE; CONSCLIENT *pCL; if (args == (char *)0) pCE = pGE->pCElist; else pCE = HuntForConsole(pGE, args); for (; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { int comma = 0; char *s = (char *)0; FilePrint(pCLServing->fd, FLAGTRUE, "%s:%s,%lu,%hu:", pCE->server, myHostname, (unsigned long)thepid, pGE->port); switch (pCE->type) { case EXEC: FilePrint(pCLServing->fd, FLAGTRUE, "|:%s,%lu,%s,%d:", (pCE->exec != (char *)0 ? pCE->exec : "/bin/sh"), (unsigned long)pCE->ipid, pCE->execSlave, FileFDNum(pCE->cofile)); break; #if HAVE_FREEIPMI case IPMI: FilePrint(pCLServing->fd, FLAGTRUE, "@:%s,%d:", pCE->host, FileFDNum(pCE->cofile)); break; #endif case HOST: FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu,%s,%d:", pCE->host, pCE->netport, (pCE->raw == FLAGTRUE ? "raw" : "telnet"), FileFDNum(pCE->cofile)); break; case NOOP: FileWrite(pCLServing->fd, FLAGTRUE, "#::", 3); break; case UDS: FilePrint(pCLServing->fd, FLAGTRUE, "%%:%s,%d:", pCE->uds, FileFDNum(pCE->cofile)); break; case DEVICE: FilePrint(pCLServing->fd, FLAGTRUE, "/:%s,%s%c,%d:", pCE->device, (pCE->baud ? pCE->baud->acrate : ""), (pCE->parity ? pCE->parity->key[0] : ' '), FileFDNum(pCE->cofile)); break; case UNKNOWNTYPE: /* shut up gcc */ break; } if (pCE->pCLwr) { FilePrint(pCLServing->fd, FLAGTRUE, "w@%s@%ld", pCE->pCLwr->acid->string, tyme - pCE->pCLwr->typetym); comma = 1; } for (pCL = pCE->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL == pCE->pCLwr) continue; if (comma) FilePrint(pCLServing->fd, FLAGTRUE, ","); if (pCL->fcon) FilePrint(pCLServing->fd, FLAGTRUE, "r@%s@%ld@%s", pCL->acid->string, tyme - pCL->typetym, (pCL->fwantwr && !pCL->fro) ? "rw" : "ro"); else FilePrint(pCLServing->fd, FLAGTRUE, "s@%s@%ld@%s", pCL->acid->string, tyme - pCL->typetym, (pCL->fwantwr && !pCL->fro) ? "rw" : "ro"); comma = 1; } FilePrint(pCLServing->fd, FLAGTRUE, ":%s:%s:%s,%s,%s,%s,%s,%d,%d:%d:%s:", ((pCE->fup && pCE->ioState == ISNORMAL) ? (pCE->initfile == (CONSFILE *)0 ? "up" : "init") : "down"), (pCE->fronly ? "ro" : "rw"), (pCE->logfile == (char *)0 ? "" : pCE->logfile), (pCE->nolog ? "nolog" : "log"), (pCE->activitylog == FLAGTRUE ? "act" : "noact"), (pCE->breaklog == FLAGTRUE ? "brk" : "nobrk"), (pCE->tasklog == FLAGTRUE ? "task" : "notask"), pCE->mark, (pCE->fdlog ? pCE->fdlog->fd : -1), pCE->breakNum, (pCE->autoReUp ? "autoup" : "noautoup")); if (pCE->aliases != (NAMES *)0) { NAMES *n; comma = 0; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { if (comma) FilePrint(pCLServing->fd, FLAGTRUE, ","); FilePrint(pCLServing->fd, FLAGTRUE, "%s", n->name); comma = 1; } } BuildTmpString((char *)0); s = (char *)0; if (pCE->type == DEVICE) { if (pCE->hupcl == FLAGTRUE) s = BuildTmpString(",hupcl"); if (pCE->cstopb == FLAGTRUE) s = BuildTmpString(",cstopb"); #if defined(CRTSCTS) if (pCE->crtscts == FLAGTRUE) s = BuildTmpString(",crtscts"); #endif } if (pCE->type == DEVICE || pCE->type == EXEC) { if (pCE->ixon == FLAGTRUE) s = BuildTmpString(",ixon"); if (pCE->ixany == FLAGTRUE) s = BuildTmpString(",ixany"); if (pCE->ixoff == FLAGTRUE) s = BuildTmpString(",ixoff"); } if (pCE->ondemand == FLAGTRUE) s = BuildTmpString(",ondemand"); if (pCE->reinitoncc == FLAGTRUE) s = BuildTmpString(",reinitoncc"); if (pCE->striphigh == FLAGTRUE) s = BuildTmpString(",striphigh"); if (pCE->autoreinit == FLAGTRUE) s = BuildTmpString(",autoreinit"); if (pCE->unloved == FLAGTRUE) s = BuildTmpString(",unloved"); if (pCE->login == FLAGTRUE) s = BuildTmpString(",login"); FilePrint(pCLServing->fd, FLAGFALSE, ":%s:%s:%d:%s\r\n", (s == (char *)0 ? "" : s + 1), (pCE->initcmd == (char *)0 ? "" : pCE->initcmd), pCE->idletimeout, (pCE->idlestring == (char *)0 ? "" : pCE->idlestring)); BuildTmpString((char *)0); if (args != (char *)0) break; } } void CommandLogging(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { if (pCLServing->fwr) { pCEServing->nolog = !pCEServing->nolog; if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, "logging off]\r\n", -1); TagLogfile(pCEServing, "Console logging disabled by %s", pCLServing->acid->string); } else { FileWrite(pCLServing->fd, FLAGFALSE, "logging on]\r\n", -1); TagLogfile(pCEServing, "Console logging restored by %s", pCLServing->acid->string); } } else { FilePrint(pCLServing->fd, FLAGFALSE, "attach to toggle logging]\r\n"); } } void CommandOpen(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { CONSCLIENT *pCL; if (!pCLServing->fwr) { FileWrite(pCLServing->fd, FLAGFALSE, "attach to reopen]\r\n", -1); return; } /* with a close/re-open we might * change fd's */ ConsInit(pCEServing); if (pCEServing->fup && (pCEServing->initfile != (CONSFILE *)0 || pCEServing->ioState == INCONNECT)) { FileWrite(pCLServing->fd, FLAGFALSE, "connecting...", -1); pCLServing->fiwait = 1; } else if (pCEServing->fronly) { FilePrint(pCLServing->fd, FLAGFALSE, "%s -- read-only]\r\n", pCEServing->fup ? "up" : "down"); } else if ((CONSCLIENT *)0 == (pCL = pCEServing->pCLwr)) { pCEServing->pCLwr = pCLServing; pCLServing->fwr = 1; FilePrint(pCLServing->fd, FLAGFALSE, "%s -- attached]\r\n", pCEServing->fup ? "up" : "down"); TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } else if (pCL == pCLServing) { FilePrint(pCLServing->fd, FLAGFALSE, "%s]\r\n", pCEServing->fup ? "up" : "down"); TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } else { FilePrint(pCLServing->fd, FLAGFALSE, "%s, %s is attached]\r\n", pCEServing->fup ? "up" : "down", pCL->acid->string); } } void CommandWho(GRPENT *pGE, CONSCLIENT *pCLServing, CONSENT *pCEServing, long tyme) { CONSCLIENT *pCL; for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { FilePrint(pCLServing->fd, FLAGFALSE, " %-32.32s %c %-7.7s %6s %s\r\n", pCL->acid->string, pCL == pCLServing ? '*' : ' ', pCL->fcon ? (pCL->fwr ? "attach" : "spy") : "stopped", IdleTyme(tyme - pCL->typetym), pCL->actym); } } char * TelOpt(int o) { static char opt[128]; char *telopts[] = { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO", "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING", "TTYLOC", "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW", "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON" }; if (o < sizeof(telopts) / sizeof(char *)) return telopts[o]; else { sprintf(opt, "%d", o); return opt; } } void DoConsoleRead(CONSENT *pCEServing) { unsigned char acIn[BUFSIZ], acInOrig[BUFSIZ]; int nr, i; CONSCLIENT *pCL; int cofile = FileFDNum(pCEServing->cofile); if (!pCEServing->fup) { FD_CLR(cofile, &rinit); FD_CLR(cofile, &winit); return; } /* read terminal line */ if ((nr = FileRead(pCEServing->cofile, acInOrig, sizeof(acInOrig))) < 0) { Error("[%s] read failure: unexpected EOF", pCEServing->server); ConsoleError(pCEServing); return; } CONDDEBUG((1, "DoConsoleRead(): read %d bytes from fd %d", nr, cofile)); if (nr > 0) { pCEServing->lastWrite = time((time_t *)0); if (pCEServing->idletimeout != (time_t)0 && (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > pCEServing->lastWrite + pCEServing->idletimeout)) timers[T_CIDLE] = pCEServing->lastWrite + pCEServing->idletimeout; } if (pCEServing->type == HOST && pCEServing->raw != FLAGTRUE) { /* Do a little Telnet Protocol interpretation * state = 0: normal * = 1: Saw a IAC char * = 2: Saw a DONT/WONT command * = 3: Saw a WILL command * = 4: Saw a DO command * = 5: Saw a \r */ int new = 0, state; state = pCEServing->telnetState; for (i = 0; i < nr; ++i) { if (state == 0 && acInOrig[i] == IAC) { CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet `IAC'", pCEServing->server)); state = 1; } else if (state == 1 && acInOrig[i] != IAC) { if (acInOrig[i] == WILL) { state = 3; CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet cmd `WILL'", pCEServing->server)); } else if (acInOrig[i] == DO) { state = 4; CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet cmd `DO'", pCEServing->server)); } else if (acInOrig[i] == DONT) { state = 2; CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet cmd `DONT'", pCEServing->server)); } else if (acInOrig[i] == WONT) { state = 2; CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet cmd `WONT'", pCEServing->server)); } else { CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet cmd `%u'", pCEServing->server, acInOrig[i])); state = 0; } } else if (state == 2) { CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet option `%s'", pCEServing->server, TelOpt(acInOrig[i]))); state = 0; } else if (state == 3) { CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet option `%s'", pCEServing->server, TelOpt(acInOrig[i]))); if ((acInOrig[i] == TELOPT_ECHO && pCEServing->sentDoEcho != FLAGTRUE) || (acInOrig[i] == TELOPT_SGA && pCEServing->sentDoSGA != FLAGTRUE)) { PutConsole(pCEServing, IAC, 2); PutConsole(pCEServing, DO, 2); PutConsole(pCEServing, acInOrig[i], 2); CONDDEBUG((1, "DoConsoleRead(): [%s] sent telnet DO `%s'", pCEServing->server, TelOpt(acInOrig[i]))); if (acInOrig[i] == TELOPT_ECHO) { pCEServing->sentDoEcho = FLAGTRUE; } else { pCEServing->sentDoSGA = FLAGTRUE; } } state = 0; } else if (state == 4) { CONDDEBUG((1, "DoConsoleRead(): [%s] got telnet option `%s'", pCEServing->server, TelOpt(acInOrig[i]))); PutConsole(pCEServing, IAC, 2); PutConsole(pCEServing, WONT, 2); PutConsole(pCEServing, acInOrig[i], 2); CONDDEBUG((1, "DoConsoleRead(): [%s] sent telnet WONT `%s'", pCEServing->server, TelOpt(acInOrig[i]))); state = 0; } else { if (state == 5) { state = 0; if (acInOrig[i] == '\000') continue; } if (acInOrig[i] == IAC) CONDDEBUG((1, "DoConsoleRead(): [%s] quoted `IAC'", pCEServing->server)); if (pCEServing->striphigh == FLAGTRUE) acIn[new++] = acInOrig[i] & 127; else acIn[new++] = acInOrig[i]; if (acInOrig[i] == '\r') state = 5; else state = 0; } } pCEServing->telnetState = state; nr = new; } else { for (i = 0; i < nr; ++i) { if (pCEServing->striphigh == FLAGTRUE) acIn[i] = acInOrig[i] & 127; else acIn[i] = acInOrig[i]; } } if (nr == 0) return; /* log it and write to all connections on this server */ if (!pCEServing->nolog) { WriteLog(pCEServing, (char *)acIn, nr); } /* if we have a command running, interface with it and then * allow the normal stuff to happen (so folks can watch) */ if (pCEServing->initfile != (CONSFILE *)0) FileWrite(pCEServing->initfile, FLAGFALSE, (char *)acIn, nr); /* output all console info nobody is attached * or output to unifiedlog if it's open */ if (unifiedlog != (CONSFILE *)0 || (pCEServing->pCLwr == (CONSCLIENT *)0 && pCEServing->unloved == FLAGTRUE)) { /* run through the console ouptut, * add each character to the output line * drop and reset if we have too much * or are at the end of a line (ksb) */ for (i = 0; i < nr; ++i) { pCEServing->acline[pCEServing->iend++] = acIn[i]; if (pCEServing->iend < sizeof(pCEServing->acline) && '\n' != acIn[i]) continue; /* unloved */ if (pCEServing->pCLwr == (CONSCLIENT *)0 && pCEServing->unloved == FLAGTRUE) { write(1, pCEServing->server, strlen(pCEServing->server)); write(1, ": ", 2); write(1, pCEServing->acline, pCEServing->iend); } /* unified */ if (unifiedlog != (CONSFILE *)0) { FileWrite(unifiedlog, FLAGTRUE, pCEServing->server, -1); if (pCEServing->pCLwr == (CONSCLIENT *)0) FileWrite(unifiedlog, FLAGTRUE, ": ", 2); else FileWrite(unifiedlog, FLAGTRUE, "*: ", 3); FileWrite(unifiedlog, FLAGFALSE, pCEServing->acline, pCEServing->iend); } pCEServing->iend = 0; } } /* write console info to clients (not suspended) */ for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon || pCL->iState == S_CEXEC) FileWrite(pCL->fd, FLAGFALSE, (char *)acIn, nr); } } void DoTaskRead(CONSENT *pCEServing) { unsigned char acInOrig[BUFSIZ]; int nr, fd; CONSCLIENT *pCL; if (pCEServing->taskfile == (CONSFILE *)0) return; fd = FileFDNum(pCEServing->taskfile); /* read from task */ if ((nr = FileRead(pCEServing->taskfile, acInOrig, sizeof(acInOrig))) < 0) { CONDDEBUG((1, "DoTaskRead(): got %d bytes from fd %d", nr, fd)); StopTask(pCEServing); return; } CONDDEBUG((1, "DoTaskRead(): read %d bytes from fd %d", nr, fd)); /* write console info to clients (not suspended) */ for (pCL = pCEServing->pCLon; (CONSCLIENT *)0 != pCL; pCL = pCL->pCLnext) { if (pCL->fcon) FileWrite(pCL->fd, FLAGFALSE, (char *)acInOrig, nr); } } void DoCommandRead(CONSENT *pCEServing) { unsigned char acInOrig[BUFSIZ]; int nr, i, fd; if (pCEServing->initfile == (CONSFILE *)0) return; fd = FileFDNum(pCEServing->initfile); /* read from command */ if ((nr = FileRead(pCEServing->initfile, acInOrig, sizeof(acInOrig))) < 0) { StopInit(pCEServing); return; } CONDDEBUG((1, "DoCommandRead(): read %d bytes from fd %d", nr, fd)); for (i = 0; i < nr; ++i) { if (pCEServing->striphigh == FLAGTRUE) PutConsole(pCEServing, acInOrig[i] & 127, 1); else PutConsole(pCEServing, acInOrig[i], 1); } } unsigned char CM[] = { 0xdf, 0xd2, 0xd2, 0xdf, 0xab, 0x90, 0x90, 0x93, 0xdf, 0x96, 0x8c, 0xdf, 0x9e, 0x91, 0xdf, 0xd5, 0x9e, 0x92, 0x9e, 0x85, 0x96, 0x91, 0x98, 0xd5, 0xdf, 0x9d, 0x9e, 0x91, 0x9b, 0xd1, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xb7, 0x9e, 0x89, 0x9a, 0xdf, 0x86, 0x90, 0x8a, 0xdf, 0x8d, 0x9a, 0x9e, 0x9b, 0xdf, 0x8b, 0x97, 0x9a, 0xdf, 0x92, 0x9e, 0x91, 0x8f, 0x9e, 0x98, 0x9a, 0xc0, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xbe, 0x93, 0x88, 0x9e, 0x86, 0x8c, 0xdf, 0x93, 0x9a, 0x8b, 0xdf, 0x8b, 0x97, 0x9a, 0xdf, 0x88, 0x90, 0x90, 0x94, 0x96, 0x9a, 0xdf, 0x88, 0x96, 0x91, 0xd1, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0x89, 0x96, 0x92, 0xdf, 0x96, 0x8c, 0xdf, 0xd5, 0x8b, 0x90, 0x90, 0xd5, 0xdf, 0x9c, 0x90, 0x90, 0x93, 0xde, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xb3, 0x9e, 0x8b, 0x9a, 0x8d, 0x9e, 0x93, 0x8a, 0x8c, 0xc5, 0xdf, 0xc9, 0xd3, 0xc8, 0xd3, 0xca, 0xd3, 0xc7, 0xd3, 0xcb, 0xd3, 0xc6, 0xd3, 0xce, 0xcc, 0xd3, 0xce, 0xd3, 0xce, 0xcd, 0xd3, 0xcd, 0xd3, 0xce, 0xce, 0xd3, 0xcc, 0xd3, 0xce, 0xcf, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0xb2, 0x90, 0x8a, 0x91, 0x8b, 0xdf, 0x9e, 0xdf, 0x91, 0x9a, 0x88, 0xdf, 0x8c, 0x9c, 0x8d, 0x9e, 0x8b, 0x9c, 0x97, 0xdf, 0x92, 0x90, 0x91, 0x94, 0x9a, 0x86, 0xdf, 0x9e, 0x91, 0x9b, 0xdf, 0x8b, 0x8d, 0x86, 0xdf, 0x9e, 0x98, 0x9e, 0x96, 0x91, 0xd1, 0xff, 0xdf, 0xd2, 0xd2, 0xdf, 0x96, 0x99, 0xdf, 0x86, 0x90, 0x8a, 0xdf, 0x9b, 0x9e, 0x8d, 0x9a, 0xd1, 0xd1, 0xd1, 0xff, 0xff }; unsigned char * Challenge(void) { int i; static unsigned char **n = (unsigned char **)0; static int cnt = 0; static int cur = 0; static int rnd = 0; if (n == (unsigned char **)0) { int j; for (i = 0; i < sizeof(CM); i++) { if (CM[i] == 0xff) cnt++; } n = (unsigned char **)calloc(cnt, sizeof(unsigned char *)); j = 0; for (i = 0; i < sizeof(CM); i++) { if (n[j] == (unsigned char *)0) n[j] = &(CM[i]); if (CM[i] == 0xff) { j++; } CM[i] = CM[i] ^ 0xff; } cnt--; cur = time(NULL) % cnt; rnd = time(NULL) % 2; } if (++rnd % 2 == 0) { rnd = 0; if (cur >= cnt) cur = 0; return n[cur++]; } return n[cnt]; } /* This routine is used to gather input from the * client and save it in the accmd string. * GatherLine returns 0 until the user signals * they're "done" (a \r), then it returns 1. */ typedef enum gatherType { G_INT, G_TEXT } GATHERTYPE; int GatherLine(char c, int limit, GATHERTYPE g, CONSCLIENT *pCL) { if (c == '\r') return 1; if ((limit <= 0 || pCL->accmd->used - 1 < limit) && ((g == G_TEXT && (c == '\a' || (c >= ' ' && c <= '~'))) || (g == G_INT && isdigit((int)c)))) { BuildStringChar(c, pCL->accmd); FileWrite(pCL->fd, FLAGFALSE, (char *)&c, 1); } else if ((c == '\b' || c == 0x7f) && pCL->accmd->used > 1) { if (pCL->accmd->string[pCL->accmd->used - 2] != '\a') FileWrite(pCL->fd, FLAGFALSE, "\b \b", 3); pCL->accmd->string[pCL->accmd->used - 2] = '\000'; pCL->accmd->used--; } else if ((c == 0x15) && pCL->accmd->used > 1) { while (pCL->accmd->used > 1) { if (pCL->accmd->string[pCL->accmd->used - 2] != '\a') FileWrite(pCL->fd, FLAGFALSE, "\b \b", 3); pCL->accmd->string[pCL->accmd->used - 2] = '\000'; pCL->accmd->used--; } } return 0; } void DoClientRead(GRPENT *pGE, CONSCLIENT *pCLServing) { CONSENT *pCEServing = pCLServing->pCEto; int nr, i, l; unsigned char acIn[BUFSIZ], acInOrig[BUFSIZ]; time_t tyme; static STRING *bcast = (STRING *)0; static STRING *acA1 = (STRING *)0; static STRING *acA2 = (STRING *)0; if (bcast == (STRING *)0) bcast = AllocString(); if (acA1 == (STRING *)0) acA1 = AllocString(); if (acA2 == (STRING *)0) acA2 = AllocString(); /* read connection */ if ((nr = FileRead(pCLServing->fd, acIn, sizeof(acIn))) < 0) { DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } if (nr == 0) return; /* update last keystroke time */ pCLServing->typetym = tyme = time((time_t *)0); while ((l = ParseIACBuf(pCLServing->fd, acIn, &nr)) >= 0) { if (l == 0) { if (FileSawQuoteExec(pCLServing->fd) == FLAGTRUE) { if (pCLServing->iState == S_CWAIT) { pCLServing->iState = S_CEXEC; if (pCEServing->pCLwr == pCLServing) FileWrite(pCLServing->fd, FLAGFALSE, "[rw]\r\n", 6); else FileWrite(pCLServing->fd, FLAGFALSE, "[ro]\r\n", 6); } } if (FileSawQuoteAbrt(pCLServing->fd) == FLAGTRUE) { if (pCLServing->iState == S_CWAIT || pCLServing->iState == S_CEXEC) { pCLServing->fcon = 1; pCLServing->iState = S_NORMAL; } } /* not used (yet?) if (FileSawQuoteSusp(pCLServing->fd) == FLAGTRUE) { } */ continue; } for (i = 0; i < l; ++i) { acInOrig[i] = acIn[i]; if (pCEServing->striphigh == FLAGTRUE) { acIn[i] &= 127; } } for (i = 0; i < l; ++i) { if (pGE->pCEctl == pCEServing) { static char *pcArgs; static char *pcCmd; CONDDEBUG((1, "state = %d", pCLServing->iState)); if ('\n' != acIn[i]) { BuildStringChar(acIn[i], pCLServing->accmd); continue; } if ((pCLServing->accmd->used > 1) && ('\r' == pCLServing->accmd->string[pCLServing->accmd->used - 2])) { pCLServing->accmd->string[pCLServing->accmd->used - 2] = '\000'; pCLServing->accmd->used--; } /* process password here...before we corrupt accmd */ if (pCLServing->iState == S_PASSWD) { if (CheckPasswd (pCLServing, pCLServing->accmd->string, FLAGFALSE) != AUTH_SUCCESS) { FileWrite(pCLServing->fd, FLAGFALSE, "invalid password\r\n", -1); BuildString((char *)0, pCLServing->accmd); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } Verbose(" login %s", pCLServing->acid->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); pCLServing->iState = S_NORMAL; BuildString((char *)0, pCLServing->accmd); continue; } if ((pcArgs = strchr(pCLServing->accmd->string, ' ')) != (char *)0) { *pcArgs++ = '\000'; } if (pcArgs != (char *)0) pcArgs = PruneSpace(pcArgs); pcCmd = PruneSpace(pCLServing->accmd->string); if (strcmp(pcCmd, "help") == 0) { static char *apcHelp1[] = { "exit disconnect\r\n", "help this help message\r\n", "login log in\r\n", #if HAVE_OPENSSL "ssl start ssl session\r\n", #endif #if HAVE_GSSAPI "gssapi log in with gssapi\r\n", #endif (char *)0 }; static char *apcHelp2[] = { "broadcast send broadcast message\r\n", "call connect to given console\r\n", "disconnect* disconnect the given user(s)\r\n", "examine examine port and baud rates\r\n", "exit disconnect\r\n", "group show users in this group\r\n", "help this help message\r\n", "hosts show host status and user\r\n", "info show console information\r\n", "textmsg send a text message\r\n", "* = requires admin privileges\r\n", (char *)0 }; char **ppc; for (ppc = (pCLServing->iState == S_IDENT ? apcHelp1 : apcHelp2); (char *)0 != *ppc; ++ppc) { FileWrite(pCLServing->fd, FLAGTRUE, *ppc, -1); } FileWrite(pCLServing->fd, FLAGFALSE, (char *)0, 0); } else if (strcmp(pcCmd, "exit") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "goodbye\r\n", -1); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; #if HAVE_OPENSSL } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "ssl") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); if (!AttemptSSL(pCLServing)) { DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } #endif #if HAVE_GSSAPI } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "gssapi") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); /* Change the I/O mode right away, we'll do the read * and accept when the select gets back to us */ pCLServing->ioState = INGSSACCEPT; #endif } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "login") == 0) { #if HAVE_OPENSSL if (config->sslrequired == FLAGTRUE && FileGetType(pCLServing->fd) != SSLSocket) { FileWrite(pCLServing->fd, FLAGFALSE, "encryption required\r\n", -1); } else { #endif if (pcArgs == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "login requires argument\r\n", -1); } else { BuildString((char *)0, pCLServing->username); BuildString((char *)0, pCLServing->acid); BuildString(pcArgs, pCLServing->username); BuildString(pcArgs, pCLServing->acid); BuildStringChar('@', pCLServing->acid); BuildString(pCLServing->peername->string, pCLServing->acid); if (pCLServing->caccess == 't' || CheckPasswd(pCLServing, "", FLAGTRUE) == AUTH_SUCCESS) { pCLServing->iState = S_NORMAL; Verbose(" login %s", pCLServing->acid->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); } else { FilePrint(pCLServing->fd, FLAGFALSE, "passwd? %s\r\n", myHostname); pCLServing->iState = S_PASSWD; } } #if HAVE_OPENSSL } #endif } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "call") == 0) { if (pcArgs == (char *)0) FileWrite(pCLServing->fd, FLAGFALSE, "call requires argument\r\n", -1); else { CONSENT *pCEwant = (CONSENT *)0; pCEwant = HuntForConsole(pGE, pcArgs); if (pCEwant == (CONSENT *)0) { FilePrint(pCLServing->fd, FLAGFALSE, "%s: no such console\r\n", pcArgs); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } pCLServing->fro = ClientAccess(pCEwant, pCLServing->username->string); if (pCLServing->fro == -1) { FilePrint(pCLServing->fd, FLAGFALSE, "%s: permission denied\r\n", pcArgs); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } if (pCEwant->login != FLAGTRUE) { if (pCEwant->motd == (char *)0) { FilePrint(pCLServing->fd, FLAGFALSE, "%s: no logins allowed at this time\r\n", pcArgs); } else { FilePrint(pCLServing->fd, FLAGFALSE, "%s: %s\r\n", pcArgs, pCEwant->motd); } DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; } /* remove from current host */ if ((CONSCLIENT *)0 != pCLServing->pCLnext) { pCLServing->pCLnext->ppCLbnext = pCLServing->ppCLbnext; } *(pCLServing->ppCLbnext) = pCLServing->pCLnext; if (pCLServing->fwr) { pCLServing->fwr = 0; pCLServing->fwantwr = 0; TagLogfileAct(pCEServing, "%s detached", pCLServing->acid->string); pCEServing->pCLwr = (CONSCLIENT *)0; FindWrite(pCEServing); } /* inform operators of the change */ Verbose(" attach %s to %s", pCLServing->acid->string, pCEwant->server); Msg("[%s] login %s", pCEwant->server, pCLServing->acid->string); /* set new host and link into new host list */ pCEServing = pCEwant; pCLServing->pCEto = pCEServing; pCLServing->pCLnext = pCEServing->pCLon; pCLServing->ppCLbnext = &pCEServing->pCLon; if ((CONSCLIENT *)0 != pCLServing->pCLnext) { pCLServing->pCLnext->ppCLbnext = &pCLServing->pCLnext; } pCEServing->pCLon = pCLServing; /* try to reopen line if specified at server startup */ if ((pCEServing->ondemand == FLAGTRUE || pCEServing->reinitoncc == FLAGTRUE) && !pCEServing->fup) ConsInit(pCEServing); /* try for attach on new console */ if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, "[console is read-only]\r\n", -1); } else if (((CONSCLIENT *)0 == pCEServing->pCLwr) && !pCLServing->fro) { pCEServing->pCLwr = pCLServing; pCLServing->fwr = 1; FileWrite(pCLServing->fd, FLAGFALSE, "[attached]\r\n", -1); /* this keeps the ops console neat */ pCEServing->iend = 0; TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } else { ClientWantsWrite(pCLServing); FileWrite(pCLServing->fd, FLAGFALSE, "[spy]\r\n", -1); } pCLServing->fcon = 0; pCLServing->iState = S_NORMAL; } } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "info") == 0) { CommandInfo(pGE, pCLServing, pCEServing, tyme, pcArgs); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "examine") == 0) { CommandExamine(pGE, pCLServing, pCEServing, tyme, pcArgs); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "group") == 0) { CommandGroup(pGE, pCLServing, pCEServing, tyme, pcArgs); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "hosts") == 0) { CommandHosts(pGE, pCLServing, pCEServing, tyme, pcArgs); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "broadcast") == 0) { if (pcArgs == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "broadcast requires argument\r\n", -1); } else { BuildString((char *)0, bcast); BuildStringChar('[', bcast); BuildString(pCLServing->acid->string, bcast); BuildString(": ", bcast); BuildString(pcArgs, bcast); BuildString("]\r\n", bcast); SendAllClientsMsg(pGE, bcast->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); } } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "textmsg") == 0) { char *pcMsg; if (pcArgs == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "textmsg requires two arguments\r\n", -1); } else { if ((pcMsg = strchr(pcArgs, ' ')) != (char *)0) { *pcMsg++ = '\000'; } if (pcMsg == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "textmsg requires two arguments\r\n", -1); } else { pcMsg = PruneSpace(pcMsg); BuildString((char *)0, bcast); BuildStringChar('[', bcast); BuildString(pCLServing->acid->string, bcast); BuildString(": ", bcast); BuildString(pcMsg, bcast); BuildString("]\r\n", bcast); SendCertainClientsMsg(pGE, pcArgs, bcast->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); } } } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "disconnect") == 0) { if (pcArgs == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "disconnect requires argument\r\n", -1); } else { if (ConsentUserOk (pADList, pCLServing->username->string) == 1) { int num; Verbose("disconnect command (of `%s') by %s", pcArgs, pCLServing->acid->string); num = DisconnectCertainClients(pGE, pCLServing-> acid->string, pcArgs); /* client expects this string to be formatted * in this way only. */ FilePrint(pCLServing->fd, FLAGFALSE, "ok -- disconnected %d users\r\n", num); } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } } else { FileWrite(pCLServing->fd, FLAGFALSE, "unknown command\r\n", -1); CONDDEBUG((1, "command %s state %d", pcCmd, pCLServing->iState)); } BuildString((char *)0, pCLServing->accmd); } else statestart: switch (pCLServing->iState) { case S_IDENT: case S_PASSWD: /* these are not used in this mode */ break; case S_NOTE: if (GatherLine(acIn[i], 0, G_TEXT, pCLServing)) { FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n", 3); BuildString((char *)0, bcast); BuildString("NOTE -- ", bcast); BuildString(pCLServing->acid->string, bcast); BuildString(": ", bcast); BuildString(pCLServing->accmd->string, bcast); TagLogfile(pCEServing, bcast->string); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_NORMAL; } continue; case S_BCAST: if (GatherLine(acIn[i], 0, G_TEXT, pCLServing)) { FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n", 3); BuildString((char *)0, bcast); BuildStringChar('[', bcast); BuildString(pCLServing->acid->string, bcast); BuildString(": ", bcast); BuildString(pCLServing->accmd->string, bcast); BuildString("]\r\n", bcast); SendClientsMsg(pCEServing, bcast->string); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_NORMAL; } continue; case S_REPLAY: case S_PLAYBACK: if (GatherLine(acIn[i], 4, G_INT, pCLServing)) { unsigned short *s; if (pCLServing->iState == S_REPLAY) s = &(pCLServing->replay); else s = &(pCLServing->playback); if (pCLServing->accmd->used > 1) { unsigned short u; u = (unsigned short) atoi(pCLServing->accmd->string); /* i'm limiting the value here to 10000 for a couple * of reasons: * * 1. this process could be busy parsing a file for * a long time if the logfile and replay are big. * 2. who needs more than 10000 lines of replay? if * you need that much, go find the raw logfile. * and if you don't have access, find someone who * does. * * change this to something bigger if you want, but * it would scare me...even 10000 seems a bit large, * but no one *has* to set the value so large. * and watch out...it's an unsigned short, so you * can't go really huge. */ if (u > 10000) FileWrite(pCLServing->fd, FLAGFALSE, " ", 18); else *s = u; } else FilePrint(pCLServing->fd, FLAGFALSE, "%u", *s); FileWrite(pCLServing->fd, FLAGFALSE, "]\r\n", 3); pCLServing->iState = S_NORMAL; } continue; case S_QUOTE: /* send octal code */ /* must type in 3 octal digits */ if (acIn[i] >= '0' && acIn[i] <= '7') { BuildStringChar(acIn[i], pCLServing->accmd); if (pCLServing->accmd->used < 4) { FileWrite(pCLServing->fd, FLAGFALSE, (char *)&acIn[i], 1); continue; } FileWrite(pCLServing->fd, FLAGTRUE, (char *)&acIn[i], 1); FileWrite(pCLServing->fd, FLAGFALSE, "]", 1); pCLServing->accmd->string[0] = (((pCLServing->accmd->string[0] - '0') * 8 + (pCLServing->accmd->string[1] - '0')) * 8) + (pCLServing->accmd->string[2] - '0'); PutConsole(pCEServing, pCLServing->accmd->string[0], 1); BuildString((char *)0, pCLServing->accmd); } else { FileWrite(pCLServing->fd, FLAGFALSE, " aborted]\r\n", -1); } pCLServing->iState = S_NORMAL; continue; case S_SUSP: if (!pCEServing->fup) { FileWrite(pCLServing->fd, FLAGFALSE, " -- line down]\r\n", -1); } else if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, " -- read-only]\r\n", -1); } else if (((CONSCLIENT *)0 == pCEServing->pCLwr) && !pCLServing->fro) { pCEServing->pCLwr = pCLServing; pCLServing->fwr = 1; if (pCEServing->nolog) { FileWrite(pCLServing->fd, FLAGFALSE, " -- attached (nologging)]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, " -- attached]\r\n", -1); } TagLogfileAct(pCEServing, "%s attached", pCLServing->acid->string); } else { FileWrite(pCLServing->fd, FLAGFALSE, " -- spy mode]\r\n", -1); } pCLServing->fcon = 1; pCLServing->iState = S_NORMAL; continue; case S_CWAIT: continue; case S_NORMAL: /* if it is an escape sequence shift states */ if (acInOrig[i] == pCLServing->ic[0]) { pCLServing->iState = S_ESC1; continue; } /* fall through */ case S_CEXEC: /* if we can write, write to slave tty */ if (pCEServing->fup && pCEServing->initfile == (CONSFILE *)0 && pCEServing->ioState == ISNORMAL && pCLServing->fwr && !pCLServing->fiwait) { PutConsole(pCEServing, acIn[i], 1); continue; } /* if the client is stuck in spy mode * give them a clue as to how to get out * (LLL nice to put chars out as ^Ec, rather * than octal escapes, but....) */ if (!pCLServing->fiwait && ('\r' == acIn[i] || '\n' == acIn[i])) { char *m = ""; if (pCLServing->fwr) m = ConsState(pCEServing); else m = "read-only"; FilePrint(pCLServing->fd, FLAGFALSE, "[%s -- use %s %s ? for help]\r\n", m, FmtCtl(pCLServing->ic[0], acA1), FmtCtl(pCLServing->ic[1], acA2)); } continue; case S_HALT1: /* halt sequence? */ pCLServing->iState = S_NORMAL; if (acIn[i] != '?' && ((acIn[i] < '0' || acIn[i] > '9') && (acIn[i] < 'a' || acIn[i] > 'z'))) { FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); continue; } if (acIn[i] == '?') { int i; int mod; FileWrite(pCLServing->fd, FLAGFALSE, "list]\r\n", -1); i = pCEServing->breakNum; mod = i > 9 ? BREAKALPHAOFFSET : 0; if (i == 0 || breakList[i - 1].seq->used <= 1 || pCEServing->breaklist == (char *)0 || ((char *)0 == strchr(pCEServing->breaklist, '0' + i + mod) && (char *)0 == strchr(pCEServing->breaklist, '*'))) FileWrite(pCLServing->fd, FLAGTRUE, " 0 - 0ms, \r\n", -1); else { FmtCtlStr(breakList[i - 1].seq->string, breakList[i - 1].seq->used - 1, acA1); FilePrint(pCLServing->fd, FLAGTRUE, " 0 - %3dms, `%s'\r\n", breakList[i - 1].delay, acA1->string); } if (pCEServing->breaklist != (char *)0) { for (i = 0; i < BREAKLISTSIZE; i++) { char btc; mod = i > 8 ? BREAKALPHAOFFSET : 0; btc = '1' + i + mod; if ((char *)0 == strchr(pCEServing->breaklist, btc) && (char *)0 == strchr(pCEServing->breaklist, '*')) continue; if (breakList[i].seq->used > 1) { FmtCtlStr(breakList[i].seq->string, breakList[i].seq->used - 1, acA1); FilePrint(pCLServing->fd, FLAGTRUE, " %c - %3dms, `%s'\r\n", btc, breakList[i].delay, acA1->string); } } } FileWrite(pCLServing->fd, FLAGFALSE, (char *)0, 0); } else { if (pCLServing->fwr) { int bt = acIn[i] - '0' - (acIn[i] > '9' ? BREAKALPHAOFFSET : 0); SendBreak(pCLServing, pCEServing, bt); } else FileWrite(pCLServing->fd, FLAGFALSE, "attach to send break]\r\n", -1); } continue; case S_TASK: /* task invocation */ pCLServing->iState = S_NORMAL; if (pCEServing->taskpid != 0 && acIn[i] == '.') { FileWrite(pCLServing->fd, FLAGFALSE, "terminate]\r\n", -1); StopTask(pCEServing); continue; } if (acIn[i] != '?' && ((acIn[i] < '0' || acIn[i] > '9') && (acIn[i] < 'a' || acIn[i] > 'z'))) { FileWrite(pCLServing->fd, FLAGFALSE, "aborted]\r\n", -1); continue; } if (acIn[i] == '?') { TASKS *t; int saw = 0; if (pCEServing->taskpid != 0) { STRING *acA1 = AllocString(); STRING *acA2 = AllocString(); FilePrint(pCLServing->fd, FLAGFALSE, "running - pid %lu - use `%s%s!.' to terminate]\r\n", (unsigned long)pGE->pid, FmtCtl(pCLServing->ic[0], acA1), FmtCtl(pCLServing->ic[1], acA2)); DestroyString(acA1); DestroyString(acA2); return; } else { FileWrite(pCLServing->fd, FLAGFALSE, "list]\r\n", -1); if (pCEServing->tasklist != (char *)0) { for (t = taskList; t != (TASKS *)0; t = t->next) { if ((char *)0 == strchr(pCEServing->tasklist, t->id) && (char *)0 == strchr(pCEServing->tasklist, '*')) continue; if (t->descr->used > 1) { FilePrint(pCLServing->fd, FLAGTRUE, " %c - `%s'\r\n", t->id, t->descr->string); } else if (t->cmd->used > 1) { FilePrint(pCLServing->fd, FLAGTRUE, " %c - `%s'\r\n", t->id, t->cmd->string); } saw++; } } if (saw == 0) FileWrite(pCLServing->fd, FLAGTRUE, " \r\n", -1); FileWrite(pCLServing->fd, FLAGFALSE, (char *)0, 0); } } else { if (pCLServing->fwr) { InvokeTask(pCLServing, pCEServing, acIn[i]); } else FileWrite(pCLServing->fd, FLAGFALSE, "attach to invoke task]\r\n", -1); } continue; case S_CONFIRM: if (acIn[i] == 'y' || acIn[i] == 'Y') { pCLServing->confirmed = FLAGTRUE; FileWrite(pCLServing->fd, FLAGFALSE, "y ", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "n ", -1); } pCLServing->iState = pCLServing->cState; acIn[i] = pCLServing->cOption; goto statestart; case S_CATTN: /* redef escape sequence? */ pCLServing->ic[0] = acInOrig[i]; FmtCtl(acInOrig[i], acA1); FilePrint(pCLServing->fd, FLAGFALSE, "%s ", acA1->string); pCLServing->iState = S_CESC; continue; case S_CESC: /* escape sequent 2 */ pCLServing->ic[1] = acInOrig[i]; pCLServing->iState = S_NORMAL; FmtCtl(acInOrig[i], acA1); FilePrint(pCLServing->fd, FLAGFALSE, "%s ok]\r\n", acA1->string); continue; case S_ESC1: /* first char in escape sequence */ if (acInOrig[i] == pCLServing->ic[1]) { if (pCLServing->fecho) FileWrite(pCLServing->fd, FLAGFALSE, "\r\n[", 3); else FileWrite(pCLServing->fd, FLAGFALSE, "[", 1); pCLServing->iState = S_CMD; continue; } /* ^E^Ec or ^_^_^[ * pass (possibly stripped) first ^E (^_) and * stay in same state */ if (acInOrig[i] == pCLServing->ic[0]) { if (pCLServing->fwr) { PutConsole(pCEServing, acIn[i], 1); } continue; } /* ^Ex or ^_x * pass both characters to slave tty (possibly stripped) */ pCLServing->iState = S_NORMAL; if (pCLServing->fwr) { char c = pCLServing->ic[0]; if (pCEServing->striphigh == FLAGTRUE) c = c & 127; PutConsole(pCEServing, c, 1); PutConsole(pCEServing, acIn[i], 1); } continue; case S_CMD: /* have 1/2 of the escape sequence */ pCLServing->iState = S_NORMAL; switch (acIn[i]) { case '=': if (!pCLServing->fcon) { char *m = ConsState(pCEServing); if (strcmp(m, "up") == 0) FileWrite(pCLServing->fd, FLAGFALSE, "up]\r\n", -1); else FilePrint(pCLServing->fd, FLAGFALSE, "`%s' -- console is %s]\r\n", pCEServing->server, m); } else goto unknownchar; break; case ';': if (pCLServing->fcon) { if (ConsentUserOk (pLUList, pCLServing->username->string) == 1) goto unknownchar; FileSetQuoteIAC(pCLServing->fd, FLAGFALSE); FilePrint(pCLServing->fd, FLAGFALSE, "%c%c", OB_IAC, OB_GOTO); FileSetQuoteIAC(pCLServing->fd, FLAGTRUE); goto bottomSuspend; } else { FileWrite(pCLServing->fd, FLAGFALSE, "connected]\r\n", -1); pCLServing->fcon = 1; } break; case 'a': /* attach */ CommandAttach(pGE, pCLServing, pCEServing, tyme); break; case 'b': /* broadcast message */ FileWrite(pCLServing->fd, FLAGFALSE, "Enter message: ", -1); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_BCAST; break; case 'c': if (!pCLServing->fwr) { goto unknownchar; } CommandChangeFlow(pGE, pCLServing, pCEServing, tyme); break; case 'd': /* down a console */ if (!pCLServing->fwr) { goto unknownchar; } CommandDown(pGE, pCLServing, pCEServing, tyme); break; case 'e': /* redefine escape keys */ pCLServing->iState = S_CATTN; FileWrite(pCLServing->fd, FLAGFALSE, "redef: ", -1); break; case 'f': /* force attach */ CommandForce(pGE, pCLServing, pCEServing, tyme); break; case 'g': /* group info */ FilePrint(pCLServing->fd, FLAGFALSE, "group %s]\r\n", pGE->pCEctl->server); CommandGroup(pGE, pCLServing, pCEServing, tyme, (char *)0); break; case 'h': /* help */ case '?': HelpUser(pCLServing); break; case 'i': FileWrite(pCLServing->fd, FLAGFALSE, "info]\r\n", -1); CommandInfo(pGE, pCLServing, pCEServing, tyme, (char *)0); break; case 'L': if (!pCLServing->fwr) { goto unknownchar; } CommandLogging(pGE, pCLServing, pCEServing, tyme); break; case 'l': /* halt character 1 */ if (!pCLServing->fwr) { goto unknownchar; } if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, "can't halt read-only console]\r\n", -1); continue; } pCLServing->iState = S_HALT1; FileWrite(pCLServing->fd, FLAGFALSE, "halt ", -1); break; case 'm': /* message of the day */ if (pCEServing->motd == (char *)0) FileWrite(pCLServing->fd, FLAGFALSE, "-- MOTD --]\r\n", -1); else FilePrint(pCLServing->fd, FLAGFALSE, "-- MOTD -- %s]\r\n", pCEServing->motd); break; case 'n': /* note message to log file */ if (pCEServing->fdlog == (CONSFILE *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "no log file on this console]\r\n", -1); } else { FileWrite(pCLServing->fd, FLAGFALSE, "Enter note: ", -1); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_NOTE; } break; case 'o': /* close and re-open line */ CommandOpen(pGE, pCLServing, pCEServing, tyme); break; case 'P': /* broadcast message */ FilePrint(pCLServing->fd, FLAGFALSE, "set playback (%d): ", pCLServing->playback); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_PLAYBACK; break; case 'p': /* replay lines (longer, in theory) */ FileWrite(pCLServing->fd, FLAGFALSE, "playback]\r\n", -1); Replay(pCEServing, pCLServing->fd, pCLServing->playback); break; case '\022': /* ^R */ FileWrite(pCLServing->fd, FLAGFALSE, "^R]\r\n", -1); Replay(pCEServing, pCLServing->fd, 1); break; case 'R': /* broadcast message */ FilePrint(pCLServing->fd, FLAGFALSE, "set replay (%d): ", pCLServing->replay); BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_REPLAY; break; case 'r': /* replay lines */ FileWrite(pCLServing->fd, FLAGFALSE, "replay]\r\n", -1); Replay(pCEServing, pCLServing->fd, pCLServing->replay); break; case 's': /* spy mode */ if (!pCLServing->fwr) { goto unknownchar; } pCLServing->fwantwr = 0; BumpClient(pCEServing, (char *)0); TagLogfileAct(pCEServing, "%s detached", pCLServing->acid->string); FindWrite(pCEServing); FileWrite(pCLServing->fd, FLAGFALSE, "spying]\r\n", -1); break; case 'u': /* hosts on server this */ FileWrite(pCLServing->fd, FLAGFALSE, "hosts]\r\n", -1); CommandHosts(pGE, pCLServing, pCEServing, tyme, (char *)0); break; case 'v': /* version */ FilePrint(pCLServing->fd, FLAGFALSE, "version `%s']\r\n", MyVersion()); break; case 'w': /* who */ FilePrint(pCLServing->fd, FLAGFALSE, "who %s]\r\n", pCEServing->server); CommandWho(pGE, pCLServing, pCEServing, tyme); break; case 'x': FileWrite(pCLServing->fd, FLAGFALSE, "examine]\r\n", -1); CommandExamine(pGE, pCLServing, pCEServing, tyme, (char *)0); break; case 'z': /* suspend the client */ case '\032': if (ConsentUserOk (pLUList, pCLServing->username->string) == 1) goto unknownchar; FileSetQuoteIAC(pCLServing->fd, FLAGFALSE); FilePrint(pCLServing->fd, FLAGFALSE, "%c%c", OB_IAC, OB_SUSP); FileSetQuoteIAC(pCLServing->fd, FLAGTRUE); bottomSuspend: pCLServing->fcon = 0; pCLServing->iState = S_SUSP; if (pCEServing->pCLwr == pCLServing) { BumpClient(pCEServing, (char *)0); TagLogfileAct(pCEServing, "%s detached", pCLServing-> acid->string); FindWrite(pCEServing); } break; case '!': /* invoke a task */ if (!pCLServing->fwr) { goto unknownchar; } pCLServing->iState = S_TASK; FileWrite(pCLServing->fd, FLAGFALSE, "task ", -1); break; case '|': /* wait for client */ if (!pCLServing->fwr || ConsentUserOk(pLUList, pCLServing-> username->string) == 1) goto unknownchar; FileSetQuoteIAC(pCLServing->fd, FLAGFALSE); FilePrint(pCLServing->fd, FLAGFALSE, "%c%c", OB_IAC, OB_EXEC); FileSetQuoteIAC(pCLServing->fd, FLAGTRUE); pCLServing->fcon = 0; pCLServing->iState = S_CWAIT; break; case '.': /* disconnect */ case '\004': case '\003': FileWrite(pCLServing->fd, FLAGFALSE, "disconnect]\r\n", -1); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); return; case ' ': /* abort escape sequence */ case '\n': case '\r': FileWrite(pCLServing->fd, FLAGFALSE, "ignored]\r\n", -1); break; case '\\': /* quote mode (send ^Q,^S) */ if (pCEServing->fronly) { FileWrite(pCLServing->fd, FLAGFALSE, "can't write to read-only console]\r\n", -1); continue; } if (!pCLServing->fwr) { FileWrite(pCLServing->fd, FLAGFALSE, "attach to send character]\r\n", -1); continue; } BuildString((char *)0, pCLServing->accmd); pCLServing->iState = S_QUOTE; FileWrite(pCLServing->fd, FLAGFALSE, "quote \\", -1); break; case 0xD6: /* 'v' with high bit set */ /* this is really just used "behind the scenes", * but of someone wants to type it, fine...we * just need a way to query the server for what * version it is so the client can determine * functionality */ FilePrint(pCLServing->fd, FLAGFALSE, "%u]\r\n", VERSION_UINT); break; default: /* unknown sequence */ unknownchar: #if USE_EXTENDED_MESSAGES FilePrint(pCLServing->fd, FLAGFALSE, "unknown -- use `?'%s]\r\n", Challenge()); #else FileWrite(pCLServing->fd, FLAGFALSE, "unknown -- use `?']\r\n", -1); #endif break; } continue; } } nr -= l; MemMove(acIn, acIn + l, nr); } } void FlushConsole(CONSENT *pCEServing) { static STRING *buf = (STRING *)0; int offset = 0; /* we buffered console data in PutConsole() so that we can * send more than 1-byte payloads, if we get more than 1-byte * of data from a client connection. here we flush that buffer, * possibly putting it into the write buffer (but we don't really * need to worry about that here. */ if (pCEServing->wbuf->used <= 1) { return; } if (!(pCEServing->fup && pCEServing->ioState == ISNORMAL)) { /* if we have data but aren't up, drop it */ BuildString((char *)0, pCEServing->wbuf); pCEServing->wbufIAC = 0; return; } if (buf == (STRING *)0) buf = AllocString(); BuildString((char *)0, buf); /* while wbuf * if wbufIAC == 1, yikes * else if wbufIAC == 0, buffer all data, move offset * else if wbufIAC > 2, buffer data, wbufIAC = 2, move offset * else if wbufIAC == 2, then * if heavy * write buffer * if flushed, do heavy, else break * break * else if light * buffer data * search for new wbufIAC */ { static STRING *s; if (s == (STRING *)0) s = AllocString(); BuildString((char *)0, s); FmtCtlStr(pCEServing->wbuf->string, pCEServing->wbuf->used, s); CONDDEBUG((1, "Kiddie(): wbuf=%s", s->string)); } while (pCEServing->wbuf->used > 1 && offset < pCEServing->wbuf->used - 1) { CONDDEBUG((1, "Kiddie(): wbuf->used=%d, offset=%d, wbufIAC=%d", pCEServing->wbuf->used, offset, pCEServing->wbufIAC)); if (pCEServing->wbufIAC >= pCEServing->wbuf->used) { /* this should never really happen...but in case it * does, just reset wbufIAC and try again. */ CONDDEBUG((1, "Kiddie(): invalid wbufIAC setting for [%s]", pCEServing->server)); } else if (pCEServing->wbufIAC == 1) { Error("[%s] internal failure: wbufIAC==1", pCEServing->server); offset = pCEServing->wbuf->used - 1; /* bail */ } else if (pCEServing->wbufIAC == 0) { CONDDEBUG((1, "Kiddie(): flushing final %d non-IAC bytes to [%s]", pCEServing->wbuf->used - 1 - offset, pCEServing->server)); BuildStringN(pCEServing->wbuf->string + offset, pCEServing->wbuf->used - 1 - offset, buf); offset = pCEServing->wbuf->used - 1; } else if (pCEServing->wbufIAC > 2) { CONDDEBUG((1, "Kiddie(): flushing %d non-IAC bytes to [%s]", pCEServing->wbufIAC - 2, pCEServing->server)); BuildStringN(pCEServing->wbuf->string + offset, pCEServing->wbufIAC - 2, buf); offset += pCEServing->wbufIAC - 2; pCEServing->wbufIAC = 2; continue; } else { /* wbufIAC == 2 */ unsigned char next = (unsigned char)pCEServing->wbuf->string[offset + 1]; if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'z') || (next == BREAK && pCEServing->type != HOST)) { CONDDEBUG((1, "Kiddie(): heavy IAC for [%s]", pCEServing->server)); offset += 2; /* if we have buffered data, send it */ if (buf->used > 1) { CONDDEBUG((1, "Kiddie(): heavy IAC flushing %d leading bytes for [%s]", buf->used - 1, pCEServing->server)); if (FileWrite (pCEServing->cofile, FLAGFALSE, buf->string, buf->used - 1) < 0) { Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); BuildString((char *)0, buf); break; } BuildString((char *)0, buf); } /* if we didn't flush everything, bail and get * it the next time around (hopefully it'll have * cleared...or will soon. */ if (!FileBufEmpty(pCEServing->cofile)) { CONDDEBUG((1, "Kiddie(): heavy IAC (wait for flush) for [%s]", pCEServing->server)); break; } /* Do the operation */ if ((next >= '0' && next <= '9') || (next >= 'a' && next <= 'z')) { int delay = BREAKDELAYDEFAULT; int bnum = next - '1' - (next > '9' ? BREAKALPHAOFFSET : 0); if (next != '0') delay = breakList[bnum].delay; /* in theory this sets the break length to whatever * the "default" break sequence is for the console. * but, i think it would be better to just use the * global default (250ms right now) so that you * don't have to change things or get anything * unexpected. remember, this is really just for * idle strings... else { if (pCEServing->breakNum != 0 && breakList[pCEServbing->breakNum - 1].seq->used <= 1) delay = breakList[pCEServbing->breakNum - 1].delay; } */ CONDDEBUG((1, "Kiddie(): heavy IAC - doing usleep() for [%s] (break #%c - delay %dms)", pCEServing->server, next, delay)); if (delay != 0) usleep(delay * 1000); } else if (next == BREAK) { CONDDEBUG((1, "Kiddie(): heavy IAC - sending break for [%s]", pCEServing->server)); #if HAVE_FREEIPMI if (pCEServing->type == IPMI) { if (ipmiconsole_ctx_generate_break (pCEServing->ipmictx) == -1) { if (pCEServing->pCLwr != (CONSCLIENT *)0) FilePrint(pCEServing->pCLwr->fd, FLAGFALSE, "[ipmiconsole_ctx_generate_break() failed: %s]\r\n", ipmiconsole_ctx_errormsg (pCEServing->ipmictx)); } } else { #endif if (tcsendbreak(FileFDNum(pCEServing->cofile), 0) == -1) { if (pCEServing->pCLwr != (CONSCLIENT *)0) FileWrite(pCEServing->pCLwr->fd, FLAGFALSE, "[tcsendbreak() failed]\r\n", -1); } #if HAVE_FREEIPMI } #endif } /* we do this 'cause we just potentially paused for * a half-second doing a break...or even the * intentional usleep(). we could take out the * justHadDelay bits and continue with the stream, * but this allows us to process other consoles and * then come around and do more on this one. you * see, someone could have a '\d\z\d\z\d\z' sequence * as a break string and we'd have about a 2 second * delay added up if we process it all at once. * we're just trying to be nice here. */ break; } else { CONDDEBUG((1, "Kiddie(): soft IAC for fd [%s]", pCEServing->server)); offset += 2; if (next == IAC) { CONDDEBUG((1, "Kiddie(): soft IAC processing IAC for [%s]", pCEServing->server)); BuildStringChar((char)IAC, buf); } else if (next == BREAK && pCEServing->type == HOST) { CONDDEBUG((1, "Kiddie(): soft IAC processing HOST BREAK for [%s]", pCEServing->server)); BuildStringChar((char)IAC, buf); BuildStringChar((char)BREAK, buf); } else { CONDDEBUG((1, "Kiddie(): soft IAC unprocessable IAC for [%s]", pCEServing->server)); } } } /* hunt for a new IAC position */ if (offset < pCEServing->wbuf->used - 1) { char *iac = StringChar(pCEServing->wbuf, offset, (char)IAC); CONDDEBUG((1, "Kiddie(): hunting for new IAC for [%s]", pCEServing->server)); if (iac == (char *)0) pCEServing->wbufIAC = 0; else pCEServing->wbufIAC = (iac - pCEServing->wbuf->string - offset) + 2; } else pCEServing->wbufIAC = 0; } if (buf->used > 1) { CONDDEBUG((1, "Kiddie(): flushing buffer of %d bytes for [%s]", buf->used - 1, pCEServing->server)); if (FileWrite (pCEServing->cofile, FLAGFALSE, buf->string, buf->used - 1) < 0) { Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); return; } BuildString((char *)0, buf); } /* nuke the data alread sent */ if (offset >= pCEServing->wbuf->used - 1) { BuildString((char *)0, pCEServing->wbuf); } else if (offset > 0) { ShiftString(pCEServing->wbuf, offset); } if (pCEServing->wbuf->used > 1) { char *iac = StringChar(pCEServing->wbuf, 0, (char)IAC); CONDDEBUG((1, "Kiddie(): hunting for new IAC for [%s]", pCEServing->server)); if (iac == (char *)0) pCEServing->wbufIAC = 0; else pCEServing->wbufIAC = (iac - pCEServing->wbuf->string) + 2; CONDDEBUG((1, "Kiddie(): watching writability for fd %d 'cause we have buffered data", FileFDNum(pCEServing->cofile))); FD_SET(FileFDNum(pCEServing->cofile), &winit); } else { pCEServing->wbufIAC = 0; if (FileBufEmpty(pCEServing->cofile)) { CONDDEBUG((1, "Kiddie(): removing writability for fd %d 'cause we don't have buffered data", FileFDNum(pCEServing->cofile))); FD_CLR(FileFDNum(pCEServing->cofile), &winit); } } pCEServing->lastWrite = time((time_t *)0); if (pCEServing->idletimeout != (time_t)0 && (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > pCEServing->lastWrite + pCEServing->idletimeout)) timers[T_CIDLE] = pCEServing->lastWrite + pCEServing->idletimeout; } /* routine used by the child processes. (ksb/fine) * Most of it is escape sequence parsing. * fine: * All of it is squirrely code, for which I most humbly apologize * ksb: * Note the states a client can be in, all of the client processing * is done one character at a time, we buffer and shift a lot -- this * stops the denial of services attack where a user telnets to the * group port and just hangs it (by not following the protocol). I've * repaired this by letting all new clients on a bogus console that is * a kinda control line for the group. They have to use the `;' * command to shift to a real console before they can get any (real) * thrills. * * If you were not awake in your finite state machine course this code * should scare the shit out of you; but there are a few invarients: * - the fwr (I can write) bit always is set *after* the * notification that to the console (and reset before) * - we never look at more than one character at a time, even * when we read a hunk from the MUX we string it out in a loop * - look at the output (x, u, w) and attach (a, f, ;) commands * for more clues * * NB: the ZZZ markers below indicate places where I didn't have time * (machine?) to test some missing bit of tty diddling, I'd love * patches for other ioctl/termio/termios stuff -- ksb * */ static void Kiddie(GRPENT *pGE, int sfd) { CONSCLIENT *pCL, /* console we must scan/notify */ *pCLServing; /* client we are serving */ CONSENT *pCEServing; /* console we are talking to */ GRPENT *pGEtmp; REMOTE *pRCtmp; int ret; time_t tyme; time_t tymer; int fd; socklen_t so; fd_set rmask; fd_set wmask; struct timeval tv; struct timeval *tvp; /* nuke the other group lists - of no use in the child */ while (pGroups != (GRPENT *)0) { pGEtmp = pGroups->pGEnext; if (pGroups != pGE) DestroyGroup(pGroups); pGroups = pGEtmp; } pGroups = pGE; pGE->pGEnext = (GRPENT *)0; /* nuke the remote consoles - of no use in the child */ while (pRCList != (REMOTE *)0) { pRCtmp = pRCList->pRCnext; DestroyRemoteConsole(pRCList); pRCList = pRCtmp; } if ((pGE->pCEctl = (CONSENT *)calloc(1, sizeof(CONSENT))) == (CONSENT *)0) OutOfMem(); /* turn off signals that master() might have turned on * (only matters if respawned) */ SimpleSignal(SIGQUIT, SIG_IGN); SimpleSignal(SIGPIPE, SIG_IGN); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_IGN); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_IGN); #endif #if defined(SIGPOLL) SimpleSignal(SIGPOLL, SIG_IGN); #endif #if defined(SIGXFSZ) SimpleSignal(SIGXFSZ, SIG_IGN); #endif SimpleSignal(SIGTERM, FlagGoAway); SimpleSignal(SIGCHLD, FlagReapVirt); SimpleSignal(SIGINT, FlagGoAwayAlso); BuildTmpString((char *)0); if ((pGE->pCEctl->server = StrDup(BuildTmpStringPrint("ctl_%hu", pGE->port))) == (char *)0) OutOfMem(); /* set up stuff for the select() call once, then just copy it * rinit is all the fd's we might get data on, we copy it * to rmask before we call select, this saves lots of prep work * we used to do in the loop, but we have to mod rinit whenever * we add a connection or drop one... (ksb) */ /*maxfd = GetMaxFiles(); */ FD_ZERO(&rinit); FD_ZERO(&winit); FD_SET(sfd, &rinit); if (maxfd < sfd + 1) maxfd = sfd + 1; /* open all the files we need for the consoles in our group * if we can't get one (bitch and) flag as down */ ReUp(pGE, 0); /* prime the list of free connection slots */ if ((pGE->pCLfree = (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT))) == (CONSCLIENT *)0) OutOfMem(); pGE->pCLfree->acid = AllocString(); pGE->pCLfree->username = AllocString(); pGE->pCLfree->peername = AllocString(); pGE->pCLfree->accmd = AllocString(); /* on a SIGHUP we should close and reopen our log files and * reread the config file */ SimpleSignal(SIGHUP, FlagSawChldHUP); /* on a SIGUSR2 we should close and reopen our log files */ SimpleSignal(SIGUSR2, FlagSawChldUSR2); /* on a SIGUSR1 we try to bring up all downed consoles */ SimpleSignal(SIGUSR1, FlagReUp); /* prime the pump */ RollLogs(pGE); Mark(pGE); /* the MAIN loop a group server */ pGE->pCLall = (CONSCLIENT *)0; while (1) { /* check signal flags */ if (fSawGoAway) { fSawGoAway = 0; DeUtmp(pGE, sfd); } if (fSawReapVirt) { fSawReapVirt = 0; ReapVirt(pGE); } if (fSawChldHUP) { fSawChldHUP = 0; ReopenLogfile(); ReopenUnifiedlog(); ReReadCfg(sfd, -1); pGE = pGroups; ReOpen(pGE); ReUp(pGE, 0); } if (fSawChldUSR2) { fSawChldUSR2 = 0; ReopenLogfile(); ReopenUnifiedlog(); ReOpen(pGE); ReUp(pGE, 0); } if (fSawReUp) { fSawReUp = 0; ReUp(pGE, 0); } /* check for timeouts with consoles */ tymer = (time_t)0; tyme = time((time_t *)0); /* check for state timeouts (currently connect() timeouts) */ if (timers[T_STATE] != (time_t)0 && tyme >= timers[T_STATE]) { timers[T_STATE] = (time_t)0; for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0; pCEServing = pCEServing->pCEnext) { if (pCEServing->stateTimer == (time_t)0) continue; if (pCEServing->stateTimer > tyme) { if (timers[T_STATE] == (time_t)0 || timers[T_STATE] > pCEServing->stateTimer) timers[T_STATE] = pCEServing->stateTimer; continue; } pCEServing->stateTimer = (time_t)0; if (pCEServing->ioState != INCONNECT) continue; SendIWaitClientsMsg(pCEServing, "down]\r\n"); Error("[%s] connect timeout: forcing down", pCEServing->server); /* can't use ConsoleError() here otherwise we could reinit * the console repeatedly (immediately). we know there are * no clients attached, so it's basically the same. */ ConsDown(pCEServing, FLAGTRUE, FLAGTRUE); } } /* process any idle timeouts */ if (timers[T_CIDLE] != (time_t)0 && tyme >= timers[T_CIDLE]) { timers[T_CIDLE] = (time_t)0; for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0; pCEServing = pCEServing->pCEnext) { /* if we aren't in a normal state, skip it */ if (!(pCEServing->fup && pCEServing->ioState == ISNORMAL)) continue; /* should we check for a r/w user too and only idle when * they aren't connected? right now, i think we want to * do the idle stuff even if we have users */ if (pCEServing->idletimeout != 0) { time_t chime = pCEServing->lastWrite + pCEServing->idletimeout; if (tyme < chime) { if (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > chime) timers[T_CIDLE] = chime; continue; } ExpandString(pCEServing->idlestring, pCEServing, 0); TagLogfileAct(pCEServing, "idle timeout"); FlushConsole(pCEServing); SendClientsMsg(pCEServing, "[-- idle timeout --]\r\n"); /* we're not technically correct here in saying the write * happened, but we don't want to accidentally trigger * another idle action, so we lie...when the buffer gets * flushed, this will be updated and correct. */ pCEServing->lastWrite = tyme; chime = tyme + pCEServing->idletimeout; if (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > chime) timers[T_CIDLE] = chime; } } } /* see if we need to bring things back up or mark logfiles * or do other such events here. we call time() each time * in case one of the subroutines actually takes a long time * to complete */ if (timers[T_MARK] != (time_t)0 && time((time_t *)0) >= timers[T_MARK]) Mark(pGE); if (timers[T_INITDELAY] != (time_t)0 && time((time_t *)0) >= timers[T_INITDELAY]) { timers[T_INITDELAY] = (time_t)0; ReUp(pGE, -1); } if (timers[T_REINIT] != (time_t)0 && time((time_t *)0) >= timers[T_REINIT]) ReUp(pGE, 2); /* must do ReUp(,1) last for timers to work right */ if (timers[T_AUTOUP] != (time_t)0 && time((time_t *)0) >= timers[T_AUTOUP]) ReUp(pGE, 1); /* do we need to check on log file sizes? */ if (timers[T_ROLL] != (time_t)0 && time((time_t *)0) >= timers[T_ROLL]) RollLogs(pGE); /* check on various timers and set the appropriate timeout */ /* all this so we don't have to use alarm() any more... */ /* look for the next nearest timeout */ for (ret = 0; ret < T_MAX; ret++) { if (timers[ret] != (time_t)0 && (tymer == (time_t)0 || tymer > timers[ret])) tymer = timers[ret]; } /* if we have a timer, figure out the delay left */ if (tymer != (time_t)0) { tyme = time((time_t *)0); if (tymer > tyme) /* in the future */ tv.tv_sec = tymer - tyme; else /* now or in the past */ tv.tv_sec = 1; tv.tv_usec = 0; tvp = &tv; } else /* no timeout */ tvp = (struct timeval *)0; if (tvp == (struct timeval *)0) { CONDDEBUG((1, "Kiddie(): no select timeout")); } else { CONDDEBUG((1, "Kiddie(): select timeout of %d seconds", tv.tv_sec)); } rmask = rinit; wmask = winit; if ((ret = select(maxfd, &rmask, &wmask, (fd_set *)0, tvp)) == -1) { if (errno != EINTR) { Error("Kiddie(): select(): %s", strerror(errno)); break; } continue; } if (ret == 0) /* timeout -- loop back up and handle it */ continue; /* anything on a console? */ for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0; pCEServing = pCEServing->pCEnext) { if (!pCEServing->fup || pCEServing->type == NOOP) continue; switch (pCEServing->ioState) { case INCONNECT: #if HAVE_FREEIPMI if (pCEServing->type == IPMI) { if (FileCanRead (pCEServing->cofile, &rmask, &wmask)) { if (IPMICONSOLE_CTX_STATUS_SOL_ESTABLISHED == ipmiconsole_ctx_status (pCEServing->ipmictx)) { /* Read in the NULL from OUTPUT_ON_SOL_ESTABLISHED flag */ char b[1]; FileRead(pCEServing->cofile, b, 1); /* trust it's NULL */ } else { Error("[%s] IPMI error: %s: forcing down", pCEServing->server, ipmiconsole_ctx_errormsg(pCEServing-> ipmictx)); /* no ConsoleError() for same reason as above */ SendIWaitClientsMsg(pCEServing, "down]\r\n"); ConsDown(pCEServing, FLAGTRUE, FLAGTRUE); break; } } else break; } else { #endif /* freeipmi */ /* deal with this state above as well */ if (FileCanWrite (pCEServing->cofile, &rmask, &wmask)) { socklen_t slen; int flags = 0; int cofile = FileFDNum(pCEServing->cofile); slen = sizeof(flags); /* So, getsockopt seems to return -1 if there is * something interesting in SO_ERROR under * solaris...sheesh. So, the error message has * the small change it's not accurate. */ if (getsockopt (cofile, SOL_SOCKET, SO_ERROR, (char *)&flags, &slen) < 0) { Error ("[%s] getsockopt(%u,SO_ERROR): %s: forcing down", pCEServing->server, cofile, strerror(errno)); /* no ConsoleError() for same reason as above */ SendIWaitClientsMsg(pCEServing, "down]\r\n"); ConsDown(pCEServing, FLAGTRUE, FLAGTRUE); break; } if (flags != 0) { Error("[%s] connect(%u): %s: forcing down", pCEServing->server, cofile, strerror(flags)); /* no ConsoleError() for same reason as above */ SendIWaitClientsMsg(pCEServing, "down]\r\n"); ConsDown(pCEServing, FLAGTRUE, FLAGTRUE); break; } /* waiting for a connect(), we watch the write bit, * so switch around and now watch for the read and * start gathering data */ FD_SET(cofile, &rinit); FD_CLR(cofile, &winit); } else break; #if HAVE_FREEIPMI } #endif pCEServing->ioState = ISNORMAL; pCEServing->lastWrite = time((time_t *)0); #if HAVE_GETTIMEOFDAY if (gettimeofday(&tv, (void *)0) == 0) pCEServing->lastInit = tv; #else if ((tv = time((time_t *)0)) != (time_t)-1) pCEServing->lastInit = tv; #endif if (pCEServing->idletimeout != (time_t)0 && (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > pCEServing->lastWrite + pCEServing->idletimeout)) timers[T_CIDLE] = pCEServing->lastWrite + pCEServing->idletimeout; if (pCEServing->downHard == FLAGTRUE) { Msg("[%s] console up", pCEServing->server); pCEServing->downHard = FLAGFALSE; } SendIWaitClientsMsg(pCEServing, "up]\r\n"); StartInit(pCEServing); break; case ISNORMAL: if (FileCanRead(pCEServing->cofile, &rmask, &wmask)) DoConsoleRead(pCEServing); if (FileCanRead(pCEServing->initfile, &rmask, &wmask)) DoCommandRead(pCEServing); if (FileCanRead(pCEServing->taskfile, &rmask, &wmask)) DoTaskRead(pCEServing); /* fall through to ISFLUSHING for buffered data */ case ISFLUSHING: /* write cofile data */ if (!FileBufEmpty(pCEServing->cofile) && FileCanWrite(pCEServing->cofile, &rmask, &wmask)) { CONDDEBUG((1, "Master(): flushing fd %d", FileFDNum(pCEServing->cofile))); if (FileWrite (pCEServing->cofile, FLAGFALSE, (char *)0, 0) < 0) { Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); break; } } /* write fdlog data */ if (!FileBufEmpty(pCEServing->fdlog) && FileCanWrite(pCEServing->fdlog, &rmask, &wmask)) { CONDDEBUG((1, "Kiddie(): flushing fd %d", FileFDNum(pCEServing->fdlog))); if (FileWrite (pCEServing->fdlog, FLAGFALSE, (char *)0, 0) < 0) { Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); break; } } /* write initfile data */ if (!FileBufEmpty(pCEServing->initfile) && FileCanWrite(pCEServing->initfile, &rmask, &wmask)) { CONDDEBUG((1, "Kiddie(): flushing fd %d", FileFDNum(pCEServing->initfile))); if (FileWrite (pCEServing->initfile, FLAGFALSE, (char *)0, 0) < 0) { Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); break; } } /* stop if we're in ISFLUSHING state and out of data */ if ((pCEServing->ioState == ISFLUSHING) && FileBufEmpty(pCEServing->cofile) && FileBufEmpty(pCEServing->fdlog) && FileBufEmpty(pCEServing->initfile)) /* no ConsoleError() for same reason as above */ ConsDown(pCEServing, FLAGFALSE, FLAGTRUE); break; default: /* this really can't ever happen */ Error ("Kiddie(): console socket state == %d -- THIS IS A BUG", pCEServing->ioState); /* no ConsoleError() for same reason as above */ ConsDown(pCEServing, FLAGTRUE, FLAGTRUE); break; } } /* anything on a client? */ for (pCLServing = pGE->pCLall; (CONSCLIENT *)0 != pCLServing; pCLServing = pCLServing->pCLscan) { switch (pCLServing->ioState) { #if HAVE_OPENSSL case INSSLACCEPT: if (FileCanSSLAccept(pCLServing->fd, &rmask, &wmask)) { int r; if ((r = FileSSLAccept(pCLServing->fd)) < 0) DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); else if (r == 1) pCLServing->ioState = ISNORMAL; } break; #endif #if HAVE_GSSAPI case INGSSACCEPT: { int r; if ((r = AttemptGSSAPI(pCLServing)) < 0) DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); else if (r == 1) pCLServing->ioState = ISNORMAL; } break; #endif case ISNORMAL: if (FileCanRead(pCLServing->fd, &rmask, &wmask)) DoClientRead(pGE, pCLServing); /* fall through to ISFLUSHING for buffered data */ case ISFLUSHING: if (!FileBufEmpty(pCLServing->fd) && FileCanWrite(pCLServing->fd, &rmask, &wmask)) { CONDDEBUG((1, "Kiddie(): flushing fd %d", FileFDNum(pCLServing->fd))); if (FileWrite (pCLServing->fd, FLAGFALSE, (char *)0, 0) < 0) { DisconnectClient(pGE, pCLServing, (char *)0, FLAGTRUE); break; } } if ((pCLServing->ioState == ISFLUSHING) && FileBufEmpty(pCLServing->fd)) DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); break; default: /* this really can't ever happen */ Error ("Kiddie(): client socket state == %d -- THIS IS A BUG", pCLServing->ioState); DisconnectClient(pGE, pCLServing, (char *)0, FLAGFALSE); break; } } /* we buffered console data in PutConsole() so that we can * send more than 1-byte payloads, if we get more than 1-byte * of data from a client connection. here we flush that buffer, * possibly putting it into the write buffer (but we don't really * need to worry about that here). */ for (pCEServing = pGE->pCElist; pCEServing != (CONSENT *)0; pCEServing = pCEServing->pCEnext) FlushConsole(pCEServing); /* if nothing on control line, get more */ if (!FD_ISSET(sfd, &rmask)) { continue; } /* accept new connections and deal with them */ #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION dmallocMarkClientConnection = dmalloc_mark(); #endif so = sizeof(INADDR_STYPE); fd = accept(sfd, (struct sockaddr *)&pGE->pCLfree->cnct_port, &so); if (fd < 0) { Error("Kiddie(): accept(): %s", strerror(errno)); #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "Kiddie(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif continue; } if (SetFlags(fd, O_NONBLOCK, 0)) { pGE->pCLfree->fd = FileOpenFD(fd, simpleSocket); FileSetQuoteIAC(pGE->pCLfree->fd, FLAGTRUE); } else pGE->pCLfree->fd = (CONSFILE *)0; if ((CONSFILE *)0 == pGE->pCLfree->fd) { Error("Kiddie(): FileOpenFD(): %s", strerror(errno)); close(fd); #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "Kiddie(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif continue; } /* save pCL so we can advance to the next free one */ pCL = pGE->pCLfree; pGE->pCLfree = pCL->pCLnext; /* init the identification stuff */ BuildString((char *)0, pCL->peername); BuildString((char *)0, pCL->acid); BuildString("@", pCL->acid); BuildString((char *)0, pCL->username); BuildString("", pCL->username); StrCpy(pCL->actym, StrTime(&(pCL->tym)), sizeof(pCL->actym)); pCL->typetym = pCL->tym; /* link into the control list for the dummy console */ pCL->pCEto = pGE->pCEctl; pCL->pCLnext = pGE->pCEctl->pCLon; pCL->ppCLbnext = &pGE->pCEctl->pCLon; if ((CONSCLIENT *)0 != pCL->pCLnext) { pCL->pCLnext->ppCLbnext = &pCL->pCLnext; } pGE->pCEctl->pCLon = pCL; /* link into all clients list */ pCL->pCLscan = pGE->pCLall; pCL->ppCLbscan = &pGE->pCLall; if ((CONSCLIENT *)0 != pCL->pCLscan) { pCL->pCLscan->ppCLbscan = &pCL->pCLscan; } pGE->pCLall = pCL; FD_SET(FileFDNum(pCL->fd), &rinit); if (maxfd < FileFDNum(pCL->fd) + 1) maxfd = FileFDNum(pCL->fd) + 1; /* init the fsm */ pCL->fecho = 0; pCL->iState = S_IDENT; pCL->ic[0] = DEFATTN; pCL->ic[1] = DEFESC; pCL->replay = DEFREPLAY; pCL->playback = DEFPLAYBACK; BuildString((char *)0, pCL->accmd); /* mark as stopped (no output from console) * and spy only (on chars to console) */ pCL->fcon = 0; pCL->fwr = 0; pCL->fwantwr = 0; /* remove from the free list * if we ran out of free slots, calloc one... */ if ((CONSCLIENT *)0 == pGE->pCLfree) { if ((pGE->pCLfree = (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT))) == (CONSCLIENT *)0) OutOfMem(); pGE->pCLfree->acid = AllocString(); pGE->pCLfree->username = AllocString(); pGE->pCLfree->peername = AllocString(); pGE->pCLfree->accmd = AllocString(); } if (ClientAccessOk(pCL)) { pCL->ioState = ISNORMAL; /* say hi to start */ FileWrite(pCL->fd, FLAGFALSE, "ok\r\n", -1); BuildString(pCL->peername->string, pCL->acid); CONDDEBUG((1, "Kiddie(): client acid initialized to `%s'", pCL->acid->string)); } else DisconnectClient(pGE, pCL, (char *)0, FLAGFALSE); } } /* create a child process: (fine) * fork off a process for each group with an open socket for connections */ void Spawn(GRPENT *pGE, int msfd) { pid_t pid; int sfd; #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS # if USE_IPV6 int error; struct addrinfo *rp, hints, *res; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; unsigned short bindBasePort; # else socklen_t so; struct sockaddr_in lstn_port; # endif # if HAVE_SETSOCKOPT int true = 1; # endif unsigned short portInc = 0; #else struct sockaddr_un lstn_port; static STRING *portPath = (STRING *)0; #endif #if !USE_IPV6 /* get a socket for listening */ # if HAVE_MEMSET memset((void *)&lstn_port, 0, sizeof(lstn_port)); # else bzero((char *)&lstn_port, sizeof(lstn_port)); # endif #endif #if USE_IPV6 for (rp = bindBaseAddr; rp != NULL; rp = rp->ai_next) { if ((sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) { Error("Spawn(): socket(): %s", strerror(errno)); continue; } # if HAVE_SETSOCKOPT if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true, sizeof(true)) < 0) { Error("Spawn(): setsockopt(%u,SO_REUSEADDR): %s", sfd, strerror(errno)); return; } # endif if (!SetFlags(sfd, O_NONBLOCK, 0)) return; error = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); if (error) { Error("Spawn(): getnameinfo failed: %s", gai_strerror(error)); return; } bindBasePort = (unsigned short)strtol(serv, NULL, 10); while (bind(sfd, rp->ai_addr, rp->ai_addrlen) < 0) { if (bindBasePort && ( # if defined(EADDRINUSE) (errno == EADDRINUSE) || # endif (errno == EACCES)) && ++portInc) { /* * instead of checking for ai_family and modifying * the structure directly we will generate new addrinfo * structure, copy over first entry and release it. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV; snprintf(serv, sizeof(serv), "%hu", bindBasePort + portInc); error = getaddrinfo(host, serv, &hints, &res); if (error) goto OUT; memcpy(rp->ai_addr, res->ai_addr, rp->ai_addrlen); freeaddrinfo(res); continue; } else goto OUT; } break; /* if we're here bind was succesful so get out */ OUT: portInc = 0; close(sfd); } /* if rp is null we did not bind */ if (rp == NULL) { Error("Spawn(): could not bind"); Bye(EX_OSERR); } if (-1 == getsockname(sfd, rp->ai_addr, &rp->ai_addrlen)) { Error("Spawn(): getsockname(%u): %s", sfd, strerror(errno)); Bye(EX_OSERR); } /* * if bindBasePort is 0 we did auto-assign our port. to find out * what we bind to we need to call getsockname (above) and call * getnameinfo again. otherwise we have the port info in * bindBasePort + portInc */ if (!bindBasePort) { error = getnameinfo(rp->ai_addr, rp->ai_addrlen, NULL, 0, serv, sizeof(serv), NI_NUMERICSERV); if (error) { Error("Spawn(): getnameinfo failed: %s", gai_strerror(error)); Bye(EX_OSERR); } pGE->port = (unsigned short)strtol(serv, NULL, 10); } else pGE->port = bindBasePort + portInc; #elif USE_UNIX_DOMAIN_SOCKETS lstn_port.sun_family = AF_UNIX; if (portPath == (STRING *)0) portPath = AllocString(); BuildStringPrint(portPath, "%s/%u", interface, pGE->id); if (portPath->used > sizeof(lstn_port.sun_path)) { Error("Spawn(): path to socket too long: %s", portPath->string); Bye(EX_OSERR); } StrCpy(lstn_port.sun_path, portPath->string, sizeof(lstn_port.sun_path)); /* create a socket to listen on * (prepared by master so he can see the port number of the kid) */ if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { Error("Spawn(): socket(): %s", strerror(errno)); Bye(EX_OSERR); } if (!SetFlags(sfd, O_NONBLOCK, 0)) Bye(EX_OSERR); if (bind(sfd, (struct sockaddr *)&lstn_port, sizeof(lstn_port)) < 0) { Error("Spawn(): bind(%s): %s", lstn_port.sun_path, strerror(errno)); Bye(EX_OSERR); } # ifdef TRUST_UDS_CRED /* Allow everyone to connect, but we later auth them via SO_PEERCRED */ chmod(lstn_port.sun_path, 0666); # endif pGE->port = pGE->id; #else lstn_port.sin_family = AF_INET; lstn_port.sin_addr.s_addr = bindAddr; lstn_port.sin_port = htons(bindBasePort); /* create a socket to listen on * (prepared by master so he can see the port number of the kid) */ if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { Error("Spawn(): socket(): %s", strerror(errno)); Bye(EX_OSERR); } # if HAVE_SETSOCKOPT if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true, sizeof(true)) < 0) { Error("Spawn(): setsockopt(%u,SO_REUSEADDR): %s", sfd, strerror(errno)); Bye(EX_OSERR); } # endif if (!SetFlags(sfd, O_NONBLOCK, 0)) Bye(EX_OSERR); while (bind(sfd, (struct sockaddr *)&lstn_port, sizeof(lstn_port)) < 0) { if (bindBasePort && ( # if defined(EADDRINUSE) (errno == EADDRINUSE) || # endif (errno == EACCES)) && ++portInc) { lstn_port.sin_port = htons(bindBasePort + portInc); } else { Error("Spawn(): bind(%hu): %s", ntohs(lstn_port.sin_port), strerror(errno)); Bye(EX_OSERR); } } so = sizeof(lstn_port); if (-1 == getsockname(sfd, (struct sockaddr *)&lstn_port, &so)) { Error("Spawn(): getsockname(%u): %s", sfd, strerror(errno)); Bye(EX_OSERR); } pGE->port = ntohs(lstn_port.sin_port); #endif fflush(stderr); fflush(stdout); switch (pid = fork()) { case -1: Error("Spawn(): fork(): %s", strerror(errno)); Bye(EX_OSERR); default: close(sfd); /* hmm...there seems to be a potential linux bug here as well. * if you have a parent and child both sharing a socket and the * parent is able to close it and create a new socket (same port * request) before the child is able to listen() and you have * been using SO_REUSEADDR, then you get two processes listening * to the same port - only one appears to get the connections. * sleeping a bit not only throttles startup impact (a bit) but * it gives the child a chance to listen() before the parent * possibly opens another socket to the port. this really is only * an issue if you use the same port with -p and -b, i think. */ usleep(750000); /* pause 0.75 sec to throttle startup a bit */ pGE->pid = pid; return; case 0: pGE->pid = thepid = getpid(); isMaster = 0; break; } #if HAVE_SETPROCTITLE if (config->setproctitle == FLAGTRUE) setproctitle("group %u: port %hu, %d %s", pGE->id, pGE->port, pGE->imembers, pGE->imembers == 1 ? "console" : "consoles"); #endif /* close the master fd - which is there *except* on startup */ if (msfd != -1) close(msfd); /* clean out the master client lists - they aren't useful here and just * cause extra file descriptors and memory allocation to lie around, * not a very good thing! */ while (pCLmall != (CONSCLIENT *)0) { CONSCLIENT *pCL; if (pCLmall->fd != (CONSFILE *)0) { int fd; fd = FileUnopen(pCLmall->fd); pCLmall->fd = (CONSFILE *)0; CONDDEBUG((1, "Spawn(): closing Master() client fd %d", fd)); close(fd); FD_CLR(fd, &rinit); FD_CLR(fd, &winit); } pCL = pCLmall->pCLscan; DestroyClient(pCLmall); pCLmall = pCL; } while (pCLmfree != (CONSCLIENT *)0) { CONSCLIENT *pCL; pCL = pCLmfree->pCLnext; DestroyClient(pCLmfree); pCLmfree = pCL; } if (listen(sfd, SOMAXCONN) < 0) { #if USE_UNIX_DOMAIN_SOCKETS Error("Spawn(): listen(%s): %s", lstn_port.sun_path, strerror(errno)); #else Error("Spawn(): listen(%hu): %s", pGE->port, strerror(errno)); #endif Bye(EX_OSERR); } Kiddie(pGE, sfd); /* should never get here...but on errors we could */ close(sfd); #if USE_UNIX_DOMAIN_SOCKETS unlink(lstn_port.sun_path); #endif Bye(EX_SOFTWARE); } conserver-8.2.4/conserver/group.h000066400000000000000000000062421344660520400170560ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* timers used to have various things happen */ #define T_STATE 0 #define T_CIDLE 1 #define T_MARK 2 #define T_REINIT 3 #define T_AUTOUP 4 #define T_ROLL 5 #define T_INITDELAY 6 #define T_MAX 7 /* T_MAX *must* be last */ /* return values used by CheckPass() */ #define AUTH_SUCCESS 0 /* ok */ #define AUTH_NOUSER 1 /* no user */ #define AUTH_INVALID 2 /* invalid password */ typedef struct grpent { /* group info */ unsigned int id; /* uniqueue group id */ unsigned short port; /* port group listens on */ pid_t pid; /* pid of server for group */ int imembers; /* number of consoles in this group */ CONSENT *pCElist; /* list of consoles in this group */ CONSENT *pCEctl; /* our control `console' */ CONSCLIENT *pCLall; /* all clients to scan after select */ CONSCLIENT *pCLfree; /* head of free list */ struct grpent *pGEnext; /* next group entry */ } GRPENT; extern time_t timers[]; extern void Spawn(GRPENT *, int); extern int CheckPass(char *, char *, FLAG); extern void TagLogfile(const CONSENT *, char *, ...); extern void TagLogfileAct(const CONSENT *, char *, ...); extern void DestroyGroup(GRPENT *); extern void DestroyConsent(GRPENT *, CONSENT *); extern void SendClientsMsg(CONSENT *, char *); extern void ResetMark(void); extern void DestroyConsentUsers(CONSENTUSERS **); extern CONSENTUSERS *ConsentFindUser(CONSENTUSERS *, char *); extern int ConsentUserOk(CONSENTUSERS *, char *); extern void DisconnectClient(GRPENT *, CONSCLIENT *, char *, FLAG); extern int ClientAccess(CONSENT *, char *); extern void DestroyClient(CONSCLIENT *); extern int CheckPasswd(CONSCLIENT *, char *, FLAG); extern void DeUtmp(GRPENT *, int); extern void ClientWantsWrite(CONSCLIENT *); extern void SendIWaitClientsMsg(CONSENT *, char *); #if HAVE_OPENSSL extern int AttemptSSL(CONSCLIENT *); #endif conserver-8.2.4/conserver/main.c000066400000000000000000001446761344660520400166570ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include #include #if HAVE_OPENSSL # include #endif #if HAVE_GSSAPI # include #endif int fAll = 0, fNoinit = 0, fVersion = 0, fStrip = 0, fReopen = 0, fNoautoreup = 0, fSyntaxOnly = 0; char *pcConfig = CONFIGFILE; int cMaxMemb = MAXMEMB; #if USE_IPV6 struct addrinfo *bindAddr; struct addrinfo *bindBaseAddr; #else in_addr_t bindAddr = INADDR_ANY; unsigned short bindPort; unsigned short bindBasePort; struct sockaddr_in in_port; #endif static STRING *startedMsg = (STRING *)0; CONFIG *optConf = (CONFIG *)0; CONFIG *config = (CONFIG *)0; char *interface = (char *)0; CONFIG defConfig = { (STRING *)0, FLAGTRUE, 'r', FLAGFALSE, LOGFILEPATH, PASSWDFILE, DEFPORT, FLAGTRUE, FLAGTRUE, 0, DEFBASEPORT, (char *)0, 0 #if HAVE_SETPROCTITLE , FLAGFALSE #endif #if HAVE_OPENSSL , (char *)0, FLAGTRUE, FLAGFALSE, (char *)0 #endif }; CONSFILE *unifiedlog = (CONSFILE *)0; #if HAVE_DMALLOC && DMALLOC_MARK_MAIN unsigned long dmallocMarkMain = 0; #endif #if HAVE_OPENSSL # if OPENSSL_VERSION_NUMBER < 0x10100000L int DH_set0_pqg(DH *dh, BIGNUM * p, BIGNUM * q, BIGNUM * g) { /* If the fields p and g in d are NULL, the corresponding input * parameters MUST be non-NULL. q may remain NULL. */ if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) return 0; if (p != NULL) { BN_free(dh->p); dh->p = p; } if (q != NULL) { BN_free(dh->q); dh->q = q; } if (g != NULL) { BN_free(dh->g); dh->g = g; } if (q != NULL) { dh->length = BN_num_bits(q); } return 1; } # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ SSL_CTX *ctx = (SSL_CTX *)0; DH *dh512 = (DH *)0; DH *dh1024 = (DH *)0; DH *dh2048 = (DH *)0; DH *dh4096 = (DH *)0; DH * DHFromArray(unsigned char *dh_p, size_t dh_p_size, unsigned char *dh_g, size_t dh_g_size) { DH *dh; BIGNUM *p, *g; p = BN_bin2bn(dh_p, dh_p_size, NULL); if (p == NULL) { return (NULL); } g = BN_bin2bn(dh_g, dh_g_size, NULL); if (g == NULL) { BN_free(g); return (NULL); } if ((dh = DH_new()) == NULL) { BN_free(p); BN_free(g); return (NULL); } if (!DH_set0_pqg(dh, p, NULL, g)) { BN_free(p); BN_free(g); DH_free(dh); return (NULL); } return (dh); } DH * GetDH512(void) { static unsigned char dh512_p[] = { 0xF5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x2A, 0x05, 0x5F, }; static unsigned char dh512_g[] = { 0x02, }; return DHFromArray(dh512_p, sizeof(dh512_p), dh512_g, sizeof(dh512_g)); } DH * GetDH1024(void) { static unsigned char dh1024_p[] = { 0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD, 0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B, 0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C, 0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6, 0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B, 0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D, 0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43, 0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C, 0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x2F, 0x78, 0xC7, }; static unsigned char dh1024_g[] = { 0x02, }; return DHFromArray(dh1024_p, sizeof(dh1024_p), dh1024_g, sizeof(dh1024_g)); } DH * GetDH2048(void) { static unsigned char dh2048_p[] = { 0xF6, 0x42, 0x57, 0xB7, 0x08, 0x7F, 0x08, 0x17, 0x72, 0xA2, 0xBA, 0xD6, 0xA9, 0x42, 0xF3, 0x05, 0xE8, 0xF9, 0x53, 0x11, 0x39, 0x4F, 0xB6, 0xF1, 0x6E, 0xB9, 0x4B, 0x38, 0x20, 0xDA, 0x01, 0xA7, 0x56, 0xA3, 0x14, 0xE9, 0x8F, 0x40, 0x55, 0xF3, 0xD0, 0x07, 0xC6, 0xCB, 0x43, 0xA9, 0x94, 0xAD, 0xF7, 0x4C, 0x64, 0x86, 0x49, 0xF8, 0x0C, 0x83, 0xBD, 0x65, 0xE9, 0x17, 0xD4, 0xA1, 0xD3, 0x50, 0xF8, 0xF5, 0x59, 0x5F, 0xDC, 0x76, 0x52, 0x4F, 0x3D, 0x3D, 0x8D, 0xDB, 0xCE, 0x99, 0xE1, 0x57, 0x92, 0x59, 0xCD, 0xFD, 0xB8, 0xAE, 0x74, 0x4F, 0xC5, 0xFC, 0x76, 0xBC, 0x83, 0xC5, 0x47, 0x30, 0x61, 0xCE, 0x7C, 0xC9, 0x66, 0xFF, 0x15, 0xF9, 0xBB, 0xFD, 0x91, 0x5E, 0xC7, 0x01, 0xAA, 0xD3, 0x5B, 0x9E, 0x8D, 0xA0, 0xA5, 0x72, 0x3A, 0xD4, 0x1A, 0xF0, 0xBF, 0x46, 0x00, 0x58, 0x2B, 0xE5, 0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD, 0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B, 0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C, 0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6, 0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B, 0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D, 0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43, 0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C, 0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x32, 0x0B, 0x3B, }; static unsigned char dh2048_g[] = { 0x02, }; return DHFromArray(dh2048_p, sizeof(dh2048_p), dh2048_g, sizeof(dh2048_g)); } DH * GetDH4096(void) { static unsigned char dh4096_p[] = { 0xFA, 0x14, 0x72, 0x52, 0xC1, 0x4D, 0xE1, 0x5A, 0x49, 0xD4, 0xEF, 0x09, 0x2D, 0xC0, 0xA8, 0xFD, 0x55, 0xAB, 0xD7, 0xD9, 0x37, 0x04, 0x28, 0x09, 0xE2, 0xE9, 0x3E, 0x77, 0xE2, 0xA1, 0x7A, 0x18, 0xDD, 0x46, 0xA3, 0x43, 0x37, 0x23, 0x90, 0x97, 0xF3, 0x0E, 0xC9, 0x03, 0x50, 0x7D, 0x65, 0xCF, 0x78, 0x62, 0xA6, 0x3A, 0x62, 0x22, 0x83, 0xA1, 0x2F, 0xFE, 0x79, 0xBA, 0x35, 0xFF, 0x59, 0xD8, 0x1D, 0x61, 0xDD, 0x1E, 0x21, 0x13, 0x17, 0xFE, 0xCD, 0x38, 0x87, 0x9E, 0xF5, 0x4F, 0x79, 0x10, 0x61, 0x8D, 0xD4, 0x22, 0xF3, 0x5A, 0xED, 0x5D, 0xEA, 0x21, 0xE9, 0x33, 0x6B, 0x48, 0x12, 0x0A, 0x20, 0x77, 0xD4, 0x25, 0x60, 0x61, 0xDE, 0xF6, 0xB4, 0x4F, 0x1C, 0x63, 0x40, 0x8B, 0x3A, 0x21, 0x93, 0x8B, 0x79, 0x53, 0x51, 0x2C, 0xCA, 0xB3, 0x7B, 0x29, 0x56, 0xA8, 0xC7, 0xF8, 0xF4, 0x7B, 0x08, 0x5E, 0xA6, 0xDC, 0xA2, 0x45, 0x12, 0x56, 0xDD, 0x41, 0x92, 0xF2, 0xDD, 0x5B, 0x8F, 0x23, 0xF0, 0xF3, 0xEF, 0xE4, 0x3B, 0x0A, 0x44, 0xDD, 0xED, 0x96, 0x84, 0xF1, 0xA8, 0x32, 0x46, 0xA3, 0xDB, 0x4A, 0xBE, 0x3D, 0x45, 0xBA, 0x4E, 0xF8, 0x03, 0xE5, 0xDD, 0x6B, 0x59, 0x0D, 0x84, 0x1E, 0xCA, 0x16, 0x5A, 0x8C, 0xC8, 0xDF, 0x7C, 0x54, 0x44, 0xC4, 0x27, 0xA7, 0x3B, 0x2A, 0x97, 0xCE, 0xA3, 0x7D, 0x26, 0x9C, 0xAD, 0xF4, 0xC2, 0xAC, 0x37, 0x4B, 0xC3, 0xAD, 0x68, 0x84, 0x7F, 0x99, 0xA6, 0x17, 0xEF, 0x6B, 0x46, 0x3A, 0x7A, 0x36, 0x7A, 0x11, 0x43, 0x92, 0xAD, 0xE9, 0x9C, 0xFB, 0x44, 0x6C, 0x3D, 0x82, 0x49, 0xCC, 0x5C, 0x6A, 0x52, 0x42, 0xF8, 0x42, 0xFB, 0x44, 0xF9, 0x39, 0x73, 0xFB, 0x60, 0x79, 0x3B, 0xC2, 0x9E, 0x0B, 0xDC, 0xD4, 0xA6, 0x67, 0xF7, 0x66, 0x3F, 0xFC, 0x42, 0x3B, 0x1B, 0xDB, 0x4F, 0x66, 0xDC, 0xA5, 0x8F, 0x66, 0xF9, 0xEA, 0xC1, 0xED, 0x31, 0xFB, 0x48, 0xA1, 0x82, 0x7D, 0xF8, 0xE0, 0xCC, 0xB1, 0xC7, 0x03, 0xE4, 0xF8, 0xB3, 0xFE, 0xB7, 0xA3, 0x13, 0x73, 0xA6, 0x7B, 0xC1, 0x0E, 0x39, 0xC7, 0x94, 0x48, 0x26, 0x00, 0x85, 0x79, 0xFC, 0x6F, 0x7A, 0xAF, 0xC5, 0x52, 0x35, 0x75, 0xD7, 0x75, 0xA4, 0x40, 0xFA, 0x14, 0x74, 0x61, 0x16, 0xF2, 0xEB, 0x67, 0x11, 0x6F, 0x04, 0x43, 0x3D, 0x11, 0x14, 0x4C, 0xA7, 0x94, 0x2A, 0x39, 0xA1, 0xC9, 0x90, 0xCF, 0x83, 0xC6, 0xFF, 0x02, 0x8F, 0xA3, 0x2A, 0xAC, 0x26, 0xDF, 0x0B, 0x8B, 0xBE, 0x64, 0x4A, 0xF1, 0xA1, 0xDC, 0xEE, 0xBA, 0xC8, 0x03, 0x82, 0xF6, 0x62, 0x2C, 0x5D, 0xB6, 0xBB, 0x13, 0x19, 0x6E, 0x86, 0xC5, 0x5B, 0x2B, 0x5E, 0x3A, 0xF3, 0xB3, 0x28, 0x6B, 0x70, 0x71, 0x3A, 0x8E, 0xFF, 0x5C, 0x15, 0xE6, 0x02, 0xA4, 0xCE, 0xED, 0x59, 0x56, 0xCC, 0x15, 0x51, 0x07, 0x79, 0x1A, 0x0F, 0x25, 0x26, 0x27, 0x30, 0xA9, 0x15, 0xB2, 0xC8, 0xD4, 0x5C, 0xCC, 0x30, 0xE8, 0x1B, 0xD8, 0xD5, 0x0F, 0x19, 0xA8, 0x80, 0xA4, 0xC7, 0x01, 0xAA, 0x8B, 0xBA, 0x53, 0xBB, 0x47, 0xC2, 0x1F, 0x6B, 0x54, 0xB0, 0x17, 0x60, 0xED, 0x79, 0x21, 0x95, 0xB6, 0x05, 0x84, 0x37, 0xC8, 0x03, 0xA4, 0xDD, 0xD1, 0x06, 0x69, 0x8F, 0x4C, 0x39, 0xE0, 0xC8, 0x5D, 0x83, 0x1D, 0xBE, 0x6A, 0x9A, 0x99, 0xF3, 0x9F, 0x0B, 0x45, 0x29, 0xD4, 0xCB, 0x29, 0x66, 0xEE, 0x1E, 0x7E, 0x3D, 0xD7, 0x13, 0x4E, 0xDB, 0x90, 0x90, 0x58, 0xCB, 0x5E, 0x9B, 0xCD, 0x2E, 0x2B, 0x0F, 0xA9, 0x4E, 0x78, 0xAC, 0x05, 0x11, 0x7F, 0xE3, 0x9E, 0x27, 0xD4, 0x99, 0xE1, 0xB9, 0xBD, 0x78, 0xE1, 0x84, 0x41, 0xA0, 0xDF, }; static unsigned char dh4096_g[] = { 0x02, }; return DHFromArray(dh4096_p, sizeof(dh4096_p), dh4096_g, sizeof(dh4096_g)); } DH * TmpDHCallback(SSL *ssl, int is_export, int keylength) { CONDDEBUG((1, "TmpDHCallback(): asked for a DH key length %u", keylength)); switch (keylength) { case 512: if (dh512 == (DH *)0) dh512 = GetDH512(); return dh512; case 1024: if (dh1024 == (DH *)0) dh1024 = GetDH1024(); return dh1024; case 2048: if (dh2048 == (DH *)0) dh2048 = GetDH2048(); return dh2048; default: if (dh4096 == (DH *)0) dh4096 = GetDH4096(); return dh4096; } } void SetupSSL(void) { if (ctx == (SSL_CTX *)0) { char *ciphers; int verifymode; # if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); if (!SSL_library_init()) { Error("SetupSSL(): SSL_library_init() failed"); Bye(EX_SOFTWARE); } # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ if ((ctx = SSL_CTX_new(TLS_method())) == (SSL_CTX *)0) { Error("SetupSSL(): SSL_CTX_new() failed"); Bye(EX_SOFTWARE); } if (SSL_CTX_set_default_verify_paths(ctx) != 1) { Error ("SetupSSL(): could not load SSL default CA file and/or directory"); Bye(EX_SOFTWARE); } if (config->sslcredentials != (char *)0) { if (SSL_CTX_use_certificate_chain_file (ctx, config->sslcredentials) != 1) { Error ("SetupSSL(): could not load SSL certificate from `%s'", config->sslcredentials); Bye(EX_SOFTWARE); } if (SSL_CTX_use_PrivateKey_file (ctx, config->sslcredentials, SSL_FILETYPE_PEM) != 1) { Error ("SetupSSL(): could not load SSL private key from `%s'", config->sslcredentials); Bye(EX_SOFTWARE); } ciphers = "ALL:!LOW:!EXP:!MD5:!aNULL:@STRENGTH"; } else { ciphers = "ALL:aNULL:!LOW:!EXP:!MD5:@STRENGTH" CIPHER_SEC0; } if (config->sslcacertificatefile != (char *)0) { STACK_OF(X509_NAME) * cert_names; cert_names = SSL_load_client_CA_file(config->sslcacertificatefile); if (cert_names != NULL) { SSL_CTX_set_client_CA_list(ctx, cert_names); if (SSL_CTX_load_verify_locations (ctx, config->sslcacertificatefile, NULL) != 1) { Error("Could not setup CA certificate file to '%s'", config->sslcacertificatefile); Bye(EX_UNAVAILABLE); } } else { Error ("SetupSSL(): could not load SSL client CA list from `%s'", config->sslcacertificatefile); Bye(EX_SOFTWARE); } } verifymode = SSL_VERIFY_PEER; if (config->sslreqclientcert == FLAGTRUE) verifymode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; SSL_CTX_set_verify(ctx, verifymode, SSLVerifyCallback); SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); SSL_CTX_set_tmp_dh_callback(ctx, TmpDHCallback); if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) { Error("SetupSSL(): setting SSL cipher list failed"); Bye(EX_SOFTWARE); } /* might want to turn this back on at some point, but i can't * see why right now. */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); } } #endif #if HAVE_GSSAPI gss_name_t gss_myname = GSS_C_NO_NAME; gss_cred_id_t gss_mycreds = GSS_C_NO_CREDENTIAL; void SetupGSSAPI(void) { OM_uint32 stmaj, stmin; char namestr[128]; gss_buffer_desc namebuf; snprintf(namestr, 128, "host@%s", myHostname); namebuf.value = namestr; namebuf.length = strlen(namestr) + 1; stmaj = gss_import_name(&stmin, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_myname); /* XXX: handle error */ if (stmaj != GSS_S_COMPLETE) { Error("gss_import_name failed"); } /* Get some initial credentials */ stmaj = gss_acquire_cred(&stmin, gss_myname, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_mycreds, NULL, NULL); if (stmaj != GSS_S_COMPLETE) { Error("Could not acquire GSS-API credentials"); } } #endif void ReopenLogfile(void) { static int tag = 1; /* redirect stdout and stderr to the logfile. * * first time through any problems will show up (stderr still there). * after that, all bets are off...probably not see the errors (well, * aside from the tail of the old logfile, if it was rolled). */ if (config->daemonmode != FLAGTRUE) return; close(1); /* so, if we aren't in daemon mode, we just return before closing * anything. if we are, there are two possibilities. first, if * logfile is set, we close fd 1, open a file, etc. all should be * well. if logfile isn't set, we end up closing fd 1 and 2 and * returning (in case the logfile was set and then unset [config * file change]). */ if (config->logfile == (char *)0) { close(2); return; } if (1 != open(config->logfile, O_WRONLY | O_CREAT | O_APPEND, 0644)) { tag = 0; Error("ReopenLogfile(): open(%s): %s", config->logfile, strerror(errno)); Bye(EX_TEMPFAIL); } close(2); dup(1); if (isMaster && tag) { Msg(MyVersion()); Msg(startedMsg->string); } tag = 0; } void ReopenUnifiedlog(void) { /* close any existing */ if (unifiedlog != (CONSFILE *)0) FileClose(&unifiedlog); /* return if we aren't opening again */ if (config->unifiedlog == (char *)0) return; /* open a new one */ if ((unifiedlog = FileOpen(config->unifiedlog, O_WRONLY | O_CREAT | O_APPEND, 0644)) == (CONSFILE *)0) { Error("ReopenUnifiedlog(): open(%s): %s", config->unifiedlog, strerror(errno)); return; } } /* become a daemon (ksb) */ static void Daemonize(void) { int res; #if !HAVE_SETSID int td; #endif Msg("daemonizing"); SimpleSignal(SIGQUIT, SIG_IGN); SimpleSignal(SIGINT, SIG_IGN); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_IGN); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_IGN); #endif #if defined(SIGTSTP) SimpleSignal(SIGTSTP, SIG_IGN); #endif #if defined(SIGXFSZ) SimpleSignal(SIGXFSZ, SIG_IGN); #endif fflush(stdout); fflush(stderr); switch (res = fork()) { case -1: Error("Daemonize(): fork(): %s", strerror(errno)); Bye(EX_TEMPFAIL); case 0: thepid = getpid(); break; default: Bye(EX_OK); } ReopenLogfile(); /* Further disassociate this process from the terminal * Maybe this will allow you to start a daemon from rsh, * i.e. with no controlling terminal. */ #if HAVE_SETSID setsid(); #else tcsetpgrp(0, getpid()); /* lose our controlling terminal */ if (-1 != (td = open("/dev/tty", O_RDWR, 0600))) { ioctl(td, TIOCNOTTY, (char *)0); close(td); } #endif } /* output a long message to the user (ksb) */ static void Usage(int wantfull) { static char u_terse[] = "[-7dDEFhinoRSuvV] [-a type] [-m max] [-M master] [-p port] [-b port] [-c cred] [-C config] [-P passwd] [-L logfile] [-O min] [-U logfile]"; static char *full[] = { "7 strip the high bit off all console data", "a type set the default access type", "b port base port for secondary channel (any by default)", #if HAVE_OPENSSL "c cred load an SSL certificate and key from the PEM encoded file", #else "c cred ignored - encryption not compiled into code", #endif "C config give a new config file to the server process", "d become a daemon, redirecting stdout/stderr to logfile", "D enable debug output, sent to stderr", #if HAVE_OPENSSL "E don't require encrypted client connections", #else "E ignored - encryption not compiled into code", #endif "F do not automatically reinitialize failed consoles", "h output this message", "i initialize console connections on demand", "L logfile give a new logfile path to the server process", "m max maximum consoles managed per process", #if USE_UNIX_DOMAIN_SOCKETS "M master directory that holds the Unix domain sockets", #else "M master address to listen on (all addresses by default)", #endif "n obsolete - see -u", "o reopen downed console on client connect", "O min reopen all downed consoles every minutes", #if USE_UNIX_DOMAIN_SOCKETS "p port ignored - Unix domain sockets compiled into code", #else "p port port to listen on", #endif "P passwd give a new passwd file to the server process", "R disable automatic client redirection", "S syntax check of configuration file", "u copy \"unloved\" console data to stdout", "U logfile copy all console data to the \"unified\" logfile", "v be verbose on startup", "V output version info", (char *)0 }; fprintf(stderr, "usage: %s %s\n", progname, u_terse); if (wantfull) { int i; for (i = 0; full[i] != (char *)0; i++) fprintf(stderr, "\t%s\n", full[i]); } } /* show the user our version info (ksb) */ static void Version(void) { static STRING *acA1 = (STRING *)0; static STRING *acA2 = (STRING *)0; int i; char *optionlist[] = { #if HAVE_DMALLOC "dmalloc", #endif #if HAVE_FREEIPMI "freeipmi", #endif #if USE_LIBWRAP "libwrap", #endif #if HAVE_OPENSSL "openssl", #endif #if HAVE_PAM "pam", #endif #if TRUST_REVERSE_DNS "trustrevdns", #endif #if USE_UNIX_DOMAIN_SOCKETS "uds", #endif (char *)0 }; if (acA1 == (STRING *)0) acA1 = AllocString(); if (acA2 == (STRING *)0) acA2 = AllocString(); isMultiProc = 0; Msg(MyVersion()); Msg("default access type `%c'", defConfig.defaultaccess); Msg("default escape sequence `%s%s'", FmtCtl(DEFATTN, acA1), FmtCtl(DEFESC, acA2)); Msg("default configuration in `%s'", CONFIGFILE); Msg("default password in `%s'", defConfig.passwdfile); Msg("default logfile is `%s'", defConfig.logfile); Msg("default pidfile is `%s'", PIDFILE); Msg("default limit is %d member%s per group", MAXMEMB, MAXMEMB == 1 ? "" : "s"); #if USE_UNIX_DOMAIN_SOCKETS Msg("default socket directory `%s'", UDSDIR); #else Msg("default primary port referenced as `%s'", defConfig.primaryport); Msg("default secondary base port referenced as `%s'", defConfig.secondaryport); #endif BuildString((char *)0, acA1); if (optionlist[0] == (char *)0) BuildString("none", acA1); for (i = 0; optionlist[i] != (char *)0; i++) { if (i == 0) BuildString(optionlist[i], acA1); else { BuildString(", ", acA1); BuildString(optionlist[i], acA1); } } Msg("options: %s", acA1->string); #if HAVE_DMALLOC BuildString((char *)0, acA1); BuildStringChar('0' + DMALLOC_VERSION_MAJOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_MINOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_PATCH, acA1); # if defined(DMALLOC_VERSION_BETA) if (DMALLOC_VERSION_BETA != 0) { BuildString("-b", acA1); BuildStringChar('0' + DMALLOC_VERSION_BETA, acA1); } # endif Msg("dmalloc version: %s", acA1->string); #endif #if HAVE_FREEIPMI BuildString((char *)0, acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_MAJOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_MINOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_PATCH, acA1); Msg("freeipmi version: %s", acA1->string); #endif #if HAVE_OPENSSL Msg("openssl version: %s", OPENSSL_VERSION_TEXT); #endif Msg("built with `%s'", CONFIGINVOCATION); if (fVerbose) printf(COPYRIGHT); Bye(EX_OK); } void DestroyDataStructures(void) { GRPENT *pGE; REMOTE *pRC; ACCESS *pAC; while (pGroups != (GRPENT *)0) { pGE = pGroups->pGEnext; DestroyGroup(pGroups); pGroups = pGE; } while (pRCList != (REMOTE *)0) { pRC = pRCList->pRCnext; DestroyRemoteConsole(pRCList); pRCList = pRC; } while (pACList != (ACCESS *)0) { pAC = pACList->pACnext; DestroyAccessList(pACList); pACList = pAC; } DestroyConsentUsers(&pADList); DestroyConsentUsers(&pLUList); DestroyConfig(pConfig); DestroyConfig(optConf); DestroyConfig(config); #if HAVE_OPENSSL if (ctx != (SSL_CTX *)0) SSL_CTX_free(ctx); if (dh512 != (DH *)0) DH_free(dh512); if (dh1024 != (DH *)0) DH_free(dh1024); if (dh2048 != (DH *)0) DH_free(dh2048); if (dh4096 != (DH *)0) DH_free(dh4096); #endif #if USE_IPV6 /* clean up addrinfo stucts */ freeaddrinfo(bindAddr); freeaddrinfo(bindBaseAddr); #else if (myAddrs != (struct in_addr *)0) free(myAddrs); #endif DestroyBreakList(); DestroyTaskList(); DestroyStrings(); DestroyUserList(); if (substData != (SUBST *)0) free(substData); } void SummarizeDataStructures(void) { GRPENT *pGE; REMOTE *pRC; ACCESS *pAC; STRING *str; CONSENT *pCE; NAMES *usr; int count; long size; long total = 0; extern STRING *allStrings; if (!fDebug) return; for (size = 0, count = 0, pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext, count++) { size += sizeof(GRPENT); } CONDDEBUG((1, "Memory Usage (GRPENT objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext, count++) { size += strlen(pCE->server) + sizeof(CONSENT); if (pCE->host != (char *)0) size += strlen(pCE->server); if (pCE->device != (char *)0) size += strlen(pCE->device); if (pCE->exec != (char *)0) size += strlen(pCE->exec); if (pCE->master != (char *)0) size += strlen(pCE->master); if (pCE->logfile != (char *)0) size += strlen(pCE->logfile); if (pCE->initcmd != (char *)0) size += strlen(pCE->initcmd); if (pCE->execSlave != (char *)0) size += strlen(pCE->execSlave); if (pCE->motd != (char *)0) size += strlen(pCE->motd); if (pCE->idlestring != (char *)0) size += strlen(pCE->idlestring); if (pCE->replstring != (char *)0) size += strlen(pCE->replstring); if (pCE->tasklist != (char *)0) size += strlen(pCE->tasklist); if (pCE->breaklist != (char *)0) size += strlen(pCE->breaklist); #if HAVE_FREEIPMI if (pCE->username != (char *)0) size += strlen(pCE->username); if (pCE->password != (char *)0) size += strlen(pCE->password); #endif if (pCE->fdlog != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->cofile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->initfile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->taskfile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->aliases != (NAMES *)0) { NAMES *n; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { size += sizeof(NAMES) + strlen(n->name); } } if (pCE->ro) { CONSENTUSERS *u; for (u = pCE->ro; u != (CONSENTUSERS *)0; u = u->next) { size += sizeof(CONSENTUSERS) + strlen(u->user->name); } } if (pCE->rw) { CONSENTUSERS *u; for (u = pCE->rw; u != (CONSENTUSERS *)0; u = u->next) { size += sizeof(CONSENTUSERS) + strlen(u->user->name); } } } } CONDDEBUG((1, "Memory Usage (CONSENT objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pRC = pRCList; pRC != (REMOTE *)0; pRC = pRC->pRCnext, count++) { size += strlen(pRC->rserver) + strlen(pRC->rhost) + sizeof(REMOTE); if (pRC->aliases != (NAMES *)0) { NAMES *n; for (n = pRC->aliases; n != (NAMES *)0; n = n->next) { size += sizeof(NAMES) + strlen(n->name); } } } CONDDEBUG((1, "Memory Usage (REMOTE objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pAC = pACList; pAC != (ACCESS *)0; pAC = pAC->pACnext, count++) { size += strlen(pAC->pcwho) + sizeof(ACCESS); } CONDDEBUG((1, "Memory Usage (ACCESS objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, str = allStrings; str != (STRING *)0; str = str->next, count++) { size += str->allocated + sizeof(STRING); } CONDDEBUG((1, "Memory Usage (STRING objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, usr = userList; usr != (NAMES *)0; usr = usr->next, count++) { size += strlen(usr->name) + sizeof(NAMES); } CONDDEBUG((1, "Memory Usage (userList objects): %ld (%d)", size, count)); total += size; CONDDEBUG((1, "Memory Usage (total): %ld", total)); } void DumpDataStructures(void) { GRPENT *pGE; CONSENT *pCE; REMOTE *pRC; int i; TASKS *t; #if HAVE_FREEIPMI static STRING *tmpString = (STRING *)0; if (tmpString == (STRING *)0) tmpString = AllocString(); #endif #if HAVE_DMALLOC && DMALLOC_MARK_MAIN CONDDEBUG((1, "DumpDataStructures(): dmalloc / MarkMain")); dmalloc_log_changed(dmallocMarkMain, 1, 0, 1); #endif #define EMPTYSTR(x) x == (char *)0 ? "(null)" : x #define FLAGSTR(x) x == FLAGTRUE ? "true" : (x == FLAGFALSE ? "false" : "unset") if (!fDebug) return; SummarizeDataStructures(); for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { CONDDEBUG((1, "DumpDataStructures(): group: id=%u port=%hu, pid=%lu, imembers=%d", pGE->id, pGE->port, (unsigned long)pGE->pid, pGE->imembers)); for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { switch (pCE->type) { case DEVICE: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=DEVICE", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): baud=%s, parity=%s, device=%s", pCE->baud->acrate, pCE->parity->key, EMPTYSTR(pCE->device))); break; case EXEC: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=EXEC", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): execSlave=%s, exec=%s, ipid=%lu", EMPTYSTR(pCE->execSlave), EMPTYSTR(pCE->exec), (unsigned long)pCE->ipid)); CONDDEBUG((1, "DumpDataStructures(): execuid=%d, execgid=%d", pCE->execuid, pCE->execgid)); break; #if HAVE_FREEIPMI case IPMI: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=IPMI", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): host=%s, username=%s, password=%s, ipmiprivlevel=%d", EMPTYSTR(pCE->host), EMPTYSTR(pCE->username), EMPTYSTR(pCE->password), pCE->ipmiprivlevel)); CONDDEBUG((1, "DumpDataStructures(): ipmiwrkset=%d, ipmiworkaround=%u, ipmiciphersuite=%d", pCE->ipmiwrkset, pCE->ipmiworkaround, pCE->ipmiciphersuite)); FmtCtlStr(pCE->ipmikg->string, pCE->ipmikg->used - 1, tmpString); CONDDEBUG((1, "DumpDataStructures(): ipmikg=%s", EMPTYSTR(tmpString->string))); break; #endif case HOST: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=HOST", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): host=%s, raw=%s, netport=%hu, port=%hu, telnetState=%d", EMPTYSTR(pCE->host), FLAGSTR(pCE->raw), pCE->netport, pCE->port, pCE->telnetState)); break; case NOOP: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=NOOP", EMPTYSTR(pCE->server))); break; case UDS: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=UDS", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): uds=%s", EMPTYSTR(pCE->uds))); break; case UNKNOWNTYPE: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=UNKNOWNTYPE", EMPTYSTR(pCE->server))); break; } if (pCE->aliases != (NAMES *)0) { NAMES *n; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { CONDDEBUG((1, "DumpDataStructures(): alias=%s", n->name)); } } CONDDEBUG((1, "DumpDataStructures(): fup=%d, fronly=%d, logfile=%s, breakNum=%d", pCE->fup, pCE->fronly, EMPTYSTR(pCE->logfile), pCE->breakNum)); CONDDEBUG((1, "DumpDataStructures(): mark=%d, nextMark=%ld, autoReup=%hu, downHard=%s", pCE->mark, pCE->nextMark, pCE->autoReUp, FLAGSTR(pCE->downHard))); CONDDEBUG((1, "DumpDataStructures(): nolog=%d, cofile=%d, activitylog=%s, breaklog=%s", pCE->nolog, FileFDNum(pCE->cofile), FLAGSTR(pCE->activitylog), FLAGSTR(pCE->breaklog))); CONDDEBUG((1, "DumpDataStructures(): tasklog=%s, ixon=%s, ixany=%s, ixoff=%s", FLAGSTR(pCE->tasklog), FLAGSTR(pCE->ixon), FLAGSTR(pCE->ixany), FLAGSTR(pCE->ixoff))); CONDDEBUG((1, "DumpDataStructures(): autoreinit=%s, hupcl=%s, cstopb=%s, ondemand=%s", FLAGSTR(pCE->autoreinit), FLAGSTR(pCE->hupcl), FLAGSTR(pCE->cstopb), FLAGSTR(pCE->ondemand))); #if defined(CRTSCTS) CONDDEBUG((1, "DumpDataStructures(): crtscts=%s", FLAGSTR(pCE->crtscts))); #endif CONDDEBUG((1, "DumpDataStructures(): reinitoncc=%s, striphigh=%s", FLAGSTR(pCE->reinitoncc), FLAGSTR(pCE->striphigh))); CONDDEBUG((1, "DumpDataStructures(): unloved=%s, login=%s", FLAGSTR(pCE->unloved), FLAGSTR(pCE->login))); CONDDEBUG((1, "DumpDataStructures(): initpid=%lu, initcmd=%s, initfile=%d", (unsigned long)pCE->initpid, EMPTYSTR(pCE->initcmd), FileFDNum(pCE->initfile))); CONDDEBUG((1, "DumpDataStructures(): inituid=%d, initgid=%d", pCE->inituid, pCE->initgid)); CONDDEBUG((1, "DumpDataStructures(): motd=%s, idletimeout=%d, idlestring=%s, replstring=%s", EMPTYSTR(pCE->motd), pCE->idletimeout, EMPTYSTR(pCE->idlestring), EMPTYSTR(pCE->replstring))); CONDDEBUG((1, "DumpDataStructures(): tasklist=%s, breaklist=%s, taskpid=%lu, taskfile=%d", EMPTYSTR(pCE->tasklist), EMPTYSTR(pCE->breaklist), (unsigned long)pCE->taskpid, FileFDNum(pCE->taskfile))); if (pCE->ro) { CONSENTUSERS *u; for (u = pCE->ro; u != (CONSENTUSERS *)0; u = u->next) { CONDDEBUG((1, "DumpDataStructures(): ro=%s%s", (u->not ? "!" : ""), u->user->name)); } } if (pCE->rw) { CONSENTUSERS *u; for (u = pCE->rw; u != (CONSENTUSERS *)0; u = u->next) { CONDDEBUG((1, "DumpDataStructures(): rw=%s%s", (u->not ? "!" : ""), u->user->name)); } } CONDDEBUG((1, "DumpDataStructures(): ------")); } } for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) { CONDDEBUG((1, "DumpDataStructures(): remote: rserver=%s, rhost=%s", EMPTYSTR(pRC->rserver), EMPTYSTR(pRC->rhost))); if (pRC->aliases != (NAMES *)0) { NAMES *n; for (n = pRC->aliases; n != (NAMES *)0; n = n->next) { CONDDEBUG((1, "DumpDataStructures(): alias=%s", n->name)); } } } for (i = 0; i < BREAKLISTSIZE; i++) { CONDDEBUG((1, "DumpDataStructures(): break: #%c, string=%s, delay=%d, confirm=%s", '1' + i + (i > 8 ? BREAKALPHAOFFSET : 0), EMPTYSTR(breakList[i].seq->string), breakList[i].delay, FLAGSTR(breakList[i].confirm))); } for (t = taskList; t != (TASKS *)0; t = t->next) { CONDDEBUG((1, "DumpDataStructures(): task: id=%c, cmd=%s, descr=%s, uid=%d, gid=%d, subst=%s, confirm=%s", t->id, EMPTYSTR(t->cmd->string), EMPTYSTR(t->descr->string), t->uid, t->gid, EMPTYSTR(t->subst), FLAGSTR(t->confirm))); } } /* This makes sure a directory exists and tries to create it if it * doesn't. returns 0 for success, -1 for error */ #if USE_UNIX_DOMAIN_SOCKETS int VerifyEmptyDirectory(char *d) { struct stat dstat; DIR *dir; struct dirent *de; # if 0 /* See below */ STRING *path = (STRING *)0; # endif int retval = 0; while (1) { if (stat(d, &dstat) == -1) { if (errno == ENOENT) { if (mkdir(d, 0755) == -1) { Error("mkdir(%s): %s", d, strerror(errno)); return -1; } CONDDEBUG((1, "VerifyEmptyDirectory: created `%s'", d)); continue; } else { Error("stat(%s): %s", d, strerror(errno)); return -1; } } if (S_ISDIR(dstat.st_mode)) break; return -1; } /* now make sure it's empty...erase anything you see, etc */ if ((dir = opendir(d)) == (DIR *) 0) { Error("opendir(%s): %s", d, strerror(errno)); return -1; } while ((de = readdir(dir)) != (struct dirent *)0) { if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0)) continue; /* we're going to just let the user deal with non-empty directories */ Error("non-empty directory `%s'", d); retval = -1; break; /* this is probably too extreme. if someone happens to point conserver * at /etc, for example, it could (if running as root) nuke the password * database, config files, etc. too many important files could be * shredded with a small typo. */ # if 0 if (path == (STRING *)0) path = AllocString(); BuildStringPrint(path, "%s/%s", d, de->d_name); if (stat(path->string, &dstat) == -1) { Error("stat(%s): %s", path->string, strerror(errno)); retval = -1; break; } if (S_ISDIR(dstat.st_mode)) { if (rmdir(path->string) != 0) { Error("rmdir(%s): %s", path->string, strerror(errno)); retval = -1; break; } } else { if (unlink(path->string) != 0) { Error("unlink(%s): %s", path->string, strerror(errno)); retval = -1; break; } } # endif } # if 0 /* See above */ if (path != (STRING *)0) DestroyString(path); # endif /* free dir data structure */ closedir(dir); return retval; } #endif /* find out where/who we are (ksb) * parse optons * read in the config file, open the log file * spawn the kids to drive the console groups * become the master server * shutdown with grace * exit happy */ int main(int argc, char **argv) { int i; FILE *fpConfig = (FILE *)0; static char acOpts[] = "7a:b:c:C:dDEFhiL:m:M:noO:p:P:RSuU:Vv"; extern int optopt; extern char *optarg; struct passwd *pwd; char *origuser = (char *)0; char *curuser = (char *)0; int curuid = 0; GRPENT *pGE = (GRPENT *)0; #if !USE_UNIX_DOMAIN_SOCKETS # if USE_IPV6 int s; struct addrinfo hints; # else # if HAVE_INET_ATON struct in_addr inetaddr; # endif # endif #endif isMultiProc = 1; /* make sure stuff has the pid */ thepid = getpid(); if ((char *)0 == (progname = strrchr(argv[0], '/'))) { progname = argv[0]; } else { ++progname; } setpwent(); /* if we read from stdin (by accident) we don't wanna block. * we just don't want any more input at this point. */ close(0); if (0 != open("/dev/null", O_RDWR, 0644)) { Error("open(/dev/null): %s", strerror(errno)); Bye(EX_OSFILE); } #if HAVE_SETLINEBUF setlinebuf(stdout); setlinebuf(stderr); #endif #if HAVE_SETVBUF setvbuf(stdout, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); #endif /* Initialize the break list */ InitBreakList(); /* prep the config options */ if ((optConf = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); if ((config = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); while (EOF != (i = getopt(argc, argv, acOpts))) { switch (i) { case '7': fStrip = 1; break; case 'a': optConf->defaultaccess = *optarg; if (isupper((int)(optConf->defaultaccess))) optConf->defaultaccess = tolower(optConf->defaultaccess); switch (optConf->defaultaccess) { case 'r': case 'a': case 't': break; default: Error("unknown access type `%s'", optarg); Bye(EX_USAGE); } break; case 'b': if ((optConf->secondaryport = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'c': #if HAVE_OPENSSL if ((optConf->sslcredentials = StrDup(optarg)) == (char *)0) OutOfMem(); #endif break; case 'C': pcConfig = optarg; break; case 'd': optConf->daemonmode = FLAGTRUE; break; case 'D': fDebug++; break; case 'E': #if HAVE_OPENSSL optConf->sslrequired = FLAGFALSE; #endif break; case 'F': fNoautoreup = 1; break; case 'h': Usage(1); Bye(EX_OK); case 'i': fNoinit = 1; break; case 'L': if ((optConf->logfile = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'm': cMaxMemb = atoi(optarg); if (cMaxMemb <= 0) { Error("ignoring invalid -m option (%d <= 0)", cMaxMemb); cMaxMemb = MAXMEMB; } break; case 'M': interface = StrDup(optarg); break; case 'n': /* noop now */ break; case 'o': /* try reopening downed consoles on connect */ fReopen = 1; break; case 'O': /* How often to try opening all down consoles, in minutes */ optConf->reinitcheck = atoi(optarg); break; case 'p': if ((optConf->primaryport = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'P': if ((optConf->passwdfile = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'R': optConf->redirect = FLAGFALSE; break; case 'S': fSyntaxOnly++; break; case 'u': fAll = 1; break; case 'U': if ((optConf->unifiedlog = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'V': fVersion = 1; break; case 'v': fVerbose = 1; break; case '\?': Usage(0); Bye(EX_USAGE); default: Error("option %c needs a parameter", optopt); Bye(EX_USAGE); } } if (fVersion) { Version(); Bye(EX_OK); } Msg(MyVersion()); #if HAVE_GETLOGIN origuser = getlogin(); #endif curuid = getuid(); if ((struct passwd *)0 != (pwd = getpwuid(curuid))) curuser = pwd->pw_name; /* chuck any empty username */ if (curuser != (char *)0 && curuser[0] == '\000') curuser = (char *)0; if (startedMsg == (STRING *)0) startedMsg = AllocString(); if (curuser == (char *)0) if (origuser == (char *)0) BuildStringPrint(startedMsg, "started as uid %d by uid %d", curuid, curuid); else BuildStringPrint(startedMsg, "started as uid %d by `%s'", curuid, origuser); else BuildStringPrint(startedMsg, "started as `%s' by `%s'", curuser, (origuser == (char *)0) ? curuser : origuser); endpwent(); Msg("%s", startedMsg->string); #if HAVE_GETSPNAM && !HAVE_PAM if (!fSyntaxOnly && (geteuid() != 0)) { Msg("warning: running as a non-root user - any shadow password usage will most likely fail!"); } #endif if (fSyntaxOnly) Msg("performing configuration file syntax check"); /* must do all this so IsMe() works right */ if (gethostname(myHostname, MAXHOSTNAME) != 0) { Error("gethostname(): %s", strerror(errno)); Bye(EX_OSERR); } #if !USE_IPV6 ProbeInterfaces(bindAddr); #endif /* initialize the timers */ for (i = 0; i < T_MAX; i++) timers[i] = (time_t)0; /* read the config file */ if ((FILE *)0 == (fpConfig = fopen(pcConfig, "r"))) { Error("fopen(%s): %s", pcConfig, strerror(errno)); Bye(EX_NOINPUT); } ReadCfg(pcConfig, fpConfig); fclose(fpConfig); #if !USE_UNIX_DOMAIN_SOCKETS /* set up the port to bind to */ if (optConf->primaryport != (char *)0) config->primaryport = StrDup(optConf->primaryport); else if (pConfig->primaryport != (char *)0) config->primaryport = StrDup(pConfig->primaryport); else config->primaryport = StrDup(defConfig.primaryport); if (config->primaryport == (char *)0) OutOfMem(); # if !USE_IPV6 /* Look for non-numeric characters */ for (i = 0; config->primaryport[i] != '\000'; i++) if (!isdigit((int)config->primaryport[i])) break; if (config->primaryport[i] == '\000') { /* numeric only */ bindPort = atoi(config->primaryport); } else { /* non-numeric only */ struct servent *pSE; if ((struct servent *)0 == (pSE = getservbyname(config->primaryport, "tcp"))) { Error("getservbyname(%s) failed", config->primaryport); Bye(EX_OSERR); } else { bindPort = ntohs((unsigned short)pSE->s_port); } } # endif /* set up the secondary port to bind to */ if (optConf->secondaryport != (char *)0) config->secondaryport = StrDup(optConf->secondaryport); else if (pConfig->secondaryport != (char *)0) config->secondaryport = StrDup(pConfig->secondaryport); else config->secondaryport = StrDup(defConfig.secondaryport); if (config->secondaryport == (char *)0) OutOfMem(); # if !USE_IPV6 /* Look for non-numeric characters */ for (i = 0; config->secondaryport[i] != '\000'; i++) if (!isdigit((int)config->secondaryport[i])) break; if (config->secondaryport[i] == '\000') { /* numeric only */ bindBasePort = atoi(config->secondaryport); } else { /* non-numeric only */ struct servent *pSE; if ((struct servent *)0 == (pSE = getservbyname(config->secondaryport, "tcp"))) { Error("getservbyname(%s) failed", config->secondaryport); Bye(EX_OSERR); } else { bindBasePort = ntohs((unsigned short)pSE->s_port); } } # endif #endif #if USE_IPV6 /* set up the address to bind to */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_PASSIVE; /* create list or IPs suitable for primaryport */ s = getaddrinfo(interface, config->primaryport, &hints, &bindAddr); if (s) { Error("getaddrinfo(%s): %s", interface, gai_strerror(s)); Bye(EX_OSERR); } /* create list or IPs suitable for secondaryport */ s = getaddrinfo(interface, config->secondaryport, &hints, &bindBaseAddr); if (s) { Error("getaddrinfo(%s): %s", interface, gai_strerror(s)); Bye(EX_OSERR); } #elif USE_UNIX_DOMAIN_SOCKETS /* Don't do any redirects if we're purely local * (but it allows them to see where remote consoles are) */ optConf->redirect = FLAGFALSE; if (interface == (char *)0) interface = UDSDIR; #else /* set up the address to bind to */ if (interface == (char *)0 || (interface[0] == '*' && interface[1] == '\000')) bindAddr = INADDR_ANY; else { # if HAVE_INET_ATON if (inet_aton(interface, &inetaddr) == 0) { Error("inet_aton(%s): %s", interface, "invalid IP address"); Bye(EX_OSERR); } bindAddr = inetaddr.s_addr; # else bindAddr = inet_addr(interface); if (bindAddr == (in_addr_t) (-1)) { Error("inet_addr(%s): %s", interface, "invalid IP address"); Bye(EX_OSERR); } # endif } if (fDebug) { struct in_addr ba; ba.s_addr = bindAddr; CONDDEBUG((1, "main(): bind address set to `%s'", inet_ntoa(ba))); } #endif if (optConf->passwdfile != (char *)0) config->passwdfile = StrDup(optConf->passwdfile); else if (pConfig->passwdfile != (char *)0) config->passwdfile = StrDup(pConfig->passwdfile); else config->passwdfile = StrDup(defConfig.passwdfile); if (config->passwdfile == (char *)0) OutOfMem(); if (optConf->logfile != (char *)0) config->logfile = StrDup(optConf->logfile); else if (pConfig->logfile != (char *)0) config->logfile = StrDup(pConfig->logfile); else config->logfile = StrDup(defConfig.logfile); if (config->logfile == (char *)0) OutOfMem(); if (optConf->reinitcheck != 0) config->reinitcheck = optConf->reinitcheck; else if (pConfig->reinitcheck != 0) config->reinitcheck = pConfig->reinitcheck; else config->reinitcheck = defConfig.reinitcheck; if (optConf->defaultaccess != '\000') config->defaultaccess = optConf->defaultaccess; else if (pConfig->defaultaccess != '\000') config->defaultaccess = pConfig->defaultaccess; else config->defaultaccess = defConfig.defaultaccess; if (optConf->daemonmode != FLAGUNKNOWN) config->daemonmode = optConf->daemonmode; else if (pConfig->daemonmode != FLAGUNKNOWN) config->daemonmode = pConfig->daemonmode; else config->daemonmode = defConfig.daemonmode; if (optConf->redirect != FLAGUNKNOWN) config->redirect = optConf->redirect; else if (pConfig->redirect != FLAGUNKNOWN) config->redirect = pConfig->redirect; else config->redirect = defConfig.redirect; if (optConf->autocomplete != FLAGUNKNOWN) config->autocomplete = optConf->autocomplete; else if (pConfig->autocomplete != FLAGUNKNOWN) config->autocomplete = pConfig->autocomplete; else config->autocomplete = defConfig.autocomplete; if (optConf->loghostnames != FLAGUNKNOWN) config->loghostnames = optConf->loghostnames; else if (pConfig->loghostnames != FLAGUNKNOWN) config->loghostnames = pConfig->loghostnames; else config->loghostnames = defConfig.loghostnames; if (optConf->unifiedlog != (char *)0) { config->unifiedlog = StrDup(optConf->unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } else if (pConfig->unifiedlog != (char *)0) { config->unifiedlog = StrDup(pConfig->unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } else if (defConfig.unifiedlog != (char *)0) { config->unifiedlog = StrDup(defConfig.unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } if (optConf->initdelay != 0) config->initdelay = optConf->initdelay; else if (pConfig->initdelay != 0) config->initdelay = pConfig->initdelay; else config->initdelay = defConfig.initdelay; #if HAVE_OPENSSL if (optConf->sslrequired != FLAGUNKNOWN) config->sslrequired = optConf->sslrequired; else if (pConfig->sslrequired != FLAGUNKNOWN) config->sslrequired = pConfig->sslrequired; else config->sslrequired = defConfig.sslrequired; if (optConf->sslreqclientcert != FLAGUNKNOWN) config->sslreqclientcert = optConf->sslreqclientcert; else if (pConfig->sslreqclientcert != FLAGUNKNOWN) config->sslreqclientcert = pConfig->sslreqclientcert; else config->sslreqclientcert = defConfig.sslreqclientcert; if (optConf->sslcredentials != (char *)0) config->sslcredentials = StrDup(optConf->sslcredentials); else if (pConfig->sslcredentials != (char *)0) config->sslcredentials = StrDup(pConfig->sslcredentials); else config->sslcredentials = StrDup(defConfig.sslcredentials); if (optConf->sslcacertificatefile != (char *)0) config->sslcacertificatefile = StrDup(optConf->sslcacertificatefile); else if (pConfig->sslcacertificatefile != (char *)0) config->sslcacertificatefile = StrDup(pConfig->sslcacertificatefile); else config->sslcacertificatefile = StrDup(defConfig.sslcacertificatefile); #endif #if HAVE_SETPROCTITLE if (optConf->setproctitle != FLAGUNKNOWN) config->setproctitle = optConf->setproctitle; else if (pConfig->setproctitle != FLAGUNKNOWN) config->setproctitle = pConfig->setproctitle; else config->setproctitle = defConfig.setproctitle; #endif #if HAVE_DMALLOC && DMALLOC_MARK_MAIN dmallocMarkMain = dmalloc_mark(); #endif if (pGroups == (GRPENT *)0 && pRCList == (REMOTE *)0) { Error("no consoles found in configuration file"); } else if (fSyntaxOnly) { /* short-circuit */ #if USE_UNIX_DOMAIN_SOCKETS } else if (VerifyEmptyDirectory(interface) == -1) { Error("Master(): %s: unusable socket directory", interface); #endif } else { #if HAVE_OPENSSL /* Prep the SSL layer */ SetupSSL(); #endif #if HAVE_GSSAPI SetupGSSAPI(); #endif if (config->daemonmode == FLAGTRUE) Daemonize(); ReopenUnifiedlog(); /* if no one can use us we need to come up with a default */ if (pACList == (ACCESS *)0) #if USE_IPV6 SetDefAccess(); #else SetDefAccess(myAddrs, myHostname); #endif /* spawn all the children, so fix kids has an initial pid */ for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (pGE->imembers == 0) continue; Spawn(pGE, -1); Verbose("group #%d pid %lu on port %hu", pGE->id, (unsigned long)pGE->pid, pGE->port); } #if HAVE_SETPROCTITLE if (config->setproctitle == FLAGTRUE) { REMOTE *pRC; GRPENT *pGE; int local = 0, remote = 0; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) local += pGE->imembers; for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) remote++; setproctitle("master: port %hu, %d local, %d remote", # if USE_IPV6 config->primaryport, # elif USE_UNIX_DOMAIN_SOCKETS 0, # else bindPort, # endif local, remote); } #endif if (fVerbose) { ACCESS *pACtmp; for (pACtmp = pACList; pACtmp != (ACCESS *)0; pACtmp = pACtmp->pACnext) { Verbose("access type `%c' for `%s'", pACtmp->ctrust, pACtmp->pcwho); } } pRCUniq = FindUniq(pRCList); /* output unique console server peers? */ if (fVerbose) { REMOTE *pRC; for (pRC = pRCUniq; (REMOTE *)0 != pRC; pRC = pRC->pRCuniq) { Verbose("peer server on `%s'", pRC->rhost); } } fflush(stdout); fflush(stderr); Master(); /* stop putting kids back, and shoot them */ SimpleSignal(SIGCHLD, SIG_DFL); SignalKids(SIGTERM); } if (unifiedlog != (CONSFILE *)0) FileClose(&unifiedlog); DumpDataStructures(); Msg("terminated"); endpwent(); if (fSyntaxOnly && fErrorPrinted) Bye(EX_DATAERR); else Bye(EX_OK); return EX_OK; /* never gets here clears the compiler warning */ } conserver-8.2.4/conserver/main.h000066400000000000000000000036241344660520400166470ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* program options and stuff */ extern int fAll, fNoinit, fInteractive, fStrip, fDaemon, fReopen, fNoautoreup, fSyntaxOnly; #if USE_IPV6 extern struct addrinfo *bindAddr; extern struct addrinfo *bindBaseAddr; #else extern in_addr_t bindAddr; extern struct sockaddr_in in_port; #endif extern unsigned short bindPort, bindBasePort; extern char *pcConfig; extern int cMaxMemb; extern CONFIG *optConf; extern CONFIG *config; extern CONFIG defConfig; extern CONSFILE *unifiedlog; #if USE_UNIX_DOMAIN_SOCKETS extern char *interface; #endif #if HAVE_OPENSSL extern SSL_CTX *ctx; #endif #if HAVE_GSSAPI extern gss_name_t gss_myname; extern gss_cred_id_t gss_mycreds; #endif extern void ReopenLogfile(void); extern void ReopenUnifiedlog(void); extern void DumpDataStructures(void); conserver-8.2.4/conserver/master.c000066400000000000000000000707161344660520400172170ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include static sig_atomic_t fSawQuit = 0, fSawHUP = 0, fSawUSR2 = 0, fSawUSR1 = 0, fSawCHLD = 0; CONSCLIENT *pCLmfree = (CONSCLIENT *)0; CONSCLIENT *pCLmall = (CONSCLIENT *)0; #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION static unsigned long dmallocMarkClientConnection = 0; #endif static RETSIGTYPE FlagSawCHLD(int sig) { fSawCHLD = 1; #if !HAVE_SIGACTION SimpleSignal(SIGCHLD, FlagSawCHLD); #endif } /* check all the kids and respawn as needed. (fine) * Called when master process receives SIGCHLD */ static void FixKids(int msfd) { pid_t pid; int UWbuf; GRPENT *pGE; while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) { if (0 == pid) { break; } /* stopped child is just continuted */ if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) { Msg("child pid %lu: stopped, sending SIGCONT", (unsigned long)pid); continue; } for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (0 == pGE->imembers) continue; if (pid != pGE->pid) continue; /* A couple ways to shut down the whole system */ if (WIFEXITED(UWbuf) && (WEXITSTATUS(UWbuf) == EX_UNAVAILABLE)) { Msg("child pid %lu: exit(%d), shutting down", (unsigned long)pGE->pid, WEXITSTATUS(UWbuf)); fSawQuit = 1; /* So we don't kill something that's dead */ pGE->pid = -1; break; } if (WIFSIGNALED(UWbuf) && (WTERMSIG(UWbuf) == SIGTERM)) { Msg("child pid %lu: signal(%d), shutting down", (unsigned long)pGE->pid, WTERMSIG(UWbuf)); fSawQuit = 1; /* So we don't kill something that's dead */ pGE->pid = -1; break; } /* If not, then just a simple restart of the child */ if (WIFEXITED(UWbuf)) Msg("child pid %lu: exit(%d), restarting", pGE->pid, WEXITSTATUS(UWbuf)); if (WIFSIGNALED(UWbuf)) Msg("child pid %lu: signal(%d), restarting", pGE->pid, WTERMSIG(UWbuf)); /* this kid kid is dead, start another */ Spawn(pGE, msfd); Verbose("group #%d pid %lu on port %hu", pGE->id, (unsigned long)pGE->pid, pGE->port); } } } /* kill all the kids and exit. * Called when master process receives SIGTERM */ static RETSIGTYPE FlagQuitIt(int arg) { fSawQuit = 1; #if !HAVE_SIGACTION SimpleSignal(SIGTERM, FlagQuitIt); #endif } /* yes, this is basically the same as FlagQuitIt but we *may* * want to do something special on SIGINT at some point. */ static RETSIGTYPE FlagSawINT(int arg) { fSawQuit = 1; #if !HAVE_SIGACTION SimpleSignal(SIGINT, FlagSawINT); #endif } static RETSIGTYPE FlagSawHUP(int arg) { fSawHUP = 1; #if !HAVE_SIGACTION SimpleSignal(SIGHUP, FlagSawHUP); #endif } static RETSIGTYPE FlagSawUSR2(int arg) { fSawUSR2 = 1; #if !HAVE_SIGACTION SimpleSignal(SIGUSR2, FlagSawUSR2); #endif } static RETSIGTYPE FlagSawUSR1(int arg) { fSawUSR1 = 1; #if !HAVE_SIGACTION SimpleSignal(SIGUSR1, FlagSawUSR1); #endif } /* Signal all the kids... */ void SignalKids(int arg) { GRPENT *pGE; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (0 == pGE->imembers || -1 == pGE->pid) continue; CONDDEBUG((1, "SignalKids(): sending pid %lu signal %d", (unsigned long)pGE->pid, arg)); if (-1 == kill(pGE->pid, arg)) { Error("SignalKids(): kill(%lu): %s", (unsigned long)pGE->pid, strerror(errno)); } } } REMOTE * FindRemoteConsole(char *args) { REMOTE *pRC; NAMES *name; for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) { if (strcasecmp(args, pRC->rserver) == 0) return pRC; for (name = pRC->aliases; name != (NAMES *)0; name = name->next) { if (strcasecmp(args, name->name) == 0) return pRC; } } return pRC; } void CommandCall(CONSCLIENT *pCL, char *args) { int found; REMOTE *pRC, *pRCFound; unsigned short prnum = 0; char *ambiguous = (char *)0; CONSENT *pCE; GRPENT *pGE; found = 0; pRCFound = (REMOTE *)0; ambiguous = BuildTmpString((char *)0); /* look for a local machine */ for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (pGE->imembers == 0) continue; if ((pCE = FindConsoleName(pGE->pCElist, args)) != (CONSENT *)0) { prnum = pGE->port; ambiguous = BuildTmpString(pCE->server); ambiguous = BuildTmpString(", "); ++found; } } if (config->redirect == FLAGTRUE || (config->redirect != FLAGTRUE && found == 0)) { if ((pRC = FindRemoteConsole(args)) != (REMOTE *)0) { ambiguous = BuildTmpString(pRC->rserver); ambiguous = BuildTmpString(", "); ++found; pRCFound = pRC; } } if (found == 0 && config->autocomplete == FLAGTRUE) { /* Then look for substring matches */ NAMES *name = (NAMES *)0; int foundOne = 0; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (0 == pGE->imembers) continue; for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { foundOne = 0; if (strncasecmp(args, pCE->server, strlen(args)) == 0) { prnum = pGE->port; ambiguous = BuildTmpString(pCE->server); ambiguous = BuildTmpString(", "); ++foundOne; } for (name = pCE->aliases; name != (NAMES *)0; name = name->next) { if (strncasecmp(args, name->name, strlen(args)) != 0) continue; prnum = pGE->port; ambiguous = BuildTmpString(name->name); ambiguous = BuildTmpString(", "); ++foundOne; } if (foundOne) ++found; } } /* look for a remote server if redirect is enabled or if * redirect is not enabled and we haven't found a unique * console match */ if (config->redirect == FLAGTRUE || (config->redirect != FLAGTRUE && found != 1)) { for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) { foundOne = 0; if (strncasecmp(args, pRC->rserver, strlen(args)) == 0) { pRCFound = pRC; ambiguous = BuildTmpString(pRC->rserver); ambiguous = BuildTmpString(", "); ++foundOne; } for (name = pRC->aliases; name != (NAMES *)0; name = name->next) { if (strncasecmp(args, name->name, strlen(args)) != 0) continue; pRCFound = pRC; ambiguous = BuildTmpString(name->name); ambiguous = BuildTmpString(", "); ++foundOne; } if (foundOne) ++found; } } } switch (found) { case 0: FilePrint(pCL->fd, FLAGFALSE, "console `%s' not found\r\n", args); break; case 1: if ((REMOTE *)0 != pRCFound) { if (config->redirect != FLAGTRUE) { FilePrint(pCL->fd, FLAGFALSE, "automatic redirection disabled - console on master `%s'\r\n", pRCFound->rhost); } else { FilePrint(pCL->fd, FLAGFALSE, "@%s\r\n", pRCFound->rhost); } } else { FilePrint(pCL->fd, FLAGFALSE, "%hu\r\n", prnum); } break; default: found = strlen(ambiguous); ambiguous[found - 2] = '\000'; FilePrint(pCL->fd, FLAGFALSE, "ambiguous console abbreviation, `%s'\r\n\tchoices are %s\r\n", args, ambiguous); break; } BuildTmpString((char *)0); /* we're done - clean up */ ambiguous = (char *)0; } void DropMasterClient(CONSCLIENT *pCLServing, FLAG force) { /* if we have data buffered and aren't forced to close, * we can't close quite yet */ if (force != FLAGTRUE && !FileBufEmpty(pCLServing->fd)) { pCLServing->ioState = ISFLUSHING; return; } if (pCLServing->iState == S_NORMAL) Verbose(" logout %s", pCLServing->acid->string); /* drop a connection */ FD_CLR(FileFDNum(pCLServing->fd), &rinit); FD_CLR(FileFDNum(pCLServing->fd), &winit); FileClose(&pCLServing->fd); pCLServing->ioState = ISDISCONNECTED; /* remove from the "all" list */ if ((CONSCLIENT *)0 != pCLServing->pCLscan) { pCLServing->pCLscan->ppCLbscan = pCLServing->ppCLbscan; } *(pCLServing->ppCLbscan) = pCLServing->pCLscan; /* put on the free list */ pCLServing->pCLnext = pCLmfree; pCLmfree = pCLServing; /* we didn't touch pCLServing->pCLscan so the loop works */ #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "Master(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif } void DoNormalRead(CONSCLIENT *pCLServing) { char *pcCmd; char *pcArgs; int nr, i, l; unsigned char acIn[BUFSIZ]; /* read connection */ if ((nr = FileRead(pCLServing->fd, acIn, sizeof(acIn))) < 0) { DropMasterClient(pCLServing, FLAGFALSE); return; } while ((l = ParseIACBuf(pCLServing->fd, acIn, &nr)) >= 0) { if (l == 0) /* we ignore special OB_IAC stuff */ continue; for (i = 0; i < l; ++i) { if ('\n' != acIn[i]) { BuildStringChar(acIn[i], pCLServing->accmd); continue; } if ((pCLServing->accmd->used > 1) && ('\r' == pCLServing->accmd->string[pCLServing->accmd->used - 2])) { pCLServing->accmd->string[pCLServing->accmd->used - 2] = '\000'; pCLServing->accmd->used--; } /* process password here...before we corrupt accmd */ if (pCLServing->iState == S_PASSWD) { if (CheckPasswd (pCLServing, pCLServing->accmd->string, FLAGFALSE) != AUTH_SUCCESS) { FileWrite(pCLServing->fd, FLAGFALSE, "invalid password\r\n", -1); BuildString((char *)0, pCLServing->accmd); DropMasterClient(pCLServing, FLAGFALSE); return; } Verbose(" login %s", pCLServing->acid->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", 4); pCLServing->iState = S_NORMAL; BuildString((char *)0, pCLServing->accmd); continue; } if ((char *)0 != (pcArgs = strchr(pCLServing->accmd->string, ':'))) { *pcArgs++ = '\000'; } else if ((char *)0 != (pcArgs = strchr(pCLServing->accmd->string, ' '))) { *pcArgs++ = '\000'; } if (pcArgs != (char *)0) pcArgs = PruneSpace(pcArgs); pcCmd = PruneSpace(pCLServing->accmd->string); if (strcmp(pcCmd, "help") == 0) { static char *apcHelp1[] = { "exit disconnect\r\n", "help this help message\r\n", "login log in\r\n", #if HAVE_OPENSSL "ssl start ssl session\r\n", #endif #if HAVE_GSSAPI "gssapi log in with gssapi\r\n", #endif (char *)0 }; static char *apcHelp2[] = { "call provide port for given console\r\n", "exit disconnect\r\n", "groups provide ports for group leaders\r\n", "help this help message\r\n", "master provide a list of master servers\r\n", "newlogs* close and open all logfiles (SIGUSR2)\r\n", "pid provide pid of master process\r\n", "quit* terminate conserver (SIGTERM)\r\n", "restart* restart conserver (SIGHUP) - deprecated\r\n", "reconfig* reread config file (SIGHUP)\r\n", "version provide version info for server\r\n", "up* bring up all downed consoles (SIGUSR1)\r\n", "* = requires admin privileges\r\n", (char *)0 }; char **ppc; for (ppc = (pCLServing->iState == S_IDENT ? apcHelp1 : apcHelp2); (char *)0 != *ppc; ++ppc) { FileWrite(pCLServing->fd, FLAGTRUE, *ppc, -1); } FileWrite(pCLServing->fd, FLAGFALSE, (char *)0, 0); } else if (strcmp(pcCmd, "exit") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "goodbye\r\n", -1); DropMasterClient(pCLServing, FLAGFALSE); return; #if HAVE_OPENSSL } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "ssl") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); if (!AttemptSSL(pCLServing)) { DropMasterClient(pCLServing, FLAGFALSE); return; } #endif #if HAVE_GSSAPI } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "gssapi") == 0) { FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", -1); /* Change the I/O mode right away, we'll do the read * and accept when the select gets back to us */ pCLServing->ioState = INGSSACCEPT; #endif } else if (pCLServing->iState == S_IDENT && strcmp(pcCmd, "login") == 0) { #if HAVE_OPENSSL if (config->sslrequired == FLAGTRUE && FileGetType(pCLServing->fd) != SSLSocket) { FileWrite(pCLServing->fd, FLAGFALSE, "encryption required\r\n", -1); } else { #endif if (pcArgs == (char *)0) { FileWrite(pCLServing->fd, FLAGFALSE, "login requires argument\r\n", -1); } else { BuildString((char *)0, pCLServing->username); BuildString((char *)0, pCLServing->acid); BuildString(pcArgs, pCLServing->username); BuildString(pcArgs, pCLServing->acid); BuildStringChar('@', pCLServing->acid); BuildString(pCLServing->peername->string, pCLServing->acid); if (pCLServing->caccess == 't' || CheckPasswd(pCLServing, "", FLAGTRUE) == AUTH_SUCCESS) { pCLServing->iState = S_NORMAL; Verbose(" login %s", pCLServing->acid->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok\r\n", 4); } else { FilePrint(pCLServing->fd, FLAGFALSE, "passwd? %s\r\n", myHostname); pCLServing->iState = S_PASSWD; } } #if HAVE_OPENSSL } #endif } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "master") == 0) { int iSep = 1; if ((GRPENT *)0 != pGroups) { #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS SOCKADDR_STYPE lcl; socklen_t so = sizeof(lcl); if (-1 == getsockname(FileFDNum(pCLServing->fd), (struct sockaddr *)&lcl, &so)) { FileWrite(pCLServing->fd, FLAGFALSE, "getsockname failed, try again later\r\n", -1); Error("Master(): getsockname(%u): %s", FileFDNum(pCLServing->fd), strerror(errno)); iSep = -1; } else { # if USE_IPV6 int error; char addr[NI_MAXHOST]; error = getnameinfo((struct sockaddr *)&lcl, so, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); if (!error) FilePrint(pCLServing->fd, FLAGTRUE, "@%s", addr); # else FilePrint(pCLServing->fd, FLAGTRUE, "@%s", inet_ntoa(lcl.sin_addr)); # endif iSep = 0; } #else FilePrint(pCLServing->fd, FLAGTRUE, "@0"); iSep = 0; #endif } if (iSep >= 0) { if (config->redirect == FLAGTRUE) { REMOTE *pRC; char *s; for (pRC = pRCUniq; (REMOTE *)0 != pRC; pRC = pRC->pRCuniq) { s = ":@%s"; s += iSep; FilePrint(pCLServing->fd, FLAGTRUE, s, pRC->rhost); iSep = 0; } } FileWrite(pCLServing->fd, FLAGFALSE, "\r\n", -1); } } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "pid") == 0) { FilePrint(pCLServing->fd, FLAGFALSE, "%lu\r\n", (unsigned long)thepid); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "version") == 0) { FilePrint(pCLServing->fd, FLAGFALSE, "version `%s'\r\n", MyVersion()); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "quit") == 0) { if (ConsentUserOk(pADList, pCLServing->username->string) == 1) { Verbose("quit command by %s", pCLServing->acid->string); FileWrite(pCLServing->fd, FLAGFALSE, "ok -- terminated\r\n", -1); DropMasterClient(pCLServing, FLAGFALSE); kill(thepid, SIGTERM); return; } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "restart") == 0) { if (ConsentUserOk(pADList, pCLServing->username->string) == 1) { FileWrite(pCLServing->fd, FLAGFALSE, "ok -- restarting\r\n", -1); Verbose("restart command by %s", pCLServing->acid->string); kill(thepid, SIGHUP); } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "reconfig") == 0) { if (ConsentUserOk(pADList, pCLServing->username->string) == 1) { FileWrite(pCLServing->fd, FLAGFALSE, "ok -- reconfiguring\r\n", -1); Verbose("reconfig command by %s", pCLServing->acid->string); kill(thepid, SIGHUP); } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "up") == 0) { if (ConsentUserOk(pADList, pCLServing->username->string) == 1) { FileWrite(pCLServing->fd, FLAGFALSE, "ok -- bringing up consoles\r\n", -1); Verbose("up command by %s", pCLServing->acid->string); kill(thepid, SIGUSR1); } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "newlogs") == 0) { if (ConsentUserOk(pADList, pCLServing->username->string) == 1) { FileWrite(pCLServing->fd, FLAGFALSE, "ok -- opening new logfiles\r\n", -1); Verbose("newlogs command by %s", pCLServing->acid->string); kill(thepid, SIGUSR2); } else FileWrite(pCLServing->fd, FLAGFALSE, "unauthorized command\r\n", -1); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "groups") == 0) { int iSep = 1; GRPENT *pGE; char *s; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (0 == pGE->imembers) continue; s = ":%hu"; s += iSep; FilePrint(pCLServing->fd, FLAGTRUE, s, pGE->port); iSep = 0; } FileWrite(pCLServing->fd, FLAGFALSE, "\r\n", 2); } else if (pCLServing->iState == S_NORMAL && strcmp(pcCmd, "call") == 0) { if (pcArgs == (char *)0) FileWrite(pCLServing->fd, FLAGFALSE, "call requires argument\r\n", -1); else CommandCall(pCLServing, pcArgs); } else { FileWrite(pCLServing->fd, FLAGFALSE, "unknown command\r\n", -1); } BuildString((char *)0, pCLServing->accmd); } nr -= l; MemMove(acIn, acIn + l, nr); } } /* this routine is used by the master console server process (ksb) */ void Master(void) { int cfd; int msfd; socklen_t so; fd_set rmask, wmask; #if USE_IPV6 || !USE_UNIX_DOMAIN_SOCKETS # if USE_IPV6 struct addrinfo *rp; # else struct sockaddr_in master_port; # endif # if HAVE_SETSOCKOPT int true = 1; # endif #else struct sockaddr_un master_port; static STRING *portPath = (STRING *)0; #endif FILE *fp; CONSCLIENT *pCLServing = (CONSCLIENT *)0; CONSCLIENT *pCL = (CONSCLIENT *)0; /* set up signal handler */ SimpleSignal(SIGPIPE, SIG_IGN); SimpleSignal(SIGQUIT, SIG_IGN); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_IGN); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_IGN); #endif #if defined(SIGPOLL) SimpleSignal(SIGPOLL, SIG_IGN); #endif #if defined(SIGXFSZ) SimpleSignal(SIGXFSZ, SIG_IGN); #endif SimpleSignal(SIGCHLD, FlagSawCHLD); SimpleSignal(SIGTERM, FlagQuitIt); SimpleSignal(SIGUSR1, FlagSawUSR1); SimpleSignal(SIGHUP, FlagSawHUP); SimpleSignal(SIGUSR2, FlagSawUSR2); SimpleSignal(SIGINT, FlagSawINT); /* prime the free connection slots */ if ((pCLmfree = (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT))) == (CONSCLIENT *)0) OutOfMem(); pCLmfree->accmd = AllocString(); pCLmfree->peername = AllocString(); pCLmfree->username = AllocString(); pCLmfree->acid = AllocString(); /* set up port for master to listen on */ #if !USE_IPV6 # if HAVE_MEMSET memset((void *)&master_port, 0, sizeof(master_port)); # else bzero((char *)&master_port, sizeof(master_port)); # endif #endif #if USE_IPV6 for (rp = bindAddr; rp != NULL; rp = rp->ai_next) { if ((msfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) continue; # if HAVE_SETSOCKOPT if (setsockopt (msfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true, sizeof(true)) < 0) goto fail; # endif if (!SetFlags(msfd, O_NONBLOCK, 0)) goto fail; if (bind(msfd, rp->ai_addr, rp->ai_addrlen) == 0) break; fail: close(msfd); } if (listen(msfd, SOMAXCONN) < 0) { Error("Master(): listen(): %s", strerror(errno)); return; } /* save addrlen for accept */ so = rp->ai_addrlen; #elif USE_UNIX_DOMAIN_SOCKETS master_port.sun_family = AF_UNIX; if (portPath == (STRING *)0) portPath = AllocString(); BuildStringPrint(portPath, "%s/0", interface); if (portPath->used > sizeof(master_port.sun_path)) { Error("Master(): path to socket too long: %s", portPath->string); return; } StrCpy(master_port.sun_path, portPath->string, sizeof(master_port.sun_path)); if ((msfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { Error("Master(): socket(AF_UNIX,SOCK_STREAM): %s", strerror(errno)); return; } if (!SetFlags(msfd, O_NONBLOCK, 0)) return; if (bind(msfd, (struct sockaddr *)&master_port, sizeof(master_port)) < 0) { Error("Master(): bind(%s): %s", master_port.sun_path, strerror(errno)); return; } if (listen(msfd, SOMAXCONN) < 0) { Error("Master(): listen(%s): %s", master_port.sun_path, strerror(errno)); return; } # ifdef TRUST_UDS_CRED /* Allow everyone to connect, but we later auth them via SO_PEERCRED */ chmod(master_port.sun_path, 0666); # endif #else master_port.sin_family = AF_INET; master_port.sin_addr.s_addr = bindAddr; master_port.sin_port = htons(bindPort); if ((msfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { Error("Master(): socket(AF_INET,SOCK_STREAM): %s", strerror(errno)); return; } # if HAVE_SETSOCKOPT if (setsockopt (msfd, SOL_SOCKET, SO_REUSEADDR, (char *)&true, sizeof(true)) < 0) { Error("Master(): setsockopt(%u,SO_REUSEADDR): %s", msfd, strerror(errno)); return; } # endif if (!SetFlags(msfd, O_NONBLOCK, 0)) return; if (bind(msfd, (struct sockaddr *)&master_port, sizeof(master_port)) < 0) { Error("Master(): bind(%hu): %s", ntohs(master_port.sin_port), strerror(errno)); return; } if (listen(msfd, SOMAXCONN) < 0) { Error("Master(): listen(%hu): %s", ntohs(master_port.sin_port), strerror(errno)); return; } #endif fp = fopen(PIDFILE, "w"); if (fp) { fprintf(fp, "%lu\n", (unsigned long)getpid()); fclose(fp); } else { Error("Master(): can't write pid to %s: %s", PIDFILE, strerror(errno)); } FD_ZERO(&rinit); FD_SET(msfd, &rinit); maxfd = msfd + 1; for (fSawQuit = 0; !fSawQuit; /* can't close here :-( */ ) { if (fSawCHLD) { fSawCHLD = 0; FixKids(msfd); } if (fSawHUP) { fSawHUP = 0; Msg("processing SIGHUP"); ReopenLogfile(); ReopenUnifiedlog(); SignalKids(SIGHUP); ReReadCfg(msfd, msfd); /* fix up the client descriptors since ReReadCfg() doesn't * see them like it can in the child processes */ for (pCL = pCLmall; pCL != (CONSCLIENT *)0; pCL = pCL->pCLscan) { FD_SET(FileFDNum(pCL->fd), &rinit); if (maxfd < FileFDNum(pCL->fd) + 1) maxfd = FileFDNum(pCL->fd) + 1; if (!FileBufEmpty(pCL->fd)) FD_SET(FileFDNum(pCL->fd), &winit); } } if (fSawUSR1) { fSawUSR1 = 0; Msg("processing SIGUSR1"); SignalKids(SIGUSR1); } if (fSawUSR2) { fSawUSR2 = 0; Msg("processing SIGUSR2"); ReopenLogfile(); ReopenUnifiedlog(); SignalKids(SIGUSR2); } if (fSawQuit) { /* Something above set the quit flag */ break; } rmask = rinit; wmask = winit; if (-1 == select(maxfd, &rmask, &wmask, (fd_set *)0, (struct timeval *)0)) { if (errno != EINTR) { Error("Master(): select(): %s", strerror(errno)); break; } continue; } /* anything on a connection? */ for (pCLServing = pCLmall; (CONSCLIENT *)0 != pCLServing; pCLServing = pCLServing->pCLscan) { switch (pCLServing->ioState) { #if HAVE_OPENSSL case INSSLACCEPT: if (FileCanSSLAccept(pCLServing->fd, &rmask, &wmask)) { int r; if ((r = FileSSLAccept(pCLServing->fd)) < 0) DropMasterClient(pCLServing, FLAGFALSE); else if (r == 1) pCLServing->ioState = ISNORMAL; } break; #endif #if HAVE_GSSAPI case INGSSACCEPT: { int r; if ((r = AttemptGSSAPI(pCLServing)) < 0) DropMasterClient(pCLServing, FLAGFALSE); else if (r == 1) pCLServing->ioState = ISNORMAL; } break; #endif case ISNORMAL: if (FileCanRead(pCLServing->fd, &rmask, &wmask)) DoNormalRead(pCLServing); /* fall through to ISFLUSHING for buffered data */ case ISFLUSHING: if (!FileBufEmpty(pCLServing->fd) && FileCanWrite(pCLServing->fd, &rmask, &wmask)) { CONDDEBUG((1, "Master(): flushing fd %d", FileFDNum(pCLServing->fd))); if (FileWrite (pCLServing->fd, FLAGFALSE, (char *)0, 0) < 0) { DropMasterClient(pCLServing, FLAGTRUE); break; } } if ((pCLServing->ioState == ISFLUSHING) && FileBufEmpty(pCLServing->fd)) DropMasterClient(pCLServing, FLAGFALSE); break; default: /* this really can't ever happen */ Error ("Master(): client socket state == %d -- THIS IS A BUG", pCLServing->ioState); DropMasterClient(pCLServing, FLAGFALSE); break; } } /* if nothing on control line, get more */ if (!FD_ISSET(msfd, &rmask)) continue; /* accept new connections and deal with them */ #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION dmallocMarkClientConnection = dmalloc_mark(); #endif #if !USE_IPV6 so = sizeof(struct sockaddr_in); #endif for (cfd = 0; cfd == 0;) { cfd = accept(msfd, (struct sockaddr *)&pCLmfree->cnct_port, &so); if (cfd < 0 && errno == EINTR) cfd = 0; } if (cfd < 0) { Error("Master(): accept(%u): %s", msfd, strerror(errno)); #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "Master(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif continue; } /* set to non-blocking and wrap in a File object */ if (SetFlags(cfd, O_NONBLOCK, 0)) { pCLmfree->fd = FileOpenFD(cfd, simpleSocket); FileSetQuoteIAC(pCLmfree->fd, FLAGTRUE); } else pCLmfree->fd = (CONSFILE *)0; if ((CONSFILE *)0 == pCLmfree->fd) { Error("Master(): FileOpenFD(%u): %s", cfd, strerror(errno)); #if HAVE_DMALLOC && DMALLOC_MARK_CLIENT_CONNECTION CONDDEBUG((1, "Master(): dmalloc / MarkClientConnection")); dmalloc_log_changed(dmallocMarkClientConnection, 1, 0, 1); #endif continue; } /* remove from the free list */ pCL = pCLmfree; pCLmfree = pCL->pCLnext; /* add another if we ran out */ if (pCLmfree == (CONSCLIENT *)0) { if ((pCLmfree = (CONSCLIENT *)calloc(1, sizeof(CONSCLIENT))) == (CONSCLIENT *)0) OutOfMem(); pCLmfree->accmd = AllocString(); pCLmfree->peername = AllocString(); pCLmfree->username = AllocString(); pCLmfree->acid = AllocString(); } /* link into all clients list */ pCL->pCLscan = pCLmall; pCL->ppCLbscan = &pCLmall; if ((CONSCLIENT *)0 != pCL->pCLscan) { pCL->pCLscan->ppCLbscan = &pCL->pCLscan; } pCLmall = pCL; FD_SET(cfd, &rinit); if (maxfd < cfd + 1) maxfd = cfd + 1; /* init the fsm */ pCL->iState = S_IDENT; BuildString((char *)0, pCL->accmd); BuildString((char *)0, pCL->peername); BuildString((char *)0, pCL->username); BuildString((char *)0, pCL->acid); if (ClientAccessOk(pCL)) { pCL->ioState = ISNORMAL; /* say hi to start */ FileWrite(pCL->fd, FLAGFALSE, "ok\r\n", 4); } else DropMasterClient(pCL, FLAGFALSE); } close(msfd); #if USE_UNIX_DOMAIN_SOCKETS unlink(master_port.sun_path); #endif /* clean up the free list */ while (pCLmfree != (CONSCLIENT *)0) { pCL = pCLmfree->pCLnext; DestroyClient(pCLmfree); pCLmfree = pCL; } unlink(PIDFILE); } conserver-8.2.4/conserver/master.h000066400000000000000000000024431344660520400172140ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright 1992 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ /* * stuff the master process needs */ extern CONSCLIENT *pCLmall; extern CONSCLIENT *pCLmfree; extern void Master(void); extern void SignalKids(int); conserver-8.2.4/conserver/readcfg.c000066400000000000000000004113141344660520400173100ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ /* * Notes/Thoughts: * * - building access lists doesn't remove any dups in AccessDestroy(). * it just joins lists that match the current host. it would be nice * to have only unique items in the list. * * - the *Abort() stuff may not play well with the *Begin() stuff - if * it's reusing the space, could we not have values trickle over into * the next section? - i think i may have fixed that. * * - add the flow tag at some point * * s+ m max maximum consoles managed per process * */ #include #include #include #include #include #include #include #include #include #include /***** external things *****/ NAMES *userList = (NAMES *)0; GRPENT *pGroups = (GRPENT *)0; REMOTE *pRCList = (REMOTE *)0; ACCESS *pACList = (ACCESS *)0; CONSENTUSERS *pADList = (CONSENTUSERS *)0; CONSENTUSERS *pLUList = (CONSENTUSERS *)0; REMOTE *pRCUniq = (REMOTE *)0; CONFIG *pConfig = (CONFIG *)0; BREAKS breakList[BREAKLISTSIZE]; TASKS *taskList = (TASKS *)0; SUBST *taskSubst = (SUBST *)0; /***** internal things *****/ #define ALLWORDSEP ", \f\v\t\n\r" int isStartup = 0; GRPENT *pGroupsOld = (GRPENT *)0; GRPENT *pGEstage = (GRPENT *)0; GRPENT *pGE = (GRPENT *)0; static unsigned int groupID = 1; REMOTE **ppRC = (REMOTE **)0; /* 'task' handling (plus) */ void ProcessYesNo(char *id, FLAG *flag) { if (id == (char *)0 || id[0] == '\000') *flag = FLAGFALSE; else if (strcasecmp("yes", id) == 0 || strcasecmp("true", id) == 0 || strcasecmp("on", id) == 0) *flag = FLAGTRUE; else if (strcasecmp("no", id) == 0 || strcasecmp("false", id) == 0 || strcasecmp("off", id) == 0) *flag = FLAGFALSE; else if (isMaster) Error("invalid boolean entry `%s' [%s:%d]", id, file, line); } void DestroyTask(TASKS *task) { if (task->cmd != (STRING *)0) { DestroyString(task->cmd); task->cmd = (STRING *)0; } if (task->descr != (STRING *)0) { DestroyString(task->descr); task->descr = (STRING *)0; } if (task->subst != (char *)0) free(task->subst); free(task); } void DestroyTaskList(void) { TASKS *n; while (taskList != (TASKS *)0) { n = taskList->next; DestroyTask(taskList); taskList = n; } if (taskSubst != (SUBST *)0) { free(taskSubst); taskSubst = (SUBST *)0; } } void InitBreakList(void) { int i; for (i = 0; i < BREAKLISTSIZE; i++) { breakList[i].seq = (STRING *)0; breakList[i].delay = 0; breakList[i].confirm = FLAGUNKNOWN; } } void DestroyBreakList(void) { int i; for (i = 0; i < BREAKLISTSIZE; i++) { if (breakList[i].seq != (STRING *)0) { DestroyString(breakList[i].seq); breakList[i].seq = (STRING *)0; } } } void DestroyUserList(void) { NAMES *n; while (userList != (NAMES *)0) { n = userList->next; if (userList->name != (char *)0) free(userList->name); free(userList); userList = n; } } NAMES * FindUserList(char *id) { NAMES *u; for (u = userList; u != (NAMES *)0; u = u->next) { if (strcmp(u->name, id) == 0) return u; } return u; } NAMES * AddUserList(char *id) { NAMES *u; if ((u = FindUserList(id)) == (NAMES *)0) { if ((u = (NAMES *)calloc(1, sizeof(NAMES))) == (NAMES *)0) OutOfMem(); if ((u->name = StrDup(id)) == (char *)0) OutOfMem(); u->next = userList; userList = u; } return u; } /* 'break' handling */ STRING *parserBreak = (STRING *)0; int parserBreakDelay = 0; int parserBreakNum = 0; FLAG parserBreakConfirm = FLAGFALSE; CONSENTUSERS * ConsentAddUser(CONSENTUSERS **ppCU, char *id, short not) { CONSENTUSERS *u = (CONSENTUSERS *)0; CONSENTUSERS *p = (CONSENTUSERS *)0; for (u = *ppCU; u != (CONSENTUSERS *)0; u = u->next) { if (strcmp(u->user->name, id) == 0) { u->not = not; /* at head of list already? */ if (p != (CONSENTUSERS *)0) { /* move it */ p->next = u->next; u->next = *ppCU; *ppCU = u; } return u; } p = u; } if ((u = (CONSENTUSERS *)calloc(1, sizeof(CONSENTUSERS))) == (CONSENTUSERS *)0) OutOfMem(); u->user = AddUserList(id); u->not = not; u->next = *ppCU; *ppCU = u; return u; } void BreakBegin(char *id) { CONDDEBUG((1, "BreakBegin(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000') || ((id[0] < '1' || id[0] > '9') && (id[0] < 'a' || id[0] > 'z')) || id[1] != '\000') { if (isMaster) Error("invalid break number `%s' [%s:%d]", id, file, line); parserBreakNum = 0; } else { parserBreakNum = id[0] - '0' - (id[0] > '9' ? BREAKALPHAOFFSET : 0); if (parserBreak == (STRING *)0) parserBreak = AllocString(); else BuildString((char *)0, parserBreak); parserBreakDelay = BREAKDELAYDEFAULT; parserBreakConfirm = FLAGFALSE; } } void BreakEnd(void) { CONDDEBUG((1, "BreakEnd() [%s:%d]", file, line)); if (parserBreakNum == 0) return; BuildString((char *)0, breakList[parserBreakNum - 1].seq); BuildString(parserBreak->string, breakList[parserBreakNum - 1].seq); breakList[parserBreakNum - 1].delay = parserBreakDelay; breakList[parserBreakNum - 1].confirm = parserBreakConfirm; parserBreakNum = 0; } void BreakAbort(void) { CONDDEBUG((1, "BreakAbort() [%s:%d]", file, line)); parserBreakNum = 0; } void BreakDestroy(void) { CONDDEBUG((1, "BreakDestroy() [%s:%d]", file, line)); if (parserBreak != (STRING *)0) { DestroyString(parserBreak); parserBreak = (STRING *)0; } #if DUMPDATA { int i; for (i = 0; i < BREAKLISTSIZE; i++) { Msg("Break[%d] = `%s', delay=%d", i, breakList[i].seq == (STRING *)0 ? "(null)" : (breakList[i]. seq->string ? breakList[i]. seq->string : "(null)"), breakList[i].delay); } } #endif } void BreakItemString(char *id) { CONDDEBUG((1, "BreakItemString(%s) [%s:%d]", id, file, line)); BuildString((char *)0, parserBreak); if ((id == (char *)0) || (*id == '\000')) return; BuildString(id, parserBreak); } void BreakItemDelay(char *id) { char *p; int delay; CONDDEBUG((1, "BreakItemDelay(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { parserBreakDelay = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number or the number is out of bounds */ if ((*p != '\000') || ((delay = atoi(id)) > 999)) { if (isMaster) Error("invalid delay number `%s' [%s:%d]", id, file, line); return; } parserBreakDelay = delay; } void BreakItemConfirm(char *id) { CONDDEBUG((1, "BreakItemConfirm(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserBreakConfirm)); } /* 'group' handling */ typedef struct parserGroup { STRING *name; CONSENTUSERS *users; struct parserGroup *next; } PARSERGROUP; PARSERGROUP *parserGroups = (PARSERGROUP *)0; PARSERGROUP *parserGroupTemp = (PARSERGROUP *)0; void DestroyParserGroup(PARSERGROUP *pg) { PARSERGROUP **ppg = &parserGroups; if (pg == (PARSERGROUP *)0) return; CONDDEBUG((2, "DestroyParserGroup(): %s", pg->name->string)); while (*ppg != (PARSERGROUP *)0) { if (*ppg == pg) { break; } else { ppg = &((*ppg)->next); } } if (*ppg != (PARSERGROUP *)0) *ppg = pg->next; DestroyString(pg->name); DestroyConsentUsers(&(pg->users)); free(pg); } PARSERGROUP * GroupFind(char *id) { PARSERGROUP *pg; for (pg = parserGroups; pg != (PARSERGROUP *)0; pg = pg->next) { if (strcmp(id, pg->name->string) == 0) return pg; } return pg; } void GroupBegin(char *id) { CONDDEBUG((1, "GroupBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { if (isMaster) Error("empty group name [%s:%d]", file, line); return; } if (parserGroupTemp != (PARSERGROUP *)0) DestroyParserGroup(parserGroupTemp); if ((parserGroupTemp = (PARSERGROUP *)calloc(1, sizeof(PARSERGROUP))) == (PARSERGROUP *)0) OutOfMem(); parserGroupTemp->name = AllocString(); BuildString(id, parserGroupTemp->name); } void GroupEnd(void) { PARSERGROUP *pg = (PARSERGROUP *)0; CONDDEBUG((1, "GroupEnd() [%s:%d]", file, line)); if (parserGroupTemp->name->used <= 1) { DestroyParserGroup(parserGroupTemp); parserGroupTemp = (PARSERGROUP *)0; return; } /* if we're overriding an existing group, nuke it */ if ((pg = GroupFind(parserGroupTemp->name->string)) != (PARSERGROUP *)0) { DestroyParserGroup(pg); } /* add the temp to the head of the list */ parserGroupTemp->next = parserGroups; parserGroups = parserGroupTemp; parserGroupTemp = (PARSERGROUP *)0; } void GroupAbort(void) { CONDDEBUG((1, "GroupAbort() [%s:%d]", file, line)); DestroyParserGroup(parserGroupTemp); parserGroupTemp = (PARSERGROUP *)0; } void GroupDestroy(void) { CONDDEBUG((1, "GroupDestroy() [%s:%d]", file, line)); #if DUMPDATA { PARSERGROUP *pg; NAMES *u; for (pg = parserGroups; pg != (PARSERGROUP *)0; pg = pg->next) { CONSENTUSERS *pcu; Msg("Group = %s", pg->name->string); for (pcu = pg->users; pcu != (CONSENTUSERS *)0; pcu = pcu->next) { Msg(" User = %s", pcu->user->name); } } Msg("UserList..."); for (u = userList; u != (NAMES *)0; u = u->next) { Msg(" User = %s", u->name); } } #endif while (parserGroups != (PARSERGROUP *)0) DestroyParserGroup(parserGroups); DestroyParserGroup(parserGroupTemp); parserGroups = parserGroupTemp = (PARSERGROUP *)0; } CONSENTUSERS * GroupAddUser(PARSERGROUP *pg, char *id, short not) { return ConsentAddUser(&(pg->users), id, not); } void CopyConsentUserList(CONSENTUSERS *s, CONSENTUSERS **d, short not) { /* we have to add things backwards, since it's an ordered list */ if (s == (CONSENTUSERS *)0 || d == (CONSENTUSERS **)0) return; CopyConsentUserList(s->next, d, not); ConsentAddUser(d, s->user->name, not ? !s->not : s->not); } void GroupItemUsers(char *id) { char *token = (char *)0; PARSERGROUP *pg = (PARSERGROUP *)0; CONDDEBUG((1, "GroupItemUsers(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { DestroyConsentUsers(&(parserGroupTemp->users)); return; } for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { short not; if (token[0] == '!') { token++; not = 1; } else not = 0; if ((pg = GroupFind(token)) == (PARSERGROUP *)0) GroupAddUser(parserGroupTemp, token, not); else CopyConsentUserList(pg->users, &(parserGroupTemp->users), not); } } /* 'default' handling */ CONSENT *parserDefaults = (CONSENT *)0; CONSENT **parserDefaultsTail = &parserDefaults; CONSENT *parserDefaultTemp = (CONSENT *)0; void DestroyParserDefaultOrConsole(CONSENT *c, CONSENT **ph, CONSENT ***pt) { if (c == (CONSENT *)0) return; CONDDEBUG((2, "DestroyParserDefaultOrConsole(): %s", c->server)); if (ph != (CONSENT **)0) { while (*ph != (CONSENT *)0) { if (*ph == c) { break; } else { ph = &((*ph)->pCEnext); } } /* if we were in a chain... */ if (*ph != (CONSENT *)0) { /* unlink from the chain */ *ph = c->pCEnext; /* and possibly fix tail ptr... */ if (c->pCEnext == (CONSENT *)0) (*pt) = ph; } } DestroyConsentUsers(&(c->ro)); DestroyConsentUsers(&(c->rw)); if (c->server != (char *)0) free(c->server); if (c->host != (char *)0) free(c->host); if (c->uds != (char *)0) free(c->uds); if (c->udssubst != (char *)0) free(c->udssubst); if (c->master != (char *)0) free(c->master); if (c->exec != (char *)0) free(c->exec); if (c->device != (char *)0) free(c->device); if (c->devicesubst != (char *)0) free(c->devicesubst); if (c->execsubst != (char *)0) free(c->execsubst); if (c->initsubst != (char *)0) free(c->initsubst); if (c->logfile != (char *)0) free(c->logfile); if (c->initcmd != (char *)0) free(c->initcmd); if (c->motd != (char *)0) free(c->motd); if (c->idlestring != (char *)0) free(c->idlestring); if (c->replstring != (char *)0) free(c->replstring); if (c->tasklist != (char *)0) free(c->tasklist); if (c->breaklist != (char *)0) free(c->breaklist); if (c->execSlave != (char *)0) free(c->execSlave); #if HAVE_FREEIPMI if (c->username != (char *)0) free(c->username); if (c->password != (char *)0) free(c->password); if (c->ipmikg != (STRING *)0) DestroyString(c->ipmikg); #endif while (c->aliases != (NAMES *)0) { NAMES *name; name = c->aliases->next; if (c->aliases->name != (char *)0) free(c->aliases->name); free(c->aliases); c->aliases = name; } if (c->wbuf != (STRING *)0) DestroyString(c->wbuf); free(c); } CONSENT * FindParserDefaultOrConsole(CONSENT *c, char *id) { for (; c != (CONSENT *)0; c = c->pCEnext) { if (strcasecmp(id, c->server) == 0) return c; } return c; } void ApplyDefault(CONSENT *d, CONSENT *c) { if (d->type != UNKNOWNTYPE) c->type = d->type; if (d->breakNum != 0) c->breakNum = d->breakNum; if (d->baud != (BAUD *)0) c->baud = d->baud; if (d->parity != (PARITY *)0) c->parity = d->parity; if (d->idletimeout != 0) c->idletimeout = d->idletimeout; if (d->logfilemax != 0) c->logfilemax = d->logfilemax; if (d->inituid != 0) c->inituid = d->inituid; if (d->initgid != 0) c->initgid = d->initgid; if (d->execuid != 0) c->execuid = d->execuid; if (d->execgid != 0) c->execgid = d->execgid; if (d->raw != FLAGUNKNOWN) c->raw = d->raw; if (d->port != 0) c->port = d->port; if (d->netport != 0) c->netport = d->netport; if (d->portinc != 0) c->portinc = d->portinc; if (d->portbase != 0) c->portbase = d->portbase; if (d->spinmax != 0) c->spinmax = d->spinmax; if (d->spintimer != 0) c->spintimer = d->spintimer; if (d->mark != 0) c->mark = d->mark; if (d->nextMark != 0) c->nextMark = d->nextMark; if (d->activitylog != FLAGUNKNOWN) c->activitylog = d->activitylog; if (d->breaklog != FLAGUNKNOWN) c->breaklog = d->breaklog; if (d->tasklog != FLAGUNKNOWN) c->tasklog = d->tasklog; if (d->hupcl != FLAGUNKNOWN) c->hupcl = d->hupcl; if (d->cstopb != FLAGUNKNOWN) c->cstopb = d->cstopb; if (d->ixany != FLAGUNKNOWN) c->ixany = d->ixany; if (d->ixon != FLAGUNKNOWN) c->ixon = d->ixon; if (d->ixoff != FLAGUNKNOWN) c->ixoff = d->ixoff; #if defined(CRTSCTS) if (d->crtscts != FLAGUNKNOWN) c->crtscts = d->crtscts; #endif if (d->ondemand != FLAGUNKNOWN) c->ondemand = d->ondemand; if (d->striphigh != FLAGUNKNOWN) c->striphigh = d->striphigh; if (d->reinitoncc != FLAGUNKNOWN) c->reinitoncc = d->reinitoncc; if (d->autoreinit != FLAGUNKNOWN) c->autoreinit = d->autoreinit; if (d->unloved != FLAGUNKNOWN) c->unloved = d->unloved; if (d->login != FLAGUNKNOWN) c->login = d->login; if (d->host != (char *)0) { if (c->host != (char *)0) free(c->host); if ((c->host = StrDup(d->host)) == (char *)0) OutOfMem(); } if (d->uds != (char *)0) { if (c->uds != (char *)0) free(c->uds); if ((c->uds = StrDup(d->uds)) == (char *)0) OutOfMem(); } if (d->udssubst != (char *)0) { if (c->udssubst != (char *)0) free(c->udssubst); if ((c->udssubst = StrDup(d->udssubst)) == (char *)0) OutOfMem(); } if (d->master != (char *)0) { if (c->master != (char *)0) free(c->master); if ((c->master = StrDup(d->master)) == (char *)0) OutOfMem(); } if (d->exec != (char *)0) { if (c->exec != (char *)0) free(c->exec); if ((c->exec = StrDup(d->exec)) == (char *)0) OutOfMem(); } if (d->device != (char *)0) { if (c->device != (char *)0) free(c->device); if ((c->device = StrDup(d->device)) == (char *)0) OutOfMem(); } if (d->devicesubst != (char *)0) { if (c->devicesubst != (char *)0) free(c->devicesubst); if ((c->devicesubst = StrDup(d->devicesubst)) == (char *)0) OutOfMem(); } if (d->execsubst != (char *)0) { if (c->execsubst != (char *)0) free(c->execsubst); if ((c->execsubst = StrDup(d->execsubst)) == (char *)0) OutOfMem(); } if (d->initsubst != (char *)0) { if (c->initsubst != (char *)0) free(c->initsubst); if ((c->initsubst = StrDup(d->initsubst)) == (char *)0) OutOfMem(); } if (d->logfile != (char *)0) { if (c->logfile != (char *)0) free(c->logfile); if ((c->logfile = StrDup(d->logfile)) == (char *)0) OutOfMem(); } if (d->initcmd != (char *)0) { if (c->initcmd != (char *)0) free(c->initcmd); if ((c->initcmd = StrDup(d->initcmd)) == (char *)0) OutOfMem(); } if (d->motd != (char *)0) { if (c->motd != (char *)0) free(c->motd); if ((c->motd = StrDup(d->motd)) == (char *)0) OutOfMem(); } if (d->idlestring != (char *)0) { if (c->idlestring != (char *)0) free(c->idlestring); if ((c->idlestring = StrDup(d->idlestring)) == (char *)0) OutOfMem(); } if (d->replstring != (char *)0) { if (c->replstring != (char *)0) free(c->replstring); if ((c->replstring = StrDup(d->replstring)) == (char *)0) OutOfMem(); } if (d->tasklist != (char *)0) { if (c->tasklist != (char *)0) free(c->tasklist); if ((c->tasklist = StrDup(d->tasklist)) == (char *)0) OutOfMem(); } if (d->breaklist != (char *)0) { if (c->breaklist != (char *)0) free(c->breaklist); if ((c->breaklist = StrDup(d->breaklist)) == (char *)0) OutOfMem(); } #if HAVE_FREEIPMI if (d->ipmiwrkset != 0) { c->ipmiworkaround = d->ipmiworkaround; c->ipmiwrkset = d->ipmiwrkset; } if (d->ipmiciphersuite != 0) c->ipmiciphersuite = d->ipmiciphersuite; if (d->ipmiprivlevel != IPMIL_UNKNOWN) c->ipmiprivlevel = d->ipmiprivlevel; if (d->username != (char *)0) { if (c->username != (char *)0) free(c->username); if ((c->username = StrDup(d->username)) == (char *)0) OutOfMem(); } if (d->password != (char *)0) { if (c->password != (char *)0) free(c->password); if ((c->password = StrDup(d->password)) == (char *)0) OutOfMem(); } if (d->ipmikg != (STRING *)0) { if (c->ipmikg != (STRING *)0) BuildString((char *)0, c->ipmikg); else c->ipmikg = AllocString(); BuildStringN(d->ipmikg->string, d->ipmikg->used - 1, c->ipmikg); } #endif CopyConsentUserList(d->ro, &(c->ro), 0); CopyConsentUserList(d->rw, &(c->rw), 0); } void DefaultBegin(char *id) { CONDDEBUG((1, "DefaultBegin(%s) [%s: %d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { if (isMaster) Error("empty default name [%s:%d]", file, line); return; } if (parserDefaultTemp != (CONSENT *)0) DestroyParserDefaultOrConsole(parserDefaultTemp, (CONSENT **)0, (CONSENT ***)0); if ((parserDefaultTemp = (CONSENT *)calloc(1, sizeof(CONSENT))) == (CONSENT *)0) OutOfMem(); if ((parserDefaultTemp->server = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultEnd(void) { CONSENT *c = (CONSENT *)0; CONDDEBUG((1, "DefaultEnd() [%s:%d]", file, line)); if (parserDefaultTemp->server == (char *)0) { DestroyParserDefaultOrConsole(parserDefaultTemp, (CONSENT **)0, (CONSENT ***)0); parserDefaultTemp = (CONSENT *)0; return; } /* if we're overriding an existing default, nuke it */ if ((c = FindParserDefaultOrConsole(parserDefaults, parserDefaultTemp->server)) != (CONSENT *)0) { DestroyParserDefaultOrConsole(c, &parserDefaults, &parserDefaultsTail); } /* add the temp to the tail of the list */ *parserDefaultsTail = parserDefaultTemp; parserDefaultsTail = &(parserDefaultTemp->pCEnext); parserDefaultTemp = (CONSENT *)0; } void DefaultAbort(void) { CONDDEBUG((1, "DefaultAbort() [%s:%d]", file, line)); DestroyParserDefaultOrConsole(parserDefaultTemp, (CONSENT **)0, (CONSENT ***)0); parserDefaultTemp = (CONSENT *)0; } void DefaultDestroy(void) { CONDDEBUG((1, "DefaultDestroy() [%s:%d]", file, line)); while (parserDefaults != (CONSENT *)0) DestroyParserDefaultOrConsole(parserDefaults, &parserDefaults, &parserDefaultsTail); DestroyParserDefaultOrConsole(parserDefaultTemp, (CONSENT **)0, (CONSENT ***)0); parserDefaults = parserDefaultTemp = (CONSENT *)0; } void ProcessBaud(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->baud = (BAUD *)0; return; } c->baud = FindBaud(id); if (c->baud == (BAUD *)0) { if (isMaster) Error("invalid baud rate `%s' [%s:%d]", id, file, line); } } void DefaultItemBaud(char *id) { CONDDEBUG((1, "DefaultItemBaud(%s) [%s:%d]", id, file, line)); ProcessBaud(parserDefaultTemp, id); } void ProcessBreak(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->breakNum = 0; return; } if (((id[0] >= '1' && id[0] <= '9') || (id[0] >= 'a' && id[0] <= 'z')) && (id[1] == '\000')) { c->breakNum = id[0] - '0' - (id[0] > '9' ? BREAKALPHAOFFSET : 0); return; } if (isMaster) Error("invalid break number `%s' [%s:%d]", id, file, line); } void DefaultItemBreak(char *id) { CONDDEBUG((1, "DefaultItemBreak(%s) [%s:%d]", id, file, line)); ProcessBreak(parserDefaultTemp, id); } void ProcessDevice(CONSENT *c, char *id) { if (c->device != (char *)0) { free(c->device); c->device = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; if ((c->device = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemDevice(char *id) { CONDDEBUG((1, "DefaultItemDevice(%s) [%s:%d]", id, file, line)); ProcessDevice(parserDefaultTemp, id); } /* substitution support */ SUBST *substData = (SUBST *)0; int substTokenCount[255]; int SubstTokenCount(char c) { return substTokenCount[(unsigned)c]; } void ZeroSubstTokenCount(void) { #if HAVE_MEMSET memset((void *)&substTokenCount, 0, sizeof(substTokenCount)); #else bzero((char *)&substTokenCount, sizeof(substTokenCount)); #endif } int SubstValue(char c, char **s, int *i) { int retval = 0; CONSENT *pCE; static char *empty = ""; if (substData->data == (void *)0) return 0; pCE = (CONSENT *)(substData->data); if (s != (char **)0) { if (c == 'h') { if (pCE->host == (char *)0) { (*s) = empty; } else { (*s) = pCE->host; } retval = 1; } else if (c == 'c') { if (pCE->server == (char *)0) { (*s) = empty; } else { (*s) = pCE->server; } retval = 1; } else if (c == 'r') { if (pCE->replstring == (char *)0) { (*s) = empty; } else { (*s) = pCE->replstring; } retval = 1; } } if (i != (int *)0) { if (c == 'p') { (*i) = pCE->port; retval = 1; } else if (c == 'P') { (*i) = pCE->netport; retval = 1; } } return retval; } SUBSTTOKEN SubstToken(char c) { switch (c) { case 'p': case 'P': substTokenCount[(unsigned)c]++; return ISNUMBER; case 'h': case 'c': case 'r': substTokenCount[(unsigned)c]++; return ISSTRING; default: return ISNOTHING; } } void InitSubstCallback(void) { if (substData == (SUBST *)0) { if ((substData = (SUBST *)calloc(1, sizeof(SUBST))) == (SUBST *)0) OutOfMem(); substData->value = &SubstValue; substData->token = &SubstToken; ZeroSubstTokenCount(); } } void DefaultItemDevicesubst(char *id) { CONDDEBUG((1, "DefaultItemDevicesubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserDefaultTemp->devicesubst), "devicesubst", id); } void DefaultItemExecsubst(char *id) { CONDDEBUG((1, "DefaultItemExecsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserDefaultTemp->execsubst), "execsubst", id); } void DefaultItemUdssubst(char *id) { CONDDEBUG((1, "DefaultItemUdssubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserDefaultTemp->udssubst), "udssubst", id); } void DefaultItemInitsubst(char *id) { CONDDEBUG((1, "DefaultItemInitsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserDefaultTemp->initsubst), "initsubst", id); } void ProcessUidGid(uid_t * uid, gid_t * gid, char *id) { char *colon = (char *)0; int i; CONDDEBUG((1, "ProcessUidGid(%s) [%s:%d]", id, file, line)); *uid = *gid = 0; if (id == (char *)0 || id[0] == '\000') return; /* hunt for colon */ if ((colon = strchr(id, ':')) != (char *)0) *colon = '\000'; if (id[0] != '\000') { /* Look for non-numeric characters */ for (i = 0; id[i] != '\000'; i++) if (!isdigit((int)id[i])) break; if (id[i] == '\000') { *uid = (uid_t) atoi(id); } else { struct passwd *pwd = (struct passwd *)0; if ((pwd = getpwnam(id)) == (struct passwd *)0) { CONDDEBUG((1, "ProcessUidGid(): getpwnam(%s): %s", id, strerror(errno))); if (isMaster) Error("invalid user name `%s' [%s:%d]", id, file, line); } else { *uid = pwd->pw_uid; } } } if (colon != (char *)0) { *colon = ':'; colon++; if (*colon != '\000') { /* Look for non-numeric characters */ for (i = 0; colon[i] != '\000'; i++) if (!isdigit((int)colon[i])) break; if (colon[i] == '\000') { *gid = (gid_t) atoi(colon); } else { struct group *grp = (struct group *)0; if ((grp = getgrnam(colon)) == (struct group *)0) { CONDDEBUG((1, "ProcessUidGid(): getgrnam(%s): %s", colon, strerror(errno))); if (isMaster) Error("invalid group name `%s' [%s:%d]", colon, file, line); } else { *gid = grp->gr_gid; } } } } } void ProcessInitrunas(CONSENT *c, char *id) { CONDDEBUG((1, "ProcessInitrunas(%s) [%s:%d]", id, file, line)); ProcessUidGid(&(c->inituid), &(c->initgid), id); } void ProcessExecrunas(CONSENT *c, char *id) { CONDDEBUG((1, "ProcessExecrunas(%s) [%s:%d]", id, file, line)); ProcessUidGid(&(c->execuid), &(c->execgid), id); } void DefaultItemInitrunas(char *id) { CONDDEBUG((1, "DefaultItemInitrunas(%s) [%s:%d]", id, file, line)); ProcessInitrunas(parserDefaultTemp, id); } #if HAVE_FREEIPMI void ProcessIpmiPrivLevel(CONSENT *c, char *id) { if (!strcasecmp("user", id)) c->ipmiprivlevel = IPMIL_USER; else if (!strcasecmp("operator", id)) c->ipmiprivlevel = IPMIL_OPERATOR; else if (!strcasecmp("admin", id)) c->ipmiprivlevel = IPMIL_ADMIN; else Error("invalid ipmiprivlevel `%s' [%s:%d]", id, file, line); } void DefaultItemIpmiPrivLevel(char *id) { CONDDEBUG((1, "DefaultItemIpmiPrivLevel(%s) [%s:%d]", id, file, line)); ProcessIpmiPrivLevel(parserDefaultTemp, id); } #endif /*freeipmi */ void DefaultItemExecrunas(char *id) { CONDDEBUG((1, "DefaultItemExecrunas(%s) [%s:%d]", id, file, line)); ProcessExecrunas(parserDefaultTemp, id); } void ProcessExec(CONSENT *c, char *id) { if (c->exec != (char *)0) { free(c->exec); c->exec = (char *)0; } if (id == (char *)0 || id[0] == '\000') { return; } if ((c->exec = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemExec(char *id) { CONDDEBUG((1, "DefaultItemExec(%s) [%s:%d]", id, file, line)); ProcessExec(parserDefaultTemp, id); } void ProcessFlow(CONSENT *c, char *id) { if (isMaster) Error("unimplemented code for `flow' [%s:%d]", file, line); } void DefaultItemFlow(char *id) { CONDDEBUG((1, "DefaultItemFlow(%s) [%s:%d]", id, file, line)); ProcessFlow(parserDefaultTemp, id); } void ProcessHost(CONSENT *c, char *id) { if (c->host != (char *)0) { free(c->host); c->host = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; if ((c->host = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemHost(char *id) { CONDDEBUG((1, "DefaultItemHost(%s) [%s:%d]", id, file, line)); ProcessHost(parserDefaultTemp, id); } void ProcessUds(CONSENT *c, char *id) { if (c->uds != (char *)0) { free(c->uds); c->uds = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; if ((c->uds = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemUds(char *id) { CONDDEBUG((1, "DefaultItemUds(%s) [%s:%d]", id, file, line)); ProcessUds(parserDefaultTemp, id); } #if HAVE_FREEIPMI void ProcessIpmiKG(CONSENT *c, char *id) { char s; char oct = '\000'; short octs = 0; short backslash = 0; char *i = id; static STRING *t = (STRING *)0; if (t == (STRING *)0) t = AllocString(); if ((id == (char *)0) || (*id == '\000')) { if (c->ipmikg != (STRING *)0) { DestroyString(c->ipmikg); c->ipmikg = (STRING *)0; } return; } BuildString((char *)0, t); while ((s = (*i++)) != '\000') { if (octs > 0 && octs < 3 && s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } if (octs != 0) { BuildStringChar(oct, t); octs = 0; oct = '\000'; } if (backslash) { backslash = 0; if (s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } BuildStringChar(s, t); continue; } if (s == '\\') { backslash = 1; continue; } BuildStringChar(s, t); } if (octs != 0) BuildStringChar(oct, t); if (backslash) BuildStringChar('\\', t); if (t->used > 21) { /* max 20 chars */ if (isMaster) Error("ipmikg string `%s' over 20 characters [%s:%d]", id, file, line); return; } if (!ipmiconsole_k_g_is_valid((unsigned char *)t->string, t->used - 1)) { if (isMaster) Error("invalid ipmikg string `%s' [%s:%d]", id, file, line); return; } if (c->ipmikg == (STRING *)0) c->ipmikg = AllocString(); BuildString((char *)0, c->ipmikg); BuildStringN(t->string, t->used - 1, c->ipmikg); } void DefaultItemIpmiKG(char *id) { CONDDEBUG((1, "DefaultItemIpmiKG(%s) [%s:%d]", id, file, line)); ProcessIpmiKG(parserDefaultTemp, id); } void ProcessUsername(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->username = (char *)0; return; } c->username = strdup(id); } void DefaultItemUsername(char *id) { CONDDEBUG((1, "DefaultItemUsername(%s) [%s:%d]", id, file, line)); ProcessUsername(parserDefaultTemp, id); } void ProcessIpmiCipherSuite(CONSENT *c, char *id) { char *p; int i; if ((id == (char *)0) || (*id == '\000')) { c->ipmiciphersuite = 0; return; } /* if we have -1, allow it (we allow >= -1 now) */ if (id[0] == '-' && id[1] == '1' && id[2] == '\000') { c->ipmiciphersuite = 1; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid ipmiciphersuite number `%s' [%s:%d]", id, file, line); return; } i = atoi(id); if (ipmiconsole_cipher_suite_id_is_valid(i)) c->ipmiciphersuite = i + 2; else { if (isMaster) Error("invalid ipmiciphersuite number `%s' [%s:%d]", id, file, line); return; } } void DefaultItemIpmiCipherSuite(char *id) { CONDDEBUG((1, "DefaultItemIpmiCipherSuite(%s) [%s:%d]", id, file, line)); ProcessIpmiCipherSuite(parserDefaultTemp, id); } void ProcessIpmiWorkaround(CONSENT *c, char *id) { unsigned int flag; char *token = (char *)0; short valid = 0; unsigned int wrk = 0; if ((id == (char *)0) || (*id == '\000')) { c->ipmiworkaround = 0; c->ipmiwrkset = 1; return; } for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { short not; if (token[0] == '!') { token++; not = 1; } else not = 0; if (!strcmp(token, "default")) flag = IPMICONSOLE_WORKAROUND_DEFAULT; # if defined(IPMICONSOLE_WORKAROUND_AUTHENTICATION_CAPABILITIES) else if (!strcmp(token, "auth-capabilites")) flag = IPMICONSOLE_WORKAROUND_AUTHENTICATION_CAPABILITIES; # endif # if defined(IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION) else if (!strcmp(token, "intel-session")) flag = IPMICONSOLE_WORKAROUND_INTEL_2_0_SESSION; # endif # if defined(IPMICONSOLE_WORKAROUND_SUPERMICRO_2_0_SESSION) else if (!strcmp(token, "supermicro-session")) flag = IPMICONSOLE_WORKAROUND_SUPERMICRO_2_0_SESSION; # endif # if defined(IPMICONSOLE_WORKAROUND_SUN_2_0_SESSION) else if (!strcmp(token, "sun-session")) flag = IPMICONSOLE_WORKAROUND_SUN_2_0_SESSION; # endif # if defined(IPMICONSOLE_WORKAROUND_OPEN_SESSION_PRIVILEGE) else if (!strcmp(token, "privilege")) flag = IPMICONSOLE_WORKAROUND_OPEN_SESSION_PRIVILEGE; # endif # if defined(IPMICONSOLE_WORKAROUND_NON_EMPTY_INTEGRITY_CHECK_VALUE) else if (!strcmp(token, "integrity")) flag = IPMICONSOLE_WORKAROUND_NON_EMPTY_INTEGRITY_CHECK_VALUE; # endif # if defined(IPMICONSOLE_WORKAROUND_NO_CHECKSUM_CHECK) else if (!strcmp(token, "checksum")) flag = IPMICONSOLE_WORKAROUND_NO_CHECKSUM_CHECK; # endif # if defined(IPMICONSOLE_WORKAROUND_SERIAL_ALERTS_DEFERRED) else if (!strcmp(token, "serial-alerts")) flag = IPMICONSOLE_WORKAROUND_SERIAL_ALERTS_DEFERRED; # endif # if defined(IPMICONSOLE_WORKAROUND_INCREMENT_SOL_PACKET_SEQUENCE) else if (!strcmp(token, "packet-sequence")) flag = IPMICONSOLE_WORKAROUND_INCREMENT_SOL_PACKET_SEQUENCE; # endif # if defined(IPMICONSOLE_WORKAROUND_IGNORE_SOL_PAYLOAD_SIZE) else if (!strcmp(token, "ignore-payload-size")) flag = IPMICONSOLE_WORKAROUND_IGNORE_SOL_PAYLOAD_SIZE; # endif # if defined(IPMICONSOLE_WORKAROUND_IGNORE_SOL_PORT) else if (!strcmp(token, "ignore-port")) flag = IPMICONSOLE_WORKAROUND_IGNORE_SOL_PORT; # endif # if defined(IPMICONSOLE_WORKAROUND_SKIP_SOL_ACTIVATION_STATUS) else if (!strcmp(token, "activation-status")) flag = IPMICONSOLE_WORKAROUND_SKIP_SOL_ACTIVATION_STATUS; # endif # if defined(IPMICONSOLE_WORKAROUND_SKIP_CHANNEL_PAYLOAD_SUPPORT) else if (!strcmp(token, "channel-payload")) flag = IPMICONSOLE_WORKAROUND_SKIP_CHANNEL_PAYLOAD_SUPPORT; # endif else { if (isMaster) Error("invalid ipmiworkaround `%s' [%s:%d]", token, file, line); continue; } if (not) { wrk &= ~flag; } else { wrk |= flag; } valid = 1; } if (valid) { if (ipmiconsole_workaround_flags_is_valid(wrk)) { c->ipmiworkaround = wrk; c->ipmiwrkset = 1; } else { if (isMaster) Error("invalid ipmiworkaround setting [%s:%d]", file, line); return; } } } void DefaultItemIpmiWorkaround(char *id) { CONDDEBUG((1, "DefaultItemIpmiWorkaround(%s) [%s:%d]", id, file, line)); ProcessIpmiWorkaround(parserDefaultTemp, id); } #endif /*freeipmi */ void ProcessInclude(CONSENT *c, char *id) { CONSENT *inc = (CONSENT *)0; if ((id == (char *)0) || (*id == '\000')) return; if ((inc = FindParserDefaultOrConsole(parserDefaults, id)) != (CONSENT *)0) { ApplyDefault(inc, c); } else { if (isMaster) Error("invalid default name `%s' [%s:%d]", id, file, line); } } void DefaultItemInclude(char *id) { CONDDEBUG((1, "DefaultItemInclude(%s) [%s:%d]", id, file, line)); ProcessInclude(parserDefaultTemp, id); } void ProcessLogfile(CONSENT *c, char *id) { if (c->logfile != (char *)0) { free(c->logfile); c->logfile = (char *)0; } if (id == (char *)0 || id[0] == '\000') { return; } if ((c->logfile = StrDup(id)) == (char *)0) OutOfMem(); } void ProcessInitcmd(CONSENT *c, char *id) { if (c->initcmd != (char *)0) { free(c->initcmd); c->initcmd = (char *)0; } if (id == (char *)0 || id[0] == '\000') { return; } if ((c->initcmd = StrDup(id)) == (char *)0) OutOfMem(); } void ProcessMOTD(CONSENT *c, char *id) { if (c->motd != (char *)0) { free(c->motd); c->motd = (char *)0; } if (id == (char *)0 || id[0] == '\000') { return; } if ((c->motd = StrDup(id)) == (char *)0) OutOfMem(); } void ProcessIdlestring(CONSENT *c, char *id) { if (c->idlestring != (char *)0) { free(c->idlestring); c->idlestring = (char *)0; } if (id == (char *)0 || id[0] == '\000') { return; } if ((c->idlestring = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemLogfile(char *id) { CONDDEBUG((1, "DefaultItemLogfile(%s) [%s:%d]", id, file, line)); ProcessLogfile(parserDefaultTemp, id); } void ProcessLogfilemax(CONSENT *c, char *id) { char *p; off_t v = 0; c->logfilemax = 0; if (id == (char *)0 || id[0] == '\000') return; for (p = id; *p != '\000'; p++) { if (!isdigit((int)(*p))) break; v = v * 10 + (*p - '0'); } /* if it wasn't just numbers */ if (*p != '\000') { if ((*p == 'k' || *p == 'K') && *(p + 1) == '\000') { v *= 1024; } else if ((*p == 'm' || *p == 'M') && *(p + 1) == '\000') { v *= 1024 * 1024; } else { if (isMaster) Error("invalid `logfilemax' specification `%s' [%s:%d]", id, file, line); return; } } if (v < 2048) { if (isMaster) Error ("invalid `logfilemax' specification `%s' (must be >= 2K) [%s:%d]", id, file, line); return; } c->logfilemax = v; } void DefaultItemLogfilemax(char *id) { CONDDEBUG((1, "DefaultItemLogfilemax(%s) [%s:%d]", id, file, line)); ProcessLogfilemax(parserDefaultTemp, id); } void DefaultItemInitcmd(char *id) { CONDDEBUG((1, "DefaultItemInitcmd(%s) [%s:%d]", id, file, line)); ProcessInitcmd(parserDefaultTemp, id); } void DefaultItemMOTD(char *id) { CONDDEBUG((1, "DefaultItemMOTD(%s) [%s:%d]", id, file, line)); ProcessMOTD(parserDefaultTemp, id); } void DefaultItemIdlestring(char *id) { CONDDEBUG((1, "DefaultItemIdlestring(%s) [%s:%d]", id, file, line)); ProcessIdlestring(parserDefaultTemp, id); } void ProcessMaster(CONSENT *c, char *id) { if (c->master != (char *)0) { free(c->master); c->master = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; if ((c->master = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemMaster(char *id) { CONDDEBUG((1, "DefaultItemMaster(%s) [%s:%d]", id, file, line)); ProcessMaster(parserDefaultTemp, id); } void ProcessOptions(CONSENT *c, char *id) { char *token = (char *)0; int negative = 0; if ((id == (char *)0) || (*id == '\000')) { c->hupcl = FLAGUNKNOWN; c->cstopb = FLAGUNKNOWN; c->ixany = FLAGUNKNOWN; c->ixon = FLAGUNKNOWN; c->ixoff = FLAGUNKNOWN; #if defined(CRTSCTS) c->crtscts = FLAGUNKNOWN; #endif c->ondemand = FLAGUNKNOWN; c->striphigh = FLAGUNKNOWN; c->reinitoncc = FLAGUNKNOWN; c->autoreinit = FLAGUNKNOWN; c->unloved = FLAGUNKNOWN; c->login = FLAGUNKNOWN; return; } for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { if (token[0] == '!') { negative = 1; token++; } else { negative = 0; } if (strcasecmp("hupcl", token) == 0) c->hupcl = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("ixany", token) == 0) c->ixany = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("ixon", token) == 0) c->ixon = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("ixoff", token) == 0) c->ixoff = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("cstopb", token) == 0) c->cstopb = negative ? FLAGFALSE : FLAGTRUE; #if defined(CRTSCTS) else if (strcasecmp("crtscts", token) == 0) c->crtscts = negative ? FLAGFALSE : FLAGTRUE; #endif else if (strcasecmp("ondemand", token) == 0) c->ondemand = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("striphigh", token) == 0) c->striphigh = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("reinitoncc", token) == 0) c->reinitoncc = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("autoreinit", token) == 0) c->autoreinit = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("unloved", token) == 0) c->unloved = negative ? FLAGFALSE : FLAGTRUE; else if (strcasecmp("login", token) == 0) c->login = negative ? FLAGFALSE : FLAGTRUE; else if (isMaster) Error("invalid option `%s' [%s:%d]", token, file, line); } } void DefaultItemOptions(char *id) { CONDDEBUG((1, "DefaultItemOptions(%s) [%s:%d]", id, file, line)); ProcessOptions(parserDefaultTemp, id); } void ProcessParity(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->parity = (PARITY *)0; return; } c->parity = FindParity(id); if (c->parity == (PARITY *)0) { if (isMaster) Error("invalid parity type `%s' [%s:%d]", id, file, line); } } void DefaultItemParity(char *id) { CONDDEBUG((1, "DefaultItemParity(%s) [%s:%d]", id, file, line)); ProcessParity(parserDefaultTemp, id); } #if HAVE_FREEIPMI void ProcessPassword(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->password = (char *)0; return; } c->password = strdup(id); } void DefaultItemPassword(char *id) { CONDDEBUG((1, "DefaultItemPassword(%s) [%s:%d]", id, file, line)); ProcessPassword(parserDefaultTemp, id); } #endif /*freeipmi */ void ProcessPort(CONSENT *c, char *id) { char *p; if ((id == (char *)0) || (*id == '\000')) { c->port = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it was a number */ if (*p == '\000') { c->port = (unsigned short)atoi(id) + 1; } else { /* non-numeric */ struct servent *se; if ((struct servent *)0 == (se = getservbyname(id, "tcp"))) { if (isMaster) Error ("invalid port name `%s': getservbyname() failure [%s:%d]", id, file, line); return; } else { c->port = ntohs((unsigned short)se->s_port) + 1; } } } void ProcessPortinc(CONSENT *c, char *id) { char *p; if ((id == (char *)0) || (*id == '\000')) { c->portinc = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid portinc number `%s' [%s:%d]", id, file, line); return; } c->portinc = (unsigned short)atoi(id) + 1; } void ProcessPortbase(CONSENT *c, char *id) { char *p; if ((id == (char *)0) || (*id == '\000')) { c->portbase = 0; return; } /* if we have -1, allow it (we allow >= -1 now) */ if (id[0] == '-' && id[1] == '1' && id[2] == '\000') { c->portbase = 1; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid portbase number `%s' [%s:%d]", id, file, line); return; } c->portbase = (unsigned short)atoi(id) + 2; } void DefaultItemPort(char *id) { CONDDEBUG((1, "DefaultItemPort(%s) [%s:%d]", id, file, line)); ProcessPort(parserDefaultTemp, id); } void DefaultItemPortbase(char *id) { CONDDEBUG((1, "DefaultItemPortbase(%s) [%s:%d]", id, file, line)); ProcessPortbase(parserDefaultTemp, id); } void DefaultItemPortinc(char *id) { CONDDEBUG((1, "DefaultItemPortinc(%s) [%s:%d]", id, file, line)); ProcessPortinc(parserDefaultTemp, id); } void ProcessInitspinmax(CONSENT *c, char *id) { char *p; int i; if ((id == (char *)0) || (*id == '\000')) { c->spinmax = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid initspinmax number `%s' [%s:%d]", id, file, line); return; } i = atoi(id); if (i > 254) { if (isMaster) Error("invalid initspinmax number `%s' [%s:%d]", id, file, line); return; } c->spinmax = i + 1; } void DefaultItemInitspinmax(char *id) { CONDDEBUG((1, "DefaultItemInitspinmax(%s) [%s:%d]", id, file, line)); ProcessInitspinmax(parserDefaultTemp, id); } void ProcessInitspintimer(CONSENT *c, char *id) { char *p; int i; if ((id == (char *)0) || (*id == '\000')) { c->spintimer = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid initspintimer number `%s' [%s:%d]", id, file, line); return; } i = atoi(id); if (i > 254) { if (isMaster) Error("invalid initspintimer number `%s' [%s:%d]", id, file, line); return; } c->spintimer = i + 1; } void DefaultItemInitspintimer(char *id) { CONDDEBUG((1, "DefaultItemInitspintimer(%s) [%s:%d]", id, file, line)); ProcessInitspintimer(parserDefaultTemp, id); } void ProcessProtocol(CONSENT *c, char *id) { if ((id == (char *)0) || (*id == '\000')) { c->raw = FLAGUNKNOWN; return; } if (strcmp(id, "telnet") == 0) { c->raw = FLAGFALSE; return; } if (strcmp(id, "raw") == 0) { c->raw = FLAGTRUE; return; } if (isMaster) Error("invalid protocol name `%s' [%s:%d]", id, file, line); } void DefaultItemProtocol(char *id) { CONDDEBUG((1, "DefaultItemProtocol(%s) [%s:%d]", id, file, line)); ProcessProtocol(parserDefaultTemp, id); } void ProcessReplstring(CONSENT *c, char *id) { if (c->replstring != (char *)0) { free(c->replstring); c->replstring = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; if ((c->replstring = StrDup(id)) == (char *)0) OutOfMem(); } void DefaultItemReplstring(char *id) { CONDDEBUG((1, "DefaultItemReplstring(%s) [%s:%d]", id, file, line)); ProcessReplstring(parserDefaultTemp, id); } void ProcessTasklist(CONSENT *c, char *id) { char *token = (char *)0; char *list = (char *)0; CONDDEBUG((1, "ProcessTasklist(%s) [%s:%d]", id, file, line)); if (c->tasklist != (char *)0) { free(c->tasklist); c->tasklist = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; list = BuildTmpString((char *)0); for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { if (token[1] != '\000' || ((token[0] < '0' || token[0] > '9') && (token[0] < 'a' || token[0] > 'z') && token[0] != '*')) { if (isMaster) Error("invalid tasklist reference `%s' [%s:%d]", token, file, line); continue; } list = BuildTmpStringChar(token[0]); } if (list == (char *)0 || *list == '\000') return; if ((c->tasklist = StrDup(list)) == (char *)0) OutOfMem(); } void DefaultItemTasklist(char *id) { CONDDEBUG((1, "DefaultItemTasklist(%s) [%s:%d]", id, file, line)); ProcessTasklist(parserDefaultTemp, id); } void ProcessBreaklist(CONSENT *c, char *id) { char *token = (char *)0; char *list = (char *)0; CONDDEBUG((1, "ProcessBreaklist(%s) [%s:%d]", id, file, line)); if (c->breaklist != (char *)0) { free(c->breaklist); c->breaklist = (char *)0; } if ((id == (char *)0) || (*id == '\000')) return; list = BuildTmpString((char *)0); for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { if (token[1] != '\000' || (((token[0] < '0' || token[0] > '9') && (token[0] < 'a' || token[0] > 'z')) && token[0] != '*')) { if (isMaster) Error("invalid breaklist reference `%s' [%s:%d]", token, file, line); continue; } list = BuildTmpStringChar(token[0]); } if (list == (char *)0 || *list == '\000') return; if ((c->breaklist = StrDup(list)) == (char *)0) OutOfMem(); } void DefaultItemBreaklist(char *id) { CONDDEBUG((1, "DefaultItemBreaklist(%s) [%s:%d]", id, file, line)); ProcessBreaklist(parserDefaultTemp, id); } void ProcessIdletimeout(CONSENT *c, char *id) { char *p; int factor = 0; if ((id == (char *)0) || (*id == '\000')) { c->idletimeout = 0; return; } for (p = id; factor == 0 && *p != '\000'; p++) if (*p == 's' || *p == 'S') factor = 1; else if (*p == 'm' || *p == 'M') factor = 60; else if (*p == 'h' || *p == 'H') factor = 60 * 60; else if (!isdigit((int)(*p))) break; /* if it wasn't a number or a qualifier wasn't at the end */ if (*p != '\000') { if (isMaster) Error("invalid idletimeout specification `%s' [%s:%d]", id, file, line); return; } c->idletimeout = (time_t)atoi(id) * (factor == 0 ? 1 : factor); } void DefaultItemIdletimeout(char *id) { CONDDEBUG((1, "DefaultItemIdletimeout(%s) [%s:%d]", id, file, line)); ProcessIdletimeout(parserDefaultTemp, id); } void ProcessRoRw(CONSENTUSERS **ppCU, char *id) { char *token = (char *)0; PARSERGROUP *pg = (PARSERGROUP *)0; CONDDEBUG((1, "ProcessRoRw(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { DestroyConsentUsers(ppCU); return; } for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { short not; if (token[0] == '!') { token++; not = 1; } else not = 0; if ((pg = GroupFind(token)) == (PARSERGROUP *)0) ConsentAddUser(ppCU, token, not); else CopyConsentUserList(pg->users, ppCU, not); } } void DefaultItemRo(char *id) { CONDDEBUG((1, "DefaultItemRo(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserDefaultTemp->ro), id); } void DefaultItemRw(char *id) { CONDDEBUG((1, "DefaultItemRw(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserDefaultTemp->rw), id); } void ProcessTimestamp(CONSENT *c, char *id) { time_t tyme; char *p = (char *)0, *n = (char *)0; FLAG activity = FLAGFALSE, bactivity = FLAGFALSE, tactivity = FLAGFALSE; int factor = 0, pfactor = 0; int value = 0, pvalue = 0; if ((id == (char *)0) || (*id == '\000')) { c->breaklog = FLAGFALSE; c->tasklog = FLAGFALSE; c->activitylog = FLAGFALSE; c->nextMark = 0; c->mark = 0; return; } /* Parse the [number(m|h|d|l)[a][b]] spec */ tyme = time((time_t *)0); for (p = id; *p != '\000'; p++) { if (*p == 'a' || *p == 'A') { if (n != (char *)0) { if (isMaster) Error ("invalid timestamp specification `%s': numeral before `a' (ignoring numeral) [%s:%d]", id, file, line); } activity = FLAGTRUE; } else if (*p == 'b' || *p == 'B') { if (n != (char *)0) { if (isMaster) Error ("invalid timestamp specification `%s': numeral before `b' (ignoring numeral) [%s:%d]", id, file, line); } bactivity = FLAGTRUE; } else if (*p == 't' || *p == 'T') { if (n != (char *)0) { if (isMaster) Error ("invalid timestamp specification `%s': numeral before `t' (ignoring numeral) [%s:%d]", id, file, line); } tactivity = FLAGTRUE; } else if (*p == 'm' || *p == 'M') { pfactor = 60; } else if (*p == 'h' || *p == 'H') { pfactor = 60 * 60; } else if (*p == 'd' || *p == 'D') { pfactor = 60 * 60 * 24; } else if (*p == 'l' || *p == 'L') { pfactor = -1; } else if (isdigit((int)*p)) { if (n == (char *)0) n = p; } else if (isspace((int)*p)) { if (n != (char *)0) { pfactor = 60; } } else { if (isMaster) Error ("invalid timestamp specification `%s': unknown character `%c' [%s:%d]", id, *p, file, line); return; } if (pfactor) { if (n == (char *)0) { if (isMaster) Error ("invalid timestamp specification `%s': missing numeric prefix for `%c' [%s:%d]", id, *p, file, line); return; } else { *p = '\000'; pvalue = atoi(n); if (pvalue < 0) { if (isMaster) Error ("negative timestamp specification `%s' [%s:%d]", id, file, line); return; } n = (char *)0; factor = pfactor; value = pvalue * pfactor; pvalue = pfactor = 0; } } } if (n != (char *)0) { pvalue = atoi(n); if (pvalue < 0) { if (isMaster) Error("negative timestamp specification `%s' [%s:%d]", id, file, line); return; } factor = 60; value = pvalue * factor; } CONDDEBUG((1, "ProcessTimestamp(): mark spec of `%s' parsed: factor=%d, value=%d, activity=%d, bactivity=%d, tactivity=%d", id, factor, value, activity, bactivity, tactivity)); c->activitylog = activity; c->breaklog = bactivity; c->tasklog = tactivity; if (factor && value) { c->mark = value; if (factor > 0) { tyme -= (tyme % 60); /* minute boundary */ if ((value <= 60 * 60 && (60 * 60) % value == 0) || (value > 60 * 60 && (60 * 60 * 24) % value == 0)) { struct tm *tm; time_t now; /* the increment is a "nice" subdivision of an hour * or a day */ now = tyme; if ((struct tm *)0 != (tm = localtime(&tyme))) { tyme -= tm->tm_min * 60; /* hour boundary */ tyme -= tm->tm_hour * 60 * 60; /* day boundary */ tyme += ((now - tyme) / value) * value; /* up to nice bound */ } } c->nextMark = tyme + value; /* next boundary */ } else { c->nextMark = value; } } else { c->nextMark = c->mark = 0; } } void DefaultItemTimestamp(char *id) { CONDDEBUG((1, "DefaultItemTimestamp(%s) [%s:%d]", id, file, line)); ProcessTimestamp(parserDefaultTemp, id); } void ProcessType(CONSENT *c, char *id) { CONSTYPE t = UNKNOWNTYPE; if ((id == (char *)0) || (*id == '\000')) { c->type = t; return; } if (strcasecmp("device", id) == 0) t = DEVICE; #if HAVE_FREEIPMI else if (strcasecmp("ipmi", id) == 0) t = IPMI; #endif else if (strcasecmp("exec", id) == 0) t = EXEC; else if (strcasecmp("host", id) == 0) t = HOST; else if (strcasecmp("noop", id) == 0) t = NOOP; else if (strcasecmp("uds", id) == 0) t = UDS; if (t == UNKNOWNTYPE) { if (isMaster) Error("invalid console type `%s' [%s:%d]", id, file, line); } else c->type = t; } void DefaultItemType(char *id) { CONDDEBUG((1, "DefaultItemType(%s) [%s:%d]", id, file, line)); ProcessType(parserDefaultTemp, id); } /* 'console' handling */ CONSENT *parserConsoles = (CONSENT *)0; CONSENT **parserConsolesTail = &parserConsoles; CONSENT *parserConsoleTemp = (CONSENT *)0; void ConsoleBegin(char *id) { CONSENT *c; CONDDEBUG((1, "ConsoleBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { if (isMaster) Error("empty console name [%s:%d]", file, line); return; } if (parserConsoleTemp != (CONSENT *)0) DestroyParserDefaultOrConsole(parserConsoleTemp, (CONSENT **)0, (CONSENT ***)0); if ((parserConsoleTemp = (CONSENT *)calloc(1, sizeof(CONSENT))) == (CONSENT *)0) OutOfMem(); if ((parserConsoleTemp->breaklist = StrDup("*")) == (char *)0) OutOfMem(); if ((parserConsoleTemp->tasklist = StrDup("*")) == (char *)0) OutOfMem(); /* prime the pump with a default of "*" */ if ((c = FindParserDefaultOrConsole(parserDefaults, "*")) != (CONSENT *)0) { ApplyDefault(c, parserConsoleTemp); } if ((parserConsoleTemp->server = StrDup(id)) == (char *)0) OutOfMem(); } /* returns 1 if there's an error, otherwise 0 */ int CheckSubst(char *label, char *subst) { int invalid = 0; ZeroSubstTokenCount(); ProcessSubst(substData, (char **)0, (char **)0, label, subst); if (SubstTokenCount('p') && parserConsoleTemp->port == 0) { if (isMaster) Error ("[%s] console references 'port' in '%s' without defining 'port' attribute (ignoring %s) [%s:%d]", parserConsoleTemp->server, label, label, file, line); invalid = 1; } if (SubstTokenCount('h') && parserConsoleTemp->host == (char *)0) { if (isMaster) Error ("[%s] console references 'host' in '%s' without defining 'host' attribute (ignoring %s) [%s:%d]", parserConsoleTemp->server, label, label, file, line); invalid = 1; } return invalid; } void ConsoleEnd(void) { int invalid = 0; CONSENT *c = (CONSENT *)0; CONDDEBUG((1, "ConsoleEnd() [%s:%d]", file, line)); if (parserConsoleTemp->master == (char *)0) { if (isMaster) Error("[%s] console missing 'master' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } switch (parserConsoleTemp->type) { case EXEC: if (parserConsoleTemp->execsubst != (char *)0) { if (CheckSubst("execsubst", parserConsoleTemp->execsubst)) { free(parserConsoleTemp->execsubst); parserConsoleTemp->execsubst = (char *)0; } } break; case DEVICE: if (parserConsoleTemp->device == (char *)0) { if (isMaster) Error ("[%s] console missing 'device' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } if (parserConsoleTemp->baud == (BAUD *)0) { if (isMaster) Error("[%s] console missing 'baud' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } if (parserConsoleTemp->parity == (PARITY *)0) { if (isMaster) Error ("[%s] console missing 'parity' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } if (parserConsoleTemp->devicesubst != (char *)0) { if (CheckSubst ("devicesubst", parserConsoleTemp->devicesubst)) { free(parserConsoleTemp->devicesubst); parserConsoleTemp->devicesubst = (char *)0; } } break; #if HAVE_FREEIPMI case IPMI: if (parserConsoleTemp->host == (char *)0) { if (isMaster) Error("[%s] console missing 'host' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } break; #endif case HOST: if (parserConsoleTemp->host == (char *)0) { if (isMaster) Error("[%s] console missing 'host' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } if (parserConsoleTemp->port == 0) { if (isMaster) Error("[%s] console missing 'port' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } break; case NOOP: break; case UDS: if (parserConsoleTemp->uds == (char *)0) { if (isMaster) Error("[%s] console missing 'uds' attribute [%s:%d]", parserConsoleTemp->server, file, line); invalid = 1; } if (parserConsoleTemp->udssubst != (char *)0) { if (CheckSubst("udssubst", parserConsoleTemp->udssubst)) { free(parserConsoleTemp->udssubst); parserConsoleTemp->udssubst = (char *)0; } } break; case UNKNOWNTYPE: if (isMaster) Error("[%s] console type unknown %d [%s:%d]", parserConsoleTemp->server, parserConsoleTemp->type, file, line); invalid = 1; break; } if (parserConsoleTemp->initsubst != (char *)0 && parserConsoleTemp->initcmd != (char *)0) { if (CheckSubst("initsubst", parserConsoleTemp->initsubst)) { free(parserConsoleTemp->initsubst); parserConsoleTemp->initsubst = (char *)0; } } if (invalid != 0) { DestroyParserDefaultOrConsole(parserConsoleTemp, (CONSENT **)0, (CONSENT ***)0); parserConsoleTemp = (CONSENT *)0; return; } /* if we're overriding an existing console, nuke it */ if ((c = FindParserDefaultOrConsole(parserConsoles, parserConsoleTemp->server)) != (CONSENT *)0) { if (isMaster) Error("console definition for `%s' overridden [%s:%d]", parserConsoleTemp->server, file, line); DestroyParserDefaultOrConsole(c, &parserConsoles, &parserConsolesTail); } /* add the temp to the tail of the list */ *parserConsolesTail = parserConsoleTemp; parserConsolesTail = &(parserConsoleTemp->pCEnext); parserConsoleTemp = (CONSENT *)0; } void ConsoleAbort(void) { CONDDEBUG((1, "ConsoleAbort() [%s:%d]", file, line)); DestroyParserDefaultOrConsole(parserConsoleTemp, (CONSENT **)0, (CONSENT ***)0); parserConsoleTemp = (CONSENT *)0; } void SwapStr(char **s1, char **s2) { char *s; s = *s1; *s1 = *s2; *s2 = s; } void ExpandLogfile(CONSENT *c, char *id) { char *amp = (char *)0; char *p = (char *)0; char *tmp = (char *)0; if (id == (char *)0) return; /* * Here we substitute the console name for any '&' character in the * logfile name. That way you can just have something like * "/var/console/&" for each of the conserver.cf entries. */ p = id; BuildTmpString((char *)0); while ((amp = strchr(p, '&')) != (char *)0) { *amp = '\000'; BuildTmpString(p); BuildTmpString(c->server); p = amp + 1; *amp = '&'; } tmp = BuildTmpString(p); if ((c->logfile = StrDup(tmp)) == (char *)0) OutOfMem(); } /* this will adjust parserConsoles/parserConsolesTail if we're adding * a new console. */ void ConsoleAdd(CONSENT *c) { CONSENT *pCEmatch = (CONSENT *)0; GRPENT *pGEmatch = (GRPENT *)0, *pGEtmp = (GRPENT *)0; CONSCLIENT *pCLtmp = (CONSCLIENT *)0; /* check for remote consoles */ if (!IsMe(c->master)) { if (isMaster) { REMOTE *pRCTemp; if ((pRCTemp = (REMOTE *)calloc(1, sizeof(REMOTE))) == (REMOTE *)0) OutOfMem(); if ((pRCTemp->rhost = StrDup(c->master)) == (char *)0) OutOfMem(); if ((pRCTemp->rserver = StrDup(c->server)) == (char *)0) OutOfMem(); pRCTemp->aliases = c->aliases; c->aliases = (NAMES *)0; *ppRC = pRCTemp; ppRC = &pRCTemp->pRCnext; CONDDEBUG((1, "[%s] remote on %s", c->server, c->master)); } return; } /* * i hope this is right: * in master during startup, * pGroupsOld = *empty* * pGroups = filling with groups of consoles * pGEstage = *empty* * in master during reread, * pGroupsOld = shrinking groups as they move to pGEstage * pGroups = filling with groups of new consoles * pGEstage = filling with groups from pGroupsOld * in slave during reread, * pGroupsOld = shrinking groups as they move to pGEstage * pGroups = *empty* * pGEstage = filling with groups from pGroupsOld * * now, pGroups in the slave during a reread may actually be * temporarily used to hold stuff that's moving to pGEstage. * in the master it might also have group stubs as well. * but by the end, if it has anything, it's all empty groups * in the slave and a mix of real (new) and empty in the master. */ if (!isStartup) { CONSENT **ppCE; /* hunt for a local match, "pCEmatch != (CONSENT *)0" if found */ pCEmatch = (CONSENT *)0; for (pGEmatch = pGroupsOld; pGEmatch != (GRPENT *)0; pGEmatch = pGEmatch->pGEnext) { for (ppCE = &pGEmatch->pCElist, pCEmatch = pGEmatch->pCElist; pCEmatch != (CONSENT *)0; ppCE = &pCEmatch->pCEnext, pCEmatch = pCEmatch->pCEnext) { if (strcasecmp(c->server, pCEmatch->server) == 0) { /* extract pCEmatch from the linked list */ *ppCE = pCEmatch->pCEnext; pGEmatch->imembers--; break; } } if (pCEmatch != (CONSENT *)0) break; } /* we're a child and we didn't find a match, next! */ if (!isMaster && (pCEmatch == (CONSENT *)0)) return; /* otherwise....we'll fall through and build a group with a * single console. at then end we'll do all the hard work * of shuffling things around, comparing, etc. this way we * end up with the same parsed/pruned strings in the same * fields and we don't have to do a lot of the same work here * (especially the whitespace pruning) */ } /* ok, we're ready to rock and roll...first, lets make * sure we have a group to go in and then we'll pop * out a console and start filling it up */ /* let's get going with a group */ if (pGroups == (GRPENT *)0) { if ((pGroups = (GRPENT *)calloc(1, sizeof(GRPENT))) == (GRPENT *)0) OutOfMem(); pGE = pGroups; pGE->pid = -1; pGE->id = groupID++; } /* if we've filled up the group, get another... */ if (cMaxMemb == pGE->imembers) { if ((pGE->pGEnext = (GRPENT *)calloc(1, sizeof(GRPENT))) == (GRPENT *)0) OutOfMem(); pGE = pGE->pGEnext; pGE->pid = -1; pGE->id = groupID++; } /* ok, now for the hard part of the reread */ if (pCEmatch == (CONSENT *)0) { /* add new console */ CONSENT **ph = &parserConsoles; while (*ph != (CONSENT *)0) { if (*ph == c) { break; } else { ph = &((*ph)->pCEnext); } } /* if we were in a chain... */ if (*ph != (CONSENT *)0) { /* unlink from the chain */ *ph = c->pCEnext; /* and possibly fix tail ptr... */ if (c->pCEnext == (CONSENT *)0) parserConsolesTail = ph; } /* putting into action, so allocate runtime items */ if (c->wbuf == (STRING *)0) c->wbuf = AllocString(); c->pCEnext = pGE->pCElist; pGE->pCElist = c; pGE->imembers++; } else { /* pCEmatch != (CONSENT *) 0 - modify console */ short closeMatch = 1; /* see if the group is already staged */ for (pGEtmp = pGEstage; pGEtmp != (GRPENT *)0; pGEtmp = pGEtmp->pGEnext) { if (pGEtmp->id == pGEmatch->id) break; } /* if not, allocate one, copy the data, and reset things */ if (pGEtmp == (GRPENT *)0) { if ((pGEtmp = (GRPENT *)calloc(1, sizeof(GRPENT))) == (GRPENT *)0) OutOfMem(); /* copy the data */ *pGEtmp = *pGEmatch; /* don't destroy the fake console */ pGEmatch->pCEctl = (CONSENT *)0; /* prep counters and such */ pGEtmp->pCElist = (CONSENT *)0; pGEtmp->pCLall = (CONSCLIENT *)0; pGEtmp->imembers = 0; /* link in to the staging area */ pGEtmp->pGEnext = pGEstage; pGEstage = pGEtmp; /* fix the free list (the easy one) */ /* the ppCLbnext link needs to point to the new group */ if (pGEtmp->pCLfree != (CONSCLIENT *)0) pGEtmp->pCLfree->ppCLbnext = &pGEtmp->pCLfree; pGEmatch->pCLfree = (CONSCLIENT *)0; if (pGEtmp->pCEctl) { /* fix the half-logged in clients */ /* the pCLscan list needs to be rebuilt */ /* file descriptors need to be watched */ for (pCLtmp = pGEtmp->pCEctl->pCLon; pCLtmp != (CONSCLIENT *)0; pCLtmp = pCLtmp->pCLnext) { /* remove cleanly from the old group */ if ((CONSCLIENT *)0 != pCLtmp->pCLscan) { pCLtmp->pCLscan->ppCLbscan = pCLtmp->ppCLbscan; } *(pCLtmp->ppCLbscan) = pCLtmp->pCLscan; /* insert into the new group */ pCLtmp->pCLscan = pGEtmp->pCLall; pCLtmp->ppCLbscan = &pGEtmp->pCLall; if (pCLtmp->pCLscan != (CONSCLIENT *)0) { pCLtmp->pCLscan->ppCLbscan = &pCLtmp->pCLscan; } pGEtmp->pCLall = pCLtmp; /* set file descriptors */ FD_SET(FileFDNum(pCLtmp->fd), &rinit); if (maxfd < FileFDNum(pCLtmp->fd) + 1) maxfd = FileFDNum(pCLtmp->fd) + 1; if (!FileBufEmpty(pCLtmp->fd)) FD_SET(FileFDNum(pCLtmp->fd), &winit); } } } /* fix the real clients */ /* the pCLscan list needs to be rebuilt */ /* file descriptors need to be watched */ for (pCLtmp = pCEmatch->pCLon; pCLtmp != (CONSCLIENT *)0; pCLtmp = pCLtmp->pCLnext) { /* remove cleanly from the old group */ if ((CONSCLIENT *)0 != pCLtmp->pCLscan) { pCLtmp->pCLscan->ppCLbscan = pCLtmp->ppCLbscan; } *(pCLtmp->ppCLbscan) = pCLtmp->pCLscan; /* insert into the new group */ pCLtmp->pCLscan = pGEtmp->pCLall; pCLtmp->ppCLbscan = &pGEtmp->pCLall; if (pCLtmp->pCLscan != (CONSCLIENT *)0) { pCLtmp->pCLscan->ppCLbscan = &pCLtmp->pCLscan; } pGEtmp->pCLall = pCLtmp; /* set file descriptors */ FD_SET(FileFDNum(pCLtmp->fd), &rinit); if (maxfd < FileFDNum(pCLtmp->fd) + 1) maxfd = FileFDNum(pCLtmp->fd) + 1; if (!FileBufEmpty(pCLtmp->fd)) FD_SET(FileFDNum(pCLtmp->fd), &winit); } /* add the original console to the new group */ pCEmatch->pCEnext = pGEtmp->pCElist; pGEtmp->pCElist = pCEmatch; pGEtmp->imembers++; if (pCEmatch->cofile != (CONSFILE *)0) { int cofile = FileFDNum(pCEmatch->cofile); FD_SET(cofile, &rinit); if (maxfd < cofile + 1) maxfd = cofile + 1; if (!FileBufEmpty(pCEmatch->cofile)) FD_SET(cofile, &winit); } if (pCEmatch->initfile != (CONSFILE *)0) { int initfile = FileFDNum(pCEmatch->initfile); FD_SET(initfile, &rinit); if (maxfd < initfile + 1) maxfd = initfile + 1; if (!FileBufEmpty(pCEmatch->initfile)) FD_SET(FileFDOutNum(pCEmatch->initfile), &winit); } if (pCEmatch->taskfile != (CONSFILE *)0) { int taskfile = FileFDNum(pCEmatch->taskfile); FD_SET(taskfile, &rinit); if (maxfd < taskfile + 1) maxfd = taskfile + 1; } /* now check for any changes between pCEmatch & c. * we can munch the pCEmatch structure 'cause ConsDown() * doesn't depend on anything we touch here */ if (pCEmatch->type != c->type) { pCEmatch->type = c->type; closeMatch = 0; } if (pCEmatch->logfile != (char *)0 && c->logfile != (char *)0) { if (strcmp(pCEmatch->logfile, c->logfile) != 0) { SwapStr(&pCEmatch->logfile, &c->logfile); closeMatch = 0; } } else if (pCEmatch->logfile != (char *)0 || c->logfile != (char *)0) { SwapStr(&pCEmatch->logfile, &c->logfile); closeMatch = 0; } if (pCEmatch->initcmd != (char *)0 && c->initcmd != (char *)0) { if (strcmp(pCEmatch->initcmd, c->initcmd) != 0) { SwapStr(&pCEmatch->initcmd, &c->initcmd); /* only trigger reinit if we're running the old command */ if (pCEmatch->initpid != 0) closeMatch = 0; } } else if (pCEmatch->initcmd != (char *)0 || c->initcmd != (char *)0) { SwapStr(&pCEmatch->initcmd, &c->initcmd); /* only trigger reinit if we're running the old command */ if (pCEmatch->initpid != 0) closeMatch = 0; } switch (pCEmatch->type) { case EXEC: if (pCEmatch->exec != (char *)0 && c->exec != (char *)0) { if (strcmp(pCEmatch->exec, c->exec) != 0) { SwapStr(&pCEmatch->exec, &c->exec); closeMatch = 0; } } else if (pCEmatch->exec != (char *)0 || c->exec != (char *)0) { SwapStr(&pCEmatch->exec, &c->exec); closeMatch = 0; } if (pCEmatch->execuid != c->execuid) { pCEmatch->execuid = c->execuid; closeMatch = 0; } if (pCEmatch->execgid != c->execgid) { pCEmatch->execgid = c->execgid; closeMatch = 0; } if (pCEmatch->ixany != c->ixany) { pCEmatch->ixany = c->ixany; closeMatch = 0; } if (pCEmatch->ixon != c->ixon) { pCEmatch->ixon = c->ixon; closeMatch = 0; } if (pCEmatch->ixoff != c->ixoff) { pCEmatch->ixoff = c->ixoff; closeMatch = 0; } #if defined(CRTSCTS) if (pCEmatch->crtscts != c->crtscts) { pCEmatch->crtscts = c->crtscts; closeMatch = 0; } #endif break; case DEVICE: if (pCEmatch->device != (char *)0 && c->device != (char *)0) { if (strcmp(pCEmatch->device, c->device) != 0) { SwapStr(&pCEmatch->device, &c->device); closeMatch = 0; } } else if (pCEmatch->device != (char *)0 || c->device != (char *)0) { SwapStr(&pCEmatch->device, &c->device); closeMatch = 0; } if (pCEmatch->baud != c->baud) { pCEmatch->baud = c->baud; closeMatch = 0; } if (pCEmatch->parity != c->parity) { pCEmatch->parity = c->parity; closeMatch = 0; } if (pCEmatch->hupcl != c->hupcl) { pCEmatch->hupcl = c->hupcl; closeMatch = 0; } if (pCEmatch->cstopb != c->cstopb) { pCEmatch->cstopb = c->cstopb; closeMatch = 0; } if (pCEmatch->ixany != c->ixany) { pCEmatch->ixany = c->ixany; closeMatch = 0; } if (pCEmatch->ixon != c->ixon) { pCEmatch->ixon = c->ixon; closeMatch = 0; } if (pCEmatch->ixoff != c->ixoff) { pCEmatch->ixoff = c->ixoff; closeMatch = 0; } #if defined(CRTSCTS) if (pCEmatch->crtscts != c->crtscts) { pCEmatch->crtscts = c->crtscts; closeMatch = 0; } #endif break; #if HAVE_FREEIPMI case IPMI: if (pCEmatch->host != (char *)0 && c->host != (char *)0) { if (strcasecmp(pCEmatch->host, c->host) != 0) { SwapStr(&pCEmatch->host, &c->host); closeMatch = 0; } } else if (pCEmatch->host != (char *)0 || c->host != (char *)0) { SwapStr(&pCEmatch->host, &c->host); closeMatch = 0; } if (pCEmatch->username != (char *)0 && c->username != (char *)0) { if (strcmp(pCEmatch->username, c->username) != 0) { SwapStr(&pCEmatch->username, &c->username); closeMatch = 0; } } else if (pCEmatch->username != (char *)0 || c->username != (char *)0) { SwapStr(&pCEmatch->username, &c->username); closeMatch = 0; } if (pCEmatch->password != (char *)0 && c->password != (char *)0) { if (strcmp(pCEmatch->password, c->password) != 0) { SwapStr(&pCEmatch->password, &c->password); closeMatch = 0; } } else if (pCEmatch->password != (char *)0 || c->password != (char *)0) { SwapStr(&pCEmatch->password, &c->password); closeMatch = 0; } if (pCEmatch->ipmiprivlevel != c->ipmiprivlevel) { pCEmatch->ipmiprivlevel = c->ipmiprivlevel; closeMatch = 0; } if (pCEmatch->ipmiworkaround != c->ipmiworkaround) { pCEmatch->ipmiworkaround = c->ipmiworkaround; closeMatch = 0; } if (pCEmatch->ipmiciphersuite != c->ipmiciphersuite) { pCEmatch->ipmiciphersuite = c->ipmiciphersuite; closeMatch = 0; } if (pCEmatch->ipmikg->used != 0 && c->ipmikg->used == pCEmatch->ipmikg->used) { if ( # if HAVE_MEMCMP memcmp(pCEmatch->ipmikg->string, c->ipmikg, c->ipmikg->used) != 0 # else bcmp(pCEmatch->ipmikg->string, c->ipmikg, c->ipmikg->used) != 0 # endif ) { BuildString((char *)0, pCEmatch->ipmikg); BuildStringN(c->ipmikg->string, c->ipmikg->used - 1, pCEmatch->ipmikg); closeMatch = 0; } } else if (pCEmatch->ipmikg->used != 0 || c->ipmikg->used != 0) { BuildString((char *)0, pCEmatch->ipmikg); BuildStringN(c->ipmikg->string, c->ipmikg->used - 1, pCEmatch->ipmikg); closeMatch = 0; } break; #endif /* freeipmi */ case HOST: if (pCEmatch->host != (char *)0 && c->host != (char *)0) { if (strcasecmp(pCEmatch->host, c->host) != 0) { SwapStr(&pCEmatch->host, &c->host); closeMatch = 0; } } else if (pCEmatch->host != (char *)0 || c->host != (char *)0) { SwapStr(&pCEmatch->host, &c->host); closeMatch = 0; } if (pCEmatch->netport != c->netport) { pCEmatch->netport = c->netport; closeMatch = 0; } break; case NOOP: break; case UDS: if (pCEmatch->uds != (char *)0 && c->uds != (char *)0) { if (strcasecmp(pCEmatch->uds, c->uds) != 0) { SwapStr(&pCEmatch->uds, &c->uds); closeMatch = 0; } } else if (pCEmatch->uds != (char *)0 || c->uds != (char *)0) { SwapStr(&pCEmatch->uds, &c->uds); closeMatch = 0; } break; case UNKNOWNTYPE: break; } /* and now the rest (minus the "runtime" members - see below) */ pCEmatch->idletimeout = c->idletimeout; if (pCEmatch->idletimeout != (time_t)0 && (timers[T_CIDLE] == (time_t)0 || timers[T_CIDLE] > pCEmatch->lastWrite + pCEmatch->idletimeout)) timers[T_CIDLE] = pCEmatch->lastWrite + pCEmatch->idletimeout; pCEmatch->logfilemax = c->logfilemax; if (pCEmatch->logfilemax != (off_t) 0 && timers[T_ROLL] == (time_t)0) timers[T_ROLL] = time((time_t *)0); SwapStr(&pCEmatch->motd, &c->motd); SwapStr(&pCEmatch->idlestring, &c->idlestring); SwapStr(&pCEmatch->replstring, &c->breaklist); SwapStr(&pCEmatch->tasklist, &c->tasklist); SwapStr(&pCEmatch->breaklist, &c->tasklist); pCEmatch->portinc = c->portinc; pCEmatch->portbase = c->portbase; pCEmatch->spinmax = c->spinmax; pCEmatch->spintimer = c->spintimer; pCEmatch->activitylog = c->activitylog; pCEmatch->breaklog = c->breaklog; pCEmatch->tasklog = c->tasklog; pCEmatch->raw = c->raw; pCEmatch->mark = c->mark; pCEmatch->nextMark = c->nextMark; pCEmatch->breakNum = c->breakNum; pCEmatch->ondemand = c->ondemand; pCEmatch->striphigh = c->striphigh; pCEmatch->reinitoncc = c->reinitoncc; pCEmatch->autoreinit = c->autoreinit; pCEmatch->unloved = c->unloved; pCEmatch->login = c->login; pCEmatch->inituid = c->inituid; pCEmatch->initgid = c->initgid; while (pCEmatch->aliases != (NAMES *)0) { NAMES *name; name = pCEmatch->aliases->next; if (pCEmatch->aliases->name != (char *)0) free(pCEmatch->aliases->name); free(pCEmatch->aliases); pCEmatch->aliases = name; } pCEmatch->aliases = c->aliases; c->aliases = (NAMES *)0; /* we have to override the ro/rw lists... */ /* so first destroy the existing (which point to freed space anyway) */ DestroyConsentUsers(&(pCEmatch->ro)); DestroyConsentUsers(&(pCEmatch->rw)); /* now copy over the new stuff */ CopyConsentUserList(c->ro, &(pCEmatch->ro), 0); CopyConsentUserList(c->rw, &(pCEmatch->rw), 0); /* the code above shouldn't touch any of the "runtime" members * 'cause the ConsDown() code needs to be able to rely on those * to shut things down. */ if (!closeMatch && !isMaster) { SendClientsMsg(pCEmatch, "[-- Conserver reconfigured - console reset --]\r\n"); ConsDown(pCEmatch, FLAGFALSE, FLAGTRUE); } } } void ConsoleDestroy(void) { GRPENT **ppGE = (GRPENT **)0; GRPENT *pGEtmp = (GRPENT *)0; CONSENT *c = (CONSENT *)0; CONSENT *cNext = (CONSENT *)0; REMOTE *pRCtmp = (REMOTE *)0; CONDDEBUG((1, "ConsoleDestroy() [%s:%d]", file, line)); /* move aside any existing groups */ pGroupsOld = pGroups; pGroups = (GRPENT *)0; /* init other trackers */ pGE = pGEstage = (GRPENT *)0; /* nuke the old remote consoles */ while (pRCList != (REMOTE *)0) { pRCtmp = pRCList->pRCnext; DestroyRemoteConsole(pRCList); pRCList = pRCtmp; } ppRC = &pRCList; /* add and reconfigure consoles * this will potentially adjust parserConsoles/parserConsolesTail * so we need to peek at the pCEnext pointer ahead of time */ for (c = parserConsoles; c != (CONSENT *)0; c = cNext) { /* time to set some defaults and fix up values */ #if HAVE_FREEIPMI if (c->ipmiprivlevel == 0) c->ipmiprivlevel = IPMIL_ADMIN; c->ipmiprivlevel--; if (c->ipmiciphersuite == 0) c->ipmiciphersuite = 1; c->ipmiciphersuite -= 2; if (c->ipmikg == (STRING *)0) c->ipmikg = AllocString(); if (c->ipmiwrkset == 0) { c->ipmiworkaround = IPMICONSOLE_WORKAROUND_DEFAULT; c->ipmiwrkset = 1; } #endif /* default break number */ if (c->breakNum == 0) c->breakNum = 1; /* initspin* values are +1, so adjust (since we don't * compare on a reread) */ if (c->spinmax == 0) c->spinmax = 5; else c->spinmax--; if (c->spintimer == 0) c->spintimer = 1; else c->spintimer--; /* portbase, portinc, and port values are +2, +1, +1, so a zero can * show that no value was given. defaults: portbase=0, portinc=1 */ if (c->portbase != 0) c->portbase -= 2; if (c->portinc != 0) c->portinc--; else c->portinc = 1; /* if this is ever false, we don't actually use the port value, so * doesn't matter if we "default" to zero...it's all enforced in * ConsoleEnd() */ if (c->port != 0) c->port--; /* now calculate the "real" port number */ /* this formula could give -1 because * portbase >= -1, portinc >= 0, and port >= 0 * since it's an unsigned type, it'll wrap back around * look very, very, bizarre. but, oh well. yeah, a * user can shoot himself in the foot with a bad config * file, but it won't hurt too much. */ c->netport = c->portbase + c->portinc * c->port; /* prepare for substitutions */ substData->data = (void *)c; /* check for substitutions */ if (c->type == DEVICE && c->devicesubst != (char *)0) ProcessSubst(substData, &(c->device), (char **)0, (char *)0, c->devicesubst); if (c->type == EXEC && c->execsubst != (char *)0) ProcessSubst(substData, &(c->exec), (char **)0, (char *)0, c->execsubst); if (c->type == UDS && c->udssubst != (char *)0) ProcessSubst(substData, &(c->uds), (char **)0, (char *)0, c->udssubst); if (c->initcmd != (char *)0 && c->initsubst != (char *)0) ProcessSubst(substData, &(c->initcmd), (char **)0, (char *)0, c->initsubst); /* go ahead and do the '&' substitution */ if (c->logfile != (char *)0) { char *lf; lf = c->logfile; ExpandLogfile(c, lf); free(lf); } /* set the idlestring default, if needed */ if (c->idlestring == (char *)0 && (c->idlestring = StrDup("\\n")) == (char *)0) OutOfMem(); /* set the options that default true */ if (c->autoreinit == FLAGUNKNOWN) c->autoreinit = FLAGTRUE; if (c->ixon == FLAGUNKNOWN) c->ixon = FLAGTRUE; if (c->ixoff == FLAGUNKNOWN) { if (c->type == EXEC) c->ixoff = FLAGFALSE; else c->ixoff = FLAGTRUE; } /* set the options that default false */ if (c->activitylog == FLAGUNKNOWN) c->activitylog = FLAGFALSE; if (c->raw == FLAGUNKNOWN) c->raw = FLAGFALSE; if (c->breaklog == FLAGUNKNOWN) c->breaklog = FLAGFALSE; if (c->tasklog == FLAGUNKNOWN) c->tasklog = FLAGFALSE; if (c->hupcl == FLAGUNKNOWN) c->hupcl = FLAGFALSE; if (c->ixany == FLAGUNKNOWN) c->ixany = FLAGFALSE; if (c->cstopb == FLAGUNKNOWN) c->cstopb = FLAGFALSE; #if defined(CRTSCTS) if (c->crtscts == FLAGUNKNOWN) c->crtscts = FLAGFALSE; #endif if (c->ondemand == FLAGUNKNOWN) c->ondemand = FLAGFALSE; if (c->reinitoncc == FLAGUNKNOWN) c->reinitoncc = FLAGFALSE; if (c->striphigh == FLAGUNKNOWN) c->striphigh = FLAGFALSE; if (c->unloved == FLAGUNKNOWN) c->unloved = FLAGFALSE; if (c->login == FLAGUNKNOWN) c->login = FLAGTRUE; /* set some forced options, based on situations */ if (c->type == NOOP) { c->login = FLAGFALSE; ProcessLogfile(c, (char *)0); } /* now let command-line args override things */ if (fNoautoreup) c->autoreinit = FLAGFALSE; if (fNoinit) c->ondemand = FLAGTRUE; if (fStrip) c->striphigh = FLAGTRUE; if (fReopen) c->reinitoncc = FLAGTRUE; if (fAll) c->unloved = FLAGTRUE; /* now remember where we're headed and do the dirty work */ cNext = c->pCEnext; /* perform all post-processing checks */ if (c->type == UDS) { struct sockaddr_un port; int limit, len; limit = sizeof(port.sun_path); len = strlen(c->uds); if (len >= limit) { if (isMaster) Error("[%s] 'uds' path too large (%d >= %d) [%s:%d]", c->server, len, limit, file, line); continue; } } if (fSyntaxOnly > 1) { static STRING *s = (STRING *)0; if (s == (STRING *)0) s = AllocString(); BuildString((char *)0, s); BuildString(BuildTmpStringPrint ("{%s:%s:", c->server, c->master), s); if (c->aliases != (NAMES *)0) { NAMES *n; for (n = c->aliases; n != (NAMES *)0; n = n->next) { if (n == c->aliases) BuildStringChar(',', s); BuildString(n->name, s); } } BuildStringChar(':', s); switch (c->type) { case EXEC: BuildString(BuildTmpStringPrint ("|:%s", (c->exec != (char *)0 ? c->exec : "/bin/sh")), s); break; case HOST: BuildString(BuildTmpStringPrint ("!:%s,%hu", c->host, c->netport), s); break; case NOOP: BuildString("#:", s); break; case UDS: BuildString(BuildTmpStringPrint("%%:%s", c->uds), s); break; case DEVICE: BuildString(BuildTmpStringPrint ("/:%s,%s%c", c->device, (c->baud ? c->baud->acrate : ""), (c->parity ? c->parity->key[0] : ' ')), s); break; #if HAVE_FREEIPMI case IPMI: BuildString(BuildTmpStringPrint("@:%s", c->host), s); #endif case UNKNOWNTYPE: /* shut up gcc */ break; } BuildStringChar('}', s); Msg("%s", s->string); } ConsoleAdd(c); } /* go through and nuke groups (if a child or are empty) */ for (ppGE = &pGroups; *ppGE != (GRPENT *)0;) { if (!isMaster || (*ppGE)->imembers == 0) { pGEtmp = *ppGE; *ppGE = (*ppGE)->pGEnext; DestroyGroup(pGEtmp); } else { ppGE = &((*ppGE)->pGEnext); } } /* now append the staged groups (old matching groups/consoles) */ *ppGE = pGEstage; /* reset the trackers */ pGE = pGEstage = (GRPENT *)0; /* nuke the old groups lists (non-matching groups/consoles) */ while (pGroupsOld != (GRPENT *)0) { pGEtmp = pGroupsOld->pGEnext; DestroyGroup(pGroupsOld); pGroupsOld = pGEtmp; } while (parserConsoles != (CONSENT *)0) DestroyParserDefaultOrConsole(parserConsoles, &parserConsoles, &parserConsolesTail); DestroyParserDefaultOrConsole(parserConsoleTemp, (CONSENT **)0, (CONSENT ***)0); parserConsoles = parserConsoleTemp = (CONSENT *)0; /* here we check on the client permissions and adjust accordingly */ if (!isMaster && pGroups != (GRPENT *)0) { CONSENT *pCE = (CONSENT *)0; CONSCLIENT *pCL = (CONSCLIENT *)0; CONSCLIENT *pCLnext = (CONSCLIENT *)0; int access = -1; for (pCE = pGroups->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { for (pCL = pCE->pCLon; pCL != (CONSCLIENT *)0; pCL = pCLnext) { pCLnext = pCL->pCLnext; /* in case we drop client */ access = ClientAccess(pCE, pCL->username->string); if (access == -1) { DisconnectClient(pGroups, pCL, "[Conserver reconfigured - access denied]\r\n", FLAGFALSE); continue; } if (pCL->fro == access) continue; pCL->fro = access; if (access) { FileWrite(pCL->fd, FLAGFALSE, "[Conserver reconfigured - r/w access removed]\r\n", -1); if (pCL->fwr) { BumpClient(pCE, (char *)0); TagLogfileAct(pCE, "%s detached", pCL->acid->string); if (pCE->nolog) { pCE->nolog = 0; TagLogfile(pCE, "Console logging restored (bumped)"); } FindWrite(pCE); } } else { FileWrite(pCL->fd, FLAGFALSE, "[Conserver reconfigured - r/w access granted]\r\n", -1); } } } } } CONSENT * FindConsoleName(CONSENT *c, char *id) { NAMES *a = (NAMES *)0; for (; c != (CONSENT *)0; c = c->pCEnext) { if (strcasecmp(id, c->server) == 0) return c; for (a = c->aliases; a != (NAMES *)0; a = a->next) if (strcasecmp(id, a->name) == 0) return c; } return c; } void ConsoleItemAliases(char *id) { char *token = (char *)0; NAMES *name = (NAMES *)0; CONSENT *c = (CONSENT *)0; CONDDEBUG((1, "ConsoleItemAliases(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { while (parserConsoleTemp->aliases != (NAMES *)0) { name = parserConsoleTemp->aliases->next; if (parserConsoleTemp->aliases->name != (char *)0) free(parserConsoleTemp->aliases->name); free(parserConsoleTemp->aliases); parserConsoleTemp->aliases = name; } return; } for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { if ((c = FindConsoleName(parserConsoles, token)) != (CONSENT *)0) { if (isMaster) Error ("alias name `%s' invalid: already in use by console `%s' [%s:%d]", token, c->server, file, line); continue; } if ((c = FindConsoleName(parserConsoleTemp, token)) != (CONSENT *)0) { if (isMaster) Error("alias name `%s' repeated: ignored [%s:%d]", token, file, line); continue; } if ((name = (NAMES *)calloc(1, sizeof(NAMES))) == (NAMES *)0) OutOfMem(); if ((name->name = StrDup(token)) == (char *)0) OutOfMem(); name->next = parserConsoleTemp->aliases; parserConsoleTemp->aliases = name; } } void ConsoleItemBaud(char *id) { CONDDEBUG((1, "ConsoleItemBaud(%s) [%s:%d]", id, file, line)); ProcessBaud(parserConsoleTemp, id); } void ConsoleItemBreak(char *id) { CONDDEBUG((1, "ConsoleItemBreak(%s) [%s:%d]", id, file, line)); ProcessBreak(parserConsoleTemp, id); } void ConsoleItemDevice(char *id) { CONDDEBUG((1, "ConsoleItemDevice(%s) [%s:%d]", id, file, line)); ProcessDevice(parserConsoleTemp, id); } void ConsoleItemDevicesubst(char *id) { CONDDEBUG((1, "ConsoleItemDevicesubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserConsoleTemp->devicesubst), "devicesubst", id); } void ConsoleItemExecsubst(char *id) { CONDDEBUG((1, "ConsoleItemExecsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserConsoleTemp->execsubst), "execsubst", id); } void ConsoleItemUdssubst(char *id) { CONDDEBUG((1, "ConsoleItemUdssubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserConsoleTemp->udssubst), "udssubst", id); } void ConsoleItemInitsubst(char *id) { CONDDEBUG((1, "ConsoleItemInitsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserConsoleTemp->initsubst), "initsubst", id); } void ConsoleItemInitrunas(char *id) { CONDDEBUG((1, "ConsoleItemInitrunas(%s) [%s:%d]", id, file, line)); ProcessInitrunas(parserConsoleTemp, id); } void ConsoleItemExecrunas(char *id) { CONDDEBUG((1, "ConsoleItemExecrunas(%s) [%s:%d]", id, file, line)); ProcessExecrunas(parserConsoleTemp, id); } void ConsoleItemExec(char *id) { CONDDEBUG((1, "ConsoleItemExec(%s) [%s:%d]", id, file, line)); ProcessExec(parserConsoleTemp, id); } void ConsoleItemFlow(char *id) { CONDDEBUG((1, "ConsoleItemFlow(%s) [%s:%d]", id, file, line)); ProcessFlow(parserConsoleTemp, id); } void ConsoleItemHost(char *id) { CONDDEBUG((1, "ConsoleItemHost(%s) [%s:%d]", id, file, line)); ProcessHost(parserConsoleTemp, id); } void ConsoleItemUds(char *id) { CONDDEBUG((1, "ConsoleItemUds(%s) [%s:%d]", id, file, line)); ProcessUds(parserConsoleTemp, id); } #if HAVE_FREEIPMI void ConsoleItemIpmiKG(char *id) { CONDDEBUG((1, "ConsoleItemIpmiKG(%s) [%s:%d]", id, file, line)); ProcessIpmiKG(parserConsoleTemp, id); } void ConsoleItemUsername(char *id) { CONDDEBUG((1, "ConsoleItemUsername(%s) [%s:%d]", id, file, line)); ProcessUsername(parserConsoleTemp, id); } void ConsoleItemIpmiCipherSuite(char *id) { CONDDEBUG((1, "ConsoleItemIpmiCipherSuite(%s) [%s:%d]", id, file, line)); ProcessIpmiCipherSuite(parserConsoleTemp, id); } void ConsoleItemIpmiWorkaround(char *id) { CONDDEBUG((1, "ConsoleItemIpmiWorkaround(%s) [%s:%d]", id, file, line)); ProcessIpmiWorkaround(parserConsoleTemp, id); } #endif /*freeipmi */ void ConsoleItemInclude(char *id) { CONDDEBUG((1, "ConsoleItemInclude(%s) [%s:%d]", id, file, line)); ProcessInclude(parserConsoleTemp, id); } void ConsoleItemLogfile(char *id) { CONDDEBUG((1, "ConsoleItemLogfile(%s) [%s:%d]", id, file, line)); ProcessLogfile(parserConsoleTemp, id); } void ConsoleItemLogfilemax(char *id) { CONDDEBUG((1, "ConsoleItemLogfilemax(%s) [%s:%d]", id, file, line)); ProcessLogfilemax(parserConsoleTemp, id); } void ConsoleItemInitcmd(char *id) { CONDDEBUG((1, "ConsoleItemInitcmd(%s) [%s:%d]", id, file, line)); ProcessInitcmd(parserConsoleTemp, id); } void ConsoleItemMOTD(char *id) { CONDDEBUG((1, "ConsoleItemMOTD(%s) [%s:%d]", id, file, line)); ProcessMOTD(parserConsoleTemp, id); } void ConsoleItemIdlestring(char *id) { CONDDEBUG((1, "ConsoleItemIdlestring(%s) [%s:%d]", id, file, line)); ProcessIdlestring(parserConsoleTemp, id); } void ConsoleItemMaster(char *id) { CONDDEBUG((1, "ConsoleItemMaster(%s) [%s:%d]", id, file, line)); ProcessMaster(parserConsoleTemp, id); } void ConsoleItemOptions(char *id) { CONDDEBUG((1, "ConsoleItemOptions(%s) [%s:%d]", id, file, line)); ProcessOptions(parserConsoleTemp, id); } void ConsoleItemParity(char *id) { CONDDEBUG((1, "ConsoleItemParity(%s) [%s:%d]", id, file, line)); ProcessParity(parserConsoleTemp, id); } #if HAVE_FREEIPMI void ConsoleItemPassword(char *id) { CONDDEBUG((1, "ConsoleItemPassword(%s) [%s:%d]", id, file, line)); ProcessPassword(parserConsoleTemp, id); } #endif /*freeipmi */ void ConsoleItemPort(char *id) { CONDDEBUG((1, "ConsoleItemPort(%s) [%s:%d]", id, file, line)); ProcessPort(parserConsoleTemp, id); } void ConsoleItemPortbase(char *id) { CONDDEBUG((1, "ConsoleItemPortbase(%s) [%s:%d]", id, file, line)); ProcessPortbase(parserConsoleTemp, id); } void ConsoleItemPortinc(char *id) { CONDDEBUG((1, "ConsoleItemPortinc(%s) [%s:%d]", id, file, line)); ProcessPortinc(parserConsoleTemp, id); } void ConsoleItemInitspinmax(char *id) { CONDDEBUG((1, "ConsoleItemInitspinmax(%s) [%s:%d]", id, file, line)); ProcessInitspinmax(parserConsoleTemp, id); } void ConsoleItemInitspintimer(char *id) { CONDDEBUG((1, "ConsoleItemInitspintimer(%s) [%s:%d]", id, file, line)); ProcessInitspintimer(parserConsoleTemp, id); } void ConsoleItemProtocol(char *id) { CONDDEBUG((1, "ConsoleItemProtocol(%s) [%s:%d]", id, file, line)); ProcessProtocol(parserConsoleTemp, id); } void ConsoleItemReplstring(char *id) { CONDDEBUG((1, "ConsoleItemReplstring(%s) [%s:%d]", id, file, line)); ProcessReplstring(parserConsoleTemp, id); } void ConsoleItemTasklist(char *id) { CONDDEBUG((1, "ConsoleItemTasklist(%s) [%s:%d]", id, file, line)); ProcessTasklist(parserConsoleTemp, id); } void ConsoleItemBreaklist(char *id) { CONDDEBUG((1, "ConsoleItemBreaklist(%s) [%s:%d]", id, file, line)); ProcessBreaklist(parserConsoleTemp, id); } void ConsoleItemIdletimeout(char *id) { CONDDEBUG((1, "ConsoleItemIdletimeout(%s) [%s:%d]", id, file, line)); ProcessIdletimeout(parserConsoleTemp, id); } void ConsoleItemRo(char *id) { CONDDEBUG((1, "ConsoleItemRo(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserConsoleTemp->ro), id); } void ConsoleItemRw(char *id) { CONDDEBUG((1, "ConsoleItemRw(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserConsoleTemp->rw), id); } void ConsoleItemTimestamp(char *id) { CONDDEBUG((1, "ConsoleItemTimestamp(%s) [%s:%d]", id, file, line)); ProcessTimestamp(parserConsoleTemp, id); } void ConsoleItemType(char *id) { CONDDEBUG((1, "ConsoleItemType(%s) [%s:%d]", id, file, line)); ProcessType(parserConsoleTemp, id); } /* 'access' handling */ typedef struct parserAccess { STRING *name; ACCESS *access; CONSENTUSERS *admin; CONSENTUSERS *limited; struct parserAccess *next; } PARSERACCESS; PARSERACCESS *parserAccesses = (PARSERACCESS *)0; PARSERACCESS **parserAccessesTail = &parserAccesses; PARSERACCESS *parserAccessTemp = (PARSERACCESS *)0; void DestroyParserAccess(PARSERACCESS *pa) { PARSERACCESS **ppa = &parserAccesses; ACCESS *a = (ACCESS *)0; char *m = (char *)0; if (pa == (PARSERACCESS *)0) return; while (*ppa != (PARSERACCESS *)0) { if (*ppa == pa) { break; } else { ppa = &((*ppa)->next); } } BuildTmpString((char *)0); m = BuildTmpString(pa->name->string); /* if we were in a chain... */ if (*ppa != (PARSERACCESS *)0) { /* unlink from the chain */ *ppa = pa->next; /* and possibly fix tail ptr... */ if (pa->next == (PARSERACCESS *)0) parserAccessesTail = ppa; } DestroyString(pa->name); for (a = pa->access; a != (ACCESS *)0;) { ACCESS *n = a->pACnext; BuildTmpStringChar(','); m = BuildTmpString(a->pcwho); DestroyAccessList(a); a = n; } DestroyConsentUsers(&(pa->admin)); DestroyConsentUsers(&(pa->limited)); free(pa); CONDDEBUG((2, "DestroyParserAccess(): %s", m)); } PARSERACCESS * AccessFind(char *id) { PARSERACCESS *pa; for (pa = parserAccesses; pa != (PARSERACCESS *)0; pa = pa->next) { if (strcasecmp(id, pa->name->string) == 0) return pa; } return pa; } void AccessAddACL(PARSERACCESS *pa, ACCESS *access) { ACCESS **ppa = (ACCESS **)0; ACCESS *new = (ACCESS *)0; for (ppa = &(pa->access); *ppa != (ACCESS *)0; ppa = &((*ppa)->pACnext)) { if ((*ppa)->ctrust == access->ctrust && (*ppa)->isCIDR == access->isCIDR && strcasecmp((*ppa)->pcwho, access->pcwho) == 0) { return; } } if ((new = (ACCESS *)calloc(1, sizeof(ACCESS))) == (ACCESS *)0) OutOfMem(); *new = *access; if ((new->pcwho = StrDup(access->pcwho)) == (char *)0) OutOfMem(); /* link into the list at the end */ new->pACnext = (ACCESS *)0; *ppa = new; } void AccessBegin(char *id) { CONDDEBUG((1, "AccessBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { if (isMaster) Error("empty access name [%s:%d]", file, line); return; } if (parserAccessTemp != (PARSERACCESS *)0) DestroyParserAccess(parserAccessTemp); if ((parserAccessTemp = (PARSERACCESS *)calloc(1, sizeof(PARSERACCESS))) == (PARSERACCESS *)0) OutOfMem(); parserAccessTemp->name = AllocString(); BuildString(id, parserAccessTemp->name); } void AccessEnd(void) { PARSERACCESS *pa = (PARSERACCESS *)0; CONDDEBUG((1, "AccessEnd() [%s:%d]", file, line)); if (parserAccessTemp->name->used <= 1) { DestroyParserAccess(parserAccessTemp); parserAccessTemp = (PARSERACCESS *)0; return; } /* if we're overriding an existing group, nuke it */ if ((pa = AccessFind(parserAccessTemp->name->string)) != (PARSERACCESS *)0) { DestroyParserAccess(pa); } /* add the temp to the tail of the list */ *parserAccessesTail = parserAccessTemp; parserAccessesTail = &(parserAccessTemp->next); parserAccessTemp = (PARSERACCESS *)0; } void AccessAbort(void) { CONDDEBUG((1, "AccessAbort() [%s:%d]", file, line)); DestroyParserAccess(parserAccessTemp); parserAccessTemp = (PARSERACCESS *)0; } void AccessDestroy(void) { ACCESS *a; PARSERACCESS *p; ACCESS **ppa; CONSENTUSERS **pad; CONSENTUSERS **plu; CONDDEBUG((1, "AccessDestroy() [%s:%d]", file, line)); /* clean out the access restrictions */ while (pACList != (ACCESS *)0) { a = pACList->pACnext; DestroyAccessList(pACList); pACList = a; } pACList = (ACCESS *)0; DestroyConsentUsers(&(pADList)); DestroyConsentUsers(&(pLUList)); pADList = (CONSENTUSERS *)0; pLUList = (CONSENTUSERS *)0; ppa = &(pACList); pad = &(pADList); plu = &(pLUList); for (p = parserAccesses; p != (PARSERACCESS *)0; p = p->next) { #if DUMPDATA Msg("ParserAccess = %s", p->name->string); for (a = p->access; a != (ACCESS *)0; a = a->pACnext) { Msg(" Access = %c, %d, %s", a->ctrust, a->isCIDR, a->pcwho); } { CONSENTUSERS *u; for (u = p->admin; u != (CONSENTUSERS *)0; u = u->next) { Msg(" Admin = %s", u->user->name); } for (u = p->limited; u != (CONSENTUSERS *)0; u = u->next) { Msg(" Limited = %s", u->user->name); } } #endif if ((p->name->used == 2 && p->name->string[0] == '*') || IsMe(p->name->string)) { CONDDEBUG((1, "AccessDestroy(): adding ACL `%s'", p->name->string)); *ppa = p->access; p->access = (ACCESS *)0; /* add any admin users to the list */ if (p->admin != (CONSENTUSERS *)0) { *pad = p->admin; p->admin = (CONSENTUSERS *)0; } /* add any limited users to the list */ if (p->limited != (CONSENTUSERS *)0) { *plu = p->limited; p->limited = (CONSENTUSERS *)0; } /* advance to the end of the list so we can append more * this will potentially have duplicates in the access * list, but since we're using the first seen, it's more * overhead, but no big deal */ while (*ppa != (ACCESS *)0) { ppa = &((*ppa)->pACnext); } while (*pad != (CONSENTUSERS *)0) { pad = &((*pad)->next); } while (*plu != (CONSENTUSERS *)0) { plu = &((*plu)->next); } } } while (parserAccesses != (PARSERACCESS *)0) DestroyParserAccess(parserAccesses); DestroyParserAccess(parserAccessTemp); parserAccesses = parserAccessTemp = (PARSERACCESS *)0; } void AccessItemAdmin(char *id) { CONDDEBUG((1, "AccessItemAdmin(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserAccessTemp->admin), id); } void AccessItemLimited(char *id) { CONDDEBUG((1, "AccessItemLimited(%s) [%s:%d]", id, file, line)); ProcessRoRw(&(parserAccessTemp->limited), id); } void AccessItemInclude(char *id) { char *token = (char *)0; PARSERACCESS *pa = (PARSERACCESS *)0; CONDDEBUG((1, "AccessItemInclude(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) return; for (token = strtok(id, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { if ((pa = AccessFind(token)) == (PARSERACCESS *)0) { if (isMaster) Error("unknown access name `%s' [%s:%d]", token, file, line); } else { ACCESS *a; for (a = pa->access; a != (ACCESS *)0; a = a->pACnext) { AccessAddACL(parserAccessTemp, a); } if (pa->admin != (CONSENTUSERS *)0) CopyConsentUserList(pa->admin, &(parserAccessTemp->admin), 0); if (pa->limited != (CONSENTUSERS *)0) CopyConsentUserList(pa->limited, &(parserAccessTemp->limited), 0); } } } void AccessProcessACL(char trust, char *acl) { char *token = (char *)0; ACCESS **ppa = (ACCESS **)0; ACCESS *pa = (ACCESS *)0; #if HAVE_INET_ATON struct in_addr inetaddr; #else in_addr_t addr; #endif /* an empty acl will clear out that type of acl */ if ((acl == (char *)0) || (*acl == '\000')) { /* move the old access list aside */ ACCESS *a = parserAccessTemp->access; parserAccessTemp->access = (ACCESS *)0; /* go through the access list */ while (a != (ACCESS *)0) { ACCESS *n = a->pACnext; /* if it's not the trust that we see, add it back */ if (a->ctrust != trust) AccessAddACL(parserAccessTemp, a); /* destroy the old one */ DestroyAccessList(a); a = n; } } for (token = strtok(acl, ALLWORDSEP); token != (char *)0; token = strtok(NULL, ALLWORDSEP)) { int i = 0, isCIDR = 0; int nCount = 0, dCount = 0, sCount = 0, mCount = 0, sPos = 0; /* Scan for [0-9./], and stop if you find something else */ for (i = 0; token[i] != '\000'; i++) { if (isdigit((int)(token[i]))) { /* count up digits before and after the slash */ if (sCount) nCount++; else mCount++; } else if (token[i] == '/') { sCount++; sPos = i; } else if (token[i] == '.') { /* if we see non-digits after the slash, cause error */ if (sCount) dCount += 10; dCount++; } else break; } if (token[i] == '\000') { /* assuming CIDR notation */ if (dCount == 3 && ((sCount == 1 && nCount > 0) || (sCount == 0 && nCount == 0))) { if (sCount == 1) { int mask = atoi(&(token[sPos + 1])); if (mask < 0 || mask > 255) { goto cidrerror; } token[sPos] = '\000'; } #if HAVE_INET_ATON if (inet_aton(token, &inetaddr) == 0) goto cidrerror; #else addr = inet_addr(token); if (addr == (in_addr_t) (-1)) goto cidrerror; #endif if (sCount == 1) { token[sPos] = '/'; } } else { cidrerror: if (isMaster) Error("invalid ACL CIDR notation `%s' [%s:%d]", token, file, line); return; } isCIDR = 1; } /* ok...either a hostname or CIDR notation */ if ((pa = (ACCESS *)calloc(1, sizeof(ACCESS))) == (ACCESS *)0) OutOfMem(); pa->ctrust = trust; pa->isCIDR = isCIDR; if ((pa->pcwho = StrDup(token)) == (char *)0) OutOfMem(); for (ppa = &(parserAccessTemp->access); *ppa != (ACCESS *)0; ppa = &((*ppa)->pACnext)) { if ((*ppa)->ctrust == pa->ctrust && (*ppa)->isCIDR == pa->isCIDR && strcasecmp((*ppa)->pcwho, pa->pcwho) == 0) { /* already exists, so skip it */ DestroyAccessList(pa); break; } } if (*ppa == (ACCESS *)0) *ppa = pa; /* add to end of list */ } } void AccessItemAllowed(char *id) { CONDDEBUG((1, "AccessItemAllowed(%s) [%s:%d]", id, file, line)); AccessProcessACL('a', id); } void AccessItemRejected(char *id) { CONDDEBUG((1, "AccessItemRejected(%s) [%s:%d]", id, file, line)); AccessProcessACL('r', id); } void AccessItemTrusted(char *id) { CONDDEBUG((1, "AccessItemTrusted(%s) [%s:%d]", id, file, line)); AccessProcessACL('t', id); } /* 'config' handling */ CONFIG *parserConfigTemp = (CONFIG *)0; void DestroyConfig(CONFIG *c) { if (c == (CONFIG *)0) return; if (c->logfile != (char *)0) free(c->logfile); if (c->passwdfile != (char *)0) free(c->passwdfile); if (c->primaryport != (char *)0) free(c->primaryport); if (c->secondaryport != (char *)0) free(c->secondaryport); if (c->unifiedlog != (char *)0) free(c->unifiedlog); #if HAVE_OPENSSL if (c->sslcredentials != (char *)0) free(c->sslcredentials); if (c->sslcacertificatefile != (char *)0) free(c->sslcacertificatefile); #endif free(c); } void ConfigBegin(char *id) { CONDDEBUG((1, "ConfigBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { if (isMaster) Error("empty config name [%s:%d]", file, line); return; } if (parserConfigTemp != (CONFIG *)0) DestroyConfig(parserConfigTemp); if ((parserConfigTemp = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); parserConfigTemp->name = AllocString(); BuildString(id, parserConfigTemp->name); } void ConfigEnd(void) { CONDDEBUG((1, "ConfigEnd() [%s:%d]", file, line)); if (parserConfigTemp == (CONFIG *)0) return; if (parserConfigTemp->name->used > 1) { if ((parserConfigTemp->name->string[0] == '*' && parserConfigTemp->name->string[1] == '\000') || IsMe(parserConfigTemp->name->string)) { /* go through and copy over any items seen */ if (parserConfigTemp->logfile != (char *)0) { if (pConfig->logfile != (char *)0) free(pConfig->logfile); pConfig->logfile = parserConfigTemp->logfile; parserConfigTemp->logfile = (char *)0; } if (parserConfigTemp->passwdfile != (char *)0) { if (pConfig->passwdfile != (char *)0) free(pConfig->passwdfile); pConfig->passwdfile = parserConfigTemp->passwdfile; parserConfigTemp->passwdfile = (char *)0; } if (parserConfigTemp->unifiedlog != (char *)0) { if (pConfig->unifiedlog != (char *)0) free(pConfig->unifiedlog); pConfig->unifiedlog = parserConfigTemp->unifiedlog; parserConfigTemp->unifiedlog = (char *)0; } if (parserConfigTemp->primaryport != (char *)0) { if (pConfig->primaryport != (char *)0) free(pConfig->primaryport); pConfig->primaryport = parserConfigTemp->primaryport; parserConfigTemp->primaryport = (char *)0; } if (parserConfigTemp->defaultaccess != '\000') pConfig->defaultaccess = parserConfigTemp->defaultaccess; if (parserConfigTemp->autocomplete != FLAGUNKNOWN) pConfig->autocomplete = parserConfigTemp->autocomplete; if (parserConfigTemp->daemonmode != FLAGUNKNOWN) pConfig->daemonmode = parserConfigTemp->daemonmode; if (parserConfigTemp->redirect != FLAGUNKNOWN) pConfig->redirect = parserConfigTemp->redirect; if (parserConfigTemp->loghostnames != FLAGUNKNOWN) pConfig->loghostnames = parserConfigTemp->loghostnames; if (parserConfigTemp->reinitcheck != 0) pConfig->reinitcheck = parserConfigTemp->reinitcheck; if (parserConfigTemp->initdelay != 0) pConfig->initdelay = parserConfigTemp->initdelay; if (parserConfigTemp->secondaryport != (char *)0) { if (pConfig->secondaryport != (char *)0) free(pConfig->secondaryport); pConfig->secondaryport = parserConfigTemp->secondaryport; parserConfigTemp->secondaryport = (char *)0; } #if HAVE_OPENSSL if (parserConfigTemp->sslcredentials != (char *)0) { if (pConfig->sslcredentials != (char *)0) free(pConfig->sslcredentials); pConfig->sslcredentials = parserConfigTemp->sslcredentials; parserConfigTemp->sslcredentials = (char *)0; } if (parserConfigTemp->sslcacertificatefile != (char *)0) { if (pConfig->sslcacertificatefile != (char *)0) free(pConfig->sslcacertificatefile); pConfig->sslcacertificatefile = parserConfigTemp->sslcacertificatefile; parserConfigTemp->sslcacertificatefile = (char *)0; } if (parserConfigTemp->sslrequired != FLAGUNKNOWN) pConfig->sslrequired = parserConfigTemp->sslrequired; if (parserConfigTemp->sslreqclientcert != FLAGUNKNOWN) pConfig->sslreqclientcert = parserConfigTemp->sslreqclientcert; #endif #if HAVE_SETPROCTITLE if (parserConfigTemp->setproctitle != FLAGUNKNOWN) pConfig->setproctitle = parserConfigTemp->setproctitle; #endif } } DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } void ConfigAbort(void) { CONDDEBUG((1, "ConfigAbort() [%s:%d]", file, line)); if (parserConfigTemp == (CONFIG *)0) return; DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } void ConfigDestroy(void) { CONDDEBUG((1, "ConfigDestroy() [%s:%d]", file, line)); if (parserConfigTemp == (CONFIG *)0) return; DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } void ConfigItemDefaultaccess(char *id) { CONDDEBUG((1, "ConfigItemDefaultaccess(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { parserConfigTemp->defaultaccess = '\000'; return; } if (strcasecmp("allowed", id) == 0) parserConfigTemp->defaultaccess = 'a'; else if (strcasecmp("rejected", id) == 0) parserConfigTemp->defaultaccess = 'r'; else if (strcasecmp("trusted", id) == 0) parserConfigTemp->defaultaccess = 't'; else { if (isMaster) Error("invalid access type `%s' [%s:%d]", id, file, line); } } #if HAVE_FREEIPMI void ConsoleItemIpmiPrivLevel(char *id) { CONDDEBUG((1, "ConsoleItemIpmiPrivLevel(%s) [%s:%d]", id, file, line)); ProcessIpmiPrivLevel(parserConsoleTemp, id); } #endif /*freeipmi */ void ConfigItemAutocomplete(char *id) { CONDDEBUG((1, "ConfigItemAutocomplete(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserConfigTemp->autocomplete)); } void ConfigItemDaemonmode(char *id) { CONDDEBUG((1, "ConfigItemDaemonmode(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserConfigTemp->daemonmode)); } void ConfigItemLogfile(char *id) { CONDDEBUG((1, "ConfigItemLogfile(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->logfile != (char *)0) free(parserConfigTemp->logfile); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->logfile = (char *)0; return; } if ((parserConfigTemp->logfile = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemPasswordfile(char *id) { CONDDEBUG((1, "ConfigItemPasswordfile(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->passwdfile != (char *)0) free(parserConfigTemp->passwdfile); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->passwdfile = (char *)0; return; } if ((parserConfigTemp->passwdfile = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemUnifiedlog(char *id) { CONDDEBUG((1, "ConfigItemUnifiedlog(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->unifiedlog != (char *)0) free(parserConfigTemp->unifiedlog); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->unifiedlog = (char *)0; return; } if ((parserConfigTemp->unifiedlog = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemPrimaryport(char *id) { CONDDEBUG((1, "ConfigItemPrimaryport(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->primaryport != (char *)0) free(parserConfigTemp->primaryport); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->primaryport = (char *)0; return; } if ((parserConfigTemp->primaryport = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemRedirect(char *id) { CONDDEBUG((1, "ConfigItemRedirect(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserConfigTemp->redirect)); } void ConfigItemLoghostnames(char *id) { CONDDEBUG((1, "ConfigItemLoghostnames(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserConfigTemp->loghostnames)); } void ConfigItemReinitcheck(char *id) { char *p; CONDDEBUG((1, "ConfigItemReinitcheck(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->reinitcheck = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid reinitcheck value `%s' [%s:%d]", id, file, line); return; } parserConfigTemp->reinitcheck = atoi(id); } void ConfigItemInitdelay(char *id) { char *p; CONDDEBUG((1, "ConfigItemInitdelay(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->initdelay = 0; return; } for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid initdelay value `%s' [%s:%d]", id, file, line); return; } parserConfigTemp->initdelay = atoi(id); } void ConfigItemSecondaryport(char *id) { CONDDEBUG((1, "ConfigItemSecondaryport(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->secondaryport != (char *)0) free(parserConfigTemp->secondaryport); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->secondaryport = (char *)0; return; } if ((parserConfigTemp->secondaryport = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemSslcredentials(char *id) { CONDDEBUG((1, "ConfigItemSslcredentials(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL if (parserConfigTemp->sslcredentials != (char *)0) free(parserConfigTemp->sslcredentials); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->sslcredentials = (char *)0; return; } if ((parserConfigTemp->sslcredentials = StrDup(id)) == (char *)0) OutOfMem(); #else if (isMaster) Error ("sslcredentials ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslcacertificatefile(char *id) { CONDDEBUG((1, "ConfigItemSslcacertificatefile(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL if (parserConfigTemp->sslcacertificatefile != (char *)0) free(parserConfigTemp->sslcacertificatefile); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->sslcacertificatefile = (char *)0; return; } if ((parserConfigTemp->sslcacertificatefile = StrDup(id)) == (char *)0) OutOfMem(); #else if (isMaster) Error ("sslcacertificatefile ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslrequired(char *id) { CONDDEBUG((1, "ConfigItemSslrequired(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL ProcessYesNo(id, &(parserConfigTemp->sslrequired)); #else if (isMaster) Error ("sslrequired ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslreqclientcert(char *id) { CONDDEBUG((1, "ConfigItemSslreqclientcert(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL ProcessYesNo(id, &(parserConfigTemp->sslreqclientcert)); #else if (isMaster) Error ("sslreqclientcert ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSetproctitle(char *id) { CONDDEBUG((1, "ConfigItemSetproctitle(%s) [%s:%d]", id, file, line)); #if HAVE_SETPROCTITLE ProcessYesNo(id, &(parserConfigTemp->setproctitle)); #else if (isMaster) Error ("setproctitle ignored - operating system support does not exist [%s:%d]", file, line); #endif } /* task parsing */ TASKS *parserTask; void TaskBegin(char *id) { CONDDEBUG((1, "TaskBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000' || id[1] != '\000' || ((id[0] < '0' || id[0] > '9') && (id[0] < 'a' || id[0] > 'z'))) { if (isMaster) Error("invalid task id `%s' [%s:%d]", id, file, line); } else { if (parserTask == (TASKS *)0) { if ((parserTask = (TASKS *)calloc(1, sizeof(TASKS))) == (TASKS *)0) OutOfMem(); parserTask->cmd = AllocString(); parserTask->descr = AllocString(); } if (taskSubst == (SUBST *)0) { if ((taskSubst = (SUBST *)calloc(1, sizeof(SUBST))) == (SUBST *)0) OutOfMem(); taskSubst->value = &SubstValue; taskSubst->token = &SubstToken; } BuildString((char *)0, parserTask->cmd); BuildString((char *)0, parserTask->descr); parserTask->confirm = FLAGFALSE; parserTask->id = id[0]; } } void TaskEnd(void) { TASKS *t; TASKS **prev; CONDDEBUG((1, "TaskEnd() [%s:%d]", file, line)); /* skip this if we've marked it that way or if there is no command to run */ if (parserTask == (TASKS *)0 || parserTask->id == ' ' || parserTask->cmd->used <= 1) return; /* create an ordered list */ prev = &taskList; t = taskList; while (t != (TASKS *)0 && t->id < parserTask->id) { prev = &(t->next); t = t->next; } *prev = parserTask; if (t != (TASKS *)0 && parserTask->id == t->id) { parserTask->next = t->next; DestroyTask(t); } else { parserTask->next = t; } parserTask = (TASKS *)0; } void TaskAbort(void) { CONDDEBUG((1, "TaskAbort() [%s:%d]", file, line)); if (parserTask == (TASKS *)0 || parserTask->id == ' ') return; parserTask->id = ' '; } void TaskDestroy(void) { CONDDEBUG((1, "TaskDestroy() [%s:%d]", file, line)); if (parserTask != (TASKS *)0) { DestroyTask(parserTask); parserTask = (TASKS *)0; } } void TaskItemRunas(char *id) { CONDDEBUG((1, "TaskItemRunas(%s) [%s:%d]", id, file, line)); if (parserTask == (TASKS *)0 || parserTask->id == ' ') return; ProcessUidGid(&(parserTask->uid), &(parserTask->gid), id); } void TaskItemSubst(char *id) { CONDDEBUG((1, "TaskItemSubst(%s) [%s:%d]", id, file, line)); if (parserTask == (TASKS *)0 || parserTask->id == ' ') return; ProcessSubst(taskSubst, (char **)0, &(parserTask->subst), "subst", id); } void TaskItemCmd(char *id) { CONDDEBUG((1, "TaskItemCmd(%s) [%s:%d]", id, file, line)); if (parserTask == (TASKS *)0 || parserTask->id == ' ') return; BuildString((char *)0, parserTask->cmd); if ((id == (char *)0) || (*id == '\000')) return; BuildString(id, parserTask->cmd); } void TaskItemDescr(char *id) { CONDDEBUG((1, "TaskItemDescr(%s) [%s:%d]", id, file, line)); if (parserTask == (TASKS *)0 || parserTask->id == ' ') return; BuildString((char *)0, parserTask->descr); if ((id == (char *)0) || (*id == '\000')) return; BuildString(id, parserTask->descr); } void TaskItemConfirm(char *id) { CONDDEBUG((1, "TaskItemConfirm(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserTask->confirm)); } /* now all the real nitty-gritty bits for making things work */ ITEM keyTask[] = { {"cmd", TaskItemCmd}, {"confirm", TaskItemConfirm}, {"description", TaskItemDescr}, {"runas", TaskItemRunas}, {"subst", TaskItemSubst}, {(char *)0, (void *)0} }; ITEM keyBreak[] = { {"confirm", BreakItemConfirm}, {"delay", BreakItemDelay}, {"string", BreakItemString}, {(char *)0, (void *)0} }; ITEM keyGroup[] = { {"users", GroupItemUsers}, {(char *)0, (void *)0} }; ITEM keyDefault[] = { {"baud", DefaultItemBaud}, {"break", DefaultItemBreak}, {"breaklist", DefaultItemBreaklist}, {"device", DefaultItemDevice}, {"devicesubst", DefaultItemDevicesubst}, {"exec", DefaultItemExec}, {"execrunas", DefaultItemExecrunas}, {"execsubst", DefaultItemExecsubst}, /* {"flow", DefaultItemFlow}, */ {"host", DefaultItemHost}, {"idlestring", DefaultItemIdlestring}, {"idletimeout", DefaultItemIdletimeout}, {"include", DefaultItemInclude}, {"initcmd", DefaultItemInitcmd}, {"initrunas", DefaultItemInitrunas}, {"initspinmax", DefaultItemInitspinmax}, {"initspintimer", DefaultItemInitspintimer}, {"initsubst", DefaultItemInitsubst}, {"logfile", DefaultItemLogfile}, {"logfilemax", DefaultItemLogfilemax}, {"master", DefaultItemMaster}, {"motd", DefaultItemMOTD}, {"options", DefaultItemOptions}, {"parity", DefaultItemParity}, {"port", DefaultItemPort}, {"portbase", DefaultItemPortbase}, {"portinc", DefaultItemPortinc}, {"protocol", DefaultItemProtocol}, {"replstring", DefaultItemReplstring}, {"ro", DefaultItemRo}, {"rw", DefaultItemRw}, {"tasklist", DefaultItemTasklist}, {"timestamp", DefaultItemTimestamp}, {"type", DefaultItemType}, {"uds", DefaultItemUds}, {"udssubst", DefaultItemUdssubst}, #if HAVE_FREEIPMI {"ipmiciphersuite", DefaultItemIpmiCipherSuite}, {"ipmikg", DefaultItemIpmiKG}, {"ipmiprivlevel", DefaultItemIpmiPrivLevel}, {"ipmiworkaround", DefaultItemIpmiWorkaround}, {"password", DefaultItemPassword}, {"username", DefaultItemUsername}, #endif {(char *)0, (void *)0} }; ITEM keyConsole[] = { {"aliases", ConsoleItemAliases}, {"baud", ConsoleItemBaud}, {"break", ConsoleItemBreak}, {"breaklist", ConsoleItemBreaklist}, {"device", ConsoleItemDevice}, {"devicesubst", ConsoleItemDevicesubst}, {"exec", ConsoleItemExec}, {"execrunas", ConsoleItemExecrunas}, {"execsubst", ConsoleItemExecsubst}, /* {"flow", ConsoleItemFlow}, */ {"host", ConsoleItemHost}, {"idlestring", ConsoleItemIdlestring}, {"idletimeout", ConsoleItemIdletimeout}, {"include", ConsoleItemInclude}, {"initcmd", ConsoleItemInitcmd}, {"initrunas", ConsoleItemInitrunas}, {"initspinmax", ConsoleItemInitspinmax}, {"initspintimer", ConsoleItemInitspintimer}, {"initsubst", ConsoleItemInitsubst}, {"logfile", ConsoleItemLogfile}, {"logfilemax", ConsoleItemLogfilemax}, {"master", ConsoleItemMaster}, {"motd", ConsoleItemMOTD}, {"options", ConsoleItemOptions}, {"parity", ConsoleItemParity}, {"port", ConsoleItemPort}, {"portbase", ConsoleItemPortbase}, {"portinc", ConsoleItemPortinc}, {"protocol", ConsoleItemProtocol}, {"replstring", ConsoleItemReplstring}, {"ro", ConsoleItemRo}, {"rw", ConsoleItemRw}, {"tasklist", ConsoleItemTasklist}, {"timestamp", ConsoleItemTimestamp}, {"type", ConsoleItemType}, {"uds", ConsoleItemUds}, {"udssubst", ConsoleItemUdssubst}, #if HAVE_FREEIPMI {"ipmiciphersuite", ConsoleItemIpmiCipherSuite}, {"ipmikg", ConsoleItemIpmiKG}, {"ipmiprivlevel", ConsoleItemIpmiPrivLevel}, {"ipmiworkaround", ConsoleItemIpmiWorkaround}, {"password", ConsoleItemPassword}, {"username", ConsoleItemUsername}, #endif {(char *)0, (void *)0} }; ITEM keyAccess[] = { {"admin", AccessItemAdmin}, {"allowed", AccessItemAllowed}, {"include", AccessItemInclude}, {"limited", AccessItemLimited}, {"rejected", AccessItemRejected}, {"trusted", AccessItemTrusted}, {(char *)0, (void *)0} }; ITEM keyConfig[] = { {"autocomplete", ConfigItemAutocomplete}, {"defaultaccess", ConfigItemDefaultaccess}, {"daemonmode", ConfigItemDaemonmode}, {"initdelay", ConfigItemInitdelay}, {"logfile", ConfigItemLogfile}, {"loghostnames", ConfigItemLoghostnames}, {"passwdfile", ConfigItemPasswordfile}, {"primaryport", ConfigItemPrimaryport}, {"redirect", ConfigItemRedirect}, {"reinitcheck", ConfigItemReinitcheck}, {"secondaryport", ConfigItemSecondaryport}, {"setproctitle", ConfigItemSetproctitle}, {"sslcredentials", ConfigItemSslcredentials}, {"sslcacertificatefile", ConfigItemSslcacertificatefile}, {"sslrequired", ConfigItemSslrequired}, {"sslreqclientcert", ConfigItemSslreqclientcert}, {"unifiedlog", ConfigItemUnifiedlog}, {(char *)0, (void *)0} }; SECTION sections[] = { {"task", TaskBegin, TaskEnd, TaskAbort, TaskDestroy, keyTask}, {"break", BreakBegin, BreakEnd, BreakAbort, BreakDestroy, keyBreak}, {"group", GroupBegin, GroupEnd, GroupAbort, GroupDestroy, keyGroup}, {"default", DefaultBegin, DefaultEnd, DefaultAbort, DefaultDestroy, keyDefault}, {"console", ConsoleBegin, ConsoleEnd, ConsoleAbort, ConsoleDestroy, keyConsole}, {"access", AccessBegin, AccessEnd, AccessAbort, AccessDestroy, keyAccess}, {"config", ConfigBegin, ConfigEnd, ConfigAbort, ConfigDestroy, keyConfig}, {(char *)0, (void *)0, (void *)0, (void *)0, (void *)0} }; void ReadCfg(char *filename, FILE *fp) { int i; #if HAVE_DMALLOC && DMALLOC_MARK_READCFG unsigned long dmallocMarkReadCfg = 0; #endif #if HAVE_DMALLOC && DMALLOC_MARK_READCFG dmallocMarkReadCfg = dmalloc_mark(); #endif isStartup = (pGroups == (GRPENT *)0 && pRCList == (REMOTE *)0); /* initialize the break lists */ for (i = 0; i < BREAKLISTSIZE; i++) { if (breakList[i].seq == (STRING *)0) { breakList[i].seq = AllocString(); } else { BuildString((char *)0, breakList[i].seq); } breakList[i].delay = BREAKDELAYDEFAULT; breakList[i].confirm = FLAGFALSE; } BuildString("\\z", breakList[0].seq); BuildString("\\r~^b", breakList[1].seq); BuildString("#.", breakList[2].seq); BuildString("\\r\\d~\\d^b", breakList[3].seq); breakList[3].delay = 600; /* initialize the user list */ DestroyUserList(); /* initialize the task list */ DestroyTaskList(); /* initialize the config set */ if (pConfig != (CONFIG *)0) { DestroyConfig(pConfig); pConfig = (CONFIG *)0; } if ((pConfig = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); /* initialize the substition bits */ InitSubstCallback(); /* ready to read in the data */ ParseFile(filename, fp, 0); #if HAVE_DMALLOC && DMALLOC_MARK_READCFG CONDDEBUG((1, "ReadCfg(): dmalloc / MarkReadCfg")); dmalloc_log_changed(dmallocMarkReadCfg, 1, 0, 1); #endif } void ReReadCfg(int fd, int msfd) { FILE *fpConfig; if ((FILE *)0 == (fpConfig = fopen(pcConfig, "r"))) { if (isMaster) Error("ReReadCfg(): fopen(%s): %s", pcConfig, strerror(errno)); return; } FD_ZERO(&rinit); FD_ZERO(&winit); if (fd > 0) { FD_SET(fd, &rinit); if (maxfd < fd + 1) maxfd = fd + 1; } ReadCfg(pcConfig, fpConfig); fclose(fpConfig); if (pGroups == (GRPENT *)0 && pRCList == (REMOTE *)0) { if (isMaster) { Error("no consoles found in configuration file"); kill(thepid, SIGTERM); /* shoot myself in the head */ return; } else { Msg("no consoles to manage in child process after reconfiguration - child exiting"); DeUtmp((GRPENT *)0, fd); } } /* check for changes to master & child values */ if (optConf->logfile == (char *)0) { char *p; if (pConfig->logfile == (char *)0) p = defConfig.logfile; else p = pConfig->logfile; if (config->logfile == (char *)0 || strcmp(p, config->logfile) != 0) { if (config->logfile != (char *)0) free(config->logfile); if ((config->logfile = StrDup(p)) == (char *)0) OutOfMem(); ReopenLogfile(); } } /* check for changes to unifiedlog...this might (and does) have * a default of (char *)0, so it's slightly different than the * other code that does similar stuff (like logfile) */ if (optConf->unifiedlog == (char *)0) { char *p; if (pConfig->unifiedlog == (char *)0) p = defConfig.unifiedlog; else p = pConfig->unifiedlog; if (config->unifiedlog == (char *)0 || p == (char *)0 || strcmp(p, config->unifiedlog) != 0) { if (config->unifiedlog != (char *)0) free(config->unifiedlog); if (p == (char *)0) config->unifiedlog = p; else if ((config->unifiedlog = StrDup(p)) == (char *)0) OutOfMem(); ReopenUnifiedlog(); } } if (optConf->defaultaccess == '\000') { if (pConfig->defaultaccess == '\000') config->defaultaccess = defConfig.defaultaccess; else if (pConfig->defaultaccess != config->defaultaccess) config->defaultaccess = pConfig->defaultaccess; /* gets used below by SetDefAccess() */ } if (optConf->passwdfile == (char *)0) { char *p; if (pConfig->passwdfile == (char *)0) p = defConfig.passwdfile; else p = pConfig->passwdfile; if (config->passwdfile == (char *)0 || strcmp(p, config->passwdfile) != 0) { if (config->passwdfile != (char *)0) free(config->passwdfile); if ((config->passwdfile = StrDup(p)) == (char *)0) OutOfMem(); /* gets used on-the-fly */ } } if (optConf->redirect == FLAGUNKNOWN) { if (pConfig->redirect == FLAGUNKNOWN) config->redirect = defConfig.redirect; else if (pConfig->redirect != config->redirect) config->redirect = pConfig->redirect; /* gets used on-the-fly */ } if (optConf->autocomplete == FLAGUNKNOWN) { if (pConfig->autocomplete == FLAGUNKNOWN) config->autocomplete = defConfig.autocomplete; else if (pConfig->autocomplete != config->autocomplete) config->autocomplete = pConfig->autocomplete; /* gets used on-the-fly */ } if (optConf->loghostnames == FLAGUNKNOWN) { if (pConfig->loghostnames == FLAGUNKNOWN) config->loghostnames = defConfig.loghostnames; else if (pConfig->loghostnames != config->loghostnames) config->loghostnames = pConfig->loghostnames; /* gets used on-the-fly */ } if (optConf->reinitcheck == 0) { if (pConfig->reinitcheck == 0) config->reinitcheck = defConfig.reinitcheck; else if (pConfig->reinitcheck != config->reinitcheck) config->reinitcheck = pConfig->reinitcheck; /* gets used on-the-fly */ } if (optConf->initdelay == 0) { if (pConfig->initdelay == 0) config->initdelay = defConfig.initdelay; else if (pConfig->initdelay != config->initdelay) config->initdelay = pConfig->initdelay; /* gets used on-the-fly */ } #if HAVE_OPENSSL if (optConf->sslrequired == FLAGUNKNOWN) { if (pConfig->sslrequired == FLAGUNKNOWN) config->sslrequired = defConfig.sslrequired; else if (pConfig->sslrequired != config->sslrequired) config->sslrequired = pConfig->sslrequired; /* gets used on-the-fly */ } #endif /* if no one can use us we need to come up with a default */ if (pACList == (ACCESS *)0) #if USE_IPV6 SetDefAccess(); #else SetDefAccess(myAddrs, myHostname); #endif if (isMaster) { GRPENT *pGE; /* process any new options (command-line flags might have * overridden things, so just need to check on new pConfig * values for changes). * the checks here produce warnings, and are inside the * isMaster check so it only pops out once. */ if (optConf->daemonmode == FLAGUNKNOWN) { if (pConfig->daemonmode == FLAGUNKNOWN) pConfig->daemonmode = defConfig.daemonmode; if (pConfig->daemonmode != config->daemonmode) { config->daemonmode = pConfig->daemonmode; Msg("warning: `daemonmode' config option changed - you must restart for it to take effect"); } } #if !USE_UNIX_DOMAIN_SOCKETS if (optConf->primaryport == (char *)0) { char *p; if (pConfig->primaryport == (char *)0) p = defConfig.primaryport; else p = pConfig->primaryport; if (config->primaryport == (char *)0 || strcmp(p, config->primaryport) != 0) { if (config->primaryport != (char *)0) free(config->primaryport); if ((config->primaryport = StrDup(p)) == (char *)0) OutOfMem(); Msg("warning: `primaryport' config option changed - you must restart for it to take effect"); } } if (optConf->secondaryport == (char *)0) { char *p; if (pConfig->secondaryport == (char *)0) p = defConfig.secondaryport; else p = pConfig->secondaryport; if (config->secondaryport == (char *)0 || strcmp(p, config->secondaryport) != 0) { if (config->secondaryport != (char *)0) free(config->secondaryport); if ((config->secondaryport = StrDup(p)) == (char *)0) OutOfMem(); Msg("warning: `secondaryport' config option changed - you must restart for it to take effect"); } } #endif #if HAVE_OPENSSL if (optConf->sslcredentials == (char *)0) { if (pConfig->sslcredentials == (char *)0) { if (config->sslcredentials != (char *)0) { free(config->sslcredentials); config->sslcredentials = (char *)0; Msg("warning: `sslcredentials' config option changed - you must restart for it to take effect"); } } else { if (config->sslcredentials == (char *)0 || strcmp(pConfig->sslcredentials, config->sslcredentials) != 0) { if (config->sslcredentials != (char *)0) free(config->sslcredentials); if ((config->sslcredentials = StrDup(pConfig->sslcredentials)) == (char *)0) OutOfMem(); Msg("warning: `sslcredentials' config option changed - you must restart for it to take effect"); } } } if (optConf->sslcacertificatefile == (char *)0) { if (pConfig->sslcacertificatefile == (char *)0) { if (config->sslcacertificatefile != (char *)0) { free(config->sslcacertificatefile); config->sslcacertificatefile = (char *)0; Msg("warning: `sslcacertificatefile' config option changed - you must restart for it to take effect"); } } else { if (config->sslcacertificatefile == (char *)0 || strcmp(pConfig->sslcacertificatefile, config->sslcacertificatefile) != 0) { if (config->sslcacertificatefile != (char *)0) free(config->sslcacertificatefile); if ((config->sslcacertificatefile = StrDup(pConfig->sslcacertificatefile)) == (char *)0) OutOfMem(); Msg("warning: `sslcacertificatefile' config option changed - you must restart for it to take effect"); } } } if (optConf->sslreqclientcert == FLAGUNKNOWN) { if (pConfig->sslreqclientcert == FLAGUNKNOWN) { if (config->sslreqclientcert != defConfig.sslreqclientcert) { Msg("warning: `sslreqclientcert' config option changed - you must restart for it to take effect"); config->sslreqclientcert = defConfig.sslreqclientcert; } } else if (config->sslreqclientcert != pConfig->sslreqclientcert) { Msg("warning: `sslreqclientcert' config option changed - you must restart for it to take effect"); config->sslreqclientcert = pConfig->sslreqclientcert; } } #endif #if HAVE_SETPROCTITLE if (optConf->setproctitle == FLAGUNKNOWN) { if (pConfig->setproctitle == FLAGUNKNOWN) pConfig->setproctitle = defConfig.setproctitle; if (pConfig->setproctitle != config->setproctitle) { config->setproctitle = pConfig->setproctitle; Msg("warning: `setproctitle' config option changed - you must restart for it to take effect"); } } #endif /* spawn all the children, so fix kids has an initial pid */ for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (pGE->imembers == 0 || pGE->pid != -1) continue; Spawn(pGE, msfd); Verbose("group #%d pid %lu on port %hu", pGE->id, (unsigned long)pGE->pid, pGE->port); } if (fVerbose) { ACCESS *pACtmp; for (pACtmp = pACList; pACtmp != (ACCESS *)0; pACtmp = pACtmp->pACnext) { Verbose("access type `%c' for `%s'", pACtmp->ctrust, pACtmp->pcwho); } } pRCUniq = FindUniq(pRCList); /* output unique console server peers? */ if (fVerbose) { REMOTE *pRC; for (pRC = pRCUniq; (REMOTE *)0 != pRC; pRC = pRC->pRCuniq) { Verbose("peer server on `%s'", pRC->rhost); } } } #if HAVE_SETPROCTITLE if (config->setproctitle == FLAGTRUE) { if (isMaster) { REMOTE *pRC; GRPENT *pGE; int local = 0, remote = 0; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) local += pGE->imembers; for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) remote++; setproctitle("master: port %hu, %d local, %d remote", bindPort, local, remote); } else setproctitle("group %u: port %hu, %d %s", pGroups->id, pGroups->port, pGroups->imembers, pGroups->imembers == 1 ? "console" : "consoles"); } #endif } conserver-8.2.4/conserver/readcfg.h000066400000000000000000000040111344660520400173050ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ #define BREAKDELAYDEFAULT 250 #define BREAKLISTSIZE 35 /* ('z'-('a'-1))+('9'-('1'-1)) */ #define BREAKALPHAOFFSET 39 /* ('a'-('9'+1)) */ typedef struct config { STRING *name; FLAG autocomplete; char defaultaccess; FLAG daemonmode; char *logfile; char *passwdfile; char *primaryport; FLAG redirect; FLAG loghostnames; int reinitcheck; char *secondaryport; char *unifiedlog; int initdelay; #if HAVE_SETPROCTITLE FLAG setproctitle; #endif #if HAVE_OPENSSL char *sslcredentials; FLAG sslrequired; FLAG sslreqclientcert; char *sslcacertificatefile; #endif } CONFIG; typedef struct breaks { STRING *seq; int delay; FLAG confirm; } BREAKS; typedef struct tasks { char id; STRING *cmd; STRING *descr; uid_t uid; gid_t gid; char *subst; FLAG confirm; struct tasks *next; } TASKS; extern NAMES *userList; /* user list */ extern GRPENT *pGroups; /* group info */ extern REMOTE *pRCList; /* list of remote consoles we know about */ extern REMOTE *pRCUniq; /* list of uniq console servers */ extern ACCESS *pACList; /* `who do you love' (or trust) */ extern CONSENTUSERS *pADList; /* list of admin users */ extern CONSENTUSERS *pLUList; /* list of limited users */ extern BREAKS breakList[BREAKLISTSIZE]; /* list of break sequences */ extern TASKS *taskList; /* list of tasks */ extern SUBST *taskSubst; /* substitution function data for tasks */ extern CONFIG *pConfig; /* settings seen by config parser */ extern SUBST *substData; /* substitution function data */ extern void ReadCfg(char *, FILE *); extern void ReReadCfg(int, int); extern void DestroyBreakList(void); extern void InitBreakList(void); extern void DestroyTaskList(void); extern void DestroyUserList(void); extern void DestroyConfig(CONFIG *); extern NAMES *FindUserList(char *); extern NAMES *AddUserList(char *); extern CONSENT *FindConsoleName(CONSENT *, char *); conserver-8.2.4/conserver/version.h000066400000000000000000000011171344660520400174030ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ #define COPYRIGHT "@(#) Copyright 1990 The Ohio State University.\n\ @(#) Copyright 1992 Purdue Research Foundation.\n\ @(#) Copyright 1998 GNAC, Inc.\n\ @(#) Copyright 2000 conserver.com.\n\ All rights reserved.\n" #define VERSION_DATE "2019/03/26" #define VERSION_MAJOR 8 #define VERSION_MINOR 2 #define VERSION_REV 4 #define VERSION_TEXT "conserver.com version" #define VERSION_UINT (VERSION_MAJOR * 1000000 + VERSION_MINOR * 1000 + VERSION_REV) conserver-8.2.4/console/000077500000000000000000000000001344660520400152015ustar00rootroot00000000000000conserver-8.2.4/console/Makefile.in000066400000000000000000000026711344660520400172540ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sysconfdir = @sysconfdir@ mandir = @mandir@ ### Installation programs and flags INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s LN_S = @LN_S@ MKDIR = @MKDIR@ ### Compiler and link options CC = @CC@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" CPPFLAGS = -I.. -I$(top_srcdir) -I$(srcdir) -I$(top_srcdir)/conserver $(DEFS) @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below CONSOLE_OBJS = console.o getpassword.o readconf.o ../conserver/cutil.o CONSOLE_HDRS = ../config.h $(top_srcdir)/compat.h \ $(top_srcdir)/conserver/cutil.h \ $(top_srcdir)/conserver/version.h \ $(srcdir)/getpassword.h $(srcdir)/readconf.h ALL = console all: $(ALL) $(CONSOLE_OBJS): $(CONSOLE_HDRS) console: $(CONSOLE_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o console $(CONSOLE_OBJS) $(LIBS) ../conserver/cutil.o: ( cd ../conserver && $(MAKE) $(MAKE_FLAGS) cutil.o ) || exit 1; .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< clean: rm -f *~ *.o $(ALL) core distclean: clean rm -f Makefile install: console $(MKDIR) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) console $(DESTDIR)$(bindir) $(MKDIR) $(DESTDIR)$(mandir)/man1 $(INSTALL) -m 0644 console.man $(DESTDIR)$(mandir)/man1/console.1 .PHONY: clean distclean install conserver-8.2.4/console/console.c000066400000000000000000001756571344660520400170340ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #if HAVE_OPENSSL # include #endif #if HAVE_GSSAPI # include #endif #if USE_IPV6 # include # include #endif int fReplay = 0, fVersion = 0; int showExecData = 1; int chAttn = -1, chEsc = -1; unsigned short bindPort; CONSFILE *cfstdout; int disconnectCount = 0; STRING *execCmd = (STRING *)0; CONSFILE *execCmdFile = (CONSFILE *)0; pid_t execCmdPid = 0; CONSFILE *gotoConsole = (CONSFILE *)0; CONSFILE *prevConsole = (CONSFILE *)0; char *gotoName = (char *)0; char *prevName = (char *)0; CONFIG *optConf = (CONFIG *)0; CONFIG *config = (CONFIG *)0; FLAG interact = FLAGFALSE; unsigned int sversion = 0; #if defined(TIOCGWINSZ) struct winsize ws; #endif #if HAVE_OPENSSL SSL_CTX *ctx = (SSL_CTX *)0; void SetupSSL(void) { if (ctx == (SSL_CTX *)0) { char *ciphers; # if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); if (!SSL_library_init()) { Error("SSL library initialization failed"); Bye(EX_UNAVAILABLE); } # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ if ((ctx = SSL_CTX_new(TLS_method())) == (SSL_CTX *)0) { Error("Creating SSL context failed"); Bye(EX_UNAVAILABLE); } if (SSL_CTX_set_default_verify_paths(ctx) != 1) { Error("Could not load SSL default CA file and/or directory"); Bye(EX_UNAVAILABLE); } if (config->sslcacertificatefile != (char *)0 || config->sslcacertificatepath != (char *)0) { if (SSL_CTX_load_verify_locations (ctx, config->sslcacertificatefile, config->sslcacertificatepath) != 1) { if (config->sslcacertificatefile != (char *)0) Error("Could not setup ca certificate file to '%s'", config->sslcacertificatefile); if (config->sslcacertificatepath != (char *)0) Error("Could not setup ca certificate path to '%s'", config->sslcacertificatepath); Bye(EX_UNAVAILABLE); } } if (config->sslcredentials != (char *)0) { if (SSL_CTX_use_certificate_chain_file (ctx, config->sslcredentials) != 1) { Error("Could not load SSL certificate from '%s'", config->sslcredentials); Bye(EX_UNAVAILABLE); } if (SSL_CTX_use_PrivateKey_file (ctx, config->sslcredentials, SSL_FILETYPE_PEM) != 1) { Error("Could not SSL private key from '%s'", config->sslcredentials); Bye(EX_UNAVAILABLE); } ciphers = "ALL:!LOW:!EXP:!MD5:!aNULL:@STRENGTH"; } else { # if defined(REQ_SERVER_CERT) ciphers = "ALL:!LOW:!EXP:!MD5:!aNULL:@STRENGTH"; # else ciphers = "ALL:aNULL:!LOW:!EXP:!MD5:@STRENGTH" CIPHER_SEC0; # endif } SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback); SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) { Error("Setting SSL cipher list failed"); Bye(EX_UNAVAILABLE); } } } void AttemptSSL(CONSFILE *pcf) { SSL *ssl; if (ctx == (SSL_CTX *)0) { Error("WTF? The SSL context disappeared?!?!?"); Bye(EX_UNAVAILABLE); } if (!(ssl = SSL_new(ctx))) { Error("Couldn't create new SSL context"); Bye(EX_UNAVAILABLE); } FileSetSSL(pcf, ssl); SSL_set_fd(ssl, FileFDNum(pcf)); CONDDEBUG((1, "About to SSL_connect() on fd %d", FileFDNum(pcf))); if (SSL_connect(ssl) <= 0) { Error("SSL negotiation failed"); ERR_print_errors_fp(stderr); Bye(EX_UNAVAILABLE); } FileSetType(pcf, SSLSocket); CONDDEBUG((1, "SSL Connection: %s :: %s", SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl))); } #endif #if HAVE_GSSAPI gss_name_t gss_server_name = GSS_C_NO_NAME; gss_ctx_id_t secctx = GSS_C_NO_CONTEXT; gss_buffer_desc mytok = GSS_C_EMPTY_BUFFER; int CanGetGSSContext(const char *servername) { char namestr[128]; gss_buffer_desc namebuf, dbuf; OM_uint32 stmaj, stmin, mctx, dmin; snprintf(namestr, 128, "host@%s", servername); namebuf.value = namestr; namebuf.length = strlen(namestr) + 1; stmaj = gss_import_name(&stmin, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_server_name); /* XXX: handle error */ if (stmaj != GSS_S_COMPLETE) { Error("gss_import_name failed"); return 0; } secctx = GSS_C_NO_CONTEXT; mytok.length = 0; mytok.value = NULL; stmaj = gss_init_sec_context(&stmin, GSS_C_NO_CREDENTIAL, &secctx, gss_server_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &mytok, NULL, NULL); if (stmaj != GSS_S_COMPLETE && stmaj != GSS_S_CONTINUE_NEEDED) { gss_release_name(&stmin, &gss_server_name); return 0; } return mytok.length; } int AttemptGSSAPI(CONSFILE *pcf) { OM_uint32 stmaj, stmin; gss_buffer_desc servertok; char buf[1024]; int nr; int ret; FileSetQuoteIAC(pcf, FLAGFALSE); FileWrite(pcf, FLAGFALSE, mytok.value, mytok.length); FileSetQuoteIAC(pcf, FLAGTRUE); nr = FileRead(pcf, buf, sizeof(buf)); servertok.length = nr; servertok.value = buf; stmaj = gss_init_sec_context(&stmin, GSS_C_NO_CREDENTIAL, &secctx, gss_server_name, GSS_C_NULL_OID, GSS_C_MUTUAL_FLAG, 0, GSS_C_NO_CHANNEL_BINDINGS, &servertok, NULL, &mytok, NULL, NULL); gss_release_buffer(&stmin, &mytok); ret = (stmaj == GSS_S_COMPLETE); gss_release_name(&stmin, &gss_server_name); return ret; } #endif /* output a control (or plain) character as a UNIX user would expect it (ksb) */ static void PutCtlc(int c, FILE *fp) { if (0 != (0200 & c)) { putc('M', fp); putc('-', fp); c &= ~0200; } if (isprint(c)) { putc(c, fp); return; } putc('^', fp); if (c == 0177) { putc('?', fp); return; } putc(c + 0100, fp); } /* output a long message to the user */ static void Usage(int wantfull) { static char *full[] = { "7 strip the high bit off all console data", "a(A) attach politely (and replay last 20 lines)", "b(B) send broadcast message to all users (on master)", #if HAVE_OPENSSL "c cred load an SSL certificate and key from the PEM encoded file", #else "c cred ignored - encryption not compiled into code", #endif "C config override per-user config file", "d disconnect [user][@console]", "D enable debug output, sent to stderr", "e esc set the initial escape characters", #if HAVE_OPENSSL "E don't attempt encrypted connections", #else "E ignored - encryption not compiled into code", #endif "f(F) force read/write connection (and replay)", "h output this message", "i(I) display status info in machine-parseable form (on master)", "l user use username instead of current username", "M master master server to poll first", "n do not read system-wide config file", "p port port to connect to", "P display pids of daemon(s)", "q(Q) send a quit command to the (master) server", "r(R) display (master) daemon version (think 'r'emote version)", "s(S) spy on a console (and replay)", "t send a text message to [user][@console]", "u show users on the various consoles", #if HAVE_OPENSSL "U allow unencrypted connections if SSL not available", #else "U ignored - encryption not compiled into code", #endif "v be more verbose", "V show version information", "w(W) show who is on which console (on master)", "x examine ports and baud rates", "z(Z) cmd send a command to the (master) server (think 'z'ap)", (char *)0 }; fprintf(stderr, "usage: %s [generic-args] [-aAfFsS] [-e esc] console\n\ %s [generic-args] [-iIuwWx] [console]\n\ %s [generic-args] [-hPqQrRV] [-[bB] message] [-d [user][@console]]\n\ [-t [user][@console] message] [-[zZ] cmd]\n\n\ generic-args: [-7DEnUv] [-c cred] [-C config] [-M master]\n\ [-p port] [-l username]\n", progname, progname, progname); if (wantfull) { int i; fprintf(stderr, "\n"); for (i = 0; full[i] != (char *)0; i++) fprintf(stderr, "\t%s\n", full[i]); } } /* expain who we are and which revision we are (ksb) */ static void Version(void) { int i; static STRING *acA1 = (STRING *)0; static STRING *acA2 = (STRING *)0; char *optionlist[] = { #if HAVE_DMALLOC "dmalloc", #endif #if USE_LIBWRAP "libwrap", #endif #if HAVE_OPENSSL "openssl", #endif #if HAVE_GSSAPI "gssapi", #endif #if USE_UNIX_DOMAIN_SOCKETS "uds", #endif (char *)0 }; if (acA1 == (STRING *)0) acA1 = AllocString(); if (acA2 == (STRING *)0) acA2 = AllocString(); Msg(MyVersion()); #if USE_UNIX_DOMAIN_SOCKETS Msg("default socket directory `%s'", UDSDIR); #else Msg("default initial master server `%s'", MASTERHOST); Msg("default port referenced as `%s'", DEFPORT); #endif Msg("default escape sequence `%s%s'", FmtCtl(DEFATTN, acA1), FmtCtl(DEFESC, acA2)); Msg("default site-wide configuration in `%s'", CLIENTCONFIGFILE); Msg("default per-user configuration in `%s'", "$HOME/.consolerc"); BuildString((char *)0, acA1); if (optionlist[0] == (char *)0) BuildString("none", acA1); for (i = 0; optionlist[i] != (char *)0; i++) { if (i == 0) BuildString(optionlist[i], acA1); else { BuildString(", ", acA1); BuildString(optionlist[i], acA1); } } Msg("options: %s", acA1->string); #if HAVE_DMALLOC BuildString((char *)0, acA1); BuildStringChar('0' + DMALLOC_VERSION_MAJOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_MINOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_PATCH, acA1); # if defined(DMALLOC_VERSION_BETA) if (DMALLOC_VERSION_BETA != 0) { BuildString("-b", acA1); BuildStringChar('0' + DMALLOC_VERSION_BETA, acA1); } # endif Msg("dmalloc version: %s", acA1->string); #endif #if HAVE_OPENSSL Msg("openssl version: %s", OPENSSL_VERSION_TEXT); #endif Msg("built with `%s'", CONFIGINVOCATION); if (fVerbose) printf(COPYRIGHT); } /* convert text to control chars, we take `cat -v' style (ksb) * ^X (or ^x) contro-x * M-x x plus 8th bit * c a plain character */ static int ParseChar(char **ppcSrc, char *pcOut) { int cvt, n; char *pcScan = *ppcSrc; if ('M' == pcScan[0] && '-' == pcScan[1] && '\000' != pcScan[2]) { cvt = 0x80; pcScan += 2; } else { cvt = 0; } if ('\000' == *pcScan) { return 1; } if ('^' == (n = *pcScan++)) { if ('\000' == (n = *pcScan++)) { return 1; } if (islower(n)) { n = toupper(n); } if ('@' <= n && n <= '_') { cvt |= n - '@'; } else if ('?' == *pcScan) { cvt |= '\177'; } else { return 1; } } else { cvt |= n; } if ((char *)0 != pcOut) { *pcOut = cvt; } *ppcSrc = pcScan; return 0; } /* */ static void ValidateEsc(void) { unsigned char c1, c2; if (config->striphigh != FLAGTRUE) return; if (chAttn == -1 || chEsc == -1) { c1 = DEFATTN; c2 = DEFESC; } else { c1 = chAttn; c2 = chEsc; } if (c1 > 127 || c2 > 127) { Error("High-bit set in escape sequence: not allowed with -7"); Bye(EX_UNAVAILABLE); } } /* find the two characters that makeup the users escape sequence (ksb) */ static void ParseEsc(char *pcText) { char *pcTemp; char c1, c2; pcTemp = pcText; if (ParseChar(&pcTemp, &c1) || ParseChar(&pcTemp, &c2)) { Error("poorly formed escape sequence `%s\'", pcText); Bye(EX_UNAVAILABLE); } if ('\000' != *pcTemp) { Error("too many characters in new escape sequence at ...`%s\'", pcTemp); Bye(EX_UNAVAILABLE); } chAttn = c1; chEsc = c2; } /* set the port for socket connection (ksb) * return the fd for the new connection; if we can use the loopback, do * as a side effect we set ThisHost to a short name for this host */ CONSFILE * GetPort(char *pcToHost, unsigned short sPort) { int s; #if USE_IPV6 int error; char host[NI_MAXHOST]; char serv[NI_MAXSERV]; struct addrinfo *ai, *rp, hints; #elif USE_UNIX_DOMAIN_SOCKETS struct sockaddr_un port; static STRING *portPath = (STRING *)0; #else struct hostent *hp = (struct hostent *)0; struct sockaddr_in port; #endif #if HAVE_SETSOCKOPT int one = 1; #endif #if USE_IPV6 # if HAVE_MEMSET memset(&hints, 0, sizeof(hints)); # else bzero(&hints, sizeof(hints)); # endif #else # if HAVE_MEMSET memset((void *)(&port), '\000', sizeof(port)); # else bzero((char *)(&port), sizeof(port)); # endif #endif #if USE_IPV6 hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; snprintf(serv, sizeof(serv), "%hu", sPort); error = getaddrinfo(pcToHost, serv, &hints, &ai); if (error) { Error("getaddrinfo(%s): %s", pcToHost, gai_strerror(error)); return (CONSFILE *)0; } rp = ai; while (rp) { error = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); if (error) { continue; } CONDDEBUG((1, "GetPort: hostname=%s, ip=%s, port=%s", pcToHost, host, serv)); /* set up the socket to talk to the server for all consoles * (it will tell us who to talk to to get a real connection) */ s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (s != -1) { # if HAVE_SETSOCKOPT if (setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) goto fail; # endif if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) goto success; fail: close(s); } rp = rp->ai_next; } Error("Unable to connect to %s:%s", host, serv); return (CONSFILE *)0; success: freeaddrinfo(ai); #elif USE_UNIX_DOMAIN_SOCKETS if (portPath == (STRING *)0) portPath = AllocString(); BuildStringPrint(portPath, "%s/%hu", config->master, sPort); port.sun_family = AF_UNIX; if (portPath->used > sizeof(port.sun_path)) { Error("GetPort: path to socket too long: %s", portPath->string); return (CONSFILE *)0; } StrCpy(port.sun_path, portPath->string, sizeof(port.sun_path)); CONDDEBUG((1, "GetPort: socket=%s", port.sun_path)); /* set up the socket to talk to the server for all consoles * (it will tell us who to talk to to get a real connection) */ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { Error("socket(AF_UNIX,SOCK_STREAM): %s", strerror(errno)); return (CONSFILE *)0; } if (connect(s, (struct sockaddr *)(&port), sizeof(port)) < 0) { Error("connect(): %s: %s", port.sun_path, strerror(errno)); return (CONSFILE *)0; } #else # if HAVE_INET_ATON if (inet_aton(pcToHost, &(port.sin_addr)) == 0) # else port.sin_addr.s_addr = inet_addr(pcToHost); if ((in_addr_t) (-1) == port.sin_addr.s_addr) # endif { if ((struct hostent *)0 != (hp = gethostbyname(pcToHost))) { # if HAVE_MEMCPY memcpy((char *)&port.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length); # else bcopy((char *)hp->h_addr, (char *)&port.sin_addr.s_addr, hp->h_length); # endif } else { Error("gethostbyname(%s): %s", pcToHost, hstrerror(h_errno)); return (CONSFILE *)0; } } port.sin_port = sPort; port.sin_family = AF_INET; if (fDebug) { if ((struct hostent *)0 != hp && (char *)0 != hp->h_name) { CONDDEBUG((1, "GetPort: hostname=%s (%s), ip=%s, port=%hu", hp->h_name, pcToHost, inet_ntoa(port.sin_addr), ntohs(sPort))); } else { CONDDEBUG((1, "GetPort: hostname= (%s), ip=%s, port=%hu", pcToHost, inet_ntoa(port.sin_addr), ntohs(sPort))); } } /* set up the socket to talk to the server for all consoles * (it will tell us who to talk to to get a real connection) */ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { Error("socket(AF_INET,SOCK_STREAM): %s", strerror(errno)); return (CONSFILE *)0; } # if HAVE_SETSOCKOPT if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof(one)) < 0) { Error("setsockopt(SO_KEEPALIVE): %s", strerror(errno)); close(s); return (CONSFILE *)0; } # endif if (connect(s, (struct sockaddr *)(&port), sizeof(port)) < 0) { Error("connect(): %hu@%s: %s", ntohs(port.sin_port), pcToHost, strerror(errno)); close(s); return (CONSFILE *)0; } #endif return FileOpenFD(s, simpleSocket); } /* the next two routines assure that the users tty is in the * correct mode for us to do our thing */ static int screwy = 0; static struct termios o_tios; /* * show characters that are already tty processed, * and read characters before cononical processing * we really use cbreak at PUCC because we need even parity... */ static void C2Raw(void) { struct termios n_tios; if (!isatty(0) || 0 != screwy) return; if (0 != tcgetattr(0, &o_tios)) { Error("tcgetattr(0): %s", strerror(errno)); Bye(EX_UNAVAILABLE); } n_tios = o_tios; n_tios.c_iflag &= ~(INLCR | IGNCR | ICRNL | IUCLC | IXON); n_tios.c_oflag &= ~OPOST; n_tios.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN); n_tios.c_cc[VMIN] = 1; n_tios.c_cc[VTIME] = 0; if (0 != tcsetattr(0, TCSANOW, &n_tios)) { Error("tcsetattr(0, TCSANOW): %s", strerror(errno)); Bye(EX_UNAVAILABLE); } screwy = 1; } /* * put the tty back as it was, however that was */ static void C2Cooked(void) { if (!screwy) return; tcsetattr(0, TCSANOW, &o_tios); screwy = 0; } void DestroyDataStructures(void) { C2Cooked(); if (cfstdout != (CONSFILE *)0) FileUnopen(cfstdout); DestroyConfig(pConfig); DestroyConfig(optConf); DestroyConfig(config); DestroyTerminal(pTerm); #if !USE_IPV6 if (myAddrs != (struct in_addr *)0) free(myAddrs); #endif DestroyStrings(); if (substData != (SUBST *)0) free(substData); } char * ReadReply(CONSFILE *fd, FLAG toEOF) { int nr; static char buf[1024]; static STRING *result = (STRING *)0; if (result == (STRING *)0) result = AllocString(); else BuildString((char *)0, result); while (1) { int l; switch (nr = FileRead(fd, buf, sizeof(buf))) { case 0: /* fall through */ case -1: if (result->used > 1 || toEOF == FLAGTRUE) break; C2Cooked(); Error("lost connection"); Bye(EX_UNAVAILABLE); default: while ((l = ParseIACBuf(fd, buf, &nr)) >= 0) { if (l == 0) continue; BuildStringN(buf, l, result); nr -= l; MemMove(buf, buf + l, nr); } BuildStringN(buf, nr, result); if (toEOF == FLAGTRUE) /* if toEOF, read until EOF */ continue; if ((result->used > 1) && strchr(result->string, '\n') != (char *)0) break; continue; } break; } if (fDebug) { static STRING *tmpString = (STRING *)0; if (tmpString == (STRING *)0) tmpString = AllocString(); BuildString((char *)0, tmpString); FmtCtlStr(result->string, result->used - 1, tmpString); CONDDEBUG((1, "ReadReply: `%s'", tmpString->string)); } return result->string; } static void ReapVirt(void) { pid_t pid; int UWbuf; while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) { if (0 == pid) break; /* stopped child is just continued */ if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) { Msg("child pid %lu: stopped, sending SIGCONT", (unsigned long)pid); continue; } if (WIFEXITED(UWbuf)) Verbose("child process %lu: exit(%d)", pid, WEXITSTATUS(UWbuf)); if (WIFSIGNALED(UWbuf)) Verbose("child process %lu: signal(%d)", pid, WTERMSIG(UWbuf)); if (pid == execCmdPid) { if (WIFEXITED(UWbuf)) FilePrint(cfstdout, FLAGFALSE, "[local command terminated - pid %lu: exit(%d)]\r\n", pid, WEXITSTATUS(UWbuf)); if (WIFSIGNALED(UWbuf)) FilePrint(cfstdout, FLAGFALSE, "[local command terminated - pid %lu: signal(%d)]\r\n", pid, WTERMSIG(UWbuf)); } } } static sig_atomic_t fSawReapVirt = 0; #if HAVE_SIGACTION static #endif RETSIGTYPE FlagReapVirt(int sig) { fSawReapVirt = 1; #if !HAVE_SIGACTION SimpleSignal(SIGCHLD, FlagReapVirt); #endif } /* invoke the execcmd command */ void ExecCmd(void) { int i; pid_t iNewGrp; extern char **environ; int pin[2]; int pout[2]; static char *apcArgv[] = { "/bin/sh", "-ce", (char *)0, (char *)0 }; if (execCmd == (STRING *)0 || execCmd->used <= 1) return; CONDDEBUG((1, "ExecCmd(): `%s'", execCmd->string)); /* pin[0] = parent read, pin[1] = child write */ if (pipe(pin) != 0) { Error("ExecCmd(): pipe(): %s", strerror(errno)); return; } /* pout[0] = child read, pout[l] = parent write */ if (pipe(pout) != 0) { close(pin[0]); close(pin[1]); Error("ExecCmd(): pipe(): %s", strerror(errno)); return; } fflush(stdout); fflush(stderr); switch (execCmdPid = fork()) { case -1: return; case 0: thepid = getpid(); break; default: close(pout[0]); close(pin[1]); if ((execCmdFile = FileOpenPipe(pin[0], pout[1])) == (CONSFILE *)0) { Error("ExecCmd(): FileOpenPipe(%d,%d) failed", pin[0], pout[1]); close(pin[0]); close(pout[1]); kill(execCmdPid, SIGHUP); return; } FilePrint(cfstdout, FLAGFALSE, "[local command running - pid %lu]\r\n", execCmdPid); FD_SET(pin[0], &rinit); if (maxfd < pin[0] + 1) maxfd = pin[0] + 1; fflush(stderr); return; } close(pin[0]); close(pout[1]); /* put the signals back that we ignore (trapped auto-reset to default) */ SimpleSignal(SIGPIPE, SIG_DFL); SimpleSignal(SIGCHLD, SIG_DFL); /* setup new process with clean file descriptors * stderr still goes to stderr...so user sees it */ #ifdef HAVE_CLOSEFROM for (i = 3; i <= pout[0] || i <= pin[1]; i++) { if (i != pout[0] && i != pin[1]) close(i); } closefrom(i); #else i = GetMaxFiles(); for ( /* i above */ ; --i > 2;) { if (i != pout[0] && i != pin[1]) close(i); } #endif close(1); close(0); #if HAVE_SETSID iNewGrp = setsid(); if (-1 == iNewGrp) { Error("ExecCmd(): setsid(): %s", strerror(errno)); iNewGrp = thepid; } #else iNewGrp = thepid; #endif if (dup(pout[0]) != 0 || dup(pin[1]) != 1) { Error("ExecCmd(): fd sync error"); Bye(EX_OSERR); } close(pout[0]); close(pin[1]); tcsetpgrp(0, iNewGrp); apcArgv[2] = execCmd->string; execve(apcArgv[0], apcArgv, environ); Error("ExecCmd(): execve(%s): %s", apcArgv[2], strerror(errno)); Bye(EX_OSERR); return; } void GetUserInput(STRING *str) { char c; if (str == (STRING *)0) return; BuildString((char *)0, str); for (;;) { if (read(0, &c, 1) == 0) break; if (c == '\n' || c == '\r') { break; } if (c >= ' ' && c <= '~') { BuildStringChar(c, str); FileWrite(cfstdout, FLAGFALSE, &c, 1); } else if ((c == '\b' || c == 0x7f) && str->used > 1) { FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); str->string[str->used - 2] = '\000'; str->used--; } else if ((c == 0x15) && str->used > 1) { while (str->used > 1) { FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); str->string[str->used - 2] = '\000'; str->used--; } } else if ((c == 0x17) && str->used > 1) { while (str->used > 1 && isspace((int)(str->string[str->used - 2]))) { FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); str->string[str->used - 2] = '\000'; str->used--; } while (str->used > 1 && !isspace((int)(str->string[str->used - 2]))) { FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); str->string[str->used - 2] = '\000'; str->used--; } } } } void DoExec(CONSFILE *pcf) { showExecData = 1; FileWrite(cfstdout, FLAGFALSE, "exec: ", 6); GetUserInput(execCmd); FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3); if (execCmd != (STRING *)0 && execCmd->used > 1) { ExecCmd(); BuildString((char *)0, execCmd); if (execCmdFile == (CONSFILE *)0) { /* exec failed */ /* say forget it */ FileSetQuoteIAC(pcf, FLAGFALSE); FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT); FileSetQuoteIAC(pcf, FLAGTRUE); } else { char *r; /* go back to blocking mode */ SetFlags(FileFDNum(pcf), 0, O_NONBLOCK); /* say we're ready */ FileSetQuoteIAC(pcf, FLAGFALSE); FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_EXEC); FileSetQuoteIAC(pcf, FLAGTRUE); r = ReadReply(pcf, FLAGFALSE); /* now back to non-blocking, now that we've got reply */ SetFlags(FileFDNum(pcf), O_NONBLOCK, 0); /* if we aren't still r/w, abort */ if (strncmp(r, "[rw]", 4) != 0) { FileWrite(cfstdout, FLAGFALSE, "[no longer read-write - aborting command]\r\n", -1); FD_CLR(FileFDNum(execCmdFile), &rinit); FD_CLR(FileFDOutNum(execCmdFile), &winit); FileClose(&execCmdFile); FileSetQuoteIAC(pcf, FLAGFALSE); FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT); FileSetQuoteIAC(pcf, FLAGTRUE); kill(execCmdPid, SIGHUP); } } } else { /* say forget it */ FileSetQuoteIAC(pcf, FLAGFALSE); FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT); FileSetQuoteIAC(pcf, FLAGTRUE); } } void ExpandString(char *str, CONSFILE *c) { char s; short backslash = 0; short cntrl = 0; char oct = '\000'; short octs = 0; static STRING *exp = (STRING *)0; if (str == (char *)0 || c == (CONSFILE *)0) return; if (exp == (STRING *)0) exp = AllocString(); BuildString((char *)0, exp); backslash = 0; cntrl = 0; while ((s = (*str++)) != '\000') { if (octs > 0 && octs < 3 && s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } if (octs != 0) { BuildStringChar(oct, exp); octs = 0; oct = '\000'; } if (backslash) { backslash = 0; if (s == 'a') s = '\a'; else if (s == 'b') s = '\b'; else if (s == 'f') s = '\f'; else if (s == 'n') s = '\n'; else if (s == 'r') s = '\r'; else if (s == 't') s = '\t'; else if (s == 'v') s = '\v'; else if (s == '^') s = '^'; else if (s >= '0' && s <= '7') { ++octs; oct = oct * 8 + (s - '0'); continue; } BuildStringChar(s, exp); continue; } if (cntrl) { cntrl = 0; if (s == '?') s = 0x7f; /* delete */ else s = s & 0x1f; BuildStringChar(s, exp); continue; } if (s == '\\') { backslash = 1; continue; } if (s == '^') { cntrl = 1; continue; } BuildStringChar(s, exp); } if (octs != 0) BuildStringChar(oct, exp); if (backslash) BuildStringChar('\\', exp); if (cntrl) BuildStringChar('^', exp); if (exp->used > 1) FileWrite(c, FLAGFALSE, exp->string, exp->used - 1); } void PrintSubst(CONSFILE *pcf, char *pcMach, char *string, char *subst) { if (string == (char *)0) return; if (subst != (char *)0) { char *str; if ((str = StrDup(string)) == (char *)0) OutOfMem(); substData->data = (void *)config; config->console = pcMach; ProcessSubst(substData, &str, (char **)0, (char *)0, subst); ExpandString(str, pcf); free(str); } else ExpandString(string, pcf); } void Interact(CONSFILE *pcf, char *pcMach) { int i; int nc; fd_set rmask, wmask; int justSuspended = 0; static char acMesg[8192]; /* if this is true, it means we successfully moved to a new console * so we need to close the old one. */ if (prevConsole != (CONSFILE *)0) { FileClose(&prevConsole); PrintSubst(cfstdout, prevName, pTerm->detach, pTerm->detachsubst); } if (prevName != (char *)0) { free(prevName); prevName = (char *)0; } /* this is only true in other parts of the code iff pcf == gotoConsole */ if (gotoConsole != (CONSFILE *)0) { gotoConsole = (CONSFILE *)0; FilePrint(cfstdout, FLAGFALSE, "[returning to `%s'", pcMach); FileWrite(pcf, FLAGFALSE, "\n", 1); } PrintSubst(cfstdout, pcMach, pTerm->attach, pTerm->attachsubst); C2Raw(); /* set socket to non-blocking */ SetFlags(FileFDNum(pcf), O_NONBLOCK, 0); /* read from stdin and the socket (non-blocking!). * rmask indicates which descriptors to read from, * the others are not used, nor is the result from * select, read, or write. */ FD_ZERO(&rinit); FD_ZERO(&winit); FD_SET(FileFDNum(pcf), &rinit); FD_SET(0, &rinit); if (maxfd < FileFDNum(pcf) + 1) maxfd = FileFDNum(pcf) + 1; for (;;) { justSuspended = 0; if (fSawReapVirt) { fSawReapVirt = 0; ReapVirt(); } /* reset read mask and select on it */ rmask = rinit; wmask = winit; if (-1 == select(maxfd, &rmask, &wmask, (fd_set *)0, (struct timeval *)0)) { if (errno != EINTR) { Error("Master(): select(): %s", strerror(errno)); break; } continue; } /* anything from execCmd */ if (execCmdFile != (CONSFILE *)0) { if (FileCanRead(execCmdFile, &rmask, &wmask)) { if ((nc = FileRead(execCmdFile, acMesg, sizeof(acMesg))) < 0) { FD_CLR(FileFDNum(execCmdFile), &rinit); FD_CLR(FileFDOutNum(execCmdFile), &winit); FileClose(&execCmdFile); FileSetQuoteIAC(pcf, FLAGFALSE); FilePrint(pcf, FLAGFALSE, "%c%c", OB_IAC, OB_ABRT); FileSetQuoteIAC(pcf, FLAGTRUE); } else { if (config->striphigh == FLAGTRUE) { for (i = 0; i < nc; ++i) acMesg[i] &= 127; } FileWrite(pcf, FLAGFALSE, acMesg, nc); } } else if (!FileBufEmpty(execCmdFile) && FileCanWrite(execCmdFile, &rmask, &wmask)) { CONDDEBUG((1, "Interact(): flushing fd %d", FileFDNum(execCmdFile))); if (FileWrite(execCmdFile, FLAGFALSE, (char *)0, 0) < 0) { /* -bryan */ break; } } } /* anything from socket? */ if (FileCanRead(pcf, &rmask, &wmask)) { int l; if ((nc = FileRead(pcf, acMesg, sizeof(acMesg))) < 0) { /* if we got an error/eof after returning from suspend */ if (justSuspended) { fprintf(stderr, "\n"); Error("lost connection"); } break; } while ((l = ParseIACBuf(pcf, acMesg, &nc)) >= 0) { if (l == 0) { if (execCmdFile == (CONSFILE *)0) { if (FileSawQuoteExec(pcf) == FLAGTRUE) DoExec(pcf); else if (FileSawQuoteSusp(pcf) == FLAGTRUE) { justSuspended = 1; #if defined(SIGSTOP) FileWrite(cfstdout, FLAGFALSE, "stop]", 5); C2Cooked(); PrintSubst(cfstdout, pcMach, pTerm->detach, pTerm->detachsubst); kill(thepid, SIGSTOP); PrintSubst(cfstdout, pcMach, pTerm->attach, pTerm->attachsubst); C2Raw(); FileWrite(cfstdout, FLAGFALSE, "[press any character to continue", 32); #else FileWrite(cfstdout, FLAGFALSE, "stop not supported -- press any character to continue", 53); #endif } else if (FileSawQuoteGoto(pcf) == FLAGTRUE) { gotoConsole = pcf; if (gotoName != (char *)0) free(gotoName); if ((gotoName = StrDup(pcMach)) == (char *)0) OutOfMem(); C2Cooked(); return; } } else { if (FileSawQuoteAbrt(pcf) == FLAGTRUE) { FD_CLR(FileFDNum(execCmdFile), &rinit); FD_CLR(FileFDOutNum(execCmdFile), &winit); FileClose(&execCmdFile); kill(execCmdPid, SIGHUP); } } continue; } if (config->striphigh == FLAGTRUE) { for (i = 0; i < l; ++i) acMesg[i] &= 127; } if (execCmdFile != (CONSFILE *)0) { FileWrite(execCmdFile, FLAGFALSE, acMesg, l); if (showExecData) FileWrite(cfstdout, FLAGFALSE, acMesg, l); } else FileWrite(cfstdout, FLAGFALSE, acMesg, l); nc -= l; MemMove(acMesg, acMesg + l, nc); } } else if (!FileBufEmpty(pcf) && FileCanWrite(pcf, &rmask, &wmask)) { CONDDEBUG((1, "Interact(): flushing fd %d", FileFDNum(pcf))); if (FileWrite(pcf, FLAGFALSE, (char *)0, 0) < 0) { /* -bryan */ break; } } /* anything from stdin? */ if (FD_ISSET(0, &rmask)) { if ((nc = read(0, acMesg, sizeof(acMesg))) <= 0) { if (screwy) break; else { FD_CLR(0, &rinit); continue; } } if (execCmdFile == (CONSFILE *)0) { if (config->striphigh == FLAGTRUE) { for (i = 0; i < nc; ++i) acMesg[i] &= 127; } FileWrite(pcf, FLAGFALSE, acMesg, nc); } else { for (i = 0; i < nc; ++i) { if (acMesg[i] == '\n' || acMesg[i] == '\r') FilePrint(cfstdout, FLAGFALSE, "[local command running - pid %lu]\r\n", execCmdPid); else if (acMesg[i] == 0x03) { /* ctrl-c */ kill(execCmdPid, SIGHUP); FilePrint(cfstdout, FLAGFALSE, "[local command sent SIGHUP - pid %lu]\r\n", execCmdPid); } else if (acMesg[i] == 0x1c) { /* ctrl-\ */ kill(execCmdPid, SIGKILL); FilePrint(cfstdout, FLAGFALSE, "[local command sent SIGKILL - pid %lu]\r\n", execCmdPid); } else if (acMesg[i] == 'o' || acMesg[i] == 'O') { showExecData = !showExecData; FilePrint(cfstdout, FLAGFALSE, "[local command data %s]\r\n", showExecData ? "on" : "off"); } } } } } C2Cooked(); PrintSubst(cfstdout, pcMach, pTerm->detach, pTerm->detachsubst); if (fVerbose) printf("Console %s closed.\n", pcMach); } /* interact with a group server (ksb) */ void CallUp(CONSFILE *pcf, char *pcMaster, char *pcMach, char *pcHow, char *result) { int fIn = '-'; char *r = (char *)0; if (fVerbose) { Msg("%s to %s (on %s)", pcHow, pcMach, pcMaster); } #if !defined(__CYGWIN__) # if defined(F_SETOWN) if (fcntl(FileFDNum(pcf), F_SETOWN, thepid) == -1) { Error("fcntl(F_SETOWN,%lu): %d: %s", (unsigned long)thepid, FileFDNum(pcf), strerror(errno)); } # else # if defined(SIOCSPGRP) { int iTemp; /* on the HP-UX systems if different */ iTemp = -thepid; if (ioctl(FileFDNum(pcf), SIOCSPGRP, &iTemp) == -1) { Error("ioctl(%d,SIOCSPGRP): %s", FileFDNum(pcf), strerror(errno)); } } # endif # endif #endif SimpleSignal(SIGCHLD, FlagReapVirt); /* if we are going for a particular console * send sign-on stuff, then wait for some indication of what mode * we got from the server (if we are the only people on we get write * access by default, which is fine for most people). */ /* how did we do, did we get a read-only or read-write? */ if (0 == strcmp(result, "[attached]\r\n")) { /* OK -- we are good as gold */ fIn = 'a'; } else if (0 == strcmp(result, "[spy]\r\n") || 0 == strcmp(result, "[ok]\r\n") || 0 == strcmp(result, "[read-only -- initializing]\r\n")) { /* Humph, someone else is on * or we have an old version of the server (4.X) */ fIn = 's'; } else if (0 == strcmp(result, "[console is read-only]\r\n")) { fIn = 'r'; } else if (0 == strcmp(result, "[line to console is down]\r\n")) { /* ouch, the machine is down on the server */ fIn = '-'; Error("%s is down", pcMach); if (fVerbose) { printf("[use `"); PutCtlc(chAttn, stdout); PutCtlc(chEsc, stdout); printf("o\' to open console line]\n"); } } else { FilePrint(cfstdout, FLAGFALSE, "%s: %s", pcMach, result); Bye(EX_UNAVAILABLE); } /* change escape sequence (if set on the command line) * and replay the log for the user, if asked */ if (chAttn == -1 || chEsc == -1) { chAttn = DEFATTN; chEsc = DEFESC; } else { /* tell the conserver to change escape sequences, assume OK * (we'll find out soon enough) */ FilePrint(pcf, FLAGFALSE, "%c%ce%c%c", DEFATTN, DEFESC, chAttn, chEsc); r = ReadReply(pcf, FLAGFALSE); if (strncmp(r, "[redef:", 7) != 0) { Error("protocol botch on redef of escape sequence"); Bye(EX_UNAVAILABLE); } } /* try to grok the state of the console */ FilePrint(pcf, FLAGFALSE, "%c%c=", chAttn, chEsc); r = ReadReply(pcf, FLAGFALSE); if (strncmp(r, "[unknown", 8) != 0 && strncmp(r, "[up]", 4) != 0) FileWrite(cfstdout, FLAGFALSE, r, -1); /* try to grok the version of the server */ FilePrint(pcf, FLAGFALSE, "%c%c%c", chAttn, chEsc, 0xD6); r = ReadReply(pcf, FLAGFALSE); if (strncmp(r, "[unknown", 8) != 0) sversion = AtoU(r + 1); printf("[Enter `"); PutCtlc(chAttn, stdout); PutCtlc(chEsc, stdout); printf("?\' for help]\n"); /* try and display the MOTD */ FilePrint(pcf, FLAGFALSE, "%c%cm", chAttn, chEsc); r = ReadReply(pcf, FLAGFALSE); if (strncmp(r, "[unknown", 8) != 0 && strncmp(r, "[-- MOTD --]", 12) != 0) FileWrite(cfstdout, FLAGFALSE, r, -1); if (sversion >= 8001014) { if (config->playback) { FilePrint(pcf, FLAGFALSE, "%c%cP%hu\r", chAttn, chEsc, #if defined(TIOCGWINSZ) config->playback == 1 ? ws.ws_row : #endif config->playback - 1); r = ReadReply(pcf, FLAGFALSE); } if (config->replay) { FilePrint(pcf, FLAGFALSE, "%c%cR%hu\r", chAttn, chEsc, #if defined(TIOCGWINSZ) config->replay == 1 ? ws.ws_row : #endif config->replay - 1); r = ReadReply(pcf, FLAGFALSE); } } FilePrint(pcf, FLAGFALSE, "%c%c;", chAttn, chEsc); r = ReadReply(pcf, FLAGFALSE); if (strncmp(r, "[unknown", 8) != 0 && strncmp(r, "[connected]", 11) != 0) FileWrite(cfstdout, FLAGFALSE, r, -1); /* if the host is not down, finish the connection, and force * the correct attachment for the user */ if (fIn != '-') { if (fIn == 'r') { if (*pcHow != 's') { Error("%s is read-only", pcMach); } } else if (fIn != (*pcHow == 'f' ? 'a' : *pcHow)) { FilePrint(pcf, FLAGFALSE, "%c%c%c", chAttn, chEsc, *pcHow); } if (fReplay) { FilePrint(pcf, FLAGFALSE, "%c%cr", chAttn, chEsc); } else if (fVerbose) { FilePrint(pcf, FLAGFALSE, "%c%c\022", chAttn, chEsc); } } fflush(stdout); fflush(stderr); Interact(pcf, pcMach); } /* shouldn't need more than 3 levels of commands (but alloc 4 just 'cause) * worst case so far: master, groups, broadcast * (cmdarg == broadcast msg) * * some sample "stacks" of commands: * * console -q: master, quit * console -Q: quit * console foo: call, attach (interact==FLAGTRUE) * console -f foo: call, force (interact==FLAGTRUE) * console -w: master, groups, group * console -I: groups, info * console -i foo: call, info (interact==FLAGFALSE) * */ char *cmds[4] = { (char *)0, (char *)0, (char *)0, (char *)0 }; char *cmdarg = (char *)0; /* call a machine master for group master ports and machine master ports * take a list like "1782@localhost:@mentor.cc.purdue.edu:@pop.stat.purdue.edu" * and send the given command to the group leader at 1782 * and ask the machine master at mentor for more group leaders * and ask the machine master at pop.stat for more group leaders */ int DoCmds(char *master, char *pports, int cmdi) { CONSFILE *pcf; char *t; char *next; char *server; unsigned short port; char *result = (char *)0; int len; char *ports; char *pcopy; char *serverName; #if HAVE_GSSAPI int toksize; #endif if ((pcopy = ports = StrDup(pports)) == (char *)0) OutOfMem(); len = strlen(ports); while (len > 0 && (ports[len - 1] == '\r' || ports[len - 1] == '\n')) len--; ports[len] = '\000'; for ( /* param */ ; *ports != '\000'; ports = next) { if ((next = strchr(ports, ':')) == (char *)0) next = ""; else *next++ = '\000'; if ((server = strchr(ports, '@')) != (char *)0) { *server++ = '\000'; if (*server == '\000') server = master; } else server = master; #if USE_UNIX_DOMAIN_SOCKETS serverName = "localhost"; #else serverName = server; #endif if (*ports == '\000') { #if USE_IPV6 port = bindPort; #elif USE_UNIX_DOMAIN_SOCKETS port = 0; #else port = htons(bindPort); #endif } else if (!isdigit((int)(ports[0]))) { Error("invalid port spec for %s: `%s'", serverName, ports); continue; } else { #if USE_IPV6 port = (short)atoi(ports); #elif USE_UNIX_DOMAIN_SOCKETS port = (short)atoi(ports); #else port = htons((short)atoi(ports)); #endif } attemptLogin: if ((pcf = GetPort(server, port)) == (CONSFILE *)0) continue; FileSetQuoteIAC(pcf, FLAGTRUE); t = ReadReply(pcf, FLAGFALSE); if (strcmp(t, "ok\r\n") != 0) { FileClose(&pcf); FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t); continue; } #if HAVE_OPENSSL if (config->sslenabled == FLAGTRUE) { FileWrite(pcf, FLAGFALSE, "ssl\r\n", 5); t = ReadReply(pcf, FLAGFALSE); if (strcmp(t, "ok\r\n") == 0) { AttemptSSL(pcf); if (FileGetType(pcf) != SSLSocket) { Error("Encryption not supported by server `%s'", serverName); FileClose(&pcf); continue; } } else if (config->sslrequired == FLAGTRUE) { Error("Encryption not supported by server `%s'", serverName); FileClose(&pcf); continue; } } #endif #if HAVE_GSSAPI if ((toksize = CanGetGSSContext(server)) > 0) { FilePrint(pcf, FLAGFALSE, "gssapi %d\r\n", toksize); t = ReadReply(pcf, FLAGFALSE); if (strcmp(t, "ok\r\n") == 0) { if (AttemptGSSAPI(pcf)) { goto gssapi_logged_me_in; } } } #endif FilePrint(pcf, FLAGFALSE, "login %s\r\n", config->username); t = ReadReply(pcf, FLAGFALSE); if (strncmp(t, "passwd?", 7) == 0) { static int count = 0; static STRING *tmpString = (STRING *)0; char *hostname = (char *)0; if (t[7] == ' ') { hostname = PruneSpace(t + 7); if (*hostname == '\000') hostname = serverName; } else hostname = serverName; if (tmpString == (STRING *)0) tmpString = AllocString(); if (tmpString->used <= 1) { char *pass; BuildStringPrint(tmpString, "Enter %s@%s's password: ", config->username, hostname); pass = GetPassword(tmpString->string); if (pass == (char *)0) { Error("could not get password from tty for `%s'", serverName); FileClose(&pcf); continue; } BuildString((char *)0, tmpString); BuildString(pass, tmpString); BuildString("\r\n", tmpString); } FileWrite(pcf, FLAGFALSE, tmpString->string, tmpString->used - 1); t = ReadReply(pcf, FLAGFALSE); if (strcmp(t, "ok\r\n") != 0) { FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t); if (++count < 3) { BuildString((char *)0, tmpString); goto attemptLogin; } Error("too many bad passwords for `%s'", serverName); count = 0; FileClose(&pcf); continue; } else count = 0; } else if (strcmp(t, "ok\r\n") != 0) { FileClose(&pcf); FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t); continue; } #if HAVE_GSSAPI gssapi_logged_me_in: #endif /* now that we're logged in, we can do something */ /* if we're on the last cmd or the command is 'call' and we * have an arg (always true if it's 'call'), then send the arg */ if ((cmdi == 0 || cmds[cmdi][0] == 'c') && cmdarg != (char *)0) FilePrint(pcf, FLAGFALSE, "%s %s\r\n", cmds[cmdi], cmdarg); else FilePrint(pcf, FLAGFALSE, "%s\r\n", cmds[cmdi]); /* if we haven't gone down the stack, do "normal" stuff. * if we did hit the bottom, we send the exit\r\n now so * that the ReadReply can stop once the socket closes. */ if (cmdi != 0) { t = ReadReply(pcf, FLAGFALSE); /* save the result */ if (result != (char *)0) free(result); if ((result = StrDup(t)) == (char *)0) OutOfMem(); } /* if we're working on finding a console */ if (cmds[cmdi][0] == 'c') { static int limit = 0; /* did we get a redirect? */ if (result[0] == '@' || (result[0] >= '0' && result[0] <= '9')) { if (limit++ > 10) { Error("forwarding level too deep!"); Bye(EX_SOFTWARE); } FileWrite(pcf, FLAGFALSE, "exit\r\n", 6); t = ReadReply(pcf, FLAGTRUE); } else if (interact == FLAGFALSE && result[0] == '[' && cmdi > 0) { FileClose(&pcf); /* reconnect to same, but with the next command (info, examine, etc) */ DoCmds(master, pports, cmdi - 1); break; } else { /* if we're not trying to connect to a console */ if (interact == FLAGFALSE) { FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, result); FileClose(&pcf); continue; } if (result[0] != '[') { /* did we not get a connection? */ int len; limit = 0; FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, result); FileWrite(pcf, FLAGFALSE, "exit\r\n", 6); t = ReadReply(pcf, FLAGTRUE); /* strip off the goodbye from the tail of the result */ len = strlen(t); if (len > 8 && strcmp("goodbye\r\n", t + len - 9) == 0) { *(t + len - 9) = '\000'; } FileWrite(cfstdout, FLAGFALSE, t, -1); FileClose(&pcf); continue; } else { limit = 0; CallUp(pcf, server, cmdarg, cmds[0], result); if (pcf != gotoConsole) FileClose(&pcf); break; } } } else if (cmds[cmdi][0] == 'q') { if (cmdi == 0) { t = ReadReply(pcf, FLAGFALSE); FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, t); } else { FilePrint(cfstdout, FLAGFALSE, "%s: %s", serverName, result); } /* only say 'exit' if 'quit' failed...since it's dying anyway */ if (t[0] != 'o' || t[1] != 'k') { FileWrite(pcf, FLAGFALSE, "exit\r\n", 6); t = ReadReply(pcf, FLAGTRUE); } } else { /* all done */ /* ok, this is whacky. if cmdi==0, we haven't read back the * reply yet, so 't' is going to have multiple lines out output * since we send the 'exit' command...first line (or set of * lines) would be the previous command, and then a 'goodbye' * (ideally). we monkey around below because of this. * like i said. wacky. */ FileWrite(pcf, FLAGFALSE, "exit\r\n", 6); t = ReadReply(pcf, cmdi == 0 ? FLAGTRUE : FLAGFALSE); if (cmdi == 0) { int len; /* if we hit bottom, this is where we get our results */ if (result != (char *)0) free(result); if ((result = StrDup(t)) == (char *)0) OutOfMem(); /* strip off the goodbye from the tail of the result */ len = strlen(result); if (len > 8 && strcmp("goodbye\r\n", result + len - 9) == 0) { len -= 9; *(result + len) = '\000'; } /* if (not 'broadcast' and not 'textmsg') or * result doesn't start with 'ok' (only checks this if * it's a 'broadcast' or 'textmsg') */ if (cmds[0][0] == 'd') { if (result[0] != 'o' || result[1] != 'k') { FileWrite(cfstdout, FLAGTRUE, serverName, -1); FileWrite(cfstdout, FLAGTRUE, ": ", 2); FileWrite(cfstdout, FLAGFALSE, result, len); } else { disconnectCount += atoi(result + 19); } } else if ((cmds[0][0] != 'b' && cmds[0][0] != 't') || (result[0] != 'o' || result[1] != 'k')) { /* did a 'master' before this or doing a 'disconnect', * 'reconfig', 'newlogs', or 'up' */ if ((cmds[1] != (char *)0 && cmds[1][0] == 'm') || cmds[0][0] == 'd' || cmds[0][0] == 'r' || cmds[0][0] == 'n' || cmds[0][0] == 'u') { FileWrite(cfstdout, FLAGTRUE, serverName, -1); FileWrite(cfstdout, FLAGTRUE, ": ", 2); } FileWrite(cfstdout, FLAGFALSE, result, len); } } } FileClose(&pcf); /* this would only be true if we got extra redirects (@... above) */ if (cmds[cmdi][0] == 'c') DoCmds(server, result, cmdi); else if (cmdi > 0) DoCmds(server, result, cmdi - 1); if (result != (char *)0) free(result); result = (char *)0; } if (result != (char *)0) free(result); free(pcopy); return 0; } /* mainline for console client program (ksb) * setup who we are, and what our loopback addr is * parse the cmd line, * (optionally) get a shutdown passwd * Gather results * exit happy or sad */ int main(int argc, char **argv) { char *pcCmd; struct passwd *pwdMe = (struct passwd *)0; int opt; int fLocal; static STRING *acPorts = (STRING *)0; static char acOpts[] = "7aAb:B:c:C:d:De:EfFhiIl:M:np:PqQrRsSt:uUvVwWxz:Z:"; extern int optind; extern int optopt; extern char *optarg; static STRING *textMsg = (STRING *)0; int cmdi; static STRING *consoleName = (STRING *)0; short readSystemConf = 1; char *userConf = (char *)0; typedef struct zaps { char *opt; char *cmd; char *desc; } ZAPS; ZAPS zap[] = { {"bringup, SIGUSR1", "up", "bring up any consoles that are down"}, {"help", (char *)0, "this help message"}, {"pid", "pid", "display master process ids"}, {"quit, SIGTERM", "quit", "terminate the server"}, {"reconfig, SIGHUP", "reconfig", "reread configuration file, then do 'reopen' actions"}, {"reopen, SIGUSR2", "newlogs", "reopen all logfiles, then do 'bringup' actions"}, {"version", "version", "display version information"} }; int isZap = 0; isMultiProc = 0; /* make sure stuff DOESN'T have the pid */ thepid = getpid(); if (textMsg == (STRING *)0) textMsg = AllocString(); if (acPorts == (STRING *)0) acPorts = AllocString(); if ((char *)0 == (progname = strrchr(argv[0], '/'))) { progname = argv[0]; } else { ++progname; } /* prep the config options */ if ((optConf = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); if ((config = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); if ((pConfig = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); /* and the terminal options */ if ((pTerm = (TERM *)calloc(1, sizeof(TERM))) == (TERM *)0) OutOfMem(); /* command line parsing */ pcCmd = (char *)0; fLocal = 0; while ((opt = getopt(argc, argv, acOpts)) != EOF) { switch (opt) { case '7': /* strip high-bit */ optConf->striphigh = FLAGTRUE; break; case 'A': /* attach with log replay */ fReplay = 1; /* fall through */ case 'a': /* attach */ pcCmd = "attach"; break; case 'B': /* broadcast message */ fLocal = 1; /* fall through */ case 'b': pcCmd = "broadcast"; if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'C': userConf = optarg; break; case 'c': #if HAVE_OPENSSL if ((optConf->sslcredentials = StrDup(optarg)) == (char *)0) OutOfMem(); #endif break; case 'D': fDebug++; break; case 'd': pcCmd = "disconnect"; if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'E': #if HAVE_OPENSSL optConf->sslenabled = FLAGFALSE; #endif break; case 'e': /* set escape chars */ if ((optConf->escape = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'F': /* force attach with log replay */ fReplay = 1; /* fall through */ case 'f': /* force attach */ pcCmd = "force"; break; case 'I': fLocal = 1; /* fall through */ case 'i': pcCmd = "info"; break; case 'l': if ((optConf->username = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'M': if ((optConf->master = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'n': readSystemConf = 0; break; case 'p': if ((optConf->port = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'P': /* send a pid command to the server */ pcCmd = "pid"; break; case 'Q': /* only quit this host */ fLocal = 1; /*fallthough */ case 'q': /* send quit command to server */ pcCmd = "quit"; break; case 'R': fLocal = 1; /*fallthrough */ case 'r': /* display daemon version */ pcCmd = "version"; break; case 'S': /* spy with log replay */ fReplay = 1; /* fall through */ case 's': /* spy */ pcCmd = "spy"; break; case 't': BuildString((char *)0, textMsg); if (optarg == (char *)0 || *optarg == '\000') { Error("no destination specified for -t", optarg); Bye(EX_UNAVAILABLE); } else if (strchr(optarg, ' ') != (char *)0) { Error("-t option cannot contain a space: `%s'", optarg); Bye(EX_UNAVAILABLE); } BuildString("textmsg ", textMsg); BuildString(optarg, textMsg); pcCmd = textMsg->string; break; case 'U': #if HAVE_OPENSSL optConf->sslrequired = FLAGFALSE; #endif break; case 'u': pcCmd = "hosts"; break; case 'W': fLocal = 1; /*fallthrough */ case 'w': /* who */ pcCmd = "group"; break; case 'x': pcCmd = "examine"; break; case 'v': fVerbose = 1; break; case 'V': fVersion = 1; break; case 'Z': /* only send cmd this host */ fLocal = 1; /*fallthough */ case 'z': /* send a command to the server */ pcCmd = (char *)0; for (isZap = sizeof(zap) / sizeof(ZAPS) - 1; isZap >= 0; isZap--) { char *token = (char *)0; char *str = (char *)0; if (zap[isZap].cmd == (char *)0) /* skip non-action ones */ continue; BuildTmpString((char *)0); str = BuildTmpString(zap[isZap].opt); for (token = strtok(str, ", "); token != (char *)0; token = strtok(NULL, ", ")) { if (strcasecmp(optarg, token) == 0) { pcCmd = zap[isZap].cmd; isZap++; break; } } if (pcCmd) break; } if (isZap < 0) { if (strcasecmp(optarg, "help") == 0) { STRING *help; help = AllocString(); BuildString("available -z commands:\n\n", help); for (isZap = 0; isZap < sizeof(zap) / sizeof(ZAPS); isZap++) { char *str; BuildTmpString((char *)0); str = BuildTmpStringPrint(" %16s %s\n", zap[isZap].opt, zap[isZap].desc); BuildString(str, help); } Error(help->string); } else Error("invalid -z command: `%s' (try `help')", optarg); Bye(EX_UNAVAILABLE); } break; case 'h': /* huh? */ Usage(1); Bye(EX_OK); case '\?': /* huh? */ Usage(0); Bye(EX_UNAVAILABLE); default: Error("option %c needs a parameter", optopt); Bye(EX_UNAVAILABLE); } } if (fVersion) { Version(); Bye(EX_OK); } #if !USE_IPV6 ProbeInterfaces(INADDR_ANY); #endif if (readSystemConf) ReadConf(CLIENTCONFIGFILE, FLAGFALSE); if (userConf == (char *)0) { /* read the config files */ char *h = (char *)0; if (((h = getenv("HOME")) == (char *)0) && ((pwdMe = getpwuid(getuid())) == (struct passwd *)0)) { Error("$HOME does not exist and getpwuid fails: %d: %s", (int)(getuid()), strerror(errno)); } else { if (h == (char *)0) { if (pwdMe->pw_dir == (char *)0 || pwdMe->pw_dir[0] == '\000') { Error("Home directory for uid %d is not defined", (int)(getuid())); Bye(EX_UNAVAILABLE); } else { h = pwdMe->pw_dir; } } } if (h != (char *)0) { BuildTmpString((char *)0); BuildTmpString(h); h = BuildTmpString("/.consolerc"); ReadConf(h, FLAGFALSE); BuildTmpString((char *)0); } } else ReadConf(userConf, FLAGTRUE); if (optConf->striphigh != FLAGUNKNOWN) config->striphigh = optConf->striphigh; else if (pConfig->striphigh != FLAGUNKNOWN) config->striphigh = pConfig->striphigh; else config->striphigh = FLAGFALSE; if (optConf->escape != (char *)0) ParseEsc(optConf->escape); else if (pConfig->escape != (char *)0) ParseEsc(pConfig->escape); if (optConf->username != (char *)0) config->username = StrDup(optConf->username); else if (pConfig->username != (char *)0) config->username = StrDup(pConfig->username); else config->username = (char *)0; if (optConf->master != (char *)0 && optConf->master[0] != '\000') config->master = StrDup(optConf->master); else if (pConfig->master != (char *)0 && pConfig->master[0] != '\000') config->master = StrDup(pConfig->master); else config->master = StrDup( #if USE_UNIX_DOMAIN_SOCKETS UDSDIR #else MASTERHOST /* which machine is current */ #endif ); if (config->master == (char *)0) OutOfMem(); if (optConf->port != (char *)0 && optConf->port[0] != '\000') config->port = StrDup(optConf->port); else if (pConfig->port != (char *)0 && pConfig->port[0] != '\000') config->port = StrDup(pConfig->port); else config->port = StrDup(DEFPORT); if (config->port == (char *)0) OutOfMem(); if (optConf->replay != 0) config->replay = optConf->replay; else if (pConfig->replay != 0) config->replay = pConfig->replay; else config->replay = 0; if (optConf->playback != 0) config->playback = optConf->playback; else if (pConfig->playback != 0) config->playback = pConfig->playback; else config->playback = 0; #if HAVE_OPENSSL if (optConf->sslcredentials != (char *)0 && optConf->sslcredentials[0] != '\000') config->sslcredentials = StrDup(optConf->sslcredentials); else if (pConfig->sslcredentials != (char *)0 && pConfig->sslcredentials[0] != '\000') config->sslcredentials = StrDup(pConfig->sslcredentials); else config->sslcredentials = (char *)0; if (pConfig->sslcacertificatefile != (char *)0 && pConfig->sslcacertificatefile[0] != '\000') config->sslcacertificatefile = StrDup(pConfig->sslcacertificatefile); else config->sslcacertificatefile = (char *)0; if (pConfig->sslcacertificatepath != (char *)0 && pConfig->sslcacertificatepath[0] != '\000') config->sslcacertificatepath = StrDup(pConfig->sslcacertificatepath); else config->sslcacertificatepath = (char *)0; if (optConf->sslenabled != FLAGUNKNOWN) config->sslenabled = optConf->sslenabled; else if (pConfig->sslenabled != FLAGUNKNOWN) config->sslenabled = pConfig->sslenabled; else config->sslenabled = FLAGTRUE; if (optConf->sslrequired != FLAGUNKNOWN) config->sslrequired = optConf->sslrequired; else if (pConfig->sslrequired != FLAGUNKNOWN) config->sslrequired = pConfig->sslrequired; else config->sslrequired = FLAGTRUE; #endif /* finish resolving the command to do */ if (pcCmd == (char *)0) { pcCmd = "attach"; } if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') { /* attach, force-attach, and spy */ if (optind >= argc) { Error("missing console name"); Bye(EX_UNAVAILABLE); } if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(argv[optind++])) == (char *)0) OutOfMem(); } else if (*pcCmd == 't') { /* text message */ if (optind >= argc) { Error("missing message text"); Bye(EX_UNAVAILABLE); } if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(argv[optind++])) == (char *)0) OutOfMem(); } else if (*pcCmd == 'i' || *pcCmd == 'e' || *pcCmd == 'h' || *pcCmd == 'g') { /* info, e(x)amine, hosts (u), groups (w) */ if (optind < argc) { if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(argv[optind++])) == (char *)0) OutOfMem(); } } if (optind < argc) { Error("extra garbage on command line? (%s...)", argv[optind]); Bye(EX_UNAVAILABLE); } #if !USE_UNIX_DOMAIN_SOCKETS /* Look for non-numeric characters */ for (opt = 0; config->port[opt] != '\000'; opt++) if (!isdigit((int)config->port[opt])) break; if (config->port[opt] == '\000') { /* numeric only */ bindPort = atoi(config->port); } else { /* non-numeric only */ struct servent *pSE; if ((pSE = getservbyname(config->port, "tcp")) == (struct servent *)0) { Error("getservbyname(%s) failed", config->port); Bye(EX_UNAVAILABLE); } else { bindPort = ntohs((unsigned short)pSE->s_port); } } #endif if (config->username == (char *)0 || config->username[0] == '\000') { if (config->username != (char *)0) free(config->username); if (((config->username = getenv("LOGNAME")) == (char *)0) && ((config->username = getenv("USER")) == (char *)0) && ((pwdMe = getpwuid(getuid())) == (struct passwd *)0)) { Error ("$LOGNAME and $USER do not exist and getpwuid fails: %d: %s", (int)(getuid()), strerror(errno)); Bye(EX_UNAVAILABLE); } if (config->username == (char *)0) { if (pwdMe->pw_name == (char *)0 || pwdMe->pw_name[0] == '\000') { Error("Username for uid %d does not exist", (int)(getuid())); Bye(EX_UNAVAILABLE); } else { config->username = pwdMe->pw_name; } } if ((config->username = StrDup(config->username)) == (char *)0) OutOfMem(); } if (execCmd == (STRING *)0) execCmd = AllocString(); SimpleSignal(SIGPIPE, SIG_IGN); cfstdout = FileOpenFD(1, simpleFile); BuildString((char *)0, acPorts); BuildStringChar('@', acPorts); BuildString(config->master, acPorts); #if HAVE_OPENSSL SetupSSL(); /* should only do if we want ssl - provide flag! */ #endif /* stack up the commands for DoCmds() */ cmdi = -1; cmds[++cmdi] = pcCmd; if (*pcCmd == 'q' || *pcCmd == 'v' || *pcCmd == 'p' || *pcCmd == 'r' || isZap) { if (!fLocal) cmds[++cmdi] = "master"; } else if (*pcCmd == 'a' || *pcCmd == 'f' || *pcCmd == 's') { ValidateEsc(); cmds[++cmdi] = "call"; interact = FLAGTRUE; } else if (cmdarg != (char *)0 && (*pcCmd == 'i' || *pcCmd == 'e' || *pcCmd == 'h' || *pcCmd == 'g')) { cmds[++cmdi] = "call"; } else { cmds[++cmdi] = "groups"; if (!fLocal) cmds[++cmdi] = "master"; } #if defined(TIOCGWINSZ) if (interact == FLAGTRUE) { int fd; # if HAVE_MEMSET memset((void *)(&ws), '\000', sizeof(ws)); # else bzero((char *)(&ws), sizeof(ws)); # endif if ((fd = open("/dev/tty", O_RDONLY)) != -1) { ioctl(fd, TIOCGWINSZ, &ws); } close(fd); } #endif if (fDebug) { int i; for (i = cmdi; i >= 0; i--) { CONDDEBUG((1, "cmds[%d] = %s", i, cmds[i])); } } for (;;) { if (gotoConsole == (CONSFILE *)0) DoCmds(config->master, acPorts->string, cmdi); else Interact(gotoConsole, gotoName); /* if we didn't ask for another console, done */ if (gotoConsole == (CONSFILE *)0 && prevConsole == (CONSFILE *)0) break; if (consoleName == (STRING *)0) consoleName = AllocString(); C2Raw(); if (prevConsole == (CONSFILE *)0) FileWrite(cfstdout, FLAGFALSE, "console: ", 9); else FileWrite(cfstdout, FLAGFALSE, "[console: ", 10); GetUserInput(consoleName); FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3); C2Cooked(); if (consoleName->used > 1) { if (cmdarg != (char *)0) free(cmdarg); if ((cmdarg = StrDup(consoleName->string)) == (char *)0) OutOfMem(); if (prevConsole == (CONSFILE *)0) { prevConsole = gotoConsole; gotoConsole = (CONSFILE *)0; prevName = gotoName; gotoName = (char *)0; } } else { if (prevConsole != (CONSFILE *)0) { gotoConsole = prevConsole; prevConsole = (CONSFILE *)0; gotoName = prevName; prevName = (char *)0; } } } if (cmdarg != (char *)0) free(cmdarg); if (*pcCmd == 'd') FilePrint(cfstdout, FLAGFALSE, "disconnected %d %s\n", disconnectCount, disconnectCount == 1 ? "user" : "users"); Bye(0); return 0; /* noop - Bye() terminates us */ } conserver-8.2.4/console/console.man.in000066400000000000000000000533061344660520400177540ustar00rootroot00000000000000.TH CONSOLE 1 "@CONSERVER_DATE@" "conserver-@CONSERVER_VERSION@" "conserver" .SH NAME console \- console server client program .SH SYNOPSIS .B console .RI [ generic-args ] .RB [ \-aAfFsS ] .BR [ \-e .IR esc ] .I console .br .B console .RI [ generic-args ] .RB [ \-iIuwWx ] .RI [ console ] .br .B console .RI [ generic-args ] .RB [ \-hPqQrRV ] .RB [ \- [ bB ] .IR message ] .RB [ \-d .RI [ user ][\f3@\fP console ]] .RB [ \-t .RI [ user ][\f3@\fP console ] .IR message ] .RB [ \- [ zZ ] .IR cmd ] .PP .IR generic-args : .RB [ \-7DEnUv ] .RB [ \-c .IR cred ] .RB [ \-C .IR config ] .BR [ \-M .IR master ] .BR [ \-p .IR port ] .BR [ \-l .IR user ] .SH DESCRIPTION .B Console is used to manipulate console terminals remotely or to poll running .BR conserver (8) daemons for status information. .PP In the first form above, .B console asks the user's password before granting interactive access to a console (on a non-trusted system), since such a session may provide single-user access. If the server's autocompletion feature is enabled, only as much of the console name as is required to identify it uniquely to the server is required. .PP For non-interactive options, .B console outputs only the requested information and exits. .PP .B Console knows only of a primary .B conserver host (see the .B \-M option below), to which it initially connects. In a multi-server environment, the primary server may refer the client to a different server handling the requested console, or it will provide a list of all servers if required (as when .B console is invoked with the .B \-r option). .B Console then opens connections to the appropriate server(s). It is not necessary for the user of .B console to know which server manages which consoles, as long as .B console knows a valid primary server and all available consoles are listed in the primary server's configuration file. .SH OPTIONS .PP Options may be given as separate arguments (e.g., .B \-v .BR \-w ) or clustered (e.g., .BR \-vw ). Options and their arguments may be separated by optional white space. Option arguments containing spaces or other characters special to the shell must be quoted. .TP 11 .B \-7 Strip the high bit off of all data received, whether from user input or from the server, before any processing occurs. Disallows escape sequence characters with the high bit set. .TP .B \-a Access a console with a two-way (read-write) connection (this is the default). The connection is dropped to spy mode if someone else is attached read-write. .TP .BI \-b message Broadcast a .I message to all users connected to each server. .TP .BI \-B message Same as .B \-b but just send a .I message to users on the primary server. .TP .BI \-c cred Load an SSL certificate and key from the PEM encoded file .IR cred . .TP .BI \-C config Use the per-user configuration file .IR config . .TP .B \-d Disconnect the users specified by .IR user @ console . You may specify the target as .I user (disconnect the .IR user, regardless of what console they are attached to), .RI @ console (disconnect all users attached to .IR console ), or .IR user @ console (disconnect the .I user attached to .IR console ). .TP .B \-D Enable debugging output. .TP .BI \-e esc Set the initial two-character escape sequence to those represented by .IR esc . Any of the forms output by .BR cat (1)'s .B \-v option are accepted. The default value is .RB `` ^Ec ''. .TP .B \-E If encryption has been built into the code .RB ( --with-openssl ), encrypted client connections are, by default, a requirement. This option disables any attempt at creating an encrypted connection. If you'd like to use encrypted connections when your server supports it, but fallback to non-encrypted otherwise, the .B \-U option is what you want. .TP .B \-f Same as .B \-a except it will force any existing connection into spy mode. .TP .B \-h Display a brief help message. .TP .B \-i Display status information in a machine-parseable format (see below for the details). .TP .B \-I Same as .B \-i but just acts on the primary server. .TP .BI \-l user Set the login name used for authentication to .IR user . By default, .B console uses $USER if its uid matches the user's real uid, or $LOGNAME if its uid matches the user's real uid, or else the name associated with the user's real uid. .TP .BI \-M master The .B console client program polls .I master as the primary server, rather than the default set at compile time (typically .RB `` console ''). The default .I master may be changed at compile time using the .B --with-master option. If .B --with-uds is used to enable Unix domain sockets, however, this option points .B console to the directory which holds those sockets. The default .I master directory .RB (`` /tmp/conserver '') may be changed at compile time using the .B --with-uds option. .TP .BI \-n Do not read the system-wide configuration file. .TP .BI \-p port Set the port to connect to. This may be either a port number or a service name. The default .I port may be changed at compile time using the .B --with-port option. If the .B --with-uds option was used, this option is ignored. .TP .B \-P Display the pid of the master daemon process on each server. .TP .B \-q The .B console client connects to each server to request that the server daemon quit (shut down). The root password of the host(s) running conserver is required unless the local host is listed as ``trusted'' in the conserver.cf file; in that case, just press . .TP .B \-Q Same as .B \-q but just acts on the primary server. .TP .B \-r Display daemon versions. The .B console client connects to each server to request its version information. .TP .B \-R Same as .B \-r but just acts on the primary server. .TP .B \-s Request a read-only (spy mode) connection. In this mode all the escape sequences (below) work, or report errors, but all other keyboard input is discarded. .TP .B \-t Send a text .I message to .IR user @ console . You may specify the target as .I user (send to .IR user, regardless of what console they are attached to), .RI @ console (send to all users attached to .IR console ), or .IR user @ console (send to .I user attached to .IR console ). .TP .B \-u Show a list of all consoles with status (`up', `down', or `init') and attached users .RI ( user @ host if attached read-write, `' if only users in spy mode, or `'). .TP .B \-U If encryption has been built into the code .RB ( --with-openssl ), encrypted client connections are, by default, a requirement. This option allows the client to attempt an encrypted connection but fall back to a non-encrypted connection if the server doesn't support encryption. If the encryption handshake is failing, disabling encryption on the client with the .B \-E option is probably what you want. .TP .B \-v Be more verbose when building the connection(s). Use this option in combination with any of `show' options (below) for added benefit. .TP .B \-V Output the version and settings of the console client program and then exit. .TP .B \-w Show a list of all who are currently connected to consoles, including the hostnames where the .B console connections originate and the idle times. This is useful to see if anybody is actively using the console system if it becomes necessary to shut down .BR conserver . .TP .B \-W Same as .B \-w but just acts on the primary server. .TP .B \-x Show a list of consoles and devices. .TP .BI \-z cmd Sends a command .RI ( cmd ) to each server and displays the result. The valid commands are: .RS .sp .PD 0 .TP 12 .B bringup Try to connect all consoles marked as down (this is equivalent to sending the server a SIGUSR1) .TP .B SIGUSR1 Same as .B bringup .TP .B help Displays the help message .TP .B pid Returns the pid of the server (this is equivalent to .BR \-P ) .TP .B quit Instructs the server to shut down (this is equivalent to .B \-q or sending the server a SIGTERM) .TP .B SIGTERM Same as .B quit .TP .B reconfig Instructs the server to reload the configuration file, then perform the actions of .B reopen (this is equivalent to sending the server a SIGHUP) .TP .B SIGHUP Same as .B reconfig .TP .B reopen Instructs the server to reopen all logfiles, then perform the actions of .B bringup (this is equivalent to sending the server a SIGUSR2) .TP .B SIGUSR2 Same as .B reopen .TP .B version Returns the version of the server (this is equivalent to .BR \-V ) .PD .RE .TP .BI \-Z cmd Same as .B \-z but just sends .I cmd to the primary server. .PP The .BR \-A , .BR \-F ", or" .B \-S options have the same effect as their lower-case variants. In addition, they each request the last 20 lines of the console output after making the connection (as if .RB `` ^Ecr '' were typed). .PP The .BR \-i , .BR \-u , .BR \-w ", and" .B \-x options can be given a console name, which will limit their output to that console. .PP The .B \-i option outputs status information regarding each console in 15 colon-separated fields. .TP .I name The name of the console. .TP .I hostname,pid,socket The hostname, pid, and socket number of the child process managing the console. .TP .I type The type of console. Values will be a `/' for a local device, `|' for a command, `!' for a remote port, `%' for a Unix domain socket, and `#' for a noop console. .TP .I console-details The details regarding the console. The values here (all comma-separated) depend on the type of the console. Local devices will have values of the device file, baud rate/parity, and file descriptor for the device. Commands will have values of the command, the command's pid, the pseudo-tty, and file descriptor for the pseudo-tty. Remote ports will have values of the remote hostname, remote port number, ``raw'' or ``telnet'' protocol, and file descriptor for the socket connection. Unix domain sockets will have the path to the socket and the file descriptor for the socket connection. Noop consoles will have nothing. .TP .I users-list The details of each user connected to the console. The details for each user are an `@' separated list of `w', `r', or `s' (for read-write, read-only, or suspended), username, hostname the user is on, the user's idle time, and (for `r' and `s' users only) ``rw'' or ``ro'' (if the user wants read-write mode or not). Each user bundle is separated by commas. .TP .I state The state of the console. Values with either be ``up'', ``down'', or ``init''. .TP .I perm This value will either be ``rw'' or ``ro''. It will only be ``ro'' if the console is a local device (`/' type) and the permissions are such that the server can open the file for read, but not write. .TP .I logfile-details The details regarding the logging for the console. The comma-separated values will be the logfile, ``log'' or ``nolog'' (if logging is on or not - toggled via ``^EcL''), ``act'' or ``noact'' (if activity logging is enabled or not - the `a' timestamp option), the timestamp interval, and the file descriptor of the logfile. .TP .I break The default break sequence used for the console. .TP .I reup If the console is currently down and the automatic reconnection code is at work, it will have the value of ``autoup'', otherwise it will be ``noautoup''. .TP .I aliases The console aliases are presented in a comma-separated list. .TP .I options The active options for the console are presented in a comma-separated list. .TP .I initcmd The initcmd configuration option for the console. .TP .I idletimeout The idletimeout configuration option for the console. .TP .I idlestring The idlestring configuration option for the console. .SH CONFIGURATION .B Console reads configuration information from the system-wide configuration file .RB ( console.cf ), then the per-user configuration file .RB ( .consolerc ), and then applies command-line arguments. Each configuration location can override the previous. The same happens when parsing an individual file - the later entries always override the earlier entries. Because of that, you should put ``global'' defaults first and more specific defaults second. .PP The configuration file is read using the same parser as .BR conserver.cf (5), and you should check that manpage for parser details. .B Console recognizes the following configuration blocks. .TP \f3config\fP \f2hostname\fP|\f2ipaddr\fP .br Define a configuration block for the client host named .I hostname or using the address .IR ipaddr . If the value of ``*'' is used, the configuration block will be applied to all client hosts. .RS .TP \f3escape\fP \f2esc\fP .br Set the escape sequence (see the .B \-e command-line flag). .TP \f3master\fP \f2master\fP .br Set the default master to .I master (see the .B \-M command-line flag). .TP \f3playback\fP \f2num\fP|\f3""\fP .br Override the playback length for the .B p escape command to .I num lines (if the server supports it). Using the special value of ``0'' will cause the client to use the number of lines of the current terminal (if that can be determined). If the null string (``""'') is used, the playback length will not be overridden. .TP \f3port\fP \f2port\fP .br Set the default port to .I port (see the .B \-p command-line flag). .TP \f3replay\fP \f2num\fP|\f3""\fP .br Override the replay length for the .B r escape command to .I num lines (if the server supports it). Using the special value of ``0'' will cause the client to use the number of lines of the current terminal (if that can be determined). If the null string (``""'') is used, the replay length will not be overridden. .TP \f3sslcacertificatefile\fP \f2filename\fP .br Load the valid CA certificates for the .SM SSL connection from the PEM encoded file. .TP \f3sslcacertificatepath\fP \f2directory\fP .br Load the valid CA certificates for the .SM SSL connection from the PEM encoded files in the directory. .TP \f3sslcredentials\fP \f2filename\fP .br Set the .SM SSL credentials file location (see the .B \-c command-line flag). .TP \f3sslenabled\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not encryption is attempted when talking to servers (see the .B \-E command-line flag). .TP \f3sslrequired\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not encryption is required when talking to servers (see the .B \-U command-line flag). .TP \f3striphigh\fP \f3yes\fP|\f3true\fP|\f3on\fP|\f3no\fP|\f3false\fP|\f3off\fP .br Set whether or not to strip the high bit off all data received (see the .B \-7 command-line flag). .TP \f3username\fP \f2user\fP .br Set the username passed to the server to .I user (see the .B \-l command-line flag). .RE .TP \f3terminal\fP \f2type\fP .br Define a configuration block when using a terminal of type .IR type . If the value of ``*'' is used, the configuration block will be applied to all terminal types. .RS .TP \f3attach\fP \f2string\fP|\f3""\fP .br Set a .I string to print when successfully attached to a console. Character substitions will be performed based on the .B attachsubst value and occur .I before interpretation of the special characters below. If the null string (``\f3""\fP'') is used, no string will be printed. .I string is a simple character string with the exception of `\e' and `^': .RS .RS .sp .PD 0 .TP 6 .B \ea alert .TP .B \eb backspace .TP .B \ef form-feed .TP .B \en newline .TP .B \er carriage-return .TP .B \et tab .TP .B \ev vertical-tab .TP .B \e\e backslash .TP .B \e^ circumflex .TP .BI \e ooo octal representation of a character (where .I ooo is one to three octal digits) .TP .BI \e c character .I c .TP .B ^? delete .TP .BI ^ c control character .RI ( c is ``and''ed with 0x1f) .PD .RE .RE .IP An interesting use of .B attach and .B attachsubst would be: .RS .IP .ft CR .nf terminal xterm { attach "^[]0;conserver: U@C^G"; attachsubst U=us,C=cs; } .fi .ft .RE .TP \f3attachsubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B attach value. A series of replacements can be defined by specifying a comma-separated list of \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP sequences where .I c is any printable character, .I t specifies the replacement value, .I n is a field length (optional), and .I f is the format string. .I t can be one of the characters below, catagorized as a string replacement or a numeric replacement, which dictates the use of the .I n and .I f fields. .RS .RS .sp .PD 0 .TP String Replacement .TP .B u username .TP .B c console name .sp .PP Numeric Replacement .TP none available (yet) .PD .RE .RE .IP For string replacements, if the replacement isn't at least .I n characters, it will be padded with space characters on the left. .I f must be `s'. For numeric replacements, the value will be formatted to at least .I n characters, padded with 0s if .I n begins with a 0, and space characters otherwise. .I f must be either `d', `x' or `X', specifying a decimal, lower-case hexadecimal, or an uppercase hexadecimal conversion. If the null string (``\f3""\fP'') is used, no replacements will be done. .TP \f3detach\fP \f2string\fP|\f3""\fP .br Set a .I string to print once detached from a console. Character substitions will be performed based on the .B detachsubst value. See the .B attach option for an explanation of .IR string . If the null string (``\f3""\fP'') is used, no string will be printed. .TP \f3detachsubst\fP \f2c\fP\f3=\fP\f2t\fP[\f2n\fP]\f2f\fP[\f3,\fP...]|\f3""\fP .br Perform character substitutions on the .B detach value. See the .B attachsubst option for an explanation of the format string. .RE .PP A simple configuration to set a new default escape sequence and override the master location would be: .IP .ft CR .nf # override options for all hosts config * { master localhost; escape ^Ee; } # set things more specific to host1 # note: if the entries were reversed, host1 # would also use localhost. config host1 { master console1; } .fi .ft .SH "ESCAPE SEQUENCES" The connection can be controlled by a two-character escape sequence, followed by a command. The default escape sequence is ``control-E c'' (octal 005 143). (The escape sequences are actually processed by the server; see the .BR conserver (8) man page for more information.) Commands are: .sp .PD 0 .TP 13 .B \. disconnect .TP .B ; move to another console .TP .B a attach read-write if nobody already is .TP .B b send broadcast message to all users on this console .TP .B c toggle flow control (don't do this) .TP .B d down the current console .TP .BI e cc change the escape sequence to the next two characters .TP .B f forcibly attach read-write .TP .B g group info .TP .B i information dump .TP .B L toggle logging on/off .TP .B l? list the break sequences available .TP .B l0 send the break sequence associated with this console .TP .B l1-9a-z send the specific break sequence .TP .B m display the "message of the day" .TP .B o close (if open) and reopen the line (to clear errors (silo overflows)) and the log file .TP .B p playback the last 60 lines of output .TP .B P set number of playback lines .TP .B r replay the last 20 lines of output .TP .B R set number of replay lines .TP .B s switch to spy mode (read only) .TP .B u show status of hosts/users in this group .TP .B v show the version of the group server .TP .B w who is using this console .TP .B x examine this group's devices and modes .TP .B z suspend this connection .TP .B ! invoke task .TP .B | attach a local command to the console .TP .B ? display list of commands .TP .BR ^M " (return)" continue, ignore the escape sequence .TP .BR ^R " (ctrl-R)" replay the last line only .TP .BI \e ooo send character having octal code .IR ooo " (must" specify three octal digits) .PD .PP If any other character is hit after the escape sequence, all three characters will be discarded. Note that a line break or a down command can only be sent from a read-write connection. To send the escape sequence through the connection one must redefine the outer escape sequence, or use .BI ^Ec\e ooo to send the first escape character before typing the second character directly. .PP In the .B \-u output, the login ``'' indicates no one is viewing that console, and the login ``'' indicates that no one has a read-write connection (only read-only). .PP When running a local command via .RB `` ^Ec| '', you can type .RB ` ^C ' to send the command a SIGHUP, .RB ` ^\e ' to send the command a SIGKILL, and .RB ` o ' to toggle the display of the console data. .SH EXAMPLES .TP 15 console \-u Outputs something like: .IP .ft CR .nf dumb up expert up ksb@mentor tyro up mentor up sage up fine@cis .fi .ft .IP The .B indicates no one is viewing .IR dumb or .IR mentor , the .B indicates only read-only connections exist for .IR tyro , and other .IR login @ host entries indicate users attached read-write to .I sage and .IR expert . .TP console \-w Outputs something like: .IP .ft CR .nf ksb@extra attach 2days expert file@cis attach 21:46 sage dmr@alice spy \00:04 tyro .fi .ft .IP The third column is the idle time of the user. Either .IR hours : minutes or number of days is displayed. .TP console \-e "^[1" lv426 Requests a connection to the host ``lv426'' with the escape characters set to ``escape one''. .SH FILES .PP The following default file locations may be overridden at compile time or by the command-line options described above. Run .B console \-V to see the defaults set at compile time. .PP .PD 0 .TP 25 .B /etc/console.cf system-wide configuration file .TP .B \s-1$HOME\s0/.consolerc per-user configuration file .PD .SH BUGS It is possible to create a loop of console connections, with ugly results. Never run .B console from within a console connection (unless you set each escape sequence differently). .PP The \-i output can produce more than the stated number of fields of information if the user-provided information has embedded colons. .PP I'm sure there are more, I just don't know where they are. Please let me know if you find any. .SH AUTHORS Thomas A. Fine, Ohio State Computer Science .br Kevin Braunsdorf, Purdue University Computing Center .br Bryan Stansell, conserver.com .SH "SEE ALSO" .BR conserver.cf (5), .BR conserver.passwd (5), .BR conserver (8) conserver-8.2.4/console/getpassword.c000066400000000000000000000054211344660520400177110ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ #include #include #include #include /* the next two routines assure that the users tty is in the * correct mode for us to do our thing */ static int screwy = 0; static struct termios o_tios; /* this holds the password given to us by the user */ static STRING *pass = (STRING *)0; /* * show characters that are already tty processed, * and read characters before cononical processing * we really use cbreak at PUCC because we need even parity... */ static void C2Raw(int fd) { struct termios n_tios; if (!isatty(fd) || 0 != screwy) return; if (0 != tcgetattr(fd, &o_tios)) { Error("tcgetattr(%d): %s", fd, strerror(errno)); Bye(EX_UNAVAILABLE); } n_tios = o_tios; n_tios.c_iflag &= ~(IUCLC | IXON); n_tios.c_oflag &= ~OPOST; n_tios.c_lflag &= ~(ISIG | ECHO | IEXTEN); n_tios.c_cc[VMIN] = 1; n_tios.c_cc[VTIME] = 0; if (0 != tcsetattr(fd, TCSANOW, &n_tios)) { Error("tcsetattr(%d, TCSANOW): %s", fd, strerror(errno)); Bye(EX_UNAVAILABLE); } screwy = 1; } /* * put the tty back as it was, however that was */ static void C2Normal(int fd) { if (!screwy) return; tcsetattr(fd, TCSANOW, &o_tios); screwy = 0; } char * GetPassword(char *prompt) { int fd; int nc; char buf[BUFSIZ]; int done = 0; if (prompt == (char *)0) prompt = ""; if ((pass = AllocString()) == (STRING *)0) OutOfMem(); BuildString((char *)0, pass); if ((fd = open("/dev/tty", O_RDWR)) == -1) { Error("could not open `/dev/tty': %s", strerror(errno)); return (char *)0; } C2Raw(fd); write(fd, prompt, strlen(prompt)); while (!done) { int i; if ((nc = read(0, buf, sizeof(buf))) == 0) break; for (i = 0; i < nc; ++i) { if (buf[i] == 0x0d || buf[i] == 0x0a) { /* CR, NL */ done = 1; break; } else BuildStringChar(buf[i], pass); } } C2Normal(fd); /* { static STRING *c = (STRING *) 0; if ((c = AllocString()) == (STRING *) 0) OutOfMem(); write(fd, "\n'", 2); if (pass->used) { FmtCtlStr(pass->string, pass->used - 1, c); write(fd, c->string, c->used - 1); } write(fd, "'\n", 2); } */ write(fd, "\n", 1); close(fd); /* this way a (char*)0 is only returned on error */ if (pass->string == (char *)0) return ""; else return pass->string; } void ClearPassword(void) { if (pass == (STRING *)0 || pass->allocated == 0) return; #if HAVE_MEMSET memset((void *)(pass->string), '\000', pass->allocated); #else bzero((char *)(pass->string), pass->allocated); #endif BuildString((char *)0, pass); } conserver-8.2.4/console/getpassword.h000066400000000000000000000002571344660520400177200ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ extern char *GetPassword(char *); extern void *ClearPassword(void); conserver-8.2.4/console/readconf.c000066400000000000000000000432021344660520400171270ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ #include #include #include CONFIG *parserConfigTemp = (CONFIG *)0; CONFIG *parserConfigDefault = (CONFIG *)0; CONFIG *pConfig = (CONFIG *)0; TERM *parserTermTemp = (TERM *)0; TERM *parserTermDefault = (TERM *)0; TERM *pTerm = (TERM *)0; void DestroyConfig(CONFIG *c) { if (c == (CONFIG *)0) return; if (c->username != (char *)0) free(c->username); if (c->master != (char *)0) free(c->master); if (c->port != (char *)0) free(c->port); if (c->escape != (char *)0) free(c->escape); #if HAVE_OPENSSL if (c->sslcredentials != (char *)0) free(c->sslcredentials); if (c->sslcacertificatefile != (char *)0) free(c->sslcacertificatefile); if (c->sslcacertificatepath != (char *)0) free(c->sslcacertificatepath); #endif free(c); } void ApplyConfigDefault(CONFIG *c) { if (parserConfigDefault == (CONFIG *)0) return; if (parserConfigDefault->username != (char *)0) { if (c->username != (char *)0) free(c->username); if ((c->username = StrDup(parserConfigDefault->username)) == (char *)0) OutOfMem(); } if (parserConfigDefault->master != (char *)0) { if (c->master != (char *)0) free(c->master); if ((c->master = StrDup(parserConfigDefault->master)) == (char *)0) OutOfMem(); } if (parserConfigDefault->port != (char *)0) { if (c->port != (char *)0) free(c->port); if ((c->port = StrDup(parserConfigDefault->port)) == (char *)0) OutOfMem(); } if (parserConfigDefault->escape != (char *)0) { if (c->escape != (char *)0) free(c->escape); if ((c->escape = StrDup(parserConfigDefault->escape)) == (char *)0) OutOfMem(); } if (parserConfigDefault->striphigh != FLAGUNKNOWN) c->striphigh = parserConfigDefault->striphigh; if (parserConfigDefault->replay != FLAGUNKNOWN) c->replay = parserConfigDefault->replay; if (parserConfigDefault->playback != FLAGUNKNOWN) c->playback = parserConfigDefault->playback; #if HAVE_OPENSSL if (parserConfigDefault->sslcredentials != (char *)0) { if (c->sslcredentials != (char *)0) free(c->sslcredentials); if ((c->sslcredentials = StrDup(parserConfigDefault->sslcredentials)) == (char *)0) OutOfMem(); } if (parserConfigDefault->sslcacertificatefile != (char *)0) { if (c->sslcacertificatefile != (char *)0) free(c->sslcacertificatefile); if ((c->sslcacertificatefile = StrDup(parserConfigDefault->sslcacertificatefile)) == (char *)0) OutOfMem(); } if (parserConfigDefault->sslcacertificatepath != (char *)0) { if (c->sslcacertificatepath != (char *)0) free(c->sslcacertificatepath); if ((c->sslcacertificatepath = StrDup(parserConfigDefault->sslcacertificatepath)) == (char *)0) OutOfMem(); } if (parserConfigDefault->sslrequired != FLAGUNKNOWN) c->sslrequired = parserConfigDefault->sslrequired; if (parserConfigDefault->sslenabled != FLAGUNKNOWN) c->sslenabled = parserConfigDefault->sslenabled; #endif } void ConfigBegin(char *id) { CONDDEBUG((1, "ConfigBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { Error("empty config name [%s:%d]", file, line); return; } if (parserConfigTemp != (CONFIG *)0) DestroyConfig(parserConfigTemp); if ((parserConfigTemp = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); ApplyConfigDefault(parserConfigTemp); parserConfigTemp->name = AllocString(); BuildString(id, parserConfigTemp->name); } void ConfigEnd(void) { CONDDEBUG((1, "ConfigEnd() [%s:%d]", file, line)); if (parserConfigTemp == (CONFIG *)0) return; if (parserConfigTemp->name->used > 1) { if ((parserConfigTemp->name->string[0] == '*' && parserConfigTemp->name->string[1] == '\000') || IsMe(parserConfigTemp->name->string)) { DestroyConfig(parserConfigDefault); parserConfigDefault = parserConfigTemp; parserConfigTemp = (CONFIG *)0; } } DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } void ConfigAbort(void) { CONDDEBUG((1, "ConfigAbort() [%s:%d]", file, line)); if (parserConfigTemp == (CONFIG *)0) return; DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } void ConfigDestroy(void) { CONDDEBUG((1, "ConfigDestroy() [%s:%d]", file, line)); if (parserConfigTemp != (CONFIG *)0) { DestroyConfig(parserConfigTemp); parserConfigTemp = (CONFIG *)0; } if (parserConfigDefault != (CONFIG *)0) { DestroyConfig(pConfig); pConfig = parserConfigDefault; parserConfigDefault = (CONFIG *)0; } } void DestroyTerminal(TERM *t) { if (t == (TERM *)0) return; if (t->attach != (char *)0) free(t->attach); if (t->attachsubst != (char *)0) free(t->attachsubst); if (t->detach != (char *)0) free(t->detach); if (t->detachsubst != (char *)0) free(t->detachsubst); free(t); } void ApplyTermDefault(TERM *t) { if (parserTermDefault == (TERM *)0) return; if (parserTermDefault->attach != (char *)0) { if (t->attach != (char *)0) free(t->attach); if ((t->attach = StrDup(parserTermDefault->attach)) == (char *)0) OutOfMem(); } if (parserTermDefault->attachsubst != (char *)0) { if (t->attachsubst != (char *)0) free(t->attachsubst); if ((t->attachsubst = StrDup(parserTermDefault->attachsubst)) == (char *)0) OutOfMem(); } if (parserTermDefault->detach != (char *)0) { if (t->detach != (char *)0) free(t->detach); if ((t->detach = StrDup(parserTermDefault->detach)) == (char *)0) OutOfMem(); } if (parserTermDefault->detachsubst != (char *)0) { if (t->detachsubst != (char *)0) free(t->detachsubst); if ((t->detachsubst = StrDup(parserTermDefault->detachsubst)) == (char *)0) OutOfMem(); } } void TerminalBegin(char *id) { CONDDEBUG((1, "TerminalBegin(%s) [%s:%d]", id, file, line)); if (id == (char *)0 || id[0] == '\000') { Error("empty terminal name [%s:%d]", file, line); return; } if (parserTermTemp != (TERM *)0) DestroyTerminal(parserTermTemp); if ((parserTermTemp = (TERM *)calloc(1, sizeof(TERM))) == (TERM *)0) OutOfMem(); ApplyTermDefault(parserTermTemp); parserTermTemp->name = AllocString(); BuildString(id, parserTermTemp->name); } void TerminalEnd(void) { static char *term = (char *)0; CONDDEBUG((1, "TerminalEnd() [%s:%d]", file, line)); if (parserTermTemp == (TERM *)0) return; if (term == (char *)0) { if ((term = getenv("TERM")) == (char *)0) { term = ""; } } if (parserTermTemp->name->used > 1) { if ((parserTermTemp->name->string[0] == '*' && parserTermTemp->name->string[1] == '\000') || strcmp(parserTermTemp->name->string, term) == 0) { DestroyTerminal(parserTermDefault); parserTermDefault = parserTermTemp; parserTermTemp = (TERM *)0; } } DestroyTerminal(parserTermTemp); parserTermTemp = (TERM *)0; } void TerminalAbort(void) { CONDDEBUG((1, "TerminalAbort() [%s:%d]", file, line)); if (parserTermTemp == (TERM *)0) return; DestroyTerminal(parserTermTemp); parserTermTemp = (TERM *)0; } void TerminalDestroy(void) { CONDDEBUG((1, "TerminalDestroy() [%s:%d]", file, line)); if (parserTermTemp != (TERM *)0) { DestroyTerminal(parserTermTemp); parserTermTemp = (TERM *)0; } if (parserTermDefault != (TERM *)0) { DestroyTerminal(pTerm); pTerm = parserTermDefault; parserTermDefault = (TERM *)0; } } void ProcessYesNo(char *id, FLAG *flag) { if (id == (char *)0 || id[0] == '\000') *flag = FLAGFALSE; else if (strcasecmp("yes", id) == 0 || strcasecmp("true", id) == 0 || strcasecmp("on", id) == 0) *flag = FLAGTRUE; else if (strcasecmp("no", id) == 0 || strcasecmp("false", id) == 0 || strcasecmp("off", id) == 0) *flag = FLAGFALSE; } void ConfigItemEscape(char *id) { CONDDEBUG((1, "ConfigItemEscape(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->escape != (char *)0) free(parserConfigTemp->escape); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->escape = (char *)0; return; } if ((parserConfigTemp->escape = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemMaster(char *id) { CONDDEBUG((1, "ConfigItemMaster(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->master != (char *)0) free(parserConfigTemp->master); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->master = (char *)0; return; } if ((parserConfigTemp->master = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemPlayback(char *id) { int i; CONDDEBUG((1, "ConfigItemPlayback(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->playback = 0; return; } for (i = 0; id[i] != '\000'; i++) { if (!isdigit((int)id[i])) { Error("invalid playback value [%s:%d]", file, line); return; } } if (i > 4) { Error("playback value too large [%s:%d]", file, line); return; } parserConfigTemp->playback = (unsigned short)atoi(id) + 1; } void ConfigItemPort(char *id) { CONDDEBUG((1, "ConfigItemPort(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->port != (char *)0) free(parserConfigTemp->port); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->port = (char *)0; return; } if ((parserConfigTemp->port = StrDup(id)) == (char *)0) OutOfMem(); } void ConfigItemReplay(char *id) { int i; CONDDEBUG((1, "ConfigItemReplay(%s) [%s:%d]", id, file, line)); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->replay = 0; return; } for (i = 0; id[i] != '\000'; i++) { if (!isdigit((int)id[i])) { Error("invalid replay value [%s:%d]", file, line); return; } } if (i > 4) { Error("replay value too large [%s:%d]", file, line); return; } parserConfigTemp->replay = (unsigned short)atoi(id) + 1; } void ConfigItemSslcredentials(char *id) { CONDDEBUG((1, "ConfigItemSslcredentials(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL if (parserConfigTemp->sslcredentials != (char *)0) free(parserConfigTemp->sslcredentials); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->sslcredentials = (char *)0; return; } if ((parserConfigTemp->sslcredentials = StrDup(id)) == (char *)0) OutOfMem(); #else Error ("sslcredentials ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslcacertificatefile(char *id) { CONDDEBUG((1, "ConfigItemSslcacertificatefile(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL if (parserConfigTemp->sslcacertificatefile != (char *)0) free(parserConfigTemp->sslcacertificatefile); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->sslcacertificatefile = (char *)0; return; } if ((parserConfigTemp->sslcacertificatefile = StrDup(id)) == (char *)0) OutOfMem(); #else Error ("sslcacertificatefile ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslcacertificatepath(char *id) { CONDDEBUG((1, "ConfigItemSslcacertificatepath(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL if (parserConfigTemp->sslcacertificatepath != (char *)0) free(parserConfigTemp->sslcacertificatepath); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->sslcacertificatepath = (char *)0; return; } if ((parserConfigTemp->sslcacertificatepath = StrDup(id)) == (char *)0) OutOfMem(); #else Error ("sslcacertificatepath ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslrequired(char *id) { CONDDEBUG((1, "ConfigItemSslrequired(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL ProcessYesNo(id, &(parserConfigTemp->sslrequired)); #else Error ("sslrequired ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemSslenabled(char *id) { CONDDEBUG((1, "ConfigItemSslenabled(%s) [%s:%d]", id, file, line)); #if HAVE_OPENSSL ProcessYesNo(id, &(parserConfigTemp->sslenabled)); #else Error("sslenabled ignored - encryption not compiled into code [%s:%d]", file, line); #endif } void ConfigItemStriphigh(char *id) { CONDDEBUG((1, "ConfigItemStriphigh(%s) [%s:%d]", id, file, line)); ProcessYesNo(id, &(parserConfigTemp->striphigh)); } void ConfigItemUsername(char *id) { CONDDEBUG((1, "ConfigItemUsername(%s) [%s:%d]", id, file, line)); if (parserConfigTemp->username != (char *)0) free(parserConfigTemp->username); if ((id == (char *)0) || (*id == '\000')) { parserConfigTemp->username = (char *)0; return; } if ((parserConfigTemp->username = StrDup(id)) == (char *)0) OutOfMem(); } SUBST *substData = (SUBST *)0; SUBSTTOKEN SubstToken(char c) { switch (c) { case 'u': case 'c': return ISSTRING; default: return ISNOTHING; } } int SubstValue(char c, char **s, int *i) { int retval = 0; if (s != (char **)0) { CONFIG *pc; if (substData->data == (void *)0) return 0; pc = (CONFIG *)(substData->data); if (c == 'u') { (*s) = pc->username; retval = 1; } else if (c == 'c') { (*s) = pc->console; retval = 1; } } return retval; } void InitSubstCallback(void) { if (substData == (SUBST *)0) { if ((substData = (SUBST *)calloc(1, sizeof(SUBST))) == (SUBST *)0) OutOfMem(); substData->value = &SubstValue; substData->token = &SubstToken; } } void TerminalItemAttach(char *id) { CONDDEBUG((1, "TerminalItemAttach(%s) [%s:%d]", id, file, line)); if (parserTermTemp->attach != (char *)0) free(parserTermTemp->attach); if ((id == (char *)0) || (*id == '\000')) { parserTermTemp->attach = (char *)0; return; } if ((parserTermTemp->attach = StrDup(id)) == (char *)0) OutOfMem(); } void TerminalItemAttachsubst(char *id) { CONDDEBUG((1, "TerminalItemAttachsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserTermTemp->attachsubst), "attachsubst", id); } void TerminalItemDetach(char *id) { CONDDEBUG((1, "TerminalItemDetach(%s) [%s:%d]", id, file, line)); if (parserTermTemp->detach != (char *)0) free(parserTermTemp->detach); if ((id == (char *)0) || (*id == '\000')) { parserTermTemp->detach = (char *)0; return; } if ((parserTermTemp->detach = StrDup(id)) == (char *)0) OutOfMem(); } void TerminalItemDetachsubst(char *id) { CONDDEBUG((1, "TerminalItemDetachsubst(%s) [%s:%d]", id, file, line)); ProcessSubst(substData, (char **)0, &(parserTermTemp->detachsubst), "detachsubst", id); } ITEM keyConfig[] = { {"escape", ConfigItemEscape}, {"master", ConfigItemMaster}, {"playback", ConfigItemPlayback}, {"port", ConfigItemPort}, {"replay", ConfigItemReplay}, {"sslcredentials", ConfigItemSslcredentials}, {"sslcacertificatefile", ConfigItemSslcacertificatefile}, {"sslcacertificatepath", ConfigItemSslcacertificatepath}, {"sslrequired", ConfigItemSslrequired}, {"sslenabled", ConfigItemSslenabled}, {"striphigh", ConfigItemStriphigh}, {"username", ConfigItemUsername}, {(char *)0, (void *)0} }; ITEM keyTerminal[] = { {"attach", TerminalItemAttach}, {"attachsubst", TerminalItemAttachsubst}, {"detach", TerminalItemDetach}, {"detachsubst", TerminalItemDetachsubst}, {(char *)0, (void *)0} }; SECTION sections[] = { {"config", ConfigBegin, ConfigEnd, ConfigAbort, ConfigDestroy, keyConfig}, {"terminal", TerminalBegin, TerminalEnd, TerminalAbort, TerminalDestroy, keyTerminal}, {(char *)0, (void *)0, (void *)0, (void *)0, (void *)0} }; void ReadConf(char *filename, FLAG verbose) { FILE *fp; if ((FILE *)0 == (fp = fopen(filename, "r"))) { if (verbose == FLAGTRUE) Error("could not open `%s'", filename); return; } /* initialize the substition bits */ InitSubstCallback(); parserConfigDefault = pConfig; pConfig = (CONFIG *)0; parserTermDefault = pTerm; pTerm = (TERM *)0; ParseFile(filename, fp, 0); /* shouldn't really happen, but in case i screw up the stuff * ParseFile calls... */ if (pConfig == (CONFIG *)0) { if ((pConfig = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); } if (pTerm == (TERM *)0) { if ((pTerm = (TERM *)calloc(1, sizeof(TERM))) == (TERM *)0) OutOfMem(); } if (fDebug) { #define EMPTYSTR(x) x == (char *)0 ? "(null)" : x #define FLAGSTR(x) x == FLAGTRUE ? "true" : (x == FLAGFALSE ? "false" : "unset") CONDDEBUG((1, "pConfig->username = %s", EMPTYSTR(pConfig->username))); CONDDEBUG((1, "pConfig->master = %s", EMPTYSTR(pConfig->master))); CONDDEBUG((1, "pConfig->port = %s", EMPTYSTR(pConfig->port))); CONDDEBUG((1, "pConfig->escape = %s", EMPTYSTR(pConfig->escape))); CONDDEBUG((1, "pConfig->striphigh = %s", FLAGSTR(pConfig->striphigh))); CONDDEBUG((1, "pConfig->replay = %hu", pConfig->replay)); CONDDEBUG((1, "pConfig->playback = %hu", pConfig->playback)); #if HAVE_OPENSSL CONDDEBUG((1, "pConfig->sslcredentials = %s", EMPTYSTR(pConfig->sslcredentials))); CONDDEBUG((1, "pConfig->sslcacertificatefile = %s", EMPTYSTR(pConfig->sslcacertificatefile))); CONDDEBUG((1, "pConfig->sslcacertificatepath = %s", EMPTYSTR(pConfig->sslcacertificatepath))); CONDDEBUG((1, "pConfig->sslrequired = %s", FLAGSTR(pConfig->sslrequired))); CONDDEBUG((1, "pConfig->sslenabled = %s", FLAGSTR(pConfig->sslenabled))); #endif CONDDEBUG((1, "pTerm->attach = %s", EMPTYSTR(pTerm->attach))); CONDDEBUG((1, "pTerm->attachsubst = %s", EMPTYSTR(pTerm->attachsubst))); CONDDEBUG((1, "pTerm->detach = %s", EMPTYSTR(pTerm->detach))); CONDDEBUG((1, "pTerm->detachsubst = %s", EMPTYSTR(pTerm->detachsubst))); } fclose(fp); } conserver-8.2.4/console/readconf.h000066400000000000000000000014331344660520400171340ustar00rootroot00000000000000/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) */ typedef struct config { STRING *name; char *console; char *username; char *master; char *port; char *escape; FLAG striphigh; unsigned short replay; unsigned short playback; #if HAVE_OPENSSL char *sslcredentials; char *sslcacertificatefile; char *sslcacertificatepath; FLAG sslrequired; FLAG sslenabled; #endif } CONFIG; typedef struct term { STRING *name; char *attach; char *attachsubst; char *detach; char *detachsubst; } TERM; extern CONFIG *pConfig; extern TERM *pTerm; extern SUBST *substData; extern void ReadConf(char *, FLAG); extern void DestroyConfig(CONFIG *); extern void DestroyTerminal(TERM *); conserver-8.2.4/contrib/000077500000000000000000000000001344660520400151775ustar00rootroot00000000000000conserver-8.2.4/contrib/README000066400000000000000000000012521344660520400160570ustar00rootroot00000000000000Various contributions by folks.... chat Author: Greg Woods Synopsis: A send/expect program...source code from the NetBSD distribution and modified by Greg Woods to work a bit better with conserver solaris-package Author: Michael Sullivan Synopsis: Creates a solaris package redhat-rpm Author: Paul Heinlein Synopsis: Files for a Redhat-tuned RPM maketestcerts Author: Bryan Stansell Synopsis: Silly script I used to create test SSL certs I can't verify that these scripts will work for everyone. Hopefully they will be helpful. Bryan Stansell conserver-8.2.4/contrib/chat/000077500000000000000000000000001344660520400161165ustar00rootroot00000000000000conserver-8.2.4/contrib/chat/Makefile.in000066400000000000000000000020051344660520400201600ustar00rootroot00000000000000### Path settings datarootdir = @datarootdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sysconfdir = @sysconfdir@ mandir = @mandir@ ### Installation programs and flags INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ -s LN_S = @LN_S@ MKDIR = @MKDIR@ ### Compiler and link options CC = @CC@ CFLAGS = @CFLAGS@ DEFS = @DEFS@ CPPFLAGS = -I$(top_srcdir) -I$(srcdir) $(DEFS) @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @SET_MAKE@ ### Makefile rules - no user-servicable parts below CHAT_OBJS = chat.o CHAT_HDRS = ../../config.h ALL = chat all: $(ALL) chat: $(CHAT_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o chat $(CHAT_OBJS) $(LIBS) .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< clean: rm -f *~ *.o $(ALL) core distclean: clean rm -f Makefile install: chat $(MKDIR) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) chat $(DESTDIR)$(bindir) $(MKDIR) $(DESTDIR)$(mandir)/man1 $(INSTALL) chat.man $(DESTDIR)$(mandir)/man1/chat.1 .PHONY: clean distclean install conserver-8.2.4/contrib/chat/README000066400000000000000000000004751344660520400170040ustar00rootroot00000000000000Information from Greg Woods : This version of "chat" is derived from the NetBSD variant found in /usr/src/usr.sbin/pppd/chat. It has had a new '-I' command-line flag added so that it can ignore the fact it's not running on a TTY device (i.e. to allow it to work over a socket). conserver-8.2.4/contrib/chat/chat.c000066400000000000000000001016511344660520400172050ustar00rootroot00000000000000/* $NetBSD: chat.c,v 1.25 2001/09/24 13:22:38 wiz Exp $ */ /* * Chat -- a program for automatic session establishment (i.e. dial * the phone and log in). * * Standard termination codes: * 0 - successful completion of the script * 1 - invalid argument, expect string too large, etc. * 2 - error on an I/O operation or fatal error condition. * 3 - timeout waiting for a simple string. * 4 - the first string declared as "ABORT" * 5 - the second string declared as "ABORT" * 6 - ... and so on for successive ABORT strings. * * This software is in the public domain. * * ----------------- * 22-May-99 added environment substitutuion, enabled with -E switch. * Andreas Arens . * * 12-May-99 added a feature to read data to be sent from a file, * if the send string starts with @. Idea from gpk . * * added -T and -U option and \T and \U substitution to pass a phone * number into chat script. Two are needed for some ISDN TA applications. * Keith Dart * * * Added SAY keyword to send output to stderr. * This allows to turn ECHO OFF and to output specific, user selected, * text to give progress messages. This best works when stderr * exists (i.e.: pppd in nodetach mode). * * Added HANGUP directives to allow for us to be called * back. When HANGUP is set to NO, chat will not hangup at HUP signal. * We rely on timeouts in that case. * * Added CLR_ABORT to clear previously set ABORT string. This has been * dictated by the HANGUP above as "NO CARRIER" (for example) must be * an ABORT condition until we know the other host is going to close * the connection for call back. As soon as we have completed the * first stage of the call back sequence, "NO CARRIER" is a valid, non * fatal string. As soon as we got called back (probably get "CONNECT"), * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. * Note that CLR_ABORT packs the abort_strings[] array so that we do not * have unused entries not being reclaimed. * * In the same vein as above, added CLR_REPORT keyword. * * Allow for comments. Line starting with '#' are comments and are * ignored. If a '#' is to be expected as the first character, the * expect string must be quoted. * * * Francis Demierre * Thu May 15 17:15:40 MET DST 1997 * * * Added -r "report file" switch & REPORT keyword. * Robert Geer * * Added -s "use stderr" and -S "don't use syslog" switches. * June 18, 1997 * Karl O. Pinc * * * Added -e "echo" switch & ECHO keyword * Dick Streefland * * * Considerable updates and modifications by * Al Longyear * Paul Mackerras * * * The original author is: * * Karl Fox * Morning Star Technologies, Inc. * 1760 Zollinger Road * Columbus, OH 43221 * (614)451-1883 * */ #ifndef __STDC__ #define const #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef TERMIO #undef TERMIOS #define TERMIOS #endif #ifdef TERMIO #include #endif #ifdef TERMIOS #include #endif #define STR_LEN 1024 #ifndef SIGTYPE #define SIGTYPE void #endif #undef __P #undef __V #ifdef __STDC__ #include #define __V(x) x #define __P(x) x #else #include #define __V(x) (va_alist) va_dcl #define __P(x) () #define const #endif #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif #ifdef SUNOS extern int sys_nerr; extern char *sys_errlist[]; #define memmove(to, from, n) bcopy(from, to, n) #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ "unknown error") #endif char *program_name; #define MAX_ABORTS 50 #define MAX_REPORTS 50 #define DEFAULT_CHAT_TIMEOUT 45 int echo = 0; int verbose = 0; int to_log = 1; int to_stderr = 0; int Verbose = 0; int quiet = 0; int report = 0; int use_env = 0; int exit_code = 0; int do_set_tty = 1; FILE* report_fp = (FILE *) 0; char *report_file = (char *) 0; char *chat_file = (char *) 0; char *phone_num = (char *) 0; char *phone_num2 = (char *) 0; int timeout = DEFAULT_CHAT_TIMEOUT; int have_tty_parameters = 0; #ifdef TERMIO #define term_parms struct termio #define get_term_param(param) ioctl(0, TCGETA, param) #define set_term_param(param) ioctl(0, TCSETA, param) struct termio saved_tty_parameters; #endif #ifdef TERMIOS #define term_parms struct termios #define get_term_param(param) tcgetattr(0, param) #define set_term_param(param) tcsetattr(0, TCSANOW, param) struct termios saved_tty_parameters; #endif char *abort_string[MAX_ABORTS], *fail_reason = (char *)0, fail_buffer[50]; int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; int clear_abort_next = 0; char *report_string[MAX_REPORTS] ; char report_buffer[50] ; int n_reports = 0, report_next = 0, report_gathering = 0 ; int clear_report_next = 0; int say_next = 0, hup_next = 0; void *dup_mem __P((void *b, size_t c)); void *copy_of __P((char *s)); void usage __P((void)); void logf __P((const char *fmt, ...)); void fatal __P((int code, const char *fmt, ...)); SIGTYPE sigalrm __P((int signo)); SIGTYPE sigint __P((int signo)); SIGTYPE sigterm __P((int signo)); SIGTYPE sighup __P((int signo)); void unalarm __P((void)); void init __P((void)); void set_tty_parameters __P((void)); void echo_stderr __P((int)); void break_sequence __P((void)); void terminate __P((int status)); void do_file __P((char *chat_file)); int get_string __P((register char *string)); int put_string __P((register char *s)); int write_char __P((int c)); int put_char __P((int c)); int get_char __P((void)); void chat_send __P((register char *s)); char *character __P((int c)); void chat_expect __P((register char *s)); char *clean __P((register char *s, int sending)); void break_sequence __P((void)); void pack_array __P((char **array, int end)); char *expect_strtok __P((char *, char *)); int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */ int main __P((int, char *[])); void *dup_mem(b, c) void *b; size_t c; { void *ans = malloc (c); if (!ans) fatal(2, "memory error!"); memcpy (ans, b, c); return ans; } void *copy_of (s) char *s; { return dup_mem (s, strlen (s) + 1); } /* * chat [ -eEIvVsS ] [ -T number ] [ -U number ] [ -t timeout ] [ -f chat-file ] \ * [ -r report-file ] \ * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] * * Perform a UUCP-dialer-like chat script on stdin and stdout. */ int main(argc, argv) int argc; char **argv; { int option; int i; program_name = *argv; tzset(); while ((option = getopt(argc, argv, ":eEf:Ir:sSt:T:U:vV")) != -1) { switch (option) { case 'e': ++echo; break; case 'E': ++use_env; break; case 'v': ++verbose; break; case 'V': ++Verbose; break; case 's': ++to_stderr; break; case 'S': to_log = 0; break; case 'f': if (optarg != NULL) chat_file = copy_of(optarg); else usage(); break; case 't': if (optarg != NULL) timeout = atoi(optarg); else usage(); break; case 'I': do_set_tty = 0; break; case 'r': if (optarg) { if (report_fp != NULL) fclose (report_fp); report_file = copy_of (optarg); report_fp = fopen (report_file, "a"); if (report_fp != NULL) { if (verbose) fprintf (report_fp, "Opening \"%s\"...\n", report_file); report = 1; } } break; case 'T': if (optarg != NULL) phone_num = copy_of(optarg); else usage(); break; case 'U': if (optarg != NULL) phone_num2 = copy_of(optarg); else usage(); break; default: usage(); break; } } argc -= optind; argv += optind; /* * Default the report file to the stderr location */ if (report_fp == NULL) report_fp = stderr; if (to_log) { #ifdef ultrix openlog("chat", LOG_PID); #else openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); if (verbose) setlogmask(LOG_UPTO(LOG_INFO)); else setlogmask(LOG_UPTO(LOG_WARNING)); #endif } init(); if (chat_file != NULL) { if (argc) usage(); else do_file (chat_file); } else { for (i = 0; i < argc; i++) { chat_expect(argv[i]); if (++i < argc) chat_send(argv[i]); } } terminate(0); return 0; } /* * Process a chat script when read from a file. */ void do_file (chat_file) char *chat_file; { int linect, sendflg; char *sp, *arg, quote; char buf [STR_LEN]; FILE *cfp; cfp = fopen (chat_file, "r"); if (cfp == NULL) fatal(1, "%s -- open failed: %m", chat_file); linect = 0; sendflg = 0; while (fgets(buf, STR_LEN, cfp) != NULL) { sp = strchr (buf, '\n'); if (sp) *sp = '\0'; linect++; sp = buf; /* lines starting with '#' are comments. If a real '#' is to be expected, it should be quoted .... */ if ( *sp == '#' ) continue; while (*sp != '\0') { if (*sp == ' ' || *sp == '\t') { ++sp; continue; } if (*sp == '"' || *sp == '\'') { quote = *sp++; arg = sp; while (*sp != quote) { if (*sp == '\0') fatal(1, "unterminated quote (line %d)", linect); if (*sp++ == '\\') { if (*sp != '\0') ++sp; } } } else { arg = sp; while (*sp != '\0' && *sp != ' ' && *sp != '\t') ++sp; } if (*sp != '\0') *sp++ = '\0'; if (sendflg) chat_send (arg); else chat_expect (arg); sendflg = !sendflg; } } fclose (cfp); } /* * We got an error parsing the command line. */ void usage() { fprintf(stderr, "\ Usage: %s [-eEIsSvV] [-t timeout] [-r report-file]\n\ [-T phone-number] [-U phone-number2]\n\ {-f chat-file | chat-script}\n\ Chat-Script: [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]\n", program_name); exit(1); } char line[1024]; /* * Send a message to syslog and/or stderr. */ void logf __V((const char *fmt, ...)) { va_list args; #ifdef __STDC__ va_start(args, fmt); #else char *fmt; va_start(args); fmt = va_arg(args, char *); #endif vfmtmsg(line, sizeof(line), fmt, args); va_end(args); if (to_log) syslog(LOG_INFO, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); } /* * Print an error message and terminate. */ void fatal __V((int code, const char *fmt, ...)) { va_list args; #ifdef __STDC__ va_start(args, fmt); #else int code; char *fmt; va_start(args); code = va_arg(args, int); fmt = va_arg(args, char *); #endif vfmtmsg(line, sizeof(line), fmt, args); va_end(args); if (to_log) syslog(LOG_ERR, "%s", line); if (to_stderr) fprintf(stderr, "%s\n", line); terminate(code); } int alarmed = 0; SIGTYPE sigalrm(signo) int signo; { int flags; alarm(1); alarmed = 1; /* Reset alarm to avoid race window */ signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ if ((flags = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); if (verbose) logf("alarm"); } void unalarm() { int flags; if ((flags = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); } SIGTYPE sigint(signo) int signo; { fatal(2, "SIGINT"); } SIGTYPE sigterm(signo) int signo; { fatal(2, "SIGTERM"); } SIGTYPE sighup(signo) int signo; { fatal(2, "SIGHUP"); } void init() { signal(SIGINT, sigint); signal(SIGTERM, sigterm); signal(SIGHUP, sighup); set_tty_parameters(); signal(SIGALRM, sigalrm); alarm(0); alarmed = 0; } void set_tty_parameters() { term_parms t; if (!do_set_tty) return; #if defined(get_term_param) if (get_term_param (&t) < 0) fatal(2, "Can't get terminal parameters: %m"); saved_tty_parameters = t; have_tty_parameters = 1; t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; t.c_oflag |= OPOST | ONLCR; t.c_lflag = 0; t.c_cc[VERASE] = t.c_cc[VKILL] = 0; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; if (set_term_param (&t) < 0) fatal(2, "Can't set terminal parameters: %m"); #endif } void break_sequence() { #ifdef TERMIOS tcsendbreak (0, 0); #endif } void terminate(status) int status; { static int terminating = 0; if (terminating) exit(status); terminating = 1; echo_stderr(-1); /* * Allow the last of the report string to be gathered before we terminate. */ if (report_gathering) { int c, rep_len; rep_len = strlen(report_buffer); while (rep_len + 1 <= sizeof(report_buffer)) { alarm(1); c = get_char(); alarm(0); if (c < 0 || iscntrl(c)) break; report_buffer[rep_len] = c; ++rep_len; } report_buffer[rep_len] = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { if (verbose) fprintf (report_fp, "Closing \"%s\".\n", report_file); fclose (report_fp); report_fp = (FILE *) NULL; } #if defined(get_term_param) if (have_tty_parameters) { if (set_term_param (&saved_tty_parameters) < 0) fatal(2, "Can't restore terminal parameters: %m"); } #endif exit(status); } /* * 'Clean up' this string. */ char *clean(s, sending) register char *s; int sending; /* set to 1 when sending (putting) this string. */ { char temp[STR_LEN], env_str[STR_LEN], cur_chr; register char *s1, *phchar; int add_return = sending; #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) #define isalnumx(chr) ((((chr) >= '0') && ((chr) <= '9')) \ || (((chr) >= 'a') && ((chr) <= 'z')) \ || (((chr) >= 'A') && ((chr) <= 'Z')) \ || (chr) == '_') s1 = temp; while (*s) { cur_chr = *s++; if (cur_chr == '^') { cur_chr = *s++; if (cur_chr == '\0') { *s1++ = '^'; break; } cur_chr &= 0x1F; if (cur_chr != 0) { *s1++ = cur_chr; } continue; } if (use_env && cur_chr == '$') { /* ARI */ phchar = env_str; while (isalnumx(*s)) *phchar++ = *s++; *phchar = '\0'; phchar = getenv(env_str); if (phchar) while (*phchar) *s1++ = *phchar++; continue; } if (cur_chr != '\\') { *s1++ = cur_chr; continue; } cur_chr = *s++; if (cur_chr == '\0') { if (sending) { *s1++ = '\\'; *s1++ = '\\'; } break; } switch (cur_chr) { case 'b': *s1++ = '\b'; break; case 'c': if (sending && *s == '\0') add_return = 0; else *s1++ = cur_chr; break; case '\\': case 'K': case 'p': case 'd': if (sending) *s1++ = '\\'; *s1++ = cur_chr; break; case 'T': if (sending && phone_num) { for (phchar = phone_num; *phchar != '\0'; phchar++) *s1++ = *phchar; } else { *s1++ = '\\'; *s1++ = 'T'; } break; case 'U': if (sending && phone_num2) { for (phchar = phone_num2; *phchar != '\0'; phchar++) *s1++ = *phchar; } else { *s1++ = '\\'; *s1++ = 'U'; } break; case 'q': quiet = 1; break; case 'r': *s1++ = '\r'; break; case 'n': *s1++ = '\n'; break; case 's': *s1++ = ' '; break; case 't': *s1++ = '\t'; break; case 'N': if (sending) { *s1++ = '\\'; *s1++ = '\0'; } else *s1++ = 'N'; break; case '$': /* ARI */ if (use_env) { *s1++ = cur_chr; break; } /* FALL THROUGH */ default: if (isoctal (cur_chr)) { cur_chr &= 0x07; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; if (isoctal (*s)) { cur_chr <<= 3; cur_chr |= *s++ - '0'; } } if (cur_chr != 0 || sending) { if (sending && (cur_chr == '\\' || cur_chr == 0)) *s1++ = '\\'; *s1++ = cur_chr; } break; } if (sending) *s1++ = '\\'; *s1++ = cur_chr; break; } } if (add_return) *s1++ = '\r'; *s1++ = '\0'; /* guarantee closure */ *s1++ = '\0'; /* terminate the string */ return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ } /* * A modified version of 'strtok'. This version skips \ sequences. */ char *expect_strtok (s, term) char *s, *term; { static char *str = ""; int escape_flag = 0; char *result; /* * If a string was specified then do initial processing. */ if (s) str = s; /* * If this is the escape flag then reset it and ignore the character. */ if (*str) result = str; else result = (char *) 0; while (*str) { if (escape_flag) { escape_flag = 0; ++str; continue; } if (*str == '\\') { ++str; escape_flag = 1; continue; } /* * If this is not in the termination string, continue. */ if (strchr (term, *str) == (char *) 0) { ++str; continue; } /* * This is the terminator. Mark the end of the string and stop. */ *str++ = '\0'; break; } return (result); } /* * Process the expect string */ void chat_expect (s) char *s; { char *expect; char *reply; if (strcmp(s, "HANGUP") == 0) { ++hup_next; return; } if (strcmp(s, "ABORT") == 0) { ++abort_next; return; } if (strcmp(s, "CLR_ABORT") == 0) { ++clear_abort_next; return; } if (strcmp(s, "REPORT") == 0) { ++report_next; return; } if (strcmp(s, "CLR_REPORT") == 0) { ++clear_report_next; return; } if (strcmp(s, "TIMEOUT") == 0) { ++timeout_next; return; } if (strcmp(s, "ECHO") == 0) { ++echo_next; return; } if (strcmp(s, "SAY") == 0) { ++say_next; return; } /* * Fetch the expect and reply string. */ for (;;) { expect = expect_strtok (s, "-"); s = (char *) 0; if (expect == (char *) 0) return; reply = expect_strtok (s, "-"); /* * Handle the expect string. If successful then exit. */ if (get_string (expect)) return; /* * If there is a sub-reply string then send it. Otherwise any condition * is terminal. */ if (reply == (char *) 0 || exit_code != 3) break; chat_send (reply); } /* * The expectation did not occur. This is terminal. */ if (fail_reason) logf("Failed (%s)", fail_reason); else logf("Failed"); terminate(exit_code); } /* * Translate the input character to the appropriate string for printing * the data. */ char *character(c) int c; { static char string[10]; char *meta; meta = (c & 0x80) ? "M-" : ""; c &= 0x7F; if (c < 32) sprintf(string, "%s^%c", meta, (int)c + '@'); else if (c == 127) sprintf(string, "%s^?", meta); else sprintf(string, "%s%c", meta, c); return (string); } /* * process the reply string */ void chat_send (s) register char *s; { char file_data[STR_LEN]; if (say_next) { say_next = 0; s = clean(s, 1); write(2, s, strlen(s)); free(s); return; } if (hup_next) { hup_next = 0; if (strcmp(s, "OFF") == 0) signal(SIGHUP, SIG_IGN); else signal(SIGHUP, sighup); return; } if (echo_next) { echo_next = 0; echo = (strcmp(s, "ON") == 0); return; } if (abort_next) { char *s1; abort_next = 0; if (n_aborts >= MAX_ABORTS) fatal(2, "Too many ABORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long ABORT string ('%v')", s); abort_string[n_aborts++] = s1; if (verbose) logf("abort on (%v)", s); return; } if (clear_abort_next) { char *s1; int i; int old_max; int pack = 0; clear_abort_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) + 1 > sizeof(fail_buffer)) fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); old_max = n_aborts; for (i=0; i < n_aborts; i++) { if ( strcmp(s1,abort_string[i]) == 0 ) { free(abort_string[i]); abort_string[i] = NULL; pack++; n_aborts--; if (verbose) logf("clear abort on (%v)", s); } } free(s1); if (pack) pack_array(abort_string,old_max); return; } if (report_next) { char *s1; report_next = 0; if (n_reports >= MAX_REPORTS) fatal(2, "Too many REPORT strings"); s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) fatal(1, "Illegal or too-long REPORT string ('%v')", s); report_string[n_reports++] = s1; if (verbose) logf("report (%v)", s); return; } if (clear_report_next) { char *s1; int i; int old_max; int pack = 0; clear_report_next = 0; s1 = clean(s, 0); if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) fatal(1, "Illegal or too-long REPORT string ('%v')", s); old_max = n_reports; for (i=0; i < n_reports; i++) { if ( strcmp(s1,report_string[i]) == 0 ) { free(report_string[i]); report_string[i] = NULL; pack++; n_reports--; if (verbose) logf("clear report (%v)", s); } } free(s1); if (pack) pack_array(report_string,old_max); return; } if (timeout_next) { timeout_next = 0; timeout = atoi(s); if (timeout <= 0) timeout = DEFAULT_CHAT_TIMEOUT; if (verbose) logf("timeout set to %d seconds", timeout); return; } /* * The syntax @filename means read the string to send from the * file `filename'. */ if (s[0] == '@') { /* skip the @ and any following white-space */ char *fn = s; while (*++fn == ' ' || *fn == '\t') ; if (*fn != 0) { FILE *f; int n = 0; /* open the file and read until STR_LEN-1 bytes or end-of-file */ f = fopen(fn, "r"); if (f == NULL) fatal(1, "%s -- open failed: %m", fn); while (n < STR_LEN - 1) { int nr = fread(&file_data[n], 1, STR_LEN - 1 - n, f); if (nr < 0) fatal(1, "%s -- read error", fn); if (nr == 0) break; n += nr; } fclose(f); /* use the string we got as the string to send, but trim off the final newline if any. */ if (n > 0 && file_data[n-1] == '\n') --n; file_data[n] = 0; s = file_data; } } if (strcmp(s, "EOT") == 0) s = "^D\\c"; else if (strcmp(s, "BREAK") == 0) s = "\\K\\c"; if (!put_string(s)) fatal(1, "Failed"); } int get_char() { int status; char c; status = read(0, &c, 1); switch (status) { case 1: return ((int)c & 0x7F); default: logf("warning: read() on stdin returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin: %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int put_char(c) int c; { int status; char ch = c; usleep(10000); /* inter-character typing delay (?) */ status = write(1, &ch, 1); switch (status) { case 1: return (0); default: logf("warning: write() on stdout returned %d", status); case -1: if ((status = fcntl(0, F_GETFL, 0)) == -1) fatal(2, "Can't get file mode flags on stdin, %m"); if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) fatal(2, "Can't set file mode flags on stdin: %m"); return (-1); } } int write_char (c) int c; { if (alarmed || put_char(c) < 0) { alarm(0); alarmed = 0; if (verbose) { if (errno == EINTR || errno == EWOULDBLOCK) logf(" -- write timed out"); else logf(" -- write failed: %m"); } return (0); } return (1); } int put_string (s) register char *s; { quiet = 0; s = clean(s, 1); if (verbose) { if (quiet) logf("send (?????\?)"); /* backslash to avoid trigraph ??) */ else logf("send (%v)", s); } alarm(timeout); alarmed = 0; while (*s) { register char c = *s++; if (c != '\\') { if (!write_char (c)) return 0; continue; } c = *s++; switch (c) { case 'd': sleep(1); break; case 'K': break_sequence(); break; case 'p': usleep(10000); /* 1/100th of a second (arg is microseconds) */ break; default: if (!write_char (c)) return 0; break; } } alarm(0); alarmed = 0; return (1); } /* * Echo a character to stderr. * When called with -1, a '\n' character is generated when * the cursor is not at the beginning of a line. */ void echo_stderr(n) int n; { static int need_lf; char *s; switch (n) { case '\r': /* ignore '\r' */ break; case -1: if (need_lf == 0) break; /* fall through */ case '\n': write(2, "\n", 1); need_lf = 0; break; default: s = character(n); write(2, s, strlen(s)); need_lf = 1; break; } } /* * 'Wait for' this string to appear on this file descriptor. */ int get_string(string) register char *string; { char temp[STR_LEN]; int c, printed = 0, len, minlen; register char *s = temp, *end = s + STR_LEN; char *logged = temp; fail_reason = (char *)0; string = clean(string, 0); len = strlen(string); minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; if (verbose) logf("expect (%v)", string); if (len > STR_LEN) { logf("expect string is too long"); exit_code = 1; return 0; } if (len == 0) { if (verbose) logf("got it"); return (1); } alarm(timeout); alarmed = 0; while ( ! alarmed && (c = get_char()) >= 0) { int n, abort_len, report_len; if (echo) echo_stderr(c); if (verbose && c == '\n') { if (s == logged) logf(""); /* blank line */ else logf("%0.*v", s - logged, logged); logged = s + 1; } *s++ = c; if (verbose && s >= logged + 80) { logf("%0.*v", s - logged, logged); logged = s; } if (Verbose) { if (c == '\n') fputc( '\n', stderr ); else if (c != '\r') fprintf( stderr, "%s", character(c) ); } if (!report_gathering) { for (n = 0; n < n_reports; ++n) { if ((report_string[n] != (char*) NULL) && s - temp >= (report_len = strlen(report_string[n])) && strncmp(s - report_len, report_string[n], report_len) == 0) { time_t time_now = time ((time_t*) NULL); struct tm* tm_now = localtime (&time_now); strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); strcat (report_buffer, report_string[n]); report_string[n] = (char *) NULL; report_gathering = 1; break; } } } else { if (!iscntrl (c)) { int rep_len = strlen (report_buffer); report_buffer[rep_len] = c; report_buffer[rep_len + 1] = '\0'; } else { report_gathering = 0; fprintf (report_fp, "chat: %s\n", report_buffer); } } if (s - temp >= len && c == string[len - 1] && strncmp(s - len, string, len) == 0) { if (verbose) { if (s > logged) logf("%0.*v", s - logged, logged); logf(" -- got it\n"); } alarm(0); alarmed = 0; return (1); } for (n = 0; n < n_aborts; ++n) { if (s - temp >= (abort_len = strlen(abort_string[n])) && strncmp(s - abort_len, abort_string[n], abort_len) == 0) { if (verbose) { if (s > logged) logf("%0.*v", s - logged, logged); logf(" -- failed"); } alarm(0); alarmed = 0; exit_code = n + 4; strcpy(fail_reason = fail_buffer, abort_string[n]); return (0); } } if (s >= end) { if (logged < s - minlen) { if (verbose) logf("%0.*v", s - logged, logged); logged = s; } s -= minlen; memmove(temp, s, minlen); logged = temp + (logged - s); s = temp + minlen; } if (alarmed && verbose) logf("warning: alarm synchronization problem"); } alarm(0); if (verbose && printed) { if (alarmed) logf(" -- read timed out"); else logf(" -- read failed: %m"); } exit_code = 3; alarmed = 0; return (0); } /* * Gross kludge to handle Solaris versions >= 2.6 having usleep. */ #ifdef SOL2 #include #if MAXUID > 65536 /* then this is Solaris 2.6 or later */ #undef NO_USLEEP #endif #endif /* SOL2 */ #ifdef NO_USLEEP #include #include /* usleep -- support routine for 4.2BSD system call emulations last edit: 29-Oct-1984 D A Gwyn */ extern int select(); int usleep( usec ) /* returns 0 if ok, else -1 */ long usec; /* delay in microseconds */ { static struct { /* `timeval' */ long tv_sec; /* seconds */ long tv_usec; /* microsecs */ } delay; /* _select() timeout */ delay.tv_sec = usec / 1000000L; delay.tv_usec = usec % 1000000L; return select(0, (long *)0, (long *)0, (long *)0, &delay); } #endif void pack_array (array, end) char **array; /* The address of the array of string pointers */ int end; /* The index of the next free entry before CLR_ */ { int i, j; for (i = 0; i < end; i++) { if (array[i] == NULL) { for (j = i+1; j < end; ++j) if (array[j] != NULL) array[i++] = array[j]; for (; i < end; ++i) array[i] = NULL; break; } } } /* * vfmtmsg - format a message into a buffer. Like vsprintf except we * also specify the length of the output buffer, and we handle the * %m (error message) format. * Doesn't do floating-point formats. * Returns the number of chars put into buf. */ #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) int vfmtmsg(buf, buflen, fmt, args) char *buf; int buflen; const char *fmt; va_list args; { int c, i, n; int width, prec, fillch; int base, len, neg, quoted; unsigned long val = 0; char *str, *buf0; const char *f; unsigned char *p; char num[32]; static char hexchars[] = "0123456789abcdef"; buf0 = buf; --buflen; while (buflen > 0) { for (f = fmt; *f != '%' && *f != 0; ++f) ; if (f > fmt) { len = f - fmt; if (len > buflen) len = buflen; memcpy(buf, fmt, len); buf += len; buflen -= len; fmt = f; } if (*fmt == 0) break; c = *++fmt; width = prec = 0; fillch = ' '; if (c == '0') { fillch = '0'; c = *++fmt; } if (c == '*') { width = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { width = width * 10 + c - '0'; c = *++fmt; } } if (c == '.') { c = *++fmt; if (c == '*') { prec = va_arg(args, int); c = *++fmt; } else { while (isdigit(c)) { prec = prec * 10 + c - '0'; c = *++fmt; } } } str = 0; base = 0; neg = 0; ++fmt; switch (c) { case 'd': i = va_arg(args, int); if (i < 0) { neg = 1; val = -i; } else val = i; base = 10; break; case 'o': val = va_arg(args, unsigned int); base = 8; break; case 'x': val = va_arg(args, unsigned int); base = 16; break; case 'p': val = (unsigned long) va_arg(args, void *); base = 16; neg = 2; break; case 's': str = va_arg(args, char *); break; case 'c': num[0] = va_arg(args, int); num[1] = 0; str = num; break; case 'm': str = strerror(errno); break; case 'v': /* "visible" string */ case 'q': /* quoted string */ quoted = c == 'q'; p = va_arg(args, unsigned char *); if (fillch == '0' && prec > 0) { n = prec; } else { n = strlen((char *)p); if (prec > 0 && prec < n) n = prec; } while (n > 0 && buflen > 0) { c = *p++; --n; if (!quoted && c >= 0x80) { OUTCHAR('M'); OUTCHAR('-'); c -= 0x80; } if (quoted && (c == '"' || c == '\\')) OUTCHAR('\\'); if (c < 0x20 || (0x7f <= c && c < 0xa0)) { if (quoted) { OUTCHAR('\\'); switch (c) { case '\t': OUTCHAR('t'); break; case '\n': OUTCHAR('n'); break; case '\b': OUTCHAR('b'); break; case '\f': OUTCHAR('f'); break; default: OUTCHAR('x'); OUTCHAR(hexchars[c >> 4]); OUTCHAR(hexchars[c & 0xf]); } } else { if (c == '\t') OUTCHAR(c); else { OUTCHAR('^'); OUTCHAR(c ^ 0x40); } } } else OUTCHAR(c); } continue; default: *buf++ = '%'; if (c != '%') --fmt; /* so %z outputs %z etc. */ --buflen; continue; } if (base != 0) { str = num + sizeof(num); *--str = 0; while (str > num + neg) { *--str = hexchars[val % base]; val = val / base; if (--prec <= 0 && val == 0) break; } switch (neg) { case 1: *--str = '-'; break; case 2: *--str = 'x'; *--str = '0'; break; } len = num + sizeof(num) - 1 - str; } else { len = strlen(str); if (prec > 0 && len > prec) len = prec; } if (width > 0) { if (width > buflen) width = buflen; if ((n = width - len) > 0) { buflen -= n; for (; n > 0; --n) *buf++ = fillch; } } if (len > buflen) len = buflen; memcpy(buf, str, len); buf += len; buflen -= len; } *buf = 0; return buf - buf0; } conserver-8.2.4/contrib/chat/chat.man000066400000000000000000000446711344660520400175460ustar00rootroot00000000000000.\" -*- nroff -*- .\" manual page [] for chat 1.8 .\" Id: chat.8,v 1.9 1999/09/06 05:10:23 paulus Exp .\" SH section heading .\" SS subsection heading .\" LP paragraph .\" IP indented paragraph .\" TP hanging label .TH CHAT 8 "22 May 1999" "Chat Version 1.22" .SH "NAME" chat \- Automated conversational script with a modem .SH "SYNOPSIS" .B chat [ .I options ] .I script .SH "DESCRIPTION" .LP The \fIchat\fR program defines a conversational exchange between the computer and the modem. Its primary purpose is to establish the connection between the Point-to-Point Protocol Daemon (\fIpppd\fR) and the remote's \fIpppd\fR process. .SH "OPTIONS" .TP .B -f \fI Read the chat script from the chat \fIfile\fR. The use of this option is mutually exclusive with the chat script parameters. The user must have read access to the file. Multiple lines are permitted in the file. Space or horizontal tab characters should be used to separate the strings. .TP .B -t \fI Set the timeout for the expected string to be received. If the string is not received within the time limit then the reply string is not sent. An alternate reply may be sent or the script will fail if there is no alternate reply string. A failed script will cause the \fIchat\fR program to terminate with a non-zero error code. .TP .B -r \fI Set the file for output of the report strings. If you use the keyword \fIREPORT\fR, the resulting strings are written to this file. If this option is not used and you still use \fIREPORT\fR keywords, the \fIstderr\fR file is used for the report strings. .TP .B -e Start with the echo option turned on. Echoing may also be turned on or off at specific points in the chat script by using the \fIECHO\fR keyword. When echoing is enabled, all output from the modem is echoed to \fIstderr\fR. .TP .B -E Enables environment variable substituion within chat scripts using the standard \fI$xxx\fR syntax. .TP .B -v Request that the \fIchat\fR script be executed in a verbose mode. The \fIchat\fR program will then log the execution state of the chat script as well as all text received from the modem and the output strings sent to the modem. The default is to log through the SYSLOG; the logging method may be altered with the -S and -s flags. SYSLOGs are logged to facility LOG_LOCAL2. .TP .B -V Request that the \fIchat\fR script be executed in a stderr verbose mode. The \fIchat\fR program will then log all text received from the modem and the output strings sent to the modem to the stderr device. This device is usually the local console at the station running the chat or pppd program. .TP .B -s Use stderr. All log messages from '-v' and all error messages will be sent to stderr. .TP .B -S Do not use the SYSLOG. By default, error messages are sent to the SYSLOG. The use of -S will prevent both log messages from '-v' and error messages from being sent to the SYSLOG (to facility LOG_LOCAL2). .TP .B -T \fI Pass in an arbitary string, usually a phone number, that will be substituted for the \eT substitution metacharacter in a send string. .TP .B -U \fI Pass in a second string, usually a phone number, that will be substituted for the \eU substitution metacharacter in a send string. This is useful when dialing an ISDN terminal adapter that requires two numbers. .TP .B script If the script is not specified in a file with the \fI-f\fR option then the script is included as parameters to the \fIchat\fR program. .SH "CHAT SCRIPT" .LP The \fIchat\fR script defines the communications. .LP A script consists of one or more "expect-send" pairs of strings, separated by spaces, with an optional "subexpect-subsend" string pair, separated by a dash as in the following example: .IP ogin:-BREAK-ogin: ppp ssword: hello2u2 .LP This line indicates that the \fIchat\fR program should expect the string "ogin:". If it fails to receive a login prompt within the time interval allotted, it is to send a break sequence to the remote and then expect the string "ogin:". If the first "ogin:" is received then the break sequence is not generated. .LP Once it received the login prompt the \fIchat\fR program will send the string ppp and then expect the prompt "ssword:". When it receives the prompt for the password, it will send the password hello2u2. .LP A carriage return is normally sent following the reply string. It is not expected in the "expect" string unless it is specifically requested by using the \er character sequence. .LP The expect sequence should contain only what is needed to identify the string. Since it is normally stored on a disk file, it should not contain variable information. It is generally not acceptable to look for time strings, network identification strings, or other variable pieces of data as an expect string. .LP To help correct for characters which may be corrupted during the initial sequence, look for the string "ogin:" rather than "login:". It is possible that the leading "l" character may be received in error and you may never find the string even though it was sent by the system. For this reason, scripts look for "ogin:" rather than "login:" and "ssword:" rather than "password:". .LP A very simple script might look like this: .IP ogin: ppp ssword: hello2u2 .LP In other words, expect ....ogin:, send ppp, expect ...ssword:, send hello2u2. .LP In actual practice, simple scripts are rare. At the vary least, you should include sub-expect sequences should the original string not be received. For example, consider the following script: .IP ogin:--ogin: ppp ssword: hello2u2 .LP This would be a better script than the simple one used earlier. This would look for the same login: prompt, however, if one was not received, a single return sequence is sent and then it will look for login: again. Should line noise obscure the first login prompt then sending the empty line will usually generate a login prompt again. .SH "COMMENTS" Comments can be embedded in the chat script. A comment is a line which starts with the \fB#\fR (hash) character in column 1. Such comment lines are just ignored by the chat program. If a '#' character is to be expected as the first character of the expect sequence, you should quote the expect string, or give its octal value, `\e043'. In a script file if you want to wait for a prompt that starts with a '#' character, you would have to write something like this: .IP # Now wait for the prompt and send logout string .br \'# ' logout .SH "SENDING DATA FROM A FILE" If the string to send starts with an at sign (@), the rest of the string is taken to be the name of a file to read to get the string to send. If the last character of the data read is a newline, it is removed. The file can be a named pipe (or fifo) instead of a regular file. This provides a way for \fBchat\fR to communicate with another program, for example, a program to prompt the user and receive a password typed in. .SH "ABORT STRINGS" Many modems will report the status of the call as a string. These strings may be \fBCONNECTED\fR or \fBNO CARRIER\fR or \fBBUSY\fR. It is often desirable to terminate the script should the modem fail to connect to the remote. The difficulty is that a script would not know exactly which modem string it may receive. On one attempt, it may receive \fBBUSY\fR while the next time it may receive \fBNO CARRIER\fR. .LP These "abort" strings may be specified in the script using the \fIABORT\fR sequence. It is written in the script as in the following example: .IP ABORT BUSY ABORT 'NO CARRIER' '' ATZ OK ATDT5551212 CONNECT .LP This sequence will expect nothing; and then send the string ATZ. The expected response to this is the string \fIOK\fR. When it receives \fIOK\fR, the string ATDT5551212 to dial the telephone. The expected string is \fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the script is executed. However, should the modem find a busy telephone, it will send the string \fIBUSY\fR. This will cause the string to match the abort character sequence. The script will then fail because it found a match to the abort string. If it received the string \fINO CARRIER\fR, it will abort for the same reason. Either string may be received. Either string will terminate the \fIchat\fR script. .SH "CLR_ABORT STRINGS" This sequence allows for clearing previously set \fBABORT\fR strings. \fBABORT\fR strings are kept in an array of a pre-determined size (at compilation time); \fBCLR_ABORT\fR will reclaim the space for cleared entries so that new strings can use that space. .SH "SAY STRINGS" The \fBSAY\fR directive allows the script to send strings to the user at the terminal via standard error. If \fBchat\fR is being run by pppd, and pppd is running as a daemon (detached from its controlling terminal), standard error will normally be redirected to the file /etc/ppp/connect-errors. .LP \fBSAY\fR strings must be enclosed in single or double quotes. If carriage return and line feed are needed in the string to be output, you must explicitly add them to your string. .LP The SAY strings could be used to give progress messages in sections of the script where you want to have 'ECHO OFF' but still let the user know what is happening. An example is: .IP ABORT BUSY .br ECHO OFF .br SAY "Dialling your ISP...\en" .br \'' ATDT5551212 .br TIMEOUT 120 .br SAY "Waiting up to 2 minutes for connection ... " .br CONNECT '' .br SAY "Connected, now logging in ...\n" .br ogin: account .br ssword: pass .br $ \c SAY "Logged in OK ...\n" \fIetc ...\fR .LP This sequence will only present the SAY strings to the user and all the details of the script will remain hidden. For example, if the above script works, the user will see: .IP Dialling your ISP... .br Waiting up to 2 minutes for connection ... Connected, now logging in ... .br Logged in OK ... .LP .SH "REPORT STRINGS" A \fBreport\fR string is similar to the ABORT string. The difference is that the strings, and all characters to the next control character such as a carriage return, are written to the report file. .LP The report strings may be used to isolate the transmission rate of the modem's connect string and return the value to the chat user. The analysis of the report string logic occurs in conjunction with the other string processing such as looking for the expect string. The use of the same string for a report and abort sequence is probably not very useful, however, it is possible. .LP The report strings to no change the completion code of the program. .LP These "report" strings may be specified in the script using the \fIREPORT\fR sequence. It is written in the script as in the following example: .IP REPORT CONNECT ABORT BUSY '' ATDT5551212 CONNECT '' ogin: account .LP This sequence will expect nothing; and then send the string ATDT5551212 to dial the telephone. The expected string is \fICONNECT\fR. If the string \fICONNECT\fR is received the remainder of the script is executed. In addition the program will write to the expect-file the string "CONNECT" plus any characters which follow it such as the connection rate. .SH "CLR_REPORT STRINGS" This sequence allows for clearing previously set \fBREPORT\fR strings. \fBREPORT\fR strings are kept in an array of a pre-determined size (at compilation time); \fBCLR_REPORT\fR will reclaim the space for cleared entries so that new strings can use that space. .SH "ECHO" The echo options controls whether the output from the modem is echoed to \fIstderr\fR. This option may be set with the \fI-e\fR option, but it can also be controlled by the \fIECHO\fR keyword. The "expect-send" pair \fIECHO\fR \fION\fR enables echoing, and \fIECHO\fR \fIOFF\fR disables it. With this keyword you can select which parts of the conversation should be visible. For instance, with the following script: .IP ABORT 'BUSY' .br ABORT 'NO CARRIER' .br '' ATZ .br OK\er\en ATD1234567 .br \er\en \ec .br ECHO ON .br CONNECT \ec .br ogin: account .LP all output resulting from modem configuration and dialing is not visible, but starting with the \fICONNECT\fR (or \fIBUSY\fR) message, everything will be echoed. .SH "HANGUP" The HANGUP options control whether a modem hangup should be considered as an error or not. This option is useful in scripts for dialling systems which will hang up and call your system back. The HANGUP options can be \fBON\fR or \fBOFF\fR. .br When HANGUP is set OFF and the modem hangs up (e.g., after the first stage of logging in to a callback system), \fBchat\fR will continue running the script (e.g., waiting for the incoming call and second stage login prompt). As soon as the incoming call is connected, you should use the \fBHANGUP ON\fR directive to reinstall normal hang up signal behavior. Here is an (simple) example script: .IP ABORT 'BUSY' .br '' ATZ .br OK\er\en ATD1234567 .br \er\en \ec .br CONNECT \ec .br \'Callback login:' call_back_ID .br HANGUP OFF .br ABORT "Bad Login" .br \'Callback Password:' Call_back_password .br TIMEOUT 120 .br CONNECT \ec .br HANGUP ON .br ABORT "NO CARRIER" .br ogin:--BREAK--ogin: real_account .br \fIetc ...\fR .LP .SH "TIMEOUT" The initial timeout value is 45 seconds. This may be changed using the \fB-t\fR parameter. .LP To change the timeout value for the next expect string, the following example may be used: .IP ATZ OK ATDT5551212 CONNECT TIMEOUT 10 ogin:--ogin: TIMEOUT 5 assword: hello2u2 .LP This will change the timeout to 10 seconds when it expects the login: prompt. The timeout is then changed to 5 seconds when it looks for the password prompt. .LP The timeout, once changed, remains in effect until it is changed again. .SH "SENDING EOT" The special reply string of \fIEOT\fR indicates that the chat program should send an EOT character to the remote. This is normally the End-of-file character sequence. A return character is not sent following the EOT. .PR The EOT sequence may be embedded into the send string using the sequence \fI^D\fR. .SH "GENERATING BREAK" The special reply string of \fIBREAK\fR will cause a break condition to be sent. The break is a special signal on the transmitter. The normal processing on the receiver is to change the transmission rate. It may be used to cycle through the available transmission rates on the remote until you are able to receive a valid login prompt. .PR The break sequence may be embedded into the send string using the \fI\eK\fR sequence. .SH "ESCAPE SEQUENCES" The expect and reply strings may contain escape sequences. All of the sequences are legal in the reply string. Many are legal in the expect. Those which are not valid in the expect sequence are so indicated. .TP .B '' Expects or sends a null string. If you send a null string then it will still send the return character. This sequence may either be a pair of apostrophe or quote characters. .TP .B \eb represents a backspace character. .TP .B \ec Suppresses the newline at the end of the reply string. This is the only method to send a string without a trailing return character. It must be at the end of the send string. For example, the sequence hello\ec will simply send the characters h, e, l, l, o. .I (not valid in expect.) .TP .B \ed Delay for one second. The program uses sleep(1) which will delay to a maximum of one second. .I (not valid in expect.) .TP .B \eK Insert a BREAK .I (not valid in expect.) .TP .B \en Send a newline or linefeed character. .TP .B \eN Send a null character. The same sequence may be represented by \e0. .I (not valid in expect.) .TP .B \ep Pause for a fraction of a second. The delay is 1/10th of a second. .I (not valid in expect.) .TP .B \eq Suppress writing the string to the SYSLOG. The string ?????? is written to the log in its place. .I (not valid in expect.) .TP .B \er Send or expect a carriage return. .TP .B \es Represents a space character in the string. This may be used when it is not desirable to quote the strings which contains spaces. The sequence 'HI\ TIM' and HI\esTIM are the same. .TP .B \et Send or expect a tab character. .TP .B \eT Send the phone number string as specified with the \fI-T\fR option .I (not valid in expect.) .TP .B \eU Send the phone number 2 string as specified with the \fI-U\fR option .I (not valid in expect.) .TP .B \e\e Send or expect a backslash character. .TP .B \eddd Collapse the octal digits (ddd) into a single ASCII character and send that character. .I (some characters are not valid in expect.) .TP .B \^^C Substitute the sequence with the control character represented by C. For example, the character DC1 (17) is shown as \^^Q. .I (some characters are not valid in expect.) .SH "ENVIRONMENT VARIABLES" Environment variables are available within chat scripts, if the \fI-E\fR option was specified in the command line. The metacharacter \fI$\fR is used to introduce the name of the environment variable to substitute. If the substition fails, because the requested environment variable is not set, \fInothing\fR is replaced for the variable. .SH "TERMINATION CODES" The \fIchat\fR program will terminate with the following completion codes. .TP .B 0 The normal termination of the program. This indicates that the script was executed without error to the normal conclusion. .TP .B 1 One or more of the parameters are invalid or an expect string was too large for the internal buffers. This indicates that the program as not properly executed. .TP .B 2 An error occurred during the execution of the program. This may be due to a read or write operation failing for some reason or chat receiving a signal such as SIGINT. .TP .B 3 A timeout event occurred when there was an \fIexpect\fR string without having a "-subsend" string. This may mean that you did not program the script correctly for the condition or that some unexpected event has occurred and the expected string could not be found. .TP .B 4 The first string marked as an \fIABORT\fR condition occurred. .TP .B 5 The second string marked as an \fIABORT\fR condition occurred. .TP .B 6 The third string marked as an \fIABORT\fR condition occurred. .TP .B 7 The fourth string marked as an \fIABORT\fR condition occurred. .TP .B ... The other termination codes are also strings marked as an \fIABORT\fR condition. .LP Using the termination code, it is possible to determine which event terminated the script. It is possible to decide if the string "BUSY" was received from the modem as opposed to "NO DIAL TONE". While the first event may be retried, the second will probably have little chance of succeeding during a retry. .SH "SEE ALSO" Additional information about \fIchat\fR scripts may be found with UUCP documentation. The \fIchat\fR script was taken from the ideas proposed by the scripts used by the \fIuucico\fR program. .LP uucp(1), uucico(8) .SH "COPYRIGHT" The \fIchat\fR program is in public domain. This is not the GNU public license. If it breaks then you get to keep both pieces. conserver-8.2.4/contrib/maketestcerts000077500000000000000000000034131344660520400200040ustar00rootroot00000000000000#!/bin/sh # # This is a "simple" script that I've used to create test certificates # for conserver and it's OpenSSL bits. It's far from perfect...or useful # outside of my own purposes. If this helps, cool. In the end I put the # rootcert.pem file in my global certs directory (OPENSSL_ROOT/ssl/certs), # point the server to server.pem and point the client at client.pem. I # then run the c_rehash command. # # You can also use the sslcacertificatefile options to point the client/server # at rootcert.pem instead of populating the global repository # [ -f rootreq.pem -a -f rootkey.pem ] || cat < root.pem [ -f serverreq.pem -a -f serverkey.pem ] || cat < server.pem [ -f clientreq.pem -a -f clientkey.pem ] || cat < client.pem conserver-8.2.4/contrib/redhat-rpm/000077500000000000000000000000001344660520400172425ustar00rootroot00000000000000conserver-8.2.4/contrib/redhat-rpm/README000066400000000000000000000016571344660520400201330ustar00rootroot00000000000000Bits of some relavent communication from Paul Heinlein: Date: Tue, 17 Apr 2001 13:19:46 -0700 (PDT) From: Paul Heinlein To: Bryan Stansell Subject: Red Hat conserver init script for Linux Below my sig I've pasted an init script that will start and stop conserver on Red Hat Linux. It's set up to work with the chkconfig runlevel manager that ships with Red Hat. ---------------------------------------------- Date: Tue, 17 Apr 2001 15:58:18 -0700 (PDT) From: Paul Heinlein To: Bryan Stansell Subject: Re: Conserver patch If this spec file goes in the root directory of the distribution as conserver.spec, then Red Hat users will be able to build rpms by doing no more than % rpm -ta conserver-7.x.x.tar.gz I'll be happy to do any tweaking necessary to get this to work. conserver-8.2.4/contrib/redhat-rpm/conserver.defaults000066400000000000000000000001331344660520400227760ustar00rootroot00000000000000# server options #OPTIONS="-p 33000 -d" # run as different user that root #RUNAS=conservr conserver-8.2.4/contrib/redhat-rpm/conserver.init000077500000000000000000000022351344660520400221420ustar00rootroot00000000000000#!/bin/sh # # conserver -- serial-port console daemon # # chkconfig: 2345 92 08 # description: conserver is a serial-port console daemon # config: /etc/conserver.cf # DAEMON=/usr/sbin/conserver # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Source defaults . /etc/default/conserver # Check that networking is up. [ ${NETWORKING} = "no" ] && exit 0 # make sure conserver is installed and executable [ -x $DAEMON ] || exit 1 start() { echo -n "Starting conserver: " daemon --user "${RUNAS-}" $DAEMON ${OPTIONS--d} RETVAL=$? [ "$RETVAL" = 0 ] && touch /var/lock/subsys/conserver echo } stop() { echo -n "Shutting down conserver: " killproc conserver RETVAL=$? [ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/conserver echo } # See how we were called. case "$1" in start) start ;; stop) stop ;; status) status conserver ;; restart) stop start ;; reload) echo -n "Reloading conserver: " killproc conserver -HUP RETVAL=$? echo ;; *) echo "Usage: conserver {start|stop|restart|reload|status}" RETVAL=1 esac exit $RETVAL conserver-8.2.4/contrib/redhat-rpm/conserver.spec000066400000000000000000000120201344660520400221170ustar00rootroot00000000000000# # rpm spec file for conserver, but I don't think it'll work on any # platform that doesn't have red hat rpm >= 4.0.2 installed. # %define pkg conserver %define ver # define the name of the machine on which the main conserver # daemon will be running if you don't want to use the default # hostname (console) %define master console # what red hat (or other distibution) version are you running? %define distver 1 # compile arguments. defaults to 0 # example: rpmbuild -bb conserver.spec --with openssl %define with_openssl %{?_with_openssl: 1} %{?!_with_openssl: 0} %define with_libwrap %{?_with_libwrap: 1} %{?!_with_libwrap: 0} %define with_pam %{?_with_pam: 1} %{?!_with_pam: 0} %define with_dmalloc %{?_with_dmalloc: 1} %{?!_with_dmalloc: 0} %define with_freeipmi %{?_with_freeipmi: 1} %{?!_with_freeipmi: 0} # additionally you can use macros logfile pidfile # example: rpmbuild -bb conserver.spec --define "pidfile /var/run/conserver/pid" Name: %{pkg} Version: %{ver} Release: %{distver} License: BSD Summary: Serial console server daemon/client Group: System Environment/Daemons URL: http://www.conserver.com/ Source: http://www.conserver.com/%{pkg}-%{ver}.tar.gz BuildRoot: %{_tmppath}/%{pkg}-buildroot %if %{with_openssl} Requires: openssl BuildRequires: openssl-devel %endif %if %{with_pam} BuildRequires: pam-devel %endif %if %{with_libwrap} Requires: tcp_wrappers %endif %if %{with_dmalloc} Requires: dmalloc BuildRequires: dmalloc %endif %if %{with_freeipmi} Requires: freeipmi BuildRequires: freeipmi-devel %endif Prefix: %{_prefix} %package server Summary: Serial console server daemon Group: System Environment/Daemons %package client Summary: Serial console server client Group: Applications/Internet %description Conserver is a daemon that allows multiple users to watch a serial console at the same time. It can log the data, allows users to take write-access of a console (one at a time), and has a variety of bells and whistles to accentuate that basic functionality. %description server conserver-server is a daemon that allows multiple users to watch a serial console at the same time. It can log the data, allows users to take write-access of a console (one at a time), and has a variety of bells and whistles to accentuate that basic functionality. %description client conserver-client to connect to conserver-server using a tcp port. Allows multiple users to watch a serial console at the same time. %prep %{__rm} -rf %{buildroot} %setup -q %build # we don't want to install the solaris conserver.rc file f="conserver/Makefile.in" %{__mv} $f $f.orig %{__sed} -e 's/^.*conserver\.rc.*$//' < $f.orig > $f %configure %{?_with_openssl} %{?_with_libwrap} %{?_with_dmalloc} %{?_with_freeipmi} %{?_with_pam} %{?logfile: --with-logfile=%{logfile}} %{?pidfile: --with-pidfile=%{pidfile}} %{?master: --with-master=%{master}} make %install %{makeinstall} # put commented copies of the sample configure files in the # system configuration directory %{__mkdir_p} %{buildroot}/%{_sysconfdir} %{__sed} -e 's/^/#/' \ < conserver.cf/conserver.cf \ > %{buildroot}/%{_sysconfdir}/conserver.cf %{__sed} -e 's/^/#/' \ < conserver.cf/conserver.passwd \ > %{buildroot}/%{_sysconfdir}/conserver.passwd # install copy of init script %{__mkdir_p} %{buildroot}/%{_initrddir} %{__cp} contrib/redhat-rpm/conserver.init %{buildroot}/%{_initrddir}/conserver # install copy of init script defaults %{__mkdir_p} %{buildroot}/%{_sysconfdir}/default %{__cp} contrib/redhat-rpm/conserver.defaults %{buildroot}/%{_sysconfdir}/default/conserver %clean %{__rm} -rf %{buildroot} %post server if [ -x %{_initrddir}/conserver ]; then /sbin/chkconfig --add conserver fi # make sure /etc/services has a conserver entry if ! egrep '\' /etc/services > /dev/null 2>&1 ; then echo "console 782/tcp conserver" >> /etc/services fi %preun server if [ "$1" = 0 ]; then if [ -x %{_initrddir}/conserver ]; then %{_initrddir}/conserver stop /sbin/chkconfig --del conserver fi fi # we need this even if empty #%files %files server %defattr(-,root,root) %doc CHANGES FAQ INSTALL README conserver.cf %config(noreplace) %{_sysconfdir}/conserver.cf %config(noreplace) %{_sysconfdir}/conserver.passwd %config(noreplace) %{_sysconfdir}/default/conserver %attr(555,root,root) %{_initrddir}/conserver %{_libdir}/conserver/convert %{_mandir}/man8/conserver.8.gz %{_mandir}/man5/conserver.cf.5.gz %{_mandir}/man5/conserver.passwd.5.gz %{_datadir}/examples/conserver/conserver.cf %{_datadir}/examples/conserver/conserver.passwd %{_sbindir}/conserver %files client %defattr(-,root,root) %doc CHANGES FAQ INSTALL README %{_bindir}/console %{_mandir}/man1/console.1.gz %changelog * Wed Oct 14 2009 Jodok Ole Muellers - Changed the conserver.spec file to create separate subpackages for client and server by using the %package directive. * Wed Sep 25 2009 Fabien Wernli - added configure prerequisites * Thu Sep 24 2009 Fabien Wernli - added prefix to configure - changed some hardcoded values to proper macros: didn't work on x64 lib -> lib64 conserver-8.2.4/contrib/solaris-package/000077500000000000000000000000001344660520400202445ustar00rootroot00000000000000conserver-8.2.4/contrib/solaris-package/Makefile000066400000000000000000000032051344660520400217040ustar00rootroot00000000000000# Makefile for System V package PKGNAME = conserver # where to put package PKGSPOOLDIR = /var/spool/pkg # where package's files ultimately go (when package is installed) PKGROOT = /opt # temporary directory used while building package INSTALLROOT = /tmp/conserverinstallroot BUILDDIR = ../.. # temporary install directories BINDIR = $(INSTALLROOT)/bin LIBDIR = $(INSTALLROOT)/lib MAN1MDIR = $(INSTALLROOT)/share/man/man1m MAN4DIR = $(INSTALLROOT)/share/man/man4 MAN1MEXT = 1m MAN4EXT = 4 # command to correct section numbers and file names in manual pages FIXMANCMD = sed -e '/^\.TH/s/8/1m/' -e 's/([18]L)/(1m)/g' -e 's/(5L)/(4)/g' -e 's|/etc/|/etc/opt/conserver/|' -e 's|/usr/local/lib/|/etc/opt/conserver/|' # command to change program location in script FIXSCRIPTCMD = sed -e 's!/usr/local!$(PKGROOT)!' package: pkginfo prototype fakeinstall test -d $(PKGSPOOLDIR) || mkdir $(PKGSPOOLDIR) pkgmk -d $(PKGSPOOLDIR) -o -a `uname -p` -r $(INSTALLROOT) $(RM) -r $(INSTALLROOT) fakeinstall: mkdir -p $(BINDIR) $(LIBDIR) $(MAN1MDIR) $(MAN4DIR) /usr/ucb/install -cs $(BUILDDIR)/conserver/conserver $(BINDIR)/conserver /usr/ucb/install -cs $(BUILDDIR)/console/console $(BINDIR)/console $(FIXMANCMD) man_tbl_header $(BUILDDIR)/conserver/conserver.man > $(MAN1MDIR)/conserver.$(MAN1MEXT) $(FIXMANCMD) man_tbl_header $(BUILDDIR)/console/console.man > $(MAN1MDIR)/console.$(MAN1MEXT) $(FIXMANCMD) $(BUILDDIR)/conserver.cf/conserver.cf.man > $(MAN4DIR)/conserver.cf.$(MAN4EXT) $(FIXMANCMD) $(BUILDDIR)/conserver.cf/conserver.passwd.man > $(MAN4DIR)/conserver.passwd.$(MAN4EXT) $(FIXSCRIPTCMD) $(BUILDDIR)/conserver/conserver.rc > $(LIBDIR)/conserver.rc conserver-8.2.4/contrib/solaris-package/man_tbl_header000066400000000000000000000000061344660520400231070ustar00rootroot00000000000000'\" t conserver-8.2.4/contrib/solaris-package/pkginfo000066400000000000000000000002511344660520400216220ustar00rootroot00000000000000PKG="conserver" NAME="Console server and client" CATEGORY="system" VERSION= DESC="Console server and client" CLASSES=none ARCH=sparc VENDOR="conserver.com" BASEDIR=/opt conserver-8.2.4/contrib/solaris-package/prototype000066400000000000000000000007671344660520400222460ustar00rootroot00000000000000!search . i pkginfo d none bin 0755 root bin f none bin/conserver 0755 bin bin f none bin/console 0755 bin bin d none lib 0755 root bin f none lib/conserver.rc 0644 root root d none share 0755 root bin d none share/man 0755 bin bin d none share/man/man1m 0755 bin bin f none share/man/man1m/conserver.1m 0644 bin bin f none share/man/man1m/console.1m 0644 bin bin d none share/man/man5 0755 bin bin f none share/man/man5/conserver.cf.5 0644 bin bin f none share/man/man5/conserver.passwd.5 0644 bin bin conserver-8.2.4/install-sh000077500000000000000000000127361344660520400155540ustar00rootroot00000000000000#!/bin/sh # # install - install a program, script, or datafile # This comes from X11R5 (mit/util/scripts/install.sh). # # Copyright 1991 by the Massachusetts Institute of Technology # # Permission to use, copy, modify, distribute, and sell this software and its # documentation for any purpose is hereby granted without fee, provided that # the above copyright notice appear in all copies and that both that # copyright notice and this permission notice appear in supporting # documentation, and that the name of M.I.T. not be used in advertising or # publicity pertaining to distribution of the software without specific, # written prior permission. M.I.T. makes no representations about the # suitability of this software for any purpose. It is provided "as is" # without express or implied warranty. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. It can only install one file at a time, a restriction # shared with many OS's install programs. # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit="${DOITPROG-}" # put in absolute paths if you don't have them in your path; or use env. vars. mvprog="${MVPROG-mv}" cpprog="${CPPROG-cp}" chmodprog="${CHMODPROG-chmod}" chownprog="${CHOWNPROG-chown}" chgrpprog="${CHGRPPROG-chgrp}" stripprog="${STRIPPROG-strip}" rmprog="${RMPROG-rm}" mkdirprog="${MKDIRPROG-mkdir}" transformbasename="" transform_arg="" instcmd="$mvprog" chmodcmd="$chmodprog 0755" chowncmd="" chgrpcmd="" stripcmd="" rmcmd="$rmprog -f" mvcmd="$mvprog" src="" dst="" dir_arg="" while [ x"$1" != x ]; do case $1 in -c) instcmd="$cpprog" shift continue;; -d) dir_arg=true shift continue;; -m) chmodcmd="$chmodprog $2" shift shift continue;; -o) chowncmd="$chownprog $2" shift shift continue;; -g) chgrpcmd="$chgrpprog $2" shift shift continue;; -s) stripcmd="$stripprog" shift continue;; -t=*) transformarg=`echo $1 | sed 's/-t=//'` shift continue;; -b=*) transformbasename=`echo $1 | sed 's/-b=//'` shift continue;; *) if [ x"$src" = x ] then src=$1 else # this colon is to work around a 386BSD /bin/sh bug : dst=$1 fi shift continue;; esac done if [ x"$src" = x ] then echo "install: no input file specified" exit 1 else true fi if [ x"$dir_arg" != x ]; then dst=$src src="" if [ -d $dst ]; then instcmd=: chmodcmd="" else instcmd=mkdir fi else # Waiting for this to be detected by the "$instcmd $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if [ -f $src -o -d $src ] then true else echo "install: $src does not exist" exit 1 fi if [ x"$dst" = x ] then echo "install: no destination specified" exit 1 else true fi # If destination is a directory, append the input filename; if your system # does not like double slashes in filenames, you may need to add some logic if [ -d $dst ] then dst="$dst"/`basename $src` else true fi fi ## this sed command emulates the dirname command dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` # Make sure that the destination directory exists. # this part is taken from Noah Friedman's mkinstalldirs script # Skip lots of stat calls in the usual case. if [ ! -d "$dstdir" ]; then defaultIFS=' ' IFS="${IFS-${defaultIFS}}" oIFS="${IFS}" # Some sh's can't handle IFS=/ for some reason. IFS='%' set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` IFS="${oIFS}" pathcomp='' while [ $# -ne 0 ] ; do pathcomp="${pathcomp}${1}" shift if [ ! -d "${pathcomp}" ] ; then $mkdirprog "${pathcomp}" else true fi pathcomp="${pathcomp}/" done fi if [ x"$dir_arg" != x ] then $doit $instcmd $dst && if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi else # If we're going to rename the final executable, determine the name now. if [ x"$transformarg" = x ] then dstfile=`basename $dst` else dstfile=`basename $dst $transformbasename | sed $transformarg`$transformbasename fi # don't allow the sed command to completely eliminate the filename if [ x"$dstfile" = x ] then dstfile=`basename $dst` else true fi # Make a temp file name in the proper directory. dsttmp=$dstdir/#inst.$$# # Move or copy the file name to the temp name $doit $instcmd $src $dsttmp && trap "rm -f ${dsttmp}" 0 && # and set any options; do chmod last to preserve setuid bits # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $instcmd $src $dsttmp" command. if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && # Now rename the file to the real destination. $doit $rmcmd -f $dstdir/$dstfile && $doit $mvcmd $dsttmp $dstdir/$dstfile fi && exit 0 conserver-8.2.4/package/000077500000000000000000000000001344660520400151325ustar00rootroot00000000000000conserver-8.2.4/package/README.md000066400000000000000000000011101344660520400164020ustar00rootroot00000000000000Creating a new release ---------------------- - Create a new release branch `git checkout -b release-vx.y.z` - Edit `conserver/version.h` - Update `CHANGES` with output of `./package/create-changes vx.y.z..` - Double-check and merge release branch - Run `./package/make-and-stage-release` to create distribution, pgp sign, tag release, and push to github Requirements: - autoconf - githubrelease (pypi) - gpg Publishing a release -------------------- - Use github to promote from draft or use the command output from `make-and-stage-release` - Send announcement on mailing lists conserver-8.2.4/package/create-changes000077500000000000000000000006321344660520400177320ustar00rootroot00000000000000#!/bin/bash set -e [ -z "$1" ] && echo "Usage: $0 tag" && exit 1 range="$1" date=`./package/get-version date` changedate=`date -j -f '%Y/%m/%d' "$date" '+%B %-d, %Y'` echo "version `./package/get-version number` ($changedate):" git log --no-merges --pretty=tformat:' - %s (%an <%ae>)' "$range" | tail -r | awk '{if (! l[$0]) {l[$0]++; print}}' | sed -e 's/ *(Bryan Stansell )$//' conserver-8.2.4/package/get-version000077500000000000000000000011701344660520400173210ustar00rootroot00000000000000#!/bin/sh set -e # awk gets stdin from /dev/null 'cause when autoconf runs this via m4_esyscmd_s, # stdin is closed and awk assumes there will always be an open stdin and you end # up with a bogus message: # # awk: i/o error occurred while closing /dev/stdin # input record number 20, file conserver/version.h # source line number 1 # # case "$1" in number) awk '$2=="VERSION_MAJOR"{maj=$NF} $2=="VERSION_MINOR"{min=$NF} $2=="VERSION_REV"{rev=$NF} END{print maj "." min "." rev}' conserver/version.h < /dev/null ;; date) awk '$2=="VERSION_DATE"{print $NF}' conserver/version.h < /dev/null | tr -d '"' ;; esac conserver-8.2.4/package/make-and-stage-release000077500000000000000000000033201344660520400212520ustar00rootroot00000000000000#!/bin/bash set -e local=false && [ "$1" = "local" ] && local=true [ -f conserver/version.h ] || { echo "you are in the wrong place" ; exit 1; } ver=`./package/get-version number` if [ ! -f ../conserver-$ver.tar.gz ] || $local; then ( [ -d build ] && rm -rf build mkdir build git archive --format=tar.gz --prefix=conserver-$ver/ v$ver | (cd build; tar zxf -) cd build/conserver-$ver ../../package/setup-configure rm -rf package sed -i "" -e "/^%define ver/s| ver.*| ver $ver|" contrib/redhat-rpm/conserver.spec sed -i "" -e "/^VERSION=/s|.*|VERSION=\"$ver\"|" contrib/solaris-package/pkginfo ) $local && exit echo "Creating ../conserver-$ver.tar.gz" tar zcf ../conserver-$ver.tar.gz -C build conserver-$ver rm -rf build [ -f ../conserver-$ver.tar.gz.asc ] && rm ../conserver-$ver.tar.gz.asc fi if [ ! -f ../conserver-$ver.tar.gz.asc ]; then echo "Signing ../conserver-$ver.tar.gz" gpg -ab --local-user bryan@conserver.com ../conserver-$ver.tar.gz fi body="\`\`\` `sed -ne '/^ver/,/^$/p' CHANGES | sed -e '/^$/,$d'` \`\`\`" ls -l ../conserver-$ver.tar.gz* echo "Ready to push (y/N)?" read i if [ "$i" = "y" ]; then git tag -a -m "Release $ver" v$ver && git push githubrelease release conserver/conserver create --name conserver-$ver --body "$body" v$ver ../conserver-$ver.tar.gz* echo You can publish this release on the website or with: echo " "githubrelease release conserver/conserver publish v$ver else echo Ok, here is the command I would have used: echo " "git tag -a -m \""Release $ver"\" v$ver \&\& git push echo " "githubrelease release conserver/conserver create --name conserver-$ver --body \""$body"\" v$ver ../conserver-$ver.tar.gz* fi conserver-8.2.4/package/run-gindent000077500000000000000000000006071344660520400173150ustar00rootroot00000000000000#!/bin/bash gindent -npro -i4 -ip4 -cli4 -br -brs -cdw -ce -npcs -ncs -nhnl -l75 -lc75 -nbbo -ppi1 ` echo -T SOCKADDR_STYPE echo -T INADDR_STYPE echo -T time_t echo -T X509_STORE_CTX echo -T FILE echo -T SSL echo -T fd_set echo -T pam_handle_t echo -T SSL_CTX echo -T DH sed -ne '/typedef/,/}/p' */*.[ch] | grep '^[ ]*}' | sed -e 's/[ ]*}/-T/' -e 's/;//' | sort -u ` */*.[ch] conserver-8.2.4/package/setup-configure000077500000000000000000000010701344660520400201750ustar00rootroot00000000000000#!/bin/sh set -e curl -s -o config.guess 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess' curl -s -o config.sub 'https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub' # We set lang to avoid this with the gcc docker image on cirrus: # # ./package/setup-configure # perl: warning: Setting locale failed. # perl: warning: Please check that your locale settings: # LANGUAGE = (unset), # LC_ALL = (unset), # LANG = "en_US.UTF-8" # are supported and installed on your system. LANG=C autoreconf conserver-8.2.4/test/000077500000000000000000000000001344660520400145165ustar00rootroot00000000000000conserver-8.2.4/test/dotest000077500000000000000000000046571344660520400157620ustar00rootroot00000000000000#!/bin/sh pid=0 testnum=0 exitval=0 cleanup() { [ -f test.out ] && rm -f test.out [ -f c.cf ] && rm -f c.cf [ $pid -eq 0 ] && return 0 kill $pid for i in *.log; do [ "$i" != "conserver.log" ] && [ -f "$i" ] && rm -f "$i"; done [ "$exitval" = 0 ] && rm -f conserver.log [ -d 127.0.0.1 ] && sleep 1 && rm -rf 127.0.0.1 exit $exitval } dotest() { testnum=`expr $testnum + 1` $ECHO "executing test #$testnum...$EE" if [ "$2" ]; then eval "$2" > test.out 2>&1 else echo "$1" | \ ../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 shell > test.out 2>&1 fi if [ "$record" ]; then echo "recorded" mv test.out results/test$testnum else if [ -f results/test$testnum ]; then if diff -i results/test$testnum >test$testnum.diff test.out 2>&1; then echo "succeeded" rm -f test$testnum.diff else echo "failed (diffs in test$testnum.diff)" exitval=1 fi else echo "unknown (not recorded)" fi rm -f test.out fi } [ ! -f ../conserver/conserver -o ! -f ../console/console ] && \ echo 'binaries do not exist - did you run make yet?' && exit 1 trap cleanup 15 if [ "`echo -n`" = "-n" ]; then ECHO="echo" EE="\c" else ECHO="echo -n" EE="" fi $ECHO "starting conserver...$EE" rm -f c.cf cp test1.cf c.cf ../conserver/conserver -M 127.0.0.1 -p 7777 -v -C c.cf \ -P test.passwd -m 32 > conserver.log 2>&1 & pid=$! echo "pid $pid" sleep 3 [ ! -d results ] && mkdir results dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -u | sed -e 's/[0-9][0-9]*//g' -e 's/[ ][ ]*/ /g'" dotest 'c?c.' dotest 'cl?c.' dotest 'cdc.' dotest 'coc.' echo "moving in second config file" rm -f c.cf cp test2.cf c.cf kill -1 $pid sleep 3 dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -u | sed -e 's/[0-9][0-9]*//g' -e 's/[ ][ ]*/ /g'" dotest 'c?c.' dotest 'cl?c.' dotest 'cdc.' dotest 'cocacoc.' dotest EVAL "echo 'tu.' | ../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -e 'tu' shell" dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -R | sed -e 's/ [^ ]*$//'" dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -x | sed -e 's/ on [^ ]* */ on /'" dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -x sh | sed -e '1s/^[^:]*//'" dotest EVAL "../console/console -n -C /dev/null -M 127.0.0.1 -p 7777 -x shell | sed -e 's/ on [^ ]* */ on /'" cleanup conserver-8.2.4/test/results/000077500000000000000000000000001344660520400162175ustar00rootroot00000000000000conserver-8.2.4/test/results/test1000066400000000000000000000000441344660520400172000ustar00rootroot00000000000000 shell up shell up conserver-8.2.4/test/results/test10000066400000000000000000000001261344660520400172610ustar00rootroot00000000000000[`shell' -- console is down] [Enter `^Ec?' for help] [up] [ok] [up] [disconnect] conserver-8.2.4/test/results/test11000066400000000000000000000000451344660520400172620ustar00rootroot00000000000000[Enter `tu?' for help] [disconnect] conserver-8.2.4/test/results/test12000066400000000000000000000000371344660520400172640ustar00rootroot00000000000000version `conserver.com version conserver-8.2.4/test/results/test13000066400000000000000000000001731344660520400172660ustar00rootroot00000000000000 shellb on at Local shella on at Local shell on at Local conserver-8.2.4/test/results/test14000066400000000000000000000001141344660520400172620ustar00rootroot00000000000000: ambiguous console abbreviation, `sh' choices are shellb, shella, shell conserver-8.2.4/test/results/test15000066400000000000000000000000511344660520400172630ustar00rootroot00000000000000 shell on at Local conserver-8.2.4/test/results/test2000066400000000000000000000022771344660520400172130ustar00rootroot00000000000000[Enter `^Ec?' for help] [help] . disconnect ; move to another console a attach read/write b send broadcast message c toggle flow control d down a console e change escape sequence f force attach read/write g group info i information dump L toggle logging on/off l? break sequence list l0 send break per config file l1-9a-z send specific break sequence m display message of the day n write a note to the logfile o (re)open the tty and log file p playback the last 60 lines P set number of playback lines r replay the last 20 lines R set number of replay lines s spy mode (read only) u show host status v show version info w who is on this console x show console baud info z suspend the connection ! invoke task | attach local command ? print this message ignore/abort command ^R replay the last line \ooo send character by octal code [disconnect] conserver-8.2.4/test/results/test3000066400000000000000000000002571344660520400172100ustar00rootroot00000000000000[Enter `^Ec?' for help] [halt list] 0 - 250ms, `\z' 1 - 250ms, `\z' 2 - 250ms, `\r~^b' 3 - 250ms, `#.' 4 - 600ms, `\r\d~\d^b' 5 - 250ms, `\rtest\r' [disconnect] conserver-8.2.4/test/results/test4000066400000000000000000000000631344660520400172040ustar00rootroot00000000000000[Enter `^Ec?' for help] [line down] [disconnect] conserver-8.2.4/test/results/test5000066400000000000000000000001121344660520400172000ustar00rootroot00000000000000[`shell' -- console is down] [Enter `^Ec?' for help] [up] [disconnect] conserver-8.2.4/test/results/test6000066400000000000000000000000701344660520400172040ustar00rootroot00000000000000 shellb up shella up shell up conserver-8.2.4/test/results/test7000066400000000000000000000022771344660520400172200ustar00rootroot00000000000000[Enter `^Ec?' for help] [help] . disconnect ; move to another console a attach read/write b send broadcast message c toggle flow control d down a console e change escape sequence f force attach read/write g group info i information dump L toggle logging on/off l? break sequence list l0 send break per config file l1-9a-z send specific break sequence m display message of the day n write a note to the logfile o (re)open the tty and log file p playback the last 60 lines P set number of playback lines r replay the last 20 lines R set number of replay lines s spy mode (read only) u show host status v show version info w who is on this console x show console baud info z suspend the connection ! invoke task | attach local command ? print this message ignore/abort command ^R replay the last line \ooo send character by octal code [disconnect] conserver-8.2.4/test/results/test8000066400000000000000000000002321344660520400172060ustar00rootroot00000000000000[Enter `^Ec?' for help] [halt list] 0 - 250ms, `\z' 1 - 250ms, `\z' 2 - 250ms, `\r~^b' 3 - 250ms, `#.' 4 - 250ms, `hiya there\r' [disconnect] conserver-8.2.4/test/results/test9000066400000000000000000000000631344660520400172110ustar00rootroot00000000000000[Enter `^Ec?' for help] [line down] [disconnect] conserver-8.2.4/test/test.cf000066400000000000000000000005541344660520400160130ustar00rootroot00000000000000# test conserver config file default full { rw *; } default * { logfile ./&; timestamp ""; include full; } break 5 { string "\rtest\r"; } console shell { master localhost; logfile ./&.log; timestamp 5; type exec; exec ""; } console shell2 { master localhost; logfile ./shell2.log; timestamp 2; type exec; exec ""; } access * { trusted 127.0.0.1; } conserver-8.2.4/test/test.passwd000066400000000000000000000000001344660520400167060ustar00rootroot00000000000000conserver-8.2.4/test/test1.cf000066400000000000000000000005541344660520400160740ustar00rootroot00000000000000# test conserver config file default full { rw *; } default * { logfile ./&; timestamp ""; include full; } break 5 { string "\rtest\r"; } console shell { master 127.0.0.1; logfile ./&.log; timestamp 5; type exec; exec ""; } console shell2 { master 127.0.0.1; logfile ./shell2.log; timestamp 2; type exec; exec ""; } access * { trusted 127.0.0.1; } conserver-8.2.4/test/test2.cf000066400000000000000000000006711344660520400160750ustar00rootroot00000000000000# test conserver config file default full { rw *; } default * { logfile ./&; timestamp ""; include full; } break 4 { string "hiya there\r"; } console shell { master 127.0.0.1; logfile ./&.log; timestamp 5; type exec; exec ""; } console shella { master 127.0.0.1; logfile ./&.log; timestamp 5; type exec; exec ""; } console shellb { master 127.0.0.1; logfile ./&.log; type exec; exec ""; } access * { trusted 127.0.0.1; }