diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:39:25 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:39:25 +0000 |
commit | 7d07d2de2f52d4e2eba169e5563165309a795128 (patch) | |
tree | c3590f60f61233b4a571cfe3bfc08f6ab6591c88 /libexec | |
parent | f9ab90d9d6d02989a075d0f0074496d5b1045e4b (diff) | |
download | FreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.zip FreeBSD-src-7d07d2de2f52d4e2eba169e5563165309a795128.tar.gz |
BSD 4.4 Lite Libexec Sources
Diffstat (limited to 'libexec')
85 files changed, 22437 insertions, 0 deletions
diff --git a/libexec/Makefile b/libexec/Makefile new file mode 100644 index 0000000..90d75f0 --- /dev/null +++ b/libexec/Makefile @@ -0,0 +1,12 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +SUBDIR= bugfiler comsat fingerd ftpd getNAME getty kpasswdd lfs_cleanerd \ + mail.local makekey rexecd rlogind rshd talkd telnetd tftpd uucpd + +.if ${MACHINE} == "hp300" +SUBDIR+=rbootd +.elif ${MACHINE} == "luna68k" +SUBDIR+=rbootd +.endif + +.include <bsd.subdir.mk> diff --git a/libexec/Makefile.inc b/libexec/Makefile.inc new file mode 100644 index 0000000..f9922b6 --- /dev/null +++ b/libexec/Makefile.inc @@ -0,0 +1,3 @@ +# @(#)Makefile.inc 8.1 (Berkeley) 6/4/93 + +BINDIR?= /usr/libexec diff --git a/libexec/bugfiler/Makefile b/libexec/bugfiler/Makefile new file mode 100644 index 0000000..98489ad --- /dev/null +++ b/libexec/bugfiler/Makefile @@ -0,0 +1,17 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= bugfiler +CFLAGS+=-I${.CURDIR} +SRCS= bugfiler.c error.c gethead.c process.c redist.c reply.c +BINOWN= root +BINMODE=4555 +MAN1= sendbug.0 +MAN8= bugfiler.0 + +beforeinstall: + install -c -o bin -g ${BINGRP} -m 555 \ + ${.CURDIR}/sendbug.sh ${DESTDIR}/usr/bin/sendbug + install -c -o bin -g ${BINGRP} -m 444 ${.CURDIR}/bugformat \ + ${DESTDIR}/usr/share/misc + +.include <bsd.prog.mk> diff --git a/libexec/bugfiler/bug.h b/libexec/bugfiler/bug.h new file mode 100644 index 0000000..69ea986 --- /dev/null +++ b/libexec/bugfiler/bug.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)bug.h 8.1 (Berkeley) 6/4/93 + */ + +#define BUGS_HOME "owner-bugs@ucbvax.Berkeley.EDU" +#define BUGS_ID "bugs" + +/* + * the METOO definition has the bugfiler exit with an error (-1) status + * if there's a problem. This causes sendmail to send off a copy of the + * report (as failed mail) to the "owner" of the mail alias that executed + * the bugfiler. This is great if you would have otherwise lost the bug + * report. It's not so great if you get a whole bunch of mail that you + * really don't want. + */ +#define METOO + +/* files */ +#define ACK_FILE "bug:ack" /* acknowledge file */ +#define DIST_FILE "bug:redist" /* redistribution file */ +#define ERROR_FILE "log" /* error file */ +#define LOCK_FILE "bug:lock" /* lock file name */ +#define SUMMARY_FILE "summary" /* summary file */ +#define TMP_BUG "errors/BUG_XXXXXX" /* tmp bug report */ +#define TMP_DIR "errors" /* tmp directory */ + +#define CHN (char *)NULL /* null arg string */ +#define COMMENT '#' /* comment in redist file */ +#define EOS (char)NULL /* end of string */ +#define ERR -1 /* error return */ +#define MAXLINELEN 200 /* max line length in message */ +#define NO 0 /* no/false */ +#define OK 0 /* okay return */ +#define YES 1 /* yes/true */ + +typedef struct { + short found, /* line number if found */ + redist; /* if part of redist headers */ + int (*valid)(); /* validation routine */ + short len; /* length of tag */ + char *tag, /* leading tag */ + *line; /* actual line */ +} HEADER; +extern HEADER mailhead[]; + +#define DATE_TAG 0 /* "Date:" offset */ +#define FROM_TAG 1 /* "From " offset */ +#define CFROM_TAG 2 /* "From:" offset */ +#define INDX_TAG 3 /* "Index:" offset */ +#define MSG_TAG 4 /* "Message-Id:" offset */ +#define RPLY_TAG 5 /* "Reply-To:" offset */ +#define RET_TAG 6 /* "Return-Path:" offset */ +#define SUBJ_TAG 7 /* "Subject:" offset */ +#define TO_TAG 8 /* "To:" offset */ +#define APPAR_TO_TAG 9 /* "Apparently-To:" offset */ + +/* so sizeof doesn't return 0 */ +extern char bfr[MAXBSIZE], /* general I/O buffer */ + dir[MAXNAMLEN], /* subject and folder */ + folder[MAXNAMLEN], + tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ diff --git a/libexec/bugfiler/bugfiler.8 b/libexec/bugfiler/bugfiler.8 new file mode 100644 index 0000000..1fc86d2 --- /dev/null +++ b/libexec/bugfiler/bugfiler.8 @@ -0,0 +1,290 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)bugfiler.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt BUGFILER 8 +.Os BSD 4.2 +.Sh NAME +.Nm bugfiler +.Nd file bug reports in folders automatically +.Sh SYNOPSIS +.Nm bugfiler +.Op Fl ar +.Op Fl v Ar version +.Sh DESCRIPTION +.Nm Bugfiler +is a program to automatically intercept, acknowledge, +redistribute and store bug reports. +.Nm Bugfiler +is normally invoked +by the mail delivery program with a line similar to the following in +.Pa /etc/aliases . +.Bd -literal -offset indent +bugs: "|bugfiler" +.Ed +.Pp +It should be noted that the login +.Dq bugs +must exist for the bugfiler +to run. Unless otherwise noted all paths used by +.Nm bugfiler +are +relative to the home directory of this login. +.Nm Bugfiler +also +expects all of its files and directories to be owned by +.Dq bugs . +.Pp +Available options. +.Bl -tag -width Ds +.It Fl a +Do not send automatic mail acknowledgement to the bug report filer. +(The default is to send the acknowledgement with the file +.Pa ~bugs/version/bug:ack +appended). +.It Fl r +Do not redistribute. +.It Fl v Ar version +Override the +.Ar version +provided within the bug report itself. +.El +.Pp +For the bug report to be correctly filed, it must contain a line +in the following format: +.Pp +.Bd -filled -offset indent -compact +.Bl -column Index folder +.It Index: Ta Em folder Ta Ar version +.El +.Ed +.Pp +The directories +.Pa ~bugs/ Ns Ar version +and +.Pa ~bugs/ Ns Ar version/ Ns Em folder +must exist before +.Nm bugfiler +attempts to store the bug report. Bug +reports will be stored in files named by the concatenation of +.Ar version , +.Em folder , +and sequential numbers, i.e. if +.Ar version +is +.Dq 4.3 Tn BSD +and +.Em folder +is +.Dq ucb +the first bug report will be placed in +.Pa ~bugs/4.3BSD/ucb/1 . +If +.Em folder +contains more than one component only +the first one will be used, e.g. if +.Em folder +is +.Dq bin/from.c +or +.Dq bin/adb/con.c +it will be treated as if it were simply +.Dq bin . +.Pp +.Pp +If the +.Fl r +flag is not supplied, redistribution of the bug reports +is done as specified in the file +.Pa ~bugs/version/bug:redist . +This file +is in the format of the +.Xr aliases 5 +file, including comments and +entries requiring multiple lines, with the single exception that the +.Em folder +component of the +.Dq Index: +line replaces the name to alias. +The special folder +.Dq all: +receives a redistribution of all bug reports +sent to this +.Ar version . +For example, the +.Pa bug:redist +file +.Pp +.Bd -literal -offset indent -compact +# bigbug gets a copy of everything +all: bigbug +# ucb folder redistribution list +ucb: karels, kjd@coke.berkeley.edu + ra@beno.css.gov +.Ed +.Pp +will send copies of all bug reports with +.Dq ucb +as the +.Em folder +to bigbug, karels, kjd, and ra. +.Pp +Reports that cannot be filed, due to an invalid +.Dq Index: +line or +some other error, are placed in the directory +.Pa ~bugs/errors . +The +.Nm bugfiler +maintainer should correct these bug reports and then +run +.Nm bugfiler , +with the corrected report as its standard input, +as bug reports with errors are neither acknowledged or redistributed. +All reports that +.Nm bugfiler +handles are logged in +.Pa ~bugs/log. +.Pp +Valid bugs are also logged in the file +.Pa ~bugs/version/summary. +This file has an entry for each bug report for +.Ar version +in the +format: +.Pp +.Bd -literal -offset indent -compact +Filename Date + Subject: + Index: + Owner: Bugs Bunny + Status: Received +.Ed +.Pp +.Li Filename +is the concatenation of +.Ar version , +.Em folder , +and a number +as described above. +.Xr Date +is the date as reported by the system +clock, using +.Xr ctime 3 . +The +.Li Subject: +and +.Li Index: +lines are +copies of the +.Dq Subject: +and +.Dq index: +lines contained in the bug +report. The +.Li Owner +and +.Li Status +fields are intended to provide a +rudimentary method of tracking the status of bug reports. +.Pp +The file +.Pa ~bugs/bug:lock +is the focus of all locking for +.Nm bugfiler . +If you wish to manipulate any of the log or error files, rename or remove +it and +.Nm bugfiler +will treat all bug reports that it receives as if +they were incorrectly formatted, i.e. it will place them in the directory +.Pa ~bugs/errors , +for later recovery by the +.Nm bugfiler +maintainer. +Obviously, this file must be created when you first install +.Nm bugfiler . +.Pp +All errors that occur before +.Pa ~bugs/log +is found are logged into the system +log file, using +.Xr syslog 8 . +.Sh FILES +.Bl -tag -width /usr/share/misc/bugformatxx -compact +.It Pa ~bugs/bug:ack +the acknowledgement message +.It Pa ~bugs/bug:redist +the redistribution list +.It Pa ~bugs/bug:lock +the locking file +.It Pa ~bugs/errors/BUG_?????? +bug reports with format errors +.It Pa ~bugs/log +the log file +.It Pa ~bugs/folder/summary +the summary files +.It Pa /usr/sbin/sendmail +the mail delivery program +.It Pa /usr/share/misc/bugformat +a sample bug report format +.El +.Sh SEE ALSO +.Xr sendbug 1 , +.Xr aliases 5 , +.Xr syslog 8 +.Sh BUGS +Since mail can be forwarded in a number of different ways, +.Nm bugfiler +does not recognize forwarded mail and will acknowledge to the forwarder +instead of the original sender unless there is a +.Dq Reply-To +field in the +header. +.Pp +This version of +.Nm bugfiler +is not compatible with the version +released with +.Bx 4.3 +in that it doesn't complain to the sender about +incorrectly formatted bug reports. +Frankly, we got tired of the profanity, not to mention the extended +conversations +.Nm bugfiler +was holding with +.Xr vacation 1 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/bugfiler/bugfiler.c b/libexec/bugfiler/bugfiler.c new file mode 100644 index 0000000..75dbef3 --- /dev/null +++ b/libexec/bugfiler/bugfiler.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 1983, 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1986, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)bugfiler.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Bug report processing program, designed to be invoked + * through aliases(5). + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" + +char bfr[MAXBSIZE], /* general I/O buffer */ + tmpname[sizeof(TMP_BUG) + 5]; /* temp bug file */ + +static void logit __P((void)); +static void make_copy __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; /* getopt arguments */ + register struct passwd *pwd; /* bugs password entry */ + register int ch; /* getopts char */ + int do_ack, /* acknowledge bug report */ + do_redist; /* redistribut BR */ + char *argversion; /* folder name provided */ + + do_ack = do_redist = YES; + argversion = NULL; + while ((ch = getopt(argc, argv, "av:r")) != EOF) + switch(ch) { + case 'a': + do_ack = NO; + break; + case 'v': + argversion = optarg; + break; + case 'r': + do_redist = NO; + break; + case '?': + default: + fputs("usage: bugfiler [-ar] [-v version]\n", stderr); + error("usage: bugfiler [-ar] [-v version]", CHN); + } + + if (!(pwd = getpwnam(BUGS_ID))) + error("can't find bugs login.", BUGS_ID); + + if (chdir(pwd->pw_dir)) /* change to bugs home directory */ + error("can't chdir to %s.", pwd->pw_dir); + + if (seteuid(pwd->pw_uid)) + error("can't set id to %s.", BUGS_ID); + + (void)umask(02); /* everything is 664 */ + seterr(); /* redirect to log file */ + logit(); /* log report arrival */ + make_copy(); /* save copy in case */ + gethead(do_redist); + + if (argversion) /* specific folder requested */ + (void)strcpy(dir, argversion); + + process(); + + if (seteuid(0)) + error("can't set id to root.", CHN); + if (do_ack) + reply(); + if (do_redist) + redist(); + (void)unlink(tmpname); + exit(OK); +} + +/* + * make_copy -- + * make a copy of bug report in error folder + */ +static void +make_copy() +{ + register int cnt, /* read return value */ + tfd; /* temp file descriptor */ + + if (access(TMP_DIR, F_OK)) + (void)mkdir(TMP_DIR, S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH); + (void)strcpy(tmpname, TMP_BUG); + if (tfd = mkstemp(tmpname)) { + while ((cnt = read(fileno(stdin), + bfr, sizeof(bfr))) != ERR && cnt) + write(tfd, bfr, cnt); + (void)close(tfd); + return; + } + error("can't make copy using %s.", tmpname); +} + +/* + * logit -- + * log this run of the bugfiler + */ +static void +logit() +{ + struct timeval tp; + char *C1, *C2; + + if (gettimeofday(&tp, (struct timezone *)NULL)) + error("can't get time of day.", CHN); + for (C1 = C2 = ctime(&tp.tv_sec); *C1 && *C1 != '\n'; ++C1); + *C1 = EOS; + fputs(C2, stderr); +} diff --git a/libexec/bugfiler/bugformat b/libexec/bugfiler/bugformat new file mode 100644 index 0000000..97a767d --- /dev/null +++ b/libexec/bugfiler/bugformat @@ -0,0 +1,32 @@ +Subject: Short summary of the problem (please make this meaningful!) +Index: folder 4.4BSD-alpha + +Description: + Detailed description of the problem, suggestion, or complaint. +Repeat-By: + Describe the sequence of events that causes the problem + to occur. +Fix: + Description of how to fix the problem. If you don't know a + fix for the problem, don't include this section. + +-------- Remove this line and what's below it, for reference only. -------- + +To ensure that your bug report is handled correctly by bugfiler(8), +you must replace "folder" (on the line above starting with "Index:") +with one of the following values: + + folder ::= bin | doc | etc | games | ideas | include | lib + | local | man | misc | new | sys | ucb + | usr.bin | usr.lib + +If you're not running 4.3BSD, you should also replace "4.3BSD" on +the same line with one of the following values. + + version ::= 4.3BSD | 4.3BSD-tahoe | 4.3BSD-reno | net2 + | 4.4BSD-alpha + +For example, if your bug concerns the program "/usr/bin/file" and +you're currently running 4.3BSD-Reno, you should replace "folder" +with "usr.bin/file", and "4.4BSD-alpha" with "4.3BSD-Reno". The folder +"ideas" is for suggestions. diff --git a/libexec/bugfiler/error.c b/libexec/bugfiler/error.c new file mode 100644 index 0000000..bd00b2b --- /dev/null +++ b/libexec/bugfiler/error.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)error.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "bug.h" +#include "extern.h" + +static short err_redir; /* stderr redirected */ + +/* + * seterr -- + * redirect stderr for error processing + */ +void +seterr() +{ + if (!freopen(ERROR_FILE, "a", stderr)) + error("can't open error file %s.", ERROR_FILE); + err_redir = YES; +} + +/* + * error -- + * write errors to log file and die + */ +void +error(fmt, arg) + register char *fmt, + *arg; +{ + static char logmsg[MAXLINELEN]; /* syslog message */ + + if (err_redir) { + /* don't combine these, "fmt" may not require "arg" */ + fprintf(stderr, "\t%s\n\t", tmpname); + fprintf(stderr, fmt, arg); + fputc('\n', stderr); + } + else { + sprintf(logmsg, "bugfiler: %s", fmt); + syslog(LOG_ERR, logmsg, arg); + } +#ifdef METOO + exit(ERR); +#else + exit(OK); +#endif +} diff --git a/libexec/bugfiler/extern.h b/libexec/bugfiler/extern.h new file mode 100644 index 0000000..b096f3d --- /dev/null +++ b/libexec/bugfiler/extern.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/4/93 + */ + +void error __P((char *, char *)); +void gethead __P((int)); +int process __P((void)); +void redist __P((void)); +void reply __P((void)); +void seterr __P((void)); diff --git a/libexec/bugfiler/gethead.c b/libexec/bugfiler/gethead.c new file mode 100644 index 0000000..ba7e361 --- /dev/null +++ b/libexec/bugfiler/gethead.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)gethead.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" +#include "bug.h" +#include "extern.h" + +static int chk1 __P((char *)); +static int pbuf __P((char *)); + +#define ENT(X) sizeof(X) - 1, X +HEADER mailhead[] = { /* mail headers */ + { NO, YES, NULL, ENT("Date:"), }, + { NO, NO, NULL, ENT("From "), }, + { NO, YES, NULL, ENT("From:"), }, + { NO, NO, chk1, ENT("Index:"), }, + { NO, YES, NULL, ENT("Message-Id:"), }, + { NO, YES, NULL, ENT("Reply-To:"), }, + { NO, YES, NULL, ENT("Return-Path:"), }, + { NO, NO, pbuf, ENT("Subject:"), }, + { NO, YES, NULL, ENT("To:"), }, + { NO, NO, NULL, ENT("Apparently-To:"), }, + { ERR, } +}; + +FILE *dfp; /* distf file pointer */ +char dir[MAXNAMLEN], /* subject and folder */ + folder[MAXNAMLEN]; + +/* + * gethead -- + * read mail and bug headers from bug report, construct redist headers + */ +void +gethead(redist) + int redist; +{ + register HEADER *hp; /* mail header pointer */ + + if (redist) { + int fd; + char *distf; + + distf = strdup(_PATH_TMP); + if (!(fd = mkstemp(distf)) || !(dfp = fdopen(fd, "w+"))) + error("can't create redistribution file %s.", distf); + /* disappear after last reference is closed */ + (void)unlink(distf); + free(distf); + } + if (!freopen(tmpname, "r", stdin)) + error("can't read temporary bug file %s.", tmpname); + + while (fgets(bfr, sizeof(bfr), stdin)) { + for (hp = mailhead; hp->found != ERR; ++hp) + if (!hp->found) + if (!strncmp(hp->tag, bfr, hp->len)) { + if (hp->valid && !((*(hp->valid))(bfr))) + break; + if (!(hp->line = + malloc((u_int)(strlen(bfr) + 1)))) + error("malloc failed.", CHN); + (void)strcpy(hp->line, bfr); + hp->found = YES; + break; + } + if ((hp->found == ERR || hp->redist) && redist) + fputs(bfr, dfp); + } + + if (!mailhead[INDX_TAG].found) + error("no readable \"Index:\" header in bug report.", CHN); +} + +/* + * chk1 -- + * parse the "Index:" line into folder and directory + */ +static int +chk1(line) + char *line; +{ + register char *C; /* tmp pointer */ + struct stat sbuf; /* existence check */ + + if (sscanf(line, " Index: %s %s ", folder, dir) != 2) + return(NO); + if (C = strchr(folder, '/')) { /* deal with "bin/from.c" */ + if (C == folder) + return(NO); + *C = EOS; + } + if (stat(dir, &sbuf) || (sbuf.st_mode & S_IFMT) != S_IFDIR) + return(NO); + (void)pbuf(line); + return(YES); +} + +/* + * pbuf -- + * kludge so that summary file looks pretty + */ +static int +pbuf(line) + char *line; +{ + register char *rp, /* tmp pointers */ + *wp; + + for (rp = line; *rp == ' ' || *rp == '\t'; ++rp); + for (wp = line; *rp; ++wp) { + if ((*wp = *rp++) != ' ' && *wp != '\t') + continue; + *wp = ' '; + while (*rp == ' ' || *rp == '\t') + ++rp; + } + if (wp[-1] == ' ') /* wp can't == line */ + --wp; + *wp = EOS; + return(YES); +} diff --git a/libexec/bugfiler/pathnames.h b/libexec/bugfiler/pathnames.h new file mode 100644 index 0000000..7ff5ab6 --- /dev/null +++ b/libexec/bugfiler/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#define MAIL_CMD "/usr/sbin/sendmail -i -t -F \"Bugs Bunny\" -f owner-bugs" +#undef _PATH_TMP +#define _PATH_TMP "/tmp/BUG_XXXXXX" diff --git a/libexec/bugfiler/process.c b/libexec/bugfiler/process.c new file mode 100644 index 0000000..6d376fd --- /dev/null +++ b/libexec/bugfiler/process.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)process.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> + +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" + +char pfile[MAXPATHLEN]; /* permanent file name */ + +static int getnext __P((void)); + +/* + * process -- + * copy report to permanent file, + * update summary file. + */ +int +process() +{ + register int rval; /* read return value */ + struct timeval tp; /* time of day */ + int lfd; /* lock file descriptor */ + + if (access(LOCK_FILE, R_OK) || (lfd = open(LOCK_FILE, O_RDONLY, 0)) < 0) + error("can't find lock file %s.", LOCK_FILE); + if (flock(lfd, LOCK_EX)) + error("can't get lock.", CHN); + sprintf(pfile, "%s/%s/%d", dir, folder, getnext()); + fprintf(stderr, "\t%s\n", pfile); + if (!(freopen(pfile, "w", stdout))) + error("can't create %s.", pfile); + rewind(stdin); + while ((rval = read(fileno(stdin), bfr, sizeof(bfr))) != ERR && rval) + if (write(fileno(stdout), bfr, rval) != rval) + error("write to %s failed.", pfile); + + /* append information to the summary file */ + sprintf(bfr, "%s/%s", dir, SUMMARY_FILE); + if (!(freopen(bfr, "a", stdout))) + error("can't append to summary file %s.", bfr); + if (gettimeofday(&tp, (struct timezone *)NULL)) + error("can't get time of day.", CHN); + printf("\n%s\t\t%s\t%s\t%s\tOwner: Bugs Bunny\n\tStatus: Received\n", + pfile, ctime(&tp.tv_sec), mailhead[INDX_TAG].line, + mailhead[SUBJ_TAG].found ? mailhead[SUBJ_TAG].line : "Subject:\n"); + (void)flock(lfd, LOCK_UN); + (void)fclose(stdout); +} + +/* + * getnext -- + * get next file name (number) + */ +static int +getnext() +{ + register struct dirent *d; /* directory structure */ + register DIR *dirp; /* directory pointer */ + register int highval, newval; + register char *p; + + (void)sprintf(bfr, "%s/%s", dir, folder); + if (!(dirp = opendir(bfr))) + error("can't read folder directory %s.", bfr); + for (highval = -1; d = readdir(dirp);) { + for (p = d->d_name; *p && isdigit(*p); ++p); + if (!*p && (newval = atoi(d->d_name)) > highval) + highval = newval; + } + closedir(dirp); + return(++highval); +} diff --git a/libexec/bugfiler/redist.c b/libexec/bugfiler/redist.c new file mode 100644 index 0000000..87a18b6 --- /dev/null +++ b/libexec/bugfiler/redist.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)redist.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <ctype.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> + +#include "bug.h" +#include "pathnames.h" +#include "extern.h" + +/* + * redist -- + * Redistribute a bug report to those people indicated in the + * redistribution list file. + */ +void +redist() +{ + extern FILE *dfp; /* dist file fp */ + extern char pfile[]; /* permanent bug file */ + register char *C1, *C2; + FILE *pf; + int group; + + (void)sprintf(bfr, "%s/%s", dir, DIST_FILE); + if (!freopen(bfr, "r", stdin)) + return; + for (pf = NULL, group = 0; fgets(bfr, sizeof(bfr), stdin);) { + if (C1 = strchr(bfr, '\n')) + *C1 = '\0'; +nextline: if (*bfr == COMMENT || + isspace(*bfr) || !(C1 = index(bfr, ':'))) + continue; + *C1 = EOS; + if (!strcmp(bfr, folder) || !strcmp(bfr, "all")) { + for (++C1; *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); + if (!*C1) /* if empty list */ + continue; + if (!pf) { + if (!(pf = popen(MAIL_CMD, "w"))) + error("sendmail pipe failed.", CHN); + if (mailhead[SUBJ_TAG].found) + fprintf(pf, + "%s", mailhead[SUBJ_TAG].line); + else + fprintf(pf, + "Subject: Untitled Bug Report\n"); + if (!mailhead[TO_TAG].line) { + if (mailhead[APPAR_TO_TAG].line) + fprintf(pf, "To%s", + strchr(mailhead[APPAR_TO_TAG].line, + ':')); + else + fprintf(pf, "To: %s\n", BUGS_ID); + } + fputs("Resent-To: ", pf); + } + /* + * write out first entry, then succeeding entries + * backward compatible, handles back slashes at end + * of line + */ + if (group++) + fputs(", ", pf); + for (;;) { + if (C2 = strchr(C1, '\\')) + *C2 = EOS; + fputs(C1, pf); + if (!fgets(bfr, sizeof(bfr), stdin)) + break; + if (C1 = strchr(bfr, '\n')) + *C1 = '\0'; + if (*bfr != ' ' && *bfr != '\t') + goto nextline; + for (C1 = bfr; + *C1 && (*C1 == ' ' || *C1 == '\t'); ++C1); + } + } + } + if (!pf) + return; + + putc('\n', pf); + + rewind(dfp); + /* add Reference header and copy bug report out */ + while (fgets(bfr, sizeof(bfr), dfp) && *bfr != '\n') + fputs(bfr, pf); + fprintf(pf, "\n%sReference: %s\n\n", mailhead[INDX_TAG].line, pfile); + while (fgets(bfr, sizeof(bfr), dfp)) + fputs(bfr, pf); + (void)pclose(pf); +} diff --git a/libexec/bugfiler/reply.c b/libexec/bugfiler/reply.c new file mode 100644 index 0000000..0f99401 --- /dev/null +++ b/libexec/bugfiler/reply.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1986, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)reply.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> + +#include <dirent.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "bug.h" +#include "extern.h" +#include "pathnames.h" + +/* + * reply -- + * tell the user we got their silly little bug report + */ +void +reply() +{ + register char *C, /* traveling pointer */ + *to; /* who we're replying to */ + register int afd, /* ack file descriptor */ + rval; /* return value */ + FILE *pf; /* pipe pointer */ + + if (mailhead[RPLY_TAG].found) { + for (C = mailhead[RPLY_TAG].line + mailhead[RPLY_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + if (mailhead[FROM_TAG].found) { + for (C = mailhead[FROM_TAG].line + mailhead[FROM_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + if (mailhead[CFROM_TAG].found) { + for (C = mailhead[CFROM_TAG].line + mailhead[CFROM_TAG].len; + *C != '\n' && (*C == ' ' || *C == '\t');++C); + if (*C) + goto gotone; + } + return; + + /* if it's a foo <XXX>, get the XXX, else get foo (first string) */ +gotone: if (to = strchr(C, '<')) + for (C = ++to; + *C != '\n' && *C != ' ' && *C != '\t' && *C != '>';++C); + else { + to = C; + for (to = C++;*C != '\n' && *C != ' ' && *C != '\t';++C); + } + *C = EOS; + + if (!(pf = popen(MAIL_CMD, "w"))) + error("sendmail pipe failed.", CHN); + + fprintf(pf, "Reply-To: %s\nFrom: %s (Bugs Bunny)\nTo: %s\n", + BUGS_HOME, BUGS_HOME, to); + if (mailhead[SUBJ_TAG].found) + fprintf(pf, "Subject: Re:%s", + mailhead[SUBJ_TAG].line + mailhead[SUBJ_TAG].len); + else + fputs("Subject: Bug report acknowledgement.\n", pf); + if (mailhead[DATE_TAG].found) + fprintf(pf, "In-Acknowledgement-Of: Your message of %s", + mailhead[DATE_TAG].line + mailhead[DATE_TAG].len); + if (mailhead[MSG_TAG].found) + fprintf(pf, "\t\t%s", mailhead[MSG_TAG].line); + fputs("Precedence: bulk\n\n", pf); /* vacation(1) uses this... */ + fflush(pf); + + (void)sprintf(bfr, "%s/%s", dir, ACK_FILE); + if ((afd = open(bfr, O_RDONLY, 0)) >= 0) { + while ((rval = read(afd, bfr, sizeof(bfr))) != ERR && rval) + (void)write(fileno(pf), bfr, rval); + (void)close(afd); + } + pclose(pf); +} diff --git a/libexec/bugfiler/sendbug.1 b/libexec/bugfiler/sendbug.1 new file mode 100644 index 0000000..31ca802 --- /dev/null +++ b/libexec/bugfiler/sendbug.1 @@ -0,0 +1,85 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)sendbug.1 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt SENDBUG 1 +.Os BSD 4.2 +.Sh NAME +.Nm sendbug +.Nd mail a system bug report to 4bsd-bugs +.Sh SYNOPSIS +.Nm sendbug +.Op Ar address +.Sh DESCRIPTION +Bug reports sent to `4bsd-bugs@Berkeley.EDU' are intercepted +by a program which expects bug reports to conform to a standard format. +.Nm Sendbug +is a shell script to help the user compose and mail bug reports +in the correct format. +.Nm Sendbug +works by invoking the editor specified by the environment variable +.Ev EDITOR +on a temporary copy of the bug report format outline. The user must fill in the +appropriate fields and exit the editor. +.Nm Sendbug +then mails the completed report to `4bsd-bugs@Berkeley.EDU' or the +.Ar address +specified on the command line. +.Sh ENVIRONMENT +.Nm Sendbug +will utilize the following environment variable if it exists: +.Bl -tag -width EDITOR +.It Ev EDITOR +Specifies the preferred editor. If +.Ev EDITOR +is not set, +.Nm +defaults to +.Xr vi 1 . +.El +.Sh FILES +.Bl -tag -width /usr/share/misc/bugformat -compact +.It Pa /usr/share/misc/bugformat +Contains the bug report outline. +.El +.Sh SEE ALSO +.Xr vi 1 , +.Xr environ 7 , +.Xr bugfiler 8 , +.Xr sendmail 8 +.Sh HISTORY +The +.Nm sendbug +command +appeared in +.Bx 4.2 . diff --git a/libexec/bugfiler/sendbug.sh b/libexec/bugfiler/sendbug.sh new file mode 100644 index 0000000..911ef9d --- /dev/null +++ b/libexec/bugfiler/sendbug.sh @@ -0,0 +1,66 @@ +#!/bin/sh - +# +# Copyright (c) 1983, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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 BY THE REGENTS 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. +# +# @(#)sendbug.sh 8.1 (Berkeley) 6/4/93 +# + +# create a bug report and mail it to '4bsd-bugs'. + +PATH=/bin:/sbin:/usr/sbin:/usr/bin +export PATH + +TEMP=/tmp/bug$$ +FORMAT=/usr/share/misc/bugformat + +# uucp sites should use ": ${BUGADDR=ucbvax!4bsd-bugs}" with a suitable path. +: ${BUGADDR=4bsd-bugs@CS.Berkeley.EDU} +: ${EDITOR=vi} + +trap 'rm -f $TEMP ; exit 1' 1 2 3 13 15 + +cp $FORMAT $TEMP +chmod u+w $TEMP +if $EDITOR $TEMP +then + if cmp -s $FORMAT $TEMP + then + echo "File not changed, no bug report submitted." + exit + fi + case "$#" in + 0) sendmail -t -oi $BUGADDR < $TEMP ;; + *) sendmail -t -oi "$@" < $TEMP ;; + esac +fi + +rm -f $TEMP diff --git a/libexec/comsat/Makefile b/libexec/comsat/Makefile new file mode 100644 index 0000000..0e11b5f --- /dev/null +++ b/libexec/comsat/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= comsat +MAN8= comsat.0 + +.include <bsd.prog.mk> diff --git a/libexec/comsat/comsat.8 b/libexec/comsat/comsat.8 new file mode 100644 index 0000000..395522c --- /dev/null +++ b/libexec/comsat/comsat.8 @@ -0,0 +1,96 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)comsat.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt COMSAT 8 +.Os BSD 4.2 +.Sh NAME +.Nm comsat +.Nd biff server +.Sh SYNOPSIS +.Nm comsat +.Sh DESCRIPTION +.Nm Comsat +is the server process which receives reports of incoming mail +and notifies users if they have requested this service. +.Nm Comsat +receives messages on a datagram port associated with the +.Dq biff +service +specification (see +.Xr services 5 +and +.Xr inetd 8 ) . +The one line messages are of the form: +.Pp +.Dl user@mailbox-offset +.Pp +If the +.Em user +specified is logged in to the system and the associated terminal has +the owner execute bit turned on (by a +.Dq Li biff y ) , +the +.Em offset +is used as a seek offset into the appropriate mailbox file and +the first 7 lines or 560 characters of the message are printed +on the user's terminal. Lines which appear to be part of +the message header other than the +.Dq From , +.Dq \&To , +.Dq Date , +or +.Dq Subject +lines are not included in the displayed message. +.Sh FILES +.Bl -tag -width /var/run/utmp -compact +.It Pa /var/run/utmp +to find out who's logged on and on what terminals +.El +.Sh SEE ALSO +.Xr biff 1 , +.Xr inetd 8 +.Sh BUGS +The message header filtering is prone to error. +The density of the information presented is near the theoretical minimum. +.Pp +Users should be notified of mail which arrives on other +machines than the one to which they are currently logged in. +.Pp +The notification should appear in a separate window so it +does not mess up the screen. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/comsat/comsat.c b/libexec/comsat/comsat.c new file mode 100644 index 0000000..e2e4477 --- /dev/null +++ b/libexec/comsat/comsat.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)comsat.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <sgtty.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <utmp.h> + +int debug = 0; +#define dsyslog if (debug) syslog + +#define MAXIDLE 120 + +char hostname[MAXHOSTNAMELEN]; +struct utmp *utmp = NULL; +time_t lastmsgtime; +int nutmp, uf; + +void jkfprintf __P((FILE *, char[], off_t)); +void mailfor __P((char *)); +void notify __P((struct utmp *, off_t)); +void onalrm __P((int)); +void reapchildren __P((int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct sockaddr_in from; + register int cc; + int fromlen; + char msgbuf[100]; + + /* verify proper invocation */ + fromlen = sizeof(from); + if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { + (void)fprintf(stderr, + "comsat: getsockname: %s.\n", strerror(errno)); + exit(1); + } + openlog("comsat", LOG_PID, LOG_DAEMON); + if (chdir(_PATH_MAILDIR)) { + syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR); + (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + exit(1); + } + if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) { + syslog(LOG_ERR, "open: %s: %m", _PATH_UTMP); + (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + exit(1); + } + (void)time(&lastmsgtime); + (void)gethostname(hostname, sizeof(hostname)); + onalrm(0); + (void)signal(SIGALRM, onalrm); + (void)signal(SIGTTOU, SIG_IGN); + (void)signal(SIGCHLD, reapchildren); + for (;;) { + cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0); + if (cc <= 0) { + if (errno != EINTR) + sleep(1); + errno = 0; + continue; + } + if (!nutmp) /* no one has logged in yet */ + continue; + sigblock(sigmask(SIGALRM)); + msgbuf[cc] = '\0'; + (void)time(&lastmsgtime); + mailfor(msgbuf); + sigsetmask(0L); + } +} + +void +reapchildren(signo) + int signo; +{ + while (wait3(NULL, WNOHANG, NULL) > 0); +} + +void +onalrm(signo) + int signo; +{ + static u_int utmpsize; /* last malloced size for utmp */ + static u_int utmpmtime; /* last modification time for utmp */ + struct stat statbf; + + if (time(NULL) - lastmsgtime >= MAXIDLE) + exit(0); + (void)alarm((u_int)15); + (void)fstat(uf, &statbf); + if (statbf.st_mtime > utmpmtime) { + utmpmtime = statbf.st_mtime; + if (statbf.st_size > utmpsize) { + utmpsize = statbf.st_size + 10 * sizeof(struct utmp); + if ((utmp = realloc(utmp, utmpsize)) == NULL) { + syslog(LOG_ERR, "%s", strerror(errno)); + exit(1); + } + } + (void)lseek(uf, (off_t)0, L_SET); + nutmp = read(uf, utmp, (int)statbf.st_size)/sizeof(struct utmp); + } +} + +void +mailfor(name) + char *name; +{ + register struct utmp *utp = &utmp[nutmp]; + register char *cp; + off_t offset; + + if (!(cp = strchr(name, '@'))) + return; + *cp = '\0'; + offset = atoi(cp + 1); + while (--utp >= utmp) + if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name))) + notify(utp, offset); +} + +static char *cr; + +void +notify(utp, offset) + register struct utmp *utp; + off_t offset; +{ + FILE *tp; + struct stat stb; + struct sgttyb gttybuf; + char tty[20], name[sizeof(utmp[0].ut_name) + 1]; + + (void)snprintf(tty, sizeof(tty), "%s%.*s", + _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line); + if (strchr(tty + sizeof(_PATH_DEV) - 1, '/')) { + /* A slash is an attempt to break security... */ + syslog(LOG_AUTH | LOG_NOTICE, "'/' in \"%s\"", tty); + return; + } + if (stat(tty, &stb) || !(stb.st_mode & S_IEXEC)) { + dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_name, tty); + return; + } + dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty); + if (fork()) + return; + (void)signal(SIGALRM, SIG_DFL); + (void)alarm((u_int)30); + if ((tp = fopen(tty, "w")) == NULL) { + dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); + _exit(-1); + } + (void)ioctl(fileno(tp), TIOCGETP, >tybuf); + cr = (gttybuf.sg_flags&CRMOD) && !(gttybuf.sg_flags&RAW) ? + "\n" : "\n\r"; + (void)strncpy(name, utp->ut_name, sizeof(utp->ut_name)); + name[sizeof(name) - 1] = '\0'; + (void)fprintf(tp, "%s\007New mail for %s@%.*s\007 has arrived:%s----%s", + cr, name, (int)sizeof(hostname), hostname, cr, cr); + jkfprintf(tp, name, offset); + (void)fclose(tp); + _exit(0); +} + +void +jkfprintf(tp, name, offset) + register FILE *tp; + char name[]; + off_t offset; +{ + register char *cp, ch; + register FILE *fi; + register int linecnt, charcnt, inheader; + register struct passwd *p; + char line[BUFSIZ]; + + /* Set effective uid to user in case mail drop is on nfs */ + if ((p = getpwnam(name)) != NULL) + (void) setuid(p->pw_uid); + + if ((fi = fopen(name, "r")) == NULL) + return; + + (void)fseek(fi, offset, L_SET); + /* + * Print the first 7 lines or 560 characters of the new mail + * (whichever comes first). Skip header crap other than + * From, Subject, To, and Date. + */ + linecnt = 7; + charcnt = 560; + inheader = 1; + while (fgets(line, sizeof(line), fi) != NULL) { + if (inheader) { + if (line[0] == '\n') { + inheader = 0; + continue; + } + if (line[0] == ' ' || line[0] == '\t' || + strncmp(line, "From:", 5) && + strncmp(line, "Subject:", 8)) + continue; + } + if (linecnt <= 0 || charcnt <= 0) { + (void)fprintf(tp, "...more...%s", cr); + (void)fclose(fi); + return; + } + /* strip weird stuff so can't trojan horse stupid terminals */ + for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { + ch = toascii(ch); + if (!isprint(ch) && !isspace(ch)) + ch |= 0x40; + (void)fputc(ch, tp); + } + (void)fputs(cr, tp); + --linecnt; + } + (void)fprintf(tp, "----%s\n", cr); + (void)fclose(fi); +} diff --git a/libexec/fingerd/Makefile b/libexec/fingerd/Makefile new file mode 100644 index 0000000..e2ed65c --- /dev/null +++ b/libexec/fingerd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= fingerd +MAN8= fingerd.0 + +.include <bsd.prog.mk> diff --git a/libexec/fingerd/fingerd.8 b/libexec/fingerd/fingerd.8 new file mode 100644 index 0000000..c5c0762 --- /dev/null +++ b/libexec/fingerd/fingerd.8 @@ -0,0 +1,139 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)fingerd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt FINGERD 8 +.Os BSD 4.3 +.Sh NAME +.Nm fingerd +.Nd remote user information server +.Sh SYNOPSIS +.Nm fingerd +.Op Fl s +.Op Fl l +.Op Fl p Ar filename +.Sh DESCRIPTION +.Nm Fingerd +is a simple protocol based on +.%T RFC1196 +that provides an interface to the +Name and Finger programs at several network sites. +The program is supposed to return a friendly, +human-oriented status report on either the system at the moment +or a particular person in depth. +There is no required format and the +protocol consists mostly of specifying a single +.Dq command line . +.Pp +.Nm Fingerd +listens for +.Tn TCP +requests at port 79. +Once connected it reads a single command line +terminated by a +.Aq Tn CRLF +which is passed to +.Xr finger 1 . +.Nm Fingerd +closes its connections as soon as the output is finished. +.Pp +If the line is null (i.e. just a +.Aq Tn CRLF +is sent) then +.Xr finger +returns a +.Dq default +report that lists all people logged into +the system at that moment. +.Pp +If a user name is specified (e.g. +.Pf eric Aq Tn CRLF ) +then the +response lists more extended information for only that particular user, +whether logged in or not. +Allowable +.Dq names +in the command line include both +.Dq login names +and +.Dq user names . +If a name is ambiguous, all possible derivations are returned. +.Pp +The following options may be passed to +.Nm fingerd +as server program arguments in +.Pa /etc/inetd.conf : +.Bl -tag -width Ds +.It Fl s +Enable secure mode. +Queries without a user name are rejected and +forwarding of queries to other remote hosts is denied. +.It Fl l +Enable logging. +The name of the host originating the query is reported via +.Xr syslog 3 +at LOG_NOTICE priority. +.It Fl p +Use an alternate program as the local information provider. +The default local program +executed by +.Nm fingerd +is +.Xr finger 1 . +By specifying a customized local server, +this option allows a system manager +to have more control over what information is +provided to remote sites. +.El +.Sh SEE ALSO +.Xr finger 1 +.Sh BUGS +Connecting directly to the server from a +.Tn TIP +or an equally narrow-minded +.Tn TELNET Ns \-protocol +user program can result +in meaningless attempts at option negotiation being sent to the +server, which will foul up the command line interpretation. +.Nm Fingerd +should be taught to filter out +.Tn IAC Ns \'s +and perhaps even respond +negatively +.Pq Tn IAC WON'T +to all option commands received. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/libexec/fingerd/fingerd.c b/libexec/fingerd/fingerd.c new file mode 100644 index 0000000..74173a9 --- /dev/null +++ b/libexec/fingerd/fingerd.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)fingerd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> + +#include <unistd.h> +#include <syslog.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include "pathnames.h" + +void err __P((const char *, ...)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register FILE *fp; + register int ch; + register char *lp; + struct hostent *hp; + struct sockaddr_in sin; + int p[2], logging, secure, sval; +#define ENTRIES 50 + char **ap, *av[ENTRIES + 1], **comp, line[1024], *prog; + + prog = _PATH_FINGER; + logging = secure = 0; + openlog("fingerd", LOG_PID | LOG_CONS, LOG_DAEMON); + opterr = 0; + while ((ch = getopt(argc, argv, "slp:")) != EOF) + switch (ch) { + case 'l': + logging = 1; + break; + case 'p': + prog = optarg; + break; + case 's': + secure = 1; + break; + case '?': + default: + err("illegal option -- %c", ch); + } + + if (logging) { + sval = sizeof(sin); + if (getpeername(0, (struct sockaddr *)&sin, &sval) < 0) + err("getpeername: %s", strerror(errno)); + if (hp = gethostbyaddr((char *)&sin.sin_addr.s_addr, + sizeof(sin.sin_addr.s_addr), AF_INET)) + lp = hp->h_name; + else + lp = inet_ntoa(sin.sin_addr); + syslog(LOG_NOTICE, "query from %s", lp); + } + + if (!fgets(line, sizeof(line), stdin)) + exit(1); + + comp = &av[1]; + for (lp = line, ap = &av[2];;) { + *ap = strtok(lp, " \t\r\n"); + if (!*ap) { + if (secure && ap == &av[2]) { + puts("must provide username\r\n"); + exit(1); + } + break; + } + if (secure && strchr(*ap, '@')) { + puts("fowarding service denied\r\n"); + exit(1); + } + + /* RFC742: "/[Ww]" == "-l" */ + if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) { + av[1] = "-l"; + comp = &av[0]; + } + else if (++ap == av + ENTRIES) + break; + lp = NULL; + } + + if (lp = strrchr(prog, '/')) + *comp = ++lp; + else + *comp = prog; + if (pipe(p) < 0) + err("pipe: %s", strerror(errno)); + + switch(vfork()) { + case 0: + (void)close(p[0]); + if (p[1] != 1) { + (void)dup2(p[1], 1); + (void)close(p[1]); + } + execv(prog, comp); + err("execv: %s: %s", prog, strerror(errno)); + _exit(1); + case -1: + err("fork: %s", strerror(errno)); + } + (void)close(p[1]); + if (!(fp = fdopen(p[0], "r"))) + err("fdopen: %s", strerror(errno)); + while ((ch = getc(fp)) != EOF) { + if (ch == '\n') + putchar('\r'); + putchar(ch); + } + exit(0); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + exit(1); + /* NOTREACHED */ +} diff --git a/libexec/fingerd/pathnames.h b/libexec/fingerd/pathnames.h new file mode 100644 index 0000000..864a8f6 --- /dev/null +++ b/libexec/fingerd/pathnames.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#define _PATH_FINGER "/usr/bin/finger" diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile new file mode 100644 index 0000000..946aab7 --- /dev/null +++ b/libexec/ftpd/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.2 (Berkeley) 4/4/94 + +PROG= ftpd +CFLAGS+=-DSETPROCTITLE +SRCS= ftpd.c ftpcmd.c logwtmp.c popen.c +MAN8= ftpd.0 +CLEANFILES+=ftpcmd.c y.tab.h + +.include <bsd.prog.mk> diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h new file mode 100644 index 0000000..e3336b5 --- /dev/null +++ b/libexec/ftpd/extern.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)extern.h 8.2 (Berkeley) 4/4/94 + */ + +void blkfree __P((char **)); +char **copyblk __P((char **)); +void cwd __P((char *)); +void delete __P((char *)); +void dologout __P((int)); +void fatal __P((char *)); +int ftpd_pclose __P((FILE *)); +FILE *ftpd_popen __P((char *, char *)); +char *getline __P((char *, int, FILE *)); +void logwtmp __P((char *, char *, char *)); +void lreply __P((int, const char *, ...)); +void makedir __P((char *)); +void nack __P((char *)); +void pass __P((char *)); +void passive __P((void)); +void perror_reply __P((int, char *)); +void pwd __P((void)); +void removedir __P((char *)); +void renamecmd __P((char *, char *)); +char *renamefrom __P((char *)); +void reply __P((int, const char *, ...)); +void retrieve __P((char *, char *)); +void send_file_list __P((char *)); +void setproctitle __P((const char *, ...)); +void statcmd __P((void)); +void statfilecmd __P((char *)); +void store __P((char *, char *, int)); +void upper __P((char *)); +void user __P((char *)); +void yyerror __P((char *)); diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y new file mode 100644 index 0000000..6ec3d25 --- /dev/null +++ b/libexec/ftpd/ftpcmd.y @@ -0,0 +1,1266 @@ +/* + * Copyright (c) 1985, 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 + */ + +/* + * Grammar for FTP commands. + * See RFC 959. + */ + +%{ + +#ifndef lint +static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/ftp.h> + +#include <ctype.h> +#include <errno.h> +#include <glob.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "extern.h" + +extern struct sockaddr_in data_dest; +extern int logged_in; +extern struct passwd *pw; +extern int guest; +extern int logging; +extern int type; +extern int form; +extern int debug; +extern int timeout; +extern int maxtimeout; +extern int pdata; +extern char hostname[], remotehost[]; +extern char proctitle[]; +extern int usedefault; +extern int transflag; +extern char tmpline[]; + +off_t restart_point; + +static int cmd_type; +static int cmd_form; +static int cmd_bytesz; +char cbuf[512]; +char *fromname; + +%} + +%union { + int i; + char *s; +} + +%token + A B C E F I + L N P R S T + + SP CRLF COMMA + + USER PASS ACCT REIN QUIT PORT + PASV TYPE STRU MODE RETR STOR + APPE MLFL MAIL MSND MSOM MSAM + MRSQ MRCP ALLO REST RNFR RNTO + ABOR DELE CWD LIST NLST SITE + STAT HELP NOOP MKD RMD PWD + CDUP STOU SMNT SYST SIZE MDTM + + UMASK IDLE CHMOD + + LEXERR + +%token <s> STRING +%token <i> NUMBER + +%type <i> check_login octal_number byte_size +%type <i> struct_code mode_code type_code form_code +%type <s> pathstring pathname password username + +%start cmd_list + +%% + +cmd_list + : /* empty */ + | cmd_list cmd + { + fromname = (char *) 0; + restart_point = (off_t) 0; + } + | cmd_list rcmd + ; + +cmd + : USER SP username CRLF + { + user($3); + free($3); + } + | PASS SP password CRLF + { + pass($3); + free($3); + } + | PORT SP host_port CRLF + { + usedefault = 0; + if (pdata >= 0) { + (void) close(pdata); + pdata = -1; + } + reply(200, "PORT command successful."); + } + | PASV CRLF + { + passive(); + } + | TYPE SP type_code CRLF + { + switch (cmd_type) { + + case TYPE_A: + if (cmd_form == FORM_N) { + reply(200, "Type set to A."); + type = cmd_type; + form = cmd_form; + } else + reply(504, "Form must be N."); + break; + + case TYPE_E: + reply(504, "Type E not implemented."); + break; + + case TYPE_I: + reply(200, "Type set to I."); + type = cmd_type; + break; + + case TYPE_L: +#if NBBY == 8 + if (cmd_bytesz == 8) { + reply(200, + "Type set to L (byte size 8)."); + type = cmd_type; + } else + reply(504, "Byte size must be 8."); +#else /* NBBY == 8 */ + UNIMPLEMENTED for NBBY != 8 +#endif /* NBBY == 8 */ + } + } + | STRU SP struct_code CRLF + { + switch ($3) { + + case STRU_F: + reply(200, "STRU F ok."); + break; + + default: + reply(504, "Unimplemented STRU type."); + } + } + | MODE SP mode_code CRLF + { + switch ($3) { + + case MODE_S: + reply(200, "MODE S ok."); + break; + + default: + reply(502, "Unimplemented MODE type."); + } + } + | ALLO SP NUMBER CRLF + { + reply(202, "ALLO command ignored."); + } + | ALLO SP NUMBER SP R SP NUMBER CRLF + { + reply(202, "ALLO command ignored."); + } + | RETR check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve((char *) 0, $4); + if ($4 != NULL) + free($4); + } + | STOR check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "w", 0); + if ($4 != NULL) + free($4); + } + | APPE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "a", 0); + if ($4 != NULL) + free($4); + } + | NLST check_login CRLF + { + if ($2) + send_file_list("."); + } + | NLST check_login SP STRING CRLF + { + if ($2 && $4 != NULL) + send_file_list($4); + if ($4 != NULL) + free($4); + } + | LIST check_login CRLF + { + if ($2) + retrieve("/bin/ls -lgA", ""); + } + | LIST check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + retrieve("/bin/ls -lgA %s", $4); + if ($4 != NULL) + free($4); + } + | STAT check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + statfilecmd($4); + if ($4 != NULL) + free($4); + } + | STAT CRLF + { + statcmd(); + } + | DELE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + delete($4); + if ($4 != NULL) + free($4); + } + | RNTO SP pathname CRLF + { + if (fromname) { + renamecmd(fromname, $3); + free(fromname); + fromname = (char *) 0; + } else { + reply(503, "Bad sequence of commands."); + } + free($3); + } + | ABOR CRLF + { + reply(225, "ABOR command successful."); + } + | CWD check_login CRLF + { + if ($2) + cwd(pw->pw_dir); + } + | CWD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + cwd($4); + if ($4 != NULL) + free($4); + } + | HELP CRLF + { + help(cmdtab, (char *) 0); + } + | HELP SP STRING CRLF + { + char *cp = $3; + + if (strncasecmp(cp, "SITE", 4) == 0) { + cp = $3 + 4; + if (*cp == ' ') + cp++; + if (*cp) + help(sitetab, cp); + else + help(sitetab, (char *) 0); + } else + help(cmdtab, $3); + } + | NOOP CRLF + { + reply(200, "NOOP command successful."); + } + | MKD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + makedir($4); + if ($4 != NULL) + free($4); + } + | RMD check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + removedir($4); + if ($4 != NULL) + free($4); + } + | PWD check_login CRLF + { + if ($2) + pwd(); + } + | CDUP check_login CRLF + { + if ($2) + cwd(".."); + } + | SITE SP HELP CRLF + { + help(sitetab, (char *) 0); + } + | SITE SP HELP SP STRING CRLF + { + help(sitetab, $5); + } + | SITE SP UMASK check_login CRLF + { + int oldmask; + + if ($4) { + oldmask = umask(0); + (void) umask(oldmask); + reply(200, "Current UMASK is %03o", oldmask); + } + } + | SITE SP UMASK check_login SP octal_number CRLF + { + int oldmask; + + if ($4) { + if (($6 == -1) || ($6 > 0777)) { + reply(501, "Bad UMASK value"); + } else { + oldmask = umask($6); + reply(200, + "UMASK set to %03o (was %03o)", + $6, oldmask); + } + } + } + | SITE SP CHMOD check_login SP octal_number SP pathname CRLF + { + if ($4 && ($8 != NULL)) { + if ($6 > 0777) + reply(501, + "CHMOD: Mode value must be between 0 and 0777"); + else if (chmod($8, $6) < 0) + perror_reply(550, $8); + else + reply(200, "CHMOD command successful."); + } + if ($8 != NULL) + free($8); + } + | SITE SP IDLE CRLF + { + reply(200, + "Current IDLE time limit is %d seconds; max %d", + timeout, maxtimeout); + } + | SITE SP IDLE SP NUMBER CRLF + { + if ($5 < 30 || $5 > maxtimeout) { + reply(501, + "Maximum IDLE time must be between 30 and %d seconds", + maxtimeout); + } else { + timeout = $5; + (void) alarm((unsigned) timeout); + reply(200, + "Maximum IDLE time set to %d seconds", + timeout); + } + } + | STOU check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + store($4, "w", 1); + if ($4 != NULL) + free($4); + } + | SYST CRLF + { +#ifdef unix +#ifdef BSD + reply(215, "UNIX Type: L%d Version: BSD-%d", + NBBY, BSD); +#else /* BSD */ + reply(215, "UNIX Type: L%d", NBBY); +#endif /* BSD */ +#else /* unix */ + reply(215, "UNKNOWN Type: L%d", NBBY); +#endif /* unix */ + } + + /* + * SIZE is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return size of file in a format suitable for + * using with RESTART (we just count bytes). + */ + | SIZE check_login SP pathname CRLF + { + if ($2 && $4 != NULL) + sizecmd($4); + if ($4 != NULL) + free($4); + } + + /* + * MDTM is not in RFC959, but Postel has blessed it and + * it will be in the updated RFC. + * + * Return modification time of file as an ISO 3307 + * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx + * where xxx is the fractional second (of any precision, + * not necessarily 3 digits) + */ + | MDTM check_login SP pathname CRLF + { + if ($2 && $4 != NULL) { + struct stat stbuf; + if (stat($4, &stbuf) < 0) + reply(550, "%s: %s", + $4, strerror(errno)); + else if (!S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", $4); + } else { + struct tm *t; + t = gmtime(&stbuf.st_mtime); + reply(213, + "19%02d%02d%02d%02d%02d%02d", + t->tm_year, t->tm_mon+1, t->tm_mday, + t->tm_hour, t->tm_min, t->tm_sec); + } + } + if ($4 != NULL) + free($4); + } + | QUIT CRLF + { + reply(221, "Goodbye."); + dologout(0); + } + | error CRLF + { + yyerrok; + } + ; +rcmd + : RNFR check_login SP pathname CRLF + { + char *renamefrom(); + + restart_point = (off_t) 0; + if ($2 && $4) { + fromname = renamefrom($4); + if (fromname == (char *) 0 && $4) { + free($4); + } + } + } + | REST SP byte_size CRLF + { + fromname = (char *) 0; + restart_point = $3; /* XXX $3 is only "int" */ + reply(350, "Restarting at %qd. %s", restart_point, + "Send STORE or RETRIEVE to initiate transfer."); + } + ; + +username + : STRING + ; + +password + : /* empty */ + { + $$ = (char *)calloc(1, sizeof(char)); + } + | STRING + ; + +byte_size + : NUMBER + ; + +host_port + : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA + NUMBER COMMA NUMBER + { + char *a, *p; + + a = (char *)&data_dest.sin_addr; + a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; + p = (char *)&data_dest.sin_port; + p[0] = $9; p[1] = $11; + data_dest.sin_family = AF_INET; + } + ; + +form_code + : N + { + $$ = FORM_N; + } + | T + { + $$ = FORM_T; + } + | C + { + $$ = FORM_C; + } + ; + +type_code + : A + { + cmd_type = TYPE_A; + cmd_form = FORM_N; + } + | A SP form_code + { + cmd_type = TYPE_A; + cmd_form = $3; + } + | E + { + cmd_type = TYPE_E; + cmd_form = FORM_N; + } + | E SP form_code + { + cmd_type = TYPE_E; + cmd_form = $3; + } + | I + { + cmd_type = TYPE_I; + } + | L + { + cmd_type = TYPE_L; + cmd_bytesz = NBBY; + } + | L SP byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $3; + } + /* this is for a bug in the BBN ftp */ + | L byte_size + { + cmd_type = TYPE_L; + cmd_bytesz = $2; + } + ; + +struct_code + : F + { + $$ = STRU_F; + } + | R + { + $$ = STRU_R; + } + | P + { + $$ = STRU_P; + } + ; + +mode_code + : S + { + $$ = MODE_S; + } + | B + { + $$ = MODE_B; + } + | C + { + $$ = MODE_C; + } + ; + +pathname + : pathstring + { + /* + * Problem: this production is used for all pathname + * processing, but only gives a 550 error reply. + * This is a valid reply in some cases but not in others. + */ + if (logged_in && $1 && *$1 == '~') { + glob_t gl; + int flags = + GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + if (glob($1, flags, NULL, &gl) || + gl.gl_pathc == 0) { + reply(550, "not found"); + $$ = NULL; + } else { + $$ = strdup(gl.gl_pathv[0]); + } + globfree(&gl); + free($1); + } else + $$ = $1; + } + ; + +pathstring + : STRING + ; + +octal_number + : NUMBER + { + int ret, dec, multby, digit; + + /* + * Convert a number that was read as decimal number + * to what it would be if it had been read as octal. + */ + dec = $1; + multby = 1; + ret = 0; + while (dec) { + digit = dec%10; + if (digit > 7) { + ret = -1; + break; + } + ret += digit * multby; + multby *= 8; + dec /= 10; + } + $$ = ret; + } + ; + + +check_login + : /* empty */ + { + if (logged_in) + $$ = 1; + else { + reply(530, "Please login with USER and PASS."); + $$ = 0; + } + } + ; + +%% + +extern jmp_buf errcatch; + +#define CMD 0 /* beginning of command */ +#define ARGS 1 /* expect miscellaneous arguments */ +#define STR1 2 /* expect SP followed by STRING */ +#define STR2 3 /* expect STRING */ +#define OSTR 4 /* optional SP then STRING */ +#define ZSTR1 5 /* SP then optional STRING */ +#define ZSTR2 6 /* optional STRING after SP */ +#define SITECMD 7 /* SITE command */ +#define NSTR 8 /* Number followed by a string */ + +struct tab { + char *name; + short token; + short state; + short implemented; /* 1 if command is implemented */ + char *help; +}; + +struct tab cmdtab[] = { /* In order defined in RFC 765 */ + { "USER", USER, STR1, 1, "<sp> username" }, + { "PASS", PASS, ZSTR1, 1, "<sp> password" }, + { "ACCT", ACCT, STR1, 0, "(specify account)" }, + { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, + { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, + { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, + { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, + { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, + { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, + { "STRU", STRU, ARGS, 1, "(specify file structure)" }, + { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, + { "RETR", RETR, STR1, 1, "<sp> file-name" }, + { "STOR", STOR, STR1, 1, "<sp> file-name" }, + { "APPE", APPE, STR1, 1, "<sp> file-name" }, + { "MLFL", MLFL, OSTR, 0, "(mail file)" }, + { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, + { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, + { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, + { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, + { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, + { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, + { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, + { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, + { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, + { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, + { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, + { "DELE", DELE, STR1, 1, "<sp> file-name" }, + { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, + { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, + { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, + { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, + { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, + { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { "NOOP", NOOP, ARGS, 1, "" }, + { "MKD", MKD, STR1, 1, "<sp> path-name" }, + { "XMKD", MKD, STR1, 1, "<sp> path-name" }, + { "RMD", RMD, STR1, 1, "<sp> path-name" }, + { "XRMD", RMD, STR1, 1, "<sp> path-name" }, + { "PWD", PWD, ARGS, 1, "(return current directory)" }, + { "XPWD", PWD, ARGS, 1, "(return current directory)" }, + { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, + { "STOU", STOU, STR1, 1, "<sp> file-name" }, + { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, + { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, + { NULL, 0, 0, 0, 0 } +}; + +struct tab sitetab[] = { + { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, + { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, + { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, + { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, + { NULL, 0, 0, 0, 0 } +}; + +static char *copy __P((char *)); +static void help __P((struct tab *, char *)); +static struct tab * + lookup __P((struct tab *, char *)); +static void sizecmd __P((char *)); +static void toolong __P((int)); +static int yylex __P((void)); + +static struct tab * +lookup(p, cmd) + struct tab *p; + char *cmd; +{ + + for (; p->name != NULL; p++) + if (strcmp(cmd, p->name) == 0) + return (p); + return (0); +} + +#include <arpa/telnet.h> + +/* + * getline - a hacked up version of fgets to ignore TELNET escape codes. + */ +char * +getline(s, n, iop) + char *s; + int n; + FILE *iop; +{ + int c; + register char *cs; + + cs = s; +/* tmpline may contain saved command from urgent mode interruption */ + for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { + *cs++ = tmpline[c]; + if (tmpline[c] == '\n') { + *cs++ = '\0'; + if (debug) + syslog(LOG_DEBUG, "command: %s", s); + tmpline[0] = '\0'; + return(s); + } + if (c == 0) + tmpline[0] = '\0'; + } + while ((c = getc(iop)) != EOF) { + c &= 0377; + if (c == IAC) { + if ((c = getc(iop)) != EOF) { + c &= 0377; + switch (c) { + case WILL: + case WONT: + c = getc(iop); + printf("%c%c%c", IAC, DONT, 0377&c); + (void) fflush(stdout); + continue; + case DO: + case DONT: + c = getc(iop); + printf("%c%c%c", IAC, WONT, 0377&c); + (void) fflush(stdout); + continue; + case IAC: + break; + default: + continue; /* ignore command */ + } + } + } + *cs++ = c; + if (--n <= 0 || c == '\n') + break; + } + if (c == EOF && cs == s) + return (NULL); + *cs++ = '\0'; + if (debug) { + if (!guest && strncasecmp("pass ", s, 5) == 0) { + /* Don't syslog passwords */ + syslog(LOG_DEBUG, "command: %.5s ???", s); + } else { + register char *cp; + register int len; + + /* Don't syslog trailing CR-LF */ + len = strlen(s); + cp = s + len - 1; + while (cp >= s && (*cp == '\n' || *cp == '\r')) { + --cp; + --len; + } + syslog(LOG_DEBUG, "command: %.*s", len, s); + } + } + return (s); +} + +static void +toolong(signo) + int signo; +{ + + reply(421, + "Timeout (%d seconds): closing control connection.", timeout); + if (logging) + syslog(LOG_INFO, "User %s timed out after %d seconds", + (pw ? pw -> pw_name : "unknown"), timeout); + dologout(1); +} + +static int +yylex() +{ + static int cpos, state; + char *cp, *cp2; + struct tab *p; + int n; + char c; + + for (;;) { + switch (state) { + + case CMD: + (void) signal(SIGALRM, toolong); + (void) alarm((unsigned) timeout); + if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { + reply(221, "You could at least say goodbye."); + dologout(0); + } + (void) alarm(0); +#ifdef SETPROCTITLE + if (strncasecmp(cbuf, "PASS", 4) != NULL) + setproctitle("%s: %s", proctitle, cbuf); +#endif /* SETPROCTITLE */ + if ((cp = strchr(cbuf, '\r'))) { + *cp++ = '\n'; + *cp = '\0'; + } + if ((cp = strpbrk(cbuf, " \n"))) + cpos = cp - cbuf; + if (cpos == 0) + cpos = 4; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cbuf); + p = lookup(cmdtab, cbuf); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + yylval.s = p->name; + return (p->token); + } + break; + + case SITECMD: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + cp = &cbuf[cpos]; + if ((cp2 = strpbrk(cp, " \n"))) + cpos = cp2 - cbuf; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + upper(cp); + p = lookup(sitetab, cp); + cbuf[cpos] = c; + if (p != 0) { + if (p->implemented == 0) { + state = CMD; + nack(p->name); + longjmp(errcatch,0); + /* NOTREACHED */ + } + state = p->state; + yylval.s = p->name; + return (p->token); + } + state = CMD; + break; + + case OSTR: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR1: + case ZSTR1: + dostr1: + if (cbuf[cpos] == ' ') { + cpos++; + state = state == OSTR ? STR2 : ++state; + return (SP); + } + break; + + case ZSTR2: + if (cbuf[cpos] == '\n') { + state = CMD; + return (CRLF); + } + /* FALLTHROUGH */ + + case STR2: + cp = &cbuf[cpos]; + n = strlen(cp); + cpos += n - 1; + /* + * Make sure the string is nonempty and \n terminated. + */ + if (n > 1 && cbuf[cpos] == '\n') { + cbuf[cpos] = '\0'; + yylval.s = copy(cp); + cbuf[cpos] = '\n'; + state = ARGS; + return (STRING); + } + break; + + case NSTR: + if (cbuf[cpos] == ' ') { + cpos++; + return (SP); + } + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval.i = atoi(cp); + cbuf[cpos] = c; + state = STR1; + return (NUMBER); + } + state = STR1; + goto dostr1; + + case ARGS: + if (isdigit(cbuf[cpos])) { + cp = &cbuf[cpos]; + while (isdigit(cbuf[++cpos])) + ; + c = cbuf[cpos]; + cbuf[cpos] = '\0'; + yylval.i = atoi(cp); + cbuf[cpos] = c; + return (NUMBER); + } + switch (cbuf[cpos++]) { + + case '\n': + state = CMD; + return (CRLF); + + case ' ': + return (SP); + + case ',': + return (COMMA); + + case 'A': + case 'a': + return (A); + + case 'B': + case 'b': + return (B); + + case 'C': + case 'c': + return (C); + + case 'E': + case 'e': + return (E); + + case 'F': + case 'f': + return (F); + + case 'I': + case 'i': + return (I); + + case 'L': + case 'l': + return (L); + + case 'N': + case 'n': + return (N); + + case 'P': + case 'p': + return (P); + + case 'R': + case 'r': + return (R); + + case 'S': + case 's': + return (S); + + case 'T': + case 't': + return (T); + + } + break; + + default: + fatal("Unknown state in scanner."); + } + yyerror((char *) 0); + state = CMD; + longjmp(errcatch,0); + } +} + +void +upper(s) + char *s; +{ + while (*s != '\0') { + if (islower(*s)) + *s = toupper(*s); + s++; + } +} + +static char * +copy(s) + char *s; +{ + char *p; + + p = malloc((unsigned) strlen(s) + 1); + if (p == NULL) + fatal("Ran out of memory."); + (void) strcpy(p, s); + return (p); +} + +static void +help(ctab, s) + struct tab *ctab; + char *s; +{ + struct tab *c; + int width, NCMDS; + char *type; + + if (ctab == sitetab) + type = "SITE "; + else + type = ""; + width = 0, NCMDS = 0; + for (c = ctab; c->name != NULL; c++) { + int len = strlen(c->name); + + if (len > width) + width = len; + NCMDS++; + } + width = (width + 8) &~ 7; + if (s == 0) { + int i, j, w; + int columns, lines; + + lreply(214, "The following %scommands are recognized %s.", + type, "(* =>'s unimplemented)"); + columns = 76 / width; + if (columns == 0) + columns = 1; + lines = (NCMDS + columns - 1) / columns; + for (i = 0; i < lines; i++) { + printf(" "); + for (j = 0; j < columns; j++) { + c = ctab + j * lines + i; + printf("%s%c", c->name, + c->implemented ? ' ' : '*'); + if (c + lines >= &ctab[NCMDS]) + break; + w = strlen(c->name) + 1; + while (w < width) { + putchar(' '); + w++; + } + } + printf("\r\n"); + } + (void) fflush(stdout); + reply(214, "Direct comments to ftp-bugs@%s.", hostname); + return; + } + upper(s); + c = lookup(ctab, s); + if (c == (struct tab *)0) { + reply(502, "Unknown command %s.", s); + return; + } + if (c->implemented) + reply(214, "Syntax: %s%s %s", type, c->name, c->help); + else + reply(214, "%s%-*s\t%s; unimplemented.", type, width, + c->name, c->help); +} + +static void +sizecmd(filename) + char *filename; +{ + switch (type) { + case TYPE_L: + case TYPE_I: { + struct stat stbuf; + if (stat(filename, &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) + reply(550, "%s: not a plain file.", filename); + else + reply(213, "%qu", stbuf.st_size); + break; } + case TYPE_A: { + FILE *fin; + int c; + off_t count; + struct stat stbuf; + fin = fopen(filename, "r"); + if (fin == NULL) { + perror_reply(550, filename); + return; + } + if (fstat(fileno(fin), &stbuf) < 0 || !S_ISREG(stbuf.st_mode)) { + reply(550, "%s: not a plain file.", filename); + (void) fclose(fin); + return; + } + + count = 0; + while((c=getc(fin)) != EOF) { + if (c == '\n') /* will get expanded to \r\n */ + count++; + count++; + } + (void) fclose(fin); + + reply(213, "%qd", count); + break; } + default: + reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); + } +} diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 new file mode 100644 index 0000000..a7c5cae --- /dev/null +++ b/libexec/ftpd/ftpd.8 @@ -0,0 +1,290 @@ +.\" Copyright (c) 1985, 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)ftpd.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd April 19, 1994 +.Dt FTPD 8 +.Os BSD 4.2 +.Sh NAME +.Nm ftpd +.Nd +Internet File Transfer Protocol server +.Sh SYNOPSIS +.Nm ftpd +.Op Fl dl +.Op Fl T Ar maxtimeout +.Op Fl t Ar timeout +.Sh DESCRIPTION +.Nm Ftpd +is the +Internet File Transfer Protocol +server process. The server uses the +.Tn TCP +protocol +and listens at the port specified in the +.Dq ftp +service specification; see +.Xr services 5 . +.Pp +Available options: +.Bl -tag -width Ds +.It Fl d +Debugging information is written to the syslog using LOG_FTP. +.It Fl l +Each successful and failed +.Xr ftp 1 +session is logged using syslog with a facility of LOG_FTP. +If this option is specified twice, the retrieve (get), store (put), append, +delete, make directory, remove directory and rename operations and +their filename arguments are also logged. +.It Fl T +A client may also request a different timeout period; +the maximum period allowed may be set to +.Ar timeout +seconds with the +.Fl T +option. +The default limit is 2 hours. +.It Fl t +The inactivity timeout period is set to +.Ar timeout +seconds (the default is 15 minutes). +.El +.Pp +The file +.Pa /etc/nologin +can be used to disable ftp access. +If the file exists, +.Nm +displays it and exits. +If the file +.Pa /etc/ftpwelcome +exists, +.Nm +prints it before issuing the +.Dq ready +message. +If the file +.Pa /etc/motd +exists, +.Nm +prints it after a successful login. +.Pp +The ftp server currently supports the following ftp requests. +The case of the requests is ignored. +.Bl -column "Request" -offset indent +.It Request Ta "Description" +.It ABOR Ta "abort previous command" +.It ACCT Ta "specify account (ignored)" +.It ALLO Ta "allocate storage (vacuously)" +.It APPE Ta "append to a file" +.It CDUP Ta "change to parent of current working directory" +.It CWD Ta "change working directory" +.It DELE Ta "delete a file" +.It HELP Ta "give help information" +.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA" +.It MKD Ta "make a directory" +.It MDTM Ta "show last modification time of file" +.It MODE Ta "specify data transfer" Em mode +.It NLST Ta "give name list of files in directory" +.It NOOP Ta "do nothing" +.It PASS Ta "specify password" +.It PASV Ta "prepare for server-to-server transfer" +.It PORT Ta "specify data connection port" +.It PWD Ta "print the current working directory" +.It QUIT Ta "terminate session" +.It REST Ta "restart incomplete transfer" +.It RETR Ta "retrieve a file" +.It RMD Ta "remove a directory" +.It RNFR Ta "specify rename-from file name" +.It RNTO Ta "specify rename-to file name" +.It SITE Ta "non-standard commands (see next section)" +.It SIZE Ta "return size of file" +.It STAT Ta "return status of server" +.It STOR Ta "store a file" +.It STOU Ta "store a file with a unique name" +.It STRU Ta "specify data transfer" Em structure +.It SYST Ta "show operating system type of server system" +.It TYPE Ta "specify data transfer" Em type +.It USER Ta "specify user name" +.It XCUP Ta "change to parent of current working directory (deprecated)" +.It XCWD Ta "change working directory (deprecated)" +.It XMKD Ta "make a directory (deprecated)" +.It XPWD Ta "print the current working directory (deprecated)" +.It XRMD Ta "remove a directory (deprecated)" +.El +.Pp +The following non-standard or +.Tn UNIX +specific commands are supported +by the +SITE request. +.Pp +.Bl -column Request -offset indent +.It Sy Request Ta Sy Description +.It UMASK Ta change umask, e.g. ``SITE UMASK 002'' +.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' +.It CHMOD Ta change mode of a file, e.g. ``SITE CHMOD 755 filename'' +.It HELP Ta give help information. +.El +.Pp +The remaining ftp requests specified in Internet RFC 959 +are +recognized, but not implemented. +MDTM and SIZE are not specified in RFC 959, but will appear in the +next updated FTP RFC. +.Pp +The ftp server will abort an active file transfer only when the +ABOR +command is preceded by a Telnet "Interrupt Process" (IP) +signal and a Telnet "Synch" signal in the command Telnet stream, +as described in Internet RFC 959. +If a +STAT +command is received during a data transfer, preceded by a Telnet IP +and Synch, transfer status will be returned. +.Pp +.Nm Ftpd +interprets file names according to the +.Dq globbing +conventions used by +.Xr csh 1 . +This allows users to utilize the metacharacters +.Dq Li \&*?[]{}~ . +.Pp +.Nm Ftpd +authenticates users according to three rules. +.Pp +.Bl -enum -offset indent +.It +The login name must be in the password data base, +.Pa /etc/passwd , +and not have a null password. +In this case a password must be provided by the client before any +file operations may be performed. +.It +The login name must not appear in the file +.Pa /etc/ftpusers . +.It +The user must have a standard shell returned by +.Xr getusershell 3 . +.It +If the user name is +.Dq anonymous +or +.Dq ftp , +an +anonymous ftp account must be present in the password +file (user +.Dq ftp ) . +In this case the user is allowed +to log in by specifying any password (by convention an email address for +the user should be used as the password). +.El +.Pp +In the last case, +.Nm ftpd +takes special measures to restrict the client's access privileges. +The server performs a +.Xr chroot 2 +to the home directory of the +.Dq ftp +user. +In order that system security is not breached, it is recommended +that the +.Dq ftp +subtree be constructed with care, following these rules: +.Bl -tag -width "~ftp/pub" -offset indent +.It Pa ~ftp +Make the home directory owned by +.Dq root +and unwritable by anyone. +.It Pa ~ftp/bin +Make this directory owned by +.Dq root +and unwritable by anyone (mode 555). +The program +.Xr ls 1 +must be present to support the list command. +This program should be mode 111. +.It Pa ~ftp/etc +Make this directory owned by +.Dq root +and unwritable by anyone (mode 555). +The files +.Xr passwd 5 +and +.Xr group 5 +must be present for the +.Xr ls +command to be able to produce owner names rather than numbers. +The password field in +.Xr passwd +is not used, and should not contain real passwords. +The file +.Pa motd , +if present, will be printed after a successful login. +These files should be mode 444. +.It Pa ~ftp/pub +Make this directory mode 777 and owned by +.Dq ftp . +Guests +can then place files which are to be accessible via the anonymous +account in this directory. +.El +.Sh FILES +.Bl -tag -width /etc/ftpwelcome -compact +.It Pa /etc/ftpusers +List of unwelcome/restricted users. +.It Pa /etc/ftpwelcome +Welcome notice. +.It Pa /etc/motd +Welcome notice after login. +.It Pa /etc/nologin +Displayed and access refused. +.El +.Sh SEE ALSO +.Xr ftp 1 , +.Xr getusershell 3 , +.Xr syslogd 8 +.Sh BUGS +The server must run as the super-user +to create sockets with privileged port numbers. It maintains +an effective user id of the logged in user, reverting to +the super-user only when binding addresses to sockets. The +possible security holes have been extensively +scrutinized, but are possibly incomplete. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c new file mode 100644 index 0000000..6e23bd7 --- /dev/null +++ b/libexec/ftpd/ftpd.c @@ -0,0 +1,1654 @@ +/* + * Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ftpd.c 8.4 (Berkeley) 4/16/94"; +#endif /* not lint */ + +/* + * FTP server. + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> + +#define FTP_NAMES +#include <arpa/ftp.h> +#include <arpa/inet.h> +#include <arpa/telnet.h> + +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <limits.h> +#include <netdb.h> +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "pathnames.h" +#include "extern.h" + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +static char version[] = "Version 6.00"; + +extern off_t restart_point; +extern char cbuf[]; + +struct sockaddr_in ctrl_addr; +struct sockaddr_in data_source; +struct sockaddr_in data_dest; +struct sockaddr_in his_addr; +struct sockaddr_in pasv_addr; + +int data; +jmp_buf errcatch, urgcatch; +int logged_in; +struct passwd *pw; +int debug; +int timeout = 900; /* timeout after 15 minutes of inactivity */ +int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */ +int logging; +int guest; +int type; +int form; +int stru; /* avoid C keyword */ +int mode; +int usedefault = 1; /* for data transfers */ +int pdata = -1; /* for passive mode */ +sig_atomic_t transflag; +off_t file_size; +off_t byte_count; +#if !defined(CMASK) || CMASK == 0 +#undef CMASK +#define CMASK 027 +#endif +int defumask = CMASK; /* default umask value */ +char tmpline[7]; +char hostname[MAXHOSTNAMELEN]; +char remotehost[MAXHOSTNAMELEN]; + +/* + * Timeout intervals for retrying connections + * to hosts that don't accept PORT cmds. This + * is a kludge, but given the problems with TCP... + */ +#define SWAITMAX 90 /* wait at most 90 seconds */ +#define SWAITINT 5 /* interval between retries */ + +int swaitmax = SWAITMAX; +int swaitint = SWAITINT; + +#ifdef SETPROCTITLE +char **Argv = NULL; /* pointer to argument vector */ +char *LastArgv = NULL; /* end of argv */ +char proctitle[LINE_MAX]; /* initial part of title */ +#endif /* SETPROCTITLE */ + +#define LOGCMD(cmd, file) \ + if (logging > 1) \ + syslog(LOG_INFO,"%s %s%s", cmd, \ + *(file) == '/' ? "" : curdir(), file); +#define LOGCMD2(cmd, file1, file2) \ + if (logging > 1) \ + syslog(LOG_INFO,"%s %s%s %s%s", cmd, \ + *(file1) == '/' ? "" : curdir(), file1, \ + *(file2) == '/' ? "" : curdir(), file2); +#define LOGBYTES(cmd, file, cnt) \ + if (logging > 1) { \ + if (cnt == (off_t)-1) \ + syslog(LOG_INFO,"%s %s%s", cmd, \ + *(file) == '/' ? "" : curdir(), file); \ + else \ + syslog(LOG_INFO, "%s %s%s = %qd bytes", \ + cmd, (*(file) == '/') ? "" : curdir(), file, cnt); \ + } + +static void ack __P((char *)); +static void myoob __P((int)); +static int checkuser __P((char *)); +static FILE *dataconn __P((char *, off_t, char *)); +static void dolog __P((struct sockaddr_in *)); +static char *curdir __P((void)); +static void end_login __P((void)); +static FILE *getdatasock __P((char *)); +static char *gunique __P((char *)); +static void lostconn __P((int)); +static int receive_data __P((FILE *, FILE *)); +static void send_data __P((FILE *, FILE *, off_t)); +static struct passwd * + sgetpwnam __P((char *)); +static char *sgetsave __P((char *)); + +static char * +curdir() +{ + static char path[MAXPATHLEN+1+1]; /* path + '/' + '\0' */ + + if (getcwd(path, sizeof(path)-2) == NULL) + return (""); + if (path[1] != '\0') /* special case for root dir. */ + strcat(path, "/"); + /* For guest account, skip / since it's chrooted */ + return (guest ? path+1 : path); +} + +int +main(argc, argv, envp) + int argc; + char *argv[]; + char **envp; +{ + int addrlen, ch, on = 1, tos; + char *cp, line[LINE_MAX]; + FILE *fd; + + /* + * LOG_NDELAY sets up the logging connection immediately, + * necessary for anonymous ftp's that chroot and can't do it later. + */ + openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); + addrlen = sizeof(his_addr); + if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); + exit(1); + } + addrlen = sizeof(ctrl_addr); + if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); + exit(1); + } +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); +#endif + data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); + debug = 0; +#ifdef SETPROCTITLE + /* + * Save start and extent of argv for setproctitle. + */ + Argv = argv; + while (*envp) + envp++; + LastArgv = envp[-1] + strlen(envp[-1]); +#endif /* SETPROCTITLE */ + + while ((ch = getopt(argc, argv, "dlt:T:u:v")) != EOF) { + switch (ch) { + case 'd': + debug = 1; + break; + + case 'l': + logging++; /* > 1 == extra logging */ + break; + + case 't': + timeout = atoi(optarg); + if (maxtimeout < timeout) + maxtimeout = timeout; + break; + + case 'T': + maxtimeout = atoi(optarg); + if (timeout > maxtimeout) + timeout = maxtimeout; + break; + + case 'u': + { + long val = 0; + + val = strtol(optarg, &optarg, 8); + if (*optarg != '\0' || val < 0) + warnx("bad value for -u"); + else + defumask = val; + break; + } + + case 'v': + debug = 1; + break; + + default: + warnx("unknown flag -%c ignored", optopt); + break; + } + } + (void) freopen(_PATH_DEVNULL, "w", stderr); + (void) signal(SIGPIPE, lostconn); + (void) signal(SIGCHLD, SIG_IGN); + if ((int)signal(SIGURG, myoob) < 0) + syslog(LOG_ERR, "signal: %m"); + + /* Try to handle urgent data inline */ +#ifdef SO_OOBINLINE + if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt: %m"); +#endif + +#ifdef F_SETOWN + if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) + syslog(LOG_ERR, "fcntl F_SETOWN: %m"); +#endif + dolog(&his_addr); + /* + * Set up default state + */ + data = -1; + type = TYPE_A; + form = FORM_N; + stru = STRU_F; + mode = MODE_S; + tmpline[0] = '\0'; + + /* If logins are disabled, print out the message. */ + if ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(530, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + reply(530, "System not available."); + exit(0); + } + if ((fd = fopen(_PATH_FTPWELCOME, "r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(220, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + /* reply(220,) must follow */ + } + (void) gethostname(hostname, sizeof(hostname)); + reply(220, "%s FTP server (%s) ready.", hostname, version); + (void) setjmp(errcatch); + for (;;) + (void) yyparse(); + /* NOTREACHED */ +} + +static void +lostconn(signo) + int signo; +{ + + if (debug) + syslog(LOG_DEBUG, "lost connection"); + dologout(-1); +} + +static char ttyline[20]; + +/* + * Helper function for sgetpwnam(). + */ +static char * +sgetsave(s) + char *s; +{ + char *new = malloc((unsigned) strlen(s) + 1); + + if (new == NULL) { + perror_reply(421, "Local resource failure: malloc"); + dologout(1); + /* NOTREACHED */ + } + (void) strcpy(new, s); + return (new); +} + +/* + * Save the result of a getpwnam. Used for USER command, since + * the data returned must not be clobbered by any other command + * (e.g., globbing). + */ +static struct passwd * +sgetpwnam(name) + char *name; +{ + static struct passwd save; + struct passwd *p; + + if ((p = getpwnam(name)) == NULL) + return (p); + if (save.pw_name) { + free(save.pw_name); + free(save.pw_passwd); + free(save.pw_gecos); + free(save.pw_dir); + free(save.pw_shell); + } + save = *p; + save.pw_name = sgetsave(p->pw_name); + save.pw_passwd = sgetsave(p->pw_passwd); + save.pw_gecos = sgetsave(p->pw_gecos); + save.pw_dir = sgetsave(p->pw_dir); + save.pw_shell = sgetsave(p->pw_shell); + return (&save); +} + +static int login_attempts; /* number of failed login attempts */ +static int askpasswd; /* had user command, ask for passwd */ +static char curname[10]; /* current USER name */ + +/* + * USER command. + * Sets global passwd pointer pw if named account exists and is acceptable; + * sets askpasswd if a PASS command is expected. If logged in previously, + * need to reset state. If name is "ftp" or "anonymous", the name is not in + * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. + * If account doesn't exist, ask for passwd anyway. Otherwise, check user + * requesting login privileges. Disallow anyone who does not have a standard + * shell as returned by getusershell(). Disallow anyone mentioned in the file + * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. + */ +void +user(name) + char *name; +{ + char *cp, *shell; + + if (logged_in) { + if (guest) { + reply(530, "Can't change user from guest login."); + return; + } + end_login(); + } + + guest = 0; + if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { + if (checkuser("ftp") || checkuser("anonymous")) + reply(530, "User %s access denied.", name); + else if ((pw = sgetpwnam("ftp")) != NULL) { + guest = 1; + askpasswd = 1; + reply(331, + "Guest login ok, type your name as password."); + } else + reply(530, "User %s unknown.", name); + if (!askpasswd && logging) + syslog(LOG_NOTICE, + "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost); + return; + } + if (pw = sgetpwnam(name)) { + if ((shell = pw->pw_shell) == NULL || *shell == 0) + shell = _PATH_BSHELL; + while ((cp = getusershell()) != NULL) + if (strcmp(cp, shell) == 0) + break; + endusershell(); + + if (cp == NULL || checkuser(name)) { + reply(530, "User %s access denied.", name); + if (logging) + syslog(LOG_NOTICE, + "FTP LOGIN REFUSED FROM %s, %s", + remotehost, name); + pw = (struct passwd *) NULL; + return; + } + } + if (logging) + strncpy(curname, name, sizeof(curname)-1); + reply(331, "Password required for %s.", name); + askpasswd = 1; + /* + * Delay before reading passwd after first failed + * attempt to slow down passwd-guessing programs. + */ + if (login_attempts) + sleep((unsigned) login_attempts); +} + +/* + * Check if a user is in the file _PATH_FTPUSERS + */ +static int +checkuser(name) + char *name; +{ + FILE *fd; + int found = 0; + char *p, line[BUFSIZ]; + + if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) { + while (fgets(line, sizeof(line), fd) != NULL) + if ((p = strchr(line, '\n')) != NULL) { + *p = '\0'; + if (line[0] == '#') + continue; + if (strcmp(p, name) == 0) { + found = 1; + break; + } + } + (void) fclose(fd); + } + return (found); +} + +/* + * Terminate login as previous user, if any, resetting state; + * used when USER command is given or login fails. + */ +static void +end_login() +{ + + (void) seteuid((uid_t)0); + if (logged_in) + logwtmp(ttyline, "", ""); + pw = NULL; + logged_in = 0; + guest = 0; +} + +void +pass(passwd) + char *passwd; +{ + char *salt, *xpasswd; + FILE *fd; + + if (logged_in || askpasswd == 0) { + reply(503, "Login with USER first."); + return; + } + askpasswd = 0; + if (!guest) { /* "ftp" is only account allowed no password */ + if (pw == NULL) + salt = "xx"; + else + salt = pw->pw_passwd; + xpasswd = crypt(passwd, salt); + /* The strcmp does not catch null passwords! */ + if (pw == NULL || *pw->pw_passwd == '\0' || + strcmp(xpasswd, pw->pw_passwd)) { + reply(530, "Login incorrect."); + if (logging) + syslog(LOG_NOTICE, + "FTP LOGIN FAILED FROM %s, %s", + remotehost, curname); + pw = NULL; + if (login_attempts++ >= 5) { + syslog(LOG_NOTICE, + "repeated login failures from %s", + remotehost); + exit(0); + } + return; + } + } + login_attempts = 0; /* this time successful */ + if (setegid((gid_t)pw->pw_gid) < 0) { + reply(550, "Can't set gid."); + return; + } + (void) initgroups(pw->pw_name, pw->pw_gid); + + /* open wtmp before chroot */ + (void)sprintf(ttyline, "ftp%d", getpid()); + logwtmp(ttyline, pw->pw_name, remotehost); + logged_in = 1; + + if (guest) { + /* + * We MUST do a chdir() after the chroot. Otherwise + * the old current directory will be accessible as "." + * outside the new root! + */ + if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) { + reply(550, "Can't set guest privileges."); + goto bad; + } + } else if (chdir(pw->pw_dir) < 0) { + if (chdir("/") < 0) { + reply(530, "User %s: can't change directory to %s.", + pw->pw_name, pw->pw_dir); + goto bad; + } else + lreply(230, "No directory! Logging in with home=/"); + } + if (seteuid((uid_t)pw->pw_uid) < 0) { + reply(550, "Can't set uid."); + goto bad; + } + /* + * Display a login message, if it exists. + * N.B. reply(230,) must follow the message. + */ + if ((fd = fopen(_PATH_FTPLOGINMESG, "r")) != NULL) { + char *cp, line[LINE_MAX]; + + while (fgets(line, sizeof(line), fd) != NULL) { + if ((cp = strchr(line, '\n')) != NULL) + *cp = '\0'; + lreply(230, "%s", line); + } + (void) fflush(stdout); + (void) fclose(fd); + } + if (guest) { + reply(230, "Guest login ok, access restrictions apply."); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), + "%s: anonymous/%.*s", remotehost, + sizeof(proctitle) - sizeof(remotehost) - + sizeof(": anonymous/"), passwd); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + if (logging) + syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", + remotehost, passwd); + } else { + reply(230, "User %s logged in.", pw->pw_name); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), + "%s: %s", remotehost, pw->pw_name); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + if (logging) + syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", + remotehost, pw->pw_name); + } + (void) umask(defumask); + return; +bad: + /* Forget all about it... */ + end_login(); +} + +void +retrieve(cmd, name) + char *cmd, *name; +{ + FILE *fin, *dout; + struct stat st; + int (*closefunc) __P((FILE *)); + + if (cmd == 0) { + fin = fopen(name, "r"), closefunc = fclose; + st.st_size = 0; + } else { + char line[BUFSIZ]; + + (void) sprintf(line, cmd, name), name = line; + fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose; + st.st_size = -1; + st.st_blksize = BUFSIZ; + } + if (fin == NULL) { + if (errno != 0) { + perror_reply(550, name); + if (cmd == 0) { + LOGCMD("get", name); + } + } + return; + } + byte_count = -1; + if (cmd == 0 && (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode))) { + reply(550, "%s: not a plain file.", name); + goto done; + } + if (restart_point) { + if (type == TYPE_A) { + off_t i, n; + int c; + + n = restart_point; + i = 0; + while (i++ < n) { + if ((c=getc(fin)) == EOF) { + perror_reply(550, name); + goto done; + } + if (c == '\n') + i++; + } + } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { + perror_reply(550, name); + goto done; + } + } + dout = dataconn(name, st.st_size, "w"); + if (dout == NULL) + goto done; + send_data(fin, dout, st.st_blksize); + (void) fclose(dout); + data = -1; + pdata = -1; +done: + if (cmd == 0) + LOGBYTES("get", name, byte_count); + (*closefunc)(fin); +} + +void +store(name, mode, unique) + char *name, *mode; + int unique; +{ + FILE *fout, *din; + struct stat st; + int (*closefunc) __P((FILE *)); + + if (unique && stat(name, &st) == 0 && + (name = gunique(name)) == NULL) { + LOGCMD(*mode == 'w' ? "put" : "append", name); + return; + } + + if (restart_point) + mode = "r+"; + fout = fopen(name, mode); + closefunc = fclose; + if (fout == NULL) { + perror_reply(553, name); + LOGCMD(*mode == 'w' ? "put" : "append", name); + return; + } + byte_count = -1; + if (restart_point) { + if (type == TYPE_A) { + off_t i, n; + int c; + + n = restart_point; + i = 0; + while (i++ < n) { + if ((c=getc(fout)) == EOF) { + perror_reply(550, name); + goto done; + } + if (c == '\n') + i++; + } + /* + * We must do this seek to "current" position + * because we are changing from reading to + * writing. + */ + if (fseek(fout, 0L, L_INCR) < 0) { + perror_reply(550, name); + goto done; + } + } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { + perror_reply(550, name); + goto done; + } + } + din = dataconn(name, (off_t)-1, "r"); + if (din == NULL) + goto done; + if (receive_data(din, fout) == 0) { + if (unique) + reply(226, "Transfer complete (unique file name:%s).", + name); + else + reply(226, "Transfer complete."); + } + (void) fclose(din); + data = -1; + pdata = -1; +done: + LOGBYTES(*mode == 'w' ? "put" : "append", name, byte_count); + (*closefunc)(fout); +} + +static FILE * +getdatasock(mode) + char *mode; +{ + int on = 1, s, t, tries; + + if (data >= 0) + return (fdopen(data, mode)); + (void) seteuid((uid_t)0); + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + goto bad; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *) &on, sizeof(on)) < 0) + goto bad; + /* anchor socket to avoid multi-homing problems */ + data_source.sin_family = AF_INET; + data_source.sin_addr = ctrl_addr.sin_addr; + for (tries = 1; ; tries++) { + if (bind(s, (struct sockaddr *)&data_source, + sizeof(data_source)) >= 0) + break; + if (errno != EADDRINUSE || tries > 10) + goto bad; + sleep(tries); + } + (void) seteuid((uid_t)pw->pw_uid); +#ifdef IP_TOS + on = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); +#endif + return (fdopen(s, mode)); +bad: + /* Return the real value of errno (close may change it) */ + t = errno; + (void) seteuid((uid_t)pw->pw_uid); + (void) close(s); + errno = t; + return (NULL); +} + +static FILE * +dataconn(name, size, mode) + char *name; + off_t size; + char *mode; +{ + char sizebuf[32]; + FILE *file; + int retry = 0, tos; + + file_size = size; + byte_count = 0; + if (size != (off_t) -1) + (void) sprintf(sizebuf, " (%qd bytes)", size); + else + (void) strcpy(sizebuf, ""); + if (pdata >= 0) { + struct sockaddr_in from; + int s, fromlen = sizeof(from); + + s = accept(pdata, (struct sockaddr *)&from, &fromlen); + if (s < 0) { + reply(425, "Can't open data connection."); + (void) close(pdata); + pdata = -1; + return (NULL); + } + (void) close(pdata); + pdata = s; +#ifdef IP_TOS + tos = IPTOS_LOWDELAY; + (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, + sizeof(int)); +#endif + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (fdopen(pdata, mode)); + } + if (data >= 0) { + reply(125, "Using existing data connection for '%s'%s.", + name, sizebuf); + usedefault = 1; + return (fdopen(data, mode)); + } + if (usedefault) + data_dest = his_addr; + usedefault = 1; + file = getdatasock(mode); + if (file == NULL) { + reply(425, "Can't create data socket (%s,%d): %s.", + inet_ntoa(data_source.sin_addr), + ntohs(data_source.sin_port), strerror(errno)); + return (NULL); + } + data = fileno(file); + while (connect(data, (struct sockaddr *)&data_dest, + sizeof(data_dest)) < 0) { + if (errno == EADDRINUSE && retry < swaitmax) { + sleep((unsigned) swaitint); + retry += swaitint; + continue; + } + perror_reply(425, "Can't build data connection"); + (void) fclose(file); + data = -1; + return (NULL); + } + reply(150, "Opening %s mode data connection for '%s'%s.", + type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); + return (file); +} + +/* + * Tranfer the contents of "instr" to "outstr" peer using the appropriate + * encapsulation of the data subject * to Mode, Structure, and Type. + * + * NB: Form isn't handled. + */ +static void +send_data(instr, outstr, blksize) + FILE *instr, *outstr; + off_t blksize; +{ + int c, cnt, filefd, netfd; + char *buf; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return; + } + switch (type) { + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + if (c == '\n') { + if (ferror(outstr)) + goto data_err; + (void) putc('\r', outstr); + } + (void) putc(c, outstr); + } + fflush(outstr); + transflag = 0; + if (ferror(instr)) + goto file_err; + if (ferror(outstr)) + goto data_err; + reply(226, "Transfer complete."); + return; + + case TYPE_I: + case TYPE_L: + if ((buf = malloc((u_int)blksize)) == NULL) { + transflag = 0; + perror_reply(451, "Local resource failure: malloc"); + return; + } + netfd = fileno(outstr); + filefd = fileno(instr); + while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 && + write(netfd, buf, cnt) == cnt) + byte_count += cnt; + transflag = 0; + (void)free(buf); + if (cnt != 0) { + if (cnt < 0) + goto file_err; + goto data_err; + } + reply(226, "Transfer complete."); + return; + default: + transflag = 0; + reply(550, "Unimplemented TYPE %d in send_data", type); + return; + } + +data_err: + transflag = 0; + perror_reply(426, "Data connection"); + return; + +file_err: + transflag = 0; + perror_reply(551, "Error on input file"); +} + +/* + * Transfer data from peer to "outstr" using the appropriate encapulation of + * the data subject to Mode, Structure, and Type. + * + * N.B.: Form isn't handled. + */ +static int +receive_data(instr, outstr) + FILE *instr, *outstr; +{ + int c; + int cnt, bare_lfs = 0; + char buf[BUFSIZ]; + + transflag++; + if (setjmp(urgcatch)) { + transflag = 0; + return (-1); + } + switch (type) { + + case TYPE_I: + case TYPE_L: + while ((cnt = read(fileno(instr), buf, sizeof(buf))) > 0) { + if (write(fileno(outstr), buf, cnt) != cnt) + goto file_err; + byte_count += cnt; + } + if (cnt < 0) + goto data_err; + transflag = 0; + return (0); + + case TYPE_E: + reply(553, "TYPE E not implemented."); + transflag = 0; + return (-1); + + case TYPE_A: + while ((c = getc(instr)) != EOF) { + byte_count++; + if (c == '\n') + bare_lfs++; + while (c == '\r') { + if (ferror(outstr)) + goto data_err; + if ((c = getc(instr)) != '\n') { + (void) putc ('\r', outstr); + if (c == '\0' || c == EOF) + goto contin2; + } + } + (void) putc(c, outstr); + contin2: ; + } + fflush(outstr); + if (ferror(instr)) + goto data_err; + if (ferror(outstr)) + goto file_err; + transflag = 0; + if (bare_lfs) { + lreply(226, + "WARNING! %d bare linefeeds received in ASCII mode", + bare_lfs); + (void)printf(" File may not have transferred correctly.\r\n"); + } + return (0); + default: + reply(550, "Unimplemented TYPE %d in receive_data", type); + transflag = 0; + return (-1); + } + +data_err: + transflag = 0; + perror_reply(426, "Data Connection"); + return (-1); + +file_err: + transflag = 0; + perror_reply(452, "Error writing file"); + return (-1); +} + +void +statfilecmd(filename) + char *filename; +{ + FILE *fin; + int c; + char line[LINE_MAX]; + + (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename); + fin = ftpd_popen(line, "r"); + lreply(211, "status of %s:", filename); + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + if (ferror(stdout)){ + perror_reply(421, "control connection"); + (void) ftpd_pclose(fin); + dologout(1); + /* NOTREACHED */ + } + if (ferror(fin)) { + perror_reply(551, filename); + (void) ftpd_pclose(fin); + return; + } + (void) putc('\r', stdout); + } + (void) putc(c, stdout); + } + (void) ftpd_pclose(fin); + reply(211, "End of Status"); +} + +void +statcmd() +{ + struct sockaddr_in *sin; + u_char *a, *p; + + lreply(211, "%s FTP server status:", hostname, version); + printf(" %s\r\n", version); + printf(" Connected to %s", remotehost); + if (!isdigit(remotehost[0])) + printf(" (%s)", inet_ntoa(his_addr.sin_addr)); + printf("\r\n"); + if (logged_in) { + if (guest) + printf(" Logged in anonymously\r\n"); + else + printf(" Logged in as %s\r\n", pw->pw_name); + } else if (askpasswd) + printf(" Waiting for password\r\n"); + else + printf(" Waiting for user name\r\n"); + printf(" TYPE: %s", typenames[type]); + if (type == TYPE_A || type == TYPE_E) + printf(", FORM: %s", formnames[form]); + if (type == TYPE_L) +#if NBBY == 8 + printf(" %d", NBBY); +#else + printf(" %d", bytesize); /* need definition! */ +#endif + printf("; STRUcture: %s; transfer MODE: %s\r\n", + strunames[stru], modenames[mode]); + if (data != -1) + printf(" Data connection open\r\n"); + else if (pdata != -1) { + printf(" in Passive mode"); + sin = &pasv_addr; + goto printaddr; + } else if (usedefault == 0) { + printf(" PORT"); + sin = &data_dest; +printaddr: + a = (u_char *) &sin->sin_addr; + p = (u_char *) &sin->sin_port; +#define UC(b) (((int) b) & 0xff) + printf(" (%d,%d,%d,%d,%d,%d)\r\n", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); +#undef UC + } else + printf(" No data connection\r\n"); + reply(211, "End of status"); +} + +void +fatal(s) + char *s; +{ + + reply(451, "Error in server: %s\n", s); + reply(221, "Closing connection due to server error."); + dologout(0); + /* NOTREACHED */ +} + +void +#if __STDC__ +reply(int n, const char *fmt, ...) +#else +reply(n, fmt, va_alist) + int n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); + if (debug) { + syslog(LOG_DEBUG, "<--- %d ", n); + vsyslog(LOG_DEBUG, fmt, ap); + } +} + +void +#if __STDC__ +lreply(int n, const char *fmt, ...) +#else +lreply(n, fmt, va_alist) + int n; + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)printf("%d- ", n); + (void)vprintf(fmt, ap); + (void)printf("\r\n"); + (void)fflush(stdout); + if (debug) { + syslog(LOG_DEBUG, "<--- %d- ", n); + vsyslog(LOG_DEBUG, fmt, ap); + } +} + +static void +ack(s) + char *s; +{ + + reply(250, "%s command successful.", s); +} + +void +nack(s) + char *s; +{ + + reply(502, "%s command not implemented.", s); +} + +/* ARGSUSED */ +void +yyerror(s) + char *s; +{ + char *cp; + + if (cp = strchr(cbuf,'\n')) + *cp = '\0'; + reply(500, "'%s': command not understood.", cbuf); +} + +void +delete(name) + char *name; +{ + struct stat st; + + LOGCMD("delete", name); + if (stat(name, &st) < 0) { + perror_reply(550, name); + return; + } + if ((st.st_mode&S_IFMT) == S_IFDIR) { + if (rmdir(name) < 0) { + perror_reply(550, name); + return; + } + goto done; + } + if (unlink(name) < 0) { + perror_reply(550, name); + return; + } +done: + ack("DELE"); +} + +void +cwd(path) + char *path; +{ + + if (chdir(path) < 0) + perror_reply(550, path); + else + ack("CWD"); +} + +void +makedir(name) + char *name; +{ + + LOGCMD("mkdir", name); + if (mkdir(name, 0777) < 0) + perror_reply(550, name); + else + reply(257, "MKD command successful."); +} + +void +removedir(name) + char *name; +{ + + LOGCMD("rmdir", name); + if (rmdir(name) < 0) + perror_reply(550, name); + else + ack("RMD"); +} + +void +pwd() +{ + char path[MAXPATHLEN + 1]; + + if (getwd(path) == (char *)NULL) + reply(550, "%s.", path); + else + reply(257, "\"%s\" is current directory.", path); +} + +char * +renamefrom(name) + char *name; +{ + struct stat st; + + if (stat(name, &st) < 0) { + perror_reply(550, name); + return ((char *)0); + } + reply(350, "File exists, ready for destination name"); + return (name); +} + +void +renamecmd(from, to) + char *from, *to; +{ + + LOGCMD2("rename", from, to); + if (rename(from, to) < 0) + perror_reply(550, "rename"); + else + ack("RNTO"); +} + +static void +dolog(sin) + struct sockaddr_in *sin; +{ + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof(struct in_addr), AF_INET); + + if (hp) + (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)); + else + (void) strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof(remotehost)); +#ifdef SETPROCTITLE + snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); + setproctitle(proctitle); +#endif /* SETPROCTITLE */ + + if (logging) + syslog(LOG_INFO, "connection from %s", remotehost); +} + +/* + * Record logout in wtmp file + * and exit with supplied status. + */ +void +dologout(status) + int status; +{ + + if (logged_in) { + (void) seteuid((uid_t)0); + logwtmp(ttyline, "", ""); + } + /* beware of flushing buffers after a SIGPIPE */ + _exit(status); +} + +static void +myoob(signo) + int signo; +{ + char *cp; + + /* only process if transfer occurring */ + if (!transflag) + return; + cp = tmpline; + if (getline(cp, 7, stdin) == NULL) { + reply(221, "You could at least say goodbye."); + dologout(0); + } + upper(cp); + if (strcmp(cp, "ABOR\r\n") == 0) { + tmpline[0] = '\0'; + reply(426, "Transfer aborted. Data connection closed."); + reply(226, "Abort successful"); + longjmp(urgcatch, 1); + } + if (strcmp(cp, "STAT\r\n") == 0) { + if (file_size != (off_t) -1) + reply(213, "Status: %qd of %qd bytes transferred", + byte_count, file_size); + else + reply(213, "Status: %qd bytes transferred", byte_count); + } +} + +/* + * Note: a response of 425 is not mentioned as a possible response to + * the PASV command in RFC959. However, it has been blessed as + * a legitimate response by Jon Postel in a telephone conversation + * with Rick Adams on 25 Jan 89. + */ +void +passive() +{ + int len; + char *p, *a; + + pdata = socket(AF_INET, SOCK_STREAM, 0); + if (pdata < 0) { + perror_reply(425, "Can't open passive connection"); + return; + } + pasv_addr = ctrl_addr; + pasv_addr.sin_port = 0; + (void) seteuid((uid_t)0); + if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { + (void) seteuid((uid_t)pw->pw_uid); + goto pasv_error; + } + (void) seteuid((uid_t)pw->pw_uid); + len = sizeof(pasv_addr); + if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + goto pasv_error; + if (listen(pdata, 1) < 0) + goto pasv_error; + a = (char *) &pasv_addr.sin_addr; + p = (char *) &pasv_addr.sin_port; + +#define UC(b) (((int) b) & 0xff) + + reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), + UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); + return; + +pasv_error: + (void) close(pdata); + pdata = -1; + perror_reply(425, "Can't open passive connection"); + return; +} + +/* + * Generate unique name for file with basename "local". + * The file named "local" is already known to exist. + * Generates failure reply on error. + */ +static char * +gunique(local) + char *local; +{ + static char new[MAXPATHLEN]; + struct stat st; + int count; + char *cp; + + cp = strrchr(local, '/'); + if (cp) + *cp = '\0'; + if (stat(cp ? local : ".", &st) < 0) { + perror_reply(553, cp ? local : "."); + return ((char *) 0); + } + if (cp) + *cp = '/'; + (void) strcpy(new, local); + cp = new + strlen(new); + *cp++ = '.'; + for (count = 1; count < 100; count++) { + (void)sprintf(cp, "%d", count); + if (stat(new, &st) < 0) + return (new); + } + reply(452, "Unique file name cannot be created."); + return (NULL); +} + +/* + * Format and send reply containing system error number. + */ +void +perror_reply(code, string) + int code; + char *string; +{ + + reply(code, "%s: %s.", string, strerror(errno)); +} + +static char *onefile[] = { + "", + 0 +}; + +void +send_file_list(whichf) + char *whichf; +{ + struct stat st; + DIR *dirp = NULL; + struct dirent *dir; + FILE *dout = NULL; + char **dirlist, *dirname; + int simple = 0; + int freeglob = 0; + glob_t gl; + + if (strpbrk(whichf, "~{[*?") != NULL) { + int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + freeglob = 1; + if (glob(whichf, flags, 0, &gl)) { + reply(550, "not found"); + goto out; + } else if (gl.gl_pathc == 0) { + errno = ENOENT; + perror_reply(550, whichf); + goto out; + } + dirlist = gl.gl_pathv; + } else { + onefile[0] = whichf; + dirlist = onefile; + simple = 1; + } + + if (setjmp(urgcatch)) { + transflag = 0; + goto out; + } + while (dirname = *dirlist++) { + if (stat(dirname, &st) < 0) { + /* + * If user typed "ls -l", etc, and the client + * used NLST, do what the user meant. + */ + if (dirname[0] == '-' && *dirlist == NULL && + transflag == 0) { + retrieve("/bin/ls %s", dirname); + goto out; + } + perror_reply(550, whichf); + if (dout != NULL) { + (void) fclose(dout); + transflag = 0; + data = -1; + pdata = -1; + } + goto out; + } + + if (S_ISREG(st.st_mode)) { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, "w"); + if (dout == NULL) + goto out; + transflag++; + } + fprintf(dout, "%s%s\n", dirname, + type == TYPE_A ? "\r" : ""); + byte_count += strlen(dirname) + 1; + continue; + } else if (!S_ISDIR(st.st_mode)) + continue; + + if ((dirp = opendir(dirname)) == NULL) + continue; + + while ((dir = readdir(dirp)) != NULL) { + char nbuf[MAXPATHLEN]; + + if (dir->d_name[0] == '.' && dir->d_namlen == 1) + continue; + if (dir->d_name[0] == '.' && dir->d_name[1] == '.' && + dir->d_namlen == 2) + continue; + + sprintf(nbuf, "%s/%s", dirname, dir->d_name); + + /* + * We have to do a stat to insure it's + * not a directory or special file. + */ + if (simple || (stat(nbuf, &st) == 0 && + S_ISREG(st.st_mode))) { + if (dout == NULL) { + dout = dataconn("file list", (off_t)-1, + "w"); + if (dout == NULL) + goto out; + transflag++; + } + if (nbuf[0] == '.' && nbuf[1] == '/') + fprintf(dout, "%s%s\n", &nbuf[2], + type == TYPE_A ? "\r" : ""); + else + fprintf(dout, "%s%s\n", nbuf, + type == TYPE_A ? "\r" : ""); + byte_count += strlen(nbuf) + 1; + } + } + (void) closedir(dirp); + } + + if (dout == NULL) + reply(550, "No files found."); + else if (ferror(dout) != 0) + perror_reply(550, "Data connection"); + else + reply(226, "Transfer complete."); + + transflag = 0; + if (dout != NULL) + (void) fclose(dout); + data = -1; + pdata = -1; +out: + if (freeglob) { + freeglob = 0; + globfree(&gl); + } +} + +#ifdef SETPROCTITLE +/* + * Clobber argv so ps will show what we're doing. (Stolen from sendmail.) + * Warning, since this is usually started from inetd.conf, it often doesn't + * have much of an environment or arglist to overwrite. + */ +void +#if __STDC__ +setproctitle(const char *fmt, ...) +#else +setproctitle(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + int i; + va_list ap; + char *p, *bp, ch; + char buf[LINE_MAX]; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vsnprintf(buf, sizeof(buf), fmt, ap); + + /* make ps print our process name */ + p = Argv[0]; + *p++ = '-'; + + i = strlen(buf); + if (i > LastArgv - p - 2) { + i = LastArgv - p - 2; + buf[i] = '\0'; + } + bp = buf; + while (ch = *bp++) + if (ch != '\n' && ch != '\r') + *p++ = ch; + while (p < LastArgv) + *p++ = ' '; +} +#endif /* SETPROCTITLE */ diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c new file mode 100644 index 0000000..d40840c --- /dev/null +++ b/libexec/ftpd/logwtmp.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)logwtmp.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <utmp.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include "extern.h" + +static int fd = -1; + +/* + * Modified version of logwtmp that holds wtmp file open + * after first call, for use with ftp (which may chroot + * after login, but before logout). + */ +void +logwtmp(line, name, host) + char *line, *name, *host; +{ + struct utmp ut; + struct stat buf; + + if (fd < 0 && (fd = open(_PATH_WTMP, O_WRONLY|O_APPEND, 0)) < 0) + return; + if (fstat(fd, &buf) == 0) { + (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name)); + (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host)); + (void)time(&ut.ut_time); + if (write(fd, (char *)&ut, sizeof(struct utmp)) != + sizeof(struct utmp)) + (void)ftruncate(fd, buf.st_size); + } +} diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h new file mode 100644 index 0000000..2a50063 --- /dev/null +++ b/libexec/ftpd/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_FTPUSERS "/etc/ftpusers" +#define _PATH_FTPWELCOME "/etc/ftpwelcome" +#define _PATH_FTPLOGINMESG "/etc/motd" diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c new file mode 100644 index 0000000..b26732e --- /dev/null +++ b/libexec/ftpd/popen.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software written by Ken Arnold and + * published in UNIX Review, Vol. 6, No. 8. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + */ + +#ifndef lint +static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <glob.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +/* + * Special version of popen which avoids call to shell. This ensures noone + * may create a pipe to a hidden program as a side effect of a list or dir + * command. + */ +static int *pids; +static int fds; + +FILE * +ftpd_popen(program, type) + char *program, *type; +{ + char *cp; + FILE *iop; + int argc, gargc, pdes[2], pid; + char **pop, *argv[100], *gargv[1000]; + + if (*type != 'r' && *type != 'w' || type[1]) + return (NULL); + + if (!pids) { + if ((fds = getdtablesize()) <= 0) + return (NULL); + if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL) + return (NULL); + memset(pids, 0, fds * sizeof(int)); + } + if (pipe(pdes) < 0) + return (NULL); + + /* break up string into pieces */ + for (argc = 0, cp = program;; cp = NULL) + if (!(argv[argc++] = strtok(cp, " \t\n"))) + break; + + /* glob each piece */ + gargv[0] = argv[0]; + for (gargc = argc = 1; argv[argc]; argc++) { + glob_t gl; + int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; + + memset(&gl, 0, sizeof(gl)); + if (glob(argv[argc], flags, NULL, &gl)) + gargv[gargc++] = strdup(argv[argc]); + else + for (pop = gl.gl_pathv; *pop; pop++) + gargv[gargc++] = strdup(*pop); + globfree(&gl); + } + gargv[gargc] = NULL; + + iop = NULL; + switch(pid = vfork()) { + case -1: /* error */ + (void)close(pdes[0]); + (void)close(pdes[1]); + goto pfree; + /* NOTREACHED */ + case 0: /* child */ + if (*type == 'r') { + if (pdes[1] != STDOUT_FILENO) { + dup2(pdes[1], STDOUT_FILENO); + (void)close(pdes[1]); + } + dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ + (void)close(pdes[0]); + } else { + if (pdes[0] != STDIN_FILENO) { + dup2(pdes[0], STDIN_FILENO); + (void)close(pdes[0]); + } + (void)close(pdes[1]); + } + execv(gargv[0], gargv); + _exit(1); + } + /* parent; assume fdopen can't fail... */ + if (*type == 'r') { + iop = fdopen(pdes[0], type); + (void)close(pdes[1]); + } else { + iop = fdopen(pdes[1], type); + (void)close(pdes[0]); + } + pids[fileno(iop)] = pid; + +pfree: for (argc = 1; gargv[argc] != NULL; argc++) + free(gargv[argc]); + + return (iop); +} + +int +ftpd_pclose(iop) + FILE *iop; +{ + int fdes, omask, status; + pid_t pid; + + /* + * pclose returns -1 if stream is not associated with a + * `popened' command, or, if already `pclosed'. + */ + if (pids == 0 || pids[fdes = fileno(iop)] == 0) + return (-1); + (void)fclose(iop); + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); + while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) + continue; + (void)sigsetmask(omask); + pids[fdes] = 0; + if (pid < 0) + return (pid); + if (WIFEXITED(status)) + return (WEXITSTATUS(status)); + return (1); +} diff --git a/libexec/getNAME/Makefile b/libexec/getNAME/Makefile new file mode 100644 index 0000000..a78bc28 --- /dev/null +++ b/libexec/getNAME/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= getNAME +NOMAN= noman + +.include <bsd.prog.mk> diff --git a/libexec/getNAME/getNAME.c b/libexec/getNAME/getNAME.c new file mode 100644 index 0000000..2ab64b4 --- /dev/null +++ b/libexec/getNAME/getNAME.c @@ -0,0 +1,339 @@ +/*- + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1980, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)getNAME.c 8.1 (Berkeley) 6/30/93"; +#endif /* not lint */ + +/* + * Get name sections from manual pages. + * -t for building toc + * -i for building intro entries + * other apropos database + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int tocrc; +int intro; +int typeflag; + +void doname __P((char *)); +void dorefname __P((char *)); +void getfrom __P((char *)); +void split __P((char *, char *)); +void trimln __P((char *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "itw")) != EOF) + switch(ch) { + case 'i': + intro = 1; + break; + case 't': + tocrc = 1; + break; + case 'w': + typeflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + for (; *argv; ++argv) + getfrom(*argv); + exit(0); +} + +void +getfrom(pathname) + char *pathname; +{ + int i = 0; + char *name, *loc; + char headbuf[BUFSIZ]; + char linbuf[BUFSIZ]; + + if (freopen(pathname, "r", stdin) == 0) { + perror(pathname); + return; + } + if (name = strrchr(pathname, '/')) + name++; + else + name = pathname; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) { + if (typeflag) + printf("%-60s UNKNOWN\n", pathname); + return; + } + if (headbuf[0] != '.') + continue; + if ((headbuf[1] == 'T' && headbuf[2] == 'H') || + (headbuf[1] == 't' && headbuf[2] == 'h')) + break; + if (headbuf[1] == 'D' && headbuf[2] == 't') { + if (typeflag) { + printf("%-60s NEW\n", pathname); + return; + } + goto newman; + } + } + if (typeflag) { + printf("%-60s OLD\n", pathname); + return; + } + for (;;) { + if (fgets(linbuf, sizeof linbuf, stdin) == NULL) + return; + if (linbuf[0] != '.') + continue; + if (linbuf[1] == 'S' && linbuf[2] == 'H') + break; + if (linbuf[1] == 's' && linbuf[2] == 'h') + break; + } + trimln(headbuf); + if (tocrc) + doname(name); + if (!tocrc && !intro) + printf("%s\t", headbuf); + linbuf[0] = '\0'; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) + break; + if (headbuf[0] == '.') { + if (headbuf[1] == 'S' && headbuf[2] == 'H') + break; + if (headbuf[1] == 's' && headbuf[2] == 'h') + break; + } + if (i != 0) + strcat(linbuf, " "); + i++; + trimln(headbuf); + strcat(linbuf, headbuf); + } + if (intro) + split(linbuf, name); + else + printf("%s\n", linbuf); + return; + +newman: + for (;;) { + if (fgets(linbuf, sizeof linbuf, stdin) == NULL) + return; + if (linbuf[0] != '.') + continue; + if (linbuf[1] == 'S' && linbuf[2] == 'h') + break; + } + trimln(headbuf); + if (tocrc) + doname(name); + if (!tocrc && !intro) + printf(".TH%s\t", &headbuf[3]); + linbuf[0] = '\0'; + for (;;) { + if (fgets(headbuf, sizeof headbuf, stdin) == NULL) + break; + if (headbuf[0] == '.') { + if (headbuf[1] == 'S' && headbuf[2] == 'h') + break; + } + if (i != 0) + strcat(linbuf, " "); + i++; + trimln(headbuf); + for (loc = strchr(headbuf, ' '); loc; loc = strchr(loc, ' ')) + if (loc[1] == ',') + strcpy(loc, &loc[1]); + else + loc++; + if (headbuf[0] != '.') { + strcat(linbuf, headbuf); + } else { + /* + * Get rid of quotes in macros. + */ + for (loc = strchr(&headbuf[4], '"'); loc; ) { + strcpy(loc, &loc[1]); + loc = strchr(loc, '"'); + } + /* + * Handle cross references + */ + if (headbuf[1] == 'X' && headbuf[2] == 'r') { + for (loc = &headbuf[4]; *loc != ' '; loc++) + continue; + loc[0] = '('; + loc[2] = ')'; + loc[3] = '\0'; + } + /* + * Put dash between names and description. + */ + if (headbuf[1] == 'N' && headbuf[2] == 'd') + strcat(linbuf, "\\- "); + /* + * Skip over macro names. + */ + strcat(linbuf, &headbuf[4]); + } + } + if (intro) + split(linbuf, name); + else + printf("%s\n", linbuf); +} + +void +trimln(cp) + register char *cp; +{ + + while (*cp) + cp++; + if (*--cp == '\n') + *cp = 0; +} + +void +doname(name) + char *name; +{ + register char *dp = name, *ep; + +again: + while (*dp && *dp != '.') + putchar(*dp++); + if (*dp) + for (ep = dp+1; *ep; ep++) + if (*ep == '.') { + putchar(*dp++); + goto again; + } + putchar('('); + if (*dp) + dp++; + while (*dp) + putchar (*dp++); + putchar(')'); + putchar(' '); +} + +void +split(line, name) + char *line, *name; +{ + register char *cp, *dp; + char *sp, *sep; + + cp = strchr(line, '-'); + if (cp == 0) + return; + sp = cp + 1; + for (--cp; *cp == ' ' || *cp == '\t' || *cp == '\\'; cp--) + ; + *++cp = '\0'; + while (*sp && (*sp == ' ' || *sp == '\t')) + sp++; + for (sep = "", dp = line; dp && *dp; dp = cp, sep = "\n") { + cp = strchr(dp, ','); + if (cp) { + register char *tp; + + for (tp = cp - 1; *tp == ' ' || *tp == '\t'; tp--) + ; + *++tp = '\0'; + for (++cp; *cp == ' ' || *cp == '\t'; cp++) + ; + } + printf("%s%s\t", sep, dp); + dorefname(name); + printf("\t%s", sp); + } +} + +void +dorefname(name) + char *name; +{ + register char *dp = name, *ep; + +again: + while (*dp && *dp != '.') + putchar(*dp++); + if (*dp) + for (ep = dp+1; *ep; ep++) + if (*ep == '.') { + putchar(*dp++); + goto again; + } + putchar('.'); + if (*dp) + dp++; + while (*dp) + putchar (*dp++); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: getNAME [-it] file ...\n"); + exit(1); +} diff --git a/libexec/getty/ttydefaults.c b/libexec/getty/ttydefaults.c new file mode 100644 index 0000000..518f41b --- /dev/null +++ b/libexec/getty/ttydefaults.c @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)ttydefaults.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/termios.h> + +#include "extern.h" + +void +set_ttydefaults(fd) + int fd; +{ + struct termios term; + + tcgetattr(fd, &term); + term.c_iflag = TTYDEF_IFLAG; + term.c_oflag = TTYDEF_OFLAG; + term.c_lflag = TTYDEF_LFLAG; + term.c_cflag = TTYDEF_CFLAG; + tcsetattr(fd, TCSAFLUSH, &term); +} diff --git a/libexec/kpasswdd/Makefile b/libexec/kpasswdd/Makefile new file mode 100644 index 0000000..a01cbf8 --- /dev/null +++ b/libexec/kpasswdd/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= kpasswdd +SRCS= kpasswdd.c des_rw.c +CFLAGS+=-DCRYPT -DKERBEROS -I${.CURDIR}/../../usr.bin/passwd +DPADD= ${LIBKDB} ${LIBKRB} ${LIBDES} +LDADD= -lkdb -lkrb -ldes +.PATH: ${.CURDIR}/../../usr.bin/rlogin +MAN8= kpasswdd.0 + +.include <bsd.prog.mk> diff --git a/libexec/kpasswdd/kpasswdd.8 b/libexec/kpasswdd/kpasswdd.8 new file mode 100644 index 0000000..f6a401f --- /dev/null +++ b/libexec/kpasswdd/kpasswdd.8 @@ -0,0 +1,60 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)kpasswdd.8 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt KPASSWDD 8 +.Os +.Sh NAME +.Nm kpasswdd +.Nd Kerberos password changing daemon +.Sh SYNOPSIS +.Nm kpasswdd +.Sh DESCRIPTION +.Nm Kpasswdd +is the server for the +.Xr passwd 1 +program. +The server provides a remote password changing facility +with Kerberos authentication. +A user must provide the old Kerberos password, encrypted +in a random session key, to the server. +.Nm Kpasswdd +runs only on the Kerberos server, as it directly updates the +Kerberos database. +.Sh SEE ALSO +.Xr kerberos 1 , +.Xr passwd 1 +.Sh HISTORY +The +.Nm kpasswdd +utility first appeared in 4.4BSD. diff --git a/libexec/kpasswdd/kpasswdd.c b/libexec/kpasswdd/kpasswdd.c new file mode 100644 index 0000000..23ff1f8 --- /dev/null +++ b/libexec/kpasswdd/kpasswdd.c @@ -0,0 +1,271 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kpasswdd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * kpasswdd - update a principal's passwd field in the Kerberos + * database. Called from inetd. + * K. Fall + * 12-Dec-88 + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/signal.h> +#include <netinet/in.h> +#include <pwd.h> +#include <syslog.h> +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#include <kerberosIV/krb_db.h> +#include <stdio.h> +#include "kpasswd_proto.h" + +static struct kpasswd_data kpwd_data; +static des_cblock master_key, key; +static Key_schedule master_key_schedule, + key_schedule, random_sched; +long mkeyversion; +AUTH_DAT kdata; +static Principal principal_data; +static struct update_data ud_data; + +char inst[INST_SZ]; +char version[9]; +KTEXT_ST ticket; + +char *progname; /* for the library */ + +main() +{ + struct sockaddr_in foreign; + int foreign_len = sizeof(foreign); + int rval, more; + static char name[] = "kpasswdd"; + + static struct rlimit rl = { 0, 0 }; + + progname = name; + openlog("kpasswdd", LOG_CONS | LOG_PID, LOG_AUTH); + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + if (setrlimit(RLIMIT_CORE, &rl) < 0) { + syslog(LOG_ERR, "setrlimit: %m"); + exit(1); + } + + if (getpeername(0, &foreign, &foreign_len) < 0) { + syslog(LOG_ERR,"getpeername: %m"); + exit(1); + } + + strcpy(inst, "*"); + rval = krb_recvauth( + 0L, /* options--!MUTUAL */ + 0, /* file desc */ + &ticket, /* client's ticket */ + SERVICE, /* expected service */ + inst, /* expected instance */ + &foreign, /* foreign addr */ + (struct sockaddr_in *) 0, /* local addr */ + &kdata, /* returned krb data */ + "", /* service keys file */ + (bit_64 *) NULL, /* returned key schedule */ + version + ); + + + if (rval != KSUCCESS) { + syslog(LOG_NOTICE, "krb_recvauth: %s", krb_err_txt[rval]); + cleanup(); + exit(1); + } + + if (*version == '\0') { + /* indicates error on client's side (no tickets, etc.) */ + cleanup(); + exit(0); + } else if (strcmp(version, "KPWDV0.1") != 0) { + syslog(LOG_NOTICE, + "kpasswdd version conflict (recv'd %s)", + version); + cleanup(); + exit(1); + } + + + /* get master key */ + if (kdb_get_master_key(0, master_key, master_key_schedule) != 0) { + syslog(LOG_ERR, "couldn't get master key"); + cleanup(); + exit(1); + } + + mkeyversion = kdb_get_master_key(NULL, master_key, master_key_schedule); + + if (mkeyversion < 0) { + syslog(LOG_NOTICE, "couldn't verify master key"); + cleanup(); + exit(1); + } + + /* get principal info */ + rval = kerb_get_principal( + kdata.pname, + kdata.pinst, + &principal_data, + 1, + &more + ); + + if (rval < 0) { + syslog(LOG_NOTICE, + "error retrieving principal record for %s.%s", + kdata.pname, kdata.pinst); + cleanup(); + exit(1); + } + + if (rval != 1 || (more != 0)) { + syslog(LOG_NOTICE, "more than 1 dbase entry for %s.%s", + kdata.pname, kdata.pinst); + cleanup(); + exit(1); + } + + /* get the user's key */ + + bcopy(&principal_data.key_low, key, 4); + bcopy(&principal_data.key_high, ((long *) key) + 1, 4); + kdb_encrypt_key(key, key, master_key, master_key_schedule, + DECRYPT); + key_sched(key, key_schedule); + des_set_key(key, key_schedule); + + + /* get random key and send it over {random} Kperson */ + + random_key(kpwd_data.random_key); + strcpy(kpwd_data.secure_msg, SECURE_STRING); + if (des_write(0, &kpwd_data, sizeof(kpwd_data)) != sizeof(kpwd_data)) { + syslog(LOG_NOTICE, "error writing initial data"); + cleanup(); + exit(1); + } + + bzero(key, sizeof(key)); + bzero(key_schedule, sizeof(key_schedule)); + + /* now read update info: { info }Krandom */ + + key_sched(kpwd_data.random_key, random_sched); + des_set_key(kpwd_data.random_key, random_sched); + if (des_read(0, &ud_data, sizeof(ud_data)) != sizeof(ud_data)) { + syslog(LOG_NOTICE, "update aborted"); + cleanup(); + exit(1); + } + + /* validate info string by looking at the embedded string */ + + if (strcmp(ud_data.secure_msg, SECURE_STRING) != 0) { + syslog(LOG_NOTICE, "invalid update from %s", + inet_ntoa(foreign.sin_addr)); + cleanup(); + exit(1); + } + + /* produce the new key entry in the database { key }Kmaster */ + string_to_key(ud_data.pw, key); + kdb_encrypt_key(key, key, + master_key, master_key_schedule, + ENCRYPT); + bcopy(key, &principal_data.key_low, 4); + bcopy(((long *) key) + 1, + &principal_data.key_high, 4); + bzero(key, sizeof(key)); + principal_data.key_version++; + if (kerb_put_principal(&principal_data, 1)) { + syslog(LOG_ERR, "couldn't write new record for %s.%s", + principal_data.name, principal_data.instance); + cleanup(); + exit(1); + } + + syslog(LOG_NOTICE,"wrote new password field for %s.%s from %s", + principal_data.name, + principal_data.instance, + inet_ntoa(foreign.sin_addr) + ); + + send_ack(0, "Update complete.\n"); + cleanup(); + exit(0); +} + +cleanup() +{ + bzero(&kpwd_data, sizeof(kpwd_data)); + bzero(master_key, sizeof(master_key)); + bzero(master_key_schedule, sizeof(master_key_schedule)); + bzero(key, sizeof(key)); + bzero(key_schedule, sizeof(key_schedule)); + bzero(random_sched, sizeof(random_sched)); + bzero(&principal_data, sizeof(principal_data)); + bzero(&ud_data, sizeof(ud_data)); +} + +send_ack(remote, msg) + int remote; + char *msg; +{ + int cc; + cc = des_write(remote, msg, strlen(msg) + 1); + if (cc <= 0) { + syslog(LOG_NOTICE, "error writing ack"); + cleanup(); + exit(1); + } +} diff --git a/libexec/lfs_cleanerd/Makefile b/libexec/lfs_cleanerd/Makefile new file mode 100644 index 0000000..6993cf7 --- /dev/null +++ b/libexec/lfs_cleanerd/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= lfs_cleanerd +CFLAGS+=-I/sys/ufs/lfs -I${.CURDIR} -DDIAGNOSTIC +MAN8= lfs_cleanerd.0 +SRCS= cleanerd.c lfs_cksum.c library.c misc.c print.c + +.PATH: /sys/ufs/lfs + +.include <bsd.prog.mk> diff --git a/libexec/lfs_cleanerd/clean.h b/libexec/lfs_cleanerd/clean.h new file mode 100644 index 0000000..0dd8775 --- /dev/null +++ b/libexec/lfs_cleanerd/clean.h @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)clean.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * The LFS user-level library will be used when writing cleaners and + * checkers for LFS file systems. It will have facilities for finding + * and parsing LFS segments. + */ + +#define DUMP_SUM_HEADER 0x0001 +#define DUMP_INODE_ADDRS 0x0002 +#define DUMP_FINFOS 0x0004 +#define DUMP_ALL 0xFFFF + +#define IFILE_NAME "ifile" + +/* + * Cleaner parameters + * BUSY_LIM: lower bound of the number of segments currently available + * as a percentage of the total number of free segments possibly + * available. + * IDLE_LIM: Same as BUSY_LIM but used when the system is idle. + * MIN_SEGS: Minimum number of segments you should always have. + * I have no idea what this should be, but it should probably + * be a function of lfsp. + * NUM_TO_CLEAN: Number of segments to clean at once. Again, this + * should probably be based on the file system size and how + * full or empty the segments being cleaned are. + */ + +#define BUSY_LIM 0.50 +#define IDLE_LIM 0.90 + +#define MIN_SEGS(lfsp) (3) +#define NUM_TO_CLEAN(fsp) (1) + +#define MAXLOADS 3 +#define ONE_MIN 0 +#define FIVE_MIN 1 +#define FIFTEEN_MIN 2 + +typedef struct fs_info { + struct statfs *fi_statfsp; /* fsstat info from getfsstat */ + struct lfs fi_lfs; /* superblock */ + CLEANERINFO *fi_cip; /* Cleaner info from ifile */ + SEGUSE *fi_segusep; /* segment usage table (from ifile) */ + IFILE *fi_ifilep; /* ifile table (from ifile) */ + u_long fi_daddr_shift; /* shift to get byte offset of daddr */ + u_long fi_ifile_count; /* # entries in the ifile table */ + off_t fi_ifile_length; /* length of the ifile */ +} FS_INFO; + +/* + * XXX: size (in bytes) of a segment + * should lfs_bsize be fsbtodb(fs,1), blksize(fs), or lfs_dsize? + */ +#define seg_size(fs) ((fs)->lfs_ssize << (fs)->lfs_bshift) + +/* daddr -> byte offset */ +#define datobyte(fs, da) ((da) << (fs)->fi_daddr_shift) +#define bytetoda(fs, byte) ((byte) >> (fs)->fi_daddr_shift) + +#define CLEANSIZE(fsp) (fsp->fi_lfs.lfs_cleansz << fsp->fi_lfs.lfs_bshift) +#define SEGTABSIZE(fsp) (fsp->fi_lfs.lfs_segtabsz << fsp->fi_lfs.lfs_bshift) + +#define IFILE_ENTRY(fs, if, i) \ + ((IFILE *)((caddr_t)(if) + ((i) / (fs)->lfs_ifpb << (fs)->lfs_bshift)) \ + + (i) % (fs)->lfs_ifpb) + +#define SEGUSE_ENTRY(fs, su, i) \ + ((SEGUSE *)((caddr_t)(su) + (fs)->lfs_bsize * ((i) / (fs)->lfs_sepb)) +\ + (i) % (fs)->lfs_sepb) + +__BEGIN_DECLS +int dump_summary __P((struct lfs *, SEGSUM *, u_long, daddr_t **)); +void err __P((const int, const char *, ...)); +int fs_getmntinfo __P((struct statfs **, char *, int)); +int get __P((int, off_t, void *, size_t)); +FS_INFO *get_fs_info __P((struct statfs *, int)); +int lfs_segmapv __P((FS_INFO *, int, caddr_t, BLOCK_INFO **, int *)); +int mmap_segment __P((FS_INFO *, int, caddr_t *, int)); +void munmap_segment __P((FS_INFO *, caddr_t, int)); +void reread_fs_info __P((FS_INFO *, int)); +void toss __P((void *, int *, size_t, + int (*)(const void *, const void *, const void *), void *)); + +/* + * USEFUL DEBUGGING FUNCTIONS: + */ +#ifdef VERBOSE +#define PRINT_FINFO(fp, ip) { \ + (void)printf(" %s %s%d version %d nblocks %d\n", \ + (ip)->if_version > (fp)->fi_version ? "TOSSING" : "KEEPING", \ + "FINFO for inode: ", (fp)->fi_ino, \ + (fp)->fi_version, (fp)->fi_nblocks); \ + fflush(stdout); \ +} + +#define PRINT_INODE(b, bip) { \ + (void) printf("\t%s inode: %d daddr: 0x%lx create: %s\n", \ + b ? "KEEPING" : "TOSSING", (bip)->bi_inode, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_BINFO(bip) { \ + (void)printf("\tinode: %d lbn: %d daddr: 0x%lx create: %s\n", \ + (bip)->bi_inode, (bip)->bi_lbn, (bip)->bi_daddr, \ + ctime((time_t *)&(bip)->bi_segcreate)); \ + fflush(stdout); \ +} + +#define PRINT_SEGUSE(sup, n) { \ + (void)printf("Segment %d nbytes=%lu\tflags=%c%c%c ninos=%d nsums=%d lastmod: %s\n", \ + n, (sup)->su_nbytes, \ + (sup)->su_flags & SEGUSE_DIRTY ? 'D' : 'C', \ + (sup)->su_flags & SEGUSE_ACTIVE ? 'A' : ' ', \ + (sup)->su_flags & SEGUSE_SUPERBLOCK ? 'S' : ' ', \ + (sup)->su_ninos, (sup)->su_nsums, \ + ctime((time_t *)&(sup)->su_lastmod)); \ + fflush(stdout); \ +} + +void dump_super __P((struct lfs *)); +void dump_cleaner_info __P((void *)); +void print_SEGSUM __P(( struct lfs *, SEGSUM *)); +void print_CLEANERINFO __P((CLEANERINFO *)); +#else +#define PRINT_FINFO(fp, ip) +#define PRINT_INODE(b, bip) +#define PRINT_BINFO(bip) +#define PRINT_SEGUSE(sup, n) +#define dump_cleaner_info(cip) +#define dump_super(lfsp) +#endif +__END_DECLS diff --git a/libexec/lfs_cleanerd/cleanerd.c b/libexec/lfs_cleanerd/cleanerd.c new file mode 100644 index 0000000..515d2d5 --- /dev/null +++ b/libexec/lfs_cleanerd/cleanerd.c @@ -0,0 +1,498 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cleanerd.c 8.2 (Berkeley) 1/13/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "clean.h" +char *special = "cleanerd"; +int do_small = 0; +int do_mmap = 0; +struct cleaner_stats { + int blocks_read; + int blocks_written; + int segs_cleaned; + int segs_empty; + int segs_error; +} cleaner_stats; + +struct seglist { + int sl_id; /* segment number */ + int sl_cost; /* cleaning cost */ + char sl_empty; /* is segment empty */ +}; + +struct tossstruct { + struct lfs *lfs; + int seg; +}; + +/* function prototypes for system calls; not sure where they should go */ +int lfs_segwait __P((fsid_t *, struct timeval *)); +int lfs_segclean __P((fsid_t *, u_long)); +int lfs_bmapv __P((fsid_t *, BLOCK_INFO *, int)); +int lfs_markv __P((fsid_t *, BLOCK_INFO *, int)); + +/* function prototypes */ +int bi_tossold __P((const void *, const void *, const void *)); +int choose_segments __P((FS_INFO *, struct seglist *, + int (*)(FS_INFO *, SEGUSE *))); +void clean_fs __P((FS_INFO *, int (*)(FS_INFO *, SEGUSE *))); +int clean_loop __P((FS_INFO *)); +int clean_segment __P((FS_INFO *, int)); +int cost_benefit __P((FS_INFO *, SEGUSE *)); +int cost_compare __P((const void *, const void *)); +void sig_report __P((int)); + +/* + * Cleaning Cost Functions: + * + * These return the cost of cleaning a segment. The higher the cost value + * the better it is to clean the segment, so empty segments have the highest + * cost. (It is probably better to think of this as a priority value + * instead). + * + * This is the cost-benefit policy simulated and described in Rosenblum's + * 1991 SOSP paper. + */ + +int +cost_benefit(fsp, su) + FS_INFO *fsp; /* file system information */ + SEGUSE *su; +{ + struct lfs *lfsp; + struct timeval t; + int age; + int live; + + gettimeofday(&t, NULL); + + live = su->su_nbytes; + age = t.tv_sec < su->su_lastmod ? 0 : t.tv_sec - su->su_lastmod; + + lfsp = &fsp->fi_lfs; + if (live == 0) + return (t.tv_sec * lblkno(lfsp, seg_size(lfsp))); + else { + /* + * from lfsSegUsage.c (Mendel's code). + * priority calculation is done using INTEGER arithmetic. + * sizes are in BLOCKS (that is why we use lblkno below). + * age is in seconds. + * + * priority = ((seg_size - live) * age) / (seg_size + live) + */ +#ifdef VERBOSE + if (live < 0 || live > seg_size(lfsp)) { + err(0, "Bad segusage count: %d", live); + live = 0; + } +#endif + return (lblkno(lfsp, seg_size(lfsp) - live) * age) + / lblkno(lfsp, seg_size(lfsp) + live); + } +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FS_INFO *fsp; + struct statfs *lstatfsp; /* file system stats */ + struct timeval timeout; /* sleep timeout */ + fsid_t fsid; + int i, nodaemon; + int opt, cmd_err; + char *fs_name; /* name of filesystem to clean */ + extern int optind; + + cmd_err = nodaemon = 0; + while ((opt = getopt(argc, argv, "smd")) != EOF) { + switch (opt) { + case 's': /* small writes */ + do_small = 1; + break; + case 'm': + do_mmap = 1; + break; + case 'd': + nodaemon = 1; + break; + default: + ++cmd_err; + } + } + argc -= optind; + argv += optind; + if (cmd_err || (argc != 1)) + err(1, "usage: lfs_cleanerd [-smd] fs_name"); + + fs_name = argv[0]; + + signal(SIGINT, sig_report); + signal(SIGUSR1, sig_report); + signal(SIGUSR2, sig_report); + if (fs_getmntinfo(&lstatfsp, fs_name, MOUNT_LFS) == 0) { + /* didn't find the filesystem */ + err(1, "lfs_cleanerd: filesystem %s isn't an LFS!", fs_name); + } + + if (!nodaemon) /* should we become a daemon, chdir to / & close fd's */ + if (daemon(0, 0) == -1) + err(1, "lfs_cleanerd: couldn't become a daemon!"); + + timeout.tv_sec = 5*60; /* five minutes */ + timeout.tv_usec = 0; + fsid.val[0] = 0; + fsid.val[1] = 0; + + for (fsp = get_fs_info(lstatfsp, do_mmap); ; + reread_fs_info(fsp, do_mmap)) { + /* + * clean the filesystem, and, if it needed cleaning + * (i.e. it returned nonzero) try it again + * to make sure that some nasty process hasn't just + * filled the disk system up. + */ + if (clean_loop(fsp)) + continue; + +#ifdef VERBOSE + (void)printf("Cleaner going to sleep.\n"); +#endif + if (lfs_segwait(&fsid, &timeout) < 0) + err(0, "lfs_segwait: returned error\n"); +#ifdef VERBOSE + (void)printf("Cleaner waking up.\n"); +#endif + } +} + +/* return the number of segments cleaned */ +int +clean_loop(fsp) + FS_INFO *fsp; /* file system information */ +{ + double loadavg[MAXLOADS]; + time_t now; + u_long max_free_segs; + + /* + * Compute the maximum possible number of free segments, given the + * number of free blocks. + */ + max_free_segs = fsp->fi_statfsp->f_bfree / fsp->fi_lfs.lfs_ssize; + + /* + * We will clean if there are not enough free blocks or total clean + * space is less than BUSY_LIM % of possible clean space. + */ + now = time((time_t *)NULL); + if (fsp->fi_cip->clean < max_free_segs && + (fsp->fi_cip->clean <= MIN_SEGS(&fsp->fi_lfs) || + fsp->fi_cip->clean < max_free_segs * BUSY_LIM)) { + printf("Cleaner Running at %s (%d of %d segments available)\n", + ctime(&now), fsp->fi_cip->clean, max_free_segs); + clean_fs(fsp, cost_benefit); + return (1); + } else { + /* + * We will also clean if the system is reasonably idle and + * the total clean space is less then IDLE_LIM % of possible + * clean space. + */ + if (getloadavg(loadavg, MAXLOADS) == -1) { + perror("getloadavg: failed\n"); + return (-1); + } + if (loadavg[ONE_MIN] == 0.0 && loadavg[FIVE_MIN] && + fsp->fi_cip->clean < max_free_segs * IDLE_LIM) { + clean_fs(fsp, cost_benefit); + printf("Cleaner Running at %s (system idle)\n", + ctime(&now)); + return (1); + } + } + printf("Cleaner Not Running at %s\n", ctime(&now)); + return (0); +} + + +void +clean_fs(fsp, cost_func) + FS_INFO *fsp; /* file system information */ + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct seglist *segs, *sp; + int i; + + if ((segs = + malloc(fsp->fi_lfs.lfs_nseg * sizeof(struct seglist))) == NULL) { + err(0, "malloc failed"); + return; + } + i = choose_segments(fsp, segs, cost_func); +#ifdef VERBOSE + printf("clean_fs: found %d segments to clean in file system %s\n", + i, fsp->fi_statfsp->f_mntonname); + fflush(stdout); +#endif + if (i) + for (i = MIN(i, NUM_TO_CLEAN(fsp)), sp = segs; i-- ; ++sp) { + if (clean_segment(fsp, sp->sl_id) < 0) + perror("clean_segment failed"); + else if (lfs_segclean(&fsp->fi_statfsp->f_fsid, + sp->sl_id) < 0) + perror("lfs_segclean failed"); + printf("Completed cleaning segment %d\n", sp->sl_id); + } + free(segs); +} + +/* + * Segment with the highest priority get sorted to the beginning of the + * list. This sort assumes that empty segments always have a higher + * cost/benefit than any utilized segment. + */ +int +cost_compare(a, b) + const void *a; + const void *b; +{ + return (((struct seglist *)b)->sl_cost - + ((struct seglist *)a)->sl_cost); +} + + +/* + * Returns the number of segments to be cleaned with the elements of seglist + * filled in. + */ +int +choose_segments(fsp, seglist, cost_func) + FS_INFO *fsp; + struct seglist *seglist; + int (*cost_func) __P((FS_INFO *, SEGUSE *)); +{ + struct lfs *lfsp; + struct seglist *sp; + SEGUSE *sup; + int i, nsegs; + + lfsp = &fsp->fi_lfs; + +#ifdef VERBOSE + (void)printf("Entering choose_segments\n"); +#endif + dump_super(lfsp); + dump_cleaner_info(fsp->fi_cip); + + for (sp = seglist, i = 0; i < lfsp->lfs_nseg; ++i) { + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, i); + PRINT_SEGUSE(sup, i); + if (!(sup->su_flags & SEGUSE_DIRTY) || + sup->su_flags & SEGUSE_ACTIVE) + continue; +#ifdef VERBOSE + (void)printf("\tchoosing segment %d\n", i); +#endif + sp->sl_cost = (*cost_func)(fsp, sup); + sp->sl_id = i; + sp->sl_empty = sup->su_nbytes ? 0 : 1; + ++sp; + } + nsegs = sp - seglist; + qsort(seglist, nsegs, sizeof(struct seglist), cost_compare); +#ifdef VERBOSE + (void)printf("Returning %d segments\n", nsegs); +#endif + return (nsegs); +} + + +int +clean_segment(fsp, id) + FS_INFO *fsp; /* file system information */ + int id; /* segment number */ +{ + BLOCK_INFO *block_array, *bp; + SEGUSE *sp; + struct lfs *lfsp; + struct tossstruct t; + caddr_t seg_buf; + int num_blocks, maxblocks, clean_blocks; + + lfsp = &fsp->fi_lfs; + sp = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, id); + +#ifdef VERBOSE + (void)printf("cleaning segment %d: contains %lu bytes\n", id, + sp->su_nbytes); + fflush(stdout); +#endif + /* XXX could add debugging to verify that segment is really empty */ + if (sp->su_nbytes == sp->su_nsums * LFS_SUMMARY_SIZE) { + ++cleaner_stats.segs_empty; + return (0); + } + + /* map the segment into a buffer */ + if (mmap_segment(fsp, id, &seg_buf, do_mmap) < 0) { + err(0, "mmap_segment failed"); + ++cleaner_stats.segs_error; + return (-1); + } + /* get a list of blocks that are contained by the segment */ + if (lfs_segmapv(fsp, id, seg_buf, &block_array, &num_blocks) < 0) { + err(0, "clean_segment: lfs_segmapv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + cleaner_stats.blocks_read += fsp->fi_lfs.lfs_ssize; + +#ifdef VERBOSE + (void)printf("lfs_segmapv returned %d blocks\n", num_blocks); + fflush(stdout); +#endif + + /* get the current disk address of blocks contained by the segment */ + if (lfs_bmapv(&fsp->fi_statfsp->f_fsid, block_array, num_blocks) < 0) { + perror("clean_segment: lfs_bmapv failed\n"); + ++cleaner_stats.segs_error; + return -1; + } + + /* Now toss any blocks not in the current segment */ + t.lfs = lfsp; + t.seg = id; + toss(block_array, &num_blocks, sizeof(BLOCK_INFO), bi_tossold, &t); + + /* Check if last element should be tossed */ + if (num_blocks && bi_tossold(&t, block_array + num_blocks - 1, NULL)) + --num_blocks; + +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + u_long *lp; + int i; + + (void)printf("after bmapv still have %d blocks\n", num_blocks); + fflush(stdout); + if (num_blocks) + printf("BLOCK INFOS\n"); + for (_bip = block_array, i=0; i < num_blocks; ++_bip, ++i) { + PRINT_BINFO(_bip); + lp = (u_long *)_bip->bi_bp; + } + } +#endif + cleaner_stats.blocks_written += num_blocks; + if (do_small) + maxblocks = MAXPHYS / fsp->fi_lfs.lfs_bsize - 1; + else + maxblocks = num_blocks; + + for (bp = block_array; num_blocks > 0; bp += clean_blocks) { + clean_blocks = maxblocks < num_blocks ? maxblocks : num_blocks; + if (lfs_markv(&fsp->fi_statfsp->f_fsid, + bp, clean_blocks) < 0) { + err(0, "clean_segment: lfs_markv failed"); + ++cleaner_stats.segs_error; + return (-1); + } + num_blocks -= clean_blocks; + } + + free(block_array); + munmap_segment(fsp, seg_buf, do_mmap); + ++cleaner_stats.segs_cleaned; + return (0); +} + + +int +bi_tossold(client, a, b) + const void *client; + const void *a; + const void *b; +{ + const struct tossstruct *t; + + t = (struct tossstruct *)client; + + return (((BLOCK_INFO *)a)->bi_daddr == LFS_UNUSED_DADDR || + datosn(t->lfs, ((BLOCK_INFO *)a)->bi_daddr) != t->seg); +} + +void +sig_report(sig) + int sig; +{ + printf("lfs_cleanerd:\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n\t\t%s%d\n", + "blocks_read ", cleaner_stats.blocks_read, + "blocks_written ", cleaner_stats.blocks_written, + "segs_cleaned ", cleaner_stats.segs_cleaned, + "segs_empty ", cleaner_stats.segs_empty, + "seg_error ", cleaner_stats.segs_error); + if (sig == SIGUSR2) { + cleaner_stats.blocks_read = 0; + cleaner_stats.blocks_written = 0; + cleaner_stats.segs_cleaned = 0; + cleaner_stats.segs_empty = 0; + cleaner_stats.segs_error = 0; + } + if (sig == SIGINT) + exit(0); +} diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.8 b/libexec/lfs_cleanerd/lfs_cleanerd.8 new file mode 100644 index 0000000..3d134db --- /dev/null +++ b/libexec/lfs_cleanerd/lfs_cleanerd.8 @@ -0,0 +1,77 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)lfs_cleanerd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd "December 11, 1993" +.Dt LFS_CLEANERD 8 +.Os BSD 4.4 +.Sh NAME +.Nm lfs_cleanerd +.Nd garbage collect a log-structured file system +.Sh SYNOPSIS +.Nm lfs_cleanerd +.Op Fl ds +.Pa node +.Sh DESCRIPTION +The +.Nm lfs_cleanerd +command starts a daemon process which garbage-collects +the log-structured file system residing at the point named by +.Ar node +in the global file system namespace. +This command is normally executed by +.Xr mount_lfs 8 +when the log-structured file system is mounted. +The daemon will exit within a few minutes +of when the file system it was cleaning is unmounted. +.Pp +Garbage collection on a log-structured file system is done by scanning +the file system's segments for active, i.e. referenced, data and copying +it to new segments. +When all of the active data in a given segment has been copied to a new +segment that segment can be marked as empty, thus reclaiming the space +taken by the inactive data which was in it. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl d +Run in debug mode. +Do not become a daemon process, and print debugging information. +.It Fl s +When cleaning the file system, read data in small chunks. +.El +.Sh SEE ALSO +.Xr mount_lfs 8 +.Sh HISTORY +The +.Nm lfs_cleanerd +utility first appeared in 4.4BSD. diff --git a/libexec/lfs_cleanerd/library.c b/libexec/lfs_cleanerd/library.c new file mode 100644 index 0000000..b825a02 --- /dev/null +++ b/libexec/lfs_cleanerd/library.c @@ -0,0 +1,671 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)library.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/types.h> +#include <sys/mman.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "clean.h" + +void add_blocks __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t, daddr_t)); +void add_inodes __P((FS_INFO *, BLOCK_INFO *, int *, SEGSUM *, caddr_t, + daddr_t)); +int bi_compare __P((const void *, const void *)); +int bi_toss __P((const void *, const void *, const void *)); +void get_ifile __P((FS_INFO *, int)); +int get_superblock __P((FS_INFO *, struct lfs *)); +int pseg_valid __P((FS_INFO *, SEGSUM *)); + +/* + * This function will get information on a a filesystem which matches + * the name and type given. If a "name" is in a filesystem of the given + * type, then buf is filled with that filesystem's info, and the + * a non-zero value is returned. + */ +int +fs_getmntinfo(buf, name, type) + struct statfs **buf; + char *name; + int type; +{ + /* allocate space for the filesystem info */ + *buf = (struct statfs *)malloc(sizeof(struct statfs)); + if (*buf == NULL) + return 0; + + /* grab the filesystem info */ + if (statfs(name, *buf) < 0) { + free(*buf); + return 0; + } + + /* check to see if it's the one we want */ + if (((*buf)->f_type != type) || + strncmp(name, (*buf)->f_mntonname, MNAMELEN)) { + /* "this is not the filesystem you're looking for */ + free(*buf); + return 0; + } + + return 1; +} + +/* + * Get all the information available on an LFS file system. + * Returns an pointer to an FS_INFO structure, NULL on error. + */ +FS_INFO * +get_fs_info (lstatfsp, use_mmap) + struct statfs *lstatfsp; /* IN: pointer to statfs struct */ + int use_mmap; /* IN: mmap or read */ +{ + FS_INFO *fsp; + int i; + + fsp = (FS_INFO *)malloc(sizeof(FS_INFO)); + if (fsp == NULL) + return NULL; + bzero(fsp, sizeof(FS_INFO)); + + fsp->fi_statfsp = lstatfsp; + if (get_superblock (fsp, &fsp->fi_lfs)) + err(1, "get_fs_info: get_superblock failed"); + fsp->fi_daddr_shift = + fsp->fi_lfs.lfs_bshift - fsp->fi_lfs.lfs_fsbtodb; + get_ifile (fsp, use_mmap); + return (fsp); +} + +/* + * If we are reading the ifile then we need to refresh it. Even if + * we are mmapping it, it might have grown. Finally, we need to + * refresh the file system information (statfs) info. + */ +void +reread_fs_info(fsp, use_mmap) + FS_INFO *fsp; /* IN: prointer fs_infos to reread */ + int use_mmap; +{ + int i; + + if (statfs(fsp->fi_statfsp->f_mntonname, fsp->fi_statfsp)) + err(1, "reread_fs_info: statfs failed"); + get_ifile (fsp, use_mmap); +} + +/* + * Gets the superblock from disk (possibly in face of errors) + */ +int +get_superblock (fsp, sbp) + FS_INFO *fsp; /* local file system info structure */ + struct lfs *sbp; +{ + char mntfromname[MNAMELEN+1]; + int fid; + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "get_superblock: bad open"); + return (-1); + } + + get(fid, LFS_LABELPAD, sbp, sizeof(struct lfs)); + close (fid); + + return (0); +} + +/* + * This function will map the ifile into memory. It causes a + * fatal error on failure. + */ +void +get_ifile (fsp, use_mmap) + FS_INFO *fsp; + int use_mmap; + +{ + struct stat file_stat; + caddr_t ifp; + char *ifile_name; + int count, fid; + + ifp = NULL; + ifile_name = malloc(strlen(fsp->fi_statfsp->f_mntonname) + + strlen(IFILE_NAME)+2); + strcat(strcat(strcpy(ifile_name, fsp->fi_statfsp->f_mntonname), "/"), + IFILE_NAME); + + if ((fid = open(ifile_name, O_RDWR, (mode_t)0)) < 0) + err(1, "get_ifile: bad open"); + + if (fstat (fid, &file_stat)) + err(1, "get_ifile: fstat failed"); + + if (use_mmap && file_stat.st_size == fsp->fi_ifile_length) { + (void) close(fid); + return; + } + + /* get the ifile */ + if (use_mmap) { + if (fsp->fi_cip) + munmap((caddr_t)fsp->fi_cip, fsp->fi_ifile_length); + ifp = mmap ((caddr_t)0, file_stat.st_size, + PROT_READ|PROT_WRITE, 0, fid, (off_t)0); + if (ifp == (caddr_t)(-1)) + err(1, "get_ifile: mmap failed"); + } else { + if (fsp->fi_cip) + free(fsp->fi_cip); + if (!(ifp = malloc (file_stat.st_size))) + err (1, "get_ifile: malloc failed"); +redo_read: + count = read (fid, ifp, (size_t) file_stat.st_size); + + if (count < 0) + err(1, "get_ifile: bad ifile read"); + else if (count < file_stat.st_size) { + err(0, "get_ifile"); + if (lseek(fid, 0, SEEK_SET) < 0) + err(1, "get_ifile: bad ifile lseek"); + goto redo_read; + } + } + fsp->fi_ifile_length = file_stat.st_size; + close (fid); + + fsp->fi_cip = (CLEANERINFO *)ifp; + fsp->fi_segusep = (SEGUSE *)(ifp + CLEANSIZE(fsp)); + fsp->fi_ifilep = (IFILE *)((caddr_t)fsp->fi_segusep + SEGTABSIZE(fsp)); + + /* + * The number of ifile entries is equal to the number of blocks + * blocks in the ifile minus the ones allocated to cleaner info + * and segment usage table multiplied by the number of ifile + * entries per page. + */ + fsp->fi_ifile_count = (fsp->fi_ifile_length >> fsp->fi_lfs.lfs_bshift - + fsp->fi_lfs.lfs_cleansz - fsp->fi_lfs.lfs_segtabsz) * + fsp->fi_lfs.lfs_ifpb; + + free (ifile_name); +} + +/* + * This function will scan a segment and return a list of + * <inode, blocknum> pairs which indicate which blocks were + * contained as live data within the segment when the segment + * summary was read (it may have "died" since then). Any given + * pair will be listed at most once. + */ +int +lfs_segmapv(fsp, seg, seg_buf, blocks, bcount) + FS_INFO *fsp; /* pointer to local file system information */ + int seg; /* the segment number */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + BLOCK_INFO **blocks; /* OUT: array of block_info for live blocks */ + int *bcount; /* OUT: number of active blocks in segment */ +{ + BLOCK_INFO *bip; + SEGSUM *sp; + SEGUSE *sup; + FINFO *fip; + struct lfs *lfsp; + caddr_t s, segend; + daddr_t pseg_addr, seg_addr; + int i, nelem, nblocks, sumsize; + time_t timestamp; + + lfsp = &fsp->fi_lfs; + nelem = 2 * lfsp->lfs_ssize; + if (!(bip = malloc(nelem * sizeof(BLOCK_INFO)))) + goto err0; + + sup = SEGUSE_ENTRY(lfsp, fsp->fi_segusep, seg); + s = seg_buf + (sup->su_flags & SEGUSE_SUPERBLOCK ? LFS_SBPAD : 0); + seg_addr = sntoda(lfsp, seg); + pseg_addr = seg_addr + (sup->su_flags & SEGUSE_SUPERBLOCK ? btodb(LFS_SBPAD) : 0); +#ifdef VERBOSE + printf("\tsegment buffer at: 0x%x\tseg_addr 0x%x\n", s, seg_addr); +#endif /* VERBOSE */ + + *bcount = 0; + for (segend = seg_buf + seg_size(lfsp), timestamp = 0; s < segend; ) { + sp = (SEGSUM *)s; + +#ifdef VERBOSE + printf("\tpartial at: 0x%x\n", pseg_addr); + print_SEGSUM(lfsp, sp); + fflush(stdout); +#endif /* VERBOSE */ + + nblocks = pseg_valid(fsp, sp); + if (nblocks <= 0) + break; + + /* Check if we have hit old data */ + if (timestamp > ((SEGSUM*)s)->ss_create) + break; + timestamp = ((SEGSUM*)s)->ss_create; + +#ifdef DIAGNOSTIC + /* Verfiy size of summary block */ + sumsize = sizeof(SEGSUM) + + (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + for (fip = (FINFO *)(sp + 1); i < sp->ss_nfinfo; ++i) { + sumsize += sizeof(FINFO) + + (fip->fi_nblocks - 1) * sizeof(daddr_t); + fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks]); + } + if (sumsize > LFS_SUMMARY_SIZE) { + fprintf(stderr, + "Segment %d summary block too big: %d\n", + seg, sumsize); + exit(1); + } +#endif + + if (*bcount + nblocks + sp->ss_ninos > nelem) { + nelem = *bcount + nblocks + sp->ss_ninos; + bip = realloc (bip, nelem * sizeof(BLOCK_INFO)); + if (!bip) + goto err0; + } + add_blocks(fsp, bip, bcount, sp, seg_buf, seg_addr, pseg_addr); + add_inodes(fsp, bip, bcount, sp, seg_buf, seg_addr); + pseg_addr += fsbtodb(lfsp, nblocks) + + bytetoda(fsp, LFS_SUMMARY_SIZE); + s += (nblocks << lfsp->lfs_bshift) + LFS_SUMMARY_SIZE; + } + qsort(bip, *bcount, sizeof(BLOCK_INFO), bi_compare); + toss(bip, bcount, sizeof(BLOCK_INFO), bi_toss, NULL); +#ifdef VERBOSE + { + BLOCK_INFO *_bip; + int i; + + printf("BLOCK INFOS\n"); + for (_bip = bip, i=0; i < *bcount; ++_bip, ++i) + PRINT_BINFO(_bip); + } +#endif + *blocks = bip; + return (0); + +err0: *bcount = 0; + return (-1); + +} + +/* + * This will parse a partial segment and fill in BLOCK_INFO structures + * for each block described in the segment summary. It will not include + * blocks or inodes from files with new version numbers. + */ +void +add_blocks (fsp, bip, countp, sp, seg_buf, segaddr, psegaddr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* Block info array */ + int *countp; /* IN/OUT: number of blocks in array */ + SEGSUM *sp; /* segment summmary pointer */ + caddr_t seg_buf; /* buffer containing segment */ + daddr_t segaddr; /* address of this segment */ + daddr_t psegaddr; /* address of this partial segment */ +{ + IFILE *ifp; + FINFO *fip; + caddr_t bp; + daddr_t *dp, *iaddrp; + int db_per_block, i, j; + u_long page_size; + +#ifdef VERBOSE + printf("FILE INFOS\n"); +#endif + db_per_block = fsbtodb(&fsp->fi_lfs, 1); + page_size = fsp->fi_lfs.lfs_bsize; + bp = seg_buf + datobyte(fsp, psegaddr - segaddr) + LFS_SUMMARY_SIZE; + bip += *countp; + psegaddr += bytetoda(fsp, LFS_SUMMARY_SIZE); + iaddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + --iaddrp; + for (fip = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; + ++i, fip = (FINFO *)(&fip->fi_blocks[fip->fi_nblocks])) { + + ifp = IFILE_ENTRY(&fsp->fi_lfs, fsp->fi_ifilep, fip->fi_ino); + PRINT_FINFO(fip, ifp); + if (ifp->if_version > fip->fi_version) + continue; + dp = &(fip->fi_blocks[0]); + for (j = 0; j < fip->fi_nblocks; j++, dp++) { + while (psegaddr == *iaddrp) { + psegaddr += db_per_block; + bp += page_size; + --iaddrp; + } + bip->bi_inode = fip->fi_ino; + bip->bi_lbn = *dp; + bip->bi_daddr = psegaddr; + bip->bi_segcreate = (time_t)(sp->ss_create); + bip->bi_bp = bp; + bip->bi_version = ifp->if_version; + psegaddr += db_per_block; + bp += page_size; + ++bip; + ++(*countp); + } + } +} + +/* + * For a particular segment summary, reads the inode blocks and adds + * INODE_INFO structures to the array. Returns the number of inodes + * actually added. + */ +void +add_inodes (fsp, bip, countp, sp, seg_buf, seg_addr) + FS_INFO *fsp; /* pointer to super block */ + BLOCK_INFO *bip; /* block info array */ + int *countp; /* pointer to current number of inodes */ + SEGSUM *sp; /* segsum pointer */ + caddr_t seg_buf; /* the buffer containing the segment's data */ + daddr_t seg_addr; /* disk address of seg_buf */ +{ + struct dinode *di; + struct lfs *lfsp; + IFILE *ifp; + BLOCK_INFO *bp; + daddr_t *daddrp; + ino_t inum; + int i; + + if (sp->ss_ninos <= 0) + return; + + bp = bip + *countp; + lfsp = &fsp->fi_lfs; +#ifdef VERBOSE + (void) printf("INODES:\n"); +#endif + daddrp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (i = 0; i < sp->ss_ninos; ++i) { + if (i % INOPB(lfsp) == 0) { + --daddrp; + di = (struct dinode *)(seg_buf + + ((*daddrp - seg_addr) << fsp->fi_daddr_shift)); + } else + ++di; + + inum = di->di_inumber; + bp->bi_lbn = LFS_UNUSED_LBN; + bp->bi_inode = inum; + bp->bi_daddr = *daddrp; + bp->bi_bp = di; + bp->bi_segcreate = sp->ss_create; + + if (inum == LFS_IFILE_INUM) { + bp->bi_version = 1; /* Ifile version should be 1 */ + bp++; + ++(*countp); + PRINT_INODE(1, bp); + } else { + ifp = IFILE_ENTRY(lfsp, fsp->fi_ifilep, inum); + PRINT_INODE(ifp->if_daddr == *daddrp, bp); + bp->bi_version = ifp->if_version; + if (ifp->if_daddr == *daddrp) { + bp++; + ++(*countp); + } + } + } +} + +/* + * Checks the summary checksum and the data checksum to determine if the + * segment is valid or not. Returns the size of the partial segment if it + * is valid, * and 0 otherwise. Use dump_summary to figure out size of the + * the partial as well as whether or not the checksum is valid. + */ +int +pseg_valid (fsp, ssp) + FS_INFO *fsp; /* pointer to file system info */ + SEGSUM *ssp; /* pointer to segment summary block */ +{ + caddr_t p; + int i, nblocks; + u_long *datap; + + if ((nblocks = dump_summary(&fsp->fi_lfs, ssp, 0, NULL)) <= 0 || + nblocks > fsp->fi_lfs.lfs_ssize - 1) + return(0); + + /* check data/inode block(s) checksum too */ + datap = (u_long *)malloc(nblocks * sizeof(u_long)); + p = (caddr_t)ssp + LFS_SUMMARY_SIZE; + for (i = 0; i < nblocks; ++i) { + datap[i] = *((u_long *)p); + p += fsp->fi_lfs.lfs_bsize; + } + if (cksum ((void *)datap, nblocks * sizeof(u_long)) != ssp->ss_datasum) + return (0); + + return (nblocks); +} + + +/* #define MMAP_SEGMENT */ +/* + * read a segment into a memory buffer + */ +int +mmap_segment (fsp, segment, segbuf, use_mmap) + FS_INFO *fsp; /* file system information */ + int segment; /* segment number */ + caddr_t *segbuf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read */ +{ + struct lfs *lfsp; + int fid; /* fildes for file system device */ + daddr_t seg_daddr; /* base disk address of segment */ + off_t seg_byte; + size_t ssize; + char mntfromname[MNAMELEN+2]; + + lfsp = &fsp->fi_lfs; + + /* get the disk address of the beginning of the segment */ + seg_daddr = sntoda(lfsp, segment); + seg_byte = datobyte(fsp, seg_daddr); + ssize = seg_size(lfsp); + + strcpy(mntfromname, "/dev/r"); + strcat(mntfromname, fsp->fi_statfsp->f_mntfromname+5); + + if ((fid = open(mntfromname, O_RDONLY, (mode_t)0)) < 0) { + err(0, "mmap_segment: bad open"); + return (-1); + } + + if (use_mmap) { + *segbuf = mmap ((caddr_t)0, seg_size(lfsp), PROT_READ, + 0, fid, seg_byte); + if (*(long *)segbuf < 0) { + err(0, "mmap_segment: mmap failed"); + return (NULL); + } + } else { +#ifdef VERBOSE + printf("mmap_segment\tseg_daddr: %lu\tseg_size: %lu\tseg_offset: %qu\n", + seg_daddr, ssize, seg_byte); +#endif + /* malloc the space for the buffer */ + *segbuf = malloc(ssize); + if (!*segbuf) { + err(0, "mmap_segment: malloc failed"); + return(NULL); + } + + /* read the segment data into the buffer */ + if (lseek (fid, seg_byte, SEEK_SET) != seg_byte) { + err (0, "mmap_segment: bad lseek"); + free(*segbuf); + return (-1); + } + + if (read (fid, *segbuf, ssize) != ssize) { + err (0, "mmap_segment: bad read"); + free(*segbuf); + return (-1); + } + } + close (fid); + + return (0); +} + +void +munmap_segment (fsp, seg_buf, use_mmap) + FS_INFO *fsp; /* file system information */ + caddr_t seg_buf; /* pointer to buffer area */ + int use_mmap; /* mmap instead of read/write */ +{ + if (use_mmap) + munmap (seg_buf, seg_size(&fsp->fi_lfs)); + else + free (seg_buf); +} + + +/* + * USEFUL DEBUGGING TOOLS: + */ +void +print_SEGSUM (lfsp, p) + struct lfs *lfsp; + SEGSUM *p; +{ + if (p) + (void) dump_summary(lfsp, p, DUMP_ALL, NULL); + else printf("0x0"); + fflush(stdout); +} + +int +bi_compare(a, b) + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + int diff; + + ba = a; + bb = b; + + if (diff = (int)(ba->bi_inode - bb->bi_inode)) + return (diff); + if (diff = (int)(ba->bi_lbn - bb->bi_lbn)) { + if (ba->bi_lbn == LFS_UNUSED_LBN) + return(-1); + else if (bb->bi_lbn == LFS_UNUSED_LBN) + return(1); + else if (ba->bi_lbn < 0 && bb->bi_lbn >= 0) + return(1); + else if (bb->bi_lbn < 0 && ba->bi_lbn >= 0) + return(-1); + else + return (diff); + } + if (diff = (int)(ba->bi_segcreate - bb->bi_segcreate)) + return (diff); + diff = (int)(ba->bi_daddr - bb->bi_daddr); + return (diff); +} + +int +bi_toss(dummy, a, b) + const void *dummy; + const void *a; + const void *b; +{ + const BLOCK_INFO *ba, *bb; + + ba = a; + bb = b; + + return(ba->bi_inode == bb->bi_inode && ba->bi_lbn == bb->bi_lbn); +} + +void +toss(p, nump, size, dotoss, client) + void *p; + int *nump; + size_t size; + int (*dotoss) __P((const void *, const void *, const void *)); + void *client; +{ + int i; + void *p1; + + if (*nump == 0) + return; + + for (i = *nump; --i > 0;) { + p1 = p + size; + if (dotoss(client, p, p1)) { + memmove(p, p1, i * size); + --(*nump); + } else + p += size; + } +} diff --git a/libexec/lfs_cleanerd/misc.c b/libexec/lfs_cleanerd/misc.c new file mode 100644 index 0000000..ad6e11a --- /dev/null +++ b/libexec/lfs_cleanerd/misc.c @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +extern char *special; + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +err(const int fatal, const char *fmt, ...) +#else +err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", special); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + if (errno) + (void)fprintf(stderr, " %s", strerror(errno)); + (void)fprintf(stderr, "\n"); + if (fatal) + exit(1); +} + +void +get(fd, off, p, len) + int fd; + off_t off; + void *p; + size_t len; +{ + int rbytes; + + if (lseek(fd, off, SEEK_SET) < 0) + err(1, "%s: %s", special, strerror(errno)); + if ((rbytes = read(fd, p, len)) < 0) + err(1, "%s: %s", special, strerror(errno)); + if (rbytes != len) + err(1, "%s: short read (%d, not %d)", special, rbytes, len); +} diff --git a/libexec/lfs_cleanerd/print.c b/libexec/lfs_cleanerd/print.c new file mode 100644 index 0000000..5c3863a --- /dev/null +++ b/libexec/lfs_cleanerd/print.c @@ -0,0 +1,218 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#include <sys/time.h> + +#include <ufs/ufs/dinode.h> +#include <ufs/lfs/lfs.h> + +#include <stdlib.h> +#include <stdio.h> +#include "clean.h" + +/* + * Print out a summary block; return number of blocks in segment; 0 + * for empty segment or corrupt segment. + * Returns a pointer to the array of inode addresses. + */ +int +dump_summary(lfsp, sp, flags, iaddrp) + struct lfs *lfsp; + SEGSUM *sp; + u_long flags; + daddr_t **iaddrp; +{ + int i, j, numblocks; + daddr_t *dp; + + FINFO *fp; + int ck; + + if (sp->ss_sumsum != (ck = cksum(&sp->ss_datasum, + LFS_SUMMARY_SIZE - sizeof(sp->ss_sumsum)))) + return(-1); + + if (flags & DUMP_SUM_HEADER) { + (void)printf(" %s0x%X\t%s%d\t%s%d\n %s0x%X\t%s0x%X", + "next ", sp->ss_next, + "nfinfo ", sp->ss_nfinfo, + "ninos ", sp->ss_ninos, + "sumsum ", sp->ss_sumsum, + "datasum ", sp->ss_datasum ); + (void)printf("\tcreate %s", ctime((time_t *)&sp->ss_create)); + } + + numblocks = (sp->ss_ninos + INOPB(lfsp) - 1) / INOPB(lfsp); + + /* Dump out inode disk addresses */ + if (flags & DUMP_INODE_ADDRS) + printf(" Inode addresses:"); + + dp = (daddr_t *)((caddr_t)sp + LFS_SUMMARY_SIZE); + for (--dp, i = 0; i < sp->ss_ninos; --dp) + if (flags & DUMP_INODE_ADDRS) { + (void)printf("\t0x%lx", *dp); + if (++i % 7 == 0) + (void)printf("\n"); + } else + ++i; + if (iaddrp) + *iaddrp = ++dp; + if (flags & DUMP_INODE_ADDRS) + printf("\n"); + + for (fp = (FINFO *)(sp + 1), i = 0; i < sp->ss_nfinfo; ++i) { + numblocks += fp->fi_nblocks; + if (flags & DUMP_FINFOS) { + (void)printf(" %s%d version %d nblocks %d\n", + "FINFO for inode: ", fp->fi_ino, + fp->fi_version, fp->fi_nblocks); + dp = &(fp->fi_blocks[0]); + for (j = 0; j < fp->fi_nblocks; j++, dp++) { + (void)printf("\t%d", *dp); + if ((j % 8) == 7) + (void)printf("\n"); + } + if ((j % 8) != 0) + (void)printf("\n"); + fp = (FINFO *)dp; + } else { + fp = (FINFO *)(&fp->fi_blocks[fp->fi_nblocks]); + } + } + return (numblocks); +} + +#ifdef VERBOSE +void +dump_cleaner_info(ipage) + void *ipage; +{ + CLEANERINFO *cip; + + cip = (CLEANERINFO *)ipage; + (void)printf("segments clean\t%d\tsegments dirty\t%d\n\n", + cip->clean, cip->dirty); +} + +void +dump_super(lfsp) + struct lfs *lfsp; +{ + int i; + + (void)printf("%s0x%X\t%s0x%X\t%s%d\t%s%d\n", + "magic ", lfsp->lfs_magic, + "version ", lfsp->lfs_version, + "size ", lfsp->lfs_size, + "ssize ", lfsp->lfs_ssize); + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "dsize ", lfsp->lfs_dsize, + "bsize ", lfsp->lfs_bsize, + "fsize ", lfsp->lfs_fsize, + "frag ", lfsp->lfs_frag); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "minfree ", lfsp->lfs_minfree, + "inopb ", lfsp->lfs_inopb, + "ifpb ", lfsp->lfs_ifpb, + "nindir ", lfsp->lfs_nindir); + + (void)printf("%s%d\t\t%s%d\t%s%d\t%s%d\n", + "nseg ", lfsp->lfs_nseg, + "nspf ", lfsp->lfs_nspf, + "cleansz ", lfsp->lfs_cleansz, + "segtabsz ", lfsp->lfs_segtabsz); + + (void)printf("%s0x%X\t%s%d\t%s0x%X\t%s%d\n", + "segmask ", lfsp->lfs_segmask, + "segshift ", lfsp->lfs_segshift, + "bmask ", lfsp->lfs_bmask, + "bshift ", lfsp->lfs_bshift); + + (void)printf("%s0x%X\t\t%s%d\t%s0x%X\t%s%d\n", + "ffmask ", lfsp->lfs_ffmask, + "ffshift ", lfsp->lfs_ffshift, + "fbmask ", lfsp->lfs_fbmask, + "fbshift ", lfsp->lfs_fbshift); + + (void)printf("%s%d\t\t%s0x%X\t%s0x%qx\n", + "fsbtodb ", lfsp->lfs_fsbtodb, + "cksum ", lfsp->lfs_cksum, + "maxfilesize ", lfsp->lfs_maxfilesize); + + (void)printf("Superblock disk addresses:\t"); + for (i = 0; i < LFS_MAXNUMSB; i++) { + (void)printf(" 0x%X", lfsp->lfs_sboffs[i]); + if ( i == (LFS_MAXNUMSB >> 1)) + (void)printf("\n\t\t\t\t"); + } + (void)printf("\n"); + + (void)printf("Checkpoint Info\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\n", + "free ", lfsp->lfs_free, + "idaddr ", lfsp->lfs_idaddr, + "ifile ", lfsp->lfs_ifile); + (void)printf("%s%d\t%s%d\t%s%d\n", + "bfree ", lfsp->lfs_bfree, + "avail ", lfsp->lfs_avail, + "uinodes ", lfsp->lfs_uinodes); + (void)printf("%s%d\t%s0x%X\t%s0x%X\n%s0x%X\t%s0x%X\t", + "nfiles ", lfsp->lfs_nfiles, + "lastseg ", lfsp->lfs_lastseg, + "nextseg ", lfsp->lfs_nextseg, + "curseg ", lfsp->lfs_curseg, + "offset ", lfsp->lfs_offset); + (void)printf("tstamp %s", ctime((time_t *)&lfsp->lfs_tstamp)); + (void)printf("\nIn-Memory Information\n"); + (void)printf("%s%d\t%s0x%X\t%s%d\t%s%d\t%s%d\n", + "seglock ", lfsp->lfs_seglock, + "iocount ", lfsp->lfs_iocount, + "writer ", lfsp->lfs_writer, + "dirops ", lfsp->lfs_dirops, + "doifile ", lfsp->lfs_doifile ); + (void)printf("%s%d\t%s%d\t%s0x%X\t%s%d\n", + "nactive ", lfsp->lfs_nactive, + "fmod ", lfsp->lfs_fmod, + "clean ", lfsp->lfs_clean, + "ronly ", lfsp->lfs_ronly); +} +#endif /* VERBOSE */ diff --git a/libexec/mail.local/Makefile b/libexec/mail.local/Makefile new file mode 100644 index 0000000..e8556d8 --- /dev/null +++ b/libexec/mail.local/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 7/19/93 + +PROG= mail.local +MAN8= mail.local.0 +BINOWN= root +BINMODE=4555 +INSTALLFLAGS=-fschg + +.include <bsd.prog.mk> diff --git a/libexec/mail.local/mail.local.8 b/libexec/mail.local/mail.local.8 new file mode 100644 index 0000000..661615c --- /dev/null +++ b/libexec/mail.local/mail.local.8 @@ -0,0 +1,105 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)mail.local.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MAIL.LOCAL 8 +.Os +.Sh NAME +.Nm mail.local +.Nd store mail in a mailbox +.Sh SYNOPSIS +.Nm mail.local +.Op Fl f Ar from +.Ar user ... +.Sh DESCRIPTION +.Nm Mail.local +reads the standard input up to an end-of-file and appends it to each +.Ar user's +.Pa mail +file. +The +.Ar user +must be a valid user name. +.Pp +The options are as follows: +.Bl -tag -width xxxfrom +.It Fl f Ar from +Specify the sender's name. +.El +.Pp +Individual mail messages in the mailbox are delimited by an empty +line followed by a line beginning with the string ``From ''. +A line containing the string ``From '', the sender's name and a time stamp +is prepended to each delivered mail message. +A blank line is appended to each message. +A greater-than character (``>'') is prepended to any line in the message +which could be mistaken for a ``From '' delimiter line. +.Pp +The mail files are exclusively locked with +.Xr flock 2 +while mail is appended. +.Pp +If the ``biff'' service is returned by +.Xr getservbyname 3 , +the biff server is notified of delivered mail. +.Pp +The +.Nm mail.local +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev TZ +Used to set the appropriate time zone on the timestamp. +.El +.Sh FILES +.Bl -tag -width /tmp/local.XXXXXX -compact +.It Pa /tmp/local.XXXXXX +temporary files +.It Pa /var/mail/user +user's mailbox directory +.El +.Sh SEE ALSO +.Xr mail 1 , +.Xr xsend 1 , +.Xr flock 2 , +.Xr getservbyname 3 , +.Xr comsat 8 , +.Xr sendmail 8 +.Sh HISTORY +A superset of +.Nm mail.local +(handling mailbox reading as well as mail delivery) +appeared in +.At v7 . +as the program +.Nm mail . diff --git a/libexec/mail.local/mail.local.c b/libexec/mail.local/mail.local.c new file mode 100644 index 0000000..0de8978 --- /dev/null +++ b/libexec/mail.local/mail.local.c @@ -0,0 +1,511 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mail.local.c 8.6 (Berkeley) 4/8/94"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "pathnames.h" + +int eval = EX_OK; /* sysexits.h error value. */ + +void deliver __P((int, char *)); +void e_to_sys __P((int)); +__dead void err __P((const char *, ...)); +void notifybiff __P((char *)); +int store __P((char *)); +void usage __P((void)); +void vwarn __P((const char *, _BSD_VA_LIST_)); +void warn __P((const char *, ...)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct passwd *pw; + int ch, fd; + uid_t uid; + char *from; + + openlog("mail.local", 0, LOG_MAIL); + + from = NULL; + while ((ch = getopt(argc, argv, "df:r:")) != EOF) + switch(ch) { + case 'd': /* Backward compatible. */ + break; + case 'f': + case 'r': /* Backward compatible. */ + if (from != NULL) { + warn("multiple -f options"); + usage(); + } + from = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + /* + * If from not specified, use the name from getlogin() if the + * uid matches, otherwise, use the name from the password file + * corresponding to the uid. + */ + uid = getuid(); + if (!from && (!(from = getlogin()) || + !(pw = getpwnam(from)) || pw->pw_uid != uid)) + from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; + + /* + * There is no way to distinguish the error status of one delivery + * from the rest of the deliveries. So, if we failed hard on one + * or more deliveries, but had no failures on any of the others, we + * return a hard failure. If we failed temporarily on one or more + * deliveries, we return a temporary failure regardless of the other + * failures. This results in the delivery being reattempted later + * at the expense of repeated failures and multiple deliveries. + */ + for (fd = store(from); *argv; ++argv) + deliver(fd, *argv); + exit(eval); +} + +int +store(from) + char *from; +{ + FILE *fp; + time_t tval; + int fd, eline; + char *tn, line[2048]; + + tn = strdup(_PATH_LOCTMP); + if ((fd = mkstemp(tn)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { + e_to_sys(errno); + err("unable to open temporary file"); + } + (void)unlink(tn); + free(tn); + + (void)time(&tval); + (void)fprintf(fp, "From %s %s", from, ctime(&tval)); + + line[0] = '\0'; + for (eline = 1; fgets(line, sizeof(line), stdin);) { + if (line[0] == '\n') + eline = 1; + else { + if (eline && line[0] == 'F' && + !memcmp(line, "From ", 5)) + (void)putc('>', fp); + eline = 0; + } + (void)fprintf(fp, "%s", line); + if (ferror(fp)) { + e_to_sys(errno); + err("temporary file write error"); + } + } + + /* If message not newline terminated, need an extra. */ + if (!strchr(line, '\n')) + (void)putc('\n', fp); + /* Output a newline; note, empty messages are allowed. */ + (void)putc('\n', fp); + + if (fflush(fp) == EOF || ferror(fp)) { + e_to_sys(errno); + err("temporary file write error"); + } + return (fd); +} + +void +deliver(fd, name) + int fd; + char *name; +{ + struct stat fsb, sb; + struct passwd *pw; + int mbfd, nr, nw, off; + char biffmsg[100], buf[8*1024], path[MAXPATHLEN]; + off_t curoff; + + /* + * Disallow delivery to unknown names -- special mailboxes can be + * handled in the sendmail aliases file. + */ + if (!(pw = getpwnam(name))) { + if (eval != EX_TEMPFAIL) + eval = EX_UNAVAILABLE; + warn("unknown name: %s", name); + return; + } + + (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name); + + /* + * If the mailbox is linked or a symlink, fail. There's an obvious + * race here, that the file was replaced with a symbolic link after + * the lstat returned, but before the open. We attempt to detect + * this by comparing the original stat information and information + * returned by an fstat of the file descriptor returned by the open. + * + * NB: this is a symptom of a larger problem, that the mail spooling + * directory is writeable by the wrong users. If that directory is + * writeable, system security is compromised for other reasons, and + * it cannot be fixed here. + * + * If we created the mailbox, set the owner/group. If that fails, + * just return. Another process may have already opened it, so we + * can't unlink it. Historically, binmail set the owner/group at + * each mail delivery. We no longer do this, assuming that if the + * ownership or permissions were changed there was a reason. + * + * XXX + * open(2) should support flock'ing the file. + */ +tryagain: + if (lstat(path, &sb)) { + mbfd = open(path, + O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); + if (mbfd == -1) { + if (errno == EEXIST) + goto tryagain; + } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) { + e_to_sys(errno); + warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name); + return; + } + } else if (sb.st_nlink != 1 || S_ISLNK(sb.st_mode)) { + e_to_sys(errno); + warn("%s: linked file", path); + return; + } else { + mbfd = open(path, O_APPEND|O_WRONLY, 0); + if (mbfd != -1 && + (fstat(mbfd, &fsb) || fsb.st_nlink != 1 || + S_ISLNK(fsb.st_mode) || sb.st_dev != fsb.st_dev || + sb.st_ino != fsb.st_ino)) { + warn("%s: file changed after open", path); + (void)close(mbfd); + return; + } + } + + if (mbfd == -1) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + return; + } + + /* Wait until we can get a lock on the file. */ + if (flock(mbfd, LOCK_EX)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + goto err1; + } + + /* Get the starting offset of the new message for biff. */ + curoff = lseek(mbfd, (off_t)0, SEEK_END); + (void)snprintf(biffmsg, sizeof(biffmsg), "%s@%qd\n", name, curoff); + + /* Copy the message into the file. */ + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { + e_to_sys(errno); + warn("temporary file: %s", strerror(errno)); + goto err1; + } + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (off = 0; off < nr; nr -= nw, off += nw) + if ((nw = write(mbfd, buf + off, nr)) < 0) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + goto err2;; + } + if (nr < 0) { + e_to_sys(errno); + warn("temporary file: %s", strerror(errno)); + goto err2;; + } + + /* Flush to disk, don't wait for update. */ + if (fsync(mbfd)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); +err2: (void)ftruncate(mbfd, curoff); +err1: (void)close(mbfd); + return; + } + + /* Close and check -- NFS doesn't write until the close. */ + if (close(mbfd)) { + e_to_sys(errno); + warn("%s: %s", path, strerror(errno)); + return; + } + + notifybiff(biffmsg); +} + +void +notifybiff(msg) + char *msg; +{ + static struct sockaddr_in addr; + static int f = -1; + struct hostent *hp; + struct servent *sp; + int len; + + if (!addr.sin_family) { + /* Be silent if biff service not available. */ + if (!(sp = getservbyname("biff", "udp"))) + return; + if (!(hp = gethostbyname("localhost"))) { + warn("localhost: %s", strerror(errno)); + return; + } + addr.sin_family = hp->h_addrtype; + memmove(&addr.sin_addr, hp->h_addr, hp->h_length); + addr.sin_port = sp->s_port; + } + if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + warn("socket: %s", strerror(errno)); + return; + } + len = strlen(msg) + 1; + if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) + != len) + warn("sendto biff: %s", strerror(errno)); +} + +void +usage() +{ + eval = EX_USAGE; + err("usage: mail.local [-f from] user ..."); +} + +#if __STDC__ +void +err(const char *fmt, ...) +#else +void +err(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); + + exit(eval); +} + +void +#if __STDC__ +warn(const char *fmt, ...) +#else +warn(fmt, va_alist) + const char *fmt; + va_dcl +#endif +{ + va_list ap; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + vwarn(fmt, ap); + va_end(ap); +} + +void +vwarn(fmt, ap) + const char *fmt; + _BSD_VA_LIST_ ap; +{ + /* + * Log the message to stderr. + * + * Don't use LOG_PERROR as an openlog() flag to do this, + * it's not portable enough. + */ + if (eval != EX_USAGE) + (void)fprintf(stderr, "mail.local: "); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + + /* Log the message to syslog. */ + vsyslog(LOG_ERR, fmt, ap); +} + +/* + * e_to_sys -- + * Guess which errno's are temporary. Gag me. + */ +void +e_to_sys(num) + int num; +{ + /* Temporary failures override hard errors. */ + if (eval == EX_TEMPFAIL) + return; + + switch(num) { /* Hopefully temporary errors. */ +#ifdef EAGAIN + case EAGAIN: /* Resource temporarily unavailable */ +#endif +#ifdef EDQUOT + case EDQUOT: /* Disc quota exceeded */ +#endif +#ifdef EBUSY + case EBUSY: /* Device busy */ +#endif +#ifdef EPROCLIM + case EPROCLIM: /* Too many processes */ +#endif +#ifdef EUSERS + case EUSERS: /* Too many users */ +#endif +#ifdef ECONNABORTED + case ECONNABORTED: /* Software caused connection abort */ +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: /* Connection refused */ +#endif +#ifdef ECONNRESET + case ECONNRESET: /* Connection reset by peer */ +#endif +#ifdef EDEADLK + case EDEADLK: /* Resource deadlock avoided */ +#endif +#ifdef EFBIG + case EFBIG: /* File too large */ +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: /* Host is down */ +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: /* No route to host */ +#endif +#ifdef EMFILE + case EMFILE: /* Too many open files */ +#endif +#ifdef ENETDOWN + case ENETDOWN: /* Network is down */ +#endif +#ifdef ENETRESET + case ENETRESET: /* Network dropped connection on reset */ +#endif +#ifdef ENETUNREACH + case ENETUNREACH: /* Network is unreachable */ +#endif +#ifdef ENFILE + case ENFILE: /* Too many open files in system */ +#endif +#ifdef ENOBUFS + case ENOBUFS: /* No buffer space available */ +#endif +#ifdef ENOMEM + case ENOMEM: /* Cannot allocate memory */ +#endif +#ifdef ENOSPC + case ENOSPC: /* No space left on device */ +#endif +#ifdef EROFS + case EROFS: /* Read-only file system */ +#endif +#ifdef ESTALE + case ESTALE: /* Stale NFS file handle */ +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: /* Connection timed out */ +#endif +#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) + case EWOULDBLOCK: /* Operation would block. */ +#endif + eval = EX_TEMPFAIL; + break; + default: + eval = EX_UNAVAILABLE; + break; + } +} diff --git a/libexec/mail.local/pathnames.h b/libexec/mail.local/pathnames.h new file mode 100644 index 0000000..8e43925 --- /dev/null +++ b/libexec/mail.local/pathnames.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ +#include <paths.h> + +#define _PATH_LOCTMP "/tmp/local.XXXXXX" diff --git a/libexec/rexecd/Makefile b/libexec/rexecd/Makefile new file mode 100644 index 0000000..aaedac5 --- /dev/null +++ b/libexec/rexecd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rexecd +MAN8= rexecd.0 + +.include <bsd.prog.mk> diff --git a/libexec/rexecd/rexecd.8 b/libexec/rexecd/rexecd.8 new file mode 100644 index 0000000..2dda22b --- /dev/null +++ b/libexec/rexecd/rexecd.8 @@ -0,0 +1,148 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)rexecd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt REXECD 8 +.Os BSD 4.2 +.Sh NAME +.Nm rexecd +.Nd remote execution server +.Sh SYNOPSIS +.Nm rexecd +.Sh DESCRIPTION +.Nm Rexecd +is the server for the +.Xr rexec 3 +routine. The server provides remote execution facilities +with authentication based on user names and +passwords. +.Pp +.Nm Rexecd +listens for service requests at the port indicated in +the ``exec'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server reads characters from the socket up +to a NUL +.Pq Ql \e0 +byte. The resultant string is +interpreted as an +.Tn ASCII +number, base 10. +.It +If the number received in step 1 is non-zero, +it is interpreted as the port number of a secondary +stream to be used for the +.Em stderr . +A second connection is then created to the specified +port on the client's machine. +.It +A NUL terminated user name of at most 16 characters +is retrieved on the initial socket. +.It +A NUL terminated, unencrypted password of at most +16 characters is retrieved on the initial socket. +.It +A NUL terminated command to be passed to a +shell is retrieved on the initial socket. The length of +the command is limited by the upper bound on the size of +the system's argument list. +.It +.Nm Rexecd +then validates the user as is done at login time +and, if the authentication was successful, changes +to the user's home directory, and establishes the user +and group protections of the user. +If any of these steps fail the connection is +aborted with a diagnostic message returned. +.It +A NUL byte is returned on the initial socket +and the command line is passed to the normal login +shell of the user. The +shell inherits the network connections established +by +.Nm rexecd . +.El +.Sh DIAGNOSTICS +Except for the last one listed below, +all diagnostic messages are returned on the initial socket, +after which any network connections are closed. +An error is indicated by a leading byte with a value of +1 (0 is returned in step 7 above upon successful completion +of all the steps prior to the command execution). +.Pp +.Bl -tag -width Ds +.It Sy username too long +The name is +longer than 16 characters. +.It Sy password too long +The password is longer than 16 characters. +.It Sy command too long +The command line passed exceeds the size of the argument +list (as configured into the system). +.It Sy Login incorrect. +No password file entry for the user name existed. +.It Sy Password incorrect. +The wrong password was supplied. +.It Sy \&No remote directory. +The +.Xr chdir +command to the home directory failed. +.It Sy Try again. +A +.Xr fork +by the server failed. +.It Sy <shellname>: ... +The user's login shell could not be started. +This message is returned +on the connection associated with the +.Em stderr , +and is not preceded by a flag byte. +.El +.Sh SEE ALSO +.Xr rexec 3 +.Sh BUGS +Indicating ``Login incorrect'' as opposed to ``Password incorrect'' +is a security breach which allows people to probe a system for users +with null passwords. +.Pp +A facility to allow all data and password exchanges to be encrypted should be +present. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/rexecd/rexecd.c b/libexec/rexecd/rexecd.c new file mode 100644 index 0000000..796ad9c --- /dev/null +++ b/libexec/rexecd/rexecd.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rexecd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include <netinet/in.h> + +#include <errno.h> +#include <netdb.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/*VARARGS1*/ +int error(); + +/* + * remote execute server: + * username\0 + * password\0 + * command\0 + * data + */ +/*ARGSUSED*/ +main(argc, argv) + int argc; + char **argv; +{ + struct sockaddr_in from; + int fromlen; + + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + (void)fprintf(stderr, + "rexecd: getpeername: %s\n", strerror(errno)); + exit(1); + } + doit(0, &from); +} + +char username[20] = "USER="; +char homedir[64] = "HOME="; +char shell[64] = "SHELL="; +char path[sizeof(_PATH_DEFPATH) + sizeof("PATH=")] = "PATH="; +char *envinit[] = + {homedir, shell, path, username, 0}; +char **environ; + +struct sockaddr_in asin = { AF_INET }; + +doit(f, fromp) + int f; + struct sockaddr_in *fromp; +{ + char cmdbuf[NCARGS+1], *cp, *namep; + char user[16], pass[16]; + struct passwd *pwd; + int s; + u_short port; + int pv[2], pid, ready, readfrom, cc; + char buf[BUFSIZ], sig; + int one = 1; + + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); +#ifdef DEBUG + { int t = open(_PATH_TTY, 2); + if (t >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + dup2(f, 0); + dup2(f, 1); + dup2(f, 2); + (void) alarm(60); + port = 0; + for (;;) { + char c; + if (read(f, &c, 1) != 1) + exit(1); + if (c == 0) + break; + port = port * 10 + c - '0'; + } + (void) alarm(0); + if (port != 0) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + exit(1); + if (bind(s, (struct sockaddr *)&asin, sizeof (asin)) < 0) + exit(1); + (void) alarm(60); + fromp->sin_port = htons(port); + if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) + exit(1); + (void) alarm(0); + } + getstr(user, sizeof(user), "username"); + getstr(pass, sizeof(pass), "password"); + getstr(cmdbuf, sizeof(cmdbuf), "command"); + setpwent(); + pwd = getpwnam(user); + if (pwd == NULL) { + error("Login incorrect.\n"); + exit(1); + } + endpwent(); + if (*pwd->pw_passwd != '\0') { + namep = crypt(pass, pwd->pw_passwd); + if (strcmp(namep, pwd->pw_passwd)) { + error("Password incorrect.\n"); + exit(1); + } + } + if (chdir(pwd->pw_dir) < 0) { + error("No remote directory.\n"); + exit(1); + } + (void) write(2, "\0", 1); + if (port) { + (void) pipe(pv); + pid = fork(); + if (pid == -1) { + error("Try again.\n"); + exit(1); + } + if (pid) { + (void) close(0); (void) close(1); (void) close(2); + (void) close(f); (void) close(pv[1]); + readfrom = (1<<s) | (1<<pv[0]); + ioctl(pv[1], FIONBIO, (char *)&one); + /* should set s nbio! */ + do { + ready = readfrom; + (void) select(16, (fd_set *)&ready, + (fd_set *)NULL, (fd_set *)NULL, + (struct timeval *)NULL); + if (ready & (1<<s)) { + if (read(s, &sig, 1) <= 0) + readfrom &= ~(1<<s); + else + killpg(pid, sig); + } + if (ready & (1<<pv[0])) { + cc = read(pv[0], buf, sizeof (buf)); + if (cc <= 0) { + shutdown(s, 1+1); + readfrom &= ~(1<<pv[0]); + } else + (void) write(s, buf, cc); + } + } while (readfrom); + exit(0); + } + setpgrp(0, getpid()); + (void) close(s); (void)close(pv[0]); + dup2(pv[1], 2); + } + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; + if (f > 2) + (void) close(f); + (void) setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); + (void)strcat(path, _PATH_DEFPATH); + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + strncat(shell, pwd->pw_shell, sizeof(shell)-7); + strncat(username, pwd->pw_name, sizeof(username)-6); + cp = strrchr(pwd->pw_shell, '/'); + if (cp) + cp++; + else + cp = pwd->pw_shell; + execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); + perror(pwd->pw_shell); + exit(1); +} + +/*VARARGS1*/ +error(fmt, a1, a2, a3) + char *fmt; + int a1, a2, a3; +{ + char buf[BUFSIZ]; + + buf[0] = 1; + (void) sprintf(buf+1, fmt, a1, a2, a3); + (void) write(2, buf, strlen(buf)); +} + +getstr(buf, cnt, err) + char *buf; + int cnt; + char *err; +{ + char c; + + do { + if (read(0, &c, 1) != 1) + exit(1); + *buf++ = c; + if (--cnt == 0) { + error("%s too long\n", err); + exit(1); + } + } while (c != 0); +} diff --git a/libexec/rlogind/Makefile b/libexec/rlogind/Makefile new file mode 100644 index 0000000..639d3f6 --- /dev/null +++ b/libexec/rlogind/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rlogind +CFLAGS+=-DKERBEROS -DCRYPT +SRCS= rlogind.c des_rw.c +MAN8= rlogind.0 +DPADD= ${LIBUTIL} ${LIBKRB} ${LIBDES} +LDADD= -lutil -lkrb -ldes +.PATH: ${.CURDIR}/../../usr.bin/rlogin + +.include <bsd.prog.mk> diff --git a/libexec/rlogind/pathnames.h b/libexec/rlogind/pathnames.h new file mode 100644 index 0000000..5240f19 --- /dev/null +++ b/libexec/rlogind/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_LOGIN "/usr/bin/login" diff --git a/libexec/rlogind/rlogind.8 b/libexec/rlogind/rlogind.8 new file mode 100644 index 0000000..9c19933 --- /dev/null +++ b/libexec/rlogind/rlogind.8 @@ -0,0 +1,168 @@ +.\" Copyright (c) 1983, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)rlogind.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt RLOGIND 8 +.Os BSD 4.2 +.Sh NAME +.Nm rlogind +.Nd remote login server +.Sh SYNOPSIS +.Nm rlogind +.Op Fl aln +.Sh DESCRIPTION +.Nm Rlogind +is the server for the +.Xr rlogin 1 +program. The server provides a remote login facility +with authentication based on privileged port numbers from trusted hosts. +.Pp +Options supported by +.Nm rlogind : +.Bl -tag -width Ds +.It Fl a +Ask hostname for verification. +.It Fl l +Prevent any authentication based on the user's +.Dq Pa .rhosts +file, unless the user is logging in as the superuser. +.It Fl n +Disable keep-alive messages. +.El +.Pp +.Nm Rlogind +listens for service requests at the port indicated in +the ``login'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server checks the client's source port. +If the port is not in the range 512-1023, the server +aborts the connection. +.It +The server checks the client's source address +and requests the corresponding host name (see +.Xr gethostbyaddr 3 , +.Xr hosts 5 +and +.Xr named 8 ) . +If the hostname cannot be determined, +the dot-notation representation of the host address is used. +If the hostname is in the same domain as the server (according to +the last two components of the domain name), +or if the +.Fl a +option is given, +the addresses for the hostname are requested, +verifying that the name and address correspond. +Normal authentication is bypassed if the address verification fails. +.El +.Pp +Once the source port and address have been checked, +.Nm rlogind +proceeds with the authentication process described in +.Xr rshd 8 . +It then allocates a pseudo terminal (see +.Xr pty 4 ) , +and manipulates file descriptors so that the slave +half of the pseudo terminal becomes the +.Em stdin , +.Em stdout , +and +.Em stderr +for a login process. +The login process is an instance of the +.Xr login 1 +program, invoked with the +.Fl f +option if authentication has succeeded. +If automatic authentication fails, the user is +prompted to log in as if on a standard terminal line. +.Pp +The parent of the login process manipulates the master side of +the pseudo terminal, operating as an intermediary +between the login process and the client instance of the +.Xr rlogin +program. In normal operation, the packet protocol described +in +.Xr pty 4 +is invoked to provide +.Ql ^S/^Q +type facilities and propagate +interrupt signals to the remote programs. The login process +propagates the client terminal's baud rate and terminal type, +as found in the environment variable, +.Ql Ev TERM ; +see +.Xr environ 7 . +The screen or window size of the terminal is requested from the client, +and window size changes from the client are propagated to the pseudo terminal. +.Pp +Transport-level keepalive messages are enabled unless the +.Fl n +option is present. +The use of keepalive messages allows sessions to be timed out +if the client crashes or becomes unreachable. +.Sh DIAGNOSTICS +All initial diagnostic messages are indicated +by a leading byte with a value of 1, +after which any network connections are closed. +If there are no errors before +.Xr login +is invoked, a null byte is returned as in indication of success. +.Bl -tag -width Ds +.It Sy Try again. +A +.Xr fork +by the server failed. +.El +.Sh SEE ALSO +.Xr login 1 , +.Xr ruserok 3 , +.Xr rshd 8 +.Sh BUGS +The authentication procedure used here assumes the integrity +of each client machine and the connecting medium. This is +insecure, but is useful in an ``open'' environment. +.Pp +A facility to allow all data exchanges to be encrypted should be +present. +.Pp +A more extensible protocol should be used. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/rlogind/rlogind.c b/libexec/rlogind/rlogind.c new file mode 100644 index 0000000..6f5f5e1 --- /dev/null +++ b/libexec/rlogind/rlogind.c @@ -0,0 +1,759 @@ +/*- + * Copyright (c) 1983, 1988, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1988, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rlogind.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * remote login server: + * \0 + * remuser\0 + * locuser\0 + * terminal_type/speed\0 + * data + */ + +#define FD_SETSIZE 16 /* don't need many bits for select */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <termios.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <pwd.h> +#include <syslog.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "pathnames.h" + +#ifndef TIOCPKT_WINDOW +#define TIOCPKT_WINDOW 0x80 +#endif + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#define SECURE_MESSAGE "This rlogin session is using DES encryption for all transmissions.\r\n" + +AUTH_DAT *kdata; +KTEXT ticket; +u_char auth_buf[sizeof(AUTH_DAT)]; +u_char tick_buf[sizeof(KTEXT_ST)]; +Key_schedule schedule; +int doencrypt, retval, use_kerberos, vacuous; + +#define ARGSTR "alnkvx" +#else +#define ARGSTR "aln" +#endif /* KERBEROS */ + +char *env[2]; +#define NMAX 30 +char lusername[NMAX+1], rusername[NMAX+1]; +static char term[64] = "TERM="; +#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation */ +int keepalive = 1; +int check_all = 0; + +struct passwd *pwd; + +void doit __P((int, struct sockaddr_in *)); +int control __P((int, char *, int)); +void protocol __P((int, int)); +void cleanup __P((int)); +void fatal __P((int, char *, int)); +int do_rlogin __P((struct sockaddr_in *)); +void getstr __P((char *, int, char *)); +void setup_term __P((int)); +int do_krb_login __P((struct sockaddr_in *)); +void usage __P((void)); +int local_domain __P((char *)); +char *topdomain __P((char *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int __check_rhosts_file; + struct sockaddr_in from; + int ch, fromlen, on; + + openlog("rlogind", LOG_PID | LOG_CONS, LOG_AUTH); + + opterr = 0; + while ((ch = getopt(argc, argv, ARGSTR)) != EOF) + switch (ch) { + case 'a': + check_all = 1; + break; + case 'l': + __check_rhosts_file = 0; + break; + case 'n': + keepalive = 0; + break; +#ifdef KERBEROS + case 'k': + use_kerberos = 1; + break; + case 'v': + vacuous = 1; + break; +#ifdef CRYPT + case 'x': + doencrypt = 1; + break; +#endif +#endif + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos && vacuous) { + usage(); + fatal(STDERR_FILENO, "only one of -k and -v allowed", 0); + } +#endif + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR,"Can't get peer name of remote host: %m"); + fatal(STDERR_FILENO, "Can't get peer name of remote host", 1); + } + on = 1; + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + on = IPTOS_LOWDELAY; + if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + doit(0, &from); +} + +int child; +int netf; +char line[MAXPATHLEN]; +int confirmed; + +struct winsize win = { 0, 0, 0, 0 }; + + +void +doit(f, fromp) + int f; + struct sockaddr_in *fromp; +{ + int master, pid, on = 1; + int authenticated = 0; + register struct hostent *hp; + char hostname[2 * MAXHOSTNAMELEN + 1]; + char c; + + alarm(60); + read(f, &c, 1); + + if (c != 0) + exit(1); +#ifdef KERBEROS + if (vacuous) + fatal(f, "Remote host requires Kerberos authentication", 0); +#endif + + alarm(0); + fromp->sin_port = ntohs((u_short)fromp->sin_port); + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(struct in_addr), + fromp->sin_family); + if (hp) + (void)strcpy(hostname, hp->h_name); + else + (void)strcpy(hostname, inet_ntoa(fromp->sin_addr)); + +#ifdef KERBEROS + if (use_kerberos) { + retval = do_krb_login(fromp); + if (retval == 0) + authenticated++; + else if (retval > 0) + fatal(f, krb_err_txt[retval], 0); + write(f, &c, 1); + confirmed = 1; /* we sent the null! */ + } else +#endif + { + if (fromp->sin_family != AF_INET || + fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE, "Connection from %s on illegal port", + inet_ntoa(fromp->sin_addr)); + fatal(f, "Permission denied", 0); + } +#ifdef IP_OPTIONS + { + u_char optbuf[BUFSIZ/3], *cp; + char lbuf[BUFSIZ], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + if (getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, + &optsize) == 0 && optsize != 0) { + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "Connection received using IP options (ignored):%s", + lbuf); + if (setsockopt(0, ipproto, IP_OPTIONS, + (char *)NULL, optsize) != 0) { + syslog(LOG_ERR, + "setsockopt IP_OPTIONS NULL: %m"); + exit(1); + } + } + } +#endif + if (do_rlogin(fromp) == 0) + authenticated++; + } + if (confirmed == 0) { + write(f, "", 1); + confirmed = 1; /* we sent the null! */ + } +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + (void) des_write(f, SECURE_MESSAGE, sizeof(SECURE_MESSAGE) - 1); +#endif +#endif + netf = f; + + pid = forkpty(&master, line, NULL, &win); + if (pid < 0) { + if (errno == ENOENT) + fatal(f, "Out of ptys", 0); + else + fatal(f, "Forkpty", 1); + } + if (pid == 0) { + if (f > 2) /* f should always be 0, but... */ + (void) close(f); + setup_term(0); + if (authenticated) { +#ifdef KERBEROS + if (use_kerberos && (pwd->pw_uid == 0)) + syslog(LOG_INFO|LOG_AUTH, + "ROOT Kerberos login from %s.%s@%s on %s\n", + kdata->pname, kdata->pinst, kdata->prealm, + hostname); +#endif + + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, "-f", lusername, (char *)NULL); + } else + execl(_PATH_LOGIN, "login", "-p", + "-h", hostname, lusername, (char *)NULL); + fatal(STDERR_FILENO, _PATH_LOGIN, 1); + /*NOTREACHED*/ + } +#ifdef CRYPT +#ifdef KERBEROS + /* + * If encrypted, don't turn on NBIO or the des read/write + * routines will croak. + */ + + if (!doencrypt) +#endif +#endif + ioctl(f, FIONBIO, &on); + ioctl(master, FIONBIO, &on); + ioctl(master, TIOCPKT, &on); + signal(SIGCHLD, cleanup); + protocol(f, master); + signal(SIGCHLD, SIG_IGN); + cleanup(0); +} + +char magic[2] = { 0377, 0377 }; +char oobdata[] = {TIOCPKT_WINDOW}; + +/* + * Handle a "control" request (signaled by magic being present) + * in the data stream. For now, we are only willing to handle + * window size changes. + */ +int +control(pty, cp, n) + int pty; + char *cp; + int n; +{ + struct winsize w; + + if (n < 4+sizeof (w) || cp[2] != 's' || cp[3] != 's') + return (0); + oobdata[0] &= ~TIOCPKT_WINDOW; /* we know he heard */ + bcopy(cp+4, (char *)&w, sizeof(w)); + w.ws_row = ntohs(w.ws_row); + w.ws_col = ntohs(w.ws_col); + w.ws_xpixel = ntohs(w.ws_xpixel); + w.ws_ypixel = ntohs(w.ws_ypixel); + (void)ioctl(pty, TIOCSWINSZ, &w); + return (4+sizeof (w)); +} + +/* + * rlogin "protocol" machine. + */ +void +protocol(f, p) + register int f, p; +{ + char pibuf[1024+1], fibuf[1024], *pbp, *fbp; + register pcc = 0, fcc = 0; + int cc, nfd, n; + char cntl; + + /* + * Must ignore SIGTTOU, otherwise we'll stop + * when we try and set slave pty's window shape + * (our controlling tty is the master pty). + */ + (void) signal(SIGTTOU, SIG_IGN); + send(f, oobdata, 1, MSG_OOB); /* indicate new rlogin */ + if (f > p) + nfd = f + 1; + else + nfd = p + 1; + if (nfd > FD_SETSIZE) { + syslog(LOG_ERR, "select mask too small, increase FD_SETSIZE"); + fatal(f, "internal error (select mask too small)", 0); + } + for (;;) { + fd_set ibits, obits, ebits, *omask; + + FD_ZERO(&ebits); + FD_ZERO(&ibits); + FD_ZERO(&obits); + omask = (fd_set *)NULL; + if (fcc) { + FD_SET(p, &obits); + omask = &obits; + } else + FD_SET(f, &ibits); + if (pcc >= 0) + if (pcc) { + FD_SET(f, &obits); + omask = &obits; + } else + FD_SET(p, &ibits); + FD_SET(p, &ebits); + if ((n = select(nfd, &ibits, omask, &ebits, 0)) < 0) { + if (errno == EINTR) + continue; + fatal(f, "select", 1); + } + if (n == 0) { + /* shouldn't happen... */ + sleep(5); + continue; + } +#define pkcontrol(c) ((c)&(TIOCPKT_FLUSHWRITE|TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)) + if (FD_ISSET(p, &ebits)) { + cc = read(p, &cntl, 1); + if (cc == 1 && pkcontrol(cntl)) { + cntl |= oobdata[0]; + send(f, &cntl, 1, MSG_OOB); + if (cntl & TIOCPKT_FLUSHWRITE) { + pcc = 0; + FD_CLR(p, &ibits); + } + } + } + if (FD_ISSET(f, &ibits)) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + fcc = des_read(f, fibuf, sizeof(fibuf)); + else +#endif +#endif + fcc = read(f, fibuf, sizeof(fibuf)); + if (fcc < 0 && errno == EWOULDBLOCK) + fcc = 0; + else { + register char *cp; + int left, n; + + if (fcc <= 0) + break; + fbp = fibuf; + + top: + for (cp = fibuf; cp < fibuf+fcc-1; cp++) + if (cp[0] == magic[0] && + cp[1] == magic[1]) { + left = fcc - (cp-fibuf); + n = control(p, cp, left); + if (n) { + left -= n; + if (left > 0) + bcopy(cp+n, cp, left); + fcc -= n; + goto top; /* n^2 */ + } + } + FD_SET(p, &obits); /* try write */ + } + } + + if (FD_ISSET(p, &obits) && fcc > 0) { + cc = write(p, fbp, fcc); + if (cc > 0) { + fcc -= cc; + fbp += cc; + } + } + + if (FD_ISSET(p, &ibits)) { + pcc = read(p, pibuf, sizeof (pibuf)); + pbp = pibuf; + if (pcc < 0 && errno == EWOULDBLOCK) + pcc = 0; + else if (pcc <= 0) + break; + else if (pibuf[0] == 0) { + pbp++, pcc--; +#ifdef CRYPT +#ifdef KERBEROS + if (!doencrypt) +#endif +#endif + FD_SET(f, &obits); /* try write */ + } else { + if (pkcontrol(pibuf[0])) { + pibuf[0] |= oobdata[0]; + send(f, &pibuf[0], 1, MSG_OOB); + } + pcc = 0; + } + } + if ((FD_ISSET(f, &obits)) && pcc > 0) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + cc = des_write(f, pbp, pcc); + else +#endif +#endif + cc = write(f, pbp, pcc); + if (cc < 0 && errno == EWOULDBLOCK) { + /* + * This happens when we try write after read + * from p, but some old kernels balk at large + * writes even when select returns true. + */ + if (!FD_ISSET(p, &ibits)) + sleep(5); + continue; + } + if (cc > 0) { + pcc -= cc; + pbp += cc; + } + } + } +} + +void +cleanup(signo) + int signo; +{ + char *p; + + p = line + sizeof(_PATH_DEV) - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + shutdown(netf, 2); + exit(1); +} + +void +fatal(f, msg, syserr) + int f; + char *msg; + int syserr; +{ + int len; + char buf[BUFSIZ], *bp = buf; + + /* + * Prepend binary one to message if we haven't sent + * the magic null as confirmation. + */ + if (!confirmed) + *bp++ = '\01'; /* error indicator */ + if (syserr) + len = sprintf(bp, "rlogind: %s: %s.\r\n", + msg, strerror(errno)); + else + len = sprintf(bp, "rlogind: %s.\r\n", msg); + (void) write(f, buf, bp + len - buf); + exit(1); +} + +int +do_rlogin(dest) + struct sockaddr_in *dest; +{ + getstr(rusername, sizeof(rusername), "remuser too long"); + getstr(lusername, sizeof(lusername), "locuser too long"); + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type too long"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + if (pwd->pw_uid == 0) + return (-1); + /* XXX why don't we syslog() failure? */ + return (iruserok(dest->sin_addr.s_addr, 0, rusername, lusername)); +} + +void +getstr(buf, cnt, errmsg) + char *buf; + int cnt; + char *errmsg; +{ + char c; + + do { + if (read(0, &c, 1) != 1) + exit(1); + if (--cnt < 0) + fatal(STDOUT_FILENO, errmsg, 0); + *buf++ = c; + } while (c != 0); +} + +extern char **environ; + +void +setup_term(fd) + int fd; +{ + register char *cp = index(term+ENVSIZE, '/'); + char *speed; + struct termios tt; + +#ifndef notyet + tcgetattr(fd, &tt); + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + cfsetspeed(&tt, atoi(speed)); + } + + tt.c_iflag = TTYDEF_IFLAG; + tt.c_oflag = TTYDEF_OFLAG; + tt.c_lflag = TTYDEF_LFLAG; + tcsetattr(fd, TCSAFLUSH, &tt); +#else + if (cp) { + *cp++ = '\0'; + speed = cp; + cp = index(speed, '/'); + if (cp) + *cp++ = '\0'; + tcgetattr(fd, &tt); + cfsetspeed(&tt, atoi(speed)); + tcsetattr(fd, TCSAFLUSH, &tt); + } +#endif + + env[0] = term; + env[1] = 0; + environ = env; +} + +#ifdef KERBEROS +#define VERSION_SIZE 9 + +/* + * Do the remote kerberos login to the named host with the + * given inet address + * + * Return 0 on valid authorization + * Return -1 on valid authentication, no authorization + * Return >0 for error conditions + */ +int +do_krb_login(dest) + struct sockaddr_in *dest; +{ + int rc; + char instance[INST_SZ], version[VERSION_SIZE]; + long authopts = 0L; /* !mutual */ + struct sockaddr_in faddr; + + kdata = (AUTH_DAT *) auth_buf; + ticket = (KTEXT) tick_buf; + + instance[0] = '*'; + instance[1] = '\0'; + +#ifdef CRYPT + if (doencrypt) { + rc = sizeof(faddr); + if (getsockname(0, (struct sockaddr *)&faddr, &rc)) + return (-1); + authopts = KOPT_DO_MUTUAL; + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, &faddr, + kdata, "", schedule, version); + des_set_key(kdata->session, schedule); + + } else +#endif + rc = krb_recvauth( + authopts, 0, + ticket, "rcmd", + instance, dest, (struct sockaddr_in *) 0, + kdata, "", (bit_64 *) 0, version); + + if (rc != KSUCCESS) + return (rc); + + getstr(lusername, sizeof(lusername), "locuser"); + /* get the "cmd" in the rcmd protocol */ + getstr(term+ENVSIZE, sizeof(term)-ENVSIZE, "Terminal type"); + + pwd = getpwnam(lusername); + if (pwd == NULL) + return (-1); + + /* returns nonzero for no access */ + if (kuserok(kdata, lusername) != 0) + return (-1); + + return (0); + +} +#endif /* KERBEROS */ + +void +usage() +{ +#ifdef KERBEROS + syslog(LOG_ERR, "usage: rlogind [-aln] [-k | -v]"); +#else + syslog(LOG_ERR, "usage: rlogind [-aln]"); +#endif +} + +/* + * Check whether host h is in our local domain, + * defined as sharing the last two components of the domain part, + * or the entire domain part if the local domain has only one component. + * If either name is unqualified (contains no '.'), + * assume that the host is local, as it will be + * interpreted as such. + */ +int +local_domain(h) + char *h; +{ + char localhost[MAXHOSTNAMELEN]; + char *p1, *p2; + + localhost[0] = 0; + (void) gethostname(localhost, sizeof(localhost)); + p1 = topdomain(localhost); + p2 = topdomain(h); + if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) + return (1); + return (0); +} + +char * +topdomain(h) + char *h; +{ + register char *p; + char *maybe = NULL; + int dots = 0; + + for (p = h + strlen(h); p >= h; p--) { + if (*p == '.') { + if (++dots == 2) + return (p); + maybe = p; + } + } + return (maybe); +} diff --git a/libexec/rshd/Makefile b/libexec/rshd/Makefile new file mode 100644 index 0000000..0b448aa --- /dev/null +++ b/libexec/rshd/Makefile @@ -0,0 +1,11 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= rshd +CFLAGS+=-DKERBEROS -DCRYPT +SRCS= rshd.c des_rw.c +MAN8= rshd.0 +DPADD= ${LIBKRB} ${LIBDES} +LDADD= -lkrb -ldes +.PATH: ${.CURDIR}/../../usr.bin/rlogin + +.include <bsd.prog.mk> diff --git a/libexec/rshd/rshd.8 b/libexec/rshd/rshd.8 new file mode 100644 index 0000000..82e1991 --- /dev/null +++ b/libexec/rshd/rshd.8 @@ -0,0 +1,209 @@ +.\" Copyright (c) 1983, 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)rshd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt RSHD 8 +.Os BSD 4.2 +.Sh NAME +.Nm rshd +.Nd remote shell server +.Sh SYNOPSIS +.Nm rshd +.Op Fl alnL +.Sh DESCRIPTION +The +.Nm rshd +server +is the server for the +.Xr rcmd 3 +routine and, consequently, for the +.Xr rsh 1 +program. The server provides remote execution facilities +with authentication based on privileged port numbers from trusted hosts. +.Pp +The +.Nm rshd +server +listens for service requests at the port indicated in +the ``cmd'' service specification; see +.Xr services 5 . +When a service request is received the following protocol +is initiated: +.Bl -enum +.It +The server checks the client's source port. +If the port is not in the range 512-1023, the server +aborts the connection. +.It +The server reads characters from the socket up +to a null (`\e0') byte. The resultant string is +interpreted as an +.Tn ASCII +number, base 10. +.It +If the number received in step 2 is non-zero, +it is interpreted as the port number of a secondary +stream to be used for the +.Em stderr . +A second connection is then created to the specified +port on the client's machine. The source port of this +second connection is also in the range 512-1023. +.It +The server checks the client's source address +and requests the corresponding host name (see +.Xr gethostbyaddr 3 , +.Xr hosts 5 +and +.Xr named 8 ) . +If the hostname cannot be determined, +the dot-notation representation of the host address is used. +If the hostname is in the same domain as the server (according to +the last two components of the domain name), +or if the +.Fl a +option is given, +the addresses for the hostname are requested, +verifying that the name and address correspond. +If address verification fails, the connection is aborted +with the message, ``Host address mismatch.'' +.It +A null terminated user name of at most 16 characters +is retrieved on the initial socket. This user name +is interpreted as the user identity on the +.Em client Ns 's +machine. +.It +A null terminated user name of at most 16 characters +is retrieved on the initial socket. This user name +is interpreted as a user identity to use on the +.Sy server Ns 's +machine. +.It +A null terminated command to be passed to a +shell is retrieved on the initial socket. The length of +the command is limited by the upper bound on the size of +the system's argument list. +.It +.Nm Rshd +then validates the user using +.Xr ruserok 3 , +which uses the file +.Pa /etc/hosts.equiv +and the +.Pa .rhosts +file found in the user's home directory. The +.Fl l +option prevents +.Xr ruserok 3 +from doing any validation based on the user's ``.rhosts'' file, +unless the user is the superuser. +.It +If the file +.Pa /etc/nologin +exists and the user is not the superuser, +the connection is closed. +.It +A null byte is returned on the initial socket +and the command line is passed to the normal login +shell of the user. The +shell inherits the network connections established +by +.Nm rshd . +.El +.Pp +Transport-level keepalive messages are enabled unless the +.Fl n +option is present. +The use of keepalive messages allows sessions to be timed out +if the client crashes or becomes unreachable. +.Pp +The +.Fl L +option causes all successful accesses to be logged to +.Xr syslogd 8 +as +.Li auth.info +messages. +.Sh DIAGNOSTICS +Except for the last one listed below, +all diagnostic messages +are returned on the initial socket, +after which any network connections are closed. +An error is indicated by a leading byte with a value of +1 (0 is returned in step 10 above upon successful completion +of all the steps prior to the execution of the login shell). +.Bl -tag -width indent +.It Sy Locuser too long. +The name of the user on the client's machine is +longer than 16 characters. +.It Sy Ruser too long. +The name of the user on the remote machine is +longer than 16 characters. +.It Sy Command too long . +The command line passed exceeds the size of the argument +list (as configured into the system). +.It Sy Login incorrect. +No password file entry for the user name existed. +.It Sy Remote directory. +The +.Xr chdir +command to the home directory failed. +.It Sy Permission denied. +The authentication procedure described above failed. +.It Sy Can't make pipe. +The pipe needed for the +.Em stderr , +wasn't created. +.It Sy Can't fork; try again. +A +.Xr fork +by the server failed. +.It Sy <shellname>: ... +The user's login shell could not be started. This message is returned +on the connection associated with the +.Em stderr , +and is not preceded by a flag byte. +.El +.Sh SEE ALSO +.Xr rsh 1 , +.Xr rcmd 3 , +.Xr ruserok 3 +.Sh BUGS +The authentication procedure used here assumes the integrity +of each client machine and the connecting medium. This is +insecure, but is useful in an ``open'' environment. +.Pp +A facility to allow all data exchanges to be encrypted should be +present. +.Pp +A more extensible protocol (such as Telnet) should be used. diff --git a/libexec/rshd/rshd.c b/libexec/rshd/rshd.c new file mode 100644 index 0000000..1b9eea9 --- /dev/null +++ b/libexec/rshd/rshd.c @@ -0,0 +1,783 @@ +/*- + * Copyright (c) 1988, 1989, 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1988, 1989, 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rshd.c 8.2 (Berkeley) 4/6/94"; +#endif /* not lint */ + +/* + * remote shell server: + * [port]\0 + * remuser\0 + * locuser\0 + * command\0 + * data + */ +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +int keepalive = 1; +int check_all; +int log_success; /* If TRUE, log all successful accesses */ +int sent_null; + +void doit __P((struct sockaddr_in *)); +void error __P((const char *, ...)); +void getstr __P((char *, int, char *)); +int local_domain __P((char *)); +char *topdomain __P((char *)); +void usage __P((void)); + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> +#define VERSION_SIZE 9 +#define SECURE_MESSAGE "This rsh session is using DES encryption for all transmissions.\r\n" +#define OPTIONS "alnkvxL" +char authbuf[sizeof(AUTH_DAT)]; +char tickbuf[sizeof(KTEXT_ST)]; +int doencrypt, use_kerberos, vacuous; +Key_schedule schedule; +#else +#define OPTIONS "alnL" +#endif + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern int __check_rhosts_file; + struct linger linger; + int ch, on = 1, fromlen; + struct sockaddr_in from; + + openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON); + + opterr = 0; + while ((ch = getopt(argc, argv, OPTIONS)) != EOF) + switch (ch) { + case 'a': + check_all = 1; + break; + case 'l': + __check_rhosts_file = 0; + break; + case 'n': + keepalive = 0; + break; +#ifdef KERBEROS + case 'k': + use_kerberos = 1; + break; + + case 'v': + vacuous = 1; + break; + +#ifdef CRYPT + case 'x': + doencrypt = 1; + break; +#endif +#endif + case 'L': + log_success = 1; + break; + case '?': + default: + usage(); + break; + } + + argc -= optind; + argv += optind; + +#ifdef KERBEROS + if (use_kerberos && vacuous) { + syslog(LOG_ERR, "only one of -k and -v allowed"); + exit(2); + } +#ifdef CRYPT + if (doencrypt && !use_kerberos) { + syslog(LOG_ERR, "-k is required for -x"); + exit(2); + } +#endif +#endif + + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + syslog(LOG_ERR, "getpeername: %m"); + _exit(1); + } + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, + sizeof(on)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + linger.l_onoff = 1; + linger.l_linger = 60; /* XXX */ + if (setsockopt(0, SOL_SOCKET, SO_LINGER, (char *)&linger, + sizeof (linger)) < 0) + syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m"); + doit(&from); + /* NOTREACHED */ +} + +char username[20] = "USER="; +char homedir[64] = "HOME="; +char shell[64] = "SHELL="; +char path[100] = "PATH="; +char *envinit[] = + {homedir, shell, path, username, 0}; +char **environ; + +void +doit(fromp) + struct sockaddr_in *fromp; +{ + extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ + struct hostent *hp; + struct passwd *pwd; + u_short port; + fd_set ready, readfrom; + int cc, nfd, pv[2], pid, s; + int one = 1; + char *hostname, *errorstr, *errorhost; + char *cp, sig, buf[BUFSIZ]; + char cmdbuf[NCARGS+1], locuser[16], remuser[16]; + char remotehost[2 * MAXHOSTNAMELEN + 1]; + +#ifdef KERBEROS + AUTH_DAT *kdata = (AUTH_DAT *) NULL; + KTEXT ticket = (KTEXT) NULL; + char instance[INST_SZ], version[VERSION_SIZE]; + struct sockaddr_in fromaddr; + int rc; + long authopts; + int pv1[2], pv2[2]; + fd_set wready, writeto; + + fromaddr = *fromp; +#endif + + (void) signal(SIGINT, SIG_DFL); + (void) signal(SIGQUIT, SIG_DFL); + (void) signal(SIGTERM, SIG_DFL); +#ifdef DEBUG + { int t = open(_PATH_TTY, 2); + if (t >= 0) { + ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + fromp->sin_port = ntohs((u_short)fromp->sin_port); + if (fromp->sin_family != AF_INET) { + syslog(LOG_ERR, "malformed \"from\" address (af %d)\n", + fromp->sin_family); + exit(1); + } +#ifdef IP_OPTIONS + { + u_char optbuf[BUFSIZ/3], *cp; + char lbuf[BUFSIZ], *lp; + int optsize = sizeof(optbuf), ipproto; + struct protoent *ip; + + if ((ip = getprotobyname("ip")) != NULL) + ipproto = ip->p_proto; + else + ipproto = IPPROTO_IP; + if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && + optsize != 0) { + lp = lbuf; + for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) + sprintf(lp, " %2.2x", *cp); + syslog(LOG_NOTICE, + "Connection received from %s using IP options (ignored):%s", + inet_ntoa(fromp->sin_addr), lbuf); + if (setsockopt(0, ipproto, IP_OPTIONS, + (char *)NULL, optsize) != 0) { + syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); + exit(1); + } + } + } +#endif + +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (fromp->sin_port >= IPPORT_RESERVED || + fromp->sin_port < IPPORT_RESERVED/2) { + syslog(LOG_NOTICE|LOG_AUTH, + "Connection from %s on illegal port %u", + inet_ntoa(fromp->sin_addr), + fromp->sin_port); + exit(1); + } + + (void) alarm(60); + port = 0; + for (;;) { + char c; + if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { + if (cc < 0) + syslog(LOG_NOTICE, "read: %m"); + shutdown(0, 1+1); + exit(1); + } + if (c== 0) + break; + port = port * 10 + c - '0'; + } + + (void) alarm(0); + if (port != 0) { + int lport = IPPORT_RESERVED - 1; + s = rresvport(&lport); + if (s < 0) { + syslog(LOG_ERR, "can't get stderr port: %m"); + exit(1); + } +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (port >= IPPORT_RESERVED) { + syslog(LOG_ERR, "2nd port not reserved\n"); + exit(1); + } + fromp->sin_port = htons(port); + if (connect(s, (struct sockaddr *)fromp, sizeof (*fromp)) < 0) { + syslog(LOG_INFO, "connect second port %d: %m", port); + exit(1); + } + } + +#ifdef KERBEROS + if (vacuous) { + error("rshd: remote host requires Kerberos authentication\n"); + exit(1); + } +#endif + +#ifdef notdef + /* from inetd, socket is already on 0, 1, 2 */ + dup2(f, 0); + dup2(f, 1); + dup2(f, 2); +#endif + errorstr = NULL; + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (struct in_addr), + fromp->sin_family); + if (hp) { + /* + * If name returned by gethostbyaddr is in our domain, + * attempt to verify that we haven't been fooled by someone + * in a remote net; look up the name and check that this + * address corresponds to the name. + */ + hostname = hp->h_name; +#ifdef KERBEROS + if (!use_kerberos) +#endif + if (check_all || local_domain(hp->h_name)) { + strncpy(remotehost, hp->h_name, sizeof(remotehost) - 1); + remotehost[sizeof(remotehost) - 1] = 0; + errorhost = remotehost; + hp = gethostbyname(remotehost); + if (hp == NULL) { + syslog(LOG_INFO, + "Couldn't look up address for %s", + remotehost); + errorstr = + "Couldn't look up address for your host (%s)\n"; + hostname = inet_ntoa(fromp->sin_addr); + } else for (; ; hp->h_addr_list++) { + if (hp->h_addr_list[0] == NULL) { + syslog(LOG_NOTICE, + "Host addr %s not listed for host %s", + inet_ntoa(fromp->sin_addr), + hp->h_name); + errorstr = + "Host address mismatch for %s\n"; + hostname = inet_ntoa(fromp->sin_addr); + break; + } + if (!bcmp(hp->h_addr_list[0], + (caddr_t)&fromp->sin_addr, + sizeof(fromp->sin_addr))) { + hostname = hp->h_name; + break; + } + } + } + } else + errorhost = hostname = inet_ntoa(fromp->sin_addr); + +#ifdef KERBEROS + if (use_kerberos) { + kdata = (AUTH_DAT *) authbuf; + ticket = (KTEXT) tickbuf; + authopts = 0L; + strcpy(instance, "*"); + version[VERSION_SIZE - 1] = '\0'; +#ifdef CRYPT + if (doencrypt) { + struct sockaddr_in local_addr; + rc = sizeof(local_addr); + if (getsockname(0, (struct sockaddr *)&local_addr, + &rc) < 0) { + syslog(LOG_ERR, "getsockname: %m"); + error("rlogind: getsockname: %m"); + exit(1); + } + authopts = KOPT_DO_MUTUAL; + rc = krb_recvauth(authopts, 0, ticket, + "rcmd", instance, &fromaddr, + &local_addr, kdata, "", schedule, + version); + des_set_key(kdata->session, schedule); + } else +#endif + rc = krb_recvauth(authopts, 0, ticket, "rcmd", + instance, &fromaddr, + (struct sockaddr_in *) 0, + kdata, "", (bit_64 *) 0, version); + if (rc != KSUCCESS) { + error("Kerberos authentication failure: %s\n", + krb_err_txt[rc]); + exit(1); + } + } else +#endif + getstr(remuser, sizeof(remuser), "remuser"); + + getstr(locuser, sizeof(locuser), "locuser"); + getstr(cmdbuf, sizeof(cmdbuf), "command"); + setpwent(); + pwd = getpwnam(locuser); + if (pwd == NULL) { + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: unknown login. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + if (errorstr == NULL) + errorstr = "Login incorrect.\n"; + goto fail; + } + if (chdir(pwd->pw_dir) < 0) { + (void) chdir("/"); +#ifdef notdef + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: no home directory. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + error("No remote directory.\n"); + exit(1); +#endif + } + +#ifdef KERBEROS + if (use_kerberos) { + if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { + if (kuserok(kdata, locuser) != 0) { + syslog(LOG_INFO|LOG_AUTH, + "Kerberos rsh denied to %s.%s@%s", + kdata->pname, kdata->pinst, kdata->prealm); + error("Permission denied.\n"); + exit(1); + } + } + } else +#endif + + if (errorstr || + pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && + iruserok(fromp->sin_addr.s_addr, pwd->pw_uid == 0, + remuser, locuser) < 0) { + if (__rcmd_errstr) + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied (%s). cmd='%.80s'", + remuser, hostname, locuser, __rcmd_errstr, + cmdbuf); + else + syslog(LOG_INFO|LOG_AUTH, + "%s@%s as %s: permission denied. cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); +fail: + if (errorstr == NULL) + errorstr = "Permission denied.\n"; + error(errorstr, errorhost); + exit(1); + } + + if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { + error("Logins currently disabled.\n"); + exit(1); + } + + (void) write(STDERR_FILENO, "\0", 1); + sent_null = 1; + + if (port) { + if (pipe(pv) < 0) { + error("Can't make pipe.\n"); + exit(1); + } +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + if (pipe(pv1) < 0) { + error("Can't make 2nd pipe.\n"); + exit(1); + } + if (pipe(pv2) < 0) { + error("Can't make 3rd pipe.\n"); + exit(1); + } + } +#endif +#endif + pid = fork(); + if (pid == -1) { + error("Can't fork; try again.\n"); + exit(1); + } + if (pid) { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + static char msg[] = SECURE_MESSAGE; + (void) close(pv1[1]); + (void) close(pv2[1]); + des_write(s, msg, sizeof(msg) - 1); + + } else +#endif +#endif + { + (void) close(0); + (void) close(1); + } + (void) close(2); + (void) close(pv[1]); + + FD_ZERO(&readfrom); + FD_SET(s, &readfrom); + FD_SET(pv[0], &readfrom); + if (pv[0] > s) + nfd = pv[0]; + else + nfd = s; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + FD_ZERO(&writeto); + FD_SET(pv2[0], &writeto); + FD_SET(pv1[0], &readfrom); + + nfd = MAX(nfd, pv2[0]); + nfd = MAX(nfd, pv1[0]); + } else +#endif +#endif + ioctl(pv[0], FIONBIO, (char *)&one); + + /* should set s nbio! */ + nfd++; + do { + ready = readfrom; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + wready = writeto; + if (select(nfd, &ready, + &wready, (fd_set *) 0, + (struct timeval *) 0) < 0) + break; + } else +#endif +#endif + if (select(nfd, &ready, (fd_set *)0, + (fd_set *)0, (struct timeval *)0) < 0) + break; + if (FD_ISSET(s, &ready)) { + int ret; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + ret = des_read(s, &sig, 1); + else +#endif +#endif + ret = read(s, &sig, 1); + if (ret <= 0) + FD_CLR(s, &readfrom); + else + killpg(pid, sig); + } + if (FD_ISSET(pv[0], &ready)) { + errno = 0; + cc = read(pv[0], buf, sizeof(buf)); + if (cc <= 0) { + shutdown(s, 1+1); + FD_CLR(pv[0], &readfrom); + } else { +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + (void) + des_write(s, buf, cc); + else +#endif +#endif + (void) + write(s, buf, cc); + } + } +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt && FD_ISSET(pv1[0], &ready)) { + errno = 0; + cc = read(pv1[0], buf, sizeof(buf)); + if (cc <= 0) { + shutdown(pv1[0], 1+1); + FD_CLR(pv1[0], &readfrom); + } else + (void) des_write(STDOUT_FILENO, + buf, cc); + } + + if (doencrypt && FD_ISSET(pv2[0], &wready)) { + errno = 0; + cc = des_read(STDIN_FILENO, + buf, sizeof(buf)); + if (cc <= 0) { + shutdown(pv2[0], 1+1); + FD_CLR(pv2[0], &writeto); + } else + (void) write(pv2[0], buf, cc); + } +#endif +#endif + + } while (FD_ISSET(s, &readfrom) || +#ifdef CRYPT +#ifdef KERBEROS + (doencrypt && FD_ISSET(pv1[0], &readfrom)) || +#endif +#endif + FD_ISSET(pv[0], &readfrom)); + exit(0); + } + setpgrp(0, getpid()); + (void) close(s); + (void) close(pv[0]); +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + close(pv1[0]); close(pv2[0]); + dup2(pv1[1], 1); + dup2(pv2[1], 0); + close(pv1[1]); + close(pv2[1]); + } +#endif +#endif + dup2(pv[1], 2); + close(pv[1]); + } + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; +#if BSD > 43 + if (setlogin(pwd->pw_name) < 0) + syslog(LOG_ERR, "setlogin() failed: %m"); +#endif + (void) setgid((gid_t)pwd->pw_gid); + initgroups(pwd->pw_name, pwd->pw_gid); + (void) setuid((uid_t)pwd->pw_uid); + environ = envinit; + strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); + strcat(path, _PATH_DEFPATH); + strncat(shell, pwd->pw_shell, sizeof(shell)-7); + strncat(username, pwd->pw_name, sizeof(username)-6); + cp = strrchr(pwd->pw_shell, '/'); + if (cp) + cp++; + else + cp = pwd->pw_shell; + endpwent(); + if (log_success || pwd->pw_uid == 0) { +#ifdef KERBEROS + if (use_kerberos) + syslog(LOG_INFO|LOG_AUTH, + "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'", + kdata->pname, kdata->pinst, kdata->prealm, + hostname, locuser, cmdbuf); + else +#endif + syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", + remuser, hostname, locuser, cmdbuf); + } + execl(pwd->pw_shell, cp, "-c", cmdbuf, 0); + perror(pwd->pw_shell); + exit(1); +} + +/* + * Report error to client. Note: can't be used until second socket has + * connected to client, or older clients will hang waiting for that + * connection first. + */ +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +error(const char *fmt, ...) +#else +error(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; + int len; + char *bp, buf[BUFSIZ]; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + bp = buf; + if (sent_null == 0) { + *bp++ = 1; + len = 1; + } else + len = 0; + (void)vsnprintf(bp, sizeof(buf) - 1, fmt, ap); + (void)write(STDERR_FILENO, buf, len + strlen(bp)); +} + +void +getstr(buf, cnt, err) + char *buf, *err; + int cnt; +{ + char c; + + do { + if (read(STDIN_FILENO, &c, 1) != 1) + exit(1); + *buf++ = c; + if (--cnt == 0) { + error("%s too long\n", err); + exit(1); + } + } while (c != 0); +} + +/* + * Check whether host h is in our local domain, + * defined as sharing the last two components of the domain part, + * or the entire domain part if the local domain has only one component. + * If either name is unqualified (contains no '.'), + * assume that the host is local, as it will be + * interpreted as such. + */ +int +local_domain(h) + char *h; +{ + char localhost[MAXHOSTNAMELEN]; + char *p1, *p2; + + localhost[0] = 0; + (void) gethostname(localhost, sizeof(localhost)); + p1 = topdomain(localhost); + p2 = topdomain(h); + if (p1 == NULL || p2 == NULL || !strcasecmp(p1, p2)) + return (1); + return (0); +} + +char * +topdomain(h) + char *h; +{ + char *p, *maybe = NULL; + int dots = 0; + + for (p = h + strlen(h); p >= h; p--) { + if (*p == '.') { + if (++dots == 2) + return (p); + maybe = p; + } + } + return (maybe); +} + +void +usage() +{ + + syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS); + exit(2); +} diff --git a/libexec/talkd/Makefile b/libexec/talkd/Makefile new file mode 100644 index 0000000..51f6806 --- /dev/null +++ b/libexec/talkd/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= ntalkd +SRCS= talkd.c announce.c process.c table.c print.c ttymsg.c +.PATH: ${.CURDIR}/../../usr.bin/wall +MAN8= talkd.0 + +.include <bsd.prog.mk> diff --git a/libexec/talkd/announce.c b/libexec/talkd/announce.c new file mode 100644 index 0000000..e8c9915 --- /dev/null +++ b/libexec/talkd/announce.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)announce.c 8.2 (Berkeley) 1/7/94"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <sgtty.h> +#include <errno.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <paths.h> + +extern char hostname[]; + +/* + * Announce an invitation to talk. + */ + +/* + * See if the user is accepting messages. If so, announce that + * a talk is requested. + */ +announce(request, remote_machine) + CTL_MSG *request; + char *remote_machine; +{ + char full_tty[32]; + FILE *tf; + struct stat stbuf; + + (void)snprintf(full_tty, sizeof(full_tty), + "%s%s", _PATH_DEV, request->r_tty); + if (stat(full_tty, &stbuf) < 0 || (stbuf.st_mode&020) == 0) + return (PERMISSION_DENIED); + return (print_mesg(request->r_tty, tf, request, remote_machine)); +} + +#define max(a,b) ( (a) > (b) ? (a) : (b) ) +#define N_LINES 5 +#define N_CHARS 120 + +/* + * Build a block of characters containing the message. + * It is sent blank filled and in a single block to + * try to keep the message in one piece if the recipient + * in in vi at the time + */ +print_mesg(tty, tf, request, remote_machine) + char *tty; + FILE *tf; + CTL_MSG *request; + char *remote_machine; +{ + struct timeval clock; + struct timezone zone; + struct tm *localtime(); + struct tm *localclock; + struct iovec iovec; + char line_buf[N_LINES][N_CHARS]; + int sizes[N_LINES]; + char big_buf[N_LINES*N_CHARS]; + char *bptr, *lptr, *ttymsg(); + int i, j, max_size; + + i = 0; + max_size = 0; + gettimeofday(&clock, &zone); + localclock = localtime( &clock.tv_sec ); + (void)sprintf(line_buf[i], " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "Message from Talk_Daemon@%s at %d:%02d ...", + hostname, localclock->tm_hour , localclock->tm_min ); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "talk: connection requested by %s@%s", + request->l_name, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], "talk: respond with: talk %s@%s", + request->l_name, remote_machine); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + (void)sprintf(line_buf[i], " "); + sizes[i] = strlen(line_buf[i]); + max_size = max(max_size, sizes[i]); + i++; + bptr = big_buf; + *bptr++ = ''; /* send something to wake them up */ + *bptr++ = '\r'; /* add a \r in case of raw mode */ + *bptr++ = '\n'; + for (i = 0; i < N_LINES; i++) { + /* copy the line into the big buffer */ + lptr = line_buf[i]; + while (*lptr != '\0') + *(bptr++) = *(lptr++); + /* pad out the rest of the lines with blanks */ + for (j = sizes[i]; j < max_size + 2; j++) + *(bptr++) = ' '; + *(bptr++) = '\r'; /* add a \r in case of raw mode */ + *(bptr++) = '\n'; + } + *bptr = '\0'; + iovec.iov_base = big_buf; + iovec.iov_len = bptr - big_buf; + /* + * we choose a timeout of RING_WAIT-5 seconds so that we don't + * stack up processes trying to write messages to a tty + * that is permanently blocked. + */ + if (ttymsg(&iovec, 1, tty, RING_WAIT - 5) != NULL) + return (FAILED); + + return (SUCCESS); +} diff --git a/libexec/talkd/print.c b/libexec/talkd/print.c new file mode 100644 index 0000000..9c0085b --- /dev/null +++ b/libexec/talkd/print.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* debug print routines */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <syslog.h> +#include <stdio.h> + +static char *types[] = + { "leave_invite", "look_up", "delete", "announce" }; +#define NTYPES (sizeof (types) / sizeof (types[0])) +static char *answers[] = + { "success", "not_here", "failed", "machine_unknown", "permission_denied", + "unknown_request", "badversion", "badaddr", "badctladdr" }; +#define NANSWERS (sizeof (answers) / sizeof (answers[0])) + +print_request(cp, mp) + char *cp; + register CTL_MSG *mp; +{ + char tbuf[80], *tp; + + if (mp->type > NTYPES) { + (void)sprintf(tbuf, "type %d", mp->type); + tp = tbuf; + } else + tp = types[mp->type]; + syslog(LOG_DEBUG, "%s: %s: id %d, l_user %s, r_user %s, r_tty %s", + cp, tp, mp->id_num, mp->l_name, mp->r_name, mp->r_tty); +} + +print_response(cp, rp) + char *cp; + register CTL_RESPONSE *rp; +{ + char tbuf[80], *tp, abuf[80], *ap; + + if (rp->type > NTYPES) { + (void)sprintf(tbuf, "type %d", rp->type); + tp = tbuf; + } else + tp = types[rp->type]; + if (rp->answer > NANSWERS) { + (void)sprintf(abuf, "answer %d", rp->answer); + ap = abuf; + } else + ap = answers[rp->answer]; + syslog(LOG_DEBUG, "%s: %s: %s, id %d", cp, tp, ap, ntohl(rp->id_num)); +} diff --git a/libexec/talkd/process.c b/libexec/talkd/process.c new file mode 100644 index 0000000..dd05e6b --- /dev/null +++ b/libexec/talkd/process.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)process.c 8.2 (Berkeley) 11/16/93"; +#endif /* not lint */ + +/* + * process.c handles the requests, which can be of three types: + * ANNOUNCE - announce to a user that a talk is wanted + * LEAVE_INVITE - insert the request into the table + * LOOK_UP - look up to see if a request is waiting in + * in the table for the local user + * DELETE - delete invitation + */ +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <protocols/talkd.h> +#include <netdb.h> +#include <syslog.h> +#include <stdio.h> +#include <string.h> +#include <paths.h> + +CTL_MSG *find_request(); +CTL_MSG *find_match(); + +process_request(mp, rp) + register CTL_MSG *mp; + register CTL_RESPONSE *rp; +{ + register CTL_MSG *ptr; + extern int debug; + + rp->vers = TALK_VERSION; + rp->type = mp->type; + rp->id_num = htonl(0); + if (mp->vers != TALK_VERSION) { + syslog(LOG_WARNING, "Bad protocol version %d", mp->vers); + rp->answer = BADVERSION; + return; + } + mp->id_num = ntohl(mp->id_num); + mp->addr.sa_family = ntohs(mp->addr.sa_family); + if (mp->addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "Bad address, family %d", + mp->addr.sa_family); + rp->answer = BADADDR; + return; + } + mp->ctl_addr.sa_family = ntohs(mp->ctl_addr.sa_family); + if (mp->ctl_addr.sa_family != AF_INET) { + syslog(LOG_WARNING, "Bad control address, family %d", + mp->ctl_addr.sa_family); + rp->answer = BADCTLADDR; + return; + } + mp->pid = ntohl(mp->pid); + if (debug) + print_request("process_request", mp); + switch (mp->type) { + + case ANNOUNCE: + do_announce(mp, rp); + break; + + case LEAVE_INVITE: + ptr = find_request(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } else + insert_table(mp, rp); + break; + + case LOOK_UP: + ptr = find_match(mp); + if (ptr != (CTL_MSG *)0) { + rp->id_num = htonl(ptr->id_num); + rp->addr = ptr->addr; + rp->addr.sa_family = htons(ptr->addr.sa_family); + rp->answer = SUCCESS; + } else + rp->answer = NOT_HERE; + break; + + case DELETE: + rp->answer = delete_invite(mp->id_num); + break; + + default: + rp->answer = UNKNOWN_REQUEST; + break; + } + if (debug) + print_response("process_request", rp); +} + +do_announce(mp, rp) + register CTL_MSG *mp; + CTL_RESPONSE *rp; +{ + struct hostent *hp; + CTL_MSG *ptr; + int result; + + /* see if the user is logged */ + result = find_user(mp->r_name, mp->r_tty); + if (result != SUCCESS) { + rp->answer = result; + return; + } +#define satosin(sa) ((struct sockaddr_in *)(sa)) + hp = gethostbyaddr((char *)&satosin(&mp->ctl_addr)->sin_addr, + sizeof (struct in_addr), AF_INET); + if (hp == (struct hostent *)0) { + rp->answer = MACHINE_UNKNOWN; + return; + } + ptr = find_request(mp); + if (ptr == (CTL_MSG *) 0) { + insert_table(mp, rp); + rp->answer = announce(mp, hp->h_name); + return; + } + if (mp->id_num > ptr->id_num) { + /* + * This is an explicit re-announce, so update the id_num + * field to avoid duplicates and re-announce the talk. + */ + ptr->id_num = new_id(); + rp->id_num = htonl(ptr->id_num); + rp->answer = announce(mp, hp->h_name); + } else { + /* a duplicated request, so ignore it */ + rp->id_num = htonl(ptr->id_num); + rp->answer = SUCCESS; + } +} + +#include <utmp.h> + +/* + * Search utmp for the local user + */ +find_user(name, tty) + char *name, *tty; +{ + struct utmp ubuf; + int status; + FILE *fd; + struct stat statb; + char line[sizeof(ubuf.ut_line) + 1]; + char ftty[sizeof(_PATH_DEV) - 1 + sizeof(line)]; + + if ((fd = fopen(_PATH_UTMP, "r")) == NULL) { + fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP); + return (FAILED); + } +#define SCMPN(a, b) strncmp(a, b, sizeof (a)) + status = NOT_HERE; + (void) strcpy(ftty, _PATH_DEV); + while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) + if (SCMPN(ubuf.ut_name, name) == 0) { + strncpy(line, ubuf.ut_line, sizeof(ubuf.ut_line)); + line[sizeof(ubuf.ut_line)] = '\0'; + if (*tty == '\0') { + status = PERMISSION_DENIED; + /* no particular tty was requested */ + (void) strcpy(ftty + sizeof(_PATH_DEV) - 1, + line); + if (stat(ftty, &statb) == 0) { + if (!(statb.st_mode & 020)) + continue; + (void) strcpy(tty, line); + status = SUCCESS; + break; + } + } + if (strcmp(line, tty) == 0) { + status = SUCCESS; + break; + } + } + fclose(fd); + return (status); +} diff --git a/libexec/talkd/table.c b/libexec/talkd/table.c new file mode 100644 index 0000000..cc8cb66 --- /dev/null +++ b/libexec/talkd/table.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)table.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Routines to handle insertion, deletion, etc on the table + * of requests kept by the daemon. Nothing fancy here, linear + * search on a double-linked list. A time is kept with each + * entry so that overly old invitations can be eliminated. + * + * Consider this a mis-guided attempt at modularity + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */ + +#define NIL ((TABLE_ENTRY *)0) + +extern int debug; +struct timeval tp; +struct timezone txp; + +typedef struct table_entry TABLE_ENTRY; + +struct table_entry { + CTL_MSG request; + long time; + TABLE_ENTRY *next; + TABLE_ENTRY *last; +}; + +TABLE_ENTRY *table = NIL; +CTL_MSG *find_request(); +CTL_MSG *find_match(); + +/* + * Look in the table for an invitation that matches the current + * request looking for an invitation + */ +CTL_MSG * +find_match(request) + register CTL_MSG *request; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + if (debug) + print_request("find_match", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->l_name, ptr->request.r_name) == 0 && + strcmp(request->r_name, ptr->request.l_name) == 0 && + ptr->request.type == LEAVE_INVITE) + return (&ptr->request); + } + return ((CTL_MSG *)0); +} + +/* + * Look for an identical request, as opposed to a complimentary + * one as find_match does + */ +CTL_MSG * +find_request(request) + register CTL_MSG *request; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + /* + * See if this is a repeated message, and check for + * out of date entries in the table while we are it. + */ + if (debug) + print_request("find_request", request); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if ((ptr->time - current_time) > MAX_LIFE) { + /* the entry is too old */ + if (debug) + print_request("deleting expired entry", + &ptr->request); + delete(ptr); + continue; + } + if (debug) + print_request("", &ptr->request); + if (strcmp(request->r_name, ptr->request.r_name) == 0 && + strcmp(request->l_name, ptr->request.l_name) == 0 && + request->type == ptr->request.type && + request->pid == ptr->request.pid) { + /* update the time if we 'touch' it */ + ptr->time = current_time; + return (&ptr->request); + } + } + return ((CTL_MSG *)0); +} + +insert_table(request, response) + CTL_MSG *request; + CTL_RESPONSE *response; +{ + register TABLE_ENTRY *ptr; + time_t current_time; + + gettimeofday(&tp, &txp); + current_time = tp.tv_sec; + request->id_num = new_id(); + response->id_num = htonl(request->id_num); + /* insert a new entry into the top of the list */ + ptr = (TABLE_ENTRY *)malloc(sizeof(TABLE_ENTRY)); + if (ptr == NIL) { + syslog(LOG_ERR, "insert_table: Out of memory"); + _exit(1); + } + ptr->time = current_time; + ptr->request = *request; + ptr->next = table; + if (ptr->next != NIL) + ptr->next->last = ptr; + ptr->last = NIL; + table = ptr; +} + +/* + * Generate a unique non-zero sequence number + */ +new_id() +{ + static int current_id = 0; + + current_id = (current_id + 1) % MAX_ID; + /* 0 is reserved, helps to pick up bugs */ + if (current_id == 0) + current_id = 1; + return (current_id); +} + +/* + * Delete the invitation with id 'id_num' + */ +delete_invite(id_num) + int id_num; +{ + register TABLE_ENTRY *ptr; + + ptr = table; + if (debug) + syslog(LOG_DEBUG, "delete_invite(%d)", id_num); + for (ptr = table; ptr != NIL; ptr = ptr->next) { + if (ptr->request.id_num == id_num) + break; + if (debug) + print_request("", &ptr->request); + } + if (ptr != NIL) { + delete(ptr); + return (SUCCESS); + } + return (NOT_HERE); +} + +/* + * Classic delete from a double-linked list + */ +delete(ptr) + register TABLE_ENTRY *ptr; +{ + + if (debug) + print_request("delete", &ptr->request); + if (table == ptr) + table = ptr->next; + else if (ptr->last != NIL) + ptr->last->next = ptr->next; + if (ptr->next != NIL) + ptr->next->last = ptr->last; + free((char *)ptr); +} diff --git a/libexec/talkd/talkd.8 b/libexec/talkd/talkd.8 new file mode 100644 index 0000000..36dfb28 --- /dev/null +++ b/libexec/talkd/talkd.8 @@ -0,0 +1,75 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)talkd.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt TALKD 8 +.Os BSD 4.3 +.Sh NAME +.Nm talkd +.Nd remote user communication server +.Sh SYNOPSIS +.Nm talkd +.Sh DESCRIPTION +.Nm Talkd +is the server that notifies a user that someone else wants to +initiate a conversation. +It acts as a repository of invitations, responding to requests +by clients wishing to rendezvous to hold a conversation. +In normal operation, a client, the caller, +initiates a rendezvous by sending a +.Tn CTL_MSG +to the server of +type +.Tn LOOK_UP +(see +.Aq Pa protocols/talkd.h ) . +This causes the server to search its invitation +tables to check if an invitation currently exists for the caller +(to speak to the callee specified in the message). +If the lookup fails, +the caller then sends an +.Tn ANNOUNCE +message causing the server to +broadcast an announcement on the callee's login ports requesting contact. +When the callee responds, the local server uses the +recorded invitation to respond with the appropriate rendezvous +address and the caller and callee client programs establish a +stream connection through which the conversation takes place. +.Sh SEE ALSO +.Xr talk 1 , +.Xr write 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/libexec/talkd/talkd.c b/libexec/talkd/talkd.c new file mode 100644 index 0000000..669931b --- /dev/null +++ b/libexec/talkd/talkd.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)talkd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * The top level of the daemon, the format is heavily borrowed + * from rwhod.c. Basically: find out who and where you are; + * disconnect all descriptors and ttys, and then endless + * loop on waiting for and processing requests + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <protocols/talkd.h> +#include <signal.h> +#include <syslog.h> +#include <time.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <paths.h> + +CTL_MSG request; +CTL_RESPONSE response; + +int sockt; +int debug = 0; +void timeout(); +long lastmsgtime; + +char hostname[32]; + +#define TIMEOUT 30 +#define MAXIDLE 120 + +main(argc, argv) + int argc; + char *argv[]; +{ + register CTL_MSG *mp = &request; + int cc; + + if (getuid()) { + fprintf(stderr, "%s: getuid: not super-user\n", argv[0]); + exit(1); + } + openlog("talkd", LOG_PID, LOG_DAEMON); + if (gethostname(hostname, sizeof (hostname) - 1) < 0) { + syslog(LOG_ERR, "gethostname: %m"); + _exit(1); + } + if (chdir(_PATH_DEV) < 0) { + syslog(LOG_ERR, "chdir: %s: %m", _PATH_DEV); + _exit(1); + } + if (argc > 1 && strcmp(argv[1], "-d") == 0) + debug = 1; + signal(SIGALRM, timeout); + alarm(TIMEOUT); + for (;;) { + extern int errno; + + cc = recv(0, (char *)mp, sizeof (*mp), 0); + if (cc != sizeof (*mp)) { + if (cc < 0 && errno != EINTR) + syslog(LOG_WARNING, "recv: %m"); + continue; + } + lastmsgtime = time(0); + process_request(mp, &response); + /* can block here, is this what I want? */ + cc = sendto(sockt, (char *)&response, + sizeof (response), 0, (struct sockaddr *)&mp->ctl_addr, + sizeof (mp->ctl_addr)); + if (cc != sizeof (response)) + syslog(LOG_WARNING, "sendto: %m"); + } +} + +void +timeout() +{ + + if (time(0) - lastmsgtime >= MAXIDLE) + _exit(0); + alarm(TIMEOUT); +} diff --git a/libexec/telnetd/Makefile b/libexec/telnetd/Makefile new file mode 100644 index 0000000..4b1d530 --- /dev/null +++ b/libexec/telnetd/Makefile @@ -0,0 +1,36 @@ +# @(#)Makefile 8.2 (Berkeley) 12/15/93 + +PROG= telnetd +CFLAGS+=-DLINEMODE -DKLUDGELINEMODE -DUSE_TERMIO -DDIAGNOSTICS +CFLAGS+=-DOLD_ENVIRON -DENV_HACK +CFLAGS+=-DAUTHENTICATION -DENCRYPTION -I${.CURDIR}/../../lib +SRCS= authenc.c global.c slc.c state.c sys_term.c telnetd.c \ + termstat.c utility.c +DPADD= ${LIBUTIL} ${LIBTERM} +LDADD= -lutil -ltermcap -ltelnet +LDADD+= -lkrb -ldes +MAN8= telnetd.0 + +# These are the sources that have encryption stuff in them. +CRYPT_SRC= authenc.c ext.h state.c telnetd.c termstat.c +CRYPT_SRC+= utility.c Makefile +NOCRYPT_DIR=${.CURDIR}/Nocrypt + +.include <bsd.prog.mk> + +nocrypt: +#ifdef ENCRYPTION + @for i in ${CRYPT_SRC}; do \ + if [ ! -d ${NOCRYPT_DIR} ]; then \ + echo Creating subdirectory ${NOCRYPT_DIR}; \ + mkdir ${NOCRYPT_DIR}; \ + fi; \ + echo ${NOCRYPT_DIR}/$$i; \ + unifdef -UENCRYPTION ${.CURDIR}/$$i | \ + sed "s/ || defined(ENCRYPTION)//" > ${NOCRYPT_DIR}/$$i; \ + done + +placeholder: +#else /* ENCRYPTION */ + @echo "Encryption code already removed." +#endif /* ENCRYPTION */ diff --git a/libexec/telnetd/authenc.c b/libexec/telnetd/authenc.c new file mode 100644 index 0000000..fcd17fc --- /dev/null +++ b/libexec/telnetd/authenc.c @@ -0,0 +1,91 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)authenc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) +#include "telnetd.h" +#include <libtelnet/misc.h> + + int +net_write(str, len) + unsigned char *str; + int len; +{ + if (nfrontp + len < netobuf + BUFSIZ) { + bcopy((void *)str, (void *)nfrontp, len); + nfrontp += len; + return(len); + } + return(0); +} + + void +net_encrypt() +{ +#ifdef ENCRYPTION + char *s = (nclearto > nbackp) ? nclearto : nbackp; + if (s < nfrontp && encrypt_output) { + (*encrypt_output)((unsigned char *)s, nfrontp - s); + } + nclearto = nfrontp; +#endif /* ENCRYPTION */ +} + + int +telnet_spin() +{ + ttloop(); + return(0); +} + + char * +telnet_getenv(val) + char *val; +{ + extern char *getenv(); + return(getenv(val)); +} + + char * +telnet_gets(prompt, result, length, echo) + char *prompt; + char *result; + int length; + int echo; +{ + return((char *)0); +} +#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */ diff --git a/libexec/telnetd/defs.h b/libexec/telnetd/defs.h new file mode 100644 index 0000000..a73d4a6 --- /dev/null +++ b/libexec/telnetd/defs.h @@ -0,0 +1,296 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)defs.h 8.1 (Berkeley) 6/4/93 + */ + +/* + * Telnet server defines + */ +#include <sys/types.h> +#include <sys/param.h> + +#ifndef BSD +# define BSD 43 +#endif + +#if defined(CRAY) && !defined(LINEMODE) +# define SYSV_TERMIO +# define LINEMODE +# define KLUDGELINEMODE +# define DIAGNOSTICS +# if defined(UNICOS50) && !defined(UNICOS5) +# define UNICOS5 +# endif +# if !defined(UNICOS5) +# define BFTPDAEMON +# define HAS_IP_TOS +# endif +#endif /* CRAY */ +#if defined(UNICOS5) && !defined(NO_SETSID) +# define NO_SETSID +#endif + +#if defined(PRINTOPTIONS) && defined(DIAGNOSTICS) +#define TELOPTS +#define TELCMDS +#define SLC_NAMES +#endif + +#if defined(SYSV_TERMIO) && !defined(USE_TERMIO) +# define USE_TERMIO +#endif + +#include <sys/socket.h> +#ifndef CRAY +#include <sys/wait.h> +#endif /* CRAY */ +#include <fcntl.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/time.h> +#ifndef FILIO_H +#include <sys/ioctl.h> +#else +#include <sys/filio.h> +#endif + +#include <netinet/in.h> + +#include <arpa/telnet.h> + +#include <stdio.h> +#ifdef __STDC__ +#include <stdlib.h> +#endif +#include <signal.h> +#include <errno.h> +#include <netdb.h> +#include <syslog.h> +#ifndef LOG_DAEMON +#define LOG_DAEMON 0 +#endif +#ifndef LOG_ODELAY +#define LOG_ODELAY 0 +#endif +#include <ctype.h> +#ifndef NO_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +#ifndef USE_TERMIO +#include <sgtty.h> +#else +# ifdef SYSV_TERMIO +# include <termio.h> +# else +# include <termios.h> +# endif +#endif +#if !defined(USE_TERMIO) || defined(NO_CC_T) +typedef unsigned char cc_t; +#endif + +#ifdef __STDC__ +#include <unistd.h> +#endif + +#ifndef _POSIX_VDISABLE +# ifdef VDISABLE +# define _POSIX_VDISABLE VDISABLE +# else +# define _POSIX_VDISABLE ((unsigned char)'\377') +# endif +#endif + + +#ifdef CRAY +# ifdef CRAY1 +# include <sys/pty.h> +# ifndef FD_ZERO +# include <sys/select.h> +# endif /* FD_ZERO */ +# endif /* CRAY1 */ + +#include <memory.h> +#endif /* CRAY */ + +#ifdef __hpux +#include <sys/ptyio.h> +#endif + +#if !defined(TIOCSCTTY) && defined(TCSETCTTY) +# define TIOCSCTTY TCSETCTTY +#endif + +#ifndef FD_SET +#ifndef HAVE_fd_set +typedef struct fd_set { int fds_bits[1]; } fd_set; +#endif + +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) +#endif /* FD_SET */ + +/* + * I/O data buffers defines + */ +#define NETSLOP 64 +#ifdef CRAY +#undef BUFSIZ +#define BUFSIZ 2048 +#endif + +#define NIACCUM(c) { *netip++ = c; \ + ncc++; \ + } + +/* clock manipulations */ +#define settimer(x) (clocks.x = ++clocks.system) +#define sequenceIs(x,y) (clocks.x < clocks.y) + +/* + * Linemode support states, in decreasing order of importance + */ +#define REAL_LINEMODE 0x04 +#define KLUDGE_OK 0x03 +#define NO_AUTOKLUDGE 0x02 +#define KLUDGE_LINEMODE 0x01 +#define NO_LINEMODE 0x00 + +/* + * Structures of information for each special character function. + */ +typedef struct { + unsigned char flag; /* the flags for this function */ + cc_t val; /* the value of the special character */ +} slcent, *Slcent; + +typedef struct { + slcent defset; /* the default settings */ + slcent current; /* the current settings */ + cc_t *sptr; /* a pointer to the char in */ + /* system data structures */ +} slcfun, *Slcfun; + +#ifdef DIAGNOSTICS +/* + * Diagnostics capabilities + */ +#define TD_REPORT 0x01 /* Report operations to client */ +#define TD_EXERCISE 0x02 /* Exercise client's implementation */ +#define TD_NETDATA 0x04 /* Display received data stream */ +#define TD_PTYDATA 0x08 /* Display data passed to pty */ +#define TD_OPTIONS 0x10 /* Report just telnet options */ +#endif /* DIAGNOSTICS */ + +/* + * We keep track of each side of the option negotiation. + */ + +#define MY_STATE_WILL 0x01 +#define MY_WANT_STATE_WILL 0x02 +#define MY_STATE_DO 0x04 +#define MY_WANT_STATE_DO 0x08 + +/* + * Macros to check the current state of things + */ + +#define my_state_is_do(opt) (options[opt]&MY_STATE_DO) +#define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) +#define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) +#define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) + +#define my_state_is_dont(opt) (!my_state_is_do(opt)) +#define my_state_is_wont(opt) (!my_state_is_will(opt)) +#define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) +#define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) + +#define set_my_state_do(opt) (options[opt] |= MY_STATE_DO) +#define set_my_state_will(opt) (options[opt] |= MY_STATE_WILL) +#define set_my_want_state_do(opt) (options[opt] |= MY_WANT_STATE_DO) +#define set_my_want_state_will(opt) (options[opt] |= MY_WANT_STATE_WILL) + +#define set_my_state_dont(opt) (options[opt] &= ~MY_STATE_DO) +#define set_my_state_wont(opt) (options[opt] &= ~MY_STATE_WILL) +#define set_my_want_state_dont(opt) (options[opt] &= ~MY_WANT_STATE_DO) +#define set_my_want_state_wont(opt) (options[opt] &= ~MY_WANT_STATE_WILL) + +/* + * Tricky code here. What we want to know is if the MY_STATE_WILL + * and MY_WANT_STATE_WILL bits have the same value. Since the two + * bits are adjacent, a little arithmatic will show that by adding + * in the lower bit, the upper bit will be set if the two bits were + * different, and clear if they were the same. + */ +#define my_will_wont_is_changing(opt) \ + ((options[opt]+MY_STATE_WILL) & MY_WANT_STATE_WILL) + +#define my_do_dont_is_changing(opt) \ + ((options[opt]+MY_STATE_DO) & MY_WANT_STATE_DO) + +/* + * Make everything symetrical + */ + +#define HIS_STATE_WILL MY_STATE_DO +#define HIS_WANT_STATE_WILL MY_WANT_STATE_DO +#define HIS_STATE_DO MY_STATE_WILL +#define HIS_WANT_STATE_DO MY_WANT_STATE_WILL + +#define his_state_is_do my_state_is_will +#define his_state_is_will my_state_is_do +#define his_want_state_is_do my_want_state_is_will +#define his_want_state_is_will my_want_state_is_do + +#define his_state_is_dont my_state_is_wont +#define his_state_is_wont my_state_is_dont +#define his_want_state_is_dont my_want_state_is_wont +#define his_want_state_is_wont my_want_state_is_dont + +#define set_his_state_do set_my_state_will +#define set_his_state_will set_my_state_do +#define set_his_want_state_do set_my_want_state_will +#define set_his_want_state_will set_my_want_state_do + +#define set_his_state_dont set_my_state_wont +#define set_his_state_wont set_my_state_dont +#define set_his_want_state_dont set_my_want_state_wont +#define set_his_want_state_wont set_my_want_state_dont + +#define his_will_wont_is_changing my_do_dont_is_changing +#define his_do_dont_is_changing my_will_wont_is_changing diff --git a/libexec/telnetd/ext.h b/libexec/telnetd/ext.h new file mode 100644 index 0000000..19bc0d6 --- /dev/null +++ b/libexec/telnetd/ext.h @@ -0,0 +1,240 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)ext.h 8.2 (Berkeley) 12/15/93 + */ + +/* + * Telnet server variable declarations + */ +extern char options[256]; +extern char do_dont_resp[256]; +extern char will_wont_resp[256]; +extern int linemode; /* linemode on/off */ +#ifdef LINEMODE +extern int uselinemode; /* what linemode to use (on/off) */ +extern int editmode; /* edit modes in use */ +extern int useeditmode; /* edit modes to use */ +extern int alwayslinemode; /* command line option */ +# ifdef KLUDGELINEMODE +extern int lmodetype; /* Client support for linemode */ +# endif /* KLUDGELINEMODE */ +#endif /* LINEMODE */ +extern int flowmode; /* current flow control state */ +extern int restartany; /* restart output on any character state */ +#ifdef DIAGNOSTICS +extern int diagnostic; /* telnet diagnostic capabilities */ +#endif /* DIAGNOSTICS */ +#ifdef BFTPDAEMON +extern int bftpd; /* behave as bftp daemon */ +#endif /* BFTPDAEMON */ +#if defined(SecurID) +extern int require_SecurID; +#endif +#if defined(AUTHENTICATION) +extern int auth_level; +#endif + +extern slcfun slctab[NSLC + 1]; /* slc mapping table */ + +char *terminaltype; + +/* + * I/O data buffers, pointers, and counters. + */ +extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; + +extern char netibuf[BUFSIZ], *netip; + +extern char netobuf[BUFSIZ+NETSLOP], *nfrontp, *nbackp; +extern char *neturg; /* one past last bye of urgent data */ + +extern int pcc, ncc; + +#if defined(CRAY2) && defined(UNICOS5) +extern int unpcc; /* characters left unprocessed by CRAY-2 terminal routine */ +extern char *unptyip; /* pointer to remaining characters in buffer */ +#endif + +extern int pty, net; +extern char *line; +extern int SYNCHing; /* we are in TELNET SYNCH mode */ + +#ifndef P +# ifdef __STDC__ +# define P(x) x +# else +# define P(x) () +# endif +#endif + +extern void + _termstat P((void)), + add_slc P((int, int, int)), + check_slc P((void)), + change_slc P((int, int, int)), + cleanup P((int)), + clientstat P((int, int, int)), + copy_termbuf P((char *, int)), + deferslc P((void)), + defer_terminit P((void)), + do_opt_slc P((unsigned char *, int)), + doeof P((void)), + dooption P((int)), + dontoption P((int)), + edithost P((char *, char *)), + fatal P((int, char *)), + fatalperror P((int, char *)), + get_slc_defaults P((void)), + init_env P((void)), + init_termbuf P((void)), + interrupt P((void)), + localstat P((void)), + flowstat P((void)), + netclear P((void)), + netflush P((void)), +#ifdef DIAGNOSTICS + printoption P((char *, int)), + printdata P((char *, char *, int)), + printsub P((int, unsigned char *, int)), +#endif + ptyflush P((void)), + putchr P((int)), + putf P((char *, char *)), + recv_ayt P((void)), + send_do P((int, int)), + send_dont P((int, int)), + send_slc P((void)), + send_status P((void)), + send_will P((int, int)), + send_wont P((int, int)), + sendbrk P((void)), + sendsusp P((void)), + set_termbuf P((void)), + start_login P((char *, int, char *)), + start_slc P((int)), +#if defined(AUTHENTICATION) + start_slave P((char *)), +#else + start_slave P((char *, int, char *)), +#endif + suboption P((void)), + telrcv P((void)), + ttloop P((void)), + tty_binaryin P((int)), + tty_binaryout P((int)); + +extern int + end_slc P((unsigned char **)), + getnpty P((void)), +#ifndef convex + getpty P((int *)), +#endif + login_tty P((int)), + spcset P((int, cc_t *, cc_t **)), + stilloob P((int)), + terminit P((void)), + termstat P((void)), + tty_flowmode P((void)), + tty_restartany P((void)), + tty_isbinaryin P((void)), + tty_isbinaryout P((void)), + tty_iscrnl P((void)), + tty_isecho P((void)), + tty_isediting P((void)), + tty_islitecho P((void)), + tty_isnewmap P((void)), + tty_israw P((void)), + tty_issofttab P((void)), + tty_istrapsig P((void)), + tty_linemode P((void)); + +extern void + tty_rspeed P((int)), + tty_setecho P((int)), + tty_setedit P((int)), + tty_setlinemode P((int)), + tty_setlitecho P((int)), + tty_setsig P((int)), + tty_setsofttab P((int)), + tty_tspeed P((int)), + willoption P((int)), + wontoption P((int)), + writenet P((unsigned char *, int)); + +#ifdef ENCRYPTION +extern void (*encrypt_output) P((unsigned char *, int)); +extern int (*decrypt_input) P((int)); +extern char *nclearto; +#endif /* ENCRYPTION */ + + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +extern struct { + int + system, /* what the current time is */ + echotoggle, /* last time user entered echo character */ + modenegotiated, /* last time operating mode negotiated */ + didnetreceive, /* last time we read data from network */ + ttypesubopt, /* ttype subopt is received */ + tspeedsubopt, /* tspeed subopt is received */ + environsubopt, /* environ subopt is received */ + oenvironsubopt, /* old environ subopt is received */ + xdisplocsubopt, /* xdisploc subopt is received */ + baseline, /* time started to do timed action */ + gotDM; /* when did we last see a data mark */ +} clocks; + + +#if defined(CRAY2) && defined(UNICOS5) +extern int needtermstat; +#endif + +#ifndef DEFAULT_IM +# ifdef CRAY +# define DEFAULT_IM "\r\n\r\nCray UNICOS (%h) (%t)\r\n\r\r\n\r" +# else +# ifdef sun +# define DEFAULT_IM "\r\n\r\nSunOS UNIX (%h) (%t)\r\n\r\r\n\r" +# else +# ifdef ultrix +# define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r" +# else +# define DEFAULT_IM "\r\n\r\n4.4 BSD UNIX (%h) (%t)\r\n\r\r\n\r" +# endif +# endif +# endif +#endif diff --git a/libexec/telnetd/global.c b/libexec/telnetd/global.c new file mode 100644 index 0000000..af21acc --- /dev/null +++ b/libexec/telnetd/global.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)global.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Allocate global variables. We do this + * by including the header file that defines + * them all as externs, but first we define + * the keyword "extern" to be nothing, so that + * we will actually allocate the space. + */ + +#include "defs.h" +#define extern +#include "ext.h" diff --git a/libexec/telnetd/pathnames.h b/libexec/telnetd/pathnames.h new file mode 100644 index 0000000..c8b0806 --- /dev/null +++ b/libexec/telnetd/pathnames.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#if BSD > 43 + +# include <paths.h> + +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/usr/bin/login" +# endif + +#else + +# define _PATH_TTY "/dev/tty" +# ifndef _PATH_LOGIN +# define _PATH_LOGIN "/bin/login" +# endif + +#endif + +#ifdef BFTPDAEMON +#define BFTPPATH "/usr/ucb/bftp" +#endif /* BFTPDAEMON */ diff --git a/libexec/telnetd/slc.c b/libexec/telnetd/slc.c new file mode 100644 index 0000000..145746a --- /dev/null +++ b/libexec/telnetd/slc.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)slc.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include "telnetd.h" + +#ifdef LINEMODE +/* + * local varibles + */ +static unsigned char *def_slcbuf = (unsigned char *)0; +static int def_slclen = 0; +static int slcchange; /* change to slc is requested */ +static unsigned char *slcptr; /* pointer into slc buffer */ +static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */ + +/* + * send_slc + * + * Write out the current special characters to the client. + */ + void +send_slc() +{ + register int i; + + /* + * Send out list of triplets of special characters + * to client. We only send info on the characters + * that are currently supported. + */ + for (i = 1; i <= NSLC; i++) { + if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT) + continue; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + +} /* end of send_slc */ + +/* + * default_slc + * + * Set pty special characters to all the defaults. + */ + void +default_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { + slctab[i].current.val = slctab[i].defset.val; + if (slctab[i].current.val == (cc_t)(_POSIX_VDISABLE)) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + if (slctab[i].sptr) { + *(slctab[i].sptr) = slctab[i].defset.val; + } + } + slcchange = 1; + +} /* end of default_slc */ +#endif /* LINEMODE */ + +/* + * get_slc_defaults + * + * Initialize the slc mapping table. + */ + void +get_slc_defaults() +{ + register int i; + + init_termbuf(); + + for (i = 1; i <= NSLC; i++) { + slctab[i].defset.flag = + spcset(i, &slctab[i].defset.val, &slctab[i].sptr); + slctab[i].current.flag = SLC_NOSUPPORT; + slctab[i].current.val = 0; + } + +} /* end of get_slc_defaults */ + +#ifdef LINEMODE +/* + * add_slc + * + * Add an slc triplet to the slc buffer. + */ + void +add_slc(func, flag, val) + register char func, flag; + register cc_t val; +{ + + if ((*slcptr++ = (unsigned char)func) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)flag) == 0xff) + *slcptr++ = 0xff; + + if ((*slcptr++ = (unsigned char)val) == 0xff) + *slcptr++ = 0xff; + +} /* end of add_slc */ + +/* + * start_slc + * + * Get ready to process incoming slc's and respond to them. + * + * The parameter getit is non-zero if it is necessary to grab a copy + * of the terminal control structures. + */ + void +start_slc(getit) + register int getit; +{ + + slcchange = 0; + if (getit) + init_termbuf(); + (void) sprintf((char *)slcbuf, "%c%c%c%c", + IAC, SB, TELOPT_LINEMODE, LM_SLC); + slcptr = slcbuf + 4; + +} /* end of start_slc */ + +/* + * end_slc + * + * Finish up the slc negotiation. If something to send, then send it. + */ + int +end_slc(bufp) + register unsigned char **bufp; +{ + register int len; + void netflush(); + + /* + * If a change has occured, store the new terminal control + * structures back to the terminal driver. + */ + if (slcchange) { + set_termbuf(); + } + + /* + * If the pty state has not yet been fully processed and there is a + * deferred slc request from the client, then do not send any + * sort of slc negotiation now. We will respond to the client's + * request very soon. + */ + if (def_slcbuf && (terminit() == 0)) { + return(0); + } + + if (slcptr > (slcbuf + 4)) { + if (bufp) { + *bufp = &slcbuf[4]; + return(slcptr - slcbuf - 4); + } else { + (void) sprintf((char *)slcptr, "%c%c", IAC, SE); + slcptr += 2; + len = slcptr - slcbuf; + writenet(slcbuf, len); + netflush(); /* force it out immediately */ + DIAG(TD_OPTIONS, printsub('>', slcbuf+2, len-2);); + } + } + return (0); + +} /* end of end_slc */ + +/* + * process_slc + * + * Figure out what to do about the client's slc + */ + void +process_slc(func, flag, val) + register unsigned char func, flag; + register cc_t val; +{ + register int hislevel, mylevel, ack; + + /* + * Ensure that we know something about this function + */ + if (func > NSLC) { + add_slc(func, SLC_NOSUPPORT, 0); + return; + } + + /* + * Process the special case requests of 0 SLC_DEFAULT 0 + * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't + * worry about whether the value is actually 0 or not. + */ + if (func == 0) { + if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) { + default_slc(); + send_slc(); + } else if (flag == SLC_VARIABLE) { + send_slc(); + } + return; + } + + /* + * Appears to be a function that we know something about. So + * get on with it and see what we know. + */ + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].current.flag & SLC_LEVELBITS; + ack = flag & SLC_ACK; + /* + * ignore the command if: + * the function value and level are the same as what we already have; + * or the level is the same and the ack bit is set + */ + if (hislevel == mylevel && (val == slctab[func].current.val || ack)) { + return; + } else if (ack) { + /* + * If we get here, we got an ack, but the levels don't match. + * This shouldn't happen. If it does, it is probably because + * we have sent two requests to set a variable without getting + * a response between them, and this is the first response. + * So, ignore it, and wait for the next response. + */ + return; + } else { + change_slc(func, flag, val); + } + +} /* end of process_slc */ + +/* + * change_slc + * + * Process a request to change one of our special characters. + * Compare client's request with what we are capable of supporting. + */ + void +change_slc(func, flag, val) + register char func, flag; + register cc_t val; +{ + register int hislevel, mylevel; + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].defset.flag & SLC_LEVELBITS; + /* + * If client is setting a function to NOSUPPORT + * or DEFAULT, then we can easily and directly + * accomodate the request. + */ + if (hislevel == SLC_NOSUPPORT) { + slctab[func].current.flag = flag; + slctab[func].current.val = (cc_t)_POSIX_VDISABLE; + flag |= SLC_ACK; + add_slc(func, flag, val); + return; + } + if (hislevel == SLC_DEFAULT) { + /* + * Special case here. If client tells us to use + * the default on a function we don't support, then + * return NOSUPPORT instead of what we may have as a + * default level of DEFAULT. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = SLC_NOSUPPORT; + } else { + slctab[func].current.flag = slctab[func].defset.flag; + } + slctab[func].current.val = slctab[func].defset.val; + add_slc(func, slctab[func].current.flag, + slctab[func].current.val); + return; + } + + /* + * Client wants us to change to a new value or he + * is telling us that he can't change to our value. + * Some of the slc's we support and can change, + * some we do support but can't change, + * and others we don't support at all. + * If we can change it then we have a pointer to + * the place to put the new value, so change it, + * otherwise, continue the negotiation. + */ + if (slctab[func].sptr) { + /* + * We can change this one. + */ + slctab[func].current.val = val; + *(slctab[func].sptr) = val; + slctab[func].current.flag = flag; + flag |= SLC_ACK; + slcchange = 1; + add_slc(func, flag, val); + } else { + /* + * It is not possible for us to support this + * request as he asks. + * + * If our level is DEFAULT, then just ack whatever was + * sent. + * + * If he can't change and we can't change, + * then degenerate to NOSUPPORT. + * + * Otherwise we send our level back to him, (CANTCHANGE + * or NOSUPPORT) and if CANTCHANGE, send + * our value as well. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = flag; + slctab[func].current.val = val; + flag |= SLC_ACK; + } else if (hislevel == SLC_CANTCHANGE && + mylevel == SLC_CANTCHANGE) { + flag &= ~SLC_LEVELBITS; + flag |= SLC_NOSUPPORT; + slctab[func].current.flag = flag; + } else { + flag &= ~SLC_LEVELBITS; + flag |= mylevel; + slctab[func].current.flag = flag; + if (mylevel == SLC_CANTCHANGE) { + slctab[func].current.val = + slctab[func].defset.val; + val = slctab[func].current.val; + } + + } + add_slc(func, flag, val); + } + +} /* end of change_slc */ + +#if defined(USE_TERMIO) && (VEOF == VMIN) +cc_t oldeofc = '\004'; +#endif + +/* + * check_slc + * + * Check the special characters in use and notify the client if any have + * changed. Only those characters that are capable of being changed are + * likely to have changed. If a local change occurs, kick the support level + * and flags up to the defaults. + */ + void +check_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { +#if defined(USE_TERMIO) && (VEOF == VMIN) + /* + * In a perfect world this would be a neat little + * function. But in this world, we should not notify + * client of changes to the VEOF char when + * ICANON is off, because it is not representing + * a special character. + */ + if (i == SLC_EOF) { + if (!tty_isediting()) + continue; + else if (slctab[i].sptr) + oldeofc = *(slctab[i].sptr); + } +#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */ + if (slctab[i].sptr && + (*(slctab[i].sptr) != slctab[i].current.val)) { + slctab[i].current.val = *(slctab[i].sptr); + if (*(slctab[i].sptr) == (cc_t)_POSIX_VDISABLE) + slctab[i].current.flag = SLC_NOSUPPORT; + else + slctab[i].current.flag = slctab[i].defset.flag; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + } + +} /* check_slc */ + +/* + * do_opt_slc + * + * Process an slc option buffer. Defer processing of incoming slc's + * until after the terminal state has been processed. Save the first slc + * request that comes along, but discard all others. + * + * ptr points to the beginning of the buffer, len is the length. + */ + void +do_opt_slc(ptr, len) + register unsigned char *ptr; + register int len; +{ + register unsigned char func, flag; + cc_t val; + register unsigned char *end = ptr + len; + + if (terminit()) { /* go ahead */ + while (ptr < end) { + func = *ptr++; + if (ptr >= end) break; + flag = *ptr++; + if (ptr >= end) break; + val = (cc_t)*ptr++; + + process_slc(func, flag, val); + + } + } else { + /* + * save this slc buffer if it is the first, otherwise dump + * it. + */ + if (def_slcbuf == (unsigned char *)0) { + def_slclen = len; + def_slcbuf = (unsigned char *)malloc((unsigned)len); + if (def_slcbuf == (unsigned char *)0) + return; /* too bad */ + bcopy(ptr, def_slcbuf, len); + } + } + +} /* end of do_opt_slc */ + +/* + * deferslc + * + * Do slc stuff that was deferred. + */ + void +deferslc() +{ + if (def_slcbuf) { + start_slc(1); + do_opt_slc(def_slcbuf, def_slclen); + (void) end_slc(0); + free(def_slcbuf); + def_slcbuf = (unsigned char *)0; + def_slclen = 0; + } + +} /* end of deferslc */ + +#endif /* LINEMODE */ diff --git a/libexec/telnetd/state.c b/libexec/telnetd/state.c new file mode 100644 index 0000000..2d327a5 --- /dev/null +++ b/libexec/telnetd/state.c @@ -0,0 +1,1620 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +#endif + +unsigned char doopt[] = { IAC, DO, '%', 'c', 0 }; +unsigned char dont[] = { IAC, DONT, '%', 'c', 0 }; +unsigned char will[] = { IAC, WILL, '%', 'c', 0 }; +unsigned char wont[] = { IAC, WONT, '%', 'c', 0 }; +int not42 = 1; + +/* + * Buffer for sub-options, and macros + * for suboptions buffer manipulations + */ +unsigned char subbuffer[512], *subpointer= subbuffer, *subend= subbuffer; + +#define SB_CLEAR() subpointer = subbuffer +#define SB_TERM() { subend = subpointer; SB_CLEAR(); } +#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ + *subpointer++ = (c); \ + } +#define SB_GET() ((*subpointer++)&0xff) +#define SB_EOF() (subpointer >= subend) +#define SB_LEN() (subend - subpointer) + +#ifdef ENV_HACK +unsigned char *subsave; +#define SB_SAVE() subsave = subpointer; +#define SB_RESTORE() subpointer = subsave; +#endif + + +/* + * State for recv fsm + */ +#define TS_DATA 0 /* base state */ +#define TS_IAC 1 /* look for double IAC's */ +#define TS_CR 2 /* CR-LF ->'s CR */ +#define TS_SB 3 /* throw away begin's... */ +#define TS_SE 4 /* ...end's (suboption negotiation) */ +#define TS_WILL 5 /* will option negotiation */ +#define TS_WONT 6 /* wont " */ +#define TS_DO 7 /* do " */ +#define TS_DONT 8 /* dont " */ + + void +telrcv() +{ + register int c; + static int state = TS_DATA; +#if defined(CRAY2) && defined(UNICOS5) + char *opfrontp = pfrontp; +#endif + + while (ncc > 0) { + if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) + break; + c = *netip++ & 0377, ncc--; +#ifdef ENCRYPTION + if (decrypt_input) + c = (*decrypt_input)(c); +#endif /* ENCRYPTION */ + switch (state) { + + case TS_CR: + state = TS_DATA; + /* Strip off \n or \0 after a \r */ + if ((c == 0) || (c == '\n')) { + break; + } + /* FALL THROUGH */ + + case TS_DATA: + if (c == IAC) { + state = TS_IAC; + break; + } + /* + * We now map \r\n ==> \r for pragmatic reasons. + * Many client implementations send \r\n when + * the user hits the CarriageReturn key. + * + * We USED to map \r\n ==> \n, since \r\n says + * that we want to be in column 1 of the next + * printable line, and \n is the standard + * unix way of saying that (\r is only good + * if CRMOD is set, which it normally is). + */ + if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) { + int nc = *netip; +#ifdef ENCRYPTION + if (decrypt_input) + nc = (*decrypt_input)(nc & 0xff); +#endif /* ENCRYPTION */ +#ifdef LINEMODE + /* + * If we are operating in linemode, + * convert to local end-of-line. + */ + if (linemode && (ncc > 0) && (('\n' == nc) || + ((0 == nc) && tty_iscrnl())) ) { + netip++; ncc--; + c = '\n'; + } else +#endif + { +#ifdef ENCRYPTION + if (decrypt_input) + (void)(*decrypt_input)(-1); +#endif /* ENCRYPTION */ + state = TS_CR; + } + } + *pfrontp++ = c; + break; + + case TS_IAC: +gotiac: switch (c) { + + /* + * Send the process on the pty side an + * interrupt. Do this with a NULL or + * interrupt char; depending on the tty mode. + */ + case IP: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + interrupt(); + break; + + case BREAK: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + sendbrk(); + break; + + /* + * Are You There? + */ + case AYT: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + recv_ayt(); + break; + + /* + * Abort Output + */ + case AO: + { + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + + if (slctab[SLC_AO].sptr && + *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) { + *pfrontp++ = + (unsigned char)*slctab[SLC_AO].sptr; + } + + netclear(); /* clear buffer back */ + *nfrontp++ = IAC; + *nfrontp++ = DM; + neturg = nfrontp-1; /* off by one XXX */ + DIAG(TD_OPTIONS, + printoption("td: send IAC", DM)); + break; + } + + /* + * Erase Character and + * Erase Line + */ + case EC: + case EL: + { + cc_t ch; + + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + ptyflush(); /* half-hearted */ + init_termbuf(); + if (c == EC) + ch = *slctab[SLC_EC].sptr; + else + ch = *slctab[SLC_EL].sptr; + if (ch != (cc_t)(_POSIX_VDISABLE)) + *pfrontp++ = (unsigned char)ch; + break; + } + + /* + * Check for urgent data... + */ + case DM: + DIAG(TD_OPTIONS, + printoption("td: recv IAC", c)); + SYNCHing = stilloob(net); + settimer(gotDM); + break; + + + /* + * Begin option subnegotiation... + */ + case SB: + state = TS_SB; + SB_CLEAR(); + continue; + + case WILL: + state = TS_WILL; + continue; + + case WONT: + state = TS_WONT; + continue; + + case DO: + state = TS_DO; + continue; + + case DONT: + state = TS_DONT; + continue; + case EOR: + if (his_state_is_will(TELOPT_EOR)) + doeof(); + break; + + /* + * Handle RFC 10xx Telnet linemode option additions + * to command stream (EOF, SUSP, ABORT). + */ + case xEOF: + doeof(); + break; + + case SUSP: + sendsusp(); + break; + + case ABORT: + sendbrk(); + break; + + case IAC: + *pfrontp++ = c; + break; + } + state = TS_DATA; + break; + + case TS_SB: + if (c == IAC) { + state = TS_SE; + } else { + SB_ACCUM(c); + } + break; + + case TS_SE: + if (c != SE) { + if (c != IAC) { + /* + * bad form of suboption negotiation. + * handle it in such a way as to avoid + * damage to local state. Parse + * suboption buffer found so far, + * then treat remaining stream as + * another command sequence. + */ + + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(c); + subpointer -= 2; + + SB_TERM(); + suboption(); + state = TS_IAC; + goto gotiac; + } + SB_ACCUM(c); + state = TS_SB; + } else { + /* for DIAGNOSTICS */ + SB_ACCUM(IAC); + SB_ACCUM(SE); + subpointer -= 2; + + SB_TERM(); + suboption(); /* handle sub-option */ + state = TS_DATA; + } + break; + + case TS_WILL: + willoption(c); + state = TS_DATA; + continue; + + case TS_WONT: + wontoption(c); + state = TS_DATA; + continue; + + case TS_DO: + dooption(c); + state = TS_DATA; + continue; + + case TS_DONT: + dontoption(c); + state = TS_DATA; + continue; + + default: + syslog(LOG_ERR, "telnetd: panic state=%d\n", state); + printf("telnetd: panic state=%d\n", state); + exit(1); + } + } +#if defined(CRAY2) && defined(UNICOS5) + if (!linemode) { + char xptyobuf[BUFSIZ+NETSLOP]; + char xbuf2[BUFSIZ]; + register char *cp; + int n = pfrontp - opfrontp, oc; + bcopy(opfrontp, xptyobuf, n); + pfrontp = opfrontp; + pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, + xbuf2, &oc, BUFSIZ); + for (cp = xbuf2; oc > 0; --oc) + if ((*nfrontp++ = *cp++) == IAC) + *nfrontp++ = IAC; + } +#endif /* defined(CRAY2) && defined(UNICOS5) */ +} /* end of telrcv */ + +/* + * The will/wont/do/dont state machines are based on Dave Borman's + * Telnet option processing state machine. + * + * These correspond to the following states: + * my_state = the last negotiated state + * want_state = what I want the state to go to + * want_resp = how many requests I have sent + * All state defaults are negative, and resp defaults to 0. + * + * When initiating a request to change state to new_state: + * + * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { + * do nothing; + * } else { + * want_state = new_state; + * send new_state; + * want_resp++; + * } + * + * When receiving new_state: + * + * if (want_resp) { + * want_resp--; + * if (want_resp && (new_state == my_state)) + * want_resp--; + * } + * if ((want_resp == 0) && (new_state != want_state)) { + * if (ok_to_switch_to new_state) + * want_state = new_state; + * else + * want_resp++; + * send want_state; + * } + * my_state = new_state; + * + * Note that new_state is implied in these functions by the function itself. + * will and do imply positive new_state, wont and dont imply negative. + * + * Finally, there is one catch. If we send a negative response to a + * positive request, my_state will be the positive while want_state will + * remain negative. my_state will revert to negative when the negative + * acknowlegment arrives from the peer. Thus, my_state generally tells + * us not only the last negotiated state, but also tells us what the peer + * wants to be doing as well. It is important to understand this difference + * as we may wish to be processing data streams based on our desired state + * (want_state) or based on what the peer thinks the state is (my_state). + * + * This all works fine because if the peer sends a positive request, the data + * that we receive prior to negative acknowlegment will probably be affected + * by the positive state, and we can process it as such (if we can; if we + * can't then it really doesn't matter). If it is that important, then the + * peer probably should be buffering until this option state negotiation + * is complete. + * + */ + void +send_do(option, init) + int option, init; +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_will(option)) || + his_want_state_is_will(option)) + return; + /* + * Special case for TELOPT_TM: We send a DO, but pretend + * that we sent a DONT, so that we can send more DOs if + * we want to. + */ + if (option == TELOPT_TM) + set_his_want_state_wont(option); + else + set_his_want_state_will(option); + do_dont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)doopt, option); + nfrontp += sizeof (dont) - 2; + + DIAG(TD_OPTIONS, printoption("td: send do", option)); +} + +#ifdef AUTHENTICATION +extern void auth_request(); +#endif +#ifdef LINEMODE +extern void doclientstat(); +#endif +#ifdef ENCRYPTION +extern void encrypt_send_support(); +#endif /* ENCRYPTION */ + + void +willoption(option) + int option; +{ + int changeok = 0; + void (*func)() = 0; + + /* + * process input from peer. + */ + + DIAG(TD_OPTIONS, printoption("td: recv will", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_will(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_wont(option)) { + switch (option) { + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_ECHO: + /* + * See comments below for more info. + */ + not42 = 0; /* looks like a 4.2 system */ + break; + + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * This telnetd implementation does not really + * support timing marks, it just uses them to + * support the kludge linemode stuff. If we + * receive a will or wont TM in response to our + * do TM request that may have been sent to + * determine kludge linemode support, process + * it, otherwise TM should get a negative + * response back. + */ + /* + * Handle the linemode kludge stuff. + * If we are not currently supporting any + * linemode at all, then we assume that this + * is the client telling us to use kludge + * linemode in response to our query. Set the + * linemode type that is to be supported, note + * that the client wishes to use linemode, and + * eat the will TM as though it never arrived. + */ + if (lmodetype < KLUDGE_LINEMODE) { + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == NO_AUTOKLUDGE) { + lmodetype = KLUDGE_OK; + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + /* + * We never respond to a WILL TM, and + * we leave the state WONT. + */ + return; + + case TELOPT_LFLOW: + /* + * If we are going to support flow control + * option, then don't worry peer that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_DEFAULT; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; + case TELOPT_TTYPE: + case TELOPT_SGA: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + case TELOPT_OLD_ENVIRON: + changeok++; + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + changeok++; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + changeok++; + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + func = encrypt_send_support; + changeok++; + break; +#endif /* ENCRYPTION */ + + default: + break; + } + if (changeok) { + set_his_want_state_will(option); + send_do(option, 0); + } else { + do_dont_resp[option]++; + send_dont(option, 0); + } + } else { + /* + * Option processing that should happen when + * we receive conformation of a change in + * state that we had requested. + */ + switch (option) { + case TELOPT_ECHO: + not42 = 0; /* looks like a 4.2 system */ + /* + * Egads, he responded "WILL ECHO". Turn + * it off right now! + */ + send_dont(option, 1); + /* + * "WILL ECHO". Kludge upon kludge! + * A 4.2 client is now echoing user input at + * the tty. This is probably undesireable and + * it should be stopped. The client will + * respond WONT TM to the DO TM that we send to + * check for kludge linemode. When the WONT TM + * arrives, linemode will be turned off and a + * change propogated to the pty. This change + * will cause us to process the new pty state + * in localstat(), which will notice that + * linemode is off and send a WILL ECHO + * so that we are properly in character mode and + * all is well. + */ + break; +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + func = doclientstat; + break; +#endif /* LINEMODE */ + +#ifdef AUTHENTICATION + case TELOPT_AUTHENTICATION: + func = auth_request; + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + func = encrypt_send_support; + break; +#endif /* ENCRYPTION */ + case TELOPT_LFLOW: + func = flowstat; + break; + } + } + } + set_his_state_will(option); + if (func) + (*func)(); +} /* end of willoption */ + + void +send_dont(option, init) + int option, init; +{ + if (init) { + if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) || + his_want_state_is_wont(option)) + return; + set_his_want_state_wont(option); + do_dont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)dont, option); + nfrontp += sizeof (doopt) - 2; + + DIAG(TD_OPTIONS, printoption("td: send dont", option)); +} + + void +wontoption(option) + int option; +{ + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv wont", option)); + + if (do_dont_resp[option]) { + do_dont_resp[option]--; + if (do_dont_resp[option] && his_state_is_wont(option)) + do_dont_resp[option]--; + } + if (do_dont_resp[option] == 0) { + if (his_want_state_is_will(option)) { + /* it is always ok to change to negative state */ + switch (option) { + case TELOPT_ECHO: + not42 = 1; /* doesn't seem to be a 4.2 system */ + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(0); + set_termbuf(); + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * If real linemode is supported, then client is + * asking to turn linemode off. + */ + if (lmodetype != REAL_LINEMODE) + break; + lmodetype = KLUDGE_LINEMODE; +# endif /* KLUDGELINEMODE */ + clientstat(TELOPT_LINEMODE, WONT, 0); + break; +#endif /* LINEMODE */ + + case TELOPT_TM: + /* + * If we get a WONT TM, and had sent a DO TM, + * don't respond with a DONT TM, just leave it + * as is. Short circut the state machine to + * achive this. + */ + set_his_want_state_wont(TELOPT_TM); + return; + + case TELOPT_LFLOW: + /* + * If we are not going to support flow control + * option, then let peer know that we can't + * change the flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + + /* + * For options that we might spin waiting for + * sub-negotiation, if the client turns off the + * option rather than responding to the request, + * we have to treat it here as if we got a response + * to the sub-negotiation, (by updating the timers) + * so that we'll break out of the loop. + */ + case TELOPT_TTYPE: + settimer(ttypesubopt); + break; + + case TELOPT_TSPEED: + settimer(tspeedsubopt); + break; + + case TELOPT_XDISPLOC: + settimer(xdisplocsubopt); + break; + + case TELOPT_OLD_ENVIRON: + settimer(oenvironsubopt); + break; + + case TELOPT_NEW_ENVIRON: + settimer(environsubopt); + break; + + default: + break; + } + set_his_want_state_wont(option); + if (his_state_is_will(option)) + send_dont(option, 0); + } else { + switch (option) { + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + if (lmodetype < NO_AUTOKLUDGE) { + lmodetype = NO_LINEMODE; + clientstat(TELOPT_LINEMODE, WONT, 0); + send_will(TELOPT_SGA, 1); + send_will(TELOPT_ECHO, 1); + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + auth_finished(0, AUTH_REJECT); + break; +#endif + default: + break; + } + } + } + set_his_state_wont(option); + +} /* end of wontoption */ + + void +send_will(option, init) + int option, init; +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_will(option))|| + my_want_state_is_will(option)) + return; + set_my_want_state_will(option); + will_wont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)will, option); + nfrontp += sizeof (doopt) - 2; + + DIAG(TD_OPTIONS, printoption("td: send will", option)); +} + +#if !defined(LINEMODE) || !defined(KLUDGELINEMODE) +/* + * When we get a DONT SGA, we will try once to turn it + * back on. If the other side responds DONT SGA, we + * leave it at that. This is so that when we talk to + * clients that understand KLUDGELINEMODE but not LINEMODE, + * we'll keep them in char-at-a-time mode. + */ +int turn_on_sga = 0; +#endif + + void +dooption(option) + int option; +{ + int changeok = 0; + + /* + * Process client input. + */ + + DIAG(TD_OPTIONS, printoption("td: recv do", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_will(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_wont(option))) { + switch (option) { + case TELOPT_ECHO: +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if (lmodetype == NO_LINEMODE) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(1); + set_termbuf(); + } + changeok++; + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we must + * process an incoming do SGA for linemode + * purposes. + */ + if (lmodetype == KLUDGE_LINEMODE) { + /* + * Receipt of "do SGA" in kludge + * linemode is the peer asking us to + * turn off linemode. Make note of + * the request. + */ + clientstat(TELOPT_LINEMODE, WONT, 0); + /* + * If linemode did not get turned off + * then don't tell peer that we did. + * Breaking here forces a wont SGA to + * be returned. + */ + if (linemode) + break; + } +#else + turn_on_sga = 0; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + changeok++; + break; + + case TELOPT_STATUS: + changeok++; + break; + + case TELOPT_TM: + /* + * Special case for TM. We send a WILL, but + * pretend we sent a WONT. + */ + send_will(option, 0); + set_my_want_state_wont(option); + set_my_state_wont(option); + return; + + case TELOPT_LOGOUT: + /* + * When we get a LOGOUT option, respond + * with a WILL LOGOUT, make sure that + * it gets written out to the network, + * and then just go away... + */ + set_my_want_state_will(TELOPT_LOGOUT); + send_will(TELOPT_LOGOUT, 0); + set_my_state_will(TELOPT_LOGOUT); + (void)netflush(); + cleanup(0); + /* NOT REACHED */ + break; + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + changeok++; + break; +#endif /* ENCRYPTION */ + case TELOPT_LINEMODE: + case TELOPT_TTYPE: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_LFLOW: + case TELOPT_XDISPLOC: +#ifdef TELOPT_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: + default: + break; + } + if (changeok) { + set_my_want_state_will(option); + send_will(option, 0); + } else { + will_wont_resp[option]++; + send_wont(option, 0); + } + } + set_my_state_will(option); + +} /* end of dooption */ + + void +send_wont(option, init) + int option, init; +{ + if (init) { + if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) || + my_want_state_is_wont(option)) + return; + set_my_want_state_wont(option); + will_wont_resp[option]++; + } + (void) sprintf(nfrontp, (char *)wont, option); + nfrontp += sizeof (wont) - 2; + + DIAG(TD_OPTIONS, printoption("td: send wont", option)); +} + + void +dontoption(option) + int option; +{ + /* + * Process client input. + */ + + + DIAG(TD_OPTIONS, printoption("td: recv dont", option)); + + if (will_wont_resp[option]) { + will_wont_resp[option]--; + if (will_wont_resp[option] && my_state_is_wont(option)) + will_wont_resp[option]--; + } + if ((will_wont_resp[option] == 0) && (my_want_state_is_will(option))) { + switch (option) { + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(0); + set_termbuf(); + break; + + case TELOPT_ECHO: /* we should stop echoing */ +#ifdef LINEMODE +# ifdef KLUDGELINEMODE + if ((lmodetype != REAL_LINEMODE) && + (lmodetype != KLUDGE_LINEMODE)) +# else + if (his_state_is_wont(TELOPT_LINEMODE)) +# endif +#endif + { + init_termbuf(); + tty_setecho(0); + set_termbuf(); + } + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we + * must process an incoming do SGA for + * linemode purposes. + */ + if ((lmodetype == KLUDGE_LINEMODE) || + (lmodetype == KLUDGE_OK)) { + /* + * The client is asking us to turn + * linemode on. + */ + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + /* + * If we did not turn line mode on, + * then what do we say? Will SGA? + * This violates design of telnet. + * Gross. Very Gross. + */ + } + break; +#else + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + set_my_state_wont(option); + if (turn_on_sga ^= 1) + send_will(option, 1); + return; +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + default: + break; + } + + set_my_want_state_wont(option); + if (my_state_is_will(option)) + send_wont(option, 0); + } + set_my_state_wont(option); + +} /* end of dontoption */ + +#ifdef ENV_HACK +int env_ovar = -1; +int env_ovalue = -1; +#else /* ENV_HACK */ +# define env_ovar OLD_ENV_VAR +# define env_ovalue OLD_ENV_VALUE +#endif /* ENV_HACK */ + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type is + * Linemode + * Window size + * Terminal speed + */ + void +suboption() +{ + register int subchar; + + DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);}); + + subchar = SB_GET(); + switch (subchar) { + case TELOPT_TSPEED: { + register int xspeed, rspeed; + + if (his_state_is_wont(TELOPT_TSPEED)) /* Ignore if option disabled */ + break; + + settimer(tspeedsubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + + xspeed = atoi((char *)subpointer); + + while (SB_GET() != ',' && !SB_EOF()); + if (SB_EOF()) + return; + + rspeed = atoi((char *)subpointer); + clientstat(TELOPT_TSPEED, xspeed, rspeed); + + break; + + } /* end of case TELOPT_TSPEED */ + + case TELOPT_TTYPE: { /* Yaaaay! */ + static char terminalname[41]; + + if (his_state_is_wont(TELOPT_TTYPE)) /* Ignore if option disabled */ + break; + settimer(ttypesubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) { + return; /* ??? XXX but, this is the most robust */ + } + + terminaltype = terminalname; + + while ((terminaltype < (terminalname + sizeof terminalname-1)) && + !SB_EOF()) { + register int c; + + c = SB_GET(); + if (isupper(c)) { + c = tolower(c); + } + *terminaltype++ = c; /* accumulate name */ + } + *terminaltype = 0; + terminaltype = terminalname; + break; + } /* end of case TELOPT_TTYPE */ + + case TELOPT_NAWS: { + register int xwinsize, ywinsize; + + if (his_state_is_wont(TELOPT_NAWS)) /* Ignore if option disabled */ + break; + + if (SB_EOF()) + return; + xwinsize = SB_GET() << 8; + if (SB_EOF()) + return; + xwinsize |= SB_GET(); + if (SB_EOF()) + return; + ywinsize = SB_GET() << 8; + if (SB_EOF()) + return; + ywinsize |= SB_GET(); + clientstat(TELOPT_NAWS, xwinsize, ywinsize); + + break; + + } /* end of case TELOPT_NAWS */ + +#ifdef LINEMODE + case TELOPT_LINEMODE: { + register int request; + + if (his_state_is_wont(TELOPT_LINEMODE)) /* Ignore if option disabled */ + break; + /* + * Process linemode suboptions. + */ + if (SB_EOF()) + break; /* garbage was sent */ + request = SB_GET(); /* get will/wont */ + + if (SB_EOF()) + break; /* another garbage check */ + + if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ + /* + * Process suboption buffer of slc's + */ + start_slc(1); + do_opt_slc(subpointer, subend - subpointer); + (void) end_slc(0); + break; + } else if (request == LM_MODE) { + if (SB_EOF()) + return; + useeditmode = SB_GET(); /* get mode flag */ + clientstat(LM_MODE, 0, 0); + break; + } + + if (SB_EOF()) + break; + switch (SB_GET()) { /* what suboption? */ + case LM_FORWARDMASK: + /* + * According to spec, only server can send request for + * forwardmask, and client can only return a positive response. + * So don't worry about it. + */ + + default: + break; + } + break; + } /* end of case TELOPT_LINEMODE */ +#endif + case TELOPT_STATUS: { + int mode; + + if (SB_EOF()) + break; + mode = SB_GET(); + switch (mode) { + case TELQUAL_SEND: + if (my_state_is_will(TELOPT_STATUS)) + send_status(); + break; + + case TELQUAL_IS: + break; + + default: + break; + } + break; + } /* end of case TELOPT_STATUS */ + + case TELOPT_XDISPLOC: { + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + settimer(xdisplocsubopt); + subpointer[SB_LEN()] = '\0'; + (void)setenv("DISPLAY", (char *)subpointer, 1); + break; + } /* end of case TELOPT_XDISPLOC */ + +#ifdef TELOPT_NEW_ENVIRON + case TELOPT_NEW_ENVIRON: +#endif + case TELOPT_OLD_ENVIRON: { + register int c; + register char *cp, *varp, *valp; + + if (SB_EOF()) + return; + c = SB_GET(); + if (c == TELQUAL_IS) { + if (subchar == TELOPT_OLD_ENVIRON) + settimer(oenvironsubopt); + else + settimer(environsubopt); + } else if (c != TELQUAL_INFO) { + return; + } + +#ifdef TELOPT_NEW_ENVIRON + if (subchar == TELOPT_NEW_ENVIRON) { + while (!SB_EOF()) { + c = SB_GET(); + if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR)) + break; + } + } else +#endif + { +#ifdef ENV_HACK + /* + * We only want to do this if we haven't already decided + * whether or not the other side has its VALUE and VAR + * reversed. + */ + if (env_ovar < 0) { + register int last = -1; /* invalid value */ + int empty = 0; + int got_var = 0, got_value = 0, got_uservar = 0; + + /* + * The other side might have its VALUE and VAR values + * reversed. To be interoperable, we need to determine + * which way it is. If the first recognized character + * is a VAR or VALUE, then that will tell us what + * type of client it is. If the fist recognized + * character is a USERVAR, then we continue scanning + * the suboption looking for two consecutive + * VAR or VALUE fields. We should not get two + * consecutive VALUE fields, so finding two + * consecutive VALUE or VAR fields will tell us + * what the client is. + */ + SB_SAVE(); + while (!SB_EOF()) { + c = SB_GET(); + switch(c) { + case OLD_ENV_VAR: + if (last < 0 || last == OLD_ENV_VAR + || (empty && (last == OLD_ENV_VALUE))) + goto env_ovar_ok; + got_var++; + last = OLD_ENV_VAR; + break; + case OLD_ENV_VALUE: + if (last < 0 || last == OLD_ENV_VALUE + || (empty && (last == OLD_ENV_VAR))) + goto env_ovar_wrong; + got_value++; + last = OLD_ENV_VALUE; + break; + case ENV_USERVAR: + /* count strings of USERVAR as one */ + if (last != ENV_USERVAR) + got_uservar++; + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + last = ENV_USERVAR; + break; + case ENV_ESC: + if (!SB_EOF()) + c = SB_GET(); + /* FALL THROUGH */ + default: + empty = 0; + continue; + } + empty = 1; + } + if (empty) { + if (last == OLD_ENV_VALUE) + goto env_ovar_ok; + if (last == OLD_ENV_VAR) + goto env_ovar_wrong; + } + /* + * Ok, the first thing was a USERVAR, and there + * are not two consecutive VAR or VALUE commands, + * and none of the VAR or VALUE commands are empty. + * If the client has sent us a well-formed option, + * then the number of VALUEs received should always + * be less than or equal to the number of VARs and + * USERVARs received. + * + * If we got exactly as many VALUEs as VARs and + * USERVARs, the client has the same definitions. + * + * If we got exactly as many VARs as VALUEs and + * USERVARS, the client has reversed definitions. + */ + if (got_uservar + got_var == got_value) { + env_ovar_ok: + env_ovar = OLD_ENV_VAR; + env_ovalue = OLD_ENV_VALUE; + } else if (got_uservar + got_value == got_var) { + env_ovar_wrong: + env_ovar = OLD_ENV_VALUE; + env_ovalue = OLD_ENV_VAR; + DIAG(TD_OPTIONS, {sprintf(nfrontp, + "ENVIRON VALUE and VAR are reversed!\r\n"); + nfrontp += strlen(nfrontp);}); + + } + } + SB_RESTORE(); +#endif + + while (!SB_EOF()) { + c = SB_GET(); + if ((c == env_ovar) || (c == ENV_USERVAR)) + break; + } + } + + if (SB_EOF()) + return; + + cp = varp = (char *)subpointer; + valp = 0; + + while (!SB_EOF()) { + c = SB_GET(); + if (subchar == TELOPT_OLD_ENVIRON) { + if (c == env_ovar) + c = NEW_ENV_VAR; + else if (c == env_ovalue) + c = NEW_ENV_VALUE; + } + switch (c) { + + case NEW_ENV_VALUE: + *cp = '\0'; + cp = valp = (char *)subpointer; + break; + + case NEW_ENV_VAR: + case ENV_USERVAR: + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + cp = varp = (char *)subpointer; + valp = 0; + break; + + case ENV_ESC: + if (SB_EOF()) + break; + c = SB_GET(); + /* FALL THROUGH */ + default: + *cp++ = c; + break; + } + } + *cp = '\0'; + if (valp) + (void)setenv(varp, valp, 1); + else + unsetenv(varp); + break; + } /* end of case TELOPT_NEW_ENVIRON */ +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + if (SB_EOF()) + break; + switch(SB_GET()) { + case TELQUAL_SEND: + case TELQUAL_REPLY: + /* + * These are sent by us and cannot be sent by + * the client. + */ + break; + case TELQUAL_IS: + auth_is(subpointer, SB_LEN()); + break; + case TELQUAL_NAME: + auth_name(subpointer, SB_LEN()); + break; + } + break; +#endif +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + if (SB_EOF()) + break; + switch(SB_GET()) { + case ENCRYPT_SUPPORT: + encrypt_support(subpointer, SB_LEN()); + break; + case ENCRYPT_IS: + encrypt_is(subpointer, SB_LEN()); + break; + case ENCRYPT_REPLY: + encrypt_reply(subpointer, SB_LEN()); + break; + case ENCRYPT_START: + encrypt_start(subpointer, SB_LEN()); + break; + case ENCRYPT_END: + encrypt_end(); + break; + case ENCRYPT_REQSTART: + encrypt_request_start(subpointer, SB_LEN()); + break; + case ENCRYPT_REQEND: + /* + * We can always send an REQEND so that we cannot + * get stuck encrypting. We should only get this + * if we have been able to get in the correct mode + * anyhow. + */ + encrypt_request_end(); + break; + case ENCRYPT_ENC_KEYID: + encrypt_enc_keyid(subpointer, SB_LEN()); + break; + case ENCRYPT_DEC_KEYID: + encrypt_dec_keyid(subpointer, SB_LEN()); + break; + default: + break; + } + break; +#endif /* ENCRYPTION */ + + default: + break; + } /* end of switch */ + +} /* end of suboption */ + + void +doclientstat() +{ + clientstat(TELOPT_LINEMODE, WILL, 0); +} + +#define ADD(c) *ncp++ = c; +#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } + void +send_status() +{ + unsigned char statusbuf[256]; + register unsigned char *ncp; + register unsigned char i; + + ncp = statusbuf; + + netflush(); /* get rid of anything waiting to go out */ + + ADD(IAC); + ADD(SB); + ADD(TELOPT_STATUS); + ADD(TELQUAL_IS); + + /* + * We check the want_state rather than the current state, + * because if we received a DO/WILL for an option that we + * don't support, and the other side didn't send a DONT/WONT + * in response to our WONT/DONT, then the "state" will be + * WILL/DO, and the "want_state" will be WONT/DONT. We + * need to go by the latter. + */ + for (i = 0; i < (unsigned char)NTELOPTS; i++) { + if (my_want_state_is_will(i)) { + ADD(WILL); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + if (his_want_state_is_will(i)) { + ADD(DO); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + } + + if (his_want_state_is_will(TELOPT_LFLOW)) { + ADD(SB); + ADD(TELOPT_LFLOW); + if (flowmode) { + ADD(LFLOW_ON); + } else { + ADD(LFLOW_OFF); + } + ADD(SE); + + if (restartany >= 0) { + ADD(SB) + ADD(TELOPT_LFLOW); + if (restartany) { + ADD(LFLOW_RESTART_ANY); + } else { + ADD(LFLOW_RESTART_XON); + } + ADD(SE) + ADD(SB); + } + } + +#ifdef LINEMODE + if (his_want_state_is_will(TELOPT_LINEMODE)) { + unsigned char *cp, *cpe; + int len; + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_MODE); + ADD_DATA(editmode); + if (editmode == IAC) + ADD(IAC); + ADD(SE); + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_SLC); + start_slc(0); + send_slc(); + len = end_slc(&cp); + for (cpe = cp + len; cp < cpe; cp++) + ADD_DATA(*cp); + ADD(SE); + } +#endif /* LINEMODE */ + + ADD(IAC); + ADD(SE); + + writenet(statusbuf, ncp - statusbuf); + netflush(); /* Send it on its way */ + + DIAG(TD_OPTIONS, + {printsub('>', statusbuf, ncp - statusbuf); netflush();}); +} diff --git a/libexec/telnetd/sys_term.c b/libexec/telnetd/sys_term.c new file mode 100644 index 0000000..1e50216 --- /dev/null +++ b/libexec/telnetd/sys_term.c @@ -0,0 +1,2135 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)sys_term.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#include "pathnames.h" + +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +#endif + +#if defined(CRAY) || defined(__hpux) +# define PARENT_DOES_UTMP +#endif + +#ifdef NEWINIT +#include <initreq.h> +int utmp_len = MAXHOSTNAMELEN; /* sizeof(init_request.host) */ +#else /* NEWINIT*/ +# ifdef UTMPX +# include <utmpx.h> +struct utmpx wtmp; +# else +# include <utmp.h> +struct utmp wtmp; +# endif /* UTMPX */ + +int utmp_len = sizeof(wtmp.ut_host); +# ifndef PARENT_DOES_UTMP +char wtmpf[] = "/usr/adm/wtmp"; +char utmpf[] = "/etc/utmp"; +# else /* PARENT_DOES_UTMP */ +char wtmpf[] = "/etc/wtmp"; +# endif /* PARENT_DOES_UTMP */ + +# ifdef CRAY +#include <tmpdir.h> +#include <sys/wait.h> +# if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) + /* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS +# endif + +# if defined(_SC_CRAY_SECURE_SYS) +#include <sys/sysv.h> +#include <sys/secstat.h> +extern int secflag; +extern struct sysv sysv; +# endif /* _SC_CRAY_SECURE_SYS */ +# endif /* CRAY */ +#endif /* NEWINIT */ + +#ifdef STREAMSPTY +#include <sac.h> +#include <sys/stropts.h> +#endif + +#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) +#define SCMPN(a, b) strncmp(a, b, sizeof(a)) + +#ifdef STREAMS +#include <sys/stream.h> +#endif +#ifdef __hpux +#include <sys/resource.h> +#include <sys/proc.h> +#endif +#include <sys/tty.h> +#ifdef t_erase +#undef t_erase +#undef t_kill +#undef t_intrc +#undef t_quitc +#undef t_startc +#undef t_stopc +#undef t_eofc +#undef t_brkc +#undef t_suspc +#undef t_dsuspc +#undef t_rprntc +#undef t_flushc +#undef t_werasc +#undef t_lnextc +#endif + +#if defined(UNICOS5) && defined(CRAY2) && !defined(EXTPROC) +# define EXTPROC 0400 +#endif + +#ifndef USE_TERMIO +struct termbuf { + struct sgttyb sg; + struct tchars tc; + struct ltchars ltc; + int state; + int lflags; +} termbuf, termbuf2; +# define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val) +# define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val) +# define cfgetospeed(tp) (tp)->sg.sg_ospeed +# define cfgetispeed(tp) (tp)->sg.sg_ispeed +#else /* USE_TERMIO */ +# ifdef SYSV_TERMIO +# define termios termio +# endif +# ifndef TCSANOW +# ifdef TCSETS +# define TCSANOW TCSETS +# define TCSADRAIN TCSETSW +# define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) +# else +# ifdef TCSETA +# define TCSANOW TCSETA +# define TCSADRAIN TCSETAW +# define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) +# else +# define TCSANOW TIOCSETA +# define TCSADRAIN TIOCSETAW +# define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) +# endif +# endif +# define tcsetattr(f, a, t) ioctl(f, a, t) +# define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) +# ifdef CIBAUD +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \ + (tp)->c_cflag |= ((val)<<IBSHIFT) +# define cfgetispeed(tp) (((tp)->c_cflag & CIBAUD)>>IBSHIFT) +# else +# define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ + (tp)->c_cflag |= (val) +# define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) +# endif +# endif /* TCSANOW */ +struct termios termbuf, termbuf2; /* pty control structure */ +# ifdef STREAMSPTY +int ttyfd = -1; +# endif +#endif /* USE_TERMIO */ + +/* + * init_termbuf() + * copy_termbuf(cp) + * set_termbuf() + * + * These three routines are used to get and set the "termbuf" structure + * to and from the kernel. init_termbuf() gets the current settings. + * copy_termbuf() hands in a new "termbuf" to write to the kernel, and + * set_termbuf() writes the structure into the kernel. + */ + + void +init_termbuf() +{ +#ifndef USE_TERMIO + (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg); + (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc); + (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc); +# ifdef TIOCGSTATE + (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state); +# endif +#else +# ifdef STREAMSPTY + (void) tcgetattr(ttyfd, &termbuf); +# else + (void) tcgetattr(pty, &termbuf); +# endif +#endif + termbuf2 = termbuf; +} + +#if defined(LINEMODE) && defined(TIOCPKT_IOCTL) + void +copy_termbuf(cp, len) + char *cp; + int len; +{ + if (len > sizeof(termbuf)) + len = sizeof(termbuf); + bcopy(cp, (char *)&termbuf, len); + termbuf2 = termbuf; +} +#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ + + void +set_termbuf() +{ + /* + * Only make the necessary changes. + */ +#ifndef USE_TERMIO + if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) + (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg); + if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) + (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); + if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, + sizeof(termbuf.ltc))) + (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc); + if (termbuf.lflags != termbuf2.lflags) + (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); +#else /* USE_TERMIO */ + if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) +# ifdef STREAMSPTY + (void) tcsetattr(ttyfd, TCSANOW, &termbuf); +# else + (void) tcsetattr(pty, TCSANOW, &termbuf); +# endif +# if defined(CRAY2) && defined(UNICOS5) + needtermstat = 1; +# endif +#endif /* USE_TERMIO */ +} + + +/* + * spcset(func, valp, valpp) + * + * This function takes various special characters (func), and + * sets *valp to the current value of that character, and + * *valpp to point to where in the "termbuf" structure that + * value is kept. + * + * It returns the SLC_ level of support for this function. + */ + +#ifndef USE_TERMIO + int +spcset(func, valp, valpp) + int func; + cc_t *valp; + cc_t **valpp; +{ + switch(func) { + case SLC_EOF: + *valp = termbuf.tc.t_eofc; + *valpp = (cc_t *)&termbuf.tc.t_eofc; + return(SLC_VARIABLE); + case SLC_EC: + *valp = termbuf.sg.sg_erase; + *valpp = (cc_t *)&termbuf.sg.sg_erase; + return(SLC_VARIABLE); + case SLC_EL: + *valp = termbuf.sg.sg_kill; + *valpp = (cc_t *)&termbuf.sg.sg_kill; + return(SLC_VARIABLE); + case SLC_IP: + *valp = termbuf.tc.t_intrc; + *valpp = (cc_t *)&termbuf.tc.t_intrc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + *valp = termbuf.tc.t_quitc; + *valpp = (cc_t *)&termbuf.tc.t_quitc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: + *valp = termbuf.tc.t_startc; + *valpp = (cc_t *)&termbuf.tc.t_startc; + return(SLC_VARIABLE); + case SLC_XOFF: + *valp = termbuf.tc.t_stopc; + *valpp = (cc_t *)&termbuf.tc.t_stopc; + return(SLC_VARIABLE); + case SLC_AO: + *valp = termbuf.ltc.t_flushc; + *valpp = (cc_t *)&termbuf.ltc.t_flushc; + return(SLC_VARIABLE); + case SLC_SUSP: + *valp = termbuf.ltc.t_suspc; + *valpp = (cc_t *)&termbuf.ltc.t_suspc; + return(SLC_VARIABLE); + case SLC_EW: + *valp = termbuf.ltc.t_werasc; + *valpp = (cc_t *)&termbuf.ltc.t_werasc; + return(SLC_VARIABLE); + case SLC_RP: + *valp = termbuf.ltc.t_rprntc; + *valpp = (cc_t *)&termbuf.ltc.t_rprntc; + return(SLC_VARIABLE); + case SLC_LNEXT: + *valp = termbuf.ltc.t_lnextc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_FORW1: + *valp = termbuf.tc.t_brkc; + *valpp = (cc_t *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_BRK: + case SLC_SYNCH: + case SLC_AYT: + case SLC_EOR: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_DEFAULT); + default: + *valp = (cc_t)0; + *valpp = (cc_t *)0; + return(SLC_NOSUPPORT); + } +} + +#else /* USE_TERMIO */ + + int +spcset(func, valp, valpp) + int func; + cc_t *valp; + cc_t **valpp; +{ + +#define setval(a, b) *valp = termbuf.c_cc[a]; \ + *valpp = &termbuf.c_cc[a]; \ + return(b); +#define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT); + + switch(func) { + case SLC_EOF: + setval(VEOF, SLC_VARIABLE); + case SLC_EC: + setval(VERASE, SLC_VARIABLE); + case SLC_EL: + setval(VKILL, SLC_VARIABLE); + case SLC_IP: + setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: +#ifdef VSTART + setval(VSTART, SLC_VARIABLE); +#else + defval(0x13); +#endif + case SLC_XOFF: +#ifdef VSTOP + setval(VSTOP, SLC_VARIABLE); +#else + defval(0x11); +#endif + case SLC_EW: +#ifdef VWERASE + setval(VWERASE, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_RP: +#ifdef VREPRINT + setval(VREPRINT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_LNEXT: +#ifdef VLNEXT + setval(VLNEXT, SLC_VARIABLE); +#else + defval(0); +#endif + case SLC_AO: +#if !defined(VDISCARD) && defined(VFLUSHO) +# define VDISCARD VFLUSHO +#endif +#ifdef VDISCARD + setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); +#else + defval(0); +#endif + case SLC_SUSP: +#ifdef VSUSP + setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN); +#else + defval(0); +#endif +#ifdef VEOL + case SLC_FORW1: + setval(VEOL, SLC_VARIABLE); +#endif +#ifdef VEOL2 + case SLC_FORW2: + setval(VEOL2, SLC_VARIABLE); +#endif + case SLC_AYT: +#ifdef VSTATUS + setval(VSTATUS, SLC_VARIABLE); +#else + defval(0); +#endif + + case SLC_BRK: + case SLC_SYNCH: + case SLC_EOR: + defval(0); + + default: + *valp = 0; + *valpp = 0; + return(SLC_NOSUPPORT); + } +} +#endif /* USE_TERMIO */ + +#ifdef CRAY +/* + * getnpty() + * + * Return the number of pty's configured into the system. + */ + int +getnpty() +{ +#ifdef _SC_CRAY_NPTY + int numptys; + + if ((numptys = sysconf(_SC_CRAY_NPTY)) != -1) + return numptys; + else +#endif /* _SC_CRAY_NPTY */ + return 128; +} +#endif /* CRAY */ + +#ifndef convex +/* + * getpty() + * + * Allocate a pty. As a side effect, the external character + * array "line" contains the name of the slave side. + * + * Returns the file descriptor of the opened pty. + */ +#ifndef __GNUC__ +char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#else +static char Xline[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +char *line = Xline; +#endif +#ifdef CRAY +char *myline = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +#endif /* CRAY */ + + int +getpty(ptynum) +int *ptynum; +{ + register int p; +#ifdef STREAMSPTY + int t; + char *ptsname(); + + p = open("/dev/ptmx", 2); + if (p > 0) { + grantpt(p); + unlockpt(p); + strcpy(line, ptsname(p)); + return(p); + } + +#else /* ! STREAMSPTY */ +#ifndef CRAY + register char *cp, *p1, *p2; + register int i; +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + int dummy; +#endif + +#ifndef __hpux + (void) sprintf(line, "/dev/ptyXX"); + p1 = &line[8]; + p2 = &line[9]; +#else + (void) sprintf(line, "/dev/ptym/ptyXX"); + p1 = &line[13]; + p2 = &line[14]; +#endif + + for (cp = "pqrstuvwxyzPQRST"; *cp; cp++) { + struct stat stb; + + *p1 = *cp; + *p2 = '0'; + /* + * This stat() check is just to keep us from + * looping through all 256 combinations if there + * aren't that many ptys available. + */ + if (stat(line, &stb) < 0) + break; + for (i = 0; i < 16; i++) { + *p2 = "0123456789abcdef"[i]; + p = open(line, 2); + if (p > 0) { +#ifndef __hpux + line[5] = 't'; +#else + for (p1 = &line[8]; *p1; p1++) + *p1 = *(p1+1); + line[9] = 't'; +#endif + chown(line, 0, 0); + chmod(line, 0600); +#if defined(sun) && defined(TIOCGPGRP) && BSD < 199207 + if (ioctl(p, TIOCGPGRP, &dummy) == 0 + || errno != EIO) { + chmod(line, 0666); + close(p); + line[5] = 'p'; + } else +#endif /* defined(sun) && defined(TIOCGPGRP) && BSD < 199207 */ + return(p); + } + } + } +#else /* CRAY */ + extern lowpty, highpty; + struct stat sb; + + for (*ptynum = lowpty; *ptynum <= highpty; (*ptynum)++) { + (void) sprintf(myline, "/dev/pty/%03d", *ptynum); + p = open(myline, 2); + if (p < 0) + continue; + (void) sprintf(line, "/dev/ttyp%03d", *ptynum); + /* + * Here are some shenanigans to make sure that there + * are no listeners lurking on the line. + */ + if(stat(line, &sb) < 0) { + (void) close(p); + continue; + } + if(sb.st_uid || sb.st_gid || sb.st_mode != 0600) { + chown(line, 0, 0); + chmod(line, 0600); + (void)close(p); + p = open(myline, 2); + if (p < 0) + continue; + } + /* + * Now it should be safe...check for accessability. + */ + if (access(line, 6) == 0) + return(p); + else { + /* no tty side to pty so skip it */ + (void) close(p); + } + } +#endif /* CRAY */ +#endif /* STREAMSPTY */ + return(-1); +} +#endif /* convex */ + +#ifdef LINEMODE +/* + * tty_flowmode() Find out if flow control is enabled or disabled. + * tty_linemode() Find out if linemode (external processing) is enabled. + * tty_setlinemod(on) Turn on/off linemode. + * tty_isecho() Find out if echoing is turned on. + * tty_setecho(on) Enable/disable character echoing. + * tty_israw() Find out if terminal is in RAW mode. + * tty_binaryin(on) Turn on/off BINARY on input. + * tty_binaryout(on) Turn on/off BINARY on output. + * tty_isediting() Find out if line editing is enabled. + * tty_istrapsig() Find out if signal trapping is enabled. + * tty_setedit(on) Turn on/off line editing. + * tty_setsig(on) Turn on/off signal trapping. + * tty_issofttab() Find out if tab expansion is enabled. + * tty_setsofttab(on) Turn on/off soft tab expansion. + * tty_islitecho() Find out if typed control chars are echoed literally + * tty_setlitecho() Turn on/off literal echo of control chars + * tty_tspeed(val) Set transmit speed to val. + * tty_rspeed(val) Set receive speed to val. + */ + +#ifdef convex +static int linestate; +#endif + + int +tty_linemode() +{ +#ifndef convex +#ifndef USE_TERMIO + return(termbuf.state & TS_EXTPROC); +#else + return(termbuf.c_lflag & EXTPROC); +#endif +#else + return(linestate); +#endif +} + + void +tty_setlinemode(on) + int on; +{ +#ifdef TIOCEXT +# ifndef convex + set_termbuf(); +# else + linestate = on; +# endif + (void) ioctl(pty, TIOCEXT, (char *)&on); +# ifndef convex + init_termbuf(); +# endif +#else /* !TIOCEXT */ +# ifdef EXTPROC + if (on) + termbuf.c_lflag |= EXTPROC; + else + termbuf.c_lflag &= ~EXTPROC; +# endif +#endif /* TIOCEXT */ +} +#endif /* LINEMODE */ + + int +tty_isecho() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & ECHO); +#else + return (termbuf.c_lflag & ECHO); +#endif +} + + int +tty_flowmode() +{ +#ifndef USE_TERMIO + return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0); +#else + return((termbuf.c_iflag & IXON) ? 1 : 0); +#endif +} + + int +tty_restartany() +{ +#ifndef USE_TERMIO +# ifdef DECCTQ + return((termbuf.lflags & DECCTQ) ? 0 : 1); +# else + return(-1); +# endif +#else + return((termbuf.c_iflag & IXANY) ? 1 : 0); +#endif +} + + void +tty_setecho(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= ECHO|CRMOD; + else + termbuf.sg.sg_flags &= ~(ECHO|CRMOD); +#else + if (on) + termbuf.c_lflag |= ECHO; + else + termbuf.c_lflag &= ~ECHO; +#endif +} + + int +tty_israw() +{ +#ifndef USE_TERMIO + return(termbuf.sg.sg_flags & RAW); +#else + return(!(termbuf.c_lflag & ICANON)); +#endif +} + +#if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + int +tty_setraw(on) +{ +# ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= RAW; + else + termbuf.sg.sg_flags &= ~RAW; +# else + if (on) + termbuf.c_lflag &= ~ICANON; + else + termbuf.c_lflag |= ICANON; +# endif +} +#endif + + void +tty_binaryin(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LPASS8; + else + termbuf.lflags &= ~LPASS8; +#else + if (on) { + termbuf.c_iflag &= ~ISTRIP; + } else { + termbuf.c_iflag |= ISTRIP; + } +#endif +} + + void +tty_binaryout(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LLITOUT; + else + termbuf.lflags &= ~LLITOUT; +#else + if (on) { + termbuf.c_cflag &= ~(CSIZE|PARENB); + termbuf.c_cflag |= CS8; + termbuf.c_oflag &= ~OPOST; + } else { + termbuf.c_cflag &= ~CSIZE; + termbuf.c_cflag |= CS7|PARENB; + termbuf.c_oflag |= OPOST; + } +#endif +} + + int +tty_isbinaryin() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LPASS8); +#else + return(!(termbuf.c_iflag & ISTRIP)); +#endif +} + + int +tty_isbinaryout() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LLITOUT); +#else + return(!(termbuf.c_oflag&OPOST)); +#endif +} + +#ifdef LINEMODE + int +tty_isediting() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags & (CBREAK|RAW))); +#else + return(termbuf.c_lflag & ICANON); +#endif +} + + int +tty_istrapsig() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags&RAW)); +#else + return(termbuf.c_lflag & ISIG); +#endif +} + + void +tty_setedit(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags &= ~CBREAK; + else + termbuf.sg.sg_flags |= CBREAK; +#else + if (on) + termbuf.c_lflag |= ICANON; + else + termbuf.c_lflag &= ~ICANON; +#endif +} + + void +tty_setsig(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + ; +#else + if (on) + termbuf.c_lflag |= ISIG; + else + termbuf.c_lflag &= ~ISIG; +#endif +} +#endif /* LINEMODE */ + + int +tty_issofttab() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & XTABS); +#else +# ifdef OXTABS + return (termbuf.c_oflag & OXTABS); +# endif +# ifdef TABDLY + return ((termbuf.c_oflag & TABDLY) == TAB3); +# endif +#endif +} + + void +tty_setsofttab(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= XTABS; + else + termbuf.sg.sg_flags &= ~XTABS; +#else + if (on) { +# ifdef OXTABS + termbuf.c_oflag |= OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB3; +# endif + } else { +# ifdef OXTABS + termbuf.c_oflag &= ~OXTABS; +# endif +# ifdef TABDLY + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB0; +# endif + } +#endif +} + + int +tty_islitecho() +{ +#ifndef USE_TERMIO + return (!(termbuf.lflags & LCTLECH)); +#else +# ifdef ECHOCTL + return (!(termbuf.c_lflag & ECHOCTL)); +# endif +# ifdef TCTLECH + return (!(termbuf.c_lflag & TCTLECH)); +# endif +# if !defined(ECHOCTL) && !defined(TCTLECH) + return (0); /* assumes ctl chars are echoed '^x' */ +# endif +#endif +} + + void +tty_setlitecho(on) + int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags &= ~LCTLECH; + else + termbuf.lflags |= LCTLECH; +#else +# ifdef ECHOCTL + if (on) + termbuf.c_lflag &= ~ECHOCTL; + else + termbuf.c_lflag |= ECHOCTL; +# endif +# ifdef TCTLECH + if (on) + termbuf.c_lflag &= ~TCTLECH; + else + termbuf.c_lflag |= TCTLECH; +# endif +#endif +} + + int +tty_iscrnl() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & CRMOD); +#else + return (termbuf.c_iflag & ICRNL); +#endif +} + +/* + * A table of available terminal speeds + */ +struct termspeeds { + int speed; + int value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 9600, B9600 }, { 19200, B9600 }, + { 38400, B9600 }, { -1, B9600 } +}; + + void +tty_tspeed(val) + int val; +{ + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + cfsetospeed(&termbuf, tp->value); +} + + void +tty_rspeed(val) + int val; +{ + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; + cfsetispeed(&termbuf, tp->value); +} + +#if defined(CRAY2) && defined(UNICOS5) + int +tty_isnewmap() +{ + return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) && + !(termbuf.c_oflag & ONLRET)); +} +#endif + +#ifdef PARENT_DOES_UTMP +# ifndef NEWINIT +extern struct utmp wtmp; +extern char wtmpf[]; +# else /* NEWINIT */ +int gotalarm; + + /* ARGSUSED */ + void +nologinproc(sig) + int sig; +{ + gotalarm++; +} +# endif /* NEWINIT */ +#endif /* PARENT_DOES_UTMP */ + +#ifndef NEWINIT +# ifdef PARENT_DOES_UTMP +extern void utmp_sig_init P((void)); +extern void utmp_sig_reset P((void)); +extern void utmp_sig_wait P((void)); +extern void utmp_sig_notify P((int)); +# endif /* PARENT_DOES_UTMP */ +#endif + +/* + * getptyslave() + * + * Open the slave side of the pty, and do any initialization + * that is necessary. The return value is a file descriptor + * for the slave side. + */ + int +getptyslave() +{ + register int t = -1; + +#if !defined(CRAY) || !defined(NEWINIT) +# ifdef LINEMODE + int waslm; +# endif +# ifdef TIOCGWINSZ + struct winsize ws; + extern int def_row, def_col; +# endif + extern int def_tspeed, def_rspeed; + /* + * Opening the slave side may cause initilization of the + * kernel tty structure. We need remember the state of + * if linemode was turned on + * terminal window size + * terminal speed + * so that we can re-set them if we need to. + */ +# ifdef LINEMODE + waslm = tty_linemode(); +# endif + + + /* + * Make sure that we don't have a controlling tty, and + * that we are the session (process group) leader. + */ +# ifdef TIOCNOTTY + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } +# endif + + +# ifdef PARENT_DOES_UTMP + /* + * Wait for our parent to get the utmp stuff to get done. + */ + utmp_sig_wait(); +# endif + + t = cleanopen(line); + if (t < 0) + fatalperror(net, line); + +#ifdef STREAMSPTY +#ifdef USE_TERMIO + ttyfd = t; +#endif + if (ioctl(t, I_PUSH, "ptem") < 0) + fatal(net, "I_PUSH ptem"); + if (ioctl(t, I_PUSH, "ldterm") < 0) + fatal(net, "I_PUSH ldterm"); + if (ioctl(t, I_PUSH, "ttcompat") < 0) + fatal(net, "I_PUSH ttcompat"); + if (ioctl(pty, I_PUSH, "pckt") < 0) + fatal(net, "I_PUSH pckt"); +#endif + + /* + * set up the tty modes as we like them to be. + */ + init_termbuf(); +# ifdef TIOCGWINSZ + if (def_row || def_col) { + bzero((char *)&ws, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void)ioctl(t, TIOCSWINSZ, (char *)&ws); + } +# endif + + /* + * Settings for sgtty based systems + */ +# ifndef USE_TERMIO + termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; +# endif /* USE_TERMIO */ + + /* + * Settings for UNICOS (and HPUX) + */ +# if defined(CRAY) || defined(__hpux) + termbuf.c_oflag = OPOST|ONLCR|TAB3; + termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; + termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; + termbuf.c_cflag = EXTB|HUPCL|CS8; +# endif + + /* + * Settings for all other termios/termio based + * systems, other than 4.4BSD. In 4.4BSD the + * kernel does the initial terminal setup. + */ +# if defined(USE_TERMIO) && !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) +# ifndef OXTABS +# define OXTABS 0 +# endif + termbuf.c_lflag |= ECHO; + termbuf.c_oflag |= ONLCR|OXTABS; + termbuf.c_iflag |= ICRNL; + termbuf.c_iflag &= ~IXOFF; +# endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */ + tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); + tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); +# ifdef LINEMODE + if (waslm) + tty_setlinemode(1); +# endif /* LINEMODE */ + + /* + * Set the tty modes, and make this our controlling tty. + */ + set_termbuf(); + if (login_tty(t) == -1) + fatalperror(net, "login_tty"); +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + if (net > 2) + (void) close(net); +#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Leave the pty open so that we can write out the rlogin + * protocol for /bin/login, if the authentication works. + */ +#else + if (pty > 2) { + (void) close(pty); + pty = -1; + } +#endif +} + +#if !defined(CRAY) || !defined(NEWINIT) +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif +/* + * Open the specified slave side of the pty, + * making sure that we have a clean tty. + */ + int +cleanopen(line) + char *line; +{ + register int t; +#if defined(_SC_CRAY_SECURE_SYS) + struct secstat secbuf; +#endif /* _SC_CRAY_SECURE_SYS */ + +#ifndef STREAMSPTY + /* + * Make sure that other people can't open the + * slave side of the connection. + */ + (void) chown(line, 0, 0); + (void) chmod(line, 0600); +#endif + +# if !defined(CRAY) && (BSD > 43) + (void) revoke(line); +# endif +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + t = open(line, O_RDWR|O_NOCTTY); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (t < 0) + return(-1); + + /* + * Hangup anybody else using this ttyp, then reopen it for + * ourselves. + */ +# if !(defined(CRAY) || defined(__hpux)) && (BSD <= 43) && !defined(STREAMSPTY) + (void) signal(SIGHUP, SIG_IGN); + vhangup(); + (void) signal(SIGHUP, SIG_DFL); + t = open(line, O_RDWR|O_NOCTTY); + if (t < 0) + return(-1); +# endif +# if defined(CRAY) && defined(TCVHUP) + { + register int i; + (void) signal(SIGHUP, SIG_IGN); + (void) ioctl(t, TCVHUP, (char *)0); + (void) signal(SIGHUP, SIG_DFL); + setpgrp(); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (secstat(line, &secbuf) < 0) + return(-1); + if (setulvl(secbuf.st_slevel) < 0) + return(-1); + if (setucmp(secbuf.st_compart) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + i = open(line, O_RDWR); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(sysv.sy_minlvl) < 0) + return(-1); + if (setucmp(0) < 0) + return(-1); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + if (i < 0) + return(-1); + (void) close(t); + t = i; + } +# endif /* defined(CRAY) && defined(TCVHUP) */ + return(t); +} +#endif /* !defined(CRAY) || !defined(NEWINIT) */ + +#if BSD <= 43 + + int +login_tty(t) + int t; +{ + if (setsid() < 0) { +#ifdef ultrix + /* + * The setsid() may have failed because we + * already have a pgrp == pid. Zero out + * our pgrp and try again... + */ + if ((setpgrp(0, 0) < 0) || (setsid() < 0)) +#endif + fatalperror(net, "setsid()"); + } +# ifdef TIOCSCTTY + if (ioctl(t, TIOCSCTTY, (char *)0) < 0) + fatalperror(net, "ioctl(sctty)"); +# if defined(CRAY) + /* + * Close the hard fd to /dev/ttypXXX, and re-open through + * the indirect /dev/tty interface. + */ + close(t); + if ((t = open("/dev/tty", O_RDWR)) < 0) + fatalperror(net, "open(/dev/tty)"); +# endif +# else + /* + * We get our controlling tty assigned as a side-effect + * of opening up a tty device. But on BSD based systems, + * this only happens if our process group is zero. The + * setsid() call above may have set our pgrp, so clear + * it out before opening the tty... + */ + (void) setpgrp(0, 0); + close(open(line, O_RDWR)); +# endif + if (t != 0) + (void) dup2(t, 0); + if (t != 1) + (void) dup2(t, 1); + if (t != 2) + (void) dup2(t, 2); + if (t > 2) + close(t); + return(0); +} +#endif /* BSD <= 43 */ + +#ifdef NEWINIT +char *gen_id = "fe"; +#endif + +/* + * startslave(host) + * + * Given a hostname, do whatever + * is necessary to startup the login process on the slave side of the pty. + */ + +/* ARGSUSED */ + void +startslave(host, autologin, autoname) + char *host; + int autologin; + char *autoname; +{ + register int i; + long time(); + char name[256]; +#ifdef NEWINIT + extern char *ptyip; + struct init_request request; + void nologinproc(); + register int n; +#endif /* NEWINIT */ + +#if defined(AUTHENTICATION) + if (!autoname || !autoname[0]) + autologin = 0; + + if (autologin < auth_level) { + fatal(net, "Authorization failed"); + exit(1); + } +#endif + +#ifndef NEWINIT +# ifdef PARENT_DOES_UTMP + utmp_sig_init(); +# endif /* PARENT_DOES_UTMP */ + + if ((i = fork()) < 0) + fatalperror(net, "fork"); + if (i) { +# ifdef PARENT_DOES_UTMP + /* + * Cray parent will create utmp entry for child and send + * signal to child to tell when done. Child waits for signal + * before doing anything important. + */ + register int pid = i; + void sigjob P((int)); + + setpgrp(); + utmp_sig_reset(); /* reset handler to default */ + /* + * Create utmp entry for child + */ + (void) time(&wtmp.ut_time); + wtmp.ut_type = LOGIN_PROCESS; + wtmp.ut_pid = pid; + SCPYN(wtmp.ut_user, "LOGIN"); + SCPYN(wtmp.ut_host, host); + SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1); +#ifndef __hpux + SCPYN(wtmp.ut_id, wtmp.ut_line+3); +#else + SCPYN(wtmp.ut_id, wtmp.ut_line+7); +#endif + pututline(&wtmp); + endutent(); + if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) { + (void) write(i, (char *)&wtmp, sizeof(struct utmp)); + (void) close(i); + } +#ifdef CRAY + (void) signal(WJSIGNAL, sigjob); +#endif + utmp_sig_notify(pid); +# endif /* PARENT_DOES_UTMP */ + } else { + getptyslave(autologin); + start_login(host, autologin, autoname); + /*NOTREACHED*/ + } +#else /* NEWINIT */ + + /* + * Init will start up login process if we ask nicely. We only wait + * for it to start up and begin normal telnet operation. + */ + if ((i = open(INIT_FIFO, O_WRONLY)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't open %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + memset((char *)&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + SCPYN(request.gen_id, gen_id); + SCPYN(request.tty_id, &line[8]); + SCPYN(request.host, host); + SCPYN(request.term_type, terminaltype ? terminaltype : "network"); +#if !defined(UNICOS5) + request.signal = SIGCLD; + request.pid = getpid(); +#endif +#ifdef BFTPDAEMON + /* + * Are we working as the bftp daemon? + */ + if (bftpd) { + SCPYN(request.exec_name, BFTPPATH); + } +#endif /* BFTPDAEMON */ + if (write(i, (char *)&request, sizeof(request)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't write to %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + (void) close(i); + (void) signal(SIGALRM, nologinproc); + for (i = 0; ; i++) { + char tbuf[128]; + alarm(15); + n = read(pty, ptyip, BUFSIZ); + if (i == 3 || n >= 0 || !gotalarm) + break; + gotalarm = 0; + sprintf(tbuf, "telnetd: waiting for /etc/init to start login process on %s\r\n", line); + (void) write(net, tbuf, strlen(tbuf)); + } + if (n < 0 && gotalarm) + fatal(net, "/etc/init didn't start login process"); + pcc += n; + alarm(0); + (void) signal(SIGALRM, SIG_DFL); + + return; +#endif /* NEWINIT */ +} + +char *envinit[3]; +extern char **environ; + + void +init_env() +{ + extern char *getenv(); + char **envp; + + envp = envinit; + if (*envp = getenv("TZ")) + *envp++ -= 3; +#if defined(CRAY) || defined(__hpux) + else + *envp++ = "TZ=GMT0"; +#endif + *envp = 0; + environ = envinit; +} + +#ifndef NEWINIT + +/* + * start_login(host) + * + * Assuming that we are now running as a child processes, this + * function will turn us into the login process. + */ + + void +start_login(host, autologin, name) + char *host; + int autologin; + char *name; +{ + register char *cp; + register char **argv; + char **addarg(); + extern char *getenv(); +#ifdef UTMPX + register int pid = getpid(); + struct utmpx utmpx; +#endif +#ifdef SOLARIS + char *term; + char termbuf[64]; +#endif + +#ifdef UTMPX + /* + * Create utmp entry for child + */ + + bzero(&utmpx, sizeof(utmpx)); + SCPYN(utmpx.ut_user, ".telnet"); + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utmpx.ut_pid = pid; + utmpx.ut_id[0] = 't'; + utmpx.ut_id[1] = 'n'; + utmpx.ut_id[2] = SC_WILDC; + utmpx.ut_id[3] = SC_WILDC; + utmpx.ut_type = LOGIN_PROCESS; + (void) time(&utmpx.ut_tv.tv_sec); + if (makeutx(&utmpx) == NULL) + fatal(net, "makeutx failed"); +#endif + + /* + * -h : pass on name of host. + * WARNING: -h is accepted by login if and only if + * getuid() == 0. + * -p : don't clobber the environment (so terminal type stays set). + * + * -f : force this login, he has already been authenticated + */ + argv = addarg(0, "login"); + +#if !defined(NO_LOGIN_H) + +# if defined (AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + /* + * Don't add the "-h host" option if we are going + * to be adding the "-r host" option down below... + */ + if ((auth_level < 0) || (autologin != AUTH_VALID)) +# endif + { + argv = addarg(argv, "-h"); + argv = addarg(argv, host); +#ifdef SOLARIS + /* + * SVR4 version of -h takes TERM= as second arg, or - + */ + term = getenv("TERM"); + if (term == NULL || term[0] == 0) { + term = "-"; + } else { + strcpy(termbuf, "TERM="); + strncat(termbuf, term, sizeof(termbuf) - 6); + term = termbuf; + } + argv = addarg(argv, term); +#endif + } +#endif +#if !defined(NO_LOGIN_P) + argv = addarg(argv, "-p"); +#endif +#ifdef BFTPDAEMON + /* + * Are we working as the bftp daemon? If so, then ask login + * to start bftp instead of shell. + */ + if (bftpd) { + argv = addarg(argv, "-e"); + argv = addarg(argv, BFTPPATH); + } else +#endif +#if defined (SecurID) + /* + * don't worry about the -f that might get sent. + * A -s is supposed to override it anyhow. + */ + if (require_SecurID) + argv = addarg(argv, "-s"); +#endif +#if defined (AUTHENTICATION) + if (auth_level >= 0 && autologin == AUTH_VALID) { +# if !defined(NO_LOGIN_F) + argv = addarg(argv, "-f"); + argv = addarg(argv, name); +# else +# if defined(LOGIN_R) + /* + * We don't have support for "login -f", but we + * can fool /bin/login into thinking that we are + * rlogind, and allow us to log in without a + * password. The rlogin protocol expects + * local-user\0remote-user\0term/speed\0 + */ + + if (pty > 2) { + register char *cp; + char speed[128]; + int isecho, israw, xpty, len; + extern int def_rspeed; +# ifndef LOGIN_HOST + /* + * Tell login that we are coming from "localhost". + * If we passed in the real host name, then the + * user would have to allow .rhost access from + * every machine that they want authenticated + * access to work from, which sort of defeats + * the purpose of an authenticated login... + * So, we tell login that the session is coming + * from "localhost", and the user will only have + * to have "localhost" in their .rhost file. + */ +# define LOGIN_HOST "localhost" +# endif + argv = addarg(argv, "-r"); + argv = addarg(argv, LOGIN_HOST); + + xpty = pty; +# ifndef STREAMSPTY + pty = 0; +# else + ttyfd = 0; +# endif + init_termbuf(); + isecho = tty_isecho(); + israw = tty_israw(); + if (isecho || !israw) { + tty_setecho(0); /* Turn off echo */ + tty_setraw(1); /* Turn on raw */ + set_termbuf(); + } + len = strlen(name)+1; + write(xpty, name, len); + write(xpty, name, len); + sprintf(speed, "%s/%d", (cp = getenv("TERM")) ? cp : "", + (def_rspeed > 0) ? def_rspeed : 9600); + len = strlen(speed)+1; + write(xpty, speed, len); + + if (isecho || !israw) { + init_termbuf(); + tty_setecho(isecho); + tty_setraw(israw); + set_termbuf(); + if (!israw) { + /* + * Write a newline to ensure + * that login will be able to + * read the line... + */ + write(xpty, "\n", 1); + } + } + pty = xpty; + } +# else + argv = addarg(argv, name); +# endif +# endif + } else +#endif + if (getenv("USER")) { + argv = addarg(argv, getenv("USER")); +#if defined(LOGIN_ARGS) && defined(NO_LOGIN_P) + { + register char **cpp; + for (cpp = environ; *cpp; cpp++) + argv = addarg(argv, *cpp); + } +#endif + /* + * Assume that login will set the USER variable + * correctly. For SysV systems, this means that + * USER will no longer be set, just LOGNAME by + * login. (The problem is that if the auto-login + * fails, and the user then specifies a different + * account name, he can get logged in with both + * LOGNAME and USER in his environment, but the + * USER value will be wrong. + */ + unsetenv("USER"); + } +#if defined(AUTHENTICATION) && defined(NO_LOGIN_F) && defined(LOGIN_R) + if (pty > 2) + close(pty); +#endif + closelog(); + execv(_PATH_LOGIN, argv); + + syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); + fatalperror(net, _PATH_LOGIN); + /*NOTREACHED*/ +} + + char ** +addarg(argv, val) + register char **argv; + register char *val; +{ + register char **cpp; + + if (argv == NULL) { + /* + * 10 entries, a leading length, and a null + */ + argv = (char **)malloc(sizeof(*argv) * 12); + if (argv == NULL) + return(NULL); + *argv++ = (char *)10; + *argv = (char *)0; + } + for (cpp = argv; *cpp; cpp++) + ; + if (cpp == &argv[(int)argv[-1]]) { + --argv; + *argv = (char *)((int)(*argv) + 10); + argv = (char **)realloc(argv, (int)(*argv) + 2); + if (argv == NULL) + return(NULL); + argv++; + cpp = &argv[(int)argv[-1] - 10]; + } + *cpp++ = val; + *cpp = 0; + return(argv); +} +#endif /* NEWINIT */ + +/* + * cleanup() + * + * This is the routine to call when we are all through, to + * clean up anything that needs to be cleaned up. + */ + /* ARGSUSED */ + void +cleanup(sig) + int sig; +{ +#ifndef PARENT_DOES_UTMP +# if (BSD > 43) || defined(convex) + char *p; + + p = line + sizeof("/dev/") - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + (void) shutdown(net, 2); + exit(1); +# else + void rmut(); + + rmut(); + vhangup(); /* XXX */ + (void) shutdown(net, 2); + exit(1); +# endif +#else /* PARENT_DOES_UTMP */ +# ifdef NEWINIT + (void) shutdown(net, 2); + exit(1); +# else /* NEWINIT */ +# ifdef CRAY + static int incleanup = 0; + register int t; + + /* + * 1: Pick up the zombie, if we are being called + * as the signal handler. + * 2: If we are a nested cleanup(), return. + * 3: Try to clean up TMPDIR. + * 4: Fill in utmp with shutdown of process. + * 5: Close down the network and pty connections. + * 6: Finish up the TMPDIR cleanup, if needed. + */ + if (sig == SIGCHLD) + while (waitpid(-1, 0, WNOHANG) > 0) + ; /* VOID */ + t = sigblock(sigmask(SIGCHLD)); + if (incleanup) { + sigsetmask(t); + return; + } + incleanup = 1; + sigsetmask(t); + if (secflag) { + /* + * We need to set ourselves back to a null + * label to clean up. + */ + + setulvl(sysv.sy_minlvl); + setucmp((long)0); + } + + t = cleantmp(&wtmp); + setutent(); /* just to make sure */ +# endif /* CRAY */ + rmut(line); + close(pty); + (void) shutdown(net, 2); +# ifdef CRAY + if (t == 0) + cleantmp(&wtmp); +# endif /* CRAY */ + exit(1); +# endif /* NEWINT */ +#endif /* PARENT_DOES_UTMP */ +} + +#if defined(PARENT_DOES_UTMP) && !defined(NEWINIT) +/* + * _utmp_sig_rcv + * utmp_sig_init + * utmp_sig_wait + * These three functions are used to coordinate the handling of + * the utmp file between the server and the soon-to-be-login shell. + * The server actually creates the utmp structure, the child calls + * utmp_sig_wait(), until the server calls utmp_sig_notify() and + * signals the future-login shell to proceed. + */ +static int caught=0; /* NZ when signal intercepted */ +static void (*func)(); /* address of previous handler */ + + void +_utmp_sig_rcv(sig) + int sig; +{ + caught = 1; + (void) signal(SIGUSR1, func); +} + + void +utmp_sig_init() +{ + /* + * register signal handler for UTMP creation + */ + if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1) + fatalperror(net, "telnetd/signal"); +} + + void +utmp_sig_reset() +{ + (void) signal(SIGUSR1, func); /* reset handler to default */ +} + +# ifdef __hpux +# define sigoff() /* do nothing */ +# define sigon() /* do nothing */ +# endif + + void +utmp_sig_wait() +{ + /* + * Wait for parent to write our utmp entry. + */ + sigoff(); + while (caught == 0) { + pause(); /* wait until we get a signal (sigon) */ + sigoff(); /* turn off signals while we check caught */ + } + sigon(); /* turn on signals again */ +} + + void +utmp_sig_notify(pid) +{ + kill(pid, SIGUSR1); +} + +# ifdef CRAY +static int gotsigjob = 0; + + /*ARGSUSED*/ + void +sigjob(sig) + int sig; +{ + register int jid; + register struct jobtemp *jp; + + while ((jid = waitjob(NULL)) != -1) { + if (jid == 0) { + return; + } + gotsigjob++; + jobend(jid, NULL, NULL); + } +} + +/* + * Clean up the TMPDIR that login created. + * The first time this is called we pick up the info + * from the utmp. If the job has already gone away, + * then we'll clean up and be done. If not, then + * when this is called the second time it will wait + * for the signal that the job is done. + */ + int +cleantmp(wtp) + register struct utmp *wtp; +{ + struct utmp *utp; + static int first = 1; + register int mask, omask, ret; + extern struct utmp *getutid P((const struct utmp *_Id)); + + + mask = sigmask(WJSIGNAL); + + if (first == 0) { + omask = sigblock(mask); + while (gotsigjob == 0) + sigpause(omask); + return(1); + } + first = 0; + setutent(); /* just to make sure */ + + utp = getutid(wtp); + if (utp == 0) { + syslog(LOG_ERR, "Can't get /etc/utmp entry to clean TMPDIR"); + return(-1); + } + /* + * Nothing to clean up if the user shell was never started. + */ + if (utp->ut_type != USER_PROCESS || utp->ut_jid == 0) + return(1); + + /* + * Block the WJSIGNAL while we are in jobend(). + */ + omask = sigblock(mask); + ret = jobend(utp->ut_jid, utp->ut_tpath, utp->ut_user); + sigsetmask(omask); + return(ret); +} + + int +jobend(jid, path, user) + register int jid; + register char *path; + register char *user; +{ + static int saved_jid = 0; + static char saved_path[sizeof(wtmp.ut_tpath)+1]; + static char saved_user[sizeof(wtmp.ut_user)+1]; + + if (path) { + strncpy(saved_path, path, sizeof(wtmp.ut_tpath)); + strncpy(saved_user, user, sizeof(wtmp.ut_user)); + saved_path[sizeof(saved_path)] = '\0'; + saved_user[sizeof(saved_user)] = '\0'; + } + if (saved_jid == 0) { + saved_jid = jid; + return(0); + } + cleantmpdir(jid, saved_path, saved_user); + return(1); +} + +/* + * Fork a child process to clean up the TMPDIR + */ +cleantmpdir(jid, tpath, user) + register int jid; + register char *tpath; + register char *user; +{ + switch(fork()) { + case -1: + syslog(LOG_ERR, "TMPDIR cleanup(%s): fork() failed: %m\n", + tpath); + break; + case 0: + execl(CLEANTMPCMD, CLEANTMPCMD, user, tpath, 0); + syslog(LOG_ERR, "TMPDIR cleanup(%s): execl(%s) failed: %m\n", + tpath, CLEANTMPCMD); + exit(1); + default: + /* + * Forget about child. We will exit, and + * /etc/init will pick it up. + */ + break; + } +} +# endif /* CRAY */ +#endif /* defined(PARENT_DOES_UTMP) && !defined(NEWINIT) */ + +/* + * rmut() + * + * This is the function called by cleanup() to + * remove the utmp entry for this person. + */ + +#ifdef UTMPX + void +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + + struct utmpx *utxp, utmpx; + + /* + * This updates the utmpx and utmp entries and make a wtmp/x entry + */ + + SCPYN(utmpx.ut_line, line + sizeof("/dev/") - 1); + utxp = getutxline(&utmpx); + if (utxp) { + utxp->ut_type = DEAD_PROCESS; + utxp->ut_exit.e_termination = 0; + utxp->ut_exit.e_exit = 0; + (void) time(&utmpx.ut_tv.tv_sec); + utmpx.ut_tv.tv_usec = 0; + modutx(utxp); + } + endutxent(); +} /* end of rmut */ +#endif + +#if !defined(UTMPX) && !(defined(CRAY) || defined(__hpux)) && BSD <= 43 + void +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + + f = open(utmpf, O_RDWR); + if (f >= 0) { + (void) fstat(f, &statbf); + utmp = (struct utmp *)malloc((unsigned)statbf.st_size); + if (!utmp) + syslog(LOG_ERR, "utmp malloc failed"); + if (statbf.st_size && utmp) { + nutmp = read(f, (char *)utmp, (int)statbf.st_size); + nutmp /= sizeof(struct utmp); + + for (u = utmp ; u < &utmp[nutmp] ; u++) { + if (SCMPN(u->ut_line, line+5) || + u->ut_name[0]==0) + continue; + (void) lseek(f, ((long)u)-((long)utmp), L_SET); + SCPYN(u->ut_name, ""); + SCPYN(u->ut_host, ""); + (void) time(&u->ut_time); + (void) write(f, (char *)u, sizeof(wtmp)); + found++; + } + } + (void) close(f); + } + if (found) { + f = open(wtmpf, O_WRONLY|O_APPEND); + if (f >= 0) { + SCPYN(wtmp.ut_line, line+5); + SCPYN(wtmp.ut_name, ""); + SCPYN(wtmp.ut_host, ""); + (void) time(&wtmp.ut_time); + (void) write(f, (char *)&wtmp, sizeof(wtmp)); + (void) close(f); + } + } + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[strlen("/dev/")] = 'p'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} /* end of rmut */ +#endif /* CRAY */ + +#ifdef __hpux +rmut (line) +char *line; +{ + struct utmp utmp; + struct utmp *utptr; + int fd; /* for /etc/wtmp */ + + utmp.ut_type = USER_PROCESS; + (void) strncpy(utmp.ut_id, line+12, sizeof(utmp.ut_id)); + (void) setutent(); + utptr = getutid(&utmp); + /* write it out only if it exists */ + if (utptr) { + utptr->ut_type = DEAD_PROCESS; + utptr->ut_time = time((long *) 0); + (void) pututline(utptr); + /* set wtmp entry if wtmp file exists */ + if ((fd = open(wtmpf, O_WRONLY | O_APPEND)) >= 0) { + (void) write(fd, utptr, sizeof(utmp)); + (void) close(fd); + } + } + (void) endutent(); + + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[14] = line[13]; + line[13] = line[12]; + line[8] = 'm'; + line[9] = '/'; + line[10] = 'p'; + line[11] = 't'; + line[12] = 'y'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} +#endif diff --git a/libexec/telnetd/telnetd.8 b/libexec/telnetd/telnetd.8 new file mode 100644 index 0000000..fee5526 --- /dev/null +++ b/libexec/telnetd/telnetd.8 @@ -0,0 +1,605 @@ +.\" Copyright (c) 1983, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)telnetd.8 8.3 (Berkeley) 3/1/94 +.\" +.Dd March 1, 1994 +.Dt TELNETD 8 +.Os BSD 4.2 +.Sh NAME +.Nm telnetd +.Nd DARPA +.Tn TELNET +protocol server +.Sh SYNOPSIS +.Nm /usr/libexec/telnetd +.Op Fl BUhlkns +.Op Fl D Ar debugmode +.Op Fl I Ns Ar initid +.Op Fl S Ar tos +.Op Fl X Ar authtype +.Op Fl a Ar authmode +.Op Fl edebug +.Op Fl r Ns Ar lowpty-highpty +.Op Fl u Ar len +.Op Fl debug Op Ar port +.Sh DESCRIPTION +The +.Nm telnetd +command is a server which supports the +.Tn DARPA +standard +.Tn TELNET +virtual terminal protocol. +.Nm Telnetd +is normally invoked by the internet server (see +.Xr inetd 8 ) +for requests to connect to the +.Tn TELNET +port as indicated by the +.Pa /etc/services +file (see +.Xr services 5 ) . +The +.Fl debug +option may be used to start up +.Nm telnetd +manually, instead of through +.Xr inetd 8 . +If started up this way, +.Ar port +may be specified to run +.Nm telnetd +on an alternate +.Tn TCP +port number. +.Pp +The +.Nm telnetd +command accepts the following options: +.Bl -tag -width "-a authmode" +.It Fl a Ar authmode +This option may be used for specifying what mode should +be used for authentication. +Note that this option is only useful if +.Nm telnetd +has been compiled with support for the +.Dv AUTHENTICATION +option. +There are several valid values for +.Ar authmode: +.Bl -tag -width debug +.It debug +Turns on authentication debugging code. +.It user +Only allow connections when the remote user +can provide valid authentication information +to identify the remote user, +and is allowed access to the specified account +without providing a password. +.It valid +Only allow connections when the remote user +can provide valid authentication information +to identify the remote user. +The +.Xr login 1 +command will provide any additional user verification +needed if the remote user is not allowed automatic +access to the specified account. +.It other +Only allow connections that supply some authentication information. +This option is currently not supported +by any of the existing authentication mechanisms, +and is thus the same as specifying +.Fl a +.Cm valid . +.It none +This is the default state. +Authentication information is not required. +If no or insufficient authentication information +is provided, then the +.Xr login 1 +program will provide the necessary user +verification. +.It off +This disables the authentication code. +All user verification will happen through the +.Xr login 1 +program. +.El +.It Fl B +Specifies bftp server mode. In this mode, +.Nm telnetd +causes login to start a +.Xr bftp 1 +session rather than the user's +normal shell. In bftp daemon mode normal +logins are not supported, and it must be used +on a port other than the normal +.Tn TELNET +port. +.It Fl D Ar debugmode +This option may be used for debugging purposes. +This allows +.Nm telnetd +to print out debugging information +to the connection, allowing the user to see what +.Nm telnetd +is doing. +There are several possible values for +.Ar debugmode: +.Bl -tag -width exercise +.It Cm options +Prints information about the negotiation of +.Tn TELNET +options. +.It Cm report +Prints the +.Cm options +information, plus some additional information +about what processing is going on. +.It Cm netdata +Displays the data stream received by +.Nm telnetd. +.It Cm ptydata +Displays data written to the pty. +.It Cm exercise +Has not been implemented yet. +.El +.It Fl debug +Enables debugging on each socket created by +.Nm telnetd +(see +.Dv SO_DEBUG +in +.Xr socket 2 ) . +.It Fl edebug +If +.Nm telnetd +has been compiled with support for data encryption, then the +.Fl edebug +option may be used to enable encryption debugging code. +.It Fl h +Disables the printing of host-specific information before +login has been completed. +.It Fl I Ar initid +This option is only applicable to +.Tn UNICOS +systems prior to 7.0. +It specifies the +.Dv ID +from +.Pa /etc/inittab +to use when init starts login sessions. The default +.Dv ID +is +.Dv fe. +.It Fl k +This option is only useful if +.Nm telnetd +has been compiled with both linemode and kludge linemode +support. If the +.Fl k +option is specified, then if the remote client does not +support the +.Dv LINEMODE +option, then +.Nm telnetd +will operate in character at a time mode. +It will still support kludge linemode, but will only +go into kludge linemode if the remote client requests +it. +(This is done by by the client sending +.Dv DONT SUPPRESS-GO-AHEAD +and +.Dv DONT ECHO . ) +The +.Fl k +option is most useful when there are remote clients +that do not support kludge linemode, but pass the heuristic +(if they respond with +.Dv WILL TIMING-MARK +in response to a +.Dv DO TIMING-MARK) +for kludge linemode support. +.It Fl l +Specifies line mode. Tries to force clients to use line- +at-a-time mode. +If the +.Dv LINEMODE +option is not supported, it will go +into kludge linemode. +.It Fl n +Disable +.Dv TCP +keep-alives. Normally +.Nm telnetd +enables the +.Tn TCP +keep-alive mechanism to probe connections that +have been idle for some period of time to determine +if the client is still there, so that idle connections +from machines that have crashed or can no longer +be reached may be cleaned up. +.It Fl r Ar lowpty-highpty +This option is only enabled when +.Nm telnetd +is compiled for +.Dv UNICOS. +It specifies an inclusive range of pseudo-terminal devices to +use. If the system has sysconf variable +.Dv _SC_CRAY_NPTY +configured, the default pty search range is 0 to +.Dv _SC_CRAY_NPTY; +otherwise, the default range is 0 to 128. Either +.Ar lowpty +or +.Ar highpty +may be omitted to allow changing +either end of the search range. If +.Ar lowpty +is omitted, the - character is still required so that +.Nm telnetd +can differentiate +.Ar highpty +from +.Ar lowpty . +.It Fl s +This option is only enabled if +.Nm telnetd +is compiled with support for +.Tn SecurID +cards. +It causes the +.Fl s +option to be passed on to +.Xr login 1 , +and thus is only useful if +.Xr login 1 +supports the +.Fl s +flag to indicate that only +.Tn SecurID +validated logins are allowed, and is +usually useful for controlling remote logins +from outside of a firewall. +.It Fl S Ar tos +.It Fl u Ar len +This option is used to specify the size of the field +in the +.Dv utmp +structure that holds the remote host name. +If the resolved host name is longer than +.Ar len , +the dotted decimal value will be used instead. +This allows hosts with very long host names that +overflow this field to still be uniquely identified. +Specifying +.Fl u0 +indicates that only dotted decimal addresses +should be put into the +.Pa utmp +file. +.It Fl U +This option causes +.Nm telnetd +to refuse connections from addresses that +cannot be mapped back into a symbolic name +via the +.Xr gethostbyaddr 3 +routine. +.It Fl X Ar authtype +This option is only valid if +.Nm telnetd +has been built with support for the authentication option. +It disables the use of +.Ar authtype +authentication, and +can be used to temporarily disable +a specific authentication type without having to recompile +.Nm telnetd . +.El +.Pp +.Nm Telnetd +operates by allocating a pseudo-terminal device (see +.Xr pty 4 ) +for a client, then creating a login process which has +the slave side of the pseudo-terminal as +.Dv stdin , +.Dv stdout +and +.Dv stderr . +.Nm Telnetd +manipulates the master side of the pseudo-terminal, +implementing the +.Tn TELNET +protocol and passing characters +between the remote client and the login process. +.Pp +When a +.Tn TELNET +session is started up, +.Nm telnetd +sends +.Tn TELNET +options to the client side indicating +a willingness to do the +following +.Tn TELNET +options, which are described in more detail below: +.Bd -literal -offset indent +DO AUTHENTICATION +WILL ENCRYPT +DO TERMINAL TYPE +DO TSPEED +DO XDISPLOC +DO NEW-ENVIRON +DO ENVIRON +WILL SUPPRESS GO AHEAD +DO ECHO +DO LINEMODE +DO NAWS +WILL STATUS +DO LFLOW +DO TIMING-MARK +.Ed +.Pp +The pseudo-terminal allocated to the client is configured +to operate in \*(lqcooked\*(rq mode, and with +.Dv XTABS and +.Dv CRMOD +enabled (see +.Xr tty 4 ) . +.Pp +.Nm Telnetd +has support for enabling locally the following +.Tn TELNET +options: +.Bl -tag -width "DO AUTHENTICATION" +.It "WILL ECHO" +When the +.Dv LINEMODE +option is enabled, a +.Dv WILL ECHO +or +.Dv WONT ECHO +will be sent to the client to indicate the +current state of terminal echoing. +When terminal echo is not desired, a +.Dv WILL ECHO +is sent to indicate that +.Tn telnetd +will take care of echoing any data that needs to be +echoed to the terminal, and then nothing is echoed. +When terminal echo is desired, a +.Dv WONT ECHO +is sent to indicate that +.Tn telnetd +will not be doing any terminal echoing, so the +client should do any terminal echoing that is needed. +.It "WILL BINARY" +Indicates that the client is willing to send a +8 bits of data, rather than the normal 7 bits +of the Network Virtual Terminal. +.It "WILL SGA" +Indicates that it will not be sending +.Dv IAC GA, +go ahead, commands. +.It "WILL STATUS" +Indicates a willingness to send the client, upon +request, of the current status of all +.Tn TELNET +options. +.It "WILL TIMING-MARK" +Whenever a +.Dv DO TIMING-MARK +command is received, it is always responded +to with a +.Dv WILL TIMING-MARK +.It "WILL LOGOUT" +When a +.Dv DO LOGOUT +is received, a +.Dv WILL LOGOUT +is sent in response, and the +.Tn TELNET +session is shut down. +.It "WILL ENCRYPT" +Only sent if +.Nm telnetd +is compiled with support for data encryption, and +indicates a willingness to decrypt +the data stream. +.El +.Pp +.Nm Telnetd +has support for enabling remotely the following +.Tn TELNET +options: +.Bl -tag -width "DO AUTHENTICATION" +.It "DO BINARY" +Sent to indicate that +.Tn telnetd +is willing to receive an 8 bit data stream. +.It "DO LFLOW" +Requests that the client handle flow control +characters remotely. +.It "DO ECHO" +This is not really supported, but is sent to identify a 4.2BSD +.Xr telnet 1 +client, which will improperly respond with +.Dv WILL ECHO. +If a +.Dv WILL ECHO +is received, a +.Dv DONT ECHO +will be sent in response. +.It "DO TERMINAL-TYPE" +Indicates a desire to be able to request the +name of the type of terminal that is attached +to the client side of the connection. +.It "DO SGA" +Indicates that it does not need to receive +.Dv IAC GA, +the go ahead command. +.It "DO NAWS" +Requests that the client inform the server when +the window (display) size changes. +.It "DO TERMINAL-SPEED" +Indicates a desire to be able to request information +about the speed of the serial line to which +the client is attached. +.It "DO XDISPLOC" +Indicates a desire to be able to request the name +of the X windows display that is associated with +the telnet client. +.It "DO NEW-ENVIRON" +Indicates a desire to be able to request environment +variable information, as described in RFC 1572. +.It "DO ENVIRON" +Indicates a desire to be able to request environment +variable information, as described in RFC 1408. +.It "DO LINEMODE" +Only sent if +.Nm telnetd +is compiled with support for linemode, and +requests that the client do line by line processing. +.It "DO TIMING-MARK" +Only sent if +.Nm telnetd +is compiled with support for both linemode and +kludge linemode, and the client responded with +.Dv WONT LINEMODE. +If the client responds with +.Dv WILL TM, +the it is assumed that the client supports +kludge linemode. +Note that the +.Op Fl k +option can be used to disable this. +.It "DO AUTHENTICATION" +Only sent if +.Nm telnetd +is compiled with support for authentication, and +indicates a willingness to receive authentication +information for automatic login. +.It "DO ENCRYPT" +Only sent if +.Nm telnetd +is compiled with support for data encryption, and +indicates a willingness to decrypt +the data stream. +.Sh ENVIRONMENT +.Sh FILES +.Pa /etc/services +.br +.Pa /etc/inittab +(UNICOS systems only) +.br +.Pa /etc/iptos +(if supported) +.br +.Pa /usr/ucb/bftp +(if supported) +.Sh "SEE ALSO" +.Xr telnet 1 , +.Xr login 1 , +.Xr bftp 1 +(if supported) +.Sh STANDARDS +.Bl -tag -compact -width RFC-1572 +.It Cm RFC-854 +.Tn TELNET +PROTOCOL SPECIFICATION +.It Cm RFC-855 +TELNET OPTION SPECIFICATIONS +.It Cm RFC-856 +TELNET BINARY TRANSMISSION +.It Cm RFC-857 +TELNET ECHO OPTION +.It Cm RFC-858 +TELNET SUPPRESS GO AHEAD OPTION +.It Cm RFC-859 +TELNET STATUS OPTION +.It Cm RFC-860 +TELNET TIMING MARK OPTION +.It Cm RFC-861 +TELNET EXTENDED OPTIONS - LIST OPTION +.It Cm RFC-885 +TELNET END OF RECORD OPTION +.It Cm RFC-1073 +Telnet Window Size Option +.It Cm RFC-1079 +Telnet Terminal Speed Option +.It Cm RFC-1091 +Telnet Terminal-Type Option +.It Cm RFC-1096 +Telnet X Display Location Option +.It Cm RFC-1123 +Requirements for Internet Hosts -- Application and Support +.It Cm RFC-1184 +Telnet Linemode Option +.It Cm RFC-1372 +Telnet Remote Flow Control Option +.It Cm RFC-1416 +Telnet Authentication Option +.It Cm RFC-1411 +Telnet Authentication: Kerberos Version 4 +.It Cm RFC-1412 +Telnet Authentication: SPX +.It Cm RFC-1571 +Telnet Environment Option Interoperability Issues +.It Cm RFC-1572 +Telnet Environment Option +.Sh BUGS +Some +.Tn TELNET +commands are only partially implemented. +.Pp +Because of bugs in the original 4.2 BSD +.Xr telnet 1 , +.Nm telnetd +performs some dubious protocol exchanges to try to discover if the remote +client is, in fact, a 4.2 BSD +.Xr telnet 1 . +.Pp +Binary mode +has no common interpretation except between similar operating systems +(Unix in this case). +.Pp +The terminal type name received from the remote client is converted to +lower case. +.Pp +.Nm Telnetd +never sends +.Tn TELNET +.Dv IAC GA +(go ahead) commands. diff --git a/libexec/telnetd/telnetd.c b/libexec/telnetd/telnetd.c new file mode 100644 index 0000000..6860534 --- /dev/null +++ b/libexec/telnetd/telnetd.c @@ -0,0 +1,1582 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)telnetd.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#include "telnetd.h" +#include "pathnames.h" + +#if defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY) +/* + * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can + * use it to tell us to turn off all the socket security code, + * since that is only used in UNICOS 7.0 and later. + */ +# undef _SC_CRAY_SECURE_SYS +#endif + +#if defined(_SC_CRAY_SECURE_SYS) +#include <sys/sysv.h> +#include <sys/secdev.h> +# ifdef SO_SEC_MULTI /* 8.0 code */ +#include <sys/secparm.h> +#include <sys/usrv.h> +# endif /* SO_SEC_MULTI */ +int secflag; +char tty_dev[16]; +struct secdev dv; +struct sysv sysv; +# ifdef SO_SEC_MULTI /* 8.0 code */ +struct socksec ss; +# else /* SO_SEC_MULTI */ /* 7.0 code */ +struct socket_security ss; +# endif /* SO_SEC_MULTI */ +#endif /* _SC_CRAY_SECURE_SYS */ + +#if defined(AUTHENTICATION) +#include <libtelnet/auth.h> +int auth_level = 0; +#endif +#if defined(SecurID) +int require_SecurID = 0; +#endif + +extern int utmp_len; +int registerd_host_only = 0; + +#ifdef STREAMSPTY +# include <stropts.h> +# include <termio.h> +/* make sure we don't get the bsd version */ +# include "/usr/include/sys/tty.h" +# include <sys/ptyvar.h> + +/* + * Because of the way ptyibuf is used with streams messages, we need + * ptyibuf+1 to be on a full-word boundary. The following wierdness + * is simply to make that happen. + */ +long ptyibufbuf[BUFSIZ/sizeof(long)+1]; +char *ptyibuf = ((char *)&ptyibufbuf[1])-1; +char *ptyip = ((char *)&ptyibufbuf[1])-1; +char ptyibuf2[BUFSIZ]; +unsigned char ctlbuf[BUFSIZ]; +struct strbuf strbufc, strbufd; + +int readstream(); + +#else /* ! STREAMPTY */ + +/* + * I/O data buffers, + * pointers, and counters. + */ +char ptyibuf[BUFSIZ], *ptyip = ptyibuf; +char ptyibuf2[BUFSIZ]; + +#endif /* ! STREAMPTY */ + +int hostinfo = 1; /* do we print login banner? */ + +#ifdef CRAY +extern int newmap; /* nonzero if \n maps to ^M^J */ +int lowpty = 0, highpty; /* low, high pty numbers */ +#endif /* CRAY */ + +int debug = 0; +int keepalive = 1; +char *progname; + +extern void usage P((void)); + +/* + * The string to pass to getopt(). We do it this way so + * that only the actual options that we support will be + * passed off to getopt(). + */ +char valid_opts[] = { + 'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U', +#ifdef AUTHENTICATION + 'a', ':', 'X', ':', +#endif +#ifdef BFTPDAEMON + 'B', +#endif +#ifdef DIAGNOSTICS + 'D', ':', +#endif +#ifdef ENCRYPTION + 'e', ':', +#endif +#if defined(CRAY) && defined(NEWINIT) + 'I', ':', +#endif +#ifdef LINEMODE + 'l', +#endif +#ifdef CRAY + 'r', ':', +#endif +#ifdef SecurID + 's', +#endif + '\0' +}; + +main(argc, argv) + char *argv[]; +{ + struct sockaddr_in from; + int on = 1, fromlen; + register int ch; + extern char *optarg; + extern int optind; +#if defined(IPPROTO_IP) && defined(IP_TOS) + int tos = -1; +#endif + + pfrontp = pbackp = ptyobuf; + netip = netibuf; + nfrontp = nbackp = netobuf; +#ifdef ENCRYPTION + nclearto = 0; +#endif /* ENCRYPTION */ + + progname = *argv; + +#ifdef CRAY + /* + * Get number of pty's before trying to process options, + * which may include changing pty range. + */ + highpty = getnpty(); +#endif /* CRAY */ + + while ((ch = getopt(argc, argv, valid_opts)) != EOF) { + switch(ch) { + +#ifdef AUTHENTICATION + case 'a': + /* + * Check for required authentication level + */ + if (strcmp(optarg, "debug") == 0) { + extern int auth_debug_mode; + auth_debug_mode = 1; + } else if (strcasecmp(optarg, "none") == 0) { + auth_level = 0; + } else if (strcasecmp(optarg, "other") == 0) { + auth_level = AUTH_OTHER; + } else if (strcasecmp(optarg, "user") == 0) { + auth_level = AUTH_USER; + } else if (strcasecmp(optarg, "valid") == 0) { + auth_level = AUTH_VALID; + } else if (strcasecmp(optarg, "off") == 0) { + /* + * This hack turns off authentication + */ + auth_level = -1; + } else { + fprintf(stderr, + "telnetd: unknown authorization level for -a\n"); + } + break; +#endif /* AUTHENTICATION */ + +#ifdef BFTPDAEMON + case 'B': + bftpd++; + break; +#endif /* BFTPDAEMON */ + + case 'd': + if (strcmp(optarg, "ebug") == 0) { + debug++; + break; + } + usage(); + /* NOTREACHED */ + break; + +#ifdef DIAGNOSTICS + case 'D': + /* + * Check for desired diagnostics capabilities. + */ + if (!strcmp(optarg, "report")) { + diagnostic |= TD_REPORT|TD_OPTIONS; + } else if (!strcmp(optarg, "exercise")) { + diagnostic |= TD_EXERCISE; + } else if (!strcmp(optarg, "netdata")) { + diagnostic |= TD_NETDATA; + } else if (!strcmp(optarg, "ptydata")) { + diagnostic |= TD_PTYDATA; + } else if (!strcmp(optarg, "options")) { + diagnostic |= TD_OPTIONS; + } else { + usage(); + /* NOT REACHED */ + } + break; +#endif /* DIAGNOSTICS */ + +#ifdef ENCRYPTION + case 'e': + if (strcmp(optarg, "debug") == 0) { + extern int encrypt_debug_mode; + encrypt_debug_mode = 1; + break; + } + usage(); + /* NOTREACHED */ + break; +#endif /* ENCRYPTION */ + + case 'h': + hostinfo = 0; + break; + +#if defined(CRAY) && defined(NEWINIT) + case 'I': + { + extern char *gen_id; + gen_id = optarg; + break; + } +#endif /* defined(CRAY) && defined(NEWINIT) */ + +#ifdef LINEMODE + case 'l': + alwayslinemode = 1; + break; +#endif /* LINEMODE */ + + case 'k': +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + lmodetype = NO_AUTOKLUDGE; +#else + /* ignore -k option if built without kludge linemode */ +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + break; + + case 'n': + keepalive = 0; + break; + +#ifdef CRAY + case 'r': + { + char *strchr(); + char *c; + + /* + * Allow the specification of alterations + * to the pty search range. It is legal to + * specify only one, and not change the + * other from its default. + */ + c = strchr(optarg, '-'); + if (c) { + *c++ = '\0'; + highpty = atoi(c); + } + if (*optarg != '\0') + lowpty = atoi(optarg); + if ((lowpty > highpty) || (lowpty < 0) || + (highpty > 32767)) { + usage(); + /* NOT REACHED */ + } + break; + } +#endif /* CRAY */ + +#ifdef SecurID + case 's': + /* SecurID required */ + require_SecurID = 1; + break; +#endif /* SecurID */ + case 'S': +#ifdef HAS_GETTOS + if ((tos = parsetos(optarg, "tcp")) < 0) + fprintf(stderr, "%s%s%s\n", + "telnetd: Bad TOS argument '", optarg, + "'; will try to use default TOS"); +#else + fprintf(stderr, "%s%s\n", "TOS option unavailable; ", + "-S flag not supported\n"); +#endif + break; + + case 'u': + utmp_len = atoi(optarg); + break; + + case 'U': + registerd_host_only = 1; + break; + +#ifdef AUTHENTICATION + case 'X': + /* + * Check for invalid authentication types + */ + auth_disable_name(optarg); + break; +#endif /* AUTHENTICATION */ + + default: + fprintf(stderr, "telnetd: %c: unknown option\n", ch); + /* FALLTHROUGH */ + case '?': + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (debug) { + int s, ns, foo; + struct servent *sp; + static struct sockaddr_in sin = { AF_INET }; + + if (argc > 1) { + usage(); + /* NOT REACHED */ + } else if (argc == 1) { + if (sp = getservbyname(*argv, "tcp")) { + sin.sin_port = sp->s_port; + } else { + sin.sin_port = atoi(*argv); + if ((int)sin.sin_port <= 0) { + fprintf(stderr, "telnetd: %s: bad port #\n", *argv); + usage(); + /* NOT REACHED */ + } + sin.sin_port = htons((u_short)sin.sin_port); + } + } else { + sp = getservbyname("telnet", "tcp"); + if (sp == 0) { + fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); + exit(1); + } + sin.sin_port = sp->s_port; + } + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + perror("telnetd: socket");; + exit(1); + } + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { + perror("bind"); + exit(1); + } + if (listen(s, 1) < 0) { + perror("listen"); + exit(1); + } + foo = sizeof sin; + ns = accept(s, (struct sockaddr *)&sin, &foo); + if (ns < 0) { + perror("accept"); + exit(1); + } + (void) dup2(ns, 0); + (void) close(ns); + (void) close(s); +#ifdef convex + } else if (argc == 1) { + ; /* VOID*/ /* Just ignore the host/port name */ +#endif + } else if (argc > 0) { + usage(); + /* NOT REACHED */ + } + +#if defined(_SC_CRAY_SECURE_SYS) + secflag = sysconf(_SC_CRAY_SECURE_SYS); + + /* + * Get socket's security label + */ + if (secflag) { + int szss = sizeof(ss); +#ifdef SO_SEC_MULTI /* 8.0 code */ + int sock_multi; + int szi = sizeof(int); +#endif /* SO_SEC_MULTI */ + + bzero((char *)&dv, sizeof(dv)); + + if (getsysv(&sysv, sizeof(struct sysv)) != 0) { + perror("getsysv"); + exit(1); + } + + /* + * Get socket security label and set device values + * {security label to be set on ttyp device} + */ +#ifdef SO_SEC_MULTI /* 8.0 code */ + if ((getsockopt(0, SOL_SOCKET, SO_SECURITY, + (char *)&ss, &szss) < 0) || + (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI, + (char *)&sock_multi, &szi) < 0)) { + perror("getsockopt"); + exit(1); + } else { + dv.dv_actlvl = ss.ss_actlabel.lt_level; + dv.dv_actcmp = ss.ss_actlabel.lt_compart; + if (!sock_multi) { + dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl; + dv.dv_valcmp = dv.dv_actcmp; + } else { + dv.dv_minlvl = ss.ss_minlabel.lt_level; + dv.dv_maxlvl = ss.ss_maxlabel.lt_level; + dv.dv_valcmp = ss.ss_maxlabel.lt_compart; + } + dv.dv_devflg = 0; + } +#else /* SO_SEC_MULTI */ /* 7.0 code */ + if (getsockopt(0, SOL_SOCKET, SO_SECURITY, + (char *)&ss, &szss) >= 0) { + dv.dv_actlvl = ss.ss_slevel; + dv.dv_actcmp = ss.ss_compart; + dv.dv_minlvl = ss.ss_minlvl; + dv.dv_maxlvl = ss.ss_maxlvl; + dv.dv_valcmp = ss.ss_maxcmp; + } +#endif /* SO_SEC_MULTI */ + } +#endif /* _SC_CRAY_SECURE_SYS */ + + openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); + fromlen = sizeof (from); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + fprintf(stderr, "%s: ", progname); + perror("getpeername"); + _exit(1); + } + if (keepalive && + setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof (on)) < 0) { + syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); + } + +#if defined(IPPROTO_IP) && defined(IP_TOS) + { +# if defined(HAS_GETTOS) + struct tosent *tp; + if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) + tos = tp->t_tos; +# endif + if (tos < 0) + tos = 020; /* Low Delay bit */ + if (tos + && (setsockopt(0, IPPROTO_IP, IP_TOS, + (char *)&tos, sizeof(tos)) < 0) + && (errno != ENOPROTOOPT) ) + syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + } +#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ + net = 0; + doit(&from); + /* NOTREACHED */ +} /* end of main */ + + void +usage() +{ + fprintf(stderr, "Usage: telnetd"); +#ifdef AUTHENTICATION + fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t"); +#endif +#ifdef BFTPDAEMON + fprintf(stderr, " [-B]"); +#endif + fprintf(stderr, " [-debug]"); +#ifdef DIAGNOSTICS + fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); +#endif +#ifdef AUTHENTICATION + fprintf(stderr, " [-edebug]"); +#endif + fprintf(stderr, " [-h]"); +#if defined(CRAY) && defined(NEWINIT) + fprintf(stderr, " [-Iinitid]"); +#endif +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + fprintf(stderr, " [-k]"); +#endif +#ifdef LINEMODE + fprintf(stderr, " [-l]"); +#endif + fprintf(stderr, " [-n]"); +#ifdef CRAY + fprintf(stderr, " [-r[lowpty]-[highpty]]"); +#endif + fprintf(stderr, "\n\t"); +#ifdef SecurID + fprintf(stderr, " [-s]"); +#endif +#ifdef HAS_GETTOS + fprintf(stderr, " [-S tos]"); +#endif +#ifdef AUTHENTICATION + fprintf(stderr, " [-X auth-type]"); +#endif + fprintf(stderr, " [-u utmp_hostname_length] [-U]"); + fprintf(stderr, " [port]\n"); + exit(1); +} + +/* + * getterminaltype + * + * Ask the other end to send along its terminal type and speed. + * Output is the variable terminaltype filled in. + */ +static unsigned char ttytype_sbbuf[] = { + IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE +}; + + int +getterminaltype(name) + char *name; +{ + int retval = -1; + void _gettermname(); + + settimer(baseline); +#if defined(AUTHENTICATION) + /* + * Handle the Authentication option before we do anything else. + */ + send_do(TELOPT_AUTHENTICATION, 1); + while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) + ttloop(); + if (his_state_is_will(TELOPT_AUTHENTICATION)) { + retval = auth_wait(name); + } +#endif + +#ifdef ENCRYPTION + send_will(TELOPT_ENCRYPT, 1); +#endif /* ENCRYPTION */ + send_do(TELOPT_TTYPE, 1); + send_do(TELOPT_TSPEED, 1); + send_do(TELOPT_XDISPLOC, 1); + send_do(TELOPT_NEW_ENVIRON, 1); + send_do(TELOPT_OLD_ENVIRON, 1); + while ( +#ifdef ENCRYPTION + his_do_dont_is_changing(TELOPT_ENCRYPT) || +#endif /* ENCRYPTION */ + his_will_wont_is_changing(TELOPT_TTYPE) || + his_will_wont_is_changing(TELOPT_TSPEED) || + his_will_wont_is_changing(TELOPT_XDISPLOC) || + his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || + his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { + ttloop(); + } +#ifdef ENCRYPTION + /* + * Wait for the negotiation of what type of encryption we can + * send with. If autoencrypt is not set, this will just return. + */ + if (his_state_is_will(TELOPT_ENCRYPT)) { + encrypt_wait(); + } +#endif /* ENCRYPTION */ + if (his_state_is_will(TELOPT_TSPEED)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_XDISPLOC)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_NEW_ENVIRON)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { + static unsigned char sb[] = + { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; + + bcopy(sb, nfrontp, sizeof sb); + nfrontp += sizeof sb; + } + if (his_state_is_will(TELOPT_TTYPE)) { + + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + } + if (his_state_is_will(TELOPT_TSPEED)) { + while (sequenceIs(tspeedsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_XDISPLOC)) { + while (sequenceIs(xdisplocsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_NEW_ENVIRON)) { + while (sequenceIs(environsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_OLD_ENVIRON)) { + while (sequenceIs(oenvironsubopt, baseline)) + ttloop(); + } + if (his_state_is_will(TELOPT_TTYPE)) { + char first[256], last[256]; + + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); + + /* + * If the other side has already disabled the option, then + * we have to just go with what we (might) have already gotten. + */ + if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { + (void) strncpy(first, terminaltype, sizeof(first)); + for(;;) { + /* + * Save the unknown name, and request the next name. + */ + (void) strncpy(last, terminaltype, sizeof(last)); + _gettermname(); + if (terminaltypeok(terminaltype)) + break; + if ((strncmp(last, terminaltype, sizeof(last)) == 0) || + his_state_is_wont(TELOPT_TTYPE)) { + /* + * We've hit the end. If this is the same as + * the first name, just go with it. + */ + if (strncmp(first, terminaltype, sizeof(first)) == 0) + break; + /* + * Get the terminal name one more time, so that + * RFC1091 compliant telnets will cycle back to + * the start of the list. + */ + _gettermname(); + if (strncmp(first, terminaltype, sizeof(first)) != 0) + (void) strncpy(terminaltype, first, sizeof(first)); + break; + } + } + } + } + return(retval); +} /* end of getterminaltype */ + + void +_gettermname() +{ + /* + * If the client turned off the option, + * we can't send another request, so we + * just return. + */ + if (his_state_is_wont(TELOPT_TTYPE)) + return; + settimer(baseline); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); +} + + int +terminaltypeok(s) + char *s; +{ + char buf[1024]; + + if (terminaltype == NULL) + return(1); + + /* + * tgetent() will return 1 if the type is known, and + * 0 if it is not known. If it returns -1, it couldn't + * open the database. But if we can't open the database, + * it won't help to say we failed, because we won't be + * able to verify anything else. So, we treat -1 like 1. + */ + if (tgetent(buf, s) == 0) + return(0); + return(1); +} + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif /* MAXHOSTNAMELEN */ + +char *hostname; +char host_name[MAXHOSTNAMELEN]; +char remote_host_name[MAXHOSTNAMELEN]; + +#ifndef convex +extern void telnet P((int, int)); +#else +extern void telnet P((int, int, char *)); +#endif + +/* + * Get a pty, scan input lines. + */ +doit(who) + struct sockaddr_in *who; +{ + char *host, *inet_ntoa(); + int t; + struct hostent *hp; + int level; + int ptynum; + char user_name[256]; + + /* + * Find an available pty to use. + */ +#ifndef convex + pty = getpty(&ptynum); + if (pty < 0) + fatal(net, "All network ports in use"); +#else + for (;;) { + char *lp; + extern char *line, *getpty(); + + if ((lp = getpty()) == NULL) + fatal(net, "Out of ptys"); + + if ((pty = open(lp, 2)) >= 0) { + strcpy(line,lp); + line[5] = 't'; + break; + } + } +#endif + +#if defined(_SC_CRAY_SECURE_SYS) + /* + * set ttyp line security label + */ + if (secflag) { + char slave_dev[16]; + + sprintf(tty_dev, "/dev/pty/%03d", ptynum); + if (setdevs(tty_dev, &dv) < 0) + fatal(net, "cannot set pty security"); + sprintf(slave_dev, "/dev/ttyp%03d", ptynum); + if (setdevs(slave_dev, &dv) < 0) + fatal(net, "cannot set tty security"); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + /* get name of connected client */ + hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), + who->sin_family); + + if (hp == NULL && registerd_host_only) { + fatal(net, "Couldn't resolve your address into a host name.\r\n\ + Please contact your net administrator"); + } else if (hp && + (strlen(hp->h_name) <= ((utmp_len < 0) ? -utmp_len : utmp_len))) { + host = hp->h_name; + } else { + host = inet_ntoa(who->sin_addr); + } + /* + * We must make a copy because Kerberos is probably going + * to also do a gethost* and overwrite the static data... + */ + strncpy(remote_host_name, host, sizeof(remote_host_name)-1); + remote_host_name[sizeof(remote_host_name)-1] = 0; + host = remote_host_name; + + (void) gethostname(host_name, sizeof (host_name)); + hostname = host_name; + +#if defined(AUTHENTICATION) || defined(ENCRYPTION) + auth_encrypt_init(hostname, host, "TELNETD", 1); +#endif + + init_env(); + /* + * get terminal type. + */ + *user_name = 0; + level = getterminaltype(user_name); + setenv("TERM", terminaltype ? terminaltype : "network", 1); + + /* + * Start up the login process on the slave side of the terminal + */ +#ifndef convex + startslave(host, level, user_name); + +#if defined(_SC_CRAY_SECURE_SYS) + if (secflag) { + if (setulvl(dv.dv_actlvl) < 0) + fatal(net,"cannot setulvl()"); + if (setucmp(dv.dv_actcmp) < 0) + fatal(net, "cannot setucmp()"); + } +#endif /* _SC_CRAY_SECURE_SYS */ + + telnet(net, pty); /* begin server processing */ +#else + telnet(net, pty, host); +#endif + /*NOTREACHED*/ +} /* end of doit */ + +#if defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) + int +Xterm_output(ibufp, obuf, icountp, ocount) + char **ibufp, *obuf; + int *icountp, ocount; +{ + int ret; + ret = term_output(*ibufp, obuf, *icountp, ocount); + *ibufp += *icountp; + *icountp = 0; + return(ret); +} +#define term_output Xterm_output +#endif /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */ + +/* + * Main loop. Select from pty and network, and + * hand data to telnet receiver finite state machine. + */ + void +#ifndef convex +telnet(f, p) +#else +telnet(f, p, host) +#endif + int f, p; +#ifdef convex + char *host; +#endif +{ + int on = 1; +#define TABBUFSIZ 512 + char defent[TABBUFSIZ]; + char defstrs[TABBUFSIZ]; +#undef TABBUFSIZ + char *HE; + char *HN; + char *IM; + void netflush(); + + /* + * Initialize the slc mapping table. + */ + get_slc_defaults(); + + /* + * Do some tests where it is desireable to wait for a response. + * Rather than doing them slowly, one at a time, do them all + * at once. + */ + if (my_state_is_wont(TELOPT_SGA)) + send_will(TELOPT_SGA, 1); + /* + * Is the client side a 4.2 (NOT 4.3) system? We need to know this + * because 4.2 clients are unable to deal with TCP urgent data. + * + * To find out, we send out a "DO ECHO". If the remote system + * answers "WILL ECHO" it is probably a 4.2 client, and we note + * that fact ("WILL ECHO" ==> that the client will echo what + * WE, the server, sends it; it does NOT mean that the client will + * echo the terminal input). + */ + send_do(TELOPT_ECHO, 1); + +#ifdef LINEMODE + if (his_state_is_wont(TELOPT_LINEMODE)) { + /* Query the peer for linemode support by trying to negotiate + * the linemode option. + */ + linemode = 0; + editmode = 0; + send_do(TELOPT_LINEMODE, 1); /* send do linemode */ + } +#endif /* LINEMODE */ + + /* + * Send along a couple of other options that we wish to negotiate. + */ + send_do(TELOPT_NAWS, 1); + send_will(TELOPT_STATUS, 1); + flowmode = 1; /* default flow control state */ + restartany = -1; /* uninitialized... */ + send_do(TELOPT_LFLOW, 1); + + /* + * Spin, waiting for a response from the DO ECHO. However, + * some REALLY DUMB telnets out there might not respond + * to the DO ECHO. So, we spin looking for NAWS, (most dumb + * telnets so far seem to respond with WONT for a DO that + * they don't understand...) because by the time we get the + * response, it will already have processed the DO ECHO. + * Kludge upon kludge. + */ + while (his_will_wont_is_changing(TELOPT_NAWS)) + ttloop(); + + /* + * But... + * The client might have sent a WILL NAWS as part of its + * startup code; if so, we'll be here before we get the + * response to the DO ECHO. We'll make the assumption + * that any implementation that understands about NAWS + * is a modern enough implementation that it will respond + * to our DO ECHO request; hence we'll do another spin + * waiting for the ECHO option to settle down, which is + * what we wanted to do in the first place... + */ + if (his_want_state_is_will(TELOPT_ECHO) && + his_state_is_will(TELOPT_NAWS)) { + while (his_will_wont_is_changing(TELOPT_ECHO)) + ttloop(); + } + /* + * On the off chance that the telnet client is broken and does not + * respond to the DO ECHO we sent, (after all, we did send the + * DO NAWS negotiation after the DO ECHO, and we won't get here + * until a response to the DO NAWS comes back) simulate the + * receipt of a will echo. This will also send a WONT ECHO + * to the client, since we assume that the client failed to + * respond because it believes that it is already in DO ECHO + * mode, which we do not want. + */ + if (his_want_state_is_will(TELOPT_ECHO)) { + DIAG(TD_OPTIONS, + {sprintf(nfrontp, "td: simulating recv\r\n"); + nfrontp += strlen(nfrontp);}); + willoption(TELOPT_ECHO); + } + + /* + * Finally, to clean things up, we turn on our echo. This + * will break stupid 4.2 telnets out of local terminal echo. + */ + + if (my_state_is_wont(TELOPT_ECHO)) + send_will(TELOPT_ECHO, 1); + +#ifndef STREAMSPTY + /* + * Turn on packet mode + */ + (void) ioctl(p, TIOCPKT, (char *)&on); +#endif + +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * Continuing line mode support. If client does not support + * real linemode, attempt to negotiate kludge linemode by sending + * the do timing mark sequence. + */ + if (lmodetype < REAL_LINEMODE) + send_do(TELOPT_TM, 1); +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + /* + * Call telrcv() once to pick up anything received during + * terminal type negotiation, 4.2/4.3 determination, and + * linemode negotiation. + */ + telrcv(); + + (void) ioctl(f, FIONBIO, (char *)&on); + (void) ioctl(p, FIONBIO, (char *)&on); +#if defined(CRAY2) && defined(UNICOS5) + init_termdriver(f, p, interrupt, sendbrk); +#endif + +#if defined(SO_OOBINLINE) + (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, + (char *)&on, sizeof on); +#endif /* defined(SO_OOBINLINE) */ + +#ifdef SIGTSTP + (void) signal(SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + /* + * Ignoring SIGTTOU keeps the kernel from blocking us + * in ttioct() in /sys/tty.c. + */ + (void) signal(SIGTTOU, SIG_IGN); +#endif + + (void) signal(SIGCHLD, cleanup); + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Cray-2 will send a signal when pty modes are changed by slave + * side. Set up signal handler now. + */ + if ((int)signal(SIGUSR1, termstat) < 0) + perror("signal"); + else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) + perror("ioctl:TCSIGME"); + /* + * Make processing loop check terminal characteristics early on. + */ + termstat(); +#endif + +#ifdef TIOCNOTTY + { + register int t; + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + } +#endif + +#if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY) + (void) setsid(); + ioctl(p, TIOCSCTTY, 0); +#endif + + /* + * Show banner that getty never gave. + * + * We put the banner in the pty input buffer. This way, it + * gets carriage return null processing, etc., just like all + * other pty --> client data. + */ + +#if !defined(CRAY) || !defined(NEWINIT) + if (getenv("USER")) + hostinfo = 0; +#endif + + if (getent(defent, "default") == 1) { + char *getstr(); + char *cp=defstrs; + + HE = getstr("he", &cp); + HN = getstr("hn", &cp); + IM = getstr("im", &cp); + if (HN && *HN) + (void) strcpy(host_name, HN); + if (IM == 0) + IM = ""; + } else { + IM = DEFAULT_IM; + HE = 0; + } + edithost(HE, host_name); + if (hostinfo && *IM) + putf(IM, ptyibuf2); + + if (pcc) + (void) strncat(ptyibuf2, ptyip, pcc+1); + ptyip = ptyibuf2; + pcc = strlen(ptyip); +#ifdef LINEMODE + /* + * Last check to make sure all our states are correct. + */ + init_termbuf(); + localstat(); +#endif /* LINEMODE */ + + DIAG(TD_REPORT, + {sprintf(nfrontp, "td: Entering processing loop\r\n"); + nfrontp += strlen(nfrontp);}); + +#ifdef convex + startslave(host); +#endif + + for (;;) { + fd_set ibits, obits, xbits; + register int c; + + if (ncc < 0 && pcc < 0) + break; + +#if defined(CRAY2) && defined(UNICOS5) + if (needtermstat) + _termstat(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + FD_ZERO(&ibits); + FD_ZERO(&obits); + FD_ZERO(&xbits); + /* + * Never look for input if there's still + * stuff in the corresponding output buffer + */ + if (nfrontp - nbackp || pcc > 0) { + FD_SET(f, &obits); + } else { + FD_SET(p, &ibits); + } + if (pfrontp - pbackp || ncc > 0) { + FD_SET(p, &obits); + } else { + FD_SET(f, &ibits); + } + if (!SYNCHing) { + FD_SET(f, &xbits); + } + if ((c = select(16, &ibits, &obits, &xbits, + (struct timeval *)0)) < 1) { + if (c == -1) { + if (errno == EINTR) { + continue; + } + } + sleep(5); + continue; + } + + /* + * Any urgent data? + */ + if (FD_ISSET(net, &xbits)) { + SYNCHing = 1; + } + + /* + * Something to read from the network... + */ + if (FD_ISSET(net, &ibits)) { +#if !defined(SO_OOBINLINE) + /* + * In 4.2 (and 4.3 beta) systems, the + * OOB indication and data handling in the kernel + * is such that if two separate TCP Urgent requests + * come in, one byte of TCP data will be overlaid. + * This is fatal for Telnet, but we try to live + * with it. + * + * In addition, in 4.2 (and...), a special protocol + * is needed to pick up the TCP Urgent data in + * the correct sequence. + * + * What we do is: if we think we are in urgent + * mode, we look to see if we are "at the mark". + * If we are, we do an OOB receive. If we run + * this twice, we will do the OOB receive twice, + * but the second will fail, since the second + * time we were "at the mark", but there wasn't + * any data there (the kernel doesn't reset + * "at the mark" until we do a normal read). + * Once we've read the OOB data, we go ahead + * and do normal reads. + * + * There is also another problem, which is that + * since the OOB byte we read doesn't put us + * out of OOB state, and since that byte is most + * likely the TELNET DM (data mark), we would + * stay in the TELNET SYNCH (SYNCHing) state. + * So, clocks to the rescue. If we've "just" + * received a DM, then we test for the + * presence of OOB data when the receive OOB + * fails (and AFTER we did the normal mode read + * to clear "at the mark"). + */ + if (SYNCHing) { + int atmark; + + (void) ioctl(net, SIOCATMARK, (char *)&atmark); + if (atmark) { + ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); + if ((ncc == -1) && (errno == EINVAL)) { + ncc = read(net, netibuf, sizeof (netibuf)); + if (sequenceIs(didnetreceive, gotDM)) { + SYNCHing = stilloob(net); + } + } + } else { + ncc = read(net, netibuf, sizeof (netibuf)); + } + } else { + ncc = read(net, netibuf, sizeof (netibuf)); + } + settimer(didnetreceive); +#else /* !defined(SO_OOBINLINE)) */ + ncc = read(net, netibuf, sizeof (netibuf)); +#endif /* !defined(SO_OOBINLINE)) */ + if (ncc < 0 && errno == EWOULDBLOCK) + ncc = 0; + else { + if (ncc <= 0) { + break; + } + netip = netibuf; + } + DIAG((TD_REPORT | TD_NETDATA), + {sprintf(nfrontp, "td: netread %d chars\r\n", ncc); + nfrontp += strlen(nfrontp);}); + DIAG(TD_NETDATA, printdata("nd", netip, ncc)); + } + + /* + * Something to read from the pty... + */ + if (FD_ISSET(p, &ibits)) { +#ifndef STREAMSPTY + pcc = read(p, ptyibuf, BUFSIZ); +#else + pcc = readstream(p, ptyibuf, BUFSIZ); +#endif + /* + * On some systems, if we try to read something + * off the master side before the slave side is + * opened, we get EIO. + */ + if (pcc < 0 && (errno == EWOULDBLOCK || +#ifdef EAGAIN + errno == EAGAIN || +#endif + errno == EIO)) { + pcc = 0; + } else { + if (pcc <= 0) + break; +#if !defined(CRAY2) || !defined(UNICOS5) +#ifdef LINEMODE + /* + * If ioctl from pty, pass it through net + */ + if (ptyibuf[0] & TIOCPKT_IOCTL) { + copy_termbuf(ptyibuf+1, pcc-1); + localstat(); + pcc = 1; + } +#endif /* LINEMODE */ + if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { + netclear(); /* clear buffer back */ +#ifndef NO_URGENT + /* + * There are client telnets on some + * operating systems get screwed up + * royally if we send them urgent + * mode data. + */ + *nfrontp++ = IAC; + *nfrontp++ = DM; + neturg = nfrontp-1; /* off by one XXX */ +#endif + } + if (his_state_is_will(TELOPT_LFLOW) && + (ptyibuf[0] & + (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { + int newflow = + ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; + if (newflow != flowmode) { + flowmode = newflow; + (void) sprintf(nfrontp, + "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON + : LFLOW_OFF, + IAC, SE); + nfrontp += 6; + } + } + pcc--; + ptyip = ptyibuf+1; +#else /* defined(CRAY2) && defined(UNICOS5) */ + if (!uselinemode) { + unpcc = pcc; + unptyip = ptyibuf; + pcc = term_output(&unptyip, ptyibuf2, + &unpcc, BUFSIZ); + ptyip = ptyibuf2; + } else + ptyip = ptyibuf; +#endif /* defined(CRAY2) && defined(UNICOS5) */ + } + } + + while (pcc > 0) { + if ((&netobuf[BUFSIZ] - nfrontp) < 2) + break; + c = *ptyip++ & 0377, pcc--; + if (c == IAC) + *nfrontp++ = c; +#if defined(CRAY2) && defined(UNICOS5) + else if (c == '\n' && + my_state_is_wont(TELOPT_BINARY) && newmap) + *nfrontp++ = '\r'; +#endif /* defined(CRAY2) && defined(UNICOS5) */ + *nfrontp++ = c; + if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { + if (pcc > 0 && ((*ptyip & 0377) == '\n')) { + *nfrontp++ = *ptyip++ & 0377; + pcc--; + } else + *nfrontp++ = '\0'; + } + } +#if defined(CRAY2) && defined(UNICOS5) + /* + * If chars were left over from the terminal driver, + * note their existence. + */ + if (!uselinemode && unpcc) { + pcc = unpcc; + unpcc = 0; + ptyip = unptyip; + } +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) + netflush(); + if (ncc > 0) + telrcv(); + if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) + ptyflush(); + } + cleanup(0); +} /* end of telnet */ + +#ifndef TCSIG +# ifdef TIOCSIG +# define TCSIG TIOCSIG +# endif +#endif + +#ifdef STREAMSPTY + +int flowison = -1; /* current state of flow: -1 is unknown */ + +int readstream(p, ibuf, bufsize) + int p; + char *ibuf; + int bufsize; +{ + int flags = 0; + int ret = 0; + struct termios *tsp; + struct termio *tp; + struct iocblk *ip; + char vstop, vstart; + int ixon; + int newflow; + + strbufc.maxlen = BUFSIZ; + strbufc.buf = (char *)ctlbuf; + strbufd.maxlen = bufsize-1; + strbufd.len = 0; + strbufd.buf = ibuf+1; + ibuf[0] = 0; + + ret = getmsg(p, &strbufc, &strbufd, &flags); + if (ret < 0) /* error of some sort -- probably EAGAIN */ + return(-1); + + if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) { + /* data message */ + if (strbufd.len > 0) { /* real data */ + return(strbufd.len + 1); /* count header char */ + } else { + /* nothing there */ + errno = EAGAIN; + return(-1); + } + } + + /* + * It's a control message. Return 1, to look at the flag we set + */ + + switch (ctlbuf[0]) { + case M_FLUSH: + if (ibuf[1] & FLUSHW) + ibuf[0] = TIOCPKT_FLUSHWRITE; + return(1); + + case M_IOCTL: + ip = (struct iocblk *) (ibuf+1); + + switch (ip->ioc_cmd) { + case TCSETS: + case TCSETSW: + case TCSETSF: + tsp = (struct termios *) + (ibuf+1 + sizeof(struct iocblk)); + vstop = tsp->c_cc[VSTOP]; + vstart = tsp->c_cc[VSTART]; + ixon = tsp->c_iflag & IXON; + break; + case TCSETA: + case TCSETAW: + case TCSETAF: + tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk)); + vstop = tp->c_cc[VSTOP]; + vstart = tp->c_cc[VSTART]; + ixon = tp->c_iflag & IXON; + break; + default: + errno = EAGAIN; + return(-1); + } + + newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0; + if (newflow != flowison) { /* it's a change */ + flowison = newflow; + ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP; + return(1); + } + } + + /* nothing worth doing anything about */ + errno = EAGAIN; + return(-1); +} +#endif /* STREAMSPTY */ + +/* + * Send interrupt to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write intr char. + */ + void +interrupt() +{ + ptyflush(); /* half-hearted */ + +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGINT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_IP].sptr ? + (unsigned char)*slctab[SLC_IP].sptr : '\177'; +#endif /* TCSIG */ +} + +/* + * Send quit to process on other side of pty. + * If it is in raw mode, just write NULL; + * otherwise, write quit char. + */ + void +sendbrk() +{ + ptyflush(); /* half-hearted */ +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGQUIT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_ABORT].sptr ? + (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; +#endif /* TCSIG */ +} + + void +sendsusp() +{ +#ifdef SIGTSTP + ptyflush(); /* half-hearted */ +# ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGTSTP); +# else /* TCSIG */ + *pfrontp++ = slctab[SLC_SUSP].sptr ? + (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; +# endif /* TCSIG */ +#endif /* SIGTSTP */ +} + +/* + * When we get an AYT, if ^T is enabled, use that. Otherwise, + * just send back "[Yes]". + */ + void +recv_ayt() +{ +#if defined(SIGINFO) && defined(TCSIG) + if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { + (void) ioctl(pty, TCSIG, (char *)SIGINFO); + return; + } +#endif + (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); + nfrontp += 9; +} + + void +doeof() +{ + init_termbuf(); + +#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) + if (!tty_isediting()) { + extern char oldeofc; + *pfrontp++ = oldeofc; + return; + } +#endif + *pfrontp++ = slctab[SLC_EOF].sptr ? + (unsigned char)*slctab[SLC_EOF].sptr : '\004'; +} diff --git a/libexec/telnetd/telnetd.h b/libexec/telnetd/telnetd.h new file mode 100644 index 0000000..234b973 --- /dev/null +++ b/libexec/telnetd/telnetd.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)telnetd.h 8.1 (Berkeley) 6/4/93 + */ + + +#include "defs.h" +#include "ext.h" + +#ifdef DIAGNOSTICS +#define DIAG(a,b) if (diagnostic & (a)) b +#else +#define DIAG(a,b) +#endif + +/* other external variables */ +extern char **environ; +extern int errno; + diff --git a/libexec/telnetd/termstat.c b/libexec/telnetd/termstat.c new file mode 100644 index 0000000..a3f6931 --- /dev/null +++ b/libexec/telnetd/termstat.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)termstat.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +#include "telnetd.h" + +/* + * local variables + */ +int def_tspeed = -1, def_rspeed = -1; +#ifdef TIOCSWINSZ +int def_row = 0, def_col = 0; +#endif +#ifdef LINEMODE +static int _terminit = 0; +#endif /* LINEMODE */ + +#if defined(CRAY2) && defined(UNICOS5) +int newmap = 1; /* nonzero if \n maps to ^M^J */ +#endif + +#ifdef LINEMODE +/* + * localstat + * + * This function handles all management of linemode. + * + * Linemode allows the client to do the local editing of data + * and send only complete lines to the server. Linemode state is + * based on the state of the pty driver. If the pty is set for + * external processing, then we can use linemode. Further, if we + * can use real linemode, then we can look at the edit control bits + * in the pty to determine what editing the client should do. + * + * Linemode support uses the following state flags to keep track of + * current and desired linemode state. + * alwayslinemode : true if -l was specified on the telnetd + * command line. It means to have linemode on as much as + * possible. + * + * lmodetype: signifies whether the client can + * handle real linemode, or if use of kludgeomatic linemode + * is preferred. It will be set to one of the following: + * REAL_LINEMODE : use linemode option + * NO_KLUDGE : don't initiate kludge linemode. + * KLUDGE_LINEMODE : use kludge linemode + * NO_LINEMODE : client is ignorant of linemode + * + * linemode, uselinemode : linemode is true if linemode + * is currently on, uselinemode is the state that we wish + * to be in. If another function wishes to turn linemode + * on or off, it sets or clears uselinemode. + * + * editmode, useeditmode : like linemode/uselinemode, but + * these contain the edit mode states (edit and trapsig). + * + * The state variables correspond to some of the state information + * in the pty. + * linemode: + * In real linemode, this corresponds to whether the pty + * expects external processing of incoming data. + * In kludge linemode, this more closely corresponds to the + * whether normal processing is on or not. (ICANON in + * system V, or COOKED mode in BSD.) + * If the -l option was specified (alwayslinemode), then + * an attempt is made to force external processing on at + * all times. + * + * The following heuristics are applied to determine linemode + * handling within the server. + * 1) Early on in starting up the server, an attempt is made + * to negotiate the linemode option. If this succeeds + * then lmodetype is set to REAL_LINEMODE and all linemode + * processing occurs in the context of the linemode option. + * 2) If the attempt to negotiate the linemode option failed, + * and the "-k" (don't initiate kludge linemode) isn't set, + * then we try to use kludge linemode. We test for this + * capability by sending "do Timing Mark". If a positive + * response comes back, then we assume that the client + * understands kludge linemode (ech!) and the + * lmodetype flag is set to KLUDGE_LINEMODE. + * 3) Otherwise, linemode is not supported at all and + * lmodetype remains set to NO_LINEMODE (which happens + * to be 0 for convenience). + * 4) At any time a command arrives that implies a higher + * state of linemode support in the client, we move to that + * linemode support. + * + * A short explanation of kludge linemode is in order here. + * 1) The heuristic to determine support for kludge linemode + * is to send a do timing mark. We assume that a client + * that supports timing marks also supports kludge linemode. + * A risky proposition at best. + * 2) Further negotiation of linemode is done by changing the + * the server's state regarding SGA. If server will SGA, + * then linemode is off, if server won't SGA, then linemode + * is on. + */ + void +localstat() +{ + void netflush(); + int need_will_echo = 0; + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Keep track of that ol' CR/NL mapping while we're in the + * neighborhood. + */ + newmap = tty_isnewmap(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + /* + * Check for state of BINARY options. + */ + if (tty_isbinaryin()) { + if (his_want_state_is_wont(TELOPT_BINARY)) + send_do(TELOPT_BINARY, 1); + } else { + if (his_want_state_is_will(TELOPT_BINARY)) + send_dont(TELOPT_BINARY, 1); + } + + if (tty_isbinaryout()) { + if (my_want_state_is_wont(TELOPT_BINARY)) + send_will(TELOPT_BINARY, 1); + } else { + if (my_want_state_is_will(TELOPT_BINARY)) + send_wont(TELOPT_BINARY, 1); + } + + /* + * Check for changes to flow control if client supports it. + */ + flowstat(); + + /* + * Check linemode on/off state + */ + uselinemode = tty_linemode(); + + /* + * If alwayslinemode is on, and pty is changing to turn it off, then + * force linemode back on. + */ + if (alwayslinemode && linemode && !uselinemode) { + uselinemode = 1; + tty_setlinemode(uselinemode); + } + +#ifdef ENCRYPTION + /* + * If the terminal is not echoing, but editing is enabled, + * something like password input is going to happen, so + * if we the other side is not currently sending encrypted + * data, ask the other side to start encrypting. + */ + if (his_state_is_will(TELOPT_ENCRYPT)) { + static int enc_passwd = 0; + if (uselinemode && !tty_isecho() && tty_isediting() + && (enc_passwd == 0) && !decrypt_input) { + encrypt_send_request_start(); + enc_passwd = 1; + } else if (enc_passwd) { + encrypt_send_request_end(); + enc_passwd = 0; + } + } +#endif /* ENCRYPTION */ + + /* + * Do echo mode handling as soon as we know what the + * linemode is going to be. + * If the pty has echo turned off, then tell the client that + * the server will echo. If echo is on, then the server + * will echo if in character mode, but in linemode the + * client should do local echoing. The state machine will + * not send anything if it is unnecessary, so don't worry + * about that here. + * + * If we need to send the WILL ECHO (because echo is off), + * then delay that until after we have changed the MODE. + * This way, when the user is turning off both editing + * and echo, the client will get editing turned off first. + * This keeps the client from going into encryption mode + * and then right back out if it is doing auto-encryption + * when passwords are being typed. + */ + if (uselinemode) { + if (tty_isecho()) + send_wont(TELOPT_ECHO, 1); + else + need_will_echo = 1; +#ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_OK) + lmodetype = KLUDGE_LINEMODE; +#endif + } + + /* + * If linemode is being turned off, send appropriate + * command and then we're all done. + */ + if (!uselinemode && linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_dont(TELOPT_LINEMODE, 1); +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) + send_will(TELOPT_SGA, 1); +# endif /* KLUDGELINEMODE */ + send_will(TELOPT_ECHO, 1); + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * If using real linemode check edit modes for possible later use. + * If we are in kludge linemode, do the SGA negotiation. + */ + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig()) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; +# ifdef KLUDGELINEMODE + } else if (lmodetype == KLUDGE_LINEMODE) { + if (tty_isediting() && uselinemode) + send_wont(TELOPT_SGA, 1); + else + send_will(TELOPT_SGA, 1); + } +# endif /* KLUDGELINEMODE */ + + /* + * Negotiate linemode on if pty state has changed to turn it on. + * Send appropriate command and send along edit mode, then all done. + */ + if (uselinemode && !linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_LINEMODE) { + send_wont(TELOPT_SGA, 1); + } else if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + send_do(TELOPT_LINEMODE, 1); + /* send along edit modes */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; +# ifdef KLUDGELINEMODE + } +# endif /* KLUDGELINEMODE */ + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * None of what follows is of any value if not using + * real linemode. + */ + if (lmodetype < REAL_LINEMODE) + goto done; +# endif /* KLUDGELINEMODE */ + + if (linemode && his_state_is_will(TELOPT_LINEMODE)) { + /* + * If edit mode changed, send edit mode. + */ + if (useeditmode != editmode) { + /* + * Send along appropriate edit mode mask. + */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + /* + * Check for changes to special characters in use. + */ + start_slc(0); + check_slc(); + (void) end_slc(0); + } + +done: + if (need_will_echo) + send_will(TELOPT_ECHO, 1); + /* + * Some things should be deferred until after the pty state has + * been set by the local process. Do those things that have been + * deferred now. This only happens once. + */ + if (_terminit == 0) { + _terminit = 1; + defer_terminit(); + } + + netflush(); + set_termbuf(); + return; + +} /* end of localstat */ +#endif /* LINEMODE */ + +/* + * flowstat + * + * Check for changes to flow control + */ + void +flowstat() +{ + if (his_state_is_will(TELOPT_LFLOW)) { + if (tty_flowmode() != flowmode) { + flowmode = tty_flowmode(); + (void) sprintf(nfrontp, "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + flowmode ? LFLOW_ON : LFLOW_OFF, + IAC, SE); + nfrontp += 6; + } + if (tty_restartany() != restartany) { + restartany = tty_restartany(); + (void) sprintf(nfrontp, "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + restartany ? LFLOW_RESTART_ANY + : LFLOW_RESTART_XON, + IAC, SE); + nfrontp += 6; + } + } +} + +/* + * clientstat + * + * Process linemode related requests from the client. + * Client can request a change to only one of linemode, editmode or slc's + * at a time, and if using kludge linemode, then only linemode may be + * affected. + */ + void +clientstat(code, parm1, parm2) + register int code, parm1, parm2; +{ + void netflush(); + + /* + * Get a copy of terminal characteristics. + */ + init_termbuf(); + + /* + * Process request from client. code tells what it is. + */ + switch (code) { +#ifdef LINEMODE + case TELOPT_LINEMODE: + /* + * Don't do anything unless client is asking us to change + * modes. + */ + uselinemode = (parm1 == WILL); + if (uselinemode != linemode) { +# ifdef KLUDGELINEMODE + /* + * If using kludge linemode, make sure that + * we can do what the client asks. + * We can not turn off linemode if alwayslinemode + * and the ICANON bit is set. + */ + if (lmodetype == KLUDGE_LINEMODE) { + if (alwayslinemode && tty_isediting()) { + uselinemode = 1; + } + } + + /* + * Quit now if we can't do it. + */ + if (uselinemode == linemode) + return; + + /* + * If using real linemode and linemode is being + * turned on, send along the edit mode mask. + */ + if (lmodetype == REAL_LINEMODE && uselinemode) +# else /* KLUDGELINEMODE */ + if (uselinemode) +# endif /* KLUDGELINEMODE */ + { + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig) + useeditmode |= MODE_TRAPSIG; + if (tty_issofttab()) + useeditmode |= MODE_SOFT_TAB; + if (tty_islitecho()) + useeditmode |= MODE_LIT_ECHO; + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode, IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + tty_setlinemode(uselinemode); + + linemode = uselinemode; + + if (!linemode) + send_will(TELOPT_ECHO, 1); + } + break; + + case LM_MODE: + { + register int ack, changed; + + /* + * Client has sent along a mode mask. If it agrees with + * what we are currently doing, ignore it; if not, it could + * be viewed as a request to change. Note that the server + * will change to the modes in an ack if it is different from + * what we currently have, but we will not ack the ack. + */ + useeditmode &= MODE_MASK; + ack = (useeditmode & MODE_ACK); + useeditmode &= ~MODE_ACK; + + if (changed = (useeditmode ^ editmode)) { + /* + * This check is for a timing problem. If the + * state of the tty has changed (due to the user + * application) we need to process that info + * before we write in the state contained in the + * ack!!! This gets out the new MODE request, + * and when the ack to that command comes back + * we'll set it and be in the right mode. + */ + if (ack) + localstat(); + if (changed & MODE_EDIT) + tty_setedit(useeditmode & MODE_EDIT); + + if (changed & MODE_TRAPSIG) + tty_setsig(useeditmode & MODE_TRAPSIG); + + if (changed & MODE_SOFT_TAB) + tty_setsofttab(useeditmode & MODE_SOFT_TAB); + + if (changed & MODE_LIT_ECHO) + tty_setlitecho(useeditmode & MODE_LIT_ECHO); + + set_termbuf(); + + if (!ack) { + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode|MODE_ACK, + IAC, SE); + nfrontp += 7; + } + + editmode = useeditmode; + } + + break; + + } /* end of case LM_MODE */ +#endif /* LINEMODE */ + + case TELOPT_NAWS: +#ifdef TIOCSWINSZ + { + struct winsize ws; + + def_col = parm1; + def_row = parm2; +#ifdef LINEMODE + /* + * Defer changing window size until after terminal is + * initialized. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + + /* + * Change window size as requested by client. + */ + + ws.ws_col = parm1; + ws.ws_row = parm2; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif /* TIOCSWINSZ */ + + break; + + case TELOPT_TSPEED: + { + def_tspeed = parm1; + def_rspeed = parm2; +#ifdef LINEMODE + /* + * Defer changing the terminal speed. + */ + if (terminit() == 0) + return; +#endif /* LINEMODE */ + /* + * Change terminal speed as requested by client. + * We set the receive speed first, so that if we can't + * store seperate receive and transmit speeds, the transmit + * speed will take precedence. + */ + tty_rspeed(parm2); + tty_tspeed(parm1); + set_termbuf(); + + break; + + } /* end of case TELOPT_TSPEED */ + + default: + /* What? */ + break; + } /* end of switch */ + +#if defined(CRAY2) && defined(UNICOS5) + /* + * Just in case of the likely event that we changed the pty state. + */ + rcv_ioctl(); +#endif /* defined(CRAY2) && defined(UNICOS5) */ + + netflush(); + +} /* end of clientstat */ + +#if defined(CRAY2) && defined(UNICOS5) + void +termstat() +{ + needtermstat = 1; +} + + void +_termstat() +{ + needtermstat = 0; + init_termbuf(); + localstat(); + rcv_ioctl(); +} +#endif /* defined(CRAY2) && defined(UNICOS5) */ + +#ifdef LINEMODE +/* + * defer_terminit + * + * Some things should not be done until after the login process has started + * and all the pty modes are set to what they are supposed to be. This + * function is called when the pty state has been processed for the first time. + * It calls other functions that do things that were deferred in each module. + */ + void +defer_terminit() +{ + + /* + * local stuff that got deferred. + */ + if (def_tspeed != -1) { + clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); + def_tspeed = def_rspeed = 0; + } + +#ifdef TIOCSWINSZ + if (def_col || def_row) { + struct winsize ws; + + bzero((char *)&ws, sizeof(ws)); + ws.ws_col = def_col; + ws.ws_row = def_row; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif + + /* + * The only other module that currently defers anything. + */ + deferslc(); + +} /* end of defer_terminit */ + +/* + * terminit + * + * Returns true if the pty state has been processed yet. + */ + int +terminit() +{ + return(_terminit); + +} /* end of terminit */ +#endif /* LINEMODE */ diff --git a/libexec/telnetd/utility.c b/libexec/telnetd/utility.c new file mode 100644 index 0000000..8c08bdc --- /dev/null +++ b/libexec/telnetd/utility.c @@ -0,0 +1,1192 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)utility.c 8.2 (Berkeley) 12/15/93"; +#endif /* not lint */ + +#define PRINTOPTIONS +#include "telnetd.h" + +/* + * utility functions performing io related tasks + */ + +/* + * ttloop + * + * A small subroutine to flush the network output buffer, get some data + * from the network, and pass it through the telnet state machine. We + * also flush the pty input buffer (by dropping its data) if it becomes + * too full. + */ + + void +ttloop() +{ + void netflush(); + + DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop\r\n"); + nfrontp += strlen(nfrontp);}); + if (nfrontp-nbackp) { + netflush(); + } + ncc = read(net, netibuf, sizeof netibuf); + if (ncc < 0) { + syslog(LOG_INFO, "ttloop: read: %m\n"); + exit(1); + } else if (ncc == 0) { + syslog(LOG_INFO, "ttloop: peer died: %m\n"); + exit(1); + } + DIAG(TD_REPORT, {sprintf(nfrontp, "td: ttloop read %d chars\r\n", ncc); + nfrontp += strlen(nfrontp);}); + netip = netibuf; + telrcv(); /* state machine */ + if (ncc > 0) { + pfrontp = pbackp = ptyobuf; + telrcv(); + } +} /* end of ttloop */ + +/* + * Check a descriptor to see if out of band data exists on it. + */ + int +stilloob(s) + int s; /* socket number */ +{ + static struct timeval timeout = { 0 }; + fd_set excepts; + int value; + + do { + FD_ZERO(&excepts); + FD_SET(s, &excepts); + value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); + } while ((value == -1) && (errno == EINTR)); + + if (value < 0) { + fatalperror(pty, "select"); + } + if (FD_ISSET(s, &excepts)) { + return 1; + } else { + return 0; + } +} + + void +ptyflush() +{ + int n; + + if ((n = pfrontp - pbackp) > 0) { + DIAG((TD_REPORT | TD_PTYDATA), + { sprintf(nfrontp, "td: ptyflush %d chars\r\n", n); + nfrontp += strlen(nfrontp); }); + DIAG(TD_PTYDATA, printdata("pd", pbackp, n)); + n = write(pty, pbackp, n); + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(0); + } + pbackp += n; + if (pbackp == pfrontp) + pbackp = pfrontp = ptyobuf; +} + +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ + char * +nextitem(current) + char *current; +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + register char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} /* end of nextitem */ + + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ + void +netclear() +{ + register char *thisitem, *next; + char *good; +#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ + ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) + +#ifdef ENCRYPTION + thisitem = nclearto > netobuf ? nclearto : netobuf; +#else /* ENCRYPTION */ + thisitem = netobuf; +#endif /* ENCRYPTION */ + + while ((next = nextitem(thisitem)) <= nbackp) { + thisitem = next; + } + + /* Now, thisitem is first before/at boundary. */ + +#ifdef ENCRYPTION + good = nclearto > netobuf ? nclearto : netobuf; +#else /* ENCRYPTION */ + good = netobuf; /* where the good bytes go */ +#endif /* ENCRYPTION */ + + while (nfrontp > thisitem) { + if (wewant(thisitem)) { + int length; + + next = thisitem; + do { + next = nextitem(next); + } while (wewant(next) && (nfrontp > next)); + length = next-thisitem; + bcopy(thisitem, good, length); + good += length; + thisitem = next; + } else { + thisitem = nextitem(thisitem); + } + } + + nbackp = netobuf; + nfrontp = good; /* next byte to be sent */ + neturg = 0; +} /* end of netclear */ + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + */ + void +netflush() +{ + int n; + extern int not42; + + if ((n = nfrontp - nbackp) > 0) { + DIAG(TD_REPORT, + { sprintf(nfrontp, "td: netflush %d chars\r\n", n); + n += strlen(nfrontp); /* get count first */ + nfrontp += strlen(nfrontp); /* then move pointer */ + }); +#ifdef ENCRYPTION + if (encrypt_output) { + char *s = nclearto ? nclearto : nbackp; + if (nfrontp - s > 0) { + (*encrypt_output)((unsigned char *)s, nfrontp-s); + nclearto = nfrontp; + } + } +#endif /* ENCRYPTION */ + /* + * if no urgent data, or if the other side appears to be an + * old 4.2 client (and thus unable to survive TCP urgent data), + * write the entire buffer in non-OOB mode. + */ + if ((neturg == 0) || (not42 == 0)) { + n = write(net, nbackp, n); /* normal write */ + } else { + n = neturg - nbackp; + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + if (n > 1) { + n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ + } else { + n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ + } + } + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(0); + } + nbackp += n; +#ifdef ENCRYPTION + if (nbackp > nclearto) + nclearto = 0; +#endif /* ENCRYPTION */ + if (nbackp >= neturg) { + neturg = 0; + } + if (nbackp == nfrontp) { + nbackp = nfrontp = netobuf; +#ifdef ENCRYPTION + nclearto = 0; +#endif /* ENCRYPTION */ + } + return; +} /* end of netflush */ + + +/* + * writenet + * + * Just a handy little function to write a bit of raw data to the net. + * It will force a transmit of the buffer if necessary + * + * arguments + * ptr - A pointer to a character string to write + * len - How many bytes to write + */ + void +writenet(ptr, len) + register unsigned char *ptr; + register int len; +{ + /* flush buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < len) { + /* if this fails, don't worry, buffer is a little big */ + netflush(); + } + + bcopy(ptr, nfrontp, len); + nfrontp += len; + +} /* end of writenet */ + + +/* + * miscellaneous functions doing a variety of little jobs follow ... + */ + + + void +fatal(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ]; + + (void) sprintf(buf, "telnetd: %s.\r\n", msg); +#ifdef ENCRYPTION + if (encrypt_output) { + /* + * Better turn off encryption first.... + * Hope it flushes... + */ + encrypt_send_end(); + netflush(); + } +#endif /* ENCRYPTION */ + (void) write(f, buf, (int)strlen(buf)); + sleep(1); /*XXX*/ + exit(1); +} + + void +fatalperror(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ], *strerror(); + + (void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno)); + fatal(f, buf); +} + +char editedhost[32]; + + void +edithost(pat, host) + register char *pat; + register char *host; +{ + register char *res = editedhost; + char *strncpy(); + + if (!pat) + pat = ""; + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + (void) strncpy(res, host, + sizeof editedhost - (res - editedhost) -1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +static char *putlocation; + + void +putstr(s) + register char *s; +{ + + while (*s) + putchr(*s++); +} + + void +putchr(cc) + int cc; +{ + *putlocation++ = cc; +} + +/* + * This is split on two lines so that SCCS will not see the M + * between two % signs and expand it... + */ +static char fmtstr[] = { "%l:%M\ +%P on %A, %d %B %Y" }; + + void +putf(cp, where) + register char *cp; + char *where; +{ + char *slash; + time_t t; + char db[100]; +#ifdef STREAMSPTY + extern char *index(); +#else + extern char *rindex(); +#endif + + putlocation = where; + + while (*cp) { + if (*cp != '%') { + putchr(*cp++); + continue; + } + switch (*++cp) { + + case 't': +#ifdef STREAMSPTY + /* names are like /dev/pts/2 -- we want pts/2 */ + slash = index(line+1, '/'); +#else + slash = rindex(line, '/'); +#endif + if (slash == (char *) 0) + putstr(line); + else + putstr(&slash[1]); + break; + + case 'h': + putstr(editedhost); + break; + + case 'd': + (void)time(&t); + (void)strftime(db, sizeof(db), fmtstr, localtime(&t)); + putstr(db); + break; + + case '%': + putchr('%'); + break; + } + cp++; + } +} + +#ifdef DIAGNOSTICS +/* + * Print telnet options and commands in plain text, if possible. + */ + void +printoption(fmt, option) + register char *fmt; + register int option; +{ + if (TELOPT_OK(option)) + sprintf(nfrontp, "%s %s\r\n", fmt, TELOPT(option)); + else if (TELCMD_OK(option)) + sprintf(nfrontp, "%s %s\r\n", fmt, TELCMD(option)); + else + sprintf(nfrontp, "%s %d\r\n", fmt, option); + nfrontp += strlen(nfrontp); + return; +} + + void +printsub(direction, pointer, length) + char direction; /* '<' or '>' */ + unsigned char *pointer; /* where suboption data sits */ + int length; /* length of suboption data */ +{ + register int i; + char buf[512]; + + if (!(diagnostic & TD_OPTIONS)) + return; + + if (direction) { + sprintf(nfrontp, "td: %s suboption ", + direction == '<' ? "recv" : "send"); + nfrontp += strlen(nfrontp); + if (length >= 3) { + register int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) { + sprintf(nfrontp, "(terminated by "); + nfrontp += strlen(nfrontp); + if (TELOPT_OK(i)) + sprintf(nfrontp, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + sprintf(nfrontp, "%s ", TELCMD(i)); + else + sprintf(nfrontp, "%d ", i); + nfrontp += strlen(nfrontp); + if (TELOPT_OK(j)) + sprintf(nfrontp, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + sprintf(nfrontp, "%s", TELCMD(j)); + else + sprintf(nfrontp, "%d", j); + nfrontp += strlen(nfrontp); + sprintf(nfrontp, ", not IAC SE!) "); + nfrontp += strlen(nfrontp); + } + } + length -= 2; + } + if (length < 1) { + sprintf(nfrontp, "(Empty suboption??\?)"); + nfrontp += strlen(nfrontp); + return; + } + switch (pointer[0]) { + case TELOPT_TTYPE: + sprintf(nfrontp, "TERMINAL-TYPE "); + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND"); + break; + default: + sprintf(nfrontp, + "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + nfrontp += strlen(nfrontp); + break; + case TELOPT_TSPEED: + sprintf(nfrontp, "TERMINAL-SPEED"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, " IS %.*s", length-2, (char *)pointer+2); + nfrontp += strlen(nfrontp); + break; + default: + if (pointer[1] == 1) + sprintf(nfrontp, " SEND"); + else + sprintf(nfrontp, " %d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; + + case TELOPT_LFLOW: + sprintf(nfrontp, "TOGGLE-FLOW-CONTROL"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case LFLOW_OFF: + sprintf(nfrontp, " OFF"); break; + case LFLOW_ON: + sprintf(nfrontp, " ON"); break; + case LFLOW_RESTART_ANY: + sprintf(nfrontp, " RESTART-ANY"); break; + case LFLOW_RESTART_XON: + sprintf(nfrontp, " RESTART-XON"); break; + default: + sprintf(nfrontp, " %d (unknown)", pointer[1]); + } + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case TELOPT_NAWS: + sprintf(nfrontp, "NAWS"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + if (length == 2) { + sprintf(nfrontp, " ?%d?", pointer[1]); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, " %d %d (%d)", + pointer[1], pointer[2], + (int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2]))); + nfrontp += strlen(nfrontp); + if (length == 4) { + sprintf(nfrontp, " ?%d?", pointer[3]); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, " %d %d (%d)", + pointer[3], pointer[4], + (int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4]))); + nfrontp += strlen(nfrontp); + for (i = 5; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case TELOPT_LINEMODE: + sprintf(nfrontp, "LINEMODE "); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case WILL: + sprintf(nfrontp, "WILL "); + goto common; + case WONT: + sprintf(nfrontp, "WONT "); + goto common; + case DO: + sprintf(nfrontp, "DO "); + goto common; + case DONT: + sprintf(nfrontp, "DONT "); + common: + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(no option??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[2]) { + case LM_FORWARDMASK: + sprintf(nfrontp, "Forward Mask"); + nfrontp += strlen(nfrontp); + for (i = 3; i < length; i++) { + sprintf(nfrontp, " %x", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + default: + sprintf(nfrontp, "%d (unknown)", pointer[2]); + nfrontp += strlen(nfrontp); + for (i = 3; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; + + case LM_SLC: + sprintf(nfrontp, "SLC"); + nfrontp += strlen(nfrontp); + for (i = 2; i < length - 2; i += 3) { + if (SLC_NAME_OK(pointer[i+SLC_FUNC])) + sprintf(nfrontp, " %s", SLC_NAME(pointer[i+SLC_FUNC])); + else + sprintf(nfrontp, " %d", pointer[i+SLC_FUNC]); + nfrontp += strlen(nfrontp); + switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) { + case SLC_NOSUPPORT: + sprintf(nfrontp, " NOSUPPORT"); break; + case SLC_CANTCHANGE: + sprintf(nfrontp, " CANTCHANGE"); break; + case SLC_VARIABLE: + sprintf(nfrontp, " VARIABLE"); break; + case SLC_DEFAULT: + sprintf(nfrontp, " DEFAULT"); break; + } + nfrontp += strlen(nfrontp); + sprintf(nfrontp, "%s%s%s", + pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "", + pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : ""); + nfrontp += strlen(nfrontp); + if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN| + SLC_FLUSHOUT| SLC_LEVELBITS)) { + sprintf(nfrontp, "(0x%x)", pointer[i+SLC_FLAGS]); + nfrontp += strlen(nfrontp); + } + sprintf(nfrontp, " %d;", pointer[i+SLC_VALUE]); + nfrontp += strlen(nfrontp); + if ((pointer[i+SLC_VALUE] == IAC) && + (pointer[i+SLC_VALUE+1] == IAC)) + i++; + } + for (; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + + case LM_MODE: + sprintf(nfrontp, "MODE "); + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(no mode??\?)"); + nfrontp += strlen(nfrontp); + break; + } + { + char tbuf[32]; + sprintf(tbuf, "%s%s%s%s%s", + pointer[2]&MODE_EDIT ? "|EDIT" : "", + pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "", + pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "", + pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "", + pointer[2]&MODE_ACK ? "|ACK" : ""); + sprintf(nfrontp, "%s", tbuf[1] ? &tbuf[1] : "0"); + nfrontp += strlen(nfrontp); + } + if (pointer[2]&~(MODE_EDIT|MODE_TRAPSIG|MODE_ACK)) { + sprintf(nfrontp, " (0x%x)", pointer[2]); + nfrontp += strlen(nfrontp); + } + for (i = 3; i < length; i++) { + sprintf(nfrontp, " ?0x%x?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + default: + sprintf(nfrontp, "%d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + } + break; + + case TELOPT_STATUS: { + register char *cp; + register int j, k; + + sprintf(nfrontp, "STATUS"); + nfrontp += strlen(nfrontp); + + switch (pointer[1]) { + default: + if (pointer[1] == TELQUAL_SEND) + sprintf(nfrontp, " SEND"); + else + sprintf(nfrontp, " %d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + case TELQUAL_IS: + sprintf(nfrontp, " IS\r\n"); + nfrontp += strlen(nfrontp); + + for (i = 2; i < length; i++) { + switch(pointer[i]) { + case DO: cp = "DO"; goto common2; + case DONT: cp = "DONT"; goto common2; + case WILL: cp = "WILL"; goto common2; + case WONT: cp = "WONT"; goto common2; + common2: + i++; + if (TELOPT_OK(pointer[i])) + sprintf(nfrontp, " %s %s", cp, TELOPT(pointer[i])); + else + sprintf(nfrontp, " %s %d", cp, pointer[i]); + nfrontp += strlen(nfrontp); + + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); + break; + + case SB: + sprintf(nfrontp, " SB "); + nfrontp += strlen(nfrontp); + i++; + j = k = i; + while (j < length) { + if (pointer[j] == SE) { + if (j+1 == length) + break; + if (pointer[j+1] == SE) + j++; + else + break; + } + pointer[k++] = pointer[j++]; + } + printsub(0, &pointer[i], k - i); + if (i < length) { + sprintf(nfrontp, " SE"); + nfrontp += strlen(nfrontp); + i = j; + } else + i = j - 1; + + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); + + break; + + default: + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + break; + } + } + break; + } + break; + } + + case TELOPT_XDISPLOC: + sprintf(nfrontp, "X-DISPLAY-LOCATION "); + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS \"%.*s\"", length-2, (char *)pointer+2); + break; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND"); + break; + default: + sprintf(nfrontp, "- unknown qualifier %d (0x%x).", + pointer[1], pointer[1]); + } + nfrontp += strlen(nfrontp); + break; + + case TELOPT_NEW_ENVIRON: + sprintf(nfrontp, "NEW-ENVIRON "); + goto env_common1; + case TELOPT_OLD_ENVIRON: + sprintf(nfrontp, "OLD-ENVIRON"); + env_common1: + nfrontp += strlen(nfrontp); + switch (pointer[1]) { + case TELQUAL_IS: + sprintf(nfrontp, "IS "); + goto env_common; + case TELQUAL_SEND: + sprintf(nfrontp, "SEND "); + goto env_common; + case TELQUAL_INFO: + sprintf(nfrontp, "INFO "); + env_common: + nfrontp += strlen(nfrontp); + { + register int noquote = 2; + for (i = 2; i < length; i++ ) { + switch (pointer[i]) { + case NEW_ENV_VAR: + sprintf(nfrontp, "\" VAR " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case NEW_ENV_VALUE: + sprintf(nfrontp, "\" VALUE " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case ENV_ESC: + sprintf(nfrontp, "\" ESC " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + case ENV_USERVAR: + sprintf(nfrontp, "\" USERVAR " + noquote); + nfrontp += strlen(nfrontp); + noquote = 2; + break; + + default: + def_case: + if (isprint(pointer[i]) && pointer[i] != '"') { + if (noquote) { + *nfrontp++ = '"'; + noquote = 0; + } + *nfrontp++ = pointer[i]; + } else { + sprintf(nfrontp, "\" %03o " + noquote, + pointer[i]); + nfrontp += strlen(nfrontp); + noquote = 2; + } + break; + } + } + if (!noquote) + *nfrontp++ = '"'; + break; + } + } + break; + +#if defined(AUTHENTICATION) + case TELOPT_AUTHENTICATION: + sprintf(nfrontp, "AUTHENTICATION"); + nfrontp += strlen(nfrontp); + + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case TELQUAL_REPLY: + case TELQUAL_IS: + sprintf(nfrontp, " %s ", (pointer[1] == TELQUAL_IS) ? + "IS" : "REPLY"); + nfrontp += strlen(nfrontp); + if (AUTHTYPE_NAME_OK(pointer[2])) + sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[2])); + else + sprintf(nfrontp, "%d ", pointer[2]); + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, "(partial suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, "%s|%s", + ((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + nfrontp += strlen(nfrontp); + + auth_printsub(&pointer[1], length - 1, buf, sizeof(buf)); + sprintf(nfrontp, "%s", buf); + nfrontp += strlen(nfrontp); + break; + + case TELQUAL_SEND: + i = 2; + sprintf(nfrontp, " SEND "); + nfrontp += strlen(nfrontp); + while (i < length) { + if (AUTHTYPE_NAME_OK(pointer[i])) + sprintf(nfrontp, "%s ", AUTHTYPE_NAME(pointer[i])); + else + sprintf(nfrontp, "%d ", pointer[i]); + nfrontp += strlen(nfrontp); + if (++i >= length) { + sprintf(nfrontp, "(partial suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + sprintf(nfrontp, "%s|%s ", + ((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ? + "CLIENT" : "SERVER", + ((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ? + "MUTUAL" : "ONE-WAY"); + nfrontp += strlen(nfrontp); + ++i; + } + break; + + case TELQUAL_NAME: + i = 2; + sprintf(nfrontp, " NAME \""); + nfrontp += strlen(nfrontp); + while (i < length) + *nfrontp += pointer[i++]; + *nfrontp += '"'; + break; + + default: + for (i = 2; i < length; i++) { + sprintf(nfrontp, " ?%d?", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; +#endif + +#ifdef ENCRYPTION + case TELOPT_ENCRYPT: + sprintf(nfrontp, "ENCRYPT"); + nfrontp += strlen(nfrontp); + if (length < 2) { + sprintf(nfrontp, " (empty suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + switch (pointer[1]) { + case ENCRYPT_START: + sprintf(nfrontp, " START"); + nfrontp += strlen(nfrontp); + break; + + case ENCRYPT_END: + sprintf(nfrontp, " END"); + nfrontp += strlen(nfrontp); + break; + + case ENCRYPT_REQSTART: + sprintf(nfrontp, " REQUEST-START"); + nfrontp += strlen(nfrontp); + break; + + case ENCRYPT_REQEND: + sprintf(nfrontp, " REQUEST-END"); + nfrontp += strlen(nfrontp); + break; + + case ENCRYPT_IS: + case ENCRYPT_REPLY: + sprintf(nfrontp, " %s ", (pointer[1] == ENCRYPT_IS) ? + "IS" : "REPLY"); + nfrontp += strlen(nfrontp); + if (length < 3) { + sprintf(nfrontp, " (partial suboption??\?)"); + nfrontp += strlen(nfrontp); + break; + } + if (ENCTYPE_NAME_OK(pointer[2])) + sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[2])); + else + sprintf(nfrontp, " %d (unknown)", pointer[2]); + nfrontp += strlen(nfrontp); + + encrypt_printsub(&pointer[1], length - 1, buf, sizeof(buf)); + sprintf(nfrontp, "%s", buf); + nfrontp += strlen(nfrontp); + break; + + case ENCRYPT_SUPPORT: + i = 2; + sprintf(nfrontp, " SUPPORT "); + nfrontp += strlen(nfrontp); + while (i < length) { + if (ENCTYPE_NAME_OK(pointer[i])) + sprintf(nfrontp, "%s ", ENCTYPE_NAME(pointer[i])); + else + sprintf(nfrontp, "%d ", pointer[i]); + nfrontp += strlen(nfrontp); + i++; + } + break; + + case ENCRYPT_ENC_KEYID: + sprintf(nfrontp, " ENC_KEYID", pointer[1]); + nfrontp += strlen(nfrontp); + goto encommon; + + case ENCRYPT_DEC_KEYID: + sprintf(nfrontp, " DEC_KEYID", pointer[1]); + nfrontp += strlen(nfrontp); + goto encommon; + + default: + sprintf(nfrontp, " %d (unknown)", pointer[1]); + nfrontp += strlen(nfrontp); + encommon: + for (i = 2; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + break; +#endif /* ENCRYPTION */ + + default: + if (TELOPT_OK(pointer[0])) + sprintf(nfrontp, "%s (unknown)", TELOPT(pointer[0])); + else + sprintf(nfrontp, "%d (unknown)", pointer[i]); + nfrontp += strlen(nfrontp); + for (i = 1; i < length; i++) { + sprintf(nfrontp, " %d", pointer[i]); + nfrontp += strlen(nfrontp); + } + break; + } + sprintf(nfrontp, "\r\n"); + nfrontp += strlen(nfrontp); +} + +/* + * Dump a data buffer in hex and ascii to the output data stream. + */ + void +printdata(tag, ptr, cnt) + register char *tag; + register char *ptr; + register int cnt; +{ + register int i; + char xbuf[30]; + + while (cnt) { + /* flush net output buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < 80) { + netflush(); + } + + /* add a line of output */ + sprintf(nfrontp, "%s: ", tag); + nfrontp += strlen(nfrontp); + for (i = 0; i < 20 && cnt; i++) { + sprintf(nfrontp, "%02x", *ptr); + nfrontp += strlen(nfrontp); + if (isprint(*ptr)) { + xbuf[i] = *ptr; + } else { + xbuf[i] = '.'; + } + if (i % 2) { + *nfrontp = ' '; + nfrontp++; + } + cnt--; + ptr++; + } + xbuf[i] = '\0'; + sprintf(nfrontp, " %s\r\n", xbuf ); + nfrontp += strlen(nfrontp); + } +} +#endif /* DIAGNOSTICS */ diff --git a/libexec/tftpd/Makefile b/libexec/tftpd/Makefile new file mode 100644 index 0000000..f94acc0 --- /dev/null +++ b/libexec/tftpd/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= tftpd +SRCS= tftpd.c tftpsubs.c +MAN8= tftpd.0 +CFLAGS+=-I${.CURDIR}/../../usr.bin/tftp +.PATH: ${.CURDIR}/../../usr.bin/tftp + +.include <bsd.prog.mk> diff --git a/libexec/tftpd/tftpd.8 b/libexec/tftpd/tftpd.8 new file mode 100644 index 0000000..430c1c4 --- /dev/null +++ b/libexec/tftpd/tftpd.8 @@ -0,0 +1,106 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 BY THE REGENTS 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. +.\" +.\" @(#)tftpd.8 8.1 (Berkeley) 6/4/93 +.\" +.Dd June 4, 1993 +.Dt TFTPD 8 +.Os BSD 4.2 +.Sh NAME +.Nm tftpd +.Nd +Internet Trivial File Transfer Protocol server +.Sh SYNOPSIS +.Nm tftpd +.Op Fl l +.Op Fl n +.Op Ar directory ... +.Sh DESCRIPTION +.Nm Tftpd +is a server which supports the +Internet Trivial File Transfer +Protocol (\c +.Tn RFC 783). +The +.Tn TFTP +server operates +at the port indicated in the +.Ql tftp +service description; +see +.Xr services 5 . +The server is normally started by +.Xr inetd 8 . +.Pp +The use of +.Xr tftp 1 +does not require an account or password on the remote system. +Due to the lack of authentication information, +.Nm tftpd +will allow only publicly readable files to be +accessed. +Files containing the string ``/\|\fB.\|.\fP\|/'' are not allowed. +Files may be written only if they already exist and are publicly writable. +Note that this extends the concept of +.Dq public +to include +all users on all hosts that can be reached through the network; +this may not be appropriate on all systems, and its implications +should be considered before enabling tftp service. +The server should have the user ID with the lowest possible privilege. +.Pp +Access to files may be restricted by invoking +.Nm tftpd +with a list of directories by including up to 20 pathnames +as server program arguments in +.Pa /etc/inetd.conf . +In this case access is restricted to files whose +names are prefixed by the one of the given directories. +The given directories are also treated as a search path for +relative filename requests. +.Pp +The options are: +.Bl -tag -width Ds +.It Fl l +Logs all requests using +.Xr syslog 3 . +.It Fl n +Suppresses negative acknowledgement of requests for nonexistent +relative filenames. +.El +.Sh SEE ALSO +.Xr tftp 1 , +.Xr inetd 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/libexec/tftpd/tftpd.c b/libexec/tftpd/tftpd.c new file mode 100644 index 0000000..2c74e3d --- /dev/null +++ b/libexec/tftpd/tftpd.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)tftpd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * Trivial file transfer protocol server. + * + * This version includes many modifications by Jim Guyton + * <guyton@rand-unix>. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/tftp.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "tftpsubs.h" + +#define TIMEOUT 5 + +int peer; +int rexmtval = TIMEOUT; +int maxtimeout = 5*TIMEOUT; + +#define PKTSIZE SEGSIZE+4 +char buf[PKTSIZE]; +char ackbuf[PKTSIZE]; +struct sockaddr_in from; +int fromlen; + +void tftp __P((struct tftphdr *, int)); + +/* + * Null-terminated directory prefix list for absolute pathname requests and + * search list for relative pathname requests. + * + * MAXDIRS should be at least as large as the number of arguments that + * inetd allows (currently 20). + */ +#define MAXDIRS 20 +static struct dirlist { + char *name; + int len; +} dirs[MAXDIRS+1]; +static int suppress_naks; +static int logging; + +static char *errtomsg __P((int)); +static void nak __P((int)); +static char *verifyhost __P((struct sockaddr_in *)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register struct tftphdr *tp; + register int n; + int ch, on; + struct sockaddr_in sin; + + openlog("tftpd", LOG_PID, LOG_FTP); + while ((ch = getopt(argc, argv, "ln")) != EOF) { + switch (ch) { + case 'l': + logging = 1; + break; + case 'n': + suppress_naks = 1; + break; + default: + syslog(LOG_WARNING, "ignoring unknown option -%c", ch); + } + } + if (optind < argc) { + struct dirlist *dirp; + + /* Get list of directory prefixes. Skip relative pathnames. */ + for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS]; + optind++) { + if (argv[optind][0] == '/') { + dirp->name = argv[optind]; + dirp->len = strlen(dirp->name); + dirp++; + } + } + } + + on = 1; + if (ioctl(0, FIONBIO, &on) < 0) { + syslog(LOG_ERR, "ioctl(FIONBIO): %m\n"); + exit(1); + } + fromlen = sizeof (from); + n = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &fromlen); + if (n < 0) { + syslog(LOG_ERR, "recvfrom: %m\n"); + exit(1); + } + /* + * Now that we have read the message out of the UDP + * socket, we fork and exit. Thus, inetd will go back + * to listening to the tftp port, and the next request + * to come in will start up a new instance of tftpd. + * + * We do this so that inetd can run tftpd in "wait" mode. + * The problem with tftpd running in "nowait" mode is that + * inetd may get one or more successful "selects" on the + * tftp port before we do our receive, so more than one + * instance of tftpd may be started up. Worse, if tftpd + * break before doing the above "recvfrom", inetd would + * spawn endless instances, clogging the system. + */ + { + int pid; + int i, j; + + for (i = 1; i < 20; i++) { + pid = fork(); + if (pid < 0) { + sleep(i); + /* + * flush out to most recently sent request. + * + * This may drop some request, but those + * will be resent by the clients when + * they timeout. The positive effect of + * this flush is to (try to) prevent more + * than one tftpd being started up to service + * a single request from a single client. + */ + j = sizeof from; + i = recvfrom(0, buf, sizeof (buf), 0, + (struct sockaddr *)&from, &j); + if (i > 0) { + n = i; + fromlen = j; + } + } else { + break; + } + } + if (pid < 0) { + syslog(LOG_ERR, "fork: %m\n"); + exit(1); + } else if (pid != 0) { + exit(0); + } + } + from.sin_family = AF_INET; + alarm(0); + close(0); + close(1); + peer = socket(AF_INET, SOCK_DGRAM, 0); + if (peer < 0) { + syslog(LOG_ERR, "socket: %m\n"); + exit(1); + } + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + syslog(LOG_ERR, "bind: %m\n"); + exit(1); + } + if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) { + syslog(LOG_ERR, "connect: %m\n"); + exit(1); + } + tp = (struct tftphdr *)buf; + tp->th_opcode = ntohs(tp->th_opcode); + if (tp->th_opcode == RRQ || tp->th_opcode == WRQ) + tftp(tp, n); + exit(1); +} + +struct formats; +int validate_access __P((char **, int)); +void sendfile __P((struct formats *)); +void recvfile __P((struct formats *)); + +struct formats { + char *f_mode; + int (*f_validate) __P((char **, int)); + void (*f_send) __P((struct formats *)); + void (*f_recv) __P((struct formats *)); + int f_convert; +} formats[] = { + { "netascii", validate_access, sendfile, recvfile, 1 }, + { "octet", validate_access, sendfile, recvfile, 0 }, +#ifdef notdef + { "mail", validate_user, sendmail, recvmail, 1 }, +#endif + { 0 } +}; + +/* + * Handle initial connection protocol. + */ +void +tftp(tp, size) + struct tftphdr *tp; + int size; +{ + register char *cp; + int first = 1, ecode; + register struct formats *pf; + char *filename, *mode; + + filename = cp = tp->th_stuff; +again: + while (cp < buf + size) { + if (*cp == '\0') + break; + cp++; + } + if (*cp != '\0') { + nak(EBADOP); + exit(1); + } + if (first) { + mode = ++cp; + first = 0; + goto again; + } + for (cp = mode; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + for (pf = formats; pf->f_mode; pf++) + if (strcmp(pf->f_mode, mode) == 0) + break; + if (pf->f_mode == 0) { + nak(EBADOP); + exit(1); + } + ecode = (*pf->f_validate)(&filename, tp->th_opcode); + if (logging) { + syslog(LOG_INFO, "%s: %s request for %s: %s", + verifyhost(&from), + tp->th_opcode == WRQ ? "write" : "read", + filename, errtomsg(ecode)); + } + if (ecode) { + /* + * Avoid storms of naks to a RRQ broadcast for a relative + * bootfile pathname from a diskless Sun. + */ + if (suppress_naks && *filename != '/' && ecode == ENOTFOUND) + exit(0); + nak(ecode); + exit(1); + } + if (tp->th_opcode == WRQ) + (*pf->f_recv)(pf); + else + (*pf->f_send)(pf); + exit(0); +} + + +FILE *file; + +/* + * Validate file access. Since we + * have no uid or gid, for now require + * file to exist and be publicly + * readable/writable. + * If we were invoked with arguments + * from inetd then the file must also be + * in one of the given directory prefixes. + * Note also, full path name must be + * given as we have no login directory. + */ +int +validate_access(filep, mode) + char **filep; + int mode; +{ + struct stat stbuf; + int fd; + struct dirlist *dirp; + static char pathname[MAXPATHLEN]; + char *filename = *filep; + + /* + * Prevent tricksters from getting around the directory restrictions + */ + if (strstr(filename, "/../")) + return (EACCESS); + + if (*filename == '/') { + /* + * Allow the request if it's in one of the approved locations. + * Special case: check the null prefix ("/") by looking + * for length = 1 and relying on the arg. processing that + * it's a /. + */ + for (dirp = dirs; dirp->name != NULL; dirp++) { + if (dirp->len == 1 || + (!strncmp(filename, dirp->name, dirp->len) && + filename[dirp->len] == '/')) + break; + } + /* If directory list is empty, allow access to any file */ + if (dirp->name == NULL && dirp != dirs) + return (EACCESS); + if (stat(filename, &stbuf) < 0) + return (errno == ENOENT ? ENOTFOUND : EACCESS); + if ((stbuf.st_mode & S_IFMT) != S_IFREG) + return (ENOTFOUND); + if (mode == RRQ) { + if ((stbuf.st_mode & S_IROTH) == 0) + return (EACCESS); + } else { + if ((stbuf.st_mode & S_IWOTH) == 0) + return (EACCESS); + } + } else { + int err; + + /* + * Relative file name: search the approved locations for it. + * Don't allow write requests or ones that avoid directory + * restrictions. + */ + + if (mode != RRQ || !strncmp(filename, "../", 3)) + return (EACCESS); + + /* + * If the file exists in one of the directories and isn't + * readable, continue looking. However, change the error code + * to give an indication that the file exists. + */ + err = ENOTFOUND; + for (dirp = dirs; dirp->name != NULL; dirp++) { + sprintf(pathname, "%s/%s", dirp->name, filename); + if (stat(pathname, &stbuf) == 0 && + (stbuf.st_mode & S_IFMT) == S_IFREG) { + if ((stbuf.st_mode & S_IROTH) != 0) { + break; + } + err = EACCESS; + } + } + if (dirp->name == NULL) + return (err); + *filep = filename = pathname; + } + fd = open(filename, mode == RRQ ? 0 : 1); + if (fd < 0) + return (errno + 100); + file = fdopen(fd, (mode == RRQ)? "r":"w"); + if (file == NULL) { + return errno+100; + } + return (0); +} + +int timeout; +jmp_buf timeoutbuf; + +void +timer() +{ + + timeout += rexmtval; + if (timeout >= maxtimeout) + exit(1); + longjmp(timeoutbuf, 1); +} + +/* + * Send the requested file. + */ +void +sendfile(pf) + struct formats *pf; +{ + struct tftphdr *dp, *r_init(); + register struct tftphdr *ap; /* ack packet */ + register int size, n; + volatile int block; + + signal(SIGALRM, timer); + dp = r_init(); + ap = (struct tftphdr *)ackbuf; + block = 1; + do { + size = readit(file, &dp, pf->f_convert); + if (size < 0) { + nak(errno + 100); + goto abort; + } + dp->th_opcode = htons((u_short)DATA); + dp->th_block = htons((u_short)block); + timeout = 0; + (void)setjmp(timeoutbuf); + +send_data: + if (send(peer, dp, size + 4, 0) != size + 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + read_ahead(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); /* read the ack */ + n = recv(peer, ackbuf, sizeof (ackbuf), 0); + alarm(0); + if (n < 0) { + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + ap->th_opcode = ntohs((u_short)ap->th_opcode); + ap->th_block = ntohs((u_short)ap->th_block); + + if (ap->th_opcode == ERROR) + goto abort; + + if (ap->th_opcode == ACK) { + if (ap->th_block == block) + break; + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (ap->th_block == (block -1)) + goto send_data; + } + + } + block++; + } while (size == SEGSIZE); +abort: + (void) fclose(file); +} + +void +justquit() +{ + exit(0); +} + + +/* + * Receive a file. + */ +void +recvfile(pf) + struct formats *pf; +{ + struct tftphdr *dp, *w_init(); + register struct tftphdr *ap; /* ack buffer */ + register int n, size; + volatile int block; + + signal(SIGALRM, timer); + dp = w_init(); + ap = (struct tftphdr *)ackbuf; + block = 0; + do { + timeout = 0; + ap->th_opcode = htons((u_short)ACK); + ap->th_block = htons((u_short)block); + block++; + (void) setjmp(timeoutbuf); +send_ack: + if (send(peer, ackbuf, 4, 0) != 4) { + syslog(LOG_ERR, "tftpd: write: %m\n"); + goto abort; + } + write_behind(file, pf->f_convert); + for ( ; ; ) { + alarm(rexmtval); + n = recv(peer, dp, PKTSIZE, 0); + alarm(0); + if (n < 0) { /* really? */ + syslog(LOG_ERR, "tftpd: read: %m\n"); + goto abort; + } + dp->th_opcode = ntohs((u_short)dp->th_opcode); + dp->th_block = ntohs((u_short)dp->th_block); + if (dp->th_opcode == ERROR) + goto abort; + if (dp->th_opcode == DATA) { + if (dp->th_block == block) { + break; /* normal */ + } + /* Re-synchronize with the other side */ + (void) synchnet(peer); + if (dp->th_block == (block-1)) + goto send_ack; /* rexmit */ + } + } + /* size = write(file, dp->th_data, n - 4); */ + size = writeit(file, &dp, n - 4, pf->f_convert); + if (size != (n-4)) { /* ahem */ + if (size < 0) nak(errno + 100); + else nak(ENOSPACE); + goto abort; + } + } while (size == SEGSIZE); + write_behind(file, pf->f_convert); + (void) fclose(file); /* close data file */ + + ap->th_opcode = htons((u_short)ACK); /* send the "final" ack */ + ap->th_block = htons((u_short)(block)); + (void) send(peer, ackbuf, 4, 0); + + signal(SIGALRM, justquit); /* just quit on timeout */ + alarm(rexmtval); + n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */ + alarm(0); + if (n >= 4 && /* if read some data */ + dp->th_opcode == DATA && /* and got a data block */ + block == dp->th_block) { /* then my last ack was lost */ + (void) send(peer, ackbuf, 4, 0); /* resend final ack */ + } +abort: + return; +} + +struct errmsg { + int e_code; + char *e_msg; +} errmsgs[] = { + { EUNDEF, "Undefined error code" }, + { ENOTFOUND, "File not found" }, + { EACCESS, "Access violation" }, + { ENOSPACE, "Disk full or allocation exceeded" }, + { EBADOP, "Illegal TFTP operation" }, + { EBADID, "Unknown transfer ID" }, + { EEXISTS, "File already exists" }, + { ENOUSER, "No such user" }, + { -1, 0 } +}; + +static char * +errtomsg(error) + int error; +{ + static char buf[20]; + register struct errmsg *pe; + if (error == 0) + return "success"; + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + return pe->e_msg; + sprintf(buf, "error %d", error); + return buf; +} + +/* + * Send a nak packet (error message). + * Error code passed in is one of the + * standard TFTP codes, or a UNIX errno + * offset by 100. + */ +static void +nak(error) + int error; +{ + register struct tftphdr *tp; + int length; + register struct errmsg *pe; + + tp = (struct tftphdr *)buf; + tp->th_opcode = htons((u_short)ERROR); + tp->th_code = htons((u_short)error); + for (pe = errmsgs; pe->e_code >= 0; pe++) + if (pe->e_code == error) + break; + if (pe->e_code < 0) { + pe->e_msg = strerror(error - 100); + tp->th_code = EUNDEF; /* set 'undef' errorcode */ + } + strcpy(tp->th_msg, pe->e_msg); + length = strlen(pe->e_msg); + tp->th_msg[length] = '\0'; + length += 5; + if (send(peer, buf, length, 0) != length) + syslog(LOG_ERR, "nak: %m\n"); +} + +static char * +verifyhost(fromp) + struct sockaddr_in *fromp; +{ + struct hostent *hp; + + hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof (fromp->sin_addr), + fromp->sin_family); + if (hp) + return hp->h_name; + else + return inet_ntoa(fromp->sin_addr); +} diff --git a/libexec/uucpd/Makefile b/libexec/uucpd/Makefile new file mode 100644 index 0000000..45df1cd --- /dev/null +++ b/libexec/uucpd/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +PROG= uucpd +NOMAN= noman + +.include <bsd.prog.mk> diff --git a/libexec/uucpd/pathnames.h b/libexec/uucpd/pathnames.h new file mode 100644 index 0000000..e9823ee --- /dev/null +++ b/libexec/uucpd/pathnames.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/4/93 + */ + +#include <paths.h> + +#define _PATH_UUCICO "/usr/lib/uucp/uucico" diff --git a/libexec/uucpd/uucpd.c b/libexec/uucpd/uucpd.c new file mode 100644 index 0000000..5b958ce --- /dev/null +++ b/libexec/uucpd/uucpd.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Rick Adams. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1985, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)uucpd.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * 4.2BSD TCP/IP server for uucico + * uucico's TCP channel causes this server to be run at the remote end. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <pwd.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "pathnames.h" + +struct sockaddr_in hisctladdr; +int hisaddrlen = sizeof hisctladdr; +struct sockaddr_in myctladdr; +int mypid; + +char Username[64]; +char *nenv[] = { + Username, + NULL, +}; +extern char **environ; + +main(argc, argv) +int argc; +char **argv; +{ +#ifndef BSDINETD + register int s, tcp_socket; + struct servent *sp; +#endif !BSDINETD + extern int errno; + int dologout(); + + environ = nenv; +#ifdef BSDINETD + close(1); close(2); + dup(0); dup(0); + hisaddrlen = sizeof (hisctladdr); + if (getpeername(0, &hisctladdr, &hisaddrlen) < 0) { + fprintf(stderr, "%s: ", argv[0]); + perror("getpeername"); + _exit(1); + } + if (fork() == 0) + doit(&hisctladdr); + dologout(); + exit(1); +#else !BSDINETD + sp = getservbyname("uucp", "tcp"); + if (sp == NULL){ + perror("uucpd: getservbyname"); + exit(1); + } + if (fork()) + exit(0); + if ((s=open(_PATH_TTY, 2)) >= 0){ + ioctl(s, TIOCNOTTY, (char *)0); + close(s); + } + + bzero((char *)&myctladdr, sizeof (myctladdr)); + myctladdr.sin_family = AF_INET; + myctladdr.sin_port = sp->s_port; +#ifdef BSD4_2 + tcp_socket = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_socket < 0) { + perror("uucpd: socket"); + exit(1); + } + if (bind(tcp_socket, (char *)&myctladdr, sizeof (myctladdr)) < 0) { + perror("uucpd: bind"); + exit(1); + } + listen(tcp_socket, 3); /* at most 3 simultaneuos uucp connections */ + signal(SIGCHLD, dologout); + + for(;;) { + s = accept(tcp_socket, &hisctladdr, &hisaddrlen); + if (s < 0){ + if (errno == EINTR) + continue; + perror("uucpd: accept"); + exit(1); + } + if (fork() == 0) { + close(0); close(1); close(2); + dup(s); dup(s); dup(s); + close(tcp_socket); close(s); + doit(&hisctladdr); + exit(1); + } + close(s); + } +#endif BSD4_2 + +#endif !BSDINETD +} + +doit(sinp) +struct sockaddr_in *sinp; +{ + char user[64], passwd[64]; + char *xpasswd, *crypt(); + struct passwd *pw, *getpwnam(); + + alarm(60); + printf("login: "); fflush(stdout); + if (readline(user, sizeof user) < 0) { + fprintf(stderr, "user read\n"); + return; + } + /* truncate username to 8 characters */ + user[8] = '\0'; + pw = getpwnam(user); + if (pw == NULL) { + fprintf(stderr, "user unknown\n"); + return; + } + if (strcmp(pw->pw_shell, _PATH_UUCICO)) { + fprintf(stderr, "Login incorrect."); + return; + } + if (pw->pw_passwd && *pw->pw_passwd != '\0') { + printf("Password: "); fflush(stdout); + if (readline(passwd, sizeof passwd) < 0) { + fprintf(stderr, "passwd read\n"); + return; + } + xpasswd = crypt(passwd, pw->pw_passwd); + if (strcmp(xpasswd, pw->pw_passwd)) { + fprintf(stderr, "Login incorrect."); + return; + } + } + alarm(0); + sprintf(Username, "USER=%s", user); + dologin(pw, sinp); + setgid(pw->pw_gid); +#ifdef BSD4_2 + initgroups(pw->pw_name, pw->pw_gid); +#endif BSD4_2 + chdir(pw->pw_dir); + setuid(pw->pw_uid); +#ifdef BSD4_2 + execl(UUCICO, "uucico", (char *)0); +#endif BSD4_2 + perror("uucico server: execl"); +} + +readline(p, n) +register char *p; +register int n; +{ + char c; + + while (n-- > 0) { + if (read(0, &c, 1) <= 0) + return(-1); + c &= 0177; + if (c == '\n' || c == '\r') { + *p = '\0'; + return(0); + } + *p++ = c; + } + return(-1); +} + +#include <utmp.h> +#ifdef BSD4_2 +#include <fcntl.h> +#endif BSD4_2 + +#define SCPYN(a, b) strncpy(a, b, sizeof (a)) + +struct utmp utmp; + +dologout() +{ + union wait status; + int pid, wtmp; + +#ifdef BSDINETD + while ((pid=wait((int *)&status)) > 0) { +#else !BSDINETD + while ((pid=wait3((int *)&status,WNOHANG,0)) > 0) { +#endif !BSDINETD + wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND); + if (wtmp >= 0) { + sprintf(utmp.ut_line, "uucp%.4d", pid); + SCPYN(utmp.ut_name, ""); + SCPYN(utmp.ut_host, ""); + (void) time(&utmp.ut_time); + (void) write(wtmp, (char *)&utmp, sizeof (utmp)); + (void) close(wtmp); + } + } +} + +/* + * Record login in wtmp file. + */ +dologin(pw, sin) +struct passwd *pw; +struct sockaddr_in *sin; +{ + char line[32]; + char remotehost[32]; + int wtmp, f; + struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr, + sizeof (struct in_addr), AF_INET); + + if (hp) { + strncpy(remotehost, hp->h_name, sizeof (remotehost)); + endhostent(); + } else + strncpy(remotehost, inet_ntoa(sin->sin_addr), + sizeof (remotehost)); + wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND); + if (wtmp >= 0) { + /* hack, but must be unique and no tty line */ + sprintf(line, "uucp%.4d", getpid()); + SCPYN(utmp.ut_line, line); + SCPYN(utmp.ut_name, pw->pw_name); + SCPYN(utmp.ut_host, remotehost); + time(&utmp.ut_time); + (void) write(wtmp, (char *)&utmp, sizeof (utmp)); + (void) close(wtmp); + } + if ((f = open(_PATH_LASTLOG, O_RDWR)) >= 0) { + struct lastlog ll; + + time(&ll.ll_time); + lseek(f, (long)pw->pw_uid * sizeof(struct lastlog), 0); + strcpy(line, remotehost); + SCPYN(ll.ll_line, line); + SCPYN(ll.ll_host, remotehost); + (void) write(f, (char *) &ll, sizeof ll); + (void) close(f); + } +} |