--- /dev/null
+/Makefile/1.44/Fri May 18 16:08:12 2007//
+/Makefile.inc/1.2/Sun Jan 28 19:34:26 2001//
+D/comsat////
+D/fingerd////
+D/ftpd////
+D/getNAME////
+D/getty////
+D/identd////
+D/ld.so////
+D/lockspool////
+D/login_chpass////
+D/login_krb5////
+D/login_krb5-or-pwd////
+D/login_lchpass////
+D/login_passwd////
+D/login_radius////
+D/login_reject////
+D/login_skey////
+D/login_tis////
+D/login_token////
+D/mail.local////
+D/makewhatis////
+D/rpc.rquotad////
+D/rpc.rstatd////
+D/rpc.rusersd////
+D/rpc.rwalld////
+D/rpc.sprayd////
+D/rpc.yppasswdd////
+D/rshd////
+D/spamd////
+D/spamd-setup////
+D/spamlogd////
+D/talkd////
+D/tcpd////
+D/tftp-proxy////
+D/tftpd////
+D/uucpd////
--- /dev/null
+src/libexec
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# from: @(#)Makefile 5.7 (Berkeley) 4/1/91
+# $OpenBSD: Makefile,v 1.44 2007/05/18 16:08:12 deraadt Exp $
+
+.include <bsd.own.mk>
+
+SUBDIR= comsat fingerd ftpd getNAME getty identd lockspool \
+ mail.local makewhatis rpc.rquotad rpc.rstatd rpc.rusersd \
+ rpc.rwalld rpc.sprayd spamd spamlogd spamd-setup rshd \
+ talkd tcpd tftp-proxy tftpd uucpd
+
+SUBDIR+=login_passwd login_skey login_reject login_chpass \
+ login_lchpass login_token login_radius login_tis
+
+.if (${YP:L} == "yes")
+SUBDIR+=rpc.yppasswdd
+.endif
+
+.if (${ELF_TOOLCHAIN:L} == "yes")
+SUBDIR+=ld.so
+.endif
+
+.if (${KERBEROS5:L} == "yes")
+SUBDIR+=login_krb5 login_krb5-or-pwd
+.endif
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.2 2001/01/28 19:34:26 niklas Exp $
+
+BINDIR?= /usr/libexec
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:27 2001//
+/comsat.8/1.6/Thu May 31 19:19:39 2007//
+/comsat.c/1.36/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/comsat
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:27 niklas Exp $
+
+PROG= comsat
+MAN= comsat.8
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: comsat.8,v 1.6 2007/05/31 19:19:39 jmc Exp $
+.\" 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. 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.
+.\"
+.\" from: @(#)comsat.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt COMSAT 8
+.Os
+.Sh NAME
+.Nm comsat
+.Nd biff server
+.Sh SYNOPSIS
+.Nm comsat
+.Sh DESCRIPTION
+.Nm
+is the server process which receives reports of incoming mail
+and notifies users if they have requested this service.
+.Nm
+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 HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
+.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.
--- /dev/null
+/* $OpenBSD: comsat.c,v 1.36 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+#include <sys/limits.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <vis.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(FILE *, char[], off_t);
+void mailfor(char *);
+void notify(struct utmp *, off_t);
+void readutmp(int);
+void doreadutmp(void);
+void reapchildren(int);
+
+volatile sig_atomic_t wantreadutmp;
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_storage from;
+ struct sigaction sa;
+ ssize_t cc;
+ socklen_t fromlen;
+ char msgbuf[100];
+ sigset_t sigset;
+
+ /* verify proper invocation */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) == -1) {
+ (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)) == -1) {
+ 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));
+ doreadutmp();
+
+ (void)signal(SIGTTOU, SIG_IGN);
+
+ bzero(&sa, sizeof sa);
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = readutmp;
+ sa.sa_flags = 0; /* no SA_RESTART */
+ (void)sigaction(SIGALRM, &sa, NULL);
+
+ sa.sa_handler = reapchildren;
+ sa.sa_flags = SA_RESTART;
+ (void)sigaction(SIGCHLD, &sa, NULL);
+
+ for (;;) {
+ if (wantreadutmp) {
+ wantreadutmp = 0;
+ doreadutmp();
+ }
+
+ cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
+ if (cc <= 0) {
+ if (errno != EINTR)
+ sleep(1);
+ continue;
+ }
+ if (!nutmp) /* no one has logged in yet */
+ continue;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGALRM);
+ sigprocmask(SIG_SETMASK, &sigset, NULL);
+ msgbuf[cc] = '\0';
+ (void)time(&lastmsgtime);
+ mailfor(msgbuf);
+ sigemptyset(&sigset);
+ sigprocmask(SIG_SETMASK, &sigset, NULL);
+ }
+}
+
+/* ARGSUSED */
+void
+reapchildren(int signo)
+{
+ int save_errno = errno;
+
+ while (wait3(NULL, WNOHANG, NULL) > 0)
+ ;
+ errno = save_errno;
+}
+
+/* ARGSUSED */
+void
+readutmp(int signo)
+{
+ wantreadutmp = 1;
+}
+
+void
+doreadutmp(void)
+{
+ static u_int utmpsize; /* last malloced size for utmp */
+ static time_t utmpmtime; /* last modification time for utmp */
+ struct stat statbf;
+
+ if (time(NULL) - lastmsgtime >= MAXIDLE)
+ exit(0);
+ (void)fstat(uf, &statbf);
+ if (statbf.st_mtime > utmpmtime) {
+ utmpmtime = statbf.st_mtime;
+ /* avoid int overflow */
+ if (statbf.st_size > INT_MAX - 10 * sizeof(struct utmp)) {
+ syslog(LOG_ALERT, "utmp file excessively large");
+ exit(1);
+ }
+ if (statbf.st_size > utmpsize) {
+ u_int nutmpsize = statbf.st_size + 10 *
+ sizeof(struct utmp);
+ struct utmp *u;
+
+ if ((u = realloc(utmp, nutmpsize)) == NULL) {
+ free(utmp);
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(1);
+ }
+ utmp = u;
+ utmpsize = nutmpsize;
+ }
+ (void)lseek(uf, 0, SEEK_SET);
+ nutmp = read(uf, utmp, statbf.st_size)/sizeof(struct utmp);
+ }
+ (void)alarm(15);
+}
+
+void
+mailfor(char *name)
+{
+ struct utmp *utp = &utmp[nutmp];
+ char utname[UT_NAMESIZE+1];
+ char *cp;
+ off_t offset;
+
+ if (!(cp = strchr(name, '@')))
+ return;
+ *cp = '\0';
+ offset = atoi(cp + 1);
+ while (--utp >= utmp) {
+ memcpy(utname, utp->ut_name, UT_NAMESIZE);
+ utname[UT_NAMESIZE] = '\0';
+ if (!strncmp(utname, name, UT_NAMESIZE))
+ notify(utp, offset);
+ }
+}
+
+static char *cr;
+
+void
+notify(struct utmp *utp, off_t offset)
+{
+ FILE *tp;
+ struct stat stb;
+ struct termios ttybuf;
+ char tty[MAXPATHLEN], name[UT_NAMESIZE + 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",
+ (int)sizeof(utp->ut_name), utp->ut_name, tty);
+ return;
+ }
+ dsyslog(LOG_DEBUG, "notify %.*s on %s", (int)sizeof(utp->ut_name),
+ utp->ut_name, tty);
+ if (fork())
+ return;
+ (void)signal(SIGALRM, SIG_DFL);
+ (void)alarm(30);
+ if ((tp = fopen(tty, "w")) == NULL) {
+ dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
+ _exit(1);
+ }
+ (void)tcgetattr(fileno(tp), &ttybuf);
+ cr = (ttybuf.c_oflag & ONLCR) && (ttybuf.c_oflag & OPOST) ?
+ "\n" : "\n\r";
+ memcpy(name, utp->ut_name, UT_NAMESIZE);
+ name[UT_NAMESIZE] = '\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(FILE *tp, char name[], off_t offset)
+{
+ char *cp, ch;
+ char visout[5], *s2;
+ FILE *fi;
+ int linecnt, charcnt, inheader;
+ struct passwd *p;
+ char line[BUFSIZ];
+
+ /* Set effective uid to user in case mail drop is on nfs */
+ if ((p = getpwnam(name)) != NULL) {
+ (void) seteuid(p->pw_uid);
+ (void) setuid(p->pw_uid);
+ }
+
+ if ((fi = fopen(name, "r")) == NULL)
+ return;
+
+ (void)fseeko(fi, offset, SEEK_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);
+ vis(visout, ch, VIS_SAFE|VIS_NOSLASH, cp[1]);
+ for (s2 = visout; *s2; s2++)
+ (void)fputc(*s2, tp);
+ }
+ (void)fputs(cr, tp);
+ --linecnt;
+ }
+ (void)fprintf(tp, "----%s\n", cr);
+ (void)fclose(fi);
+}
--- /dev/null
+/usr/obj/libexec/comsat
\ No newline at end of file
--- /dev/null
+/Makefile/1.3/Sun Jan 28 19:34:27 2001//
+/fingerd.8/1.18/Tue Sep 25 06:28:13 2007//
+/fingerd.c/1.35/Tue Oct 27 23:59:31 2009//
+/pathnames.h/1.4/Mon Jun 2 19:38:24 2003//
+D
--- /dev/null
+src/libexec/fingerd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.3 2001/01/28 19:34:27 niklas Exp $
+
+PROG= fingerd
+MAN= fingerd.8
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: fingerd.8,v 1.18 2007/09/25 06:28:13 jmc Exp $
+.\"
+.\" 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. 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.
+.\"
+.\" from: @(#)fingerd.8 8.1 (Berkeley) 6/4/93
+.\" $Id: fingerd.8,v 1.18 2007/09/25 06:28:13 jmc Exp $
+.\"
+.Dd $Mdocdate: September 25 2007 $
+.Dt FINGERD 8
+.Os
+.Sh NAME
+.Nm fingerd
+.Nd remote user information server
+.Sh SYNOPSIS
+.Nm fingerd
+.Op Fl lMmpSsu
+.Op Fl P Ar filename
+.Sh DESCRIPTION
+.Nm
+implements a simple protocol based on RFC 1288
+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
+is started by
+.Xr inetd 8 ,
+which 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
+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 1
+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
+as server program arguments in
+.Pa /etc/inetd.conf :
+.Bl -tag -width Ds
+.It Fl l
+Enable logging.
+The name of the host originating the query and the actual request
+is reported via
+.Xr syslog 3
+at LOG_NOTICE priority.
+A request of the form
+.Sq /W
+or
+.Sq /w
+will return long output.
+Empty requests will return all currently logged in users.
+All other requests look for specific users.
+See RFC 1288 for details.
+.It Fl M
+Enables matching of
+.Ar user
+names.
+This is disabled by default if the system is running YP.
+.It Fl m
+Prevent matching of
+.Ar user
+names.
+.Ar User
+is usually a login name; however, matching will also be done on the
+users' real names, unless the
+.Fl m
+option is supplied.
+.It Fl P Ar filename
+Use an alternate program as the local information provider.
+The default local program
+executed by
+.Nm
+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.
+.It Fl p
+Prevents
+.Xr finger 1
+from displaying the contents of the
+.Dq Pa .plan
+and
+.Dq Pa .project
+files.
+.It Fl S
+Prints user information in short mode, one line per user.
+This overrides the
+.Dq Pa Whois switch
+that may be passed in from the remote client.
+.It Fl s
+Enable secure mode.
+Forwarding of queries to other remote hosts is denied.
+.It Fl u
+Queries without a user name are rejected.
+.El
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
+.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
+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.
--- /dev/null
+/* $OpenBSD: fingerd.c,v 1.35 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <err.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "pathnames.h"
+
+__dead void logerr(const char *, ...);
+__dead void usage(void);
+
+void
+usage(void)
+{
+ syslog(LOG_ERR,
+ "usage: fingerd [-lMmpSsu] [-P filename]");
+ exit(2);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int ch, ac = 2;
+ int p[2], logging, secure, user_required, short_list;
+#define ENTRIES 50
+ char **comp, *prog;
+ char **ap, *av[ENTRIES + 1], line[8192], *lp, *hname;
+ char hostbuf[MAXHOSTNAMELEN];
+
+ prog = _PATH_FINGER;
+ logging = secure = user_required = short_list = 0;
+ openlog("fingerd", LOG_PID, LOG_DAEMON);
+ opterr = 0;
+ while ((ch = getopt(argc, argv, "sluSmMpP:")) != -1)
+ switch (ch) {
+ case 'l':
+ logging = 1;
+ break;
+ case 'P':
+ prog = optarg;
+ break;
+ case 's':
+ secure = 1;
+ break;
+ case 'u':
+ user_required = 1;
+ break;
+ case 'S':
+ if (ac < ENTRIES) {
+ short_list = 1;
+ av[ac++] = "-s";
+ }
+ break;
+ case 'm':
+ if (ac < ENTRIES)
+ av[ac++] = "-m";
+ break;
+ case 'M':
+ if (ac < ENTRIES)
+ av[ac++] = "-M";
+ break;
+ case 'p':
+ if (ac < ENTRIES)
+ av[ac++] = "-p";
+ break;
+ default:
+ usage();
+ }
+
+ if (logging) {
+ struct sockaddr_storage ss;
+ struct sockaddr *sa;
+ socklen_t sval;
+
+ sval = sizeof(ss);
+ if (getpeername(0, (struct sockaddr *)&ss, &sval) < 0)
+ err(1, "getpeername");
+ sa = (struct sockaddr *)&ss;
+ if (getnameinfo(sa, sa->sa_len, hostbuf, sizeof(hostbuf),
+ NULL, 0, 0) != 0) {
+ strlcpy(hostbuf, "?", sizeof(hostbuf));
+ }
+ hname = hostbuf;
+ }
+
+ if (fgets(line, sizeof(line), stdin) == NULL) {
+ if (logging)
+ syslog(LOG_NOTICE, "query from %s: %s", hname,
+ feof(stdin) ? "EOF" : strerror(errno));
+ exit(1);
+ }
+
+ if (logging)
+ syslog(LOG_NOTICE, "query from %s: `%.*s'", hname,
+ (int)strcspn(line, "\r\n"), line);
+
+ /*
+ * Note: we assume that finger(1) will treat "--" as end of
+ * command args (ie: that it uses getopt(3)).
+ */
+ av[ac++] = "--";
+ comp = &av[1];
+ for (lp = line, ap = &av[ac]; ac < ENTRIES;) {
+ size_t len;
+
+ if ((*ap = strtok(lp, " \t\r\n")) == NULL)
+ break;
+ lp = NULL;
+ if (secure && strchr(*ap, '@')) {
+ (void) puts("forwarding service denied\r");
+ exit(1);
+ }
+
+ len = strlen(*ap);
+ while (len > 0 && (*ap)[len - 1] == '@')
+ (*ap)[--len] = '\0';
+ if (**ap == '\0')
+ continue;
+
+ /* RFC1196: "/[Ww]" == "-l" */
+ if ((*ap)[0] == '/' && ((*ap)[1] == 'W' || (*ap)[1] == 'w')) {
+ if (!short_list) {
+ av[1] = "-l";
+ comp = &av[0];
+ }
+ } else {
+ ap++;
+ ac++;
+ }
+ }
+ av[ENTRIES - 1] = NULL;
+
+ if ((lp = strrchr(prog, '/')))
+ *comp = ++lp;
+ else
+ *comp = prog;
+
+ if (user_required) {
+ for (ap = comp + 1; strcmp("--", *(ap++)); )
+ ;
+ if (*ap == NULL) {
+ (void) puts("must provide username\r");
+ exit(1);
+ }
+ }
+
+ if (pipe(p) < 0)
+ logerr("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);
+ logerr("execv: %s: %s", prog, strerror(errno));
+ case -1:
+ logerr("fork: %s", strerror(errno));
+ }
+ (void) close(p[1]);
+ if (!(fp = fdopen(p[0], "r")))
+ logerr("fdopen: %s", strerror(errno));
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == '\n')
+ putchar('\r');
+ putchar(ch);
+ }
+ exit(0);
+}
+
+void
+logerr(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void) vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
--- /dev/null
+/usr/obj/libexec/fingerd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: pathnames.h,v 1.4 2003/06/02 19:38:24 millert Exp $*/
+
+/*
+ * 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. 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.
+ *
+ * from: @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#define _PATH_FINGER "/usr/bin/finger"
--- /dev/null
+/Makefile/1.24/Wed Jun 15 16:13:27 2005//
+/extern.h/1.17/Fri Sep 12 16:12:08 2008//
+/ftpcmd.y/1.53/Tue Oct 27 23:59:31 2009//
+/ftpd.8/1.65/Thu May 31 19:19:39 2007//
+/logutmp.c/1.11/Mon Jun 30 12:03:51 2008//
+/logwtmp.c/1.11/Tue Oct 27 23:59:31 2009//
+/monitor.c/1.20/Thu Jun 4 01:12:39 2009//
+/monitor.h/1.5/Thu Mar 1 20:06:27 2007//
+/monitor_fdpass.c/1.4/Mon Mar 24 16:11:00 2008//
+/pathnames.h/1.6/Mon Jun 2 19:38:24 2003//
+/popen.c/1.24/Mon Apr 5 23:11:44 2010//
+/ftpd.c/1.189/Sun Jun 27 18:29:54 2010//
+D
--- /dev/null
+src/libexec/ftpd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.24 2005/06/15 16:13:27 mickey Exp $
+# $NetBSD: Makefile,v 1.13 1996/02/16 02:07:41 cgd Exp $
+# @(#)Makefile 8.2 (Berkeley) 4/4/94
+
+PROG= ftpd
+CFLAGS+=-DHASSETPROCTITLE -Dunix
+SRCS= ftpd.c ftpcmd.y logutmp.c logwtmp.c monitor.c monitor_fdpass.c popen.c
+MAN= ftpd.8
+YFLAGS=
+CLEANFILES+=y.tab.c
+
+.include <bsd.own.mk>
+
+# our internal version of ls.
+
+LSDIR = ${.CURDIR}/../../bin/ls
+.PATH: ${LSDIR}
+SRCS += ls.c cmp.c print.c util.c
+CFLAGS += -I${.CURDIR} -I${LSDIR}
+
+# not really used
+CPPFLAGS+=-DINET6
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.if (${TCP_WRAPPERS:L} == "yes")
+CFLAGS+=-DTCPWRAPPERS
+LDADD+= -lwrap
+DPADD+= ${LIBWRAP}
+.endif
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: extern.h,v 1.17 2008/09/12 16:12:08 moritz Exp $ */
+/* $NetBSD: extern.h,v 1.2 1995/04/11 02:44:49 cgd Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * 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. Neither the name of the project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 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. 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(char **);
+char **copyblk(char **);
+void cwd(char *);
+void delete(char *);
+void dologout(int);
+void fatal(char *);
+int ftpd_pclose(FILE *);
+FILE *ftpd_popen(char *, char *);
+int getline(char *, int, FILE *);
+void ftpdlogwtmp(char *, char *, char *);
+void lreply(int, const char *, ...);
+void makedir(char *);
+void nack(char *);
+enum auth_ret
+ pass(char *);
+void passive(void);
+int epsvproto2af(int);
+int af2epsvproto(int);
+void long_passive(char *, int);
+int extended_port(const char *);
+void epsv_protounsupp(const char *);
+void perror_reply(int, char *);
+void pwd(void);
+void removedir(char *);
+void renamecmd(char *, char *);
+char *renamefrom(char *);
+void reply(int, const char *, ...);
+void reply_r(int, const char *, ...);
+void retrieve(char *, char *);
+void send_file_list(char *);
+void setproctitle(const char *, ...);
+void statcmd(void);
+void statfilecmd(char *);
+void store(char *, char *, int);
+void upper(char *);
+void user(char *);
+void yyerror(char *);
+void toolong(int);
+
+struct utmp;
+void ftpd_login(struct utmp *ut);
+int ftpd_logout(char *);
+
+int yyparse(void);
+
+union sockunion {
+ struct sockinet {
+ u_int8_t si_len;
+ sa_family_t si_family;
+ in_port_t si_port;
+ } su_si;
+ struct sockaddr_in su_sin;
+ struct sockaddr_in6 su_sin6;
+};
+#define su_len su_si.si_len
+#define su_family su_si.si_family
+#define su_port su_si.si_port
--- /dev/null
+/* $OpenBSD: ftpcmd.y,v 1.53 2009/10/27 23:59:31 deraadt Exp $ */
+/* $NetBSD: ftpcmd.y,v 1.7 1996/04/08 19:03:11 jtc Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+%{
+
+#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 <signal.h>
+#include <tzfile.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "monitor.h"
+#include "extern.h"
+
+extern union sockunion 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[];
+extern int portcheck;
+extern union sockunion his_addr;
+extern int umaskchange;
+
+off_t restart_point;
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+static int state;
+static int quit;
+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 ALL
+
+ 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
+
+ LPRT LPSV EPRT EPSV
+
+ UMASK IDLE CHMOD
+
+ LEXERR
+
+%token <s> STRING
+%token <i> NUMBER
+
+%type <i> check_login check_login_epsvall octal_number byte_size
+%type <i> struct_code mode_code type_code form_code
+%type <s> pathstring pathname password username
+%type <i> host_port host_long_port4 host_long_port6
+
+%start cmd_list
+
+%%
+
+cmd_list
+ : /* empty */
+ | cmd_list cmd
+ {
+ if (fromname) {
+ free(fromname);
+ fromname = NULL;
+ }
+ restart_point = (off_t) 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd
+ : USER SP username CRLF
+ {
+ monitor_user($3);
+ free($3);
+ }
+ | PASS SP password CRLF
+ {
+ quit = monitor_pass($3);
+ memset($3, 0, strlen($3));
+ free($3);
+
+ /* Terminate unprivileged pre-auth slave */
+ if (quit)
+ _exit(0);
+ }
+ | PORT check_login_epsvall SP host_port CRLF
+ {
+ if ($2) {
+ if ($4) {
+ usedefault = 1;
+ reply(500,
+ "Illegal PORT rejected (range errors).");
+ } else if (portcheck &&
+ ntohs(data_dest.su_sin.sin_port) < IPPORT_RESERVED) {
+ usedefault = 1;
+ reply(500,
+ "Illegal PORT rejected (reserved port).");
+ } else if (portcheck &&
+ memcmp(&data_dest.su_sin.sin_addr,
+ &his_addr.su_sin.sin_addr,
+ sizeof data_dest.su_sin.sin_addr)) {
+ usedefault = 1;
+ reply(500,
+ "Illegal PORT rejected (address wrong).");
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
+ }
+ }
+ | LPRT check_login_epsvall SP host_long_port4 CRLF
+ {
+ if ($2) {
+ /* reject invalid host_long_port4 */
+ if ($4) {
+ reply(500,
+ "Illegal LPRT command rejected");
+ usedefault = 1;
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "LPRT command successful.");
+ }
+ }
+ }
+
+ | LPRT check_login_epsvall SP host_long_port6 CRLF
+ {
+ if ($2) {
+ /* reject invalid host_long_port6 */
+ if ($4) {
+ reply(500,
+ "Illegal LPRT command rejected");
+ usedefault = 1;
+ } else {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "LPRT command successful.");
+ }
+ }
+ }
+
+ | EPRT check_login_epsvall SP STRING CRLF
+ {
+ if ($2)
+ extended_port($4);
+ free($4);
+ }
+
+ | PASV check_login_epsvall CRLF
+ {
+ if ($2)
+ passive();
+ }
+ | LPSV check_login_epsvall CRLF
+ {
+ if ($2)
+ long_passive("LPSV", PF_UNSPEC);
+ }
+ | EPSV check_login SP NUMBER CRLF
+ {
+ if ($2)
+ long_passive("EPSV", epsvproto2af($4));
+ }
+ | EPSV check_login SP ALL CRLF
+ {
+ if ($2) {
+ reply(200, "EPSV ALL command successful.");
+ epsvall++;
+ }
+ }
+ | EPSV check_login CRLF
+ {
+ if ($2)
+ long_passive("EPSV", PF_UNSPEC);
+ }
+ | TYPE check_login SP type_code CRLF
+ {
+ if ($2) {
+ 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 (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ } else
+ reply(504, "Byte size must be 8.");
+
+ }
+ }
+ }
+ | STRU check_login SP struct_code CRLF
+ {
+ if ($2) {
+ switch ($4) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ }
+ | MODE check_login SP mode_code CRLF
+ {
+ if ($2) {
+ switch ($4) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ }
+ | ALLO check_login SP NUMBER CRLF
+ {
+ if ($2) {
+ reply(202, "ALLO command ignored.");
+ }
+ }
+ | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
+ {
+ if ($2) {
+ reply(202, "ALLO command ignored.");
+ }
+ }
+ | RETR check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ retrieve(NULL, $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);
+ 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 check_login CRLF
+ {
+ if ($2)
+ statcmd();
+ }
+ | DELE check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL)
+ delete($4);
+ if ($4 != NULL)
+ free($4);
+ }
+ | RNTO check_login SP pathname CRLF
+ {
+ if ($2 && $4 != NULL) {
+ if (fromname) {
+ renamecmd(fromname, $4);
+ free(fromname);
+ fromname = NULL;
+ } else {
+ reply(503,
+ "Bad sequence of commands.");
+ }
+ }
+ if ($4 != NULL)
+ free($4);
+ }
+ | ABOR check_login CRLF
+ {
+ if ($2)
+ 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, NULL);
+ }
+ | 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, NULL);
+ } else
+ help(cmdtab, $3);
+ free ($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, NULL);
+ }
+ | SITE SP HELP SP STRING CRLF
+ {
+ help(sitetab, $5);
+ free ($5);
+ }
+ | SITE SP UMASK check_login CRLF
+ {
+ mode_t 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
+ {
+ mode_t oldmask;
+
+ if ($4) {
+ if (($6 == -1) || ($6 > 0777)) {
+ reply(501, "Bad UMASK value");
+ } else if (!umaskchange) {
+ reply(550,
+ "No permission to change umask.");
+ } 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 == -1) || ($6 > 0777))
+ reply(501,
+ "CHMOD: Mode value must be between "
+ "0 and 0777");
+ else if (!umaskchange)
+ reply(550,
+ "No permission to change mode of %s.",
+ $8);
+ else if (chmod($8, $6) < 0)
+ perror_reply(550, $8);
+ else
+ reply(200,
+ "CHMOD command successful.");
+ }
+ if ($8 != NULL)
+ free($8);
+ }
+ | SITE SP check_login IDLE CRLF
+ {
+ if ($3)
+ reply(200,
+ "Current IDLE time limit is %d "
+ "seconds; max %d",
+ timeout, maxtimeout);
+ }
+ | SITE SP check_login IDLE SP NUMBER CRLF
+ {
+ if ($3) {
+ if ($6 < 30 || $6 > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between "
+ "30 and %d seconds",
+ maxtimeout);
+ } else {
+ timeout = $6;
+ (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 check_login CRLF
+ {
+ if ($2)
+#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,
+ "%04d%02d%02d%02d%02d%02d",
+ TM_YEAR_BASE + 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
+ {
+ yyclearin; /* discard lookahead data */
+ yyerrok; /* clear error condition */
+ state = 0; /* reset lexer state */
+ }
+ ;
+rcmd
+ : RNFR check_login SP pathname CRLF
+ {
+ restart_point = (off_t) 0;
+ if ($2 && $4) {
+ if (fromname)
+ free(fromname);
+ fromname = renamefrom($4);
+ if (fromname == NULL)
+ free($4);
+ } else if ($4) {
+ free ($4);
+ }
+ }
+
+ | REST check_login SP byte_size CRLF
+ {
+ if ($2) {
+ if (fromname) {
+ free(fromname);
+ fromname = NULL;
+ }
+ restart_point = $4; /* XXX $4 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;
+
+ if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 255 ||
+ $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
+ $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255) {
+ $$ = 1;
+ } else {
+ data_dest.su_sin.sin_len = sizeof(struct sockaddr_in);
+ data_dest.su_sin.sin_family = AF_INET;
+ p = (char *)&data_dest.su_sin.sin_port;
+ p[0] = $9; p[1] = $11;
+ a = (char *)&data_dest.su_sin.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ $$ = 0;
+ }
+ }
+ ;
+
+host_long_port4
+ : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER
+ {
+ char *a, *p;
+
+ /* reject invalid LPRT command */
+ if ($1 != 4 || $3 != 4 ||
+ $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
+ $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
+ $13 != 2 ||
+ $15 < 0 || $15 > 255 || $17 < 0 || $17 > 255) {
+ $$ = 1;
+ } else {
+ data_dest.su_sin.sin_len =
+ sizeof(struct sockaddr_in);
+ data_dest.su_family = AF_INET;
+ p = (char *)&data_dest.su_port;
+ p[0] = $15; p[1] = $17;
+ a = (char *)&data_dest.su_sin.sin_addr;
+ a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
+ $$ = 0;
+ }
+ }
+ ;
+
+host_long_port6
+ : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER
+ {
+ char *a, *p;
+
+ /* reject invalid LPRT command */
+ if ($1 != 6 || $3 != 16 ||
+ $5 < 0 || $5 > 255 || $7 < 0 || $7 > 255 ||
+ $9 < 0 || $9 > 255 || $11 < 0 || $11 > 255 ||
+ $13 < 0 || $13 > 255 || $15 < 0 || $15 > 255 ||
+ $17 < 0 || $17 > 255 || $19 < 0 || $19 > 255 ||
+ $21 < 0 || $21 > 255 || $23 < 0 || $23 > 255 ||
+ $25 < 0 || $25 > 255 || $27 < 0 || $27 > 255 ||
+ $29 < 0 || $29 > 255 || $31 < 0 || $31 > 255 ||
+ $33 < 0 || $33 > 255 || $35 < 0 || $35 > 255 ||
+ $37 != 2 ||
+ $39 < 0 || $39 > 255 || $41 < 0 || $41 > 255) {
+ $$ = 1;
+ } else {
+ data_dest.su_sin6.sin6_len =
+ sizeof(struct sockaddr_in6);
+ data_dest.su_family = AF_INET6;
+ p = (char *)&data_dest.su_port;
+ p[0] = $39; p[1] = $41;
+ a = (char *)&data_dest.su_sin6.sin6_addr;
+ a[0] = $5; a[1] = $7;
+ a[2] = $9; a[3] = $11;
+ a[4] = $13; a[5] = $15;
+ a[6] = $17; a[7] = $19;
+ a[8] = $21; a[9] = $23;
+ a[10] = $25; a[11] = $27;
+ a[12] = $29; a[13] = $31;
+ a[14] = $33; a[15] = $35;
+ if (his_addr.su_family == AF_INET6) {
+ /* XXX more sanity checks! */
+ data_dest.su_sin6.sin6_scope_id =
+ his_addr.su_sin6.sin6_scope_id;
+ }
+
+ $$ = 0;
+ }
+ }
+ ;
+
+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 && strchr($1, '~') != NULL) {
+ glob_t gl;
+ int flags =
+ GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
+ char *pptr = $1;
+
+ /*
+ * glob() will only find a leading ~, but
+ * Netscape kindly puts a slash in front of
+ * it for publish URLs. There needs to be
+ * a flag for glob() that expands tildes
+ * anywhere in the string.
+ */
+ if ((pptr[0] == '/') && (pptr[1] == '~'))
+ pptr++;
+
+ memset(&gl, 0, sizeof(gl));
+ if (glob(pptr, 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;
+ }
+ }
+ ;
+
+check_login_epsvall
+ : /* empty */
+ {
+ if (!logged_in) {
+ reply(530, "Please login with USER and PASS.");
+ $$ = 0;
+ } else if (epsvall) {
+ reply(501, "the command is disallowed "
+ "after EPSV ALL");
+ usedefault = 1;
+ $$ = 0;
+ } else
+ $$ = 1;
+ }
+ ;
+
+%%
+
+#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" },
+ { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
+ { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
+ { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
+ { "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 void help(struct tab *, char *);
+static struct tab *
+ lookup(struct tab *, char *);
+static void sizecmd(char *);
+static int yylex(void);
+
+extern int epsvall;
+
+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 (NULL);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+int
+getline(s, n, iop)
+ char *s;
+ int n;
+ FILE *iop;
+{
+ int c;
+ 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(0);
+ }
+ 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) {
+ /*
+ * If command doesn't fit into buffer, discard the
+ * rest of the command and indicate truncation.
+ * This prevents the command to be split up into
+ * multiple commands.
+ */
+ while (c != '\n' && (c = getc(iop)) != EOF)
+ ;
+ return (-2);
+ }
+ if (c == '\n')
+ break;
+ }
+ if (c == EOF && cs == s)
+ return (-1);
+ *cs++ = '\0';
+ if (debug) {
+ if (!guest && strncasecmp("pass ", s, 5) == 0) {
+ /* Don't syslog passwords */
+ syslog(LOG_DEBUG, "command: %.5s ???", s);
+ } else {
+ char *cp;
+ 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 (0);
+}
+
+/*ARGSUSED*/
+void
+toolong(signo)
+ int signo;
+{
+ struct syslog_data sdata = SYSLOG_DATA_INIT;
+
+ reply_r(421,
+ "Timeout (%d seconds): closing control connection.", timeout);
+ if (logging)
+ syslog_r(LOG_INFO, &sdata, "User %s timed out after %d seconds",
+ (pw ? pw -> pw_name : "unknown"), timeout);
+ dologout(1);
+}
+
+static int
+yylex()
+{
+ static int cpos;
+ char *cp, *cp2;
+ struct tab *p;
+ int n;
+ char c;
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) alarm((unsigned) timeout);
+ n = getline(cbuf, sizeof(cbuf)-1, stdin);
+ if (n == -1) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ } else if (n == -2) {
+ reply(500, "Command too long.");
+ alarm(0);
+ continue;
+ }
+ (void) alarm(0);
+ if ((cp = strchr(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+#ifdef HASSETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != 0) {
+ if ((cp = strpbrk(cbuf, "\n"))) {
+ c = *cp;
+ *cp = '\0';
+ setproctitle("%s: %s", proctitle, cbuf);
+ *cp = c;
+ }
+ }
+#endif /* HASSETPROCTITLE */
+ 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 != NULL) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ return (LEXERR);
+ }
+ 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 != NULL) {
+ if (p->implemented == 0) {
+ state = CMD;
+ nack(p->name);
+ return (LEXERR);
+ }
+ 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+1;
+ 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 = strdup(cp);
+ if (yylval.s == NULL)
+ fatal("Ran out of memory.");
+ 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);
+ }
+ if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 &&
+ !isalnum(cbuf[cpos + 3])) {
+ cpos += 3;
+ return ALL;
+ }
+ 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.");
+ }
+ state = CMD;
+ return (LEXERR);
+ }
+}
+
+void
+upper(s)
+ char *s;
+{
+ char *p;
+
+ for (p = s; *p; p++) {
+ if (islower(*p))
+ *p = (char)toupper(*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 == NULL) {
+ 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 == NULL) {
+ 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;
+ }
+ if (stbuf.st_size > 10240) {
+ reply(550, "%s: file too large for SIZE.", 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]);
+ }
+}
--- /dev/null
+.\" $OpenBSD: ftpd.8,v 1.65 2007/05/31 19:19:39 jmc Exp $
+.\" $NetBSD: ftpd.8,v 1.8 1996/01/14 20:55:23 thorpej Exp $
+.\"
+.\" 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. 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 $Mdocdate: May 31 2007 $
+.Dt FTPD 8
+.Os
+.Sh NAME
+.Nm ftpd
+.Nd Internet File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm ftpd
+.Op Fl 46ADdlMnPSU
+.Op Fl T Ar maxtimeout
+.Op Fl t Ar timeout
+.Op Fl u Ar mask
+.Sh DESCRIPTION
+.Nm
+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
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 4
+When
+.Fl D
+is specified, forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+When
+.Fl D
+is specified, forces
+.Nm
+to use IPv6 addresses only.
+.It Fl A
+Permit only anonymous FTP connections
+(unless the
+.Fl n
+option is specified),
+accounts listed in
+.Pa /etc/ftpchroot
+or users in a login class with the
+.Dq ftp-chroot
+variable set (see below).
+Other connection attempts are refused.
+.It Fl D
+With this option set,
+.Nm
+will detach and become a daemon, accepting connections on the FTP port and
+forking child processes to handle them.
+This has lower overhead than starting
+.Nm
+from
+.Xr inetd 8
+and is thus useful on busy servers to reduce load.
+.It Fl d
+Debugging information is written to the syslog using
+.Dv LOG_FTP .
+.It Fl l
+Each successful and failed
+FTP session is logged using syslog with a facility of
+.Dv 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 M
+Enables multihomed mode.
+Instead of simply using
+.Pa ~ftp
+for anonymous transfers, a directory matching the fully qualified name of
+the IP number the client connected to, and located inside
+.Pa ~ftp ,
+is used instead.
+.It Fl n
+Do not permit anonymous FTP logins.
+Normally they are permitted.
+.It Fl P
+Permit illegal port numbers or addresses for PORT command initiated connects.
+By default
+.Nm
+violates the RFC and thus constrains the PORT command to non-reserved ports
+and requires it use the same source address as the connection came from.
+This prevents the "FTP bounce attack" against services on both the local
+machine and other local machines.
+.It Fl S
+With this option set,
+.Nm
+logs all anonymous downloads to the file
+.Pa /var/log/ftpd
+when this file exists.
+.It Fl T Ar maxtimeout
+A client may also request a different timeout period;
+the maximum period allowed may be set to
+.Ar maxtimeout
+seconds with the
+.Fl T
+option.
+The default limit is 2 hours.
+.It Fl t Ar timeout
+The inactivity timeout period is set to
+.Ar timeout
+seconds (the default is 15 minutes).
+.It Fl U
+Each concurrent
+FTP session is logged to the file
+.Pa /var/run/utmp ,
+making them visible to commands such as
+.Xr who 1 .
+.It Fl u Ar mask
+Force the umask to
+.Ar mask ,
+instead of the default specified in
+.Pa /etc/login.conf
+(usually 022).
+Also disallows chmod.
+.El
+.Pp
+The file
+.Pa /etc/nologin
+can be used to disable FTP access.
+If the file exists,
+.Nm
+displays it and exits.
+Note: this method will disable
+.Em all
+non-root logins; see
+.Xr login 1
+for further details.
+If the file
+.Pa /etc/ftpwelcome
+exists,
+.Nm
+prints it before issuing the
+.Dq ready
+message.
+If the welcome file exists
+.Pa ( /etc/motd
+by default),
+.Nm
+prints it after a successful login.
+If the file
+.Pa .message
+exists in a directory,
+.Nm
+prints it when that directory is entered.
+.Pp
+The FTP server currently supports the following FTP requests.
+The case of the requests is ignored.
+.Bl -column "Request" -offset indent
+.It Sy Request Ta Sy Description
+.It ABOR Ta "abort previous command"
+.It ACCT Ta "specify account (not implemented)"
+.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 EPRT Ta "specify data connection port"
+.It EPSV Ta "prepare for server-to-server transfer"
+.It HELP Ta "give help information"
+.It LIST Ta "give list of files in a directory" Pq Li "ls -lgA"
+.It LPRT Ta "specify data connection port"
+.It LPSV Ta "prepare for server-to-server transfer"
+.It MDTM Ta "show last modification time of file"
+.It MKD Ta "make a directory"
+.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 REIN Ta "reinitialize (not implemented)"
+.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 SMNT Ta "structure mount (not implemented)"
+.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; not valid after login"
+.It XCUP Ta "change to parent of current working directory (deprec.)"
+.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:
+.Bl -column Request -offset indent
+.It Sy Request Ta Sy Description
+.It CHMOD Ta "change mode of a file, e.g., SITE CHMOD 755 filename"
+.It HELP Ta "give help information"
+.It IDLE Ta "set idle-timer, e.g., SITE IDLE 60"
+.It UMASK Ta "change umask, e.g., SITE UMASK 002"
+.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
+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
+authenticates users by using the service and type of
+.Ar ftp ,
+as defined in the
+.Pa /etc/login.conf
+file (see
+.Xr login.conf 5 ) .
+An authentication style
+may be specified by appending with a colon
+.Pq Sq :\&
+following the authentication style, i.e.\&
+.Dq joe:skey .
+The allowed authentication styles for
+.Nm
+may be explicitly specified by the
+.Dq auth-ftp
+entry in
+.Pa /etc/login.conf .
+.Pp
+.Nm
+authenticates users according to five rules.
+.Bl -enum -offset indent
+.It
+The login name must be in the password database 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 as described by
+.Xr shells 5 .
+.It
+If the user name appears in the file
+.Pa /etc/ftpchroot ,
+the session's root will be changed to the user's login directory by
+.Xr chroot 2
+as for an
+.Dq anonymous
+or
+.Dq ftp
+account (see next item).
+However, the user must still supply a password.
+This feature is intended as a compromise between a fully anonymous account
+and a fully privileged account.
+The account should also be set up as for an anonymous account.
+.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
+Once a user is authenticated the user must be approved by any approval
+script defined (see
+.Xr login.conf 5 ) .
+If a valid approval script (by either :approve=...: or :approve-ftp=...:
+for the user's class) is defined then it is run and must exit with a 0
+(success) status.
+When
+.Nm
+is running under the
+.Fl D
+flag (and debugging is not turned on) then the approval script will be
+called with at least the following variables specified via the
+.Fl v
+option (see
+.Xr login.conf 5 )
+to the approve script:
+.Bl -column "Variable" -offset indent
+.It Sy Variable Ta Sy Description
+.It FTPD_HOST Ta "The server's (virtual) hostname"
+.El
+.Pp
+For example (the line is broken to fit the page):
+.Bd -literal -offset indent
+/usr/libexec/auth/approve_ftpd -v FTPD_HOST=ftp.mycompany.com \e
+ username class service
+.Ed
+.Pp
+When the user logs in to the anonymous FTP account,
+.Nm
+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 (mode 555).
+.It Pa ~ftp/bin
+Make this directory owned by
+.Dq root
+and unwritable by anyone (mode 511).
+This directory is optional unless you have commands you wish
+the anonymous FTP user to be able to run (the
+.Xr ls 1
+command exists as a built-in).
+Any programs in this directory should be mode 111 (executable only).
+.It Pa ~ftp/etc
+Make this directory owned by
+.Dq root
+and unwritable by anyone (mode 511).
+The files pwd.db (see
+.Xr pwd_mkdb 8 )
+and
+.Xr group 5
+must be present for the
+.Xr ls 1
+command to be able to produce owner names rather than numbers.
+The password field in
+.Pa pwd.db
+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 555 and owned by
+.Dq root .
+This is traditionally where publicly accessible files are
+stored for download.
+.El
+.Pp
+If logging to the
+.Pa /var/log/ftpd
+file is enabled, information will be written in the following format:
+.Pp
+.Bl -tag -width XXXXXXXXXXXXXX -offset indent -compact
+.It time
+The time and date of the download, in
+.Xr ctime 3
+format.
+.It elapsed time
+The elapsed time, in seconds.
+.It remote host
+The remote host (or IP number).
+.It bytes
+The number of bytes transferred.
+.It path
+The full path (relative to the FTP chroot space) of the file transferred.
+.It type
+The type of transfer; either
+.Sq a
+for ASCII or
+.Sq b
+for binary.
+.It unused
+Unused field containing a
+.Sq * ,
+for compatibility.
+.It unused
+Unused field containing an
+.Sq o ,
+for compatibility.
+.It user type
+The type of user; either
+.Sq a
+for anonymous or
+.Sq r
+for a real user (should always be anonymous).
+.It name
+Either a system login name or the value given for
+.Dq email address
+if an anonymous user.
+.It unused
+Unused field containing a
+.Sq 0 ,
+for compatibility.
+.It real name
+The system login name if the connection is not anonymous, or a
+.Sq *
+if it is.
+.\" .It virtual host
+.\" The virtual host that the connection was made to.
+.El
+.Pp
+Although fields exist for logging information on real users, this file is
+only used for anonymous downloads.
+Unused fields exist only for compatibility with other
+.Nm
+implementations.
+.Sh LOGIN.CONF VARIABLES
+The
+.Nm
+daemon uses the following FTP-specific parameters:
+.Bl -tag -width ftp-chroot
+.It Pa auth-ftp
+The list of authentication types available to this class.
+See
+.Xr login.conf 5 .
+.It Pa ftp-chroot
+A boolean value.
+If set, users in this class will be automatically chrooted to
+the user's login directory.
+.It Pa ftp-dir
+A path to a directory.
+This value overrides the login directory for users in this class.
+A leading tilde
+.Pq Ql ~
+in
+.Pa ftp-dir
+will be expanded to the user's home directory based on the
+contents of the password database.
+.It Pa welcome
+The path of the file containing the welcome message.
+If this variable is not set,
+.Pa /etc/motd
+is used.
+.El
+.Sh PORT ALLOCATION
+For passive mode data connections,
+.Nm
+will listen to a random high TCP port.
+The interval of ports used are configurable using
+.Xr sysctl 8
+variables
+.Va net.inet.ip.porthifirst
+and
+.Va net.inet.ip.porthilast .
+.Sh FILES
+.Bl -tag -width /var/run/ftpd.pid -compact
+.It Pa /etc/ftpchroot
+list of normal users who should be chrooted
+.It Pa /etc/ftpusers
+list of unwelcome/restricted users
+.It Pa /etc/ftpwelcome
+welcome notice
+.It Pa /etc/login.conf
+authentication styles
+.It Pa /etc/motd
+printed after a successful login
+.It Pa /etc/nologin
+displayed and access refused
+.It Pa /var/log/ftpd
+log file for anonymous downloads
+.It Pa /var/run/ftpd.pid
+process ID if running in daemon mode
+.It Pa /var/run/utmp
+list of users on the system
+.El
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr login 1 ,
+.Xr skey 1 ,
+.Xr who 1 ,
+.Xr chroot 2 ,
+.Xr ctime 3 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr motd 5 ,
+.Xr services 5 ,
+.Xr shells 5 ,
+.Xr ftp-proxy 8 ,
+.Xr inetd 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
--- /dev/null
+/* $OpenBSD: ftpd.c,v 1.189 2010/06/18 06:02:57 tobias Exp $ */
+/* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */
+
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * 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. Neither the name of the project 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 PROJECT 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 PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 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. 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.
+ */
+
+/*
+ * FTP server.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#define FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include <bsd_auth.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <limits.h>
+#include <login_cap.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <vis.h>
+#include <unistd.h>
+#include <util.h>
+#include <utmp.h>
+#include <poll.h>
+
+#if defined(TCPWRAPPERS)
+#include <tcpd.h>
+#endif /* TCPWRAPPERS */
+
+#include "pathnames.h"
+#include "monitor.h"
+#include "extern.h"
+
+extern off_t restart_point;
+extern char cbuf[];
+
+union sockunion ctrl_addr;
+union sockunion data_source;
+union sockunion data_dest;
+union sockunion his_addr;
+union sockunion pasv_addr;
+
+sigset_t allsigs;
+
+int daemon_mode = 0;
+int data;
+int logged_in;
+struct passwd *pw;
+int debug = 0;
+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 anon_ok = 1;
+int anon_only = 0;
+int multihome = 0;
+int guest;
+int stats;
+int statfd = -1;
+int portcheck = 1;
+int dochroot;
+int type;
+int form;
+int stru; /* avoid C keyword */
+int mode;
+int doutmp = 0; /* update utmp file */
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+int family = AF_UNSPEC;
+volatile sig_atomic_t transflag;
+off_t file_size;
+off_t byte_count;
+#if !defined(CMASK) || CMASK == 0
+#undef CMASK
+#define CMASK 022
+#endif
+mode_t defumask = CMASK; /* default umask value */
+int umaskchange = 1; /* allow user to change umask value. */
+char tmpline[7];
+char hostname[MAXHOSTNAMELEN];
+char remotehost[MAXHOSTNAMELEN];
+char dhostname[MAXHOSTNAMELEN];
+char *guestpw;
+char ttyline[20];
+#if 0
+char *tty = ttyline; /* for klogin */
+#endif
+static struct utmp utmp; /* for utmp */
+static login_cap_t *lc;
+static auth_session_t *as;
+static volatile sig_atomic_t recvurg;
+
+#if defined(TCPWRAPPERS)
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif /* TCPWRAPPERS */
+
+char *ident = NULL;
+
+
+int epsvall = 0;
+
+/*
+ * 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 HASSETPROCTITLE
+char proctitle[BUFSIZ]; /* initial part of title */
+#endif /* HASSETPROCTITLE */
+
+#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(char *);
+static void sigurg(int);
+static void myoob(void);
+static int checkuser(char *, char *);
+static FILE *dataconn(char *, off_t, char *);
+static void dolog(struct sockaddr *);
+static char *copy_dir(char *, struct passwd *);
+static char *curdir(void);
+static void end_login(void);
+static FILE *getdatasock(char *);
+static int guniquefd(char *, char **);
+static void lostconn(int);
+static void sigquit(int);
+static int receive_data(FILE *, FILE *);
+static void replydirname(const char *, const char *);
+static int send_data(FILE *, FILE *, off_t, off_t, int);
+static struct passwd *
+ sgetpwnam(char *, struct passwd *);
+static void reapchild(int);
+#if defined(TCPWRAPPERS)
+static int check_host(struct sockaddr *);
+#endif /* TCPWRAPPERS */
+static void usage(void);
+
+void logxfer(char *, off_t, time_t);
+void set_slave_signals(void);
+
+static char *
+curdir(void)
+{
+ static char path[MAXPATHLEN+1]; /* path + '/' */
+
+ if (getcwd(path, sizeof(path)-1) == NULL)
+ return ("");
+ if (path[1] != '\0') /* special case for root dir. */
+ strlcat(path, "/", sizeof path);
+ /* For guest account, skip / since it's chrooted */
+ return (guest ? path+1 : path);
+}
+
+char *argstr = "AdDhnlMSt:T:u:UvP46";
+
+static void
+usage(void)
+{
+ syslog(LOG_ERR,
+ "usage: ftpd [-46ADdlMnPSU] [-T maxtimeout] [-t timeout] [-u mask]");
+ exit(2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ socklen_t addrlen;
+ int ch, on = 1, tos;
+ char line[LINE_MAX];
+ FILE *fp;
+ struct hostent *hp;
+ struct sigaction sa;
+ int error = 0;
+ const char *errstr;
+
+ tzset(); /* in case no timezone database in ~ftp */
+ sigfillset(&allsigs); /* used to block signals while root */
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ while ((ch = getopt(argc, argv, argstr)) != -1) {
+ switch (ch) {
+ case 'A':
+ anon_only = 1;
+ break;
+
+ case 'd':
+ case 'v': /* deprecated */
+ debug = 1;
+ break;
+
+ case 'D':
+ daemon_mode = 1;
+ break;
+
+ case 'P':
+ portcheck = 0;
+ break;
+
+ case 'h': /* deprecated */
+ break;
+
+ case 'l':
+ logging++; /* > 1 == extra logging */
+ break;
+
+ case 'M':
+ multihome = 1;
+ break;
+
+ case 'n':
+ anon_ok = 0;
+ break;
+
+ case 'S':
+ stats = 1;
+ break;
+
+ case 't':
+ timeout = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr) {
+ syslog(LOG_ERR,
+ "%s is a bad value for -t, aborting",
+ optarg);
+ exit(2);
+ }
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ break;
+
+ case 'T':
+ maxtimeout = strtonum(optarg, 0, INT_MAX,
+ &errstr);
+ if (errstr) {
+ syslog(LOG_ERR,
+ "%s is a bad value for -T, aborting",
+ optarg);
+ exit(2);
+ }
+ if (timeout > maxtimeout)
+ timeout = maxtimeout;
+ break;
+
+ case 'u':
+ {
+ long val = 0;
+ char *p;
+ umaskchange = 0;
+
+ val = strtol(optarg, &p, 8);
+ if (*optarg == '\0' || *p != '\0' || val < 0 ||
+ (val & ~ACCESSPERMS)) {
+ syslog(LOG_ERR,
+ "%s is a bad value for -u, aborting",
+ optarg);
+ exit(2);
+ }
+ defumask = val;
+ break;
+ }
+
+ case 'U':
+ doutmp = 1;
+ break;
+
+ case '4':
+ family = AF_INET;
+ break;
+
+ case '6':
+ family = AF_INET6;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ (void) freopen(_PATH_DEVNULL, "w", stderr);
+
+ /*
+ * 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);
+
+ if (getpwnam(FTPD_PRIVSEP_USER) == NULL) {
+ syslog(LOG_ERR, "privilege separation user %s not found",
+ FTPD_PRIVSEP_USER);
+ exit(1);
+ }
+ endpwent();
+
+ if (daemon_mode) {
+ int *fds, i, fd;
+ struct pollfd *pfds;
+ struct addrinfo hints, *res, *res0;
+ nfds_t n;
+
+ /*
+ * Detach from parent.
+ */
+ if (daemon(1, 1) < 0) {
+ syslog(LOG_ERR, "failed to become a daemon");
+ exit(1);
+ }
+ sa.sa_handler = reapchild;
+ (void) sigaction(SIGCHLD, &sa, NULL);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(NULL, "ftp", &hints, &res0);
+ if (error) {
+ syslog(LOG_ERR, "%s", gai_strerror(error));
+ exit(1);
+ }
+
+ n = 0;
+ for (res = res0; res; res = res->ai_next)
+ n++;
+
+ fds = calloc(n, sizeof(int));
+ pfds = calloc(n, sizeof(struct pollfd));
+ if (!fds || !pfds) {
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Open sockets, bind it to the FTP port, and start
+ * listening.
+ */
+ n = 0;
+ for (res = res0; res; res = res->ai_next) {
+ fds[n] = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (fds[n] < 0)
+ continue;
+
+ if (setsockopt(fds[n], SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0) {
+ close(fds[n]);
+ fds[n] = -1;
+ continue;
+ }
+
+ if (bind(fds[n], res->ai_addr, res->ai_addrlen) < 0) {
+ close(fds[n]);
+ fds[n] = -1;
+ continue;
+ }
+ if (listen(fds[n], 32) < 0) {
+ close(fds[n]);
+ fds[n] = -1;
+ continue;
+ }
+
+ pfds[n].fd = fds[n];
+ pfds[n].events = POLLIN;
+ n++;
+ }
+ freeaddrinfo(res0);
+
+ if (n == 0) {
+ syslog(LOG_ERR, "could not open control socket");
+ exit(1);
+ }
+
+ /* Stash pid in pidfile */
+ if (pidfile(NULL))
+ syslog(LOG_ERR, "can't open pidfile: %m");
+ /*
+ * Loop forever accepting connection requests and forking off
+ * children to handle them.
+ */
+ while (1) {
+ if (poll(pfds, n, INFTIM) < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ exit(1);
+ }
+ for (i = 0; i < n; i++)
+ if (pfds[i].revents & POLLIN) {
+ addrlen = sizeof(his_addr);
+ fd = accept(pfds[i].fd,
+ (struct sockaddr *)&his_addr,
+ &addrlen);
+ if (fd != -1) {
+ if (fork() == 0)
+ goto child;
+ close(fd);
+ }
+ }
+ }
+
+ child:
+ /* child */
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ for (i = 0; i < n; i++)
+ close(fds[i]);
+#if defined(TCPWRAPPERS)
+ /* ..in the child. */
+ if (!check_host((struct sockaddr *)&his_addr))
+ exit(1);
+#endif /* TCPWRAPPERS */
+ } else {
+ addrlen = sizeof(his_addr);
+ if (getpeername(0, (struct sockaddr *)&his_addr,
+ &addrlen) < 0) {
+ /* syslog(LOG_ERR, "getpeername (%s): %m", argv[0]); */
+ exit(1);
+ }
+ }
+
+ /* set this here so klogin can use it... */
+ (void)snprintf(ttyline, sizeof(ttyline), "ftp%ld", (long)getpid());
+
+ set_slave_signals();
+
+ addrlen = sizeof(ctrl_addr);
+ if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+ if (his_addr.su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) {
+#if 1
+ /*
+ * IPv4 control connection arrived to AF_INET6 socket.
+ * I hate to do this, but this is the easiest solution.
+ */
+ union sockunion tmp_addr;
+ const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
+
+ tmp_addr = his_addr;
+ memset(&his_addr, 0, sizeof(his_addr));
+ his_addr.su_sin.sin_family = AF_INET;
+ his_addr.su_sin.sin_len = sizeof(his_addr.su_sin);
+ memcpy(&his_addr.su_sin.sin_addr,
+ &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
+ sizeof(his_addr.su_sin.sin_addr));
+ his_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;
+
+ tmp_addr = ctrl_addr;
+ memset(&ctrl_addr, 0, sizeof(ctrl_addr));
+ ctrl_addr.su_sin.sin_family = AF_INET;
+ ctrl_addr.su_sin.sin_len = sizeof(ctrl_addr.su_sin);
+ memcpy(&ctrl_addr.su_sin.sin_addr,
+ &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
+ sizeof(ctrl_addr.su_sin.sin_addr));
+ ctrl_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;
+#else
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+ lreply(530, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) close(fd);
+ reply(530,
+ "Connection from IPv4 mapped address is not supported.");
+ exit(0);
+#endif
+ }
+#ifdef IP_TOS
+ if (his_addr.su_family == AF_INET) {
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, &tos,
+ sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+#endif
+ data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt: %m");
+#endif
+
+ dolog((struct sockaddr *)&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 ((fp = fopen(_PATH_NOLOGIN, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+ lreply(530, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fp);
+ reply(530, "System not available.");
+ exit(0);
+ }
+ if ((fp = fopen(_PATH_FTPWELCOME, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+ lreply(220, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fp);
+ /* reply(220,) must follow */
+ }
+ (void) gethostname(hostname, sizeof(hostname));
+
+ /* Make sure hostname is fully qualified. */
+ hp = gethostbyname(hostname);
+ if (hp != NULL)
+ strlcpy(hostname, hp->h_name, sizeof(hostname));
+
+ if (multihome) {
+ error = getnameinfo((struct sockaddr *)&ctrl_addr,
+ ctrl_addr.su_len, dhostname, sizeof(dhostname), NULL, 0, 0);
+ }
+
+ if (error != 0)
+ reply(220, "FTP server ready.");
+ else
+ reply(220, "%s FTP server ready.",
+ (multihome ? dhostname : hostname));
+
+ monitor_init();
+
+ for (;;)
+ (void) yyparse();
+ /* NOTREACHED */
+}
+
+/*
+ * Signal handlers.
+ */
+/*ARGSUSED*/
+static void
+lostconn(int signo)
+{
+ struct syslog_data sdata = SYSLOG_DATA_INIT;
+
+ sdata.log_fac = LOG_FTP;
+ if (debug)
+ syslog_r(LOG_DEBUG, &sdata, "lost connection");
+ dologout(1);
+}
+
+static void
+sigquit(int signo)
+{
+ struct syslog_data sdata = SYSLOG_DATA_INIT;
+
+ sdata.log_fac = LOG_FTP;
+ syslog_r(LOG_DEBUG, &sdata, "got signal %s", sys_signame[signo]);
+ dologout(1);
+}
+
+/*
+ * 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(char *name, struct passwd *pw)
+{
+ static struct passwd *save;
+ struct passwd *old;
+
+ if (pw == NULL && (pw = getpwnam(name)) == NULL)
+ return (NULL);
+ old = save;
+ save = pw_dup(pw);
+ if (save == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ if (old) {
+ memset(old->pw_passwd, 0, strlen(old->pw_passwd));
+ free(old);
+ }
+ return (save);
+}
+
+static int login_attempts; /* number of failed login attempts */
+static int askpasswd; /* had user command, ask for passwd */
+static char curname[MAXLOGNAME]; /* 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(char *name)
+{
+ char *cp, *shell, *style, *host;
+ char *class = NULL;
+
+ if (logged_in) {
+ kill_slave("user already logged in");
+ end_login();
+ }
+
+ /* Close session from previous user if there was one. */
+ if (as) {
+ auth_close(as);
+ as = NULL;
+ }
+ if (lc) {
+ login_close(lc);
+ lc = NULL;
+ }
+
+ if ((style = strchr(name, ':')) != NULL)
+ *style++ = 0;
+
+ guest = 0;
+ host = multihome ? dhostname : hostname;
+ if (anon_ok &&
+ (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0)) {
+ if (checkuser(_PATH_FTPUSERS, "ftp") ||
+ checkuser(_PATH_FTPUSERS, "anonymous"))
+ reply(530, "User %s access denied.", name);
+ else if ((pw = sgetpwnam("ftp", NULL)) != NULL) {
+ guest = 1;
+ askpasswd = 1;
+ lc = login_getclass(pw->pw_class);
+ if ((as = auth_open()) == NULL ||
+ auth_setpwd(as, pw) != 0 ||
+ auth_setoption(as, "FTPD_HOST", host) < 0) {
+ if (as) {
+ auth_close(as);
+ as = NULL;
+ }
+ login_close(lc);
+ lc = NULL;
+ reply(421, "Local resource failure");
+ return;
+ }
+ reply(331,
+ "Guest login ok, send your email address as password.");
+ } else
+ reply(530, "User %s unknown.", name);
+ if (!askpasswd && logging)
+ syslog(LOG_NOTICE,
+ "ANONYMOUS FTP LOGIN REFUSED FROM %s", remotehost);
+ return;
+ }
+
+ shell = _PATH_BSHELL;
+ if ((pw = sgetpwnam(name, NULL))) {
+ class = pw->pw_class;
+ if (pw->pw_shell != NULL && *pw->pw_shell != '\0')
+ shell = pw->pw_shell;
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ shell = cp;
+ endusershell();
+ }
+
+ /* Get login class; if invalid style treat like unknown user. */
+ lc = login_getclass(class);
+ if (lc && (style = login_getstyle(lc, style, "auth-ftp")) == NULL) {
+ login_close(lc);
+ lc = NULL;
+ pw = NULL;
+ }
+
+ /* Do pre-authentication setup. */
+ if (lc && ((as = auth_open()) == NULL ||
+ (pw != NULL && auth_setpwd(as, pw) != 0) ||
+ auth_setitem(as, AUTHV_STYLE, style) < 0 ||
+ auth_setitem(as, AUTHV_NAME, name) < 0 ||
+ auth_setitem(as, AUTHV_CLASS, class) < 0 ||
+ auth_setoption(as, "login", "yes") < 0 ||
+ auth_setoption(as, "notickets", "yes") < 0 ||
+ auth_setoption(as, "FTPD_HOST", host) < 0)) {
+ if (as) {
+ auth_close(as);
+ as = NULL;
+ }
+ login_close(lc);
+ lc = NULL;
+ reply(421, "Local resource failure");
+ return;
+ }
+ if (logging)
+ strlcpy(curname, name, sizeof(curname));
+
+ dochroot = (lc && login_getcapbool(lc, "ftp-chroot", 0)) ||
+ checkuser(_PATH_FTPCHROOT, name);
+ if (anon_only && !dochroot) {
+ reply(530, "User %s access denied.", name);
+ return;
+ }
+ if (pw) {
+ if ((!shell && !dochroot) || checkuser(_PATH_FTPUSERS, name)) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = NULL;
+ return;
+ }
+ }
+
+ if (as != NULL && (cp = auth_challenge(as)) != NULL)
+ reply(331, "%s", cp);
+ else
+ 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 "fname"
+ */
+static int
+checkuser(char *fname, char *name)
+{
+ FILE *fp;
+ int found = 0;
+ char *p, line[BUFSIZ];
+
+ if ((fp = fopen(fname, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fp) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ (void) fclose(fp);
+ }
+ 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)
+{
+ sigprocmask (SIG_BLOCK, &allsigs, NULL);
+ if (logged_in) {
+ ftpdlogwtmp(ttyline, "", "");
+ if (doutmp)
+ ftpd_logout(utmp.ut_line);
+ }
+ reply(530, "Please reconnect to work as another user");
+ _exit(0);
+}
+
+enum auth_ret
+pass(char *passwd)
+{
+ int authok;
+ unsigned int flags;
+ FILE *fp;
+ static char homedir[MAXPATHLEN];
+ char *motd, *dir, rootdir[MAXPATHLEN];
+ size_t sz_pw_dir;
+
+ if (logged_in || askpasswd == 0) {
+ reply(503, "Login with USER first.");
+ return (AUTH_FAILED);
+ }
+ askpasswd = 0;
+ if (!guest) { /* "ftp" is only account allowed no password */
+ authok = 0;
+ if (pw == NULL || pw->pw_passwd[0] == '\0') {
+ useconds_t us;
+
+ /* Sleep between 1 and 3 seconds to emulate a crypt. */
+ us = arc4random_uniform(3000000);
+ usleep(us);
+ if (as != NULL) {
+ auth_close(as);
+ as = NULL;
+ }
+ } else {
+ authok = auth_userresponse(as, passwd, 0);
+ as = NULL;
+ }
+ if (authok == 0) {
+ 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);
+ kill_slave("repeated login failures");
+ _exit(0);
+ }
+ return (AUTH_FAILED);
+ }
+ } else if (lc != NULL) {
+ /* Save anonymous' password. */
+ if (guestpw != NULL)
+ free(guestpw);
+ guestpw = strdup(passwd);
+ if (guestpw == NULL) {
+ kill_slave("out of mem");
+ fatal("Out of memory.");
+ }
+
+ authok = auth_approval(as, lc, pw->pw_name, "ftp");
+ auth_close(as);
+ as = NULL;
+ if (authok == 0) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN FAILED (HOST) as %s: approval failure.",
+ pw->pw_name);
+ reply(530, "Approval failure.");
+ kill_slave("approval failure");
+ _exit(0);
+ }
+ } else {
+ syslog(LOG_INFO|LOG_AUTH,
+ "FTP LOGIN CLASS %s MISSING for %s: approval failure.",
+ pw->pw_class, pw->pw_name);
+ reply(530, "Permission denied.");
+ kill_slave("permission denied");
+ _exit(0);
+ }
+
+ if (monitor_post_auth() == 1) {
+ /* Post-auth monitor process */
+ logged_in = 1;
+ return (AUTH_MONITOR);
+ }
+
+ login_attempts = 0; /* this time successful */
+ /* set umask via setusercontext() unless -u flag was given. */
+ flags = LOGIN_SETGROUP|LOGIN_SETPRIORITY|LOGIN_SETRESOURCES;
+ if (umaskchange)
+ flags |= LOGIN_SETUMASK;
+ else
+ (void) umask(defumask);
+ if (setusercontext(lc, pw, (uid_t)0, flags) != 0) {
+ perror_reply(421, "Local resource failure: setusercontext");
+ syslog(LOG_NOTICE, "setusercontext: %m");
+ dologout(1);
+ /* NOTREACHED */
+ }
+
+ /* open wtmp before chroot */
+ ftpdlogwtmp(ttyline, pw->pw_name, remotehost);
+
+ /* open utmp before chroot */
+ if (doutmp) {
+ memset((void *)&utmp, 0, sizeof(utmp));
+ (void)time(&utmp.ut_time);
+ (void)strncpy(utmp.ut_name, pw->pw_name, sizeof(utmp.ut_name));
+ (void)strncpy(utmp.ut_host, remotehost, sizeof(utmp.ut_host));
+ (void)strncpy(utmp.ut_line, ttyline, sizeof(utmp.ut_line));
+ ftpd_login(&utmp);
+ }
+
+ /* open stats file before chroot */
+ if (guest && (stats == 1) && (statfd < 0))
+ if ((statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND)) < 0)
+ stats = 0;
+
+ logged_in = 1;
+
+ if ((dir = login_getcapstr(lc, "ftp-dir", NULL, NULL))) {
+ char *newdir;
+
+ newdir = copy_dir(dir, pw);
+ if (newdir == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ pw->pw_dir = newdir;
+ pw = sgetpwnam(NULL, pw);
+ free(dir);
+ free(newdir);
+ }
+
+ /* make sure pw->pw_dir is big enough to hold "/" */
+ sz_pw_dir = strlen(pw->pw_dir) + 1;
+ if (sz_pw_dir < 2) {
+ pw->pw_dir = "/";
+ pw = sgetpwnam(NULL, pw);
+ sz_pw_dir = 2;
+ }
+
+ if (guest || dochroot) {
+ if (multihome && guest) {
+ struct stat ts;
+
+ /* Compute root directory. */
+ snprintf(rootdir, sizeof(rootdir), "%s/%s",
+ pw->pw_dir, dhostname);
+ if (stat(rootdir, &ts) < 0) {
+ snprintf(rootdir, sizeof(rootdir), "%s/%s",
+ pw->pw_dir, hostname);
+ }
+ } else
+ strlcpy(rootdir, pw->pw_dir, sizeof(rootdir));
+ }
+ 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(rootdir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+ strlcpy(pw->pw_dir, "/", sz_pw_dir);
+ if (setenv("HOME", "/", 1) == -1) {
+ reply(550, "Can't setup environment.");
+ goto bad;
+ }
+ } else if (dochroot) {
+ if (chroot(rootdir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't change root.");
+ goto bad;
+ }
+ strlcpy(pw->pw_dir, "/", sz_pw_dir);
+ if (setenv("HOME", "/", 1) == -1) {
+ reply(550, "Can't setup environment.");
+ 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 (setegid(pw->pw_gid) < 0 || setgid(pw->pw_gid) < 0) {
+ reply(550, "Can't set gid.");
+ goto bad;
+ }
+ if (seteuid(pw->pw_uid) < 0 || setuid(pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+ sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
+
+ /*
+ * Set home directory so that use of ~ (tilde) works correctly.
+ */
+ if (getcwd(homedir, MAXPATHLEN) != NULL) {
+ if (setenv("HOME", homedir, 1) == -1) {
+ reply(550, "Can't setup environment.");
+ goto bad;
+ }
+ }
+
+ /*
+ * Display a login message, if it exists.
+ * N.B. reply(230,) must follow the message.
+ */
+ motd = login_getcapstr(lc, "welcome", NULL, NULL);
+ if ((fp = fopen(motd ? motd : _PATH_FTPLOGINMESG, "r")) != NULL) {
+ char line[LINE_MAX];
+
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+ lreply(230, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fp);
+ }
+ if (motd != NULL)
+ free(motd);
+ if (guest) {
+ if (ident != NULL)
+ free(ident);
+ ident = strdup(passwd);
+ if (ident == NULL)
+ fatal("Ran out of memory.");
+ reply(230, "Guest login ok, access restrictions apply.");
+#ifdef HASSETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: anonymous/%.*s", remotehost,
+ (int)(sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/")), passwd);
+ setproctitle("%s", proctitle);
+#endif /* HASSETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+ remotehost, passwd);
+ } else {
+ reply(230, "User %s logged in.", pw->pw_name);
+#ifdef HASSETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle),
+ "%s: %s", remotehost, pw->pw_name);
+ setproctitle("%s", proctitle);
+#endif /* HASSETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "FTP LOGIN FROM %s as %s",
+ remotehost, pw->pw_name);
+ }
+ login_close(lc);
+ lc = NULL;
+ return (AUTH_SLAVE);
+bad:
+ /* Forget all about it... */
+ login_close(lc);
+ lc = NULL;
+ end_login();
+ return (AUTH_FAILED);
+}
+
+void
+retrieve(char *cmd, char *name)
+{
+ FILE *fin, *dout;
+ struct stat st;
+ int (*closefunc)(FILE *);
+ time_t start;
+
+ if (cmd == 0) {
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ } else {
+ char line[BUFSIZ];
+
+ (void) snprintf(line, sizeof(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) {
+ if (ferror(fin)) {
+ perror_reply(550, name);
+ goto done;
+ } else
+ break;
+ }
+ if (c == '\n')
+ i++;
+ }
+ } else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ dout = dataconn(name, st.st_size, "w");
+ if (dout == NULL)
+ goto done;
+ time(&start);
+ send_data(fin, dout, (off_t)st.st_blksize, st.st_size,
+ (restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)));
+ if ((cmd == 0) && stats)
+ logxfer(name, byte_count, start);
+ (void) fclose(dout);
+ data = -1;
+done:
+ if (pdata >= 0)
+ (void) close(pdata);
+ pdata = -1;
+ if (cmd == 0)
+ LOGBYTES("get", name, byte_count);
+ (*closefunc)(fin);
+}
+
+void
+store(char *name, char *mode, int unique)
+{
+ FILE *fout, *din;
+ int (*closefunc)(FILE *);
+ struct stat st;
+ int fd;
+
+ if (restart_point && *mode != 'a')
+ mode = "r+";
+
+ if (unique && stat(name, &st) == 0) {
+ char *nam;
+
+ fd = guniquefd(name, &nam);
+ if (fd == -1) {
+ LOGCMD(*mode == 'w' ? "put" : "append", name);
+ return;
+ }
+ name = nam;
+ fout = fdopen(fd, mode);
+ } else
+ 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) {
+ if (ferror(fout)) {
+ perror_reply(550, name);
+ goto done;
+ } else
+ break;
+ }
+ if (c == '\n')
+ i++;
+ }
+ /*
+ * We must do this seek to "current" position
+ * because we are changing from reading to
+ * writing.
+ */
+ if (fseek(fout, 0L, SEEK_CUR) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ } else if (lseek(fileno(fout), restart_point, SEEK_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(char *mode)
+{
+ int on = 1, s, t, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+ sigprocmask (SIG_BLOCK, &allsigs, NULL);
+ s = monitor_socket(ctrl_addr.su_family);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0)
+ goto bad;
+ /* anchor socket to avoid multi-homing problems */
+ data_source = ctrl_addr;
+ data_source.su_port = htons(20); /* ftp-data port */
+ for (tries = 1; ; tries++) {
+ if (monitor_bind(s, (struct sockaddr *)&data_source,
+ data_source.su_len) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep((unsigned int)tries);
+ }
+ sigprocmask (SIG_UNBLOCK, &allsigs, NULL);
+
+#ifdef IP_TOS
+ if (ctrl_addr.su_family == AF_INET) {
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, &on,
+ sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
+#endif
+#ifdef TCP_NOPUSH
+ /*
+ * Turn off push flag to keep sender TCP from sending short packets
+ * at the boundaries of each write(). Should probably do a SO_SNDBUF
+ * to set the send buffer size as well, but that may not be desirable
+ * in heavy-load situations.
+ */
+ on = 1;
+ if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
+#endif
+#ifdef SO_SNDBUF
+ on = 65536;
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &on, sizeof(on)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_SNDBUF): %m");
+#endif
+
+ return (fdopen(s, mode));
+bad:
+ /* Return the real value of errno (close may change it) */
+ t = errno;
+ sigprocmask (SIG_UNBLOCK, &allsigs, NULL);
+ if (s >= 0)
+ (void) close(s);
+ errno = t;
+ return (NULL);
+}
+
+static FILE *
+dataconn(char *name, off_t size, char *mode)
+{
+ char sizebuf[32];
+ FILE *file = NULL;
+ int retry = 0;
+ in_port_t *p;
+ u_char *fa, *ha;
+ size_t alen;
+ int error;
+
+ file_size = size;
+ byte_count = 0;
+ if (size != (off_t) -1) {
+ (void) snprintf(sizebuf, sizeof(sizebuf), " (%qd bytes)",
+ size);
+ } else
+ sizebuf[0] = '\0';
+ if (pdata >= 0) {
+ union sockunion from;
+ int s;
+ socklen_t fromlen = sizeof(from);
+
+ (void) alarm ((unsigned) timeout);
+ s = accept(pdata, (struct sockaddr *)&from, &fromlen);
+ (void) alarm (0);
+ if (s < 0) {
+ reply(425, "Can't open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return (NULL);
+ }
+ switch (from.su_family) {
+ case AF_INET:
+ p = (in_port_t *)&from.su_sin.sin_port;
+ fa = (u_char *)&from.su_sin.sin_addr;
+ ha = (u_char *)&his_addr.su_sin.sin_addr;
+ alen = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ p = (in_port_t *)&from.su_sin6.sin6_port;
+ fa = (u_char *)&from.su_sin6.sin6_addr;
+ ha = (u_char *)&his_addr.su_sin6.sin6_addr;
+ alen = sizeof(struct in6_addr);
+ break;
+ default:
+ reply(425, "Can't build data connection: "
+ "unknown address family");
+ (void) close(pdata);
+ (void) close(s);
+ pdata = -1;
+ return (NULL);
+ }
+ if (from.su_family != his_addr.su_family ||
+ ntohs(*p) < IPPORT_RESERVED) {
+ reply(425, "Can't build data connection: "
+ "address family or port error");
+ (void) close(pdata);
+ (void) close(s);
+ pdata = -1;
+ return (NULL);
+ }
+ if (portcheck && memcmp(fa, ha, alen) != 0) {
+ reply(425, "Can't build data connection: "
+ "illegal port number");
+ (void) close(pdata);
+ (void) close(s);
+ pdata = -1;
+ return (NULL);
+ }
+ (void) close(pdata);
+ pdata = s;
+ 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;
+ do {
+ if (file != NULL)
+ (void) fclose(file);
+ file = getdatasock(mode);
+ if (file == NULL) {
+ char hbuf[MAXHOSTNAMELEN], pbuf[10];
+
+ error = getnameinfo((struct sockaddr *)&data_source,
+ data_source.su_len, hbuf, sizeof(hbuf), pbuf,
+ sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (error != 0)
+ reply(425, "Can't create data socket: %s.",
+ strerror(errno));
+ else
+ reply(425,
+ "Can't create data socket (%s,%s): %s.",
+ hbuf, pbuf, strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * attempt to connect to reserved port on client machine;
+ * this looks like an attack
+ */
+ switch (data_dest.su_family) {
+ case AF_INET:
+ p = (in_port_t *)&data_dest.su_sin.sin_port;
+ fa = (u_char *)&data_dest.su_sin.sin_addr;
+ ha = (u_char *)&his_addr.su_sin.sin_addr;
+ alen = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ p = (in_port_t *)&data_dest.su_sin6.sin6_port;
+ fa = (u_char *)&data_dest.su_sin6.sin6_addr;
+ ha = (u_char *)&his_addr.su_sin6.sin6_addr;
+ alen = sizeof(struct in6_addr);
+ break;
+ default:
+ reply(425, "Can't build data connection: "
+ "unknown address family");
+ (void) fclose(file);
+ pdata = -1;
+ return (NULL);
+ }
+ if (data_dest.su_family != his_addr.su_family ||
+ ntohs(*p) < IPPORT_RESERVED || ntohs(*p) == 2049) { /* XXX */
+ reply(425, "Can't build data connection: "
+ "address family or port error");
+ (void) fclose(file);
+ return NULL;
+ }
+ if (portcheck && memcmp(fa, ha, alen) != 0) {
+ reply(435, "Can't build data connection: "
+ "illegal port number");
+ (void) fclose(file);
+ return NULL;
+ }
+
+ if (connect(fileno(file), (struct sockaddr *)&data_dest,
+ data_dest.su_len) == 0) {
+ reply(150, "Opening %s mode data connection for '%s'%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ data = fileno(file);
+ return (file);
+ }
+ if (errno != EADDRINUSE)
+ break;
+ sleep((unsigned) swaitint);
+ retry += swaitint;
+ } while (retry <= swaitmax);
+ perror_reply(425, "Can't build data connection");
+ (void) fclose(file);
+ return (NULL);
+}
+
+/*
+ * Transfer 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 int
+send_data(FILE *instr, FILE *outstr, off_t blksize, off_t filesize, int isreg)
+{
+ int c, cnt, filefd, netfd;
+ char *buf, *bp;
+ size_t len;
+
+ transflag++;
+ switch (type) {
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ if (recvurg)
+ goto got_oob;
+ 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(0);
+
+ case TYPE_I:
+ case TYPE_L:
+ /*
+ * isreg is only set if we are not doing restart and we
+ * are sending a regular file
+ */
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+
+ if (isreg && filesize < (off_t)16 * 1024 * 1024) {
+ size_t fsize = (size_t)filesize;
+
+ buf = mmap(0, fsize, PROT_READ, MAP_SHARED, filefd,
+ (off_t)0);
+ if (buf == MAP_FAILED) {
+ syslog(LOG_WARNING, "mmap(%llu): %m",
+ (unsigned long long)fsize);
+ goto oldway;
+ }
+ bp = buf;
+ len = fsize;
+ do {
+ cnt = write(netfd, bp, len);
+ if (recvurg) {
+ munmap(buf, fsize);
+ goto got_oob;
+ }
+ len -= cnt;
+ bp += cnt;
+ if (cnt > 0)
+ byte_count += cnt;
+ } while(cnt > 0 && len > 0);
+
+ transflag = 0;
+ munmap(buf, fsize);
+ if (cnt < 0)
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return(0);
+ }
+
+oldway:
+ if ((buf = malloc((size_t)blksize)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ return(-1);
+ }
+
+ while ((cnt = read(filefd, buf, (size_t)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(0);
+ default:
+ transflag = 0;
+ reply(550, "Unimplemented TYPE %d in send_data", type);
+ return(-1);
+ }
+
+data_err:
+ transflag = 0;
+ reply(426, "Data connection");
+ return(-1);
+
+file_err:
+ transflag = 0;
+ reply(551, "Error on input file");
+ return(-1);
+
+got_oob:
+ myoob();
+ recvurg = 0;
+ transflag = 0;
+ return(-1);
+}
+
+/*
+ * 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(FILE *instr, FILE *outstr)
+{
+ int c;
+ int cnt;
+ char buf[BUFSIZ];
+ struct sigaction sa, sa_saved;
+ volatile int bare_lfs = 0;
+
+ transflag++;
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ memset(&sa, 0, sizeof(sa));
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = lostconn;
+ (void) sigaction(SIGALRM, &sa, &sa_saved);
+ do {
+ (void) alarm ((unsigned) timeout);
+ cnt = read(fileno(instr), buf, sizeof(buf));
+ (void) alarm (0);
+ if (recvurg)
+ goto got_oob;
+
+ if (cnt > 0) {
+ if (write(fileno(outstr), buf, cnt) != cnt)
+ goto file_err;
+ byte_count += cnt;
+ }
+ } while (cnt > 0);
+ (void) sigaction(SIGALRM, &sa_saved, NULL);
+ 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) {
+ if (recvurg)
+ goto got_oob;
+ 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);
+ 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;
+ reply(426, "Data Connection");
+ return (-1);
+
+file_err:
+ transflag = 0;
+ reply(452, "Error writing file");
+ return (-1);
+
+got_oob:
+ myoob();
+ recvurg = 0;
+ transflag = 0;
+ return (-1);
+}
+
+void
+statfilecmd(char *filename)
+{
+ FILE *fin;
+ int c;
+ int atstart;
+ char line[LINE_MAX];
+
+ (void)snprintf(line, sizeof(line), "/bin/ls -lgA %s", filename);
+ fin = ftpd_popen(line, "r");
+ if (fin == NULL) {
+ reply(451, "Local resource failure");
+ return;
+ }
+ lreply(211, "status of %s:", filename);
+ atstart = 1;
+ 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);
+ }
+ if (atstart && isdigit(c))
+ (void) putc(' ', stdout);
+ (void) putc(c, stdout);
+ atstart = (c == '\n');
+ }
+ (void) ftpd_pclose(fin);
+ reply(211, "End of Status");
+}
+
+void
+statcmd(void)
+{
+ union sockunion *su;
+ u_char *a, *p;
+ char hbuf[MAXHOSTNAMELEN];
+ int ispassive;
+ int error;
+
+ lreply(211, "%s FTP server status:", hostname);
+ error = getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
+ printf(" Connected to %s", remotehost);
+ if (error == 0 && strcmp(remotehost, hbuf) != 0)
+ printf(" (%s)", hbuf);
+ 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]);
+ ispassive = 0;
+ if (data != -1)
+ printf(" Data connection open\r\n");
+ else if (pdata != -1) {
+ printf(" in Passive mode\r\n");
+ su = (union sockunion *)&pasv_addr;
+ ispassive++;
+ goto printaddr;
+ } else if (usedefault == 0) {
+ size_t alen;
+ int af, i;
+
+ su = (union sockunion *)&data_dest;
+printaddr:
+ /* PASV/PORT */
+ if (su->su_family == AF_INET) {
+ if (ispassive)
+ printf("211- PASV ");
+ else
+ printf("211- PORT ");
+ a = (u_char *)&su->su_sin.sin_addr;
+ p = (u_char *)&su->su_sin.sin_port;
+ printf("(%u,%u,%u,%u,%u,%u)\r\n",
+ a[0], a[1], a[2], a[3],
+ p[0], p[1]);
+ }
+
+ /* LPSV/LPRT */
+ alen = 0;
+ switch (su->su_family) {
+ case AF_INET:
+ a = (u_char *)&su->su_sin.sin_addr;
+ p = (u_char *)&su->su_sin.sin_port;
+ alen = sizeof(su->su_sin.sin_addr);
+ af = 4;
+ break;
+ case AF_INET6:
+ a = (u_char *)&su->su_sin6.sin6_addr;
+ p = (u_char *)&su->su_sin6.sin6_port;
+ alen = sizeof(su->su_sin6.sin6_addr);
+ af = 6;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ if (ispassive)
+ printf("211- LPSV ");
+ else
+ printf("211- LPRT ");
+ printf("(%u,%llu", af, (unsigned long long)alen);
+ for (i = 0; i < alen; i++)
+ printf(",%u", a[i]);
+ printf(",%u,%u,%u)\r\n", 2, p[0], p[1]);
+ }
+
+ /* EPRT/EPSV */
+ switch (su->su_family) {
+ case AF_INET:
+ af = 1;
+ break;
+ case AF_INET6:
+ af = 2;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ char pbuf[10];
+ union sockunion tmp = *su;
+
+ if (tmp.su_family == AF_INET6)
+ tmp.su_sin6.sin6_scope_id = 0;
+ if (getnameinfo((struct sockaddr *)&tmp, tmp.su_len,
+ hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
+ if (ispassive)
+ printf("211- EPSV ");
+ else
+ printf("211- EPRT ");
+ printf("(|%u|%s|%s|)\r\n",
+ af, hbuf, pbuf);
+ }
+ }
+ } else
+ printf(" No data connection\r\n");
+ reply(211, "End of status");
+}
+
+void
+fatal(char *s)
+{
+
+ reply(451, "Error in server: %s", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+void
+reply(int n, const char *fmt, ...)
+{
+ char *buf, *p, *next;
+ int rval;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rval = vasprintf(&buf, fmt, ap);
+ va_end(ap);
+ if (rval == -1 || buf == NULL) {
+ printf("421 Local resource failure: malloc\r\n");
+ fflush(stdout);
+ dologout(1);
+ }
+ next = buf;
+ while ((p = strsep(&next, "\n\r"))) {
+ printf("%d%s %s\r\n", n, (next != '\0') ? "-" : "", p);
+ if (debug)
+ syslog(LOG_DEBUG, "<--- %d%s %s", n,
+ (next != '\0') ? "-" : "", p);
+ }
+ (void)fflush(stdout);
+ free(buf);
+}
+
+
+void
+reply_r(int n, const char *fmt, ...)
+{
+ char *p, *next;
+ char msg[BUFSIZ];
+ char buf[BUFSIZ];
+ va_list ap;
+ struct syslog_data sdata = SYSLOG_DATA_INIT;
+
+ sdata.log_fac = LOG_FTP;
+ va_start(ap, fmt);
+ vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+
+ next = msg;
+
+ while ((p = strsep(&next, "\n\r"))) {
+ snprintf(buf, sizeof(buf), "%d%s %s\r\n", n,
+ (next != '\0') ? "-" : "", p);
+ write(STDOUT_FILENO, buf, strlen(buf));
+ if (debug) {
+ buf[strlen(buf) - 2] = '\0';
+ syslog_r(LOG_DEBUG, &sdata, "<--- %s", buf);
+ }
+ }
+}
+
+void
+lreply(int n, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ (void)printf("%d- ", n);
+ (void)vprintf(fmt, ap);
+ va_end(ap);
+ (void)printf("\r\n");
+ (void)fflush(stdout);
+ if (debug) {
+ va_start(ap, fmt);
+ syslog(LOG_DEBUG, "<--- %d- ", n);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static void
+ack(char *s)
+{
+
+ reply(250, "%s command successful.", s);
+}
+
+void
+nack(char *s)
+{
+
+ reply(502, "%s command not implemented.", s);
+}
+
+/* ARGSUSED */
+void
+yyerror(char *s)
+{
+ cbuf[strcspn(cbuf, "\n")] = '\0';
+ reply(500, "'%s': command not understood.", cbuf);
+}
+
+void
+delete(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(char *path)
+{
+ FILE *message;
+
+ if (chdir(path) < 0)
+ perror_reply(550, path);
+ else {
+ if ((message = fopen(_PATH_CWDMESG, "r")) != NULL) {
+ char line[LINE_MAX];
+
+ while (fgets(line, sizeof(line), message) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+ lreply(250, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(message);
+ }
+ ack("CWD");
+ }
+}
+
+void
+replydirname(const char *name, const char *message)
+{
+ char *p, *ep;
+ char npath[MAXPATHLEN * 2];
+
+ p = npath;
+ ep = &npath[sizeof(npath) - 1];
+ while (*name) {
+ if (*name == '"') {
+ if (ep - p < 2)
+ break;
+ *p++ = *name++;
+ *p++ = '"';
+ } else {
+ if (ep - p < 1)
+ break;
+ *p++ = *name++;
+ }
+ }
+ *p = '\0';
+ reply(257, "\"%s\" %s", npath, message);
+}
+
+void
+makedir(char *name)
+{
+
+ LOGCMD("mkdir", name);
+ if (mkdir(name, 0777) < 0)
+ perror_reply(550, name);
+ else
+ replydirname(name, "directory created.");
+}
+
+void
+removedir(char *name)
+{
+
+ LOGCMD("rmdir", name);
+ if (rmdir(name) < 0)
+ perror_reply(550, name);
+ else
+ ack("RMD");
+}
+
+void
+pwd(void)
+{
+ char path[MAXPATHLEN];
+
+ if (getcwd(path, sizeof(path)) == NULL)
+ perror_reply(550, "Can't get current directory");
+ else
+ replydirname(path, "is current directory.");
+}
+
+char *
+renamefrom(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(char *from, char *to)
+{
+
+ LOGCMD2("rename", from, to);
+ if (rename(from, to) < 0)
+ perror_reply(550, "rename");
+ else
+ ack("RNTO");
+}
+
+static void
+dolog(struct sockaddr *sa)
+{
+ char hbuf[sizeof(remotehost)];
+
+ if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0) == 0)
+ (void) strlcpy(remotehost, hbuf, sizeof(remotehost));
+ else
+ (void) strlcpy(remotehost, "unknown", sizeof(remotehost));
+
+#ifdef HASSETPROCTITLE
+ snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
+ setproctitle("%s", proctitle);
+#endif /* HASSETPROCTITLE */
+
+ if (logging) {
+ int error;
+ error = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf),
+ NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "connection from %s [%s]", remotehost,
+ error ? gai_strerror(error) : hbuf);
+ }
+}
+
+/*
+ * Record logout in wtmp file and exit with supplied status.
+ * NOTE: because this is called from signal handlers it cannot
+ * use stdio (or call other functions that use stdio).
+ */
+void
+dologout(int status)
+{
+
+ transflag = 0;
+
+ if (logged_in) {
+ sigprocmask(SIG_BLOCK, &allsigs, NULL);
+ ftpdlogwtmp(ttyline, "", "");
+ if (doutmp)
+ ftpd_logout(utmp.ut_line);
+ }
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+/*ARGSUSED*/
+static void
+sigurg(int signo)
+{
+
+ recvurg = 1;
+}
+
+static void
+myoob(void)
+{
+ char *cp;
+ int ret;
+
+ /* only process if transfer occurring */
+ if (!transflag)
+ return;
+ cp = tmpline;
+ ret = getline(cp, 7, stdin);
+ if (ret == -1) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ } else if (ret == -2) {
+ /* Ignore truncated command */
+ return;
+ }
+ upper(cp);
+ if (strcmp(cp, "ABOR\r\n") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful");
+ }
+ if (strcmp(cp, "STAT\r\n") == 0) {
+ tmpline[0] = '\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(void)
+{
+ socklen_t len;
+ int on;
+ u_char *p, *a;
+
+ if (pw == NULL) {
+ reply(530, "Please login with USER and PASS");
+ return;
+ }
+ if (pdata >= 0)
+ close(pdata);
+ /*
+ * XXX
+ * At this point, it would be nice to have an algorithm that
+ * inserted a growing delay in an attack scenario. Such a thing
+ * would look like continual passive sockets being opened, but
+ * nothing serious being done with them. They're not used to
+ * move data; the entire attempt is just to use tcp FIN_WAIT
+ * resources.
+ */
+ pdata = socket(AF_INET, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+
+ on = IP_PORTRANGE_HIGH;
+ if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_sin.sin_port = 0;
+ if (bind(pdata, (struct sockaddr *)&pasv_addr,
+ pasv_addr.su_len) < 0)
+ goto pasv_error;
+
+ 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 = (u_char *)&pasv_addr.su_sin.sin_addr;
+ p = (u_char *)&pasv_addr.su_sin.sin_port;
+
+ reply(227, "Entering Passive Mode (%u,%u,%u,%u,%u,%u)", a[0],
+ a[1], a[2], a[3], p[0], p[1]);
+ return;
+
+pasv_error:
+ perror_reply(425, "Can't open passive connection");
+ (void) close(pdata);
+ pdata = -1;
+ return;
+}
+
+int
+epsvproto2af(int proto)
+{
+
+ switch (proto) {
+ case 1: return AF_INET;
+#ifdef INET6
+ case 2: return AF_INET6;
+#endif
+ default: return -1;
+ }
+}
+
+int
+af2epsvproto(int af)
+{
+
+ switch (af) {
+ case AF_INET: return 1;
+#ifdef INET6
+ case AF_INET6: return 2;
+#endif
+ default: return -1;
+ }
+}
+
+/*
+ * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
+ * 229 Entering Extended Passive Mode (|||port|)
+ */
+void
+long_passive(char *cmd, int pf)
+{
+ socklen_t len;
+ int on;
+ u_char *p, *a;
+
+ if (!logged_in) {
+ syslog(LOG_NOTICE, "long passive but not logged in");
+ reply(503, "Login with USER first.");
+ return;
+ }
+
+ if (pf != PF_UNSPEC && ctrl_addr.su_family != pf) {
+ /*
+ * XXX
+ * only EPRT/EPSV ready clients will understand this
+ */
+ if (strcmp(cmd, "EPSV") != 0)
+ reply(501, "Network protocol mismatch"); /*XXX*/
+ else
+ epsv_protounsupp("Network protocol mismatch");
+
+ return;
+ }
+
+ if (pdata >= 0)
+ close(pdata);
+ /*
+ * XXX
+ * At this point, it would be nice to have an algorithm that
+ * inserted a growing delay in an attack scenario. Such a thing
+ * would look like continual passive sockets being opened, but
+ * nothing serious being done with them. They not used to move
+ * data; the entire attempt is just to use tcp FIN_WAIT
+ * resources.
+ */
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+
+ switch (ctrl_addr.su_family) {
+ case AF_INET:
+ on = IP_PORTRANGE_HIGH;
+ if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ break;
+ case AF_INET6:
+ on = IPV6_PORTRANGE_HIGH;
+ if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE,
+ &on, sizeof(on)) < 0)
+ goto pasv_error;
+ break;
+ }
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
+ goto pasv_error;
+ len = pasv_addr.su_len;
+ if (getsockname(pdata, (struct sockaddr *)&pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ p = (u_char *)&pasv_addr.su_port;
+
+ if (strcmp(cmd, "LPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ a = (u_char *)&pasv_addr.su_sin.sin_addr;
+ reply(228,
+ "Entering Long Passive Mode (%u,%u,%u,%u,%u,%u,%u,%u,%u)",
+ 4, 4, a[0], a[1], a[2], a[3], 2, p[0], p[1]);
+ return;
+ case AF_INET6:
+ a = (u_char *)&pasv_addr.su_sin6.sin6_addr;
+ reply(228,
+ "Entering Long Passive Mode (%u,%u,%u,%u,%u,%u,"
+ "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u)",
+ 6, 16, a[0], a[1], a[2], a[3], a[4],
+ a[5], a[6], a[7], a[8], a[9], a[10],
+ a[11], a[12], a[13], a[14], a[15],
+ 2, p[0], p[1]);
+ return;
+ }
+ } else if (strcmp(cmd, "EPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ case AF_INET6:
+ reply(229, "Entering Extended Passive Mode (|||%u|)",
+ ntohs(pasv_addr.su_port));
+ return;
+ }
+ } else {
+ /* more proper error code? */
+ }
+
+ pasv_error:
+ perror_reply(425, "Can't open passive connection");
+ (void) close(pdata);
+ pdata = -1;
+ return;
+}
+
+/*
+ * EPRT |proto|addr|port|
+ */
+int
+extended_port(const char *arg)
+{
+ char *tmp = NULL;
+ char *result[3];
+ char *p, *q;
+ char delim;
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
+ int i;
+ unsigned long proto;
+
+ if (epsvall) {
+ reply(501, "EPRT disallowed after EPSV ALL");
+ return -1;
+ }
+
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+
+ tmp = strdup(arg);
+ if (!tmp) {
+ fatal("not enough core.");
+ /*NOTREACHED*/
+ }
+ p = tmp;
+ delim = p[0];
+ p++;
+ memset(result, 0, sizeof(result));
+ for (i = 0; i < 3; i++) {
+ q = strchr(p, delim);
+ if (!q || *q != delim)
+ goto parsefail;
+ *q++ = '\0';
+ result[i] = p;
+ p = q;
+ }
+
+ /* some more sanity check */
+ p = NULL;
+ (void)strtoul(result[2], &p, 10);
+ if (!*result[2] || *p)
+ goto protounsupp;
+ p = NULL;
+ proto = strtoul(result[0], &p, 10);
+ if (!*result[0] || *p)
+ goto protounsupp;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = epsvproto2af((int)proto);
+ if (hints.ai_family < 0)
+ goto protounsupp;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST; /*no DNS*/
+ if (getaddrinfo(result[1], result[2], &hints, &res))
+ goto parsefail;
+ if (res->ai_next)
+ goto parsefail;
+ if (sizeof(data_dest) < res->ai_addrlen)
+ goto parsefail;
+ memcpy(&data_dest, res->ai_addr, res->ai_addrlen);
+ if (his_addr.su_family == AF_INET6 &&
+ data_dest.su_family == AF_INET6) {
+ /* XXX more sanity checks! */
+ data_dest.su_sin6.sin6_scope_id =
+ his_addr.su_sin6.sin6_scope_id;
+ }
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "EPRT command successful.");
+
+ if (tmp)
+ free(tmp);
+ if (res)
+ freeaddrinfo(res);
+ return 0;
+
+parsefail:
+ reply(500, "Invalid argument, rejected.");
+ usedefault = 1;
+ if (tmp)
+ free(tmp);
+ if (res)
+ freeaddrinfo(res);
+ return -1;
+
+protounsupp:
+ epsv_protounsupp("Protocol not supported");
+ usedefault = 1;
+ if (tmp)
+ free(tmp);
+ if (res)
+ freeaddrinfo(res);
+ return -1;
+}
+
+/*
+ * 522 Protocol not supported (proto,...)
+ * as we assume address family for control and data connections are the same,
+ * we do not return the list of address families we support - instead, we
+ * return the address family of the control connection.
+ */
+void
+epsv_protounsupp(const char *message)
+{
+ int proto;
+
+ proto = af2epsvproto(ctrl_addr.su_family);
+ if (proto < 0)
+ reply(501, "%s", message); /*XXX*/
+ else
+ reply(522, "%s, use (%d)", message, proto);
+}
+
+/*
+ * Generate unique name for file with basename "local".
+ * The file named "local" is already known to exist.
+ * Generates failure reply on error.
+ */
+static int
+guniquefd(char *local, char **nam)
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ int count, len, fd;
+ char *cp;
+
+ cp = strrchr(local, '/');
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return (-1);
+ }
+ if (cp)
+ *cp = '/';
+ len = strlcpy(new, local, sizeof(new));
+ if (len+2+1 >= sizeof(new)-1)
+ return (-1);
+ cp = new + len;
+ *cp++ = '.';
+ for (count = 1; count < 100; count++) {
+ (void)snprintf(cp, sizeof(new) - (cp - new), "%d", count);
+ fd = open(new, O_RDWR|O_CREAT|O_EXCL, 0666);
+ if (fd == -1)
+ continue;
+ if (nam)
+ *nam = new;
+ return (fd);
+ }
+ reply(452, "Unique file name cannot be created.");
+ return (-1);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+void
+perror_reply(int code, char *string)
+{
+
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+static char *onefile[] = {
+ "",
+ 0
+};
+
+void
+send_file_list(char *whichf)
+{
+ struct stat st;
+ DIR *dirp = NULL;
+ struct dirent *dir;
+ FILE *dout = NULL;
+ char **dirlist;
+ char *dirname;
+ int simple = 0;
+ volatile int freeglob = 0;
+ glob_t gl;
+
+ if (strpbrk(whichf, "~{[*?") != NULL) {
+ memset(&gl, 0, sizeof(gl));
+ freeglob = 1;
+ if (glob(whichf,
+ GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|GLOB_LIMIT,
+ 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;
+ }
+
+ 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 (recvurg) {
+ myoob();
+ recvurg = 0;
+ transflag = 0;
+ goto out;
+ }
+
+ if (dir->d_name[0] == '.' && dir->d_namlen == 1)
+ continue;
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+ dir->d_namlen == 2)
+ continue;
+
+ snprintf(nbuf, sizeof(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);
+ else {
+ if (pdata >= 0)
+ close(pdata);
+ }
+ data = -1;
+ pdata = -1;
+out:
+ if (freeglob) {
+ freeglob = 0;
+ globfree(&gl);
+ }
+}
+
+/*ARGSUSED*/
+static void
+reapchild(int signo)
+{
+ int save_errno = errno;
+ int rval;
+
+ do {
+ rval = waitpid(-1, NULL, WNOHANG);
+ } while (rval > 0 || (rval == -1 && errno == EINTR));
+ errno = save_errno;
+}
+
+void
+logxfer(char *name, off_t size, time_t start)
+{
+ char buf[400 + MAXHOSTNAMELEN*4 + MAXPATHLEN*4];
+ char dir[MAXPATHLEN], path[MAXPATHLEN], rpath[MAXPATHLEN];
+ char vremotehost[MAXHOSTNAMELEN*4], vpath[MAXPATHLEN*4];
+ char *vpw;
+ time_t now;
+ int len;
+
+ if ((statfd >= 0) && (getcwd(dir, sizeof(dir)) != NULL)) {
+ time(&now);
+
+ vpw = malloc(strlen(guest ? guestpw : pw->pw_name) * 4 + 1);
+ if (vpw == NULL)
+ return;
+
+ snprintf(path, sizeof(path), "%s/%s", dir, name);
+ if (realpath(path, rpath) == NULL)
+ strlcpy(rpath, path, sizeof(rpath));
+ strvis(vpath, rpath, VIS_SAFE|VIS_NOSLASH);
+
+ strvis(vremotehost, remotehost, VIS_SAFE|VIS_NOSLASH);
+ strvis(vpw, guest? guestpw : pw->pw_name, VIS_SAFE|VIS_NOSLASH);
+
+ len = snprintf(buf, sizeof(buf),
+ "%.24s %d %s %qd %s %c %s %c %c %s ftp %d %s %s\n",
+ ctime(&now), now - start + (now == start),
+ vremotehost, (long long)size, vpath,
+ ((type == TYPE_A) ? 'a' : 'b'), "*" /* none yet */,
+ 'o', ((guest) ? 'a' : 'r'),
+ vpw, 0 /* none yet */,
+ ((guest) ? "*" : pw->pw_name), dhostname);
+ free(vpw);
+
+ if (len >= sizeof(buf) || len == -1) {
+ if ((len = strlen(buf)) == 0)
+ return; /* should not happen */
+ buf[len - 1] = '\n';
+ }
+ write(statfd, buf, len);
+ }
+}
+
+void
+set_slave_signals(void)
+{
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+
+ sa.sa_handler = SIG_DFL;
+ (void) sigaction(SIGCHLD, &sa, NULL);
+
+ sa.sa_handler = sigurg;
+ sa.sa_flags = 0; /* don't restart syscalls for SIGURG */
+ (void) sigaction(SIGURG, &sa, NULL);
+
+ sigfillset(&sa.sa_mask); /* block all signals in handler */
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sigquit;
+ (void) sigaction(SIGHUP, &sa, NULL);
+ (void) sigaction(SIGINT, &sa, NULL);
+ (void) sigaction(SIGQUIT, &sa, NULL);
+ (void) sigaction(SIGTERM, &sa, NULL);
+
+ sa.sa_handler = lostconn;
+ (void) sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = toolong;
+ (void) sigaction(SIGALRM, &sa, NULL);
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+}
+
+#if defined(TCPWRAPPERS)
+static int
+check_host(struct sockaddr *sa)
+{
+ struct sockaddr_in *sin;
+ struct hostent *hp;
+ char *addr;
+
+ if (sa->sa_family != AF_INET)
+ return 1; /*XXX*/
+
+ sin = (struct sockaddr_in *)sa;
+ hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ addr = inet_ntoa(sin->sin_addr);
+ if (hp) {
+ if (!hosts_ctl("ftpd", hp->h_name, addr, STRING_UNKNOWN)) {
+ syslog(LOG_NOTICE, "tcpwrappers rejected: %s [%s]",
+ hp->h_name, addr);
+ return (0);
+ }
+ } else {
+ if (!hosts_ctl("ftpd", STRING_UNKNOWN, addr, STRING_UNKNOWN)) {
+ syslog(LOG_NOTICE, "tcpwrappers rejected: [%s]", addr);
+ return (0);
+ }
+ }
+ return (1);
+}
+#endif /* TCPWRAPPERS */
+
+/*
+ * Allocate space and return a copy of the specified dir.
+ * If 'dir' begins with a tilde (~), expand it.
+ */
+char *
+copy_dir(char *dir, struct passwd *pw)
+{
+ char *cp;
+ char *newdir;
+ char *user = NULL;
+
+ /* Nothing to expand */
+ if (dir[0] != '~')
+ return (strdup(dir));
+
+ /* "dir" is of form ~user/some/dir, lookup user. */
+ if (dir[1] != '/' && dir[1] != '\0') {
+ if ((cp = strchr(dir + 1, '/')) == NULL)
+ cp = dir + strlen(dir);
+ if ((user = malloc((size_t)(cp - dir))) == NULL)
+ return (NULL);
+ strlcpy(user, dir + 1, (size_t)(cp - dir));
+
+ /* Only do lookup if it is a different user. */
+ if (strcmp(user, pw->pw_name) != 0) {
+ if ((pw = getpwnam(user)) == NULL) {
+ /* No such user, interpret literally */
+ free(user);
+ return(strdup(dir));
+ }
+ }
+ free(user);
+ }
+
+ /*
+ * If there is no directory separator (/) then it is just pw_dir.
+ * Otherwise, replace ~foo with pw_dir.
+ */
+ if ((cp = strchr(dir + 1, '/')) == NULL) {
+ newdir = strdup(pw->pw_dir);
+ } else {
+ if (asprintf(&newdir, "%s%s", pw->pw_dir, cp) == -1)
+ return (NULL);
+ }
+
+ return(newdir);
+}
--- /dev/null
+/* $OpenBSD: logutmp.c,v 1.11 2008/06/30 12:03:51 ragge Exp $ */
+/*
+ * Portions Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Portions Copyright (c) 1996, Jason Downs. 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. 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.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <string.h>
+#include <ttyent.h>
+
+#include "monitor.h"
+#include "extern.h"
+
+static int fd = -1;
+static int topslot = -1;
+
+/*
+ * Special versions of login()/logout() which hold the utmp file open,
+ * for use with ftpd.
+ */
+
+void
+ftpd_login(struct utmp *ut)
+{
+ struct utmp ubuf;
+
+ /*
+ * First, loop through /etc/ttys, if needed, to initialize the
+ * top of the tty slots, since ftpd has no tty.
+ */
+ if (topslot < 0) {
+ topslot = 0;
+ while (getttyent() != (struct ttyent *)NULL)
+ topslot++;
+ }
+ if ((topslot < 0) || ((fd < 0) &&
+ (fd = open(_PATH_UTMP, O_RDWR|O_CREAT, 0644)) < 0))
+ return;
+
+ /*
+ * Now find a slot that's not in use...
+ */
+ (void)lseek(fd, (off_t)(topslot * sizeof(struct utmp)), SEEK_SET);
+
+ while (1) {
+ if (read(fd, &ubuf, sizeof(struct utmp)) ==
+ sizeof(struct utmp)) {
+ if (!ubuf.ut_name[0]) {
+ (void)lseek(fd, -(off_t)sizeof(struct utmp),
+ SEEK_CUR);
+ break;
+ }
+ topslot++;
+ } else {
+ (void)lseek(fd, (off_t)(topslot *
+ sizeof(struct utmp)), SEEK_SET);
+ break;
+ }
+ }
+
+ (void)write(fd, ut, sizeof(struct utmp));
+}
+
+int
+ftpd_logout(char *line)
+{
+ struct timeval tv;
+ struct utmp ut;
+ int rval;
+
+ rval = 0;
+ if (fd < 0)
+ return(rval);
+
+ (void)lseek(fd, 0, SEEK_SET);
+
+ while (read(fd, &ut, sizeof(struct utmp)) == sizeof(struct utmp)) {
+ if (!ut.ut_name[0] ||
+ strncmp(ut.ut_line, line, UT_LINESIZE))
+ continue;
+ bzero(ut.ut_name, UT_NAMESIZE);
+ bzero(ut.ut_host, UT_HOSTSIZE);
+ gettimeofday(&tv, NULL);
+ ut.ut_time = tv.tv_sec;
+ (void)lseek(fd, -(off_t)sizeof(struct utmp), SEEK_CUR);
+ (void)write(fd, &ut, sizeof(struct utmp));
+ rval = 1;
+ }
+ return(rval);
+}
--- /dev/null
+/* $OpenBSD: logwtmp.c,v 1.11 2009/10/27 23:59:31 deraadt Exp $ */
+/* $NetBSD: logwtmp.c,v 1.4 1995/04/11 02:44:58 cgd Exp $ */
+
+/*
+ * 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. 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.
+ *
+ */
+
+#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 <netinet/in.h>
+
+#include "monitor.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
+ftpdlogwtmp(char *line, char *name, char *host)
+{
+ struct timeval tv;
+ struct stat buf;
+ struct utmp ut;
+
+ 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));
+ gettimeofday(&tv, NULL);
+ ut.ut_time = tv.tv_sec;
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void)ftruncate(fd, buf.st_size);
+ }
+}
--- /dev/null
+/* $OpenBSD: monitor.c,v 1.20 2009/06/04 01:12:39 sthen Exp $ */
+
+/*
+ * Copyright (c) 2004 Moritz Jodeit <moritz@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "monitor.h"
+#include "extern.h"
+
+enum monitor_command {
+ CMD_USER,
+ CMD_PASS,
+ CMD_SOCKET,
+ CMD_BIND
+};
+
+enum monitor_state {
+ PREAUTH,
+ POSTAUTH
+};
+
+#ifdef HASSETPROCTITLE
+extern char remotehost[];
+#endif
+extern char ttyline[20];
+extern int debug;
+
+extern void set_slave_signals(void);
+
+int fd_monitor = -1;
+int fd_slave = -1;
+int nullfd;
+pid_t slave_pid = -1;
+enum monitor_state state = PREAUTH;
+
+void send_data(int, void *, size_t);
+void recv_data(int, void *, size_t);
+void handle_cmds(void);
+void set_monitor_signals(void);
+void sig_pass_to_slave(int);
+void sig_chld(int);
+void fatalx(char *, ...);
+void debugmsg(char *, ...);
+
+/*
+ * Send data over a socket and exit if something fails.
+ */
+void
+send_data(int sock, void *buf, size_t len)
+{
+ ssize_t n;
+ size_t pos = 0;
+ char *ptr = buf;
+
+ while (len > pos) {
+ switch (n = write(sock, ptr + pos, len - pos)) {
+ case 0:
+ kill_slave("write failure");
+ _exit(0);
+ /* NOTREACHED */
+ case -1:
+ if (errno != EINTR && errno != EAGAIN)
+ fatalx("send_data: %m");
+ break;
+ default:
+ pos += n;
+ }
+ }
+}
+
+/*
+ * Receive data from socket and exit if something fails.
+ */
+void
+recv_data(int sock, void *buf, size_t len)
+{
+ ssize_t n;
+ size_t pos = 0;
+ char *ptr = buf;
+
+ while (len > pos) {
+ switch (n = read(sock, ptr + pos, len - pos)) {
+ case 0:
+ kill_slave(NULL);
+ _exit(0);
+ /* NOTREACHED */
+ case -1:
+ if (errno != EINTR && errno != EAGAIN)
+ fatalx("recv_data: %m");
+ break;
+ default:
+ pos += n;
+ }
+ }
+}
+
+void
+set_monitor_signals(void)
+{
+ struct sigaction act;
+ int i;
+
+ sigfillset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ act.sa_handler = SIG_DFL;
+ for (i = 1; i < _NSIG; i++)
+ sigaction(i, &act, NULL);
+
+ act.sa_handler = sig_chld;
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = sig_pass_to_slave;
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+}
+
+/*
+ * Creates the privileged monitor process. It returns twice.
+ * It returns 1 for the unprivileged slave process and 0 for the
+ * user-privileged slave process after successful authentication.
+ */
+int
+monitor_init(void)
+{
+ struct passwd *pw;
+ int pair[2];
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1)
+ fatalx("socketpair failed");
+
+ fd_monitor = pair[0];
+ fd_slave = pair[1];
+
+ set_monitor_signals();
+
+ slave_pid = fork();
+ if (slave_pid == -1)
+ fatalx("fork of unprivileged slave failed");
+ if (slave_pid == 0) {
+ /* Unprivileged slave */
+ set_slave_signals();
+
+ if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL)
+ fatalx("privilege separation user %s not found",
+ FTPD_PRIVSEP_USER);
+
+ if (chroot(pw->pw_dir) == -1)
+ fatalx("chroot %s: %m", pw->pw_dir);
+ if (chdir("/") == -1)
+ fatalx("chdir /: %m");
+
+ if (setgroups(1, &pw->pw_gid) == -1)
+ fatalx("setgroups: %m");
+ if (setegid(pw->pw_gid) == -1)
+ fatalx("setegid failed");
+ if (setgid(pw->pw_gid) == -1)
+ fatalx("setgid failed");
+ if (seteuid(pw->pw_uid) == -1)
+ fatalx("seteuid failed");
+ if (setuid(pw->pw_uid) == -1)
+ fatalx("setuid failed");
+
+ endpwent();
+ close(fd_slave);
+ return (1);
+ }
+
+#ifdef HASSETPROCTITLE
+ setproctitle("%s: [priv pre-auth]", remotehost);
+#endif
+
+ handle_cmds();
+
+ /* User-privileged slave */
+ return (0);
+}
+
+/*
+ * Creates the user-privileged slave process. It is called
+ * from the privileged monitor process and returns twice. It returns 0
+ * for the user-privileged slave process and 1 for the monitor process.
+ */
+int
+monitor_post_auth()
+{
+ slave_pid = fork();
+ if (slave_pid == -1)
+ fatalx("fork of user-privileged slave failed");
+
+ snprintf(ttyline, sizeof(ttyline), "ftp%ld",
+ slave_pid == 0 ? (long)getpid() : (long)slave_pid);
+
+ if (slave_pid == 0) {
+ /* User privileged slave */
+ close(fd_slave);
+ set_slave_signals();
+ return (0);
+ }
+
+ /* We have to keep stdout open, because reply() needs it. */
+ if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
+ fatalx("cannot open %s: %m", _PATH_DEVNULL);
+ dup2(nullfd, STDIN_FILENO);
+ dup2(nullfd, STDERR_FILENO);
+ close(nullfd);
+ close(fd_monitor);
+
+ return (1);
+}
+
+/*
+ * Handles commands received from the slave process. It will not return
+ * except in one situation: After successful authentication it will
+ * return as the user-privileged slave process.
+ */
+void
+handle_cmds(void)
+{
+ enum monitor_command cmd;
+ enum auth_ret auth;
+ int err, s, slavequit, serrno, domain;
+ pid_t preauth_slave_pid;
+ size_t len;
+ union sockunion sa;
+ socklen_t salen;
+ char *name, *pw;
+
+ for (;;) {
+ recv_data(fd_slave, &cmd, sizeof(cmd));
+
+ switch (cmd) {
+ case CMD_USER:
+ debugmsg("CMD_USER received");
+
+ recv_data(fd_slave, &len, sizeof(len));
+ if (len == SIZE_T_MAX)
+ fatalx("monitor received invalid user length");
+ if ((name = malloc(len + 1)) == NULL)
+ fatalx("malloc: %m");
+ if (len > 0)
+ recv_data(fd_slave, name, len);
+ name[len] = '\0';
+
+ user(name);
+ free(name);
+ break;
+ case CMD_PASS:
+ debugmsg("CMD_PASS received");
+
+ recv_data(fd_slave, &len, sizeof(len));
+ if (len == SIZE_T_MAX)
+ fatalx("monitor received invalid pass length");
+ if ((pw = malloc(len + 1)) == NULL)
+ fatalx("malloc: %m");
+ if (len > 0)
+ recv_data(fd_slave, pw, len);
+ pw[len] = '\0';
+
+ preauth_slave_pid = slave_pid;
+
+ auth = pass(pw);
+ bzero(pw, len);
+ free(pw);
+
+ switch (auth) {
+ case AUTH_FAILED:
+ /* Authentication failure */
+ debugmsg("authentication failed");
+ slavequit = 0;
+ send_data(fd_slave, &slavequit,
+ sizeof(slavequit));
+ break;
+ case AUTH_SLAVE:
+ /* User-privileged slave */
+ debugmsg("user-privileged slave started");
+ return;
+ /* NOTREACHED */
+ case AUTH_MONITOR:
+ /* Post-auth monitor */
+ debugmsg("monitor went into post-auth phase");
+ state = POSTAUTH;
+#ifdef HASSETPROCTITLE
+ setproctitle("%s: [priv post-auth]",
+ remotehost);
+#endif
+ slavequit = 1;
+
+ send_data(fd_slave, &slavequit,
+ sizeof(slavequit));
+
+ while (waitpid(preauth_slave_pid, NULL, 0) < 0 &&
+ errno == EINTR)
+ ;
+ break;
+ default:
+ fatalx("bad return value from pass()");
+ /* NOTREACHED */
+ }
+ break;
+ case CMD_SOCKET:
+ debugmsg("CMD_SOCKET received");
+
+ if (state != POSTAUTH)
+ fatalx("CMD_SOCKET received in invalid state");
+
+ recv_data(fd_slave, &domain, sizeof(domain));
+ if (domain != AF_INET && domain != AF_INET6)
+ fatalx("monitor received invalid addr family");
+
+ s = socket(domain, SOCK_STREAM, 0);
+ serrno = errno;
+
+ send_fd(fd_slave, s);
+ if (s == -1)
+ send_data(fd_slave, &serrno, sizeof(serrno));
+ else
+ close(s);
+ break;
+ case CMD_BIND:
+ debugmsg("CMD_BIND received");
+
+ if (state != POSTAUTH)
+ fatalx("CMD_BIND received in invalid state");
+
+ s = recv_fd(fd_slave);
+
+ recv_data(fd_slave, &salen, sizeof(salen));
+ if (salen == 0 || salen > sizeof(sa))
+ fatalx("monitor received invalid sockaddr len");
+
+ bzero(&sa, sizeof(sa));
+ recv_data(fd_slave, &sa, salen);
+
+ if (sa.su_si.si_len != salen)
+ fatalx("monitor received invalid sockaddr len");
+
+ if (sa.su_si.si_family != AF_INET &&
+ sa.su_si.si_family != AF_INET6)
+ fatalx("monitor received invalid addr family");
+
+ err = bind(s, (struct sockaddr *)&sa, salen);
+ serrno = errno;
+
+ if (s >= 0)
+ close(s);
+
+ send_data(fd_slave, &err, sizeof(err));
+ if (err == -1)
+ send_data(fd_slave, &serrno, sizeof(serrno));
+ break;
+ default:
+ fatalx("monitor received unknown command %d", cmd);
+ /* NOTREACHED */
+ }
+ }
+}
+
+void
+sig_pass_to_slave(int signo)
+{
+ int olderrno = errno;
+
+ if (slave_pid > 0)
+ kill(slave_pid, signo);
+
+ errno = olderrno;
+}
+
+/* ARGSUSED */
+void
+sig_chld(int signo)
+{
+ pid_t pid;
+ int stat, olderrno = errno;
+
+ do {
+ pid = waitpid(slave_pid, &stat, WNOHANG);
+ if (pid > 0)
+ _exit(0);
+ } while (pid == -1 && errno == EINTR);
+
+ errno = olderrno;
+}
+
+void
+kill_slave(char *reason)
+{
+ if (slave_pid > 0) {
+ if (reason)
+ syslog(LOG_NOTICE, "kill slave %d: %s",
+ slave_pid, reason);
+ kill(slave_pid, SIGQUIT);
+ }
+}
+
+void
+fatalx(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ kill_slave("fatal error");
+
+ _exit(0);
+}
+
+void
+debugmsg(char *fmt, ...)
+{
+ va_list ap;
+
+ if (debug) {
+ va_start(ap, fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+monitor_user(char *name)
+{
+ enum monitor_command cmd;
+ size_t len;
+
+ cmd = CMD_USER;
+ send_data(fd_monitor, &cmd, sizeof(cmd));
+
+ len = strlen(name);
+ send_data(fd_monitor, &len, sizeof(len));
+ if (len > 0)
+ send_data(fd_monitor, name, len);
+}
+
+int
+monitor_pass(char *pass)
+{
+ enum monitor_command cmd;
+ int quitnow;
+ size_t len;
+
+ cmd = CMD_PASS;
+ send_data(fd_monitor, &cmd, sizeof(cmd));
+
+ len = strlen(pass);
+ send_data(fd_monitor, &len, sizeof(len));
+ if (len > 0)
+ send_data(fd_monitor, pass, len);
+
+ recv_data(fd_monitor, &quitnow, sizeof(quitnow));
+
+ return (quitnow);
+}
+
+int
+monitor_socket(int domain)
+{
+ enum monitor_command cmd;
+ int s, serrno;
+
+ cmd = CMD_SOCKET;
+ send_data(fd_monitor, &cmd, sizeof(cmd));
+ send_data(fd_monitor, &domain, sizeof(domain));
+
+ s = recv_fd(fd_monitor);
+ if (s == -1) {
+ recv_data(fd_monitor, &serrno, sizeof(serrno));
+ errno = serrno;
+ }
+
+ return (s);
+}
+
+int
+monitor_bind(int s, struct sockaddr *name, socklen_t namelen)
+{
+ enum monitor_command cmd;
+ int ret, serrno;
+
+ cmd = CMD_BIND;
+ send_data(fd_monitor, &cmd, sizeof(cmd));
+
+ send_fd(fd_monitor, s);
+ send_data(fd_monitor, &namelen, sizeof(namelen));
+ send_data(fd_monitor, name, namelen);
+
+ recv_data(fd_monitor, &ret, sizeof(ret));
+ if (ret == -1) {
+ recv_data(fd_monitor, &serrno, sizeof(serrno));
+ errno = serrno;
+ }
+
+ return (ret);
+}
--- /dev/null
+/* $OpenBSD: monitor.h,v 1.5 2007/03/01 20:06:27 otto Exp $ */
+
+/*
+ * Copyright (c) 2004 Moritz Jodeit <moritz@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _MONITOR_H
+#define _MONITOR_H
+
+#define FTPD_PRIVSEP_USER "_ftp"
+
+enum auth_ret {
+ AUTH_FAILED,
+ AUTH_SLAVE,
+ AUTH_MONITOR
+};
+
+int monitor_init(void);
+int monitor_post_auth(void);
+void monitor_user(char *);
+int monitor_pass(char *);
+int monitor_socket(int);
+int monitor_bind(int, struct sockaddr *, socklen_t);
+
+void kill_slave(char *);
+
+void send_fd(int, int);
+int recv_fd(int);
+
+#endif /* _MONITOR_H */
--- /dev/null
+/* $OpenBSD: monitor_fdpass.c,v 1.4 2008/03/24 16:11:00 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2002 Matthieu Herrb
+ * Copyright (c) 2001 Niels Provos <provos@citi.umich.edu>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+void
+send_fd(int sock, int fd)
+{
+ struct msghdr msg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+ struct cmsghdr *cmsg;
+ struct iovec vec;
+ int result = 0;
+ ssize_t n;
+
+ memset(&msg, 0, sizeof(msg));
+
+ if (fd >= 0) {
+ msg.msg_control = (caddr_t)&cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = fd;
+ } else
+ result = errno;
+
+ vec.iov_base = &result;
+ vec.iov_len = sizeof(int);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+
+ if ((n = sendmsg(sock, &msg, 0)) == -1)
+ syslog(LOG_WARNING, "send_fd: sendmsg(%d): %m", sock);
+ if (n != sizeof(int))
+ syslog(LOG_WARNING, "send_fd: sendmsg: expected sent 1 got %ld",
+ (long)n);
+}
+
+int
+recv_fd(int sock)
+{
+ struct msghdr msg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+ struct cmsghdr *cmsg;
+ struct iovec vec;
+ ssize_t n;
+ int result;
+ int fd;
+
+ memset(&msg, 0, sizeof(msg));
+ vec.iov_base = &result;
+ vec.iov_len = sizeof(int);
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((n = recvmsg(sock, &msg, 0)) == -1)
+ syslog(LOG_WARNING, "recv_fd: recvmsg(%d): %m", sock);
+ if (n != sizeof(int))
+ syslog(LOG_WARNING,
+ "recv_fd: recvmsg: expected received 1 got %ld", (long)n);
+ if (result == 0) {
+ cmsg = CMSG_FIRSTHDR(&msg);
+ if (cmsg == NULL) {
+ syslog(LOG_WARNING, "recv_fd: no message header");
+ return (-1);
+ }
+ if (cmsg->cmsg_type != SCM_RIGHTS)
+ syslog(LOG_WARNING, "recv_fd: expected type %d got %d",
+ SCM_RIGHTS, cmsg->cmsg_type);
+ fd = (*(int *)CMSG_DATA(cmsg));
+ return (fd);
+ } else {
+ errno = result;
+ return (-1);
+ }
+}
--- /dev/null
+/usr/obj/libexec/ftpd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: pathnames.h,v 1.6 2003/06/02 19:38:24 millert Exp $ */
+/* $NetBSD: pathnames.h,v 1.5 1995/04/11 02:44:59 cgd Exp $ */
+
+/*
+ * 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. 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_FTPCHROOT "/etc/ftpchroot"
+#define _PATH_FTPWELCOME "/etc/ftpwelcome"
+#define _PATH_FTPLOGINMESG "/etc/motd"
+#define _PATH_FTPDSTATFILE "/var/log/ftpd"
+#define _PATH_CWDMESG ".message"
--- /dev/null
+/* $OpenBSD: popen.c,v 1.24 2010/03/08 19:34:44 kettenis Exp $ */
+/* $NetBSD: popen.c,v 1.5 1995/04/11 02:45:00 cgd Exp $ */
+
+/*
+ * 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. 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.
+ *
+ */
+
+#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 <syslog.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#include "monitor.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 pid_t *pids;
+static int fds;
+
+#define MAX_ARGV 100
+#define MAX_GARGV 1000
+
+FILE *
+ftpd_popen(char *program, char *type)
+{
+ char *cp;
+ FILE *iop;
+ int argc, gargc, pdes[2];
+ pid_t pid;
+ char **pop, *argv[MAX_ARGV], *gargv[MAX_GARGV];
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ return (NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return (NULL);
+ if ((pids = calloc(fds, sizeof(pid_t))) == NULL)
+ return (NULL);
+ }
+ if (pipe(pdes) < 0)
+ return (NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program;argc < MAX_ARGV-1; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+ argv[MAX_ARGV-1] = NULL;
+
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ glob_t gl;
+
+ memset(&gl, 0, sizeof(gl));
+ if (glob(argv[argc],
+ GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE|GLOB_LIMIT,
+ NULL, &gl)) {
+ if (gargc < MAX_GARGV-1) {
+ gargv[gargc++] = strdup(argv[argc]);
+ if (gargv[gargc -1] == NULL)
+ fatal ("Out of memory.");
+ }
+
+ } else if (gl.gl_pathc > 0) {
+ for (pop = gl.gl_pathv; *pop && gargc < MAX_GARGV-1; pop++) {
+ gargv[gargc++] = strdup(*pop);
+ if (gargv[gargc - 1] == NULL)
+ fatal ("Out of memory.");
+ }
+ }
+ globfree(&gl);
+ }
+ gargv[gargc] = NULL;
+
+ iop = NULL;
+
+ switch (pid = fork()) {
+ 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]);
+ }
+ closelog();
+
+ if (strcmp(gargv[0], "/bin/ls") == 0) {
+ extern int optreset;
+ extern int ls_main(int, char **);
+
+ /* reset getopt for ls_main */
+ optreset = optind = 1;
+ exit(ls_main(gargc, gargv));
+ }
+
+ 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(FILE *iop)
+{
+ int fdes, status;
+ pid_t pid;
+ sigset_t sigset, osigset;
+
+ /*
+ * 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);
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGINT);
+ sigaddset(&sigset, SIGQUIT);
+ sigaddset(&sigset, SIGHUP);
+ sigprocmask(SIG_BLOCK, &sigset, &osigset);
+ while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR)
+ continue;
+ sigprocmask(SIG_SETMASK, &osigset, NULL);
+ pids[fdes] = 0;
+ if (pid < 0)
+ return (-1);
+ if (WIFEXITED(status))
+ return (WEXITSTATUS(status));
+ return (1);
+}
--- /dev/null
+/Makefile/1.2/Thu Nov 13 04:15:27 1997//
+/getNAME.8/1.12/Thu May 31 19:19:39 2007//
+/getNAME.c/1.16/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/getNAME
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 1997/11/13 04:15:27 millert Exp $
+# from: @(#)Makefile 8.1 (Berkeley) 6/4/93
+
+PROG= getNAME
+MAN= getNAME.8
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: getNAME.8,v 1.12 2007/05/31 19:19:39 jmc Exp $
+.\" $NetBSD: getNAME.8,v 1.2.2.1 1997/11/10 19:54:39 thorpej Exp $
+.\"
+.\" Copyright (c) 1997 Matthew R. Green
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt GETNAME 8
+.Os
+.Sh NAME
+.Nm getNAME
+.Nd "get NAME sections from manual source for whatis/apropos data base"
+.Sh SYNOPSIS
+.Nm getNAME
+.Op Fl itw
+.Ar path Op Ar path ...
+.Sh DESCRIPTION
+The
+.Nm
+program looks inside manual sources to retrieve the NAME section of
+the manual page.
+It can be used to create a table of contents, report
+the style of manual, or to create an introduction to the manual section.
+By default,
+.Nm
+returns data for use in an
+.Xr apropos 1
+database.
+.Nm
+is designed to be called from
+.Xr makewhatis 8
+or other manual grovelling tools, not to be used directly.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl i
+Returns information useful in creating an introduction to a manual
+section.
+See
+.Xr intro 1 ,
+.Xr intro 2 ,
+etc. for examples.
+.It Fl t
+Returns information useful for creating a table of contents.
+.It Fl w
+Determines whether traditional man
+.Pq Dq OLD ,
+new mandoc
+.Pq Dq NEW ,
+or some unknown
+.Pq Dq UNKNOWN
+format exists.
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr man 1 ,
+.Xr whatis 1 ,
+.Xr makewhatis 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Bx 2.0 .
+.Sh BUGS
+It would be nice if
+.Nm
+could deal with preformatted manuals.
--- /dev/null
+/* $OpenBSD: getNAME.c,v 1.16 2009/10/27 23:59:31 deraadt Exp $ */
+/* $NetBSD: getNAME.c,v 1.7.2.1 1997/11/10 19:54:46 thorpej Exp $ */
+
+/*-
+ * 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. 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.
+ */
+
+/*
+ * Get name sections from manual pages.
+ * -t for building toc
+ * -i for building intro entries
+ * -w for querying type of manual source
+ * other apropos database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+int tocrc;
+int intro;
+int typeflag;
+
+void doname(char *);
+void dorefname(char *);
+void getfrom(char *);
+void split(char *, char *);
+void trimln(char *);
+__dead void usage(void);
+int main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "itw")) != -1)
+ 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(char *pathname)
+{
+ int i = 0;
+ char *name, *loc, *s, *t;
+ char headbuf[BUFSIZ];
+ char linbuf[BUFSIZ];
+ char savebuf[BUFSIZ];
+
+ if (freopen(pathname, "r", stdin) == 0) {
+ perror(pathname);
+ return;
+ }
+ name = basename(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')
+ 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);
+ 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)
+ strlcat(linbuf, " ", sizeof(linbuf));
+ i++;
+ trimln(headbuf);
+ strlcat(linbuf, headbuf, sizeof(linbuf));
+ /* change the \- into (N) - */
+ if ((s = strstr(linbuf, "\\-")) != NULL) {
+ strlcpy(savebuf, s+1, sizeof savebuf);
+ if ((t = strchr(name, '.')) != NULL) {
+ t++;
+ *s++ = '(';
+ while (*t)
+ *s++ = *t++;
+ *s++ = ')';
+ *s++ = ' ';
+ *s++ = '\0';
+ }
+ strlcat(linbuf, savebuf, sizeof(linbuf));
+ }
+ }
+ if (intro)
+ split(linbuf, name);
+ else
+ printf("%s\n", linbuf);
+ return;
+
+newman:
+ if (typeflag) {
+ printf("%-60s NEW\n", pathname);
+ return;
+ }
+ 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);
+ 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)
+ strlcat(linbuf, " ", sizeof(linbuf));
+ i++;
+ trimln(headbuf);
+ for (loc = strchr(headbuf, ' '); loc; loc = strchr(loc, ' '))
+ if (loc[1] == ',')
+ memmove(loc, &loc[1], strlen(&loc[1])+1);
+ else
+ loc++;
+ if (headbuf[0] != '.') {
+ strlcat(linbuf, headbuf, sizeof(linbuf));
+ } else {
+ /*
+ * Get rid of quotes in macros.
+ */
+ for (loc = strchr(&headbuf[4], '"'); loc; ) {
+ memmove(loc, &loc[1], strlen(&loc[1])+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.
+ * Put section and dash between names and description.
+ */
+ if (headbuf[1] == 'N' && headbuf[2] == 'd') {
+ if ((t = strchr(name, '.')) != NULL) {
+ char *str;
+
+ if (asprintf(&str, "(%s)", t+1) == -1)
+ return;
+ strlcat(linbuf, str, sizeof(linbuf));
+ free(str);
+ }
+ strlcat(linbuf, "- ", sizeof(linbuf));
+ }
+ /*
+ * Skip over macro names.
+ */
+ strlcat(linbuf, &headbuf[4], sizeof(linbuf));
+ }
+ }
+ if (intro)
+ split(linbuf, name);
+ else
+ printf("%s\n", linbuf);
+}
+
+void
+trimln(char *cp)
+{
+
+ while (*cp)
+ cp++;
+ if (*--cp == '\n')
+ *cp = 0;
+}
+
+void
+doname(char *name)
+{
+ 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(char *line, char *name)
+{
+ 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) {
+ 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(char *name)
+{
+ 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)
+{
+ extern char *__progname;
+ (void)fprintf(stderr, "usage: %s [-itw] file ...\n", __progname);
+ exit(1);
+}
--- /dev/null
+/usr/obj/libexec/getNAME
\ No newline at end of file
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:28 2001//
+/extern.h/1.5/Mon Jun 2 19:38:24 2003//
+/getty.8/1.13/Thu May 31 19:19:39 2007//
+/gettytab.h/1.6/Mon Jun 2 19:38:24 2003//
+/main.c/1.30/Tue Oct 27 23:59:31 2009//
+/pathnames.h/1.3/Mon Jun 2 19:38:24 2003//
+/subr.c/1.19/Tue Oct 27 23:59:31 2009//
+/ttys.5/1.11/Wed Oct 22 22:16:16 2008//
+/gettytab.5/1.19/Mon Apr 5 23:11:44 2010//
+/init.c/1.8/Sun Jun 27 18:29:54 2010//
+D
--- /dev/null
+src/libexec/getty
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:28 niklas Exp $
+
+PROG= getty
+SRCS= main.c init.c subr.c
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+MAN= getty.8 gettytab.5 ttys.5
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: extern.h,v 1.5 2003/06/02 19:38:24 millert Exp $*/
+
+/*
+ * 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. 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.
+ *
+ * from: @(#)extern.h 8.1 (Berkeley) 6/4/93
+ */
+
+struct delayval;
+
+int adelay(int, struct delayval *);
+char *autobaud(void);
+int delaybits(void);
+void edithost(char *);
+void gendefaults(void);
+int getent(char *, char *);
+int getflag(char *);
+long getnum(char *);
+char *getstr(char *, char **);
+void gettable(char *, char *);
+void makeenv(char *[]);
+char *portselector(void);
+void set_ttydefaults(int);
+void setchars(void);
+void setdefaults(void);
+void setflags(int);
+int speed(int);
--- /dev/null
+.\" $OpenBSD: getty.8,v 1.13 2007/05/31 19:19:39 jmc Exp $
+.\" 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. 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.
+.\"
+.\" from: @(#)getty.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt GETTY 8
+.Os
+.Sh NAME
+.Nm getty
+.Nd set terminal mode
+.Sh SYNOPSIS
+.Nm getty
+.Oo
+.Ar type
+.Op Ar tty
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+program
+is called by
+.Xr init 8
+to open and initialize the tty line, read a login name, and invoke
+.Xr login 1 .
+.Pp
+The argument
+.Ar tty
+is the special device file in
+.Pa /dev
+to open for the terminal (for example, ``ttyh0'').
+If there is no argument or the argument is
+.Sq Fl ,
+the tty line is assumed to be open as file descriptor 0.
+.Pp
+The
+.Ar type
+argument can be used to make
+.Nm
+treat the terminal line specially.
+This argument is used as an index into the
+.Xr gettytab 5
+database, to determine the characteristics of the line.
+If there is no argument, or there is no such table, the
+.Em default
+table is used.
+If there is no
+.Pa /etc/gettytab
+a set of system defaults is used.
+If indicated by the table located,
+.Nm
+will clear the terminal screen,
+print a banner heading,
+and prompt for a login name.
+Usually either the banner or the login prompt will include
+the system hostname.
+.Pp
+Most of the default actions of
+.Nm
+can be circumvented, or modified, by a suitable
+.Xr gettytab 5
+table.
+.Pp
+The
+.Nm
+program
+can be set to timeout after some interval,
+which will cause dial up lines to hang up
+if the login name is not entered reasonably quickly.
+.Sh RESOURCES
+.Nm
+is started by
+.Xr init 8 ,
+with a process priority, umask, and resource limits based on the
+.Dq default
+entry in
+.Pa /etc/login.conf .
+.Sh FILES
+.Bl -tag -width /etc/gettytab -compact
+.It Pa /etc/gettytab
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "ttyxx: No such device or address."
+.It "ttyxx: No such file or address."
+A terminal which is turned
+on in the
+.Xr ttys 5
+file cannot be opened, likely because the requisite
+lines are either not configured into the system, the associated device
+was not attached during boot-time system configuration,
+or the special file in
+.Pa /dev
+does not exist.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr ioctl 2 ,
+.Xr tty 4 ,
+.Xr gettytab 5 ,
+.Xr login.conf 5 ,
+.Xr ttys 5 ,
+.Xr init 8
+.Sh HISTORY
+A
+.Nm
+program appeared in
+.At v6 .
--- /dev/null
+.\" $OpenBSD: gettytab.5,v 1.19 2010/02/18 13:00:18 schwarze Exp $
+.\" 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. 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.
+.\"
+.\" from: @(#)gettytab.5 8.4 (Berkeley) 4/19/94
+.\"
+.Dd $Mdocdate: February 18 2010 $
+.Dt GETTYTAB 5
+.Os
+.Sh NAME
+.Nm gettytab
+.Nd terminal configuration database
+.Sh SYNOPSIS
+.Nm gettytab
+.Sh DESCRIPTION
+The
+.Nm
+file
+is a simplified version of the
+.Xr termcap 5
+database
+used to describe terminal lines.
+The initial terminal login process
+.Xr getty 8
+accesses the
+.Nm
+file each time it starts, allowing simpler
+reconfiguration of terminal characteristics.
+Each entry in the database
+is used to describe one class of terminals.
+.Pp
+There is a default terminal class,
+.Em default ,
+that is used to set global defaults for all other classes.
+(That is, the
+.Em default
+entry is read, then the entry for the class required
+is used to override particular settings.)
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+The
+.Em default
+column below lists defaults obtained if there is
+no entry in the table obtained, nor one in the special
+.Em default
+table.
+.Bl -column indent indent indent
+.It Sy Name Type Default Description
+.It "ap bool false Terminal uses any parity."
+.It "bk str 0377 Alternative end-of-line character (input break)."
+.It "c0 num unused TTY control flags to write messages."
+.It "c1 num unused TTY control flags to read login name."
+.It "c2 num unused TTY control flags to leave terminal as."
+.It "ce bool false Use CRT erase algorithm."
+.It "ck bool false Use CRT kill algorithm."
+.It "cl str" Ta Dv NULL Ta
+.No "Screen clear sequence."
+.It "co bool false Console; add"
+.Ql \en
+after login prompt.
+.It "ds str" Ta So Li ^Y Sc Ta
+.No "Delayed suspend character."
+.It "dx bool false Set"
+.Dv DECCTLQ .
+.It "ec bool false Leave echo"
+.Tn OFF .
+.It "ep bool false Terminal uses even parity."
+.It "er str" Ta So Li ^? Sc Ta
+.No "Erase character."
+.It "et str" Ta So Li ^D Sc Ta
+.No "End of text"
+.Pq Dv EOF
+character.
+.It "ev str" Ta Dv NULL Ta
+.No "Initial environment."
+.It "f0 num unused TTY mode flags to write messages."
+.It "f1 num unused TTY mode flags to read login name."
+.It "f2 num unused TTY mode flags to leave terminal as."
+.It "fl str" Ta So Li ^O Sc Ta
+.No "Output flush character."
+.It "hc bool false Do"
+.Em not
+hangup line on last close.
+.It "he str" Ta Dv NULL Ta
+.No "Hostname editing string."
+.It "hn str hostname Hostname."
+.It "ht bool false Terminal has real tabs."
+.It "i0 num unused TTY input flags to write messages."
+.It "i1 num unused TTY input flags to read login name."
+.It "i2 num unused TTY input flags to leave terminal as."
+.It "ig bool false Ignore garbage characters in login name."
+.It "im str" Ta Dv NULL Ta
+.No "Initial (banner) message."
+.It "in str" Ta So Li ^C Sc Ta
+.No "Interrupt character."
+.It "is num unused Input speed."
+.It "kl str" Ta So Li ^U Sc Ta
+.No "Kill character."
+.It "l0 num unused TTY local flags to write messages."
+.It "l1 num unused TTY local flags to read login name."
+.It "l2 num unused TTY local flags to leave terminal as."
+.It "lc bool false Terminal has lower case."
+.It "lm str login: Login prompt."
+.It "ln str" Ta So Li ^V Sc Ta
+.No "``Literal next'' character."
+.It "lo str" Ta Pa /usr/bin/login Ta
+.No "Program to execute when name obtained."
+.It "mb bool false \&Do flow control based on carrier."
+.It "nl bool false Terminal has (or might have) a newline character."
+.It "np bool false Terminal uses no parity (i.e., 8-bit characters)."
+.It "nx str default Next table (for auto speed selection)."
+.It "o0 num unused TTY output flags to write messages."
+.It "o1 num unused TTY output flags to read login name."
+.It "o2 num unused TTY output flags to leave terminal as."
+.It "op bool false Terminal uses odd parity."
+.It "os num unused Output speed."
+.It "pc str" Ta So Li \e0 Sc Ta
+.No "Pad character."
+.It "pe bool false Use printer (hard copy) erase algorithm."
+.It "pf num 0 Delay"
+between first prompt and following flush (seconds).
+.It "pp str unused PPP authentication program."
+.It "ps bool false Line connected to a"
+.Tn MICOM
+port selector.
+.It "qu str" Ta So Li \&^\e Sc Ta
+.No "Quit character."
+.It "rp str" Ta So Li ^R Sc Ta
+.No "Line retype character."
+.It "rw bool false Do"
+.Em not
+use raw for input, use cbreak.
+.It "sp num unused Line speed (input and output)."
+.It "su str" Ta So Li ^Z Sc Ta
+.No "Suspend character."
+.It "tc str none Table continuation."
+.It "to num 0 Timeout (seconds)."
+.It "tt str" Ta Dv NULL Ta
+.No "Terminal type (for environment)."
+.It "ub bool false \&Do unbuffered output (of prompts etc)."
+.It "we str" Ta So Li ^W Sc Ta
+.No "Word erase character."
+.It "xc bool false Do"
+.Em not
+echo control characters as
+.Ql ^X .
+.It "xf str" Ta So Li ^S Sc Ta Dv XOFF
+(stop output) character.
+.It "xn str" Ta So Li ^Q Sc Ta Dv XON
+(start output) character.
+.El
+.Pp
+The following capabilities are no longer supported by
+.Xr getty 8 :
+.Bl -column indent indent indent
+.It "bd num 0 Backspace delay."
+.It "cb bool false Use CRT backspace mode."
+.It "cd num 0 Carriage-return delay."
+.It "fd num 0 Form-feed (vertical motion) delay."
+.It "nd num 0 Newline (line-feed) delay."
+.It "uc bool false Terminal is known upper case only."
+.El
+.Pp
+If no line speed is specified, speed will not be altered
+from that which prevails when
+.Xr getty 8
+is entered.
+Specifying an input or output speed will override
+line speed for stated direction only.
+.Pp
+Terminal modes to be used for the output of the message and
+for input of the login name,
+and to leave the terminal set as upon completion,
+are derived from the boolean flags specified.
+If the derivation should prove inadequate,
+any (or all) of these three may be overridden
+with one of the
+.Em \&c0 ,
+.Em \&c1 ,
+.Em \&c2 ,
+.Em \&i0 ,
+.Em \&i1 ,
+.Em \&i2 ,
+.Em \&l0 ,
+.Em \&l1 ,
+.Em \&l2 ,
+.Em \&o0 ,
+.Em \&o1 ,
+or
+.Em \&o2
+numeric specifications, which can be used to specify
+(usually in octal, with a leading
+.Ql 0 )
+the exact values of the flags.
+These flags correspond to the termios
+.Em c_cflag ,
+.Em c_iflag ,
+.Em c_lflag ,
+and
+.Em c_oflag
+fields, respectively.
+Each of these sets must be completely specified to be effective.
+The
+.Em \&f0 ,
+.Em \&f1 ,
+and
+.Em \&f2
+are excepted for backwards compatibility with a previous incarnation of
+the TTY subsystem.
+In these flags the bottom 16 bits of the (32 bits) value contain the sgttyb
+.Em sg_flags
+field, while the top 16 bits represent the local mode word.
+.Pp
+Should
+.Xr getty 8
+receive a null character
+(presumed to indicate a line break)
+it will restart using the table indicated by the
+.Em nx
+entry.
+If there is none, it will re-use its original table.
+.Pp
+Delays are specified in milliseconds;
+the nearest possible delay available in the TTY driver will be used.
+Should greater certainty be desired, delays
+with values 0, 1, 2, and 3 are interpreted as
+choosing that particular delay algorithm from the driver.
+.Pp
+The
+.Em \&cl
+screen clear string may be preceded by a (decimal) number
+of milliseconds of delay required (a la
+.Xr termcap 5 ) .
+This delay is simulated by repeated use of the pad character
+.Em \&pc .
+.Pp
+The initial message and login message
+.Po
+.Em \&im
+and
+.Em \&lm
+.Pc
+may include any of the following character sequences, which expand to
+information about the environment in which
+.Xr getty 8
+is running:
+.Pp
+.Bl -tag -width \&%xxx
+.It \&%d
+The current date.
+.It \&%h
+The hostname of the machine, which is normally obtained from the
+system using
+.Xr gethostname 3 ,
+but may also be overridden by the
+.Em \&hn
+table entry.
+In either case it may be edited with the
+.Em \&he
+string.
+A
+.Ql @
+in the
+.Em \&he
+string causes one character from the real hostname to
+be copied to the final hostname.
+A
+.Ql #
+in the
+.Em \&he
+string causes the next character of the real hostname
+to be skipped.
+Each character that
+is neither
+.Ql @
+nor
+.Ql #
+is copied into the final hostname.
+Surplus
+.Ql @
+and
+.Ql #
+characters are ignored.
+.It \&%t
+The TTY name.
+.It "\&%m, \&%r, \&%s, \&%v"
+The type of machine, release of the operating system, name of the
+operating system, and version of the kernel, respectively, as
+returned by
+.Xr uname 3 .
+.It \&%%
+A
+.Ql %
+character.
+.El
+.Pp
+When
+.Xr getty 8
+executes the login process given in the
+.Em \&lo
+string (usually
+.Pa /usr/bin/login ) ,
+it will have set
+the environment to include the terminal type, as indicated
+by the
+.Em \&tt
+string (if it exists).
+The
+.Em \&ev
+string can be used to enter additional data into the environment.
+It is a list of comma-separated strings, each of which
+will presumably be of the form
+.Em name=value .
+.Pp
+If a non-zero timeout is specified with
+.Em \&to ,
+then
+.Xr getty 8
+will exit within the indicated number of seconds, either having
+received a login name and passed control to
+.Xr login 1 ,
+or having received an alarm signal and exited.
+This may be useful to hangup dial in lines.
+.Pp
+Output from
+.Xr getty 8
+is even parity unless
+.Em \&op
+or
+.Em \&np
+is specified.
+The
+.Em \&op
+string
+may be specified with
+.Em \&ap
+to allow any parity on input, but generate odd parity output.
+Note: this only applies while
+.Xr getty 8
+is being run;
+terminal driver limitations prevent a more complete implementation.
+.Xr getty 8
+does not check parity of input characters in
+.Dv RAW
+mode.
+.Pp
+If a
+.Em \&pp
+string is specified and a PPP link bring-up sequence is recognized,
+.Xr getty 8
+will invoke the program referenced by the
+.Em \&pp
+option.
+This can be used to handle incoming PPP calls.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr gethostname 3 ,
+.Xr uname 3 ,
+.Xr termcap 5 ,
+.Xr getty 8
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
+.Sh BUGS
+The special characters (erase, kill, etc.) are reset to system defaults by
+.Xr login 1 .
+In
+.Em all
+cases,
+.Ql #
+or
+.Ql ^H
+typed in a login name will be treated as an erase character,
+and
+.Ql @
+will be treated as a kill character.
+.Pp
+The delay stuff is a real crock.
+Apart from its general lack of flexibility, some
+of the delay algorithms are not implemented.
+The terminal driver should support sane delay settings.
+.Pp
+The
+.Em \&he
+capability is stupid.
+.Pp
+The
+.Xr termcap 5
+format is horrid; something more rational should have been chosen.
--- /dev/null
+/* $OpenBSD: gettytab.h,v 1.6 2003/06/02 19:38:24 millert Exp $*/
+
+/*
+ * Copyright (c) 1983, 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. 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.
+ *
+ * from: @(#)gettytab.h 8.2 (Berkeley) 3/30/94
+ */
+
+/*
+ * Getty description definitions.
+ */
+struct gettystrs {
+ char *field; /* name to lookup in gettytab */
+ char *defalt; /* value we find by looking in defaults */
+ char *value; /* value that we find there */
+};
+
+struct gettynums {
+ char *field; /* name to lookup */
+ long defalt; /* number we find in defaults */
+ long value; /* number we find there */
+ int set; /* we actually got this one */
+};
+
+struct gettyflags {
+ char *field; /* name to lookup */
+ char invrt; /* name existing in gettytab --> false */
+ char defalt; /* true/false in defaults */
+ char value; /* true/false flag */
+ char set; /* we found it */
+};
+
+/*
+ * String values.
+ */
+#define NX gettystrs[0].value
+#define CL gettystrs[1].value
+#define IM gettystrs[2].value
+#define LM gettystrs[3].value
+#define ER gettystrs[4].value
+#define KL gettystrs[5].value
+#define ET gettystrs[6].value
+#define PC gettystrs[7].value
+#define TT gettystrs[8].value
+#define EV gettystrs[9].value
+#define LO gettystrs[10].value
+#define HN gettystrs[11].value
+#define HE gettystrs[12].value
+#define IN gettystrs[13].value
+#define QU gettystrs[14].value
+#define XN gettystrs[15].value
+#define XF gettystrs[16].value
+#define BK gettystrs[17].value
+#define SU gettystrs[18].value
+#define DS gettystrs[19].value
+#define RP gettystrs[20].value
+#define FL gettystrs[21].value
+#define WE gettystrs[22].value
+#define LN gettystrs[23].value
+#define PP gettystrs[24].value
+
+/*
+ * Numeric definitions.
+ */
+#define IS gettynums[0].value
+#define OS gettynums[1].value
+#define SP gettynums[2].value
+#define ND gettynums[3].value
+#define CD gettynums[4].value
+#define TD gettynums[5].value
+#define FD gettynums[6].value
+#define BD gettynums[7].value
+#define TO gettynums[8].value
+#define F0 gettynums[9].value
+#define F0set gettynums[9].set
+#define F1 gettynums[10].value
+#define F1set gettynums[10].set
+#define F2 gettynums[11].value
+#define F2set gettynums[11].set
+#define PF gettynums[12].value
+#define C0 gettynums[13].value
+#define C0set gettynums[13].set
+#define C1 gettynums[14].value
+#define C1set gettynums[14].set
+#define C2 gettynums[15].value
+#define C2set gettynums[15].set
+#define I0 gettynums[16].value
+#define I0set gettynums[16].set
+#define I1 gettynums[17].value
+#define I1set gettynums[17].set
+#define I2 gettynums[18].value
+#define I2set gettynums[18].set
+#define L0 gettynums[19].value
+#define L0set gettynums[19].set
+#define L1 gettynums[20].value
+#define L1set gettynums[20].set
+#define L2 gettynums[21].value
+#define L2set gettynums[21].set
+#define O0 gettynums[22].value
+#define O0set gettynums[22].set
+#define O1 gettynums[23].value
+#define O1set gettynums[23].set
+#define O2 gettynums[24].value
+#define O2set gettynums[24].set
+
+/*
+ * Boolean values.
+ */
+#define HT gettyflags[0].value
+#define NL gettyflags[1].value
+#define EP gettyflags[2].value
+#define EPset gettyflags[2].set
+#define OP gettyflags[3].value
+#define OPset gettyflags[3].set
+#define AP gettyflags[4].value
+#define APset gettyflags[4].set
+#define EC gettyflags[5].value
+#define CO gettyflags[6].value
+#define CB gettyflags[7].value
+#define CK gettyflags[8].value
+#define CE gettyflags[9].value
+#define PE gettyflags[10].value
+#define RW gettyflags[11].value
+#define XC gettyflags[12].value
+#define LC gettyflags[13].value
+#define UC gettyflags[14].value
+#define IG gettyflags[15].value
+#define PS gettyflags[16].value
+#define HC gettyflags[17].value
+#define UB gettyflags[18].value
+#define AB gettyflags[19].value
+#define DX gettyflags[20].value
+#define NP gettyflags[21].value
+#define MB gettyflags[22].value
+
+int getent(char *, char *);
+long getnum(char *);
+int getflag(char *);
+char *getstr(char *, char **);
+
+extern struct gettyflags gettyflags[];
+extern struct gettynums gettynums[];
+extern struct gettystrs gettystrs[];
+extern int hopcount;
--- /dev/null
+/* $OpenBSD: init.c,v 1.8 2010/06/07 02:01:45 guenther Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+/*
+ * Getty table initializations.
+ *
+ * Melbourne getty.
+ */
+#include <termios.h>
+#include "gettytab.h"
+#include "pathnames.h"
+
+extern struct termios tmode;
+extern char hostname[];
+
+struct gettystrs gettystrs[] = {
+ { "nx" }, /* next table */
+ { "cl" }, /* screen clear characters */
+ { "im" }, /* initial message */
+ { "lm", "login: " }, /* login message */
+ { "er", &tmode.c_cc[VERASE] }, /* erase character */
+ { "kl", &tmode.c_cc[VKILL] }, /* kill character */
+ { "et", &tmode.c_cc[VEOF] }, /* eof character (eot) */
+ { "pc", "" }, /* pad character */
+ { "tt" }, /* terminal type */
+ { "ev" }, /* environment */
+ { "lo", _PATH_LOGIN }, /* login program */
+ { "hn", hostname }, /* host name */
+ { "he" }, /* host name edit */
+ { "in", &tmode.c_cc[VINTR] }, /* interrupt char */
+ { "qu", &tmode.c_cc[VQUIT] }, /* quit char */
+ { "xn", &tmode.c_cc[VSTART] }, /* XON (start) char */
+ { "xf", &tmode.c_cc[VSTOP] }, /* XOFF (stop) char */
+ { "bk", &tmode.c_cc[VEOL] }, /* brk char (alt \n) */
+ { "su", &tmode.c_cc[VSUSP] }, /* suspend char */
+ { "ds", &tmode.c_cc[VDSUSP] }, /* delayed suspend */
+ { "rp", &tmode.c_cc[VREPRINT] },/* reprint char */
+ { "fl", &tmode.c_cc[VDISCARD] },/* flush output */
+ { "we", &tmode.c_cc[VWERASE] }, /* word erase */
+ { "ln", &tmode.c_cc[VLNEXT] }, /* literal next */
+ { "pp" }, /* ppp login program */
+ { 0 }
+};
+
+struct gettynums gettynums[] = {
+ { "is" }, /* input speed */
+ { "os" }, /* output speed */
+ { "sp" }, /* both speeds */
+ { "nd" }, /* newline delay */
+ { "cd" }, /* carriage-return delay */
+ { "td" }, /* tab delay */
+ { "fd" }, /* form-feed delay */
+ { "bd" }, /* backspace delay */
+ { "to" }, /* timeout */
+ { "f0" }, /* output flags */
+ { "f1" }, /* input flags */
+ { "f2" }, /* user mode flags */
+ { "pf" }, /* delay before flush at 1st prompt */
+ { "c0" }, /* output c_flags */
+ { "c1" }, /* input c_flags */
+ { "c2" }, /* user mode c_flags */
+ { "i0" }, /* output i_flags */
+ { "i1" }, /* input i_flags */
+ { "i2" }, /* user mode i_flags */
+ { "l0" }, /* output l_flags */
+ { "l1" }, /* input l_flags */
+ { "l2" }, /* user mode l_flags */
+ { "o0" }, /* output o_flags */
+ { "o1" }, /* input o_flags */
+ { "o2" }, /* user mode o_flags */
+ { 0 }
+};
+
+struct gettyflags gettyflags[] = {
+ { "ht", 0 }, /* has tabs */
+ { "nl", 1 }, /* has newline char */
+ { "ep", 0 }, /* even parity */
+ { "op", 0 }, /* odd parity */
+ { "ap", 0 }, /* any parity */
+ { "ec", 1 }, /* no echo */
+ { "co", 0 }, /* console special */
+ { "cb", 0 }, /* crt backspace */
+ { "ck", 1 }, /* crt kill */
+ { "ce", 1 }, /* crt erase */
+ { "pe", 0 }, /* printer erase */
+ { "rw", 1 }, /* don't use raw */
+ { "xc", 1 }, /* don't ^X ctl chars */
+ { "lc", 0 }, /* terminal has lower case */
+ { "uc", 0 }, /* terminal has no lower case */
+ { "ig", 0 }, /* ignore garbage */
+ { "ps", 0 }, /* do port selector speed select */
+ { "hc", 1 }, /* don't set hangup on close */
+ { "ub", 0 }, /* unbuffered output */
+ { "ab", 0 }, /* auto-baud detect with '\r' */
+ { "dx", 0 }, /* set decctlq */
+ { "np", 0 }, /* no parity at all (8bit chars) */
+ { "mb", 0 }, /* do MDMBUF flow control */
+ { 0 }
+};
--- /dev/null
+/* $OpenBSD: main.c,v 1.30 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "gettytab.h"
+#include "pathnames.h"
+#include "extern.h"
+
+/*
+ * Set the amount of running time that getty should accumulate
+ * before deciding that something is wrong and exit.
+ */
+#define GETTY_TIMEOUT 60 /* seconds */
+
+/* defines for auto detection of incoming PPP calls (->PAP/CHAP) */
+
+#define PPP_FRAME 0x7e /* PPP Framing character */
+#define PPP_STATION 0xff /* "All Station" character */
+#define PPP_ESCAPE 0x7d /* Escape Character */
+#define PPP_CONTROL 0x03 /* PPP Control Field */
+#define PPP_CONTROL_ESCAPED 0x23 /* PPP Control Field, escaped */
+#define PPP_LCP_HI 0xc0 /* LCP protocol - high byte */
+#define PPP_LCP_LOW 0x21 /* LCP protocol - low byte */
+
+struct termios tmode, omode;
+
+int crmod, digit, lower, upper;
+
+char hostname[MAXHOSTNAMELEN];
+struct utsname kerninfo;
+char name[MAXLOGNAME];
+char dev[] = _PATH_DEV;
+char ttyn[32];
+char *portselector(void);
+
+#define OBUFSIZ 128
+#define TABBUFSIZ 512
+
+char defent[TABBUFSIZ];
+char tabent[TABBUFSIZ];
+
+char *env[128];
+
+char partab[] = {
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0202,0004,0003,0205,0005,0206,0201,0001,
+ 0201,0001,0001,0201,0001,0201,0201,0001,
+ 0001,0201,0201,0001,0201,0001,0001,0201,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0200,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0200,0000,0000,0200,0000,0200,0200,0000,
+ 0000,0200,0200,0000,0200,0000,0000,0201
+};
+
+#define ERASE tmode.c_cc[VERASE]
+#define KILL tmode.c_cc[VKILL]
+#define EOT tmode.c_cc[VEOF]
+
+static void
+dingdong(int signo)
+{
+ tmode.c_ispeed = tmode.c_ospeed = 0;
+ (void)tcsetattr(0, TCSANOW, &tmode);
+ _exit(1);
+}
+
+volatile sig_atomic_t interrupt_flag;
+
+static void
+interrupt(int signo)
+{
+ int save_errno = errno;
+
+ interrupt_flag = 1;
+ signal(SIGINT, interrupt);
+ errno = save_errno;
+}
+
+/*
+ * Action to take when getty is running too long.
+ */
+static void
+timeoverrun(int signo)
+{
+ struct syslog_data sdata = SYSLOG_DATA_INIT;
+
+ syslog_r(LOG_ERR, &sdata,
+ "getty exiting due to excessive running time");
+ _exit(1);
+}
+
+static int getname(void);
+static void oflush(void);
+static void prompt(void);
+static void putchr(int);
+static void putf(char *);
+static void putpad(char *);
+static void xputs(char *);
+
+int
+main(int argc, char *argv[])
+{
+ extern char **environ;
+ char *tname;
+ int repcnt = 0, failopenlogged = 0;
+ struct rlimit limit;
+ int rval;
+
+ signal(SIGINT, SIG_IGN);
+/*
+ signal(SIGQUIT, SIG_DFL);
+*/
+ openlog("getty", LOG_ODELAY|LOG_CONS|LOG_PID, LOG_AUTH);
+ gethostname(hostname, sizeof(hostname));
+ if (hostname[0] == '\0')
+ strlcpy(hostname, "Amnesiac", sizeof hostname);
+ uname(&kerninfo);
+
+ /*
+ * Limit running time to deal with broken or dead lines.
+ */
+ (void)signal(SIGXCPU, timeoverrun);
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = GETTY_TIMEOUT;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+
+ /*
+ * The following is a work around for vhangup interactions
+ * which cause great problems getting window systems started.
+ * If the tty line is "-", we do the old style getty presuming
+ * that the file descriptors are already set up for us.
+ * J. Gettys - MIT Project Athena.
+ */
+ if (argc <= 2 || strcmp(argv[2], "-") == 0) {
+ snprintf(ttyn, sizeof ttyn, "%s", ttyname(0));
+ } else {
+ int i;
+
+ snprintf(ttyn, sizeof ttyn, "%s%s", dev, argv[2]);
+ if (strcmp(argv[0], "+") != 0) {
+ chown(ttyn, 0, 0);
+ chmod(ttyn, 0600);
+ revoke(ttyn);
+ /*
+ * Delay the open so DTR stays down long enough to be detected.
+ */
+ sleep(2);
+ while ((i = open(ttyn, O_RDWR)) == -1) {
+ if ((repcnt % 10 == 0) &&
+ (errno != ENXIO || !failopenlogged)) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ closelog();
+ failopenlogged = 1;
+ }
+ repcnt++;
+ sleep(60);
+ }
+ login_tty(i);
+ }
+ }
+
+ /* Start with default tty settings */
+ if (tcgetattr(0, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(1);
+ }
+ omode = tmode;
+
+ gettable("default", defent);
+ gendefaults();
+ tname = "default";
+ if (argc > 1)
+ tname = argv[1];
+ for (;;) {
+ int off;
+
+ gettable(tname, tabent);
+ if (OPset || EPset || APset)
+ APset++, OPset++, EPset++;
+ setdefaults();
+ off = 0;
+ (void)tcflush(0, TCIOFLUSH); /* clear out the crap */
+ ioctl(0, FIONBIO, &off); /* turn off non-blocking mode */
+ ioctl(0, FIOASYNC, &off); /* ditto for async mode */
+
+ if (IS)
+ cfsetispeed(&tmode, IS);
+ else if (SP)
+ cfsetispeed(&tmode, SP);
+ if (OS)
+ cfsetospeed(&tmode, OS);
+ else if (SP)
+ cfsetospeed(&tmode, SP);
+ setflags(0);
+ setchars();
+ if (tcsetattr(0, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(1);
+ }
+ if (AB) {
+ tname = autobaud();
+ continue;
+ }
+ if (PS) {
+ tname = portselector();
+ continue;
+ }
+ if (CL && *CL)
+ putpad(CL);
+ edithost(HE);
+ if (IM && *IM)
+ putf(IM);
+ if (TO) {
+ signal(SIGALRM, dingdong);
+ alarm(TO);
+ }
+ if ((rval = getname()) == 2) {
+ oflush();
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ execle(PP, "ppplogin", ttyn, (char *) 0, env);
+ syslog(LOG_ERR, "%s: %m", PP);
+ exit(1);
+ } else if (rval) {
+ int i;
+
+ oflush();
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (name[0] == '-') {
+ xputs("user names may not start with '-'.");
+ continue;
+ }
+ if (!(upper || lower || digit))
+ continue;
+ setflags(2);
+ if (crmod) {
+ tmode.c_iflag |= ICRNL;
+ tmode.c_oflag |= ONLCR;
+ }
+ if (upper || UC) {
+ tmode.c_iflag |= IUCLC;
+ tmode.c_oflag |= OLCUC;
+ tmode.c_lflag |= XCASE;
+ }
+ if (lower || LC) {
+ tmode.c_iflag &= ~IUCLC;
+ tmode.c_oflag &= ~OLCUC;
+ tmode.c_lflag &= ~XCASE;
+ }
+ if (tcsetattr(0, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(1);
+ }
+ signal(SIGINT, SIG_DFL);
+ for (i = 0; environ[i] != (char *)0; i++)
+ env[i] = environ[i];
+ makeenv(&env[i]);
+
+ limit.rlim_max = RLIM_INFINITY;
+ limit.rlim_cur = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &limit);
+ execle(LO, "login", "-p", "--", name, (char *)0, env);
+ syslog(LOG_ERR, "%s: %m", LO);
+ exit(1);
+ }
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGINT, SIG_IGN);
+ if (NX && *NX)
+ tname = NX;
+ }
+}
+
+static int
+getname(void)
+{
+ int ppp_state = 0, ppp_connection = 0;
+ unsigned char cs;
+ int c, r;
+ char *np;
+
+ /*
+ * Interrupt may happen if we use CBREAK mode
+ */
+ signal(SIGINT, interrupt);
+ setflags(1);
+ prompt();
+ if (PF > 0) {
+ oflush();
+ sleep(PF);
+ PF = 0;
+ }
+ if (tcsetattr(0, TCSANOW, &tmode) < 0) {
+ syslog(LOG_ERR, "%s: %m", ttyn);
+ exit(1);
+ }
+ crmod = digit = lower = upper = 0;
+ np = name;
+ for (;;) {
+ oflush();
+ r = read(STDIN_FILENO, &cs, 1);
+ if (r <= 0) {
+ if (r == -1 && errno == EINTR && interrupt_flag) {
+ interrupt_flag = 0;
+ return (0);
+ }
+ exit(0);
+ }
+ if ((c = cs&0177) == 0)
+ return (0);
+
+ /*
+ * PPP detection state machine..
+ * Look for sequences:
+ * PPP_FRAME, PPP_STATION, PPP_ESCAPE, PPP_CONTROL_ESCAPED or
+ * PPP_FRAME, PPP_STATION, PPP_CONTROL (deviant from RFC)
+ * See RFC1662.
+ * Derived from code from Michael Hancock <michaelh@cet.co.jp>
+ * and Erik 'PPP' Olson <eriko@wrq.com>
+ */
+ if (PP && cs == PPP_FRAME) {
+ ppp_state = 1;
+ } else if (ppp_state == 1 && cs == PPP_STATION) {
+ ppp_state = 2;
+ } else if (ppp_state == 2 && cs == PPP_ESCAPE) {
+ ppp_state = 3;
+ } else if ((ppp_state == 2 && cs == PPP_CONTROL) ||
+ (ppp_state == 3 && cs == PPP_CONTROL_ESCAPED)) {
+ ppp_state = 4;
+ } else if (ppp_state == 4 && cs == PPP_LCP_HI) {
+ ppp_state = 5;
+ } else if (ppp_state == 5 && cs == PPP_LCP_LOW) {
+ ppp_connection = 1;
+ break;
+ } else {
+ ppp_state = 0;
+ }
+
+ if (c == EOT)
+ exit(1);
+ if (c == '\r' || c == '\n' || np >= name + sizeof name -1) {
+ putf("\r\n");
+ break;
+ }
+ if (islower(c))
+ lower = 1;
+ else if (isupper(c))
+ upper = 1;
+ else if (c == ERASE || c == '#' || c == '\b') {
+ if (np > name) {
+ np--;
+ if (cfgetospeed(&tmode) >= 1200)
+ xputs("\b \b");
+ else
+ putchr(cs);
+ }
+ continue;
+ } else if (c == KILL || c == '@') {
+ putchr(cs);
+ putchr('\r');
+ if (cfgetospeed(&tmode) < 1200)
+ putchr('\n');
+ /* this is the way they do it down under ... */
+ else if (np > name)
+ xputs(" \r");
+ prompt();
+ np = name;
+ continue;
+ } else if (isdigit(c))
+ digit++;
+ if (IG && (c <= ' ' || c > 0176))
+ continue;
+ *np++ = c;
+ putchr(cs);
+ }
+ signal(SIGINT, SIG_IGN);
+ if (interrupt_flag) {
+ interrupt_flag = 0;
+ return (0);
+ }
+ *np = 0;
+ if (c == '\r')
+ crmod = 1;
+ if (upper && !lower && !LC || UC)
+ for (np = name; *np; np++)
+ if (isupper(*np))
+ *np = tolower(*np);
+ return (1 + ppp_connection);
+}
+
+static void
+putpad(char *s)
+{
+ int pad = 0;
+ speed_t ospeed = cfgetospeed(&tmode);
+
+ if (isdigit(*s)) {
+ while (isdigit(*s)) {
+ pad *= 10;
+ pad += *s++ - '0';
+ }
+ pad *= 10;
+ if (*s == '.' && isdigit(s[1])) {
+ pad += s[1] - '0';
+ s += 2;
+ }
+ }
+
+ xputs(s);
+ /*
+ * If no delay needed, or output speed is
+ * not comprehensible, then don't try to delay.
+ */
+ if (pad == 0 || ospeed <= 0)
+ return;
+
+ /*
+ * Round up by a half a character frame, and then do the delay.
+ * Too bad there are no user program accessible programmed delays.
+ * Transmitting pad characters slows many terminals down and also
+ * loads the system.
+ */
+ pad = (pad * ospeed + 50000) / 100000;
+ while (pad--)
+ putchr(*PC);
+}
+
+static void
+xputs(char *s)
+{
+ while (*s)
+ putchr(*s++);
+}
+
+char outbuf[OBUFSIZ];
+int obufcnt = 0;
+
+static void
+putchr(int cc)
+{
+ char c;
+
+ c = cc;
+ if (!NP) {
+ c |= partab[c&0177] & 0200;
+ if (OP)
+ c ^= 0200;
+ }
+ if (!UB) {
+ outbuf[obufcnt++] = c;
+ if (obufcnt >= OBUFSIZ)
+ oflush();
+ } else
+ write(STDOUT_FILENO, &c, 1);
+}
+
+static void
+oflush(void)
+{
+ if (obufcnt)
+ write(STDOUT_FILENO, outbuf, obufcnt);
+ obufcnt = 0;
+}
+
+static void
+prompt(void)
+{
+
+ putf(LM);
+ if (CO)
+ putchr('\n');
+}
+
+static void
+putf(char *cp)
+{
+ extern char editedhost[];
+ char *slash, db[100];
+ time_t t;
+
+ while (*cp) {
+ if (*cp != '%') {
+ putchr(*cp++);
+ continue;
+ }
+ switch (*++cp) {
+
+ case 't':
+ slash = strrchr(ttyn, '/');
+ if (slash == (char *) 0)
+ xputs(ttyn);
+ else
+ xputs(&slash[1]);
+ break;
+
+ case 'h':
+ xputs(editedhost);
+ break;
+
+ case 'd': {
+ (void)time(&t);
+ (void)strftime(db, sizeof(db),
+ "%l:%M%p on %A, %d %B %Y", localtime(&t));
+ xputs(db);
+ break;
+ }
+
+ case 's':
+ xputs(kerninfo.sysname);
+ break;
+
+ case 'm':
+ xputs(kerninfo.machine);
+ break;
+
+ case 'r':
+ xputs(kerninfo.release);
+ break;
+
+ case 'v':
+ xputs(kerninfo.version);
+ break;
+
+ case '%':
+ putchr('%');
+ break;
+ }
+ cp++;
+ }
+}
--- /dev/null
+/usr/obj/libexec/getty
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: pathnames.h,v 1.3 2003/06/02 19:38:24 millert Exp $*/
+
+/*
+ * 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. 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.
+ *
+ * from: @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+
+#include <paths.h>
+
+#define _PATH_GETTYTAB "/etc/gettytab"
+#define _PATH_LOGIN "/usr/bin/login"
--- /dev/null
+/* $OpenBSD: subr.c,v 1.19 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+/*
+ * Melbourne getty.
+ */
+#define COMPAT_43
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#include "gettytab.h"
+#include "pathnames.h"
+#include "extern.h"
+
+extern struct termios tmode, omode;
+
+static void compatflags(long);
+
+/*
+ * Get a table entry.
+ */
+void
+gettable(char *name, char *buf)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+ long n;
+ char *dba[2];
+ dba[0] = _PATH_GETTYTAB;
+ dba[1] = 0;
+
+ if (cgetent(&buf, dba, name) != 0)
+ return;
+
+ for (sp = gettystrs; sp->field; sp++)
+ cgetstr(buf, sp->field, &sp->value);
+ for (np = gettynums; np->field; np++) {
+ if (cgetnum(buf, np->field, &n) == -1)
+ np->set = 0;
+ else {
+ np->set = 1;
+ np->value = n;
+ }
+ }
+ for (fp = gettyflags; fp->field; fp++) {
+ if (cgetcap(buf, fp->field, ':') == NULL)
+ fp->set = 0;
+ else {
+ fp->set = 1;
+ fp->value = 1 ^ fp->invrt;
+ }
+ }
+#ifdef DEBUG
+ printf("name=\"%s\", buf=\"%s\"\n", name, buf);
+ for (sp = gettystrs; sp->field; sp++)
+ printf("cgetstr: %s=%s\n", sp->field, sp->value);
+ for (np = gettynums; np->field; np++)
+ printf("cgetnum: %s=%d\n", np->field, np->value);
+ for (fp = gettyflags; fp->field; fp++)
+ printf("cgetflags: %s='%c' set='%c'\n", fp->field,
+ fp->value + '0', fp->set + '0');
+ exit(1);
+#endif /* DEBUG */
+}
+
+void
+gendefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (sp->value)
+ sp->defalt = sp->value;
+ for (np = gettynums; np->field; np++)
+ if (np->set)
+ np->defalt = np->value;
+ for (fp = gettyflags; fp->field; fp++)
+ if (fp->set)
+ fp->defalt = fp->value;
+ else
+ fp->defalt = fp->invrt;
+}
+
+void
+setdefaults(void)
+{
+ struct gettystrs *sp;
+ struct gettynums *np;
+ struct gettyflags *fp;
+
+ for (sp = gettystrs; sp->field; sp++)
+ if (!sp->value)
+ sp->value = sp->defalt;
+ for (np = gettynums; np->field; np++)
+ if (!np->set)
+ np->value = np->defalt;
+ for (fp = gettyflags; fp->field; fp++)
+ if (!fp->set)
+ fp->value = fp->defalt;
+}
+
+static char **
+charnames[] = {
+ &ER, &KL, &IN, &QU, &XN, &XF, &ET, &BK,
+ &SU, &DS, &RP, &FL, &WE, &LN, 0
+};
+
+static char *
+charvars[] = {
+ &tmode.c_cc[VERASE], &tmode.c_cc[VKILL], &tmode.c_cc[VINTR],
+ &tmode.c_cc[VQUIT], &tmode.c_cc[VSTART], &tmode.c_cc[VSTOP],
+ &tmode.c_cc[VEOF], &tmode.c_cc[VEOL], &tmode.c_cc[VSUSP],
+ &tmode.c_cc[VDSUSP], &tmode.c_cc[VREPRINT], &tmode.c_cc[VDISCARD],
+ &tmode.c_cc[VWERASE], &tmode.c_cc[VLNEXT], 0
+};
+
+void
+setchars(void)
+{
+ int i;
+ char *p;
+
+ for (i = 0; charnames[i]; i++) {
+ p = *charnames[i];
+ if (p && *p)
+ *charvars[i] = *p;
+ else
+ *charvars[i] = _POSIX_VDISABLE;
+ }
+}
+
+/* Macros to clear/set/test flags. */
+#define SET(t, f) (t) |= (f)
+#define CLR(t, f) (t) &= ~(f)
+#define ISSET(t, f) ((t) & (f))
+
+void
+setflags(int n)
+{
+ tcflag_t iflag, oflag, cflag, lflag;
+
+#ifdef COMPAT_43
+ switch (n) {
+ case 0:
+ if (F0set) {
+ compatflags(F0);
+ return;
+ }
+ break;
+ case 1:
+ if (F1set) {
+ compatflags(F1);
+ return;
+ }
+ break;
+ default:
+ if (F2set) {
+ compatflags(F2);
+ return;
+ }
+ break;
+ }
+#endif
+
+ switch (n) {
+ case 0:
+ if (C0set && I0set && L0set && O0set) {
+ tmode.c_cflag = C0;
+ tmode.c_iflag = I0;
+ tmode.c_lflag = L0;
+ tmode.c_oflag = O0;
+ return;
+ }
+ break;
+ case 1:
+ if (C1set && I1set && L1set && O1set) {
+ tmode.c_cflag = C1;
+ tmode.c_iflag = I1;
+ tmode.c_lflag = L1;
+ tmode.c_oflag = O1;
+ return;
+ }
+ break;
+ default:
+ if (C2set && I2set && L2set && O2set) {
+ tmode.c_cflag = C2;
+ tmode.c_iflag = I2;
+ tmode.c_lflag = L2;
+ tmode.c_oflag = O2;
+ return;
+ }
+ break;
+ }
+
+ iflag = omode.c_iflag;
+ oflag = omode.c_oflag;
+ cflag = omode.c_cflag;
+ lflag = omode.c_lflag;
+
+ if (NP) {
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ CLR(iflag, ISTRIP|INPCK|IGNPAR);
+ } else if (AP || EP || OP) {
+ CLR(cflag, CSIZE);
+ SET(cflag, CS7|PARENB);
+ SET(iflag, ISTRIP);
+ if (OP && !EP) {
+ SET(iflag, INPCK|IGNPAR);
+ SET(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (EP && !OP) {
+ SET(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ if (AP)
+ CLR(iflag, INPCK);
+ } else if (AP || (EP && OP)) {
+ CLR(iflag, INPCK|IGNPAR);
+ CLR(cflag, PARODD);
+ }
+ } /* else, leave as is */
+
+ if (UC) {
+ SET(iflag, IUCLC);
+ SET(oflag, OLCUC);
+ SET(lflag, XCASE);
+ }
+
+ if (HC)
+ SET(cflag, HUPCL);
+ else
+ CLR(cflag, HUPCL);
+
+ if (MB)
+ SET(cflag, MDMBUF);
+ else
+ CLR(cflag, MDMBUF);
+
+ if (NL) {
+ SET(iflag, ICRNL);
+ SET(oflag, ONLCR|OPOST);
+ } else {
+ CLR(iflag, ICRNL);
+ CLR(oflag, ONLCR);
+ }
+
+ if (!HT)
+ SET(oflag, OXTABS|OPOST);
+ else
+ CLR(oflag, OXTABS);
+
+#ifdef XXX_DELAY
+ SET(f, delaybits());
+#endif
+
+ if (n == 1) { /* read mode flags */
+ if (RW) {
+ iflag = 0;
+ CLR(oflag, OPOST);
+ CLR(cflag, CSIZE|PARENB);
+ SET(cflag, CS8);
+ lflag = 0;
+ } else {
+ CLR(lflag, ICANON);
+ }
+ goto out;
+ }
+
+ if (n == 0)
+ goto out;
+
+#if 0
+ if (CB)
+ SET(f, CRTBS);
+#endif
+
+ if (CE)
+ SET(lflag, ECHOE);
+ else
+ CLR(lflag, ECHOE);
+
+ if (CK)
+ SET(lflag, ECHOKE);
+ else
+ CLR(lflag, ECHOKE);
+
+ if (PE)
+ SET(lflag, ECHOPRT);
+ else
+ CLR(lflag, ECHOPRT);
+
+ if (EC)
+ SET(lflag, ECHO);
+ else
+ CLR(lflag, ECHO);
+
+ if (XC)
+ SET(lflag, ECHOCTL);
+ else
+ CLR(lflag, ECHOCTL);
+
+ if (DX)
+ SET(lflag, IXANY);
+ else
+ CLR(lflag, IXANY);
+
+out:
+ tmode.c_iflag = iflag;
+ tmode.c_oflag = oflag;
+ tmode.c_cflag = cflag;
+ tmode.c_lflag = lflag;
+}
+
+#ifdef COMPAT_43
+/*
+ * Old TTY => termios, snatched from <sys/kern/tty_compat.c>
+ */
+void
+compatflags(long flags)
+{
+ tcflag_t iflag, oflag, cflag, lflag;
+
+ iflag = BRKINT|ICRNL|IMAXBEL|IXON|IXANY;
+ oflag = OPOST|ONLCR|OXTABS;
+ cflag = CREAD;
+ lflag = ICANON|ISIG|IEXTEN;
+
+ if (ISSET(flags, TANDEM))
+ SET(iflag, IXOFF);
+ else
+ CLR(iflag, IXOFF);
+ if (ISSET(flags, ECHO))
+ SET(lflag, ECHO);
+ else
+ CLR(lflag, ECHO);
+ if (ISSET(flags, CRMOD)) {
+ SET(iflag, ICRNL);
+ SET(oflag, ONLCR);
+ } else {
+ CLR(iflag, ICRNL);
+ CLR(oflag, ONLCR);
+ }
+ if (ISSET(flags, XTABS))
+ SET(oflag, OXTABS);
+ else
+ CLR(oflag, OXTABS);
+ if (ISSET(flags, LCASE)) {
+ SET(iflag, IUCLC);
+ SET(oflag, OLCUC);
+ SET(lflag, XCASE);
+ }
+ else {
+ CLR(iflag, IUCLC);
+ CLR(oflag, OLCUC);
+ CLR(lflag, XCASE);
+ }
+
+
+ if (ISSET(flags, RAW)) {
+ iflag &= IXOFF;
+ CLR(lflag, ISIG|ICANON|IEXTEN|XCASE);
+ CLR(cflag, PARENB);
+ } else {
+ SET(iflag, BRKINT|IXON|IMAXBEL);
+ SET(lflag, ISIG|IEXTEN);
+ if (ISSET(iflag, IUCLC) && ISSET(oflag, OLCUC))
+ SET(lflag, XCASE);
+ if (ISSET(flags, CBREAK))
+ CLR(lflag, ICANON);
+ else
+ SET(lflag, ICANON);
+ switch (ISSET(flags, ANYP)) {
+ case 0:
+ CLR(cflag, PARENB);
+ break;
+ case ANYP:
+ SET(cflag, PARENB);
+ CLR(iflag, INPCK);
+ break;
+ case EVENP:
+ SET(cflag, PARENB);
+ SET(iflag, INPCK);
+ CLR(cflag, PARODD);
+ break;
+ case ODDP:
+ SET(cflag, PARENB);
+ SET(iflag, INPCK);
+ SET(cflag, PARODD);
+ break;
+ }
+ }
+
+ /* Nothing we can do with CRTBS. */
+ if (ISSET(flags, PRTERA))
+ SET(lflag, ECHOPRT);
+ else
+ CLR(lflag, ECHOPRT);
+ if (ISSET(flags, CRTERA))
+ SET(lflag, ECHOE);
+ else
+ CLR(lflag, ECHOE);
+ /* Nothing we can do with TILDE. */
+ if (ISSET(flags, MDMBUF))
+ SET(cflag, MDMBUF);
+ else
+ CLR(cflag, MDMBUF);
+ if (ISSET(flags, NOHANG))
+ CLR(cflag, HUPCL);
+ else
+ SET(cflag, HUPCL);
+ if (ISSET(flags, CRTKIL))
+ SET(lflag, ECHOKE);
+ else
+ CLR(lflag, ECHOKE);
+ if (ISSET(flags, CTLECH))
+ SET(lflag, ECHOCTL);
+ else
+ CLR(lflag, ECHOCTL);
+ if (!ISSET(flags, DECCTQ))
+ SET(iflag, IXANY);
+ else
+ CLR(iflag, IXANY);
+ CLR(lflag, TOSTOP|FLUSHO|PENDIN|NOFLSH);
+ SET(lflag, ISSET(flags, TOSTOP|FLUSHO|PENDIN|NOFLSH));
+
+ if (ISSET(flags, RAW|LITOUT|PASS8)) {
+ CLR(cflag, CSIZE);
+ SET(cflag, CS8);
+ if (!ISSET(flags, RAW|PASS8))
+ SET(iflag, ISTRIP);
+ else
+ CLR(iflag, ISTRIP);
+ if (!ISSET(flags, RAW|LITOUT))
+ SET(oflag, OPOST);
+ else
+ CLR(oflag, OPOST);
+ } else {
+ CLR(cflag, CSIZE);
+ SET(cflag, CS7);
+ SET(iflag, ISTRIP);
+ SET(oflag, OPOST);
+ }
+
+ tmode.c_iflag = iflag;
+ tmode.c_oflag = oflag;
+ tmode.c_cflag = cflag;
+ tmode.c_lflag = lflag;
+}
+#endif
+
+#ifdef XXX_DELAY
+struct delayval {
+ unsigned int delay; /* delay in ms */
+ int bits;
+};
+
+/*
+ * below are random guesses, I can't be bothered checking
+ */
+
+struct delayval crdelay[] = {
+ { 1, CR1 },
+ { 2, CR2 },
+ { 3, CR3 },
+ { 83, CR1 },
+ { 166, CR2 },
+ { 0, CR3 },
+};
+
+struct delayval nldelay[] = {
+ { 1, NL1 }, /* special, calculated */
+ { 2, NL2 },
+ { 3, NL3 },
+ { 100, NL2 },
+ { 0, NL3 },
+};
+
+struct delayval bsdelay[] = {
+ { 1, BS1 },
+ { 0, 0 },
+};
+
+struct delayval ffdelay[] = {
+ { 1, FF1 },
+ { 1750, FF1 },
+ { 0, FF1 },
+};
+
+struct delayval tbdelay[] = {
+ { 1, TAB1 },
+ { 2, TAB2 },
+ { 3, XTABS }, /* this is expand tabs */
+ { 100, TAB1 },
+ { 0, TAB2 },
+};
+
+int
+delaybits()
+{
+ int f;
+
+ f = adelay(CD, crdelay);
+ f |= adelay(ND, nldelay);
+ f |= adelay(FD, ffdelay);
+ f |= adelay(TD, tbdelay);
+ f |= adelay(BD, bsdelay);
+ return (f);
+}
+
+int
+adelay(int ms, struct delayval *dp)
+{
+ if (ms == 0)
+ return (0);
+ while (dp->delay && ms > dp->delay)
+ dp++;
+ return (dp->bits);
+}
+#endif
+
+char editedhost[48];
+
+void
+edithost(char *pat)
+{
+ char *host = HN;
+ char *res = editedhost;
+
+ 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)
+ strlcpy(res, host, sizeof editedhost - (res - editedhost));
+ else
+ *res = '\0';
+}
+
+void
+makeenv(char *env[])
+{
+ static char termbuf[128] = "TERM=";
+ char *p, *q;
+ char **ep;
+
+ ep = env;
+ if (TT && *TT) {
+ strlcat(termbuf, TT, sizeof(termbuf));
+ *ep++ = termbuf;
+ }
+ if ((p = EV)) {
+ q = p;
+ while ((q = strchr(q, ','))) {
+ *q++ = '\0';
+ *ep++ = p;
+ p = q;
+ }
+ if (*p)
+ *ep++ = p;
+ }
+ *ep = (char *)0;
+}
+
+/*
+ * This speed select mechanism is written for the Develcon DATASWITCH.
+ * The Develcon sends a string of the form "B{speed}\n" at a predefined
+ * baud rate. This string indicates the user's actual speed.
+ * The routine below returns the terminal type mapped from derived speed.
+ */
+struct portselect {
+ char *ps_baud;
+ char *ps_type;
+} portspeeds[] = {
+ { "B110", "std.110" },
+ { "B134", "std.134" },
+ { "B150", "std.150" },
+ { "B300", "std.300" },
+ { "B600", "std.600" },
+ { "B1200", "std.1200" },
+ { "B2400", "std.2400" },
+ { "B4800", "std.4800" },
+ { "B9600", "std.9600" },
+ { "B19200", "std.19200" },
+ { 0 }
+};
+
+char *
+portselector(void)
+{
+ char c, baud[20], *type = "default";
+ struct portselect *ps;
+ int len;
+
+ alarm(5*60);
+ for (len = 0; len < sizeof (baud) - 1; len++) {
+ if (read(STDIN_FILENO, &c, 1) <= 0)
+ break;
+ c &= 0177;
+ if (c == '\n' || c == '\r')
+ break;
+ if (c == 'B')
+ len = 0; /* in case of leading garbage */
+ baud[len] = c;
+ }
+ baud[len] = '\0';
+ for (ps = portspeeds; ps->ps_baud; ps++)
+ if (strcmp(ps->ps_baud, baud) == 0) {
+ type = ps->ps_type;
+ break;
+ }
+ sleep(2); /* wait for connection to complete */
+ return (type);
+}
+
+/*
+ * This auto-baud speed select mechanism is written for the Micom 600
+ * portselector. Selection is done by looking at how the character '\r'
+ * is garbled at the different speeds.
+ */
+#include <sys/time.h>
+
+char *
+autobaud(void)
+{
+ fd_set rfds;
+ struct timeval timeout;
+ char c, *type = "9600-baud";
+
+ (void)tcflush(0, TCIOFLUSH);
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ if (select(1, &rfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) <= 0)
+ return (type);
+ if (read(STDIN_FILENO, &c, sizeof(char)) != sizeof(char))
+ return (type);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 20;
+ (void) select(0, (fd_set *)NULL, (fd_set *)NULL,
+ (fd_set *)NULL, &timeout);
+ (void)tcflush(0, TCIOFLUSH);
+ switch (c & 0377) {
+
+ case 0200: /* 300-baud */
+ type = "300-baud";
+ break;
+
+ case 0346: /* 1200-baud */
+ type = "1200-baud";
+ break;
+
+ case 015: /* 2400-baud */
+ case 0215:
+ type = "2400-baud";
+ break;
+
+ default: /* 4800-baud */
+ type = "4800-baud";
+ break;
+
+ case 0377: /* 9600-baud */
+ type = "9600-baud";
+ break;
+ }
+ return (type);
+}
--- /dev/null
+.\" $OpenBSD: ttys.5,v 1.11 2008/10/22 22:16:16 mpf Exp $
+.\" Copyright (c) 1985, 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. 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.
+.\"
+.\" from: @(#)ttys.5 8.1 (Berkeley) 6/4/93
+.\"
+.Dd $Mdocdate: October 22 2008 $
+.Dt TTYS 5
+.Os
+.Sh NAME
+.Nm ttys
+.Nd terminal initialization information
+.Sh DESCRIPTION
+The
+.Nm
+file contains information that is used by various routines to initialize
+and control the use of terminal special files.
+This information is read with the
+.Xr getttyent 3
+library routines.
+There is one line in the
+.Nm
+file per special device file.
+Fields are separated by tabs and/or spaces.
+Fields comprised of more than one word should be enclosed in double quotes
+.Pq Ql \&" .
+Blank lines and comments may appear anywhere in the file; comments
+are delimited by hash marks
+.Pq Ql #
+and newlines.
+Any unspecified fields will default to null.
+.Pp
+The first field is the
+name of the terminal special file as it is found in
+.Pa /dev .
+.Pp
+The second field of the file is the command to execute for the line,
+usually
+.Xr getty 8 ,
+which initializes and opens the line, setting the speed, waiting for
+a user name and executing the
+.Xr login 1
+program.
+It can be, however, any desired command, for example
+the start up for a window system terminal emulator or some other
+daemon process, and can contain multiple words if quoted.
+.Pp
+The third field is the type of terminal usually connected to that
+TTY line, normally the one found in the
+.Xr termcap 5
+database file.
+The environment variable
+.Dv TERM
+is initialized with the value by either
+.Xr getty 8
+or
+.Xr login 1 .
+.Pp
+The remaining fields set flags in the
+.Fa ty_status
+entry (see
+.Xr getttyent 3 )
+or specify a window system process that
+.Xr init 8
+will maintain for the terminal line.
+The following is a list of permitted flags for each TTY:
+.Bl -tag -width xxxxxxx
+.It Ar on
+Specify that
+.Xr init 8
+should execute the command given in the second field.
+.It Ar off
+The opposite of on.
+.It Ar secure
+If
+.Ar on
+is also specified, allows users with a UID of 0 to log in on this line.
+If set for the
+.Ar console
+entry, then
+.Xr init 8
+will start a single-user shell without asking for the superuser password.
+.El
+.Pp
+Additionally, the following flags modify the default behavior of
+the terminal line.
+Some of these flags may not be supported by a terminal line driver.
+The flag fields should not be quoted.
+.Bl -tag -width xxxxxxx
+.It Ar local
+Treat the line as if it is locally connected.
+.It Ar rtscts
+Use RTS/CTS hardware flow control, if
+possible.
+.It Ar mdmbuf
+Use DTR/DCD flow control if possible.
+.It Ar softcar
+Ignore hardware carrier on the line.
+.El
+.Pp
+The string
+.Ar window=
+may be followed by a quoted command string which
+.Xr init 8
+will execute
+.Em before
+starting the command specified by the second field.
+.Sh FILES
+.Bl -tag -width /etc/ttys -compact
+.It Pa /etc/ttys
+.El
+.Sh EXAMPLES
+.Bd -literal
+# root login on console at 1200 baud
+console "/usr/libexec/getty std.1200" vt100 on secure
+# dialup at 1200 baud, no root logins
+ttyd0 "/usr/libexec/getty d1200" dialup on # 555-1234
+# Mike's terminal: hp2621
+ttyh0 "/usr/libexec/getty std.9600" hp2621-nl on # 457 Evans
+# John's terminal: vt100
+ttyh1 "/usr/libexec/getty std.9600" vt100 on # 459 Evans
+# terminal emulate/window system
+ttyv0 "/usr/new/xterm -L :0" vs100 on window="/usr/new/Xvs100 0"
+# Network pseudo ttys -- don't enable getty
+ttyp0 none network
+ttyp1 none network off
+.Ed
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr getttyent 3 ,
+.Xr ttyslot 3 ,
+.Xr gettytab 5 ,
+.Xr termcap 5 ,
+.Xr getty 8 ,
+.Xr init 8 ,
+.Xr ttyflags 8
+.Sh HISTORY
+A
+.Nm
+file appeared in
+.At v6 .
--- /dev/null
+Credits go to (I've probably forgot someone - please don't hesitate
+to tell me!) for helping making Pidentd what it is:
+
+Casper Dik <casper@fwi.uva.nl>, Math & CS Faculty, U. of Amsterdam, NL
+ (Added support for SunOS 5 (Solaris 2))
+
+Dave Shield <D.T.Shield@compsci.liverpool.ac.uk>, CS Dept. Liverpool U., UK
+ (Added support for HP9K HPUX 8.*)
+
+Jan L. Peterson <jlp@phred.math.byu.edu>, Math Dept. BYU, USA
+ (Added support for MIPS RISC/os and fixed a few other things)
+
+Fletcher Mattox <fletcher@cs.utexas.edu>, University of Texas, USA
+ (Added support for HP9K HP-UX 7.*)
+
+Mark Monnin <mgrmem@nextwork.rose-hulman.edu>, Rose-Hulman Inst. of Tech, USA
+ (Added support for DEC Ultrix 4.*)
+
+Simon Leinen <simon@lia.di.epfl.ch>, Switzerland
+ (Added support for Silicon Graphics IRIX 4.*)
+
+Frank Maas <maas@dutiws.tudelft.nl>, Delft Univ. of Technology, The Netherlands
+ (Added support for Sequent Dynix 3.*)
+
+Andrew Herbert <andrewh@molly.cs.monash.edu.au>, Monash University, Australia
+ (Added support for System V/Release 4)
+
+David Bennet <ddt@gu.uwa.edu.au>, Australia
+ (Added support for 386BSD)
+
+Fishman M. Shmuel <fms@ccgr.technion.ac.il>, Technion Inst. of Tech., Israel
+ (Added support for Convex & 4.3BSDtahoe (then heavily hacked by me))
+
+Bradley E. Smith <brad@bradley.bradley.edu>, Bradley University, USA
+ (Added support for AT&T's own version of SVR4)
+
+RenE J.V. Bertin <bertin@neuretD.biol.ruu.nl>, Uni. of Utrecht, The Netherlands
+ (Added support for Apple A/UX 2.*)
+
+Douglas Lee Schales <Doug.Schales@sc.tamu.edu>, Texas A&M University, USA
+ (Added support for Cray UNICOS 6.*)
+
+Don Hazlewood <haz@dali.math.swt.edu>, SW Texas State U., USA
+ (Added support for A/UX 3.*)
+
+ Nigel Metheringham <nigelm@ohm.york.ac.uk>, University of York, UK
+ (Added support for NeXT, SunOS 3.*, corrections for MIPS)
+
+----------------------------------------------------------------------------
+Peter Eriksson <pen@lysator.liu.se>, Lysator, Linkoping University, Sweden.
+ (Original code for Sun SunOS 4.* and Sequent Dynix 2.*)
+
--- /dev/null
+/CREDITS/1.1.1.1/Wed Oct 18 08:43:17 1995//
+/Makefile/1.8/Sat Jun 28 01:05:21 2003//
+/identd.h/1.17/Thu Sep 16 08:25:05 2004//
+/openbsd.c/1.21/Fri Nov 10 20:44:07 2006//
+/parse.c/1.46/Sun Apr 13 00:22:17 2008//
+/identd.8/1.28/Sun Jun 27 18:29:54 2010//
+/identd.c/1.49/Sun Jun 27 18:29:54 2010//
+D
--- /dev/null
+src/libexec/identd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.8 2003/06/28 01:05:21 deraadt Exp $
+
+PROG= identd
+SRCS= identd.c openbsd.c parse.c
+MAN= identd.8
+
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: identd.8,v 1.28 2010/06/06 07:05:40 jmc Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.\" @(#)identd.8 1.9 92/02/11 Lysator
+.\" Copyright (c) 1992 Peter Eriksson, Lysator, Linkoping University.
+.\" This software has been released into the public domain.
+.\"
+.Dd $Mdocdate: June 6 2010 $
+.Dt IDENTD 8
+.Os
+.Sh NAME
+.Nm identd
+.Nd TCP/IP IDENT protocol server
+.Sh SYNOPSIS
+.Nm identd
+.Bk -words
+.Op Fl 46deHhlmNnoUv
+.Op Fl b | i | w
+.Op Fl a Ar address
+.Op Fl c Ar charset
+.Op Fl g Ar gid
+.Op Fl p Ar port
+.Op Fl t Ar seconds
+.Op Fl u Ar uid
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a server which implements the TCP/IP proposed standard
+IDENT user identification protocol
+as specified in the RFC 1413 document.
+.Pp
+.Nm
+operates by looking up specific TCP/IP
+connections and returning the user name of the
+process owning the connection.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 4
+When
+.Fl b
+is specified, forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+When
+.Fl b
+is specified, forces
+.Nm
+to use IPv6 addresses only.
+.It Fl a Ar address
+Specify a local IP address in dotted quad format
+to bind the listen socket to if running as a stand-alone daemon.
+By default the daemon listens on all local IP addresses.
+.It Fl b
+Specify operation as a stand-alone daemon.
+.It Fl c Ar charset
+Specify an optional character set designator to be included in replies.
+.Ar charset
+should be a valid character set as described in the
+MIME RFC in upper case characters.
+.It Fl d
+This flag enables some debugging code that normally should NOT
+be enabled since that breaks the protocol and may reveal information
+that should not be available to outsiders.
+.It Fl e
+Always return
+.Dq UNKNOWN-ERROR
+instead of the
+.Dq NO-USER
+or
+.Dq INVALID-PORT
+errors.
+.It Fl g Ar gid
+Specify a group ID number or group name which the
+.Nm
+server should
+switch to after binding itself to the
+TCP/IP port if running as a stand-alone daemon.
+.It Fl H
+Hide information about non existing users (e.g., connections through NAT) as
+well as existing users.
+Implies
+.Fl h .
+.It Fl h
+Hide the actual information about the user by providing an opaque
+token instead.
+This token is entered into the local system logs
+so that the administrator can later discover who the real user was.
+.It Fl i
+Tells
+.Nm identd
+to run as a process started from
+.Xr inetd 8
+with the "nowait" option in the
+.Pa /etc/inetd.conf
+file.
+Use of this mode will make
+.Xr inetd 8
+start one
+.Nm
+daemon for each connection request.
+This is the default mode of operation.
+.It Fl l
+Use
+.Xr syslogd 8
+for logging purposes.
+.It Fl m
+Allow multiple requests to be processed per session.
+Each request is specified one per line and the responses will be returned
+one per line.
+The connection will not be closed until the client closes its end of
+the connection.
+PLEASE NOTE THAT THIS MODE VIOLATES THE PROTOCOL SPECIFICATION AS
+IT CURRENTLY STANDS.
+.It Fl N
+When replying with a user name or ID, first
+check for a file
+.Pa .noident
+in the user's home directory.
+If this file is accessible, return
+.Dq HIDDEN-USER
+instead of the normal USERID response.
+.It Fl n
+Always return UID numbers instead of usernames.
+.It Fl o
+Do not reveal operating system type;
+always return
+.Dq OTHER
+instead.
+.It Fl p Ar port
+Specify an alternative port number or service name
+on which to listen when running as a stand-alone daemon.
+Default is "auth" (113).
+.It Fl t Ar seconds
+Specifies an idle timeout in seconds where a daemon running in
+"wait" mode will timeout and exit.
+The default is no timeout.
+.It Fl U
+When replying with a user name or ID, first
+check for a file
+.Pa .ident
+in the user's home directory.
+If this file is accessible, return
+at most 20 characters of the first line of the file
+instead of the normal USERID response.
+.It Fl u Ar uid
+Specify a user ID number or user name which the
+.Nm identd
+server should
+switch to after binding itself to the
+TCP/IP port if running as a stand-alone daemon.
+.Nm
+runs as user
+.Qq _identd
+by default and falls back to
+.Qq nobody
+if the
+.Qq _identd
+user does not exist.
+.It Fl v
+Log every request to syslog if
+.Fl l
+above is specified.
+.It Fl w
+Tells
+.Nm identd
+to run as a process started from
+.Xr inetd 8
+with the "wait" option in the
+.Pa /etc/inetd.conf
+file.
+This mode of operation will start a copy of
+.Nm
+at the first connection request and then
+.Nm
+will handle subsequent requests.
+Previous versions listed this as the preferred mode of
+operation due to the initial overhead of parsing the kernel nlist.
+This version does not use kmem or nlist parsing, so this reasoning
+is no longer valid.
+.El
+.Sh SEE ALSO
+.Xr inetd.conf 5
+.Sh NOTES
+.Nm
+uses the
+.Li LOG_DAEMON
+.Xr syslogd 8
+facility to log messages.
+.Pp
+Unlike previous versions of
+.Nm identd ,
+this version uses
+.Xr sysctl 3
+to obtain information from the kernel instead of parsing kmem.
+This version does not require privilege beyond what is needed to bind
+the listen port if running as a stand-alone daemon.
+.Sh BUGS
+Since
+.Nm identd
+should typically not be run as a privileged user or group,
+.Pa .ident
+files for use when running with the
+.Fl U
+flag will need to be world accessible.
+The same applies for
+.Pa .noident
+files when running with the
+.Fl N
+flag.
--- /dev/null
+/* $OpenBSD: identd.c,v 1.49 2010/06/06 07:05:40 jmc Exp $ */
+
+/*
+ * This program is in the public domain and may be used freely by anyone
+ * who wants to.
+ *
+ * Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "identd.h"
+
+extern char *__progname;
+
+int af = PF_UNSPEC;
+
+int verbose_flag;
+int debug_flag;
+int syslog_flag;
+int multi_flag;
+int unknown_flag;
+int number_flag;
+int noident_flag;
+int userident_flag;
+int token_flag;
+int no_user_token_flag;
+
+int lport;
+int fport;
+
+const char *opsys_name = "UNIX";
+const char *charset_sep = "";
+char *charset_name = "";
+
+static pid_t child_pid;
+
+void usage(void);
+void sigchld(int);
+char * gethost(struct sockaddr_storage *ss);
+
+void
+usage(void)
+{
+ syslog(LOG_ERR,
+ "usage: %s [-46deHhlmNnoUv] [-b | -i | -w] [-a address] [-c charset] "
+ "[-g gid] [-p port] [-t seconds] [-u uid]", __progname);
+ exit(2);
+}
+
+/*
+ * Return the name of the connecting host, or the IP number as a string.
+ */
+char *
+gethost4_addr(struct in_addr *addr)
+{
+ struct hostent *hp;
+
+ hp = gethostbyaddr(addr, sizeof(struct in_addr), AF_INET);
+ if (hp)
+ return hp->h_name;
+ return inet_ntoa(*addr);
+}
+
+char *
+gethost(struct sockaddr_storage *ss)
+{
+ if (ss->ss_family == AF_INET6)
+ return (gethost6((struct sockaddr_in6 *)ss));
+ return (gethost4((struct sockaddr_in *)ss));
+}
+
+char *
+gethost4(struct sockaddr_in *sin)
+{
+ struct hostent *hp;
+
+ hp = gethostbyaddr(&sin->sin_addr, sizeof(struct in_addr), AF_INET);
+ if (hp)
+ return hp->h_name;
+ return inet_ntoa(sin->sin_addr);
+}
+
+/*
+ * Return the name of the connecting host, or the IP number as a string.
+ */
+char *
+gethost6(struct sockaddr_in6 *addr)
+{
+ static char hbuf[2][NI_MAXHOST];
+ const int niflags = NI_NUMERICHOST;
+ static int bb = 0;
+ int err;
+
+ bb = (bb+1)%2;
+ err = getnameinfo((struct sockaddr *)addr, addr->sin6_len,
+ hbuf[bb], sizeof(hbuf[bb]), NULL, 0, niflags);
+ if (err != 0) {
+ syslog(LOG_ERR, "getnameinfo failed (%s)", gai_strerror(err));
+ strlcpy(hbuf[bb], "UNKNOWN", sizeof(hbuf[bb]));
+ }
+ return(hbuf[bb]);
+}
+
+volatile sig_atomic_t alarm_fired;
+
+/*
+ * Exit cleanly after our time's up.
+ */
+/* ARGSUSED */
+static void
+alarm_handler(int notused)
+{
+ alarm_fired = 1;
+}
+
+/*
+ * Main entry point into this daemon
+ */
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_storage sa, sa2;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct in_addr laddr, faddr;
+ struct in6_addr laddr6, faddr6;
+ struct passwd *pwd;
+ struct group *grp;
+ struct pollfd *pfds = NULL;
+ int i, n = 0, background_flag = 0, timeout = 0, ch;
+ char *portno = "auth";
+ char *bind_address = NULL;
+ uid_t set_uid = 0;
+ gid_t set_gid = 0;
+ extern char *optarg;
+ socklen_t len;
+ const char *errstr;
+
+ openlog(__progname, LOG_PID, LOG_DAEMON);
+
+ /* runs as _identd if possible, fallback to "nobody" */
+ if (getuid() == 0) {
+ if ((pwd = getpwnam(DEFAULT_UID)) == NULL)
+ error("no such user %s", DEFAULT_UID);
+ set_uid = pwd->pw_uid;
+ set_gid = pwd->pw_gid;
+ }
+
+ /*
+ * Parse the command line arguments
+ */
+ while ((ch = getopt(argc, argv, "46hHbwit:p:a:u:g:c:loenvdmNU")) != -1) {
+ switch (ch) {
+ case '4':
+ af = AF_INET;
+ break;
+ case '6':
+ af = AF_INET6;
+ break;
+ case 'h':
+ token_flag = 1;
+ break;
+ case 'H':
+ no_user_token_flag = token_flag = 1;
+ break;
+ case 'b': /* Start as standalone daemon */
+ background_flag = 1;
+ break;
+ case 'w': /* Start from Inetd, wait mode */
+ background_flag = 2;
+ break;
+ case 'i': /* Start from Inetd, nowait mode */
+ background_flag = 0;
+ break;
+ case 't':
+ timeout = strtonum(optarg, 0, 100000000, &errstr);
+ if (errstr)
+ error("timeout is %s: %s", errstr, optarg);
+ break;
+ case 'p':
+ portno = optarg;
+ break;
+ case 'a':
+ bind_address = optarg;
+ break;
+ case 'u':
+ pwd = getpwnam(optarg);
+ if (pwd == NULL && isdigit(optarg[0])) {
+ set_uid = atoi(optarg);
+ if ((pwd = getpwuid(set_uid)) == NULL)
+ break;
+ }
+ if (pwd == NULL)
+ error("no such user (%s) for -u option",
+ optarg);
+ else {
+ set_uid = pwd->pw_uid;
+ if (set_gid == 0)
+ set_gid = pwd->pw_gid;
+ }
+ break;
+ case 'g':
+ grp = getgrnam(optarg);
+ if (grp == NULL && isdigit(optarg[0])) {
+ set_gid = atoi(optarg);
+ break;
+ }
+ grp = getgrnam(optarg);
+ if (!grp)
+ error("no such group (%s) for -g option", optarg);
+ else
+ set_gid = grp->gr_gid;
+ break;
+ case 'c':
+ charset_name = optarg;
+ charset_sep = " , ";
+ break;
+ case 'l': /* Use the Syslog daemon for logging */
+ syslog_flag++;
+ break;
+ case 'o':
+ opsys_name = "OTHER";
+ break;
+ case 'e':
+ unknown_flag = 1;
+ break;
+ case 'n':
+ number_flag = 1;
+ break;
+ case 'v': /* Be verbose */
+ verbose_flag++;
+ break;
+ case 'd': /* Enable debugging */
+ debug_flag++;
+ break;
+ case 'm': /* Enable multiline queries */
+ multi_flag++;
+ break;
+ case 'N': /* Enable users ".noident" files */
+ noident_flag++;
+ break;
+ case 'U': /* Enable user ".ident" files */
+ userident_flag++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /*
+ * Do the special handling needed for the "-b" flag
+ */
+ if (background_flag == 1) {
+ struct addrinfo hints, *res, *res0;
+ int true = 1;
+
+ if (daemon(0, 0) != 0)
+ exit(0);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = af;
+ hints.ai_flags = AI_PASSIVE;
+ if (getaddrinfo(bind_address, portno, &hints, &res0) != 0)
+ error("main: getaddrinfo");
+
+ i = 0;
+ for (res = res0; res; res = res->ai_next)
+ i++;
+
+ pfds = calloc(i, sizeof(pfds[0]));
+ if (!pfds) {
+ freeaddrinfo(res0);
+ error("main: calloc");
+ }
+
+ i = 0;
+ for (res = res0; res; res = res->ai_next) {
+ if ((pfds[i].fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+
+ if (setsockopt(pfds[i].fd, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&true, sizeof(true))) {
+ close(pfds[i].fd);
+ continue;
+ }
+
+#ifdef IPV6_V6ONLY
+ if (res->ai_family == AF_INET6)
+ (void)setsockopt(pfds[i].fd, IPPROTO_IPV6,
+ IPV6_V6ONLY, (void *)&true, sizeof(true));
+#endif
+
+ if (bind(pfds[i].fd, res->ai_addr, res->ai_addrlen)) {
+ close(pfds[i].fd);
+ continue;
+ }
+
+ if (listen(pfds[i].fd, 3)) {
+ close(pfds[i].fd);
+ continue;
+ }
+
+ pfds[i].events = POLLIN;
+ i++;
+ }
+ freeaddrinfo(res0);
+
+ if (i == 0)
+ error("main: socket");
+
+ n = i;
+ }
+
+ /*
+ * Do the special handling needed for the "-w" flag
+ */
+ if (background_flag == 2) {
+ pfds = calloc(1, sizeof(pfds[0]));
+ if (!pfds)
+ error("main: calloc");
+
+ pfds[0].fd = 0;
+ pfds[0].events = POLLIN;
+ n = 1;
+ }
+
+ if (set_gid) {
+ if (setegid(set_gid) == -1)
+ error("main: setegid");
+ if (setgid(set_gid) == -1)
+ error("main: setgid");
+ }
+ if (set_uid) {
+ if (seteuid(set_uid) == -1)
+ error("main: seteuid");
+ if (setuid(set_uid) == -1)
+ error("main: setuid");
+ }
+
+ /*
+ * Do some special handling if the "-b" or "-w" flags are used
+ */
+ if (background_flag) {
+ int fd = 0;
+
+ signal(SIGCHLD, sigchld);
+
+ /*
+ * Loop and dispatch client handling processes
+ */
+ do {
+ /*
+ * Terminate if we've been idle for 'timeout' seconds
+ */
+ if (background_flag == 2 && timeout) {
+ signal(SIGALRM, alarm_handler);
+ alarm(timeout);
+ }
+
+ /*
+ * Wait for a connection request to occur.
+ * Ignore EINTR (Interrupted System Call).
+ */
+ do {
+ if (alarm_fired) {
+ if (syslog_flag)
+ syslog(LOG_DEBUG,
+ "SIGALRM triggered, exiting");
+ exit(0);
+ }
+
+ if (timeout)
+ i = poll(pfds, n, timeout * 1000);
+ else
+ i = poll(pfds, n, INFTIM);
+ } while (i < 0 && errno == EINTR);
+
+ /*
+ * An error occurred in poll? Just die
+ */
+ if (i < 0)
+ error("main: poll");
+
+ /*
+ * Timeout limit reached. Exit nicely
+ */
+ if (i == 0)
+ exit(0);
+
+ /*
+ * Disable the alarm timeout
+ */
+ alarm(0);
+
+ for (i = 0; i < n; i++) {
+ if ((pfds[i].revents & POLLIN) == 0)
+ continue;
+
+ /*
+ * Accept the new client
+ */
+ fd = accept(pfds[i].fd, NULL, NULL);
+ if (fd == -1)
+ error("main: accept. errno = %d", errno);
+
+ /*
+ * Fork a child, parent continues
+ */
+ child_pid = fork();
+ if (child_pid == 0)
+ break;
+
+ close(fd);
+ }
+ } while (child_pid != 0);
+
+ /*
+ * We are now in child, the parent has returned to "do" above.
+ */
+ if (dup2(fd, 0) == -1)
+ error("main: dup2: failed fd 0");
+
+ if (dup2(fd, 1) == -1)
+ error("main: dup2: failed fd 1");
+
+ if (dup2(fd, 2) == -1)
+ error("main: dup2: failed fd 2");
+ }
+
+ /*
+ * Get foreign internet address
+ */
+ len = sizeof(sa);
+ if (getpeername(0, (struct sockaddr *) &sa, &len) == -1) {
+ /*
+ * A user has tried to start us from the command line or
+ * the network link died, in which case this message won't
+ * reach to other end anyway, so lets give the poor user some
+ * errors.
+ */
+ perror("identd: getpeername()");
+ exit(1);
+ }
+ if (sa.ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)&sa;
+ faddr6 = sin6->sin6_addr;
+ } else {
+ sin = (struct sockaddr_in *)&sa;
+ faddr = sin->sin_addr;
+ }
+
+ /*
+ * Open the connection to the Syslog daemon if requested
+ */
+ if (syslog_flag)
+ syslog(LOG_INFO, "Connection from %s", gethost(&sa));
+
+ /*
+ * Get local internet address
+ */
+ len = sizeof(sa2);
+ if (getsockname(0, (struct sockaddr *) &sa2, &len) == -1) {
+ /*
+ * We can just die here, because if this fails then the
+ * network has died and we haven't got anyone to return
+ * errors to.
+ */
+ exit(1);
+ }
+ /* are we v4 or v6? */
+ if (sa2.ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)&sa2;
+ laddr6 = sin6->sin6_addr;
+ /*
+ * Get the local/foreign port pair from the luser
+ */
+ parse6(STDIN_FILENO, (struct sockaddr_in6 *)&sa2,
+ (struct sockaddr_in6 *)&sa);
+ } else {
+ sin = (struct sockaddr_in *)&sa2;
+ laddr = sin->sin_addr;
+ /*
+ * Get the local/foreign port pair from the luser
+ */
+ parse(STDIN_FILENO, &laddr, &faddr);
+ }
+
+ exit(0);
+}
+
+void
+error(char *fmt, ...)
+{
+ va_list ap, ap2;
+
+ va_start(ap, fmt);
+
+ if (syslog_flag) {
+ va_copy(ap2, ap);
+ vsyslog(LOG_ERR, fmt, ap2);
+ va_end(ap2);
+ }
+ if (debug_flag) {
+ fprintf(stderr, "%d , %d : ERROR : X-DBG : ", lport, fport);
+ vfprintf(stderr, fmt, ap);
+ perror(": ");
+ } else
+ printf("%d , %d : ERROR : UNKNOWN-ERROR\r\n", lport, fport);
+ va_end(ap);
+ exit(1);
+}
+
+void
+sigchld(int signo)
+{
+ pid_t pid;
+
+ do {
+ pid = waitpid(-1, NULL, WNOHANG);
+ } while (pid > 0 || (pid == -1 && errno == EINTR));
+}
--- /dev/null
+/* $OpenBSD: identd.h,v 1.17 2004/09/16 08:25:05 deraadt Exp $*/
+
+/*
+**
+** identd.h Common variables for the Pidentd daemon
+**
+** This program is in the public domain and may be used freely by anyone
+** who wants to.
+**
+** Last update: 6 Dec 1992
+**
+** Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
+*/
+
+#ifndef __IDENTD_H__
+#define __IDENTD_H__
+
+#define DEFAULT_UID "_identd"
+
+extern int verbose_flag;
+extern int debug_flag;
+extern int syslog_flag;
+extern int multi_flag;
+extern int unknown_flag;
+extern int number_flag;
+extern int noident_flag;
+extern int token_flag;
+extern int no_user_token_flag;
+extern int userident_flag;
+
+extern const char *opsys_name;
+extern const char *charset_sep;
+extern char *charset_name;
+
+extern int lport;
+extern int fport;
+
+int parse(int, struct in_addr *, struct in_addr *);
+int parse6(int, struct sockaddr_in6 *, struct sockaddr_in6 *);
+char *gethost4(struct sockaddr_in *);
+char *gethost4_addr(struct in_addr *);
+char *gethost6(struct sockaddr_in6 *);
+int k_getuid(struct in_addr *, int, struct in_addr *, int, uid_t *);
+int k_getuid6(struct sockaddr_in6 *, int, struct sockaddr_in6 *,
+ int, uid_t *);
+void error(char *fmt, ...);
+
+#endif
--- /dev/null
+/usr/obj/libexec/identd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: openbsd.c,v 1.21 2006/11/10 20:44:07 mk Exp $ */
+
+/*
+ * This program is in the public domain and may be used freely by anyone
+ * who wants to.
+ *
+ * Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
+ *
+ * This version eliminates the kmem search in favour of a kernel sysctl to
+ * get the user id associated with a connection - Bob Beck <beck@obtuse.com>
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/tcp.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+
+#include <arpa/inet.h>
+
+#include "identd.h"
+
+/*
+ * Return the user number for the connection owner
+ */
+int
+k_getuid(struct in_addr *faddr, int fport, struct in_addr *laddr,
+ int lport, uid_t *uid)
+{
+ int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
+ struct sockaddr_in *fin, *lin;
+ struct tcp_ident_mapping tir;
+ int err = 0;
+ size_t i;
+
+ memset(&tir, 0, sizeof (tir));
+ tir.faddr.ss_len = (sizeof (struct sockaddr_storage) & 0xff);
+ tir.laddr.ss_len = (sizeof (struct sockaddr_storage) &0xff);
+ tir.faddr.ss_family = AF_INET;
+ tir.laddr.ss_family = AF_INET;
+ fin = (struct sockaddr_in *) &tir.faddr;
+ lin = (struct sockaddr_in *) &tir.laddr;
+
+ memcpy(&fin->sin_addr, faddr, sizeof (struct in_addr));
+ memcpy(&lin->sin_addr, laddr, sizeof (struct in_addr));
+ fin->sin_port = fport;
+ lin->sin_port = lport;
+ i = sizeof (tir);
+ err = sysctl(mib, sizeof (mib) / sizeof (int), &tir, &i, NULL, 0);
+ if (!err && tir.ruid != -1) {
+ *uid = tir.ruid;
+ return (0);
+ }
+ if (err == -1)
+ syslog(LOG_DEBUG, "sysctl failed (%m)");
+
+ return (-1);
+}
+
+/*
+ * Return the user number for the connection owner
+ * New minty IPv6 version.
+ */
+int
+k_getuid6(struct sockaddr_in6 *faddr, int fport, struct sockaddr_in6 *laddr,
+ int lport, uid_t *uid)
+{
+ int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_IDENT };
+ struct sockaddr_in6 *fin, *lin;
+ struct tcp_ident_mapping tir;
+ int err = 0;
+ size_t i;
+
+ memset(&tir, 0, sizeof (tir));
+ fin = (struct sockaddr_in6 *) &tir.faddr;
+ lin = (struct sockaddr_in6 *) &tir.laddr;
+
+ if (faddr->sin6_len > sizeof(tir.faddr))
+ return -1;
+ memcpy(fin, faddr, faddr->sin6_len);
+ if (laddr->sin6_len > sizeof(tir.laddr))
+ return -1;
+ memcpy(lin, laddr, laddr->sin6_len);
+ fin->sin6_port = fport;
+ lin->sin6_port = lport;
+ i = sizeof (tir);
+ err = sysctl(mib, sizeof (mib) / sizeof (int), &tir, &i, NULL, 0);
+ if (!err && tir.ruid != -1) {
+ *uid = tir.ruid;
+ return (0);
+ }
+ if (err == -1)
+ syslog(LOG_DEBUG, "sysctl failed (%m)");
+
+ return (-1);
+}
--- /dev/null
+/* $OpenBSD: parse.c,v 1.46 2008/04/13 00:22:17 djm Exp $ */
+
+/*
+ * This program is in the public domain and may be used freely by anyone
+ * who wants to.
+ *
+ * Please send bug fixes/bug reports to: Peter Eriksson <pen@lysator.liu.se>
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "identd.h"
+
+#define IO_TIMEOUT 30 /* Timeout I/O operations after N seconds */
+
+int check_noident(char *);
+ssize_t timed_read(int, void *, size_t, time_t);
+ssize_t timed_write(int, const void *, size_t, time_t);
+int getuserident(char *homedir, char *buf, int len);
+void gentoken(char *, int);
+
+/*
+ * A small routine to check for the existence of the ".noident"
+ * file in a users home directory.
+ */
+int
+check_noident(char *homedir)
+{
+ char path[MAXPATHLEN];
+ struct stat st;
+ int n;
+
+ if (!homedir)
+ return 0;
+ if ((n = snprintf(path, sizeof(path), "%s/.noident", homedir))
+ >= sizeof(path) || n < 0)
+ return 0;
+ if (stat(path, &st) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * A small routine to check for the existence of the ".ident"
+ * file in a users home directory, and return its contents.
+ */
+int
+getuserident(char *homedir, char *buf, int len)
+{
+ char path[MAXPATHLEN];
+ int fd, nread, n;
+ struct stat st;
+
+ if (len == 0)
+ return 0;
+ if (!homedir)
+ return 0;
+ if ((n = snprintf(path, sizeof path, "%s/.ident", homedir))
+ >= sizeof(path) || n < 0)
+ return 0;
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < 0)
+ return 0;
+ if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode)) {
+ close(fd);
+ return 0;
+ }
+
+ if ((nread = read(fd, buf, len - 1)) <= 0) {
+ close(fd);
+ return 0;
+ }
+ buf[nread] = '\0';
+
+ /* remove illegal characters */
+ buf[strcspn(buf, "\r\n")] = '\0';
+
+ close(fd);
+ return 1;
+}
+
+static char token0cnv[] = "abcdefghijklmnopqrstuvwxyz";
+static char tokencnv[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+void
+gentoken(char *buf, int len)
+{
+ char *p;
+
+ if (len == 0)
+ return;
+ for (p = buf; len > 1; p++, len--) {
+ if (p == buf)
+ *p = token0cnv[arc4random_uniform(sizeof(token0cnv)-1)];
+ else
+ *p = tokencnv[arc4random_uniform(sizeof(tokencnv)-1)];
+ }
+ *p = '\0';
+}
+
+/*
+ * Returns 0 on timeout, -1 on error, #bytes read on success.
+ */
+ssize_t
+timed_read(int fd, void *buf, size_t siz, time_t timeout)
+{
+ struct timeval tv, start, after, duration, tmp;
+ int err, tot = 0, i, r;
+ struct pollfd rfd[1];
+ char *p = buf;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ while (1) {
+ rfd[0].fd = fd;
+ rfd[0].events = POLLIN;
+ rfd[0].revents = 0;
+
+ gettimeofday(&start, NULL);
+ if ((err = poll(rfd, 1, tv.tv_sec * 1000 +
+ tv.tv_usec / 1000)) <= 0)
+ return err;
+ r = read(fd, p, siz - tot);
+ if (r == -1 || r == 0)
+ return (r);
+ for (i = 0; i < r; i++)
+ if (p[i] == '\r' || p[i] == '\n') {
+ tot += r;
+ return (tot);
+ }
+ gettimeofday(&after, NULL);
+ timersub(&start, &after, &duration);
+ timersub(&tv, &duration, &tmp);
+ tv = tmp;
+ if (tv.tv_sec < 0 || !timerisset(&tv))
+ return (tot);
+ tot += r;
+ p += r;
+ }
+}
+
+/*
+ * Returns 0 on timeout, -1 on error, #bytes read on success.
+ */
+ssize_t
+timed_write(int fd, const void *buf, size_t siz, time_t timeout)
+{
+ struct pollfd wfd[2];
+ struct timeval tv;
+ int err;
+
+ wfd[0].fd = fd;
+ wfd[0].events = POLLOUT;
+ wfd[0].revents = 0;
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ if ((err = poll(wfd, 1, tv.tv_sec * 1000 +
+ tv.tv_usec / 1000)) <= 0)
+ return err;
+ return (write(fd, buf, siz));
+}
+
+int
+parse(int fd, struct in_addr *laddr, struct in_addr *faddr)
+{
+ char token[21], buf[BUFSIZ], *p;
+ struct in_addr laddr2, faddr2;
+ struct passwd *pw;
+ uid_t uid;
+ int n;
+
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, "In function parse(), from %s to %s",
+ gethost4_addr(faddr), gethost4_addr(laddr));
+
+ faddr2 = *faddr;
+ laddr2 = *laddr;
+ lport = fport = 0;
+
+ /* Read query from client */
+ if ((n = timed_read(fd, buf, sizeof(buf) - 1, IO_TIMEOUT)) <= 0) {
+ if (syslog_flag)
+ syslog(LOG_NOTICE,
+ n ? "read from %s: %m" : "read from %s: EOF",
+ gethost4_addr(faddr));
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : ERROR : UNKNOWN-ERROR\r\n", lport, fport))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ buf[n] = '\0';
+
+ /* Pull out local and remote ports */
+ p = buf;
+ while (isspace(*p))
+ p++;
+ if ((p = strtok(p, " \t,"))) {
+ lport = atoi(p);
+ if ((p = strtok(NULL, " \t,")))
+ fport = atoi(p);
+ }
+
+ if (lport < 1 || lport > 65535 || fport < 1 || fport > 65535) {
+ if (syslog_flag)
+ syslog(LOG_NOTICE,
+ "scanf: invalid-port(s): %d , %d from %s",
+ lport, fport, gethost4_addr(faddr));
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : ERROR : %s\r\n",
+ lport, fport, unknown_flag ? "UNKNOWN-ERROR" :
+ "INVALID-PORT")) >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (syslog_flag && verbose_flag)
+ syslog(LOG_NOTICE, "request for (%d,%d) from %s",
+ lport, fport, gethost4_addr(faddr));
+
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, " After fscanf(), before k_getuid()");
+
+ /*
+ * Next - get the specific TCP connection and return the
+ * uid - user number.
+ */
+ if (k_getuid(&faddr2, htons(fport), laddr, htons(lport), &uid) == -1) {
+ if (no_user_token_flag) {
+ gentoken(token, sizeof token);
+ syslog(LOG_NOTICE, "token %s == NO USER", token);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n &&
+ syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m",
+ gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (syslog_flag)
+ syslog(LOG_DEBUG, "Returning: %d , %d : NO-USER",
+ lport, fport);
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : ERROR : %s\r\n",
+ lport, fport, unknown_flag ? "UNKNOWN-ERROR" : "NO-USER"))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, " After k_getuid(), before getpwuid()");
+
+ pw = getpwuid(uid);
+ if (!pw) {
+ if (syslog_flag)
+ syslog(LOG_WARNING,
+ "getpwuid() could not map uid (%u) to name",
+ uid);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%u\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, uid))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (syslog_flag)
+ syslog(LOG_DEBUG, "Successful lookup: %d , %d : %s",
+ lport, fport, pw->pw_name);
+
+ if (noident_flag && check_noident(pw->pw_dir)) {
+ if (syslog_flag && verbose_flag)
+ syslog(LOG_NOTICE,
+ "user %s requested HIDDEN-USER for host %s: %d, %d",
+ pw->pw_name, gethost4_addr(faddr), lport, fport);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : ERROR : HIDDEN-USER\r\n", lport, fport))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (userident_flag && getuserident(pw->pw_dir, token, sizeof token)) {
+ syslog(LOG_NOTICE, "token \"%s\" == uid %u (%s)",
+ token, uid, pw->pw_name);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (token_flag) {
+ gentoken(token, sizeof token);
+ syslog(LOG_NOTICE, "token %s == uid %u (%s)", token, uid,
+ pw->pw_name);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (number_flag) {
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%u\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, uid))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : USERID : %s%s%s :%s\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, pw->pw_name))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost4_addr(faddr));
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Parse, a-la IPv6 */
+int
+parse6(int fd, struct sockaddr_in6 *laddr, struct sockaddr_in6 *faddr)
+{
+ char token[21], buf[BUFSIZ], *p;
+ struct sockaddr_in6 laddr2, faddr2;
+ struct passwd *pw;
+ uid_t uid;
+ int n;
+
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, "In function parse6(), from %s to %s",
+ gethost6(faddr), gethost6(laddr));
+
+ faddr2 = *faddr;
+ laddr2 = *laddr;
+ lport = fport = 0;
+
+ /* Read query from client */
+ if ((n = timed_read(fd, buf, sizeof(buf) - 1, IO_TIMEOUT)) <= 0) {
+ if (syslog_flag)
+ syslog(LOG_NOTICE,
+ n ? "read from %s: %m" : "read from %s: EOF",
+ gethost6(faddr));
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : ERROR : UNKNOWN-ERROR\r\n", lport, fport))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ buf[n] = '\0';
+
+ /* Pull out local and remote ports */
+ p = buf;
+ while (isspace(*p))
+ p++;
+ if ((p = strtok(p, " \t,"))) {
+ lport = atoi(p);
+ if ((p = strtok(NULL, " \t,")))
+ fport = atoi(p);
+ }
+
+ if (lport < 1 || lport > 65535 || fport < 1 || fport > 65535) {
+ if (syslog_flag)
+ syslog(LOG_NOTICE,
+ "scanf: invalid-port(s): %d , %d from %s",
+ lport, fport, gethost6(faddr));
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : ERROR : %s\r\n",
+ lport, fport, unknown_flag ? "UNKNOWN-ERROR" :
+ "INVALID-PORT")) >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (syslog_flag && verbose_flag)
+ syslog(LOG_NOTICE, "request for (%d,%d) from %s",
+ lport, fport, gethost6(faddr));
+
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, " After fscanf(), before k_getuid6()");
+
+ /*
+ * Next - get the specific TCP connection and return the
+ * uid - user number.
+ */
+ if (k_getuid6(&faddr2, htons(fport), laddr, htons(lport), &uid) == -1) {
+ if (no_user_token_flag) {
+ gentoken(token, sizeof token);
+ syslog(LOG_NOTICE, "token %s == NO USER", token);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n &&
+ syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m",
+ gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (syslog_flag)
+ syslog(LOG_DEBUG, "Returning: %d , %d : NO-USER",
+ lport, fport);
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : ERROR : %s\r\n",
+ lport, fport, unknown_flag ? "UNKNOWN-ERROR" : "NO-USER"))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+ if (debug_flag && syslog_flag)
+ syslog(LOG_DEBUG, " After k_getuid6(), before getpwuid()");
+
+ pw = getpwuid(uid);
+ if (!pw) {
+ if (syslog_flag)
+ syslog(LOG_WARNING,
+ "getpwuid() could not map uid (%u) to name",
+ uid);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%u\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, uid))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (syslog_flag)
+ syslog(LOG_DEBUG, "Successful lookup: %d , %d : %s",
+ lport, fport, pw->pw_name);
+
+ if (noident_flag && check_noident(pw->pw_dir)) {
+ if (syslog_flag && verbose_flag)
+ syslog(LOG_NOTICE,
+ "user %s requested HIDDEN-USER for host %s: %d, %d",
+ pw->pw_name, gethost6(faddr), lport, fport);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : ERROR : HIDDEN-USER\r\n", lport, fport))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (userident_flag && getuserident(pw->pw_dir, token, sizeof token)) {
+ syslog(LOG_NOTICE, "token \"%s\" == uid %u (%s)",
+ token, uid, pw->pw_name);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (token_flag) {
+ gentoken(token, sizeof token);
+ syslog(LOG_NOTICE, "token %s == uid %u (%s)", token, uid,
+ pw->pw_name);
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%s\r\n", lport, fport,
+ opsys_name, charset_sep, charset_name, token))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if (number_flag) {
+ if ((n = snprintf(buf, sizeof(buf),
+ "%d , %d : USERID : %s%s%s :%u\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, uid))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+ }
+
+ if ((n = snprintf(buf, sizeof(buf), "%d , %d : USERID : %s%s%s :%s\r\n",
+ lport, fport, opsys_name, charset_sep, charset_name, pw->pw_name))
+ >= sizeof(buf) || n < 0)
+ n = strlen(buf);
+ if (timed_write(fd, buf, n, IO_TIMEOUT) != n && syslog_flag) {
+ syslog(LOG_NOTICE, "write to %s: %m", gethost6(faddr));
+ return 1;
+ }
+ return 0;
+}
--- /dev/null
+/dir.c/1.14/Tue Oct 27 23:59:31 2009//
+/dir.h/1.2/Mon Jun 2 19:38:24 2003//
+/dl_prebind.c/1.9/Wed Apr 9 21:45:26 2008//
+/dl_prebind.h/1.2/Wed May 10 03:26:50 2006//
+/ld.so.1/1.17/Sun Aug 24 20:43:53 2008//
+/library.c/1.58/Thu Oct 2 20:12:08 2008//
+/library_mquery.c/1.36/Thu Oct 2 20:12:08 2008//
+/prebind.h/1.2/Fri May 12 22:14:04 2006//
+/resolve.c/1.49/Mon May 5 02:29:02 2008//
+/sod.c/1.23/Thu Oct 2 20:12:08 2008//
+/sod.h/1.1/Fri Jul 12 20:18:30 2002//
+/strtol.c/1.1/Sun Jul 6 20:03:58 2003//
+/util.c/1.20/Thu Oct 2 20:12:08 2008//
+/util.h/1.21/Mon May 18 20:20:01 2009//
+D/alpha////
+D/amd64////
+D/arm////
+D/hppa////
+D/i386////
+D/ldconfig////
+D/ldd////
+D/mips64////
+D/powerpc////
+D/sh////
+D/sparc////
+D/sparc64////
+/dl_printf.c/1.16/Sat Jan 2 15:01:02 2010//
+/loader.c/1.118/Sat Jan 2 15:01:02 2010//
+/Makefile/1.37/Sat Feb 6 00:59:40 2010//
+/dlfcn.c/1.81/Mon May 31 05:18:46 2010//
+/library_subr.c/1.30/Mon May 31 05:18:46 2010//
+/resolve.h/1.59/Mon May 31 05:18:46 2010//
--- /dev/null
+src/libexec/ld.so
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.37 2010/02/03 20:49:00 miod Exp $
+
+SUBDIR=ldconfig ldd
+
+MAN= ld.so.1
+
+.include <bsd.own.mk>
+.if defined(NOPIC)
+NOPROG=
+.else
+PROG= ld.so
+.endif
+
+VPATH=${.CURDIR}/../../lib/libc/string
+
+SRCS= ldasm.S loader.c resolve.c dlfcn.c dl_printf.c rtld_machine.c
+SRCS+= util.c sod.c strsep.c strtol.c dir.c library_subr.c dl_prebind.c
+.if (${MACHINE_ARCH} == "i386")
+SRCS+= library_mquery.c
+.else
+SRCS+= library.c
+.endif
+
+
+.include "${.CURDIR}/${MACHINE_CPU}/Makefile.inc"
+.PATH: ${.CURDIR}/${MACHINE_CPU}
+
+CFLAGS += -Wall -Werror
+CFLAGS += -I${.CURDIR} -I${.CURDIR}/${MACHINE_CPU} \
+ -Dstrsep=_dl_strsep
+INSTALL_STRIP=
+
+ELF_LDFLAGS+=--shared -Bsymbolic --no-undefined
+
+$(PROG):
+ $(LD) -x -e _dl_start $(ELF_LDFLAGS) -o $(PROG) $(OBJS) $(LDADD)
+
+.include <bsd.prog.mk>
--- /dev/null
+/Makefile.inc/1.2/Tue Sep 26 23:14:52 2006//
+/ldasm.S/1.17/Wed May 3 16:10:51 2006//
+/syscall.h/1.16/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.13/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.44/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/alpha
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.2 2006/09/26 23:14:52 martin Exp $
+
+CFLAGS += -fPIC -mno-fp-regs
+LIBCSRCDIR=${.CURDIR}/../../lib/libc
+.include "${LIBCSRCDIR}/arch/alpha/Makefile.inc"
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.13 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _ALPHA_ARCHDEP_H_
+#define _ALPHA_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_ALPHA_EXP /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf64_Rela
+#define RELSIZE sizeof(Elf64_Rela)
+
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+#define RTLD_PROTECT_PLT
+
+static inline void
+RELOC_REL(Elf64_Rel *r, const Elf64_Sym *s, Elf64_Addr *p, unsigned long v)
+{
+ /* Alpha does not use REL type relocations */
+ _dl_exit(20);
+}
+
+static inline void
+RELOC_RELA(Elf64_Rela *r, const Elf64_Sym *s, Elf64_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF64_R_TYPE(r->r_info) == RELOC_RELATIVE) {
+ /* handled by _reloc_alpha_got */
+ } else if (ELF64_R_TYPE(r->r_info) == RELOC_JMP_SLOT) {
+ Elf64_Addr val = v + s->st_value + r->r_addend -
+ (Elf64_Addr)(p);
+ *p = val;
+ __asm __volatile("imb" : : : "memory");
+ } else if (ELF64_R_TYPE(r->r_info) == RELOC_GLOB_DAT) {
+ *p = v + s->st_value + r->r_addend;
+ } else {
+ _dl_printf("unknown bootstrap relocation\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _ALPHA_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.17 2006/05/03 16:10:51 drahn Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ * Copyright 1996 Matt Thomas <matt@3am-software.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <machine/asm.h>
+#include <machine/pal.h>
+#include <sys/syscall.h>
+
+#define AUX_entry 9
+
+ .extern _GLOBAL_OFFSET_TABLE_
+
+/* Not really a leaf... but we are special. */
+LEAF_NOPROFILE(_dl_start, 0)
+ .set noreorder
+ br pv, L1
+L1:
+ LDGP(pv)
+
+ mov a0, s0 /* save arg */
+
+ /* relocate ourself. */
+ br s2, L2 /* get our PC */
+L2: ldiq s3, L2 /* get where the linker thought we were */
+
+ subq s2, s3, s2
+ mov s2, a1
+ lda t5, _DYNAMIC
+ addq s2, t5, a0
+
+ bsr ra, _reloc_alpha_got
+
+ /* allocate stack */
+ lda sp, (-8 - ((AUX_entry) * 8))(sp)
+
+ mov s0, a0
+ mov s2, s1 /* relocation displacement */
+ ldq a2, 0(a0) /* argc */
+ lda a3, 8(a0) /* argv */
+ mov a3, s3
+ lda t3, 1(a2)
+ sll t3, 3, t3
+ addq a3, t3, a4 /* envp */
+ mov a4, s4
+ mov a5, s5
+ lda s2, 0(sp)
+ mov s2, a1
+ mov 0, a2 /* dynamicp is unused on alpha */
+ CALL(_dl_boot_bind)
+ mov s3, a0 /* **argv */
+ mov s4, a1 /* **envp */
+ mov s1, a2 /* loff */
+ mov s2, a3 /* dl_data */
+ CALL(_dl_boot)
+ mov s0, a0
+ mov v0, pv
+ jsr ra, (pv)
+END(_dl_start)
+
+/*
+ * Lazy binding entry point, called via PLT.
+ */
+NESTED_NOPROFILE(_dl_bind_start, 0, 168, ra, 0, 0)
+ .set noat
+ /* at_reg already used by PLT code. */
+
+ /*
+ * Allocate stack frame and preserve all registers that the caller
+ * would have normally saved themselves.
+ */
+ lda sp, -168(sp)
+ stq ra, 0(sp)
+ stq v0, 8(sp)
+ stq t0, 16(sp)
+ stq t1, 24(sp)
+ stq t2, 32(sp)
+ stq t3, 40(sp)
+ stq t4, 48(sp)
+ stq t5, 56(sp)
+ stq t6, 64(sp)
+ stq t7, 72(sp)
+ stq a0, 80(sp)
+ stq a1, 88(sp)
+ stq a2, 96(sp)
+ stq a3, 104(sp)
+ stq a4, 112(sp)
+ stq a5, 120(sp)
+ stq t8, 128(sp)
+ stq t9, 136(sp)
+ stq t10, 144(sp)
+ stq t11, 152(sp)
+ stq gp, 160(sp)
+
+ /*
+ * Load our global pointer. Note, can't use pv, since it is
+ * already used by the PLT code.
+ */
+ br t0, L100
+L100: LDGP(t0)
+
+ /* Set up the arguments for _dl_bind. */
+ subq at_reg, t12, a1
+ ldq a0, 8(t12)
+ subq a1, 20, a1
+ addq a1, a1, a1
+ CALL(_dl_bind)
+
+ /* Move the destination address into position. */
+ mov v0, pv
+
+ /* Restore program registers. */
+ ldq ra, 0(sp)
+ ldq v0, 8(sp)
+ ldq t0, 16(sp)
+ ldq t1, 24(sp)
+ ldq t2, 32(sp)
+ ldq t3, 40(sp)
+ ldq t4, 48(sp)
+ ldq t5, 56(sp)
+ ldq t6, 64(sp)
+ ldq t7, 72(sp)
+ ldq a0, 80(sp)
+ ldq a1, 88(sp)
+ ldq a2, 96(sp)
+ ldq a3, 104(sp)
+ ldq a4, 112(sp)
+ ldq a5, 120(sp)
+ ldq t8, 128(sp)
+ ldq t9, 136(sp)
+ ldq t10, 144(sp)
+ ldq t11, 152(sp)
+ ldq gp, 160(sp)
+ /* XXX LDGP? */
+
+ /*
+ * We've patched the PLT; sync the I-stream.
+ */
+ imb
+
+ /* Pop the stack frame and turn control to the destination. */
+ lda sp, 168(sp)
+ jmp zero, (pv)
+END(_dl_bind_start)
+
+
+/*
+ * In reality these are not leaves, but they are stubs which does not need
+ * further register saving.
+ */
+
+LEAF_NOPROFILE(_dl_exit, 1)
+ ldiq v0, SYS_exit
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_exit)
+
+LEAF_NOPROFILE(_dl_open, 2)
+ ldiq v0, SYS_open
+ call_pal PAL_OSF1_callsys
+ beq a3, _dl_open_no_error
+ subq zero, v0, v0
+_dl_open_no_error:
+ RET
+END(_dl_open)
+
+LEAF_NOPROFILE(_dl_close, 1)
+ ldiq v0, SYS_close
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_close)
+
+LEAF_NOPROFILE(_dl_write, 3)
+ ldiq v0, SYS_write
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_write)
+
+LEAF_NOPROFILE(_dl_read, 3)
+ ldiq v0, SYS_read
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_read)
+
+LEAF_NOPROFILE(_dl_mmap, 6)
+ lda sp, -8(sp)
+ stq a5, 0(sp)
+ ldiq v0, SYS_mmap
+ call_pal PAL_OSF1_callsys
+ lda sp, 8(sp)
+ RET
+END(_dl_mmap)
+
+LEAF_NOPROFILE(_dl_munmap, 2)
+ ldiq v0, SYS_munmap
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_munmap)
+
+LEAF_NOPROFILE(_dl_mprotect, 3)
+ ldiq v0, SYS_mprotect
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_mprotect)
+
+LEAF_NOPROFILE(_dl_issetugid, 0)
+ ldiq v0, SYS_issetugid
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_issetugid)
+
+LEAF_NOPROFILE(_dl_stat, 2)
+ ldiq v0, SYS_stat
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_stat)
+
+LEAF_NOPROFILE(_dl__syscall, 3)
+ ldiq v0, SYS___syscall
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl__syscall)
+
+LEAF_NOPROFILE(_dl_fstat, 2)
+ ldiq v0, SYS_fstat
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_fstat)
+
+LEAF_NOPROFILE(_dl_fcntl, 3)
+ ldiq v0, SYS_fcntl
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_fcntl)
+
+LEAF_NOPROFILE(_dl_getdirentries, 4)
+ ldiq v0, SYS_getdirentries
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_getdirentries)
+
+/* _dl_sigprocmask does not support NULL new mask */
+LEAF_NOPROFILE(_dl_sigprocmask, 4)
+ mov a2, a5
+ ldl a1, 0(a1) /* load the set from *set */
+ ldiq v0, SYS_sigprocmask
+ call_pal PAL_OSF1_callsys
+ /* What about syscalls failing? */
+ beq a5, 1f
+ stl v0, 0(a5)
+1: mov zero, v0
+ RET
+END(_dl_sigprocmask)
+
+LEAF_NOPROFILE(_dl_sysctl, 4)
+ ldiq v0, SYS___sysctl
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_sysctl)
+
+LEAF_NOPROFILE(_dl_gettimeofday, 2)
+ ldiq v0, SYS_gettimeofday
+ call_pal PAL_OSF1_callsys
+ RET
+END(_dl_gettimeofday)
+
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.44 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 1999 Dale Rahn
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/exec.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ long i;
+ long numrela;
+ int fails = 0;
+ Elf64_Addr loff;
+ Elf64_Rela *relas;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
+ relas = (Elf64_Rela *)(object->Dyn.info[rel]);
+
+ if (relas == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ * XXX - we unprotect way to much. only the text can have cow
+ * relocations.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE)) {
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+ }
+
+ for (i = 0; i < numrela; i++, relas++) {
+ Elf64_Addr *r_addr;
+ Elf64_Addr ooff;
+ const Elf64_Sym *sym, *this;
+ const char *symn;
+
+ r_addr = (Elf64_Addr *)(relas->r_offset + loff);
+
+ if (ELF64_R_SYM(relas->r_info) == 0xffffffff)
+ continue;
+
+
+ sym = object->dyn.symtab;
+ sym += ELF64_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ this = NULL;
+ switch (ELF64_R_TYPE(relas->r_info)) {
+ case R_TYPE(REFQUAD):
+ ooff = _dl_find_symbol_bysym(object,
+ ELF64_R_SYM(relas->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ sym, NULL);
+ if (this == NULL)
+ goto resolve_failed;
+ *r_addr += ooff + this->st_value + relas->r_addend;
+ break;
+ case R_TYPE(RELATIVE):
+ /*
+ * There is a lot of unaligned RELATIVE
+ * relocs generated by gcc in the exception handlers.
+ */
+ if ((((Elf_Addr) r_addr) & 0x7) != 0) {
+ Elf_Addr tmp;
+#if 0
+_dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr,
+ ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff);
+#endif
+ _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
+ tmp += loff;
+ _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
+ } else
+ *r_addr += loff;
+ break;
+ case R_TYPE(JMP_SLOT):
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
+ sym, object, NULL);
+ if (this == NULL)
+ goto resolve_failed;
+ *r_addr = ooff + this->st_value + relas->r_addend;
+ break;
+ case R_TYPE(GLOB_DAT):
+ ooff = _dl_find_symbol_bysym(object,
+ ELF64_R_SYM(relas->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ sym, NULL);
+ if (this == NULL)
+ goto resolve_failed;
+ *r_addr = ooff + this->st_value + relas->r_addend;
+ break;
+ case R_TYPE(NONE):
+ break;
+ default:
+ _dl_printf("%s:"
+ " %s: unsupported relocation '%s' %d at %lx\n",
+ _dl_progname, object->load_name, symn,
+ ELF64_R_TYPE(relas->r_info), r_addr );
+ _dl_exit(1);
+ }
+ continue;
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
+ fails++;
+ }
+ __asm __volatile("imb" : : : "memory");
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+ return (fails);
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int reloff)
+{
+ Elf_RelA *rela;
+ Elf_Addr *addr, ooff;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ sigset_t savedmask;
+
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
+
+ addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
+ if (object->plt_size != 0 && !(*addr >= object->plt_start &&
+ *addr < (object->plt_start + object->plt_size ))) {
+ /* something is broken, relocation has already occurred */
+#if 0
+ DL_DEB(("*addr doesn't point into plt %p obj %s\n",
+ *addr, object->load_name));
+#endif
+ return *addr;
+ }
+
+ sym = object->dyn.symtab;
+ sym += ELF64_R_SYM(rela->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ /* if PLT is protected, allow the write */
+ if (object->plt_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect(addr, sizeof(Elf_Addr),
+ PROT_READ|PROT_WRITE);
+ }
+
+ *addr = ooff + this->st_value + rela->r_addend;
+
+ /* if PLT is (to be protected, change back to RO/X */
+ if (object->plt_size != 0) {
+ _dl_mprotect(addr, sizeof(Elf_Addr),
+ PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return *addr;
+}
+
+/*
+ * Relocate the Global Offset Table (GOT).
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int fails = 0;
+ Elf_Addr *pltgot;
+ extern void _dl_bind_start(void); /* XXX */
+ Elf_Addr ooff;
+ Elf_Addr plt_addr;
+ const Elf_Sym *this;
+
+
+ pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ plt_addr = NULL;
+ object->plt_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ plt_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->plt_size = ooff + this->st_value - plt_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ if (plt_addr == NULL)
+ object->plt_start = NULL;
+ else {
+ object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
+ object->plt_size += plt_addr - object->plt_start;
+ object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
+ }
+
+ if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ if (object->obj_base != 0) {
+ int i, size;
+ Elf_Addr *addr;
+ Elf_RelA *rela;
+
+ size = object->Dyn.info[DT_PLTRELSZ] /
+ sizeof(Elf_RelA);
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+
+ for (i = 0; i < size; i++) {
+ addr = (Elf_Addr *)(object->obj_base +
+ rela[i].r_offset);
+ *addr += object->obj_base;
+ }
+ }
+ }
+ if (pltgot != NULL) {
+ pltgot[2] = (Elf_Addr)_dl_bind_start;
+ pltgot[3] = (Elf_Addr)object;
+ }
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
+
+/* relocate the GOT early */
+
+void _reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase);
+
+void
+_reloc_alpha_got(Elf_Dyn *dynp, Elf_Addr relocbase)
+{
+ const Elf_RelA *rela = 0, *relalim;
+ Elf_Addr relasz = 0;
+ Elf_Addr *where;
+
+ for (; dynp->d_tag != DT_NULL; dynp++) {
+ switch (dynp->d_tag) {
+ case DT_RELA:
+ rela = (const Elf_RelA *)(relocbase + dynp->d_un.d_ptr);
+ break;
+ case DT_RELASZ:
+ relasz = dynp->d_un.d_val;
+ break;
+ }
+ }
+ relalim = (const Elf_RelA *)((caddr_t)rela + relasz);
+ for (; rela < relalim; rela++) {
+ where = (Elf_Addr *)(relocbase + rela->r_offset);
+ /* XXX For some reason I see a few GLOB_DAT relocs here. */
+ *where += (Elf_Addr)relocbase;
+ }
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.16 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long) __res < 0 && (long) __res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+void * _dl_mmap(void *, size_t, int, int, int, off_t);
+int _dl_mprotect(const void *, size_t, int);
+int _dl_munmap(const void*, size_t);
+int _dl_open(const char*, int);
+ssize_t _dl_read(int, const char*, size_t);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+long _dl__syscall(quad_t, ...);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/Makefile.inc/1.3/Sat Sep 17 01:06:53 2005//
+/syscall.h/1.4/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.4/Sat Jan 2 15:01:02 2010//
+/ldasm.S/1.7/Mon May 31 05:18:46 2010//
+/rtld_machine.c/1.15/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/amd64
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.3 2005/09/17 01:06:53 deraadt Exp $
+
+CFLAGS += -fPIC
+AFLAGS += -fpic
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.4 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _X86_64_ARCHDEP_H_
+#define _X86_64_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_AMD64 /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <sys/mman.h>
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void *
+_dl_mquery(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mquery, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+
+static inline void
+RELOC_REL(Elf64_Rel *r, const Elf64_Sym *s, Elf64_Addr *p, unsigned long v)
+{
+ /* AMD64 is a rela architecture */
+}
+
+static inline void
+RELOC_RELA(Elf64_Rela *r, const Elf64_Sym *s, Elf64_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF64_R_TYPE(r->r_info) == R_X86_64_RELATIVE) {
+ *p = v + r->r_addend;
+ } else if (ELF64_R_TYPE(r->r_info) == R_X86_64_GLOB_DAT) {
+ *p = v + s->st_value + r->r_addend;
+ } else {
+ _dl_printf("unknown bootstrap relocation\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _X86_64_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.7 2010/05/11 16:27:14 guenther Exp $ */
+
+/*
+ * Copyright (c) 2002,2004 Dale Rahn
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+
+#define DL_DATA_SIZE (16*8) /* 16 * sizeof(ELF_Addr) */
+#define DL_LOFF_OFFSET (7*8) /* index 7 */
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+ .text
+ .align 4
+ .globl _dl_start
+ .type _dl_start,@function
+_dl_start:
+ movq %rsp, %r12 # save stack pointer for _rtld
+ pushq %rbx # save ps_strings
+ subq $DL_DATA_SIZE, %rsp # allocate dl_data
+
+ leaq _DYNAMIC(%rip),%rdx # &_DYNAMIC
+ movq %rsp, %rsi # dl_data for dl_boot_bind
+ movq %r12, %rdi # load saved SP for dl_boot_bind
+ call _dl_boot_bind@PLT # _dl_boot_bind(sp,dl_data,dynamicp)
+
+ movq %rsp, %rcx # dl_data
+ movq DL_LOFF_OFFSET(%rsp), %rdx # loff from dl_data
+
+ movq (%r12), %rdi
+ leaq 16(%r12,%rdi,8), %rsi # envp
+ movq %r12, %rdi
+ addq $8,%rdi # argv
+ call _dl_boot@PLT # _dl_boot(argv,envp,loff,dl_data)
+
+ addq $DL_DATA_SIZE,%rsp # return dl_data
+
+# popq %rbx # %rbx = ps_strings - XXXDSR
+# popq %rdx # %rdx = cleanup - XXXDSR
+# popq %rcx # %rcx = obj_main - XXXDSR
+ movq %r12, %rsp
+ jmp *%rax
+
+ .section ".text"
+
+#define DL_SYSCALL(n) DL_SYSCALL2(n,n)
+#define DL_SYSCALL2(n,c) \
+ .global __CONCAT(_dl_,n) ;\
+ .type __CONCAT(_dl_,n), @function ;\
+ .align 4 ;\
+__CONCAT(_dl_,n): ;\
+ movl $(__CONCAT(SYS_,c)), %eax ;\
+ movq %rcx, %r10 ;\
+ syscall ;\
+ jb 1f ;\
+ ret
+
+DL_SYSCALL(open)
+DL_SYSCALL(fcntl)
+DL_SYSCALL(fstat)
+DL_SYSCALL(stat)
+DL_SYSCALL(read)
+DL_SYSCALL(write)
+DL_SYSCALL(close)
+DL_SYSCALL(issetugid)
+DL_SYSCALL(getdirentries)
+DL_SYSCALL(mprotect)
+DL_SYSCALL(munmap)
+DL_SYSCALL(gettimeofday)
+DL_SYSCALL(exit)
+DL_SYSCALL2(_syscall,__syscall)
+DL_SYSCALL2(sysctl,__sysctl)
+
+1:
+ /* error: result = -errno; - handled here. */
+ neg %rax
+ ret
+
+
+ /* _dl_sigprocmask: does not handle NULL new set */
+
+ .align 4
+ .global _dl_sigprocmask
+ .type _dl_sigprocmask,@function
+_dl_sigprocmask:
+ movl (%rsi),%esi # fetch indirect...
+ movl $SYS_sigprocmask, %eax
+ movq %rcx, %r10
+ syscall
+ jc 1b /* error: result = -errno */
+ testq %rdx,%rdx # test if old mask requested
+ jz 2f
+ movl %eax,(%rdx) # store old mask
+ xorq %rax,%rax
+2: ret
+
+ .align 4
+ .global _dl_bind_start
+ .type _dl_bind_start,@function
+_dl_bind_start:
+ pushfq # save registers
+ pushq %rax
+ pushq %rcx
+ pushq %rdx
+ pushq %rsi
+ pushq %rdi
+ pushq %r8
+ pushq %r9
+ pushq %r10
+ pushq %r11
+
+ movq 80(%rsp), %rdi # Copy of reloff
+ movq 88(%rsp), %rsi # Copy of obj
+ call _dl_bind@PLT # Call the binder
+ movq %rax,88(%rsp) # Store function to be called in obj
+
+ popq %r11 # restore registers
+ popq %r10
+ popq %r9
+ popq %r8
+ popq %rdi
+ popq %rsi
+ popq %rdx
+ popq %rcx
+ popq %rax
+ popfq
+
+ leaq 8(%rsp),%rsp # Discard reloff, do not change eflags
+ ret
+
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.15 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 2002,2004 Dale Rahn
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_E 0x02000000 /* ERROR */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* 0 NONE */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* 1 _64*/
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* 2 PC32 */
+ _RF_G|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 3 GOT32 */
+ _RF_E|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 4 PLT32 */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* 5 COPY */
+ _RF_S| _RF_SZ(64) | _RF_RS(0), /* 6 GLOB_DAT*/
+ _RF_S| _RF_SZ(64) | _RF_RS(0), /* 7 JUMP_SLOT*/
+ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* 8 RELATIVE*/
+ _RF_E, /* 9 GOTPCREL*/
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 10 32 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 11 32S */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 12 16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* 13 PC16 */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 14 8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* 15 PC8 */
+ _RF_E, /* 16 DPTMOD64*/
+ _RF_E, /* 17 DTPOFF64*/
+ _RF_E, /* 18 TPOFF64 */
+ _RF_E, /* 19 TLSGD */
+ _RF_E, /* 20 TLSLD */
+ _RF_E, /* 21 DTPOFF32*/
+ _RF_E, /* 22 GOTTPOFF*/
+ _RF_E /* 23 TPOFF32*/
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+#define RELOC_ERROR(t) (reloc_target_flags[t] & _RF_E)
+
+static long reloc_target_bitmask[] = {
+#define _BM(x) (x == 64? ~0 : ~(-(1UL << (x))))
+ 0, /* 0 NONE */
+ _BM(64), /* 1 _64*/
+ _BM(32), /* 2 PC32 */
+ _BM(32), /* 3 GOT32 */
+ _BM(32), /* 4 PLT32 */
+ _BM(0), /* 5 COPY */
+ _BM(64), /* 6 GLOB_DAT*/
+ _BM(64), /* 7 JUMP_SLOT*/
+ _BM(64), /* 8 RELATIVE*/
+ _BM(32), /* 9 GOTPCREL*/
+ _BM(32), /* 10 32 */
+ _BM(32), /* 11 32S */
+ _BM(16), /* 12 16 */
+ _BM(16), /* 13 PC16 */
+ _BM(8), /* 14 8 */
+ _BM(8), /* 15 PC8 */
+ 0, /* 16 DPTMOD64*/
+ 0, /* 17 DTPOFF64*/
+ 0, /* 18 TPOFF64 */
+ 0, /* 19 TLSGD */
+ 0, /* 20 TLSLD */
+ 0, /* 21 DTPOFF32*/
+ 0, /* 22 GOTTPOFF*/
+ 0 /* 23 TPOFF32*/
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+void _dl_reloc_plt(Elf_Addr *where, Elf_Addr value);
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relsz)
+{
+ long i;
+ long numrel;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_RelA *rels;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA);
+ rels = (Elf_RelA *)(object->Dyn.info[rel]);
+ if (rels == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrel; i++, rels++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(rels->r_info);
+
+ if (RELOC_ERROR(type)) {
+ _dl_printf("relocation error %d idx %d\n", type, i);
+ _dl_exit(20);
+ }
+
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(rels->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+ value = rels->r_addend;
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rels->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rels->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JUMP_SLOT))?
+ SYM_PLT:SYM_NOTPLT),
+ sym, NULL);
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(JUMP_SLOT)) {
+ _dl_reloc_plt(where, value);
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JUMP_SLOT)) ? SYM_PLT:SYM_NOTPLT),
+ dstsym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp = 0;
+ char *ptr = (char *)where;
+ int i, size = RELOC_TARGET_SIZE(type)/8;
+
+ /* Read it in one byte at a time. */
+ for (i=0; i<size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i=0; i<size; i++)
+ ptr[i] = ((tmp >> (8*i)) & 0xff);
+ } else if (RELOC_TARGET_SIZE(type) > 32) {
+ *where &= ~mask;
+ *where |= value;
+ } else {
+ Elf32_Addr *where32 = (Elf32_Addr *)where;
+
+ *where32 &= ~mask;
+ *where32 |= value;
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+void
+_dl_reloc_plt(Elf_Addr *where, Elf_Addr value)
+{
+ *where = value;
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int index)
+{
+ Elf_RelA *rel;
+ Elf_Word *addr;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ Elf_Addr ooff, newval;
+ sigset_t savedmask;
+
+ rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+
+ rel += index;
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rel->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ addr = (Elf_Word *)(object->obj_base + rel->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ newval = ooff + this->st_value + rel->r_addend;
+
+ /* if GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ|PROT_WRITE);
+ }
+
+ _dl_reloc_plt((Elf_Addr *)addr, newval);
+
+ /* put the GOT back to RO */
+ if (object->got_size != 0) {
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return(newval);
+}
+
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ extern void _dl_bind_start(void); /* XXX */
+ int fails = 0;
+ Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+ int i, num;
+ Elf_RelA *rel;
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+
+ if (pltgot == NULL)
+ return (0); /* it is possible to have no PLT/GOT relocations */
+
+ pltgot[1] = (Elf_Addr)object;
+ pltgot[2] = (Elf_Addr)&_dl_bind_start;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_RELA)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+ num = (object->Dyn.info[DT_PLTRELSZ]);
+ for (i = 0; i < num/sizeof(Elf_RelA); i++, rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(rel->r_offset + object->obj_base);
+ *where += object->obj_base;
+ }
+
+ }
+
+ /* PLT is already RO on i386, no point in mprotecting it, just GOT */
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+
+ return (fails);
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.4 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void *, unsigned int);
+int _dl_open(const char *, unsigned int);
+int _dl_read(int, const char *, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/Makefile.inc/1.2/Sun Oct 23 06:04:03 2005//
+/ldasm.S/1.6/Wed May 3 16:10:52 2006//
+/syscall.h/1.4/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.4/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.15/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/arm
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.2 2005/10/23 06:04:03 drahn Exp $
+
+CFLAGS += -fpic -msoft-float
+AFLAGS += -D_STANDALONE
+AFLAGS += -I${.CURDIR}/../../lib/libc/arch/arm
+SRCS+= divsi3.S
+ELF_LDFLAGS+=-z nocombreloc
+LIBCSRCDIR=${.CURDIR}/../../lib/libc
+.PATH: ${LIBCSRCDIR}/arch/arm/gen/
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.4 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _ARM_ARCHDEP_H_
+#define _ARM_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 4 /* Arch constraint or otherwise */
+
+#define MACHID EM_ARM /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+/* HACK */
+#define DT_PROCNUM 0
+#ifndef DT_BIND_NOW
+#define DT_BIND_NOW 0
+#endif
+
+/*
+ * The following functions are declared inline so they can
+ * be used before bootstrap linking has been finished.
+ */
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ if (ELF_R_TYPE(r->r_info) == R_ARM_RELATIVE) {
+ *p += v;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+static inline void
+RELOC_RELA(Elf32_Rela *r, const Elf32_Sym *s, Elf32_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ _dl_exit(20);
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS (PROT_READ|PROT_EXEC)
+
+#endif /* _ARM_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.6 2006/05/03 16:10:52 drahn Exp $ */
+
+/*
+ * Copyright (c) 2004 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define DL_DATA_SIZE (16 * 4) /* XXX */
+#include <machine/asm.h>
+#include <sys/syscall.h>
+#include <SYS.h>
+
+ENTRY(_dl_start)
+ mov fp, sp
+ mov r5, sp
+ mov lr, r6 @ save lr
+ sub sp, sp, #4+4+DL_DATA_SIZE
+ add r7, sp, #4 @ dl_data
+
+ mov r0, fp @ original stack
+ mov r1, r7 @ dl_data
+
+ bl _dl_boot_bind
+
+ add r0, r5, #4 @ argv
+ ldr r1, [r5, #0x0] @ envp
+ add r1, r1, #2
+ add r1, fp, r1, lsl #2
+ ldr r2, [r7, #7*4] @ loff from dl_data
+ mov r3, r7 @ dl_data
+ bl _dl_boot
+
+ mov sp, fp
+ mov fp, #0
+ mov lr, r6
+
+ mov pc, r0
+
+
+ENTRY(_dl_bind_start)
+ /*
+ * ip is pointer to got entry for this relocation
+ * lr is pointer to pltgot[2], which is entry -1 of got plt reloc.
+ * return address is on stack
+ */
+ stmdb sp!, {r0-r4,sl,fp}
+
+ sub r1, ip, lr /* r1 = 4 * (n + 1) */
+ sub r1, r1, #4 /* r1 = 4 * n */
+ mov r1, r1, lsr #2 /* r1 = n */
+
+ ldr r0, [lr, #-4]
+ bl _dl_bind
+ mov ip, r0
+ ldmia sp!, {r0-r4,sl,fp,lr}
+ mov pc, ip
+
+ /* STUB */
+
+
+/* ld.so SYSCALLS */
+
+#define DL_SYSCALL(n) DL_SYSCALL2(n,n)
+#define DL_SYSCALL2(n,c) \
+ .global __CONCAT(_dl_,n) ;\
+ .type __CONCAT(_dl_,n)%function ;\
+__CONCAT(_dl_,n): ;\
+ SYSTRAP(c) ;\
+ bcs .L_cerr ;\
+ mov pc, lr
+
+ .section ".text"
+ .align 4
+DL_SYSCALL(close)
+
+
+ .global _dl_exit
+ .type _dl_exit%function
+_dl_exit:
+ SYSTRAP(exit)
+ 1:
+ b 1b
+
+DL_SYSCALL(issetugid)
+DL_SYSCALL2(_syscall,__syscall)
+DL_SYSCALL(munmap)
+DL_SYSCALL(mprotect)
+DL_SYSCALL(open)
+DL_SYSCALL(read)
+DL_SYSCALL(write)
+DL_SYSCALL(stat)
+DL_SYSCALL(fstat)
+DL_SYSCALL(fcntl)
+DL_SYSCALL(gettimeofday)
+DL_SYSCALL2(sysctl,__sysctl)
+
+DL_SYSCALL(getdirentries)
+
+ .global _dl_sigprocmask
+ .type _dl_sigprocmask%function
+_dl_sigprocmask:
+ teq r1, #0
+ moveq r0, #1
+ moveq r1, #0
+ ldrne r1, [r1]
+ SYSTRAP(sigprocmask)
+ bcs .L_cerr
+ teq r2, #0
+ strne r0, [r2]
+ mov r0, #0
+ mov pc, lr
+
+.L_cerr:
+ mov r0, #0
+ sub r0, r0, #1
+ mov pc, lr
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.15 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 2004 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+void _dl_bind_start(void); /* XXX */
+Elf_Addr _dl_bind(elf_object_t *object, int reloff);
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_E 0x02000000 /* ERROR */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* 0 NONE */
+ _RF_S|_RF_P|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 1 PC24 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 2 ABS32 */
+ _RF_S|_RF_P|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 3 REL32 */
+ _RF_S|_RF_P|_RF_A| _RF_E, /* 4 REL13 */
+ _RF_S|_RF_A| _RF_E, /* 5 ABS16 */
+ _RF_S|_RF_A| _RF_E, /* 6 ABS12 */
+ _RF_S|_RF_A| _RF_E, /* 7 T_ABS5 */
+ _RF_S|_RF_A| _RF_E, /* 8 ABS8 */
+ _RF_S|_RF_B|_RF_A| _RF_E, /* 9 SBREL32 */
+ _RF_S|_RF_P|_RF_A| _RF_E, /* 10 T_PC22 */
+ _RF_S|_RF_P|_RF_A| _RF_E, /* 11 T_PC8 */
+ _RF_E, /* 12 Reserved */
+ _RF_S|_RF_A| _RF_E, /* 13 SWI24 */
+ _RF_S|_RF_A| _RF_E, /* 14 T_SWI8 */
+ _RF_E, /* 15 OBSL */
+ _RF_E, /* 16 OBSL */
+ _RF_E, /* 17 UNUSED */
+ _RF_E, /* 18 UNUSED */
+ _RF_E, /* 19 UNUSED */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* 20 COPY */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 21 GLOB_DAT */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* 22 JUMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0), /* 23 RELATIVE */
+ _RF_E, /* 24 GOTOFF */
+ _RF_E, /* 25 GOTPC */
+ _RF_E, /* 26 GOT32 */
+ _RF_E, /* 27 PLT32 */
+ _RF_E, /* 28 UNUSED */
+ _RF_E, /* 29 UNUSED */
+ _RF_E, /* 30 UNUSED */
+ _RF_E, /* 31 UNUSED */
+ _RF_E, /* 32 A_PCR 0 */
+ _RF_E, /* 33 A_PCR 8 */
+ _RF_E, /* 34 A_PCR 16 */
+ _RF_E, /* 35 B_PCR 0 */
+ _RF_E, /* 36 B_PCR 12 */
+ _RF_E, /* 37 B_PCR 20 */
+ _RF_E, /* 38 RELAB32 */
+ _RF_E, /* 39 ROSGREL32 */
+ _RF_E, /* 40 V4BX */
+ _RF_E, /* 41 STKCHK */
+ _RF_E /* 42 TSTKCHK */
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+static int reloc_target_bitmask[] = {
+#define _BM(x) (x == 32? ~0 : ~(-(1UL << (x))))
+ _BM(0), /* 0 NONE */
+ _BM(24), /* 1 PC24 */
+ _BM(32), /* 2 ABS32 */
+ _BM(32), /* 3 REL32 */
+ _BM(0), /* 4 REL13 */
+ _BM(0), /* 5 ABS16 */
+ _BM(0), /* 6 ABS12 */
+ _BM(0), /* 7 T_ABS5 */
+ _BM(0), /* 8 ABS8 */
+ _BM(32), /* 9 SBREL32 */
+ _BM(0), /* 10 T_PC22 */
+ _BM(0), /* 11 T_PC8 */
+ _BM(0), /* 12 Reserved */
+ _BM(0), /* 13 SWI24 */
+ _BM(0), /* 14 T_SWI8 */
+ _BM(0), /* 15 OBSL */
+ _BM(0), /* 16 OBSL */
+ _BM(0), /* 17 UNUSED */
+ _BM(0), /* 18 UNUSED */
+ _BM(0), /* 19 UNUSED */
+ _BM(32), /* 20 COPY */
+ _BM(32), /* 21 GLOB_DAT */
+ _BM(32), /* 22 JUMP_SLOT */
+ _BM(32), /* 23 RELATIVE */
+ _BM(0), /* 24 GOTOFF */
+ _BM(0), /* 25 GOTPC */
+ _BM(0), /* 26 GOT32 */
+ _BM(0), /* 27 PLT32 */
+ _BM(0), /* 28 UNUSED */
+ _BM(0), /* 29 UNUSED */
+ _BM(0), /* 30 UNUSED */
+ _BM(0), /* 31 UNUSED */
+ _BM(0), /* 32 A_PCR 0 */
+ _BM(0), /* 33 A_PCR 8 */
+ _BM(0), /* 34 A_PCR 16 */
+ _BM(0), /* 35 B_PCR 0 */
+ _BM(0), /* 36 B_PCR 12 */
+ _BM(0), /* 37 B_PCR 20 */
+ _BM(0), /* 38 RELAB32 */
+ _BM(0), /* 39 ROSGREL32 */
+ _BM(0), /* 40 V4BX */
+ _BM(0), /* 41 STKCHK */
+ _BM(0) /* 42 TSTKCHK */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+#define R_TYPE(x) R_ARM_ ## x
+
+void _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_Rel *rel);
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relsz)
+{
+ long i;
+ long numrel;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_Rel *rels;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrel = object->Dyn.info[relsz] / sizeof(Elf_Rel);
+ rels = (Elf_Rel *)(object->Dyn.info[rel]);
+
+ if (rels == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list;
+ llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrel; i++, rels++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(rels->r_info);
+
+ if (reloc_target_flags[type] & _RF_E) {
+ _dl_printf(" bad relocation %d %d\n", i, type);
+ _dl_exit(1);
+ }
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(rels->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+#ifdef LDSO_ARCH_IS_RELA_
+ value = rels->r_addend;
+#else
+ value = *where & RELOC_VALUE_BITMASK(type);
+#endif
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rels->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rels->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JUMP_SLOT)) ?
+ SYM_PLT : SYM_NOTPLT),
+ sym, NULL);
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(JUMP_SLOT)) {
+ /*
+ _dl_reloc_plt((Elf_Word *)where, value, rels);
+ */
+ *where = value;
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ dstsym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp = 0;
+ char *ptr = (char *)where;
+ int i, size = RELOC_TARGET_SIZE(type)/8;
+
+ /* Read it in one byte at a time. */
+ for (i=0; i<size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i=0; i<size; i++)
+ ptr[i] = ((tmp >> (8*i)) & 0xff);
+ } else {
+ *where &= ~mask;
+ *where |= value;
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list;
+ llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+/*
+ * Relocate the Global Offset Table (GOT).
+ * This is done by calling _dl_md_reloc on DT_JUMPREL for DL_BIND_NOW,
+ * otherwise the lazy binding plt initialization is performed.
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int fails = 0;
+ Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+ int i, num;
+ Elf_Rel *rel;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_REL)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ object->plt_size = 0; /* Text PLT on ARM */
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ object->plt_start = NULL;
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+ num = (object->Dyn.info[DT_PLTRELSZ]);
+
+ for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(rel->r_offset + object->obj_base);
+ *where += object->obj_base;
+ }
+
+ pltgot[1] = (Elf_Addr)object;
+ pltgot[2] = (Elf_Addr)_dl_bind_start;
+ }
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
+
+Elf_Addr
+_dl_bind(elf_object_t *object, int relidx)
+{
+ Elf_Rel *rel;
+ Elf_Word *addr;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ Elf_Addr ooff, newval;
+ sigset_t savedmask;
+
+ rel = ((Elf_Rel *)object->Dyn.info[DT_JMPREL]) + (relidx);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rel->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
+ newval = ooff + this->st_value;
+
+ /* if GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ|PROT_WRITE);
+ }
+
+ if (*addr != newval)
+ *addr = newval;
+
+ /* put the GOT back to RO */
+ if (object->got_size != 0) {
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+ return newval;
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.4 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void *, unsigned int);
+int _dl_open(const char *, unsigned int);
+int _dl_read(int, const char *, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/* $OpenBSD: dir.c,v 1.14 2009/10/27 23:59:31 deraadt Exp $ */
+/*
+ * 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "util.h"
+#include "dir.h"
+
+long _dl_telldir(const DIR *dirp);
+void _dl_seekdir(DIR *dirp, long loc);
+
+/*
+ * Open a directory.
+ */
+DIR *
+_dl_opendir(const char *name)
+{
+ DIR *dirp;
+ int fd;
+ struct stat sb;
+
+ if ((fd = _dl_open(name, O_RDONLY | O_NONBLOCK)) < 0)
+ return (NULL);
+ if (_dl_fstat(fd, &sb) || !S_ISDIR(sb.st_mode)) {
+ _dl_close(fd);
+ return (NULL);
+ }
+ if (_dl_fcntl(fd, F_SETFD, FD_CLOEXEC) < 0 ||
+ (dirp = (DIR *)_dl_malloc(sizeof(DIR))) == NULL) {
+ _dl_close(fd);
+ return (NULL);
+ }
+
+ dirp->dd_len = _dl_round_page(sb.st_blksize);
+ dirp->dd_buf = _dl_malloc(dirp->dd_len);
+ if (dirp->dd_buf == NULL) {
+ _dl_free(dirp);
+ _dl_close (fd);
+ return (NULL);
+ }
+ dirp->dd_seek = 0;
+ dirp->dd_loc = 0;
+ dirp->dd_fd = fd;
+ dirp->dd_flags = DTF_NODUP;
+
+ return (dirp);
+}
+
+
+/*
+ * close a directory.
+ */
+int
+_dl_closedir(DIR *dirp)
+{
+ int fd;
+ int ret;
+
+ fd = dirp->dd_fd;
+ dirp->dd_fd = -1;
+ dirp->dd_loc = 0;
+ _dl_free((void *)dirp->dd_buf);
+ _dl_free((void *)dirp);
+ ret = _dl_close(fd);
+ return (ret);
+}
+
+
+/*
+ * get next entry in a directory.
+ */
+struct dirent *
+_dl_readdir(DIR *dirp)
+{
+ struct dirent *dp;
+
+ for (;;) {
+ if (dirp->dd_loc >= dirp->dd_size) {
+ dirp->dd_loc = 0;
+ }
+ if (dirp->dd_loc == 0) {
+ dirp->dd_size = _dl_getdirentries(dirp->dd_fd,
+ dirp->dd_buf, dirp->dd_len, &dirp->dd_seek);
+ if (dirp->dd_size <= 0)
+ return (NULL);
+ }
+ dp = (struct dirent *)(dirp->dd_buf + dirp->dd_loc);
+ if ((long)dp & 03) /* bogus pointer check */
+ return (NULL);
+ if (dp->d_reclen <= 0 ||
+ dp->d_reclen > dirp->dd_len + 1 - dirp->dd_loc)
+ return (NULL);
+ dirp->dd_loc += dp->d_reclen;
+ if (dp->d_ino == 0)
+ continue;
+ return (dp);
+ }
+}
--- /dev/null
+/* $OpenBSD: dir.h,v 1.2 2003/06/02 19:38:24 millert Exp $ */
+
+/*
+ * 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. 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.
+ */
+
+DIR *_dl_opendir(const char *name);
+int _dl_closedir(DIR *dirp);
+struct dirent *_dl_readdir(DIR *dirp);
--- /dev/null
+/* $OpenBSD: dl_prebind.c,v 1.9 2008/04/09 21:45:26 kurt Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/exec.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <nlist.h>
+#include <string.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "sod.h"
+#include "stdlib.h"
+#include "dl_prebind.h"
+
+void elf_dump_footer(struct prebind_footer *footer);
+void dump_prelink(Elf_Addr base, u_long size);
+void prebind_dump_footer(struct prebind_footer *footer, char *file);
+void prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt);
+void prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numblibs,
+ char *nametab);
+void prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups);
+void prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs);
+struct prebind_footer *_dl_prebind_data_to_footer(void *data);
+
+void *_dl_prog_prebind_map;
+struct prebind_footer *prog_footer;
+extern char *_dl_noprebind;
+extern char *_dl_prebind_validate;
+
+int _dl_prebind_match_failed; /* = 0 */
+
+char *prebind_bind_now = "prebind";
+
+struct prebind_footer *
+_dl_prebind_data_to_footer(void *prebind_data)
+{
+ u_int32_t *poffset, offset;
+ struct prebind_footer *footer;
+ char *c;
+
+ poffset = prebind_data;
+ c = prebind_data;
+ offset = *poffset;
+ c += offset;
+ footer = (void *)c;
+ return footer;
+}
+
+void
+prebind_load_exe(Elf_Phdr *phdp, elf_object_t *exe_obj)
+{
+ struct prebind_footer *footer;
+
+ exe_obj->prebind_data = (void *)phdp->p_vaddr;
+ _dl_prog_prebind_map = exe_obj->prebind_data;
+
+ footer = _dl_prebind_data_to_footer(_dl_objects->prebind_data);
+
+ if (footer->bind_id[0] == BIND_ID0 &&
+ footer->bind_id[1] == BIND_ID1 &&
+ footer->bind_id[2] == BIND_ID2 &&
+ footer->bind_id[3] == BIND_ID3 &&
+ footer->prebind_version == PREBIND_VERSION) {
+ prog_footer = footer;
+ if (_dl_bindnow == NULL)
+ _dl_bindnow = prebind_bind_now;
+ } else {
+ DL_DEB(("prebind data missing\n"));
+ _dl_prog_prebind_map = NULL;
+ }
+ if (_dl_noprebind != NULL) {
+ /*prog_footer is valid, we should free it */
+ _dl_prog_prebind_map = NULL;
+ prog_footer = NULL;
+ exe_obj->prebind_data = NULL;
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+#if 0
+ else if (_dl_debug)
+ dump_prelink((long)_dl_prog_prebind_map,
+ prog_footer->prebind_size);
+#endif
+}
+
+void *
+prebind_load_fd(int fd, const char *name)
+{
+ struct prebind_footer footer;
+ struct nameidx *nameidx;
+ void *prebind_data;
+ char *nametab;
+ ssize_t len;
+ int idx;
+
+ if (_dl_prog_prebind_map == NULL || _dl_prebind_match_failed)
+ return 0;
+
+ _dl_lseek(fd, -(off_t)sizeof(struct prebind_footer), SEEK_END);
+ len = _dl_read(fd, (void *)&footer, sizeof(struct prebind_footer));
+
+ if (len != sizeof(struct prebind_footer) ||
+ footer.bind_id[0] != BIND_ID0 ||
+ footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 ||
+ footer.bind_id[3] != BIND_ID3 ||
+ footer.prebind_version != PREBIND_VERSION) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match failed %s\n", name));
+ return (NULL);
+ }
+
+ prebind_data = _dl_mmap(0, footer.prebind_size, PROT_READ,
+ MAP_FILE, fd, footer.prebind_base);
+ DL_DEB(("prebind_load_fd for lib %s\n", name));
+
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+
+ /* libraries are loaded in random order, so we just have
+ * to look thru the list to find ourselves
+ */
+ for (idx = 0; idx < prog_footer->numlibs; idx++) {
+ if (_dl_strcmp(nametab + nameidx[idx].name, name) == 0)
+ break;
+ }
+
+ if (idx == prog_footer->numlibs) {
+ _dl_prebind_match_failed = 1; /* not found */
+ } else if (footer.id0 != nameidx[idx].id0 ||
+ footer.id1 != nameidx[idx].id1) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match id0 %d pid0 %d id1 %d pid1 %d\n",
+ footer.id0, nameidx[idx].id0,
+ footer.id1, nameidx[idx].id1));
+ }
+
+ if (_dl_prebind_match_failed == 1) {
+ DL_DEB(("prebind match failed for %s\n", name));
+ }
+
+ return prebind_data;
+}
+#define NUM_STATIC_OBJS 10
+elf_object_t *objarray_static[NUM_STATIC_OBJS];
+elf_object_t **objarray;
+
+void
+prebind_symcache(elf_object_t *object, int plt)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap, *idxtolib;
+ u_int32_t *poffset, offset, symcache_cnt;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ int i = 0, cur_obj = -1, idx;
+ void *prebind_map;
+ struct nameidx *nameidx;
+ char *nametab, *c;
+ struct fixup *fixup;
+ elf_object_t *obj;
+
+ struct symcachetab *s;
+
+ if (object->prebind_data == NULL)
+ return;
+// DL_DEB(("prebind symcache %s\n", object->load_name));
+
+ obj = _dl_objects;
+ while (obj != NULL) {
+ if (obj == object)
+ cur_obj = i;
+ i++;
+ obj = obj->next;
+ }
+
+ if (cur_obj == -1)
+ return; /* unable to find object ? */
+
+ if (objarray == NULL) {
+ if (i <= NUM_STATIC_OBJS) {
+ objarray = &objarray_static[0];
+ } else {
+ objarray = _dl_malloc(sizeof(elf_object_t *) * i);
+ }
+
+ obj = _dl_objects;
+ i = 0;
+ while (obj != NULL) {
+ objarray[i] = obj;
+ i++;
+ obj = obj->next;
+ }
+ }
+
+ poffset = (u_int32_t *)object->prebind_data;
+ c = object->prebind_data;
+ offset = *poffset;
+ c += offset;
+
+ footer = (void *)c;
+ prebind_map = (void *)object->prebind_data;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ if (plt) {
+ symcachetab = prebind_map + footer->pltsymcache_idx;
+ symcache_cnt = footer->pltsymcache_cnt;
+// DL_DEB(("loading plt %d\n", symcache_cnt));
+ } else {
+ symcachetab = prebind_map + footer->symcache_idx;
+ symcache_cnt = footer->symcache_cnt;
+// DL_DEB(("loading got %d\n", symcache_cnt));
+ }
+ nametab = prebind_map + footer->nametab_idx;
+
+ libmap = _dl_prog_prebind_map + prog_footer->libmap_idx;
+ idxtolib = _dl_prog_prebind_map + libmap[cur_obj];
+
+ for (i = 0; i < symcache_cnt; i++) {
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ s = &(symcachetab[i]);
+ if (cur_obj == 0)
+ idx = s->obj_idx;
+ else
+ idx = idxtolib[s->obj_idx];
+
+ if (idx == -1) /* somehow an invalid object ref happend */
+ continue;
+#if 0
+ DL_DEB(("%s:", object->load_name));
+ DL_DEB(("symidx %d: obj %d %d sym %d flags %x\n",
+ s->idx, s->obj_idx, idx, s->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[idx];
+ sym = tobj->dyn.symtab + s->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ s->idx, s->obj_idx, tobj->load_name,
+ s->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[s->idx].obj = tobj;
+ _dl_symcache[s->idx].sym = sym;
+ _dl_symcache[s->idx].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+
+ if (!plt) {
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+ for (i = 0; i < fixupcnt[2*cur_obj]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx, f->flags));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ } else {
+
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj+1];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+#if 0
+ DL_DEB(("prebind loading symbols fixup plt %s\n",
+ object->load_name));
+#endif
+ for (i = 0; i < fixupcnt[2*cur_obj+1]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ }
+// DL_DEB(("prebind_data loaded\n"));
+}
+
+void
+prebind_free(elf_object_t *object)
+{
+ struct prebind_footer *footer;
+
+ if (object->prebind_data == NULL)
+ return;
+#ifdef DEBUG1
+ DL_DEB(("prebind_free for %s %p\n", object->load_name,
+ object->prebind_data));
+#endif
+ if (object->prebind_data != 0) {
+ footer = _dl_prebind_data_to_footer(object->prebind_data);
+
+#ifdef DEBUG1
+ DL_DEB(("freeing prebind data sz %x\n", footer->prebind_size));
+#endif
+
+ _dl_munmap((void *)ELF_TRUNC((long)object->prebind_data, _dl_pagesz),
+ ELF_ROUND((long)object->prebind_data+footer->prebind_size,
+ _dl_pagesz) - ELF_TRUNC((long)object->prebind_data, _dl_pagesz));
+
+ object->prebind_data = NULL;
+ _dl_prog_prebind_map = NULL;
+
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+}
+
+int validate_errs;
+
+struct timeval beforetp;
+
+void
+_dl_prebind_pre_resolve()
+{
+ struct prebind_footer *footer;
+ elf_object_t *object;
+ struct nameidx *nameidx;
+ char *nametab, *name;
+ int idx;
+
+ if (_dl_prog_prebind_map != NULL) {
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+ for (idx = 1, object = _dl_objects->next; object != NULL;
+ object = object->next, idx++) {
+ if (object->prebind_data == NULL) {
+ /* ld.so doesn't have prebind data */
+ if (object->next == NULL)
+ continue;
+ DL_DEB(("missing prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ footer = _dl_prebind_data_to_footer(
+ object->prebind_data);
+ if (footer == NULL ||
+ nameidx[idx].id0 != footer->id0 ||
+ nameidx[idx].id1 != footer->id1) {
+ DL_DEB(("invalid prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ name = object->load_name;
+ if (_dl_strcmp(nametab + nameidx[idx].name, name)
+ != 0) {
+ DL_DEB(("invalid prebind name %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ }
+ }
+
+ if (_dl_prebind_match_failed) {
+ for (object = _dl_objects; object != NULL;
+ object = object->next)
+ prebind_free(object);
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+
+ if (_dl_debug)
+ _dl_gettimeofday(&beforetp, NULL);
+}
+
+void
+_dl_prebind_post_resolve()
+{
+ char buf[7];
+ int i;
+ struct timeval after_tp;
+ struct timeval diff_tp;
+ elf_object_t *object;
+
+ if (_dl_debug) {
+ _dl_gettimeofday(&after_tp, NULL);
+
+ timersub(&after_tp, &beforetp, &diff_tp);
+
+ for (i = 0; i < 6; i++) {
+ buf[5-i] = (diff_tp.tv_usec % 10) + '0';
+ diff_tp.tv_usec /= 10;
+ }
+ buf[6] = '\0';
+
+ _dl_printf("relocation took %d.%s\n", diff_tp.tv_sec, buf);
+ }
+
+ for (object = _dl_objects; object != NULL; object = object->next)
+ prebind_free(object);
+
+ if (_dl_prebind_validate) {
+ if (validate_errs) {
+ _dl_printf("validate_errs %d\n", validate_errs);
+ _dl_exit(20);
+ } else {
+ _dl_exit(0);
+ }
+ }
+}
+
+void
+prebind_validate(elf_object_t *req_obj, unsigned int symidx, int flags,
+ const Elf_Sym *ref_sym)
+{
+ const Elf_Sym *sym, **this;
+ const elf_object_t *sobj;
+ const char *symn;
+ Elf_Addr ret;
+
+ /* Don't verify non-matching flags*/
+
+ sym = req_obj->dyn.symtab;
+ sym += symidx;
+ symn = req_obj->dyn.strtab + sym->st_name;
+ this = &sym;
+
+ //_dl_printf("checking %s\n", symn);
+ ret = _dl_find_symbol(symn, this, flags, ref_sym, req_obj, &sobj);
+
+ if (_dl_symcache[symidx].sym != *this ||
+ _dl_symcache[symidx].obj != sobj) {
+ _dl_printf("symbol %d mismatch on sym %s req_obj %s,\n"
+ "should be obj %s is obj %s\n",
+ symidx, symn, req_obj->load_name, sobj->load_name,
+ _dl_symcache[symidx].obj->load_name);
+ if (req_obj == sobj)
+ _dl_printf("obj %p %p\n", _dl_symcache[symidx].obj, sobj);
+ sym = _dl_symcache[symidx].obj->dyn.symtab;
+ sym += symidx;
+ symn = _dl_symcache[symidx].obj->dyn.strtab + sym->st_name;
+ _dl_printf("obj %s name %s\n",
+ _dl_symcache[symidx].obj->load_name,
+ symn);
+ }
+}
+
+#ifdef DEBUG1
+void
+prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt)
+{
+ struct symcachetab *s;
+ int i;
+
+ _dl_printf("cache: cnt %d\n", cnt);
+ for (i = 0; i < cnt; i++) {
+ s = &(symcachetab[i]);
+ _dl_printf("symidx %d: obj %d sym %d\n",
+ s->idx, s->obj_idx, s->sym_idx);
+ }
+}
+
+void
+prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numlibs, char *nametab)
+{
+ struct nameidx *n;
+ int i;
+
+ _dl_printf("libs:\n");
+ for (i = 0; i < numlibs; i++) {
+ _dl_printf("lib %d offset %d id0 %d, id1 %d\n", i,
+ nameidx[i].name, nameidx[i].id0, nameidx[i].id1);
+ }
+ for (i = 0; i < numlibs; i++) {
+ n = &(nameidx[i]);
+ _dl_printf("nametab %p n %d\n", nametab, n->name);
+ _dl_printf("lib %s %x %x\n", nametab + n->name, n->id0, n->id1);
+ }
+}
+
+void
+prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups)
+{
+ struct fixup *f;
+ int i;
+
+ _dl_printf("fixup: %d\n", numfixups);
+ for (i = 0; i < numfixups; i++) {
+ f = &(fixup[i]);
+
+ _dl_printf("idx %d obj %d sym idx %d\n",
+ f->sym, f->obj_idx, f->sym_idx);
+
+ }
+}
+
+void
+prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs)
+{
+ int i;
+
+ for (i = 0; i < numlibs; i++) {
+ //_dl_printf("lib%d off %d %s\n", i, libmap[i], strtab+libmap[i]);
+ _dl_printf("lib%d off %d\n", i, libmap[i]);
+ }
+}
+
+void
+dl_dump_footer(struct prebind_footer *footer)
+{
+// _dl_printf("base %qd\n", (long long)footer->prebind_base);
+ _dl_printf("nameidx_idx %d\n", footer->nameidx_idx);
+ _dl_printf("symcache_idx %d\n", footer->symcache_idx);
+ _dl_printf("fixupcnt_idx %d\n", footer->fixupcnt_idx);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("nametab_idx %d\n", footer->nametab_idx);
+ _dl_printf("symcache_cnt %d\n", footer->symcache_cnt);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("numlibs %d\n", footer->numlibs);
+ _dl_printf("id0 %d\n", footer->id0);
+ _dl_printf("id1 %d\n", footer->id1);
+// _dl_printf("orig_size %lld\n", (long long)footer->orig_size);
+ _dl_printf("version %d\n", footer->prebind_version);
+ _dl_printf("bind_id %c%c%c%c\n", footer->bind_id[0],
+ footer->bind_id[1], footer->bind_id[2], footer->bind_id[3]);
+}
+void
+dump_prelink(Elf_Addr base, u_long size)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ struct nameidx *nameidx;
+ struct fixup *fixup;
+ char *nametab, *id;
+ void *prebind_map;
+ int i;
+
+ id = (char *) (base+size);
+ id -= 4;
+ DL_DEB(("id %c %c %c %c\n", id[0], id[1], id[2], id[3]));
+ footer = (void *) (base+size - sizeof (struct prebind_footer));
+ dl_dump_footer(footer);
+
+ prebind_map = (void *)base;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ symcachetab = prebind_map + footer->symcache_idx;
+ fixupidx = prebind_map + footer->fixup_idx;
+ nametab = prebind_map + footer->nametab_idx;
+ fixupcnt = prebind_map + footer->fixupcnt_idx;
+ libmap = prebind_map + footer->libmap_idx;
+
+ prebind_dump_symcache(symcachetab, footer->symcache_cnt);
+ prebind_dump_nameidx(nameidx, footer->numlibs, nametab);
+ for (i = 0; i < footer->fixup_cnt; i++) {
+ _dl_printf("fixup %d cnt %d idx %d\n", i, fixupcnt[i], fixupidx[i]);
+ fixup = prebind_map + fixupidx[i];
+ prebind_dump_fixup(fixup, fixupcnt[i]);
+ }
+ prebind_dump_libmap(libmap, footer->numlibs);
+}
+#endif /* DEBUG1 */
--- /dev/null
+/* $OpenBSD: dl_prebind.h,v 1.2 2006/05/10 03:26:50 deraadt Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/exec_elf.h>
+#include "resolve.h"
+#include "prebind.h"
+
+extern char *_dl_noprebind;
+extern char *_dl_prebind_validate;
+void _dl_prebind_pre_resolve(void);
+void _dl_prebind_post_resolve(void);
+void *prebind_load_fd(int fd, const char *name);
+void prebind_load_exe(Elf_Phdr *phdp, elf_object_t *exe_obj);
+
+void prebind_validate(elf_object_t *req_obj, unsigned int symidx, int flags,
+ const Elf_Sym *ref_sym);
+extern char *_dl_prebind_validate; /* XXX */
+
+void prebind_symcache(elf_object_t *object, int pltflag);
+void prebind_free(elf_object_t *object);
+
+extern struct prebind_footer *footer;
--- /dev/null
+/* $OpenBSD: dl_printf.c,v 1.16 2010/01/02 00:59:01 deraadt Exp $ */
+
+/*-
+ * 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. 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.
+ *
+ * @(#)printf.c 8.1 (Berkeley) 6/11/93
+ */
+
+/*
+ * Scaled down version of printf(3).
+ *
+ * One additional format:
+ *
+ * The format %b is supported to decode error registers.
+ * Its usage is:
+ *
+ * printf("reg=%b\n", regval, "<base><arg>*");
+ *
+ * where <base> is the output base expressed as a control character, e.g.
+ * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
+ * the first of which gives the bit number to be inspected (origin 1), and
+ * the next characters (up to a control character, i.e. a character <= 32),
+ * give the name of the register. Thus:
+ *
+ * printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
+ *
+ * would produce output:
+ *
+ * reg=3<BITTWO,BITONE>
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include "syscall.h"
+#include "util.h"
+
+int lastfd = -1;
+#define OUTBUFSIZE 128
+static char outbuf[OUTBUFSIZE];
+static char *outptr = outbuf;
+
+static void kprintn(int, u_long, int);
+static void kdoprnt(int, const char *, va_list);
+static void _dl_flushbuf(void);
+
+static void putcharfd(int, int );
+
+static void
+putcharfd(int c, int fd)
+{
+ char b = c;
+ int len;
+
+ if (fd != lastfd) {
+ _dl_flushbuf();
+ lastfd = fd;
+ }
+ *outptr++ = b;
+ len = outptr - outbuf;
+ if ((len >= OUTBUFSIZE) || (b == '\n') || (b == '\r')) {
+ _dl_flushbuf();
+ }
+}
+
+static void
+_dl_flushbuf()
+{
+ int len = outptr - outbuf;
+ if (len != 0) {
+ _dl_write(lastfd, outbuf, len);
+ outptr = outbuf;
+ }
+}
+
+void
+_dl_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ kdoprnt(2, fmt, ap);
+ va_end(ap);
+}
+
+void
+_dl_fdprintf(int fd, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ kdoprnt(fd, fmt, ap);
+ va_end(ap);
+}
+
+void
+_dl_vprintf(const char *fmt, va_list ap)
+{
+ kdoprnt(2, fmt, ap);
+}
+
+static void
+kdoprnt(int fd, const char *fmt, va_list ap)
+{
+ unsigned long ul;
+ int lflag, ch;
+ char *p;
+
+ for (;;) {
+ while ((ch = *fmt++) != '%') {
+ if (ch == '\0')
+ return;
+ putcharfd(ch, fd);
+ }
+ lflag = 0;
+reswitch:
+ switch (ch = *fmt++) {
+ case 'l':
+ lflag = 1;
+ goto reswitch;
+ case 'b':
+ {
+ int set, n;
+
+ ul = va_arg(ap, int);
+ p = va_arg(ap, char *);
+ kprintn(fd, ul, *p++);
+
+ if (!ul)
+ break;
+
+ for (set = 0; (n = *p++);) {
+ if (ul & (1 << (n - 1))) {
+ putcharfd(set ? ',' : '<', fd);
+ for (; (n = *p) > ' '; ++p)
+ putcharfd(n, fd);
+ set = 1;
+ } else
+ for (; *p > ' '; ++p);
+ }
+ if (set)
+ putcharfd('>', fd);
+ }
+ break;
+ case 'c':
+ ch = va_arg(ap, int);
+ putcharfd(ch & 0x7f, fd);
+ break;
+ case 's':
+ p = va_arg(ap, char *);
+ while ((ch = *p++))
+ putcharfd(ch, fd);
+ break;
+ case 'd':
+ ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
+ if ((long)ul < 0) {
+ putcharfd('-', fd);
+ ul = -(long)ul;
+ }
+ kprintn(fd, ul, 10);
+ break;
+ case 'o':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ kprintn(fd, ul, 8);
+ break;
+ case 'u':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ kprintn(fd, ul, 10);
+ break;
+ case 'p':
+ putcharfd('0', fd);
+ putcharfd('x', fd);
+ lflag += sizeof(void *)==sizeof(u_long)? 1 : 0;
+ case 'x':
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ kprintn(fd, ul, 16);
+ break;
+ case 'X':
+ {
+ int l;
+
+ ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
+ if (lflag)
+ l = (sizeof(ulong) * 8) - 4;
+ else
+ l = (sizeof(u_int) * 8) - 4;
+ while (l >= 0) {
+ putcharfd("0123456789abcdef"[(ul >> l) & 0xf], fd);
+ l -= 4;
+ }
+ break;
+ }
+ default:
+ putcharfd('%', fd);
+ if (lflag)
+ putcharfd('l', fd);
+ putcharfd(ch, fd);
+ }
+ }
+ _dl_flushbuf();
+}
+
+static void
+kprintn(int fd, unsigned long ul, int base)
+{
+ /* hold a long in base 8 */
+ char *p, buf[(sizeof(long) * NBBY / 3) + 1];
+
+ p = buf;
+ do {
+ *p++ = "0123456789abcdef"[ul % base];
+ } while (ul /= base);
+ do {
+ putcharfd(*--p, fd);
+ } while (p > buf);
+}
--- /dev/null
+/* $OpenBSD: dlfcn.c,v 1.81 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <nlist.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "sod.h"
+
+int _dl_errno;
+int _dl_tracelib;
+
+int _dl_real_close(void *handle);
+void (*_dl_thread_fnc)(int) = NULL;
+void (*_dl_bind_lock_f)(int) = NULL;
+static elf_object_t *obj_from_addr(const void *addr);
+
+void *
+dlopen(const char *libname, int flags)
+{
+ elf_object_t *object;
+ int failed = 0;
+
+ if (libname == NULL)
+ return RTLD_DEFAULT;
+
+ if ((flags & RTLD_TRACE) == RTLD_TRACE) {
+ _dl_traceld = "true";
+ _dl_tracelib = 1;
+ }
+
+ DL_DEB(("dlopen: loading: %s\n", libname));
+
+ _dl_thread_kern_stop();
+
+ if (_dl_debug_map->r_brk) {
+ _dl_debug_map->r_state = RT_ADD;
+ (*((void (*)(void))_dl_debug_map->r_brk))();
+ }
+
+ _dl_loading_object = NULL;
+
+ object = _dl_load_shlib(libname, _dl_objects, OBJTYPE_DLO, flags);
+ if (object == 0) {
+ DL_DEB(("dlopen: failed to open %s\n", libname));
+ failed = 1;
+ goto loaded;
+ }
+
+ _dl_link_dlopen(object);
+
+ if (OBJECT_REF_CNT(object) > 1) {
+ /* if opened but grpsym_list has not been created */
+ if (OBJECT_DLREF_CNT(object) == 1) {
+ /* add first object manually */
+ _dl_link_grpsym(object);
+ _dl_cache_grpsym_list(object);
+ }
+ goto loaded;
+ }
+
+ /* this add_object should not be here, XXX */
+ _dl_add_object(object);
+
+ DL_DEB(("head [%s]\n", object->load_name ));
+
+ if ((failed = _dl_load_dep_libs(object, flags, 0)) == 1) {
+ _dl_real_close(object);
+ object = NULL;
+ _dl_errno = DL_CANT_LOAD_OBJ;
+ } else {
+ int err;
+ DL_DEB(("tail %s\n", object->load_name ));
+ if (_dl_traceld) {
+ _dl_show_objects();
+ _dl_unload_shlib(object);
+ _dl_exit(0);
+ }
+ err = _dl_rtld(object);
+ if (err != 0) {
+ _dl_real_close(object);
+ _dl_errno = DL_CANT_LOAD_OBJ;
+ object = 0;
+ failed = 1;
+ } else {
+ _dl_call_init(object);
+ }
+ }
+
+loaded:
+ _dl_loading_object = NULL;
+
+ if (_dl_debug_map->r_brk) {
+ _dl_debug_map->r_state = RT_CONSISTENT;
+ (*((void (*)(void))_dl_debug_map->r_brk))();
+ }
+
+ _dl_thread_kern_go();
+
+ DL_DEB(("dlopen: %s: done (%s).\n", libname,
+ failed ? "failed" : "success"));
+
+ return((void *)object);
+}
+
+void *
+dlsym(void *handle, const char *name)
+{
+ elf_object_t *object;
+ elf_object_t *dynobj;
+ const elf_object_t *pobj;
+ void *retval;
+ const Elf_Sym *sym = NULL;
+ int flags;
+
+ if (handle == NULL || handle == RTLD_NEXT ||
+ handle == RTLD_SELF || handle == RTLD_DEFAULT) {
+ void *retaddr;
+
+ retaddr = __builtin_return_address(0); /* __GNUC__ only */
+
+ if ((object = obj_from_addr(retaddr)) == NULL) {
+ _dl_errno = DL_CANT_FIND_OBJ;
+ return(0);
+ }
+
+ if (handle == RTLD_NEXT)
+ flags = SYM_SEARCH_NEXT|SYM_PLT;
+ else if (handle == RTLD_SELF)
+ flags = SYM_SEARCH_SELF|SYM_PLT;
+ else if (handle == RTLD_DEFAULT)
+ flags = SYM_SEARCH_ALL|SYM_PLT;
+ else
+ flags = SYM_DLSYM|SYM_PLT;
+
+ } else {
+ object = (elf_object_t *)handle;
+ flags = SYM_DLSYM|SYM_PLT;
+
+ dynobj = _dl_objects;
+ while (dynobj && dynobj != object)
+ dynobj = dynobj->next;
+
+ if (!dynobj || object != dynobj) {
+ _dl_errno = DL_INVALID_HANDLE;
+ return(0);
+ }
+ }
+
+ retval = (void *)_dl_find_symbol(name, &sym,
+ flags|SYM_NOWARNNOTFOUND, NULL, object, &pobj);
+
+ if (sym != NULL) {
+ retval += sym->st_value;
+#ifdef __hppa__
+ if (ELF_ST_TYPE(sym->st_info) == STT_FUNC)
+ retval = (void *)_dl_md_plabel((Elf_Addr)retval,
+ pobj->dyn.pltgot);
+#endif
+ DL_DEB(("dlsym: %s in %s: %p\n",
+ name, object->load_name, retval));
+ } else
+ _dl_errno = DL_NO_SYMBOL;
+ return (retval);
+}
+
+int
+dlctl(void *handle, int command, void *data)
+{
+ int retval;
+
+ switch (command) {
+ case DL_SETTHREADLCK:
+ DL_DEB(("dlctl: _dl_thread_fnc set to %p\n", data));
+ _dl_thread_fnc = data;
+ retval = 0;
+ break;
+ case DL_SETBINDLCK:
+ DL_DEB(("dlctl: _dl_bind_lock_f set to %p\n", data));
+ _dl_bind_lock_f = data;
+ retval = 0;
+ break;
+ case 0x20:
+ _dl_show_objects();
+ retval = 0;
+ break;
+ case 0x21:
+ {
+ struct dep_node *n, *m;
+ elf_object_t *obj;
+ _dl_printf("Load Groups:\n");
+
+ TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
+ obj = n->data;
+ _dl_printf("%s\n", obj->load_name);
+
+ _dl_printf(" children\n");
+ TAILQ_FOREACH(m, &obj->child_list, next_sib)
+ _dl_printf("\t[%s]\n", m->data->load_name);
+
+ _dl_printf(" grpref\n");
+ TAILQ_FOREACH(m, &obj->grpref_list, next_sib)
+ _dl_printf("\t[%s]\n", m->data->load_name);
+ _dl_printf("\n");
+ }
+ retval = 0;
+ break;
+ }
+ default:
+ _dl_errno = DL_INVALID_CTL;
+ retval = -1;
+ break;
+ }
+ return (retval);
+}
+
+int
+dlclose(void *handle)
+{
+ int retval;
+
+ if (handle == RTLD_DEFAULT)
+ return 0;
+
+ _dl_thread_kern_stop();
+
+ if (_dl_debug_map->r_brk) {
+ _dl_debug_map->r_state = RT_DELETE;
+ (*((void (*)(void))_dl_debug_map->r_brk))();
+ }
+
+ retval = _dl_real_close(handle);
+
+
+ if (_dl_debug_map->r_brk) {
+ _dl_debug_map->r_state = RT_CONSISTENT;
+ (*((void (*)(void))_dl_debug_map->r_brk))();
+ }
+ _dl_thread_kern_go();
+ return (retval);
+}
+
+int
+_dl_real_close(void *handle)
+{
+ elf_object_t *object;
+ elf_object_t *dynobj;
+
+ object = (elf_object_t *)handle;
+
+ dynobj = _dl_objects;
+ while (dynobj && dynobj != object)
+ dynobj = dynobj->next;
+
+ if (!dynobj || object != dynobj) {
+ _dl_errno = DL_INVALID_HANDLE;
+ return (1);
+ }
+
+ if (object->opencount == 0) {
+ _dl_errno = DL_INVALID_HANDLE;
+ return (1);
+ }
+
+ object->opencount--;
+ _dl_notify_unload_shlib(object);
+ _dl_run_all_dtors();
+ _dl_unload_shlib(object);
+ _dl_cleanup_objects();
+ return (0);
+}
+
+
+/*
+ * Return a character string describing the last dl... error occurred.
+ */
+const char *
+dlerror(void)
+{
+ const char *errmsg;
+
+ switch (_dl_errno) {
+ case 0: /* NO ERROR */
+ errmsg = NULL;
+ break;
+ case DL_NOT_FOUND:
+ errmsg = "File not found";
+ break;
+ case DL_CANT_OPEN:
+ errmsg = "Can't open file";
+ break;
+ case DL_NOT_ELF:
+ errmsg = "File not an ELF object";
+ break;
+ case DL_CANT_OPEN_REF:
+ errmsg = "Can't open referenced object";
+ break;
+ case DL_CANT_MMAP:
+ errmsg = "Can't map ELF object";
+ break;
+ case DL_INVALID_HANDLE:
+ errmsg = "Invalid handle";
+ break;
+ case DL_NO_SYMBOL:
+ errmsg = "Unable to resolve symbol";
+ break;
+ case DL_INVALID_CTL:
+ errmsg = "Invalid dlctl() command";
+ break;
+ case DL_NO_OBJECT:
+ errmsg = "No shared object contains address";
+ break;
+ case DL_CANT_FIND_OBJ:
+ errmsg = "Cannot determine caller's shared object";
+ break;
+ case DL_CANT_LOAD_OBJ:
+ errmsg = "Cannot load specified object";
+ break;
+ default:
+ errmsg = "Unknown error";
+ }
+
+ _dl_errno = 0;
+ return (errmsg);
+}
+
+void
+_dl_tracefmt(int fd, elf_object_t *object, const char *fmt1, const char *fmt2,
+ const char *objtypename)
+{
+ const char *fmt;
+ struct sod sd;
+ int i;
+ char *s;
+
+ s = _dl_strrchr(object->load_name, '/');
+ if (s != NULL)
+ s++;
+ else
+ s = object->load_name;
+ _dl_build_sod(s, &sd);
+ fmt = sd.sod_library ? fmt1 : fmt2;
+
+ for (i = 0; fmt[i]; i++) {
+ if (fmt[i] != '%' && fmt[i] != '\\') {
+ _dl_fdprintf(fd, "%c", fmt[i]);
+ continue;
+ }
+ if (fmt[i] == '%') {
+ i++;
+ switch (fmt[i]) {
+ case '\0':
+ return;
+ case '%':
+ _dl_fdprintf(fd, "%c", '%');
+ break;
+ case 'A':
+ _dl_fdprintf(fd, "%s", _dl_traceprog ?
+ _dl_traceprog : "");
+ break;
+ case 'a':
+ _dl_fdprintf(fd, "%s", _dl_progname);
+ break;
+ case 'e':
+ _dl_fdprintf(fd, "%lX",
+ (void *)(object->load_base +
+ object->load_size));
+ break;
+ case 'g':
+ _dl_fdprintf(fd, "%d", object->grprefcount);
+ break;
+ case 'm':
+ _dl_fdprintf(fd, "%d", sd.sod_major);
+ break;
+ case 'n':
+ _dl_fdprintf(fd, "%d", sd.sod_minor);
+ break;
+ case 'O':
+ _dl_fdprintf(fd, "%d", object->opencount);
+ break;
+ case 'o':
+ _dl_fdprintf(fd, "%s", sd.sod_name);
+ break;
+ case 'p':
+ _dl_fdprintf(fd, "%s", object->load_name);
+ break;
+ case 'r':
+ _dl_fdprintf(fd, "%d", object->refcount);
+ break;
+ case 't':
+ _dl_fdprintf(fd, "%s", objtypename);
+ break;
+ case 'x':
+ _dl_fdprintf(fd, "%lX", object->load_base);
+ break;
+ }
+ }
+ if (fmt[i] == '\\') {
+ i++;
+ switch (fmt[i]) {
+ case '\0':
+ return;
+ case 'n':
+ _dl_fdprintf(fd, "%c", '\n');
+ break;
+ case 'r':
+ _dl_fdprintf(fd, "%c", '\r');
+ break;
+ case 't':
+ _dl_fdprintf(fd, "%c", '\t');
+ break;
+ default:
+ _dl_fdprintf(fd, "%c", fmt[i]);
+ break;
+ }
+ }
+ }
+ _dl_free((void *)sd.sod_name);
+}
+
+void
+_dl_show_objects(void)
+{
+ elf_object_t *object;
+ char *objtypename;
+ int outputfd;
+ char *pad;
+ const char *fmt1, *fmt2;
+
+ object = _dl_objects;
+ if (_dl_traceld)
+ outputfd = STDOUT_FILENO;
+ else
+ outputfd = STDERR_FILENO;
+
+ if (sizeof(long) == 8)
+ pad = " ";
+ else
+ pad = "";
+
+ fmt1 = _dl_tracefmt1 ? _dl_tracefmt1 :
+ "\t%x %e %t %O %r %g %p\n";
+ fmt2 = _dl_tracefmt2 ? _dl_tracefmt2 :
+ "\t%x %e %t %O %r %g %p\n";
+
+ if (_dl_tracefmt1 == NULL && _dl_tracefmt2 == NULL)
+ _dl_fdprintf(outputfd, "\tStart %s End %s Type Open Ref GrpRef Name\n",
+ pad, pad);
+
+ if (_dl_tracelib) {
+ for (; object != NULL; object = object->next)
+ if (object->obj_type == OBJTYPE_LDR) {
+ object = object->next;
+ break;
+ }
+ }
+
+ for (; object != NULL; object = object->next) {
+ switch (object->obj_type) {
+ case OBJTYPE_LDR:
+ objtypename = "rtld";
+ break;
+ case OBJTYPE_EXE:
+ objtypename = "exe ";
+ break;
+ case OBJTYPE_LIB:
+ objtypename = "rlib";
+ break;
+ case OBJTYPE_DLO:
+ objtypename = "dlib";
+ break;
+ default:
+ objtypename = "????";
+ break;
+ }
+ _dl_tracefmt(outputfd, object, fmt1, fmt2, objtypename);
+ }
+
+ if (_dl_symcachestat_lookups != 0)
+ DL_DEB(("symcache lookups %d hits %d ratio %d% hits\n",
+ _dl_symcachestat_lookups, _dl_symcachestat_hits,
+ (_dl_symcachestat_hits * 100) /
+ _dl_symcachestat_lookups));
+}
+
+void
+_dl_thread_bind_lock(int what, sigset_t *omask)
+{
+ if (! what) {
+ sigset_t nmask;
+
+ sigfillset(&nmask);
+ _dl_sigprocmask(SIG_BLOCK, &nmask, omask);
+ }
+ if (_dl_bind_lock_f != NULL)
+ (*_dl_bind_lock_f)(what);
+ if (what)
+ _dl_sigprocmask(SIG_SETMASK, omask, NULL);
+}
+
+void
+_dl_thread_kern_stop(void)
+{
+ if (_dl_thread_fnc != NULL)
+ (*_dl_thread_fnc)(0);
+}
+
+void
+_dl_thread_kern_go(void)
+{
+ if (_dl_thread_fnc != NULL)
+ (*_dl_thread_fnc)(1);
+}
+
+int
+dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *data),
+ void *data)
+{
+ elf_object_t *object;
+ struct dl_phdr_info info;
+ int retval = -1;
+
+ for (object = _dl_objects; object != NULL; object = object->next) {
+ if (object->phdrp == NULL)
+ continue;
+
+ info.dlpi_addr = object->obj_base;
+ info.dlpi_name = object->load_name;
+ info.dlpi_phdr = object->phdrp;
+ info.dlpi_phnum = object->phdrc;
+ retval = callback(&info, sizeof (struct dl_phdr_info), data);
+ if (retval)
+ break;
+ }
+
+ return retval;
+}
+
+static elf_object_t *
+obj_from_addr(const void *addr)
+{
+ elf_object_t *dynobj;
+ Elf_Phdr *phdrp;
+ int phdrc;
+ Elf_Addr start;
+ int i;
+
+ for (dynobj = _dl_objects; dynobj != NULL; dynobj = dynobj->next) {
+ if (dynobj->phdrp == NULL)
+ continue;
+
+ phdrp = dynobj->phdrp;
+ phdrc = dynobj->phdrc;
+
+ for (i = 0; i < phdrc; i++, phdrp++) {
+ if (phdrp->p_type == PT_LOAD) {
+ start = dynobj->obj_base + phdrp->p_vaddr;
+ if ((Elf_Addr)addr >= start &&
+ (Elf_Addr)addr < start + phdrp->p_memsz)
+ return dynobj;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int
+dladdr(const void *addr, Dl_info *info)
+{
+ const elf_object_t *object;
+ const Elf_Sym *sym;
+ void *symbol_addr;
+ u_int32_t symoffset;
+
+ object = obj_from_addr(addr);
+
+ if (object == NULL) {
+ _dl_errno = DL_NO_OBJECT;
+ return 0;
+ }
+
+ info->dli_fname = (char *)object->load_name;
+ info->dli_fbase = (void *)object->load_base;
+ info->dli_sname = NULL;
+ info->dli_saddr = (void *)0;
+
+ /*
+ * Walk the symbol list looking for the symbol whose address is
+ * closest to the address sent in.
+ */
+ for (symoffset = 0; symoffset < object->nchains; symoffset++) {
+ sym = object->dyn.symtab + symoffset;
+
+ /*
+ * For skip the symbol if st_shndx is either SHN_UNDEF or
+ * SHN_COMMON.
+ */
+ if (sym->st_shndx == SHN_UNDEF || sym->st_shndx == SHN_COMMON)
+ continue;
+
+ /*
+ * If the symbol is greater than the specified address, or if
+ * it is further away from addr than the current nearest
+ * symbol, then reject it.
+ */
+ symbol_addr = (void *)(object->obj_base + sym->st_value);
+ if (symbol_addr > addr || symbol_addr < info->dli_saddr)
+ continue;
+
+ /* Update our idea of the nearest symbol. */
+ info->dli_sname = object->dyn.strtab + sym->st_name;
+ info->dli_saddr = symbol_addr;
+
+ /* Exact match? */
+ if (info->dli_saddr == addr)
+ break;
+ }
+
+ return 1;
+}
--- /dev/null
+/Makefile.inc/1.1/Tue May 25 21:48:00 2004//
+/syscall.h/1.4/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.5/Sat Jan 2 15:01:02 2010//
+/ldasm.S/1.6/Sat Feb 6 00:59:40 2010//
+/rtld_machine.c/1.22/Sun Jun 27 18:29:54 2010//
+D
--- /dev/null
+src/libexec/ld.so/hppa
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.1 2004/05/25 21:48:00 mickey Exp $
+
+CFLAGS += -fpic
+AFLAGS += -fpic
+#ELF_LDFLAGS=-t
+LDADD= `$(CC) -print-libgcc-file-name`
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.5 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2004 Michael Shalayeff
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _HPPA_ARCHDEP_H_
+#define _HPPA_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_PARISC /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf_Rela
+#define RELSIZE sizeof(Elf_Rela)
+
+#include <sys/mman.h>
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void *
+_dl_mquery(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mquery, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ /* HPPA does no REL type relocations */
+ _dl_exit(20);
+}
+
+/*
+ * !!!!! WARNING: THIS CODE CANNOT HANDLE ld.so RELOCATIONS OF THE FORM
+ * 0000bde8 R_PARISC_DIR32 .data+0x00000048
+ * these can be caused by static intialization foo = &bar;
+ * prepare to code around this problem, or fix it here.
+ */
+static inline void
+RELOC_RELA(Elf_RelA *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ /* XXX fille out _sl ??? */
+ if (ELF_R_TYPE(r->r_info) == RELOC_DIR32) {
+ *p = v + r->r_addend;
+ } else if (ELF_R_TYPE(r->r_info) == RELOC_IPLT) {
+ p[0] = v + s->st_value + r->r_addend;
+ p[1] = (Elf_Addr)pltgot;
+ } else if (ELF_R_TYPE(r->r_info) == RELOC_PLABEL32) {
+ *p = v + s->st_value + r->r_addend;
+ } else {
+ _dl_printf("unknown bootstrap relocation\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define MD_CALL(sobj, func, arg) \
+ hppa_call((arg), (sobj)->dyn.pltgot, (func))
+
+#define MD_ATEXIT(sobj, sym, func) \
+ MD_CALL((sobj), (void (*)())((sobj)->obj_base + (sym)->st_value), &_hppa_dl_dtors)
+
+#define GOT_PERMS PROT_READ
+
+void _hppa_dl_dtors(void);
+void hppa_call(void *, Elf_Addr *, void (*)(void));
+Elf_Addr _dl_md_plabel(Elf_Addr, Elf_Addr *);
+
+#endif /* _HPPA_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.6 2010/02/01 21:36:00 kettenis Exp $ */
+
+/*
+ * Copyright (c) 2004 Michael Shalayeff
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR OR HIS RELATIVES 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 MIND, 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.
+ */
+
+#include <sys/syscall.h>
+#include <machine/asm.h>
+#define _LOCORE
+#include <machine/frame.h>
+#include <machine/vmparam.h>
+#undef _LOCORE
+
+ENTRY(_dl_start,32)
+ copy r3, r1
+ copy sp, r3
+ stwm r1, HPPA_FRAME_SIZE+16*4(sp)
+
+ stw %arg0, HPPA_FRAME_ARG(0)(r3) /* ps_strings */
+
+#define ADDR(s,r) \
+ bl 4, t1 !\
+ depi 0, 31, 2, t1 /* kill pl bits */ !\
+ b s /* cold brunch -- never done */ !\
+ ldw 0(t1), t2 /* cat(w,w1,w2{10},w2{0..9}) << 2 */ !\
+ extru t2, 28, 10, t3 /* w2{0..9} */ !\
+ extru t2, 26, 16, r /* w1 */ !\
+ dep t3, 31, 11, r !\
+ extru,= t2, 31, 1, r0 /* w */ !\
+ depi 1, 15, 1, r !\
+ extru,= t2, 29, 1, r0 /* w2{10} */ !\
+ depi 1, 21, 1, r !\
+ sh2add r, t1, r /* plus the base */ !\
+ addi 8, r, r /* bl target is -8 */
+
+ ADDR(_GLOBAL_OFFSET_TABLE_, r19)
+ ADDR(_DYNAMIC, arg2)
+ stw arg2, HPPA_FRAME_ARG(1)(r3)
+
+ /* make sure to get a fault until it's set proper */
+ ldi -1, %dp
+
+ ldw 0(arg0), arg0
+ ldo 4(r3), arg1 /* dl_data */
+ bl _dl_boot_bind, rp
+ ldo -4(arg0), arg0
+
+ ldw HPPA_FRAME_ARG(1)(r3), arg1 /* &_DYNAMIC */
+ ldw HPPA_FRAME_ARG(0)(r3), arg3 /* ps_strings */
+ ldw 0(r19), arg2
+ sub arg1, arg2, arg2 /* loff */
+
+ ldw 0(arg3), arg0 /* argv */
+ ldw 8(arg3), arg1 /* envp */
+
+ bl _dl_boot, rp
+ ldo 4(r3), arg3 /* dl_data */
+
+ ldw HPPA_FRAME_ARG(0)(r3), arg0 /* ps_strings */
+ /* ??? cleanup, arg1 */
+ /* ??? obj, arg2 */
+
+ ldo HPPA_FRAME_SIZE(r3), sp
+ copy r0, rp
+ bv r0(ret0)
+ ldwm -HPPA_FRAME_SIZE(sp), r3
+EXIT(_dl_start)
+
+/*
+ * void _hppa_dl_dtors(void);
+ */
+ENTRY(_hppa_dl_dtors,0)
+ ADDR(_GLOBAL_OFFSET_TABLE_, r19)
+ b _dl_dtors
+ nop
+EXIT(_hppa_dl_dtors)
+
+LEAF_ENTRY(_hppa_dl_set_dp)
+ bv r0(rp)
+ copy arg0, r27
+EXIT(_hppa_dl_set_dp)
+
+/*
+ * void hppa_call(void (*arg)(void), void *r19, Elf_Addr func);
+ */
+ENTRY(hppa_call,0)
+ copy r3, r1
+ copy sp, r3
+ stwm r1, HPPA_FRAME_SIZE(sp)
+ stw r19, HPPA_FRAME_SL(r3)
+ stw rp, HPPA_FRAME_CRP(r3)
+ copy arg1, r19
+ .call
+ blr r0, rp
+ bv,n (arg2)
+ nop
+ ldw HPPA_FRAME_CRP(r3), rp
+ ldw HPPA_FRAME_SL(r3), r19
+ ldo HPPA_FRAME_SIZE(r3), sp
+ bv r0(rp)
+ ldwm -HPPA_FRAME_SIZE(sp), r3
+EXIT(hppa_call)
+
+/*
+ * This is a magic branch instruction that is used by GCC's
+ * __canonicalize_funcptr_for_compare() function to fixup relocations
+ * in order to do function pointer comparisons.
+ */
+ bl _dl_bind, rp
+
+ENTRY(_dl_bind_start,32)
+ copy r3, r1
+ copy sp, r3
+ stwm r1, HPPA_FRAME_SIZE(sp)
+
+ stw rp, HPPA_FRAME_CRP(r3)
+ stw arg0, HPPA_FRAME_ARG(0)(r3)
+ stw arg1, HPPA_FRAME_ARG(1)(r3)
+ stw arg2, HPPA_FRAME_ARG(2)(r3)
+ stw arg3, HPPA_FRAME_ARG(3)(r3)
+ stw t1, 4(r3)
+ stw ret0, 8(r3)
+ stw ret1, 12(r3)
+
+ ldw 12(r20), arg0
+ copy r19, arg1
+
+ bl _dl_bind, rp
+ copy r21, r19
+
+ /* load &func and sl from plt entry returned */
+ ldw 0(ret0), r21
+ ldw 4(ret0), r19
+
+ ldw HPPA_FRAME_ARG(0)(r3), arg0
+ ldw HPPA_FRAME_ARG(1)(r3), arg1
+ ldw HPPA_FRAME_ARG(2)(r3), arg2
+ ldw HPPA_FRAME_ARG(3)(r3), arg3
+ ldw 4(r3), t1
+ ldw 8(r3), ret0
+ ldw 12(r3), ret1
+
+ ldw HPPA_FRAME_CRP(r3), rp
+ ldo HPPA_FRAME_SIZE(r3), sp
+ bv r0(r21)
+ ldwm -HPPA_FRAME_SIZE(sp), r3
+EXIT(_dl_bind_start)
+
+#define SYSCALL(x) !\
+ stw rp, HPPA_FRAME_ERP(sr0,sp) !\
+ ldil L%SYSCALLGATE, r1 !\
+ ble 4(sr7, r1) !\
+ ldi __CONCAT(SYS_,x), t1 !\
+ comb,<> r0, t1, _dl_sysexit !\
+ ldw HPPA_FRAME_ERP(sr0,sp), rp
+
+_dl_sysexit
+ bv r0(rp)
+ sub r0, ret0, ret0
+
+ENTRY(_dl_close,0)
+ SYSCALL(close)
+ bv r0(rp)
+ nop
+EXIT(_dl_close)
+
+ENTRY(_dl_exit,0)
+ SYSCALL(exit)
+ bv r0(rp)
+ nop
+EXIT(_dl_exit)
+
+ENTRY(_dl_issetugid,0)
+ SYSCALL(issetugid)
+ bv r0(rp)
+ nop
+EXIT(_dl_issetugid)
+
+ENTRY(_dl__syscall,0)
+ SYSCALL(__syscall)
+ bv r0(rp)
+ nop
+EXIT(_dl__syscall)
+
+ENTRY(_dl_munmap,0)
+ SYSCALL(munmap)
+ bv r0(rp)
+ nop
+EXIT(_dl_munmap)
+
+ENTRY(_dl_mprotect,0)
+ SYSCALL(mprotect)
+ bv r0(rp)
+ nop
+EXIT(_dl_mprotect)
+
+ENTRY(_dl_open,0)
+ SYSCALL(open)
+ bv r0(rp)
+ nop
+EXIT(_dl_open)
+
+ENTRY(_dl_read,0)
+ SYSCALL(read)
+ bv r0(rp)
+ nop
+EXIT(_dl_read)
+
+ENTRY(_dl_write,0)
+ SYSCALL(write)
+ bv r0(rp)
+ nop
+EXIT(_dl_write)
+
+ENTRY(_dl_stat,0)
+ SYSCALL(stat)
+ bv r0(rp)
+ nop
+EXIT(_dl_stat)
+
+ENTRY(_dl_fstat,0)
+ SYSCALL(fstat)
+ bv r0(rp)
+ nop
+EXIT(_dl_fstat)
+
+ENTRY(_dl_fcntl,0)
+ SYSCALL(fcntl)
+ bv r0(rp)
+ nop
+EXIT(_dl_fcntl)
+
+ENTRY(_dl_sysctl,0)
+ SYSCALL(__sysctl)
+ bv r0(rp)
+ nop
+EXIT(_dl_issetugid)
+
+ENTRY(_dl_getdirentries,0)
+ SYSCALL(getdirentries)
+ bv r0(rp)
+ nop
+EXIT(_dl_getdirentries)
+
+ENTRY(_dl_gettimeofday,0)
+ SYSCALL(gettimeofday)
+ bv r0(rp)
+ nop
+EXIT(_dl_gettimeofday)
+
+ENTRY(_dl_sigprocmask,0)
+ stw arg2, HPPA_FRAME_ARG(2)(sp)
+
+ comb,<>,n r0, arg1, _dl_sigprocmask$nblock
+
+ b _dl_sigprocmask$call
+ ldi 1, arg0
+
+_dl_sigprocmask$nblock
+ ldw 0(arg1), arg1
+ stw arg1, HPPA_FRAME_ARG(1)(sp)
+
+_dl_sigprocmask$call
+ SYSCALL(sigprocmask)
+
+ ldw HPPA_FRAME_ARG(2)(sp), arg2
+ add,= r0, arg2, r0
+ stw ret0, 0(arg2)
+ bv r0(rp)
+ copy r0, ret0
+EXIT(_dl_sigprocmask)
+
+ .end
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.22 2010/06/05 21:40:58 miod Exp $ */
+
+/*
+ * Copyright (c) 2004 Michael Shalayeff
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ * Copyright (c) 1999 Dale Rahn
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR OR HIS RELATIVES 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 MIND, 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.
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/tree.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+#include <string.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+typedef
+struct hppa_plabel {
+ Elf_Addr pc;
+ Elf_Addr *sl;
+ SPLAY_ENTRY(hppa_plabel) node;
+} hppa_plabel_t;
+SPLAY_HEAD(_dl_md_plabels, hppa_plabel) _dl_md_plabel_root;
+
+void _hppa_dl_set_dp(Elf_Addr *dp); /* from ldasm.S */
+
+static __inline int
+_dl_md_plcmp(hppa_plabel_t *a, hppa_plabel_t *b)
+{
+ if (a->sl < b->sl)
+ return (-1);
+ else if (a->sl > b->sl)
+ return (1);
+ else if (a->pc < b->pc)
+ return (-1);
+ else if (a->pc > b->pc)
+ return (1);
+ else
+ return (0);
+}
+
+SPLAY_PROTOTYPE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
+SPLAY_GENERATE(_dl_md_plabels, hppa_plabel, node, _dl_md_plcmp);
+
+Elf_Addr
+_dl_md_plabel(Elf_Addr pc, Elf_Addr *sl)
+{
+ hppa_plabel_t key, *p;
+
+ key.pc = pc;
+ key.sl = sl;
+ p = SPLAY_FIND(_dl_md_plabels, &_dl_md_plabel_root, &key);
+ if (p == NULL) {
+ p = _dl_malloc(sizeof(*p));
+ if (p == NULL)
+ _dl_exit(5);
+ p->pc = pc;
+ p->sl = sl;
+ SPLAY_INSERT(_dl_md_plabels, &_dl_md_plabel_root, p);
+ }
+
+ return (Elf_Addr)p | 2;
+}
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ Elf_RelA *rela;
+ Elf_Addr loff;
+ int i, numrela, fails = 0;
+ size_t size;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
+ rela = (Elf_RelA *)(object->Dyn.info[rel]);
+
+#ifdef DEBUG
+ DL_DEB(("object %s relasz %x, numrela %x loff %x\n",
+ object->load_name, object->Dyn.info[relasz], numrela, loff));
+#endif
+
+ if (rela == NULL)
+ return (0);
+
+ /* either it's an ld bug or a wacky hpux abi */
+ if (!object->dyn.pltgot)
+ object->Dyn.info[DT_PLTGOT] += loff;
+
+ if (object->dyn.init && !((Elf_Addr)object->dyn.init & 2)) {
+ Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.init,
+ object->dyn.pltgot);
+#ifdef DEBUG
+ DL_DEB(("PLABEL32: %p:%p(_init) -> 0x%x in %s\n",
+ object->dyn.init, object->dyn.pltgot,
+ addr, object->load_name));
+#endif
+ object->dyn.init = (void *)addr;
+ }
+
+ if (object->dyn.fini && !((Elf_Addr)object->dyn.fini & 2)) {
+ Elf_Addr addr = _dl_md_plabel((Elf_Addr)object->dyn.fini,
+ object->dyn.pltgot);
+#ifdef DEBUG
+ DL_DEB(("PLABEL32: %p:%p(_fini) -> 0x%x in %s\n",
+ object->dyn.fini, object->dyn.pltgot,
+ addr, object->load_name));
+#endif
+ object->dyn.fini = (void *)addr;
+ }
+
+ /*
+ * this is normally done by the crt0 code but we have to make
+ * sure it's set here to allow constructors to call functions
+ * that are overridden in the user binary (that are un-pic)
+ */
+ if (object->obj_type == OBJTYPE_EXE)
+ _hppa_dl_set_dp(object->dyn.pltgot);
+
+ for (i = 0; i < numrela; i++, rela++) {
+ const elf_object_t *sobj;
+ const Elf_Sym *sym, *this;
+ Elf_Addr *pt, ooff;
+ const char *symn;
+ int type;
+
+ type = ELF_R_TYPE(rela->r_info);
+ if (type == RELOC_NONE)
+ continue;
+
+ sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
+ sobj = object;
+ symn = object->dyn.strtab + sym->st_name;
+ pt = (Elf_Addr *)(rela->r_offset + loff);
+
+ ooff = 0;
+ this = NULL;
+ if (ELF_R_SYM(rela->r_info) && sym->st_name) {
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rela->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == RELOC_IPLT) ? SYM_PLT: SYM_NOTPLT),
+ sym, &sobj);
+ if (this == NULL) {
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
+ fails++;
+ continue;
+ }
+ }
+
+#ifdef DEBUG
+ DL_DEB(("*pt=%x r_addend=%x r_sym=%x\n",
+ *pt, rela->r_addend, ELF_R_SYM(rela->r_info)));
+#endif
+
+ switch (type) {
+ case RELOC_DIR32:
+ if (ELF_R_SYM(rela->r_info) && sym->st_name) {
+ *pt = ooff + this->st_value + rela->r_addend;
+#ifdef DEBUG
+ DL_DEB(("[%x]DIR32: %s:%s -> 0x%x in %s\n",
+ i, symn, object->load_name,
+ *pt, sobj->load_name));
+#endif
+ } else {
+ /*
+ * XXX should objects ever get their
+ * sections loaded insequential this
+ * would have to get a section number
+ * (ELF_R_SYM(rela->r_info))-1 and then:
+ * *pt = sect->addr + rela->r_addend;
+ */
+ if (ELF_R_SYM(rela->r_info))
+ *pt += loff;
+ else
+ *pt += loff + rela->r_addend;
+#ifdef DEBUG
+ DL_DEB(("[%x]DIR32: %s @ 0x%x\n", i,
+ object->load_name, *pt));
+#endif
+ }
+ break;
+
+ case RELOC_PLABEL32:
+ if (ELF_R_SYM(rela->r_info)) {
+ if (ELF_ST_TYPE(this->st_info) != STT_FUNC) {
+ DL_DEB(("[%x]PLABEL32: bad\n", i));
+ break;
+ }
+ *pt = _dl_md_plabel(sobj->obj_base +
+ this->st_value + rela->r_addend,
+ sobj->dyn.pltgot);
+#ifdef DEBUG
+ DL_DEB(("[%x]PLABEL32: %s:%s -> 0x%x in %s\n",
+ i, symn, object->load_name,
+ *pt, sobj->load_name));
+#endif
+ } else {
+ *pt = loff + rela->r_addend;
+#ifdef DEBUG
+ DL_DEB(("[%x]PLABEL32: %s @ 0x%x\n", i,
+ object->load_name, *pt));
+#endif
+ }
+ break;
+
+ case RELOC_IPLT:
+ if (ELF_R_SYM(rela->r_info)) {
+ pt[0] = ooff + this->st_value + rela->r_addend;
+ pt[1] = (Elf_Addr)sobj->dyn.pltgot;
+#ifdef DEBUG
+ DL_DEB(("[%x]IPLT: %s:%s -> 0x%x:0x%x in %s\n",
+ i, symn, object->load_name,
+ pt[0], pt[1], sobj->load_name));
+#endif
+ } else {
+ pt[0] = loff + rela->r_addend;
+ pt[1] = (Elf_Addr)object->dyn.pltgot;
+#ifdef DEBUG
+ DL_DEB(("[%x]IPLT: %s @ 0x%x:0x%x\n", i,
+ object->load_name, pt[0], pt[1]));
+#endif
+ }
+ break;
+
+ case RELOC_COPY:
+ {
+ const Elf32_Sym *cpysrc = NULL;
+ size = sym->st_size;
+ ooff = _dl_find_symbol(symn, &cpysrc,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ sym, object, NULL);
+ if (cpysrc) {
+ _dl_bcopy((void *)(ooff + cpysrc->st_value),
+ pt, sym->st_size);
+#ifdef DEBUG
+ DL_DEB(("[%x]COPY: %s[%x]:%s -> %p[%x] in %s\n",
+ i, symn, ooff + cpysrc->st_value,
+ object->load_name, pt, sym->st_size,
+ sobj->load_name));
+#endif
+ } else
+ DL_DEB(("[%x]COPY: no sym\n", i));
+ break;
+ }
+ default:
+ DL_DEB(("[%x]UNKNOWN(%d): type=%d off=0x%lx "
+ "addend=0x%lx rel=0x%x\n", i, type,
+ ELF_R_TYPE(rela->r_info), rela->r_offset,
+ rela->r_addend, *pt));
+ break;
+ }
+ }
+
+ return (fails);
+}
+
+extern void _dl_bind_start(void);
+
+#define PLT_STUB_SIZE (7 * 4)
+#define PLT_ENTRY_SIZE (2 * 4)
+#define PLT_STUB_GOTOFF (4 * 4)
+
+#define PLT_STUB_MAGIC1 0x00c0ffee
+#define PLT_STUB_MAGIC2 0xdeadbeef
+
+#define PLT_STUB_INSN1 0x0e801081 /* ldw 0(%r20), %r1 */
+#define PLT_STUB_INSN2 0xe820c000 /* bv %r0(%r1) */
+
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ Elf_RelA *rela;
+ Elf_Addr ooff;
+ int i, numrela, fails = 0;
+ const Elf_Sym *this;
+
+ if (object->dyn.pltrel != DT_RELA)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL );
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ register Elf_Addr ltp __asm ("%r19");
+ Elf_Addr *got = NULL;
+
+ rela = (Elf_RelA *)(object->dyn.jmprel);
+ numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
+ ooff = object->obj_base;
+
+ /*
+ * Find the PLT stub by looking at all the
+ * relocations. The PLT stub should be at the end of
+ * the .plt section so we start with the last
+ * relocation, since the linker should have emitted
+ * them in order.
+ */
+ for (i = numrela - 1; i >= 0; i--) {
+ got = (Elf_Addr *)(ooff + rela[i].r_offset +
+ PLT_ENTRY_SIZE + PLT_STUB_SIZE);
+ if (got[-2] == PLT_STUB_MAGIC1 ||
+ got[-1] == PLT_STUB_MAGIC2)
+ break;
+ got = NULL;
+ }
+ if (got == NULL)
+ return (1);
+
+ /*
+ * Patch up the PLT stub such that it doesn't clobber
+ * %r22, which is used to pass on the errno values
+ * from failed system calls to __cerrno() in libc.
+ */
+ got[-7] = PLT_STUB_INSN1;
+ got[-6] = PLT_STUB_INSN2;
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-7]));
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-6]));
+ __asm __volatile("sync");
+#if 0
+ __asm __volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7]));
+ __asm __volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6]));
+#else
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-7]));
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-6]));
+#endif
+ __asm __volatile("sync");
+
+ /*
+ * Fill in the PLT stub such that it invokes the
+ * _dl_bind_start() trampoline to fix up the
+ * relocation.
+ */
+ got[1] = (Elf_Addr)object;
+ got[-2] = (Elf_Addr)&_dl_bind_start;
+ got[-1] = ltp;
+ /*
+ * Even though we didn't modify any instructions it
+ * seems we still need to syncronize the caches.
+ * There may be instructions in the same cache line
+ * and they end up being corrupted otherwise.
+ */
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-2]));
+ __asm __volatile("fdc 0(%0)" :: "r" (&got[-1]));
+ __asm __volatile("sync");
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-2]));
+ __asm __volatile("fic 0(%0)" :: "r" (&got[-1]));
+ __asm __volatile("sync");
+ for (i = 0; i < numrela; i++, rela++) {
+ Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset);
+
+ if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) {
+ _dl_printf("unexpected reloc 0x%x\n",
+ ELF_R_TYPE(rela->r_info));
+ return (1);
+ }
+
+ if (ELF_R_SYM(rela->r_info)) {
+ r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF;
+ r_addr[1] = (Elf_Addr) (rela -
+ (Elf_RelA *)object->dyn.jmprel);
+ } else {
+ r_addr[0] = ooff + rela->r_addend;
+ r_addr[1] = (Elf_Addr)object->dyn.pltgot;
+ }
+ }
+ }
+ if (object->got_size != 0)
+ _dl_mprotect((void *)object->got_start, object->got_size,
+ GOT_PERMS|PROT_EXEC);
+
+ return (fails);
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int reloff)
+{
+ const elf_object_t *sobj;
+ const Elf_Sym *sym, *this;
+ Elf_Addr *addr, ooff;
+ const char *symn;
+ Elf_Addr value;
+ Elf_RelA *rela;
+ sigset_t savedmask;
+
+ rela = (Elf_RelA *)object->dyn.jmprel + reloff;
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rela->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+ DL_DEB(("%s: %s\n", symn, sobj->load_name));
+
+ value = ooff + this->st_value + rela->r_addend;
+
+ /* if PLT+GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ /* mprotect the actual modified region, not the whole plt */
+ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 2,
+ PROT_READ|PROT_WRITE);
+ }
+
+ addr[0] = value;
+ addr[1] = (Elf_Addr)sobj->dyn.pltgot;
+
+ /* if PLT is (to be protected, change back to RO */
+ if (object->got_size != 0) {
+ /* mprotect the actual modified region, not the whole plt */
+ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3,
+ PROT_READ|PROT_EXEC);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return ((Elf_Addr)addr);
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.4 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void *, unsigned int);
+int _dl_open(const char *, unsigned int);
+int _dl_read(int, const char *, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/Makefile.inc/1.1/Fri Aug 23 23:02:48 2002//
+/ldasm.S/1.11/Mon Feb 16 14:17:51 2009//
+/syscall.h/1.8/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.11/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.24/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/i386
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.1 2002/08/23 23:02:48 drahn Exp $
+
+CFLAGS += -fPIC
+AFLAGS += -fpic
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.11 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _I386_ARCHDEP_H_
+#define _I386_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_386 /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <sys/mman.h>
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void *
+_dl_mquery(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mquery, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+
+static inline void
+RELOC_REL(Elf32_Rel *r, const Elf32_Sym *s, Elf32_Addr *p, unsigned long v)
+{
+
+ if (ELF32_R_TYPE(r->r_info) == RELOC_RELATIVE) {
+ *p += v;
+ } else if (ELF32_R_TYPE(r->r_info) == RELOC_GLOB_DAT) {
+ *p += v + s->st_value;
+ } else {
+ _dl_printf("unknown bootstrap relocation\n");
+ _dl_exit(6);
+ }
+}
+
+static inline void
+RELOC_RELA(Elf32_Rela *r, const Elf32_Sym *s, Elf32_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ /* does i386 use RELA type relocations? - XXX */
+
+ if (ELF32_R_TYPE(r->r_info) == RELOC_RELATIVE) {
+ *p = v + r->r_addend;
+ } else if (ELF32_R_TYPE(r->r_info) == RELOC_GLOB_DAT) {
+ *p = v + s->st_value + r->r_addend;
+ } else {
+ _dl_printf("unknown bootstrap relocation\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _I386_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.11 2009/02/16 14:17:51 kurt Exp $ */
+
+/*
+ * Copyright (c) 2002 Dale Rahn
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+
+#define DL_DATA_SIZE (16*4)
+#include <sys/syscall.h>
+#include <machine/asm.h>
+
+ .text
+ .align 4
+ .globl _dl_start
+ .type _dl_start,@function
+_dl_start:
+ movl %esp,%eax # save stack pointer for _rtld
+ pushl %ebx # save ps_strings
+ subl $DL_DATA_SIZE,%esp # allocate dl_data
+ pushl $0 # push 0 for dynamicp (unused on i386)
+ movl %esp,%ebx
+ movl %ebx,%edi # save dl_data arg for dl_boot
+ pushl %ebx # push dl_data for dl_boot_bind
+
+ mov %eax, %esi # save stack for dl_boot
+
+ pushl %eax # load saved SP for dl_boot_bind
+
+ call _dl_boot_bind@PLT # _dl_boot_bind(sp,dl_data)
+
+ pushl %edi # push saved dl_data
+ movl %edi,%ebp
+ movl (7*4)(%ebp),%eax
+ pushl %eax # push loff from dl_data
+
+ movl %esi,%ebp
+ movl $4,%eax
+ imull 0(%ebp),%eax
+ addl $8,%eax
+ addl %ebp,%eax
+ push %eax # push envp
+
+ leal 4(%ebp),%eax
+ push %eax # push argv
+
+ call _dl_boot@PLT # _dl_boot(argv,envp,loff,dl_data)
+
+ addl $7*4,%esp # pop args
+
+ addl $DL_DATA_SIZE,%esp # return dl_data
+
+ popl %ebx # %ebx = ps_strings
+ movl $0,%edx # %edx = cleanup - XXXDSR
+ movl $0,%ecx # %ecx = obj_main - XXXDSR
+ jmp *%eax
+
+
+/* copied from lib/libc/arch/i386/SYS.h - XXX */
+#define __DO_SYSCALL(x) \
+ movl $__CONCAT(SYS_, x),%eax; \
+ int $0x80
+
+
+#define DL_SYSCALL(n) DL_SYSCALL2(n,n)
+#define DL_SYSCALL2(n,c) \
+ .section ".text" ;\
+ .align 4 ;\
+ .global __CONCAT(_dl_,n) ;\
+ .type __CONCAT(_dl_,n)%function ;\
+__CONCAT(_dl_,n): ;\
+ __DO_SYSCALL(c) ;\
+ jb .L_cerr ;\
+ ret
+
+DL_SYSCALL(close)
+
+ .section ".text"
+ .align 4
+ .global _dl_exit
+ .type _dl_exit,@function
+_dl_exit:
+ mov $SYS_exit, %eax
+ int $0x80
+ ret
+
+
+DL_SYSCALL(issetugid)
+DL_SYSCALL2(_syscall,__syscall)
+DL_SYSCALL(munmap)
+DL_SYSCALL(mprotect)
+DL_SYSCALL(open)
+DL_SYSCALL(read)
+DL_SYSCALL(write)
+DL_SYSCALL(stat)
+DL_SYSCALL(fstat)
+DL_SYSCALL(fcntl)
+DL_SYSCALL(gettimeofday)
+DL_SYSCALL2(sysctl,__sysctl)
+DL_SYSCALL(getdirentries)
+
+.L_cerr:
+ /* error: result = -errno; - handled here. */
+ neg %eax
+ ret
+
+
+ /* _dl_sigprocmask: does not handle NULL new set */
+
+ .section ".text"
+ .align 4
+ .global _dl_sigprocmask
+ .type _dl_sigprocmask,@function
+_dl_sigprocmask:
+ movl 8(%esp), %ecx
+ movl (%ecx),%ecx
+ movl %ecx,8(%esp) # to new mask arg
+ mov $SYS_sigprocmask, %eax
+ int $0x80
+ jb 1f /* error: result = -errno */
+ movl 12(%esp),%ecx # fetch old mask requested
+ testl %ecx,%ecx # test if old mask requested
+ jz 2f
+ movl %eax,(%ecx) # store old mask
+ xorl %eax,%eax
+2: ret
+
+1: /* error: result = -errno; - handled here. */
+ neg %eax
+ ret
+
+
+ .align 4
+ .global _dl_bind_start
+ .type _dl_bind_start,@function
+_dl_bind_start:
+ pushf # save registers
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ pushl %ebx
+ pushl %ebp
+ pushl %esi
+ pushl %edi
+ pushl %ds
+ pushl %es
+
+ pushl 44(%esp) # Copy of reloff
+ pushl 44(%esp) # Copy of obj
+ call _dl_bind@PLT # Call the binder
+ addl $8,%esp # pop binder args
+ movl %eax,44(%esp) # Store function to be called in obj
+
+ popl %es # restore registers
+ popl %ds
+ popl %edi
+ popl %esi
+ popl %ebp
+ popl %ebx
+ popl %edx
+ popl %ecx
+ popl %eax
+ popf
+
+ leal 4(%esp),%esp # Discard reloff, do not change eflags
+ ret
+
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.24 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 2002 Dale Rahn
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* NONE */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* RELOC_32*/
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC32 */
+ _RF_G| _RF_SZ(32) | _RF_RS(00), /* GOT32 */
+ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* COPY */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_DAT */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* JUMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0), /* RELATIVE */
+ 0, /* GOTOFF XXX */
+ 0, /* GOTPC XXX */
+ 0, /* DUMMY 11 */
+ 0, /* DUMMY 12 */
+ 0, /* DUMMY 13 */
+ 0, /* DUMMY 14 */
+ 0, /* DUMMY 15 */
+ 0, /* DUMMY 16 */
+ 0, /* DUMMY 17 */
+ 0, /* DUMMY 18 */
+ 0, /* DUMMY 19 */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* RELOC_16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* PC_16 */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* RELOC_8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* RELOC_PC8 */
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+
+static long reloc_target_bitmask[] = {
+#define _BM(x) (~(-(1ULL << (x))))
+ 0, /* NONE */
+ _BM(32), /* RELOC_32*/
+ _BM(32), /* PC32 */
+ _BM(32), /* GOT32 */
+ _BM(32), /* PLT32 */
+ 0, /* COPY */
+ _BM(32), /* GLOB_DAT */
+ _BM(32), /* JUMP_SLOT */
+ _BM(32), /* RELATIVE */
+ 0, /* GOTOFF XXX */
+ 0, /* GOTPC XXX */
+ 0, /* DUMMY 11 */
+ 0, /* DUMMY 12 */
+ 0, /* DUMMY 13 */
+ 0, /* DUMMY 14 */
+ 0, /* DUMMY 15 */
+ 0, /* DUMMY 16 */
+ 0, /* DUMMY 17 */
+ 0, /* DUMMY 18 */
+ 0, /* DUMMY 19 */
+ _BM(16), /* RELOC_16 */
+ _BM(8), /* PC_16 */
+ _BM(8), /* RELOC_8 */
+ _BM(8), /* RELOC_PC8 */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+void _dl_reloc_plt(Elf_Addr *where, Elf_Addr value);
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relsz)
+{
+ long i;
+ long numrel;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_Rel *rels;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrel = object->Dyn.info[relsz] / sizeof(Elf32_Rel);
+ rels = (Elf32_Rel *)(object->Dyn.info[rel]);
+ if (rels == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrel; i++, rels++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(rels->r_info);
+
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(rels->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+ value = *where & RELOC_VALUE_BITMASK(type);
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rels->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rels->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JUMP_SLOT))?
+ SYM_PLT:SYM_NOTPLT),
+ sym, NULL);
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(JUMP_SLOT)) {
+ _dl_reloc_plt((Elf_Word *)where, value);
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ size_t size = dstsym->st_size;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JUMP_SLOT)) ? SYM_PLT:SYM_NOTPLT),
+ sym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp = 0;
+ char *ptr = (char *)where;
+ int i, size = RELOC_TARGET_SIZE(type)/8;
+
+ /* Read it in one byte at a time. */
+ for (i=0; i<size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i=0; i<size; i++)
+ ptr[i] = ((tmp >> (8*i)) & 0xff);
+ } else if (RELOC_TARGET_SIZE(type) > 32) {
+ *where &= ~mask;
+ *where |= value;
+ } else {
+ Elf32_Addr *where32 = (Elf32_Addr *)where;
+
+ *where32 &= ~mask;
+ *where32 |= value;
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+#if 0
+struct jmpslot {
+ u_short opcode;
+ u_short addr[2];
+ u_short reloc_index;
+#define JMPSLOT_RELOC_MASK 0xffff
+};
+#define JUMP 0xe990 /* NOP + JMP opcode */
+#endif
+
+void
+_dl_reloc_plt(Elf_Addr *where, Elf_Addr value)
+{
+ *where = value;
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int index)
+{
+ Elf_Rel *rel;
+ Elf_Word *addr;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ Elf_Addr ooff;
+ sigset_t savedmask;
+
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+
+ rel += index/sizeof(Elf_Rel);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rel->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ addr = (Elf_Word *)(object->obj_base + rel->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ /* if GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ|PROT_WRITE);
+ }
+
+ _dl_reloc_plt(addr, ooff + this->st_value);
+
+ /* put the GOT back to RO */
+ if (object->got_size != 0) {
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return((Elf_Addr)ooff + this->st_value);
+}
+
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ extern void _dl_bind_start(void); /* XXX */
+ int fails = 0;
+ Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+ int i, num;
+ Elf_Rel *rel;
+ struct load_list *llist;
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+
+ if (pltgot == NULL)
+ return (0); /* it is possible to have no PLT/GOT relocations */
+
+ pltgot[1] = (Elf_Addr)object;
+ pltgot[2] = (Elf_Addr)&_dl_bind_start;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_REL)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+ num = (object->Dyn.info[DT_PLTRELSZ]);
+ for (llist = object->load_list; llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(rel->r_offset + object->obj_base);
+ *where += object->obj_base;
+ }
+ for (llist = object->load_list; llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+
+ }
+ /* PLT is already RO on i386, no point in mprotecting it, just GOT */
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+
+ return (fails);
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.8 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void *, unsigned int);
+int _dl_open(const char *, unsigned int);
+int _dl_read(int, const char *, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+.\" $OpenBSD: ld.so.1,v 1.17 2008/08/24 20:43:53 martynas Exp $
+.\" $NetBSD: rtld.1,v 1.2 1995/10/08 23:43:28 pk Exp $
+.\"
+.\" Copyright (c) 1995 Paul Kranenburg
+.\" 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 Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd $Mdocdate: August 24 2008 $
+.Dt LD.SO 1
+.Os
+.Sh NAME
+.Nm ld.so
+.Nd run-time link-editor
+.Sh DESCRIPTION
+.Nm
+is a self-contained, position independent program image providing run-time
+support for loading and link-editing shared objects into a process's
+address space.
+It uses the data structures
+.Po
+see
+.Xr link 5
+.Pc
+contained within dynamically linked programs to determine which shared
+libraries are needed and loads them at a convenient virtual address
+using the
+.Xr mmap 2
+system call.
+.Pp
+After all shared libraries have been successfully loaded,
+.Nm
+proceeds to resolve external references from both the main program and
+all objects loaded.
+A mechanism is provided for initialization routines to be called,
+on a per-object basis, giving a shared object an opportunity
+to perform any extra set-up, before execution of the program proper begins.
+.\"
+.\" <talk about CTORS/DTORS>
+.\"
+.Pp
+.Nm
+is itself a shared object that is initially loaded by the kernel.
+.\"
+.\" <How it is run>
+.\"
+.Pp
+To quickly locate the required shared objects in the filesystem,
+.Nm
+may use a
+.Dq hints
+file, prepared by the
+.Xr ldconfig 8
+utility, in which the full path specification of the shared objects can be
+looked up by hashing on the 3-tuple
+.Aq library-name , major-version-number , minor-version-number .
+.Pp
+.Nm
+recognises a number of environment variables that can be used to modify
+its behaviour as follows:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ev LD_LIBRARY_PATH
+A colon separated list of directories, prepending the default search path
+for shared libraries.
+This variable is ignored for set-user-ID and set-group-ID executables.
+.Pp
+.It Ev LD_PRELOAD
+A colon separate list of library names to load before any of the regular
+libraries are loaded.
+This variable is ignored for set-user-ID and set-group-ID executables.
+.Pp
+.It Ev LD_BIND_NOW
+Specifies that the dynamic linker should process all relocations before
+transferring control to the program.
+Normally, the procedure linkage table entries are handled lazily,
+avoiding symbol lookup and relocation for unused functions.
+This variable is ignored for set-user-ID and set-group-ID executables.
+.Pp
+.It Ev LD_WARN_NON_PURE_CODE
+When set, issue a warning whenever a link-editing operation requires
+modification of the text segment of some loaded object.
+This is usually indicative of an incorrectly built library.
+.Aq not yet supported
+.Pp
+.It Ev LD_SUPPRESS_WARNINGS
+When set, no warning messages of any kind are issued.
+Normally, a warning is given if a satisfactorily versioned library
+could not be found.
+.Aq not yet supported
+.Pp
+.It Ev LD_TRACE_LOADED_OBJECTS
+When set, causes
+.Nm
+to exit after loading the shared objects and printing a summary which includes
+the absolute pathnames of all objects, to standard output.
+.Pp
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT1
+.It Ev LD_TRACE_LOADED_OBJECTS_FMT2
+When set, these variables are interpreted as format strings a la
+.Xr printf 3
+to customize the trace output and are used by
+.Xr ldd 1 's
+.Fl f
+option and allows
+.Xr ldd 1
+to be operated as a filter more conveniently.
+.Ev LD_TRACE_LOADED_OBJECTS_FMT1
+is used for tracing shared libraries;
+.Ev LD_TRACE_LOADED_OBJECTS_FMT2
+for dynamically loaded objects, the dynamic linker,
+and the main executable.
+The following conversions can be used:
+.Bl -tag -width "xxxx"
+.It %a
+The main program's name
+.Pq also known as Dq __progname .
+.It \&%A
+The value of the environment variable
+.Ev LD_TRACE_LOADED_OBJECTS_PROGNAME .
+.It %e
+The end address of the object.
+.It %g
+The object's group reference count.
+.It %m
+The object's major version number.
+.It %n
+The object's minor version number.
+.It \&%O
+The object's open count.
+.It %o
+The object name.
+.It %p
+The full pathname as determined by
+.Nm ld.so Ns 's
+library search rules.
+.It %r
+The object's reference count.
+.It %x
+The object's load address.
+.El
+.Pp
+Additionally,
+.Sy \en
+and
+.Sy \et
+are recognised and have their usual meaning.
+.Pp
+.It Ev LD_NO_INTERN_SEARCH
+When set,
+.Nm
+does not process any internal search paths that were recorded in the
+executable.
+.Aq not yet supported
+.Pp
+.It Ev LD_NORANDOM
+When set, do not load shared objects or libraries dependent objects in
+random order.
+This variable is ignored for set-user-ID and set-group-ID executables.
+.Pp
+.It Ev LD_NOSTD_PATH
+.Aq not yet supported
+When set, do not include a set of built-in standard directory paths for
+searching.
+This might be useful when running on a system with a completely
+non-standard filesystem layout.
+.Pp
+.It Ev LD_DEBUG
+When set, be verbose about what
+.Nm
+does.
+.Pp
+.It Ev LD_NOPREBIND
+When set, ignore any prebind data associated with the program or libraries.
+.Pp
+.It Ev LD_PREBINDVALIDATE
+When set, perform symbol relocation of the given binary and the associated
+libraries, compare the results against the prebind values, then exit.
+.El
+.Sh FILES
+.Bl -tag -width /var/run/ld.so.hintsXXX -compact
+.It Pa /var/run/ld.so.hints
+library location hints built by
+.Xr ldconfig 8
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr link 5 ,
+.Xr ldconfig 8
+.Sh HISTORY
+The shared library model employed first appeared in SunOS 4.0.
--- /dev/null
+/Makefile/1.7/Fri May 12 23:20:52 2006//
+/debug.c/1.4/Thu May 18 17:00:06 2006//
+/dl_prebind.c/1.2/Wed Apr 9 21:45:26 2008//
+/etc.c/1.7/Fri May 12 23:35:16 2006//
+/ld.h/1.7/Sat May 13 05:59:28 2006//
+/ldconfig.8/1.25/Thu May 31 19:19:39 2007//
+/prebind.h/1.2/Mon Jun 26 23:26:12 2006//
+/prebind_struct.h/1.3/Thu Jun 15 22:09:32 2006//
+/shlib.c/1.9/Sat May 13 16:33:40 2006//
+/sod.c/1.1/Fri May 12 23:20:53 2006//
+/library.c/1.3/Sat Jan 2 15:01:02 2010//
+/ldconfig.c/1.27/Mon Apr 5 23:11:44 2010//
+/prebind.c/1.12/Mon Apr 5 23:11:44 2010//
+/prebind_delete.c/1.10/Mon Apr 5 23:11:44 2010//
+D
--- /dev/null
+src/libexec/ld.so/ldconfig
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.7 2006/05/12 23:20:52 deraadt Exp $
+# $NetBSD: Makefile,v 1.10 1995/03/06 04:24:41 cgd Exp $
+
+PROG= ldconfig
+SRCS= ldconfig.c shlib.c etc.c prebind_delete.c debug.c prebind.c library.c sod.c
+LDDIR?= $(.CURDIR)/..
+#CFLAGS+=-Werror
+CFLAGS+=-I$(.CURDIR) -I$(.CURDIR)/..
+LDSTATIC=${STATIC}
+BINDIR= /sbin
+MAN= ldconfig.8
+
+#.PATH: $(LDDIR) $(LDDIR)/$(MACHINE_ARCH)
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: debug.c,v 1.4 2006/05/18 17:00:06 deraadt Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <elf_abi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "resolve.h"
+#include "link.h"
+#include "sod.h"
+#ifndef __mips64__
+#include "machine/reloc.h"
+#endif
+#include "prebind.h"
+#include "prebind_struct.h"
+
+#ifdef DEBUG1
+void
+dump_info(struct elf_object *object)
+{
+ int numrel, numrela, i;
+ const Elf_Sym *symt;
+ const char *strt;
+ Elf_Word *needed_list;
+
+ symt = object->dyn.symtab;
+ strt = object->dyn.strtab;
+
+ for (i = 0; i < object->nchains; i++) {
+ const Elf_Sym *sym = symt + i;
+ char *type;
+
+ switch (ELF_ST_TYPE(sym->st_info)) {
+ case STT_FUNC:
+ type = "func";
+ break;
+ case STT_OBJECT:
+ type = "object";
+ break;
+ case STT_NOTYPE:
+ type = "notype";
+ break;
+ default:
+ type = "UNKNOWN";
+ }
+ printf("symbol %d [%s] type %s value %x\n", i,
+ strt + sym->st_name,
+ type, sym->st_value);
+ }
+
+ numrel = object->dyn.relsz / sizeof(Elf_Rel);
+ numrela = object->dyn.relasz / sizeof(Elf_RelA);
+ printf("numrel %d numrela %d\n", numrel, numrela);
+
+ printf("rel relocations:\n");
+ for (i = 0; i < numrel ; i++) {
+ Elf_Rel *rel = object->dyn.rel;
+
+ printf("%d: %x sym %x type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), ELF_R_TYPE(rel[i].r_info));
+ }
+ printf("rela relocations:\n");
+ for (i = 0; i < numrela ; i++) {
+ Elf_RelA *rela = object->dyn.rela;
+
+ printf("%d: %x sym %x type %d\n", i, rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), ELF_R_TYPE(rela[i].r_info));
+ }
+ needed_list = (Elf_Addr *)object->dyn.needed;
+ for (i = 0; needed_list[i] != NULL; i++)
+ printf("NEEDED %s\n", needed_list[i] + strt);
+
+}
+#endif
+
+
+void
+elf_dump_footer(struct prebind_footer *footer)
+{
+ printf("\nbase %llx\n", (long long)footer->prebind_base);
+ printf("nameidx_idx %d\n", footer->nameidx_idx);
+ printf("symcache_idx %d\n", footer->symcache_idx);
+ printf("pltsymcache_idx %d\n", footer->pltsymcache_idx);
+ printf("fixupcnt_idx %d\n", footer->fixupcnt_idx);
+ printf("fixup_cnt %d\n", footer->fixup_cnt);
+ printf("nametab_idx %d\n", footer->nametab_idx);
+ printf("symcache_cnt %d\n", footer->symcache_cnt);
+ printf("pltsymcache_cnt %d\n", footer->pltsymcache_cnt);
+ printf("fixup_cnt %d\n", footer->fixup_cnt);
+ printf("numlibs %d\n", footer->numlibs);
+ printf("id0 %x\n", footer->id0);
+ printf("id1 %x\n", footer->id1);
+ printf("orig_size %lld\n", (long long)footer->orig_size);
+ printf("version %d\n", footer->prebind_version);
+ printf("bind_id %c%c%c%c\n", footer->bind_id[0],
+ footer->bind_id[1], footer->bind_id[2], footer->bind_id[3]);
+}
+
+
+void
+dump_symcachetab(struct symcachetab *symcachetab, int symcache_cnt,
+ struct elf_object *object, int id)
+{
+ int i;
+
+ printf("symcache for %s\n", object->load_name);
+ for (i = 0; i < symcache_cnt; i++) {
+ printf("symidx %d: obj %d sym %d\n",
+ symcachetab[i].idx,
+ symcachetab[i].obj_idx,
+ symcachetab[i].sym_idx);
+ }
+}
+
+void
+elf_print_prog_list(prog_list_ty *prog_list)
+{
+ struct elf_object *object;
+ struct proglist *pl;
+
+ TAILQ_FOREACH(pl, prog_list, list) {
+ object = TAILQ_FIRST(&(pl->curbin_list))->object;
+ printf("bin: %s\n", object->load_name);
+ elf_print_curbin_list(pl);
+ }
+}
+
+void
+elf_print_curbin_list(struct proglist *bin)
+{
+ struct objlist *ol;
+
+ TAILQ_FOREACH(ol, &(bin->curbin_list), list) {
+ printf("\t%s\n", ol->object->load_name);
+ }
+}
+
--- /dev/null
+/* $OpenBSD: dl_prebind.c,v 1.2 2008/04/09 21:45:26 kurt Exp $ */
+
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/exec.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <nlist.h>
+#include <string.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "sod.h"
+#include "stdlib.h"
+#include "dl_prebind.h"
+
+void elf_dump_footer(struct prebind_footer *footer);
+void dump_prelink(Elf_Addr base, u_long size);
+void prebind_dump_footer(struct prebind_footer *footer, char *file);
+void prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt);
+void prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numblibs,
+ char *nametab);
+void prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups);
+void prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs);
+struct prebind_footer *_dl_prebind_data_to_footer(void *data);
+
+void *_dl_prog_prebind_map;
+struct prebind_footer *prog_footer;
+extern char *_dl_noprebind;
+extern char *_dl_prebind_validate;
+
+int _dl_prebind_match_failed; /* = 0 */
+
+char *prebind_bind_now = "prebind";
+
+struct prebind_footer *
+_dl_prebind_data_to_footer(void *prebind_data)
+{
+ u_int32_t *poffset, offset;
+ struct prebind_footer *footer;
+ char *c;
+
+ poffset = prebind_data;
+ c = prebind_data;
+ offset = *poffset;
+ c += offset;
+ footer = (void *)c;
+ return footer;
+}
+
+void
+prebind_load_exe(Elf_Phdr *phdp, elf_object_t *exe_obj)
+{
+ struct prebind_footer *footer;
+
+ exe_obj->prebind_data = (void *)phdp->p_vaddr;
+ _dl_prog_prebind_map = exe_obj->prebind_data;
+
+ footer = _dl_prebind_data_to_footer(_dl_objects->prebind_data);
+
+ if (footer->bind_id[0] == BIND_ID0 &&
+ footer->bind_id[1] == BIND_ID1 &&
+ footer->bind_id[2] == BIND_ID2 &&
+ footer->bind_id[3] == BIND_ID3 &&
+ footer->prebind_version == PREBIND_VERSION) {
+ prog_footer = footer;
+ if (_dl_bindnow == NULL)
+ _dl_bindnow = prebind_bind_now;
+ } else {
+ DL_DEB(("prebind data missing\n"));
+ _dl_prog_prebind_map = NULL;
+ }
+ if (_dl_noprebind != NULL) {
+ /*prog_footer is valid, we should free it */
+ _dl_prog_prebind_map = NULL;
+ prog_footer = NULL;
+ exe_obj->prebind_data = NULL;
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+#if 0
+ else if (_dl_debug)
+ dump_prelink((long)_dl_prog_prebind_map,
+ prog_footer->prebind_size);
+#endif
+}
+
+void *
+prebind_load_fd(int fd, const char *name)
+{
+ struct prebind_footer footer;
+ struct nameidx *nameidx;
+ void *prebind_data;
+ char *nametab;
+ ssize_t len;
+ int idx;
+
+ if (_dl_prog_prebind_map == NULL || _dl_prebind_match_failed)
+ return 0;
+
+ _dl_lseek(fd, -(off_t)sizeof(struct prebind_footer), SEEK_END);
+ len = _dl_read(fd, (void *)&footer, sizeof(struct prebind_footer));
+
+ if (len != sizeof(struct prebind_footer) ||
+ footer.bind_id[0] != BIND_ID0 ||
+ footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 ||
+ footer.bind_id[3] != BIND_ID3 ||
+ footer.prebind_version != PREBIND_VERSION) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match failed %s\n", name));
+ return (NULL);
+ }
+
+ prebind_data = _dl_mmap(0, footer.prebind_size, PROT_READ,
+ MAP_FILE, fd, footer.prebind_base);
+ DL_DEB(("prebind_load_fd for lib %s\n", name));
+
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+
+ /* libraries are loaded in random order, so we just have
+ * to look thru the list to find ourselves
+ */
+ for (idx = 0; idx < prog_footer->numlibs; idx++) {
+ if (_dl_strcmp(nametab + nameidx[idx].name, name) == 0)
+ break;
+ }
+
+ if (idx == prog_footer->numlibs) {
+ _dl_prebind_match_failed = 1; /* not found */
+ } else if (footer.id0 != nameidx[idx].id0 ||
+ footer.id1 != nameidx[idx].id1) {
+ _dl_prebind_match_failed = 1;
+ DL_DEB(("prebind match id0 %d pid0 %d id1 %d pid1 %d\n",
+ footer.id0, nameidx[idx].id0,
+ footer.id1, nameidx[idx].id1));
+ }
+
+ if (_dl_prebind_match_failed == 1) {
+ DL_DEB(("prebind match failed for %s\n", name));
+ }
+
+ return prebind_data;
+}
+#define NUM_STATIC_OBJS 10
+elf_object_t *objarray_static[NUM_STATIC_OBJS];
+elf_object_t **objarray;
+
+void
+prebind_symcache(elf_object_t *object, int plt)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap, *idxtolib;
+ u_int32_t *poffset, offset, symcache_cnt;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ int i = 0, cur_obj = -1, idx;
+ void *prebind_map;
+ struct nameidx *nameidx;
+ char *nametab, *c;
+ struct fixup *fixup;
+ elf_object_t *obj;
+
+ struct symcachetab *s;
+
+ if (object->prebind_data == NULL)
+ return;
+// DL_DEB(("prebind symcache %s\n", object->load_name));
+
+ obj = _dl_objects;
+ while (obj != NULL) {
+ if (obj == object)
+ cur_obj = i;
+ i++;
+ obj = obj->next;
+ }
+
+ if (cur_obj == -1)
+ return; /* unable to find object ? */
+
+ if (objarray == NULL) {
+ if (i <= NUM_STATIC_OBJS) {
+ objarray = &objarray_static[0];
+ } else {
+ objarray = _dl_malloc(sizeof(elf_object_t *) * i);
+ }
+
+ obj = _dl_objects;
+ i = 0;
+ while (obj != NULL) {
+ objarray[i] = obj;
+ i++;
+ obj = obj->next;
+ }
+ }
+
+ poffset = (u_int32_t *)object->prebind_data;
+ c = object->prebind_data;
+ offset = *poffset;
+ c += offset;
+
+ footer = (void *)c;
+ prebind_map = (void *)object->prebind_data;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ if (plt) {
+ symcachetab = prebind_map + footer->pltsymcache_idx;
+ symcache_cnt = footer->pltsymcache_cnt;
+// DL_DEB(("loading plt %d\n", symcache_cnt));
+ } else {
+ symcachetab = prebind_map + footer->symcache_idx;
+ symcache_cnt = footer->symcache_cnt;
+// DL_DEB(("loading got %d\n", symcache_cnt));
+ }
+ nametab = prebind_map + footer->nametab_idx;
+
+ libmap = _dl_prog_prebind_map + prog_footer->libmap_idx;
+ idxtolib = _dl_prog_prebind_map + libmap[cur_obj];
+
+ for (i = 0; i < symcache_cnt; i++) {
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ s = &(symcachetab[i]);
+ if (cur_obj == 0)
+ idx = s->obj_idx;
+ else
+ idx = idxtolib[s->obj_idx];
+
+ if (idx == -1) /* somehow an invalid object ref happend */
+ continue;
+#if 0
+ DL_DEB(("%s:", object->load_name));
+ DL_DEB(("symidx %d: obj %d %d sym %d flags %x\n",
+ s->idx, s->obj_idx, idx, s->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[idx];
+ sym = tobj->dyn.symtab + s->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ s->idx, s->obj_idx, tobj->load_name,
+ s->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[s->idx].obj = tobj;
+ _dl_symcache[s->idx].sym = sym;
+ _dl_symcache[s->idx].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+
+ if (!plt) {
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+ for (i = 0; i < fixupcnt[2*cur_obj]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx, f->flags));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ } else {
+
+ fixupidx = _dl_prog_prebind_map + prog_footer->fixup_idx;
+ fixup = _dl_prog_prebind_map + fixupidx[2*cur_obj+1];
+ fixupcnt = _dl_prog_prebind_map + prog_footer->fixupcnt_idx;
+
+#if 0
+ DL_DEB(("prebind loading symbols fixup plt %s\n",
+ object->load_name));
+#endif
+ for (i = 0; i < fixupcnt[2*cur_obj+1]; i++) {
+ struct fixup *f;
+ struct elf_object *tobj;
+ const Elf_Sym *sym;
+ const char *str;
+
+ f = &(fixup[i]);
+#if 0
+ DL_DEB(("symidx %d: obj %d sym %d flags %x\n",
+ f->sym, f->obj_idx, f->sym_idx,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt));
+#endif
+ tobj = objarray[f->obj_idx];
+ sym = tobj->dyn.symtab + f->sym_idx;
+ str = tobj->dyn.strtab + sym->st_name;
+#ifdef DEBUG2
+ DL_DEB(("symidx %d: obj %d %s sym %d %s flags %d %x\n",
+ f->sym, f->obj_idx, tobj->load_name,
+ f->sym_idx, str, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt,
+ object->obj_base + sym->st_value));
+#endif
+ _dl_symcache[f->sym].obj = tobj;
+ _dl_symcache[f->sym].sym = sym;
+ _dl_symcache[f->sym].flags =
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|plt;
+ }
+ }
+// DL_DEB(("prebind_data loaded\n"));
+}
+
+void
+prebind_free(elf_object_t *object)
+{
+ struct prebind_footer *footer;
+
+ if (object->prebind_data == NULL)
+ return;
+#ifdef DEBUG1
+ DL_DEB(("prebind_free for %s %p\n", object->load_name,
+ object->prebind_data));
+#endif
+ if (object->prebind_data != 0) {
+ footer = _dl_prebind_data_to_footer(object->prebind_data);
+
+#ifdef DEBUG1
+ DL_DEB(("freeing prebind data sz %x\n", footer->prebind_size));
+#endif
+
+ _dl_munmap((void *)ELF_TRUNC((long)object->prebind_data, _dl_pagesz),
+ ELF_ROUND((long)object->prebind_data+footer->prebind_size,
+ _dl_pagesz) - ELF_TRUNC((long)object->prebind_data, _dl_pagesz));
+
+ object->prebind_data = NULL;
+ _dl_prog_prebind_map = NULL;
+
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+}
+
+int validate_errs;
+
+struct timeval beforetp;
+
+void
+_dl_prebind_pre_resolve()
+{
+ struct prebind_footer *footer;
+ elf_object_t *object;
+ struct nameidx *nameidx;
+ char *nametab, *name;
+ int idx;
+
+ if (_dl_prog_prebind_map != NULL) {
+ nameidx = _dl_prog_prebind_map + prog_footer->nameidx_idx;
+ nametab = _dl_prog_prebind_map + prog_footer->nametab_idx;
+ for (idx = 1, object = _dl_objects->next; object != NULL;
+ object = object->next, idx++) {
+ if (object->prebind_data == NULL) {
+ /* ld.so doesn't have prebind data */
+ if (object->next == NULL)
+ continue;
+ DL_DEB(("missing prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ footer = _dl_prebind_data_to_footer(
+ object->prebind_data);
+ if (footer == NULL ||
+ nameidx[idx].id0 != footer->id0 ||
+ nameidx[idx].id1 != footer->id1) {
+ DL_DEB(("invalid prebind data %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ name = object->load_name;
+ if (_dl_strcmp(nametab + nameidx[idx].name, name)
+ != 0) {
+ DL_DEB(("invalid prebind name %s\n",
+ object->load_name));
+ _dl_prebind_match_failed = 1;
+ break;
+ }
+ }
+ }
+
+ if (_dl_prebind_match_failed) {
+ for (object = _dl_objects; object != NULL;
+ object = object->next)
+ prebind_free(object);
+ if (_dl_bindnow == prebind_bind_now)
+ _dl_bindnow = NULL;
+ }
+
+ if (_dl_debug)
+ _dl_gettimeofday(&beforetp, NULL);
+}
+
+void
+_dl_prebind_post_resolve()
+{
+ char buf[7];
+ int i;
+ struct timeval after_tp;
+ struct timeval diff_tp;
+ elf_object_t *object;
+
+ if (_dl_debug) {
+ _dl_gettimeofday(&after_tp, NULL);
+
+ timersub(&after_tp, &beforetp, &diff_tp);
+
+ for (i = 0; i < 6; i++) {
+ buf[5-i] = (diff_tp.tv_usec % 10) + '0';
+ diff_tp.tv_usec /= 10;
+ }
+ buf[6] = '\0';
+
+ _dl_printf("relocation took %d.%s\n", diff_tp.tv_sec, buf);
+ }
+
+ for (object = _dl_objects; object != NULL; object = object->next)
+ prebind_free(object);
+
+ if (_dl_prebind_validate) {
+ if (validate_errs) {
+ _dl_printf("validate_errs %d\n", validate_errs);
+ _dl_exit(20);
+ } else {
+ _dl_exit(0);
+ }
+ }
+}
+
+void
+prebind_validate(elf_object_t *req_obj, unsigned int symidx, int flags,
+ const Elf_Sym *ref_sym)
+{
+ const Elf_Sym *sym, **this;
+ const elf_object_t *sobj;
+ const char *symn;
+ Elf_Addr ret;
+
+ /* Don't verify non-matching flags*/
+
+ sym = req_obj->dyn.symtab;
+ sym += symidx;
+ symn = req_obj->dyn.strtab + sym->st_name;
+ this = &sym;
+
+ //_dl_printf("checking %s\n", symn);
+ ret = _dl_find_symbol(symn, this, flags, ref_sym, req_obj, &sobj);
+
+ if (_dl_symcache[symidx].sym != *this ||
+ _dl_symcache[symidx].obj != sobj) {
+ _dl_printf("symbol %d mismatch on sym %s req_obj %s,\n"
+ "should be obj %s is obj %s\n",
+ symidx, symn, req_obj->load_name, sobj->load_name,
+ _dl_symcache[symidx].obj->load_name);
+ if (req_obj == sobj)
+ _dl_printf("obj %p %p\n", _dl_symcache[symidx].obj, sobj);
+ sym = _dl_symcache[symidx].obj->dyn.symtab;
+ sym += symidx;
+ symn = _dl_symcache[symidx].obj->dyn.strtab + sym->st_name;
+ _dl_printf("obj %s name %s\n",
+ _dl_symcache[symidx].obj->load_name,
+ symn);
+ }
+}
+
+#ifdef DEBUG1
+void
+prebind_dump_symcache(struct symcachetab *symcachetab, u_int32_t cnt)
+{
+ struct symcachetab *s;
+ int i;
+
+ _dl_printf("cache: cnt %d\n", cnt);
+ for (i = 0; i < cnt; i++) {
+ s = &(symcachetab[i]);
+ _dl_printf("symidx %d: obj %d sym %d\n",
+ s->idx, s->obj_idx, s->sym_idx);
+ }
+}
+
+void
+prebind_dump_nameidx(struct nameidx *nameidx, u_int32_t numlibs, char *nametab)
+{
+ struct nameidx *n;
+ int i;
+
+ _dl_printf("libs:\n");
+ for (i = 0; i < numlibs; i++) {
+ _dl_printf("lib %d offset %d id0 %d, id1 %d\n", i,
+ nameidx[i].name, nameidx[i].id0, nameidx[i].id1);
+ }
+ for (i = 0; i < numlibs; i++) {
+ n = &(nameidx[i]);
+ _dl_printf("nametab %p n %d\n", nametab, n->name);
+ _dl_printf("lib %s %x %x\n", nametab + n->name, n->id0, n->id1);
+ }
+}
+
+void
+prebind_dump_fixup(struct fixup *fixup, u_int32_t numfixups)
+{
+ struct fixup *f;
+ int i;
+
+ _dl_printf("fixup: %d\n", numfixups);
+ for (i = 0; i < numfixups; i++) {
+ f = &(fixup[i]);
+
+ _dl_printf("idx %d obj %d sym idx %d\n",
+ f->sym, f->obj_idx, f->sym_idx);
+
+ }
+}
+
+void
+prebind_dump_libmap(u_int32_t *libmap, u_int32_t numlibs)
+{
+ int i;
+
+ for (i = 0; i < numlibs; i++) {
+ //_dl_printf("lib%d off %d %s\n", i, libmap[i], strtab+libmap[i]);
+ _dl_printf("lib%d off %d\n", i, libmap[i]);
+ }
+}
+
+void
+dl_dump_footer(struct prebind_footer *footer)
+{
+// _dl_printf("base %qd\n", (long long)footer->prebind_base);
+ _dl_printf("nameidx_idx %d\n", footer->nameidx_idx);
+ _dl_printf("symcache_idx %d\n", footer->symcache_idx);
+ _dl_printf("fixupcnt_idx %d\n", footer->fixupcnt_idx);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("nametab_idx %d\n", footer->nametab_idx);
+ _dl_printf("symcache_cnt %d\n", footer->symcache_cnt);
+ _dl_printf("fixup_cnt %d\n", footer->fixup_cnt);
+ _dl_printf("numlibs %d\n", footer->numlibs);
+ _dl_printf("id0 %d\n", footer->id0);
+ _dl_printf("id1 %d\n", footer->id1);
+// _dl_printf("orig_size %lld\n", (long long)footer->orig_size);
+ _dl_printf("version %d\n", footer->prebind_version);
+ _dl_printf("bind_id %c%c%c%c\n", footer->bind_id[0],
+ footer->bind_id[1], footer->bind_id[2], footer->bind_id[3]);
+}
+void
+dump_prelink(Elf_Addr base, u_long size)
+{
+ u_int32_t *fixupidx, *fixupcnt, *libmap;
+ struct symcachetab *symcachetab;
+ struct prebind_footer *footer;
+ struct nameidx *nameidx;
+ struct fixup *fixup;
+ char *nametab, *id;
+ void *prebind_map;
+ int i;
+
+ id = (char *) (base+size);
+ id -= 4;
+ DL_DEB(("id %c %c %c %c\n", id[0], id[1], id[2], id[3]));
+ footer = (void *) (base+size - sizeof (struct prebind_footer));
+ dl_dump_footer(footer);
+
+ prebind_map = (void *)base;
+ nameidx = prebind_map + footer->nameidx_idx;;
+ symcachetab = prebind_map + footer->symcache_idx;
+ fixupidx = prebind_map + footer->fixup_idx;
+ nametab = prebind_map + footer->nametab_idx;
+ fixupcnt = prebind_map + footer->fixupcnt_idx;
+ libmap = prebind_map + footer->libmap_idx;
+
+ prebind_dump_symcache(symcachetab, footer->symcache_cnt);
+ prebind_dump_nameidx(nameidx, footer->numlibs, nametab);
+ for (i = 0; i < footer->fixup_cnt; i++) {
+ _dl_printf("fixup %d cnt %d idx %d\n", i, fixupcnt[i], fixupidx[i]);
+ fixup = prebind_map + fixupidx[i];
+ prebind_dump_fixup(fixup, fixupcnt[i]);
+ }
+ prebind_dump_libmap(libmap, footer->numlibs);
+}
+#endif /* DEBUG1 */
--- /dev/null
+/* $OpenBSD: etc.c,v 1.7 2006/05/12 23:35:16 deraadt Exp $ */
+
+/* Public Domain */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ld.h"
+
+#define OOM_MSG "Out of memory"
+
+char *
+xstrdup(const char *s)
+{
+ char *ptr;
+
+ if ((ptr = strdup(s)) == NULL)
+ err(1, OOM_MSG);
+ return (ptr);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if ((ptr = malloc(size)) == NULL)
+ err(1, OOM_MSG);
+ return (ptr);
+}
+
+void *
+xrealloc(void *ptr, size_t size)
+{
+ void *nptr;
+
+ if ((nptr = realloc(ptr, size)) == NULL)
+ err(1, OOM_MSG);
+ return (nptr);
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ ptr = calloc(nmemb, size);
+ if (ptr == NULL)
+ err(1, OOM_MSG);
+ return ptr;
+}
+
+char *
+concat(const char *s1, const char *s2, const char *s3)
+{
+ char *str;
+ size_t len;
+
+ len = strlen(s1) + strlen(s2) + strlen(s3) + 1;
+ str = xmalloc(len);
+
+ strlcpy(str, s1, len);
+ strlcat(str, s2, len);
+ strlcat(str, s3, len);
+
+ return (str);
+}
--- /dev/null
+/* $OpenBSD: ld.h,v 1.7 2006/05/13 05:59:28 deraadt Exp $ */
+/*
+ * Header file to make code compatible with ELF version
+ * ldconfig was taken from the a.out ld.
+ */
+#include <link.h>
+
+extern int n_search_dirs;
+extern char **search_dirs;
+
+char *xstrdup(const char *);
+void *xmalloc(size_t);
+void *xrealloc(void *, size_t);
+void *xcalloc(size_t, size_t);
+char *concat(const char *, const char *, const char *);
+
+void add_search_dir(char *name);
+void std_search_path(void);
+void add_search_path(char *path);
+void remove_search_dir(char *name);
+int getdewey(int dewey[], char *cp);
+int cmpndewey(int d1[], int n1, int d2[], int n2);
+
+#define PAGSIZ __LDPGSZ
--- /dev/null
+.\" $OpenBSD: ldconfig.8,v 1.25 2007/05/31 19:19:39 jmc Exp $
+.\"
+.\" Copyright (c) 1993,1995 Paul Kranenburg
+.\" 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 Paul Kranenburg.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LDCONFIG 8
+.Os
+.Sh NAME
+.Nm ldconfig
+.Nd configure the shared library cache
+.Sh SYNOPSIS
+.Nm ldconfig
+.Op Fl DmPRrSsUv
+.Op Ar path ...
+.Sh DESCRIPTION
+.Nm
+is used to prepare a set of
+.Dq hints
+for use by the run-time linker
+.Xr ld.so 1
+to facilitate quick lookup of shared libraries available in multiple
+directories.
+It scans a set of built-in system directories and any
+.Ar directories
+specified on the command line (in the given order) looking for shared
+libraries and stores the results in the file
+.Pa /var/run/ld.so.hints
+to forestall the overhead that would otherwise result from the
+directory search operations
+.Xr ld.so 1
+would have to perform to load the required shared libraries.
+.Pp
+The shared libraries so found will be automatically available for loading
+if needed by the program being prepared for execution.
+This obviates the need for storing search paths within the executable.
+.Pp
+The
+.Ev LD_LIBRARY_PATH
+environment variable can be used to override the use of
+directories (or the order thereof) from the cache or to specify additional
+directories where shared libraries might be found.
+.Ev LD_LIBRARY_PATH
+is a
+.Sq \&:
+separated list of directory paths which are searched by
+.Xr ld.so 1
+when it needs to load a shared library.
+It can be viewed as the run-time equivalent of the
+.Fl L
+switch of
+.Xr ld 1 .
+.Pp
+.Nm
+is typically run as part of the boot sequence.
+In addition to the built-in system directories,
+directories containing shared libraries may be specified via the
+.Ev shlib_dirs
+variable in
+.Pa /etc/rc.conf.local .
+See
+.Xr rc.conf 8
+for further information.
+.Pp
+The following options are recognized by
+.Nm ldconfig :
+.Bl -tag -width indent
+.It Fl D
+Remove any prebind information in the specified binary or shared library.
+.It Fl m
+Merge the result of the scan of the directories given as arguments into
+the existing hints file.
+The default action is to build the hints file afresh.
+This option cannot be used with
+.Fl U .
+.It Fl P
+Create and append prebind information to all executables found in
+the specified directories, and also all shared libraries which are
+required by those executables.
+.It Fl R
+Rescan the previously configured directories.
+This opens the hints file and fetches the directory list from the header.
+Any additional pathnames on the command line are also processed.
+.It Fl r
+List the current contents of
+.Pa ld.so.hints
+on the standard output.
+The hints file will not be modified.
+.It Fl S
+Perform prelinking operations in a safe mode; always copies the binaries
+when prelinking data is added to a file.
+Use this to eliminate the possibility that
+.Dv ETXTBUSY
+will occur when attempting to run a binary while prelinking
+is running.
+.It Fl s
+Do not scan the built-in system directory
+.Pq Dq /usr/lib
+for shared libraries.
+.It Fl U
+Unconfigure directories specified on the command line or remove inaccessible
+directories from search path if no directories specified.
+This option cannot be used with
+.Fl m .
+.It Fl v
+Switch on verbose mode.
+.El
+.Sh PREBINDING
+Prebinding is loosely based on an earlier concept called Prelinking, which
+also accelerated
+.Xr ld.so 1
+performance but simultaneously impaired address space randomization.
+When prebinding information is added to libraries and programs using
+.Fl P ,
+program startup can be significantly improved because
+.Xr ld.so 1
+can initialize the shared library environment much faster.
+Prebinding information adds a small amount of data to the end of each
+specified program and associated shared libraries.
+.Sh SECURITY
+Special care must be taken when loading shared libraries into the address
+space of
+.Ev set-user-Id
+programs.
+Whenever such a program is run,
+.Xr ld.so 1
+will only load shared libraries from the
+.Pa ld.so.hints
+file.
+In particular, the
+.Ev LD_LIBRARY_PATH
+is not used to search for libraries.
+Thus, the role of
+.Nm
+is dual.
+In addition to building a set of hints for quick lookup, it also serves to
+specify the trusted collection of directories from which shared objects can
+be safely loaded.
+It is presumed that the set of directories specified to
+.Nm
+are under control of the system's administrator.
+.Xr ld.so 1
+further assists set-user-Id programs by erasing the
+.Ev LD_LIBRARY_PATH
+from the environment.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev LD_LIBRARY_PATH
+Additional directories containing shared libraries,
+settable in the user's environment.
+.It Ev shlib_dirs
+Additional directories containing shared libraries,
+settable in
+.Pa /etc/rc.conf.local .
+.El
+.Sh FILES
+.Bl -tag -width Ds -compact
+.It Pa /etc/rc.conf
+.It Pa /etc/rc.conf.local
+.It Pa /var/run/ld.so.hints
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr link 5 ,
+.Xr rc.conf 8
+.Sh HISTORY
+An
+.Nm
+utility first appeared in SunOS 4.0.
+It appeared in its current form in
+.Nx 0.9a .
--- /dev/null
+/* $OpenBSD: ldconfig.c,v 1.27 2010/03/30 17:42:50 zinovik Exp $ */
+
+/*
+ * Copyright (c) 1993,1995 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ar.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <stab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "prebind.h"
+
+#include "ld.h"
+
+#undef major
+#undef minor
+
+extern char *__progname;
+
+int verbose;
+static int delete;
+static int doprebind;
+static int nostd;
+static int justread;
+int merge;
+int safe;
+static int rescan;
+static int unconfig;
+
+struct shlib_list {
+ /* Internal list of shared libraries found */
+ char *name;
+ char *path;
+ int dewey[MAXDEWEY];
+ int ndewey;
+#define major dewey[0]
+#define minor dewey[1]
+ struct shlib_list *next;
+};
+
+static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
+static char *dir_list;
+
+static void enter(char *, char *, char *, int *, int);
+static int dodir(char *, int);
+static int buildhints(void);
+static int readhints(void);
+static void listhints(void);
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-DmPRrSsUv] [path ...]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, c;
+ int rval = 0;
+
+ while ((c = getopt(argc, argv, "DmPrRsSUv")) != -1) {
+ switch (c) {
+ case 'R':
+ rescan = 1;
+ break;
+ case 'U':
+ rescan = unconfig = 1;
+ break;
+ case 'm':
+ merge = 1;
+ break;
+ case 'r':
+ justread = 1;
+ break;
+ case 's':
+ nostd = 1;
+ break;
+ case 'S':
+ safe = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'D':
+ delete = 1;
+ break;
+ case 'P':
+ doprebind = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (unconfig && merge)
+ errx(1, "cannot use -U with -m");
+
+ dir_list = xmalloc(1);
+ *dir_list = '\0';
+
+ if (justread || merge || rescan) {
+ if ((rval = readhints()) != 0)
+ return rval;
+ if (justread) {
+ listhints();
+ return 0;
+ }
+ add_search_path(dir_list);
+ dir_list = xrealloc(dir_list, 1);
+ *dir_list = '\0';
+ } else if (!nostd)
+ std_search_path();
+
+ if (delete) {
+ if (rescan || unconfig || merge || justread || nostd || doprebind)
+ errx(1, "cannot mix -U -R -r -s -P options with -D");
+ exit(prebind_delete(&argv[optind]));
+ } else if (doprebind) {
+ if (rescan || unconfig || justread || nostd)
+ errx(1, "cannot mix other options with -P");
+ exit(prebind(&argv[optind]));
+ }
+
+ if (unconfig) {
+ if (optind < argc)
+ for (i = optind; i < argc; i++)
+ remove_search_dir(argv[i]);
+ else {
+ i = 0;
+ while (i < n_search_dirs) {
+ if (access(search_dirs[i], R_OK) < 0)
+ remove_search_dir(search_dirs[i]);
+ else
+ i++;
+ }
+ }
+ } else
+ for (i = optind; i < argc; i++)
+ add_search_dir(argv[i]);
+
+ for (i = 0; i < n_search_dirs; i++) {
+ char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
+
+ free(dir_list);
+ dir_list = cp;
+ rval |= dodir(search_dirs[i], 0);
+ }
+
+ rval |= buildhints();
+
+ return rval;
+}
+
+int
+dodir(char *dir, int silent)
+{
+ DIR *dd;
+ struct dirent *dp;
+ char name[MAXPATHLEN];
+ int dewey[MAXDEWEY], ndewey;
+
+ if ((dd = opendir(dir)) == NULL) {
+ if (!silent || errno != ENOENT)
+ warn("%s", dir);
+ return -1;
+ }
+
+ while ((dp = readdir(dd)) != NULL) {
+ size_t n;
+ char *cp;
+
+ /* Check for `lib' prefix */
+ if (dp->d_name[0] != 'l' ||
+ dp->d_name[1] != 'i' ||
+ dp->d_name[2] != 'b')
+ continue;
+
+ /* Copy the entry minus prefix */
+ (void)strlcpy(name, dp->d_name + 3, sizeof name);
+ n = strlen(name);
+ if (n < 4)
+ continue;
+
+ /* Find ".so." in name */
+ for (cp = name + n - 4; cp > name; --cp) {
+ if (cp[0] == '.' &&
+ cp[1] == 's' &&
+ cp[2] == 'o' &&
+ cp[3] == '.')
+ break;
+ }
+ if (cp <= name)
+ continue;
+
+ *cp = '\0';
+ if (!isdigit(*(cp+4)))
+ continue;
+
+ bzero((caddr_t)dewey, sizeof(dewey));
+ ndewey = getdewey(dewey, cp + 4);
+ enter(dir, dp->d_name, name, dewey, ndewey);
+ }
+ closedir(dd);
+ return 0;
+}
+
+static void
+enter(char *dir, char *file, char *name, int dewey[], int ndewey)
+{
+ struct shlib_list *shp;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ if (strcmp(name, shp->name) != 0 || major != shp->major)
+ continue;
+
+ /* Name matches existing entry */
+ if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
+
+ /* Update this entry with higher versioned lib */
+ if (verbose)
+ printf("Updating lib%s.%d.%d to %s/%s\n",
+ shp->name, shp->major, shp->minor,
+ dir, file);
+
+ free(shp->name);
+ shp->name = xstrdup(name);
+ free(shp->path);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ }
+ break;
+ }
+
+ if (shp)
+ /* Name exists: older version or just updated */
+ return;
+
+ /* Allocate new list element */
+ if (verbose)
+ printf("Adding %s/%s\n", dir, file);
+
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = xstrdup(name);
+ shp->path = concat(dir, "/", file);
+ bcopy(dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+}
+
+
+#if DEBUG
+/* test */
+#undef _PATH_LD_HINTS
+#define _PATH_LD_HINTS "./ld.so.hints"
+#endif
+
+static int
+hinthash(char *cp, int vmajor, int vminor)
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+#if 0
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+#endif
+
+ return k;
+}
+
+int
+buildhints(void)
+{
+ int strtab_sz = 0, nhints = 0, fd, i, ret = -1, str_index = 0;
+ struct hints_bucket *blist;
+ struct hints_header hdr;
+ struct shlib_list *shp;
+ char *strtab, *tmpfilenam;
+ size_t n;
+
+ for (shp = shlib_head; shp; shp = shp->next) {
+ strtab_sz += 1 + strlen(shp->name);
+ strtab_sz += 1 + strlen(shp->path);
+ nhints++;
+ }
+
+ /* Fill hints file header */
+ hdr.hh_magic = HH_MAGIC;
+ hdr.hh_version = LD_HINTS_VERSION_2;
+ hdr.hh_nbucket = 1 * nhints;
+ n = hdr.hh_nbucket * sizeof(struct hints_bucket);
+ hdr.hh_hashtab = sizeof(struct hints_header);
+ hdr.hh_strtab = hdr.hh_hashtab + n;
+ hdr.hh_dirlist = strtab_sz;
+ strtab_sz += 1 + strlen(dir_list);
+ hdr.hh_strtab_sz = strtab_sz;
+ hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
+
+ if (verbose)
+ printf("Totals: entries %d, buckets %ld, string size %d\n",
+ nhints, hdr.hh_nbucket, strtab_sz);
+
+ /* Allocate buckets and string table */
+ blist = (struct hints_bucket *)xmalloc(n);
+ bzero(blist, n);
+ for (i = 0; i < hdr.hh_nbucket; i++)
+ /* Empty all buckets */
+ blist[i].hi_next = -1;
+
+ strtab = xmalloc(strtab_sz);
+
+ /* Enter all */
+ for (shp = shlib_head; shp; shp = shp->next) {
+ struct hints_bucket *bp;
+
+ bp = blist + (hinthash(shp->name, shp->major, shp->minor) %
+ hdr.hh_nbucket);
+
+ if (bp->hi_pathx) {
+ int j;
+
+ for (j = 0; j < hdr.hh_nbucket; j++) {
+ if (blist[j].hi_pathx == 0)
+ break;
+ }
+ if (j == hdr.hh_nbucket) {
+ warnx("Bummer!");
+ goto out;
+ }
+ while (bp->hi_next != -1)
+ bp = &blist[bp->hi_next];
+ bp->hi_next = j;
+ bp = blist + j;
+ }
+
+ /* Insert strings in string table */
+ bp->hi_namex = str_index;
+ strlcpy(strtab + str_index, shp->name, strtab_sz - str_index);
+ str_index += 1 + strlen(shp->name);
+
+ bp->hi_pathx = str_index;
+ strlcpy(strtab + str_index, shp->path, strtab_sz - str_index);
+ str_index += 1 + strlen(shp->path);
+
+ /* Copy versions */
+ bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
+ bp->hi_ndewey = shp->ndewey;
+ }
+
+ /* Copy search directories */
+ strlcpy(strtab + str_index, dir_list, strtab_sz - str_index);
+ str_index += 1 + strlen(dir_list);
+
+ /* Sanity check */
+ if (str_index != strtab_sz)
+ errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
+
+ tmpfilenam = concat(_PATH_LD_HINTS, ".XXXXXXXXXX", "");
+ if ((fd = mkstemp(tmpfilenam)) == -1) {
+ warn("%s", tmpfilenam);
+ goto out;
+ }
+ fchmod(fd, 0444);
+
+ if (write(fd, &hdr, sizeof(struct hints_header)) !=
+ sizeof(struct hints_header)) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+ if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
+ hdr.hh_nbucket * sizeof(struct hints_bucket)) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+ if (write(fd, strtab, strtab_sz) != strtab_sz) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+ if (close(fd) != 0) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+
+ /* Install it */
+ if (unlink(_PATH_LD_HINTS) != 0 && errno != ENOENT) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+
+ if (rename(tmpfilenam, _PATH_LD_HINTS) != 0) {
+ warn("%s", _PATH_LD_HINTS);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ free(blist);
+ free(strtab);
+ return (ret);
+}
+
+static int
+readhints(void)
+{
+ struct stat sb;
+ struct hints_bucket *blist;
+ struct hints_header *hdr;
+ struct shlib_list *shp;
+ caddr_t addr;
+ char *strtab;
+ long msize;
+ int fd, i;
+
+ if ((fd = open(_PATH_LD_HINTS, O_RDONLY, 0)) == -1) {
+ warn("%s", _PATH_LD_HINTS);
+ return -1;
+ }
+ if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
+ sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX) {
+ warn("%s", _PATH_LD_HINTS);
+ return -1;
+ }
+
+ msize = (long)sb.st_size;
+ addr = mmap(0, msize, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (addr == MAP_FAILED) {
+ warn("%s", _PATH_LD_HINTS);
+ return -1;
+ }
+
+ hdr = (struct hints_header *)addr;
+ if (HH_BADMAG(*hdr)) {
+ warnx("%s: Bad magic: %lo",
+ _PATH_LD_HINTS, hdr->hh_magic);
+ return -1;
+ }
+
+ if (hdr->hh_ehints > msize) {
+ warnx("%s: hintsize greater than filesize: 0x%lx > 0x%lx ",
+ _PATH_LD_HINTS, hdr->hh_ehints, msize);
+ return -1;
+ }
+
+ if (hdr->hh_version != LD_HINTS_VERSION_2) {
+ warnx("Unsupported version: %ld", hdr->hh_version);
+ return -1;
+ }
+
+ close(fd);
+
+ blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
+ strtab = (char *)(addr + hdr->hh_strtab);
+
+ dir_list = xstrdup(strtab + hdr->hh_dirlist);
+
+ if (rescan)
+ return (0);
+
+ for (i = 0; i < hdr->hh_nbucket; i++) {
+ struct hints_bucket *bp = &blist[i];
+
+ /* Sanity check */
+ if (bp->hi_namex >= hdr->hh_strtab_sz) {
+ warnx("Bad name index: %#x", bp->hi_namex);
+ return -1;
+ }
+ if (bp->hi_pathx >= hdr->hh_strtab_sz) {
+ warnx("Bad path index: %#x", bp->hi_pathx);
+ return -1;
+ }
+
+ /* Allocate new list element */
+ shp = (struct shlib_list *)xmalloc(sizeof *shp);
+ shp->name = xstrdup(strtab + bp->hi_namex);
+ shp->path = xstrdup(strtab + bp->hi_pathx);
+ bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
+ shp->ndewey = bp->hi_ndewey;
+ shp->next = NULL;
+
+ *shlib_tail = shp;
+ shlib_tail = &shp->next;
+ }
+ return 0;
+}
+
+static void
+listhints(void)
+{
+ struct shlib_list *shp;
+ int i;
+
+ printf("%s:\n", _PATH_LD_HINTS);
+ printf("\tsearch directories: %s\n", dir_list);
+
+ for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
+ printf("\t%d:-l%s.%d.%d => %s\n",
+ i, shp->name, shp->major, shp->minor, shp->path);
+}
--- /dev/null
+/* $OpenBSD: library.c,v 1.3 2009/12/30 04:30:01 drahn Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <elf_abi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "link.h"
+#include "sod.h"
+#include "resolve.h"
+#include "prebind.h"
+#include "prebind_struct.h"
+
+/* TODO - library path from ldconfig */
+#define DEFAULT_PATH "/usr/lib"
+
+elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int ignore_hints, const char *libpath);
+char * elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints);
+elf_object_t * elf_tryload_shlib(const char *libname);
+int elf_match_file(struct sod *sodp, char *name, int namelen);
+
+int
+load_lib(const char *name, struct elf_object *parent)
+{
+ struct sod sod, req_sod;
+ int ignore_hints, try_any_minor = 0;
+ struct elf_object *object = NULL;
+
+#if 0
+ printf("load_lib %s\n", name);
+#endif
+ ignore_hints = 0;
+
+ if(strchr(name, '/')) {
+ char *lpath, *lname;
+
+ lpath = strdup(name);
+ lname = strrchr(lpath, '/');
+ if (lname == NULL || lname[1] == '\0') {
+ free(lpath);
+ return (1); /* failed */
+ }
+ *lname = '\0';
+ lname++;
+
+ _dl_build_sod(lname, &sod);
+ req_sod = sod;
+
+ /* this code does not allow lower minors */
+fullpathagain:
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, lpath);
+ if (object != NULL)
+ goto fullpathdone;
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto fullpathagain;
+ }
+ /* ERR */
+fullpathdone:
+ free(lpath);
+ free((char *)sod.sod_name);
+ return (object == NULL); /* failed */
+ }
+ _dl_build_sod(name, &sod);
+ req_sod = sod;
+
+ /* ignore LD_LIBRARY_PATH */
+
+again:
+ if (parent->dyn.rpath != NULL) {
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, parent->dyn.rpath);
+ if (object != NULL)
+ goto done;
+ }
+ if (parent != load_object && load_object->dyn.rpath != NULL) {
+ object = elf_load_shlib_hint(&sod, &req_sod,
+ ignore_hints, load_object->dyn.rpath);
+ if (object != NULL)
+ goto done;
+ }
+ object = elf_load_shlib_hint(&sod, &req_sod, ignore_hints, NULL);
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto again;
+ }
+ if (object == NULL)
+ printf ("unable to load %s\n", name);
+
+done:
+ free((char *)sod.sod_name);
+
+ return (object == NULL);
+}
+
+/*
+ * attempt to locate and load a library based on libpath, sod info and
+ * if it needs to respect hints, passing type and flags to perform open
+ */
+elf_object_t *
+elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int ignore_hints, const char *libpath)
+{
+ elf_object_t *object = NULL;
+ char *hint;
+
+ hint = elf_find_shlib(req_sod, libpath, ignore_hints);
+ if (hint != NULL) {
+ if (req_sod->sod_minor < sod->sod_minor)
+ printf("warning: lib%s.so.%d.%d: "
+ "minor version >= %d expected, "
+ "using it anyway\n",
+ (char *)sod->sod_name, sod->sod_major,
+ req_sod->sod_minor, sod->sod_minor);
+ object = elf_tryload_shlib(hint);
+ }
+ return object;
+}
+
+char elf_hint_store[MAXPATHLEN];
+
+char *
+elf_find_shlib(struct sod *sodp, const char *searchpath, int nohints)
+{
+ char *hint, lp[PATH_MAX + 10], *path;
+ struct sod tsod, bsod; /* transient and best sod */
+ struct dirent *dp;
+ const char *pp;
+ int match, len;
+ DIR *dd;
+
+ /* if we are to search default directories, and hints
+ * are not to be used, search the standard path from ldconfig
+ * (_dl_hint_search_path) or use the default path
+ */
+ if (nohints)
+ goto nohints;
+
+ if (searchpath == NULL) {
+ /* search 'standard' locations, find any match in the hints */
+ hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major,
+ sodp->sod_minor, NULL);
+ if (hint)
+ return hint;
+ } else {
+ /* search hints requesting matches for only
+ * the searchpath directories,
+ */
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX &&
+ *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ hint = _dl_findhint((char *)sodp->sod_name,
+ sodp->sod_major, sodp->sod_minor, lp);
+ if (hint != NULL)
+ return hint;
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ }
+
+ /*
+ * For each directory in the searchpath, read the directory
+ * entries looking for a match to sod. filename compare is
+ * done by _dl_match_file()
+ */
+nohints:
+ if (searchpath == NULL) {
+ if (_dl_hint_search_path != NULL)
+ searchpath = _dl_hint_search_path;
+ else
+ searchpath = DEFAULT_PATH;
+ }
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ if ((dd = opendir(lp)) != NULL) {
+ match = 0;
+ while ((dp = readdir(dd)) != NULL) {
+ tsod = *sodp;
+ if (elf_match_file(&tsod, dp->d_name,
+ dp->d_namlen)) {
+ /*
+ * When a match is found, tsod is
+ * updated with the major+minor found.
+ * This version is compared with the
+ * largest so far (kept in bsod),
+ * and saved if larger.
+ */
+ if (!match ||
+ tsod.sod_major == -1 ||
+ tsod.sod_major > bsod.sod_major ||
+ ((tsod.sod_major ==
+ bsod.sod_major) &&
+ tsod.sod_minor > bsod.sod_minor)) {
+ bsod = tsod;
+ match = 1;
+ len = strlcpy(
+ elf_hint_store, lp,
+ MAXPATHLEN);
+ if (lp[len-1] != '/') {
+ elf_hint_store[len] =
+ '/';
+ len++;
+ }
+ strlcpy(
+ &elf_hint_store[len],
+ dp->d_name,
+ MAXPATHLEN-len);
+ if (tsod.sod_major == -1)
+ break;
+ }
+ }
+ }
+ closedir(dd);
+ if (match) {
+ *sodp = bsod;
+ return (elf_hint_store);
+ }
+ }
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ return NULL;
+}
+
+elf_object_t *
+elf_tryload_shlib(const char *libname)
+{
+ struct elf_object *object;
+ object = elf_lookup_object(libname);
+
+ if (object == NULL)
+ object = load_file(libname, OBJTYPE_LIB);
+ if (object == NULL)
+ printf("tryload_shlib %s\n", libname);
+ return object;
+}
+
+/*
+ * elf_match_file()
+ *
+ * This fucntion determines if a given name matches what is specified
+ * in a struct sod. The major must match exactly, and the minor must
+ * be same or larger.
+ *
+ * sodp is updated with the minor if this matches.
+ */
+
+int
+elf_match_file(struct sod *sodp, char *name, int namelen)
+{
+ int match;
+ struct sod lsod;
+ char *lname;
+
+ lname = name;
+ if (sodp->sod_library) {
+ if (strncmp(name, "lib", 3))
+ return 0;
+ lname += 3;
+ }
+ if (strncmp(lname, (char *)sodp->sod_name,
+ strlen((char *)sodp->sod_name)))
+ return 0;
+
+ _dl_build_sod(name, &lsod);
+
+ match = 0;
+ if (strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0 &&
+ lsod.sod_library == sodp->sod_library &&
+ (sodp->sod_major == -1 || sodp->sod_major == lsod.sod_major) &&
+ (sodp->sod_minor == -1 || lsod.sod_minor >= sodp->sod_minor)) {
+ match = 1;
+
+ /* return version matched */
+ sodp->sod_major = lsod.sod_major;
+ sodp->sod_minor = lsod.sod_minor;
+ }
+ free((char *)lsod.sod_name);
+ return match;
+}
+
--- /dev/null
+/usr/obj/libexec/ld.so/ldconfig
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: prebind.c,v 1.12 2010/03/30 17:42:50 zinovik Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <elf_abi.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <err.h>
+#include "resolve.h"
+#include "link.h"
+#include "sod.h"
+#ifndef __mips64__
+#include "machine/reloc.h"
+#endif
+#include "prebind.h"
+#include "ld.h"
+
+/* seems to make sense to limit how big of file can be dealt with */
+#define MAX_FILE_SIZE (512 * 1024 * 1024)
+
+char *shstrtab;
+
+/* alpha uses RELOC_JMP_SLOT */
+#ifdef __amd64__
+#define RELOC_JMP_SLOT R_X86_64_JUMP_SLOT
+#endif
+#ifdef __arm__
+#define RELOC_JMP_SLOT R_ARM_JUMP_SLOT
+#endif
+#ifdef __hppa__
+#define RELOC_JMP_SLOT RELOC_IPLT
+#endif
+#ifdef __hppa64__
+#define RELOC_JMP_SLOT RELOC_JMPSLOT
+#endif
+#ifdef __i386__
+#define RELOC_JMP_SLOT RELOC_JUMP_SLOT
+#endif
+#ifdef __sh__
+#define RELOC_JMP_SLOT R_SH_JMP_SLOT
+#endif
+#ifdef __mips64__
+#define RELOC_JMP_SLOT 0 /* XXX mips64 doesnt have PLT reloc */
+#endif
+/* powerpc uses RELOC_JMP_SLOT */
+/* sparc uses RELOC_JMP_SLOT */
+/* sparc64 uses RELOC_JMP_SLOT */
+#if defined(__sparc__) && !defined(__sparc64__)
+/* ARGH, our sparc/include/reloc.h is wrong (for the moment) */
+#undef RELOC_JMP_SLOT
+#define RELOC_JMP_SLOT 21
+#endif
+
+#define BUFSZ (256 * 1024)
+
+#include "prebind_struct.h"
+struct proglist *curbin;
+
+obj_list_ty library_list =
+ TAILQ_HEAD_INITIALIZER(library_list);
+
+prog_list_ty prog_list =
+ TAILQ_HEAD_INITIALIZER(prog_list);
+
+struct objarray_list {
+ struct elf_object *obj;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct proglist *proglist;
+ u_int32_t id0;
+ u_int32_t id1;
+ u_int32_t *idxtolib;
+ void *oprebind_data;
+ int numlibs;
+
+ TAILQ_HEAD(, objlist) inst_list;
+} *objarray;
+
+
+struct prebind_info {
+ struct elf_object *object;
+ struct prebind_footer *footer;
+ u_int32_t footer_offset;
+ u_int32_t nfixup;
+ struct nameidx *nameidx;
+ struct symcachetab *symcache;
+ struct symcachetab *pltsymcache;
+ u_int32_t *fixuptab;
+ u_int32_t *fixupcnt;
+ struct fixup **fixup;
+ u_int32_t *maptab;
+ u_int32_t **libmap;
+ u_int32_t *libmapcnt;
+ char *nametab;
+ u_int32_t nametablen;
+};
+
+int objarray_cnt;
+int objarray_sz;
+
+void copy_oldsymcache(int objidx, void *prebind_data);
+void elf_load_existing_prebind(struct elf_object *object, int fd);
+
+struct elf_object * elf_load_object(void *pexe, const char *name);
+void elf_free_object(struct elf_object *object);
+void map_to_virt(Elf_Phdr *, Elf_Ehdr *, Elf_Addr, u_long *);
+int load_obj_needed(struct elf_object *object);
+int load_lib(const char *name, struct elf_object *parent);
+elf_object_t * elf_load_shlib_hint(struct sod *sod, struct sod *req_sod,
+ int use_hints, const char *libpath);
+char * elf_find_shlib(struct sod *sodp, const char *searchpath,
+ int nohints);
+elf_object_t * elf_tryload_shlib(const char *libname);
+int elf_match_file(struct sod *sodp, char *name, int namelen);
+void elf_init_objarray(void);
+void elf_add_object(struct elf_object *object, int objtype);
+void elf_print_objarray(void);
+void elf_reloc(struct elf_object *object);
+
+struct elf_object * elf_lookup_object(const char *name);
+struct elf_object * elf_lookup_object_devino(dev_t dev, ino_t inode,
+ int objtype);
+void elf_free_curbin_list(struct elf_object *obj);
+void elf_resolve_curbin(void);
+struct proglist *elf_newbin(void);
+void elf_sum_reloc();
+int elf_prep_lib_prebind(struct elf_object *object);
+int elf_prep_bin_prebind(struct proglist *pl);
+void add_fixup_prog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag);
+void add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj,
+ int idx, const struct elf_object *ref_obj, const Elf_Sym *ref_sym,
+ int flag);
+
+void elf_dump_footer(struct prebind_footer *footer);
+
+void elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object);
+void elf_clear_prog_load(int fd, struct elf_object *object);
+
+void elf_find_symbol_rel(const char *s, struct elf_object *object,
+ Elf_Rel *rel, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache);
+
+void elf_find_symbol_rela(const char *s, struct elf_object *object,
+ Elf_RelA *rela, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache);
+
+int elf_find_symbol_obj(elf_object_t *object, const char *name,
+ unsigned long hash, int flags, const Elf_Sym **this,
+ const Elf_Sym **weak_sym, elf_object_t **weak_object);
+
+int prebind_writefile(int fd, struct prebind_info *info);
+int prebind_writenewfile(int infd, char *name, struct stat *st, off_t orig_size,
+ struct prebind_info *info);
+
+struct elf_object *load_object;
+
+struct elf_object *load_file(const char *filename, int lib);
+int elf_check_note(void *buf, Elf_Phdr *phdr);
+void load_file_or_dir(char *name);
+void load_dir(char *name);
+void load_exe(char *name);
+
+int
+prebind(char **argv)
+{
+ int i;
+
+ elf_init_objarray();
+
+ for (i = 0; argv[i]; i++)
+ load_file_or_dir(argv[i]);
+
+ if (verbose > 4) {
+ elf_print_objarray();
+ elf_print_prog_list(&prog_list);
+ }
+ elf_sum_reloc();
+
+ return (0);
+}
+
+/*
+ * load ELF objects at the specified path it could be
+ * either a either a directory or file, if the object is
+ * a file, attempt to load it as an executable (will ignore shared objects
+ * and any files that are not Elf execuables.
+ * if the object is a directory pass it to a routine to deal with
+ * directory parsing.
+ */
+void
+load_file_or_dir(char *name)
+{
+ struct stat sb;
+ int ret;
+
+ ret = lstat(name, &sb);
+ if (ret != 0)
+ return;
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ load_exe(name);
+ break;
+ case S_IFDIR:
+ if (verbose > 0)
+ printf("loading dir %s\n", name);
+ load_dir(name);
+ break;
+ default:
+ ; /* links and other files we skip */
+ }
+
+}
+
+/*
+ * for all of the objects in the directory, if it is a regular file
+ * load it as a binary, if it is unknown (nfs mount) stat the file
+ * and load the file for S_IFREG
+ * any other type of directory object: symlink, directory, socket, ...
+ * is ignored.
+ */
+void
+load_dir(char *name)
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ char *buf;
+
+ dirp = opendir(name);
+
+ /* if dir failes to open, skip */
+ if (dirp == NULL)
+ return;
+
+ while ((dp = readdir(dirp)) != NULL) {
+ switch (dp->d_type) {
+ case DT_UNKNOWN:
+ /*
+ * NFS will return unknown, since load_file
+ * does stat the file, this just
+ */
+ asprintf(&buf, "%s/%s", name, dp->d_name);
+ lstat(buf, &sb);
+ if (sb.st_mode == S_IFREG)
+ load_exe(buf);
+ free(buf);
+ break;
+ case DT_REG:
+ asprintf(&buf, "%s/%s", name, dp->d_name);
+ load_exe(buf);
+ free(buf);
+ break;
+ default:
+ /* other files symlinks, dirs, ... we ignore */
+ ;
+ }
+ }
+ closedir(dirp);
+}
+
+/*
+ * the given pathname is a regular file, however it may or may not
+ * be an ELF file. Attempt to load the given path and calculate prebind
+ * data for it.
+ * if the given file is not a ELF binary this will 'fail' and
+ * should not change any of the prebind state.
+ */
+void
+load_exe(char *name)
+{
+ struct elf_object *object;
+ struct elf_object *interp;
+ struct objlist *ol;
+ int fail = 0;
+
+ curbin = elf_newbin();
+ if (verbose > 0)
+ printf("processing %s\n", name);
+ object = load_file(name, OBJTYPE_EXE);
+ if (object != NULL && load_object != NULL &&
+ object->load_object == NULL) {
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ fail = load_obj_needed(ol->object);
+ if (fail != 0)
+ break; /* XXX */
+
+ }
+ if (fail == 0) {
+ interp = load_file(curbin->interp, OBJTYPE_DLO);
+ object->load_object = interp;
+ if (interp == NULL)
+ fail = 1;
+ }
+
+ /* slight abuse of this field */
+
+ if (fail == 0) {
+ objarray[object->dyn.null].proglist = curbin;
+ elf_resolve_curbin();
+ TAILQ_INSERT_TAIL(&prog_list, curbin, list);
+ } else {
+ printf("failed to load %s\n", name);
+ elf_free_curbin_list(object);
+ free(curbin);
+ }
+ if (load_object != NULL) {
+ load_object = NULL;
+ }
+ } else {
+ free(curbin);
+ }
+}
+
+/*
+ * given a path to a file, attempt to open it and load any data necessary
+ * for prebind. this function is used for executables, libraries and ld.so
+ * file, it will do a lookup on the dev/inode to use a cached version
+ * of the file if it was already loaded, in case a library is referenced
+ * by more than one program or there are hardlinks between executable names.
+ * if the file is not an elf file of the appropriate type, it will return
+ * failure.
+ */
+struct elf_object *
+load_file(const char *filename, int objtype)
+{
+ struct elf_object *obj = NULL;
+ int fd = -1, i, note_found;
+ struct stat ifstat;
+ void *buf = NULL;
+ Elf_Ehdr *ehdr;
+ Elf_Shdr *shdr;
+ Elf_Phdr *phdr;
+ char *pexe;
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ perror(filename);
+ goto done;
+ }
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror(filename);
+ goto done;
+ }
+
+ if ((ifstat.st_mode & S_IFMT) != S_IFREG)
+ goto done;
+
+ if (ifstat.st_size < sizeof (Elf_Ehdr))
+ goto done;
+
+ obj = elf_lookup_object_devino(ifstat.st_dev, ifstat.st_ino, objtype);
+ if (obj != NULL)
+ goto done;
+
+ buf = mmap(NULL, ifstat.st_size, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap\n", filename);
+ goto done;
+ }
+
+ ehdr = (Elf_Ehdr *)buf;
+
+ if (IS_ELF(*ehdr) == 0)
+ goto done;
+
+ if (ehdr->e_machine != ELF_TARG_MACH) {
+ if (verbose > 0)
+ printf("%s: wrong arch\n", filename);
+ goto done;
+ }
+
+ if (objtype == OBJTYPE_EXE) {
+ if (ehdr->e_type != ET_EXEC)
+ goto done;
+
+ note_found = 0;
+
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ if (phdr[i].p_type == PT_NOTE) {
+ note_found = elf_check_note(buf,&phdr[i]);
+ break;
+ }
+ }
+ if (note_found == 0)
+ goto done; /* no OpenBSD note found */
+ }
+
+ if ((objtype == OBJTYPE_LIB || objtype == OBJTYPE_DLO) &&
+ (ehdr->e_type != ET_DYN))
+ goto done;
+
+ pexe = buf;
+ if (ehdr->e_shstrndx == 0)
+ goto done;
+
+ shdr = (Elf_Shdr *)(pexe + ehdr->e_shoff +
+ (ehdr->e_shstrndx * ehdr->e_shentsize));
+
+ shstrtab = (char *)(pexe + shdr->sh_offset);
+
+ obj = elf_load_object(pexe, filename);
+
+ munmap(buf, ifstat.st_size);
+ buf = NULL;
+
+ if (obj != NULL) {
+ obj->obj_type = objtype;
+
+ obj->dev = ifstat.st_dev;
+ obj->inode = ifstat.st_ino;
+ if (load_object == NULL)
+ load_object = obj;
+
+ elf_add_object(obj, objtype);
+
+#ifdef DEBUG1
+ dump_info(obj);
+#endif
+ }
+ if ((objtype == OBJTYPE_LIB || objtype == OBJTYPE_DLO) &&
+ merge == 1) {
+ /*
+ * for libraries and dynamic linker, check if old prebind
+ * info exists and load it if we are in merge mode
+ */
+ elf_load_existing_prebind(obj, fd);
+ }
+done:
+ if (buf != NULL)
+ munmap(buf, ifstat.st_size);
+ if (fd != -1)
+ close(fd);
+ return obj;
+}
+
+/*
+ * check if the given executable header on a ELF executable
+ * has the proper OpenBSD note on the file if it is not present
+ * binaries will be skipped.
+ */
+int
+elf_check_note(void *buf, Elf_Phdr *phdr)
+{
+ Elf_Ehdr *ehdr;
+ u_long address;
+ u_int *pint;
+ char *osname;
+
+ ehdr = (Elf_Ehdr *)buf;
+ address = phdr->p_offset;
+ pint = (u_int *)((char *)buf + address);
+ osname = (char *)buf + address + sizeof(*pint) * 3;
+
+ if (pint[0] == 8 /* OpenBSD\0 */ &&
+ pint[1] == 4 /* ??? */ &&
+ pint[2] == 1 /* type_osversion */ &&
+ strcmp("OpenBSD", osname) == 0)
+ return 1;
+
+ return 0;
+}
+
+struct elf_object *
+elf_load_object(void *pexe, const char *name)
+{
+ struct elf_object *object;
+ Elf_Dyn *dynp = NULL, *odynp;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Addr lbase;
+ Elf_Word *needed_list;
+ int needed_cnt = 0, i;
+
+ object = calloc(1, sizeof (struct elf_object));
+ if (object == NULL) {
+ printf("unable to allocate object for %s\n", name);
+ exit(10);
+ }
+ ehdr = pexe;
+ lbase = (Elf_Addr)pexe;
+
+ object->load_base = lbase;
+ object->load_name = strdup(name);
+
+ phdr = (Elf_Phdr *)((char *)pexe + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ switch (phdr[i].p_type) {
+ case PT_DYNAMIC:
+ dynp = (Elf_Dyn *)(phdr[i].p_offset);
+ break;
+ case PT_INTERP:
+ /* XXX can only occur in programs */
+ curbin->interp = strdup((char *)((char *)pexe +
+ phdr[i].p_offset));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (dynp == 0) {
+ free(object);
+ return NULL; /* not a dynamic binary */
+ }
+
+ dynp = (Elf_Dyn *)((unsigned long)dynp + lbase);
+ odynp = dynp;
+ while (dynp->d_tag != DT_NULL) {
+ if (dynp->d_tag < DT_NUM)
+ object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
+ else if (dynp->d_tag >= DT_LOPROC &&
+ dynp->d_tag < DT_LOPROC + DT_PROCNUM)
+ object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
+ dynp->d_un.d_val;
+ if (dynp->d_tag == DT_TEXTREL)
+ object->dyn.textrel = 1;
+ if (dynp->d_tag == DT_SYMBOLIC)
+ object->dyn.symbolic = 1;
+ if (dynp->d_tag == DT_BIND_NOW)
+ object->obj_flags = RTLD_NOW;
+ if (dynp->d_tag == DT_NEEDED)
+ needed_cnt++;
+
+ dynp++;
+ }
+
+ needed_list = calloc((needed_cnt + 1), sizeof(Elf_Word));
+ if (needed_list == NULL) {
+ printf("unable to allocate needed_list for %s\n", name);
+ exit(10);
+ }
+ needed_list[needed_cnt] = 0;
+ for (dynp = odynp, i = 0; dynp->d_tag != DT_NULL; dynp++) {
+ if (dynp->d_tag == DT_NEEDED) {
+ needed_list[i] = dynp->d_un.d_val;
+ i++;
+ }
+ }
+
+ if (object->Dyn.info[DT_HASH])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_HASH]);
+ if (object->Dyn.info[DT_STRTAB])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_STRTAB]);
+ if (object->Dyn.info[DT_SYMTAB])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_SYMTAB]);
+
+ if (object->Dyn.info[DT_RELA])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_RELA]);
+ if (object->Dyn.info[DT_RPATH])
+ object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
+ if (object->Dyn.info[DT_REL])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_REL]);
+ if (object->Dyn.info[DT_JMPREL])
+ map_to_virt(phdr, ehdr, lbase, &object->Dyn.info[DT_JMPREL]);
+
+ {
+ Elf_Sym *sym;
+ char *str;
+ Elf_Rel *rel;
+ Elf_RelA *rela;
+ Elf_Addr *hash;
+ Elf_Word *hashtab;
+ void *plt;
+ size_t hashsz;
+
+ if (object->Dyn.info[DT_HASH] != 0) {
+ hash = object->dyn.hash;
+ hashtab = (void *)hash;
+ object->nbuckets = hashtab[0];
+ object->nchains = hashtab[1];
+ hashsz = (2 + object->nbuckets + object->nchains) *
+ sizeof (Elf_Word);
+ hash = malloc(hashsz);
+ if (hash == NULL) {
+ printf("unable to allocate hash for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.hash, hash, hashsz);
+ object->dyn.hash = hash;
+ object->buckets = ((Elf_Word *)hash + 2);
+ object->chains = object->buckets + object->nbuckets;
+ }
+
+ str = malloc(object->dyn.strsz);
+ if (str == NULL) {
+ printf("unable to allocate strtab for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.strtab, str, object->dyn.strsz);
+ object->dyn.strtab = str;
+
+ sym = calloc(object->nchains, sizeof(Elf_Sym));
+ if (sym == NULL) {
+ printf("unable to allocate symtab for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.symtab, sym,
+ object->nchains * sizeof(Elf_Sym));
+ object->dyn.symtab = sym;
+
+ if (object->dyn.relsz != 0) {
+ rel = malloc(object->dyn.relsz);
+ if (rel == NULL) {
+ printf("unable to allocate rel reloc for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.rel, rel, object->dyn.relsz);
+ object->dyn.rel = rel;
+ } else {
+ object->dyn.rel = NULL;
+ }
+ if (object->dyn.relasz != 0) {
+ rela = malloc(object->dyn.relasz);
+ if (rela == NULL) {
+ printf("unable to allocate rela reloc for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy(object->dyn.rela, rela, object->dyn.relasz);
+ object->dyn.rela = rela;
+ } else {
+ object->dyn.rela = NULL;
+ }
+ if (object->dyn.pltrelsz != 0) {
+ plt = malloc(object->dyn.pltrelsz);
+ if (plt == NULL) {
+ printf("unable to allocate plt reloc for %s\n",
+ name);
+ exit(10);
+ }
+ bcopy((void*)object->dyn.jmprel, plt,
+ object->dyn.pltrelsz);
+ object->dyn.jmprel = (long)plt;
+ } else {
+ object->dyn.jmprel = NULL;
+ }
+ if (object->dyn.rpath != NULL){
+ object->dyn.rpath = strdup(object->dyn.rpath);
+ if (object->dyn.rpath == NULL) {
+ printf("unable to allocate rpath for %s\n",
+ name);
+ exit(10);
+ }
+ }
+ object->dyn.needed = (Elf_Addr)needed_list;
+ }
+
+#ifdef DEBUG1
+ dump_info(object);
+#endif
+ return object;
+}
+
+/*
+ * Free any extra pieces associated with 'object'
+ */
+void
+elf_free_object(struct elf_object *object)
+{
+ free(object->load_name);
+ if (object->dyn.hash != NULL)
+ free(object->dyn.hash);
+ free((void *)object->dyn.strtab);
+ free((void *)object->dyn.symtab);
+ if (object->dyn.rel != NULL)
+ free(object->dyn.rel);
+ if (object->dyn.rela != NULL)
+ free(object->dyn.rela);
+ if (object->dyn.rpath != NULL)
+ free((void *)object->dyn.rpath);
+ free(object);
+}
+
+/*
+ * translate an object address into a file offset for the
+ * file assuming that the file is mapped at base.
+ */
+void
+map_to_virt(Elf_Phdr *phdr, Elf_Ehdr *ehdr, Elf_Addr base, u_long *vaddr)
+{
+ int i;
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ switch (phdr[i].p_type) {
+ case PT_LOAD:
+ if (phdr[i].p_vaddr > *vaddr)
+ continue;
+ if (phdr[i].p_vaddr + phdr[i].p_memsz < *vaddr)
+ continue;
+#ifdef DEBUG1
+ printf("input address %lx translated to ", *vaddr);
+#endif
+ *vaddr += phdr[i].p_offset - phdr[i].p_vaddr + base;
+#ifdef DEBUG1
+ printf("%lx, base %lx %lx %llx\n", *vaddr, base,
+ phdr[i].p_vaddr, phdr[i].p_offset );
+#endif
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * given a dynamic elf object (executable or binary)
+ * load any DT_NEEDED entries which were found when
+ * the object was initially loaded.
+ */
+int
+load_obj_needed(struct elf_object *object)
+{
+ int i;
+ Elf_Word *needed_list;
+ int err;
+
+ needed_list = (Elf_Word *)object->dyn.needed;
+ for (i = 0; needed_list[i] != NULL; i++) {
+ if (verbose > 1)
+ printf("lib: %s\n", needed_list[i] +
+ object->dyn.strtab);
+ err = load_lib(needed_list[i] + object->dyn.strtab, object);
+ if (err) {
+ printf("failed to load lib %s\n",
+ needed_list[i] + object->dyn.strtab);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * allocate a proglist entry for a new binary
+ * so that it is available for libraries to reference
+ */
+struct proglist *
+elf_newbin(void)
+{
+ struct proglist *proglist;
+ proglist = malloc(sizeof (struct proglist));
+ if (proglist == NULL) {
+ printf("unable to allocate proglist\n");
+ exit(10);
+ }
+ proglist->fixup = NULL;
+ TAILQ_INIT(&(proglist->curbin_list));
+ return proglist;
+}
+
+/*
+ * Copy the contents of a libraries symbol cache instance into
+ * the 'global' symbol cache for that library
+ * this will currently resolve conflicts between mismatched
+ * libraries by flagging any mismatches as invalid
+ * which will cause all programs to generate a fixup
+ * It probably would be interesting to modify this to keep the most
+ * common entry as a library cache, and only have a fixup in programs
+ * where the symbol is overridden.
+ * This is run once each for the (got)symcache and pltsymcache
+ */
+
+struct elf_object badobj_store;
+struct elf_object *badobj = &badobj_store;
+
+/*
+ * copy the symbols found in a library symcache to the 'master/common'
+ * symbol table note that this will skip copying the following references
+ * 1. non-existing entries
+ * 2. symobj == prog &&& obj != prog
+ * 3. symobj == prog's interpter (references to dl_open)
+ */
+void
+elf_copy_syms(struct symcache_noflag *tcache, struct symcache_noflag *scache,
+ struct elf_object *obj, struct elf_object *prog, int nsyms)
+{
+ int i;
+ int lib_prog_ref;
+ for (i = 0; i < nsyms; i++) {
+ if (scache[i].obj == NULL)
+ continue;
+
+ if (tcache[i].obj != NULL) {
+ lib_prog_ref = (obj != prog && scache[i].obj == prog);
+ if (scache[i].obj != tcache[i].obj || lib_prog_ref) {
+ if (verbose > 2) {
+ printf("sym mismatch %d: "
+ "obj %d: sym %ld %s "
+ "nobj %s\n",
+ i, (int)scache[i].obj->dyn.null,
+ scache[i].sym -
+ scache[i].obj->dyn.symtab,
+ scache[i].sym->st_name +
+ scache[i].obj->dyn.strtab,
+ scache[i].obj->load_name);
+ }
+
+ /*
+ * if one of the symbol entries
+ * happens to be a self reference
+ * go ahead and keep that reference
+ * prevents some instances of fixups
+ * for every binary, eg one program
+ * overriding malloc() will not make
+ * ever binary have a fixup for libc
+ * references to malloc()
+ */
+ if (scache[i].obj == obj) {
+ tcache[i].obj = scache[i].obj;
+ tcache[i].sym = scache[i].sym;
+ } else if (tcache[i].obj == obj) {
+ /* no change necessary */
+ } else {
+ tcache[i].obj = badobj;
+ tcache[i].sym = NULL;
+ }
+ }
+ } else {
+ if (scache[i].obj != prog) {
+ tcache[i].obj = scache[i].obj;
+ tcache[i].sym = scache[i].sym;
+ }
+ }
+ }
+}
+
+void
+insert_sym_objcache(struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flags)
+{
+ struct symcache_noflag *tcache;
+ struct elf_object *prog;
+
+ prog = TAILQ_FIRST(&(curbin->curbin_list))->object;
+
+ if (flags)
+ tcache = objarray[obj->dyn.null].pltsymcache;
+ else
+ tcache = objarray[obj->dyn.null].symcache;
+
+ if (tcache[idx].obj != NULL) {
+ if (ref_obj != tcache[idx].obj ||
+ (obj != prog && ref_obj == prog)) {
+ if (verbose > 2) {
+ printf("sym mismatch %d: "
+ "obj %d: sym %ld %s "
+ "nobj %s\n",
+ idx, (int)ref_obj->dyn.null,
+ ref_sym -
+ ref_obj->dyn.symtab,
+ ref_sym->st_name +
+ ref_obj->dyn.strtab,
+ ref_obj->load_name);
+ }
+
+ /*
+ * if one of the symbol entries
+ * happens to be a self reference
+ * go ahead and keep that reference
+ * prevents some instances of fixups
+ * for every binary, eg one program
+ * overriding malloc() will not make
+ * ever binary have a fixup for libc
+ * references to malloc()
+ */
+ if (ref_obj == obj) {
+ tcache[idx].obj = ref_obj;
+ tcache[idx].sym = ref_sym;
+ add_fixup_oldprog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ } else if (tcache[idx].obj == obj) {
+ /* no change necessary */
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ } else {
+ add_fixup_oldprog(prog, obj, idx,
+ tcache[idx].obj, tcache[idx].sym, flags);
+ tcache[idx].obj = badobj;
+ tcache[idx].sym = NULL;
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ }
+ }
+ } else {
+ if (ref_obj != prog) {
+ tcache[idx].obj = ref_obj;
+ tcache[idx].sym = ref_sym;
+ } else {
+ add_fixup_prog(prog, obj, idx, ref_obj,
+ ref_sym, flags);
+ }
+ }
+}
+
+void
+add_fixup_prog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag)
+{
+ struct proglist *pl;
+ int i, libidx, cnt;
+
+ pl = objarray[prog->dyn.null].proglist;
+
+ libidx = -1;
+ for (i = 0; i < pl->nobj; i++) {
+ if (pl->libmap[0][i] == obj->dyn.null) {
+ libidx = (i * 2) + ((flag & SYM_PLT) ? 1 : 0);
+ break;
+ }
+ }
+ if (libidx == -1) {
+ printf("unable to find object\n");
+ return;
+ }
+
+ /* have to check for duplicate patches */
+ for (i = 0; i < pl->fixupcnt[libidx]; i++) {
+ if (pl->fixup[libidx][i].sym == idx)
+ return;
+ }
+
+ if (verbose > 1)
+ printf("fixup for obj %s on prog %s sym %s: %d\n",
+ obj->load_name, prog->load_name,
+ ref_obj->dyn.strtab + ref_sym->st_name,
+ pl->fixupcnt[libidx]);
+
+ if (pl->fixupcntalloc[libidx] < pl->fixupcnt[libidx] + 1) {
+ pl->fixupcntalloc[libidx] += 16;
+ pl->fixup[libidx] = realloc(pl->fixup[libidx],
+ sizeof (struct fixup) * pl->fixupcntalloc[libidx]);
+ if (pl->fixup[libidx] == NULL) {
+ printf("realloc fixup, out of memory\n");
+ exit(20);
+ }
+ }
+ cnt = pl->fixupcnt[libidx];
+ pl->fixup[libidx][cnt].sym = idx;
+ pl->fixup[libidx][cnt].obj_idx = ref_obj->dyn.null;
+ pl->fixup[libidx][cnt].sym_idx = ref_sym - ref_obj->dyn.symtab;
+ pl->fixupcnt[libidx]++;
+}
+
+void
+add_fixup_oldprog(struct elf_object *prog, struct elf_object *obj, int idx,
+ const struct elf_object *ref_obj, const Elf_Sym *ref_sym, int flag)
+{
+ struct objlist *ol;
+
+ TAILQ_FOREACH(ol, &(objarray[obj->dyn.null].inst_list), inst_list) {
+ if (ol->load_prog == prog) {
+ continue;
+ }
+ /* process here */
+
+ add_fixup_prog(ol->load_prog, obj, idx, ref_obj, ref_sym, flag);
+ }
+
+}
+
+struct elf_object *
+elf_lookup_object(const char *name)
+{
+ struct objlist *ol;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ if (strcmp (name, ol->object->load_name) == 0) {
+ return ol->object;
+ }
+ }
+ TAILQ_FOREACH(ol, &library_list, list) {
+ if (strcmp (name, ol->object->load_name) == 0) {
+ elf_add_object_curbin_list(ol->object);
+ return ol->object;
+ }
+ }
+ return NULL;
+}
+
+struct elf_object *
+elf_lookup_object_devino(dev_t dev, ino_t inode, int objtype)
+{
+ struct objlist *ol;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode) {
+ if (ol->object->obj_type != objtype)
+ return NULL;
+ return ol->object;
+ }
+ }
+ TAILQ_FOREACH(ol, &library_list, list) {
+ if (ol->object->dev == dev &&
+ ol->object->inode == inode) {
+ if (ol->object->obj_type != objtype)
+ return NULL;
+ if (objtype != OBJTYPE_EXE)
+ elf_add_object_curbin_list(ol->object);
+ return ol->object;
+ }
+ }
+ return NULL;
+}
+
+void
+elf_find_symbol_rel(const char *name, struct elf_object *object,
+ Elf_Rel *rel, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache)
+{
+ struct objlist *ol;
+ unsigned long h = 0;
+ const char *p = name;
+ const Elf_Sym *sym, *ref_sym = NULL;
+ const Elf_Sym *weak_sym = NULL;
+ struct elf_object *weak_obj = NULL;
+ int flags = 0;
+ int found = 0;
+ int type, idx;
+ struct elf_object *ref_object = NULL;
+
+ sym = object->dyn.symtab + ELF_R_SYM(rel->r_info);
+
+ while (*p) {
+ unsigned long g;
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+
+ type = ELF_R_TYPE(rel->r_info);
+ flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND;
+ if (type == RELOC_JMP_SLOT)
+ flags |= SYM_PLT;
+
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ found = elf_find_symbol_obj(ol->object, name, h, flags, &sym,
+ &weak_sym, &weak_obj);
+ if (found) {
+ ref_object = ol->object;
+ break;
+ }
+
+ }
+ if (found) {
+ ref_object = ol->object;
+ ref_sym = sym;
+ } else if (weak_obj != NULL) {
+ found = 1;
+ ref_object = weak_obj;
+ ref_sym = weak_sym;
+ }
+ if (found == 1) {
+ idx = ELF_R_SYM(rel->r_info);
+ if (flags & SYM_PLT) {
+ pltsymcache[idx].obj = ref_object;
+ pltsymcache[idx].sym = ref_sym;
+ } else {
+ symcache[idx].obj = ref_object;
+ symcache[idx].sym = ref_sym;
+ }
+ } else {
+ /* It is not an error to have an undefined weak symbol */
+ const Elf_Sym *sym;
+ sym = object->dyn.symtab + ELF_R_SYM(rel->r_info);
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK) {
+ printf("symbol not found %s\n", name);
+ }
+ }
+}
+
+void
+elf_find_symbol_rela(const char *name, struct elf_object *object,
+ Elf_RelA *rela, struct symcache_noflag *symcache,
+ struct symcache_noflag *pltsymcache)
+{
+ struct objlist *ol;
+ unsigned long h = 0;
+ const char *p = name;
+ const Elf_Sym *sym, *ref_sym = NULL;
+ const Elf_Sym *weak_sym = NULL;
+ struct elf_object *weak_obj = NULL;
+ int flags = 0;
+ int found = 0;
+ int type, idx;
+ struct elf_object *ref_object = NULL;
+
+ sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
+
+ while (*p) {
+ unsigned long g;
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+
+ type = ELF_R_TYPE(rela->r_info);
+ flags = SYM_SEARCH_ALL|SYM_WARNNOTFOUND;
+ if (type == RELOC_JMP_SLOT)
+ flags |= SYM_PLT;
+
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+
+// printf("searching sym [%s] typ %d in obj %s\n", name, type, ol->object->load_name);
+ found = elf_find_symbol_obj(ol->object, name, h, flags, &sym,
+ &weak_sym, &weak_obj);
+ if (found) {
+ ref_object = ol->object;
+ break;
+ }
+
+ }
+ if (found) {
+ ref_object = ol->object;
+ ref_sym = sym;
+ } else if (weak_obj != NULL) {
+ found = 1;
+ ref_object = weak_obj;
+ ref_sym = weak_sym;
+ }
+ if (found == 1) {
+ idx = ELF_R_SYM(rela->r_info);
+ if (flags & SYM_PLT) {
+ pltsymcache[idx].obj = ref_object;
+ pltsymcache[idx].sym = ref_sym;
+ } else {
+ symcache[idx].obj = ref_object;
+ symcache[idx].sym = ref_sym;
+ }
+ } else {
+ /* It is not an error to have an undefined weak symbol */
+ const Elf_Sym *sym;
+ sym = object->dyn.symtab + ELF_R_SYM(rela->r_info);
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK) {
+ printf("symbol not found %s\n", name);
+ }
+ }
+}
+
+int
+elf_find_symbol_obj(elf_object_t *object, const char *name, unsigned long hash,
+ int flags, const Elf_Sym **this, const Elf_Sym **weak_sym,
+ elf_object_t **weak_object)
+{
+ const Elf_Sym *symt = object->dyn.symtab;
+ const char *strt = object->dyn.strtab;
+ long si;
+ const char *symn;
+
+ for (si = object->buckets[hash % object->nbuckets];
+ si != STN_UNDEF; si = object->chains[si]) {
+ const Elf_Sym *sym = symt + si;
+
+ if (sym->st_value == 0)
+ continue;
+
+ if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE &&
+ ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+
+ symn = strt + sym->st_name;
+ if (sym != *this && strcmp(symn, name))
+ continue;
+
+ /* allow this symbol if we are referring to a function
+ * which has a value, even if section is UNDEF.
+ * this allows &func to refer to PLT as per the
+ * ELF spec. st_value is checked above.
+ * if flags has SYM_PLT set, we must have actual
+ * symbol, so this symbol is skipped.
+ */
+ if (sym->st_shndx == SHN_UNDEF) {
+ if ((flags & SYM_PLT) || sym->st_value == 0 ||
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+ }
+
+ if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
+ *this = sym;
+ return 1;
+ } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
+ if (!*weak_sym) {
+ *weak_sym = sym;
+ *weak_object = object;
+ }
+ }
+ }
+ return 0;
+}
+
+void
+elf_reloc(struct elf_object *object)
+{
+ const Elf_Sym *sym;
+ Elf_Rel *rel;
+ Elf_RelA *rela;
+ int numrel;
+ int numrela;
+ int i;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+
+ numrel = object->dyn.relsz / sizeof(Elf_Rel);
+#ifdef DEBUG1
+ printf("rel relocations: %d\n", numrel);
+#endif
+#if 1
+ symcache = calloc(sizeof(struct symcache_noflag),
+ object->nchains);
+ pltsymcache = calloc(sizeof(struct symcache_noflag),
+ object->nchains);
+ if (symcache == NULL || pltsymcache == NULL) {
+ printf("unable to allocate memory for cache %s\n",
+ object->load_name);
+ exit(20);
+ }
+#endif
+ rel = object->dyn.rel;
+ for (i = 0; i < numrel; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), s,
+ ELF_R_TYPE(rel[i].r_info));
+#endif
+ if (ELF_R_SYM(rel[i].r_info) != 0) {
+ elf_find_symbol_rel(s, object, &rel[i],
+ symcache, pltsymcache);
+ }
+ }
+ if (numrel) {
+ numrel = object->dyn.pltrelsz / sizeof(Elf_Rel);
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+#ifdef DEBUG1
+ printf("rel plt relocations: %d\n", numrel);
+#endif
+ for (i = 0; i < numrel; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rel[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rel[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %d %s type %d\n", i, rel[i].r_offset,
+ ELF_R_SYM(rel[i].r_info), s,
+ ELF_R_TYPE(rel[i].r_info));
+#endif
+ if (ELF_R_SYM(rel[i].r_info) != 0) {
+ elf_find_symbol_rel(s, object, &rel[i],
+ symcache, pltsymcache);
+ }
+ }
+ }
+
+ numrela = object->dyn.relasz / sizeof(Elf_RelA);
+#ifdef DEBUG1
+ printf("rela relocations: %d\n", numrela);
+#endif
+ rela = object->dyn.rela;
+ for (i = 0; i < numrela; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i, rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), s,
+ ELF_R_TYPE(rela[i].r_info));
+#endif
+ if (ELF_R_SYM(rela[i].r_info) != 0) {
+ elf_find_symbol_rela(s, object, &rela[i],
+ symcache, pltsymcache);
+ }
+ }
+ if (numrela) {
+ numrela = object->dyn.pltrelsz / sizeof(Elf_RelA);
+#ifdef DEBUG1
+ printf("rela plt relocations: %d\n", numrela);
+#endif
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+
+ for (i = 0; i < numrela; i++) {
+ const char *s;
+ sym = object->dyn.symtab + ELF_R_SYM(rela[i].r_info);
+
+ /* hppa has entries without names, skip them */
+ if (sym->st_name == 0)
+ continue;
+
+ s = (ELF_R_SYM(rela[i].r_info) == 0) ? "<rel>" :
+ object->dyn.strtab + sym->st_name;
+#ifdef DEBUG1
+ printf("%d: %x sym %x %s type %d\n", i,
+ rela[i].r_offset,
+ ELF_R_SYM(rela[i].r_info), s,
+ ELF_R_TYPE(rela[i].r_info));
+#endif
+ if (ELF_R_SYM(rela[i].r_info) != 0) {
+ elf_find_symbol_rela(s, object, &rela[i],
+ symcache, pltsymcache);
+ }
+ }
+ }
+
+ for (i = 0; i < object->nchains; i++)
+ if (symcache[i].sym != NULL)
+ insert_sym_objcache(object, i, symcache[i].obj,
+ symcache[i].sym, 0);
+
+ for (i = 0; i < object->nchains; i++)
+ if (pltsymcache[i].sym != NULL)
+ insert_sym_objcache(object, i, pltsymcache[i].obj,
+ pltsymcache[i].sym, SYM_PLT);
+
+ free(symcache);
+ free(pltsymcache);
+}
+
+void
+elf_resolve_curbin(void)
+{
+ struct objlist *ol;
+ int numobj = 0;
+
+#ifdef DEBUG1
+ elf_print_curbin_list(curbin);
+#endif
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ numobj++;
+ }
+ curbin->nobj = numobj;
+ curbin->libmap = xcalloc(numobj, sizeof (u_int32_t *));
+ curbin->libmap[0] = xcalloc(numobj, sizeof (u_int32_t *));
+ curbin->fixup = xcalloc(2 * numobj, sizeof (struct fixup *));
+ curbin->fixupcnt = xcalloc(2 * numobj, sizeof (int));
+ curbin->fixupcntalloc = xcalloc(2 * numobj, sizeof (int));
+
+ numobj = 0;
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ curbin->libmap[0][numobj] = ol->object->dyn.null;
+ numobj++;
+ }
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ elf_reloc(ol->object);
+ }
+}
+
+void
+elf_add_object_curbin_list(struct elf_object *object)
+{
+ struct objlist *ol;
+
+ ol = xmalloc(sizeof (struct objlist));
+ ol->object = object;
+ TAILQ_INSERT_TAIL(&(curbin->curbin_list), ol, list);
+ if (load_object == NULL)
+ load_object = object;
+ ol->load_prog = load_object;
+
+ TAILQ_INSERT_TAIL(&(objarray[object->dyn.null].inst_list), ol,
+ inst_list);
+}
+void
+elf_init_objarray(void)
+{
+ objarray_sz = 512;
+ objarray = xcalloc(sizeof (objarray[0]), objarray_sz);
+}
+
+void
+elf_sum_reloc()
+{
+ int numobjs;
+ int err = 0;
+ struct objlist *ol;
+ struct proglist *pl;
+
+ TAILQ_FOREACH(ol, &library_list, list) {
+ err += elf_prep_lib_prebind(ol->object);
+ }
+ TAILQ_FOREACH(pl, &prog_list, list) {
+ numobjs = 0;
+ TAILQ_FOREACH(ol, &(pl->curbin_list), list) {
+ numobjs++;
+ }
+ pl->nobj = numobjs;
+ }
+
+ TAILQ_FOREACH(pl, &prog_list, list)
+ err += elf_prep_bin_prebind(pl);
+
+ if (err != 0)
+ printf("failures %d\n", err);
+}
+
+int
+elf_prep_lib_prebind(struct elf_object *object)
+{
+ int numlibs = 0;
+ int ret = 0;
+ int i;
+ int ref_obj;
+ int *libmap;
+ int *idxtolib;
+ struct nameidx *nameidx;
+ char *nametab;
+ int nametablen;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct symcachetab *symcachetab;
+ int symcache_cnt = 0;
+ struct symcachetab *pltsymcachetab;
+ int pltsymcache_cnt = 0;
+
+ symcache = objarray[object->dyn.null].symcache;
+ pltsymcache = objarray[object->dyn.null].pltsymcache;
+ libmap = xcalloc(objarray_cnt, sizeof (int));
+ idxtolib = xcalloc(objarray_cnt, sizeof (int));
+ objarray[object->dyn.null].idxtolib = idxtolib;
+
+ for (i = 0; i < objarray_cnt; i++)
+ libmap[i] = -1;
+
+ nametablen = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ ref_obj = symcache[i].obj->dyn.null;
+ symcache_cnt++;
+ if (libmap[ref_obj] != -1)
+ continue;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ nametablen += strlen(symcache[i].obj->load_name) + 1;
+ numlibs++;
+ }
+ symcachetab = xcalloc(symcache_cnt , sizeof(struct symcachetab));
+
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ symcachetab[symcache_cnt].idx = i;
+ symcachetab[symcache_cnt].obj_idx =
+ libmap[symcache[i].obj->dyn.null];
+ symcachetab[symcache_cnt].sym_idx =
+ symcache[i].sym - symcache[i].obj->dyn.symtab;
+ symcache_cnt++;
+ }
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ ref_obj = pltsymcache[i].obj->dyn.null;
+ pltsymcache_cnt++;
+ if (libmap[ref_obj] != -1)
+ continue;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ nametablen += strlen(pltsymcache[i].obj->load_name) + 1;
+ numlibs++;
+ }
+ pltsymcachetab = xcalloc(pltsymcache_cnt , sizeof(struct symcachetab));
+
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ pltsymcachetab[pltsymcache_cnt].idx = i;
+ pltsymcachetab[pltsymcache_cnt].obj_idx =
+ libmap[pltsymcache[i].obj->dyn.null];
+ pltsymcachetab[pltsymcache_cnt].sym_idx =
+ pltsymcache[i].sym - pltsymcache[i].obj->dyn.symtab;
+ pltsymcache_cnt++;
+ }
+
+ objarray[object->dyn.null].numlibs = numlibs;
+
+ nameidx = xcalloc(numlibs, sizeof (struct nameidx));
+ nametab = xmalloc(nametablen);
+
+ nametablen = 0;
+ for (i = 0; i < numlibs; i++) {
+ nameidx[i].name = nametablen;
+ nameidx[i].id0 = objarray[idxtolib[i]].id0;
+ nameidx[i].id1 = objarray[idxtolib[i]].id1;
+ nametablen += strlen(objarray[idxtolib[i]].obj->load_name) + 1;
+ strlcpy(&nametab[nameidx[i].name],
+ objarray[idxtolib[i]].obj->load_name,
+ nametablen - nameidx[i].name);
+ }
+
+ /* skip writing lib if using old prebind data */
+ if (objarray[object->dyn.null].oprebind_data == NULL)
+ ret = elf_write_lib(object, nameidx, nametab, nametablen,
+ numlibs, 0, NULL, NULL, NULL, NULL, symcachetab,
+ symcache_cnt, pltsymcachetab, pltsymcache_cnt);
+
+ free(nameidx);
+ free(nametab);
+ free(libmap);
+ free(pltsymcachetab);
+ free(symcachetab);
+
+ return ret;
+}
+
+int
+elf_prep_bin_prebind(struct proglist *pl)
+{
+ int ret;
+ int numlibs = 0;
+ int i, j;
+ int ref_obj;
+ int *libmap;
+ int *idxtolib;
+ struct nameidx *nameidx;
+ char *nametab;
+ int nametablen;
+ struct symcache_noflag *symcache;
+ struct symcache_noflag *pltsymcache;
+ struct symcachetab *symcachetab;
+ int symcache_cnt;
+ struct symcachetab *pltsymcachetab;
+ int pltsymcache_cnt;
+ struct elf_object *object;
+ struct objlist *ol;
+
+ object = TAILQ_FIRST(&(pl->curbin_list))->object;
+ symcache = objarray[object->dyn.null].symcache;
+ pltsymcache = objarray[object->dyn.null].pltsymcache;
+ libmap = xcalloc(objarray_cnt, sizeof (int));
+ idxtolib = xcalloc(pl->nobj, sizeof (int));
+
+ for (i = 0; i < objarray_cnt; i++)
+ libmap[i] = -1;
+
+ for (i = 0; i < pl->nobj; i++)
+ idxtolib[i] = -1;
+
+ nametablen = 0;
+ TAILQ_FOREACH(ol, &(pl->curbin_list), list) {
+ ref_obj = ol->object->dyn.null;
+ nametablen += strlen(ol->object->load_name) + 1;
+ libmap[ref_obj] = numlibs;
+ idxtolib[numlibs] = ref_obj;
+ numlibs++;
+ }
+
+ /* do got */
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym != NULL)
+ symcache_cnt++;
+ }
+
+ symcachetab = xcalloc(symcache_cnt , sizeof(struct symcachetab));
+
+ symcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (symcache[i].sym == NULL)
+ continue;
+ symcachetab[symcache_cnt].idx = i;
+ symcachetab[symcache_cnt].obj_idx =
+ libmap[symcache[i].obj->dyn.null];
+ symcachetab[symcache_cnt].sym_idx =
+ symcache[i].sym - symcache[i].obj->dyn.symtab;
+ symcache_cnt++;
+ }
+
+ /* now do plt */
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym != NULL)
+ pltsymcache_cnt++;
+ }
+ pltsymcachetab = xcalloc(pltsymcache_cnt , sizeof(struct symcachetab));
+
+ pltsymcache_cnt = 0;
+ for (i = 0; i < object->nchains; i++) {
+ if (pltsymcache[i].sym == NULL)
+ continue;
+ pltsymcachetab[pltsymcache_cnt].idx = i;
+ pltsymcachetab[pltsymcache_cnt].obj_idx =
+ libmap[pltsymcache[i].obj->dyn.null];
+ pltsymcachetab[pltsymcache_cnt].sym_idx =
+ pltsymcache[i].sym - pltsymcache[i].obj->dyn.symtab;
+ pltsymcache_cnt++;
+ }
+
+ objarray[object->dyn.null].numlibs = numlibs;
+
+ nameidx = xcalloc(numlibs, sizeof (struct nameidx));
+ nametab = xmalloc(nametablen);
+
+ nametablen = 0;
+ for (i = 0; i < numlibs; i++) {
+ nameidx[i].name = nametablen;
+ nameidx[i].id0 = objarray[idxtolib[i]].id0;
+ nameidx[i].id1 = objarray[idxtolib[i]].id1;
+ nametablen += strlen(objarray[idxtolib[i]].obj->load_name) + 1;
+
+ strlcpy(&nametab[nameidx[i].name],
+ objarray[idxtolib[i]].obj->load_name,
+ nametablen - nameidx[i].name);
+ }
+ pl->libmapcnt = xcalloc(numlibs, sizeof(u_int32_t));
+
+ /* have to do both got and plt fixups */
+ for (i = 0; i < numlibs; i++) {
+ for (j = 0; j < pl->fixupcnt[2*i]; j++) {
+ pl->fixup[2*i][j].obj_idx =
+ libmap[pl->fixup[2*i][j].obj_idx];
+ }
+ for (j = 0; j < pl->fixupcnt[2*i+1]; j++) {
+ pl->fixup[2*i+1][j].obj_idx =
+ libmap[pl->fixup[2*i+1][j].obj_idx];
+ }
+
+ pl->libmapcnt[i] = objarray[idxtolib[i]].numlibs;
+ pl->libmap[i] = xcalloc(objarray[idxtolib[i]].numlibs,
+ sizeof(u_int32_t));
+ if (i != 0) {
+ for (j = 0; j < objarray[idxtolib[i]].numlibs; j++) {
+ pl->libmap[i][j] =
+ libmap[objarray[idxtolib[i]].idxtolib[j]];
+ }
+ }
+ }
+
+ ret = elf_write_lib(object, nameidx, nametab, nametablen, numlibs,
+ numlibs, pl->fixup, pl->fixupcnt,
+ pl->libmap, pl->libmapcnt,
+ symcachetab, symcache_cnt,
+ pltsymcachetab, pltsymcache_cnt);
+
+ free(symcachetab);
+ free(pltsymcachetab);
+ free(idxtolib);
+ free(nameidx);
+ free(nametab);
+ free(libmap);
+
+ return ret;
+}
+
+
+int
+elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
+ char *nametab, int nametablen, int numlibs,
+ int nfixup, struct fixup **fixup, int *fixupcnt,
+ u_int32_t **libmap, int *libmapcnt,
+ struct symcachetab *symcachetab, int symcache_cnt,
+ struct symcachetab *pltsymcachetab, int pltsymcache_cnt)
+{
+ struct prebind_footer footer;
+ struct prebind_info info;
+ u_int32_t footer_offset, *maptab = NULL;
+ u_int32_t next_start, *fixuptab = NULL;
+ struct stat ifstat;
+ off_t base_offset;
+ size_t len;
+ int fd = -1, i;
+ int readonly = 0;
+
+ /* open the file, if in safe mode, only open it readonly */
+ if (safe == 0)
+ fd = open(object->load_name, O_RDWR);
+ if (fd == -1) {
+ if (safe != 0 || errno == ETXTBSY)
+ fd = open(object->load_name, O_RDONLY);
+ if (fd == -1) {
+ perror(object->load_name);
+ return 1;
+ }
+ readonly = 1;
+ }
+ lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
+ len = read(fd, &footer, sizeof(struct prebind_footer));
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror(object->load_name);
+ exit(10);
+ }
+
+ if (footer.bind_id[0] == BIND_ID0 &&
+ footer.bind_id[1] == BIND_ID1 &&
+ footer.bind_id[2] == BIND_ID2 &&
+ footer.bind_id[3] == BIND_ID3 &&
+ readonly == 0) {
+
+ ftruncate(fd, footer.orig_size);
+ elf_clear_prog_load(fd, object);
+
+ base_offset = footer.orig_size;
+ } else {
+ base_offset = ifstat.st_size;
+ }
+
+ bzero(&footer, sizeof(struct prebind_footer));
+
+
+ /* verify dev/inode - do we care about last modified? */
+
+ /* pieces to store on lib
+ *
+ * offset to footer
+ * nameidx - numlibs * sizeof nameidx
+ * symcache - symcache_cnt * sizeof (symcache_idx)
+ * pltsymcache - pltsymcache_cnt * sizeof (symcache_idx)
+ * fixup(N/A for lib) - nfixup * sizeof (symcache_idx)
+ * nametab - nametablen
+ * footer (not aligned)
+ */
+
+ footer.orig_size = base_offset;
+ base_offset = ELF_ROUND(base_offset, sizeof(u_int64_t));
+ footer.prebind_base = base_offset;
+ footer.nameidx_idx = sizeof(u_int32_t);
+ footer.symcache_idx = footer.nameidx_idx +
+ numlibs * sizeof (struct nameidx);
+ footer.pltsymcache_idx = footer.symcache_idx +
+ symcache_cnt * sizeof (struct nameidx);
+ footer.symcache_cnt = symcache_cnt;
+ footer.pltsymcache_cnt = pltsymcache_cnt;
+ footer.fixup_cnt = 0;
+ footer.numlibs = numlibs;
+ next_start = footer.pltsymcache_idx +
+ (pltsymcache_cnt * sizeof (struct symcachetab));
+ if (nfixup != 0) {
+ footer.fixup_cnt = nfixup;
+ footer.fixup_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ footer.fixupcnt_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ fixuptab = xcalloc(2*nfixup, sizeof(u_int32_t));
+ for (i = 0; i < 2*nfixup; i++) {
+ fixuptab[i] = next_start;
+ next_start += fixupcnt[i] * sizeof(struct fixup);
+ }
+ footer.libmap_idx = next_start;
+ next_start += 2*nfixup * sizeof(u_int32_t);
+ maptab = xcalloc(2*nfixup, sizeof(u_int32_t));
+ maptab[0] = next_start;
+ for (i = 1; i < nfixup; i++) {
+ maptab[i] = next_start;
+ next_start += libmapcnt[i] * sizeof(u_int32_t);
+ }
+ }
+
+ footer.nametab_idx = next_start;
+ next_start += nametablen;
+ next_start = ELF_ROUND(next_start, sizeof(u_int64_t));
+ footer_offset = next_start;
+ if (verbose > 1) {
+ printf("footer_offset %d\n", footer_offset);
+ }
+ footer.prebind_size = next_start + sizeof(struct prebind_footer);
+
+ footer.prebind_version = PREBIND_VERSION;
+ footer.id0 = objarray[object->dyn.null].id0;
+ footer.id1 = objarray[object->dyn.null].id1;
+ footer.bind_id[0] = BIND_ID0;
+ footer.bind_id[1] = BIND_ID1;
+ footer.bind_id[2] = BIND_ID2;
+ footer.bind_id[3] = BIND_ID3;
+
+
+ info.object = object;
+ info.footer = &footer;
+ info.footer_offset = footer_offset;
+ info.nameidx = nameidx;
+ info.symcache = symcachetab;
+
+
+ info.pltsymcache = pltsymcachetab;
+ info.nfixup = nfixup;
+ if (nfixup != 0) {
+ info.fixuptab = fixuptab;
+ info.fixupcnt = fixupcnt;
+ info.fixup = fixup;
+ info.maptab = maptab;
+ info.libmap = libmap;
+ info.libmapcnt = libmapcnt;
+ }
+
+ info.nametab = nametab;
+ info.nametablen = nametablen;
+
+
+ if (readonly) {
+ prebind_writenewfile(fd, object->load_name, &ifstat,
+ (off_t)footer.orig_size, &info);
+ } else {
+ prebind_writefile(fd, &info);
+ }
+
+ if (fstat(fd, &ifstat) == -1) {
+ perror(object->load_name);
+ exit(10);
+ }
+ if (nfixup != 0) {
+ free(fixuptab);
+ free(maptab);
+ }
+
+ if (verbose > 0)
+ printf("%s: prebind info %d bytes old size %lld, growth %f\n",
+ object->load_name, footer.prebind_size, footer.orig_size,
+ (double)(footer.prebind_size) / footer.orig_size);
+
+ if (verbose > 1)
+ elf_dump_footer(&footer);
+
+ close(fd);
+ return 0;
+}
+
+int
+prebind_writefile(int fd, struct prebind_info *info)
+{
+ int i;
+
+ struct prebind_footer *footer = info->footer;
+
+ lseek(fd, footer->prebind_base, SEEK_SET);
+ write(fd, &info->footer_offset, sizeof(u_int32_t));
+
+ lseek(fd, footer->prebind_base+footer->nameidx_idx, SEEK_SET);
+ write(fd, info->nameidx, footer->numlibs * sizeof (struct nameidx));
+
+ lseek(fd, footer->prebind_base+footer->symcache_idx, SEEK_SET);
+ write(fd, info->symcache, footer->symcache_cnt *
+ sizeof (struct symcachetab));
+
+ lseek(fd, footer->prebind_base+footer->pltsymcache_idx, SEEK_SET);
+ write(fd, info->pltsymcache, footer->pltsymcache_cnt *
+ sizeof (struct symcachetab));
+
+ if (info->nfixup != 0) {
+ lseek(fd, footer->prebind_base+footer->fixup_idx, SEEK_SET);
+ write(fd, info->fixuptab, 2*info->nfixup * sizeof(u_int32_t));
+ lseek(fd, footer->prebind_base+footer->fixupcnt_idx, SEEK_SET);
+ write(fd, info->fixupcnt, 2*info->nfixup * sizeof(u_int32_t));
+ for (i = 0; i < 2*info->nfixup; i++) {
+ lseek(fd, footer->prebind_base+info->fixuptab[i],
+ SEEK_SET);
+ write(fd, info->fixup[i], info->fixupcnt[i] *
+ sizeof(struct fixup));
+ }
+
+ lseek(fd, footer->prebind_base+footer->libmap_idx, SEEK_SET);
+ write(fd, info->maptab, info->nfixup * sizeof(u_int32_t));
+ for (i = 0; i < info->nfixup; i++) {
+ lseek(fd, footer->prebind_base+info->maptab[i],
+ SEEK_SET);
+ write(fd, info->libmap[i], info->libmapcnt[i] *
+ sizeof(u_int32_t));
+ }
+ }
+ lseek(fd, footer->prebind_base+footer->nametab_idx, SEEK_SET);
+ write(fd, info->nametab, info->nametablen);
+
+ lseek(fd, footer->prebind_base+info->footer_offset, SEEK_SET);
+ write(fd, footer, sizeof (struct prebind_footer));
+
+ if (info->object->obj_type == OBJTYPE_EXE)
+ elf_fixup_prog_load(fd, info->footer, info->object);
+
+ return 0;
+}
+
+int
+prebind_writenewfile(int infd, char *name, struct stat *st, off_t orig_size,
+ struct prebind_info *info)
+{
+ struct timeval tv[2];
+ char *newname, *buf;
+ ssize_t len, wlen;
+ int outfd;
+
+ if (asprintf(&newname, "%s.XXXXXXXXXX", name) == -1) {
+ if (verbose)
+ warn("asprintf");
+ return (-1);
+ }
+ outfd = open(newname, O_CREAT|O_RDWR|O_TRUNC, 0600);
+ if (outfd == -1) {
+ warn("%s", newname);
+ free(newname);
+ return (-1);
+ }
+
+ buf = malloc(BUFSZ);
+ if (buf == NULL) {
+ if (verbose)
+ warn("malloc");
+ goto fail;
+ }
+
+ /* copy old file to new file */
+ lseek(infd, (off_t)0, SEEK_SET);
+ while (1) {
+ len = read(infd, buf, BUFSIZ);
+ if (len == -1) {
+ if (verbose)
+ warn("read");
+ free(buf);
+ goto fail;
+ }
+ if (len == 0)
+ break;
+ wlen = write(outfd, buf, len);
+ if (wlen != len) {
+ free(buf);
+ goto fail;
+ }
+ }
+ free(buf);
+
+ /* now back track, and delete the header */
+ if (prebind_remove_load_section(outfd, newname) == -1)
+ goto fail;
+ if (orig_size != (off_t)-1 &&
+ ftruncate(outfd, orig_size) == -1)
+ goto fail;
+
+ prebind_writefile(outfd, info);
+
+ /* move new file into place */
+ TIMESPEC_TO_TIMEVAL(&tv[0], &st->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
+ if (futimes(outfd, tv) == -1)
+ goto fail;
+ if (fchown(outfd, st->st_uid, st->st_gid) == -1)
+ goto fail;
+ if (fchmod(outfd, st->st_mode) == -1)
+ goto fail;
+ if (fchflags(outfd, st->st_flags) == -1)
+ goto fail;
+ if (fstat(outfd, st) == -1) {
+ /* XXX */
+ goto fail;
+ }
+ if (rename(newname, name) == -1)
+ goto fail;
+
+ close (outfd);
+ return (0);
+
+fail:
+ free(newname);
+ unlink(newname);
+ close(outfd);
+ return (-1);
+}
+
+void
+elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object)
+{
+ void *buf;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Phdr phdr_empty;
+ int loadsection;
+
+ buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap for write\n", object->load_name);
+ return;
+ }
+
+ ehdr = (Elf_Ehdr *) buf;
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+
+ for (loadsection = 0; loadsection < ehdr->e_phnum; loadsection++) {
+ if (phdr[loadsection].p_type == PT_LOAD)
+ break;
+ }
+
+ /* verify that extra slot is empty */
+ bzero(&phdr_empty, sizeof(phdr_empty));
+ if (bcmp(&phdr[ehdr->e_phnum], &phdr_empty, sizeof(phdr_empty)) != 0) {
+ printf("extra slot not empty\n");
+ goto done;
+ }
+ phdr[ehdr->e_phnum].p_type = PT_LOAD;
+ phdr[ehdr->e_phnum].p_flags = PF_R | 0x08000000;
+ phdr[ehdr->e_phnum].p_offset = footer->prebind_base;
+ phdr[ehdr->e_phnum].p_vaddr = footer->prebind_base | 0x80000000;
+ phdr[ehdr->e_phnum].p_paddr = footer->prebind_base | 0x40000000;
+ phdr[ehdr->e_phnum].p_filesz = footer->prebind_size;
+ phdr[ehdr->e_phnum].p_memsz = footer->prebind_size;
+ phdr[ehdr->e_phnum].p_align = phdr[loadsection].p_align;
+ ehdr->e_phnum++;
+
+done:
+ msync(buf, 8192, MS_SYNC);
+ munmap(buf, 8192);
+}
+
+void
+elf_clear_prog_load(int fd, struct elf_object *object)
+{
+ void *buf;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ Elf_Phdr phdr_empty;
+ int loadsection;
+
+ buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ printf("%s: cannot mmap for write\n", object->load_name);
+ return;
+ }
+
+ ehdr = (Elf_Ehdr *) buf;
+ phdr = (Elf_Phdr *)((char *)buf + ehdr->e_phoff);
+
+ if (ehdr->e_type != ET_EXEC) {
+ goto done;
+ }
+
+ loadsection = ehdr->e_phnum - 1;
+ if ((phdr[loadsection].p_type != PT_LOAD) ||
+ ((phdr[loadsection].p_flags & 0x08000000) == 0)) {
+ /* doesn't look like ours */
+ printf("mapped, %s id doesn't match %lx %d %d\n",
+ object->load_name,
+ (long)(phdr[loadsection].p_vaddr),
+ phdr[loadsection].p_flags, loadsection);
+ goto done;
+ }
+
+ /* verify that extra slot is empty */
+ bzero(&phdr[loadsection], sizeof(phdr_empty));
+
+ ehdr->e_phnum--;
+
+done:
+ msync(buf, 8192, MS_SYNC);
+ munmap(buf, 8192);
+}
+
+void
+elf_add_object(struct elf_object *object, int objtype)
+{
+ struct objarray_list *newarray;
+ struct objlist *ol;
+ ol = xmalloc(sizeof (struct objlist));
+ ol->object = object;
+ if (objtype != OBJTYPE_EXE)
+ TAILQ_INSERT_TAIL(&library_list, ol, list);
+ if (objarray_cnt+1 >= objarray_sz) {
+ objarray_sz += 512;
+ newarray = realloc(objarray, sizeof (objarray[0]) *
+ objarray_sz);
+ if (newarray != NULL)
+ objarray = newarray;
+ else {
+ perror("objarray");
+ exit(20);
+ }
+ }
+ object->dyn.null = objarray_cnt; /* Major abuse, I know */
+ TAILQ_INIT(&(objarray[objarray_cnt].inst_list));
+ objarray[objarray_cnt].obj = object;
+ objarray[objarray_cnt].id0 = arc4random();
+ objarray[objarray_cnt].id1 = arc4random();
+
+ objarray[objarray_cnt].symcache = xcalloc(
+ sizeof(struct symcache_noflag), object->nchains);
+ objarray[objarray_cnt].pltsymcache = xcalloc(
+ sizeof(struct symcache_noflag), object->nchains);
+
+ objarray[objarray_cnt].oprebind_data = NULL;
+ objarray[objarray_cnt].proglist = NULL;
+ objarray[objarray_cnt].numlibs = 0;
+ objarray_cnt++;
+
+ elf_add_object_curbin_list(object);
+}
+
+void
+elf_free_curbin_list(elf_object_t *object)
+{
+ struct objlist *ol;
+ int i;
+
+ while (!TAILQ_EMPTY(&(curbin->curbin_list))) {
+ ol = TAILQ_FIRST(&(curbin->curbin_list));
+ TAILQ_REMOVE(&(objarray[ol->object->dyn.null].inst_list), ol, inst_list);
+ TAILQ_REMOVE(&(curbin->curbin_list), ol, list);
+ free(ol);
+ }
+
+ printf("trying to remove %s\n", object->load_name);
+ for (i = objarray_cnt; i != 0;) {
+ i--;
+ printf("obj %s\n", objarray[i].obj->load_name);
+ if (objarray[i].obj == object) {
+ printf("found obj at %d max obj %d\n", i, objarray_cnt);
+ TAILQ_FOREACH(ol, &(curbin->curbin_list), list) {
+ }
+ /* XXX - delete references */
+ objarray_cnt = i;
+ break;
+ }
+ }
+}
+
+void
+elf_print_objarray(void)
+{
+ int i;
+ struct objlist *ol;
+
+ printf("loaded objs # %d\n", objarray_cnt);
+ for (i = 0; i < objarray_cnt; i++) {
+ printf("%3d: %d obj %s\n", i, (int)objarray[i].obj->dyn.null,
+ objarray[i].obj->load_name);
+ TAILQ_FOREACH(ol, &(objarray[i].inst_list),
+ inst_list) {
+ printf("\tprog %s\n", ol->load_prog->load_name);
+ }
+ }
+}
+
+void
+elf_load_existing_prebind(struct elf_object *object, int fd)
+{
+ struct prebind_footer footer;
+ void *prebind_data;
+
+ lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
+ read(fd, &footer, sizeof(struct prebind_footer));
+
+ if (footer.bind_id[0] != BIND_ID0 ||
+ footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 ||
+ footer.bind_id[3] != BIND_ID3)
+ return;
+
+ prebind_data = mmap(0, footer.prebind_size, PROT_READ,
+ MAP_FILE, fd, footer.prebind_base);
+ objarray[object->dyn.null].oprebind_data = prebind_data;
+ objarray[object->dyn.null].id0 = footer.id0;
+ objarray[object->dyn.null].id1 = footer.id1;
+
+ copy_oldsymcache(object->dyn.null, prebind_data);
+}
+
+void
+copy_oldsymcache(int objidx, void *prebind_map)
+{
+ struct prebind_footer *footer;
+ struct elf_object *object;
+ struct elf_object *tobj;
+ struct symcache_noflag *tcache;
+ struct symcachetab *symcache;
+ int i, j, found, *idxtolib;
+ char *c, *nametab;
+ u_int32_t offset;
+ u_int32_t *poffset;
+ struct nameidx *nameidx;
+
+ object = objarray[objidx].obj;
+
+ poffset = (u_int32_t *)prebind_map;
+ c = prebind_map;
+ offset = *poffset;
+ c += offset;
+ footer = (void *)c;
+
+ nameidx = prebind_map + footer->nameidx_idx;
+ nametab = prebind_map + footer->nametab_idx;
+
+ idxtolib = xcalloc(footer->numlibs, sizeof(int));
+ found = 0;
+ for (i = 0; i < footer->numlibs; i++) {
+ found = 0;
+ for (j = 0; j < objarray_cnt; j++) {
+ if (objarray[j].id0 == nameidx[i].id0 &&
+ objarray[j].id1 == nameidx[i].id1) {
+ found = 1;
+ idxtolib[i] = j;
+ if (strcmp(objarray[j].obj->load_name,
+ &nametab[nameidx[i].name]) != 0) {
+ printf("warning filename mismatch"
+ " [%s] [%s]\n",
+ objarray[j].obj->load_name,
+ &nametab[nameidx[i].name]);
+ }
+ }
+ }
+ if (found == 0)
+ break;
+ }
+ if (found == 0)
+ goto done;
+
+ /* build idxtolibs */
+ tcache = objarray[objidx].symcache;
+ symcache = prebind_map + footer->symcache_idx;
+
+ for (i = 0; i < footer->symcache_cnt; i++) {
+ tobj = objarray[idxtolib[symcache[i].obj_idx]].obj;
+
+ tcache[symcache[i].idx].obj = tobj;
+ tcache[symcache[i].idx].sym = tobj->dyn.symtab +
+ symcache[i].sym_idx;
+ }
+
+ tcache = objarray[objidx].pltsymcache;
+ symcache = prebind_map + footer->pltsymcache_idx;
+ for (i = 0; i < footer->pltsymcache_cnt; i++) {
+ tobj = objarray[idxtolib[symcache[i].obj_idx]].obj;
+
+ tcache[symcache[i].idx].obj = tobj;
+ tcache[symcache[i].idx].sym = tobj->dyn.symtab +
+ symcache[i].sym_idx;
+ }
+done:
+ free(idxtolib);
+ /* munmap(prebind_map, size);*/
+}
--- /dev/null
+/* $OpenBSD: prebind.h,v 1.2 2006/06/26 23:26:12 drahn Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define PREBIND_VERSION 2
+struct prebind_footer {
+ off_t prebind_base;
+ u_int32_t nameidx_idx;
+ u_int32_t symcache_idx;
+ u_int32_t pltsymcache_idx;
+ u_int32_t fixup_idx;
+ u_int32_t nametab_idx;
+ u_int32_t fixupcnt_idx;
+ u_int32_t libmap_idx;
+
+ u_int32_t symcache_cnt;
+ u_int32_t pltsymcache_cnt;
+ u_int32_t fixup_cnt;
+ u_int32_t numlibs;
+ u_int32_t prebind_size;
+
+ u_int32_t id0;
+ u_int32_t id1;
+ /* do not modify or add fields below this point in the struct */
+ off_t orig_size;
+ u_int32_t prebind_version;
+ char bind_id[4];
+#define BIND_ID0 'P'
+#define BIND_ID1 'R'
+#define BIND_ID2 'E'
+#define BIND_ID3 'B'
+};
+
+
+struct nameidx {
+ u_int32_t name;
+ u_int32_t id0;
+ u_int32_t id1;
+};
+
+struct symcachetab {
+ u_int32_t idx;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
+
+struct fixup {
+ u_int32_t sym;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
+
+int prebind_delete(char **argv);
+int prebind(char **argv);
+int prebind_remove_load_section(int fd, char *name);
--- /dev/null
+/* $OpenBSD: prebind_delete.c,v 1.10 2010/03/30 17:42:50 zinovik Exp $ */
+
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/exec_elf.h>
+#include <elf_abi.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include "prebind.h"
+
+#define BUFSZ (256 * 1024)
+
+int strip_prebind(char *file);
+int prebind_remove_load_section(int fd, char *name);
+int prebind_newfile(int fd, char *name, struct stat *st, off_t orig_size);
+int strip_file_or_dir(char *name);
+int strip_dir(char *dir);
+
+extern int verbose;
+
+int
+prebind_delete(char **argv)
+{
+ while (*argv) {
+ if (strip_file_or_dir(*argv) == -1)
+ return (1);
+ argv++;
+ }
+ return (0);
+}
+
+int
+strip_file_or_dir(char *name)
+{
+ struct stat sb;
+ int ret = -1;
+
+ ret = lstat(name, &sb);
+ if (ret != 0)
+ return 0;
+ switch (sb.st_mode & S_IFMT) {
+ case S_IFREG:
+ ret = strip_prebind(name);
+ break;
+ case S_IFDIR:
+ if (verbose > 0)
+ printf("loading dir %s\n", name);
+ ret = strip_dir(name);
+ break;
+ default:
+ ; /* links and other files we skip */
+ ret = 0;
+ }
+ return ret;
+}
+
+int
+strip_dir(char *dir)
+{
+ struct dirent *dp;
+ struct stat sb;
+ DIR *dirp;
+ char *buf;
+ int ret;
+
+ dirp = opendir(dir);
+
+ /* if dir failes to open, skip */
+ if (dirp == NULL)
+ return 0;
+
+ ret = 0;
+ while ((dp = readdir(dirp)) != NULL && ret != -1) {
+ ret = -1;
+ switch (dp->d_type) {
+ case DT_UNKNOWN:
+ /*
+ * NFS will return unknown, since load_file
+ * does stat the file, this just
+ */
+ asprintf(&buf, "%s/%s", dir, dp->d_name);
+ lstat(buf, &sb);
+ if (sb.st_mode == S_IFREG)
+ ret = strip_prebind(buf);
+ free(buf);
+ break;
+ case DT_REG:
+ asprintf(&buf, "%s/%s", dir, dp->d_name);
+ ret = strip_prebind(buf);
+ free(buf);
+ break;
+ default:
+ /* other files symlinks, dirs, ... we ignore */
+ ret = 0;
+ ;
+ }
+ }
+ closedir(dirp);
+ return ret;
+}
+
+int
+strip_prebind(char *file)
+{
+ struct prebind_footer footer;
+ extern char *__progname;
+ int fd, rdonly = 0;
+ struct stat st;
+ ssize_t bytes;
+
+ fd = open(file, O_RDWR);
+ if (fd == -1 && errno == ETXTBSY) {
+ fd = open(file, O_RDONLY);
+ rdonly = 1;
+ }
+ if (fd == -1) {
+ warn("%s", file);
+ return (-1);
+ }
+
+ if (fstat(fd, &st) == -1)
+ return (-1);
+
+ lseek(fd, -((off_t)sizeof(struct prebind_footer)), SEEK_END);
+ bytes = read(fd, &footer, sizeof(struct prebind_footer));
+ if (bytes != sizeof(struct prebind_footer))
+ goto done;
+
+ if (footer.bind_id[0] != BIND_ID0 || footer.bind_id[1] != BIND_ID1 ||
+ footer.bind_id[2] != BIND_ID2 || footer.bind_id[3] != BIND_ID3) {
+ if (verbose)
+ fprintf(stderr, "%s: no prebind header\n", file);
+ goto done;
+ }
+
+ if (rdonly) {
+ fd = prebind_newfile(fd, file, &st, footer.orig_size);
+ } else {
+ prebind_remove_load_section(fd, file);
+ ftruncate(fd, footer.orig_size);
+ }
+
+ if (verbose)
+ fprintf(stderr, "%s: stripped %lld bytes from %s\n",
+ __progname, st.st_size - footer.orig_size, file);
+
+done:
+ if (fd != -1)
+ close(fd);
+ return (0);
+}
+
+int
+prebind_remove_load_section(int fd, char *name)
+{
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdr;
+ int loadsection;
+ char *buf;
+
+ buf = mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_FILE | MAP_SHARED,
+ fd, 0);
+ if (buf == MAP_FAILED) {
+ if (verbose)
+ warn("%s: cannot mmap for for write", name);
+ return (-1);
+ }
+
+ ehdr = (Elf_Ehdr *)buf;
+ phdr = (Elf_Phdr *)(buf + ehdr->e_phoff);
+ loadsection = ehdr->e_phnum - 1;
+
+ if (ehdr->e_type != ET_EXEC ||
+ (phdr[loadsection].p_flags & 0x08000000) == 0)
+ goto done;
+
+ if (phdr[loadsection].p_type != PT_LOAD ||
+ ((phdr[loadsection].p_flags & 0x08000000) == 0)) {
+ /* doesn't look like ours */
+ if (verbose)
+ fprintf(stderr, "mapped, %s id doesn't match %lx\n", name,
+ (long)(phdr[loadsection].p_vaddr));
+ goto done;
+ }
+
+ bzero(&phdr[loadsection], sizeof(Elf_Phdr));
+ ehdr->e_phnum--;
+done:
+ munmap(buf, 8192);
+ return (0);
+}
+
+int
+prebind_newfile(int infd, char *name, struct stat *st, off_t orig_size)
+{
+ struct timeval tv[2];
+ char *newname, *buf;
+ ssize_t len, wlen;
+ int outfd;
+
+ if (asprintf(&newname, "%s.XXXXXXXXXX", name) == -1) {
+ if (verbose)
+ warn("asprintf");
+ return (-1);
+ }
+ outfd = open(newname, O_CREAT|O_RDWR|O_TRUNC, 0600);
+ if (outfd == -1) {
+ warn("%s", newname);
+ free(newname);
+ return (-1);
+ }
+
+ buf = malloc(BUFSZ);
+ if (buf == NULL) {
+ if (verbose)
+ warn("malloc");
+ goto fail;
+ }
+
+ /* copy old file to new file */
+ lseek(infd, (off_t)0, SEEK_SET);
+ while (1) {
+ len = read(infd, buf, BUFSIZ);
+ if (len == -1) {
+ if (verbose)
+ warn("read");
+ free(buf);
+ goto fail;
+ }
+ if (len == 0)
+ break;
+ wlen = write(outfd, buf, len);
+ if (wlen != len) {
+ free(buf);
+ goto fail;
+ }
+ }
+ free(buf);
+ close (infd);
+
+ /* now back track, and delete the header */
+ if (prebind_remove_load_section(outfd, newname) == -1)
+ goto fail;
+ if (orig_size != (off_t)-1 &&
+ ftruncate(outfd, orig_size) == -1)
+ goto fail;
+
+ /* move new file into place */
+ TIMESPEC_TO_TIMEVAL(&tv[0], &st->st_atimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
+ if (futimes(outfd, tv) == -1)
+ goto fail;
+ if (fchown(outfd, st->st_uid, st->st_gid) == -1)
+ goto fail;
+ if (fchmod(outfd, st->st_mode) == -1)
+ goto fail;
+ if (fchflags(outfd, st->st_flags) == -1)
+ goto fail;
+ if (rename(newname, name) == -1)
+ goto fail;
+
+ return (outfd);
+
+fail:
+ free(newname);
+ unlink(newname);
+ close(outfd);
+ return (-1);
+}
--- /dev/null
+/* $OpenBSD: prebind_struct.h,v 1.3 2006/06/15 22:09:32 drahn Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+struct symcache_noflag {
+ const elf_object_t *obj;
+ const Elf_Sym *sym;
+};
+
+struct objlist {
+ TAILQ_ENTRY(objlist) list;
+ TAILQ_ENTRY(objlist) inst_list;
+ struct elf_object *load_prog;
+ struct elf_object *object;
+};
+
+struct proglist {
+ TAILQ_ENTRY(proglist) list;
+ TAILQ_HEAD(, objlist) curbin_list;
+ struct fixup **fixup;
+ int *fixupcnt;
+ int *fixupcntalloc;
+ int nobj;
+ u_int32_t **libmap;
+ u_int32_t *libmapcnt;
+ char *interp;
+};
+extern struct proglist *curbin;
+extern struct elf_object *load_object;
+
+typedef TAILQ_HEAD(, proglist) prog_list_ty;
+typedef TAILQ_HEAD(, objlist) obj_list_ty;
+
+extern obj_list_ty library_list;
+extern prog_list_ty prog_list;
+
+/* debug */
+void elf_print_curbin_list(struct proglist *bin);
+void elf_print_prog_list (prog_list_ty *prog_list);
+
+void elf_add_object_curbin_list(struct elf_object *object);
+
+void elf_copy_syms(struct symcache_noflag *tcache,
+ struct symcache_noflag *scache, struct elf_object *obj,
+ struct elf_object *prog, int nsyms);
+int elf_prep_lib_prebind(struct elf_object *object);
+int elf_prep_bin_prebind(struct proglist *pl);
+void elf_calc_fixups(struct proglist *pl, struct objlist *ol, int libidx);
+int elf_write_lib(struct elf_object *object, struct nameidx *nameidx,
+ char *nametab, int nametablen, int numlibs,
+ int nfixup, struct fixup **fixup, int *fixupcnt,
+ u_int32_t **libmap, int *libmapcnt,
+ struct symcachetab *symcachetab, int symcache_cnt,
+ struct symcachetab *pltsymcachetab, int pltsymcache_cnt);
+
+void dump_symcachetab(struct symcachetab *symcachetab, int symcache_cnt,
+ struct elf_object *object, int id);
+void dump_info(struct elf_object *object);
+void elf_clear_prog_load(int fd, struct elf_object *object);
+void elf_fixup_prog_load(int fd, struct prebind_footer *footer,
+ struct elf_object *object);
+void elf_dump_footer(struct prebind_footer *footer);
+
+extern int verbose;
+extern int merge;
+extern int safe;
+
+extern int64_t prebind_blocks;
+extern struct elf_object *load_object;
+struct elf_object *elf_lookup_object(const char *name);
+struct elf_object *load_file(const char *filename, int objtype);
+
+void elf_load_existing_prebind(struct elf_object *object, int fd);
--- /dev/null
+/* $OpenBSD: shlib.c,v 1.9 2006/05/13 16:33:40 deraadt Exp $ */
+/* $NetBSD: shlib.c,v 1.13 1998/04/04 01:00:29 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <ranlib.h>
+#include <a.out.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ld.h"
+
+/*
+ * Standard directories to search for files specified by -l.
+ */
+#ifndef STANDARD_SEARCH_DIRS
+#define STANDARD_SEARCH_DIRS "/usr/lib"
+#endif
+
+/*
+ * Actual vector of library search directories,
+ * including `-L'ed and LD_LIBRARY_PATH spec'd ones.
+ */
+char **search_dirs;
+int n_search_dirs;
+
+char *standard_search_dirs[] = {
+ STANDARD_SEARCH_DIRS
+};
+
+void
+add_search_dir(char *name)
+{
+ size_t len;
+ int i;
+
+ len = strlen(name);
+
+ while (len > 1 && name[len - 1] == '/')
+ --len;
+
+ for (i = 0; i < n_search_dirs; i++)
+ if (strlen(search_dirs[i]) == len &&
+ !strncmp(search_dirs[i], name, len))
+ return;
+ n_search_dirs++;
+ search_dirs = (char **)xrealloc(search_dirs,
+ n_search_dirs * sizeof search_dirs[0]);
+ search_dirs[n_search_dirs - 1] = xmalloc(++len);
+ (void)strlcpy(search_dirs[n_search_dirs - 1], name, len);
+}
+
+void
+remove_search_dir(char *name)
+{
+ size_t len;
+ int i;
+
+ len = strlen(name);
+
+ while (len > 1 && name[len - 1] == '/')
+ --len;
+
+ for (i = 0; i < n_search_dirs; i++) {
+ if (strlen(search_dirs[i]) != len ||
+ strncmp(search_dirs[i], name, len))
+ continue;
+ free(search_dirs[i]);
+ if (i < (n_search_dirs - 1))
+ bcopy(&search_dirs[i+1], &search_dirs[i],
+ (n_search_dirs - i - 1) * sizeof search_dirs[0]);
+ n_search_dirs--;
+ search_dirs = (char **)xrealloc(search_dirs,
+ n_search_dirs * sizeof search_dirs[0]);
+ break;
+ }
+}
+
+void
+add_search_path(char *path)
+{
+ char *cp, *dup;
+
+ if (path == NULL)
+ return;
+
+ /* Add search directories from `path' */
+ path = dup = strdup(path);
+ while ((cp = strsep(&path, ":")) != NULL)
+ add_search_dir(cp);
+ free(dup);
+}
+
+void
+std_search_path(void)
+{
+ int i, n;
+
+ /* Append standard search directories */
+ n = sizeof standard_search_dirs / sizeof standard_search_dirs[0];
+ for (i = 0; i < n; i++)
+ add_search_dir(standard_search_dirs[i]);
+}
+
+/*
+ * Return true if CP points to a valid dewey number.
+ * Decode and leave the result in the array DEWEY.
+ * Return the number of decoded entries in DEWEY.
+ */
+
+int
+getdewey(int dewey[], char *cp)
+{
+ int i, n;
+
+ for (n = 0, i = 0; i < MAXDEWEY; i++) {
+ if (*cp == '\0')
+ break;
+
+ if (*cp == '.') cp++;
+#ifdef SUNOS_LIB_COMPAT
+ if (!(isdigit)(*cp))
+#else
+ if (!isdigit(*cp))
+#endif
+ return 0;
+
+ dewey[n++] = strtol(cp, &cp, 10);
+ }
+ return n;
+}
+
+/*
+ * Compare two dewey arrays.
+ * Return -1 if `d1' represents a smaller value than `d2'.
+ * Return 1 if `d1' represents a greater value than `d2'.
+ * Return 0 if equal.
+ */
+int
+cmpndewey(int d1[], int n1, int d2[], int n2)
+{
+ int i;
+
+ for (i = 0; i < n1 && i < n2; i++) {
+ if (d1[i] < d2[i])
+ return -1;
+ if (d1[i] > d2[i])
+ return 1;
+ }
+ if (n1 == n2)
+ return 0;
+ if (i == n1)
+ return -1;
+ if (i == n2)
+ return 1;
+ errx(1, "cmpndewey: cant happen");
+ return 0;
+}
--- /dev/null
+/* $OpenBSD: sod.c,v 1.1 2006/05/12 23:20:53 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/syslimits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <link.h>
+#include <limits.h>
+#include <machine/exec.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if 0
+#include "syscall.h"
+#include "archdep.h"
+#include "util.h"
+#endif
+#include "sod.h"
+
+int _dl_hinthash(char *cp, int vmajor, int vminor);
+void _dl_maphints(void);
+
+/*
+ * Populate sod struct for dlopen's call to map_object
+ */
+void
+_dl_build_sod(const char *name, struct sod *sodp)
+{
+ unsigned int tuplet;
+ int major, minor;
+ char *realname, *tok, *etok, *cp;
+
+ /* default is an absolute or relative path */
+ sodp->sod_name = (long)strdup(name); /* strtok is destructive */
+ sodp->sod_library = 0;
+ sodp->sod_major = sodp->sod_minor = 0;
+
+ /* does it look like /^lib/ ? */
+ if (strncmp((char *)sodp->sod_name, "lib", 3) != 0)
+ goto backout;
+
+ /* is this a filename? */
+ if (strchr((char *)sodp->sod_name, '/'))
+ goto backout;
+
+ /* skip over 'lib' */
+ cp = (char *)sodp->sod_name + 3;
+
+ realname = cp;
+
+ /* dot guardian */
+ if ((strchr(cp, '.') == NULL) || (*(cp+strlen(cp)-1) == '.'))
+ goto backout;
+
+ cp = strstr(cp, ".so");
+ if (cp == NULL)
+ goto backout;
+
+ /* default */
+ major = minor = -1;
+
+ /* loop through name - parse skipping name */
+ for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
+ switch (tuplet) {
+ case 0:
+ /* empty tok, we already skipped to "\.so.*" */
+ break;
+ case 1:
+ /* 'so' extension */
+ break;
+ case 2:
+ /* major version extension */
+ major = strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ case 3:
+ /* minor version extension */
+ minor = strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ /* if we get here, it must be weird */
+ default:
+ goto backout;
+ }
+ }
+ if (realname == NULL)
+ goto backout;
+ cp = (char *)sodp->sod_name;
+ sodp->sod_name = (long)strdup(realname);
+ free(cp);
+ sodp->sod_library = 1;
+ sodp->sod_major = major;
+ sodp->sod_minor = minor;
+ return;
+
+backout:
+ free((char *)sodp->sod_name);
+ sodp->sod_name = (long)strdup(name);
+}
+
+static struct hints_header *hheader = NULL;
+static struct hints_bucket *hbuckets;
+static char *hstrtab;
+char *_dl_hint_search_path = NULL;
+
+#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
+
+void
+_dl_maphints(void)
+{
+ struct stat sb;
+ caddr_t addr = MAP_FAILED;
+ long hsize = 0;
+ int hfd;
+
+ if ((hfd = open(_PATH_LD_HINTS, O_RDONLY)) < 0)
+ goto bad_hints;
+
+ if (fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
+ sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
+ goto bad_hints;
+
+ hsize = (long)sb.st_size;
+ addr = (void *)mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
+ if (addr == MAP_FAILED)
+ goto bad_hints;
+
+ hheader = (struct hints_header *)addr;
+ if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
+ goto bad_hints;
+
+ if (hheader->hh_version != LD_HINTS_VERSION_1 &&
+ hheader->hh_version != LD_HINTS_VERSION_2)
+ goto bad_hints;
+
+ hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
+ hstrtab = (char *)(addr + hheader->hh_strtab);
+ if (hheader->hh_version >= LD_HINTS_VERSION_2)
+ _dl_hint_search_path = hstrtab + hheader->hh_dirlist;
+
+ /* close the file descriptor, leaving the hints mapped */
+ close(hfd);
+
+ return;
+
+bad_hints:
+ if (addr != MAP_FAILED)
+ munmap(addr, hsize);
+ if (hfd != -1)
+ close(hfd);
+ hheader = (struct hints_header *)-1;
+}
+
+char *
+_dl_findhint(char *name, int major, int minor, char *preferred_path)
+{
+ struct hints_bucket *bp;
+
+ /*
+ * If not mapped, and we have not tried before, try to map the
+ * hints, if previous attempts failed hheader is -1 and we
+ * do not wish to retry it.
+ */
+ if (hheader == NULL)
+ _dl_maphints();
+
+ /* if it failed to map, return failure */
+ if (!(HINTS_VALID))
+ return NULL;
+
+ bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
+
+ while (1) {
+ /* Sanity check */
+ if (bp->hi_namex >= hheader->hh_strtab_sz) {
+ printf("Bad name index: %#x\n", bp->hi_namex);
+ exit(7);
+ break;
+ }
+ if (bp->hi_pathx >= hheader->hh_strtab_sz) {
+ printf("Bad path index: %#x\n", bp->hi_pathx);
+ exit(7);
+ break;
+ }
+
+ if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
+ /* It's `name', check version numbers */
+ if (bp->hi_major == major &&
+ (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
+ if (preferred_path == NULL) {
+ return hstrtab + bp->hi_pathx;
+ } else {
+ char *path = hstrtab + bp->hi_pathx;
+ char *edir = strrchr(path, '/');
+
+ if ((strncmp(preferred_path, path,
+ (edir - path)) == 0) &&
+ (preferred_path[edir - path] == '\0'))
+ return path;
+ }
+ }
+ }
+
+ if (bp->hi_next == -1)
+ break;
+
+ /* Move on to next in bucket */
+ bp = &hbuckets[bp->hi_next];
+ }
+
+ /* No hints available for name */
+ return NULL;
+}
+
+int
+_dl_hinthash(char *cp, int vmajor, int vminor)
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+ if (hheader->hh_version == LD_HINTS_VERSION_1)
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+
+ return k;
+}
--- /dev/null
+/Makefile/1.3/Thu Nov 20 23:23:09 2003//
+/ldd.1/1.8/Mon Mar 2 09:27:34 2009//
+/ldd.c/1.14/Mon Mar 2 09:27:34 2009//
+D
--- /dev/null
+src/libexec/ld.so/ldd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.3 2003/11/20 23:23:09 avsm Exp $
+
+PROG= ldd
+SRCS= ldd.c
+MAN= ldd.1
+#CFLAGS+=-Werror
+
+BINDIR= /usr/bin
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: ldd.1,v 1.8 2009/03/02 09:27:34 sobrado Exp $
+.\"
+.\" Copyright (c) 1996 Per Fogelstrom
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\"
+.Dd $Mdocdate: March 2 2009 $
+.Dt LDD 1
+.Os
+.Sh NAME
+.Nm ldd
+.Nd list dynamic object dependencies
+.Sh SYNOPSIS
+.Nm ldd
+.Op Fl x
+.Ar program ...
+.Sh DESCRIPTION
+.Nm
+displays the shared objects needed to run
+.Ar program .
+.Nm
+uses the
+.Dv DT_NEEDED
+tags to determine what dynamic objects are required.
+To list the objects
+.Nm
+sets the environment variable
+.Ev LD_TRACE_LOADED_OBJECTS
+and then execs
+.Ar program .
+.Pp
+If
+.Nm
+is invoked with the
+.Fl x
+flag, the tags from
+.Ar program
+are listed without using current ldconfig configuration.
+.Sh DIAGNOSTICS
+Exit status 0 if no error.
+Exit status 1 if arg error.
+Exit status 2 if
+.Ar program
+can't be read.
+If
+.Nm
+fails to open the program file a message is printed.
+.Sh SEE ALSO
+.Xr ld.so 1 ,
+.Xr ldconfig 8
--- /dev/null
+/* $OpenBSD: ldd.c,v 1.14 2009/03/02 09:27:34 sobrado Exp $ */
+/*
+ * Copyright (c) 2001 Artur Grabowski <art@openbsd.org>
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <elf_abi.h>
+#include <err.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+
+int usage(void);
+int doit(char *);
+
+int
+main(int argc, char **argv)
+{
+ int c, xflag, ret;
+
+ xflag = 0;
+ while ((c = getopt(argc, argv, "x")) != -1) {
+ switch (c) {
+ case 'x':
+ xflag = 1;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ if (xflag)
+ errx(1, "-x not yet implemented");
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ if (setenv("LD_TRACE_LOADED_OBJECTS", "true", 1) < 0)
+ err(1, "setenv(LD_TRACE_LOADED_OBJECTS)");
+
+ ret = 0;
+ while (argc--) {
+ ret |= doit(*argv);
+ argv++;
+ }
+
+ return ret;
+}
+
+int
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-x] program ...\n", __progname);
+ exit(1);
+}
+
+
+int
+doit(char *name)
+{
+ Elf_Ehdr ehdr;
+ Elf_Phdr *phdr;
+ int fd, i, size, status, interp=0;
+ char buf[MAXPATHLEN];
+ void * dlhandle;
+
+ if ((fd = open(name, O_RDONLY)) < 0) {
+ warn("%s", name);
+ return 1;
+ }
+
+ if (read(fd, &ehdr, sizeof(ehdr)) < 0) {
+ warn("read(%s)", name);
+ close(fd);
+ return 1;
+ }
+
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) ||
+ ehdr.e_machine != ELF_TARG_MACH) {
+ warnx("%s: not an ELF executable", name);
+ close(fd);
+ return 1;
+ }
+
+ size = ehdr.e_phnum * sizeof(Elf_Phdr);
+ if ((phdr = malloc(size)) == NULL)
+ err(1, "malloc");
+
+ if (pread(fd, phdr, size, ehdr.e_phoff) != size) {
+ warn("read(%s)", name);
+ close(fd);
+ free(phdr);
+ return 1;
+ }
+
+ for (i = 0; i < ehdr.e_phnum; i++)
+ if (phdr[i].p_type == PT_INTERP) {
+ interp = 1;
+ break;
+ }
+
+ if (ehdr.e_type == ET_DYN && !interp) {
+ printf("%s:\n", name);
+ if (realpath(name, buf) == NULL) {
+ warn("realpath(%s)", name);
+ return 1;
+ }
+ dlhandle = dlopen(buf, RTLD_TRACE);
+ if (dlhandle == NULL) {
+ printf("%s\n", dlerror());
+ return 1;
+ }
+ close(fd);
+ free(phdr);
+ return 0;
+ }
+
+ close(fd);
+ free(phdr);
+
+ if (i == ehdr.e_phnum) {
+ warnx("%s: not a dynamic executable", name);
+ return 1;
+ }
+
+ printf("%s:\n", name);
+ fflush(stdout);
+ switch (fork()) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ execl(name, name, (char *)NULL);
+ perror(name);
+ _exit(1);
+ default:
+ if (wait(&status) < 0) {
+ warn("wait");
+ return 1;
+ }
+ if (WIFSIGNALED(status)) {
+ fprintf(stderr, "%s: signal %d\n", name,
+ WTERMSIG(status));
+ return 1;
+ }
+ if (WEXITSTATUS(status)) {
+ fprintf(stderr, "%s: exit status %d\n", name,
+ WEXITSTATUS(status));
+ return 1;
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/usr/obj/libexec/ld.so/ldd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: library.c,v 1.58 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2002 Dale Rahn
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include "dl_prebind.h"
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+#define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \
+ (((X) & PF_W) ? PROT_WRITE : 0) | \
+ (((X) & PF_X) ? PROT_EXEC : 0))
+
+void
+_dl_load_list_free(struct load_list *load_list)
+{
+ struct load_list *next;
+
+ while (load_list != NULL) {
+ next = load_list->next;
+ _dl_free(load_list);
+ load_list = next;
+ }
+}
+
+void
+_dl_unload_shlib(elf_object_t *object)
+{
+ struct dep_node *n;
+ DL_DEB(("unload_shlib called on %s\n", object->load_name));
+ if (OBJECT_REF_CNT(object) == 0 &&
+ (object->status & STAT_UNLOADED) == 0) {
+ object->status |= STAT_UNLOADED;
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_unload_shlib(n->data);
+ TAILQ_FOREACH(n, &object->grpref_list, next_sib)
+ _dl_unload_shlib(n->data);
+ DL_DEB(("unload_shlib unloading on %s\n", object->load_name));
+ _dl_load_list_free(object->load_list);
+ _dl_munmap((void *)object->load_base, object->load_size);
+ _dl_remove_object(object);
+ }
+}
+
+elf_object_t *
+_dl_tryload_shlib(const char *libname, int type, int flags)
+{
+ int libfile, i;
+ struct load_list *next_load, *load_list = NULL;
+ Elf_Addr maxva = 0, minva = ELFDEFNNAME(NO_ADDR);
+ Elf_Addr libaddr, loff, align = _dl_pagesz - 1;
+ elf_object_t *object;
+ char hbuf[4096];
+ Elf_Dyn *dynp = 0;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdp;
+ struct stat sb;
+ void *prebind_data;
+
+#define ROUND_PG(x) (((x) + align) & ~(align))
+#define TRUNC_PG(x) ((x) & ~(align))
+
+ object = _dl_lookup_object(libname);
+ if (object) {
+ object->obj_flags |= flags & RTLD_GLOBAL;
+ if (_dl_loading_object == NULL)
+ _dl_loading_object = object;
+ if (object->load_object != _dl_objects &&
+ object->load_object != _dl_loading_object) {
+ _dl_link_grpref(object->load_object, _dl_loading_object);
+ }
+ return(object); /* Already loaded */
+ }
+
+ libfile = _dl_open(libname, O_RDONLY);
+ if (libfile < 0) {
+ _dl_errno = DL_CANT_OPEN;
+ return(0);
+ }
+
+ if ( _dl_fstat(libfile, &sb) < 0) {
+ _dl_errno = DL_CANT_OPEN;
+ return(0);
+ }
+
+ for (object = _dl_objects; object != NULL; object = object->next) {
+ if (object->dev == sb.st_dev &&
+ object->inode == sb.st_ino) {
+ object->obj_flags |= flags & RTLD_GLOBAL;
+ _dl_close(libfile);
+ if (_dl_loading_object == NULL)
+ _dl_loading_object = object;
+ if (object->load_object != _dl_objects &&
+ object->load_object != _dl_loading_object) {
+ _dl_link_grpref(object->load_object,
+ _dl_loading_object);
+ }
+ return(object);
+ }
+ }
+
+ _dl_read(libfile, hbuf, sizeof(hbuf));
+ ehdr = (Elf_Ehdr *)hbuf;
+ if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 ||
+ ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 ||
+ ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) {
+ _dl_close(libfile);
+ _dl_errno = DL_NOT_ELF;
+ return(0);
+ }
+
+ /*
+ * Alright, we might have a winner!
+ * Figure out how much VM space we need.
+ */
+ phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
+ switch (phdp->p_type) {
+ case PT_LOAD:
+ if (phdp->p_vaddr < minva)
+ minva = phdp->p_vaddr;
+ if (phdp->p_vaddr + phdp->p_memsz > maxva)
+ maxva = phdp->p_vaddr + phdp->p_memsz;
+ break;
+ case PT_DYNAMIC:
+ dynp = (Elf_Dyn *)phdp->p_vaddr;
+ break;
+ default:
+ break;
+ }
+ }
+ minva = TRUNC_PG(minva);
+ maxva = ROUND_PG(maxva);
+
+ /*
+ * We map the entire area to see that we can get the VM
+ * space required. Map it unaccessible to start with.
+ *
+ * We must map the file we'll map later otherwise the VM
+ * system won't be able to align the mapping properly
+ * on VAC architectures.
+ */
+ libaddr = (Elf_Addr)_dl_mmap(0, maxva - minva, PROT_NONE,
+ MAP_PRIVATE|MAP_FILE, libfile, 0);
+ if (_dl_mmap_error(libaddr)) {
+ _dl_printf("%s: rtld mmap failed mapping %s.\n",
+ _dl_progname, libname);
+ _dl_close(libfile);
+ _dl_errno = DL_CANT_MMAP;
+ return(0);
+ }
+
+ loff = libaddr - minva;
+ phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
+
+ for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
+ if (phdp->p_type == PT_LOAD) {
+ char *start = (char *)(TRUNC_PG(phdp->p_vaddr)) + loff;
+ Elf_Addr off = (phdp->p_vaddr & align);
+ Elf_Addr size = off + phdp->p_filesz;
+ void *res;
+
+ res = _dl_mmap(start, ROUND_PG(size),
+ PFLAGS(phdp->p_flags),
+ MAP_FIXED|MAP_PRIVATE, libfile,
+ TRUNC_PG(phdp->p_offset));
+ next_load = _dl_malloc(sizeof(struct load_list));
+ next_load->next = load_list;
+ load_list = next_load;
+ next_load->start = start;
+ next_load->size = size;
+ next_load->prot = PFLAGS(phdp->p_flags);
+ if (_dl_mmap_error(res)) {
+ _dl_printf("%s: rtld mmap failed mapping %s.\n",
+ _dl_progname, libname);
+ _dl_close(libfile);
+ _dl_errno = DL_CANT_MMAP;
+ _dl_munmap((void *)libaddr, maxva - minva);
+ _dl_load_list_free(load_list);
+ return(0);
+ }
+ if (phdp->p_flags & PF_W) {
+ /* Zero out everything past the EOF */
+ if ((size & align) != 0)
+ _dl_memset(start + size, 0,
+ _dl_pagesz - (size & align));
+ if (ROUND_PG(size) ==
+ ROUND_PG(off + phdp->p_memsz))
+ continue;
+ start = start + ROUND_PG(size);
+ size = ROUND_PG(off + phdp->p_memsz) -
+ ROUND_PG(size);
+ res = _dl_mmap(start, size,
+ PFLAGS(phdp->p_flags),
+ MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (_dl_mmap_error(res)) {
+ _dl_printf("%s: rtld mmap failed mapping %s.\n",
+ _dl_progname, libname);
+ _dl_close(libfile);
+ _dl_errno = DL_CANT_MMAP;
+ _dl_munmap((void *)libaddr, maxva - minva);
+ _dl_load_list_free(load_list);
+ return(0);
+ }
+ }
+ }
+ }
+
+ prebind_data = prebind_load_fd(libfile, libname);
+
+ _dl_close(libfile);
+
+ dynp = (Elf_Dyn *)((unsigned long)dynp + loff);
+ object = _dl_finalize_object(libname, dynp,
+ (Elf_Phdr *)((char *)libaddr + ehdr->e_phoff), ehdr->e_phnum,type,
+ libaddr, loff);
+ if (object) {
+ object->prebind_data = prebind_data;
+ object->load_size = maxva - minva; /*XXX*/
+ object->load_list = load_list;
+ /* set inode, dev from stat info */
+ object->dev = sb.st_dev;
+ object->inode = sb.st_ino;
+ object->obj_flags |= flags;
+ } else {
+ /* XXX not possible. object cannot come back NULL */
+ _dl_munmap((void *)libaddr, maxva - minva);
+ _dl_load_list_free(load_list);
+ }
+ return(object);
+}
--- /dev/null
+/* $OpenBSD: library_mquery.c,v 1.36 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2002 Dale Rahn
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include "dl_prebind.h"
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+#define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \
+ (((X) & PF_W) ? PROT_WRITE : 0) | \
+ (((X) & PF_X) ? PROT_EXEC : 0))
+
+void
+_dl_load_list_free(struct load_list *load_list)
+{
+ struct load_list *next;
+ Elf_Addr align = _dl_pagesz - 1;
+
+ while (load_list != NULL) {
+ if (load_list->start != NULL)
+ _dl_munmap(load_list->start,
+ ((load_list->size) + align) & ~align);
+ next = load_list->next;
+ _dl_free(load_list);
+ load_list = next;
+ }
+}
+
+
+void
+_dl_unload_shlib(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ DL_DEB(("unload_shlib called on %s\n", object->load_name));
+ if (OBJECT_REF_CNT(object) == 0 &&
+ (object->status & STAT_UNLOADED) == 0) {
+ object->status |= STAT_UNLOADED;
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_unload_shlib(n->data);
+ TAILQ_FOREACH(n, &object->grpref_list, next_sib)
+ _dl_unload_shlib(n->data);
+ DL_DEB(("unload_shlib unloading on %s\n", object->load_name));
+ _dl_load_list_free(object->load_list);
+ _dl_remove_object(object);
+ }
+}
+
+
+elf_object_t *
+_dl_tryload_shlib(const char *libname, int type, int flags)
+{
+ int libfile, i;
+ struct load_list *ld, *lowld = NULL;
+ elf_object_t *object;
+ Elf_Dyn *dynp = 0;
+ Elf_Ehdr *ehdr;
+ Elf_Phdr *phdp;
+ Elf_Addr load_end = 0;
+ Elf_Addr align = _dl_pagesz - 1, off, size;
+ struct stat sb;
+ void *prebind_data;
+ char hbuf[4096];
+
+#define ROUND_PG(x) (((x) + align) & ~(align))
+#define TRUNC_PG(x) ((x) & ~(align))
+
+ object = _dl_lookup_object(libname);
+ if (object) {
+ object->obj_flags |= flags & RTLD_GLOBAL;
+ if (_dl_loading_object == NULL)
+ _dl_loading_object = object;
+ if (object->load_object != _dl_objects &&
+ object->load_object != _dl_loading_object) {
+ _dl_link_grpref(object->load_object, _dl_loading_object);
+ }
+ return(object); /* Already loaded */
+ }
+
+ libfile = _dl_open(libname, O_RDONLY);
+ if (libfile < 0) {
+ _dl_errno = DL_CANT_OPEN;
+ return(0);
+ }
+
+ if ( _dl_fstat(libfile, &sb) < 0) {
+ _dl_errno = DL_CANT_OPEN;
+ return(0);
+ }
+
+ for (object = _dl_objects; object != NULL; object = object->next) {
+ if (object->dev == sb.st_dev &&
+ object->inode == sb.st_ino) {
+ object->obj_flags |= flags & RTLD_GLOBAL;
+ _dl_close(libfile);
+ if (_dl_loading_object == NULL)
+ _dl_loading_object = object;
+ if (object->load_object != _dl_objects &&
+ object->load_object != _dl_loading_object) {
+ _dl_link_grpref(object->load_object,
+ _dl_loading_object);
+ }
+ return(object);
+ }
+ }
+
+ _dl_read(libfile, hbuf, sizeof(hbuf));
+ ehdr = (Elf_Ehdr *)hbuf;
+ if (ehdr->e_ident[0] != ELFMAG0 || ehdr->e_ident[1] != ELFMAG1 ||
+ ehdr->e_ident[2] != ELFMAG2 || ehdr->e_ident[3] != ELFMAG3 ||
+ ehdr->e_type != ET_DYN || ehdr->e_machine != MACHID) {
+ _dl_close(libfile);
+ _dl_errno = DL_NOT_ELF;
+ return(0);
+ }
+
+ /* Insertion sort */
+#define LDLIST_INSERT(ld) do { \
+ struct load_list **_ld; \
+ for (_ld = &lowld; *_ld != NULL; _ld = &(*_ld)->next) \
+ if ((*_ld)->moff > ld->moff) \
+ break; \
+ ld->next = *_ld; \
+ *_ld = ld; \
+} while (0)
+ /*
+ * Alright, we might have a winner!
+ * Figure out how much VM space we need and set up the load
+ * list that we'll use to find free VM space.
+ */
+ phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
+ for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
+ switch (phdp->p_type) {
+ case PT_LOAD:
+ off = (phdp->p_vaddr & align);
+ size = off + phdp->p_filesz;
+
+ ld = _dl_malloc(sizeof(struct load_list));
+ ld->start = NULL;
+ ld->size = size;
+ ld->moff = TRUNC_PG(phdp->p_vaddr);
+ ld->foff = TRUNC_PG(phdp->p_offset);
+ ld->prot = PFLAGS(phdp->p_flags);
+ LDLIST_INSERT(ld);
+
+ if ((ld->prot & PROT_WRITE) == 0 ||
+ ROUND_PG(size) == ROUND_PG(off + phdp->p_memsz))
+ break;
+ /* This phdr has a zfod section */
+ ld = _dl_malloc(sizeof(struct load_list));
+ ld->start = NULL;
+ ld->size = ROUND_PG(off + phdp->p_memsz) -
+ ROUND_PG(size);
+ ld->moff = TRUNC_PG(phdp->p_vaddr) +
+ ROUND_PG(size);
+ ld->foff = -1;
+ ld->prot = PFLAGS(phdp->p_flags);
+ LDLIST_INSERT(ld);
+ break;
+ case PT_DYNAMIC:
+ dynp = (Elf_Dyn *)phdp->p_vaddr;
+ break;
+ default:
+ break;
+ }
+ }
+
+#define LOFF ((Elf_Addr)lowld->start - lowld->moff)
+
+retry:
+ for (ld = lowld; ld != NULL; ld = ld->next) {
+ off_t foff;
+ int fd, flags;
+
+ /*
+ * We don't want to provide the fd/off hint for anything
+ * but the first mapping, all other might have
+ * cache-incoherent aliases and will cause this code to
+ * loop forever.
+ */
+ if (ld == lowld) {
+ fd = libfile;
+ foff = ld->foff;
+ flags = 0;
+ } else {
+ fd = -1;
+ foff = 0;
+ flags = MAP_FIXED;
+ }
+
+ ld->start = (void *)(LOFF + ld->moff);
+
+ /*
+ * Magic here.
+ * The first mquery is done with MAP_FIXED to see if
+ * the mapping we want is free. If it's not, we redo the
+ * mquery without MAP_FIXED to get the next free mapping,
+ * adjust the base mapping address to match this free mapping
+ * and restart the process again.
+ */
+ ld->start = _dl_mquery(ld->start, ROUND_PG(ld->size), ld->prot,
+ flags, fd, foff);
+ if (_dl_mmap_error(ld->start)) {
+ ld->start = (void *)(LOFF + ld->moff);
+ ld->start = _dl_mquery(ld->start, ROUND_PG(ld->size),
+ ld->prot, flags & ~MAP_FIXED, fd, foff);
+ if (_dl_mmap_error(ld->start))
+ goto fail;
+ }
+
+ if (ld->start != (void *)(LOFF + ld->moff)) {
+ lowld->start = ld->start - ld->moff + lowld->moff;
+ goto retry;
+ }
+ /*
+ * XXX - we need some kind of boundary condition here,
+ * or fix mquery to not run into the stack
+ */
+ }
+
+ for (ld = lowld; ld != NULL; ld = ld->next) {
+ int fd, flags;
+ off_t foff;
+ void *res;
+
+ if (ld->foff < 0) {
+ fd = -1;
+ foff = 0;
+ flags = MAP_FIXED|MAP_PRIVATE|MAP_ANON;
+ } else {
+ fd = libfile;
+ foff = ld->foff;
+ flags = MAP_FIXED|MAP_PRIVATE;
+ }
+ res = _dl_mmap(ld->start, ROUND_PG(ld->size), ld->prot, flags,
+ fd, foff);
+ if (_dl_mmap_error(res))
+ goto fail;
+ /* Zero out everything past the EOF */
+ if ((ld->prot & PROT_WRITE) != 0 && (ld->size & align) != 0)
+ _dl_memset((char *)ld->start + ld->size, 0,
+ _dl_pagesz - (ld->size & align));
+ load_end = (Elf_Addr)ld->start + ROUND_PG(ld->size);
+ }
+
+ prebind_data = prebind_load_fd(libfile, libname);
+
+ _dl_close(libfile);
+
+ dynp = (Elf_Dyn *)((unsigned long)dynp + LOFF);
+ object = _dl_finalize_object(libname, dynp,
+ (Elf_Phdr *)((char *)lowld->start + ehdr->e_phoff), ehdr->e_phnum,
+ type, (Elf_Addr)lowld->start, LOFF);
+ if (object) {
+ object->prebind_data = prebind_data;
+ object->load_size = (Elf_Addr)load_end - (Elf_Addr)lowld->start;
+ object->load_list = lowld;
+ /* set inode, dev from stat info */
+ object->dev = sb.st_dev;
+ object->inode = sb.st_ino;
+ object->obj_flags |= flags;
+
+ } else {
+ /* XXX no point. object is never returned NULL */
+ _dl_load_list_free(lowld);
+ }
+ return(object);
+fail:
+ _dl_printf("%s: rtld mmap failed mapping %s.\n",
+ _dl_progname, libname);
+ _dl_close(libfile);
+ _dl_errno = DL_CANT_MMAP;
+ _dl_load_list_free(lowld);
+ return(0);
+}
--- /dev/null
+/* $OpenBSD: library_subr.c,v 1.30 2010/05/09 09:53:28 matthieu Exp $ */
+
+/*
+ * Copyright (c) 2002 Dale Rahn
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/syslimits.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <dirent.h>
+#include <string.h>
+
+#include "archdep.h"
+#include "resolve.h"
+#include "dir.h"
+#include "sod.h"
+
+#define DEFAULT_PATH "/usr/lib"
+
+
+/* STATIC DATA */
+struct dlochld _dlopened_child_list;
+
+
+/*
+ * _dl_match_file()
+ *
+ * This fucntion determines if a given name matches what is specified
+ * in a struct sod. The major must match exactly, and the minor must
+ * be same or larger.
+ *
+ * sodp is updated with the minor if this matches.
+ */
+
+int
+_dl_match_file(struct sod *sodp, char *name, int namelen)
+{
+ int match;
+ struct sod lsod;
+ char *lname;
+
+ lname = name;
+ if (sodp->sod_library) {
+ if (_dl_strncmp(name, "lib", 3))
+ return 0;
+ lname += 3;
+ }
+ if (_dl_strncmp(lname, (char *)sodp->sod_name,
+ _dl_strlen((char *)sodp->sod_name)))
+ return 0;
+
+ _dl_build_sod(name, &lsod);
+
+ match = 0;
+ if ((_dl_strcmp((char *)lsod.sod_name, (char *)sodp->sod_name) == 0) &&
+ (lsod.sod_library == sodp->sod_library) &&
+ ((sodp->sod_major == -1) || (sodp->sod_major == lsod.sod_major)) &&
+ ((sodp->sod_minor == -1) ||
+ (lsod.sod_minor >= sodp->sod_minor))) {
+ match = 1;
+
+ /* return version matched */
+ sodp->sod_major = lsod.sod_major;
+ sodp->sod_minor = lsod.sod_minor;
+ }
+ _dl_free((char *)lsod.sod_name);
+ return match;
+}
+
+char _dl_hint_store[MAXPATHLEN];
+
+char *
+_dl_find_shlib(struct sod *sodp, const char *searchpath, int nohints)
+{
+ char *hint, lp[PATH_MAX + 10], *path;
+ struct dirent *dp;
+ const char *pp;
+ int match, len;
+ DIR *dd;
+ struct sod tsod, bsod; /* transient and best sod */
+
+ /* if we are to search default directories, and hints
+ * are not to be used, search the standard path from ldconfig
+ * (_dl_hint_search_path) or use the default path
+ */
+ if (nohints)
+ goto nohints;
+
+ if (searchpath == NULL) {
+ /* search 'standard' locations, find any match in the hints */
+ hint = _dl_findhint((char *)sodp->sod_name, sodp->sod_major,
+ sodp->sod_minor, NULL);
+ if (hint)
+ return hint;
+ } else {
+ /* search hints requesting matches for only
+ * the searchpath directories,
+ */
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX &&
+ *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ hint = _dl_findhint((char *)sodp->sod_name,
+ sodp->sod_major, sodp->sod_minor, lp);
+ if (hint != NULL)
+ return hint;
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ }
+
+ /*
+ * For each directory in the searchpath, read the directory
+ * entries looking for a match to sod. filename compare is
+ * done by _dl_match_file()
+ */
+nohints:
+ if (searchpath == NULL) {
+ if (_dl_hint_search_path != NULL)
+ searchpath = _dl_hint_search_path;
+ else
+ searchpath = DEFAULT_PATH;
+ }
+ _dl_memset(&bsod, 0, sizeof(bsod));
+ pp = searchpath;
+ while (pp) {
+ path = lp;
+ while (path < lp + PATH_MAX && *pp && *pp != ':' && *pp != ';')
+ *path++ = *pp++;
+ *path = 0;
+
+ /* interpret "" as curdir "." */
+ if (lp[0] == '\0') {
+ lp[0] = '.';
+ lp[1] = '\0';
+ }
+
+ if ((dd = _dl_opendir(lp)) != NULL) {
+ match = 0;
+ while ((dp = _dl_readdir(dd)) != NULL) {
+ tsod = *sodp;
+ if (_dl_match_file(&tsod, dp->d_name,
+ dp->d_namlen)) {
+ /*
+ * When a match is found, tsod is
+ * updated with the major+minor found.
+ * This version is compared with the
+ * largest so far (kept in bsod),
+ * and saved if larger.
+ */
+ if (!match ||
+ tsod.sod_major == -1 ||
+ tsod.sod_major > bsod.sod_major ||
+ ((tsod.sod_major ==
+ bsod.sod_major) &&
+ tsod.sod_minor > bsod.sod_minor)) {
+ bsod = tsod;
+ match = 1;
+ len = _dl_strlcpy(
+ _dl_hint_store, lp,
+ MAXPATHLEN);
+ if (lp[len-1] != '/') {
+ _dl_hint_store[len] =
+ '/';
+ len++;
+ }
+ _dl_strlcpy(
+ &_dl_hint_store[len],
+ dp->d_name,
+ MAXPATHLEN-len);
+ if (tsod.sod_major == -1)
+ break;
+ }
+ }
+ }
+ _dl_closedir(dd);
+ if (match) {
+ *sodp = bsod;
+ return (_dl_hint_store);
+ }
+ }
+
+ if (*pp) /* Try curdir if ':' at end */
+ pp++;
+ else
+ pp = 0;
+ }
+ return NULL;
+}
+
+/*
+ * Load a shared object. Search order is:
+ * If the name contains a '/' use only the path preceding the
+ * library name and do not continue on to other methods if not
+ * found.
+ * search hints for match in path preceding library name
+ * this will only match specific library version.
+ * search path preceding library name
+ * this will find largest minor version in path provided
+ * try the LD_LIBRARY_PATH specification (if present)
+ * search hints for match in LD_LIBRARY_PATH dirs
+ * this will only match specific libary version.
+ * search LD_LIBRARY_PATH dirs for match.
+ * this will find largest minor version in first dir found.
+ * check DT_RPATH paths, (if present)
+ * search hints for match in DT_RPATH dirs
+ * this will only match specific libary version.
+ * search DT_RPATH dirs for match.
+ * this will find largest minor version in first dir found.
+ * last look in default search directory, either as specified
+ * by ldconfig or default to '/usr/lib'
+ */
+
+
+elf_object_t *
+_dl_load_shlib(const char *libname, elf_object_t *parent, int type, int flags)
+{
+ int try_any_minor, ignore_hints;
+ struct sod sod, req_sod;
+ elf_object_t *object = NULL;
+ char *hint;
+
+ try_any_minor = 0;
+ ignore_hints = 0;
+
+ if (_dl_strchr(libname, '/')) {
+ char *lpath, *lname;
+ lpath = _dl_strdup(libname);
+ lname = _dl_strrchr(lpath, '/');
+ if (lname == NULL) {
+ _dl_free(lpath);
+ _dl_errno = DL_NOT_FOUND;
+ return (object);
+ }
+ *lname = '\0';
+ lname++;
+ if (*lname == '\0') {
+ _dl_free(lpath);
+ _dl_errno = DL_NOT_FOUND;
+ return (object);
+ }
+
+ _dl_build_sod(lname, &sod);
+ req_sod = sod;
+
+fullpathagain:
+ hint = _dl_find_shlib(&req_sod, lpath, ignore_hints);
+ if (hint != NULL)
+ goto fullpathdone;
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto fullpathagain;
+ }
+ _dl_errno = DL_NOT_FOUND;
+fullpathdone:
+ _dl_free(lpath);
+ goto done;
+ }
+
+ _dl_build_sod(libname, &sod);
+ req_sod = sod;
+
+again:
+ /* No '/' in name. Scan the known places, LD_LIBRARY_PATH first. */
+ if (_dl_libpath != NULL) {
+ hint = _dl_find_shlib(&req_sod, _dl_libpath, ignore_hints);
+ if (hint != NULL)
+ goto done;
+ }
+
+ /* Check DT_RPATH. */
+ if (parent->dyn.rpath != NULL) {
+ hint = _dl_find_shlib(&req_sod, parent->dyn.rpath, ignore_hints);
+ if (hint != NULL)
+ goto done;
+ }
+
+ /* Check main program's DT_RPATH, if parent != main program */
+ if (parent != _dl_objects && _dl_objects->dyn.rpath != NULL) {
+ hint = _dl_find_shlib(&req_sod, _dl_objects->dyn.rpath, ignore_hints);
+ if (hint != NULL)
+ goto done;
+ }
+
+ /* check 'standard' locations */
+ hint = _dl_find_shlib(&req_sod, NULL, ignore_hints);
+ if (hint != NULL)
+ goto done;
+
+ if (try_any_minor == 0) {
+ try_any_minor = 1;
+ ignore_hints = 1;
+ req_sod.sod_minor = -1;
+ goto again;
+ }
+ _dl_errno = DL_NOT_FOUND;
+done:
+ if (hint != NULL) {
+ if (req_sod.sod_minor < sod.sod_minor)
+ _dl_printf("warning: lib%s.so.%d.%d: "
+ "minor version >= %d expected, "
+ "using it anyway\n",
+ sod.sod_name, sod.sod_major,
+ req_sod.sod_minor, sod.sod_minor);
+ object = _dl_tryload_shlib(hint, type, flags);
+ }
+ _dl_free((char *)sod.sod_name);
+ return(object);
+}
+
+
+void
+_dl_link_dlopen(elf_object_t *dep)
+{
+ struct dep_node *n;
+
+ dep->opencount++;
+
+ if (OBJECT_DLREF_CNT(dep) > 1)
+ return;
+
+ n = _dl_malloc(sizeof *n);
+ if (n == NULL)
+ _dl_exit(5);
+
+ n->data = dep;
+ TAILQ_INSERT_TAIL(&_dlopened_child_list, n, next_sib);
+
+ DL_DEB(("linking %s as dlopen()ed\n", dep->load_name));
+}
+
+void
+_dl_child_refcnt_decrement(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ object->refcount--;
+ if (OBJECT_REF_CNT(object) == 0)
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_child_refcnt_decrement(n->data);
+}
+
+void
+_dl_notify_unload_shlib(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ if (OBJECT_REF_CNT(object) == 0)
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_child_refcnt_decrement(n->data);
+
+ if (OBJECT_DLREF_CNT(object) == 0) {
+ TAILQ_FOREACH(n, &object->grpref_list, next_sib) {
+ n->data->grprefcount--;
+ _dl_notify_unload_shlib(n->data);
+ }
+ }
+}
+
+void
+_dl_unload_dlopen(void)
+{
+ struct dep_node *node;
+
+ TAILQ_FOREACH_REVERSE(node, &_dlopened_child_list, dlochld, next_sib) {
+ /* dont dlclose the main program */
+ if (node->data == _dl_objects)
+ continue;
+
+ while (node->data->opencount > 0) {
+ node->data->opencount--;
+ _dl_notify_unload_shlib(node->data);
+ _dl_run_all_dtors();
+ }
+ }
+}
+
+void
+_dl_link_grpref(elf_object_t *load_group, elf_object_t *load_object)
+{
+ struct dep_node *n;
+
+ n = _dl_malloc(sizeof *n);
+ if (n == NULL)
+ _dl_exit(7);
+ n->data = load_group;
+ TAILQ_INSERT_TAIL(&load_object->grpref_list, n, next_sib);
+ load_group->grprefcount++;
+}
+
+void
+_dl_link_child(elf_object_t *dep, elf_object_t *p)
+{
+ struct dep_node *n;
+
+ n = _dl_malloc(sizeof *n);
+ if (n == NULL)
+ _dl_exit(7);
+ n->data = dep;
+ TAILQ_INSERT_TAIL(&p->child_list, n, next_sib);
+
+ dep->refcount++;
+
+ DL_DEB(("linking dep %s as child of %s\n", dep->load_name,
+ p->load_name));
+}
+
+void
+_dl_link_grpsym(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ TAILQ_FOREACH(n, &_dl_loading_object->grpsym_list, next_sib)
+ if (n->data == object)
+ return; /* found, dont bother adding */
+
+ n = _dl_malloc(sizeof *n);
+ if (n == NULL)
+ _dl_exit(8);
+ n->data = object;
+ TAILQ_INSERT_TAIL(&_dl_loading_object->grpsym_list, n, next_sib);
+}
+
+void
+_dl_cache_grpsym_list(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ /*
+ * grpsym_list is an ordered list of all child libs of the
+ * _dl_loading_object with no dups. The order is equalivant
+ * to a breath-first traversal of the child list without dups.
+ */
+
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_link_grpsym(n->data);
+
+ TAILQ_FOREACH(n, &object->child_list, next_sib)
+ _dl_cache_grpsym_list(n->data);
+}
--- /dev/null
+/* $OpenBSD: loader.c,v 1.118 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/exec.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <nlist.h>
+#include <string.h>
+#include <link.h>
+#include <dlfcn.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "sod.h"
+#include "stdlib.h"
+#include "dl_prebind.h"
+
+#include "../../lib/csu/common_elf/os-note-elf.h"
+
+/*
+ * Local decls.
+ */
+static char *_dl_getenv(const char *, char **);
+static void _dl_unsetenv(const char *, char **);
+unsigned long _dl_boot(const char **, char **, const long, long *);
+void _dl_debug_state(void);
+void _dl_setup_env(char **);
+void _dl_dtors(void);
+void _dl_boot_bind(const long, long *, Elf_Dyn *);
+void _dl_fixup_user_env(void);
+
+const char *_dl_progname;
+int _dl_pagesz;
+
+char *_dl_libpath;
+char *_dl_preload;
+char *_dl_bindnow;
+char *_dl_traceld;
+char *_dl_debug;
+char *_dl_showmap;
+char *_dl_norandom;
+char *_dl_noprebind;
+char *_dl_prebind_validate;
+char *_dl_tracefmt1, *_dl_tracefmt2, *_dl_traceprog;
+
+struct r_debug *_dl_debug_map;
+
+void _dl_dopreload(char *paths);
+
+void
+_dl_debug_state(void)
+{
+ /* Debugger stub */
+}
+
+/*
+ * Run dtors for all objects that are eligible.
+ */
+
+void
+_dl_run_all_dtors()
+{
+ elf_object_t *node;
+ int fini_complete;
+ struct dep_node *dnode;
+
+ fini_complete = 0;
+
+ while (fini_complete == 0) {
+ fini_complete = 1;
+ for (node = _dl_objects->next;
+ node != NULL;
+ node = node->next) {
+ if ((node->dyn.fini) &&
+ (OBJECT_REF_CNT(node) == 0) &&
+ (node->status & STAT_INIT_DONE) &&
+ ((node->status & STAT_FINI_DONE) == 0)) {
+ node->status |= STAT_FINI_READY;
+ }
+ }
+ for (node = _dl_objects->next;
+ node != NULL;
+ node = node->next ) {
+ if ((node->dyn.fini) &&
+ (OBJECT_REF_CNT(node) == 0) &&
+ (node->status & STAT_INIT_DONE) &&
+ ((node->status & STAT_FINI_DONE) == 0))
+ TAILQ_FOREACH(dnode, &node->child_list,
+ next_sib)
+ dnode->data->status &= ~STAT_FINI_READY;
+ }
+
+
+ for (node = _dl_objects->next;
+ node != NULL;
+ node = node->next ) {
+ if (node->status & STAT_FINI_READY) {
+ DL_DEB(("doing dtors obj %p @%p: [%s]\n",
+ node, node->dyn.fini,
+ node->load_name));
+
+ fini_complete = 0;
+ node->status |= STAT_FINI_DONE;
+ node->status &= ~STAT_FINI_READY;
+ (*node->dyn.fini)();
+ }
+ }
+ }
+}
+
+/*
+ * Routine to walk through all of the objects except the first
+ * (main executable).
+ *
+ * Big question, should dlopen()ed objects be unloaded before or after
+ * the destructor for the main application runs?
+ */
+void
+_dl_dtors(void)
+{
+ _dl_thread_kern_stop();
+
+ /* ORDER? */
+ _dl_unload_dlopen();
+
+ DL_DEB(("doing dtors\n"));
+
+ /* main program runs its dtors itself
+ * but we want to run dtors on all it's children);
+ */
+ _dl_objects->status |= STAT_FINI_DONE;
+
+ _dl_objects->opencount--;
+ _dl_notify_unload_shlib(_dl_objects);
+
+ _dl_run_all_dtors();
+}
+
+void
+_dl_dopreload(char *paths)
+{
+ char *cp, *dp;
+ elf_object_t *shlib;
+
+ dp = paths = _dl_strdup(paths);
+ if (dp == NULL) {
+ _dl_printf("preload: out of memory");
+ _dl_exit(1);
+ }
+
+ while ((cp = _dl_strsep(&dp, ":")) != NULL) {
+ shlib = _dl_load_shlib(cp, _dl_objects, OBJTYPE_LIB,
+ _dl_objects->obj_flags);
+ if (shlib == NULL) {
+ _dl_printf("%s: can't load library '%s'\n",
+ _dl_progname, cp);
+ _dl_exit(4);
+ }
+ _dl_add_object(shlib);
+ _dl_link_child(shlib, _dl_objects);
+ }
+ _dl_free(paths);
+ return;
+}
+
+/*
+ * grab interesting environment variables, zap bad env vars if
+ * issetugid
+ */
+char **_dl_so_envp;
+void
+_dl_setup_env(char **envp)
+{
+ /*
+ * Get paths to various things we are going to use.
+ */
+ _dl_libpath = _dl_getenv("LD_LIBRARY_PATH", envp);
+ _dl_preload = _dl_getenv("LD_PRELOAD", envp);
+ _dl_bindnow = _dl_getenv("LD_BIND_NOW", envp);
+ _dl_traceld = _dl_getenv("LD_TRACE_LOADED_OBJECTS", envp);
+ _dl_tracefmt1 = _dl_getenv("LD_TRACE_LOADED_OBJECTS_FMT1", envp);
+ _dl_tracefmt2 = _dl_getenv("LD_TRACE_LOADED_OBJECTS_FMT2", envp);
+ _dl_traceprog = _dl_getenv("LD_TRACE_LOADED_OBJECTS_PROGNAME", envp);
+ _dl_debug = _dl_getenv("LD_DEBUG", envp);
+ _dl_norandom = _dl_getenv("LD_NORANDOM", envp);
+ _dl_noprebind = _dl_getenv("LD_NOPREBIND", envp);
+ _dl_prebind_validate = _dl_getenv("LD_PREBINDVALIDATE", envp);
+
+ /*
+ * Don't allow someone to change the search paths if he runs
+ * a suid program without credentials high enough.
+ */
+ if (_dl_issetugid()) { /* Zap paths if s[ug]id... */
+ if (_dl_libpath) {
+ _dl_libpath = NULL;
+ _dl_unsetenv("LD_LIBRARY_PATH", envp);
+ }
+ if (_dl_preload) {
+ _dl_preload = NULL;
+ _dl_unsetenv("LD_PRELOAD", envp);
+ }
+ if (_dl_bindnow) {
+ _dl_bindnow = NULL;
+ _dl_unsetenv("LD_BIND_NOW", envp);
+ }
+ if (_dl_debug) {
+ _dl_debug = NULL;
+ _dl_unsetenv("LD_DEBUG", envp);
+ }
+ if (_dl_norandom) {
+ _dl_norandom = NULL;
+ _dl_unsetenv("LD_NORANDOM", envp);
+ }
+ }
+ _dl_so_envp = envp;
+}
+
+int
+_dl_load_dep_libs(elf_object_t *object, int flags, int booting)
+{
+ elf_object_t *dynobj;
+ Elf_Dyn *dynp;
+ unsigned int loop;
+ int libcount;
+ int depflags;
+
+ dynobj = object;
+ while (dynobj) {
+ DL_DEB(("examining: '%s'\n", dynobj->load_name));
+ libcount = 0;
+
+ /* propagate RTLD_NOW to deplibs (can be set by dynamic tags) */
+ depflags = flags | (dynobj->obj_flags & RTLD_NOW);
+
+ for (dynp = dynobj->load_dyn; dynp->d_tag; dynp++) {
+ if (dynp->d_tag == DT_NEEDED) {
+ libcount++;
+ }
+ }
+
+ if ( libcount != 0) {
+ struct listent {
+ Elf_Dyn *dynp;
+ elf_object_t *depobj;
+ } *liblist;
+ int *randomlist;
+
+ liblist = _dl_malloc(libcount * sizeof(struct listent));
+ randomlist = _dl_malloc(libcount * sizeof(int));
+
+ if (liblist == NULL)
+ _dl_exit(5);
+
+ for (dynp = dynobj->load_dyn, loop = 0; dynp->d_tag;
+ dynp++)
+ if (dynp->d_tag == DT_NEEDED)
+ liblist[loop++].dynp = dynp;
+
+ /* Randomize these */
+ for (loop = 0; loop < libcount; loop++)
+ randomlist[loop] = loop;
+
+ if (!_dl_norandom)
+ for (loop = 1; loop < libcount; loop++) {
+ unsigned int rnd;
+ int cur;
+ rnd = _dl_random();
+ rnd = rnd % (loop+1);
+ cur = randomlist[rnd];
+ randomlist[rnd] = randomlist[loop];
+ randomlist[loop] = cur;
+ }
+
+ for (loop = 0; loop < libcount; loop++) {
+ elf_object_t *depobj;
+ const char *libname;
+ libname = dynobj->dyn.strtab;
+ libname +=
+ liblist[randomlist[loop]].dynp->d_un.d_val;
+ DL_DEB(("loading: %s required by %s\n", libname,
+ dynobj->load_name));
+ depobj = _dl_load_shlib(libname, dynobj,
+ OBJTYPE_LIB, depflags);
+ if (depobj == 0) {
+ if (booting) {
+ _dl_printf(
+ "%s: can't load library '%s'\n",
+ _dl_progname, libname);
+ _dl_exit(4);
+ } else {
+ DL_DEB(("dlopen: failed to open %s\n",
+ libname));
+ _dl_free(liblist);
+ return (1);
+ }
+ }
+ liblist[randomlist[loop]].depobj = depobj;
+ }
+
+ for (loop = 0; loop < libcount; loop++) {
+ _dl_add_object(liblist[loop].depobj);
+ _dl_link_child(liblist[loop].depobj, dynobj);
+ }
+ _dl_free(liblist);
+ }
+ dynobj = dynobj->next;
+ }
+
+ /* add first object manually */
+ _dl_link_grpsym(object);
+ _dl_cache_grpsym_list(object);
+
+ return(0);
+}
+
+
+#define PFLAGS(X) ((((X) & PF_R) ? PROT_READ : 0) | \
+ (((X) & PF_W) ? PROT_WRITE : 0) | \
+ (((X) & PF_X) ? PROT_EXEC : 0))
+
+/*
+ * This is the dynamic loader entrypoint. When entering here, depending
+ * on architecture type, the stack and registers are set up according
+ * to the architectures ABI specification. The first thing required
+ * to do is to dig out all information we need to accomplish our task.
+ */
+unsigned long
+_dl_boot(const char **argv, char **envp, const long dyn_loff, long *dl_data)
+{
+ struct elf_object *exe_obj; /* Pointer to executable object */
+ struct elf_object *dyn_obj; /* Pointer to executable object */
+ struct r_debug **map_link; /* Where to put pointer for gdb */
+ struct r_debug *debug_map;
+ struct load_list *next_load, *load_list = NULL;
+ Elf_Dyn *dynp;
+ Elf_Phdr *phdp;
+ Elf_Ehdr *ehdr;
+ char *us = NULL;
+ unsigned int loop;
+ int failed;
+ struct dep_node *n;
+ Elf_Addr minva, maxva, exe_loff;
+ int align;
+
+ _dl_setup_env(envp);
+
+ _dl_progname = argv[0];
+ if (dl_data[AUX_pagesz] != 0)
+ _dl_pagesz = dl_data[AUX_pagesz];
+ else
+ _dl_pagesz = 4096;
+
+ align = _dl_pagesz - 1;
+
+#define ROUND_PG(x) (((x) + align) & ~(align))
+#define TRUNC_PG(x) ((x) & ~(align))
+
+ /*
+ * now that GOT and PLT has been relocated, and we know
+ * page size, protect it from modification
+ */
+#ifndef RTLD_NO_WXORX
+ {
+ extern char *__got_start;
+ extern char *__got_end;
+#ifdef RTLD_PROTECT_PLT
+ extern char *__plt_start;
+ extern char *__plt_end;
+#endif
+
+ _dl_mprotect((void *)ELF_TRUNC((long)&__got_start, _dl_pagesz),
+ ELF_ROUND((long)&__got_end,_dl_pagesz) -
+ ELF_TRUNC((long)&__got_start, _dl_pagesz),
+ GOT_PERMS);
+
+#ifdef RTLD_PROTECT_PLT
+ /* only for DATA_PLT or BSS_PLT */
+ _dl_mprotect((void *)ELF_TRUNC((long)&__plt_start, _dl_pagesz),
+ ELF_ROUND((long)&__plt_end,_dl_pagesz) -
+ ELF_TRUNC((long)&__plt_start, _dl_pagesz),
+ PROT_READ|PROT_EXEC);
+#endif
+ }
+#endif
+
+ DL_DEB(("rtld loading: '%s'\n", _dl_progname));
+
+ /* init this in runtime, not statically */
+ TAILQ_INIT(&_dlopened_child_list);
+
+ exe_obj = NULL;
+ _dl_loading_object = NULL;
+
+ minva = ELFDEFNNAME(NO_ADDR);
+ maxva = exe_loff = 0;
+
+ /*
+ * Examine the user application and set up object information.
+ */
+ phdp = (Elf_Phdr *)dl_data[AUX_phdr];
+ for (loop = 0; loop < dl_data[AUX_phnum]; loop++) {
+ switch (phdp->p_type) {
+ case PT_PHDR:
+ exe_loff = (Elf_Addr)dl_data[AUX_phdr] - phdp->p_vaddr;
+ us += exe_loff;
+ DL_DEB(("exe load offset: 0x%lx\n", exe_loff));
+ break;
+ case PT_DYNAMIC:
+ minva = TRUNC_PG(minva);
+ maxva = ROUND_PG(maxva);
+ exe_obj = _dl_finalize_object(argv[0] ? argv[0] : "",
+ (Elf_Dyn *)(phdp->p_vaddr + exe_loff),
+ (Elf_Phdr *)dl_data[AUX_phdr],
+ dl_data[AUX_phnum], OBJTYPE_EXE, minva + exe_loff,
+ exe_loff);
+ _dl_add_object(exe_obj);
+ break;
+ case PT_INTERP:
+ us += phdp->p_vaddr;
+ break;
+ case PT_LOAD:
+ if (phdp->p_vaddr < minva)
+ minva = phdp->p_vaddr;
+ if (phdp->p_vaddr > maxva)
+ maxva = phdp->p_vaddr + phdp->p_memsz;
+
+ next_load = _dl_malloc(sizeof(struct load_list));
+ next_load->next = load_list;
+ load_list = next_load;
+ next_load->start = (char *)TRUNC_PG(phdp->p_vaddr) + exe_loff;
+ next_load->size = (phdp->p_vaddr & align) + phdp->p_filesz;
+ next_load->prot = PFLAGS(phdp->p_flags);
+
+ if (phdp->p_flags & 0x08000000) {
+// dump_prelink(phdp->p_vaddr + exe_loff, phdp->p_memsz);
+ prebind_load_exe(phdp, exe_obj);
+ }
+ break;
+ }
+ phdp++;
+ }
+ exe_obj->load_list = load_list;
+ exe_obj->obj_flags |= RTLD_GLOBAL;
+ exe_obj->load_size = maxva - minva;
+
+ n = _dl_malloc(sizeof *n);
+ if (n == NULL)
+ _dl_exit(5);
+ n->data = exe_obj;
+ TAILQ_INSERT_TAIL(&_dlopened_child_list, n, next_sib);
+ exe_obj->opencount++;
+
+ if (_dl_preload != NULL)
+ _dl_dopreload(_dl_preload);
+
+ _dl_load_dep_libs(exe_obj, exe_obj->obj_flags, 1);
+
+ /*
+ * Now add the dynamic loader itself last in the object list
+ * so we can use the _dl_ code when serving dl.... calls.
+ * Intentionally left off the exe child_list.
+ */
+ dynp = (Elf_Dyn *)((void *)_DYNAMIC);
+ ehdr = (Elf_Ehdr *)dl_data[AUX_base];
+ dyn_obj = _dl_finalize_object(us, dynp,
+ (Elf_Phdr *)((char *)dl_data[AUX_base] + ehdr->e_phoff),
+ ehdr->e_phnum, OBJTYPE_LDR, dl_data[AUX_base], dyn_loff);
+ _dl_add_object(dyn_obj);
+
+ dyn_obj->refcount++;
+ _dl_link_grpsym(dyn_obj);
+
+ dyn_obj->status |= STAT_RELOC_DONE;
+
+ /*
+ * Everything should be in place now for doing the relocation
+ * and binding. Call _dl_rtld to do the job. Fingers crossed.
+ */
+
+ _dl_prebind_pre_resolve();
+ failed = 0;
+ if (_dl_traceld == NULL)
+ failed = _dl_rtld(_dl_objects);
+
+ _dl_prebind_post_resolve();
+
+ if (_dl_debug || _dl_traceld)
+ _dl_show_objects();
+
+ DL_DEB(("dynamic loading done, %s.\n",
+ (failed == 0) ? "success":"failed"));
+
+ if (failed != 0)
+ _dl_exit(1);
+
+ if (_dl_traceld)
+ _dl_exit(0);
+
+ _dl_loading_object = NULL;
+
+ _dl_fixup_user_env();
+
+ /*
+ * Finally make something to help gdb when poking around in the code.
+ */
+#ifdef __mips__
+ map_link = (struct r_debug **)(exe_obj->Dyn.info[DT_MIPS_RLD_MAP -
+ DT_LOPROC + DT_NUM]);
+#else
+ map_link = NULL;
+ for (dynp = exe_obj->load_dyn; dynp->d_tag; dynp++) {
+ if (dynp->d_tag == DT_DEBUG) {
+ map_link = (struct r_debug **)&dynp->d_un.d_ptr;
+ break;
+ }
+ }
+ if (dynp->d_tag != DT_DEBUG)
+ DL_DEB(("failed to mark DTDEBUG\n"));
+#endif
+ if (map_link) {
+ debug_map = (struct r_debug *)_dl_malloc(sizeof(*debug_map));
+ debug_map->r_version = 1;
+ debug_map->r_map = (struct link_map *)_dl_objects;
+ debug_map->r_brk = (Elf_Addr)_dl_debug_state;
+ debug_map->r_state = RT_CONSISTENT;
+ debug_map->r_ldbase = dyn_loff;
+ _dl_debug_map = debug_map;
+ *map_link = _dl_debug_map;
+ }
+
+ _dl_debug_state();
+
+ /*
+ * The first object is the executable itself,
+ * it is responsible for running it's own ctors/dtors
+ * thus do NOT run the ctors for the executable, all of
+ * the shared libraries which follow.
+ * Do not run init code if run from ldd.
+ */
+ if (_dl_objects->next != NULL) {
+ _dl_objects->status |= STAT_INIT_DONE;
+ _dl_call_init(_dl_objects);
+ }
+
+ /*
+ * Schedule a routine to be run at shutdown, by using atexit.
+ * Cannot call atexit directly from ld.so?
+ * Do not schedule destructors if run from ldd.
+ */
+ {
+ const elf_object_t *sobj;
+ const Elf_Sym *sym;
+ Elf_Addr ooff;
+
+ sym = NULL;
+ ooff = _dl_find_symbol("atexit", &sym,
+ SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
+ NULL, dyn_obj, &sobj);
+ if (sym == NULL)
+ _dl_printf("cannot find atexit, destructors will not be run!\n");
+ else
+#ifdef MD_ATEXIT
+ MD_ATEXIT(sobj, sym, (Elf_Addr)&_dl_dtors);
+#else
+ (*(void (*)(Elf_Addr))(sym->st_value + ooff))
+ ((Elf_Addr)_dl_dtors);
+#endif
+ }
+
+ DL_DEB(("entry point: 0x%lx\n", dl_data[AUX_entry]));
+
+ /*
+ * Return the entry point.
+ */
+ return(dl_data[AUX_entry]);
+}
+
+void
+_dl_boot_bind(const long sp, long *dl_data, Elf_Dyn *dynamicp)
+{
+ struct elf_object dynld; /* Resolver data for the loader */
+ AuxInfo *auxstack;
+ long *stack;
+ Elf_Dyn *dynp;
+ int n, argc;
+ char **argv, **envp;
+ long loff;
+
+ /*
+ * Scan argument and environment vectors. Find dynamic
+ * data vector put after them.
+ */
+ stack = (long *)sp;
+ argc = *stack++;
+ argv = (char **)stack;
+ envp = &argv[argc + 1];
+ stack = (long *)envp;
+ while (*stack++ != NULL)
+ ;
+
+ /*
+ * Zero out dl_data.
+ */
+ for (n = 0; n <= AUX_entry; n++)
+ dl_data[n] = 0;
+
+ /*
+ * Dig out auxiliary data set up by exec call. Move all known
+ * tags to an indexed local table for easy access.
+ */
+ for (auxstack = (AuxInfo *)stack; auxstack->au_id != AUX_null;
+ auxstack++) {
+ if (auxstack->au_id > AUX_entry)
+ continue;
+ dl_data[auxstack->au_id] = auxstack->au_v;
+ }
+ loff = dl_data[AUX_base]; /* XXX assumes ld.so is linked at 0x0 */
+
+ /*
+ * We need to do 'selfreloc' in case the code weren't
+ * loaded at the address it was linked to.
+ *
+ * Scan the DYNAMIC section for the loader.
+ * Cache the data for easier access.
+ */
+
+#if defined(__alpha__)
+ dynp = (Elf_Dyn *)((long)_DYNAMIC);
+#elif defined(__sparc__) || defined(__sparc64__) || defined(__powerpc__) || \
+ defined(__hppa__) || defined(__sh__)
+ dynp = dynamicp;
+#else
+ dynp = (Elf_Dyn *)((long)_DYNAMIC + loff);
+#endif
+ while (dynp != NULL && dynp->d_tag != DT_NULL) {
+ if (dynp->d_tag < DT_NUM)
+ dynld.Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
+ else if (dynp->d_tag >= DT_LOPROC &&
+ dynp->d_tag < DT_LOPROC + DT_PROCNUM)
+ dynld.Dyn.info[dynp->d_tag - DT_LOPROC + DT_NUM] =
+ dynp->d_un.d_val;
+ if (dynp->d_tag == DT_TEXTREL)
+ dynld.dyn.textrel = 1;
+ dynp++;
+ }
+
+ /*
+ * Do the 'bootstrap relocation'. This is really only needed if
+ * the code was loaded at another location than it was linked to.
+ * We don't do undefined symbols resolving (to difficult..)
+ */
+
+ /* "relocate" dyn.X values if they represent addresses */
+ {
+ int i, val;
+ /* must be code, not pic data */
+ int table[20];
+
+ i = 0;
+ table[i++] = DT_PLTGOT;
+ table[i++] = DT_HASH;
+ table[i++] = DT_STRTAB;
+ table[i++] = DT_SYMTAB;
+ table[i++] = DT_RELA;
+ table[i++] = DT_INIT;
+ table[i++] = DT_FINI;
+ table[i++] = DT_REL;
+ table[i++] = DT_JMPREL;
+ /* other processors insert their extras here */
+ table[i++] = DT_NULL;
+ for (i = 0; table[i] != DT_NULL; i++) {
+ val = table[i];
+ if (val > DT_HIPROC) /* ??? */
+ continue;
+ if (val > DT_LOPROC)
+ val -= DT_LOPROC + DT_NUM;
+ if (dynld.Dyn.info[val] != 0)
+ dynld.Dyn.info[val] += loff;
+ }
+ }
+
+ {
+ u_int32_t rs;
+ Elf_Rel *rp;
+ int i;
+
+ rp = (Elf_Rel *)(dynld.Dyn.info[DT_REL]);
+ rs = dynld.dyn.relsz;
+
+ for (i = 0; i < rs; i += sizeof (Elf_Rel)) {
+ Elf_Addr *ra;
+ const Elf_Sym *sp;
+
+ sp = dynld.dyn.symtab;
+ sp += ELF_R_SYM(rp->r_info);
+
+ if (ELF_R_SYM(rp->r_info) && sp->st_value == 0) {
+#if 0
+/* cannot printf in this function */
+ _dl_wrstderr("Dynamic loader failure: self bootstrapping impossible.\n");
+ _dl_wrstderr("Undefined symbol: ");
+ _dl_wrstderr((char *)dynld.dyn.strtab +
+ sp->st_name);
+#endif
+ _dl_exit(5);
+ }
+
+ ra = (Elf_Addr *)(rp->r_offset + loff);
+ RELOC_REL(rp, sp, ra, loff);
+ rp++;
+ }
+ }
+
+ for (n = 0; n < 2; n++) {
+ unsigned long rs;
+ Elf_RelA *rp;
+ int i;
+
+ switch (n) {
+ case 0:
+ rp = (Elf_RelA *)(dynld.Dyn.info[DT_JMPREL]);
+ rs = dynld.dyn.pltrelsz;
+ break;
+ case 1:
+ rp = (Elf_RelA *)(dynld.Dyn.info[DT_RELA]);
+ rs = dynld.dyn.relasz;
+ break;
+ default:
+ rp = NULL;
+ rs = 0;
+ }
+ for (i = 0; i < rs; i += sizeof (Elf_RelA)) {
+ Elf_Addr *ra;
+ const Elf_Sym *sp;
+
+ sp = dynld.dyn.symtab;
+ sp += ELF_R_SYM(rp->r_info);
+ if (ELF_R_SYM(rp->r_info) && sp->st_value == 0) {
+#if 0
+ _dl_wrstderr("Dynamic loader failure: self bootstrapping impossible.\n");
+ _dl_wrstderr("Undefined symbol: ");
+ _dl_wrstderr((char *)dynld.dyn.strtab +
+ sp->st_name);
+#endif
+ _dl_exit(6);
+ }
+
+ ra = (Elf_Addr *)(rp->r_offset + loff);
+ RELOC_RELA(rp, sp, ra, loff, dynld.dyn.pltgot);
+ rp++;
+ }
+ }
+
+ RELOC_GOT(&dynld, loff);
+
+ /*
+ * we have been fully relocated here, so most things no longer
+ * need the loff adjustment
+ */
+}
+
+#define DL_SM_SYMBUF_CNT 512
+sym_cache _dl_sm_symcache_buffer[DL_SM_SYMBUF_CNT];
+
+int
+_dl_rtld(elf_object_t *object)
+{
+ size_t sz;
+ int fails = 0;
+
+ if (object->next)
+ fails += _dl_rtld(object->next);
+
+ if (object->status & STAT_RELOC_DONE)
+ return 0;
+
+ sz = 0;
+ if (object->nchains < DL_SM_SYMBUF_CNT) {
+ _dl_symcache = _dl_sm_symcache_buffer;
+// DL_DEB(("using static buffer for %d entries\n",
+// object->nchains));
+ _dl_memset(_dl_symcache, 0,
+ sizeof (sym_cache) * object->nchains);
+ } else {
+ sz = ELF_ROUND(sizeof (sym_cache) * object->nchains,
+ _dl_pagesz);
+// DL_DEB(("allocating symcache sz %x with mmap\n", sz));
+
+ _dl_symcache = (void *)_dl_mmap(0, sz, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (_dl_mmap_error(_dl_symcache)) {
+ sz = 0;
+ _dl_symcache = NULL;
+ }
+ }
+ prebind_symcache(object, SYM_NOTPLT);
+
+ /*
+ * Do relocation information first, then GOT.
+ */
+ fails =_dl_md_reloc(object, DT_REL, DT_RELSZ);
+ fails += _dl_md_reloc(object, DT_RELA, DT_RELASZ);
+ prebind_symcache(object, SYM_PLT);
+ fails += _dl_md_reloc_got(object, !(_dl_bindnow ||
+ object->obj_flags & RTLD_NOW));
+
+ if (_dl_symcache != NULL) {
+ if (sz != 0)
+ _dl_munmap( _dl_symcache, sz);
+ _dl_symcache = NULL;
+ }
+ if (fails == 0)
+ object->status |= STAT_RELOC_DONE;
+
+ return (fails);
+}
+void
+_dl_call_init(elf_object_t *object)
+{
+ struct dep_node *n;
+
+ TAILQ_FOREACH(n, &object->child_list, next_sib) {
+ if (n->data->status & STAT_INIT_DONE)
+ continue;
+ _dl_call_init(n->data);
+ }
+
+ if (object->status & STAT_INIT_DONE)
+ return;
+
+ if (object->dyn.init) {
+ DL_DEB(("doing ctors obj %p @%p: [%s]\n",
+ object, object->dyn.init, object->load_name));
+ (*object->dyn.init)();
+ }
+
+ /* What about loops? */
+ object->status |= STAT_INIT_DONE;
+}
+
+static char *
+_dl_getenv(const char *var, char **env)
+{
+ const char *ep;
+
+ while ((ep = *env++)) {
+ const char *vp = var;
+
+ while (*vp && *vp == *ep) {
+ vp++;
+ ep++;
+ }
+ if (*vp == '\0' && *ep++ == '=')
+ return((char *)ep);
+ }
+ return(NULL);
+}
+
+static void
+_dl_unsetenv(const char *var, char **env)
+{
+ char *ep;
+
+ while ((ep = *env)) {
+ const char *vp = var;
+
+ while (*vp && *vp == *ep) {
+ vp++;
+ ep++;
+ }
+ if (*vp == '\0' && *ep++ == '=') {
+ char **P;
+
+ for (P = env;; ++P)
+ if (!(*P = *(P + 1)))
+ break;
+ } else
+ env++;
+ }
+}
+
+/*
+ * _dl_fixup_user_env()
+ *
+ * Set the user environment so that programs can use the environment
+ * while running constructors. Specifically, MALLOC_OPTIONS= for malloc()
+ */
+void
+_dl_fixup_user_env(void)
+{
+ const Elf_Sym *sym;
+ Elf_Addr ooff;
+ struct elf_object dummy_obj;
+
+ dummy_obj.dyn.symbolic = 0;
+ dummy_obj.load_name = "ld.so";
+
+ sym = NULL;
+ ooff = _dl_find_symbol("environ", &sym,
+ SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, &dummy_obj, NULL);
+ if (sym != NULL)
+ *((char ***)(sym->st_value + ooff)) = _dl_so_envp;
+}
--- /dev/null
+/Makefile.inc/1.1/Wed Aug 11 17:11:45 2004//
+/syscall.h/1.5/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.6/Sat Jan 2 15:01:02 2010//
+/ldasm.S/1.6/Mon Apr 5 23:11:44 2010//
+/rtld_machine.c/1.13/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/mips64
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.1 2004/08/11 17:11:45 pefo Exp $
+
+# CFLAGS += -fpic -msoft-float
+# ADDR=-Tdata 8000
+# ELF_LDFLAGS+=${ADDR}
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.6 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998-2002 Opsycon AB, Sweden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _MIPS_ARCHDEP_H_
+#define _MIPS_ARCHDEP_H_
+
+#include <link.h>
+
+#include "syscall.h"
+#include "resolve.h"
+#include "util.h"
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+#define MACHID EM_MIPS /* ELF e_machine ID value checked */
+
+
+#define RELOC_REL(relp, symp, adrp, val) \
+do { \
+ _dl_mprotect(adrp, 8, PROT_EXEC|PROT_READ|PROT_WRITE); \
+ if (ELF64_R_TYPE(relp->r_info) == R_MIPS_REL32_64) { \
+ if (ELF64_R_SYM(rp->r_info) != 0) \
+ *adrp = symp->st_value + val; \
+ else \
+ *adrp += val; \
+ } else if (ELF64_R_TYPE(relp->r_info) != R_MIPS_NONE) { \
+ _dl_exit(ELF64_R_TYPE(relp->r_info)+100); \
+ } \
+} while (0)
+
+#define RELOC_RELA(rela, sym, ptr, val, pltgot) \
+do { \
+ _dl_exit(20); /* We don't do RELA now */ \
+} while (0)
+
+struct elf_object;
+
+#define RELOC_GOT(obj, off) \
+do { \
+ struct elf_object *__dynld = obj; \
+ long __loff = off; \
+ Elf64_Addr *gotp; \
+ int i, n; \
+ const Elf_Sym *sp; \
+ \
+ /* Do all local gots */ \
+ gotp = __dynld->dyn.pltgot; \
+ n = __dynld->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];\
+ \
+ for (i = ((gotp[1] & 0x0000000080000000) ? 2 : 1); i < n; i++) {\
+ gotp[i] += __loff; \
+ } \
+ gotp += n; \
+ \
+ /* Do symbol referencing gots. There should be no global... */ \
+ n = __dynld->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - \
+ __dynld->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; \
+ sp = __dynld->dyn.symtab; \
+ sp += __dynld->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; \
+ \
+ while (n--) { \
+ if (sp->st_shndx == SHN_UNDEF || \
+ sp->st_shndx == SHN_COMMON) { \
+ _dl_exit(6); \
+ } else if (ELF64_ST_TYPE(sp->st_info) == STT_FUNC) { \
+ *gotp += __loff; \
+ } else { \
+ *gotp = sp->st_value + __loff; \
+ } \
+ gotp++; \
+ sp++; \
+ } \
+ __dynld->status |= STAT_GOT_DONE; \
+} while (0)
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _MIPS_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.6 2010/03/27 20:45:09 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998-2002 Opsycon AB, Sweden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <machine/asm.h>
+#include <sys/syscall.h>
+
+/* Stack at this stage is:
+ * struct stack {
+ * int kargc;
+ * char *kargv[1]; size depends on kargc
+ * char kargstr[1]; size varies
+ * char kenvstr[1]; size varies
+ * };
+ */
+
+FRAMESZ= MKFSIZ(4,16)
+GPOFF= FRAMESZ-2*REGSZ
+RAOFF= FRAMESZ-1*REGSZ
+
+LEAF(_dl_start, FRAMESZ) /* Not really LEAF, but we simplify */
+ PTR_SUBU sp, FRAMESZ # Some space.
+ SETUP_GP64(GPOFF, _dl_start)
+
+ LA s1, 1f
+ bgezal zero, 1f
+1:
+ PTR_SUBU s0, ra, s1 # This is the load offset
+ LA t0, _fdata
+ PTR_SRL t0, 20 # check if distance is > 2**16.
+ beqz t0, 2f
+ li t0, 0x10000
+
+ li t0, 0x100000
+2:
+
+ # This is a hack to change protection of .rodata so it
+ # can be relocated. A better way to find the location
+ # of .rodata should probably be used.
+ # We know that .rodata is aligned on 0x100000 or 0x10000
+ # and is at most 64 k in size.
+ li v0, SYS_mprotect
+ or a0, ra, 0xfff
+ xor a0, 0xfff
+ PTR_ADDU a0, t0
+ li a1, 0x10000
+ li a2, 7 /* (PROT_READ|PROT_WRITE|PROT_EXEC) */
+ syscall
+
+ PTR_ADDU a0, sp, FRAMESZ # Where stack info is.
+ PTR_ADDU a1, sp, 0 # Where fast AUX info will be.
+ LA t9, _dl_boot_bind
+ PTR_ADDU t9, s0
+ jalr t9 # Relocate ourself.
+
+ REG_L a3, FRAMESZ(sp) # argc
+ PTR_ADDU a0, sp, FRAMESZ+REGSZ # argv
+ PTR_ADDU a1, a0, REGSZ
+ PTR_SLL a3, a3, LOGREGSZ
+ PTR_ADDU a1, a3
+ PTR_ADDU a3, sp, 0 # Where fast AUX info will be.
+ move a2, s0 # Load offset
+ jal _dl_boot # Go do the linking.
+
+ RESTORE_GP64
+ PTR_ADDU sp, FRAMESZ # Restore stack pointer.
+ move t9, v0 # Entry address from _dl_boot.
+ j t9 # Go execute the 'real' program.
+END(_dl_start)
+
+LEAF(_dl__syscall, 0)
+ li v0, SYS___syscall # Indirect syscall.
+ syscall
+ bne a3, zero, 1f
+ j ra
+1:
+ li v0, -1
+ j ra
+END(_dl__syscall)
+
+FRAMESZ= MKFSIZ(4,16)
+GPOFF= FRAMESZ-2*REGSZ
+RAOFF= FRAMESZ-1*REGSZ
+A0OFF= FRAMESZ-3*REGSZ
+A1OFF= FRAMESZ-4*REGSZ
+A2OFF= FRAMESZ-5*REGSZ
+A3OFF= FRAMESZ-6*REGSZ
+A4OFF= FRAMESZ-7*REGSZ
+A5OFF= FRAMESZ-8*REGSZ
+A6OFF= FRAMESZ-9*REGSZ
+A7OFF= FRAMESZ-10*REGSZ
+S0OFF= FRAMESZ-11*REGSZ
+
+ .globl _dl_bind_start
+ .ent _dl_bind_start, 0
+_dl_bind_start:
+ ld v1, -32744(gp)
+ PTR_SUBU sp, FRAMESZ
+ SETUP_GP64(GPOFF, _dl_bind_start)
+ REG_S a0, A0OFF(sp)
+ REG_S a1, A1OFF(sp)
+ REG_S a2, A2OFF(sp)
+ REG_S a3, A3OFF(sp)
+ REG_S a4, A4OFF(sp)
+ REG_S a5, A5OFF(sp)
+ REG_S a6, A6OFF(sp)
+ REG_S a7, A7OFF(sp)
+ REG_S $15, RAOFF(sp)
+ REG_S s0, S0OFF(sp)
+ move s0, sp
+ move a0, v1
+ move a1, t8
+ jal _dl_bind
+
+ move sp, s0
+ REG_L ra, RAOFF(sp)
+ REG_L s0, S0OFF(sp)
+ REG_L a0, A0OFF(sp)
+ REG_L a1, A1OFF(sp)
+ REG_L a2, A2OFF(sp)
+ REG_L a3, A3OFF(sp)
+ REG_L a4, A4OFF(sp)
+ REG_L a5, A5OFF(sp)
+ REG_L a6, A6OFF(sp)
+ REG_L a7, A7OFF(sp)
+ RESTORE_GP64
+ PTR_ADDU sp, FRAMESZ
+ move t9, v0
+ jr t9
+ .end _dl_bind_start
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.13 2010/05/03 04:19:42 miod Exp $ */
+
+/*
+ * Copyright (c) 1998-2004 Opsycon AB, Sweden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <link.h>
+#include <signal.h>
+
+#include "resolve.h"
+#include "syscall.h"
+#include "archdep.h"
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relsz)
+{
+ int i;
+ int numrel;
+ int fails = 0;
+ struct load_list *load_list;
+ Elf64_Addr loff;
+ Elf64_Addr ooff;
+ Elf64_Addr got_start, got_end;
+ Elf64_Rel *relocs;
+ const Elf64_Sym *sym, *this;
+
+ loff = object->obj_base;
+ numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel);
+ relocs = (Elf64_Rel *)(object->Dyn.info[rel]);
+
+ if (relocs == NULL)
+ return(0);
+
+ /*
+ * Change protection of all write protected segments in the
+ * object so we can do relocations in the .rodata section.
+ * After relocation restore protection.
+ */
+ load_list = object->load_list;
+ while (load_list != NULL) {
+ if ((load_list->prot & PROT_WRITE) == 0)
+ _dl_mprotect(load_list->start, load_list->size,
+ load_list->prot|PROT_WRITE);
+ load_list = load_list->next;
+ }
+
+ /* XXX We need the got limits to know if reloc is in got. */
+ /* XXX Relocs against the got should not include the STUB address! */
+ this = NULL;
+ got_start = 0;
+ got_end = 0;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ got_start = ooff + this->st_value;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ got_end = ooff + this->st_value;
+
+ DL_DEB(("relocating %d\n", numrel));
+ for (i = 0; i < numrel; i++, relocs++) {
+ Elf64_Addr r_addr = relocs->r_offset + loff;
+ const char *symn;
+ int type;
+
+ if (ELF64_R_SYM(relocs->r_info) == 0xffffff)
+ continue;
+
+ ooff = 0;
+ sym = object->dyn.symtab;
+ sym += ELF64_R_SYM(relocs->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+ type = ELF64_R_TYPE(relocs->r_info);
+
+ this = NULL;
+ if (ELF64_R_SYM(relocs->r_info) &&
+ !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
+ ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT,
+ sym, object, NULL);
+
+ if (this == NULL) {
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
+ fails++;
+ continue;
+ }
+ }
+
+ switch (ELF64_R_TYPE(relocs->r_info)) {
+ /* XXX Handle non aligned relocs. .eh_frame
+ * XXX in libstdc++ seems to have them... */
+ u_int64_t robj;
+
+ case R_MIPS_REL32_64:
+ if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL &&
+ (ELF64_ST_TYPE(sym->st_info) == STT_SECTION ||
+ ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
+ if ((long)r_addr & 7) {
+ _dl_bcopy((char *)r_addr, &robj, sizeof(robj));
+ robj += loff + sym->st_value;
+ _dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
+ } else {
+ *(u_int64_t *)r_addr += loff + sym->st_value;
+ }
+ } else if (this && ((long)r_addr & 7)) {
+ _dl_bcopy((char *)r_addr, &robj, sizeof(robj));
+ robj += this->st_value + ooff;
+ _dl_bcopy(&robj, (char *)r_addr, sizeof(robj));
+ } else if (this) {
+ *(u_int64_t *)r_addr += this->st_value + ooff;
+ }
+ break;
+
+ case R_MIPS_NONE:
+ break;
+
+ default:
+ _dl_printf("%s: unsupported relocation '%d'\n",
+ _dl_progname, ELF64_R_TYPE(relocs->r_info));
+ _dl_exit(1);
+ }
+ }
+ DL_DEB(("done %d fails\n", fails));
+ load_list = object->load_list;
+ while (load_list != NULL) {
+ if ((load_list->prot & PROT_WRITE) == 0)
+ _dl_mprotect(load_list->start, load_list->size,
+ load_list->prot);
+ load_list = load_list->next;
+ }
+ return(fails);
+}
+
+extern void _dl_bind_start(void);
+
+/*
+ * Relocate the Global Offset Table (GOT). Currently we don't
+ * do lazy evaluation here because the GNU linker doesn't
+ * follow the ABI spec which says that if an external symbol
+ * is referenced by other relocations than CALL16 and 26 it
+ * should not be given a stub and have a zero value in the
+ * symbol table. By not doing so, we can't use pointers to
+ * external functions and use them in comparisons...
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int i, n;
+ Elf64_Addr loff;
+ Elf64_Addr ooff;
+ Elf64_Addr *gotp;
+ const Elf64_Sym *symp;
+ const Elf64_Sym *this;
+ const char *strt;
+
+ if (object->status & STAT_GOT_DONE)
+ return (0);
+
+ loff = object->obj_base;
+ strt = object->dyn.strtab;
+ gotp = object->dyn.pltgot;
+ n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM];
+
+ DL_DEB(("loff: '%p'\n", loff));
+ /*
+ * Set up pointers for run time (lazy) resolving.
+ */
+ gotp[0] = (long)_dl_bind_start;
+ gotp[1] = (long)object;
+
+ /* First do all local references. */
+ for (i = 2; i < n; i++) {
+ gotp[i] += loff;
+ }
+
+ gotp += n;
+
+ symp = object->dyn.symtab;
+ symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
+ n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] -
+ object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
+
+ this = NULL;
+ object->plt_size = 0;
+ object->got_size = 0;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_start = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_start;
+
+ /*
+ * Then do all global references according to the ABI.
+ * Quickstart is not yet implemented.
+ */
+ while (n--) {
+ if (symp->st_shndx == SHN_UNDEF &&
+ ELF64_ST_TYPE(symp->st_info) == STT_FUNC) {
+ if (symp->st_value == 0 || !lazy) {
+ this = 0;
+ ooff = _dl_find_symbol(strt + symp->st_name,
+ &this,
+ SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
+ symp, object, NULL);
+ if (this)
+ *gotp = this->st_value + ooff;
+ } else
+ *gotp = symp->st_value + loff;
+ } else if (symp->st_shndx == SHN_COMMON ||
+ symp->st_shndx == SHN_UNDEF) {
+ this = 0;
+ ooff = _dl_find_symbol(strt + symp->st_name, &this,
+ SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
+ symp, object, NULL);
+ if (this)
+ *gotp = this->st_value + ooff;
+ } else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC &&
+ symp->st_value != *gotp) {
+ *gotp += loff;
+ } else { /* Resolve all others immediately */
+ this = 0;
+ ooff = _dl_find_symbol(strt + symp->st_name, &this,
+ SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT,
+ symp, object, NULL);
+ if (this)
+ *gotp = this->st_value + ooff;
+ else
+ *gotp = symp->st_value + loff;
+ }
+ gotp++;
+ symp++;
+ }
+ object->status |= STAT_GOT_DONE;
+
+ DL_DEB(("got: %x, %x\n", object->got_start, object->got_size));
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+
+ return (0);
+}
+
+Elf_Addr
+_dl_bind(elf_object_t *object, int symidx)
+{
+ Elf_Addr *gotp = object->dyn.pltgot;
+ Elf_Addr *addr, ooff;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ sigset_t savedmask;
+ int n;
+
+ sym = object->dyn.symtab;
+ sym += symidx;
+ symn = object->dyn.strtab + sym->st_name;
+ n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] -
+ object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM];
+
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ addr = &gotp[n + symidx];
+
+ /* if GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ|PROT_WRITE);
+ }
+
+ *addr = ooff + this->st_value;
+
+ /* if GOT is (to be protected, change back to RO */
+ if (object->got_size != 0) {
+ _dl_mprotect(addr, sizeof (Elf_Addr), PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return *addr;
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.5 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 1998-2002 Opsycon AB, Sweden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+
+extern long _dl__syscall(quad_t val, ...);
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+/*
+ * Inlined system call functions that can be used before
+ * any dynamic address resolving has been done.
+ */
+
+extern inline void
+_dl_exit(int status)
+{
+ register int __status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "li $2,%1\n\t"
+ "syscall"
+ : "=r" (__status)
+ : "I" (SYS_exit), "r" (status)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ while (1)
+ ;
+}
+
+extern inline int
+_dl_open(const char* addr, int flags)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_open), "r" (addr), "r" (flags)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline int
+_dl_close(int fd)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_close), "r" (fd)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline ssize_t
+_dl_write(int fd, const char* buf, size_t len)
+{
+ register ssize_t status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_write), "r" (fd), "r" (buf), "r" (len)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline ssize_t
+_dl_read(int fd, const char* buf, size_t len)
+{
+ register ssize_t status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_read), "r" (fd), "r" (buf), "r" (len)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline void *
+_dl_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+extern inline int
+_dl_munmap(const void* addr, size_t len)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_munmap), "r" (addr), "r" (len)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline int
+_dl_mprotect(const void *addr, size_t size, int prot)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "li $2,%1\n\t"
+ "syscall"
+ : "=r" (status)
+ : "I" (SYS_mprotect), "r" (addr), "r" (size), "r" (prot)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline int
+_dl_stat(const char *addr, struct stat *sb)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall"
+ : "=r" (status)
+ : "I" (SYS_stat), "r" (addr), "r" (sb)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline int
+_dl_fstat(const int fd, struct stat *sb)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall"
+ : "=r" (status)
+ : "I" (SYS_fstat), "r" (fd), "r" (sb)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline ssize_t
+_dl_fcntl(int fd, int cmd, int flag)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_fcntl), "r" (fd), "r" (cmd), "r" (flag)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline ssize_t
+_dl_getdirentries(int fd, char *buf, int nbytes, long *basep)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "move $7,%5\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_getdirentries), "r" (fd), "r" (buf), "r" (nbytes), "r" (basep)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline int
+_dl_issetugid(void)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "li $2,%1\n\t"
+ "syscall"
+ : "=r" (status)
+ : "I" (SYS_issetugid)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+extern inline off_t
+_dl_lseek(int fd, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fd, 0, offset, whence);
+}
+
+extern inline int
+_dl_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ sigset_t sig_store;
+ sigset_t sig_store1;
+
+ if (set != NULL)
+ sig_store1 = *set;
+ else
+ sig_store1 = 0;
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "move %0, $2"
+ : "=r" (sig_store)
+ : "I" (SYS_sigprocmask), "r" (how), "r" (sig_store1)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ if (oset != NULL)
+ *oset = sig_store;
+
+ return 0;
+}
+static inline int
+_dl_sysctl(int *name, u_int namelen, void *oldp, size_t *oldplen, void *newp,
+ size_t newlen)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "move $6,%4\n\t"
+ "move $7,%5\n\t"
+ "move $8,%6\n\t"
+ "move $9,%7\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beqz $2,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS___sysctl), "r" (name), "r" (namelen), "r" (oldp),
+ "r" (oldplen), "r" (newp), "r" (newlen)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+extern inline int
+_dl_gettimeofday(struct timeval* tp, struct timezone *tzp)
+{
+ register int status __asm__ ("$2");
+
+ __asm__ volatile (
+ "move $4,%2\n\t"
+ "move $5,%3\n\t"
+ "li $2,%1\n\t"
+ "syscall\n\t"
+ "beq $7,$0,1f\n\t"
+ "li $2,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_gettimeofday), "r" (tp), "r" (tzp)
+ : "$3", "$4", "$5", "$6", "$7", "$8", "$9",
+ "$10","$11","$12","$13","$14","$15","$24","$25");
+ return status;
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/usr/obj/libexec/ld.so
\ No newline at end of file
--- /dev/null
+/Makefile.inc/1.2/Mon Dec 9 20:56:34 2002//
+/ldasm.S/1.13/Wed Jul 9 21:01:10 2003//
+/syscall.h/1.21/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.14/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.47/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/powerpc
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.2 2002/12/09 20:56:34 drahn Exp $
+
+CFLAGS += -fpic -msoft-float
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.14 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _POWERPC_ARCHDEP_H_
+#define _POWERPC_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 4 /* Arch constraint or otherwise */
+
+#define MACHID EM_PPC /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+#define RTLD_PROTECT_PLT
+
+/* HACK */
+#define DT_PROCNUM 0
+#ifndef DT_BIND_NOW
+#define DT_BIND_NOW 0
+#endif
+
+/*
+ * The following functions are declared inline so they can
+ * be used before bootstrap linking has been finished.
+ */
+
+static inline void
+_dl_dcbf(Elf32_Addr *addr)
+{
+ __asm__ volatile ("dcbst 0, %0\n\t"
+ "sync\n\t"
+ "icbi 0, %0\n\t"
+ "sync\n\t"
+ "isync"
+ : : "r" (addr) : "0");
+}
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ /* PowerPC does not use REL type relocations */
+ _dl_exit(20);
+}
+
+static inline void
+RELOC_RELA(Elf32_Rela *r, const Elf32_Sym *s, Elf32_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF32_R_TYPE(r->r_info) == RELOC_RELATIVE) {
+ *p = v + r->r_addend;
+ } else if (ELF32_R_TYPE(r->r_info) == RELOC_JMP_SLOT) {
+ Elf32_Addr val = v + s->st_value + r->r_addend -
+ (Elf32_Addr)(p);
+ if (((val & 0xfe000000) != 0) &&
+ ((val & 0xfe000000) != 0xfe000000)) {
+ /* invalid offset */
+ _dl_exit(20);
+ }
+ val &= ~0xfc000000;
+ val |= 0x48000000;
+ *p = val;
+ _dl_dcbf(p);
+ } else if (ELF32_R_TYPE((r)->r_info) == RELOC_GLOB_DAT) {
+ *p = v + s->st_value + r->r_addend;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS (PROT_READ|PROT_EXEC)
+
+#endif /* _POWERPC_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.13 2003/07/09 21:01:10 drahn Exp $ */
+
+/*
+ * Copyright (c) 1999 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define AUX_entry 9
+
+#include <machine/asm.h>
+#include <sys/syscall.h>
+
+ENTRY(_dl_start)
+ mr 19, 1
+ stwu 1, (-16 -((AUX_entry+3)*4))(1) # Some space.
+
+ mflr 27 /* save off old link register */
+ stw 27, 4(19) /* save in normal location */
+
+ # squirrel away the arguments for main
+ mr 20, 3 #argc
+ mr 21, 4 #argv
+ mr 22, 5 #envp
+ mr 23, 6 # ???
+
+ bl 1f
+ # this instruction never gets executed but can be used
+ # to find the virtual address where the page is loaded.
+ bl _GLOBAL_OFFSET_TABLE_@local-4
+ bl _DYNAMIC@local
+1:
+ mflr 5 # this stores where we are (+4)
+ lwz 18, 0(5) # load the instruction at offset_sym
+ # it contains an offset to the location
+ # of the GOT.
+
+ rlwinm 18,18,0,8,30 # mask off the offset portion of the instr.
+
+ /*
+ * these adds effectively calculate the value the
+ * bl _GLOBAL_OFFSET_TABLE_@local-4
+ * operation that would be below would calulate.
+ */
+ add 28, 18, 5
+ mr 6, 5 # save offset for later use
+
+ /* mprotect GOT-4 for correct execution of blrl instruction */
+ li 0, SYS_mprotect
+ mr 3, 28
+ li 4, 4
+ li 5, 7 /* (PROT_READ|PROT_WRITE|PROT_EXEC) */
+ sc
+
+ mr 5, 6
+
+ li 0, 0
+ dcbf 5, 18
+ sync
+ isync
+ icbi 5, 18 # make certain that the got table addr is
+ # not in the icache
+ sync
+ isync
+
+ /* This calculates the address of _DYNAMIC the same way
+ * that the GLOBAL_OFFSET_TABLE was calculated.
+ */
+ lwz 18, 4(5)
+ rlwinm 18,18,0,8,30 # mask off the offset portion of the instr.
+ add 8, 18, 5 # address of _DYNAMIC (arg6 for _dl_boot)
+ addi 18, 8, 4 # correction.
+ lwz 4, 4(28) # load address of _DYNAMIC according to got.
+ sub 4, 18, 4 # determine load offset
+
+ mr 17, 4 # save for _dl_boot
+
+
+ subi 3, 21, 4 # Get stack pointer (arg0 for _dl_boot).
+ addi 4, 1, 8 # dl_data
+ mr 5, 18 # dynamicp
+
+ bl _dl_boot_bind@local
+
+ mr 3, 21 # argv
+ mr 4, 22 # envp
+ mr 5, 17 # loff
+ addi 6, 1, 8 # dl_data
+
+ bl _dl_boot@local
+
+ mtctr 3 # put return value into ctr to execute
+
+ # get back the squirreled away the arguments for main
+ mr 3, 20
+ mr 4, 21
+ mr 5, 22
+ mr 6, 23
+ li 7, 0
+
+
+ mtlr 27
+ lwz 1, 0(1) # Restore stack pointer.
+ bctr # Go execute the 'real' program.
+
+ENTRY(_dl_bind_start)
+ stwu 1,-64(1)
+
+ stw 0,8(1) # save r0 - cerror ;-)
+ mflr 0
+ stw 0,68(1) # save lr
+
+ stw 3,12(1) # save r3-r10, C calling convention
+ stw 4,20(1) # r13 - r31 are preserved by called code
+ stw 5,24(1)
+ stw 6,28(1)
+ stw 7,32(1)
+ stw 8,36(1)
+ stw 9,40(1)
+ stw 10,44(1)
+
+ mr 3,12 # obj
+ mr 4,11 # reloff
+ bl _dl_bind@plt # _rtld_bind(obj, reloff)
+ mtctr 3
+
+ lwz 3,12(1)
+ lwz 4,20(1)
+ lwz 5,24(1)
+ lwz 6,28(1)
+ lwz 7,32(1)
+ lwz 8,36(1)
+ lwz 9,40(1)
+ lwz 10,44(1)
+
+ lwz 0,68(1) # restore lr
+ mtlr 0
+ lwz 0,8(1)
+
+ addi 1,1,64
+ bctr
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.47 2010/05/03 04:03:03 guenther Exp $ */
+
+/*
+ * Copyright (c) 1999 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+void _dl_syncicache(char *from, size_t len);
+
+/* relocation bits */
+#define HA(x) (((Elf_Addr)(x) >> 16) + (((Elf_Addr)(x) & 0x00008000) >> 15))
+#define L(x) (((Elf_Addr)x) & 0x0000ffff)
+#define ADDIS_R11_R11 0x3d6b0000
+#define ADDIS_R11_R0 0x3d600000
+#define ADDI_R11_R11 0x396b0000
+#define LWZ_R11_R11 0x816b0000
+#define LI_R11 0x39600000
+
+#define ADDIS_R12_R0 0x3d800000
+#define ADDI_R12_R12 0x398c0000
+#define MCTR_R11 0x7d6903a6
+#define MCTR_R12 0x7d8903a6
+#define BCTR 0x4e800420
+#define BR(from, to) do { \
+ int lval = (Elf32_Addr)(to) - (Elf32_Addr)(&(from)); \
+ lval &= ~0xfc000000; \
+ lval |= 0x48000000; \
+ (from) = lval; \
+} while (0)
+
+/* these are structures/functions offset from PLT region */
+#define PLT_CALL_OFFSET 6
+#define PLT_INFO_OFFSET 10
+#define PLT_1STRELA_OFFSET 18
+#define B24_VALID_RANGE(x) \
+ ((((x) & 0xfe000000) == 0x00000000) || (((x) & 0xfe000000) == 0xfe000000))
+
+void _dl_bind_start(void); /* XXX */
+Elf_Addr _dl_bind(elf_object_t *object, int reloff);
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ int i;
+ int numrela;
+ int fails = 0;
+ struct load_list *llist;
+ Elf32_Addr loff;
+ Elf32_Rela *relas;
+ /* for jmp table relocations */
+ Elf32_Addr *pltresolve;
+ Elf32_Addr *pltcall;
+ Elf32_Addr *plttable;
+ Elf32_Addr *pltinfo;
+
+ Elf32_Addr *first_rela;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela);
+ relas = (Elf32_Rela *)(object->Dyn.info[rel]);
+
+#ifdef DL_PRINTF_DEBUG
+_dl_printf("object relocation size %x, numrela %x\n",
+ object->Dyn.info[relasz], numrela);
+#endif
+
+ if (relas == NULL)
+ return(0);
+
+ pltresolve = NULL;
+ pltcall = NULL;
+ plttable = NULL;
+
+ /* for plt relocation usage */
+ if (object->Dyn.info[DT_JMPREL] != 0) {
+ /* resolver stub not set up */
+ int nplt;
+
+ /* Need to construct table to do jumps */
+ pltresolve = (Elf32_Addr *)(object->Dyn.info[DT_PLTGOT]);
+ pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
+ pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
+ first_rela = (Elf32_Addr *)(pltresolve) + PLT_1STRELA_OFFSET;
+
+ nplt = object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela);
+
+ if (nplt >= (2<<12)) {
+ plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
+ + (2 * (2<<12)) + (4 * (nplt - (2<<12)));
+ } else {
+ plttable = (Elf32_Addr *) ((Elf32_Addr)first_rela)
+ + (2 * nplt);
+ }
+
+ pltinfo[0] = (Elf32_Addr)plttable;
+
+#ifdef DL_PRINTF_DEBUG
+ _dl_printf("md_reloc: plttbl size %x\n",
+ (object->Dyn.info[DT_PLTRELSZ]/sizeof(Elf32_Rela)));
+ _dl_printf("md_reloc: plttable %x\n", plttable);
+#endif
+ pltresolve[0] = ADDIS_R12_R0 | HA(_dl_bind_start);
+ pltresolve[1] = ADDI_R12_R12 | L(_dl_bind_start);
+ pltresolve[2] = MCTR_R12;
+ pltresolve[3] = ADDIS_R12_R0 | HA(object);
+ pltresolve[4] = ADDI_R12_R12 | L(object);
+ pltresolve[5] = BCTR;
+ _dl_dcbf(&pltresolve[0]);
+ _dl_dcbf(&pltresolve[5]);
+
+ /* addis r11,r11,.PLTtable@ha*/
+ pltcall[0] = ADDIS_R11_R11 | HA(plttable);
+ /* lwz r11,plttable@l(r11) */
+ pltcall[1] = LWZ_R11_R11 | L(plttable);
+ pltcall[2] = MCTR_R11; /* mtctr r11 */
+ pltcall[3] = BCTR; /* bctr */
+ _dl_dcbf(&pltcall[0]);
+ _dl_dcbf(&pltcall[3]);
+ } else {
+ first_rela = NULL;
+ }
+
+ /*
+ * Change protection of all write protected segments in the object
+ * so we can do relocations such as REL24, REL16 etc. After
+ * relocation restore protection.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE)) {
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+ }
+
+
+ for (i = 0; i < numrela; i++, relas++) {
+ Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff);
+ Elf32_Addr ooff;
+ const Elf32_Sym *sym, *this;
+ const char *symn;
+ int type;
+
+ if (ELF32_R_SYM(relas->r_info) == 0xffffff)
+ continue;
+
+ type = ELF32_R_TYPE(relas->r_info);
+
+ if (type == RELOC_JMP_SLOT && rel != DT_JMPREL)
+ continue;
+
+ sym = object->dyn.symtab;
+ sym += ELF32_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ ooff = 0;
+ this = NULL;
+ if (ELF32_R_SYM(relas->r_info) &&
+ !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
+ ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE)) {
+ ooff = _dl_find_symbol_bysym(object,
+ ELF32_R_SYM(relas->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == RELOC_JMP_SLOT) ? SYM_PLT:SYM_NOTPLT),
+ sym, NULL);
+
+ if (this == NULL) {
+ if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
+ fails++;
+ continue;
+ }
+ }
+
+ switch (type) {
+#if 1
+ case RELOC_32:
+ if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
+ (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
+ ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
+ *r_addr = ooff + relas->r_addend;
+ } else {
+ *r_addr = ooff + this->st_value +
+ relas->r_addend;
+ }
+ break;
+#endif
+ case RELOC_RELATIVE:
+ if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL &&
+ (ELF32_ST_TYPE(sym->st_info) == STT_SECTION ||
+ ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
+ *r_addr = loff + relas->r_addend;
+
+#ifdef DL_PRINTF_DEBUG
+_dl_printf("rel1 r_addr %x val %x loff %x ooff %x addend %x\n", r_addr,
+ loff + relas->r_addend, loff, ooff, relas->r_addend);
+#endif
+
+ } else {
+ *r_addr = loff + this->st_value +
+ relas->r_addend;
+ }
+ break;
+ case RELOC_JMP_SLOT:
+ {
+ Elf32_Addr target = ooff + this->st_value +
+ relas->r_addend;
+ Elf32_Addr val = target - (Elf32_Addr)r_addr;
+
+ if (!B24_VALID_RANGE(val)){
+ int index;
+#ifdef DL_PRINTF_DEBUG
+_dl_printf(" ooff %x, sym val %x, addend %x"
+ " r_addr %x symn [%s] -> %x\n",
+ ooff, this->st_value, relas->r_addend,
+ r_addr, symn, val);
+#endif
+ /* if offset is > RELOC_24 deal with it */
+ index = (r_addr - first_rela) >> 1;
+
+ if (index >= (2 << 12)) {
+ /* addis r11,r11,.PLTtable@ha*/
+ r_addr[0] = ADDIS_R11_R0 | HA(index*4);
+ r_addr[1] = ADDI_R11_R11 | L(index*4);
+ BR(r_addr[2], pltcall);
+ } else {
+ r_addr[0] = LI_R11 | (index * 4);
+ BR(r_addr[1], pltcall);
+
+ }
+ _dl_dcbf(&r_addr[0]);
+ _dl_dcbf(&r_addr[2]);
+ val= ooff + this->st_value +
+ relas->r_addend;
+#ifdef DL_PRINTF_DEBUG
+_dl_printf(" symn [%s] val 0x%x\n", symn, val);
+#endif
+ plttable[index] = val;
+ } else {
+ /* if the offset is small enough,
+ * branch directly to the dest
+ */
+ BR(r_addr[0], target);
+ _dl_dcbf(&r_addr[0]);
+ }
+ }
+
+ break;
+ case RELOC_GLOB_DAT:
+ *r_addr = ooff + this->st_value + relas->r_addend;
+ break;
+#if 1
+ /* should not be supported ??? */
+ case RELOC_REL24:
+ {
+ Elf32_Addr val = ooff + this->st_value +
+ relas->r_addend - (Elf32_Addr)r_addr;
+ if (!B24_VALID_RANGE(val)){
+ /* invalid offset */
+ _dl_exit(20);
+ }
+ val &= ~0xfc000003;
+ val |= (*r_addr & 0xfc000003);
+ *r_addr = val;
+
+ _dl_dcbf(r_addr);
+ }
+ break;
+#endif
+#if 1
+ case RELOC_16_LO:
+ {
+ Elf32_Addr val;
+
+ val = loff + relas->r_addend;
+ *(Elf32_Half *)r_addr = val;
+
+ _dl_dcbf(r_addr);
+ }
+ break;
+#endif
+#if 1
+ case RELOC_16_HI:
+ {
+ Elf32_Addr val;
+
+ val = loff + relas->r_addend;
+ *(Elf32_Half *)r_addr = (val >> 16);
+
+ _dl_dcbf(r_addr);
+ }
+ break;
+#endif
+#if 1
+ case RELOC_16_HA:
+ {
+ Elf32_Addr val;
+
+ val = loff + relas->r_addend;
+ *(Elf32_Half *)r_addr = ((val + 0x8000) >> 16);
+
+ _dl_dcbf(r_addr);
+ }
+ break;
+#endif
+ case RELOC_REL14_TAKEN:
+ /* val |= 1 << (31-10) XXX? */
+ case RELOC_REL14:
+ case RELOC_REL14_NTAKEN:
+ {
+ Elf32_Addr val = ooff + this->st_value +
+ relas->r_addend - (Elf32_Addr)r_addr;
+ if (((val & 0xffff8000) != 0) &&
+ ((val & 0xffff8000) != 0xffff8000)) {
+ /* invalid offset */
+ _dl_exit(20);
+ }
+ val &= ~0xffff0003;
+ val |= (*r_addr & 0xffff0003);
+ *r_addr = val;
+#ifdef DL_PRINTF_DEBUG
+ _dl_printf("rel 14 %x val %x\n", r_addr, val);
+#endif
+
+ _dl_dcbf(r_addr);
+ }
+ break;
+ case RELOC_COPY:
+ {
+#ifdef DL_PRINTF_DEBUG
+ _dl_printf("copy r_addr %x, sym %x [%s] size %d val %x\n",
+ r_addr, sym, symn, sym->st_size,
+ (ooff + this->st_value+
+ relas->r_addend));
+#endif
+ /*
+ * we need to find a symbol, that is not in the current
+ * object, start looking at the beginning of the list,
+ * searching all objects but _not_ the current object,
+ * first one found wins.
+ */
+ const Elf32_Sym *cpysrc = NULL;
+ Elf32_Addr src_loff;
+ int size;
+
+ src_loff = 0;
+ src_loff = _dl_find_symbol(symn, &cpysrc,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
+ sym, object, NULL);
+ if (cpysrc != NULL) {
+ size = sym->st_size;
+ if (sym->st_size != cpysrc->st_size) {
+ _dl_printf("symbols size differ [%s] \n",
+ symn);
+ size = sym->st_size < cpysrc->st_size ?
+ sym->st_size : cpysrc->st_size;
+ }
+#ifdef DL_PRINTF_DEBUG
+_dl_printf(" found other symbol at %x size %d\n",
+ src_loff + cpysrc->st_value, cpysrc->st_size);
+#endif
+ _dl_bcopy((void *)(src_loff + cpysrc->st_value),
+ r_addr, size);
+ } else
+ fails++;
+ }
+ break;
+ case RELOC_NONE:
+ break;
+
+ default:
+ _dl_printf("%s:"
+ " %s: unsupported relocation '%s' %d at %x\n",
+ _dl_progname, object->load_name, symn,
+ ELF32_R_TYPE(relas->r_info), r_addr );
+ _dl_exit(1);
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+ return(fails);
+}
+
+/*
+ * Relocate the Global Offset Table (GOT).
+ * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW,
+ * otherwise the lazy binding plt initialization is performed.
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ Elf_Addr *pltresolve;
+ Elf_Addr *first_rela;
+ Elf_RelA *relas;
+ Elf_Addr plt_addr;
+ int i;
+ int numrela;
+ int fails = 0;
+ int index;
+ Elf32_Addr *r_addr;
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_RELA)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ plt_addr = 0;
+ object->plt_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ plt_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->plt_size = ooff + this->st_value - plt_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ if (plt_addr == NULL)
+ object->plt_start = NULL;
+ else {
+ object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
+ object->plt_size += plt_addr - object->plt_start;
+ object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
+ }
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ first_rela = (Elf32_Addr *)
+ (((Elf32_Rela *)(object->Dyn.info[DT_JMPREL]))->r_offset +
+ object->obj_base);
+ pltresolve = (Elf32_Addr *)(first_rela) - 18;
+
+ relas = (Elf32_Rela *)(object->Dyn.info[DT_JMPREL]);
+ numrela = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf32_Rela);
+ r_addr = (Elf32_Addr *)(relas->r_offset + object->obj_base);
+
+ for (i = 0, index = 0; i < numrela; i++, r_addr+=2, index++) {
+ if (index >= (2 << 12)) {
+ /* addis r11,r0,.PLTtable@ha*/
+ r_addr[0] = ADDIS_R11_R0 | HA(index*4);
+ r_addr[1] = ADDI_R11_R11 | L(index*4);
+ BR(r_addr[2], pltresolve);
+ /* only every other slot is used after
+ * index == 2^14
+ */
+ r_addr += 2;
+ } else {
+ r_addr[0] = LI_R11 | (index * 4);
+ BR(r_addr[1], pltresolve);
+ }
+ _dl_dcbf(&r_addr[0]);
+ _dl_dcbf(&r_addr[2]);
+ }
+ }
+ if (object->got_size != 0) {
+
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
+ _dl_syncicache((void*)object->got_addr, 4);
+ }
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
+
+Elf_Addr
+_dl_bind(elf_object_t *object, int reloff)
+{
+ const Elf_Sym *sym, *this;
+ Elf_Addr *r_addr, ooff;
+ const char *symn;
+ Elf_Addr value;
+ Elf_RelA *relas;
+ Elf32_Addr val;
+ Elf32_Addr *pltresolve;
+ Elf32_Addr *pltcall;
+ Elf32_Addr *pltinfo;
+ Elf32_Addr *plttable;
+ sigset_t savedmask;
+
+ relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + (reloff>>2);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ r_addr = (Elf_Addr *)(object->obj_base + relas->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ /* if PLT is protected, allow the write */
+ if (object->plt_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ }
+
+ value = ooff + this->st_value;
+
+ val = value - (Elf32_Addr)r_addr;
+
+ pltresolve = (Elf32_Addr *)
+ (Elf32_Rela *)(object->Dyn.info[DT_PLTGOT]);
+ pltcall = (Elf32_Addr *)(pltresolve) + PLT_CALL_OFFSET;
+
+ if (!B24_VALID_RANGE(val)) {
+ int index;
+ /* if offset is > RELOC_24 deal with it */
+ index = reloff >> 2;
+
+ /* update plttable before pltcall branch, to make
+ * this a safe race for threads
+ */
+ val = ooff + this->st_value + relas->r_addend;
+
+ pltinfo = (Elf32_Addr *)(pltresolve) + PLT_INFO_OFFSET;
+ plttable = (Elf32_Addr *)pltinfo[0];
+ plttable[index] = val;
+
+ if (index >= (2 << 12)) {
+ /* r_addr[0,1] is initialized to correct
+ * value in reloc_got.
+ */
+ BR(r_addr[2], pltcall);
+ _dl_dcbf(&r_addr[2]);
+ } else {
+ /* r_addr[0] is initialized to correct
+ * value in reloc_got.
+ */
+ BR(r_addr[1], pltcall);
+ _dl_dcbf(&r_addr[1]);
+ }
+ } else {
+ /* if the offset is small enough,
+ * branch directly to the dest
+ */
+ BR(r_addr[0], value);
+ _dl_dcbf(&r_addr[0]);
+ }
+
+ /* if PLT is to be protected, change back to RO/X */
+ if (object->plt_size != 0) {
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC); /* only PPC is PROT_EXE */
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+ return (value);
+}
+
+/* should not be defined here, but it is 32 for all powerpc 603-G4 */
+#define CACHELINESIZE 32
+void
+_dl_syncicache(char *from, size_t len)
+{
+ unsigned int off = 0;
+ int l = len + ((int)from & (CACHELINESIZE-1));
+
+ while (off < l) {
+ asm volatile ("dcbst %1,%0" :: "r"(from), "r"(off));
+ asm volatile ("sync");
+ asm volatile ("icbi %1, %0" :: "r"(from), "r"(off));
+ asm volatile ("sync");
+ asm volatile ("isync");
+
+ off += CACHELINESIZE;
+ }
+}
+__asm__(".section\t\".text\"\n\t"
+ ".align 2\n\t"
+ ".globl _dl__syscall\n\t"
+ ".type _dl__syscall,@function\n"
+ "_dl__syscall:\n\t"
+ "li 0, " XSTRINGIFY(SYS___syscall) "\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3, -1\n\t"
+ "1:\n\t"
+ "blr");
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.21 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/stat.h>
+
+#include <sys/syscall.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+
+static off_t _dl_lseek(int, off_t, int);
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+/*
+ * Inlined system call functions that can be used before
+ * any dynamic address resolving has been done.
+ */
+
+static inline void
+_dl_exit(int status)
+{
+ register int __status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "sc"
+ : "=r" (__status)
+ : "I" (SYS_exit), "r" (status) : "0", "3");
+
+ while (1)
+ ;
+}
+
+static inline int
+_dl_open(const char* addr, int flags)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_open), "r" (addr), "r" (flags)
+ : "0", "3", "4" );
+ return status;
+}
+
+static inline int
+_dl_close(int fd)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_close), "r" (fd)
+ : "0", "3");
+ return status;
+}
+
+static inline ssize_t
+_dl_write(int fd, const char* buf, size_t len)
+{
+ register ssize_t status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_write), "r" (fd), "r" (buf), "r" (len)
+ : "0", "3", "4", "5" );
+ return status;
+}
+
+static inline ssize_t
+_dl_read(int fd, const char* buf, size_t len)
+{
+ register ssize_t status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_read), "r" (fd), "r" (buf), "r" (len)
+ : "0", "3", "4", "5");
+ return status;
+}
+
+#define STRINGIFY(x) #x
+#define XSTRINGIFY(x) STRINGIFY(x)
+long _dl__syscall(quad_t val, ...);
+
+static inline void *
+_dl_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline int
+_dl_munmap(const void* addr, size_t len)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_munmap), "r" (addr), "r" (len)
+ : "0", "3", "4");
+ return status;
+}
+
+static inline int
+_dl_mprotect(const void *addr, size_t size, int prot)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_mprotect), "r" (addr), "r" (size), "r" (prot)
+ : "0", "3", "4", "5");
+ return status;
+}
+
+static inline int
+_dl_stat(const char *addr, struct stat *sb)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_stat), "r" (addr), "r" (sb)
+ : "0", "3", "4");
+ return status;
+}
+
+static inline int
+_dl_fstat(int fd, struct stat *sb)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_fstat), "r" (fd), "r" (sb)
+ : "0", "3", "4");
+ return status;
+}
+
+static inline int
+_dl_fcntl(int fd, int cmd, int flag)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ : "=r" (status)
+ : "I" (SYS_fcntl), "r" (fd), "r" (cmd), "r"(flag)
+ : "0", "3", "4", "5");
+ return status;
+}
+
+static inline int
+_dl_getdirentries(int fd, char *buf, int nbytes, long *basep)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "mr 6,%5\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_getdirentries), "r" (fd), "r" (buf), "r"(nbytes),
+ "r" (basep)
+ : "0", "3", "4", "5", "6");
+ return status;
+}
+
+static inline int
+_dl_issetugid(void)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_issetugid)
+ : "0", "3");
+ return status;
+}
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+static inline int
+_dl_sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+ sigset_t sig_store;
+ sigset_t sig_store1;
+
+ if (set != NULL) {
+ sig_store1 = *set;
+ } else {
+ sig_store1 = 0;
+ }
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "mr %0, 3"
+ : "=r" (sig_store)
+ : "I" (SYS_sigprocmask), "r" (how), "r" (sig_store1)
+ : "0", "3", "4");
+ if (oset != NULL)
+ *oset = sig_store;
+
+ return 0;
+}
+static inline int
+_dl_sysctl(int *name, u_int namelen, void *oldp, size_t *oldplen, void *newp,
+ size_t newlen)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "mr 5,%4\n\t"
+ "mr 6,%5\n\t"
+ "mr 7,%6\n\t"
+ "mr 8,%7\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS___sysctl), "r" (name), "r" (namelen), "r" (oldp),
+ "r" (oldplen), "r" (newp), "r" (newlen)
+ : "0", "3", "4", "5", "6", "7", "8");
+ return status;
+}
+static inline int
+_dl_gettimeofday(struct timeval *tp, struct timezone *tzp)
+{
+ register int status;
+
+ __asm__ volatile ("li 0,%1\n\t"
+ "mr 3,%2\n\t"
+ "mr 4,%3\n\t"
+ "sc\n\t"
+ "cmpwi 0, 0\n\t"
+ "beq 1f\n\t"
+ "li 3,-1\n\t"
+ "1:"
+ "mr %0,3\n\t"
+ : "=r" (status)
+ : "I" (SYS_gettimeofday), "r" (tp), "r" (tzp)
+ : "0", "3", "4" );
+ return status;
+}
+
+
+#endif /*__DL_SYSCALL_H__*/
+
--- /dev/null
+/* $OpenBSD: prebind.h,v 1.2 2006/05/12 22:14:04 drahn Exp $ */
+/*
+ * Copyright (c) 2006 Dale Rahn <drahn@dalerahn.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define PREBIND_VERSION 2
+struct prebind_footer {
+ off_t prebind_base;
+ u_int32_t nameidx_idx;
+ u_int32_t symcache_idx;
+ u_int32_t pltsymcache_idx;
+ u_int32_t fixup_idx;
+ u_int32_t nametab_idx;
+ u_int32_t fixupcnt_idx;
+ u_int32_t libmap_idx;
+
+ u_int32_t symcache_cnt;
+ u_int32_t pltsymcache_cnt;
+ u_int32_t fixup_cnt;
+ u_int32_t numlibs;
+ u_int32_t prebind_size;
+
+ u_int32_t id0;
+ u_int32_t id1;
+ /* do not modify or add fields below this point in the struct */
+ off_t orig_size;
+ u_int32_t prebind_version;
+ char bind_id[4];
+#define BIND_ID0 'P'
+#define BIND_ID1 'R'
+#define BIND_ID2 'E'
+#define BIND_ID3 'B'
+};
+
+
+struct nameidx {
+ u_int32_t name;
+ u_int32_t id0;
+ u_int32_t id1;
+};
+
+struct symcachetab {
+ u_int32_t idx;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
+
+struct fixup {
+ u_int32_t sym;
+ u_int32_t obj_idx;
+ u_int32_t sym_idx;
+};
--- /dev/null
+/* $OpenBSD: resolve.c,v 1.49 2008/05/05 02:29:02 kurt Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+
+#include <nlist.h>
+#include <link.h>
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+#include "dl_prebind.h"
+
+elf_object_t *_dl_objects;
+elf_object_t *_dl_last_object;
+elf_object_t *_dl_loading_object;
+
+/*
+ * Add a new dynamic object to the object list.
+ */
+void
+_dl_add_object(elf_object_t *object)
+{
+
+ /*
+ * if this is a new object, prev will be NULL
+ * != NULL if an object already in the list
+ * prev == NULL for the first item in the list, but that will
+ * be the executable.
+ */
+ if (object->prev != NULL)
+ return;
+
+ if (_dl_objects == NULL) { /* First object ? */
+ _dl_last_object = _dl_objects = object;
+ } else {
+ _dl_last_object->next = object;
+ object->prev = _dl_last_object;
+ _dl_last_object = object;
+ }
+}
+
+/*
+ * Initialize a new dynamic object.
+ */
+elf_object_t *
+_dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp,
+ int phdrc, const int objtype, const long lbase, const long obase)
+{
+ elf_object_t *object;
+#if 0
+ _dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n",
+ objname, dynp, objtype, lbase, obase);
+#endif
+ object = _dl_malloc(sizeof(elf_object_t));
+ object->prev = object->next = NULL;
+
+ object->load_dyn = dynp;
+ while (dynp->d_tag != DT_NULL) {
+ if (dynp->d_tag < DT_NUM)
+ object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
+ else if (dynp->d_tag >= DT_LOPROC &&
+ dynp->d_tag < DT_LOPROC + DT_PROCNUM)
+ object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
+ dynp->d_un.d_val;
+ if (dynp->d_tag == DT_TEXTREL)
+ object->dyn.textrel = 1;
+ if (dynp->d_tag == DT_SYMBOLIC)
+ object->dyn.symbolic = 1;
+ if (dynp->d_tag == DT_BIND_NOW)
+ object->obj_flags = RTLD_NOW;
+ dynp++;
+ }
+
+ /*
+ * Now relocate all pointer to dynamic info, but only
+ * the ones which have pointer values.
+ */
+ if (object->Dyn.info[DT_PLTGOT])
+ object->Dyn.info[DT_PLTGOT] += obase;
+ if (object->Dyn.info[DT_HASH])
+ object->Dyn.info[DT_HASH] += obase;
+ if (object->Dyn.info[DT_STRTAB])
+ object->Dyn.info[DT_STRTAB] += obase;
+ if (object->Dyn.info[DT_SYMTAB])
+ object->Dyn.info[DT_SYMTAB] += obase;
+ if (object->Dyn.info[DT_RELA])
+ object->Dyn.info[DT_RELA] += obase;
+ if (object->Dyn.info[DT_SONAME])
+ object->Dyn.info[DT_SONAME] += obase;
+ if (object->Dyn.info[DT_RPATH])
+ object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
+ if (object->Dyn.info[DT_REL])
+ object->Dyn.info[DT_REL] += obase;
+ if (object->Dyn.info[DT_INIT])
+ object->Dyn.info[DT_INIT] += obase;
+ if (object->Dyn.info[DT_FINI])
+ object->Dyn.info[DT_FINI] += obase;
+ if (object->Dyn.info[DT_JMPREL])
+ object->Dyn.info[DT_JMPREL] += obase;
+
+ if (object->Dyn.info[DT_HASH] != 0) {
+ Elf_Word *hashtab = (Elf_Word *)object->Dyn.info[DT_HASH];
+
+ object->nbuckets = hashtab[0];
+ object->nchains = hashtab[1];
+ object->buckets = hashtab + 2;
+ object->chains = object->buckets + object->nbuckets;
+ }
+
+ object->phdrp = phdrp;
+ object->phdrc = phdrc;
+ object->obj_type = objtype;
+ object->load_base = lbase;
+ object->obj_base = obase;
+ object->load_name = _dl_strdup(objname);
+ if (_dl_loading_object == NULL) {
+ /*
+ * no loading object, object is the loading object,
+ * as it is either executable, or dlopened()
+ */
+ _dl_loading_object = object->load_object = object;
+ DL_DEB(("head %s\n", object->load_name ));
+ } else {
+ object->load_object = _dl_loading_object;
+ }
+ DL_DEB(("obj %s has %s as head\n", object->load_name,
+ _dl_loading_object->load_name ));
+ object->refcount = 0;
+ TAILQ_INIT(&object->child_list);
+ object->opencount = 0; /* # dlopen() & exe */
+ object->grprefcount = 0;
+ /* default dev, inode for dlopen-able objects. */
+ object->dev = 0;
+ object->inode = 0;
+ TAILQ_INIT(&object->grpsym_list);
+ TAILQ_INIT(&object->grpref_list);
+
+ return(object);
+}
+
+void
+_dl_tailq_free(struct dep_node *n)
+{
+ struct dep_node *next;
+
+ while (n != NULL) {
+ next = TAILQ_NEXT(n, next_sib);
+ _dl_free(n);
+ n = next;
+ }
+}
+
+elf_object_t *free_objects;
+
+void _dl_cleanup_objects(void);
+void
+_dl_cleanup_objects()
+{
+ elf_object_t *nobj, *head;
+ struct dep_node *n, *next;
+
+ n = TAILQ_FIRST(&_dlopened_child_list);
+ while (n != NULL) {
+ next = TAILQ_NEXT(n, next_sib);
+ if (OBJECT_DLREF_CNT(n->data) == 0) {
+ TAILQ_REMOVE(&_dlopened_child_list, n, next_sib);
+ _dl_free(n);
+ }
+ n = next;
+ }
+
+ head = free_objects;
+ free_objects = NULL;
+ while (head != NULL) {
+ if (head->load_name)
+ _dl_free(head->load_name);
+ _dl_tailq_free(TAILQ_FIRST(&head->grpsym_list));
+ _dl_tailq_free(TAILQ_FIRST(&head->child_list));
+ _dl_tailq_free(TAILQ_FIRST(&head->grpref_list));
+ nobj = head->next;
+ _dl_free(head);
+ head = nobj;
+ }
+}
+
+void
+_dl_remove_object(elf_object_t *object)
+{
+ object->prev->next = object->next;
+ if (object->next)
+ object->next->prev = object->prev;
+
+ if (_dl_last_object == object)
+ _dl_last_object = object->prev;
+
+ object->next = free_objects;
+ free_objects = object;
+}
+
+
+elf_object_t *
+_dl_lookup_object(const char *name)
+{
+ elf_object_t *object;
+
+ object = _dl_objects;
+ while (object) {
+ if (_dl_strcmp(name, object->load_name) == 0)
+ return(object);
+ object = object->next;
+ }
+ return(0);
+}
+
+int _dl_find_symbol_obj(elf_object_t *object, const char *name,
+ unsigned long hash, int flags, const Elf_Sym **ref,
+ const Elf_Sym **weak_sym,
+ elf_object_t **weak_object);
+
+sym_cache *_dl_symcache;
+int _dl_symcachestat_hits;
+int _dl_symcachestat_lookups;
+
+
+Elf_Addr
+_dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx,
+ const Elf_Sym **this, int flags, const Elf_Sym *ref_sym, const elf_object_t **pobj)
+{
+ Elf_Addr ret;
+ const Elf_Sym *sym;
+ const char *symn;
+ const elf_object_t *sobj;
+
+ _dl_symcachestat_lookups ++;
+ if (_dl_symcache != NULL &&
+ symidx < req_obj->nchains &&
+ _dl_symcache[symidx].obj != NULL &&
+ _dl_symcache[symidx].sym != NULL &&
+ _dl_symcache[symidx].flags == flags) {
+
+ _dl_symcachestat_hits++;
+ sobj = _dl_symcache[symidx].obj;
+ *this = _dl_symcache[symidx].sym;
+ if (pobj)
+ *pobj = sobj;
+ if (_dl_prebind_validate) /* XXX */
+ prebind_validate(req_obj, symidx, flags, ref_sym);
+ return sobj->obj_base;
+ }
+
+ sym = req_obj->dyn.symtab;
+ sym += symidx;
+ symn = req_obj->dyn.strtab + sym->st_name;
+
+ ret = _dl_find_symbol(symn, this, flags, ref_sym, req_obj, &sobj);
+
+ if (pobj)
+ *pobj = sobj;
+
+ if (_dl_symcache != NULL && symidx < req_obj->nchains) {
+#if 0
+ DL_DEB(("cache miss %d %p %p, %p %p %s %s %d %d %s\n",
+ symidx,
+ _dl_symcache[symidx].sym, *this,
+ _dl_symcache[symidx].obj, sobj, sobj->load_name,
+ sobj->dyn.strtab + (*this)->st_name,
+ _dl_symcache[symidx].flags, flags, req_obj->load_name));
+#endif
+
+ _dl_symcache[symidx].sym = *this;
+ _dl_symcache[symidx].obj = sobj;
+ _dl_symcache[symidx].flags = flags;
+ }
+
+ return ret;
+}
+
+Elf_Addr
+_dl_find_symbol(const char *name, const Elf_Sym **this,
+ int flags, const Elf_Sym *ref_sym, elf_object_t *req_obj,
+ const elf_object_t **pobj)
+{
+ const Elf_Sym *weak_sym = NULL;
+ unsigned long h = 0;
+ const char *p = name;
+ elf_object_t *object = NULL, *weak_object = NULL;
+ int found = 0;
+ struct dep_node *n, *m;
+
+
+ while (*p) {
+ unsigned long g;
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000))
+ h ^= g >> 24;
+ h &= ~g;
+ }
+
+ if (req_obj->dyn.symbolic)
+ if (_dl_find_symbol_obj(req_obj, name, h, flags, this, &weak_sym,
+ &weak_object)) {
+ object = req_obj;
+ found = 1;
+ goto found;
+ }
+
+ if (flags & SYM_SEARCH_OBJ) {
+ if (_dl_find_symbol_obj(req_obj, name, h, flags, this,
+ &weak_sym, &weak_object)) {
+ object = req_obj;
+ found = 1;
+ }
+ } else if (flags & SYM_DLSYM) {
+ if (_dl_find_symbol_obj(req_obj, name, h, flags, this,
+ &weak_sym, &weak_object)) {
+ object = req_obj;
+ found = 1;
+ }
+ if (weak_object != NULL && found == 0) {
+ object=weak_object;
+ *this = weak_sym;
+ found = 1;
+ }
+ /* search dlopened obj and all children */
+
+ if (found == 0) {
+ TAILQ_FOREACH(n, &req_obj->load_object->grpsym_list,
+ next_sib) {
+ if (_dl_find_symbol_obj(n->data, name, h,
+ flags, this,
+ &weak_sym, &weak_object)) {
+ object = n->data;
+ found = 1;
+ break;
+ }
+ }
+ }
+ } else {
+ int skip = 0;
+
+ if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT))
+ skip = 1;
+
+ /*
+ * search dlopened objects: global or req_obj == dlopened_obj
+ * and and it's children
+ */
+ TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
+ if (((n->data->obj_flags & RTLD_GLOBAL) == 0) &&
+ (n->data != req_obj->load_object))
+ continue;
+
+ TAILQ_FOREACH(m, &n->data->grpsym_list, next_sib) {
+ if (skip == 1) {
+ if (m->data == req_obj) {
+ skip = 0;
+ if (flags & SYM_SEARCH_NEXT)
+ continue;
+ } else
+ continue;
+ }
+ if ((flags & SYM_SEARCH_OTHER) &&
+ (m->data == req_obj))
+ continue;
+ if (_dl_find_symbol_obj(m->data, name, h, flags,
+ this, &weak_sym, &weak_object)) {
+ object = m->data;
+ found = 1;
+ goto found;
+ }
+ }
+ }
+ }
+
+found:
+ if (weak_object != NULL && found == 0) {
+ object=weak_object;
+ *this = weak_sym;
+ found = 1;
+ }
+
+
+ if (found == 0) {
+ if ((ref_sym == NULL ||
+ (ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) &&
+ (flags & SYM_WARNNOTFOUND))
+ _dl_printf("%s:%s: undefined symbol '%s'\n",
+ _dl_progname, req_obj->load_name, name);
+ return (0);
+ }
+
+ if (ref_sym != NULL && ref_sym->st_size != 0 &&
+ (ref_sym->st_size != (*this)->st_size) &&
+ (ELF_ST_TYPE((*this)->st_info) != STT_FUNC) ) {
+ _dl_printf("%s:%s: %s : WARNING: "
+ "symbol(%s) size mismatch, relink your program\n",
+ _dl_progname, req_obj->load_name,
+ object->load_name, name);
+ }
+
+ if (pobj)
+ *pobj = object;
+
+ return (object->obj_base);
+}
+
+int
+_dl_find_symbol_obj(elf_object_t *object, const char *name, unsigned long hash,
+ int flags, const Elf_Sym **this, const Elf_Sym **weak_sym,
+ elf_object_t **weak_object)
+{
+ const Elf_Sym *symt = object->dyn.symtab;
+ const char *strt = object->dyn.strtab;
+ long si;
+ const char *symn;
+
+ for (si = object->buckets[hash % object->nbuckets];
+ si != STN_UNDEF; si = object->chains[si]) {
+ const Elf_Sym *sym = symt + si;
+
+ if (sym->st_value == 0)
+ continue;
+
+ if (ELF_ST_TYPE(sym->st_info) != STT_NOTYPE &&
+ ELF_ST_TYPE(sym->st_info) != STT_OBJECT &&
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+
+ symn = strt + sym->st_name;
+ if (sym != *this && _dl_strcmp(symn, name))
+ continue;
+
+ /* allow this symbol if we are referring to a function
+ * which has a value, even if section is UNDEF.
+ * this allows &func to refer to PLT as per the
+ * ELF spec. st_value is checked above.
+ * if flags has SYM_PLT set, we must have actual
+ * symbol, so this symbol is skipped.
+ */
+ if (sym->st_shndx == SHN_UNDEF) {
+ if ((flags & SYM_PLT) || sym->st_value == 0 ||
+ ELF_ST_TYPE(sym->st_info) != STT_FUNC)
+ continue;
+ }
+
+ if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
+ *this = sym;
+ return 1;
+ } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
+ if (!*weak_sym) {
+ *weak_sym = sym;
+ *weak_object = object;
+ }
+ }
+ }
+ return 0;
+}
--- /dev/null
+/* $OpenBSD: resolve.h,v 1.59 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _RESOLVE_H_
+#define _RESOLVE_H_
+
+#include <sys/queue.h>
+#include <link.h>
+#include <dlfcn.h>
+#include <signal.h>
+
+struct load_list {
+ struct load_list *next;
+ void *start;
+ size_t size;
+ int prot;
+ Elf_Addr moff;
+ long foff;
+};
+
+/*
+ * Structure describing a loaded object.
+ * The head of this struct must be compatible
+ * with struct link_map in sys/link.h
+ */
+typedef struct elf_object elf_object_t;
+struct elf_object {
+ Elf_Addr obj_base; /* object's address '0' base */
+ char *load_name; /* Pointer to object name */
+ Elf_Dyn *load_dyn; /* Pointer to object dynamic data */
+ struct elf_object *next;
+ struct elf_object *prev;
+/* End struct link_map compatible */
+ Elf_Addr load_base; /* Base address of loadable segments */
+
+ struct load_list *load_list;
+
+ u_int32_t load_size;
+ Elf_Addr got_addr;
+ Elf_Addr got_start;
+ size_t got_size;
+ Elf_Addr plt_start;
+ size_t plt_size;
+
+ union {
+ u_long info[DT_NUM + DT_PROCNUM];
+ struct {
+ Elf_Addr null; /* Not used */
+ Elf_Addr needed; /* Not used */
+ Elf_Addr pltrelsz;
+ Elf_Addr *pltgot;
+ Elf_Addr *hash;
+ const char *strtab;
+ const Elf_Sym *symtab;
+ Elf_RelA *rela;
+ Elf_Addr relasz;
+ Elf_Addr relaent;
+ Elf_Addr strsz;
+ Elf_Addr syment;
+ void (*init)(void);
+ void (*fini)(void);
+ const char *soname;
+ const char *rpath;
+ Elf_Addr symbolic;
+ Elf_Rel *rel;
+ Elf_Addr relsz;
+ Elf_Addr relent;
+ Elf_Addr pltrel;
+ Elf_Addr debug;
+ Elf_Addr textrel;
+ Elf_Addr jmprel;
+ } u;
+ } Dyn;
+#define dyn Dyn.u
+
+ int status;
+#define STAT_RELOC_DONE 0x01
+#define STAT_GOT_DONE 0x02
+#define STAT_INIT_DONE 0x04
+#define STAT_FINI_DONE 0x08
+#define STAT_FINI_READY 0x10
+#define STAT_UNLOADED 0x20
+
+ Elf_Phdr *phdrp;
+ int phdrc;
+
+ int obj_type;
+#define OBJTYPE_LDR 1
+#define OBJTYPE_EXE 2
+#define OBJTYPE_LIB 3
+#define OBJTYPE_DLO 4
+ int obj_flags;
+
+ Elf_Word *buckets;
+ u_int32_t nbuckets;
+ Elf_Word *chains;
+ u_int32_t nchains;
+ Elf_Dyn *dynamic;
+
+ TAILQ_HEAD(,dep_node) child_list; /* direct dep libs of object */
+ TAILQ_HEAD(,dep_node) grpsym_list; /* ordered complete dep list */
+ TAILQ_HEAD(,dep_node) grpref_list; /* refs to other load groups */
+
+ int refcount; /* dep libs only */
+ int opencount; /* # dlopen() & exe */
+ int grprefcount; /* load group refs */
+#define OBJECT_REF_CNT(object) \
+ ((object->refcount + object->opencount + object->grprefcount))
+#define OBJECT_DLREF_CNT(object) \
+ ((object->opencount + object->grprefcount))
+
+ /* object that caused this module to be loaded, used in symbol lookup */
+ elf_object_t *load_object;
+
+ void *prebind_data;
+
+ /* for object confirmation */
+ dev_t dev;
+ ino_t inode;
+};
+
+struct dep_node {
+ TAILQ_ENTRY(dep_node) next_sib;
+ elf_object_t *data;
+};
+
+void _dl_add_object(elf_object_t *object);
+elf_object_t *_dl_finalize_object(const char *objname, Elf_Dyn *dynp,
+ Elf_Phdr *phdrp, int phdrc, const int objtype, const long lbase,
+ const long obase);
+void _dl_remove_object(elf_object_t *object);
+void _dl_cleanup_objects(void);
+
+elf_object_t *_dl_lookup_object(const char *objname);
+elf_object_t *_dl_load_shlib(const char *, elf_object_t *, int, int);
+elf_object_t *_dl_tryload_shlib(const char *libname, int type, int flags);
+
+int _dl_md_reloc(elf_object_t *object, int rel, int relsz);
+int _dl_md_reloc_got(elf_object_t *object, int lazy);
+
+Elf_Addr _dl_find_symbol(const char *name, const Elf_Sym **this,
+ int flags, const Elf_Sym *ref_sym, elf_object_t *object,
+ const elf_object_t **pobj);
+Elf_Addr _dl_find_symbol_bysym(elf_object_t *req_obj, unsigned int symidx,
+ const Elf_Sym **ref, int flags, const Elf_Sym *ref_sym,
+ const elf_object_t **pobj);
+/*
+ * defines for _dl_find_symbol() flag field, three bits of meaning
+ * myself - clear: search all objects, set: search only this object
+ * warnnotfound - clear: no warning, set: warn if not found
+ * inplt - clear: possible plt ref set: real matching function.
+ *
+ * inplt - due to how ELF handles function addresses in shared libraries
+ * &func may actually refer to the plt entry in the main program
+ * rather than the actual function address in the .so file.
+ * This rather bizarre behavior is documented in the SVR4 ABI.
+ * when getting the function address to relocate a PLT entry
+ * the 'real' function address is necessary, not the possible PLT address.
+ */
+/* myself */
+#define SYM_SEARCH_ALL 0x00
+#define SYM_SEARCH_SELF 0x01
+#define SYM_SEARCH_OTHER 0x02
+#define SYM_SEARCH_NEXT 0x04
+#define SYM_SEARCH_OBJ 0x08
+/* warnnotfound */
+#define SYM_NOWARNNOTFOUND 0x00
+#define SYM_WARNNOTFOUND 0x10
+/* inplt */
+#define SYM_NOTPLT 0x00
+#define SYM_PLT 0x20
+
+#define SYM_DLSYM 0x40
+
+int _dl_load_dep_libs(elf_object_t *object, int flags, int booting);
+int _dl_rtld(elf_object_t *object);
+void _dl_call_init(elf_object_t *object);
+void _dl_link_child(elf_object_t *dep, elf_object_t *p);
+void _dl_link_grpsym(elf_object_t *object);
+void _dl_cache_grpsym_list(elf_object_t *object);
+void _dl_link_grpref(elf_object_t *load_group, elf_object_t *load_object);
+void _dl_link_dlopen(elf_object_t *dep);
+void _dl_unlink_dlopen(elf_object_t *dep);
+void _dl_notify_unload_shlib(elf_object_t *object);
+void _dl_unload_shlib(elf_object_t *object);
+void _dl_unload_dlopen(void);
+
+void _dl_run_all_dtors(void);
+
+/* Please don't rename; gdb(1) knows about this. */
+Elf_Addr _dl_bind(elf_object_t *object, int index);
+
+int _dl_match_file(struct sod *sodp, char *name, int namelen);
+char *_dl_find_shlib(struct sod *sodp, const char *searchpath, int nohints);
+void _dl_load_list_free(struct load_list *load_list);
+
+void _dl_thread_kern_go(void);
+void _dl_thread_kern_stop(void);
+
+void _dl_thread_bind_lock(int, sigset_t *);
+
+extern elf_object_t *_dl_objects;
+extern elf_object_t *_dl_last_object;
+
+extern elf_object_t *_dl_loading_object;
+
+extern const char *_dl_progname;
+extern struct r_debug *_dl_debug_map;
+
+extern int _dl_pagesz;
+extern int _dl_errno;
+
+extern char *_dl_libpath;
+extern char *_dl_preload;
+extern char *_dl_bindnow;
+extern char *_dl_traceld;
+extern char *_dl_tracefmt1;
+extern char *_dl_tracefmt2;
+extern char *_dl_traceprog;
+extern char *_dl_debug;
+
+#define DL_DEB(P) do { if (_dl_debug) _dl_printf P ; } while (0)
+
+#define DL_NOT_FOUND 1
+#define DL_CANT_OPEN 2
+#define DL_NOT_ELF 3
+#define DL_CANT_OPEN_REF 4
+#define DL_CANT_MMAP 5
+#define DL_NO_SYMBOL 6
+#define DL_INVALID_HANDLE 7
+#define DL_INVALID_CTL 8
+#define DL_NO_OBJECT 9
+#define DL_CANT_FIND_OBJ 10
+#define DL_CANT_LOAD_OBJ 11
+
+#define ELF_ROUND(x,malign) (((x) + (malign)-1) & ~((malign)-1))
+#define ELF_TRUNC(x,malign) ((x) & ~((malign)-1))
+
+/* symbol lookup cache */
+typedef struct sym_cache {
+ const elf_object_t *obj;
+ const Elf_Sym *sym;
+ int flags;
+} sym_cache;
+
+extern sym_cache *_dl_symcache;
+extern int _dl_symcachestat_hits;
+extern int _dl_symcachestat_lookups;
+TAILQ_HEAD(dlochld, dep_node);
+extern struct dlochld _dlopened_child_list;
+
+
+#endif /* _RESOLVE_H_ */
--- /dev/null
+/Makefile.inc/1.4/Thu Apr 3 00:13:33 2008//
+/ldasm.S/1.7/Tue Nov 14 19:47:50 2006//
+/syscall.h/1.2/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.2/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.14/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/sh
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.4 2008/04/03 00:13:33 drahn Exp $
+
+CFLAGS += -fpic
+CFLAGS+=-m4-nofpu
+AFLAGS += -D_STANDALONE
+AFLAGS += -I${.CURDIR}/../../lib/libc/arch/sh
+ELF_LDFLAGS+=-z nocombreloc
+LIBCSRCDIR=${.CURDIR}/../../lib/libc
+LDADD= `$(CC) -print-libgcc-file-name`
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.2 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SH_ARCHDEP_H_
+#define _SH_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 4 /* Arch constraint or otherwise */
+
+#define MACHID EM_SH /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <elf_abi.h>
+#include <machine/reloc.h>
+#include "syscall.h"
+#include "util.h"
+
+/* HACK */
+#define DT_PROCNUM 0
+#ifndef DT_BIND_NOW
+#define DT_BIND_NOW 0
+#endif
+
+#define RTLD_NO_WXORX
+
+/*
+ * The following functions are declared inline so they can
+ * be used before bootstrap linking has been finished.
+ */
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)(long)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ if (ELF_R_TYPE(r->r_info) == R_SH_RELATIVE) {
+ *p += v;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+static inline void
+RELOC_RELA(Elf32_Rela *r, const Elf32_Sym *s, Elf32_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF_R_TYPE(r->r_info) == R_SH_RELATIVE) {
+ *p = v + r->r_addend;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS (PROT_READ|PROT_EXEC)
+
+#endif /* _SH_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.7 2006/11/14 19:47:50 drahn Exp $ */
+
+/*
+ * Copyright (c) 2006 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define DL_DATA_SIZE (16 * 4) /* XXX */
+#include <machine/asm.h>
+#include <sys/syscall.h>
+#include <SYS.h>
+
+ENTRY(_dl_start)
+ mov r15, r12 // save for later
+ sts pr, r11
+ mov r15, r4 // boot_bind(sp, dl_data) (sp)
+ mov.l .L_datasize, r0
+ sub r0, r15
+ mov r15, r5
+ mov r5, r13
+ // not trusting register to store the data, push it on the stack.
+ // callee/caller save questions
+
+ mov r15, r14
+
+ bsr 1f
+ nop
+1:
+.L_offbase:
+ sts pr, r0
+ mov.l .L_dynamic, r6
+ add r0, r6
+ mov r14, r15
+ mov r15, r14
+ mov.l .L_boot_bind, r0
+ bsrf r0
+ nop
+.L_call_boot_bind:
+ mov r12, r4
+ add #4, r4
+ mov.l @r12, r5 //loads argc
+ add #2, r5
+ shll2 r5
+ add r12, r5 // calc argv
+
+ mov r13, r7
+ mov r7, r6
+ mov.l .L_loff, r0
+ add r0, r6
+ mov.l @r6, r6
+
+ mov.l .L_boot, r0
+ bsrf r0
+ nop
+.L_call_boot:
+
+ mov r12, r15
+ lds r11, pr
+ mov.l @r15, r4
+ mov r15, r5
+ add #4, r5
+
+ mov r4, r6
+ add #1, r6
+ shll2 r6
+ add r5, r6 // calc envp
+ jmp @r0
+ nop
+
+ .align 2
+.L_boot_bind:
+ .long _dl_boot_bind-.L_call_boot_bind
+.L_boot:
+ .long _dl_boot-.L_call_boot
+.L_datasize:
+ .long 4+4+DL_DATA_SIZE
+.L_dynamic:
+ .long _DYNAMIC-.L_offbase
+.L_loff:
+ .long 7*4
+ .size _dl_start, .-dl_start
+
+
+/*
+ * r0 - obj
+ * r1 - reloff
+ */
+
+ENTRY(_dl_bind_start)
+ mov.l r2, @-r15
+ mov.l r3, @-r15
+ mov.l r4, @-r15
+ mov.l r5, @-r15
+ mov.l r6, @-r15
+ mov.l r7, @-r15
+ sts.l pr, @-r15
+ sts.l macl, @-r15
+ sts.l mach, @-r15
+
+ mov r0, r4 /* move obj to 'C' arg */
+ mov.l .L_dl_bind, r0
+ bsrf r0
+ mov r1, r5 /* move reloff to 'C' arg */
+.L_call_dl_bind:
+
+ lds.l @r15+, mach
+ lds.l @r15+, macl
+ lds.l @r15+, pr
+ mov.l @r15+, r7
+ mov.l @r15+, r6
+ mov.l @r15+, r5
+ mov.l @r15+, r4
+ mov.l @r15+, r3
+ jmp @r0 /* jump to specified address */
+ mov.l @r15+, r2
+
+ .align 2
+.L_dl_bind:
+ .long _dl_bind-.L_call_dl_bind
+ .size _dl_bind_start, .-dl_bind_start
+
+
+ /* STUB */
+
+
+/* ld.so SYSCALLS */
+
+#define DL_SYSCALL(n) DL_SYSCALL2(n,n)
+#define DL_SYSCALL2(n,c) \
+ .global __CONCAT(_dl_,n) ;\
+ .type __CONCAT(_dl_,n)%function ;\
+__CONCAT(_dl_,n): ;\
+ SYSTRAP(c) ;\
+ bf .L_cerr ;\
+ nop ;\
+ rts ;\
+ nop
+
+#define DL_SYSCALL2_NOERR(n,c) \
+ .global __CONCAT(_dl_,n) ;\
+ .type __CONCAT(_dl_,n)%function ;\
+__CONCAT(_dl_,n): ;\
+ SYSTRAP(c) ;\
+ rts ;\
+ nop
+
+
+ .section ".text"
+ .align 4
+DL_SYSCALL(close)
+
+
+ .global _dl_exit
+ .type _dl_exit%function
+_dl_exit:
+ SYSTRAP(exit)
+1:
+ bra 1b
+ nop
+
+DL_SYSCALL(issetugid)
+DL_SYSCALL2(_syscall,__syscall)
+DL_SYSCALL(munmap)
+DL_SYSCALL(mprotect)
+DL_SYSCALL(open)
+DL_SYSCALL(read)
+
+.L_cerr:
+ mov #-1, r0
+ rts
+ nop
+
+DL_SYSCALL(write)
+DL_SYSCALL(stat)
+DL_SYSCALL(fstat)
+DL_SYSCALL(fcntl)
+DL_SYSCALL(gettimeofday)
+DL_SYSCALL2(sysctl,__sysctl)
+
+DL_SYSCALL(getdirentries)
+
+ .global _dl_sigprocmask
+ .type _dl_sigprocmask%function
+_dl_sigprocmask:
+ mov r5, r2 /* fetch new sigset pointer */
+ tst r2, r2 /* check new sigset pointer */
+ bf 1f /* if not null, indirect */
+ mov #1, r4 /* SIG_BLOCK */
+ bra 2f
+ nop
+1: mov.l @r2, r2 /* fetch indirect ... */
+ mov r2, r5 /* to new mask arg */
+2: mov.l LSYS_sigprocmask, r0
+ trapa #0x80
+ bf .L_cerr
+ mov r6, r2 /* fetch old mask requested */
+ tst r2, r2 /* test if old mask requested */
+ bt out
+ mov.l r0, @r2 /* store old mask */
+out:
+ xor r0, r0
+ rts
+ nop
+
+ .align 2
+LSYS_sigprocmask:
+ .long SYS_sigprocmask
+
+
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.14 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 2004 Dale Rahn
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#define _DYN_LOADER
+#define LDSO_ARCH_IS_RELA_
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+void _dl_bind_start(void); /* XXX */
+Elf_Addr _dl_bind(elf_object_t *object, int reloff);
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_E 0x02000000 /* ERROR */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* 0 R_SH_NONE */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 1 R_SH_DIR32 */
+ _RF_S|_RF_P|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 2 REL32 */
+ _RF_E, /* 3 R_SH_DIR8WPN */
+ _RF_E, /* 4 R_SH_IND12W */
+ _RF_E, /* 5 R_SH_DIR8WPL */
+ _RF_E, /* 6 R_SH_DIR8WPZ */
+ _RF_E, /* 7 R_SH_DIR8BP */
+ _RF_E, /* 8 R_SH_DIR8W */
+ _RF_E, /* 9 R_SH_DIR8L */
+ _RF_E, /* 10 R_SH_LOOP_START */
+ _RF_E, /* 11 R_SH_LOOP_END */
+ _RF_E, /* 12 Unused */
+ _RF_E, /* 13 Unused */
+ _RF_E, /* 14 Unused */
+ _RF_E, /* 15 Unused */
+ _RF_E, /* 16 Unused */
+ _RF_E, /* 17 Unused */
+ _RF_E, /* 18 Unused */
+ _RF_E, /* 19 Unused */
+ _RF_E, /* 20 Unused */
+ _RF_E, /* 21 Unused */
+ _RF_E, /* 22 R_SH_GNU_VTINHERIT */
+ _RF_E, /* 23 R_SH_GNU_VTENTRY */
+ _RF_E, /* 24 R_SH_SWITCH8 */
+ _RF_E, /* 25 R_SH_SWITCH16 */
+ _RF_E, /* 26 R_SH_SWITCH32 */
+ _RF_E, /* 27 R_SH_USES */
+ _RF_E, /* 28 R_SH_COUNT */
+ _RF_E, /* 29 R_SH_ALIGN */
+ _RF_E, /* 30 R_SH_CODE */
+ _RF_E, /* 31 R_SH_DATA */
+ _RF_E, /* 32 R_SH_LABEL */
+ _RF_E, /* 33 R_SH_DIR16 */
+ _RF_E, /* 34 R_SH_DIR8 */
+ _RF_E, /* 35 R_SH_DIR8UL */
+ _RF_E, /* 36 R_SH_DIR8UW */
+ _RF_E, /* 37 R_SH_DIR8U */
+ _RF_E, /* 38 R_SH_DIR8SW */
+ _RF_E, /* 39 R_SH_DIR8S */
+ _RF_E, /* 40 R_SH_DIR4UL */
+ _RF_E, /* 41 R_SH_DIR4UW */
+ _RF_E, /* 42 R_SH_DIR4U */
+ _RF_E, /* 43 R_SH_PSHA */
+ _RF_E, /* 44 R_SH_PSHL */
+ _RF_E, /* 45 R_SH_DIR5U */
+ _RF_E, /* 46 R_SH_DIR6U */
+ _RF_E, /* 47 R_SH_DIR6S */
+ _RF_E, /* 48 R_SH_DIR10S */
+ _RF_E, /* 49 R_SH_DIR10SW */
+ _RF_E, /* 50 R_SH_DIR10SL */
+ _RF_E, /* 51 R_SH_DIR10SQ */
+ _RF_E, /* 52 XXXX */
+ _RF_E, /* 53 R_SH_DIR16S */
+ _RF_E, /* 54 Unused */
+ _RF_E, /* 55 Unused */
+ _RF_E, /* 56 Unused */
+ _RF_E, /* 57 Unused */
+ _RF_E, /* 58 Unused */
+ _RF_E, /* 59 Unused */
+ _RF_E, /* 60 Unused */
+ _RF_E, /* 61 Unused */
+ _RF_E, /* 62 Unused */
+ _RF_E, /* 63 Unused */
+ _RF_E, /* 64 Unused */
+ _RF_E, /* 65 Unused */
+ _RF_E, /* 66 Unused */
+ _RF_E, /* 67 Unused */
+ _RF_E, /* 68 Unused */
+ _RF_E, /* 69 Unused */
+ _RF_E, /* 70 Unused */
+ _RF_E, /* 71 Unused */
+ _RF_E, /* 72 Unused */
+ _RF_E, /* 73 Unused */
+ _RF_E, /* 74 Unused */
+ _RF_E, /* 75 Unused */
+ _RF_E, /* 76 Unused */
+ _RF_E, /* 77 Unused */
+ _RF_E, /* 78 Unused */
+ _RF_E, /* 79 Unused */
+ _RF_E, /* 80 Unused */
+ _RF_E, /* 81 Unused */
+ _RF_E, /* 82 Unused */
+ _RF_E, /* 83 Unused */
+ _RF_E, /* 84 Unused */
+ _RF_E, /* 85 Unused */
+ _RF_E, /* 86 Unused */
+ _RF_E, /* 87 Unused */
+ _RF_E, /* 88 Unused */
+ _RF_E, /* 89 Unused */
+ _RF_E, /* 90 Unused */
+ _RF_E, /* 91 Unused */
+ _RF_E, /* 92 Unused */
+ _RF_E, /* 93 Unused */
+ _RF_E, /* 94 Unused */
+ _RF_E, /* 95 Unused */
+ _RF_E, /* 96 Unused */
+ _RF_E, /* 97 Unused */
+ _RF_E, /* 98 Unused */
+ _RF_E, /* 99 Unused */
+ _RF_E, /* 100 Unused */
+ _RF_E, /* 101 Unused */
+ _RF_E, /* 102 Unused */
+ _RF_E, /* 103 Unused */
+ _RF_E, /* 104 Unused */
+ _RF_E, /* 105 Unused */
+ _RF_E, /* 106 Unused */
+ _RF_E, /* 107 Unused */
+ _RF_E, /* 108 Unused */
+ _RF_E, /* 109 Unused */
+ _RF_E, /* 110 Unused */
+ _RF_E, /* 111 Unused */
+ _RF_E, /* 112 Unused */
+ _RF_E, /* 113 Unused */
+ _RF_E, /* 114 Unused */
+ _RF_E, /* 115 Unused */
+ _RF_E, /* 116 Unused */
+ _RF_E, /* 117 Unused */
+ _RF_E, /* 118 Unused */
+ _RF_E, /* 119 Unused */
+ _RF_E, /* 120 Unused */
+ _RF_E, /* 121 Unused */
+ _RF_E, /* 122 Unused */
+ _RF_E, /* 123 Unused */
+ _RF_E, /* 124 Unused */
+ _RF_E, /* 125 Unused */
+ _RF_E, /* 126 Unused */
+ _RF_E, /* 127 Unused */
+ _RF_E, /* 128 Unused */
+ _RF_E, /* 129 Unused */
+ _RF_E, /* 130 Unused */
+ _RF_E, /* 131 Unused */
+ _RF_E, /* 132 Unused */
+ _RF_E, /* 133 Unused */
+ _RF_E, /* 134 Unused */
+ _RF_E, /* 135 Unused */
+ _RF_E, /* 136 Unused */
+ _RF_E, /* 137 Unused */
+ _RF_E, /* 138 Unused */
+ _RF_E, /* 139 Unused */
+ _RF_E, /* 140 Unused */
+ _RF_E, /* 141 Unused */
+ _RF_E, /* 142 Unused */
+ _RF_E, /* 143 Unused */
+ _RF_E, /* 144 R_SH_TLS_GD_32 */
+ _RF_E, /* 145 R_SH_TLS_LD_32 */
+ _RF_E, /* 146 R_SH_TLS_LDO_32 */
+ _RF_E, /* 147 R_SH_TLS_IE_32 */
+ _RF_E, /* 148 R_SH_TLS_LE_32 */
+ _RF_E, /* 149 R_SH_TLS_DTPMOD32 */
+ _RF_E, /* 150 R_SH_TLS_DTPOFF32 */
+ _RF_E, /* 151 R_SH_TLS_TPOFF32 */
+ _RF_E, /* 152 Unused */
+ _RF_E, /* 153 Unused */
+ _RF_E, /* 154 Unused */
+ _RF_E, /* 155 Unused */
+ _RF_E, /* 156 Unused */
+ _RF_E, /* 157 Unused */
+ _RF_E, /* 158 Unused */
+ _RF_E, /* 159 Unused */
+ _RF_E, /* 160 R_SH_GOT32 */
+ _RF_E, /* 161 R_SH_PLT32 */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* 162 COPY */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 163 GLOB_DAT */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* 164 JMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0), /* 165 RELATIVE */
+ _RF_E, /* 166 R_SH_GOTOFF */
+ _RF_E, /* 167 R_SH_GOTPC */
+ _RF_E, /* 168 R_SH_GOTPLT32 */
+ _RF_E, /* 169 R_SH_GOT_LOW16 */
+ _RF_E, /* 170 R_SH_GOT_MEDLOW16 */
+ _RF_E, /* 171 R_SH_GOT_MEDHI16 */
+ _RF_E, /* 172 R_SH_GOT_HI16 */
+ _RF_E, /* 173 R_SH_GOTPLT_LOW16 */
+ _RF_E, /* 174 R_SH_GOTPLT_MEDLOW16 */
+ _RF_E, /* 175 R_SH_GOTPLT_MEDHI16 */
+ _RF_E, /* 176 R_SH_GOTPLT_HI16 */
+ _RF_E, /* 177 R_SH_PLT_LOW16 */
+ _RF_E, /* 178 R_SH_PLT_MEDLOW16 */
+ _RF_E, /* 179 R_SH_PLT_MEDHI16 */
+ _RF_E, /* 180 R_SH_PLT_HI16 */
+ _RF_E, /* 181 R_SH_GOTOFF_LOW16 */
+ _RF_E, /* 182 R_SH_GOTOFF_MEDLOW16 */
+ _RF_E, /* 183 R_SH_GOTOFF_MEDHI16 */
+ _RF_E, /* 184 R_SH_GOTOFF_HI16 */
+ _RF_E, /* 185 R_SH_GOTPC_LOW16 */
+ _RF_E, /* 186 R_SH_GOTPC_MEDLOW16 */
+ _RF_E, /* 187 R_SH_GOTPC_MEDHI16 */
+ _RF_E, /* 188 R_SH_GOTPC_HI16 */
+ _RF_E, /* 189 R_SH_GOT10BY4 */
+ _RF_E, /* 190 R_SH_GOTPLT10BY4 */
+ _RF_E, /* 191 R_SH_GOT10BY8 */
+ _RF_E, /* 192 R_SH_GOTPLT10BY8 */
+#ifdef SH_SUPPORT_64_BIT
+ _RF_E, /* 193 R_SH_COPY64 */
+ _RF_E, /* 194 R_SH_GLOB_DAT64 */
+ _RF_E, /* 195 R_SH_JMP_SLOT64 */
+ _RF_E, /* 196 R_SH_RELATIVE64 */
+ _RF_E, /* 197 Unused */
+ _RF_E, /* 198 Unused */
+ _RF_E, /* 199 Unused */
+ _RF_E, /* 200 Unused */
+ _RF_E, /* 201 Unused */
+ _RF_E, /* 202 Unused */
+ _RF_E, /* 203 Unused */
+ _RF_E, /* 204 Unused */
+ _RF_E, /* 205 Unused */
+ _RF_E, /* 206 Unused */
+ _RF_E, /* 207 Unused */
+ _RF_E, /* 208 Unused */
+ _RF_E, /* 209 Unused */
+ _RF_E, /* 210 Unused */
+ _RF_E, /* 211 Unused */
+ _RF_E, /* 212 Unused */
+ _RF_E, /* 213 Unused */
+ _RF_E, /* 214 Unused */
+ _RF_E, /* 215 Unused */
+ _RF_E, /* 216 Unused */
+ _RF_E, /* 217 Unused */
+ _RF_E, /* 218 Unused */
+ _RF_E, /* 219 Unused */
+ _RF_E, /* 220 Unused */
+ _RF_E, /* 221 Unused */
+ _RF_E, /* 222 Unused */
+ _RF_E, /* 223 Unused */
+ _RF_E, /* 224 Unused */
+ _RF_E, /* 225 Unused */
+ _RF_E, /* 226 Unused */
+ _RF_E, /* 227 Unused */
+ _RF_E, /* 228 Unused */
+ _RF_E, /* 229 Unused */
+ _RF_E, /* 230 Unused */
+ _RF_E, /* 231 Unused */
+ _RF_E, /* 232 Unused */
+ _RF_E, /* 233 Unused */
+ _RF_E, /* 234 Unused */
+ _RF_E, /* 235 Unused */
+ _RF_E, /* 236 Unused */
+ _RF_E, /* 237 Unused */
+ _RF_E, /* 238 Unused */
+ _RF_E, /* 239 Unused */
+ _RF_E, /* 240 Unused */
+ _RF_E, /* 241 Unused */
+ _RF_E, /* 242 R_SH_SHMEDIA_CODE */
+ _RF_E, /* 243 R_SH_PT_16 */
+ _RF_E, /* 244 R_SH_IMMS16 */
+ _RF_E, /* 245 R_SH_IMMU16 */
+ _RF_E, /* 246 R_SH_IMM_LOW16 */
+ _RF_E, /* 247 R_SH_IMM_LOW16_PCREL */
+ _RF_E, /* 248 R_SH_IMM_MEDLOW16 */
+ _RF_E, /* 249 R_SH_IMM_MEDLOW16_PCREL */
+ _RF_E, /* 250 R_SH_IMM_MEDHI16 */
+ _RF_E, /* 251 R_SH_IMM_MEDHI16_PCREL */
+ _RF_E, /* 252 R_SH_IMM_HI16 */
+ _RF_E, /* 253 R_SH_IMM_HI16_PCREL */
+ _RF_E, /* 254 R_SH_64 */
+ _RF_E, /* 255 R_SH_64_PCREL */
+ 0
+#endif
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+static int reloc_target_bitmask[] = {
+#define _BM(x) (x == 32? ~0 : ~(-(1UL << (x))))
+ _BM(0), /* 0 R_SH_NONE */
+ _BM(32), /* 1 R_SH_DIR32 */
+ _BM(32), /* 2 R_SH_REL32 */
+ _BM(8), /* 3 R_SH_DIR8WPN */
+ _BM(12), /* 4 R_SH_IND12W */
+ _BM(8), /* 5 R_SH_DIR8WPL */
+ _BM(8), /* 6 R_SH_DIR8WPZ */
+ _BM(8), /* 7 R_SH_DIR8BP */
+ _BM(8), /* 8 R_SH_DIR8W */
+ _BM(8), /* 9 R_SH_DIR8L */
+ _BM(0), /* 10 R_SH_LOOP_START */
+ _BM(0), /* 11 R_SH_LOOP_END */
+ _BM(0), /* 12 Unused */
+ _BM(0), /* 13 Unused */
+ _BM(0), /* 14 Unused */
+ _BM(0), /* 15 Unused */
+ _BM(0), /* 16 Unused */
+ _BM(0), /* 17 Unused */
+ _BM(0), /* 18 Unused */
+ _BM(0), /* 19 Unused */
+ _BM(0), /* 20 Unused */
+ _BM(0), /* 21 Unused */
+ _BM(0), /* 22 R_SH_GNU_VTINHERIT */
+ _BM(0), /* 23 R_SH_GNU_VTENTRY */
+ _BM(0), /* 24 R_SH_SWITCH8 */
+ _BM(0), /* 25 R_SH_SWITCH16 */
+ _BM(0), /* 26 R_SH_SWITCH32 */
+ _BM(0), /* 27 R_SH_USES */
+ _BM(0), /* 28 R_SH_COUNT */
+ _BM(0), /* 29 R_SH_ALIGN */
+ _BM(0), /* 30 R_SH_CODE */
+ _BM(0), /* 31 R_SH_DATA */
+ _BM(0), /* 32 R_SH_LABEL */
+ _BM(0), /* 33 R_SH_DIR16 */
+ _BM(0), /* 34 R_SH_DIR8 */
+ _BM(0), /* 35 R_SH_DIR8UL */
+ _BM(0), /* 36 R_SH_DIR8UW */
+ _BM(0), /* 37 R_SH_DIR8U */
+ _BM(0), /* 38 R_SH_DIR8SW */
+ _BM(0), /* 39 R_SH_DIR8S */
+ _BM(0), /* 40 R_SH_DIR4UL */
+ _BM(0), /* 41 R_SH_DIR4UW */
+ _BM(0), /* 42 R_SH_DIR4U */
+ _BM(0), /* 43 R_SH_PSHA */
+ _BM(0), /* 44 R_SH_PSHL */
+ _BM(0), /* 45 R_SH_DIR5U */
+ _BM(0), /* 46 R_SH_DIR6U */
+ _BM(0), /* 47 R_SH_DIR6S */
+ _BM(0), /* 48 R_SH_DIR10S */
+ _BM(0), /* 49 R_SH_DIR10SW */
+ _BM(0), /* 50 R_SH_DIR10SL */
+ _BM(0), /* 51 R_SH_DIR10SQ */
+ _BM(0), /* 52 xxx */
+ _BM(0), /* 53 R_SH_DIR16S */
+ _BM(0), /* 54 Unused */
+ _BM(0), /* 55 Unused */
+ _BM(0), /* 56 Unused */
+ _BM(0), /* 57 Unused */
+ _BM(0), /* 58 Unused */
+ _BM(0), /* 59 Unused */
+ _BM(0), /* 60 Unused */
+ _BM(0), /* 61 Unused */
+ _BM(0), /* 62 Unused */
+ _BM(0), /* 63 Unused */
+ _BM(0), /* 64 Unused */
+ _BM(0), /* 65 Unused */
+ _BM(0), /* 66 Unused */
+ _BM(0), /* 67 Unused */
+ _BM(0), /* 68 Unused */
+ _BM(0), /* 69 Unused */
+ _BM(0), /* 70 Unused */
+ _BM(0), /* 71 Unused */
+ _BM(0), /* 72 Unused */
+ _BM(0), /* 73 Unused */
+ _BM(0), /* 74 Unused */
+ _BM(0), /* 75 Unused */
+ _BM(0), /* 76 Unused */
+ _BM(0), /* 77 Unused */
+ _BM(0), /* 78 Unused */
+ _BM(0), /* 79 Unused */
+ _BM(0), /* 80 Unused */
+ _BM(0), /* 81 Unused */
+ _BM(0), /* 82 Unused */
+ _BM(0), /* 83 Unused */
+ _BM(0), /* 84 Unused */
+ _BM(0), /* 85 Unused */
+ _BM(0), /* 86 Unused */
+ _BM(0), /* 87 Unused */
+ _BM(0), /* 88 Unused */
+ _BM(0), /* 89 Unused */
+ _BM(0), /* 90 Unused */
+ _BM(0), /* 91 Unused */
+ _BM(0), /* 92 Unused */
+ _BM(0), /* 93 Unused */
+ _BM(0), /* 94 Unused */
+ _BM(0), /* 95 Unused */
+ _BM(0), /* 96 Unused */
+ _BM(0), /* 97 Unused */
+ _BM(0), /* 98 Unused */
+ _BM(0), /* 99 Unused */
+ _BM(0), /* 100 Unused */
+ _BM(0), /* 101 Unused */
+ _BM(0), /* 102 Unused */
+ _BM(0), /* 103 Unused */
+ _BM(0), /* 104 Unused */
+ _BM(0), /* 105 Unused */
+ _BM(0), /* 106 Unused */
+ _BM(0), /* 107 Unused */
+ _BM(0), /* 108 Unused */
+ _BM(0), /* 109 Unused */
+ _BM(0), /* 110 Unused */
+ _BM(0), /* 111 Unused */
+ _BM(0), /* 112 Unused */
+ _BM(0), /* 113 Unused */
+ _BM(0), /* 114 Unused */
+ _BM(0), /* 115 Unused */
+ _BM(0), /* 116 Unused */
+ _BM(0), /* 117 Unused */
+ _BM(0), /* 118 Unused */
+ _BM(0), /* 119 Unused */
+ _BM(0), /* 120 Unused */
+ _BM(0), /* 121 Unused */
+ _BM(0), /* 122 Unused */
+ _BM(0), /* 123 Unused */
+ _BM(0), /* 124 Unused */
+ _BM(0), /* 125 Unused */
+ _BM(0), /* 126 Unused */
+ _BM(0), /* 127 Unused */
+ _BM(0), /* 128 Unused */
+ _BM(0), /* 129 Unused */
+ _BM(0), /* 130 Unused */
+ _BM(0), /* 131 Unused */
+ _BM(0), /* 132 Unused */
+ _BM(0), /* 133 Unused */
+ _BM(0), /* 134 Unused */
+ _BM(0), /* 135 Unused */
+ _BM(0), /* 136 Unused */
+ _BM(0), /* 137 Unused */
+ _BM(0), /* 138 Unused */
+ _BM(0), /* 139 Unused */
+ _BM(0), /* 140 Unused */
+ _BM(0), /* 141 Unused */
+ _BM(0), /* 142 Unused */
+ _BM(0), /* 143 Unused */
+ _BM(0), /* 144 R_SH_TLS_GD_32 */
+ _BM(0), /* 145 R_SH_TLS_LD_32 */
+ _BM(0), /* 146 R_SH_TLS_LDO_32 */
+ _BM(0), /* 147 R_SH_TLS_IE_32 */
+ _BM(0), /* 148 R_SH_TLS_LE_32 */
+ _BM(0), /* 149 R_SH_TLS_DTPMOD32 */
+ _BM(0), /* 150 R_SH_TLS_DTPOFF32 */
+ _BM(0), /* 151 R_SH_TLS_TPOFF32 */
+ _BM(0), /* 152 xxx */
+ _BM(0), /* 153 xxx */
+ _BM(0), /* 154 xxx */
+ _BM(0), /* 155 xxx */
+ _BM(0), /* 156 xxx */
+ _BM(0), /* 157 xxx */
+ _BM(0), /* 158 xxx */
+ _BM(0), /* 159 xxx */
+ _BM(0), /* 160 R_SH_GOT32 */
+ _BM(0), /* 161 R_SH_PLT32 */
+ _BM(0), /* 162 R_SH_COPY */
+ _BM(32), /* 163 R_SH_GLOB_DAT */
+ _BM(0), /* 164 R_SH_JMP_SLOT */
+ _BM(32), /* 165 R_SH_RELATIVE */
+ _BM(0), /* 166 R_SH_GOTOFF */
+ _BM(0), /* 167 R_SH_GOTPC */
+ _BM(0), /* 168 R_SH_GOTPLT32 */
+ _BM(0), /* 169 R_SH_GOT_LOW16 */
+ _BM(0), /* 170 R_SH_GOT_MEDLOW16 */
+ _BM(0), /* 171 R_SH_GOT_MEDHI16 */
+ _BM(0), /* 172 R_SH_GOT_HI16 */
+ _BM(0), /* 173 R_SH_GOTPLT_LOW16 */
+ _BM(0), /* 174 R_SH_GOTPLT_MEDLOW16 */
+ _BM(0), /* 175 R_SH_GOTPLT_MEDHI16 */
+ _BM(0), /* 176 R_SH_GOTPLT_HI16 */
+ _BM(0), /* 177 R_SH_PLT_LOW16 */
+ _BM(0), /* 178 R_SH_PLT_MEDLOW16 */
+ _BM(0), /* 179 R_SH_PLT_MEDHI16 */
+ _BM(0), /* 180 R_SH_PLT_HI16 */
+ _BM(0), /* 181 R_SH_GOTOFF_LOW16 */
+ _BM(0), /* 182 R_SH_GOTOFF_MEDLOW16 */
+ _BM(0), /* 183 R_SH_GOTOFF_MEDHI16 */
+ _BM(0), /* 184 R_SH_GOTOFF_HI16 */
+ _BM(0), /* 185 R_SH_GOTPC_LOW16 */
+ _BM(0), /* 186 R_SH_GOTPC_MEDLOW16 */
+ _BM(0), /* 187 R_SH_GOTPC_MEDHI16 */
+ _BM(0), /* 188 R_SH_GOTPC_HI16 */
+ _BM(0), /* 189 R_SH_GOT10BY4 */
+ _BM(0), /* 190 R_SH_GOTPLT10BY4 */
+ _BM(0), /* 191 R_SH_GOT10BY8 */
+ _BM(0), /* 192 R_SH_GOTPLT10BY8 */
+#ifdef SH_SUPPORT_64_BIT
+ _BM(0), /* 193 R_SH_COPY64 */
+ _BM(0), /* 194 R_SH_GLOB_DAT64 */
+ _BM(0), /* 195 R_SH_JMP_SLOT64 */
+ _BM(0), /* 196 R_SH_RELATIVE64 */
+ _BM(0), /* 197 xxx */
+ _BM(0), /* 198 xxx */
+ _BM(0), /* 199 xxx */
+ _BM(0), /* 200 xxx */
+ _BM(0), /* 201 xxx */
+ _BM(0), /* 202 xxx */
+ _BM(0), /* 203 xxx */
+ _BM(0), /* 204 xxx */
+ _BM(0), /* 205 xxx */
+ _BM(0), /* 206 xxx */
+ _BM(0), /* 207 xxx */
+ _BM(0), /* 208 xxx */
+ _BM(0), /* 209 xxx */
+ _BM(0), /* 210 xxx */
+ _BM(0), /* 211 xxx */
+ _BM(0), /* 212 xxx */
+ _BM(0), /* 213 xxx */
+ _BM(0), /* 214 xxx */
+ _BM(0), /* 215 xxx */
+ _BM(0), /* 216 xxx */
+ _BM(0), /* 217 xxx */
+ _BM(0), /* 218 xxx */
+ _BM(0), /* 219 xxx */
+ _BM(0), /* 220 xxx */
+ _BM(0), /* 221 xxx */
+ _BM(0), /* 222 xxx */
+ _BM(0), /* 223 xxx */
+ _BM(0), /* 224 xxx */
+ _BM(0), /* 225 xxx */
+ _BM(0), /* 226 xxx */
+ _BM(0), /* 227 xxx */
+ _BM(0), /* 228 xxx */
+ _BM(0), /* 229 xxx */
+ _BM(0), /* 230 xxx */
+ _BM(0), /* 231 xxx */
+ _BM(0), /* 232 xxx */
+ _BM(0), /* 233 xxx */
+ _BM(0), /* 234 xxx */
+ _BM(0), /* 235 xxx */
+ _BM(0), /* 236 xxx */
+ _BM(0), /* 237 xxx */
+ _BM(0), /* 238 xxx */
+ _BM(0), /* 239 xxx */
+ _BM(0), /* 240 xxx */
+ _BM(0), /* 241 xxx */
+ _BM(0), /* 242 R_SH_SHMEDIA_CODE */
+ _BM(0), /* 243 R_SH_PT_16 */
+ _BM(0), /* 244 R_SH_IMMS16 */
+ _BM(0), /* 245 R_SH_IMMU16 */
+ _BM(0), /* 246 R_SH_IMM_LOW16 */
+ _BM(0), /* 247 R_SH_IMM_LOW16_PCREL */
+ _BM(0), /* 248 R_SH_IMM_MEDLOW16 */
+ _BM(0), /* 249 R_SH_IMM_MEDLOW16_PCREL */
+ _BM(0), /* 250 R_SH_IMM_MEDHI16 */
+ _BM(0), /* 251 R_SH_IMM_MEDHI16_PCREL */
+ _BM(0), /* 252 R_SH_IMM_HI16 */
+ _BM(0), /* 253 R_SH_IMM_HI16_PCREL */
+ _BM(0), /* 254 R_SH_64 */
+ _BM(0), /* 255 R_SH_64_PCREL */
+#endif
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+#define R_TYPE(x) R_SH_ ## x
+
+void _dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_RelA *rel);
+
+void
+_dl_reloc_plt(Elf_Word *where, Elf_Addr value, Elf_RelA *rel)
+{
+ *where = value + rel->r_addend;
+}
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ long i;
+ long numrela;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_RelA *rels;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
+ rels = (Elf_RelA *)(object->Dyn.info[rel]);
+
+ if (rels == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list;
+ llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrela; i++, rels++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(rels->r_info);
+
+ if (reloc_target_flags[type] & _RF_E) {
+ _dl_printf(" bad relocation obj %s %d %d\n", object->load_name, i, type);
+ _dl_exit(1);
+ }
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(rels->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+#ifdef LDSO_ARCH_IS_RELA_
+ value = rels->r_addend;
+#else
+ value = *where & RELOC_VALUE_BITMASK(type);
+#endif
+ else
+ value = 0;
+
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rels->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+#if 1
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rels->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JMP_SLOT)) ?
+ SYM_PLT : SYM_NOTPLT),
+ sym, NULL);
+#else
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(rels->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ SYM_PLT,
+ sym, NULL);
+#endif
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(JMP_SLOT)) {
+ _dl_reloc_plt((Elf_Word *)where, value, rels);
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ dstsym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, dstsym->st_size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp = 0;
+ char *ptr = (char *)where;
+ int i, size = RELOC_TARGET_SIZE(type)/8;
+
+ /* Read it in one byte at a time. */
+ for (i=0; i<size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i=0; i<size; i++)
+ ptr[i] = ((tmp >> (8*i)) & 0xff);
+ } else {
+ *where &= ~mask;
+ *where |= value;
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list;
+ llist != NULL;
+ llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+/*
+ * Relocate the Global Offset Table (GOT).
+ * This is done by calling _dl_md_reloc on DT_JUMPREL for DL_BIND_NOW,
+ * otherwise the lazy binding plt initialization is performed.
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int fails = 0;
+ Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+ int i, num;
+ Elf_Rel *rel;
+
+ /* XXX - lazy binding not supported yet */
+ lazy = 0;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_RELA)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ object->plt_size = 0; /* Text PLT on ARM */
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ object->plt_start = NULL;
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ rel = (Elf_Rel *)(object->Dyn.info[DT_JMPREL]);
+ num = (object->Dyn.info[DT_PLTRELSZ]);
+
+ for (i = 0; i < num/sizeof(Elf_Rel); i++, rel++) {
+ Elf_Addr *where;
+ where = (Elf_Addr *)(rel->r_offset + object->obj_base);
+ *where += object->obj_base;
+ }
+
+ pltgot[1] = (Elf_Addr)object;
+ pltgot[2] = (Elf_Addr)_dl_bind_start;
+ }
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
+
+Elf_Addr
+_dl_bind(elf_object_t *object, int relidx)
+{
+ Elf_Rel *rel;
+ Elf_Word *addr;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ Elf_Addr ooff, newval;
+ sigset_t savedmask;
+
+ rel = ((Elf_Rel *)object->Dyn.info[DT_JMPREL]) + (relidx);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rel->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ addr = (Elf_Addr *)(object->obj_base + rel->r_offset);
+ newval = ooff + this->st_value;
+
+ /* if GOT is protected, allow the write */
+ if (object->got_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ|PROT_WRITE);
+ }
+
+ if (*addr != newval)
+ *addr = newval;
+
+ /* put the GOT back to RO */
+ if (object->got_size != 0) {
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+ return newval;
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.2 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void *, unsigned int);
+int _dl_open(const char *, unsigned int);
+int _dl_read(int, const char *, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/* $OpenBSD: sod.c,v 1.23 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/syslimits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <nlist.h>
+#include <link.h>
+#include <limits.h>
+#include <machine/exec.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "util.h"
+#include "sod.h"
+
+int _dl_hinthash(char *cp, int vmajor, int vminor);
+void _dl_maphints(void);
+
+/*
+ * Populate sod struct for dlopen's call to map_object
+ */
+void
+_dl_build_sod(const char *name, struct sod *sodp)
+{
+ unsigned int tuplet;
+ int major, minor;
+ char *realname, *tok, *etok, *cp;
+
+ /* default is an absolute or relative path */
+ sodp->sod_name = (long)_dl_strdup(name); /* strtok is destructive */
+ sodp->sod_library = 0;
+ sodp->sod_major = sodp->sod_minor = 0;
+
+ /* does it look like /^lib/ ? */
+ if (_dl_strncmp((char *)sodp->sod_name, "lib", 3) != 0)
+ goto backout;
+
+ /* is this a filename? */
+ if (_dl_strchr((char *)sodp->sod_name, '/'))
+ goto backout;
+
+ /* skip over 'lib' */
+ cp = (char *)sodp->sod_name + 3;
+
+ realname = cp;
+
+ /* dot guardian */
+ if ((_dl_strchr(cp, '.') == NULL) || (*(cp+_dl_strlen(cp)-1) == '.'))
+ goto backout;
+
+ cp = _dl_strstr(cp, ".so");
+ if (cp == NULL)
+ goto backout;
+
+ /* default */
+ major = minor = -1;
+
+ /* loop through name - parse skipping name */
+ for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
+ switch (tuplet) {
+ case 0:
+ /* empty tok, we already skipped to "\.so.*" */
+ break;
+ case 1:
+ /* 'so' extension */
+ break;
+ case 2:
+ /* major version extension */
+ major = _dl_strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ case 3:
+ /* minor version extension */
+ minor = _dl_strtol(tok, &etok, 10);
+ if (*tok == '\0' || *etok != '\0')
+ goto backout;
+ break;
+ /* if we get here, it must be weird */
+ default:
+ goto backout;
+ }
+ }
+ if (realname == NULL)
+ goto backout;
+ cp = (char *)sodp->sod_name;
+ sodp->sod_name = (long)_dl_strdup(realname);
+ _dl_free(cp);
+ sodp->sod_library = 1;
+ sodp->sod_major = major;
+ sodp->sod_minor = minor;
+ return;
+
+backout:
+ _dl_free((char *)sodp->sod_name);
+ sodp->sod_name = (long)_dl_strdup(name);
+}
+
+static struct hints_header *hheader = NULL;
+static struct hints_bucket *hbuckets;
+static char *hstrtab;
+char *_dl_hint_search_path = NULL;
+
+#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
+
+void
+_dl_maphints(void)
+{
+ struct stat sb;
+ caddr_t addr = MAP_FAILED;
+ long hsize = 0;
+ int hfd;
+
+ if ((hfd = _dl_open(_PATH_LD_HINTS, O_RDONLY)) < 0)
+ goto bad_hints;
+
+ if (_dl_fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
+ sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
+ goto bad_hints;
+
+ hsize = (long)sb.st_size;
+ addr = (void *)_dl_mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
+ if (_dl_mmap_error(addr))
+ goto bad_hints;
+
+ hheader = (struct hints_header *)addr;
+ if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
+ goto bad_hints;
+
+ if (hheader->hh_version != LD_HINTS_VERSION_1 &&
+ hheader->hh_version != LD_HINTS_VERSION_2)
+ goto bad_hints;
+
+ hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
+ hstrtab = (char *)(addr + hheader->hh_strtab);
+ if (hheader->hh_version >= LD_HINTS_VERSION_2)
+ _dl_hint_search_path = hstrtab + hheader->hh_dirlist;
+
+ /* close the file descriptor, leaving the hints mapped */
+ _dl_close(hfd);
+
+ return;
+
+bad_hints:
+ if (!_dl_mmap_error(addr))
+ _dl_munmap(addr, hsize);
+ if (hfd != -1)
+ _dl_close(hfd);
+ hheader = (struct hints_header *)-1;
+}
+
+char *
+_dl_findhint(char *name, int major, int minor, char *preferred_path)
+{
+ struct hints_bucket *bp;
+
+ /*
+ * If not mapped, and we have not tried before, try to map the
+ * hints, if previous attempts failed hheader is -1 and we
+ * do not wish to retry it.
+ */
+ if (hheader == NULL)
+ _dl_maphints();
+
+ /* if it failed to map, return failure */
+ if (!(HINTS_VALID))
+ return NULL;
+
+ bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
+
+ while (1) {
+ /* Sanity check */
+ if (bp->hi_namex >= hheader->hh_strtab_sz) {
+ _dl_printf("Bad name index: %#x\n", bp->hi_namex);
+ _dl_exit(7);
+ break;
+ }
+ if (bp->hi_pathx >= hheader->hh_strtab_sz) {
+ _dl_printf("Bad path index: %#x\n", bp->hi_pathx);
+ _dl_exit(7);
+ break;
+ }
+
+ if (_dl_strcmp(name, hstrtab + bp->hi_namex) == 0) {
+ /* It's `name', check version numbers */
+ if (bp->hi_major == major &&
+ (bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
+ if (preferred_path == NULL) {
+ return hstrtab + bp->hi_pathx;
+ } else {
+ char *path = hstrtab + bp->hi_pathx;
+ char *edir = _dl_strrchr(path, '/');
+
+ if ((_dl_strncmp(preferred_path, path,
+ (edir - path)) == 0) &&
+ (preferred_path[edir - path] == '\0'))
+ return path;
+ }
+ }
+ }
+
+ if (bp->hi_next == -1)
+ break;
+
+ /* Move on to next in bucket */
+ bp = &hbuckets[bp->hi_next];
+ }
+
+ /* No hints available for name */
+ return NULL;
+}
+
+int
+_dl_hinthash(char *cp, int vmajor, int vminor)
+{
+ int k = 0;
+
+ while (*cp)
+ k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
+
+ k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
+ if (hheader->hh_version == LD_HINTS_VERSION_1)
+ k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
+
+ return k;
+}
--- /dev/null
+/* $OpenBSD: sod.h,v 1.1 2002/07/12 20:18:30 drahn Exp $ */
+
+/*
+ * Copyright (c) 1993 Paul Kranenburg
+ * 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 Paul Kranenburg.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+void _dl_build_sod(const char *name, struct sod *sodp);
+char *_dl_findhint(char *name, int major, int minor, char *prefered_path);
+extern char *_dl_hint_search_path;
--- /dev/null
+/Makefile.inc/1.2/Sat Nov 23 19:14:25 2002//
+/ldasm.S/1.15/Wed May 3 16:10:52 2006//
+/syscall.h/1.9/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.9/Sat Jan 2 15:01:02 2010//
+/rtld_machine.c/1.32/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/sparc
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.2 2002/11/23 19:14:25 drahn Exp $
+
+CFLAGS += -fpic -msoft-float -I${LIBCSRCDIR}/arch/sparc
+AFLAGS = ${CFLAGS} -DSTRONG_SPARC
+LIBCSRCDIR=${.CURDIR}/../../lib/libc
+.include "${LIBCSRCDIR}/arch/sparc/Makefile.inc"
+.PATH: ${LIBCSRCDIR}/arch/sparc/gen/
+SRCS+=umul.S mul.S
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.9 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SPARC_ARCHDEP_H_
+#define _SPARC_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_SPARC /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf32_Rela
+#define RELSIZE sizeof(Elf32_Rela)
+
+#include <elf_abi.h>
+#include <machine/exec.h>
+#include <machine/reloc.h>
+#include <sys/syscall.h>
+#include "syscall.h"
+#include "util.h"
+
+#define RTLD_PROTECT_PLT
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ /* SPARC does not use REL type relocations */
+ _dl_exit(20);
+}
+
+static inline void
+RELOC_RELA(Elf_RelA *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF_R_TYPE(r->r_info) == R_TYPE(NONE)) {
+ } else if (ELF_R_TYPE(r->r_info) == R_TYPE(RELATIVE)) {
+ *p += v + r->r_addend;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+/*
+ * this is not necessary for sparc, but can be used as a hook
+ * to insert the mul,umul,... optimization for newer sparcs.
+ */
+#define RELOC_GOT(obj, offs) _dl_mul_fixup()
+void _dl_mul_fixup(void);
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _SPARC_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.15 2006/05/03 16:10:52 drahn Exp $ */
+
+/*
+ * Copyright (c) 2001 Jason L. Wright (jason@thought.net)
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas and Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/syscall.h>
+#include <machine/trap.h>
+#include <machine/asm.h>
+
+#define DL_DATA_SIZE (16*4)
+#define ARGC (16*4)
+
+ .section ".text"
+ .align 4
+ .global _dl_start
+ .type _dl_start,@function
+_dl_start:
+ sub %g0, %g0, %fp ! clear frame
+ mov %g1, %l1 ! save ps_strings
+ sub %sp, DL_DATA_SIZE, %sp ! make room for dl_data
+ add %sp, ARGC, %l3
+
+ add %l3, DL_DATA_SIZE, %o0
+ mov %o0, %l0
+
+ /*
+ * need to figure out where _DYNAMIC is located newer binutils
+ * does not fill in GOT to read _DYNAMIC before relocation.
+ */
+ call 0f
+ nop
+ call _DYNAMIC+8 ! not executed (no delay needed)
+0: ld [%o7+8], %o2 ! load stub call instruction
+ sll %o2, 2, %o2 ! extract PC offset
+ sra %o2, 0, %o2 ! sign-extend
+
+ add %o2, %o7, %o2 ! real &_DYNAMIC
+
+ call _dl_boot_bind ! _dl_boot_bind(sp,dl_data,dynamicp)
+ mov %l3, %o1
+
+ mov %l3, %o3
+ ld [%l0], %l3 ! argc = *sp
+ sll %l3, 2, %l3 ! argc *= sizeof(long)
+ add %l0, 4, %o0 ! argv = [sp + argc]
+ add %l0, 8, %o1 ! envp = sp + 8 +
+ add %o1, %l3, %o1 ! + argc
+
+ add %o3, (7*4), %l2
+ ld [%l2], %o2 ! loff = dl_data[AUX_base];
+
+ call _dl_boot ! _dl_boot(argv,envp,loff,dl_data)
+ nop
+
+ add %sp, DL_DATA_SIZE, %sp ! restore stack
+
+ jmp %o0
+ mov %l1, %g1 ! restore ps_strings
+
+
+ .section ".text"
+ .align 4
+ .global _dl_bind_start
+ .type _dl_bind_start,@function
+_dl_bind_start: # (obj, reloff)
+ save %sp, -96, %sp /* setup standard stack frame */
+ ld [%i7 + 8], %o0 /* obj id is in second PLT slot */
+ srl %g1, 10, %o1 /* offset is in high 22 bits */
+ call _dl_bind /* Call _rtld_bind(obj, offset) */
+ sub %o1, 12*4, %o1 /* first 4 `pltrel' entries missing! */
+
+ mov %o0, %g1 /* return value == function address */
+ restore /* get rid of our context */
+ jmp %g1 /* and the jmpslot context, then go. */
+ restore
+
+ .section ".text"
+ .align 4
+ .global _dl_close
+ .type _dl_close,@function
+_dl_close:
+ mov SYS_close | SYSCALL_G2RFLAG, %g1 ! call sys_close
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_exit
+ .type _dl_exit,@function
+_dl_exit:
+ mov SYS_exit | SYSCALL_G2RFLAG, %g1 ! call sys_exit
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_issetugid
+ .type _dl_issetugid,@function
+_dl_issetugid:
+ mov SYS_issetugid | SYSCALL_G2RFLAG, %g1 ! call sys_issetugid
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl__syscall
+ .type _dl__syscall,@function
+_dl__syscall:
+ mov SYS___syscall | SYSCALL_G2RFLAG, %g1 ! call sys_syscall
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_munmap
+ .type _dl_munmap,@function
+_dl_munmap:
+ mov SYS_munmap | SYSCALL_G2RFLAG, %g1 ! calling sys_munmap
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_mprotect
+ .type _dl_mprotect,@function
+_dl_mprotect:
+ mov SYS_mprotect | SYSCALL_G2RFLAG, %g1 ! calling sys_mprotect
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_open
+ .type _dl_open,@function
+_dl_open:
+ mov SYS_open | SYSCALL_G2RFLAG, %g1 ! calling sys_open
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_read
+ .type _dl_read,@function
+_dl_read:
+ mov SYS_read | SYSCALL_G2RFLAG, %g1 ! calling sys_read
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_write
+ .type _dl_write,@function
+_dl_write:
+ mov SYS_write | SYSCALL_G2RFLAG, %g1 ! calling sys_write
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .global _dl_stat
+ .type _dl_stat,@function
+_dl_stat:
+ mov SYS_stat | SYSCALL_G2RFLAG, %g1 ! call sys_stat
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .globl _dl_fstat
+ .type _dl_fstat,@function
+_dl_fstat:
+ mov SYS_fstat | SYSCALL_G2RFLAG, %g1 ! call sys_fstat
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .globl _dl_fcntl
+ .type _dl_fcntl,@function
+_dl_fcntl:
+ mov SYS_fcntl | SYSCALL_G2RFLAG, %g1 ! call sys_fcntl
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .globl _dl_getdirentries
+ .type _dl_getdirentries,@function
+_dl_getdirentries:
+ mov SYS_getdirentries | SYSCALL_G2RFLAG, %g1 ! call sys_getdirentries
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ .section ".text"
+ .align 4
+ .globl _dl_sysctl
+ .type _dl_sysctl,@function
+_dl_sysctl:
+ mov SYS___sysctl | SYSCALL_G2RFLAG, %g1 ! call sys___sysctl
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+
+ /* _dl_sigprocmask does not support NULL new mask */
+ .section ".text"
+ .align 4
+ .globl _dl_sigprocmask
+ .type _dl_sigprocmask,@function
+_dl_sigprocmask:
+ ld [%o1], %o1
+ mov SYS_sigprocmask, %g1 ! call sys___sigprocmask
+ t ST_SYSCALL ! off to wonderland
+
+ cmp %o2, 0
+ bne,a 1f
+ st %o0, [%o2]
+1:
+ retl
+ clr %o0
+
+
+ .section ".text"
+ .align 4
+ .global _dl_gettimeofday
+ .type _dl_gettimeofday,@function
+_dl_gettimeofday:
+ mov SYS_gettimeofday | SYSCALL_G2RFLAG, %g1 ! calling sys_gettimeofday
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+/*
+ * V8 sparc .{,u}{mul,div,rem} replacements.
+ * We try to mimic them 100%. Full 64 bit sources or outputs, and
+ * these routines are required to update the condition codes.
+ */
+.globl _C_LABEL(_mulreplace), _C_LABEL(_mulreplace_end)
+_C_LABEL(_mulreplace):
+ smulcc %o0, %o1, %o0
+ retl
+ rd %y, %o1
+_C_LABEL(_mulreplace_end):
+
+.globl _C_LABEL(_umulreplace), _C_LABEL(_umulreplace_end)
+_C_LABEL(_umulreplace):
+ umulcc %o0, %o1, %o0
+ retl
+ rd %y, %o1
+_C_LABEL(_umulreplace_end):
+
+.globl _C_LABEL(_divreplace), _C_LABEL(_divreplace_end)
+_C_LABEL(_divreplace):
+ sra %o0, 31, %g1
+ wr %g1, 0, %y
+ nop
+ nop
+ nop
+ retl
+ sdivcc %o0, %o1, %o0
+_C_LABEL(_divreplace_end):
+
+.globl _C_LABEL(_udivreplace), _C_LABEL(_udivreplace_end)
+_C_LABEL(_udivreplace):
+ wr %g0, 0, %y
+ nop
+ nop
+ nop
+ retl
+ udivcc %o0, %o1, %o0
+_C_LABEL(_udivreplace_end):
+
+.globl _C_LABEL(_remreplace), _C_LABEL(_remreplace_end)
+_C_LABEL(_remreplace):
+ sra %o0, 31, %g1
+ wr %g1, 0, %y
+ nop
+ nop
+ nop
+ sdiv %o0, %o1, %o2
+ smul %o1, %o2, %o2
+ retl
+ subcc %o0, %o2, %o0
+_C_LABEL(_remreplace_end):
+
+.globl _C_LABEL(_uremreplace), _C_LABEL(_uremreplace_end)
+_C_LABEL(_uremreplace):
+ wr %g0, 0, %y
+ nop
+ nop
+ nop
+ udiv %o0, %o1, %o2
+ umul %o1, %o2, %o2
+ retl
+ subcc %o0, %o2, %o0
+_C_LABEL(_uremreplace_end):
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.32 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 1999 Dale Rahn
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* NONE */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* RELOC_8 */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* RELOC_16 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* RELOC_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 13 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LO10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */
+ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* COPY */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_DAT */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(32) | _RF_RS(0), /* RELATIVE */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* UA_32 */
+
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PLT32 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HIPLT22 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PCPLT22 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 11 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 64 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HH22 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* HM10 */
+ _RF_S|_RF_A|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* LM22 */
+ _RF_S|_RF_A|_RF_P|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* WDISP16 */
+ _RF_S|_RF_A|_RF_P|/*unknown*/ _RF_SZ(32) | _RF_RS(0), /* WDISP19 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 7 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 5 */
+ /*unknown*/ _RF_SZ(32) | _RF_RS(0), /* 6 */
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+
+static int reloc_target_bitmask[] = {
+#define _BM(x) (~(-(1ULL << (x))))
+ 0, /* NONE */
+ _BM(8), _BM(16), _BM(32), /* RELOC_8, _16, _32 */
+ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */
+ _BM(30), _BM(22), /* WDISP30, WDISP22 */
+ _BM(22), _BM(22), /* HI22, _22 */
+ _BM(13), _BM(10), /* RELOC_13, _LO10 */
+ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */
+ _BM(10), _BM(22), /* _PC10, _PC22 */
+ _BM(30), 0, /* _WPLT30, _COPY */
+ -1, -1, -1, /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+ _BM(32), _BM(32), /* _UA32, PLT32 */
+ _BM(22), _BM(10), /* _HIPLT22, LOPLT10 */
+ _BM(32), _BM(22), _BM(10), /* _PCPLT32, _PCPLT22, _PCPLT10 */
+ _BM(10), _BM(11), -1, /* _10, _11, _64 */
+ _BM(10), _BM(22), /* _OLO10, _HH22 */
+ _BM(10), _BM(22), /* _HM10, _LM22 */
+ _BM(16), _BM(19), /* _WDISP16, _WDISP19 */
+ -1, /* GLOB_JMP */
+ _BM(7), _BM(5), _BM(6) /* _7, _5, _6 */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+static inline void
+_dl_reloc_plt(Elf_Addr *where, Elf_Addr value)
+{
+ /*
+ * At the PLT entry pointed at by `where', we now construct
+ * a direct transfer to the now fully resolved function
+ * address. The resulting code in the jump slot is:
+ *
+ * sethi %hi(roffset), %g1
+ * sethi %hi(addr), %g1
+ * jmp %g1+%lo(addr)
+ *
+ * We write the third instruction first, since that leaves the
+ * previous `b,a' at the second word in place. Hence the whole
+ * PLT slot can be atomically change to the new sequence by
+ * writing the `sethi' instruction at word 2.
+ */
+#define SETHI 0x03000000
+#define JMP 0x81c06000
+#define NOP 0x01000000
+ where[2] = JMP | (value & 0x000003ff);
+ where[1] = SETHI | ((value >> 10) & 0x003fffff);
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+ /*
+ * iflush requires 5 subsequent cycles to be sure all copies
+ * are flushed from the CPU and the icache.
+ */
+ __asm __volatile("nop;nop;nop;nop;nop");
+}
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ long i;
+ long numrela;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_RelA *relas;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
+ relas = (Elf_RelA *)(object->Dyn.info[rel]);
+
+ if (relas == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrela; i++, relas++) {
+ Elf_Addr *where, ooff;
+ Elf_Word type, value, mask;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(relas->r_info);
+
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(relas->r_offset + loff);
+
+ if (type == R_TYPE(RELATIVE)) {
+ *where += (Elf_Addr)(loff + relas->r_addend);
+ continue;
+ }
+
+ if (RELOC_USE_ADDEND(type))
+ value = relas->r_addend;
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(relas->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JMP_SLOT)) ?
+ SYM_PLT : SYM_NOTPLT),
+ sym, NULL);
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ size_t size = dstsym->st_size;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JMP_SLOT)) ? SYM_PLT : SYM_NOTPLT),
+ dstsym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, size);
+ continue;
+ }
+
+ if (type == R_TYPE(JMP_SLOT)) {
+ _dl_reloc_plt(where, value);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ /* We ignore alignment restrictions here */
+ *where &= ~mask;
+ *where |= value;
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int reloff)
+{
+ const Elf_Sym *sym, *this;
+ Elf_Addr *addr, ooff;
+ const char *symn;
+ Elf_Addr value;
+ Elf_RelA *rela;
+ sigset_t savedmask;
+
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);
+
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(rela->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ value = ooff + this->st_value;
+
+ /* if PLT is protected, allow the write */
+ if (object->plt_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ /* mprotect the actual modified region, not the whole plt */
+ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ }
+
+ _dl_reloc_plt(addr, value);
+
+ /* if PLT is (to be protected, change back to RO/X */
+ if (object->plt_size != 0) {
+ /* mprotect the actual modified region, not the whole plt */
+ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3,
+ PROT_READ|PROT_EXEC);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return (value);
+}
+
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int fails = 0;
+ Elf_Addr *pltgot;
+ extern void _dl_bind_start(void); /* XXX */
+ Elf_Addr ooff;
+ const Elf_Sym *this;
+ Elf_Addr plt_addr;
+
+ pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+
+ if (pltgot != NULL) {
+ /*
+ * PLTGOT is the PLT on the sparc.
+ * The first entry holds the call the dynamic linker.
+ * We construct a `call' sequence that transfers
+ * to `_dl_bind_start()'.
+ * The second entry holds the object identification.
+ * Note: each PLT entry is three words long.
+ */
+#define SAVE 0x9de3bfc0 /* i.e. `save %sp,-64,%sp' */
+#define CALL 0x40000000
+#define NOP 0x01000000
+ pltgot[0] = SAVE;
+ pltgot[1] = CALL |
+ ((Elf_Addr)&_dl_bind_start - (Elf_Addr)&pltgot[1]) >> 2;
+ pltgot[2] = NOP;
+ pltgot[3] = (Elf_Addr) object;
+ __asm __volatile("iflush %0+8" : : "r" (pltgot));
+ __asm __volatile("iflush %0+4" : : "r" (pltgot));
+ __asm __volatile("iflush %0+0" : : "r" (pltgot));
+ /*
+ * iflush requires 5 subsequent cycles to be sure all copies
+ * are flushed from the CPU and the icache.
+ */
+ __asm __volatile("nop;nop;nop;nop;nop");
+ }
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ plt_addr = 0;
+ object->plt_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ plt_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->plt_size = ooff + this->st_value - plt_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ if (plt_addr == NULL)
+ object->plt_start = NULL;
+ else {
+ object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
+ object->plt_size += plt_addr - object->plt_start;
+ object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
+ }
+
+ if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ }
+
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
+
+
+void __mul(void);
+void _mulreplace_end(void);
+void _mulreplace(void);
+void __umul(void);
+void _umulreplace_end(void);
+void _umulreplace(void);
+
+void __div(void);
+void _divreplace_end(void);
+void _divreplace(void);
+void __udiv(void);
+void _udivreplace_end(void);
+void _udivreplace(void);
+
+void __rem(void);
+void _remreplace_end(void);
+void _remreplace(void);
+void __urem(void);
+void _uremreplace_end(void);
+void _uremreplace(void);
+
+void
+_dl_mul_fixup()
+{
+ int mib[2], v8mul;
+ size_t len;
+
+
+ mib[0] = CTL_MACHDEP;
+ mib[1] = CPU_V8MUL;
+ len = sizeof(v8mul);
+ _dl_sysctl(mib, 2, &v8mul, &len, NULL, 0);
+
+
+ if (!v8mul)
+ return;
+
+ _dl_mprotect(&__mul, _mulreplace_end-_mulreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_mulreplace, __mul, _mulreplace_end-_mulreplace);
+ _dl_mprotect(&__mul, _mulreplace_end-_mulreplace,
+ PROT_READ|PROT_EXEC);
+
+ _dl_mprotect(&__umul, _umulreplace_end-_umulreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_umulreplace, __umul, _umulreplace_end-_umulreplace);
+ _dl_mprotect(&__umul, _umulreplace_end-_umulreplace,
+ PROT_READ|PROT_EXEC);
+
+
+ _dl_mprotect(&__div, _divreplace_end-_divreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_divreplace, __div, _divreplace_end-_divreplace);
+ _dl_mprotect(&__div, _divreplace_end-_divreplace,
+ PROT_READ|PROT_EXEC);
+
+ _dl_mprotect(&__udiv, _udivreplace_end-_udivreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_udivreplace, __udiv, _udivreplace_end-_udivreplace);
+ _dl_mprotect(&__udiv, _udivreplace_end-_udivreplace,
+ PROT_READ|PROT_EXEC);
+
+
+ _dl_mprotect(&__rem, _remreplace_end-_remreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_remreplace, __rem, _remreplace_end-_remreplace);
+ _dl_mprotect(&__rem, _remreplace_end-_remreplace,
+ PROT_READ|PROT_EXEC);
+
+ _dl_mprotect(&__urem, _uremreplace_end-_uremreplace,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ _dl_bcopy(_uremreplace, __urem, _uremreplace_end-_uremreplace);
+ _dl_mprotect(&__urem, _uremreplace_end-_uremreplace,
+ PROT_READ|PROT_EXEC);
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.9 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, int, int);
+int _dl_munmap(const void*, unsigned int);
+int _dl_open(const char*, unsigned int);
+int _dl_read(int, const char*, int);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/Makefile.inc/1.1/Wed Aug 21 15:40:30 2002//
+/syscall.h/1.16/Thu Oct 2 20:12:08 2008//
+/archdep.h/1.18/Sat Jan 2 15:01:02 2010//
+/ldasm.S/1.26/Mon May 31 05:18:46 2010//
+/rtld_machine.c/1.45/Mon May 31 05:18:46 2010//
+D
--- /dev/null
+src/libexec/ld.so/sparc64
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.1 2002/08/21 15:40:30 art Exp $
+
+CFLAGS += -fpic -msoft-float
+AFLAGS += -fpic
--- /dev/null
+/* $OpenBSD: archdep.h,v 1.18 2010/01/02 12:16:35 kettenis Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SPARC64_ARCHDEP_H_
+#define _SPARC64_ARCHDEP_H_
+
+#define DL_MALLOC_ALIGN 8 /* Arch constraint or otherwise */
+
+#define MACHID EM_SPARCV9 /* ELF e_machine ID value checked */
+
+#define RELTYPE Elf64_Rela
+#define RELSIZE sizeof(Elf64_Rela)
+
+#include <elf_abi.h>
+#include <machine/exec.h>
+#include <machine/reloc.h>
+#include <sys/syscall.h>
+#include "syscall.h"
+#include "util.h"
+
+#define RTLD_PROTECT_PLT
+
+static inline void *
+_dl_mmap(void *addr, unsigned int len, unsigned int prot,
+ unsigned int flags, int fd, off_t offset)
+{
+ return((void *)_dl__syscall((quad_t)SYS_mmap, addr, len, prot,
+ flags, fd, 0, offset));
+}
+
+static inline void
+RELOC_REL(Elf_Rel *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v)
+{
+ /* SPARC64 does not use REL type relocations */
+ _dl_exit(20);
+}
+
+static inline void
+RELOC_RELA(Elf_RelA *r, const Elf_Sym *s, Elf_Addr *p, unsigned long v,
+ Elf_Addr *pltgot)
+{
+ if (ELF_R_TYPE(r->r_info) == RELOC_RELATIVE) {
+ *p = v + r->r_addend;
+ } else {
+ /* XXX - printf might not work here, but we give it a shot. */
+ _dl_printf("Unknown bootstrap relocation.\n");
+ _dl_exit(6);
+ }
+}
+
+#define RELOC_GOT(obj, offs)
+
+#define GOT_PERMS PROT_READ
+
+#endif /* _SPARC64_ARCHDEP_H_ */
--- /dev/null
+/* $OpenBSD: ldasm.S,v 1.26 2010/05/09 09:34:42 kettenis Exp $ */
+/* $NetBSD: rtld_start.S,v 1.5 2001/08/14 22:17:48 eeh Exp $ */
+
+/*
+ * Copyright (c) 2001 Jason L. Wright (jason@thought.net)
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas and Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/syscall.h>
+#include <machine/trap.h>
+#include <machine/asm.h>
+#define _LOCORE
+#include <machine/frame.h>
+
+/*
+ * ELF:
+ * On startup the stack should contain 16 extended word register save
+ * area, followed by the arg count, etc.
+ *
+ * _rtld() expects the stack pointer to point to two longwords for argument
+ * return followed by argc, etc. We need to create a pointer to
+ * &argc + 16 and pass that in. The return args will be in those
+ * locations.
+ *
+ * NB: We are violating the ELF spec by passing a pointer to the ps strings in
+ * %g1 instead of a termination routine.
+ */
+
+/* Offset of ARGC from bottom of stack */
+#define ARGC CC64FSZ
+/* XXX - DL_DATA_SIZE should be (9*8), but I can't think right now. */
+#define DL_DATA_SIZE (16*8)
+
+ .section ".text"
+ .align 16
+ .register %g2,#scratch
+
+_ENTRY(_dl_start)
+ sub %g0, %g0, %fp ! clear frame
+ mov %g1, %l1 ! save ps_strings
+ sub %sp, 48 + DL_DATA_SIZE, %sp ! make room for dl_data
+ add %sp, BIAS + ARGC, %l3
+
+ add %l3, DL_DATA_SIZE, %o0
+ mov %o0, %l0
+
+ /*
+ * need to figure out where _DYNAMIC is located, newer binutils
+ * does not fill in GOT to read _DYNAMIC before relocation.
+ */
+ call 0f
+ nop
+ call _DYNAMIC+8 ! not executed (no delay needed)
+0: ld [%o7+8], %o2 ! load stub call instruction
+ sll %o2, 2, %o2 ! extract PC offset
+ sra %o2, 0, %o2 ! sign-extend
+
+ add %o2, %o7, %o2 ! real &_DYNAMIC
+
+ call _dl_boot_bind ! _dl_boot_bind(sp,dl_data,dynamicp)
+ mov %l3, %o1
+
+ mov %l3, %o3
+ ldx [%l0], %l3 ! argc = *sp
+ sllx %l3, 3, %l3 ! argc *= sizeof(long)
+ addx %l0, 8, %o0 ! argv = [sp + argc]
+ addx %l0, 16, %o1 ! envp = sp + 16 +
+ addx %o1, %l3, %o1 ! + argc
+
+ addx %o3, (7*8), %l2
+ ldx [%l2], %o2 ! loff = dl_data[AUX_base];
+
+ call _dl_boot ! _dl_boot(argv,envp,loff,dl_data)
+ nop
+
+ add %sp, 48 + DL_DATA_SIZE, %sp ! restore stack
+ mov %l1, %g1 ! restore ps_strings
+
+ jmp %o0
+ nop
+
+ /*
+ * We have two separate entry points to the runtime linker.
+ * I'm implementing this following the SPARC v9 ABI spec.
+ *
+ * _dl_bind_start_0(x, y) is called from .PLT0, and is used for
+ * PLT entries above 32768.
+ *
+ * _dl_bind_start_1(x, y) is called from .PLT1, and is used for
+ * PLT entries below 32768.
+ *
+ * The first two entries of PLT2 contain the xword object pointer.
+ *
+ * These routines are called with two longword arguments,
+ * x and y. To calculate the address of the entry,
+ * _dl_bind_start_1(x, y) does:
+ *
+ * n = x >> 15;
+ *
+ * and _dl_bind_start_0(x, y) does:
+ *
+ * i = x - y + 8 - 32768*32;
+ * n = 32768 + (i/5120)*160 + (i%5120)/24;
+ *
+ * Neither routine needs to issue a save since it's already been
+ * done in the PLT entry.
+ */
+
+ /* NOTE: _dl_bind_start_0 is untested. Hence the debug stuff */
+
+_ENTRY(_dl_bind_start_0) # (x, y)
+ sethi %hi(32768*32-8), %l1
+ sub %o0, %o1, %l0 /* x - y */
+ or %l1, %lo(32768*32-8), %l1
+ sub %l0, %l1, %l0 /* x - y + 8 - 32768*32 */
+
+ sethi %hi(5120), %l1
+ sdivx %l0, %l1, %l1 /* Calculate i/5120 */
+ ldx [%o1 + (10*4)], %o0 /* Load object pointer from PLT2 */
+ sllx %l1, 2, %l2
+ add %l1, %l2, %l2
+ sllx %l2, 10, %l2
+ sub %l0, %l2, %l2 /* And i%5120 */
+
+ /* Let the division churn for a bit. */
+ sdivx %l2, 24, %l4 /* (i%5120)/24 */
+
+ /* 160 is (32 * 5) or (32 * (4 + 1)) */
+ sllx %l1, 2, %l3 /* 4 * (i/5120) */
+ add %l1, %l3, %l3 /* 5 * (i/5120) */
+ sllx %l3, 5, %l3 /* 32 * 5 * (i/5120) */
+
+ sethi %hi(32768), %l6
+ add %l3, %l4, %l5 /* %l5 = (i/5120)*160 + (i%5120)/24; */
+ add %l5, %l6, %l5
+
+ call _dl_bind /* Call _dl_bind(obj, offset) */
+ mov %l5, %o1
+
+ jmp %o0 /* return value == function address */
+ restore /* Dump our stack frame */
+
+_ENTRY(_dl_bind_start_1) # (x, y)
+ srax %o0, 15, %o2 /* %o0 is the index to our PLT slot */
+
+ ldx [%o1 + 8], %o0 /* The object pointer is at [%o1 + 8] */
+
+ call _dl_bind /* Call _dl_bind(obj, offset) */
+ mov %o2, %o1
+
+ jmp %o0 /* return value == function address */
+ restore /* Dump our stack frame */
+
+_ENTRY(_dl_close)
+ mov SYS_close | SYSCALL_G2RFLAG, %g1 ! call sys_close
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_exit)
+ mov SYS_exit | SYSCALL_G2RFLAG, %g1 ! call sys_exit
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_issetugid)
+ mov SYS_issetugid | SYSCALL_G2RFLAG, %g1 ! call sys_issetugid
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl__syscall)
+ mov SYS___syscall | SYSCALL_G2RFLAG, %g1 ! call sys___syscall
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_munmap)
+ mov SYS_munmap | SYSCALL_G2RFLAG, %g1 ! calling sys_munmap
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_mprotect)
+ mov SYS_mprotect | SYSCALL_G2RFLAG, %g1 ! calling sys_mprotect
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_open)
+ mov SYS_open | SYSCALL_G2RFLAG, %g1 ! calling sys_open
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_read)
+ mov SYS_read | SYSCALL_G2RFLAG, %g1 ! calling sys_read
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_write)
+ mov SYS_write | SYSCALL_G2RFLAG, %g1 ! calling sys_write
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_stat)
+ mov SYS_stat | SYSCALL_G2RFLAG, %g1 ! call sys_stat
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_fstat)
+ mov SYS_fstat | SYSCALL_G2RFLAG, %g1 ! call sys_fstat
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_fcntl)
+ mov SYS_fcntl | SYSCALL_G2RFLAG, %g1 ! call sys_fcntl
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_getdirentries)
+ mov SYS_getdirentries | SYSCALL_G2RFLAG, %g1 ! call sys_getdirentries
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+ /* _dl_sigprocmask does not support NULL new mask */
+_ENTRY(_dl_sigprocmask)
+ ld [%o1], %o1 ! indirect for new mask
+ mov SYS_sigprocmask, %g1 ! call sys_sigprocmask
+ t ST_SYSCALL ! off to wonderland
+ ! what about errors?
+ cmp %o2, 0
+ bne,a 1f ! if oset != NULL
+ st %o0, [%o2] ! *oset = oldmask
+1:
+ retl
+ clr %o0
+
+_ENTRY(_dl_sysctl)
+ mov SYS___sysctl | SYSCALL_G2RFLAG, %g1 ! call sys_getdirentries
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
+_ENTRY(_dl_gettimeofday)
+ mov SYS_gettimeofday | SYSCALL_G2RFLAG, %g1 ! calling sys_gettimeofday
+ add %o7, 8, %g2 ! just return on success
+ t ST_SYSCALL ! off to wonderland
+ retl
+ sub %g0, %o0, %o0 ! error: result = -errno
+
--- /dev/null
+/* $OpenBSD: rtld_machine.c,v 1.45 2010/05/02 04:57:01 guenther Exp $ */
+
+/*
+ * Copyright (c) 1999 Dale Rahn
+ * Copyright (c) 2001 Niklas Hallqvist
+ * Copyright (c) 2001 Artur Grabowski
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*-
+ * Copyright (c) 2000 Eduardo Horvath.
+ * Copyright (c) 1999 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Paul Kranenburg.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define _DYN_LOADER
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+
+#include <nlist.h>
+#include <link.h>
+#include <signal.h>
+
+#include "syscall.h"
+#include "archdep.h"
+#include "resolve.h"
+
+/*
+ * The following table holds for each relocation type:
+ * - the width in bits of the memory location the relocation
+ * applies to (not currently used)
+ * - the number of bits the relocation value must be shifted to the
+ * right (i.e. discard least significant bits) to fit into
+ * the appropriate field in the instruction word.
+ * - flags indicating whether
+ * * the relocation involves a symbol
+ * * the relocation is relative to the current position
+ * * the relocation is for a GOT entry
+ * * the relocation is relative to the load address
+ *
+ */
+#define _RF_S 0x80000000 /* Resolve symbol */
+#define _RF_A 0x40000000 /* Use addend */
+#define _RF_P 0x20000000 /* Location relative */
+#define _RF_G 0x10000000 /* GOT offset */
+#define _RF_B 0x08000000 /* Load address relative */
+#define _RF_U 0x04000000 /* Unaligned */
+#define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */
+#define _RF_RS(s) ((s) & 0xff) /* right shift */
+static int reloc_target_flags[] = {
+ 0, /* NONE */
+ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* RELOC_8 */
+ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* RELOC_16 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* RELOC_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* HI22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 13 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LO10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */
+ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */
+ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* COPY */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */
+ _RF_S| _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */
+ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */
+
+ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */
+ _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */
+ _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */
+ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 11 */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* 64 */
+ _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(42), /* HH22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(32), /* HM10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LM22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 7 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 5 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 6 */
+ _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */
+ _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* HIX22 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LOX10 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(22), /* H44 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(12), /* M44 */
+ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* L44 */
+ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */
+ _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */
+};
+
+#define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0)
+#define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0)
+#define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0)
+#define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0)
+#define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0)
+#define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff)
+#define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff)
+
+static long reloc_target_bitmask[] = {
+#define _BM(x) (~(-(1ULL << (x))))
+ 0, /* NONE */
+ _BM(8), _BM(16), _BM(32), /* RELOC_8, _16, _32 */
+ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */
+ _BM(30), _BM(22), /* WDISP30, WDISP22 */
+ _BM(22), _BM(22), /* HI22, _22 */
+ _BM(13), _BM(10), /* RELOC_13, _LO10 */
+ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */
+ _BM(10), _BM(22), /* _PC10, _PC22 */
+ _BM(30), 0, /* _WPLT30, _COPY */
+ -1, _BM(32), -1, /* _GLOB_DAT, JMP_SLOT, _RELATIVE */
+ _BM(32), _BM(32), /* _UA32, PLT32 */
+ _BM(22), _BM(10), /* _HIPLT22, LOPLT10 */
+ _BM(32), _BM(22), _BM(10), /* _PCPLT32, _PCPLT22, _PCPLT10 */
+ _BM(10), _BM(11), -1, /* _10, _11, _64 */
+ _BM(10), _BM(22), /* _OLO10, _HH22 */
+ _BM(10), _BM(22), /* _HM10, _LM22 */
+ _BM(22), _BM(10), _BM(22), /* _PC_HH22, _PC_HM10, _PC_LM22 */
+ _BM(16), _BM(19), /* _WDISP16, _WDISP19 */
+ -1, /* GLOB_JMP */
+ _BM(7), _BM(5), _BM(6) /* _7, _5, _6 */
+ -1, -1, /* DISP64, PLT64 */
+ _BM(22), _BM(13), /* HIX22, LOX10 */
+ _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */
+ -1, -1, _BM(16), /* REGISTER, UA64, UA16 */
+#undef _BM
+};
+#define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t])
+
+void _dl_reloc_plt(elf_object_t *object, Elf_Word *where, Elf_Addr value,
+ Elf_RelA *rela);
+void _dl_install_plt(Elf_Word *pltgot, Elf_Addr proc);
+
+int
+_dl_md_reloc(elf_object_t *object, int rel, int relasz)
+{
+ long i;
+ long numrela;
+ int fails = 0;
+ Elf_Addr loff;
+ Elf_RelA *relas;
+ struct load_list *llist;
+
+ loff = object->obj_base;
+ numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela);
+ relas = (Elf64_Rela *)(object->Dyn.info[rel]);
+
+ if (relas == NULL)
+ return(0);
+
+ /*
+ * unprotect some segments if we need it.
+ */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot|PROT_WRITE);
+ }
+ }
+
+ for (i = 0; i < numrela; i++, relas++) {
+ Elf_Addr *where, value, ooff, mask;
+ Elf_Word type;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+
+ type = ELF_R_TYPE(relas->r_info);
+
+ if (type == R_TYPE(NONE))
+ continue;
+
+ if (type == R_TYPE(JMP_SLOT) && rel != DT_JMPREL)
+ continue;
+
+ where = (Elf_Addr *)(relas->r_offset + loff);
+
+ if (RELOC_USE_ADDEND(type))
+ value = relas->r_addend;
+ else
+ value = 0;
+
+ sym = NULL;
+ symn = NULL;
+ if (RELOC_RESOLVE_SYMBOL(type)) {
+ sym = object->dyn.symtab;
+ sym += ELF_R_SYM(relas->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ if (sym->st_shndx != SHN_UNDEF &&
+ ELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ value += loff;
+ } else {
+ this = NULL;
+ ooff = _dl_find_symbol_bysym(object,
+ ELF_R_SYM(relas->r_info), &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
+ ((type == R_TYPE(JMP_SLOT)) ?
+ SYM_PLT : SYM_NOTPLT),
+ sym, NULL);
+ if (this == NULL) {
+resolve_failed:
+ if (ELF_ST_BIND(sym->st_info) !=
+ STB_WEAK)
+ fails++;
+ continue;
+ }
+ value += (Elf_Addr)(ooff + this->st_value);
+ }
+ }
+
+ if (type == R_TYPE(JMP_SLOT)) {
+ _dl_reloc_plt(object, (Elf_Word *)where, value, relas);
+ continue;
+ }
+
+ if (type == R_TYPE(COPY)) {
+ void *dstaddr = where;
+ const void *srcaddr;
+ const Elf_Sym *dstsym = sym, *srcsym = NULL;
+ size_t size = dstsym->st_size;
+ Elf_Addr soff;
+
+ soff = _dl_find_symbol(symn, &srcsym,
+ SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT,
+ dstsym, object, NULL);
+ if (srcsym == NULL)
+ goto resolve_failed;
+
+ srcaddr = (void *)(soff + srcsym->st_value);
+ _dl_bcopy(srcaddr, dstaddr, size);
+ continue;
+ }
+
+ if (RELOC_PC_RELATIVE(type))
+ value -= (Elf_Addr)where;
+ if (RELOC_BASE_RELATIVE(type))
+ value += loff;
+
+ mask = RELOC_VALUE_BITMASK(type);
+ value >>= RELOC_VALUE_RIGHTSHIFT(type);
+ value &= mask;
+
+ if (RELOC_UNALIGNED(type)) {
+ /* Handle unaligned relocations. */
+ Elf_Addr tmp = 0;
+ char *ptr = (char *)where;
+ int i, size = RELOC_TARGET_SIZE(type)/8;
+
+ /* Read it in one byte at a time. */
+ for (i=0; i<size; i++)
+ tmp = (tmp << 8) | ptr[i];
+
+ tmp &= ~mask;
+ tmp |= value;
+
+ /* Write it back out. */
+ for (i=0; i<size; i++)
+ ptr[i] = ((tmp >> (8*i)) & 0xff);
+ } else if (RELOC_TARGET_SIZE(type) > 32) {
+ *where &= ~mask;
+ *where |= value;
+ } else {
+ Elf32_Addr *where32 = (Elf32_Addr *)where;
+
+ *where32 &= ~mask;
+ *where32 |= value;
+ }
+ }
+
+ /* reprotect the unprotected segments */
+ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) {
+ for (llist = object->load_list; llist != NULL; llist = llist->next) {
+ if (!(llist->prot & PROT_WRITE))
+ _dl_mprotect(llist->start, llist->size,
+ llist->prot);
+ }
+ }
+
+ return (fails);
+}
+
+/*
+ * Instruction templates:
+ */
+#define BAA 0x10400000 /* ba,a %xcc, 0 */
+#define SETHI 0x03000000 /* sethi %hi(0), %g1 */
+#define JMP 0x81c06000 /* jmpl %g1+%lo(0), %g0 */
+#define NOP 0x01000000 /* sethi %hi(0), %g0 */
+#define OR 0x82106000 /* or %g1, 0, %g1 */
+#define ORG5 0x8a116000 /* or %g5, 0, %g5 */
+#define XOR 0x82186000 /* xor %g1, 0, %g1 */
+#define MOV71 0x8283a000 /* or %o7, 0, %g1 */
+#define MOV17 0x9c806000 /* or %g1, 0, %o7 */
+#define CALL 0x40000000 /* call 0 */
+#define SLLX 0x83287000 /* sllx %g1, 0, %g1 */
+#define SLLXG5 0x8b297000 /* sllx %g5, 0, %g5 */
+#define SRAX 0x83387000 /* srax %g1, 0, %g1 */
+#define SETHIG5 0x0b000000 /* sethi %hi(0), %g5 */
+#define ORG15 0x82804005 /* or %g1, %g5, %g1 */
+
+
+/* %hi(v) with variable shift */
+#define HIVAL(v, s) (((v) >> (s)) & 0x003fffff)
+#define LOVAL(v) ((v) & 0x000003ff)
+
+void
+_dl_reloc_plt(elf_object_t *object, Elf_Word *where, Elf_Addr value,
+ Elf_RelA *rela)
+{
+ Elf_Addr offset;
+
+ /*
+ * At the PLT entry pointed at by `where', we now construct
+ * a direct transfer to the now fully resolved function
+ * address.
+ *
+ * A PLT entry is supposed to start by looking like this:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * ba,a %xcc, .PLT1
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ * When we replace these entries we start from the second
+ * entry and do it in reverse order so the last thing we
+ * do is replace the branch. That allows us to change this
+ * atomically.
+ *
+ * We now need to find out how far we need to jump. We
+ * have a choice of several different relocation techniques
+ * which are increasingly expensive.
+ */
+
+ offset = ((Elf_Addr)where) - value;
+ if (rela->r_addend) {
+ Elf_Addr *ptr = (Elf_Addr *)where;
+ /*
+ * This entry is >32768. The relocation points to a
+ * PC-relative pointer to the _dl_bind_start_0 stub at
+ * the top of the PLT section. Update it to point to
+ * the target function.
+ */
+ ptr[0] += value - object->Dyn.info[DT_PLTGOT];
+
+ } else if (offset <= (1L<<20) && offset >= -(1L<<20)) {
+ /*
+ * We're within 1MB -- we can use a direct branch insn.
+ *
+ * We can generate this pattern:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * ba,a %xcc, addr
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[1] = BAA | ((offset >> 2) &0x3fffff);
+ __asm __volatile("iflush %0+4" : : "r" (where));
+ } else if (value < (1UL<<32)) {
+ /*
+ * We're within 32-bits of address zero.
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hi(addr), %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[2] = JMP | LOVAL(value);
+ where[1] = SETHI | HIVAL(value, 10);
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+
+ } else if (value > -(1UL<<32)) {
+ /*
+ * We're within 32-bits of address -1.
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hix(addr), %g1
+ * xor %g1, %lox(addr), %g1
+ * jmp %g1
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[3] = JMP;
+ where[2] = XOR | ((~value) & 0x00001fff);
+ where[1] = SETHI | HIVAL(~value, 10);
+ __asm __volatile("iflush %0+12" : : "r" (where));
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+
+ } else if (offset <= (1L<<32) && offset >= -((1L<<32) - 4)) {
+ /*
+ * We're within 32-bits -- we can use a direct call insn
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * mov %o7, %g1
+ * call (.+offset)
+ * mov %g1, %o7
+ * nop
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[3] = MOV17;
+ where[2] = CALL | ((offset >> 4) & 0x3fffffff);
+ where[1] = MOV71;
+ __asm __volatile("iflush %0+12" : : "r" (where));
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+
+ } else if (value < (1L<<42)) {
+ /*
+ * Target 42bits or smaller.
+ * We can generate this pattern:
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hi(addr >> 20), %g1
+ * or %g1, %lo(addr >> 10), %g1
+ * sllx %g1, 10, %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ * nop
+ * nop
+ *
+ * this can handle addresses 0 - 0x3fffffffffc
+ */
+ where[4] = JMP | LOVAL(value);
+ where[3] = SLLX | 10;
+ where[2] = OR | LOVAL(value >> 10);
+ where[1] = SETHI | HIVAL(value, 20);
+ __asm __volatile("iflush %0+16" : : "r" (where));
+ __asm __volatile("iflush %0+12" : : "r" (where));
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+
+ } else if (value > -(1UL<<41)) {
+ /*
+ * Large target >= 0xfffffe0000000000UL
+ * We can generate this pattern:
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hi(addr >> 20), %g1
+ * or %g1, %lo(addr >> 10), %g1
+ * sllx %g1, 32, %g1
+ * srax %g1, 22, %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ * nop
+ * nop
+ *
+ */
+ where[5] = JMP | LOVAL(value);
+ where[4] = SRAX | 22;
+ where[3] = SLLX | 32;
+ where[2] = OR | LOVAL(value >> 10);
+ where[1] = SETHI | HIVAL(value, 20);
+
+ __asm __volatile("iflush %0+16" : : "r" (where));
+ __asm __volatile("iflush %0+12" : : "r" (where));
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+
+ } else {
+ /*
+ * We need to load all 64-bits
+ *
+ * The resulting code in the jump slot is:
+ *
+ * sethi %hi(. - .PLT0), %g1
+ * sethi %hi(addr >> 42), %g5
+ * sethi %hi(addr >> 10), %g1
+ * or %g1, %lo(addr >> 32), %g5
+ * sllx %g5, 32, %g5
+ * or %g1, %g5, %g1
+ * jmp %g1+%lo(addr)
+ * nop
+ *
+ */
+ where[6] = JMP | LOVAL(value);
+ where[5] = ORG15;
+ where[4] = SLLXG5 | 32;
+ where[3] = ORG5 | LOVAL(value >> 32);
+ where[2] = SETHI | HIVAL(value, 10);
+ where[1] = SETHIG5 | HIVAL(value, 42);
+ __asm __volatile("iflush %0+24" : : "r" (where));
+ __asm __volatile("iflush %0+20" : : "r" (where));
+ __asm __volatile("iflush %0+16" : : "r" (where));
+ __asm __volatile("iflush %0+12" : : "r" (where));
+ __asm __volatile("iflush %0+8" : : "r" (where));
+ __asm __volatile("iflush %0+4" : : "r" (where));
+ }
+}
+
+/*
+ * Resolve a symbol at run-time.
+ */
+Elf_Addr
+_dl_bind(elf_object_t *object, int index)
+{
+ Elf_RelA *rela;
+ Elf_Word *addr;
+ Elf_Addr ooff;
+ const Elf_Sym *sym, *this;
+ const char *symn;
+ sigset_t savedmask;
+
+ rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]);
+ if (ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)) {
+ /*
+ * XXXX
+ *
+ * The first four PLT entries are reserved. There
+ * is some disagreement whether they should have
+ * associated relocation entries. Both the SPARC
+ * 32-bit and 64-bit ELF specifications say that
+ * they should have relocation entries, but the
+ * 32-bit SPARC binutils do not generate them,
+ * and now the 64-bit SPARC binutils have stopped
+ * generating them too.
+ *
+ * So, to provide binary compatibility, we will
+ * check the first entry, if it is reserved it
+ * should not be of the type JMP_SLOT. If it
+ * is JMP_SLOT, then the 4 reserved entries were
+ * not generated and our index is 4 entries too far.
+ */
+ index -= 4;
+ }
+
+ rela += index;
+
+ sym = object->dyn.symtab;
+ sym += ELF64_R_SYM(rela->r_info);
+ symn = object->dyn.strtab + sym->st_name;
+
+ addr = (Elf_Word *)(object->obj_base + rela->r_offset);
+ this = NULL;
+ ooff = _dl_find_symbol(symn, &this,
+ SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym,
+ object, NULL);
+ if (this == NULL) {
+ _dl_printf("lazy binding failed!\n");
+ *((int *)0) = 0; /* XXX */
+ }
+
+ /* if PLT is protected, allow the write */
+ if (object->plt_size != 0) {
+ _dl_thread_bind_lock(0, &savedmask);
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_WRITE|PROT_EXEC);
+ }
+
+ _dl_reloc_plt(object, addr, ooff + this->st_value, rela);
+
+ /* if PLT is (to be protected), change back to RO/X */
+ if (object->plt_size != 0) {
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+ _dl_thread_bind_lock(1, &savedmask);
+ }
+
+ return ooff + this->st_value;
+}
+
+/*
+ * Install rtld function call into this PLT slot.
+ */
+#define SAVE 0x9de3bf50
+#define SETHI_l0 0x21000000
+#define SETHI_l1 0x23000000
+#define OR_l0_l0 0xa0142000
+#define SLLX_l0_32_l0 0xa12c3020
+#define OR_l0_l1_l0 0xa0140011
+#define JMPL_l0_o1 0x93c42000
+#define MOV_g1_o0 0x90100001
+
+void
+_dl_install_plt(Elf_Word *pltgot, Elf_Addr proc)
+{
+ pltgot[0] = SAVE;
+ pltgot[1] = SETHI_l0 | HIVAL(proc, 42);
+ pltgot[2] = SETHI_l1 | HIVAL(proc, 10);
+ pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32);
+ pltgot[4] = SLLX_l0_32_l0;
+ pltgot[5] = OR_l0_l1_l0;
+ pltgot[6] = JMPL_l0_o1 | LOVAL(proc);
+ pltgot[7] = MOV_g1_o0;
+}
+
+void _dl_bind_start_0(long, long);
+void _dl_bind_start_1(long, long);
+
+/*
+ * Relocate the Global Offset Table (GOT).
+ */
+int
+_dl_md_reloc_got(elf_object_t *object, int lazy)
+{
+ int fails = 0;
+ Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];
+ Elf_Word *entry = (Elf_Word *)pltgot;
+ Elf_Addr ooff;
+ Elf_Addr plt_addr;
+ const Elf_Sym *this;
+
+ if (object->Dyn.info[DT_PLTREL] != DT_RELA)
+ return (0);
+
+ object->got_addr = NULL;
+ object->got_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__got_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__got_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->got_size = ooff + this->st_value - object->got_addr;
+
+ plt_addr = 0;
+ object->plt_size = 0;
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_start", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ plt_addr = ooff + this->st_value;
+
+ this = NULL;
+ ooff = _dl_find_symbol("__plt_end", &this,
+ SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL,
+ object, NULL);
+ if (this != NULL)
+ object->plt_size = ooff + this->st_value - plt_addr;
+
+ if (object->got_addr == NULL)
+ object->got_start = NULL;
+ else {
+ object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz);
+ object->got_size += object->got_addr - object->got_start;
+ object->got_size = ELF_ROUND(object->got_size, _dl_pagesz);
+ }
+ if (plt_addr == NULL)
+ object->plt_start = NULL;
+ else {
+ object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz);
+ object->plt_size += plt_addr - object->plt_start;
+ object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz);
+ }
+
+ if (!lazy) {
+ fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
+ } else {
+ _dl_install_plt(&entry[0], (Elf_Addr)&_dl_bind_start_0);
+ _dl_install_plt(&entry[8], (Elf_Addr)&_dl_bind_start_1);
+
+ pltgot[8] = (Elf_Addr)object;
+ }
+ if (object->got_size != 0)
+ _dl_mprotect((void*)object->got_start, object->got_size,
+ PROT_READ);
+ if (object->plt_size != 0)
+ _dl_mprotect((void*)object->plt_start, object->plt_size,
+ PROT_READ|PROT_EXEC);
+
+ return (fails);
+}
--- /dev/null
+/* $OpenBSD: syscall.h,v 1.16 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 2001 Niklas Hallqvist
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_SYSCALL_H__
+#define __DL_SYSCALL_H__
+
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+#ifndef _dl_MAX_ERRNO
+#define _dl_MAX_ERRNO 512L
+#endif
+#define _dl_mmap_error(__res) \
+ ((long)__res < 0 && (long)__res >= -_dl_MAX_ERRNO)
+
+int _dl_close(int);
+int _dl_exit(int);
+int _dl_issetugid(void);
+long _dl__syscall(quad_t, ...);
+int _dl_mprotect(const void *, size_t, int);
+int _dl_munmap(const void*, size_t);
+int _dl_open(const char*, int);
+ssize_t _dl_read(int, const char*, size_t);
+int _dl_stat(const char *, struct stat *);
+int _dl_fstat(int, struct stat *);
+int _dl_fcntl(int, int, ...);
+int _dl_getdirentries(int, char*, int, long *);
+int _dl_sigprocmask(int, const sigset_t *, sigset_t *);
+int _dl_sysctl(int *, u_int, void *, size_t *, void *, size_t);
+int _dl_gettimeofday(struct timeval *tp, struct timezone *tzp);
+
+static inline off_t
+_dl_lseek(int fildes, off_t offset, int whence)
+{
+ return _dl__syscall((quad_t)SYS_lseek, fildes, 0, offset, whence);
+}
+
+#endif /*__DL_SYSCALL_H__*/
--- /dev/null
+/* $OpenBSD: strtol.c,v 1.1 2003/07/06 20:03:58 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1990 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. 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.
+ */
+
+#include <sys/param.h>
+#include "util.h"
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+long
+_dl_strtol(const char *nptr, char **endptr, int base)
+{
+ const char *s;
+ long acc, cutoff;
+ int c;
+ int neg, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+ do {
+ c = (unsigned char) *s++;
+ } while (c <= ' ' || c >= 0x7f);
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? LONG_MIN : LONG_MAX;
+ cutlim = cutoff % base;
+ cutoff /= base;
+ if (neg) {
+ if (cutlim > 0) {
+ cutlim -= base;
+ cutoff += 1;
+ }
+ cutlim = -cutlim;
+ }
+ for (acc = 0, any = 0;; c = (unsigned char) *s++) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0)
+ continue;
+ if (neg) {
+ if (acc < cutoff || (acc == cutoff && c > cutlim)) {
+ any = -1;
+ acc = LONG_MIN;
+ } else {
+ any = 1;
+ acc *= base;
+ acc -= c;
+ }
+ } else {
+ if (acc > cutoff || (acc == cutoff && c > cutlim)) {
+ any = -1;
+ acc = LONG_MAX;
+ } else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ }
+ if (endptr != 0)
+ *endptr = (char *) (any ? s - 1 : nptr);
+ return (acc);
+}
--- /dev/null
+/* $OpenBSD: util.c,v 1.20 2008/10/02 20:12:08 kurt Exp $ */
+
+/*
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+#include <string.h>
+#include "archdep.h"
+
+/*
+ * Stack protector dummies.
+ * Ideally, a scheme to compile these stubs from libc should be used, but
+ * this would end up dragging too much code from libc here.
+ */
+long __guard[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+void __stack_smash_handler(char [], int);
+
+void
+__stack_smash_handler(char func[], int damaged)
+{
+ _dl_exit(127);
+}
+
+/*
+ * Static vars usable after bootstrapping.
+ */
+static void *_dl_malloc_pool = 0;
+static long *_dl_malloc_free = 0;
+
+char *
+_dl_strdup(const char *orig)
+{
+ char *newstr;
+ int len;
+
+ len = _dl_strlen(orig)+1;
+ newstr = _dl_malloc(len);
+ _dl_strlcpy(newstr, orig, len);
+ return (newstr);
+}
+
+
+/*
+ * The following malloc/free code is a very simplified implementation
+ * of a malloc function. However, we do not need to be very complex here
+ * because we only free memory when 'dlclose()' is called and we can
+ * reuse at least the memory allocated for the object descriptor. We have
+ * one dynamic string allocated, the library name and it is likely that
+ * we can reuse that one to without a lot of complex colapsing code.
+ */
+void *
+_dl_malloc(size_t need)
+{
+ long *p, *t, *n, have;
+
+ need = (need + 8 + DL_MALLOC_ALIGN - 1) & ~(DL_MALLOC_ALIGN - 1);
+
+ if ((t = _dl_malloc_free) != 0) { /* Try free list first */
+ n = (long *)&_dl_malloc_free;
+ while (t && t[-1] < need) {
+ n = t;
+ t = (long *)*t;
+ }
+ if (t) {
+ *n = *t;
+ _dl_memset(t, 0, t[-1] - sizeof(long));
+ return((void *)t);
+ }
+ }
+ have = _dl_round_page((long)_dl_malloc_pool) - (long)_dl_malloc_pool;
+ if (need > have) {
+ if (have >= 8 + DL_MALLOC_ALIGN) {
+ p = _dl_malloc_pool;
+ *p = have;
+ _dl_free((void *)(p + 1)); /* move to freelist */
+ }
+ _dl_malloc_pool = (void *)_dl_mmap((void *)0,
+ _dl_round_page(need), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0);
+ if (_dl_malloc_pool == 0 || _dl_mmap_error(_dl_malloc_pool)) {
+ _dl_printf("Dynamic loader failure: malloc.\n");
+ _dl_exit(7);
+ }
+ }
+ p = _dl_malloc_pool;
+ _dl_malloc_pool += need;
+ _dl_memset(p, 0, need);
+ *p = need;
+ return((void *)(p + 1));
+}
+
+void
+_dl_free(void *p)
+{
+ long *t = (long *)p;
+
+ *t = (long)_dl_malloc_free;
+ _dl_malloc_free = p;
+}
+
+
+unsigned int
+_dl_random(void)
+{
+ int mib[2];
+ unsigned int rnd;
+ size_t len;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARND;
+ len = sizeof(rnd);
+ _dl_sysctl(mib, 2, &rnd, &len, NULL, 0);
+
+ return (rnd);
+}
+
+
--- /dev/null
+/* $OpenBSD: util.h,v 1.21 2009/05/18 20:20:01 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 __DL_UTIL_H__
+#define __DL_UTIL_H__
+
+#include <stdarg.h>
+
+void *_dl_malloc(const size_t size);
+void _dl_free(void *);
+char *_dl_strdup(const char *);
+void _dl_printf(const char *fmt, ...);
+void _dl_vprintf(const char *fmt, va_list ap);
+void _dl_fdprintf(int, const char *fmt, ...);
+void _dl_show_objects(void);
+unsigned int _dl_random(void);
+ssize_t _dl_write(int fd, const char* buf, size_t len);
+
+long _dl_strtol(const char *nptr, char **endptr, int base);
+
+#define _dl_round_page(x) (((x) + (__LDPGSZ - 1)) & ~(__LDPGSZ - 1))
+
+/*
+ * The following functions are declared inline so they can
+ * be used before bootstrap linking has been finished.
+ */
+static inline void
+_dl_wrstderr(const char *s)
+{
+ const char *p = s;
+ size_t n = 0;
+
+ while (*p++)
+ n++;
+ _dl_write(2, s, n);
+}
+
+static inline void *
+_dl_memset(void *dst, const int c, size_t n)
+{
+ if (n != 0) {
+ char *d = dst;
+
+ do
+ *d++ = c;
+ while (--n != 0);
+ }
+ return (dst);
+}
+
+static inline void
+_dl_bcopy(const void *src, void *dest, int size)
+{
+ unsigned const char *psrc = src;
+ unsigned char *pdest = dest;
+ int i;
+
+ for (i = 0; i < size; i++)
+ pdest[i] = psrc[i];
+}
+
+static inline int
+_dl_strlen(const char *str)
+{
+ const char *s;
+
+ for (s = str; *s; ++s)
+ ;
+ return (s - str);
+}
+
+static inline size_t
+_dl_strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
+
+static inline int
+_dl_strncmp(const char *s1, const char *s2, size_t n)
+{
+ if (n == 0)
+ return (0);
+ do {
+ if (*s1 != *s2++)
+ return (*(unsigned char *)s1 - *(unsigned char *)--s2);
+ if (*s1++ == 0)
+ break;
+ } while (--n != 0);
+ return (0);
+}
+
+static inline int
+_dl_strcmp(const char *s1, const char *s2)
+{
+ while (*s1 == *s2++)
+ if (*s1++ == 0)
+ return (0);
+ return (*(unsigned char *)s1 - *(unsigned char *)--s2);
+}
+
+static inline const char *
+_dl_strchr(const char *p, const int ch)
+{
+ for (;; ++p) {
+ if (*p == ch)
+ return((char *)p);
+ if (!*p)
+ return((char *)NULL);
+ }
+ /* NOTREACHED */
+}
+
+static inline char *
+_dl_strrchr(const char *str, const int ch)
+{
+ const char *p;
+ char *retval = NULL;
+
+ for (p = str; *p != '\0'; ++p)
+ if (*p == ch)
+ retval = (char *)p;
+
+ return retval;
+}
+
+static inline char *
+_dl_strstr(const char *s, const char *find)
+{
+ char c, sc;
+ size_t len;
+ if ((c = *find++) != 0) {
+ len = _dl_strlen(find);
+ do {
+ do {
+ if ((sc = *s++) == 0)
+ return (NULL);
+ } while (sc != c);
+ } while (_dl_strncmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
+
+#endif /*__DL_UTIL_H__*/
--- /dev/null
+/Makefile/1.1/Sat Aug 15 21:02:22 1998//
+/lockspool.1/1.10/Wed Oct 1 20:33:07 2008//
+/lockspool.c/1.16/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/lockspool
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 1998/08/15 21:02:22 millert Exp $
+
+PROG= lockspool
+SRCS= lockspool.c locking.c
+BINOWN= root
+BINMODE=4555
+CFLAGS+=-I${.CURDIR}/../mail.local
+.PATH: ${.CURDIR}/../mail.local
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: lockspool.1,v 1.10 2008/10/01 20:33:07 millert Exp $
+.\"
+.\" Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: October 1 2008 $
+.Dt LOCKSPOOL 1
+.Os
+.Sh NAME
+.Nm lockspool
+.Nd lock user's system mailbox
+.Sh SYNOPSIS
+.Nm lockspool
+.Op Ar username
+.Sh DESCRIPTION
+.Nm
+is useful for a client mail program to attain proper locking.
+.Nm
+obtains a
+.Pa username.lock
+for the calling user and retains it until stdin is closed or a signal like
+.Dv SIGINT ,
+.Dv SIGTERM ,
+or
+.Dv SIGHUP
+is received.
+Additionally, the superuser may specify the name of a user in order
+to lock a different mailbox.
+.Pp
+If
+.Nm
+is able to create the lock file,
+.Dq 1
+is written to stdout, otherwise
+.Dq 0
+is written and an error message is written to stderr.
+.Nm
+will try up to 10 times to get the lock (sleeping
+for a short period in between tries).
+.Pp
+Typical usage is for a user mail agent (such as
+.Xr mail 1 )
+to open a pipe to
+.Nm
+when it needs to lock the user's mail spool.
+Closing the pipe will cause
+.Nm
+to release the lock.
+.Pp
+The
+.Nm
+utility exits 0 on success, and 1 if an error occurs.
+.Sh FILES
+.Bl -tag -width /var/mail/username.lock -compact
+.It Pa /var/mail/username.lock
+user's mail lock file
+.El
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr mail.local 8 ,
+.Xr sendmail 8
+.Sh HISTORY
+The
+.Nm
+program appeared in
+.Ox 2.4 .
--- /dev/null
+/* $OpenBSD: lockspool.c,v 1.16 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1998 Theo de Raadt <deraadt@theos.com>
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHORS 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.
+ */
+
+#include <signal.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <poll.h>
+#include "mail.local.h"
+
+void unhold(int);
+void usage(void);
+
+extern char *__progname;
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ struct pollfd pfd;
+ ssize_t nread;
+ char *from, c;
+ int holdfd;
+
+ openlog(__progname, LOG_PERROR, LOG_MAIL);
+
+ if (argc != 1 && argc != 2)
+ usage();
+ if (argc == 2 && getuid() != 0)
+ merr(FATAL, "you must be root to lock someone else's spool");
+
+ signal(SIGTERM, unhold);
+ signal(SIGINT, unhold);
+ signal(SIGHUP, unhold);
+ signal(SIGPIPE, unhold);
+
+ if (argc == 2)
+ pw = getpwnam(argv[1]);
+ else
+ pw = getpwuid(getuid());
+ if (pw == NULL)
+ exit (1);
+ from = pw->pw_name;
+
+ holdfd = getlock(from, pw);
+ if (holdfd == -1) {
+ write(STDOUT_FILENO, "0\n", 2);
+ exit (1);
+ }
+ write(STDOUT_FILENO, "1\n", 2);
+
+ /* wait for the other end of the pipe to close, then release the lock */
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ do {
+ if (poll(&pfd, 1, INFTIM) == -1) {
+ if (errno != EINTR)
+ break;
+ }
+ do {
+ nread = read(STDIN_FILENO, &c, 1);
+ } while (nread == 1 || (nread == -1 && errno == EINTR));
+ } while (nread == -1 && errno == EAGAIN);
+ rellock();
+ exit (0);
+}
+
+/*ARGSUSED*/
+void
+unhold(int signo)
+{
+
+ rellock();
+ _exit(0);
+}
+
+void
+usage(void)
+{
+
+ merr(FATAL, "usage: %s [username]", __progname);
+}
--- /dev/null
+/usr/obj/libexec/lockspool
\ No newline at end of file
--- /dev/null
+/Makefile/1.5/Wed Jan 2 14:33:48 2008//
+/login_chpass.8/1.6/Thu May 31 19:19:40 2007//
+/login_chpass.c/1.15/Thu Mar 9 19:14:09 2006//
+D
--- /dev/null
+src/libexec/login_chpass
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.5 2008/01/02 14:33:48 millert Exp $
+
+
+PROG= login_chpass
+SRCS= login_chpass.c
+MAN= login_chpass.8
+
+.PATH: ${.CURDIR}/../../usr.bin/passwd
+
+.include <bsd.own.mk> # For KERBEROS5 and YP
+
+CFLAGS+=-Wall
+
+#.if (${KERBEROS5:L} == "yes")
+#SRCS+= krb5_passwd.c
+#CFLAGS+= -DKRB5
+#DPADD+= ${LIBKRB5} ${LIBASN1} ${LIBDES} ${LIBCRYPTO}
+#LDADD+= -lkrb5 -lasn1 -ldes -lcrypto
+#.endif
+
+.if (${YP:L} == "yes")
+CFLAGS+=-DYP
+SRCS+= yp_passwd.c pwd_check.c pwd_gensalt.c
+DPADD+= ${LIBRPCSVC} ${LIBUTIL}
+LDADD+= -lrpcsvc -lutil
+.endif
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_chpass.8,v 1.6 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1996 Berkeley Software Design, Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 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 Berkeley Software Design,
+.\" Inc.
+.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+.\"
+.\" BSDI $From: login_chpass.8,v 1.2 1997/01/15 20:50:13 bostic Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_CHPASS 8
+.Os
+.Sh NAME
+.Nm login_chpass
+.Nd change password authentication type
+.Sh SYNOPSIS
+.Nm login_chpass
+.Op Fl s Ar service
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility is typically called from
+.Xr login 1 .
+It is functionally the same as calling the program:
+.Dq passwd Ar user .
+This will use the
+.Xr login_lchpass 8
+utility to change the user's local password.
+.Pp
+Only the
+.Li login
+service is supported.
+See
+.Xr login.conf 5 .
+The
+.Ar class
+argument is not used.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr login.conf 5 ,
+.Xr login_lchpass 8
--- /dev/null
+/* $OpenBSD: login_chpass.c,v 1.15 2006/03/09 19:14:09 millert Exp $ */
+
+/*-
+ * Copyright (c) 1995,1996 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_chpass.c,v 1.3 1996/08/21 21:01:48 prb Exp $
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <login_cap.h>
+
+#ifdef YP
+# include <netdb.h>
+# include <rpc/rpc.h>
+# include <rpcsvc/yp_prot.h>
+# include <rpcsvc/ypclnt.h>
+# define passwd yp_passwd_rec
+# include <rpcsvc/yppasswd.h>
+# undef passwd
+#endif
+
+#define _PATH_LOGIN_LCHPASS "/usr/libexec/auth/login_lchpass"
+
+#define BACK_CHANNEL 3
+
+#ifdef YP
+struct iovec iov[2] = { { BI_SILENT, sizeof(BI_SILENT) - 1 }, { "\n", 1 } };
+
+int _yp_check(char **);
+char *ypgetnewpasswd(struct passwd *, char **);
+struct passwd *ypgetpwnam(char *);
+void kbintr(int);
+int pwd_gensalt(char *, int, login_cap_t *, char);
+#endif
+
+void local_chpass(char **);
+void yp_chpass(char *);
+
+int
+main(int argc, char *argv[])
+{
+#ifdef YP
+ char *username;
+#endif
+ struct rlimit rl;
+ int c;
+
+ rl.rlim_cur = 0;
+ rl.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rl);
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog("login", LOG_ODELAY, LOG_AUTH);
+
+ while ((c = getopt(argc, argv, "s:v:")) != -1)
+ switch (c) {
+ case 'v':
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") != 0) {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ switch (argc - optind) {
+ case 2:
+ /* class is not used */
+ case 1:
+#ifdef YP
+ username = argv[optind];
+#endif
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+#ifdef YP
+ if (_yp_check(NULL))
+ yp_chpass(username);
+#endif
+ local_chpass(argv);
+ /* NOTREACHED */
+ exit(0);
+}
+
+void
+local_chpass(char *argv[])
+{
+
+ /* login_lchpass doesn't check instance so don't bother restoring it */
+ argv[0] = strrchr(_PATH_LOGIN_LCHPASS, '/') + 1;
+ execv(_PATH_LOGIN_LCHPASS, argv);
+ syslog(LOG_ERR, "%s: %m", _PATH_LOGIN_LCHPASS);
+ exit(1);
+}
+
+#ifdef YP
+void
+yp_chpass(char *username)
+{
+ char *master;
+ int r, rpcport, status;
+ struct yppasswd yppasswd;
+ struct passwd *pw;
+ struct timeval tv;
+ CLIENT *client;
+ extern char *domain;
+
+ (void)signal(SIGINT, kbintr);
+ (void)signal(SIGQUIT, kbintr);
+
+ if ((r = yp_get_default_domain(&domain)) != 0) {
+ warnx("can't get local YP domain. Reason: %s", yperr_string(r));
+ exit(1);
+ }
+
+ /*
+ * Find the host for the passwd map; it should be running
+ * the daemon.
+ */
+ if ((r = yp_master(domain, "passwd.byname", &master)) != 0) {
+ warnx("can't find the master YP server. Reason: %s",
+ yperr_string(r));
+ exit(1);
+ }
+
+ /* Ask the portmapper for the port of the daemon. */
+ if ((rpcport = getrpcport(master, YPPASSWDPROG,
+ YPPASSWDPROC_UPDATE, IPPROTO_UDP)) == 0) {
+ warnx("master YP server not running yppasswd daemon.");
+ warnx("Can't change password.");
+ exit(1);
+ }
+
+ if (rpcport >= IPPORT_RESERVED) {
+ warnx("yppasswd daemon is on an invalid port.");
+ exit(1);
+ }
+
+ /* If user doesn't exist, just prompt for old password and exit. */
+ pw = ypgetpwnam(username);
+ if (pw) {
+ if (pw->pw_uid == 0) {
+ syslog(LOG_ERR, "attempted root password change");
+ pw = NULL;
+ } else if (*pw->pw_passwd == '\0') {
+ syslog(LOG_ERR, "%s attempting to add password",
+ username);
+ pw = NULL;
+ }
+ }
+ if (pw == NULL) {
+ char *p, salt[_PASSWORD_LEN + 1];
+ login_cap_t *lc;
+
+ /* no such user, get appropriate salt to thwart timing attack */
+ if ((p = getpass("Old password:")) != NULL) {
+ if ((lc = login_getclass(NULL)) == NULL ||
+ pwd_gensalt(salt, sizeof(salt), lc, 'y') == 0)
+ strlcpy(salt, "xx", sizeof(salt));
+ crypt(p, salt);
+ memset(p, 0, strlen(p));
+ }
+ warnx("YP passwd database unchanged.");
+ exit(1);
+ }
+
+ /* prompt for new password */
+ yppasswd.newpw.pw_passwd = ypgetnewpasswd(pw, &yppasswd.oldpass);
+
+ /* tell rpc.yppasswdd */
+ yppasswd.newpw.pw_name = pw->pw_name;
+ yppasswd.newpw.pw_uid = pw->pw_uid;
+ yppasswd.newpw.pw_gid = pw->pw_gid;
+ yppasswd.newpw.pw_gecos = pw->pw_gecos;
+ yppasswd.newpw.pw_dir = pw->pw_dir;
+ yppasswd.newpw.pw_shell = pw->pw_shell;
+
+ client = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
+ if (client == NULL) {
+ warnx("cannot contact yppasswdd on %s: Reason: %s",
+ master, yperr_string(YPERR_YPBIND));
+ free(yppasswd.newpw.pw_passwd);
+ exit(1);
+ }
+ client->cl_auth = authunix_create_default();
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ r = clnt_call(client, YPPASSWDPROC_UPDATE,
+ xdr_yppasswd, &yppasswd, xdr_int, &status, tv);
+ if (r)
+ warnx("rpc to yppasswdd failed.");
+ else if (status) {
+ printf("Couldn't change YP password.\n");
+ free(yppasswd.newpw.pw_passwd);
+ exit(1);
+ }
+ printf("The YP password has been changed on %s, the master YP passwd server.\n",
+ master);
+ free(yppasswd.newpw.pw_passwd);
+ (void)writev(BACK_CHANNEL, iov, 2);
+ exit(0);
+}
+
+/* ARGSUSED */
+void
+kbintr(int signo)
+{
+ char msg[] = "YP passwd database unchanged.\n";
+ struct iovec iv[3];
+ extern char *__progname;
+
+ iv[0].iov_base = __progname;
+ iv[0].iov_len = strlen(__progname);
+ iv[1].iov_base = ": ";
+ iv[1].iov_len = 2;
+ iv[2].iov_base = msg;
+ iv[2].iov_len = sizeof(msg) - 1;
+ writev(STDERR_FILENO, iv, 3);
+
+ _exit(1);
+}
+#endif
--- /dev/null
+/usr/obj/libexec/login_chpass
\ No newline at end of file
--- /dev/null
+/Makefile/1.13/Thu Mar 9 19:14:09 2006//
+/login_krb5-or-pwd.8/1.15/Thu May 31 19:19:40 2007//
+D
--- /dev/null
+src/libexec/login_krb5-or-pwd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.13 2006/03/09 19:14:09 millert Exp $
+
+.include <bsd.own.mk>
+
+PROG= login_krb5-or-pwd
+SRCS= login.c login_passwd.c pwd_gensalt.c
+MAN= ${PROG}.8
+CFLAGS+=-DPASSWD -Wall -I${.CURDIR}/../login_passwd
+
+.if (${KERBEROS5:L} == "yes")
+SRCS+= login_krb5.c
+DPADD+= ${LIBKRB5} ${LIBASN1} ${LIBCRYPTO} ${LIBUTIL} ${LIBDES}
+LDADD+= -lkrb5 -lasn1 -lcrypto -lutil -ldes
+CFLAGS+=-DKRB5
+.PATH: ${.CURDIR}/../login_passwd ${.CURDIR}/../login_krb5 ${.CURDIR}/../../usr.bin/passwd
+.else
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+.PATH: ${.CURDIR}/../login_passwd ${.CURDIR}/../../usr.bin/passwd
+.endif
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_krb5-or-pwd.8,v 1.15 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_KRB5-OR-PWD 8
+.Os
+.Sh NAME
+.Nm login_krb5-or-pwd
+.Nd provide KerberosV or password authentication type
+.Sh SYNOPSIS
+.Nm login_krb5-or-pwd
+.Op Fl s Ar service
+.Op Fl v Ar arguments
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+program first attempts to authenticate the user via KerberosV and,
+failing that, falls back to standard password authentication.
+.Pp
+If KerberosV is not configured on the system,
+.Nm
+is equivalent to calling
+.Xr login_passwd 8 .
+When root tries to login,
+.Nm
+skips KerberosV authentication, as this may give problems in case of a
+network failure.
+.Pp
+The
+.Ar user
+argument is the user's name to be authenticated.
+.Pp
+The
+.Ar service
+argument specifies which protocol to use with the
+invoking program.
+The allowed protocols are
+.Em login ,
+.Em challenge ,
+and
+.Em response .
+(The
+.Em challenge
+protocol is silently ignored but will report success as KerberosV
+authentication is not challenge-response based).
+.Pp
+The arguments following
+.Fl v
+are the same as for
+.Xr login_krb5 8
+and
+.Xr login_passwd 8 .
+Unknown arguments are ignored.
+.Pp
+.Nm
+will prompt the user for a password and report back to the
+invoking program whether or not the authentication was
+successful.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr su 1 ,
+.Xr login.conf 5 ,
+.Xr ftpd 8 ,
+.Xr kerberos 8 ,
+.Xr login_krb5 8 ,
+.Xr login_passwd 8
--- /dev/null
+/usr/obj/libexec/login_krb5-or-pwd
\ No newline at end of file
--- /dev/null
+/Makefile/1.16/Thu Nov 20 23:23:09 2003//
+/login_krb5.8/1.15/Thu May 31 19:19:40 2007//
+/login_krb5.c/1.25/Wed Jan 14 14:53:44 2009//
+D
--- /dev/null
+src/libexec/login_krb5
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.16 2003/11/20 23:23:09 avsm Exp $
+
+.include <bsd.own.mk>
+
+PROG= login_krb5
+SRCS= login.c
+MAN= ${PROG}.8
+CFLAGS+=-Wall -I${.CURDIR}/../login_passwd
+.PATH: ${.CURDIR}/../login_passwd
+
+.if (${KERBEROS5:L} == "yes")
+SRCS+= login_krb5.c
+DPADD+= ${LIBKRB5} ${LIBASN1} ${LIBCRYPTO} ${LIBDES}
+LDADD+= -lkrb5 -lasn1 -lcrypto -ldes
+CFLAGS+=-DKRB5
+.endif
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_krb5.8,v 1.15 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_KERBEROS 8
+.Os
+.Sh NAME
+.Nm login_krb5
+.Nd provide KerberosV authentication type
+.Sh SYNOPSIS
+.Nm login_krb5
+.Bk -words
+.Op Fl s Ar service
+.Op Fl v Ar login=yes|no
+.Op Fl v Ar notickets=yes|no
+.Op Fl v Ar invokinguser=user
+.Ar user
+.Op Ar class
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility implements the KerberosV authentication mechanism.
+It is called by
+.Xr login 1 ,
+.Xr su 1 ,
+.Xr ftpd 8 ,
+and others to authenticate the
+.Ar user
+with KerberosV.
+.Pp
+The
+.Ar user
+argument is the user's name to be authenticated.
+.Pp
+The
+.Ar service
+argument specifies which protocol to use with the
+invoking program.
+The allowed protocols are
+.Em login ,
+.Em challenge ,
+and
+.Em response .
+(The
+.Em challenge
+protocol is silently ignored but will report success as KerberosV
+authentication is not challenge-response based).
+.Pp
+If
+.Ar invokinguser
+is set and the
+.Ar user
+argument is root, the principal
+invokinguser/root will be used for authentication.
+.Pp
+If the
+.Ar notickets
+argument is equal to
+.Dq no ,
+the default value, and the
+.Ar login
+argument is equal to
+.Dq yes ,
+then the ticket will be saved in a credentials cache.
+.Pp
+The
+.Ar class
+argument is ignored for compatibility with other login scripts.
+.Pp
+.Nm
+will prompt the user for a password and report back to the
+invoking program whether or not the authentication was
+successful.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr su 1 ,
+.Xr login.conf 5 ,
+.Xr ftpd 8 ,
+.Xr kerberos 8 ,
+.Xr login_krb5-or-pwd 8
--- /dev/null
+/* $OpenBSD: login_krb5.c,v 1.25 2009/01/14 14:53:44 jacekm Exp $ */
+
+/*-
+ * Copyright (c) 2001, 2002 Hans Insulander <hin@openbsd.org>.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include "common.h"
+
+#include <kerberosV/krb5.h>
+
+krb5_error_code ret;
+krb5_context context;
+krb5_ccache ccache;
+krb5_principal princ;
+
+char *__progname;
+
+static void
+krb5_syslog(krb5_context context, int level, krb5_error_code code, char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(level, "%s: %s", buf, krb5_get_err_text(context, code));
+}
+
+static void
+store_tickets(struct passwd *pwd, int ticket_newfiles, int ticket_store,
+ int token_install)
+{
+ char cc_file[MAXPATHLEN];
+ krb5_ccache ccache_store;
+#ifdef KRB524
+ int get_krb4_ticket = 0;
+ char krb4_ticket_file[MAXPATHLEN];
+#endif
+
+ if (ticket_newfiles)
+ snprintf(cc_file, sizeof(cc_file), "FILE:/tmp/krb5cc_%d",
+ pwd->pw_uid);
+ else
+ snprintf(cc_file, sizeof(cc_file), "%s",
+ krb5_cc_default_name(context));
+
+ if (ticket_store) {
+ ret = krb5_cc_resolve(context, cc_file, &ccache_store);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "krb5_cc_gen_new");
+ exit(1);
+ }
+
+ ret = krb5_cc_copy_cache(context, ccache, ccache_store);
+ if (ret != 0)
+ krb5_syslog(context, LOG_ERR, ret,
+ "krb5_cc_copy_cache");
+
+ chown(krb5_cc_get_name(context, ccache_store),
+ pwd->pw_uid, pwd->pw_gid);
+
+ fprintf(back, BI_SETENV " KRB5CCNAME %s:%s\n",
+ krb5_cc_get_type(context, ccache_store),
+ krb5_cc_get_name(context, ccache_store));
+
+#ifdef KRB524
+ get_krb4_ticket = krb5_config_get_bool_default (context,
+ NULL, get_krb4_ticket, "libdefaults",
+ "krb4_get_tickets", NULL);
+ if (get_krb4_ticket) {
+ CREDENTIALS c;
+ krb5_creds cred;
+ krb5_cc_cursor cursor;
+
+ ret = krb5_cc_start_seq_get(context, ccache, &cursor);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "start seq");
+ exit(1);
+ }
+
+ ret = krb5_cc_next_cred(context, ccache,
+ &cursor, &cred);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "next cred");
+ exit(1);
+ }
+
+ ret = krb5_cc_end_seq_get(context, ccache,
+ &cursor);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "end seq");
+ exit(1);
+ }
+
+ ret = krb524_convert_creds_kdc_ccache(context, ccache,
+ &cred, &c);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "convert");
+ } else {
+ snprintf(krb4_ticket_file,
+ sizeof(krb4_ticket_file),
+ "%s%d", TKT_ROOT, pwd->pw_uid);
+ krb_set_tkt_string(krb4_ticket_file);
+ tf_setup(&c, c.pname, c.pinst);
+ chown(krb4_ticket_file,
+ pwd->pw_uid, pwd->pw_gid);
+ }
+ }
+#endif
+ }
+
+ /* Need to chown the ticket file */
+#ifdef KRB524
+ if (get_krb4_ticket)
+ fprintf(back, BI_SETENV " KRBTKFILE %s\n",
+ krb4_ticket_file);
+#endif
+}
+
+int
+krb5_login(char *username, char *invokinguser, char *password, int login,
+ int tickets)
+{
+ int return_code = AUTH_FAILED;
+
+ if (username == NULL || password == NULL)
+ return (AUTH_FAILED);
+
+ if (strcmp(__progname, "krb5-or-pwd") == 0 &&
+ strcmp(username,"root") == 0 && invokinguser[0] == '\0')
+ return (AUTH_FAILED);
+
+ ret = krb5_init_context(&context);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret, "krb5_init_context");
+ exit(1);
+ }
+
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret, "krb5_cc_gen_new");
+ exit(1);
+ }
+
+ if (strcmp(username, "root") == 0 && invokinguser[0] != '\0') {
+ char *tmp;
+
+ ret = asprintf(&tmp, "%s/root", invokinguser);
+ if (ret == -1) {
+ krb5_syslog(context, LOG_ERR, ret, "asprintf");
+ exit(1);
+ }
+ ret = krb5_parse_name(context, tmp, &princ);
+ free(tmp);
+ } else
+ ret = krb5_parse_name(context, username, &princ);
+ if (ret != 0) {
+ krb5_syslog(context, LOG_ERR, ret, "krb5_parse_name");
+ exit(1);
+ }
+
+ ret = krb5_verify_user_lrealm(context, princ, ccache,
+ password, 1, NULL);
+
+ switch (ret) {
+ case 0: {
+ struct passwd *pwd;
+
+ pwd = getpwnam(username);
+ if (pwd == NULL) {
+ krb5_syslog(context, LOG_ERR, ret,
+ "%s: no such user", username);
+ return (AUTH_FAILED);
+ }
+ fprintf(back, BI_AUTH "\n");
+ store_tickets(pwd, login && tickets, login && tickets, login);
+ return_code = AUTH_OK;
+ break;
+ }
+ case KRB5KRB_AP_ERR_MODIFIED:
+ /* XXX syslog here? */
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ break;
+ default:
+ krb5_syslog(context, LOG_ERR, ret, "verify");
+ break;
+ }
+
+ krb5_free_principal(context, princ);
+ krb5_cc_close(context, ccache);
+ krb5_free_context(context);
+
+ return (return_code);
+}
--- /dev/null
+/usr/obj/libexec/login_krb5
\ No newline at end of file
--- /dev/null
+/Makefile/1.3/Tue Jun 19 16:38:21 2001//
+/login_lchpass.8/1.4/Thu May 31 19:19:40 2007//
+/login_lchpass.c/1.13/Thu Mar 9 19:14:10 2006//
+D
--- /dev/null
+src/libexec/login_lchpass
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.3 2001/06/19 16:38:21 millert Exp $
+
+# BSDI $From: Makefile,v 1.2 1997/08/08 18:58:22 prb Exp $
+
+PROG= login_lchpass
+SRCS= login_lchpass.c local_passwd.c pwd_check.c pwd_gensalt.c
+MAN= login_lchpass.8
+.PATH: ${.CURDIR}/../../usr.bin/passwd
+
+CFLAGS+=-Wall -Wno-unused
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_lchpass.8,v 1.4 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1996 Berkeley Software Design, Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 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 Berkeley Software Design,
+.\" Inc.
+.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+.\"
+.\" BSDI $From: login_lchpass.8,v 1.1 1996/08/06 15:56:57 prb Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_LCHPASS 8
+.Os
+.Sh NAME
+.Nm login_lchpass
+.Nd change local password authentication type
+.Sh SYNOPSIS
+.Nm login_lchpass
+.Op Fl s Ar service
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility is typically called from
+.Xr login 1 .
+It is functionally the same as calling the program:
+.Dq passwd -l Ar user .
+.Pp
+Only the
+.Li login
+service is supported.
+See
+.Xr login.conf 5 .
+The
+.Ar class
+argument is not used.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr login.conf 5
--- /dev/null
+/* $OpenBSD: login_lchpass.c,v 1.13 2006/03/09 19:14:10 millert Exp $ */
+
+/*-
+ * Copyright (c) 1995,1996 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_lchpass.c,v 1.4 1997/08/08 18:58:23 prb Exp $
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <login_cap.h>
+
+#define BACK_CHANNEL 3
+
+int local_passwd(char *, int);
+int pwd_gensalt(char *, int, login_cap_t *, char);
+
+int
+main(int argc, char *argv[])
+{
+ login_cap_t *lc;
+ struct iovec iov[2];
+ struct passwd *pwd;
+ char *username = NULL, *salt, *p, saltbuf[_PASSWORD_LEN + 1];
+ struct rlimit rl;
+ int c;
+
+ iov[0].iov_base = BI_SILENT;
+ iov[0].iov_len = sizeof(BI_SILENT) - 1;
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+
+ rl.rlim_cur = 0;
+ rl.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rl);
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog("login", LOG_ODELAY, LOG_AUTH);
+
+ while ((c = getopt(argc, argv, "v:s:")) != -1)
+ switch (c) {
+ case 'v':
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") != 0) {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ switch (argc - optind) {
+ case 2:
+ /* class is not used */
+ case 1:
+ username = argv[optind];
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ pwd = getpwnam(username);
+ if (pwd) {
+ if (pwd->pw_uid == 0) {
+ syslog(LOG_ERR, "attempted root password change");
+ pwd = NULL;
+ } else if (*pwd->pw_passwd == '\0') {
+ syslog(LOG_ERR, "%s attempting to add password",
+ username);
+ pwd = NULL;
+ }
+ }
+
+ if (pwd)
+ salt = pwd->pw_passwd;
+ else {
+ /* no such user, get appropriate salt */
+ if ((lc = login_getclass(NULL)) == NULL ||
+ pwd_gensalt(saltbuf, sizeof(saltbuf), lc, 'l') == 0)
+ salt = "xx";
+ else
+ salt = saltbuf;
+ }
+
+ (void)setpriority(PRIO_PROCESS, 0, -4);
+
+ (void)printf("Changing local password for %s.\n", username);
+ if ((p = getpass("Old Password:")) == NULL)
+ exit(1);
+
+ salt = crypt(p, salt);
+ memset(p, 0, strlen(p));
+ if (!pwd || strcmp(salt, pwd->pw_passwd) != 0)
+ exit(1);
+
+ /*
+ * We rely on local_passwd() to block signals during the
+ * critical section.
+ */
+ local_passwd(pwd->pw_name, 1);
+ (void)writev(BACK_CHANNEL, iov, 2);
+ exit(0);
+}
--- /dev/null
+/usr/obj/libexec/login_lchpass
\ No newline at end of file
--- /dev/null
+/Makefile/1.5/Thu Mar 9 19:14:10 2006//
+/common.h/1.3/Thu Mar 9 19:14:10 2006//
+/login.c/1.9/Sun Apr 2 01:00:40 2006//
+/login_passwd.8/1.8/Thu May 31 19:19:40 2007//
+/login_passwd.c/1.9/Thu Mar 9 19:14:10 2006//
+D
--- /dev/null
+src/libexec/login_passwd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.5 2006/03/09 19:14:10 millert Exp $
+
+PROG= login_passwd
+MAN= login_passwd.8
+SRCS= login.c login_passwd.c pwd_gensalt.c
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+CFLAGS+=-Wall -DPASSWD
+.PATH: ${.CURDIR}/../../usr.bin/passwd
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: common.h,v 1.3 2006/03/09 19:14:10 millert Exp $ */
+/*-
+ * Copyright (c) 2001 Hans Insulander <hin@openbsd.org>.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 _COMMON_H_
+#define _COMMON_H_
+
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+
+#include <signal.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <login_cap.h>
+#include <bsd_auth.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <err.h>
+#include <util.h>
+
+
+#define MODE_LOGIN 0
+#define MODE_CHALLENGE 1
+#define MODE_RESPONSE 2
+
+#define AUTH_OK 0
+#define AUTH_FAILED -1
+
+extern FILE *back;
+
+#ifdef PASSWD
+int pwd_login(char *, char *, char *, int, char *);
+int pwd_gensalt(char *, int, login_cap_t *, char);
+#endif
+#ifdef KRB5
+int krb5_login(char *, char *, char *, int, int);
+#endif
+
+#endif /* !_COMMON_H_ */
--- /dev/null
+/* $OpenBSD: login.c,v 1.9 2006/04/02 01:00:40 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_passwd.c,v 1.11 1997/08/08 18:58:24 prb Exp $
+ */
+
+#include "common.h"
+
+FILE *back = NULL;
+
+int
+main(int argc, char **argv)
+{
+ int opt, mode = 0, ret, lastchance = 0;
+ char *username, *password = NULL;
+ char response[1024];
+ int arg_login = 0, arg_notickets = 0;
+ char invokinguser[MAXLOGNAME];
+ char *wheel = NULL, *class = NULL;
+
+ invokinguser[0] = '\0';
+
+ setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog(NULL, LOG_ODELAY, LOG_AUTH);
+
+ while ((opt = getopt(argc, argv, "ds:v:")) != -1) {
+ switch (opt) {
+ case 'd':
+ back = stdout;
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") == 0)
+ mode = MODE_LOGIN;
+ else if (strcmp(optarg, "challenge") == 0)
+ mode = MODE_CHALLENGE;
+ else if (strcmp(optarg, "response") == 0)
+ mode = MODE_RESPONSE;
+ else {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ case 'v':
+ if (strncmp(optarg, "wheel=", 6) == 0)
+ wheel = optarg + 6;
+ else if (strncmp(optarg, "lastchance=", 11) == 0)
+ lastchance = (strcmp(optarg + 11, "yes") == 0);
+ else if (strcmp(optarg, "login=yes") == 0)
+ arg_login = 1;
+ else if (strcmp(optarg, "notickets=yes") == 0)
+ arg_notickets = 1;
+ else if (strncmp(optarg, "invokinguser=", 13) == 0)
+ snprintf(invokinguser, sizeof(invokinguser),
+ "%s", &optarg[13]);
+ /* Silently ignore unsupported variables */
+ break;
+ default:
+ syslog(LOG_ERR, "usage error1");
+ exit(1);
+ }
+ }
+
+ switch (argc - optind) {
+ case 2:
+ class = argv[optind + 1];
+ /*FALLTHROUGH*/
+ case 1:
+ username = argv[optind];
+ break;
+ default:
+ syslog(LOG_ERR, "usage error2");
+ exit(1);
+ }
+
+ if (back == NULL && (back = fdopen(3, "r+")) == NULL) {
+ syslog(LOG_ERR, "reopening back channel: %m");
+ exit(1);
+ }
+
+ /*
+ * Read password, either as from the terminal or if the
+ * response mode is active from the caller program.
+ *
+ * XXX This is completely ungrokkable, and should be rewritten.
+ */
+ switch (mode) {
+ case MODE_RESPONSE: {
+ int count;
+ mode = 0;
+ count = -1;
+ while (++count < sizeof(response) &&
+ read(3, &response[count], (size_t)1) == (ssize_t)1) {
+ if (response[count] == '\0' && ++mode == 2)
+ break;
+ if (response[count] == '\0' && mode == 1) {
+ password = response + count + 1;
+ }
+ }
+ if (mode < 2) {
+ syslog(LOG_ERR, "protocol error on back channel");
+ exit(1);
+ }
+ break;
+ }
+
+ case MODE_LOGIN:
+ password = getpass("Password:");
+ break;
+ case MODE_CHALLENGE:
+ fprintf(back, BI_AUTH "\n");
+ exit(0);
+ break;
+ default:
+ syslog(LOG_ERR, "%d: unknown mode", mode);
+ exit(1);
+ break;
+ }
+
+ ret = AUTH_FAILED;
+#ifdef KRB5
+ ret = krb5_login(username, invokinguser, password, arg_login,
+ !arg_notickets);
+#endif
+#ifdef PASSWD
+ if (ret != AUTH_OK)
+ ret = pwd_login(username, password, wheel, lastchance, class);
+#endif
+
+ if (password != NULL)
+ memset(password, 0, strlen(password));
+ if (ret != AUTH_OK)
+ fprintf(back, BI_REJECT "\n");
+
+ closelog();
+
+ exit(0);
+}
--- /dev/null
+.\" $OpenBSD: login_passwd.8,v 1.8 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_PASSWD 8
+.Os
+.Sh NAME
+.Nm login_passwd
+.Nd provide standard password authentication type
+.Sh SYNOPSIS
+.Nm login_passwd
+.Op Fl s Ar service
+.Op Fl v Ar wheel=yes|no
+.Op Fl v Ar lastchance=yes|no
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility is called by
+.Xr login 1 ,
+.Xr su 1 ,
+.Xr ftpd 8 ,
+and others to authenticate the
+.Ar user
+with passwd-style authentication.
+.Pp
+The
+.Ar user
+argument is the login name of the user to be authenticated.
+.Pp
+The
+.Ar service
+argument specifies which protocol to use with the
+invoking program.
+The allowed protocols are
+.Em login ,
+.Em challenge ,
+and
+.Em response .
+(The
+.Em challenge
+protocol is silently ignored but will report success as passwd-style
+authentication is not challenge-response based).
+.Pp
+If the
+.Ar wheel
+argument is specified and is not set to
+.Dq yes ,
+then the user will be rejected as not being in group
+.Dq wheel .
+This is used by
+.Xr su 1 .
+.Pp
+If the
+.Ar lastchance
+argument is specified and is equal to
+.Dq yes ,
+then if the user's password has expired, and it has not been
+expired longer than
+.Dq password-dead
+seconds (see
+.Xr login.conf 5 ) ,
+the user will be able to log in one last time to change the password.
+.Pp
+.Nm
+will prompt the user for a password and report back to the
+invoking program whether or not the authentication was
+successful.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr passwd 1 ,
+.Xr su 1 ,
+.Xr login.conf 5 ,
+.Xr ftpd 8
--- /dev/null
+/* $OpenBSD: login_passwd.c,v 1.9 2006/03/09 19:14:10 millert Exp $ */
+
+/*-
+ * Copyright (c) 2001 Hans Insulander <hin@openbsd.org>.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include "common.h"
+
+int
+pwd_login(char *username, char *password, char *wheel, int lastchance,
+ char *class)
+{
+ struct passwd *pwd;
+ login_cap_t *lc;
+ size_t plen;
+ char *salt, saltbuf[_PASSWORD_LEN + 1];
+
+ if (wheel != NULL && strcmp(wheel, "yes") != 0) {
+ fprintf(back, BI_VALUE " errormsg %s\n",
+ auth_mkvalue("you are not in group wheel"));
+ fprintf(back, BI_REJECT "\n");
+ return (AUTH_FAILED);
+ }
+ if (password == NULL)
+ return (AUTH_FAILED);
+
+ pwd = getpwnam(username);
+ if (pwd)
+ salt = pwd->pw_passwd;
+ else {
+ /* no such user, get appropriate salt */
+ if ((lc = login_getclass(NULL)) == NULL ||
+ pwd_gensalt(saltbuf, sizeof(saltbuf), lc, 'l') == 0)
+ salt = "xx";
+ else
+ salt = saltbuf;
+ }
+
+ setpriority(PRIO_PROCESS, 0, -4);
+
+ salt = crypt(password, salt);
+ plen = strlen(password);
+ memset(password, 0, plen);
+
+ /*
+ * Authentication fails if the user does not exist in the password
+ * database, the given password does not match the entry in the
+ * password database, or if the user's password field is empty
+ * and the given password is not the empty string.
+ */
+ if (!pwd || strcmp(salt, pwd->pw_passwd) != 0 ||
+ (*pwd->pw_passwd == '\0' && plen > 0))
+ return (AUTH_FAILED);
+
+ if (login_check_expire(back, pwd, class, lastchance) == 0)
+ fprintf(back, BI_AUTH "\n");
+ else
+ return (AUTH_FAILED);
+
+ return (AUTH_OK);
+}
--- /dev/null
+/usr/obj/libexec/login_passwd
\ No newline at end of file
--- /dev/null
+/Makefile/1.2/Thu Nov 21 22:26:32 2002//
+/login_radius.8/1.10/Fri Dec 14 14:23:25 2007//
+/login_radius.c/1.6/Wed Mar 21 03:32:28 2007//
+/login_radius.h/1.1/Sun Jul 6 21:54:38 2003//
+/raddauth.c/1.23/Fri Dec 14 14:23:25 2007//
+D
--- /dev/null
+src/libexec/login_radius
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2002/11/21 22:26:32 millert Exp $
+
+PROG= login_radius
+SRCS= login_radius.c raddauth.c
+MAN= login_radius.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+CFLAGS+=-Wall
+
+BINOWN= root
+BINGRP= _radius
+BINMODE=2555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_radius.8,v 1.10 2007/12/14 14:23:25 millert Exp $
+.\"
+.\" Copyright (c) 1996 Berkeley Software Design, Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 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 Berkeley Software Design,
+.\" Inc.
+.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+.\"
+.\" BSDI $From: login_radius.8,v 1.2 1996/11/11 18:42:02 prb Exp $
+.\"
+.Dd $Mdocdate: December 14 2007 $
+.Dt LOGIN_RADIUS 8
+.Os
+.Sh NAME
+.Nm login_radius
+.Nd contact radiusd for authentication
+.Sh SYNOPSIS
+.Nm login_radius
+.Op Fl d
+.Op Fl s Ar service
+.Op Fl v Ar name=value
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility contacts the
+.Xr radiusd
+daemon to authenticate a
+.Ar user .
+If no
+.Ar class
+is specified, the login class will be obtained from the password database.
+.Pp
+When executed as the name
+.Pa login_ Ns Ar style ,
+.Nm
+will request
+.Xr radiusd
+use the authentication specified by
+.Ar style .
+.Pp
+Available options are:
+.Bl -tag -width indent
+.It Fl d
+Debug mode.
+Output is sent to the standard output instead of the BSD
+authentication backchannel.
+.It Fl s
+Specify the service.
+Currently only
+.Li challenge ,
+.Li login ,
+and
+.Li response
+are supported.
+.It Fl v
+This option and its value are ignored.
+.El
+.Pp
+The
+.Nm
+utility needs to know a shared secret for each radius server it talks to.
+Shared secrets are stored in the file
+.Pa /etc/raddb/servers
+with the format:
+.Bd -literal -offset indent
+server shared_secret
+.Ed
+.Pp
+It is expected that rather than requesting the radius style directly
+(in which case the
+.Xr radiusd
+server uses a default style)
+that
+.Nm
+will be linked to the various mechanisms desired.
+For instance, to have all CRYPTOCard and ActivCard authentication take
+place on a remote server via the radius protocol, remove the
+.Pa login_activ
+and
+.Pa login_crypto
+modules and link
+.Pa login_radius
+to both of those names.
+Now when the user requests one of those authentication styles,
+.Nm
+will automatically forward the request to the remote
+.Xr radiusd
+and request it do the requested style of authentication.
+.Sh LOGIN.CONF VARIABLES
+The
+.Nm
+utility uses the following radius-specific
+.Pa /etc/login.conf
+variables:
+.Bl -tag -width radius-challenge-styles
+.It radius-port
+Port name or number to connect to on the radius server.
+.It radius-server
+Hostname of the radius server to contact.
+.It radius-server-alt
+Alternate radius server to use when the primary is not responding.
+.It radius-challenge-styles
+Comma-separated list of authentication styles that the radius server
+knows about.
+If the user's authentication style is in this list the challenge will
+be provided by the radius server.
+If not,
+.Nm
+will prompt the user for the password before sending the request
+(along with the password) to the radius server.
+.It radius-timeout
+Number of seconds to wait for a response from the radius server.
+Defaults to 2 seconds.
+.It radius-retries
+Number of times to attempt to contact the radius server before giving up
+(or falling back to the alternate server if there is one).
+Defaults to 6 tries.
+.El
+.Sh FILES
+.Bl -tag -compact -width xetcxraddbxserversxx
+.It Pa /etc/login.conf
+login configuration database
+.It Pa /etc/raddb/servers
+list of radius servers and their associated shared secrets
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr login.conf 5
+.Sh CAVEATS
+.Ox
+does not ship with a radius server in the default install, however
+several are available via
+.Xr packages 7 .
+.Pp
+For
+.Nm
+to function, the
+.Pa /etc/raddb
+directory must be owned by group
+.Dq _radius
+and have group-execute permissions.
+Likewise, the
+.Pa /etc/raddb/servers
+file must be readable by group
+.Dq _radius .
--- /dev/null
+/* $OpenBSD: login_radius.c,v 1.6 2007/03/21 03:32:28 tedu Exp $ */
+
+/*-
+ * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_radius.c,v 1.4 1998/04/14 00:39:02 prb Exp $
+ */
+/*
+ * Copyright(c) 1996 by tfm associates.
+ * All rights reserved.
+ *
+ * tfm associates
+ * P.O. Box 2086
+ * Eugene OR 97402-0031
+ *
+ * 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. The name of tfm associates may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TFM ASSOC``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 TFM ASSOCIATES 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.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <login_cap.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <bsd_auth.h>
+#include "login_radius.h"
+
+static int cleanstring(char *);
+
+int
+main(int argc, char **argv)
+{
+ FILE *back;
+ char challenge[1024];
+ char *class, *service, *style, *username, *password, *emsg;
+ int c, n;
+ extern char *__progname;
+
+ back = NULL;
+ password = class = service = NULL;
+
+ /*
+ * Usage: login_xxx [-s service] [-v var] user [class]
+ */
+ while ((c = getopt(argc, argv, "ds:v:")) != -1)
+ switch (c) {
+ case 'd':
+ back = stdout;
+ break;
+ case 'v':
+ break;
+ case 's': /* service */
+ service = optarg;
+ if (strcmp(service, "login") != 0 &&
+ strcmp(service, "challenge") != 0 &&
+ strcmp(service, "response") != 0)
+ errx(1, "%s not supported", service);
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ if (service == NULL)
+ service = LOGIN_DEFSERVICE;
+
+ switch (argc - optind) {
+ case 2:
+ class = argv[optind + 1];
+ case 1:
+ username = argv[optind];
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ if ((style = strrchr(__progname, '/')))
+ ++style;
+ else
+ style = __progname;
+
+ if (strncmp(style, "login_", 6) == 0)
+ style += 6;
+
+ if (!cleanstring(style))
+ errx(1, "style contains non-printables");
+ if (!cleanstring(username))
+ errx(1, "username contains non-printables");
+ if (service && !cleanstring(service))
+ errx(1, "service contains non-printables");
+ if (class && !cleanstring(class))
+ errx(1, "class contains non-printables");
+
+ /*
+ * Filedes 3 is the back channel, where we send back results.
+ */
+ if (back == NULL && (back = fdopen(3, "a")) == NULL) {
+ syslog(LOG_ERR, "reopening back channel");
+ exit(1);
+ }
+
+ memset(challenge, 0, sizeof(challenge));
+
+ if (strcmp(service, "response") == 0) {
+ c = -1;
+ n = 0;
+ while (++c < sizeof(challenge) &&
+ read(3, &challenge[c], 1) == 1) {
+ if (challenge[c] == '\0' && ++n == 2)
+ break;
+ if (challenge[c] == '\0' && n == 1)
+ password = challenge + c + 1;
+ }
+ if (n < 2) {
+ syslog(LOG_ERR, "protocol error on back channel");
+ exit(1);
+ }
+ }
+
+ emsg = NULL;
+
+ c = raddauth(username, class, style,
+ strcmp(service, "login") ? challenge : NULL, password, &emsg);
+
+ if (c == 0) {
+ if (challenge == NULL || *challenge == '\0')
+ (void)fprintf(back, BI_AUTH "\n");
+ else {
+ (void)fprintf(back, BI_VALUE " challenge %s\n",
+ auth_mkvalue(challenge));
+ (void)fprintf(back, BI_CHALLENGE "\n");
+ }
+ exit(0);
+ }
+ if (emsg && strcmp(service, "login") == 0)
+ (void)fprintf(stderr, "%s\n", emsg);
+ else if (emsg)
+ (void)fprintf(back, "value errormsg %s\n", auth_mkvalue(emsg));
+ if (strcmp(service, "challenge") == 0) {
+ (void)fprintf(back, BI_SILENT "\n");
+ exit(0);
+ }
+ (void)fprintf(back, BI_REJECT "\n");
+ exit(1);
+}
+
+static int
+cleanstring(char *s)
+{
+
+ while (isgraph(*s) && *s != '\\')
+ ++s;
+ return(*s == '\0' ? 1 : 0);
+}
--- /dev/null
+/* $OpenBSD: login_radius.h,v 1.1 2003/07/06 21:54:38 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2003 Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int
+raddauth(char *username, char *class, char *style, char *challenge,
+ char *password, char **emsg);
--- /dev/null
+/usr/obj/libexec/login_radius
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: raddauth.c,v 1.23 2007/12/14 14:23:25 millert Exp $ */
+
+/*-
+ * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: raddauth.c,v 1.6 1998/04/14 00:39:04 prb Exp $
+ */
+/*
+ * Copyright(c) 1996 by tfm associates.
+ * All rights reserved.
+ *
+ * tfm associates
+ * P.O. Box 2086
+ * Eugene OR 97402-0031
+ *
+ * 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. The name of tfm associates may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TFM ASSOC``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 TFM ASSOCIATES 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.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <login_cap.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <md5.h>
+#include "login_radius.h"
+
+
+#define MAXPWNETNAM 64 /* longest username */
+#define MAXSECRETLEN 128 /* maximum length of secret */
+
+#define AUTH_VECTOR_LEN 16
+#define AUTH_HDR_LEN 20
+#define AUTH_PASS_LEN (256 - 16)
+#define PW_AUTHENTICATION_REQUEST 1
+#define PW_AUTHENTICATION_ACK 2
+#define PW_AUTHENTICATION_REJECT 3
+#define PW_ACCESS_CHALLENGE 11
+#define PW_USER_NAME 1
+#define PW_PASSWORD 2
+#define PW_CLIENT_ID 4
+#define PW_CLIENT_PORT_ID 5
+#define PW_PORT_MESSAGE 18
+#define PW_STATE 24
+
+#ifndef RADIUS_DIR
+#define RADIUS_DIR "/etc/raddb"
+#endif
+#define RADIUS_SERVERS "servers"
+
+char *radius_dir = RADIUS_DIR;
+char auth_secret[MAXSECRETLEN+1];
+volatile sig_atomic_t timedout;
+int alt_retries;
+int retries;
+int sockfd;
+int timeout;
+in_addr_t alt_server;
+in_addr_t auth_server;
+in_port_t radius_port;
+
+typedef struct {
+ u_char code;
+ u_char id;
+ u_short length;
+ u_char vector[AUTH_VECTOR_LEN];
+ u_char data[4096 - AUTH_HDR_LEN];
+} auth_hdr_t;
+
+void servtimeout(int);
+in_addr_t get_ipaddr(char *);
+in_addr_t gethost(void);
+int rad_recv(char *, char *, u_char *);
+void parse_challenge(auth_hdr_t *, char *, char *);
+void rad_request(u_char, char *, char *, int, char *, char *);
+void getsecret(void);
+
+/*
+ * challenge -- NULL for interactive service
+ * password -- NULL for interactive service and when requesting a challenge
+ */
+int
+raddauth(char *username, char *class, char *style, char *challenge,
+ char *password, char **emsg)
+{
+ static char _pwstate[1024];
+ u_char req_id;
+ char *userstyle, *passwd, *pwstate, *rad_service;
+ int auth_port;
+ char vector[AUTH_VECTOR_LEN+1], *p, *v;
+ int i;
+ login_cap_t *lc;
+ u_int32_t r;
+ struct servent *svp;
+ struct sockaddr_in sin;
+ struct sigaction sa;
+ const char *errstr;
+
+ memset(_pwstate, 0, sizeof(_pwstate));
+ pwstate = password ? challenge : _pwstate;
+
+ if ((lc = login_getclass(class)) == NULL) {
+ snprintf(_pwstate, sizeof(_pwstate),
+ "%s: no such class", class);
+ *emsg = _pwstate;
+ return (1);
+ }
+
+ rad_service = login_getcapstr(lc, "radius-port", "radius", "radius");
+ timeout = login_getcapnum(lc, "radius-timeout", 2, 2);
+ retries = login_getcapnum(lc, "radius-retries", 6, 6);
+
+ if (timeout < 1)
+ timeout = 1;
+ if (retries < 2)
+ retries = 2;
+
+ if (challenge == NULL) {
+ passwd = NULL;
+ v = login_getcapstr(lc, "radius-challenge-styles",
+ NULL, NULL);
+ i = strlen(style);
+ while (v && (p = strstr(v, style)) != NULL) {
+ if ((p == v || p[-1] == ',') &&
+ (p[i] == ',' || p[i] == '\0')) {
+ passwd = "";
+ break;
+ }
+ v = p+1;
+ }
+ if (passwd == NULL)
+ passwd = getpass("Password:");
+ } else
+ passwd = password;
+ if (passwd == NULL)
+ passwd = "";
+
+ if ((v = login_getcapstr(lc, "radius-server", NULL, NULL)) == NULL){
+ *emsg = "radius-server not configured";
+ return (1);
+ }
+
+ auth_server = get_ipaddr(v);
+
+ if ((v = login_getcapstr(lc, "radius-server-alt", NULL, NULL)) == NULL)
+ alt_server = 0;
+ else {
+ alt_server = get_ipaddr(v);
+ alt_retries = retries/2;
+ retries >>= 1;
+ }
+
+ /* get port number */
+ radius_port = strtonum(rad_service, 1, UINT16_MAX, &errstr);
+ if (errstr) {
+ svp = getservbyname(rad_service, "udp");
+ if (svp == NULL) {
+ snprintf(_pwstate, sizeof(_pwstate),
+ "No such service: %s/udp", rad_service);
+ *emsg = _pwstate;
+ return (1);
+ }
+ radius_port = svp->s_port;
+ } else
+ radius_port = htons(radius_port);
+
+ /* get the secret from the servers file */
+ getsecret();
+
+ /* set up socket */
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ snprintf(_pwstate, sizeof(_pwstate), "%s", strerror(errno));
+ *emsg = _pwstate;
+ return (1);
+ }
+
+ /* set up client structure */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = radius_port;
+
+ req_id = (u_char) arc4random();
+ auth_port = ttyslot();
+ if (auth_port == 0)
+ auth_port = (int)getppid();
+ if (strcmp(style, "radius") != 0) {
+ if (asprintf(&userstyle, "%s:%s", username, style) == -1)
+ err(1, NULL);
+ } else
+ userstyle = username;
+
+ /* generate random vector */
+ for (i = 0; i < AUTH_VECTOR_LEN;) {
+ r = arc4random();
+ memcpy(&vector[i], &r, sizeof(r));
+ i += sizeof(r);
+ }
+ vector[AUTH_VECTOR_LEN] = '\0';
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = servtimeout;
+ sa.sa_flags = 0; /* don't restart system calls */
+ (void)sigaction(SIGALRM, &sa, NULL);
+retry:
+ if (timedout) {
+ timedout = 0;
+ if (--retries <= 0) {
+ /*
+ * If we ran out of tries but there is an alternate
+ * server, switch to it and try again.
+ */
+ if (alt_retries) {
+ auth_server = alt_server;
+ retries = alt_retries;
+ alt_retries = 0;
+ getsecret();
+ } else
+ warnx("no response from authentication server");
+ }
+ }
+
+ if (retries > 0) {
+ rad_request(req_id, userstyle, passwd, auth_port, vector,
+ pwstate);
+
+ switch (i = rad_recv(_pwstate, challenge, vector)) {
+ case PW_AUTHENTICATION_ACK:
+ /*
+ * Make sure we don't think a challenge was issued.
+ */
+ if (challenge)
+ *challenge = '\0';
+ return (0);
+
+ case PW_AUTHENTICATION_REJECT:
+ return (1);
+
+ case PW_ACCESS_CHALLENGE:
+ /*
+ * If this is a response then reject them if
+ * we got a challenge.
+ */
+ if (password)
+ return (1);
+ /*
+ * If we wanted a challenge, just return
+ */
+ if (challenge) {
+ if (strcmp(challenge, _pwstate) != 0)
+ syslog(LOG_WARNING,
+ "challenge for %s does not match state",
+ userstyle);
+ return (0);
+ }
+ req_id++;
+ if ((passwd = getpass("")) == NULL)
+ passwd = "";
+ break;
+
+ default:
+ if (timedout)
+ goto retry;
+ snprintf(_pwstate, sizeof(_pwstate),
+ "invalid response type %d\n", i);
+ *emsg = _pwstate;
+ return (1);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Build a radius authentication digest and submit it to the radius server
+ */
+void
+rad_request(u_char id, char *name, char *password, int port, char *vector,
+ char *state)
+{
+ auth_hdr_t auth;
+ int i, len, secretlen, total_length, p;
+ struct sockaddr_in sin;
+ u_char md5buf[MAXSECRETLEN+AUTH_VECTOR_LEN], digest[AUTH_VECTOR_LEN],
+ pass_buf[AUTH_PASS_LEN], *pw, *ptr;
+ u_int length;
+ in_addr_t ipaddr;
+ MD5_CTX context;
+
+ memset(&auth, 0, sizeof(auth));
+ auth.code = PW_AUTHENTICATION_REQUEST;
+ auth.id = id;
+ memcpy(auth.vector, vector, AUTH_VECTOR_LEN);
+ total_length = AUTH_HDR_LEN;
+ ptr = auth.data;
+
+ /* User name */
+ *ptr++ = PW_USER_NAME;
+ length = strlen(name);
+ if (length > MAXPWNETNAM)
+ length = MAXPWNETNAM;
+ *ptr++ = length + 2;
+ memcpy(ptr, name, length);
+ ptr += length;
+ total_length += length + 2;
+
+ /* Password */
+ length = strlen(password);
+ if (length > AUTH_PASS_LEN)
+ length = AUTH_PASS_LEN;
+
+ p = (length + AUTH_VECTOR_LEN - 1) / AUTH_VECTOR_LEN;
+ *ptr++ = PW_PASSWORD;
+ *ptr++ = p * AUTH_VECTOR_LEN + 2;
+
+ memset(pass_buf, 0, sizeof(pass_buf)); /* must zero fill */
+ strlcpy((char *)pass_buf, password, sizeof(pass_buf));
+
+ /* Calculate the md5 digest */
+ secretlen = strlen(auth_secret);
+ memcpy(md5buf, auth_secret, secretlen);
+ memcpy(md5buf + secretlen, auth.vector, AUTH_VECTOR_LEN);
+
+ total_length += 2;
+
+ /* XOR the password into the md5 digest */
+ pw = pass_buf;
+ while (p-- > 0) {
+ MD5Init(&context);
+ MD5Update(&context, md5buf, secretlen + AUTH_VECTOR_LEN);
+ MD5Final(digest, &context);
+ for (i = 0; i < AUTH_VECTOR_LEN; ++i) {
+ *ptr = digest[i] ^ *pw;
+ md5buf[secretlen+i] = *ptr++;
+ *pw++ = '\0';
+ }
+ total_length += AUTH_VECTOR_LEN;
+ }
+
+ /* Client id */
+ *ptr++ = PW_CLIENT_ID;
+ *ptr++ = sizeof(in_addr_t) + 2;
+ ipaddr = gethost();
+ memcpy(ptr, &ipaddr, sizeof(in_addr_t));
+ ptr += sizeof(in_addr_t);
+ total_length += sizeof(in_addr_t) + 2;
+
+ /* client port */
+ *ptr++ = PW_CLIENT_PORT_ID;
+ *ptr++ = sizeof(in_addr_t) + 2;
+ port = htonl(port);
+ memcpy(ptr, &port, sizeof(int));
+ ptr += sizeof(int);
+ total_length += sizeof(int) + 2;
+
+ /* Append the state info */
+ if ((state != NULL) && (strlen(state) > 0)) {
+ len = strlen(state);
+ *ptr++ = PW_STATE;
+ *ptr++ = len + 2;
+ memcpy(ptr, state, len);
+ ptr += len;
+ total_length += len + 2;
+ }
+
+ auth.length = htons(total_length);
+
+ memset(&sin, 0, sizeof (sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = auth_server;
+ sin.sin_port = radius_port;
+ if (sendto(sockfd, &auth, total_length, 0, (struct sockaddr *)&sin,
+ sizeof(sin)) == -1)
+ err(1, NULL);
+}
+
+/*
+ * Receive UDP responses from the radius server
+ */
+int
+rad_recv(char *state, char *challenge, u_char *req_vector)
+{
+ auth_hdr_t auth;
+ socklen_t salen;
+ struct sockaddr_in sin;
+ u_char recv_vector[AUTH_VECTOR_LEN], test_vector[AUTH_VECTOR_LEN];
+ MD5_CTX context;
+
+ salen = sizeof(sin);
+
+ alarm(timeout);
+ if ((recvfrom(sockfd, &auth, sizeof(auth), 0,
+ (struct sockaddr *)&sin, &salen)) < AUTH_HDR_LEN) {
+ if (timedout)
+ return(-1);
+ errx(1, "bogus auth packet from server");
+ }
+ alarm(0);
+
+ if (sin.sin_addr.s_addr != auth_server)
+ errx(1, "bogus authentication server");
+
+ /* verify server's shared secret */
+ memcpy(recv_vector, auth.vector, AUTH_VECTOR_LEN);
+ memcpy(auth.vector, req_vector, AUTH_VECTOR_LEN);
+ MD5Init(&context);
+ MD5Update(&context, (u_char *)&auth, ntohs(auth.length));
+ MD5Update(&context, auth_secret, strlen(auth_secret));
+ MD5Final(test_vector, &context);
+ if (memcmp(recv_vector, test_vector, AUTH_VECTOR_LEN) != 0)
+ errx(1, "shared secret incorrect");
+
+ if (auth.code == PW_ACCESS_CHALLENGE)
+ parse_challenge(&auth, state, challenge);
+
+ return (auth.code);
+}
+
+/*
+ * Get IP address of local hostname
+ */
+in_addr_t
+gethost(void)
+{
+ char hostname[MAXHOSTNAMELEN];
+
+ if (gethostname(hostname, sizeof(hostname)))
+ err(1, "gethost");
+ return (get_ipaddr(hostname));
+}
+
+/*
+ * Get an IP address in host in_addr_t notation from a hostname or dotted quad.
+ */
+in_addr_t
+get_ipaddr(char *host)
+{
+ struct hostent *hp;
+
+ if ((hp = gethostbyname(host)) == NULL)
+ return (0);
+
+ return (((struct in_addr *)hp->h_addr)->s_addr);
+}
+
+/*
+ * Get the secret from the servers file
+ */
+void
+getsecret(void)
+{
+ FILE *servfd;
+ char *host, *secret, buffer[MAXPATHLEN];
+ size_t len;
+
+ snprintf(buffer, sizeof(buffer), "%s/%s",
+ radius_dir, RADIUS_SERVERS);
+
+ if ((servfd = fopen(buffer, "r")) == NULL) {
+ syslog(LOG_ERR, "%s: %m", buffer);
+ return;
+ }
+
+ secret = NULL; /* Keeps gcc happy */
+ while ((host = fgetln(servfd, &len)) != NULL) {
+ if (*host == '#') {
+ memset(host, 0, len);
+ continue;
+ }
+ if (host[len-1] == '\n')
+ --len;
+ else {
+ /* No trailing newline, must allocate len+1 for NUL */
+ if ((secret = malloc(len + 1)) == NULL) {
+ memset(host, 0, len);
+ continue;
+ }
+ memcpy(secret, host, len);
+ memset(host, 0, len);
+ host = secret;
+ }
+ while (len > 0 && isspace(host[--len]))
+ ;
+ host[++len] = '\0';
+ while (isspace(*host)) {
+ ++host;
+ --len;
+ }
+ if (*host == '\0')
+ continue;
+ secret = host;
+ while (*secret && !isspace(*secret))
+ ++secret;
+ if (*secret)
+ *secret++ = '\0';
+ if (get_ipaddr(host) != auth_server) {
+ memset(host, 0, len);
+ continue;
+ }
+ while (isspace(*secret))
+ ++secret;
+ if (*secret)
+ break;
+ }
+ if (host) {
+ strlcpy(auth_secret, secret, sizeof(auth_secret));
+ memset(host, 0, len);
+ }
+ fclose(servfd);
+}
+
+void
+servtimeout(int signo)
+{
+
+ timedout = 1;
+}
+
+/*
+ * Parse a challenge received from the server
+ */
+void
+parse_challenge(auth_hdr_t *authhdr, char *state, char *challenge)
+{
+ int length;
+ int attribute, attribute_len;
+ u_char *ptr;
+
+ ptr = authhdr->data;
+ length = ntohs(authhdr->length) - AUTH_HDR_LEN;
+
+ *state = 0;
+
+ while (length > 0) {
+ attribute = *ptr++;
+ attribute_len = *ptr++;
+ length -= attribute_len;
+ attribute_len -= 2;
+
+ switch (attribute) {
+ case PW_PORT_MESSAGE:
+ if (challenge) {
+ memcpy(challenge, ptr, attribute_len);
+ challenge[attribute_len] = '\0';
+ } else
+ printf("%.*s", attribute_len, ptr);
+ break;
+ case PW_STATE:
+ memcpy(state, ptr, attribute_len);
+ state[attribute_len] = '\0';
+ break;
+ }
+ ptr += attribute_len;
+ }
+}
--- /dev/null
+/Makefile/1.2/Thu Mar 9 19:14:10 2006//
+/login_reject.8/1.6/Thu May 31 19:19:40 2007//
+/login_reject.c/1.8/Sun Apr 2 04:14:49 2006//
+D
--- /dev/null
+src/libexec/login_reject
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2006/03/09 19:14:10 millert Exp $
+
+PROG= login_reject
+SRCS= login_reject.c pwd_gensalt.c
+MAN= login_reject.8
+CFLAGS+=-Wall
+.PATH: ${.CURDIR}/../../usr.bin/passwd
+
+BINOWN= root
+BINGRP= auth
+BINMODE=555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_reject.8,v 1.6 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 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 Berkeley Software Design,
+.\" Inc.
+.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+.\"
+.\" BSDI $From: login_reject.8,v 1.2 1996/08/01 21:02:26 prb Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_REJECT 8
+.Os
+.Sh NAME
+.Nm login_reject
+.Nd provide rejected authentication
+.Sh SYNOPSIS
+.Nm login_reject
+.Op Fl s Ar service
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility provides the rejection authentication class.
+The
+.Ar user
+name, while required, is ignored.
+The
+.Ar class
+name, which is optional, is also ignored.
+The
+.Nm reject
+authentication mechanism is intended to be used to disallow certain
+types of logins.
+For example, a class entry (see
+.Xr login.conf 5 )
+may contain:
+.Bd -literal -offset indent
+:auth=krb-or-pwd,kerberos,passwd:
+:auth-ftp=reject:
+.Ed
+.Pp
+which would allow authentication for this class in most situations
+but would reject attempts to authenticate from
+.Xr ftpd 8 .
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr login.conf 5 ,
+.Xr ftpd 8
--- /dev/null
+/* $OpenBSD: login_reject.c,v 1.8 2006/04/02 04:14:49 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_reject.c,v 1.5 1996/08/22 20:43:11 prb Exp $
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <login_cap.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+extern int pwd_gensalt(char *, int, login_cap_t *, char);
+
+int
+main(int argc, char *argv[])
+{
+ struct rlimit rl;
+ login_cap_t *lc;
+ FILE *back;
+ char passbuf[1], salt[_PASSWORD_LEN + 1];
+ int mode = 0, c;
+
+ rl.rlim_cur = 0;
+ rl.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rl);
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog("login", LOG_ODELAY, LOG_AUTH);
+
+ while ((c = getopt(argc, argv, "v:s:")) != -1)
+ switch (c) {
+ case 'v':
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") == 0)
+ mode = 0;
+ else if (strcmp(optarg, "challenge") == 0)
+ mode = 1;
+ else if (strcmp(optarg, "response") == 0)
+ mode = 2;
+ else {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ switch (argc - optind) {
+ case 2:
+ case 1:
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ if (!(back = fdopen(3, "r+"))) {
+ syslog(LOG_ERR, "reopening back channel: %m");
+ exit(1);
+ }
+ if (mode == 1) {
+ fprintf(back, BI_SILENT "\n");
+ exit(0);
+ }
+
+ if (mode == 2) {
+ mode = 0;
+ c = -1;
+ while (read(3, passbuf, (size_t)1) == 1) {
+ if (passbuf[0] == '\0' && ++mode == 2)
+ break;
+ }
+ if (mode < 2) {
+ syslog(LOG_ERR, "protocol error on back channel");
+ exit(1);
+ }
+ } else
+ getpass("Password:");
+
+ if ((lc = login_getclass(NULL)) == NULL ||
+ pwd_gensalt(salt, sizeof(salt), lc, 'l') == 0)
+ strlcpy(salt, "xx", sizeof(salt));
+ crypt("password", salt);
+
+ fprintf(back, BI_REJECT "\n");
+ exit(1);
+}
--- /dev/null
+/usr/obj/libexec/login_reject
\ No newline at end of file
--- /dev/null
+/Makefile/1.2/Thu May 16 03:51:23 2002//
+/login_skey.8/1.9/Thu May 31 19:19:40 2007//
+/login_skey.c/1.23/Tue Jun 2 20:42:48 2009//
+D
--- /dev/null
+src/libexec/login_skey
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2002/05/16 03:51:23 millert Exp $
+
+PROG= login_skey
+MAN= login_skey.8
+DPADD= ${LIBSKEY}
+LDADD= -lskey
+CFLAGS+=-Wall
+
+BINOWN= root
+BINGRP= auth
+BINMODE=2555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_skey.8,v 1.9 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Sponsored in part by the Defense Advanced Research Projects
+.\" Agency (DARPA) and Air Force Research Laboratory, Air Force
+.\" Materiel Command, USAF, under agreement number F39502-99-1-0512.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_SKEY 8
+.Os
+.Sh NAME
+.Nm login_skey
+.Nd provide S/Key authentication type
+.Sh SYNOPSIS
+.Nm login_skey
+.Op Fl s Ar service
+.Op Fl v Ar fd=number
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility is called by
+.Xr login 1 ,
+.Xr su 1 ,
+.Xr ftpd 8 ,
+and others to authenticate the
+.Ar user
+with S/Key authentication.
+.Pp
+The
+.Ar service
+argument specifies which protocol to use with the
+invoking program.
+The allowed protocols are
+.Em login ,
+.Em challenge ,
+and
+.Em response .
+The default protocol is
+.Em login .
+.Pp
+The
+.Ar fd
+argument is used to specify the number of an open, locked file descriptor
+that references the user's S/Key entry.
+This is used to prevent simultaneous S/Key authorization attempts from
+using the same challenge.
+.Pp
+The
+.Ar user
+argument is the login name of the user to be authenticated.
+.Pp
+The optional
+.Ar class
+argument is accepted for consistency with the other login scripts but
+is not used.
+.Pp
+.Nm
+will look up
+.Ar user
+in the S/Key database and, depending on the desired protocol,
+will do one of three things:
+.Bl -tag -width challenge
+.It login
+Present
+.Ar user
+with an S/Key challenge, accept a response and report back to the
+invoking program whether or not the authentication was successful.
+.It challenge
+Return the current S/Key challenge for
+.Ar user .
+.It response
+Report back to the invoking program whether or not the specified
+response matches the current S/Key challenge for
+.Ar user .
+.El
+.Pp
+If
+.Ar user
+does not have an entry in the S/Key database, a fake challenge will
+be generated by the S/Key library.
+.Sh FILES
+.Bl -tag -width /etc/skey
+.It Pa /etc/skey
+directory containing user entries for S/Key
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr skey 1 ,
+.Xr skeyinfo 1 ,
+.Xr skeyinit 1 ,
+.Xr login.conf 5 ,
+.Xr ftpd 8
--- /dev/null
+/* $OpenBSD: login_skey.c,v 1.23 2009/06/02 20:42:48 jmeltzer Exp $ */
+
+/*
+ * Copyright (c) 2000, 2001, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <readpassphrase.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <login_cap.h>
+#include <bsd_auth.h>
+#include <skey.h>
+
+#define MODE_LOGIN 0
+#define MODE_CHALLENGE 1
+#define MODE_RESPONSE 2
+
+void quit(int);
+void send_fd(int);
+void suspend(int);
+
+volatile sig_atomic_t resumed;
+struct skey skey;
+
+int
+main(int argc, char *argv[])
+{
+ FILE *back = NULL;
+ char *user = NULL, *cp, *ep;
+ char challenge[SKEY_MAX_CHALLENGE+17], response[SKEY_MAX_PW_LEN+1];
+ const char *errstr;
+ int ch, fd = -1, haskey = 0, mode = MODE_LOGIN;
+
+ (void)signal(SIGINT, quit);
+ (void)signal(SIGQUIT, quit);
+ (void)signal(SIGALRM, quit);
+ (void)signal(SIGTSTP, suspend);
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ openlog(NULL, LOG_ODELAY, LOG_AUTH);
+
+ while ((ch = getopt(argc, argv, "ds:v:")) != -1) {
+ switch (ch) {
+ case 'd':
+ back = stdout;
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") == 0)
+ mode = MODE_LOGIN;
+ else if (strcmp(optarg, "challenge") == 0)
+ mode = MODE_CHALLENGE;
+ else if (strcmp(optarg, "response") == 0)
+ mode = MODE_RESPONSE;
+ else {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ case 'v':
+ if (strncmp(optarg, "fd=", 3) == 0) {
+ fd = strtonum(optarg + 3, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ syslog(LOG_ERR, "fd is %s: %s",
+ errstr, optarg + 3);
+ fd = -1;
+ }
+ }
+ /* silently ignore unsupported variables */
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 2: /* silently ignore class */
+ case 1:
+ user = *argv;
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ if (back == NULL && (back = fdopen(3, "r+")) == NULL) {
+ syslog(LOG_ERR, "reopening back channel: %m");
+ exit(1);
+ }
+
+ /*
+ * Note: our skeychallenge2() will always fill in the challenge,
+ * even if it has to create a fake one.
+ */
+ switch (mode) {
+ case MODE_LOGIN:
+ haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
+ strlcat(challenge, "\nS/Key Password:", sizeof(challenge));
+
+ /* time out getting passphrase after 2 minutes to avoid a DoS */
+ if (haskey)
+ alarm(120);
+ resumed = 0;
+ if (!readpassphrase(challenge, response, sizeof(response), 0))
+ exit(1);
+ if (response[0] == '\0')
+ readpassphrase("S/Key Password [echo on]: ",
+ response, sizeof(response), RPP_ECHO_ON);
+ alarm(0);
+ if (resumed) {
+ /*
+ * We were suspended by the user. Our lock is
+ * no longer valid so we must regain it so
+ * an attacker cannot do a partial guess of
+ * an S/Key response already in progress.
+ */
+ haskey = (skeylookup(&skey, user) == 0);
+ }
+ break;
+
+ case MODE_CHALLENGE:
+ haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
+ strlcat(challenge, "\nS/Key Password:", sizeof(challenge));
+ fprintf(back, BI_VALUE " challenge %s\n",
+ auth_mkvalue(challenge));
+ fprintf(back, BI_CHALLENGE "\n");
+ fprintf(back, BI_FDPASS "\n");
+ fflush(back);
+ send_fd(fileno(back));
+ exit(0);
+
+ case MODE_RESPONSE:
+ /* read challenge */
+ mode = -1;
+ cp = challenge;
+ ep = challenge + sizeof(challenge);
+ while (cp < ep && read(fileno(back), cp, 1) == 1) {
+ if (*cp++ == '\0') {
+ mode = MODE_CHALLENGE;
+ break;
+ }
+ }
+ if (mode != MODE_CHALLENGE) {
+ syslog(LOG_ERR,
+ "protocol error: bad/missing challenge");
+ exit(1);
+ }
+
+ /* read response */
+ cp = response;
+ ep = response + sizeof(response);
+ while (cp < ep && read(fileno(back), cp, 1) == 1) {
+ if (*cp++ == '\0') {
+ mode = MODE_RESPONSE;
+ break;
+ }
+ }
+ if (mode != MODE_RESPONSE) {
+ syslog(LOG_ERR,
+ "protocol error: bad/missing response");
+ exit(1);
+ }
+
+ /*
+ * Since the entry is locked we do not need to compare
+ * the passed in challenge to the S/Key database but
+ * maybe we should anyway?
+ */
+ haskey = (skeychallenge2(fd, &skey, user, challenge) == 0);
+ break;
+ }
+
+ /*
+ * Ignore keyboard interrupt/suspend during database update.
+ */
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTSTP, SIG_IGN);
+
+ if (haskey && skeyverify(&skey, response) == 0) {
+ if (mode == MODE_LOGIN) {
+ if (skey.n <= 1)
+ printf("Warning! You MUST change your "
+ "S/Key password now!\n");
+ else if (skey.n < 5)
+ printf("Warning! Change S/Key password soon\n");
+ }
+ fprintf(back, BI_AUTH "\n");
+ fprintf(back, BI_SECURE "\n");
+ exit(0);
+ }
+ fprintf(back, BI_REJECT "\n");
+ exit(1);
+}
+
+/* ARGSUSED */
+void
+quit(int signo)
+{
+
+ _exit(1);
+}
+
+/* ARGSUSED */
+void
+suspend(int signo)
+{
+ sigset_t nset;
+ int save_errno = errno;
+
+ /*
+ * Unlock the skey record so we don't sleep holding the lock.
+ * Unblock SIGTSTP, set it to the default action and then
+ * resend it so we are suspended properly.
+ * When we resume, reblock SIGTSTP, reset the signal handler,
+ * set a flag and restore errno.
+ */
+ alarm(0);
+ skey_unlock(&skey);
+ (void)signal(signo, SIG_DFL);
+ (void)sigemptyset(&nset);
+ (void)sigaddset(&nset, signo);
+ (void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
+ (void)kill(getpid(), signo);
+ (void)sigprocmask(SIG_BLOCK, &nset, NULL);
+ (void)signal(signo, suspend);
+ resumed = 1;
+ errno = save_errno;
+}
+
+void
+send_fd(int sock)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmp;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ cmp = CMSG_FIRSTHDR(&msg);
+ cmp->cmsg_len = CMSG_LEN(sizeof(int));
+ cmp->cmsg_level = SOL_SOCKET;
+ cmp->cmsg_type = SCM_RIGHTS;
+
+ *(int *)CMSG_DATA(cmp) = fileno(skey.keyfile);
+
+ if (sendmsg(sock, &msg, 0) < 0)
+ syslog(LOG_ERR, "sendmsg: %m");
+}
--- /dev/null
+/usr/obj/libexec/login_skey
\ No newline at end of file
--- /dev/null
+/Makefile/1.1/Tue Sep 28 15:02:01 2004//
+/login_tis.8/1.3/Thu May 31 19:19:40 2007//
+/login_tis.c/1.9/Mon Mar 24 16:11:00 2008//
+/login_tis.h/1.1/Tue Sep 28 15:02:01 2004//
+D
--- /dev/null
+src/libexec/login_tis
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 2004/09/28 15:02:01 millert Exp $
+
+PROG= login_tis
+MAN= login_tis.8
+CFLAGS+=-Wall
+LDADD+= -ldes
+DPADD+= ${LIBDES}
+
+BINOWN= root
+BINGRP= auth
+BINMODE=4555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: login_tis.8,v 1.3 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt LOGIN_TIS 8
+.Os
+.Sh NAME
+.Nm login_tis
+.Nd provide TIS Firewall Toolkit authentication type
+.Sh SYNOPSIS
+.Nm login_tis
+.Op Fl s Ar service
+.Op Fl v Ar fd=number
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm
+utility is called by
+.Xr login 1 ,
+.Xr su 1 ,
+.Xr ftpd 8 ,
+and others to authenticate the
+.Ar user
+via the
+.Tn TIS
+.Dq Firewall Toolkit
+authentication server
+.Pq Em authsrv ,
+optionally using
+.Tn DES
+encryption.
+.Pp
+The
+.Ar service
+argument specifies which protocol to use with the
+invoking program.
+The allowed protocols are
+.Em login ,
+.Em challenge ,
+and
+.Em response .
+The default protocol is
+.Em login .
+.Pp
+The
+.Ar fd
+argument is used to specify the number of an open file descriptor
+connected to
+.Em authsrv .
+This allows a persistent connection to be used for separate
+.Em challenge
+and
+.Em response
+authentication.
+.Pp
+The
+.Ar user
+argument is the login name of the user to be authenticated.
+.Pp
+The
+.Ar class
+argument is the login class of the user to be authenticated
+and is used to look up
+.Pa /etc/login.conf
+variables (see below).
+It is also sent to
+.Em authsrv
+for logging purposes.
+If no
+.Ar class
+argument is specified, the class will be obtained from the password database.
+.Pp
+.Nm
+will connect to
+.Em authsrv
+and, depending on the desired protocol,
+will do one of three things:
+.Bl -tag -width challenge
+.It login
+Present
+.Ar user
+with a challenge, accept a response and report back to the
+invoking program whether or not the authentication was successful.
+.It challenge
+Return a challenge for
+.Ar user
+if the user's entry in
+.Em authsrv
+specifies a challenge/response style of authentication.
+.It response
+Send a response to
+.Em authsrv
+and report back to the invoking program whether or not the server accepted it.
+.El
+.Sh LOGIN.CONF VARIABLES
+The
+.Nm
+utility uses the following TIS-specific
+.Pa /etc/login.conf
+variables:
+.Bl -tag -width tis-server-alt
+.It tis-keyfile
+Path to a file containing a
+.Tn DES
+key string to be used for encrypting communications end to end with
+.Em authsrv .
+This file must not be readable or writable by users other than root.
+If no
+.Ar tis-keyfile
+is specified, communication with
+.Em authsrv
+will be sent in clear text.
+.It tis-port
+Symbolic name listed in
+.Xr services 5
+or port number on which
+.Em authsrv
+listens.
+Defaults to port 7777.
+.It tis-server
+Hostname or IP address of the
+.Tn TIS
+.Em authsrv
+daemon to connect to.
+Defaults to
+.Dq localhost .
+.It tis-server-alt
+Alternate server to use when the primary is not reachable.
+.It tis-timeout
+Number of seconds to wait for a response from
+.Em authsrv .
+Defaults to 15 seconds.
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr login.conf 5 ,
+.Xr services 5 ,
+.Xr ftpd 8 ,
+.Xr login_radius 8
--- /dev/null
+/* $OpenBSD: login_tis.c,v 1.9 2008/03/24 16:11:00 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <readpassphrase.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <login_cap.h>
+#include <bsd_auth.h>
+#include <des.h> /* openssl/des.h */
+
+#include "login_tis.h"
+
+#define MODE_LOGIN 0
+#define MODE_CHALLENGE 1
+#define MODE_RESPONSE 2
+
+enum response_type {
+ error,
+ password,
+ challenge,
+ chalnecho,
+ display
+};
+
+ssize_t tis_recv(struct tis_connection *, u_char *, size_t);
+ssize_t tis_send(struct tis_connection *, u_char *, size_t);
+void quit(int);
+void send_fd(struct tis_connection *, int);
+void tis_getconf(struct tis_connection *, char *);
+ssize_t tis_decode(u_char *, size_t);
+ssize_t tis_encode(u_char *, size_t, size_t);
+int tis_getkey(struct tis_connection *);
+int tis_open(struct tis_connection *, const char *, char *);
+int tis_verify(struct tis_connection *, const char *, char *);
+enum response_type tis_authorize(struct tis_connection *, const char *,
+ const char *, char *);
+
+int
+main(int argc, char *argv[])
+{
+ struct tis_connection tc;
+ struct passwd *pw;
+ struct sigaction sa;
+ sigset_t sigset;
+ uid_t uid;
+ enum response_type rtype = error;
+ FILE *back = NULL;
+ char *user = NULL, *class = NULL, *cp, *ep;
+ char chalbuf[TIS_BUFSIZ], respbuf[TIS_BUFSIZ], ebuf[TIS_BUFSIZ];
+ int ch, mode = MODE_LOGIN;
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ (void)sigemptyset(&sigset);
+ (void)sigaddset(&sigset, SIGSTOP);
+ (void)sigaddset(&sigset, SIGTSTP);
+ (void)sigaddset(&sigset, SIGTTIN);
+ (void)sigaddset(&sigset, SIGTTOU);
+ (void)sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+ memset(&sa, 0, sizeof(sa));
+ (void)sigemptyset(&sa.sa_mask);
+ sa.sa_handler = quit;
+ (void)sigaction(SIGINT, &sa, NULL);
+ (void)sigaction(SIGQUIT, &sa, NULL);
+ (void)sigaction(SIGALRM, &sa, NULL);
+
+ openlog(NULL, LOG_ODELAY, LOG_AUTH);
+
+ tc.fd = -1;
+ while ((ch = getopt(argc, argv, "ds:v:")) != -1) {
+ switch (ch) {
+ case 'd':
+ back = stdout;
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") == 0)
+ mode = MODE_LOGIN;
+ else if (strcmp(optarg, "challenge") == 0)
+ mode = MODE_CHALLENGE;
+ else if (strcmp(optarg, "response") == 0)
+ mode = MODE_RESPONSE;
+ else {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ case 'v':
+ if (strncmp(optarg, "fd=", 3) == 0) {
+ const char *errstr;
+ tc.fd =
+ strtonum(optarg + 3, 0, INT_MAX, &errstr);
+ if (errstr != NULL) {
+ syslog(LOG_ERR, "fd is %s: %s",
+ errstr, optarg + 3);
+ tc.fd = -1;
+ }
+ }
+ /* silently ignore unsupported variables */
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch (argc) {
+ case 2:
+ user = argv[0];
+ class = argv[1];
+ break;
+ case 1:
+ /* class is not really optional so check passwd(5) entry */
+ user = argv[0];
+ if ((pw = getpwnam(user)) != NULL && pw->pw_class != NULL)
+ class = strdup(pw->pw_class);
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ if (back == NULL && (back = fdopen(3, "r+")) == NULL) {
+ syslog(LOG_ERR, "reopening back channel: %m");
+ exit(1);
+ }
+
+ tis_getconf(&tc, class);
+ if (tc.keyfile != NULL && tis_getkey(&tc) != 0)
+ exit(1);
+
+ /* no longer need special perms */
+ if ((uid = getuid()) != geteuid()) {
+ seteuid(uid);
+ setuid(uid);
+ }
+
+ if (tc.fd == -1) {
+ if (tis_open(&tc, tc.server, ebuf) == -1 && (!tc.altserver ||
+ tis_open(&tc, tc.altserver, ebuf) == -1)) {
+ syslog(LOG_ERR, "unable to connect to authsrv: %s",
+ ebuf);
+ exit(1);
+ }
+ if ((rtype = tis_authorize(&tc, user, class, chalbuf)) == error)
+ exit(1);
+ }
+
+ switch (mode) {
+ case MODE_LOGIN:
+ if (rtype == display) {
+ printf("%s", chalbuf);
+ exit(1);
+ }
+ alarm(TIS_PASSWD_TIMEOUT);
+ if (!readpassphrase(chalbuf, respbuf, sizeof(respbuf),
+ rtype == challenge ? RPP_ECHO_ON : RPP_ECHO_OFF))
+ exit(1);
+ alarm(0);
+ break;
+
+ case MODE_CHALLENGE:
+ switch (rtype) {
+ case display:
+ (void)fprintf(back, "value errormsg %s\n",
+ auth_mkvalue(chalbuf));
+ exit(1);
+ case password:
+ fprintf(back, BI_SILENT "\n");
+ break;
+ default:
+ /* XXX - should distinguish chalnecho from challenge */
+ fprintf(back, BI_VALUE " challenge %s\n",
+ auth_mkvalue(chalbuf));
+ fprintf(back, BI_CHALLENGE "\n");
+ }
+ fprintf(back, BI_FDPASS "\n");
+ fflush(back);
+ send_fd(&tc, fileno(back));
+ exit(0);
+
+ case MODE_RESPONSE:
+ /* read challenge from backchannel */
+ mode = -1;
+ cp = chalbuf;
+ ep = chalbuf + sizeof(chalbuf);
+ while (cp < ep && read(fileno(back), cp, 1) == 1) {
+ if (*cp++ == '\0') {
+ mode = MODE_CHALLENGE;
+ break;
+ }
+ }
+ if (mode != MODE_CHALLENGE) {
+ syslog(LOG_ERR,
+ "protocol error: bad/missing challenge");
+ exit(1);
+ }
+ if (rtype == error) {
+ /* didn't read the challenge ourselves so just guess */
+ if (strcmp(chalbuf, "Password:") == 0)
+ rtype = password;
+ else
+ rtype = challenge;
+ }
+
+ /* read user's response from backchannel */
+ cp = respbuf;
+ ep = respbuf + sizeof(respbuf);
+ while (cp < ep && read(fileno(back), cp, 1) == 1) {
+ if (*cp++ == '\0') {
+ mode = MODE_RESPONSE;
+ break;
+ }
+ }
+ if (mode != MODE_RESPONSE) {
+ syslog(LOG_ERR,
+ "protocol error: bad/missing response");
+ exit(1);
+ }
+ break;
+ }
+
+ if (tis_verify(&tc, respbuf, ebuf) == 0) {
+ if (ebuf[0] != '\0')
+ (void)fprintf(back, "value errormsg %s\n",
+ auth_mkvalue(ebuf));
+ fprintf(back, BI_AUTH "\n");
+ if (rtype == challenge)
+ fprintf(back, BI_SECURE "\n");
+ exit(0);
+ }
+ if (ebuf[0] != '\0')
+ (void)fprintf(back, "value errormsg %s\n", auth_mkvalue(ebuf));
+ fprintf(back, BI_REJECT "\n");
+ exit(1);
+}
+
+void
+quit(int signo)
+{
+ struct syslog_data data;
+
+ if (signo == SIGALRM)
+ syslog_r(LOG_ERR, &data, "timed out talking to authsrv");
+ _exit(1);
+}
+
+/*
+ * Send the file descriptor in struct tis_connection over a socketpair
+ * to the parent process to keep the connection to authsrv open.
+ */
+void
+send_fd(struct tis_connection *tc, int sock)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmp;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ cmp = CMSG_FIRSTHDR(&msg);
+ cmp->cmsg_len = CMSG_LEN(sizeof(int));
+ cmp->cmsg_level = SOL_SOCKET;
+ cmp->cmsg_type = SCM_RIGHTS;
+
+ *(int *)CMSG_DATA(cmp) = tc->fd;
+
+ if (sendmsg(sock, &msg, 0) < 0)
+ syslog(LOG_ERR, "sendmsg: %m");
+}
+
+/*
+ * Look up the given login class and populate struct tis_connection.
+ */
+void
+tis_getconf(struct tis_connection *tc, char *class)
+{
+ login_cap_t *lc;
+
+ if ((lc = login_getclass(class)) == NULL) {
+ tc->port = TIS_DEFPORT;
+ tc->timeout = TIS_DEFTIMEOUT;
+ tc->server = TIS_DEFSERVER;
+ tc->altserver = NULL;
+ tc->keyfile = NULL;
+ return;
+ }
+
+ tc->port = login_getcapstr(lc, "tis-port", TIS_DEFPORT, TIS_DEFPORT);
+ tc->timeout = login_getcapnum(lc, "tis-timeout", TIS_DEFTIMEOUT,
+ TIS_DEFTIMEOUT);
+ tc->server = login_getcapstr(lc, "tis-server", TIS_DEFSERVER,
+ TIS_DEFSERVER);
+ tc->altserver = login_getcapstr(lc, "tis-server-alt", NULL, NULL);
+ tc->keyfile = login_getcapstr(lc, "tis-keyfile", NULL, NULL);
+}
+
+/*
+ * Read an ASCII string from a file and convert it to a DES key.
+ */
+int
+tis_getkey(struct tis_connection *tc)
+{
+ size_t len;
+ struct stat sb;
+ des_cblock cblock;
+ char *key, *tbuf = NULL;
+ FILE *fp;
+ int error;
+
+ if ((fp = fopen(tc->keyfile, "r")) == NULL) {
+ syslog(LOG_ERR, "%s: %m", tc->keyfile);
+ return (-1);
+ }
+ if (fstat(fileno(fp), &sb) == -1) {
+ syslog(LOG_ERR, "%s: %m", tc->keyfile);
+ fclose(fp);
+ return (-1);
+ }
+ if (sb.st_uid != 0) {
+ syslog(LOG_ERR, "%s: not owned by root", tc->keyfile);
+ fclose(fp);
+ return (-1);
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ syslog(LOG_ERR, "%s: not a regular file", tc->keyfile);
+ fclose(fp);
+ return (-1);
+ }
+ if (sb.st_mode & (S_IRWXG|S_IRWXO)) {
+ syslog(LOG_ERR, "%s: readable or writable by non-owner",
+ tc->keyfile);
+ fclose(fp);
+ return (-1);
+ }
+ if ((key = fgetln(fp, &len)) == NULL || (len == 1 && key[0] == '\n')) {
+ if (ferror(fp))
+ syslog(LOG_ERR, "%s: %m", tc->keyfile);
+ else
+ syslog(LOG_ERR, "%s: empty key file", tc->keyfile);
+ fclose(fp);
+ return (-1);
+ }
+ fclose(fp);
+ if (key[len - 1] == '\n')
+ key[--len] = '\0';
+ else {
+ if ((tbuf = malloc(len + 1)) == NULL) {
+ syslog(LOG_ERR, "%s: %m", tc->keyfile);
+ return (-1);
+ }
+ memcpy(tbuf, key, len);
+ tbuf[len] = '\0';
+ key = tbuf;
+ }
+ des_string_to_key(key, &cblock);
+ error = des_set_key(&cblock, tc->keysched);
+ memset(key, 0, len);
+ memset(&cblock, 0, sizeof(cblock));
+ free(tbuf);
+ return (error);
+}
+
+/*
+ * Open a connection to authsrv and read the welcom banner.
+ * Returns the file descriptor on success and -1 on error
+ * or unrecognized welcome banner.
+ */
+int
+tis_open(struct tis_connection *tc, const char *server, char *ebuf)
+{
+ struct addrinfo hints, *res, *res0;
+ char buf[TIS_BUFSIZ];
+ int error;
+
+ ebuf[0] = '\0';
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = 0;
+ error = getaddrinfo(server, tc->port, &hints, &res0);
+ if (error) {
+ strlcpy(ebuf, gai_strerror(error), TIS_BUFSIZ);
+ return (-1);
+ }
+
+ /* connect to the first address that succeeds */
+ for (res = res0; res != NULL; res = res->ai_next) {
+ tc->fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (tc->fd != -1) {
+ if (connect(tc->fd, res->ai_addr, res->ai_addrlen) == 0)
+ break;
+ close(tc->fd);
+ }
+ }
+ if (res == NULL) {
+ strlcpy(ebuf, strerror(errno), TIS_BUFSIZ);
+ freeaddrinfo(res0);
+ tc->fd = -1;
+ return (-1);
+ }
+ freeaddrinfo(res0);
+
+ /* read welcome banner */
+ if (tis_recv(tc, buf, sizeof(buf)) == -1) {
+ strlcpy(ebuf, strerror(errno), TIS_BUFSIZ);
+ close(tc->fd);
+ tc->fd = -1;
+ return (-1);
+ }
+ if (strncmp(buf, "Authsrv ready", 13) != 0) {
+ strlcpy(ebuf, buf, TIS_BUFSIZ);
+ close(tc->fd);
+ tc->fd = -1;
+ return (-1);
+ }
+
+ return (tc->fd);
+}
+
+/*
+ * Read a one-line response from authsrv.
+ * On success, returns 0. On error, returns non-zero and calls syslog().
+ */
+ssize_t
+tis_recv(struct tis_connection *tc, u_char *buf, size_t bufsiz)
+{
+ des_key_schedule ks;
+ des_cblock iv;
+ ssize_t len;
+ u_char *cp, *ep, tbuf[TIS_BUFSIZ];
+
+ for (cp = buf, ep = buf + bufsiz; cp < ep; cp++) {
+ alarm(tc->timeout);
+ len = read(tc->fd, cp, 1);
+ alarm(0);
+ if (len != 1) {
+ if (len == -1)
+ syslog(LOG_ERR,
+ "error reading data from authsrv: %m");
+ else
+ syslog(LOG_ERR, "EOF reading data from authsrv");
+ return (-1);
+ }
+ if (*cp == '\n')
+ break;
+ }
+ if (*cp != '\n') {
+ syslog(LOG_ERR, "server response too large");
+ return (-1);
+ }
+ *cp = '\0';
+ len = cp - buf;
+
+ if (tc->keyfile != NULL) {
+ if ((len = tis_decode(buf, len)) < 0) {
+ syslog(LOG_ERR, "improperly encoded data from authsrv");
+ return (-1);
+ }
+ if (len > sizeof(tbuf)) {
+ syslog(LOG_ERR, "encrypted data too large to store");
+ return (-1);
+ }
+ memcpy(ks, tc->keysched, sizeof(ks));
+ memset(iv, 0, sizeof(iv));
+ des_ncbc_encrypt((des_cblock *)buf, (des_cblock *)tbuf,
+ len, ks, &iv, DES_DECRYPT);
+ if (strlcpy(buf, tbuf, bufsiz) >= bufsiz) {
+ syslog(LOG_ERR, "unencrypted data too large to store");
+ memset(tbuf, 0, sizeof(tbuf));
+ return (-1);
+ }
+ memset(tbuf, 0, sizeof(tbuf));
+ }
+ return (len);
+}
+
+/*
+ * Send a line to authsrv.
+ * On success, returns 0. On error, returns non-zero and calls syslog().
+ */
+ssize_t
+tis_send(struct tis_connection *tc, u_char *buf, size_t len)
+{
+ struct iovec iov[2];
+ des_key_schedule ks;
+ des_cblock iv;
+ ssize_t nwritten;
+ size_t n;
+ u_char cbuf[TIS_BUFSIZ];
+
+ if (tc->keyfile != NULL) {
+ memcpy(ks, tc->keysched, sizeof(ks));
+ memset(iv, 0, sizeof(iv));
+
+ len++; /* we need to encrypt the NUL */
+ if ((n = len % 8) != 0)
+ len += 8 - n; /* make multiple of 8 bytes */
+ if (len * 2 > sizeof(cbuf)) {
+ syslog(LOG_ERR, "encoded data too large to store");
+ return (-1);
+ }
+ des_ncbc_encrypt((des_cblock *)buf, (des_cblock *)cbuf, len,
+ ks, &iv, DES_ENCRYPT);
+ len = tis_encode(cbuf, len, sizeof(cbuf));
+ buf = cbuf;
+ }
+ iov[0].iov_base = buf;
+ iov[0].iov_len = len;
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+
+ alarm(tc->timeout);
+ nwritten = writev(tc->fd, iov, 2);
+ alarm(0);
+ if (nwritten != len + 1) {
+ if (nwritten < 0)
+ syslog(LOG_ERR, "error writing to authsrv: %m");
+ else
+ syslog(LOG_ERR, "short write sending to authsrv");
+ return (-1);
+ }
+ return (nwritten - 1); /* don't include the newline */
+}
+
+/*
+ * Convert a stream of bytes hex digits to hex octects in place.
+ * The passed in buffer must have space for len*2 bytes
+ * plus a NUL.
+ */
+ssize_t
+tis_encode(u_char *buf, size_t inlen, size_t bufsiz)
+{
+ u_char *in, *out;
+ size_t outlen;
+ const static char hextab[] = "0123456789ABCDEF";
+
+ outlen = inlen * 2;
+ if (bufsiz <= outlen)
+ return (-1);
+
+ /* convert from the end -> beginning so we can do it in place */
+ for (in = &buf[inlen - 1], out = &buf[outlen - 1]; in >= buf; in--) {
+ *out-- = hextab[*in & 0x0f];
+ *out-- = hextab[*in >> 4];
+ }
+ buf[outlen] = '\0';
+
+ return (outlen);
+}
+
+/*
+ * Convert a stream of hex digits to bytes in place.
+ */
+ssize_t
+tis_decode(u_char *buf, size_t len)
+{
+ u_char *end, *in, *out;
+ int byte;
+
+ if (len & 1)
+ return (-1); /* len must be even */
+
+ for (in = out = buf, end = buf + len; in < end; in += 2) {
+ if (in[1] >= 'A' && in[1] <= 'F')
+ byte = in[1] - 'A' + 10;
+ else
+ byte = in[1] - '0';
+ if (in[0] >= 'A' && in[0] <= 'F')
+ byte += (in[0] - 'A' + 10) * 16;
+ else
+ byte += (in[0] - '0') * 16;
+ if (byte > 0xff || byte < 0)
+ return (-1);
+ *out++ = byte;
+ }
+ *out = '\0';
+ return (out - buf);
+}
+
+/*
+ * Send an authorization string to authsrv and check the result.
+ * Returns the type of response and an output buffer.
+ */
+enum response_type
+tis_authorize(struct tis_connection *tc, const char *user,
+ const char *class, char *obuf)
+{
+ enum response_type resp;
+ u_char buf[TIS_BUFSIZ];
+ int len;
+
+ *obuf = '\0';
+ /* class is not used by authsrv (it is effectively a comment) */
+ len = snprintf(buf, sizeof(buf), "authenticate %s %s", user, class);
+ if (len == -1 || len >= sizeof(buf)) {
+ syslog(LOG_ERR, "user/class too large");
+ resp = error;
+ } else if (tis_send(tc, buf, len) < 0)
+ resp = error;
+ else if (tis_recv(tc, buf, sizeof(buf)) < 0)
+ resp = error;
+ else if (strncmp(buf, "password", 8) == 0) {
+ strlcpy(obuf, "Password:", TIS_BUFSIZ);
+ resp = password;
+ } else if (strncmp(buf, "challenge ", 10) == 0) {
+ strlcpy(obuf, buf + 10, TIS_BUFSIZ);
+ resp = challenge;
+ } else if (strncmp(buf, "chalnecho ", 10) == 0) {
+ strlcpy(obuf, buf + 10, TIS_BUFSIZ);
+ resp = chalnecho;
+ } else if (strncmp(buf, "display ", 8) == 0) {
+ strlcpy(obuf, buf, TIS_BUFSIZ);
+ resp = display;
+ } else {
+ syslog(LOG_ERR, "unexpected response from authsrv: %s", obuf);
+ resp = error;
+ }
+ memset(buf, 0, sizeof(buf));
+
+ return (resp);
+}
+
+/*
+ * Send a response string to authsrv and check the result.
+ * Returns the type of response, and an error buffer.
+ */
+int
+tis_verify(struct tis_connection *tc, const char *response, char *ebuf)
+{
+ u_char buf[TIS_BUFSIZ];
+ int len;
+
+ ebuf[0] = '\0';
+ len = snprintf(buf, sizeof(buf), "response '%s'", response);
+ if (len == -1 || len >= sizeof(buf)) {
+ syslog(LOG_ERR, "response too large");
+ return (-1);
+ }
+ if (tis_send(tc, buf, len) < 0)
+ return (-1);
+ if (tis_recv(tc, buf, sizeof(buf)) < 0)
+ return (-1);
+ if (strncmp(buf, "ok", 2) == 0) {
+ if (buf[2] != '\0')
+ strlcpy(ebuf, buf + 3, TIS_BUFSIZ);
+ memset(buf, 0, sizeof(buf));
+ return (0);
+ }
+ strlcpy(ebuf, buf, TIS_BUFSIZ);
+ memset(buf, 0, sizeof(buf));
+ return (-1);
+}
--- /dev/null
+/* $OpenBSD: login_tis.h,v 1.1 2004/09/28 15:02:01 millert Exp $ */
+
+/*
+ * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define TIS_BUFSIZ 512 /* max size of authsrv reply */
+ /* XXX - only 128 for most */
+
+#define TIS_PASSWD_TIMEOUT 120 /* password prompt timeout */
+
+/* default values for login.conf variables */
+#define TIS_DEFPORT "7777" /* default port to use */
+#define TIS_DEFSERVER "localhost" /* default server to contact */
+#define TIS_DEFTIMEOUT 15 /* default communications timeout */
+
+struct tis_connection {
+ int fd;
+ int timeout;
+ char *keyfile;
+ char *port;
+ char *server;
+ char *altserver;
+ des_key_schedule keysched;
+};
--- /dev/null
+/usr/obj/libexec/login_tis
\ No newline at end of file
--- /dev/null
+/Makefile/1.4/Thu Nov 21 22:14:51 2002//
+/init.c/1.3/Thu Feb 17 13:49:38 2005//
+/login_token.8/1.11/Thu Oct 22 09:22:46 2009//
+/login_token.c/1.8/Wed Mar 10 21:30:27 2004//
+/token.c/1.11/Sat Nov 12 14:13:16 2005//
+/token.h/1.4/Fri Sep 6 18:19:14 2002//
+/tokendb.c/1.8/Fri Sep 16 23:47:00 2005//
+/tokendb.h/1.4/Thu Nov 21 22:11:45 2002//
+D
--- /dev/null
+src/libexec/login_token
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.4 2002/11/21 22:14:51 millert Exp $
+
+PROG= login_token
+SRCS= login_token.c init.c token.c tokendb.c
+MAN= login_token.8
+DPADD= ${LIBDES}
+LDADD= -ldes
+
+TOKENS= activ crypto snk
+
+MLINKS= login_token.8 login_activ.8
+MLINKS+=login_token.8 login_crypto.8
+MLINKS+=login_token.8 login_snk.8
+
+afterinstall:
+ for i in ${TOKENS} ; do \
+ cd ${DESTDIR}${BINDIR} && \
+ rm -f login_$$i && \
+ ln ${PROG} login_$$i ; \
+ done
+
+BINOWN= root
+BINGRP= _token
+BINMODE=2555
+BINDIR= /usr/libexec/auth
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: init.c,v 1.3 2005/02/17 13:49:38 aaron Exp $ */
+
+/*-
+ * Copyright (c) 1996 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: init.c,v 1.2 1996/09/05 23:17:06 prb Exp $
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "token.h"
+#include "tokendb.h"
+
+static struct token_types types[] = {
+ { "activ", "ActivCard", "/etc/activ.db", "012345",
+ TOKEN_HEXINIT,
+ TOKEN_DECMODE | TOKEN_HEXMODE, /* avail */
+ TOKEN_HEXMODE }, /* default */
+ { "crypto", "CRYPTOCard", "/etc/crypto.db", "012345",
+ TOKEN_HEXINIT | TOKEN_PHONE,
+ TOKEN_DECMODE | TOKEN_HEXMODE | TOKEN_PHONEMODE | TOKEN_RIM,
+ TOKEN_HEXMODE }, /* default */
+ { "snk", "SNK 004", "/etc/snk.db", "222333",
+ 0,
+ TOKEN_DECMODE | TOKEN_HEXMODE, /* avail */
+ TOKEN_DECMODE }, /* default */
+ { "token", "X9.9 Token", "/etc/x99token.db", "012345",
+ TOKEN_HEXINIT,
+ TOKEN_DECMODE | TOKEN_HEXMODE | TOKEN_RIM, /* avail */
+ TOKEN_HEXMODE }, /* default */
+};
+
+static struct {
+ char *name;
+ u_int value;
+} modes[] = {
+ { "hexadecimal", TOKEN_HEXMODE },
+ { "hex", TOKEN_HEXMODE },
+ { "decimal", TOKEN_DECMODE },
+ { "dec", TOKEN_DECMODE },
+ { "phonebook", TOKEN_PHONEMODE },
+ { "phone", TOKEN_PHONEMODE },
+ { "reduced-input", TOKEN_RIM },
+ { "rim", TOKEN_RIM }
+};
+
+int
+token_init(char *path)
+{
+ char *p;
+ int i;
+
+ if ((p = strrchr(path, '/')) && p[1] != '\0')
+ path = p + 1;
+
+ for (i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
+ if (strstr(path, types[i].name) != NULL) {
+ tt = &types[i];
+ return (0);
+ }
+ if ((p = strstr(path, "token")) != NULL) {
+ fprintf(stderr, "Please invoke as one of:");
+ for (i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
+ fprintf(stderr, " %.*s%s%s",
+ p - path, path, types[i].name, p + 5);
+ fprintf(stderr, "\n");
+ exit(1);
+
+ }
+ return (-1);
+}
+
+u_int
+token_mode(char *mode)
+{
+ int i;
+
+ for (i = 0; i < sizeof(modes)/sizeof(modes[0]); ++i)
+ if (strstr(mode, modes[i].name) != NULL)
+ return (tt->modes & modes[i].value);
+ return (0);
+}
+
+char *
+token_getmode(u_int mode)
+{
+ int i;
+ static char buf[32];
+
+ for (i = 0; i < sizeof(modes)/sizeof(modes[0]); ++i)
+ if (mode == modes[i].value)
+ return(modes[i].name);
+ snprintf(buf, sizeof(buf), "0x%x", mode);
+ return(buf);
+}
--- /dev/null
+.\" $OpenBSD: login_token.8,v 1.11 2009/10/22 09:22:46 sobrado Exp $
+.\"
+.\" Copyright (c) 1995 Migration Associates Corporation. 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 Berkeley Software Design,
+.\" Inc.
+.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+.\"
+.Dd $Mdocdate: October 22 2009 $
+.Dt LOGIN_TOKEN 8
+.Os
+.Sh NAME
+.Nm login_activ , login_crypto , login_snk
+.Nd provide ActivCard, CRYPTOCard and SNK-004 authentication
+.Sh SYNOPSIS
+.Nm login_token
+.Op Fl s Ar service
+.Op Fl v Ar name=value
+.Ar user
+.Op Ar class
+.Sh DESCRIPTION
+The
+.Nm login_token
+program implements an X9.9 token card challenge response authentication
+mechanism (see
+.Xr login.conf 5 ) .
+It must be invoked by one of the names:
+.Nm login_activ , login_crypto ,
+or
+.Nm login_snk .
+.Pp
+Available options are:
+.Bl -tag -width indent
+.It Fl s
+Specify the service.
+Currently only
+.Li challenge ,
+.Li login ,
+and
+.Li response
+are supported.
+.It Fl v
+This option and its value are ignored.
+.El
+.Pp
+.Nm login_token
+will look up
+.Ar user
+in the appropriate database file, depending on what name it was called as:
+.Pa /etc/activ.db ,
+.Pa /etc/crypto.db ,
+or
+.Pa /etc/snk.db .
+It then will issue a challenge, and if the user
+is able to correctly respond (by using the appropriate token)
+the user will be authenticated.
+The
+.Ar class
+argument is unused.
+.Sh FILES
+.Bl -tag -width xetcxcrypto.db
+.It Pa /etc/activ.db
+data base of information for the ActivCard tokens.
+.It Pa /etc/crypto.db
+data base of information for the CRYPTOCard tokens.
+.It Pa /etc/snk.db
+data base of information for the SNK-004 tokens.
+.El
+.Sh DIAGNOSTICS
+Diagnostic messages are logged via
+.Xr syslog 3
+with the LOG_AUTH facility.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr login.conf 5 ,
+.Xr tokenadm 8 ,
+.Xr tokeninit 8
+.Sh AUTHORS
+.An Jack Flory Aq jpf@mig.com
--- /dev/null
+/* $OpenBSD: login_token.c,v 1.8 2004/03/10 21:30:27 millert Exp $ */
+
+/*-
+ * Copyright (c) 1995, 1996 Berkeley Software Design, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: login_token.c,v 1.2 1996/09/04 05:33:05 prb Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <err.h>
+#include <readpassphrase.h>
+#include <signal.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <login_cap.h>
+#include <bsd_auth.h>
+
+#include "token.h"
+
+int
+main(int argc, char *argv[])
+{
+ FILE *back = NULL;
+ char *class = 0;
+ char *username = 0;
+ char *instance;
+ char challenge[1024];
+ char response[1024];
+ char *pp = 0;
+ int c;
+ int mode = 0;
+ struct rlimit cds;
+ sigset_t blockset;
+
+ (void)setpriority(PRIO_PROCESS, 0, 0);
+
+ /* We block keyboard-generated signals during database accesses. */
+ sigemptyset(&blockset);
+ sigaddset(&blockset, SIGINT);
+ sigaddset(&blockset, SIGQUIT);
+ sigaddset(&blockset, SIGTSTP);
+
+ openlog(NULL, LOG_ODELAY, LOG_AUTH);
+
+ cds.rlim_cur = 0;
+ cds.rlim_max = 0;
+ if (setrlimit(RLIMIT_CORE, &cds) < 0)
+ syslog(LOG_ERR, "couldn't set core dump size to 0: %m");
+
+ (void)sigprocmask(SIG_BLOCK, &blockset, NULL);
+ if (token_init(argv[0]) < 0) {
+ syslog(LOG_ERR, "unknown token type");
+ errx(1, "unknown token type");
+ }
+ (void)sigprocmask(SIG_UNBLOCK, &blockset, NULL);
+
+ while ((c = getopt(argc, argv, "ds:v:")) != -1)
+ switch (c) {
+ case 'd': /* to remain undocumented */
+ back = stdout;
+ break;
+ case 'v':
+ break;
+ case 's': /* service */
+ if (strcmp(optarg, "login") == 0)
+ mode = 0;
+ else if (strcmp(optarg, "challenge") == 0)
+ mode = 1;
+ else if (strcmp(optarg, "response") == 0)
+ mode = 2;
+ else {
+ syslog(LOG_ERR, "%s: invalid service", optarg);
+ exit(1);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+ switch (argc - optind) {
+ case 2:
+ class = argv[optind + 1];
+ case 1:
+ username = argv[optind];
+ break;
+ default:
+ syslog(LOG_ERR, "usage error");
+ exit(1);
+ }
+
+
+ if (back == NULL && (back = fdopen(3, "r+")) == NULL) {
+ syslog(LOG_ERR, "reopening back channel");
+ exit(1);
+ }
+ if (mode == 2) {
+ mode = 0;
+ c = -1;
+ while (++c < sizeof(challenge) &&
+ read(3, &challenge[c], 1) == 1) {
+ if (challenge[c] == '\0' && ++mode == 2)
+ break;
+ if (challenge[c] == '\0' && mode == 1)
+ pp = challenge + c + 1;
+ }
+ if (mode < 2) {
+ syslog(LOG_ERR, "protocol error on back channel");
+ exit(1);
+ }
+ } else {
+ (void)sigprocmask(SIG_BLOCK, &blockset, NULL);
+ tokenchallenge(username, challenge, sizeof(challenge),
+ tt->proper);
+ (void)sigprocmask(SIG_UNBLOCK, &blockset, NULL);
+ if (mode == 1) {
+ fprintf(back, BI_VALUE " challenge %s\n",
+ auth_mkvalue(challenge));
+ fprintf(back, BI_CHALLENGE "\n");
+ exit(0);
+ }
+
+ pp = readpassphrase(challenge, response, sizeof(response), 0);
+ if (pp == NULL)
+ exit(1);
+ if (*pp == '\0') {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%s Response [echo on]: ",
+ tt->proper);
+ pp = readpassphrase(buf, response, sizeof(response),
+ RPP_ECHO_ON);
+ if (pp == NULL)
+ exit(1);
+ }
+ }
+
+ (void)sigprocmask(SIG_BLOCK, &blockset, NULL);
+ if (tokenverify(username, challenge, pp) == 0) {
+ fprintf(back, BI_AUTH "\n");
+
+ if ((instance = strchr(username, '.'))) {
+ *instance++ = 0;
+ if (strcmp(instance, "root") == 0)
+ fprintf(back, BI_ROOTOKAY "\n");
+ }
+ fprintf(back, BI_SECURE "\n");
+ exit(0);
+ }
+
+ fprintf(back, BI_REJECT "\n");
+ exit(1);
+}
--- /dev/null
+/usr/obj/libexec/login_token
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: token.c,v 1.11 2005/11/12 14:13:16 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1995 Migration Associates Corp. 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: token.c,v 1.2 1996/08/28 22:07:55 prb Exp $
+ */
+
+/*
+ * DES functions for one-way encrypting Authentication Tokens.
+ * All knowledge of DES is confined to this file.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <des.h>
+
+#include "token.h"
+#include "tokendb.h"
+
+/*
+ * Define a union of various types of arguments to DES functions.
+ * All native DES types are modulo 8 bytes in length. Cipher text
+ * needs a trailing null byte.
+ */
+
+typedef union {
+ des_cblock cb;
+ char ct[9];
+ unsigned long ul[2];
+} TOKEN_CBlock;
+
+/*
+ * Static definition of random number challenge for token.
+ * Challenge length is 8 bytes, left-justified with trailing null byte.
+ */
+
+static TOKEN_CBlock tokennumber;
+
+/*
+ * Static function prototypes
+ */
+
+static void tokenseed(TOKEN_CBlock *);
+static void lcase(char *);
+static void h2d(char *);
+static void h2cb(char *, TOKEN_CBlock *);
+static void cb2h(TOKEN_CBlock, char *);
+
+/*
+ * Generate random DES cipherblock seed. Feedback key into
+ * new_random_key to strengthen.
+ */
+
+static void
+tokenseed(TOKEN_CBlock *cb)
+{
+ cb->ul[0] = arc4random();
+ cb->ul[1] = arc4random();
+}
+
+/*
+ * Send a random challenge string to the token. The challenge
+ * is always base 10 as there are no alpha keys on the keyboard.
+ */
+
+void
+tokenchallenge(char *user, char *challenge, int size, char *card_type)
+{
+ TOKENDB_Rec tr;
+ TOKEN_CBlock cb;
+ des_key_schedule ks;
+ int r, c;
+
+ r = 1; /* no reduced input mode by default! */
+
+ if ((tt->modes & TOKEN_RIM) &&
+ tokendb_getrec(user, &tr) == 0 &&
+ (tr.mode & TOKEN_RIM)) {
+ c = 0;
+ while ((r = tokendb_lockrec(user, &tr, TOKEN_LOCKED)) == 1) {
+ if (c++ >= 60)
+ break;
+ sleep(1);
+ }
+ tr.flags &= ~TOKEN_LOCKED;
+ if (r == 0 && tr.rim[0]) {
+ h2cb(tr.secret, &cb);
+ des_fixup_key_parity(&cb.cb);
+ des_key_sched(&cb.cb, ks);
+ des_ecb_encrypt(&tr.rim, &cb.cb, ks, DES_ENCRYPT);
+ memcpy(tr.rim, cb.cb, 8);
+ for (r = 0; r < 8; ++r) {
+ if ((tr.rim[r] &= 0xf) > 9)
+ tr.rim[r] -= 10;
+ tr.rim[r] |= 0x30;
+ }
+ r = 0; /* reset it back */
+ memcpy(tokennumber.ct, tr.rim, 8);
+ tokennumber.ct[8] = 0;
+ tokendb_putrec(user, &tr);
+ }
+ }
+ if (r != 0 || tr.rim[0] == '\0') {
+ memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
+ snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8u",
+ arc4random());
+ if (r == 0) {
+ memcpy(tr.rim, tokennumber.ct, 8);
+ tokendb_putrec(user, &tr);
+ }
+ }
+
+ snprintf(challenge, size, "%s Challenge \"%s\"\r\n%s Response: ",
+ card_type, tokennumber.ct, card_type);
+}
+
+/*
+ * Verify response from user against token's predicted cipher
+ * of the random number challenge.
+ */
+
+int
+tokenverify(char *username, char *challenge, char *response)
+{
+ char *state;
+ TOKENDB_Rec tokenrec;
+ TOKEN_CBlock tmp;
+ TOKEN_CBlock cmp_text;
+ TOKEN_CBlock user_seed;
+ TOKEN_CBlock cipher_text;
+ des_key_schedule key_schedule;
+
+
+ memset(cmp_text.ct, 0, sizeof(cmp_text.ct));
+ memset(user_seed.ct, 0, sizeof(user_seed.ct));
+ memset(cipher_text.ct, 0, sizeof(cipher_text.ct));
+ memset(tokennumber.ct, 0, sizeof(tokennumber.ct));
+
+ state = strtok(challenge, "\"");
+ state = strtok(NULL, "\"");
+ tmp.ul[0] = strtoul(state, NULL, 10);
+ snprintf(tokennumber.ct, sizeof(tokennumber.ct), "%8.8lu",tmp.ul[0]);
+
+ /*
+ * Retrieve the db record for the user. Nuke it as soon as
+ * we have translated out the user's shared secret just in
+ * case we (somehow) get core dumped...
+ */
+
+ if (tokendb_getrec(username, &tokenrec))
+ return (-1);
+
+ h2cb(tokenrec.secret, &user_seed);
+ memset(&tokenrec.secret, 0, sizeof(tokenrec.secret));
+
+ if (!(tokenrec.flags & TOKEN_ENABLED))
+ return (-1);
+
+ /*
+ * Compute the anticipated response in hex. Nuke the user's
+ * shared secret asap.
+ */
+
+ des_fixup_key_parity(&user_seed.cb);
+ des_key_sched(&user_seed.cb, key_schedule);
+ memset(user_seed.ct, 0, sizeof(user_seed.ct));
+ des_ecb_encrypt(&tokennumber.cb, &cipher_text.cb, key_schedule,
+ DES_ENCRYPT);
+ memset(key_schedule, 0, sizeof(key_schedule));
+
+ /*
+ * The token thinks it's descended from VAXen. Deal with i386
+ * endian-ness of binary cipher prior to generating ascii from first
+ * 32 bits.
+ */
+
+ HTONL(cipher_text.ul[0]);
+ snprintf(cmp_text.ct, sizeof(cmp_text.ct), "%8.8lx", cipher_text.ul[0]);
+
+ if (tokenrec.mode & TOKEN_PHONEMODE) {
+ /*
+ * If we are a CRYPTOCard, we need to see if we are in
+ * "telephone number mode". If so, transmogrify the fourth
+ * digit of the cipher. Lower case response just in case
+ * it's * hex. Compare hex cipher with anticipated response
+ * from token.
+ */
+
+ lcase(response);
+
+ if (response[3] == '-')
+ cmp_text.ct[3] = '-';
+ }
+
+ if ((tokenrec.mode & TOKEN_HEXMODE) && !strcmp(response, cmp_text.ct))
+ return (0);
+
+ /*
+ * No match against the computed hex cipher. The token could be
+ * in decimal mode. Pervert the string to magic decimal equivalent.
+ */
+
+ h2d(cmp_text.ct);
+
+ if ((tokenrec.mode & TOKEN_DECMODE) && !strcmp(response, cmp_text.ct))
+ return (0);
+
+ return (-1);
+}
+
+/*
+ * Initialize a new user record in the token database.
+ */
+
+int
+tokenuserinit(int flags, char *username, unsigned char *usecret, unsigned mode)
+{
+ TOKENDB_Rec tokenrec;
+ TOKEN_CBlock secret;
+ TOKEN_CBlock nulls;
+ TOKEN_CBlock checksum;
+ TOKEN_CBlock checktxt;
+ des_key_schedule key_schedule;
+
+ memset(&secret.ct, 0, sizeof(secret));
+
+ /*
+ * If no user secret passed in, create one
+ */
+
+ if ( (flags & TOKEN_GENSECRET) )
+ tokenseed(&secret);
+ else
+ memcpy(&secret, usecret, sizeof(des_cblock));
+
+ des_fixup_key_parity(&secret.cb);
+
+ /*
+ * Check if the db record already exists. If there's no
+ * force-init flag and it exists, go away. Else,
+ * create the user's db record and put to the db.
+ */
+
+
+ if (!(flags & TOKEN_FORCEINIT) &&
+ tokendb_getrec(username, &tokenrec) == 0)
+ return (1);
+
+ memset(&tokenrec, 0, sizeof(tokenrec));
+ strlcpy(tokenrec.uname, username, sizeof(tokenrec.uname));
+ cb2h(secret, tokenrec.secret);
+ tokenrec.mode = 0;
+ tokenrec.flags = TOKEN_ENABLED | TOKEN_USEMODES;
+ tokenrec.mode = mode;
+ memset(tokenrec.reserved_char1, 0, sizeof(tokenrec.reserved_char1));
+ memset(tokenrec.reserved_char2, 0, sizeof(tokenrec.reserved_char2));
+
+ if (tokendb_putrec(username, &tokenrec))
+ return (-1);
+
+ /*
+ * Check if the shared secret was generated here. If so, we
+ * need to inform the user about it in order that it can be
+ * programmed into the token. See tokenverify() (above) for
+ * discussion of cipher generation.
+ */
+
+ if (!(flags & TOKEN_GENSECRET)) {
+ memset(&secret.ct, 0, sizeof(secret));
+ return (0);
+ }
+
+ printf("Shared secret for %s\'s token: "
+ "%03o %03o %03o %03o %03o %03o %03o %03o\n",
+ username, secret.cb[0], secret.cb[1], secret.cb[2], secret.cb[3],
+ secret.cb[4], secret.cb[5], secret.cb[6], secret.cb[7]);
+
+ des_key_sched(&secret.cb, key_schedule);
+ memset(&secret.ct, 0, sizeof(secret));
+ memset(&nulls, 0, sizeof(nulls));
+ des_ecb_encrypt(&nulls.cb, &checksum.cb, key_schedule, DES_ENCRYPT);
+ memset(key_schedule, 0, sizeof(key_schedule));
+ HTONL(checksum.ul[0]);
+ snprintf(checktxt.ct, sizeof(checktxt.ct), "%8.8lx", checksum.ul[0]);
+ printf("Hex Checksum: \"%s\"", checktxt.ct);
+
+ h2d(checktxt.ct);
+ printf("\tDecimal Checksum: \"%s\"\n", checktxt.ct);
+
+ return (0);
+}
+
+/*
+ * Magically transform a hex character string into a decimal character
+ * string as defined by the token card vendor. The string should have
+ * been lowercased by now.
+ */
+
+static void
+h2d(char *cp)
+{
+ int i;
+
+ for (i=0; i<sizeof(des_cblock); i++, cp++) {
+ if (*cp >= 'a' && *cp <= 'f')
+ *cp = tt->map[*cp - 'a'];
+ }
+}
+
+/*
+ * Translate an hex 16 byte ascii representation of an unsigned
+ * integer to a des_cblock.
+ */
+
+static void
+h2cb(char *hp, TOKEN_CBlock *cb)
+{
+ char scratch[9];
+
+ strlcpy(scratch, hp, sizeof(scratch));
+ cb->ul[0] = strtoul(scratch, NULL, 16);
+
+ strlcpy(scratch, hp + 8, sizeof(scratch));
+ cb->ul[1] = strtoul(scratch, NULL, 16);
+}
+
+/*
+ * Translate a des_cblock to an 16 byte ascii hex representation.
+ */
+
+static void
+cb2h(TOKEN_CBlock cb, char* hp)
+{
+ char scratch[17];
+
+ snprintf(scratch, 9, "%8.8lx", cb.ul[0]);
+ snprintf(scratch+8, 9, "%8.8lx", cb.ul[1]);
+ memcpy(hp, scratch, 16);
+}
+
+/*
+ * Lowercase possible hex response
+ */
+
+static void
+lcase(char *cp)
+{
+ while (*cp) {
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ cp++;
+ }
+}
--- /dev/null
+/* $OpenBSD: token.h,v 1.4 2002/09/06 18:19:14 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1995 Migration Associates Corp. 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: token.h,v 1.1 1996/08/26 20:13:10 prb Exp $
+ */
+
+/*
+ * Operations accepted by the token admin commands
+ */
+
+#define TOKEN_DISABLE 0x1 /* disable user account */
+#define TOKEN_ENABLE 0x2 /* enable user account */
+#define TOKEN_INITUSER 0x4 /* add/init user account */
+#define TOKEN_RMUSER 0x8 /* remove user account */
+#define TOKEN_UNLOCK 0x10 /* force unlock db record */
+
+/*
+ * Flags for options to modify TOKEN_INITUSER
+ */
+
+#define TOKEN_FORCEINIT 0x100 /* reinit existing account */
+#define TOKEN_GENSECRET 0x200 /* gen shared secret for token */
+
+/*
+ * Structure describing different token cards
+ */
+struct token_types {
+ char *name; /* name of card */
+ char *proper; /* proper name of card */
+ char *db; /* path to database */
+ char map[6]; /* how A-F map to decimal */
+ int options; /* various available options */
+ u_int modes; /* available modes */
+ u_int defmode; /* default mode (if none specified) */
+};
+
+struct token_types *tt; /* what type we are running as now */
+
+/*
+ * Options
+ */
+#define TOKEN_PHONE 0x0001 /* Allow phone number representation */
+#define TOKEN_HEXINIT 0x0002 /* Allow initialization in hex (and octal) */
+
+/*
+ * Function prototypes for commands involving intimate DES knowledge
+ */
+
+extern void tokenchallenge(char *, char *, int, char *);
+extern int tokenverify(char *, char *, char *);
+extern int tokenuserinit(int, char *, u_char *, u_int);
+extern int token_init(char *);
+extern u_int token_mode(char *);
+extern char * token_getmode(u_int);
--- /dev/null
+/* $OpenBSD: tokendb.c,v 1.8 2005/09/16 23:47:00 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1995 Migration Associates Corp. 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: tokendb.c,v 1.1 1996/08/26 20:13:10 prb Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "token.h"
+#include "tokendb.h"
+
+static DB *tokendb;
+
+/*
+ * Static function prototypes
+ */
+
+static int tokendb_open(void);
+static void tokendb_close(void);
+
+/*
+ * Retrieve a user record from the token database file
+ */
+
+int
+tokendb_getrec(char *username, TOKENDB_Rec *tokenrec)
+{
+ DBT key;
+ DBT data;
+ int status = 0;
+
+ key.data = username;
+ key.size = strlen(username) + 1;
+ memset(&data, 0, sizeof(data));
+
+ if (tokendb_open())
+ return(-1);
+
+ status = (tokendb->get)(tokendb, &key, &data, 0);
+ switch (status) {
+ case 1:
+ tokendb_close();
+ return(ENOENT);
+ case -1:
+ tokendb_close();
+ return(-1);
+ }
+ memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
+ if ((tokenrec->flags & TOKEN_USEMODES) == 0)
+ tokenrec->mode = tt->modes & ~TOKEN_RIM;
+ tokendb_close();
+ return (0);
+}
+
+/*
+ * Put a user record to the token database file.
+ */
+
+int
+tokendb_putrec(char *username, TOKENDB_Rec *tokenrec)
+{
+ DBT key;
+ DBT data;
+ int status = 0;
+
+ key.data = username;
+ key.size = strlen(username) + 1;
+
+ if (tokenrec->mode)
+ tokenrec->flags |= TOKEN_USEMODES;
+ data.data = tokenrec;
+ data.size = sizeof(TOKENDB_Rec);
+
+ if (!tokendb_open()) {
+ if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
+ tokendb_close();
+ return (-1);
+ }
+ status = (tokendb->put)(tokendb, &key, &data, 0);
+ }
+ tokendb_close();
+ return (status);
+}
+
+/*
+ * Remove a user record from the token database file.
+ */
+
+int
+tokendb_delrec(char *username)
+{
+ DBT key;
+ DBT data;
+ int status = 0;
+
+ key.data = username;
+ key.size = strlen(username) + 1;
+ memset(&data, 0, sizeof(data));
+
+ if (!tokendb_open()) {
+ if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
+ tokendb_close();
+ return (-1);
+ }
+ status = (tokendb->del)(tokendb, &key, 0);
+ }
+ tokendb_close();
+ return (status);
+}
+
+/*
+ * Open the token database. In order to expedite access in
+ * heavily loaded conditions, we employ a N1 lock method.
+ * Updates should be brief, so all locks wait infinitely.
+ * Wait for a read (shared) lock as all updates read first.
+ */
+
+static int
+tokendb_open(void)
+{
+ int must_set_perms = 0;
+ int must_set_mode = 0;
+ struct group *grp;
+ struct stat statb;
+
+ if ((grp = getgrnam(TOKEN_GROUP)) == NULL) {
+ printf("Missing %s group, authentication disabled\n",
+ TOKEN_GROUP);
+ fflush(stdout);
+ syslog(LOG_ALERT,
+ "the %s group is missing, token authentication disabled",
+ TOKEN_GROUP);
+ return (-1);
+ }
+
+ if (stat(tt->db, &statb) < 0) {
+ if (errno != ENOENT)
+ return (-1);
+ must_set_perms++;
+ } else {
+ if (statb.st_uid != 0 || statb.st_gid != grp->gr_gid) {
+#ifdef PARANOID
+ printf("Authentication disabled\n");
+ fflush(stdout);
+ syslog(LOG_ALERT,
+ "POTENTIAL COMPROMISE of %s. Owner was %u, "
+ "Group was %u", tt->db, statb.st_uid, statb.st_gid);
+ return (-1);
+#else
+ must_set_perms++;
+#endif
+ }
+ if ((statb.st_mode & 0777) != 0640) {
+#ifdef PARANOID
+ printf("Authentication disabled\n");
+ fflush(stdout);
+ syslog(LOG_ALERT,
+ "POTENTIAL COMPROMISE of %s. Mode was %o",
+ tt->db, statb.st_mode);
+ return (-1);
+#else
+ must_set_mode++;
+#endif
+ }
+ }
+ if (!(tokendb =
+ dbopen(tt->db, O_CREAT | O_RDWR, 0640, DB_BTREE, 0)) )
+ return (-1);
+
+ if (flock((tokendb->fd)(tokendb), LOCK_SH)) {
+ (tokendb->close)(tokendb);
+ return (-1);
+ }
+ if (must_set_perms && fchown((tokendb->fd)(tokendb), 0, grp->gr_gid))
+ syslog(LOG_INFO,
+ "Can't set owner/group of %s errno=%m", tt->db);
+ if (must_set_mode && fchmod((tokendb->fd)(tokendb), 0640))
+ syslog(LOG_INFO,
+ "Can't set mode of %s errno=%m", tt->db);
+
+ return (0);
+}
+
+/*
+ * Close the token database. We are holding an unknown lock.
+ * Release it, then close the db. Since little can be done
+ * about errors, we ignore them.
+ */
+
+static void
+tokendb_close(void)
+{
+ if (tokendb) {
+ (void)flock((tokendb->fd)(tokendb), LOCK_UN);
+ (tokendb->close)(tokendb);
+ tokendb = NULL;
+ }
+}
+
+/*
+ * Retrieve the first user record from the database, leaving the
+ * database open for the next retrieval. If the march thru the
+ * the database is aborted before end-of-file, the caller should
+ * call tokendb_close to release the read lock.
+ */
+
+int
+tokendb_firstrec(int reverse_flag, TOKENDB_Rec *tokenrec)
+{
+ DBT key;
+ DBT data;
+ int status = 0;
+
+ memset(&data, 0, sizeof(data));
+
+ if (!tokendb_open()) {
+ status = (tokendb->seq)(tokendb, &key, &data,
+ reverse_flag ? R_LAST : R_FIRST);
+ }
+ if (status) {
+ tokendb_close();
+ return (status);
+ }
+ if (!data.data) {
+ tokendb_close();
+ return (ENOENT);
+ }
+ memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
+ if ((tokenrec->flags & TOKEN_USEMODES) == 0)
+ tokenrec->mode = tt->modes & ~TOKEN_RIM;
+ return (0);
+}
+
+/*
+ * Retrieve the next sequential user record from the database. Close
+ * the database only on end-of-file or error.
+ */
+
+
+int
+tokendb_nextrec(int reverse_flag, TOKENDB_Rec *tokenrec)
+{
+ DBT key;
+ DBT data;
+ int status;
+
+ memset(&data, 0, sizeof(data));
+
+ status = (tokendb->seq)(tokendb, &key, &data,
+ reverse_flag ? R_PREV : R_NEXT);
+
+ if (status) {
+ tokendb_close();
+ return (status);
+ }
+ if (!data.data) {
+ tokendb_close();
+ return (ENOENT);
+ }
+ memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
+ if ((tokenrec->flags & TOKEN_USEMODES) == 0)
+ tokenrec->mode = tt->modes & ~TOKEN_RIM;
+ return (0);
+}
+
+/*
+ * Retrieve and lock a user record. Since there are no facilities in
+ * BSD for record locking, we hack a bit lock into the user record.
+ */
+
+int
+tokendb_lockrec(char *username, TOKENDB_Rec *tokenrec, unsigned recflags)
+{
+ DBT key;
+ DBT data;
+ int status;
+
+ key.data = username;
+ key.size = strlen(username) + 1;
+ memset(&data, 0, sizeof(data));
+
+ if (tokendb_open())
+ return(-1);
+
+ if (flock((tokendb->fd)(tokendb), LOCK_EX)) {
+ tokendb_close();
+ return(-1);
+ }
+ switch ((tokendb->get)(tokendb, &key, &data, 0)) {
+ case 1:
+ tokendb_close();
+ return (ENOENT);
+ case -1:
+ tokendb_close();
+ return(-1);
+ }
+ memcpy(tokenrec, data.data, sizeof(TOKENDB_Rec));
+
+ if ((tokenrec->flags & TOKEN_LOCKED)||(tokenrec->flags & recflags)) {
+ tokendb_close();
+ return(1);
+ }
+ data.data = tokenrec;
+ data.size = sizeof(TOKENDB_Rec);
+
+ time(&tokenrec->lock_time);
+ tokenrec->flags |= recflags;
+ status = (tokendb->put)(tokendb, &key, &data, 0);
+ tokendb_close();
+ if (status)
+ return(-1);
+ if ((tokenrec->flags & TOKEN_USEMODES) == 0)
+ tokenrec->mode = tt->modes & ~TOKEN_RIM;
+
+ return(0);
+}
+
--- /dev/null
+/* $OpenBSD: tokendb.h,v 1.4 2002/11/21 22:11:45 millert Exp $ */
+
+/*-
+ * Copyright (c) 1995 Migration Associates Corp. 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 Berkeley Software Design,
+ * Inc.
+ * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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.
+ *
+ * BSDI $From: tokendb.h,v 1.1 1996/08/26 20:13:11 prb Exp $
+ */
+
+/*
+ * Structure defining a record for a user. All fields
+ * stored in ascii to facilitate backup/reconstruction.
+ * A null byte is required after the share secret field.
+ */
+
+typedef struct {
+ char uname[L_cuserid]; /* user login name */
+ char secret[16]; /* token shared secret */
+ unsigned flags; /* record flags */
+ unsigned mode; /* token mode flags */
+ time_t lock_time; /* time of record lock */
+ u_char rim[8]; /* reduced input mode */
+ char reserved_char1[8];
+ char reserved_char2[80];
+} TOKENDB_Rec;
+
+/*
+ * Record flag values
+ */
+
+#define TOKEN_LOCKED 0x1 /* record locked for updating */
+#define TOKEN_ENABLED 0x2 /* user login method enabled */
+#define TOKEN_LOGIN 0x4 /* login in progress lock */
+#define TOKEN_USEMODES 0x8 /* use the mode field */
+
+#define TOKEN_DECMODE 0x1 /* allow decimal results */
+#define TOKEN_HEXMODE 0x2 /* allow hex results */
+#define TOKEN_PHONEMODE 0x4 /* allow phone book results */
+#define TOKEN_RIM 0x8 /* reduced imput mode */
+
+#define TOKEN_GROUP "_token" /* group that owns token database */
+
+/*
+ * Function prototypes for routines which manipulate the
+ * database for the token. These routines have no knowledge
+ * of DES or encryption. However, they will manipulate the
+ * flags field of the database record with complete abandon.
+ */
+
+extern int tokendb_delrec(char *);
+extern int tokendb_getrec(char *, TOKENDB_Rec *);
+extern int tokendb_putrec(char *, TOKENDB_Rec *);
+extern int tokendb_firstrec(int, TOKENDB_Rec *);
+extern int tokendb_nextrec(int, TOKENDB_Rec *);
+extern int tokendb_lockrec(char *, TOKENDB_Rec *, unsigned);
--- /dev/null
+/Makefile/1.3/Mon Aug 17 14:32:16 1998//
+/locking.c/1.9/Tue Oct 27 23:59:31 2009//
+/mail.local.8/1.28/Mon Jan 19 09:46:59 2009//
+/mail.local.c/1.32/Tue Oct 27 23:59:31 2009//
+/mail.local.h/1.5/Sat Apr 1 22:48:57 2006//
+/pathnames.h/1.5/Mon Jun 2 19:38:24 2003//
+D
--- /dev/null
+src/libexec/mail.local
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# from: @(#)Makefile 5.3 (Berkeley) 1/17/91
+# $OpenBSD: Makefile,v 1.3 1998/08/17 14:32:16 millert Exp $
+
+PROG= mail.local
+SRCS= mail.local.c locking.c
+MAN= mail.local.8
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: locking.c,v 1.9 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
+ * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
+ * 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. The name of the authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHORS 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.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "pathnames.h"
+#include "mail.local.h"
+
+static char lpath[MAXPATHLEN];
+
+void
+rellock(void)
+{
+
+ if (lpath[0])
+ unlink(lpath);
+}
+
+int
+getlock(char *name, struct passwd *pw)
+{
+ struct stat sb, fsb;
+ int lfd=-1;
+ char buf[8*1024];
+ int tries = 0;
+
+ (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
+ _PATH_MAILDIR, name);
+
+ if (stat(_PATH_MAILDIR, &sb) != -1 &&
+ (sb.st_mode & S_IWOTH) == S_IWOTH) {
+ /*
+ * We have a writeable spool, deal with it as
+ * securely as possible.
+ */
+ time_t ctim = -1;
+
+ seteuid(pw->pw_uid);
+ if (lstat(lpath, &sb) != -1)
+ ctim = sb.st_ctime;
+ while (1) {
+ /*
+ * Deal with existing user.lock files
+ * or directories or symbolic links that
+ * should not be here.
+ */
+ if (readlink(lpath, buf, sizeof buf-1) != -1) {
+ if (lstat(lpath, &sb) != -1 &&
+ S_ISLNK(sb.st_mode)) {
+ seteuid(sb.st_uid);
+ unlink(lpath);
+ seteuid(pw->pw_uid);
+ }
+ goto again;
+ }
+ if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) != -1)
+ break;
+again:
+ if (tries > 10) {
+ merr(NOTFATAL, "%s: %s", lpath,
+ strerror(errno));
+ seteuid(0);
+ return(-1);
+ }
+ if (tries > 9 &&
+ (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) {
+ if (fstat(lfd, &fsb) != -1 &&
+ lstat(lpath, &sb) != -1) {
+ if (fsb.st_dev == sb.st_dev &&
+ fsb.st_ino == sb.st_ino &&
+ ctim == fsb.st_ctime ) {
+ seteuid(fsb.st_uid);
+ baditem(lpath);
+ seteuid(pw->pw_uid);
+ }
+ }
+ }
+ sleep(1U << tries);
+ tries++;
+ continue;
+ }
+ seteuid(0);
+ } else {
+ /*
+ * Only root can write the spool directory.
+ */
+ while (1) {
+ if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
+ S_IRUSR|S_IWUSR)) != -1)
+ break;
+ if (tries > 9) {
+ merr(NOTFATAL, "%s: %s", lpath, strerror(errno));
+ return(-1);
+ }
+ sleep(1U << tries);
+ tries++;
+ }
+ }
+ return(lfd);
+}
+
+void
+baditem(char *path)
+{
+ char npath[MAXPATHLEN];
+
+ if (unlink(path) == 0)
+ return;
+ snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR);
+ if (mktemp(npath) == NULL)
+ return;
+ if (rename(path, npath) == -1)
+ unlink(npath);
+ else
+ merr(NOTFATAL, "nasty spool item %s renamed to %s",
+ path, npath);
+ /* XXX if we fail to rename, another attempt will happen later */
+}
+
+void
+merr(int isfatal, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+ if (isfatal)
+ exit(1);
+}
--- /dev/null
+.\" $OpenBSD: mail.local.8,v 1.28 2009/01/19 09:46:59 sobrado Exp $
+.\" Copyright (c) 1990 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. 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.
+.\"
+.\" from: @(#)mail.local.8 6.8 (Berkeley) 4/27/91
+.\"
+.Dd $Mdocdate: January 19 2009 $
+.Dt MAIL.LOCAL 8
+.Os
+.Sh NAME
+.Nm mail.local
+.Nd store mail in a mailbox
+.Sh SYNOPSIS
+.Nm mail.local
+.Op Fl Ll
+.Op Fl f Ar from
+.Ar user ...
+.Sh DESCRIPTION
+.Nm
+reads the standard input up to an end-of-file and appends it to each
+.Ar user Ns 's
+.Pa mail
+file.
+The
+.Ar user
+must be a valid user name.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f Ar from
+Specify the sender's name.
+.It Fl L
+Don't create a
+.Pa username.lock
+file while locking the spool.
+.It Fl l
+For compatibility, request that files named
+.Pa username.lock
+be used for locking.
+(This is the default behavior.)
+.El
+.Pp
+Individual mail messages in the mailbox are delimited by an empty
+line followed by a line beginning with the string
+.Dq "From\&\ " .
+A line containing the string
+.Dq "From\&\ " ,
+the sender's name and a timestamp is prepended to each delivered mail message.
+A blank line is appended to each message.
+A greater-than character
+.Pq Ql >
+is prepended to any line in the message which could be mistaken for a
+.Dq "From\&\ "
+delimiter line.
+.Pp
+Significant efforts have been made to ensure that
+.Nm
+acts as securely as possible if the spool directory is mode 1777 or 755.
+The default of mode 755 is more secure, but it prevents mail clients from using
+.Pa username.lock
+style locking.
+The use of 1777 is more flexible in an NFS shared-spool environment,
+so many sites use it.
+However, it does carry some risks, such as attackers filling the spool disk.
+Some of these problems may be alleviated
+by making the spool a separate filesystem, and placing quotas on it.
+The use of any mode other than 1777 and 755 for the spool directory is
+recommended against but may work properly.
+.Pp
+The mailbox is always locked using
+.Xr flock 2
+while mail is appended.
+Unless the
+.Fl L
+flag is specified, a
+.Pa username.lock
+file is also used.
+.Pp
+If the
+.Xr biff 1
+service is returned by
+.Xr getservbyname 3 ,
+the biff server is notified of delivered mail.
+.Pp
+.Ex -std mail.local
+.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.XXXXXXXXXX -compact
+.It Pa /tmp/local.XXXXXXXXXX
+temporary files
+.It Pa /var/mail/user
+user's mailbox directory
+.El
+.Sh SEE ALSO
+.Xr biff 1 ,
+.Xr mail 1 ,
+.Xr flock 2 ,
+.Xr getservbyname 3 ,
+.Xr comsat 8 ,
+.Xr sendmail 8
+.Sh HISTORY
+A superset of
+.Nm
+(handling mailbox reading as well as mail delivery) appeared in
+.At v7
+as the program
+.Xr mail 1 .
+.Sh BUGS
+Since
+.Xr sendmail 8
+bases its idea of whether a message has been delivered or not
+on the return value from
+.Nm mail.local ,
+using quotas in
+.Pa /var/mail
+can be problematic.
+By default,
+.Xr sendmail 8
+will ask
+.Nm
+to deliver a message to multiple recipients if possible.
+This causes problems in a quota environment since a message may be
+delivered to some users but not others due to disk quotas.
+Even though the message was delivered to some of the recipients,
+.Nm
+will exit with an exit code > 0, causing
+.Xr sendmail 8
+to attempt redelivery later.
+That means that some users will keep getting the same message every time
+.Xr sendmail 8
+runs its queue.
+.Pp
+If you are running with disk quotas on
+.Pa /var/mail
+it is imperative that you unset the
+.Dq m
+mailer flag for the
+.Sq local
+mailer.
+To do this, locate the line beginning with
+.Dq Mlocal
+in
+.Pa /etc/mail/sendmail.cf
+and remove the
+.Dq m
+from the flags section, denoted by
+.Dq F= .
+Alternately, you can override the default mailer flags by adding the line:
+.Pp
+.Dl define(`LOCAL_MAILER_FLAGS', `rn9S')dnl
+.Pp
+to your
+.Dq \.mc
+file (this is the source file that is used to generate
+.Pa /etc/mail/sendmail.cf ) .
--- /dev/null
+/* $OpenBSD: mail.local.c,v 1.32 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com>
+ * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu>
+ * Copyright (c) 1990 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pathnames.h"
+#include "mail.local.h"
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ int ch, fd, eval, lockfile=1, holdme=0;
+ uid_t uid;
+ char *from;
+
+ openlog("mail.local", LOG_PERROR, LOG_MAIL);
+
+ from = NULL;
+ while ((ch = getopt(argc, argv, "lLdf:r:H")) != -1)
+ switch (ch) {
+ case 'd': /* backward compatible */
+ break;
+ case 'f':
+ case 'r': /* backward compatible */
+ if (from)
+ merr(FATAL, "multiple -f options");
+ from = optarg;
+ break;
+ case 'l':
+ lockfile=1;
+ break;
+ case 'L':
+ lockfile=0;
+ break;
+ case 'H':
+ holdme=1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Support -H flag for backwards compat */
+ if (holdme) {
+ execl(_PATH_LOCKSPOOL, "lockspool", (char *)NULL);
+ merr(FATAL, "execl: lockspool: %s", strerror(errno));
+ } else {
+ if (!*argv)
+ usage();
+ if (geteuid() != 0)
+ merr(FATAL, "may only be run by the superuser");
+ }
+
+ /*
+ * 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 : "???";
+
+ fd = storemail(from);
+ for (eval = 0; *argv; ++argv)
+ eval |= deliver(fd, *argv, lockfile);
+ exit(eval);
+}
+
+int
+storemail(char *from)
+{
+ FILE *fp = NULL;
+ time_t tval;
+ int fd, eline;
+ size_t len;
+ char *line, *tbuf;
+
+ if ((tbuf = strdup(_PATH_LOCTMP)) == NULL)
+ merr(FATAL, "unable to allocate memory");
+ if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+")))
+ merr(FATAL, "unable to open temporary file");
+ (void)unlink(tbuf);
+ free(tbuf);
+
+ (void)time(&tval);
+ (void)fprintf(fp, "From %s %s", from, ctime(&tval));
+
+ for (eline = 1, tbuf = NULL; (line = fgetln(stdin, &len));) {
+ /* We have to NUL-terminate the line since fgetln does not */
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ else {
+ /* No trailing newline, so alloc space and copy */
+ if ((tbuf = malloc(len + 1)) == NULL)
+ merr(FATAL, "unable to allocate memory");
+ memcpy(tbuf, line, len);
+ tbuf[len] = '\0';
+ line = tbuf;
+ }
+ if (line[0] == '\0')
+ eline = 1;
+ else {
+ if (eline && line[0] == 'F' && len > 5 &&
+ !memcmp(line, "From ", 5))
+ (void)putc('>', fp);
+ eline = 0;
+ }
+ (void)fprintf(fp, "%s\n", line);
+ if (ferror(fp))
+ break;
+ }
+ if (tbuf)
+ free(tbuf);
+
+ /* Output a newline; note, empty messages are allowed. */
+ (void)putc('\n', fp);
+ (void)fflush(fp);
+ if (ferror(fp))
+ merr(FATAL, "temporary file write error");
+ return(fd);
+}
+
+int
+deliver(int fd, char *name, int lockfile)
+{
+ struct stat sb, fsb;
+ struct passwd *pw;
+ int mbfd=-1, rval=1, lfd=-1;
+ char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
+ off_t curoff;
+ size_t off;
+ ssize_t nr, nw;
+
+ /*
+ * Disallow delivery to unknown names -- special mailboxes can be
+ * handled in the sendmail aliases file.
+ */
+ if (!(pw = getpwnam(name))) {
+ merr(NOTFATAL, "unknown name: %s", name);
+ return(1);
+ }
+
+ (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
+
+ if (lockfile) {
+ lfd = getlock(name, pw);
+ if (lfd == -1)
+ return (1);
+ }
+
+ /* after this point, always exit via bad to remove lockfile */
+retry:
+ if (lstat(path, &sb)) {
+ if (errno != ENOENT) {
+ merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if ((mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) < 0) {
+ if (errno == EEXIST) {
+ /* file appeared since lstat */
+ goto retry;
+ } else {
+ merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ }
+ /*
+ * Set the owner and group. Historically, binmail repeated
+ * this at each mail delivery. We no longer do this, assuming
+ * that if the ownership or permissions were changed there
+ * was a reason for doing so.
+ */
+ if (fchown(mbfd, pw->pw_uid, pw->pw_gid) < 0) {
+ merr(NOTFATAL, "chown %u:%u: %s",
+ pw->pw_uid, pw->pw_gid, name);
+ goto bad;
+ }
+ } else {
+ if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
+ merr(NOTFATAL, "%s: linked or special file", path);
+ goto bad;
+ }
+ if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
+ S_IRUSR|S_IWUSR)) < 0) {
+ merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if (fstat(mbfd, &fsb)) {
+ /* relating error to path may be bad style */
+ merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ goto bad;
+ }
+ if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) {
+ merr(NOTFATAL, "%s: changed after open", path);
+ goto bad;
+ }
+ /* paranoia? */
+ if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) {
+ merr(NOTFATAL, "%s: linked or special file", path);
+ goto bad;
+ }
+ }
+
+ curoff = lseek(mbfd, 0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof biffmsg, "%s@%qd\n", name, curoff);
+ if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+ merr(NOTFATAL, "temporary file: %s", strerror(errno));
+ goto bad;
+ }
+
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; off += nw)
+ if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
+ merr(NOTFATAL, "%s: %s", path, strerror(errno));
+ (void)ftruncate(mbfd, curoff);
+ goto bad;
+ }
+
+ if (nr == 0) {
+ rval = 0;
+ } else {
+ (void)ftruncate(mbfd, curoff);
+ merr(FATAL, "temporary file: %s", strerror(errno));
+ }
+
+bad:
+ if (lfd != -1) {
+ rellock();
+ close(lfd);
+ }
+
+ if (mbfd != -1) {
+ (void)fsync(mbfd); /* Don't wait for update. */
+ (void)close(mbfd); /* Implicit unlock. */
+ }
+
+ if (!rval)
+ notifybiff(biffmsg);
+ return(rval);
+}
+
+void
+notifybiff(char *msg)
+{
+ static struct sockaddr_in addr;
+ static int f = -1;
+ struct hostent *hp;
+ struct servent *sp;
+ size_t len;
+
+ if (!addr.sin_family) {
+ /* Be silent if biff service not available. */
+ if (!(sp = getservbyname("biff", "udp")))
+ return;
+ if (!(hp = gethostbyname("localhost"))) {
+ merr(NOTFATAL, "localhost: %s", strerror(errno));
+ return;
+ }
+ addr.sin_len = sizeof(struct sockaddr_in);
+ addr.sin_family = hp->h_addrtype;
+ addr.sin_port = sp->s_port;
+ bcopy(hp->h_addr, &addr.sin_addr, (size_t)hp->h_length);
+ }
+ if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ merr(NOTFATAL, "socket: %s", strerror(errno));
+ return;
+ }
+ len = strlen(msg) + 1;
+ if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
+ != len)
+ merr(NOTFATAL, "sendto biff: %s", strerror(errno));
+}
+
+void
+usage(void)
+{
+ merr(FATAL, "usage: mail.local [-Ll] [-f from] user ...");
+}
--- /dev/null
+/* $OpenBSD: mail.local.h,v 1.5 2006/04/01 22:48:57 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1990 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. 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.
+ */
+
+#define FATAL 1
+#define NOTFATAL 0
+
+void baditem(char *);
+int deliver(int, char *, int);
+void merr(int, const char *, ...);
+int getlock(char *, struct passwd *);
+void notifybiff(char *);
+void rellock(void);
+int storemail(char *);
+void usage(void);
--- /dev/null
+/usr/obj/libexec/mail.local
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: pathnames.h,v 1.5 2003/06/02 19:38:24 millert Exp $*/
+
+/*-
+ * Copyright (c) 1990 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. 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.
+ *
+ * from: @(#)pathnames.h 5.3 (Berkeley) 1/17/91
+ */
+#include <paths.h>
+
+#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX"
+#define _PATH_LOCKSPOOL "/usr/libexec/lockspool"
--- /dev/null
+/Makefile/1.8/Mon Sep 1 17:18:19 2008//
+/makewhatis/1.1/Fri Aug 6 12:05:08 2004//
+/makewhatis.8/1.15/Thu Jun 26 05:42:05 2008//
+D/OpenBSD////
--- /dev/null
+src/libexec/makewhatis
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.8 2008/09/01 17:18:19 drahn Exp $
+
+MAN=makewhatis.8
+NOPROG=
+PACKAGES= \
+ OpenBSD/Makewhatis.pm \
+ OpenBSD/Makewhatis/Check.pm \
+ OpenBSD/Makewhatis/Find.pm \
+ OpenBSD/Makewhatis/Formated.pm \
+ OpenBSD/Makewhatis/Unformated.pm \
+ OpenBSD/Makewhatis/Whatis.pm
+
+SCRIPTS= \
+ makewhatis
+
+LIBBASE=/usr/libdata/perl5
+
+realinstall:
+.for i in ${PACKAGES}
+ ${INSTALL} -d -o ${LIBOWN} -g ${LIBGRP} -m ${DIRMODE} \
+ ${DESTDIR}${LIBBASE}/${i:H}
+ ${INSTALL} ${INSTALL_COPY} -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \
+ ${.CURDIR}/$i ${DESTDIR}${LIBBASE}/$i
+.endfor
+.for i in ${SCRIPTS}
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$i ${DESTDIR}${BINDIR}/$i
+.endfor
+
+.include <bsd.prog.mk>
--- /dev/null
+/Makewhatis.pm/1.6/Wed Aug 22 15:50:05 2007//
+D/Makewhatis////
--- /dev/null
+src/libexec/makewhatis/OpenBSD
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Makewhatis.pm,v 1.6 2007/08/22 15:50:05 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+
+package OpenBSD::Makewhatis;
+
+my ($picky, $testmode);
+
+
+# $subjects = scan_manpages(\@list)
+#
+# scan a set of manpages, return list of subjects
+#
+sub scan_manpages($)
+{
+ my $list = shift;
+ local $_;
+ my $done=[];
+
+ for (@$list) {
+ my ($file, $subjects);
+ if (m/\.(?:Z|gz)$/) {
+ unless (open $file, '-|', "gzip -fdc $_") {
+ warn "$0: Can't decompress $_\n";
+ next;
+ }
+ $_ = $`;
+ } else {
+ unless (open $file, '<', $_) {
+ warn "$0: Can't read $_\n";
+ next;
+ }
+ }
+ if (m/\.(?:[1-9ln][^.]*|tbl)$/) {
+ require OpenBSD::Makewhatis::Unformated;
+
+ $subjects = OpenBSD::Makewhatis::Unformated::handle($file, $_);
+ } elsif (m/\.0$/) {
+ require OpenBSD::Makewhatis::Formated;
+
+ $subjects = OpenBSD::Makewhatis::Formated::handle($file, $_);
+ # in test mode, we try harder
+ } elsif ($testmode) {
+ require OpenBSD::Makewhatis::Unformated;
+
+ $subjects = OpenBSD::Makewhatis::Unformated::handle($file, $_);
+ if (@$subjects == 0) {
+ require OpenBSD::Makewhatis::Formated;
+
+ $subjects = OpenBSD::Makewhatis::Formated::handle($file, $_);
+ }
+ } else {
+ print STDERR "Can't find type of $_\n";
+ next;
+ }
+ if ($picky) {
+ require OpenBSD::Makewhatis::Check;
+
+ for my $s (@$subjects) {
+ OpenBSD::Makewhatis::Check::verify_subject($s, $_);
+ }
+ }
+ push @$done, @$subjects;
+ }
+ return $done;
+}
+
+# build_index($dir)
+#
+# build index for $dir
+#
+sub build_index($)
+{
+ require OpenBSD::Makewhatis::Find;
+ require OpenBSD::Makewhatis::Whatis;
+
+ my $dir = shift;
+ my $list = OpenBSD::Makewhatis::Find::find_manpages($dir);
+ my $subjects = scan_manpages($list);
+ OpenBSD::Makewhatis::Whatis::write($subjects, $dir);
+}
+
+# merge($dir, \@pages)
+#
+# merge set of pages into directory index
+#
+sub merge($$)
+{
+ require OpenBSD::Makewhatis::Whatis;
+
+ my ($mandir, $args) = @_;
+
+ unless (-d $mandir) {
+ die "$0: $mandir: not a directory"
+ }
+ my $whatis = "$mandir/whatis.db";
+ my $subjects = scan_manpages($args);
+ if (open(my $old, '<', $whatis)) {
+ while (my $l = <$old>) {
+ chomp $l;
+ push(@$subjects, $l);
+ }
+ close($old);
+ }
+ OpenBSD::Makewhatis::Whatis::write($subjects, $mandir);
+}
+
+# remove(dir, \@pages)
+#
+# remove set of pages from directory index
+#
+sub remove($$)
+{
+ require OpenBSD::Makewhatis::Whatis;
+
+ my ($mandir, $args) = @_;
+ unless (-d $mandir) {
+ die "$0: $mandir: not a directory"
+ }
+ my $whatis = "$mandir/whatis.db";
+ open(my $old, '<', $whatis) or
+ die "$0: can't open $whatis to merge with: $!";
+ my $subjects = scan_manpages($args);
+ my %remove = map {$_ => 1 } @$subjects;
+ $subjects = [];
+ while (my $l = <$old>) {
+ chomp $l;
+ push(@$subjects, $l) unless defined $remove{$l};
+ }
+ close($old);
+ OpenBSD::Makewhatis::Whatis::write($subjects, $mandir);
+}
+
+# $dirs = default_dirs()
+#
+# read list of default directories from man.conf
+#
+sub default_dirs()
+{
+ my $args=[];
+ open(my $conf, '<', '/etc/man.conf') or
+ die "$0: Can't open /etc/man.conf";
+ while (my $l = <$conf>) {
+ chomp $l;
+ push(@$args, $1) if $l =~ m/^_whatdb\s+(.*)\/whatis\.db\s*$/;
+ }
+ close $conf;
+ return $args;
+}
+
+# makewhatis(\@args, \%opts)
+#
+# glue for front-end, see makewhatis(8)
+#
+sub makewhatis($$)
+{
+ my ($args, $opts) = @_;
+ if (defined $opts->{'p'}) {
+ $picky = 1;
+ }
+ if (defined $opts->{'t'}) {
+ $testmode = 1;
+ my $subjects = scan_manpages($args);
+ print join("\n", @$subjects), "\n";
+ return;
+ }
+
+ if (defined $opts->{'d'}) {
+ merge($opts->{'d'}, $args);
+ return;
+ }
+ if (defined $opts->{'u'}) {
+ remove($opts->{'u'}, $args);
+ return;
+ }
+ if (@$args == 0) {
+ $args = default_dirs();
+ }
+
+ for my $mandir (@$args) {
+ if (-d $mandir) {
+ build_index($mandir);
+ } elsif (-e $mandir || $picky) {
+ print STDERR "$0: $mandir is not a directory\n";
+ }
+ }
+}
+
+1;
--- /dev/null
+/Check.pm/1.2/Sat Mar 5 11:02:35 2005//
+/Find.pm/1.2/Sat Mar 5 11:02:35 2005//
+/Formated.pm/1.4/Sun Oct 11 08:14:37 2009//
+/Whatis.pm/1.3/Sat Mar 5 11:02:35 2005//
+/Unformated.pm/1.4/Sat Jan 2 15:01:02 2010//
+D
--- /dev/null
+src/libexec/makewhatis/OpenBSD/Makewhatis
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Check.pm,v 1.2 2005/03/05 11:02:35 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+package OpenBSD::Makewhatis::Check;
+
+sub found($$)
+{
+ my ($pattern, $filename) = @_;
+ my @candidates = glob $pattern;
+ if (@candidates > 0) {
+ # quick check of inode, dev number
+ my ($dev_cmp, $inode_cmp) = (stat $filename)[0,1];
+ for my $f (@candidates) {
+ my ($dev, $inode) = (stat $f)[0, 1];
+ if ($dev == $dev_cmp && $inode == $inode_cmp) {
+ return 1;
+ }
+ }
+ # slow check with File::Compare
+ require File::Compare;
+
+ for my $f (@candidates) {
+ if (File::Compare::compare($f, $filename) == 0) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+# verify_subject($subject, $filename):
+#
+# reparse the subject we're about to add, and check whether it makes
+# sense, e.g., is there a man page around.
+sub verify_subject($$)
+{
+ local $_ = shift;
+ my $filename = shift;
+ if (m/\s*(.*?)\s*\((.*?)\)\s-\s/) {
+ my $man = $1;
+ my $section = $2;
+ my @mans = split(/\s*,\s*|\s+/, $man);
+ my $base = $filename;
+ if ($base =~ m|/|) {
+ $base =~ s,/[^/]*$,,;
+ } else {
+ $base = '.';
+ }
+ my @notfound = ();
+ for my $func (@mans) {
+ my $i = $func;
+ next if found("$base/$i.*", $filename);
+ # try harder
+ $i =~ s/\(\)//;
+ $i =~ s/\-//g;
+ $i =~ s,^etc/,,;
+ next if found("$base/$i.*", $filename);
+ # and harder...
+ $i =~ tr/[A-Z]/[a-z]/;
+ next if found("$base/$i.*", $filename);
+ push(@notfound, $func);
+ }
+ if (@notfound > 0) {
+ print STDERR "Couldn't find ", join(', ', @notfound),
+ " in $filename:\n$_\n"
+ }
+ }
+}
+
+1;
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Find.pm,v 1.2 2005/03/05 11:02:35 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+package OpenBSD::Makewhatis::Find;
+
+use File::Find;
+
+
+# $list = find_manpages($dir)
+#
+# find all manpages under $dir, trim some duplicates.
+#
+sub find_manpages($)
+{
+ my $dir = shift;
+ my ($list, %nodes);
+ $list=[];
+ find(
+ sub {
+ return unless m/\.[\dln]\w*(?:\.Z|\.gz)?$/;
+ return unless -f $_;
+ my $unique = (stat _)[0]."/".(stat _)[1];
+ return if defined $nodes{$unique};
+ $nodes{$unique} = 1;
+ push(@$list, $File::Find::name);
+ }, $dir);
+ return $list;
+}
+
+1;
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Formated.pm,v 1.4 2009/10/11 08:14:37 sthen Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+package OpenBSD::Makewhatis::Formated;
+
+# add_formated_subject($subjects, $_, $section, $filename, $picky):
+# add subject $_ to the list of current $subjects, in section $section.
+#
+sub add_formated_subject
+{
+ my ($subjects, $line, $section, $filename, $picky) = @_;
+ local $_ = $line;
+
+ if (m/-/) {
+ s/([-+.\w\d,])\s+/$1 /g;
+ s/([a-z][A-z])-\s+/$1/g;
+ # some twits use: func -- description
+ if (m/^[^-+.\w\d]*(.*?) -(?:-?)\s+(.*)/) {
+ my ($func, $descr) = ($1, $2);
+ $func =~ s/,\s*$//;
+ # nroff will tend to cut function names at the weirdest places
+ if (length($func) > 40 && $func =~ m/,/ && $section =~ /^3/) {
+ $func =~ s/\b \b//g;
+ }
+ $_ = "$func ($section) - $descr";
+ push(@$subjects, $_);
+ return;
+ }
+ }
+
+ print STDERR "Weird subject line in $filename:\n$_\n" if $picky;
+
+ # try to find subject in line anyway
+ if (m/^\s*(.*\S)(?:\s{3,}|\(\)\s+)(.*?)\s*$/) {
+ my ($func, $descr) = ($1, $2);
+ $func =~ s/\s+/ /g;
+ $descr =~ s/\s+/ /g;
+ $_ = "$func ($section) - $descr";
+ push(@$subjects, $_);
+ return;
+ }
+
+ print STDERR "Weird subject line in $filename:\n$_\n" unless $picky;
+}
+
+# $lines = handle($file, $filename, $picky)
+#
+# handle a formatted manpage in $file
+#
+# may return several subjects, perl(3p) do !
+#
+sub handle
+{
+ my ($file, $filename, $picky) = @_;
+ local $_;
+ my ($section, $subject);
+ my @lines=();
+ my $foundname = 0;
+ while (<$file>) {
+ chomp;
+ if (m/^$/) {
+ # perl aggregates several subjects in one manpage
+ # so we don't stop after we've got one subject
+ add_formated_subject(\@lines, $subject, $section, $filename, $picky)
+ if defined $subject;
+ $subject = undef;
+ next;
+ }
+ # Remove boldface from wide characters
+ while (s/(..)\cH\cH\1/$1/g)
+ {}
+ # Remove boldface and underlining
+ while (s/_\cH//g || s/(.)\cH\1/$1/g)
+ {}
+ if (!$foundname && m/\w[-+.\w\d]*\(([-+.\w\d\/]+)\)/) {
+ $section = $1;
+ # Find architecture
+ if (m/Manual\s+\((.*?)\)/) {
+ $section = "$section/$1";
+ }
+ }
+ # Not all man pages are in english
+ # weird hex is `Namae' in japanese
+ if (m/^(?:NAME|NAMES|NAMN|NOMBRE|NOME|Name|\xbe|\xcc\xbe\xbe\xce|\xcc\xbe\xc1\xb0)\s*$/) {
+ unless (defined $section) {
+ # try to retrieve section from filename
+ if ($filename =~ m/(?:cat|man)([\dln])\//) {
+ $section = $1;
+ print STDERR "Can't find section in $filename, deducting $section from context\n" if $picky;
+ } else {
+ $section='??';
+ print STDERR "Can't find section in $filename\n";
+ }
+ }
+ $foundname = 1;
+ next;
+ }
+ if ($foundname) {
+ if (m/^\S/ || m/^\s+\*{3,}\s*$/) {
+ add_formated_subject(\@lines, $subject, $section, $filename, $picky)
+ if defined $subject;
+ last;
+ } else {
+ # deal with troff hyphenations
+ if (defined $subject and $subject =~ m/\xad\s*$/) {
+ $subject =~ s/(?:\xad\cH)*\xad\s*$//;
+ s/^\s*//;
+ }
+ # more troff hyphenation
+ if (defined $subject and $subject =~ m/\S(?:\-\cH)*\-$/) {
+ $subject =~ s/(?:\-\cH)*\-$//;
+ s/^\s*//;
+ }
+ s/^\s+/ /;
+ $subject.=$_;
+ }
+ }
+ }
+
+ print STDERR "Can't parse $filename (not a manpage ?)\n" if @lines == 0;
+ return \@lines;
+}
+
+1;
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Unformated.pm,v 1.4 2009/12/24 14:08:20 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+package OpenBSD::Makewhatis::Unformated;
+
+# add_unformated_subject($lines, $toadd, $section, $filename, $toexpand):
+#
+# build subject from list of $toadd lines, and add it to the list
+# of current subjects as section $section
+#
+sub add_unformated_subject
+{
+ my ($subjects, $toadd, $section, $filename, $toexpand, $picky) = @_;
+
+ my $exp = sub {
+ if (defined $toexpand->{$_[0]}) {
+ return $toexpand->{$_[0]};
+ } else {
+ print STDERR "$filename: can't expand $_[0]\n";
+ return "";
+ }
+ };
+
+ local $_ = join(' ', @$toadd);
+ # do interpolations
+ s/\\\*\((..)/&$exp($1)/ge;
+ s/\\\*\[(.*?)\]/&$exp($1)/ge;
+
+ # horizontal space adjustments
+ while (s/\\s[-+]?\d+//g)
+ {}
+ # unbreakable spaces
+ s/\\\s+/ /g;
+ # unbreakable em dashes
+ s/\\\|\\\(em\\\|/-/g;
+ # em dashes
+ s/\\\(em\s+/- /g;
+ # single quotes
+ s/\\\(aq/\'/g;
+ # em dashes in the middle of lines
+ s/\\\(em/-/g;
+ s/\\\*[LO]//g;
+ s/\\\(tm/(tm)/g;
+ # font changes
+ s/\\f[BIRP]//g;
+ s/\\f\(..//g;
+ # fine space adjustments
+ while (s/\\[vh]\'.*?\'//g)
+ {}
+ unless (s/\s+\\-\s+/ ($section) - / || s/\s*\\\-/ ($section) -/ ||
+ s/\s-\s/ ($section) - /) {
+ print STDERR "Weird subject line in $filename:\n$_\n" if $picky;
+ # Try guessing where the separation falls...
+ s/\s+\:\s+/ ($section) - / || s/\S+\s+/$& ($section) - / || s/\s*$/ ($section) - (empty subject)/;
+ }
+ # other dashes
+ s/\\-/-/g;
+ # escaped characters
+ s/\\\&(.)/$1/g;
+ s/\\\|/|/g;
+ # gremlins...
+ s/\\c//g;
+ # sequence of spaces
+ s/\s+$//;
+ s/^\s+//;
+ s/\s+/ /g;
+ # some damage control
+ if (m/^\Q($section) - \E/) {
+ print STDERR "Rejecting non-subject line from $filename:\n$_\n"
+ if $picky;
+ return;
+ }
+ push(@$subjects, $_);
+}
+
+# $lines = handle($file, $filename, $picky)
+#
+# handle an unformated manpage in $file
+#
+# may return several subjects, perl(3p) do !
+#
+sub handle
+{
+ my ($f, $filename, $picky) = @_;
+ my @lines = ();
+ my %toexpand = ();
+ my $so_found = 0;
+ my $found_th = 0;
+ my $found_old = 0;
+ my $found_dt = 0;
+ my $found_new = 0;
+ # subject/keep is the only way to deal with Nm/Nd pairs
+ my @subject = ();
+ my @keep = ();
+ my $nd_seen = 0;
+ local $_;
+ # retrieve basename of file
+ my ($name, $section) = $filename =~ m|(?:.*/)?(.*)\.([\w\d]+)|;
+ # scan until macro
+ while (<$f>) {
+ next unless m/^\./ || $found_old || $found_new;
+ next if m/^\.\\\"/;
+ next if m/^\.if\s+t\s+/;
+ s/^\.i[ef]\s+n\s+//;
+ s/^\.i[ef]\s+\\n\(\.g\s+//;
+ if (m/^\.\s*de/) {
+ while (<$f>) {
+ last if m/^\.\s*\./;
+ }
+ next;
+ }
+ if (m/^\.\s*ds\s+(\S+)\s+/) {
+ chomp($toexpand{$1} = $');
+ next;
+ }
+ # Some cross-refs just link to another manpage
+ $so_found = 1 if m/^\.\s*so/;
+ if (m/^\.\s*TH/ || m/^\.\s*th/) {
+ # in pricky mode, we should try to match these
+ # ($name2, $section2) = m/^\.(?:TH|th)\s+(\S+)\s+(\S+)/;
+ # scan until first section
+ $found_th = 1;
+ next;
+ }
+ if ($found_th && !$found_old && (m/^\.\s*SH/ || m/^\.\s*sh/)) {
+ $found_old = 1;
+ next;
+ }
+ if (m/^\.\s*Dt/) {
+ $section .= "/$1" if (m/^\.\s*Dt\s+\S+\s+\d\S*\s+(\S+)/);
+ $found_dt = 1;
+ next;
+ }
+ if ($found_dt && !$found_new && m/^\.\s*Sh/) {
+ $found_new = 1;
+ next;
+ }
+ if ($found_old) {
+ last if m/^\.\s*(?:SH|sh|SS|ss|nf|LI)/;
+ # several subjects in one manpage
+ if (m/^\.\s*(?:PP|Pp|br|PD|LP|sp)/) {
+ add_unformated_subject(\@lines, \@subject,
+ $section, $filename, \%toexpand, $picky)
+ if @subject != 0;
+ @subject = ();
+ next;
+ }
+ next if m/^\'/ || m/^\.\s*tr\s+/ || m/^\.\s*\\\"/ ||
+ m/^\.\s*sv/ || m/^\.\s*Vb\s+/ || m/\.\s*HP\s+/;
+ # Motif index entries, don't do anything for now.
+ next if m/^\.\s*iX/;
+ # Some other index (cook)
+ next if m/^\.\s*XX/;
+ chomp;
+ s/\.\s*(?:B|I|IR|SM|BR)\s+//;
+ if (m/^\.\s*(\S\S)/) {
+ print STDERR "$filename: not grokking $_\n"
+ if $picky;
+ next;
+ }
+ push(@subject, $_) unless m/^\s*$/;
+ next;
+ }
+ if ($found_new) {
+ last if m/^\.\s*Sh/;
+ s/\s,/,/g;
+ if (s/^\.\s*(\S\S)\s+//) {
+ my $macro = $1;
+ next if $macro eq "\\\"";
+ s/\"(.*?)\"/$1/g;
+ s/\\-/-/g;
+ $macro eq 'Xr' and s/^(\S+)\s+(\d\S*)/$1 ($2)/;
+ $macro eq 'Ox' and s/^/OpenBSD /;
+ $macro eq 'Nx' and s/^/NetBSD /;
+ if ($macro eq 'Nd') {
+ if (@keep != 0) {
+ add_unformated_subject(\@lines, \@keep,
+ $section, $filename, \%toexpand, $picky);
+ @keep = ();
+ }
+ push(@subject, "\\-");
+ $nd_seen = 1;
+ }
+ if ($nd_seen && $macro eq 'Nm') {
+ @keep = @subject;
+ @subject = ();
+ $nd_seen = 0;
+ }
+ }
+ push(@subject, $_) unless m/^\s*$/;
+ }
+ }
+ if ($found_th && !$found_old) {
+ print STDERR "Couldn't find subject in old manpage $filename\n";
+ }
+ if ($found_dt && !$found_new) {
+ print STDERR "Couldn't find subject in new manpage $filename\n";
+ }
+ unshift(@subject, @keep) if @keep != 0;
+ add_unformated_subject(\@lines, \@subject, $section,
+ $filename, \%toexpand, $picky) if @subject != 0;
+ if (!$so_found && !$found_old && !$found_new) {
+ print STDERR "Unknown manpage type $filename\n";
+ }
+ return \@lines;
+}
+
+1;
--- /dev/null
+# ex:ts=8 sw=4:
+# $OpenBSD: Whatis.pm,v 1.3 2005/03/05 11:02:35 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+use strict;
+use warnings;
+package OpenBSD::Makewhatis::Whatis;
+
+use constant MAXLINELEN => 8192;
+
+use File::Temp qw/tempfile/;
+use File::Compare;
+
+# write($list, $dir):
+#
+# write $list to file named $file, removing duplicate entries.
+# Change $file mode/owners to expected values
+# Write to temporary file first, and do the copy only if changes happened.
+#
+sub write
+{
+ my ($list, $dir) = @_;
+ my $f = "$dir/whatis.db";
+
+ my ($out, $tempname);
+ ($out, $tempname) = tempfile('/tmp/makewhatis.XXXXXXXXXX') or die "$0: Can't open temporary file";
+
+ my @sorted = sort @$list;
+ my $last;
+
+ while (my $l = shift @sorted) {
+ next if length $l > MAXLINELEN;
+ print $out $l, "\n" unless defined $last and $l eq $last;
+ $last = $l;
+ }
+ close $out;
+ if (compare($tempname, $f) == 0) {
+ unlink($tempname);
+ } else {
+ require File::Copy;
+
+ unlink($f);
+ if (File::Copy::move($tempname, $f)) {
+ chmod 0444, $f;
+ chown 0, (getgrnam 'bin')[2], $f;
+ } else {
+ print STDERR "$0: Can't create $f ($!)\n";
+ unlink($tempname);
+ exit 1;
+ }
+ }
+}
+
+1;
--- /dev/null
+#!/usr/bin/perl -w
+# ex:ts=8 sw=4:
+
+# $OpenBSD: makewhatis,v 1.1 2004/08/06 12:05:08 espie Exp $
+# Copyright (c) 2000-2004 Marc Espie <espie@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+require 5.006_000;
+
+use strict;
+use warnings;
+
+use Getopt::Std;
+use OpenBSD::Makewhatis;
+
+
+# main code
+
+my %opts;
+getopts('tpd:u:', \%opts);
+
+OpenBSD::Makewhatis::makewhatis(\@ARGV, \%opts);
+
+#while (my ($key, $value) = each %INC) {
+# print "$key => $value\n";
+#}
--- /dev/null
+.\" $OpenBSD: makewhatis.8,v 1.15 2008/06/26 05:42:05 ray Exp $
+.\" $NetBSD: makewhatis.8,v 1.2.2.1 1997/11/10 19:57:45 thorpej Exp $
+.\"
+.\" Copyright (c) 1997 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Robert Dobbs <banshee@gabriella.resort.com>.
+.\"
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd $Mdocdate: June 26 2008 $
+.Dt MAKEWHATIS 8
+.Os
+.Sh NAME
+.Nm makewhatis
+.Nd create a whatis.db database
+.Sh SYNOPSIS
+.Nm makewhatis
+.Op Fl p
+.Op Ar manpath ...
+.Nm makewhatis
+.Op Fl p
+.Fl d Ar manpath
+.Ar files ...
+.Nm makewhatis
+.Op Fl p
+.Fl u Ar manpath
+.Ar files ...
+.Nm makewhatis
+.Op Fl p
+.Fl t
+.Ar files
+.Sh DESCRIPTION
+.Nm
+strips the NAME lines from compiled or raw
+.Xr man 1
+pages and creates
+a whatis.db database (that is, a subject index)
+for use with
+.Xr apropos 1 ,
+.Xr whatis 1 ,
+and
+.Xr man 1 's
+.Fl k
+option.
+If
+.Ar manpath
+is unspecified,
+.Nm
+by default creates databases for each directory prefixed by
+the _whatdb keyword in
+.Pa /etc/man.conf .
+Man pages compressed with
+.Xr compress 1
+and
+.Xr gzip 1
+are uncompressed before processing.
+.Pp
+If the
+.Fl d
+option is used,
+.Nm
+merges the description of
+.Ar files
+with an existing
+.Pa whatis.db
+database in
+.Ar manpath .
+.Pp
+If the
+.Fl u
+option is used,
+.Nm
+removes the description of
+.Ar files
+from an existing
+.Pa whatis.db
+database in
+.Ar manpath .
+.Pp
+If the
+.Fl p
+option is used,
+.Nm
+is less forgiving and warns about incorrect man pages.
+.Pp
+The
+.Fl t
+option can be used to check a set of potential man pages without
+changing any
+.Pa whatis.db
+database.
+.Sh FILES
+.Bl -tag -width /etc/man.conf -compact
+.It Pa whatis.db
+index to man pages in directory
+.It Pa /etc/man.conf
+man configuration information
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr man 1 ,
+.Xr nroff 1 ,
+.Xr whatis 1 ,
+.Xr man.conf 5
+.Sh BUGS
+.Nm
+should parse
+.Pa /etc/man.conf
+and deal with extra configuration information.
+In particular, it does not handle
+.Xr nroff 1
+.Cm me
+format.
+Likewise, its use of
+heuristics to retrieve subjects from most man pages is not 100% accurate.
--- /dev/null
+/usr/obj/libexec/makewhatis
\ No newline at end of file
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:31 2001//
+/rpc.rquotad.8/1.8/Thu May 31 19:19:40 2007//
+/rquotad.c/1.20/Tue Sep 14 23:49:49 2004//
+D
--- /dev/null
+src/libexec/rpc.rquotad
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:31 niklas Exp $
+
+PROG = rpc.rquotad
+SRCS = rquotad.c
+MAN = rpc.rquotad.8
+MLINKS = rpc.rquotad.8 rquotad.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/rpc.rquotad
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.rquotad.8,v 1.8 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1994 Theo de Raadt
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" $Id: rpc.rquotad.8,v 1.8 2007/05/31 19:19:40 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RPC.RQUOTAD 8
+.Os
+.Sh NAME
+.Nm rquotad ,
+.Nm rpc.rquotad
+.Nd remote quota server
+.Sh SYNOPSIS
+.Nm rpc.rquotad
+.Sh DESCRIPTION
+.Nm rpc.rquotad
+is an
+.Xr rpc 3
+server which returns quotas for a user of a local filesystem
+which is NFS-mounted onto a remote machine.
+.Xr quota 1
+uses the results to display user quotas for remote filesystems.
+.Nm rpc.rquotad
+is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rquotad
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rquota.x .
+.Sh SEE ALSO
+.Xr quota 1
+.Sh BUGS
+.Bx 4.4
+and
+.Ox
+support group quotas but the rquota protocol does not.
--- /dev/null
+/* $OpenBSD: rquotad.c,v 1.20 2004/09/14 23:49:49 deraadt Exp $ */
+
+/*
+ * by Manuel Bouyer (bouyer@ensta.fr). Public domain.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <ufs/ufs/quota.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rquota.h>
+#include <arpa/inet.h>
+
+void rquota_service(struct svc_req *request, SVCXPRT *transp);
+void sendquota(struct svc_req *request, SVCXPRT *transp);
+void printerr_reply(SVCXPRT *transp);
+void initfs(void);
+int getfsquota(long id, char *path, struct dqblk *dqblk);
+int hasquota(struct fstab *fs, char **qfnamep);
+
+/*
+ * structure containing informations about ufs filesystems
+ * initialised by initfs()
+ */
+struct fs_stat {
+ struct fs_stat *fs_next; /* next element */
+ char *fs_file; /* mount point of the filesystem */
+ char *qfpathname; /* pathname of the quota file */
+ dev_t st_dev; /* device of the filesystem */
+};
+struct fs_stat *fs_begin = NULL;
+
+int from_inetd = 1;
+
+/* ARGSUSED */
+static void
+cleanup(int signo)
+{
+ (void) pmap_unset(RQUOTAPROG, RQUOTAVERS); /* XXX signal races */
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int sock = 0;
+ int proto = 0;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) pmap_unset(RQUOTAPROG, RQUOTAVERS);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /* create and register the service */
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "couldn't create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RQUOTAPROG, RQUOTAVERS, %s).",
+ proto ? "udp" : "(inetd)");
+ exit(1);
+ }
+
+ initfs(); /* init the fs_stat list */
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+void
+rquota_service(struct svc_req *request, SVCXPRT *transp)
+{
+ switch (request->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ break;
+
+ case RQUOTAPROC_GETQUOTA:
+ case RQUOTAPROC_GETACTIVEQUOTA:
+ sendquota(request, transp);
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ break;
+ }
+ if (from_inetd)
+ exit(0);
+}
+
+/* read quota for the specified id, and send it */
+void
+sendquota(struct svc_req *request, SVCXPRT *transp)
+{
+ struct getquota_args getq_args;
+ struct getquota_rslt getq_rslt;
+ struct dqblk dqblk;
+ struct timeval timev;
+
+ bzero((char *)&getq_args, sizeof(getq_args));
+ if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (request->rq_cred.oa_flavor != AUTH_UNIX) {
+ /* bad auth */
+ getq_rslt.status = Q_EPERM;
+ } else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
+ /* failed, return noquota */
+ getq_rslt.status = Q_NOQUOTA;
+ } else {
+ gettimeofday(&timev, NULL);
+ getq_rslt.status = Q_OK;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize = DEV_BSIZE;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
+ dqblk.dqb_bhardlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
+ dqblk.dqb_bsoftlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
+ dqblk.dqb_curblocks;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
+ dqblk.dqb_ihardlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
+ dqblk.dqb_isoftlimit;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
+ dqblk.dqb_curinodes;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
+ dqblk.dqb_btime - timev.tv_sec;
+ getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
+ dqblk.dqb_itime - timev.tv_sec;
+ }
+ if (!svc_sendreply(transp, xdr_getquota_rslt, (char *)&getq_rslt)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+}
+
+/* initialise the fs_tab list from entries in /etc/fstab */
+void
+initfs(void)
+{
+ struct fs_stat *fs_current = NULL;
+ struct fs_stat *fs_next = NULL;
+ char *qfpathname;
+ struct fstab *fs;
+ struct stat st;
+
+ setfsent();
+ while ((fs = getfsent())) {
+ if (strcmp(fs->fs_vfstype, "ffs"))
+ continue;
+ if (!hasquota(fs, &qfpathname))
+ continue;
+
+ fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat));
+ if (fs_current == NULL) {
+ syslog(LOG_ERR, "can't malloc: %m");
+ exit(1);
+ }
+ fs_current->fs_next = fs_next; /* next element */
+
+ fs_current->fs_file = strdup(fs->fs_file);
+ if (fs_current->fs_file == NULL) {
+ syslog(LOG_ERR, "can't strdup: %m");
+ exit(1);
+ }
+
+ fs_current->qfpathname = strdup(qfpathname);
+ if (fs_current->qfpathname == NULL) {
+ syslog(LOG_ERR, "can't strdup: %m");
+ exit(1);
+ }
+
+ stat(fs_current->fs_file, &st);
+ fs_current->st_dev = st.st_dev;
+
+ fs_next = fs_current;
+ }
+ endfsent();
+ fs_begin = fs_current;
+}
+
+/*
+ * gets the quotas for id, filesystem path.
+ * Return 0 if fail, 1 otherwise
+ */
+int
+getfsquota(long id, char *path, struct dqblk *dqblk)
+{
+ struct stat st_path;
+ struct fs_stat *fs;
+ int qcmd, fd, ret = 0;
+
+ if (stat(path, &st_path) < 0)
+ return (0);
+
+ qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
+
+ for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
+ /* where the device is the same as path */
+ if (fs->st_dev != st_path.st_dev)
+ continue;
+
+ /* find the specified filesystem. get and return quota */
+ if (quotactl(fs->fs_file, qcmd, id, (char *)dqblk) == 0)
+ return (1);
+
+ if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "open error: %s: %m", fs->qfpathname);
+ return (0);
+ }
+ if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET) ==
+ (off_t)-1) {
+ close(fd);
+ return (1);
+ }
+ switch (read(fd, dqblk, sizeof(struct dqblk))) {
+ case 0:
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero((caddr_t) dqblk, sizeof(struct dqblk));
+ ret = 1;
+ break;
+ case sizeof(struct dqblk): /* OK */
+ ret = 1;
+ break;
+ default: /* ERROR */
+ syslog(LOG_ERR, "read error: %s: %m", fs->qfpathname);
+ close(fd);
+ return (0);
+ }
+ close(fd);
+ }
+ return (ret);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ * Comes from quota.c, NetBSD 0.9
+ */
+int
+hasquota(struct fstab *fs, char **qfnamep)
+{
+ static char initname, usrname[100];
+ static char buf[BUFSIZ];
+ char *opt, *cp;
+ char *qfextension[] = INITQFNAMES;
+
+ cp = NULL;
+ if (!initname) {
+ (void)snprintf(usrname, sizeof usrname, "%s%s",
+ qfextension[USRQUOTA], QUOTAFILENAME);
+ initname = 1;
+ }
+ strlcpy(buf, fs->fs_mntops, sizeof buf);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = strchr(opt, '=')))
+ *cp++ = '\0';
+ if (strcmp(opt, usrname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void)snprintf(buf, sizeof buf, "%s/%s.%s", fs->fs_file,
+ QUOTAFILENAME, qfextension[USRQUOTA]);
+ *qfnamep = buf;
+ return (1);
+}
--- /dev/null
+/Makefile/1.8/Sat Jun 29 08:14:07 2002//
+/rpc.rstatd.8/1.9/Thu May 31 19:19:40 2007//
+/rstat_proc.c/1.27/Tue Oct 27 23:59:31 2009//
+/rstatd.c/1.23/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rpc.rstatd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.8 2002/06/29 08:14:07 deraadt Exp $
+
+PROG = rpc.rstatd
+.PATH: ${.CURDIR}/../../usr.bin/vmstat
+
+CFLAGS+=-I${.CURDIR}/../../usr.bin/vmstat -DNOKVM
+SRCS = dkstats.c rstatd.c rstat_proc.c
+MAN = rpc.rstatd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
+
--- /dev/null
+/usr/obj/libexec/rpc.rstatd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.rstatd.8,v 1.9 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1985, 1991 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. 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.
+.\"
+.\" $OpenBSD: rpc.rstatd.8,v 1.9 2007/05/31 19:19:40 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RPC.RSTATD 8
+.Os
+.Sh NAME
+.Nm rpc.rstatd
+.Nd kernel statistics server
+.Sh SYNOPSIS
+.Nm rpc.rstatd
+.Sh DESCRIPTION
+.Nm rpc.rstatd
+is a server which returns performance statistics obtained from the kernel.
+Some of these statistics may be read using the
+.Xr rup 1
+command.
+The
+.Nm rpc.rstatd
+daemon is normally invoked by
+.Xr inetd 8 .
+At startup,
+.Nm
+performs a
+.Xr chroot 2
+to
+.Pa /var/empty
+and switches to user
+.Va nobody .
+.Pp
+.Nm rpc.rstatd
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rstat.x .
+.Sh SEE ALSO
+.Xr rup 1 ,
+.Xr inetd 8
--- /dev/null
+/* $OpenBSD: rstat_proc.c,v 1.27 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+/*
+ * rstat service: built with rstat.x and derived from rpc.rstatd.c
+ *
+ * Copyright (c) 1984 by Sun Microsystems, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/vmmeter.h>
+#include <sys/dkstat.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <uvm/uvm_extern.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include "dkstats.h"
+
+#undef FSHIFT /* Use protocol's shift and scale values */
+#undef FSCALE
+#undef DK_NDRIVE
+#undef CPUSTATES
+#undef if_ipackets
+#undef if_ierrors
+#undef if_opackets
+#undef if_oerrors
+#undef if_collisions
+#include <rpcsvc/rstat.h>
+
+int cp_xlat[CPUSTATES] = { CP_USER, CP_NICE, CP_SYS, CP_IDLE };
+
+extern int dk_ndrive; /* from dkstats.c */
+extern struct _disk cur, last;
+char *memf = NULL, *nlistf = NULL;
+int hz;
+
+extern int from_inetd;
+int sincelastreq = 0; /* number of alarms since last request */
+extern int closedown;
+
+union {
+ struct stats s1;
+ struct statsswtch s2;
+ struct statstime s3;
+} stats_all;
+
+void updatestat(void);
+void updatestatsig(int sig);
+void setup(void);
+
+volatile sig_atomic_t wantupdatestat;
+
+static int stat_is_init = 0;
+
+#ifndef FSCALE
+#define FSCALE (1 << 8)
+#endif
+
+static void
+stat_init(void)
+{
+ stat_is_init = 1;
+ setup();
+ updatestat();
+ (void) signal(SIGALRM, updatestatsig);
+ alarm(1);
+}
+
+statstime *
+rstatproc_stats_3_svc(void *arg, struct svc_req *rqstp)
+{
+ if (!stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return (&stats_all.s3);
+}
+
+statsswtch *
+rstatproc_stats_2_svc(void *arg, struct svc_req *rqstp)
+{
+ if (!stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return (&stats_all.s2);
+}
+
+stats *
+rstatproc_stats_1_svc(void *arg, struct svc_req *rqstp)
+{
+ if (!stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ return (&stats_all.s1);
+}
+
+u_int *
+rstatproc_havedisk_3_svc(void *arg, struct svc_req *rqstp)
+{
+ static u_int have;
+
+ if (!stat_is_init)
+ stat_init();
+ sincelastreq = 0;
+ have = dk_ndrive != 0;
+ return (&have);
+}
+
+u_int *
+rstatproc_havedisk_2_svc(void *arg, struct svc_req *rqstp)
+{
+ return (rstatproc_havedisk_3_svc(arg, rqstp));
+}
+
+u_int *
+rstatproc_havedisk_1_svc(void *arg, struct svc_req *rqstp)
+{
+ return (rstatproc_havedisk_3_svc(arg, rqstp));
+}
+
+/* ARGSUSED */
+void
+updatestatsig(int sig)
+{
+ wantupdatestat = 1;
+}
+
+void
+updatestat(void)
+{
+ int i, mib[2], save_errno = errno;
+ struct uvmexp uvmexp;
+ size_t len;
+ struct if_data *ifdp;
+ struct ifaddrs *ifaddrs, *ifa;
+ double avrun[3];
+ struct timeval tm, btm;
+ long *cp_time = cur.cp_time;
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "entering updatestat");
+#endif
+ if (sincelastreq >= closedown) {
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "about to closedown");
+#endif
+ if (from_inetd)
+ _exit(0);
+ else {
+ stat_is_init = 0;
+ errno = save_errno;
+ return;
+ }
+ }
+ sincelastreq++;
+
+ /*
+ * dkreadstats reads in the "disk_count" as well as the "disklist"
+ * statistics. It also retrieves "hz" and the "cp_time" array.
+ */
+ dkreadstats();
+ memset(stats_all.s1.dk_xfer, '\0', sizeof(stats_all.s1.dk_xfer));
+ for (i = 0; i < dk_ndrive && i < DK_NDRIVE; i++)
+ stats_all.s1.dk_xfer[i] = cur.dk_rxfer[i] + cur.dk_wxfer[i];
+
+ for (i = 0; i < CPUSTATES; i++)
+ stats_all.s1.cp_time[i] = cp_time[cp_xlat[i]];
+ (void)getloadavg(avrun, sizeof(avrun) / sizeof(avrun[0]));
+ stats_all.s2.avenrun[0] = avrun[0] * FSCALE;
+ stats_all.s2.avenrun[1] = avrun[1] * FSCALE;
+ stats_all.s2.avenrun[2] = avrun[2] * FSCALE;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ len = sizeof(btm);
+ if (sysctl(mib, 2, &btm, &len, NULL, 0) < 0) {
+ syslog(LOG_ERR, "can't sysctl kern.boottime: %m");
+ _exit(1);
+ }
+ stats_all.s2.boottime.tv_sec = btm.tv_sec;
+ stats_all.s2.boottime.tv_usec = btm.tv_usec;
+
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "%d %d %d %d", stats_all.s1.cp_time[0],
+ stats_all.s1.cp_time[1], stats_all.s1.cp_time[2],
+ stats_all.s1.cp_time[3]);
+#endif
+
+ mib[0] = CTL_VM;
+ mib[1] = VM_UVMEXP;
+ len = sizeof(uvmexp);
+ if (sysctl(mib, 2, &uvmexp, &len, NULL, 0) < 0) {
+ syslog(LOG_ERR, "can't sysctl vm.uvmexp: %m");
+ _exit(1);
+ }
+ stats_all.s1.v_pgpgin = uvmexp.fltanget;
+ stats_all.s1.v_pgpgout = uvmexp.pdpageouts;
+ stats_all.s1.v_pswpin = uvmexp.swapins;
+ stats_all.s1.v_pswpout = uvmexp.swapouts;
+ stats_all.s1.v_intr = uvmexp.intrs;
+ stats_all.s2.v_swtch = uvmexp.swtch;
+ gettimeofday(&tm, (struct timezone *) 0);
+ stats_all.s1.v_intr -= hz*(tm.tv_sec - btm.tv_sec) +
+ hz*(tm.tv_usec - btm.tv_usec)/1000000;
+ stats_all.s1.if_ipackets = 0;
+ stats_all.s1.if_opackets = 0;
+ stats_all.s1.if_ierrors = 0;
+ stats_all.s1.if_oerrors = 0;
+ stats_all.s1.if_collisions = 0;
+ if (getifaddrs(&ifaddrs) == -1) {
+ syslog(LOG_ERR, "can't getifaddrs: %m");
+ _exit(1);
+ }
+ for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ ifdp = (struct if_data *)ifa->ifa_data;
+ stats_all.s1.if_ipackets += ifdp->ifi_ipackets;
+ stats_all.s1.if_opackets += ifdp->ifi_opackets;
+ stats_all.s1.if_ierrors += ifdp->ifi_ierrors;
+ stats_all.s1.if_oerrors += ifdp->ifi_oerrors;
+ stats_all.s1.if_collisions += ifdp->ifi_collisions;
+ }
+ freeifaddrs(ifaddrs);
+ stats_all.s3.curtime.tv_sec = tm.tv_sec;
+ stats_all.s3.curtime.tv_usec = tm.tv_usec;
+
+ alarm(1);
+ errno = save_errno;
+}
+
+void
+setup(void)
+{
+ dkinit(0);
+}
+
+void rstat_service(struct svc_req *, SVCXPRT *);
+
+void
+rstat_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ char *(*local)(void *, struct svc_req *);
+ xdrproc_t xdr_argument, xdr_result;
+ union {
+ int fill;
+ } argument;
+ char *result;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case RSTATPROC_STATS:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_statstime;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_stats_1_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_stats_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_stats_3_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ return;
+ }
+ break;
+
+ case RSTATPROC_HAVEDISK:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_int;
+ switch (rqstp->rq_vers) {
+ case RSTATVERS_ORIG:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_havedisk_1_svc;
+ break;
+ case RSTATVERS_SWTCH:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_havedisk_2_svc;
+ break;
+ case RSTATVERS_TIME:
+ local = (char *(*)(void *, struct svc_req *))
+ rstatproc_havedisk_3_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RSTATVERS_ORIG, RSTATVERS_TIME);
+ return;
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+}
--- /dev/null
+/* $OpenBSD: rstatd.c,v 1.23 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1993, John Brezak
+ * 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. The name of the author may not 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rstat.h>
+
+extern int __svc_fdsetsize;
+extern fd_set *__svc_fdset;
+extern void svc_getreqset2(fd_set *, int);
+extern void rstat_service(struct svc_req *, SVCXPRT *);
+
+void my_svc_run(void);
+
+int from_inetd = 1; /* started from inetd ? */
+int closedown = 20; /* how long to wait before going dormant */
+
+volatile sig_atomic_t gotsig;
+
+/* ARGSUSED */
+static void
+getsig(int signo)
+{
+ gotsig = 1;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int sock = 0, proto = 0;
+ socklen_t fromlen;
+ struct passwd *pw;
+ struct sockaddr_storage from;
+ SVCXPRT *transp;
+
+ openlog("rpc.rstatd", LOG_NDELAY|LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ if ((pw = getpwnam("_rstatd")) == NULL) {
+ syslog(LOG_ERR, "no such user _rstatd");
+ exit(1);
+ }
+ if (chroot("/var/empty") == -1) {
+ syslog(LOG_ERR, "cannot chdir to /var/empty.");
+ exit(1);
+ }
+ chdir("/");
+
+ if (pw) {
+ setgroups(1, &pw->pw_gid);
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+ }
+
+ if (argc == 2)
+ closedown = atoi(argv[1]);
+ if (closedown <= 0)
+ closedown = 20;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void)pmap_unset(RSTATPROG, RSTATVERS_TIME);
+ (void)pmap_unset(RSTATPROG, RSTATVERS_SWTCH);
+ (void)pmap_unset(RSTATPROG, RSTATVERS_ORIG);
+
+ (void) signal(SIGINT, getsig);
+ (void) signal(SIGTERM, getsig);
+ (void) signal(SIGHUP, getsig);
+ }
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_TIME, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_TIME, udp).");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_SWTCH, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_SWTCH, udp).");
+ exit(1);
+ }
+ if (!svc_register(transp, RSTATPROG, RSTATVERS_ORIG, rstat_service, proto)) {
+ syslog(LOG_ERR, "unable to register (RSTATPROG, RSTATVERS_ORIG, udp).");
+ exit(1);
+ }
+
+ my_svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+void
+my_svc_run(void)
+{
+ extern volatile sig_atomic_t wantupdatestat;
+ extern void updatestat(void);
+ struct pollfd *pfd = NULL, *newp;
+ int nready, saved_max_pollfd = 0;
+
+ for (;;) {
+ if (wantupdatestat) {
+ wantupdatestat = 0;
+ updatestat();
+ }
+ if (gotsig) {
+ (void) pmap_unset(RSTATPROG, RSTATVERS_TIME);
+ (void) pmap_unset(RSTATPROG, RSTATVERS_SWTCH);
+ (void) pmap_unset(RSTATPROG, RSTATVERS_ORIG);
+ exit(0);
+ }
+ if (svc_max_pollfd > saved_max_pollfd) {
+ newp = realloc(pfd, sizeof(*pfd) * svc_max_pollfd);
+ if (newp == NULL) {
+ free(pfd);
+ perror("svc_run: - realloc failed");
+ return;
+ }
+ pfd = newp;
+ saved_max_pollfd = svc_max_pollfd;
+ }
+ memcpy(pfd, svc_pollfd, sizeof(*pfd) * svc_max_pollfd);
+
+ nready = poll(pfd, svc_max_pollfd, INFTIM);
+ switch (nready) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ perror("svc_run: - poll failed");
+ free(pfd);
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreq_poll(pfd, nready);
+ }
+ }
+}
--- /dev/null
+/Makefile/1.4/Sat Jun 29 08:14:07 2002//
+/rpc.rusersd.8/1.7/Thu May 31 19:19:40 2007//
+/rusers_proc.c/1.22/Tue Oct 27 23:59:31 2009//
+/rusersd.c/1.16/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rpc.rusersd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.4 2002/06/29 08:14:07 deraadt Exp $
+
+PROG = rpc.rusersd
+SRCS = rusersd.c rusers_proc.c
+MAN = rpc.rusersd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
+
--- /dev/null
+/usr/obj/libexec/rpc.rusersd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.rusersd.8,v 1.7 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1985, 1991 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. 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.
+.\"
+.\" $Id: rpc.rusersd.8,v 1.7 2007/05/31 19:19:40 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RPC.RUSERSD 8
+.Os
+.Sh NAME
+.Nm rpc.rusersd
+.Nd logged in users server
+.Sh SYNOPSIS
+.Nm rpc.rusersd
+.Sh DESCRIPTION
+.Nm rpc.rusersd
+is a server which returns information about users
+currently logged in to the system.
+At startup,
+.Nm
+opens
+.Pa /var/run/utmp
+and subsequently performs a
+.Xr chroot 2
+to
+.Pa /var/empty
+and switches to user
+.Va nobody .
+.Pp
+The currently logged in users are queried using the
+.Xr rusers 1
+command.
+The
+.Nm rpc.rusersd
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rusersd
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rnusers.x .
+.Sh SEE ALSO
+.Xr rusers 1 ,
+.Xr w 1 ,
+.Xr who 1 ,
+.Xr inetd 8
--- /dev/null
+/* $OpenBSD: rusers_proc.c,v 1.22 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1993 John Brezak
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <paths.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rusers.h> /* New version */
+#include <rpcsvc/rnusers.h> /* Old version */
+
+extern int utmp_fd;
+
+typedef char ut_line_t[UT_LINESIZE+1];
+typedef char ut_name_t[UT_NAMESIZE+1];
+typedef char ut_host_t[UT_HOSTSIZE+1];
+
+struct rusers_utmp utmps[MAXUSERS];
+struct utmpidle *utmp_idlep[MAXUSERS];
+struct utmpidle utmp_idle[MAXUSERS];
+struct ru_utmp *ru_utmpp[MAXUSERS];
+struct ru_utmp ru_utmp[MAXUSERS];
+ut_line_t line[MAXUSERS];
+ut_name_t name[MAXUSERS];
+ut_host_t host[MAXUSERS];
+
+int *rusers_num_svc(void *, struct svc_req *);
+struct utmpidlearr *rusersproc_names_2_svc(void *, struct svc_req *);
+struct utmpidlearr *rusersproc_allnames_2_svc(void *, struct svc_req *);
+struct utmparr *rusersproc_names_1_svc(void *, struct svc_req *);
+struct utmparr *rusersproc_allnames_1_svc(void *, struct svc_req *);
+void rusers_service(struct svc_req *, SVCXPRT *);
+
+extern int from_inetd;
+
+FILE *ufp;
+
+static long
+getidle(char *tty)
+{
+ char devname[PATH_MAX];
+ struct stat st;
+ long idle;
+ time_t now;
+
+ idle = 0;
+ if (*tty == 'X') {
+ long kbd_idle, mouse_idle;
+#if !defined(__i386__)
+ kbd_idle = getidle("kbd");
+#else
+ /*
+ * XXX Icky i386 console hack.
+ */
+ kbd_idle = getidle("vga");
+#endif
+ mouse_idle = getidle("mouse");
+ idle = (kbd_idle < mouse_idle) ? kbd_idle : mouse_idle;
+ } else {
+ snprintf(devname, sizeof devname, "%s/%.*s", _PATH_DEV,
+ sizeof(tty), tty);
+ if (stat(devname, &st) < 0) {
+#ifdef DEBUG
+ printf("%s: %m\n", devname);
+#endif
+ return (0);
+ }
+ time(&now);
+#ifdef DEBUG
+ printf("%s: now=%d atime=%d\n", devname, now, st.st_atime);
+#endif
+ idle = now - st.st_atime;
+ idle = (idle + 30) / 60; /* secs->mins */
+ }
+ if (idle < 0)
+ idle = 0;
+
+ return (idle);
+}
+
+int *
+rusers_num_svc(void *arg, struct svc_req *rqstp)
+{
+ static int num_users = 0;
+ struct utmp usr;
+ int fd;
+
+ fd = dup(utmp_fd);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%m");
+ return (0);
+ }
+ lseek(fd, (off_t)0, SEEK_SET);
+ ufp = fdopen(fd, "r");
+ if (!ufp) {
+ close(fd);
+ syslog(LOG_ERR, "%m");
+ return (0);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1)
+ if (*usr.ut_name && *usr.ut_line)
+ num_users++;
+
+ fclose(ufp);
+ return (&num_users);
+}
+
+static utmp_array *
+do_names_3(int all)
+{
+ static utmp_array ut;
+ struct utmp usr;
+ int fd, nusers = 0;
+
+ bzero((char *)&ut, sizeof(ut));
+ ut.utmp_array_val = &utmps[0];
+
+ fd = dup(utmp_fd);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%m");
+ return (0);
+ }
+ lseek(fd, (off_t)0, SEEK_SET);
+ ufp = fdopen(fd, "r");
+ if (!ufp) {
+ close(fd);
+ syslog(LOG_ERR, "%m");
+ return (NULL);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 &&
+ nusers < MAXUSERS)
+ if (*usr.ut_name && *usr.ut_line) {
+ utmps[nusers].ut_type = RUSERS_USER_PROCESS;
+ utmps[nusers].ut_time = usr.ut_time;
+ utmps[nusers].ut_idle = getidle(usr.ut_line);
+ utmps[nusers].ut_line = line[nusers];
+ memset(line[nusers], 0, sizeof(line[nusers]));
+ memcpy(line[nusers], usr.ut_line, UT_LINESIZE);
+ line[nusers][UT_LINESIZE] = '\0';
+ utmps[nusers].ut_user = name[nusers];
+ memset(name[nusers], 0, sizeof(name[nusers]));
+ memcpy(name[nusers], usr.ut_name, UT_NAMESIZE);
+ name[nusers][UT_NAMESIZE] = '\0';
+ utmps[nusers].ut_host = host[nusers];
+ memset(host[nusers], 0, sizeof(host[nusers]));
+ memcpy(host[nusers], usr.ut_host, UT_HOSTSIZE);
+ host[nusers][UT_HOSTSIZE] = '\0';
+ nusers++;
+ }
+ ut.utmp_array_len = nusers;
+
+ fclose(ufp);
+ return (&ut);
+}
+
+utmp_array *
+rusersproc_names_3_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_3(0));
+}
+
+utmp_array *
+rusersproc_allnames_3_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_3(1));
+}
+
+static struct utmpidlearr *
+do_names_2(int all)
+{
+ static struct utmpidlearr ut;
+ struct utmp usr;
+ int fd, nusers = 0;
+
+ bzero((char *)&ut, sizeof(ut));
+ ut.uia_arr = utmp_idlep;
+ ut.uia_cnt = 0;
+
+ fd = dup(utmp_fd);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%m");
+ return (0);
+ }
+ lseek(fd, (off_t)0, SEEK_SET);
+ ufp = fdopen(fd, "r");
+ if (!ufp) {
+ close(fd);
+ syslog(LOG_ERR, "%m");
+ return (NULL);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 &&
+ nusers < MAXUSERS)
+ if (*usr.ut_name && *usr.ut_line) {
+ utmp_idlep[nusers] = &utmp_idle[nusers];
+ utmp_idle[nusers].ui_utmp.ut_time = usr.ut_time;
+ utmp_idle[nusers].ui_idle = getidle(usr.ut_line);
+ utmp_idle[nusers].ui_utmp.ut_line = line[nusers];
+ memset(line[nusers], 0, sizeof(line[nusers]));
+ memcpy(line[nusers], usr.ut_line, UT_LINESIZE);
+ line[nusers][UT_LINESIZE] = '\0';
+ utmp_idle[nusers].ui_utmp.ut_name = name[nusers];
+ memset(name[nusers], 0, sizeof(name[nusers]));
+ memcpy(name[nusers], usr.ut_name, UT_NAMESIZE);
+ name[nusers][UT_NAMESIZE] = '\0';
+ utmp_idle[nusers].ui_utmp.ut_host = host[nusers];
+ memset(host[nusers], 0, sizeof(host[nusers]));
+ memcpy(host[nusers], usr.ut_host, UT_HOSTSIZE);
+ host[nusers][UT_HOSTSIZE] = '\0';
+ nusers++;
+ }
+
+ ut.uia_cnt = nusers;
+ fclose(ufp);
+ return (&ut);
+}
+
+struct utmpidlearr *
+rusersproc_names_2_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_2(0));
+}
+
+struct utmpidlearr *
+rusersproc_allnames_2_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_2(1));
+}
+
+static struct utmparr *
+do_names_1(int all)
+{
+ static struct utmparr ut;
+ struct utmp usr;
+ int fd, nusers = 0;
+
+ bzero((char *)&ut, sizeof(ut));
+ ut.uta_arr = ru_utmpp;
+ ut.uta_cnt = 0;
+
+ fd = dup(utmp_fd);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%m");
+ return (0);
+ }
+ lseek(fd, (off_t)0, SEEK_SET);
+ ufp = fdopen(fd, "r");
+ if (!ufp) {
+ close(fd);
+ syslog(LOG_ERR, "%m");
+ return (NULL);
+ }
+
+ /* only entries with both name and line fields */
+ while (fread((char *)&usr, sizeof(usr), 1, ufp) == 1 &&
+ nusers < MAXUSERS)
+ if (*usr.ut_name && *usr.ut_line) {
+ ru_utmpp[nusers] = &ru_utmp[nusers];
+ ru_utmp[nusers].ut_time = usr.ut_time;
+ ru_utmp[nusers].ut_line = line[nusers];
+ memcpy(line[nusers], usr.ut_line, UT_LINESIZE);
+ line[nusers][UT_LINESIZE] = '\0';
+ ru_utmp[nusers].ut_name = name[nusers];
+ memcpy(name[nusers], usr.ut_name, UT_NAMESIZE);
+ name[nusers][UT_NAMESIZE] = '\0';
+ ru_utmp[nusers].ut_host = host[nusers];
+ memcpy(host[nusers], usr.ut_host, UT_HOSTSIZE);
+ host[nusers][UT_HOSTSIZE] = '\0';
+ nusers++;
+ }
+
+ ut.uta_cnt = nusers;
+ fclose(ufp);
+ return (&ut);
+}
+
+struct utmparr *
+rusersproc_names_1_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_1(0));
+}
+
+struct utmparr *
+rusersproc_allnames_1_svc(void *arg, struct svc_req *rqstp)
+{
+ return (do_names_1(1));
+}
+
+void
+rusers_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ char *(*local)(void *, struct svc_req *);
+ xdrproc_t xdr_argument, xdr_result;
+ union {
+ int fill;
+ } argument;
+ char *result;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ goto leave;
+
+ case RUSERSPROC_NUM:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_int;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_3:
+ case RUSERSVERS_IDLE:
+ case RUSERSVERS_ORIG:
+ local = (char *(*)(void *, struct svc_req *))
+ rusers_num_svc;
+ break;
+ default:
+ svcerr_progvers(transp, RUSERSVERS_IDLE, RUSERSVERS_3);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RUSERSPROC_NAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmp_array;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_3:
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_names_3_svc;
+ break;
+
+ case RUSERSVERS_IDLE:
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_names_2_svc;
+ break;
+
+ case RUSERSVERS_ORIG:
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_names_1_svc;
+ break;
+
+ default:
+ svcerr_progvers(transp, RUSERSVERS_IDLE, RUSERSVERS_3);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ case RUSERSPROC_ALLNAMES:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_utmp_array;
+ switch (rqstp->rq_vers) {
+ case RUSERSVERS_3:
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_allnames_3_svc;
+ break;
+
+ case RUSERSVERS_IDLE:
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_allnames_2_svc;
+ break;
+
+ case RUSERSVERS_ORIG:
+ xdr_result = (xdrproc_t)xdr_utmpidlearr;
+ local = (char *(*)(void *, struct svc_req *))
+ rusersproc_allnames_1_svc;
+ break;
+
+ default:
+ svcerr_progvers(transp, RUSERSVERS_IDLE, RUSERSVERS_3);
+ goto leave;
+ /*NOTREACHED*/
+ }
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result))
+ svcerr_systemerr(transp);
+
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
--- /dev/null
+/* $OpenBSD: rusersd.c,v 1.16 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 1993 John Brezak
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rusers.h> /* New version */
+#include <rpcsvc/rnusers.h> /* Old version */
+#include <rpc/pmap_clnt.h>
+#include <utmp.h>
+
+extern void rusers_service(struct svc_req *, SVCXPRT *);
+
+int from_inetd = 1;
+int utmp_fd;
+
+/* ARGSUSED */
+static void
+cleanup(int signo)
+{
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_3); /* XXX signal races */
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE);
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG);
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sock = 0, proto = 0;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+ struct passwd *pw;
+ SVCXPRT *transp;
+
+ if ((utmp_fd = open(_PATH_UTMP, O_RDONLY)) == -1) {
+ syslog(LOG_ERR, "cannot open %s", _PATH_UTMP);
+ exit(1);
+ }
+
+ openlog("rpc.rusersd", LOG_NDELAY|LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ pw = getpwnam("_rusersd");
+ if (!pw)
+ pw = getpwnam("nobody");
+ if (chroot("/var/empty") == -1) {
+ syslog(LOG_ERR, "cannot chdir to /var/empty.");
+ exit(1);
+ }
+ chdir("/");
+
+ if (pw) {
+ setgroups(1, &pw->pw_gid);
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+ }
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_3);
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_IDLE);
+ (void) pmap_unset(RUSERSPROG, RUSERSVERS_ORIG);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, RUSERSPROG, RUSERSVERS_3, rusers_service, proto)) {
+ syslog(LOG_ERR,
+ "unable to register (RUSERSPROG, RUSERSVERS_3, %s).",
+ proto ? "udp" : "(inetd)");
+ exit(1);
+ }
+ if (!svc_register(transp, RUSERSPROG, RUSERSVERS_IDLE, rusers_service, proto)) {
+ syslog(LOG_ERR,
+ "unable to register (RUSERSPROG, RUSERSVERS_IDLE, %s).",
+ proto ? "udp" : "(inetd)");
+ exit(1);
+ }
+ if (!svc_register(transp, RUSERSPROG, RUSERSVERS_ORIG, rusers_service, proto)) {
+ syslog(LOG_ERR,
+ "unable to register (RUSERSPROG, RUSERSVERS_ORIG, %s).",
+ proto ? "udp" : "(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:32 2001//
+/rpc.rwalld.8/1.7/Thu May 31 19:19:40 2007//
+/rwalld.c/1.14/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rpc.rwalld
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:32 niklas Exp $
+
+PROG = rpc.rwalld
+SRCS = rwalld.c
+MAN = rpc.rwalld.8
+MLINKS= rpc.rwalld.8 rwalld.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/rpc.rwalld
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.rwalld.8,v 1.7 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1985, 1991 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. 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.
+.\"
+.\" $Id: rpc.rwalld.8,v 1.7 2007/05/31 19:19:40 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RPC.RWALLD 8
+.Os
+.Sh NAME
+.Nm rwalld ,
+.Nm rpc.rwalld
+.Nd write messages to users currently logged in server
+.Sh SYNOPSIS
+.Nm rpc.rwalld
+.Sh DESCRIPTION
+.Nm rpc.rwalld
+is a server which will send a message to users
+currently logged in to the system.
+This server invokes the
+.Xr wall 1
+command to actually write the messages to the system.
+.Pp
+Messages are sent to this server by the
+.Xr rwall 1
+command.
+The
+.Nm rpc.rwalld
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.rwalld
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/rwall.x .
+.Sh SEE ALSO
+.Xr rwall 1 ,
+.Xr wall 1 ,
+.Xr inetd 8
--- /dev/null
+/* $OpenBSD: rwalld.c,v 1.14 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1993 Christopher G. Demetriou
+ * 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. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/rwall.h>
+
+#ifdef OSF
+#define WALL_CMD "/usr/sbin/wall"
+#else
+#define WALL_CMD "/usr/bin/wall -n"
+#endif
+
+void wallprog_1(struct svc_req *, SVCXPRT *);
+
+int from_inetd = 1;
+
+static void
+cleanup(int signo)
+{
+ (void) pmap_unset(WALLPROG, WALLVERS); /* XXX signal race */
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int sock = 0, proto = 0;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+ SVCXPRT *transp;
+
+ struct passwd *pw = getpwnam("_rwalld");
+ if (pw == NULL) {
+ syslog(LOG_ERR, "no such user _rwalld");
+ exit(1);
+ }
+
+ setgroups(1, &pw->pw_gid);
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) pmap_unset(WALLPROG, WALLVERS);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ }
+
+ openlog("rpc.rwalld", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ exit(1);
+ }
+ if (!svc_register(transp, WALLPROG, WALLVERS, wallprog_1, proto)) {
+ syslog(LOG_ERR, "unable to register (WALLPROG, WALLVERS, %s).",
+ proto ? "udp" : "(inetd)");
+ exit(1);
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+
+}
+
+void *
+wallproc_wall_1_svc(char **s, struct svc_req *rqstp)
+{
+ FILE *pfp;
+
+ pfp = popen(WALL_CMD, "w");
+ if (pfp != NULL) {
+ fprintf(pfp, "\007\007%s", *s);
+ pclose(pfp);
+ }
+
+ return (*s);
+}
+
+void
+wallprog_1(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ char *(*local)(char **, struct svc_req *);
+ xdrproc_t xdr_argument, xdr_result;
+ union {
+ char *wallproc_wall_1_arg;
+ } argument;
+ char *result;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ goto leave;
+
+ case WALLPROC_WALL:
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_void;
+ local = (char *(*)(char **, struct svc_req *))
+ wallproc_wall_1_svc;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ goto leave;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ goto leave;
+ }
+ result = (*local)((char **)&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+leave:
+ if (from_inetd)
+ exit(0);
+}
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:32 2001//
+/rpc.sprayd.8/1.6/Thu May 31 19:19:40 2007//
+/sprayd.c/1.10/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rpc.sprayd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:32 niklas Exp $
+
+PROG = rpc.sprayd
+SRCS = sprayd.c
+MAN = rpc.sprayd.8
+MLINKS= rpc.sprayd.8 sprayd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
+
--- /dev/null
+/usr/obj/libexec/rpc.sprayd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.sprayd.8,v 1.6 2007/05/31 19:19:40 jmc Exp $
+.\"
+.\" Copyright (c) 1994 Christos Zoulas
+.\" 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 Christos Zoulas.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\" $Id: rpc.sprayd.8,v 1.6 2007/05/31 19:19:40 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RPC.SPRAYD 8
+.Os
+.Sh NAME
+.Nm sprayd ,
+.Nm rpc.sprayd
+.Nd spray server
+.Sh SYNOPSIS
+.Nm rpc.sprayd
+.Sh DESCRIPTION
+.Nm rpc.sprayd
+is a server which records packets sent by the
+.Xr spray 8
+command and sends a traffic report to the originator of the packets.
+The
+.Nm rpc.sprayd
+daemon is normally invoked by
+.Xr inetd 8 .
+.Pp
+.Nm rpc.sprayd
+uses an RPC protocol defined in
+.Pa /usr/include/rpcsvc/spray.x .
+.Sh SEE ALSO
+.Xr spray 8
--- /dev/null
+/* $OpenBSD: sprayd.c,v 1.10 2009/10/27 23:59:31 deraadt Exp $*/
+
+/*
+ * Copyright (c) 1994 Christos Zoulas
+ * 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 Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+
+static void spray_service(struct svc_req *, SVCXPRT *);
+
+static int from_inetd = 1;
+
+#define TIMEOUT 120
+
+static void
+cleanup(int signo)
+{
+ (void) pmap_unset(SPRAYPROG, SPRAYVERS); /* XXX signal race */
+ _exit(0);
+}
+
+static void
+die(int signo)
+{
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int sock = 0;
+ int proto = 0;
+ struct sockaddr_storage from;
+ socklen_t fromlen;
+
+ /*
+ * See if inetd started us
+ */
+ fromlen = sizeof(from);
+ if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ from_inetd = 0;
+ sock = RPC_ANYSOCK;
+ proto = IPPROTO_UDP;
+ }
+
+ if (!from_inetd) {
+ daemon(0, 0);
+
+ (void) pmap_unset(SPRAYPROG, SPRAYVERS);
+
+ (void) signal(SIGINT, cleanup);
+ (void) signal(SIGTERM, cleanup);
+ (void) signal(SIGHUP, cleanup);
+ } else {
+ (void) signal(SIGALRM, die);
+ alarm(TIMEOUT);
+ }
+
+ openlog("rpc.sprayd", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service.");
+ return 1;
+ }
+ if (!svc_register(transp, SPRAYPROG, SPRAYVERS, spray_service, proto)) {
+ syslog(LOG_ERR,
+ "unable to register (SPRAYPROG, SPRAYVERS, %s).",
+ proto ? "udp" : "(inetd)");
+ return 1;
+ }
+
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ return 1;
+}
+
+
+static void
+spray_service(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ static struct timeval clear, get;
+ static spraycumul scum;
+
+ switch (rqstp->rq_proc) {
+ case SPRAYPROC_CLEAR:
+ scum.counter = 0;
+ (void) gettimeofday(&clear, 0);
+ /*FALLTHROUGH*/
+
+ case NULLPROC:
+ (void)svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case SPRAYPROC_SPRAY:
+ scum.counter++;
+ return;
+
+ case SPRAYPROC_GET:
+ (void) gettimeofday(&get, 0);
+ timersub(&get, &clear, &get);
+ scum.clock.sec = get.tv_sec;
+ scum.clock.usec = get.tv_usec;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+
+ if (!svc_sendreply(transp, xdr_spraycumul, (caddr_t)&scum)) {
+ svcerr_systemerr(transp);
+ syslog(LOG_ERR, "bad svc_sendreply");
+ }
+}
--- /dev/null
+/Makefile/1.9/Sat Nov 26 04:06:59 2005//
+/rpc.yppasswdd.8/1.22/Wed Oct 22 20:31:20 2008//
+/rpc.yppasswdd.c/1.21/Tue Oct 27 23:59:31 2009//
+/yppasswd.h/1.6/Mon Jun 2 21:08:26 2003//
+/yppasswdd_mkpw.c/1.29/Tue Oct 27 23:59:31 2009//
+/yppasswdd_proc.c/1.13/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rpc.yppasswdd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.9 2005/11/26 04:06:59 deraadt Exp $
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+
+PROG= rpc.yppasswdd
+SRCS= rpc.yppasswdd.c yppasswdd_proc.c yppasswdd_mkpw.c
+MAN= rpc.yppasswdd.8
+BINDIR=/usr/sbin
+MLINKS= rpc.yppasswdd.8 yppasswdd.8
+DPADD+= ${LIBUTIL} ${LIBRPCSVC}
+LDADD+= -lutil -lrpcsvc
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/rpc.yppasswdd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rpc.yppasswdd.8,v 1.22 2008/10/22 20:31:20 jmc Exp $
+.\"
+.\" Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.\"
+.Dd $Mdocdate: October 22 2008 $
+.Dt RPC.YPPASSWDD 8
+.Os
+.Sh NAME
+.Nm rpc.yppasswdd
+.Nd YP update password file daemon
+.Sh SYNOPSIS
+.Nm rpc.yppasswdd
+.Bk -words
+.Op Fl nogecos
+.Op Fl nopw
+.Op Fl noshell
+.Op Fl d Ar directory
+.Op Fl m Ar arg ...
+.Ek
+.Sh DESCRIPTION
+.Nm rpc.yppasswdd
+must be running on the YP master server to allow users to change information
+in the password file.
+If the user needs to change his password this is
+normally done with a program called
+.Nm yppasswd .
+This program doesn't exist in
+.Ox
+but is integrated into
+.Xr passwd 1 .
+.Nm passwd
+will automatically determine which password database should be modified.
+To force a change of a YP password when a local one also exists, use
+.Nm passwd -y .
+.Pp
+Other user information can be changed with
+.Xr chpass 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d Ar directory
+Use the specified directory to get at the password-related files instead
+of
+.Pa /etc .
+.It Fl m Ar arg ...
+Pass the specified arguments to
+.Xr make 1
+when invoking it in
+.Pa /var/yp
+to rebuild the passwd maps.
+.It Fl nogecos
+Don't allow changes of the gecos field in the passwd file.
+.It Fl nopw
+Don't allow changes of the password in the passwd file.
+.It Fl noshell
+Don't allow changes of the shell field in the passwd file.
+.El
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/passwd
+.It Pa /etc/master.passwd
+.El
+.Sh SEE ALSO
+.Xr make 1 ,
+.Xr securenet 5 ,
+.Xr ypserv.acl 5 ,
+.Xr Makefile.yp 8 ,
+.Xr yp 8 ,
+.Xr ypbind 8
+.Sh AUTHORS
+.An Mats O Jansson Aq moj@stacken.kth.se
--- /dev/null
+/* $OpenBSD: rpc.yppasswdd.c,v 1.21 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <pwd.h>
+#include <util.h>
+#include <syslog.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include "yppasswd.h"
+
+static void yppasswddprog_1(struct svc_req *, SVCXPRT *);
+void sig_child(int);
+
+int noshell, nogecos, nopw, domake;
+char make_arg[1024] = "make";
+char *dir;
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: rpc.yppasswdd [-nogecos] [-nopw] [-noshell] [-d directory] "
+ "[-m arg ...]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ SVCXPRT *transp;
+ int i = 1;
+
+ while (i < argc) {
+ if (argv[i][0] == '-') {
+ if (strcmp("-noshell", argv[i]) == 0) {
+ noshell = 1;
+ } else if (strcmp("-nogecos", argv[i]) == 0) {
+ nogecos = 1;
+ } else if (strcmp("-nopw", argv[i]) == 0) {
+ nopw = 1;
+ } else if (strcmp("-m", argv[i]) == 0) {
+ domake = 1;
+ while (i < argc) {
+ if (strlcat(make_arg, " ",
+ sizeof(make_arg)) >=
+ sizeof(make_arg) ||
+ strlcat(make_arg, argv[i],
+ sizeof(make_arg)) >=
+ sizeof(make_arg)) {
+ (void) fprintf(stderr,
+ "-m argument too long.\n");
+ exit(1);
+ }
+ i++;
+ }
+ } else if (strcmp("-d", argv[i]) == 0 &&
+ i < argc + 1) {
+ i++;
+ dir = argv[i];
+ } else
+ usage();
+ i++;
+ } else
+ usage();
+ }
+
+ (void) daemon(0, 0);
+ chdir("/etc");
+
+ (void) pmap_unset(YPPASSWDPROG, YPPASSWDVERS);
+
+ (void) signal(SIGCHLD, sig_child);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create udp service");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswddprog_1,
+ IPPROTO_UDP)) {
+ syslog(LOG_ERR, "unable to register YPPASSWDPROG, YPPASSWDVERS, udp");
+ exit(1);
+ }
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL) {
+ syslog(LOG_ERR, "cannot create tcp service");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswddprog_1,
+ IPPROTO_TCP)) {
+ syslog(LOG_ERR, "unable to register YPPASSWDPROG, YPPASSWDVERS, tcp");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned");
+ exit(1);
+}
+
+static void
+yppasswddprog_1(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ yppasswd yppasswdproc_update_1_arg;
+ } argument;
+ bool_t (*xdr_argument)(XDR *, yppasswd *);
+ char *(*local)(yppasswd *, struct svc_req *, SVCXPRT *);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *) NULL);
+ return;
+ case YPPASSWDPROC_UPDATE:
+ xdr_argument = xdr_yppasswd;
+ local = (char *(*)(yppasswd *, struct svc_req *,
+ SVCXPRT *)) yppasswdproc_update_1_svc;
+ break;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero((char *) &argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t) & argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ (*local)(&argument.yppasswdproc_update_1_arg, rqstp, transp);
+}
+
+/* ARGSUSED */
+void
+sig_child(int signo)
+{
+ int save_errno = errno;
+
+ while (wait3((int *) NULL, WNOHANG, (struct rusage *) NULL) > 0)
+ ;
+ errno = save_errno;
+}
--- /dev/null
+/* $OpenBSD: yppasswd.h,v 1.6 2003/06/02 21:08:26 maja Exp $*/
+
+/*
+ * Copyright (c) 1995 Mats O Jansson <moj@stacken.kth.se>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _YPPASSWD_H_RPCGEN
+#define _YPPASSWD_H_RPCGEN
+
+struct x_passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+};
+typedef struct x_passwd x_passwd;
+
+struct yppasswd {
+ char *oldpass;
+ x_passwd newpw;
+};
+typedef struct yppasswd yppasswd;
+
+#define YPPASSWDPROG ((u_long)100009)
+#define YPPASSWDVERS ((u_long)1)
+#define YPPASSWDPROC_UPDATE ((u_long)1)
+
+__BEGIN_DECLS
+bool_t xdr_x_passwd(XDR *, x_passwd*);
+bool_t xdr_yppasswd(XDR *, yppasswd*);
+int *yppasswdproc_update_1(yppasswd *, CLIENT *);
+int *yppasswdproc_update_1_svc(yppasswd *, struct svc_req *, SVCXPRT *);
+__END_DECLS
+
+#endif /* !_YPPASSWD_H_RPCGEN */
--- /dev/null
+/* $OpenBSD: yppasswdd_mkpw.c,v 1.29 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yppasswd.h>
+#include <db.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+#include <ctype.h>
+#include <string.h>
+#include <syslog.h>
+
+extern int noshell;
+extern int nogecos;
+extern int nopw;
+extern int make;
+extern char make_arg[];
+extern char *dir;
+
+char *ok_shell(char *);
+int badchars(char *);
+int subst(char *, char, char);
+int make_passwd(yppasswd *);
+
+char *
+ok_shell(char *name)
+{
+ char *p, *sh;
+
+ setusershell();
+ while ((sh = getusershell())) {
+ if (!strcmp(name, sh))
+ return (name);
+ /* allow just shell name, but use "real" path */
+ if ((p = strrchr(sh, '/')) && strcmp(name, p + 1) == 0)
+ return (sh);
+ }
+ return (NULL);
+}
+
+int
+badchars(char *base)
+{
+ int ampr = 0;
+ char *s;
+
+ for (s = base; *s; s++) {
+ if (*s == '&')
+ ampr++;
+ if (!isprint(*s))
+ return 1;
+ if (strchr(":\n\t\r", *s))
+ return 1;
+ }
+ if (ampr > 10)
+ return 1;
+ return 0;
+}
+
+int
+subst(char *s, char from, char to)
+{
+ int n = 0;
+
+ while (*s) {
+ if (*s == from) {
+ *s = to;
+ n++;
+ }
+ s++;
+ }
+ return (n);
+}
+
+int
+make_passwd(yppasswd *argp)
+{
+ struct passwd pw;
+ int pfd = -1, tfd;
+ char buf[11], *bp = NULL, *p, *t;
+ int n;
+ ssize_t cnt;
+ size_t resid;
+ struct stat st;
+ char *master;
+
+ pw_init();
+ if (dir)
+ pw_setdir(dir);
+ master = pw_file(_PATH_MASTERPASSWD);
+ if (!master)
+ return (1);
+ pfd = open(master, O_RDONLY);
+ if (pfd < 0)
+ goto fail;
+ if (fstat(pfd, &st))
+ goto fail;
+ p = bp = malloc((resid = st.st_size) + 1);
+ if (bp == NULL)
+ goto fail;
+ do {
+ cnt = read(pfd, p, resid);
+ if (cnt < 0)
+ goto fail;
+ p += cnt;
+ resid -= cnt;
+ } while (resid > 0);
+ close(pfd);
+ pfd = -1;
+ *p = '\0'; /* Buf oflow prevention */
+
+ p = bp;
+ subst(p, '\n', '\0');
+ for (n = 1; p < bp + st.st_size; n++, p = t) {
+ t = strchr(p, '\0') + 1;
+ cnt = subst(p, ':', '\0');
+ if (cnt != 9) {
+ syslog(LOG_WARNING, "bad entry at line %d of %s", n,
+ master);
+ continue;
+ }
+
+ if (strcmp(p, argp->newpw.pw_name) == 0)
+ break;
+ }
+ if (p >= bp + st.st_size)
+ goto fail;
+
+#define EXPAND(e) e = p; while (*p++);
+ EXPAND(pw.pw_name);
+ EXPAND(pw.pw_passwd);
+ pw.pw_uid = atoi(p); EXPAND(t);
+ pw.pw_gid = atoi(p); EXPAND(t);
+ EXPAND(pw.pw_class);
+ pw.pw_change = (time_t)atol(p); EXPAND(t);
+ pw.pw_expire = (time_t)atol(p); EXPAND(t);
+ EXPAND(pw.pw_gecos);
+ EXPAND(pw.pw_dir);
+ EXPAND(pw.pw_shell);
+
+ if (strcmp(crypt(argp->oldpass, pw.pw_passwd), pw.pw_passwd) != 0)
+ goto fail;
+
+ if (!nopw && badchars(argp->newpw.pw_passwd))
+ goto fail;
+ if (!nogecos && badchars(argp->newpw.pw_gecos))
+ goto fail;
+ if (!nogecos && badchars(argp->newpw.pw_shell))
+ goto fail;
+ if (!ok_shell(argp->newpw.pw_shell) || !ok_shell(pw.pw_shell))
+ goto fail;
+
+ /*
+ * Get the new password. Reset passwd change time to zero; when
+ * classes are implemented, go and get the "offset" value for this
+ * class and reset the timer.
+ */
+ if (!nopw) {
+ pw.pw_passwd = argp->newpw.pw_passwd;
+ pw.pw_change = 0;
+ }
+ if (!nogecos)
+ pw.pw_gecos = argp->newpw.pw_gecos;
+ if (!noshell)
+ pw.pw_shell = argp->newpw.pw_shell;
+
+ for (n = 0, p = pw.pw_gecos; *p; p++) {
+ if (*p == '&')
+ n = n + strlen(pw.pw_name) - 1;
+ }
+ if (strlen(pw.pw_name) + 1 + strlen(pw.pw_passwd) + 1 +
+ strlen((snprintf(buf, sizeof buf, "%u", pw.pw_uid), buf)) + 1 +
+ strlen((snprintf(buf, sizeof buf, "%u", pw.pw_gid), buf)) + 1 +
+ strlen(pw.pw_gecos) + n + 1 + strlen(pw.pw_dir) + 1 +
+ strlen(pw.pw_shell) >= 1023)
+ goto fail;
+
+ pfd = open(master, O_RDONLY, 0);
+ if (pfd < 0) {
+ syslog(LOG_ERR, "cannot open %s", master);
+ goto fail;
+ }
+
+ tfd = pw_lock(0);
+ if (tfd < 0)
+ goto fail;
+
+ pw_copy(pfd, tfd, &pw, NULL);
+ pw_mkdb(pw.pw_name, 0);
+ free(bp);
+
+ if (fork() == 0) {
+ chdir("/var/yp");
+ (void)umask(022);
+ system(make_arg);
+ exit(0);
+ }
+ return (0);
+
+fail:
+ if (bp)
+ free(bp);
+ if (pfd >= 0)
+ close(pfd);
+ free(master);
+ return (1);
+}
--- /dev/null
+/* $OpenBSD: yppasswdd_proc.c,v 1.13 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "yppasswd.h"
+
+int make_passwd(yppasswd *);
+
+/* ARGSUSED */
+int *
+yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp, SVCXPRT *transp)
+{
+ static int res;
+
+ bzero((char *)&res, sizeof(res));
+ res = make_passwd(argp);
+
+ if (!svc_sendreply(transp, xdr_int, (char *)&res))
+ svcerr_systemerr(transp);
+
+ if (!svc_freeargs(transp, xdr_yppasswd, (caddr_t) argp)) {
+ syslog(LOG_ERR, "unable to free arguments");
+ exit(1);
+ }
+ return ((void *)&res);
+}
--- /dev/null
+/Makefile/1.8/Wed May 14 07:35:31 2003//
+/rshd.8/1.19/Thu May 31 19:19:40 2007//
+/rshd.c/1.53/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/rshd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.8 2003/05/14 07:35:31 mho Exp $
+
+PROG= rshd
+SRCS= rshd.c
+MAN= rshd.8
+CFLAGS+= -Wall -Wno-unused
+
+.include <bsd.own.mk>
+
+#.if (${KERBEROS:L} == "yes")
+#SRCS+= des_rw.c
+#.PATH: ${.CURDIR}/../../usr.bin/rsh
+#CFLAGS+=-DKERBEROS
+#DPADD+= ${LIBKRB} ${LIBDES}
+#LDADD+= -lkrb -ldes
+#.endif
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/rshd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: rshd.8,v 1.19 2007/05/31 19:19:40 jmc Exp $
+.\" 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. 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.
+.\"
+.\" from: @(#)rshd.8 8.1 (Berkeley) 6/4/93
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt RSHD 8
+.Os
+.Sh NAME
+.Nm rshd
+.Nd remote shell server
+.Sh SYNOPSIS
+.Nm rshd
+.Op Fl aLln
+.Sh DESCRIPTION
+The
+.Nm
+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 options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Ask hostname for verification.
+.It Fl L
+Log successful accesses very verbosely.
+.It Fl l
+Prevent any authentication based on the user's
+.Pa .rhosts
+file, unless the user is logging in as the superuser.
+.It Fl n
+Disable keep-alive messages.
+.El
+.Pp
+The
+.Nm
+server listens for service requests at the port indicated in the
+.Dq 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
+.Pq Ql \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
+.Dq "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 client'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
+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
+.Pa .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 Remote directory.
+The
+.Xr chdir 2
+command to the home directory failed.
+.It Sy Permission denied.
+The authentication procedure described above failed or
+there is no password file entry for the specified user.
+.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 2
+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 ssh 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, so
+.Xr ssh 1
+should be used instead.
+.Pp
+A more extensible protocol (such as
+.Xr ssh 1 )
+should be used.
--- /dev/null
+/* $OpenBSD: rshd.c,v 1.53 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*-
+ * 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. 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.
+ */
+
+/*
+ * 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_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <login_cap.h>
+#include <bsd_auth.h>
+
+int keepalive = 1;
+int check_all;
+int log_success; /* If TRUE, log all successful accesses */
+int sent_null;
+login_cap_t *lc;
+
+void doit(struct sockaddr *);
+void error(const char *, ...);
+void getstr(char *, int, char *);
+int local_domain(char *);
+char *topdomain(char *);
+void usage(void);
+
+#ifdef KERBEROS
+#include <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"
+
+#ifdef CRYPT
+#define OPTIONS "alnkvxL"
+#else
+#define OPTIONS "alnkvL"
+#endif
+
+char authbuf[sizeof(AUTH_DAT)];
+char tickbuf[sizeof(KTEXT_ST)];
+int doencrypt, use_kerberos, vacuous;
+des_key_schedule schedule;
+#ifdef CRYPT
+int des_read(int, char *, int);
+int des_write(int, char *, int);
+void desrw_clear_key();
+void desrw_set_key(des_cblock *, des_key_schedule *);
+#endif
+#else
+#define OPTIONS "alnL"
+#endif
+
+#define P_SOCKREAD 0
+#define P_PIPEREAD 1
+#define P_CRYPTREAD 2
+#define P_CRYPTWRITE 3
+
+int
+main(int argc, char *argv[])
+{
+ extern int __check_rhosts_file;
+ struct linger linger;
+ int ch, on = 1;
+ socklen_t fromlen;
+ struct sockaddr_storage from;
+
+ openlog("rshd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+
+ opterr = 0;
+ while ((ch = getopt(argc, argv, OPTIONS)) != -1)
+ 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(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
+ /* syslog(LOG_ERR, "getpeername: %m"); */
+ exit(1);
+ }
+ if (keepalive &&
+ setsockopt(STDIN_FILENO, 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(STDIN_FILENO, SOL_SOCKET, SO_LINGER, (char *)&linger,
+ sizeof (linger)) < 0)
+ syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
+ doit((struct sockaddr *)&from);
+ /* NOTREACHED */
+ exit(0);
+}
+
+char *envinit[1] = { 0 };
+extern char **environ;
+
+void
+doit(struct sockaddr *fromp)
+{
+ extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */
+ struct addrinfo hints, *res, *res0;
+ int gaierror;
+ struct passwd *pwd;
+ u_short port;
+ in_port_t *portp;
+ struct pollfd pfd[4];
+ int cc, nfd, pv[2], s = 0, one = 1;
+ pid_t pid;
+ char *hostname, *errorstr, *errorhost = (char *) NULL;
+ char *cp, sig, buf[BUFSIZ];
+ char cmdbuf[NCARGS+1], locuser[_PW_NAME_LEN+1], remuser[_PW_NAME_LEN+1];
+ char remotehost[2 * MAXHOSTNAMELEN + 1];
+ char hostnamebuf[2 * MAXHOSTNAMELEN + 1];
+ char naddr[NI_MAXHOST];
+ char saddr[NI_MAXHOST];
+ char raddr[NI_MAXHOST];
+ char pbuf[NI_MAXSERV];
+ auth_session_t *as;
+ const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+
+#ifdef KERBEROS
+ AUTH_DAT *kdata = (AUTH_DAT *) NULL;
+ KTEXT ticket = (KTEXT) NULL;
+ char instance[INST_SZ], version[VERSION_SIZE];
+ struct sockaddr_storage fromaddr;
+ int rc;
+ long authopts;
+#ifdef CRYPT
+ int pv1[2], pv2[2];
+#endif
+
+ if (sizeof(fromaddr) < fromp->sa_len) {
+ syslog(LOG_ERR, "malformed \"from\" address (af %d)",
+ fromp->sa_family);
+ exit(1);
+ }
+ memcpy(&fromaddr, fromp, fromp->sa_len);
+#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
+ switch (fromp->sa_family) {
+ case AF_INET:
+ portp = &((struct sockaddr_in *)fromp)->sin_port;
+ break;
+ case AF_INET6:
+ portp = &((struct sockaddr_in6 *)fromp)->sin6_port;
+ break;
+ default:
+ syslog(LOG_ERR, "malformed \"from\" address (af %d)",
+ fromp->sa_family);
+ exit(1);
+ }
+ if (getnameinfo(fromp, fromp->sa_len, naddr, sizeof(naddr),
+ pbuf, sizeof(pbuf), niflags) != 0) {
+ syslog(LOG_ERR, "malformed \"from\" address (af %d)",
+ fromp->sa_family);
+ exit(1);
+ }
+
+#ifdef IP_OPTIONS
+ if (fromp->sa_family == AF_INET) {
+ struct ipoption opts;
+ socklen_t optsize = sizeof(opts);
+ int ipproto, i;
+ struct protoent *ip;
+
+ if ((ip = getprotobyname("ip")) != NULL)
+ ipproto = ip->p_proto;
+ else
+ ipproto = IPPROTO_IP;
+ if (!getsockopt(STDIN_FILENO, ipproto, IP_OPTIONS,
+ (char *)&opts, &optsize) && optsize != 0) {
+ for (i = 0; (void *)&opts.ipopt_list[i] - (void *)&opts <
+ optsize; ) {
+ u_char c = (u_char)opts.ipopt_list[i];
+ if (c == IPOPT_LSRR || c == IPOPT_SSRR)
+ exit(1);
+ if (c == IPOPT_EOL)
+ break;
+ i += (c == IPOPT_NOP) ? 1 :
+ (u_char)opts.ipopt_list[i+1];
+ }
+ }
+ }
+#endif
+
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (ntohs(*portp) >= IPPORT_RESERVED ||
+ ntohs(*portp) < IPPORT_RESERVED/2) {
+ syslog(LOG_NOTICE|LOG_AUTH,
+ "Connection from %s on illegal port %u",
+ naddr, ntohs(*portp));
+ 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(STDIN_FILENO, SHUT_RDWR);
+ exit(1);
+ }
+ if (c == 0)
+ break;
+ port = port * 10 + c - '0';
+ }
+
+ (void) alarm(0);
+ if (port != 0) {
+ int lport;
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (port >= IPPORT_RESERVED ||
+ port < IPPORT_RESERVED/2) {
+ syslog(LOG_ERR, "2nd port not reserved");
+ exit(1);
+ }
+ *portp = htons(port);
+ lport = IPPORT_RESERVED - 1;
+ s = rresvport_af(&lport, fromp->sa_family);
+ if (s < 0) {
+ syslog(LOG_ERR, "can't get stderr port: %m");
+ exit(1);
+ }
+ if (connect(s, (struct sockaddr *)fromp, fromp->sa_len) < 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;
+ if (getnameinfo(fromp, fromp->sa_len, saddr, sizeof(saddr),
+ NULL, 0, NI_NAMEREQD)== 0) {
+ /*
+ * If name returned by getnameinfo 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 = saddr;
+ res0 = NULL;
+#ifdef KERBEROS
+ if (!use_kerberos)
+#endif
+ if (check_all || local_domain(saddr)) {
+ strlcpy(remotehost, saddr, sizeof(remotehost));
+ errorhost = remotehost;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = fromp->sa_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_CANONNAME;
+ gaierror = getaddrinfo(remotehost, pbuf, &hints, &res0);
+ if (gaierror) {
+ syslog(LOG_INFO,
+ "Couldn't look up address for %s: %s",
+ remotehost, gai_strerror(gaierror));
+ errorstr =
+ "Couldn't look up address for your host (%s)\n";
+ hostname = naddr;
+ } else {
+ for (res = res0; res; res = res->ai_next) {
+ if (res->ai_family != fromp->sa_family)
+ continue;
+ if (res->ai_addrlen != fromp->sa_len)
+ continue;
+ if (getnameinfo(res->ai_addr,
+ res->ai_addrlen,
+ raddr, sizeof(raddr), NULL, 0,
+ niflags) == 0
+ && strcmp(naddr, raddr) == 0) {
+ hostname = res->ai_canonname
+ ? res->ai_canonname
+ : saddr;
+ break;
+ }
+ }
+ if (res == NULL) {
+ syslog(LOG_NOTICE,
+ "Host addr %s not listed for host %s",
+ naddr, res0->ai_canonname
+ ? res0->ai_canonname
+ : saddr);
+ errorstr =
+ "Host address mismatch for %s\n";
+ hostname = naddr;
+ }
+ }
+ }
+ strlcpy(hostnamebuf, hostname, sizeof(hostnamebuf));
+ hostname = hostnamebuf;
+ if (res0)
+ freeaddrinfo(res0);
+ } else
+ strlcpy(hostnamebuf, naddr, sizeof(hostnamebuf));
+ errorhost = hostname = hostnamebuf;
+
+#ifdef KERBEROS
+ if (use_kerberos) {
+ kdata = (AUTH_DAT *) authbuf;
+ ticket = (KTEXT) tickbuf;
+ authopts = 0L;
+ strlcpy(instance, "*", sizeof instance);
+ version[VERSION_SIZE - 1] = '\0';
+#ifdef CRYPT
+ if (doencrypt) {
+ struct sockaddr_in local_addr;
+
+ rc = sizeof(local_addr);
+ if (getsockname(STDIN_FILENO,
+ (struct sockaddr *)&local_addr, &rc) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ error("rshd: getsockname: %m");
+ exit(1);
+ }
+ authopts = KOPT_DO_MUTUAL;
+ rc = krb_recvauth(authopts, 0, ticket,
+ "rcmd", instance, (struct sockaddr_in *)&fromaddr,
+ &local_addr, kdata, "", schedule, version);
+ desrw_set_key(&kdata->session, &schedule);
+ } else
+#endif
+ rc = krb_recvauth(authopts, 0, ticket, "rcmd",
+ instance, (struct sockaddr_in *)&fromaddr,
+ NULL, kdata, "", NULL, version);
+ if (rc != KSUCCESS) {
+ error("Kerberos authentication failure: %s\n",
+ krb_get_err_text(rc));
+ exit(1);
+ }
+ } else
+#endif
+
+ getstr(remuser, sizeof(remuser), "remuser");
+ getstr(locuser, sizeof(locuser), "locuser");
+ getstr(cmdbuf, sizeof(cmdbuf), "command");
+ 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 = "Permission denied.\n";
+ goto fail;
+ }
+ lc = login_getclass(pwd->pw_class);
+ if (lc == NULL) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: unknown class. cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+ if (errorstr == NULL)
+ errorstr = "Login incorrect.\n";
+ goto fail;
+ }
+ as = auth_open();
+ if (as == NULL || auth_setpwd(as, pwd) != 0) {
+ syslog(LOG_INFO|LOG_AUTH,
+ "%s@%s as %s: unable to allocate memory. cmd='%.80s'",
+ remuser, hostname, locuser, cmdbuf);
+ if (errorstr == NULL)
+ errorstr = "Cannot allocate memory.\n";
+ goto fail;
+ }
+
+ setegid(pwd->pw_gid);
+ seteuid(pwd->pw_uid);
+ 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
+ }
+ seteuid(0);
+ setegid(0); /* XXX use a saved gid instead? */
+
+#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_sa(fromp, fromp->sa_len, 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)
+ auth_checknologin(lc);
+
+ (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(STDIN_FILENO);
+ (void) close(STDOUT_FILENO);
+ }
+ (void) close(STDERR_FILENO);
+ (void) close(pv[1]);
+
+ pfd[P_SOCKREAD].fd = s;
+ pfd[P_SOCKREAD].events = POLLIN;
+ pfd[P_PIPEREAD].fd = pv[0];
+ pfd[P_PIPEREAD].events = POLLIN;
+ nfd = 2;
+#ifdef CRYPT
+#ifdef KERBEROS
+ if (doencrypt) {
+ pfd[P_CRYPTREAD].fd = pv1[0];
+ pfd[P_CRYPTREAD].events = POLLIN;
+ pfd[P_CRYPTWRITE].fd = pv2[0];
+ pfd[P_CRYPTWRITE].events = POLLOUT;
+ nfd += 2;
+ } else
+#endif
+#endif
+ ioctl(pv[0], FIONBIO, (char *)&one);
+
+ /* should set s nbio! */
+ do {
+ if (poll(pfd, nfd, INFTIM) < 0)
+ break;
+ if (pfd[P_SOCKREAD].revents & POLLIN) {
+ 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)
+ pfd[P_SOCKREAD].revents = 0;
+ else
+ killpg(pid, sig);
+ }
+ if (pfd[P_PIPEREAD].revents & POLLIN) {
+ errno = 0;
+ cc = read(pv[0], buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(s, SHUT_RDWR);
+ pfd[P_PIPEREAD].revents = 0;
+ } 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 &&
+ (pfd[P_CRYPTREAD].revents & POLLIN)) {
+ errno = 0;
+ cc = read(pv1[0], buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(pv1[0], SHUT_RDWR);
+ pfd[P_CRYPTREAD].revents = 0;
+ } else
+ (void) des_write(STDOUT_FILENO,
+ buf, cc);
+ }
+
+ if (doencrypt &&
+ (pfd[P_CRYPTWRITE].revents & POLLIN)) {
+ errno = 0;
+ cc = des_read(STDIN_FILENO,
+ buf, sizeof(buf));
+ if (cc <= 0) {
+ shutdown(pv2[0], SHUT_RDWR);
+ pfd[P_CRYPTWRITE].revents = 0;
+ } else
+ (void) write(pv2[0], buf, cc);
+ }
+#endif
+#endif
+
+ } while ((pfd[P_SOCKREAD].revents & POLLIN) ||
+#ifdef CRYPT
+#ifdef KERBEROS
+ (doencrypt && (pfd[P_CRYPTREAD].revents & POLLIN)) ||
+#endif
+#endif
+ (pfd[P_PIPEREAD].revents & POLLIN));
+ exit(0);
+ }
+ setsid();
+ (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]);
+ } else
+ setsid();
+ if (*pwd->pw_shell == '\0')
+ pwd->pw_shell = _PATH_BSHELL;
+
+ environ = envinit;
+ if (setenv("HOME", pwd->pw_dir, 1) == -1 ||
+ setenv("SHELL", pwd->pw_shell, 1) == -1 ||
+ setenv("USER", pwd->pw_name, 1) == -1 ||
+ setenv("LOGNAME", pwd->pw_name, 1) == -1)
+ errx(1, "cannot setup environment");
+
+ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL))
+ errx(1, "cannot set user context");
+ if (auth_approval(as, lc, pwd->pw_name, "rsh") <= 0)
+ errx(1, "approval failure");
+ auth_close(as);
+ login_close(lc);
+
+ 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, (char *)NULL);
+ 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.
+ */
+void
+error(const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+ char *bp, buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ bp = buf;
+ if (sent_null == 0) {
+ *bp++ = 1;
+ len = 1;
+ } else
+ len = 0;
+ (void)vsnprintf(bp, sizeof(buf) - len, fmt, ap);
+ (void)write(STDERR_FILENO, buf, len + strlen(bp));
+ va_end(ap);
+}
+
+void
+getstr(char *buf, int cnt, char *err)
+{
+ 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(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(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(void)
+{
+
+ syslog(LOG_ERR, "usage: rshd [-%s]", OPTIONS);
+ exit(2);
+}
--- /dev/null
+/Makefile/1.4/Tue May 24 22:23:04 2005//
+/spamd-setup.8/1.19/Thu Feb 19 17:11:20 2009//
+/spamd-setup.c/1.37/Wed Sep 9 16:05:55 2009//
+D
--- /dev/null
+src/libexec/spamd-setup
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.4 2005/05/24 22:23:04 millert Exp $
+
+PROG= spamd-setup
+SRCS= spamd-setup.c
+MAN= spamd-setup.8
+
+LDADD= -lz
+DPADD= ${LIBZ}
+
+CFLAGS+= -Wall -Wstrict-prototypes
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/spamd-setup
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: spamd-setup.8,v 1.19 2009/02/19 17:11:20 jmc Exp $
+.\"
+.\" Copyright (c) 2003 Jason L. Wright (jason@thought.net)
+.\" 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd $Mdocdate: February 19 2009 $
+.Dt SPAMD-SETUP 8
+.Os
+.Sh NAME
+.Nm spamd-setup
+.Nd parse and load file of spammer addresses
+.Sh SYNOPSIS
+.Nm spamd-setup
+.Op Fl bDdn
+.Sh DESCRIPTION
+The
+.Nm
+utility sends blacklist data to
+.Xr spamd 8 ,
+as well as configuring mail rejection messages for
+blacklist entries.
+.Pp
+When
+.Nm
+is run in blacklist only mode,
+it also sends blacklist data to the
+.Xr pf 4
+table
+.Aq Ar spamd .
+The
+.Aq Ar spamd
+table must then be used in conjunction with a
+.Xr pf 4
+redirection rule to selectively redirect mail connections
+to
+.Xr spamd 8 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b
+Blacklisting only mode.
+Blacklist data is normally stored only in
+.Xr spamd 8 .
+With this flag, data is stored in both
+.Xr spamd 8
+and
+.Xr pf 4 .
+Use this flag if
+.Xr spamd 8
+is running with the
+.Fl b
+flag too.
+.It Fl D
+Daemonize;
+run
+.Nm
+in the background.
+.It Fl d
+Debug mode reports a few pieces of information.
+.It Fl n
+Dry-run mode.
+No data is shipped.
+.El
+.Pp
+Lists are specified in the configuration file
+.Pa /etc/mail/spamd.conf
+and are processed in the order specified in the
+.Ar all
+tag.
+Output is concatenated and sent to a running
+.Xr spamd 8 .
+Addresses are sent
+along with the message spamd will give on mail rejection when a
+matching client connects.
+The configuration port for
+.Xr spamd 8
+is found from
+.Xr services 5 ,
+by looking for the named service
+.Em spamd-cfg .
+.Pp
+.Nm
+reads all configuration information from the
+.Xr spamd.conf 5
+file.
+.Sh FILES
+.Bd -literal
+/etc/mail/spamd.conf
+.Ed
+.Sh SEE ALSO
+.Xr pf.conf 5 ,
+.Xr services 5 ,
+.Xr spamd.conf 5 ,
+.Xr spamd 8
+.Sh BUGS
+Blacklists removed from
+.Pa /etc/mail/spamd.conf
+are not automatically removed from the running
+.Xr spamd 8 .
+If an entry is removed from
+.Pa /etc/mail/spamd.conf
+that is currently in use, it is necessary to restart
+.Xr spamd 8 .
+This applies only to blacklists that are removed entirely, not those
+that are simply modified.
--- /dev/null
+/* $OpenBSD: spamd-setup.c,v 1.37 2009/09/09 16:05:55 claudio Exp $ */
+
+/*
+ * Copyright (c) 2003 Bob Beck. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <netinet/ip_ipsp.h>
+#include <netdb.h>
+#include <zlib.h>
+
+#define PATH_FTP "/usr/bin/ftp"
+#define PATH_PFCTL "/sbin/pfctl"
+#define PATH_SPAMD_CONF "/etc/mail/spamd.conf"
+#define SPAMD_ARG_MAX 256 /* max # of args to an exec */
+
+struct cidr {
+ u_int32_t addr;
+ u_int8_t bits;
+};
+
+struct bl {
+ u_int32_t addr;
+ int8_t b;
+ int8_t w;
+};
+
+struct blacklist {
+ char *name;
+ char *message;
+ struct bl *bl;
+ size_t blc, bls;
+ u_int8_t black;
+ int count;
+};
+
+u_int32_t imask(u_int8_t);
+u_int8_t maxblock(u_int32_t, u_int8_t);
+u_int8_t maxdiff(u_int32_t, u_int32_t);
+struct cidr *range2cidrlist(struct cidr *, int *, int *, u_int32_t,
+ u_int32_t);
+void cidr2range(struct cidr, u_int32_t *, u_int32_t *);
+char *atop(u_int32_t);
+int parse_netblock(char *, struct bl *, struct bl *, int);
+int open_child(char *, char **);
+int fileget(char *);
+int open_file(char *, char *);
+char *fix_quoted_colons(char *);
+void do_message(FILE *, char *);
+struct bl *add_blacklist(struct bl *, size_t *, size_t *, gzFile, int);
+int cmpbl(const void *, const void *);
+struct cidr *collapse_blacklist(struct bl *, size_t);
+int configure_spamd(u_short, char *, char *, struct cidr *);
+int configure_pf(struct cidr *);
+int getlist(char **, char *, struct blacklist *, struct blacklist *);
+__dead void usage(void);
+
+int debug;
+int dryrun;
+int greyonly = 1;
+
+extern char *__progname;
+
+u_int32_t
+imask(u_int8_t b)
+{
+ if (b == 0)
+ return (0);
+ return (0xffffffff << (32 - b));
+}
+
+u_int8_t
+maxblock(u_int32_t addr, u_int8_t bits)
+{
+ u_int32_t m;
+
+ while (bits > 0) {
+ m = imask(bits - 1);
+
+ if ((addr & m) != addr)
+ return (bits);
+ bits--;
+ }
+ return (bits);
+}
+
+u_int8_t
+maxdiff(u_int32_t a, u_int32_t b)
+{
+ u_int8_t bits = 0;
+ u_int32_t m;
+
+ b++;
+ while (bits < 32) {
+ m = imask(bits);
+
+ if ((a & m) != (b & m))
+ return (bits);
+ bits++;
+ }
+ return (bits);
+}
+
+struct cidr *
+range2cidrlist(struct cidr *list, int *cli, int *cls, u_int32_t start,
+ u_int32_t end)
+{
+ u_int8_t maxsize, diff;
+ struct cidr *tmp;
+
+ while (end >= start) {
+ maxsize = maxblock(start, 32);
+ diff = maxdiff(start, end);
+
+ maxsize = MAX(maxsize, diff);
+ if (*cls <= *cli + 1) { /* one extra for terminator */
+ tmp = realloc(list, (*cls + 32) * sizeof(struct cidr));
+ if (tmp == NULL)
+ errx(1, "malloc failed");
+ list = tmp;
+ *cls += 32;
+ }
+ list[*cli].addr = start;
+ list[*cli].bits = maxsize;
+ (*cli)++;
+ start = start + (1 << (32 - maxsize));
+ }
+ return (list);
+}
+
+void
+cidr2range(struct cidr cidr, u_int32_t *start, u_int32_t *end)
+{
+ *start = cidr.addr;
+ *end = cidr.addr + (1 << (32 - cidr.bits)) - 1;
+}
+
+char *
+atop(u_int32_t addr)
+{
+ struct in_addr in;
+
+ memset(&in, 0, sizeof(in));
+ in.s_addr = htonl(addr);
+ return (inet_ntoa(in));
+}
+
+int
+parse_netblock(char *buf, struct bl *start, struct bl *end, int white)
+{
+ char astring[16], astring2[16];
+ unsigned maskbits;
+ struct cidr c;
+
+ /* skip leading spaces */
+ while (*buf == ' ')
+ buf++;
+ /* bail if it's a comment */
+ if (*buf == '#')
+ return (0);
+ /* otherwise, look for a netblock of some sort */
+ if (sscanf(buf, "%15[^/]/%u", astring, &maskbits) == 2) {
+ /* looks like a cidr */
+ memset(&c.addr, 0, sizeof(c.addr));
+ if (inet_net_pton(AF_INET, astring, &c.addr, sizeof(c.addr))
+ == -1)
+ return (0);
+ c.addr = ntohl(c.addr);
+ if (maskbits > 32)
+ return (0);
+ c.bits = maskbits;
+ cidr2range(c, &start->addr, &end->addr);
+ end->addr += 1;
+ } else if (sscanf(buf, "%15[0123456789.]%*[ -]%15[0123456789.]",
+ astring, astring2) == 2) {
+ /* looks like start - end */
+ memset(&start->addr, 0, sizeof(start->addr));
+ memset(&end->addr, 0, sizeof(end->addr));
+ if (inet_net_pton(AF_INET, astring, &start->addr,
+ sizeof(start->addr)) == -1)
+ return (0);
+ start->addr = ntohl(start->addr);
+ if (inet_net_pton(AF_INET, astring2, &end->addr,
+ sizeof(end->addr)) == -1)
+ return (0);
+ end->addr = ntohl(end->addr) + 1;
+ if (start > end)
+ return (0);
+ } else if (sscanf(buf, "%15[0123456789.]", astring) == 1) {
+ /* just a single address */
+ memset(&start->addr, 0, sizeof(start->addr));
+ if (inet_net_pton(AF_INET, astring, &start->addr,
+ sizeof(start->addr)) == -1)
+ return (0);
+ start->addr = ntohl(start->addr);
+ end->addr = start->addr + 1;
+ } else
+ return (0);
+
+ if (white) {
+ start->b = 0;
+ start->w = 1;
+ end->b = 0;
+ end->w = -1;
+ } else {
+ start->b = 1;
+ start->w = 0;
+ end->b = -1;
+ end->w = 0;
+ }
+ return (1);
+}
+
+int
+open_child(char *file, char **argv)
+{
+ int pdes[2];
+
+ if (pipe(pdes) != 0)
+ return (-1);
+ switch (fork()) {
+ case -1:
+ close(pdes[0]);
+ close(pdes[1]);
+ return (-1);
+ case 0:
+ /* child */
+ close(pdes[0]);
+ if (pdes[1] != STDOUT_FILENO) {
+ dup2(pdes[1], STDOUT_FILENO);
+ close(pdes[1]);
+ }
+ execvp(file, argv);
+ _exit(1);
+ }
+
+ /* parent */
+ close(pdes[1]);
+ return (pdes[0]);
+}
+
+int
+fileget(char *url)
+{
+ char *argv[6];
+
+ argv[0] = "ftp";
+ argv[1] = "-V";
+ argv[2] = "-o";
+ argv[3] = "-";
+ argv[4] = url;
+ argv[5] = NULL;
+
+ if (debug)
+ fprintf(stderr, "Getting %s\n", url);
+
+ return (open_child(PATH_FTP, argv));
+}
+
+int
+open_file(char *method, char *file)
+{
+ char *url;
+ char **ap, **argv;
+ int len, i, oerrno;
+
+ if ((method == NULL) || (strcmp(method, "file") == 0))
+ return (open(file, O_RDONLY));
+ if ((strcmp(method, "http") == 0) ||
+ strcmp(method, "ftp") == 0) {
+ asprintf(&url, "%s://%s", method, file);
+ if (url == NULL)
+ return (-1);
+ i = fileget(url);
+ free(url);
+ return (i);
+ } else if (strcmp(method, "exec") == 0) {
+ len = strlen(file);
+ argv = calloc(len, sizeof(char *));
+ if (argv == NULL)
+ errx(1, "malloc failed");
+ for (ap = argv; ap < &argv[len - 1] &&
+ (*ap = strsep(&file, " \t")) != NULL;) {
+ if (**ap != '\0')
+ ap++;
+ }
+ *ap = NULL;
+ i = open_child(argv[0], argv);
+ oerrno = errno;
+ free(argv);
+ errno = oerrno;
+ return (i);
+ }
+ errx(1, "Unknown method %s", method);
+ return (-1); /* NOTREACHED */
+}
+
+/*
+ * fix_quoted_colons walks through a buffer returned by cgetent. We
+ * look for quoted strings, to escape colons (:) in quoted strings for
+ * getcap by replacing them with \C so cgetstr() deals with it correctly
+ * without having to see the \C bletchery in a configuration file that
+ * needs to have urls in it. Frees the buffer passed to it, passes back
+ * another larger one, with can be used with cgetxxx(), like the original
+ * buffer, it must be freed by the caller.
+ * This should really be a temporary fix until there is a sanctioned
+ * way to make getcap(3) handle quoted strings like this in a nicer
+ * way.
+ */
+char *
+fix_quoted_colons(char *buf)
+{
+ int in = 0;
+ size_t i, j = 0;
+ char *newbuf, last;
+
+ /* Allocate enough space for a buf of all colons (impossible). */
+ newbuf = malloc(2 * strlen(buf) + 1);
+ if (newbuf == NULL)
+ return (NULL);
+ last = '\0';
+ for (i = 0; i < strlen(buf); i++) {
+ switch (buf[i]) {
+ case ':':
+ if (in) {
+ newbuf[j++] = '\\';
+ newbuf[j++] = 'C';
+ } else
+ newbuf[j++] = buf[i];
+ break;
+ case '"':
+ if (last != '\\')
+ in = !in;
+ newbuf[j++] = buf[i];
+ break;
+ default:
+ newbuf[j++] = buf[i];
+ }
+ last = buf[i];
+ }
+ free(buf);
+ newbuf[j] = '\0';
+ return (newbuf);
+}
+
+void
+do_message(FILE *sdc, char *msg)
+{
+ size_t i, bs = 0, bu = 0, len;
+ ssize_t n;
+ char *buf = NULL, last, *tmp;
+ int fd;
+
+ len = strlen(msg);
+ if (msg[0] == '"' && msg[len - 1] == '"') {
+ /* quoted msg, escape newlines and send it out */
+ msg[len - 1] = '\0';
+ buf = msg + 1;
+ bu = len - 2;
+ goto sendit;
+ } else {
+ /*
+ * message isn't quoted - try to open a local
+ * file and read the message from it.
+ */
+ fd = open(msg, O_RDONLY);
+ if (fd == -1)
+ err(1, "Can't open message from %s", msg);
+ for (;;) {
+ if (bu == bs) {
+ tmp = realloc(buf, bs + 8192);
+ if (tmp == NULL)
+ errx(1, "malloc failed");
+ bs += 8192;
+ buf = tmp;
+ }
+
+ n = read(fd, buf + bu, bs - bu);
+ if (n == 0) {
+ goto sendit;
+ } else if (n == -1) {
+ err(1, "Can't read from %s", msg);
+ } else
+ bu += n;
+ }
+ buf[bu]='\0';
+ }
+ sendit:
+ fprintf(sdc, ";\"");
+ last = '\0';
+ for (i = 0; i < bu; i++) {
+ /* handle escaping the things spamd wants */
+ switch (buf[i]) {
+ case 'n':
+ if (last == '\\')
+ fprintf(sdc, "\\\\n");
+ else
+ fputc('n', sdc);
+ last = '\0';
+ break;
+ case '\n':
+ fprintf(sdc, "\\n");
+ last = '\0';
+ break;
+ case '"':
+ fputc('\\', sdc);
+ /* FALLTHROUGH */
+ default:
+ fputc(buf[i], sdc);
+ last = '\0';
+ }
+ }
+ fputc('"', sdc);
+ if (bs != 0)
+ free(buf);
+}
+
+/* retrieve a list from fd. add to blacklist bl */
+struct bl *
+add_blacklist(struct bl *bl, size_t *blc, size_t *bls, gzFile gzf, int white)
+{
+ int i, n, start, bu = 0, bs = 0, serrno = 0;
+ char *buf = NULL, *tmp;
+ struct bl *blt;
+
+ for (;;) {
+ /* read in gzf, then parse */
+ if (bu == bs) {
+ tmp = realloc(buf, bs + (1024 * 1024) + 1);
+ if (tmp == NULL) {
+ serrno = errno;
+ free(buf);
+ buf = NULL;
+ bs = 0;
+ goto bldone;
+ }
+ bs += 1024 * 1024;
+ buf = tmp;
+ }
+
+ n = gzread(gzf, buf + bu, bs - bu);
+ if (n == 0)
+ goto parse;
+ else if (n == -1) {
+ serrno = errno;
+ goto bldone;
+ } else
+ bu += n;
+ }
+ parse:
+ start = 0;
+ /* we assume that there is an IP for every 16 bytes */
+ if (*blc + bu / 8 >= *bls) {
+ *bls += bu / 8;
+ blt = realloc(bl, *bls * sizeof(struct bl));
+ if (blt == NULL) {
+ *bls -= bu / 8;
+ serrno = errno;
+ goto bldone;
+ }
+ bl = blt;
+ }
+ for (i = 0; i <= bu; i++) {
+ if (*blc + 1 >= *bls) {
+ *bls += 1024;
+ blt = realloc(bl, *bls * sizeof(struct bl));
+ if (blt == NULL) {
+ *bls -= 1024;
+ serrno = errno;
+ goto bldone;
+ }
+ bl = blt;
+ }
+ if (i == bu || buf[i] == '\n') {
+ buf[i] = '\0';
+ if (parse_netblock(buf + start,
+ bl + *blc, bl + *blc + 1, white))
+ *blc += 2;
+ start = i + 1;
+ }
+ }
+ if (bu == 0)
+ errno = EIO;
+ bldone:
+ if (buf)
+ free(buf);
+ if (serrno)
+ errno = serrno;
+ return (bl);
+}
+
+int
+cmpbl(const void *a, const void *b)
+{
+ if (((struct bl *)a)->addr > ((struct bl *) b)->addr)
+ return (1);
+ if (((struct bl *)a)->addr < ((struct bl *) b)->addr)
+ return (-1);
+ return (0);
+}
+
+/*
+ * collapse_blacklist takes blacklist/whitelist entries sorts, removes
+ * overlaps and whitelist portions, and returns netblocks to blacklist
+ * as lists of nonoverlapping cidr blocks suitable for feeding in
+ * printable form to pfctl or spamd.
+ */
+struct cidr *
+collapse_blacklist(struct bl *bl, size_t blc)
+{
+ int bs = 0, ws = 0, state=0, cli, cls, i;
+ u_int32_t bstart = 0;
+ struct cidr *cl;
+ int laststate;
+ u_int32_t addr;
+
+ if (blc == 0)
+ return (NULL);
+ cli = 0;
+ cls = (blc / 2) + 1;
+ cl = calloc(cls, sizeof(struct cidr));
+ if (cl == NULL) {
+ return (NULL);
+ }
+ qsort(bl, blc, sizeof(struct bl), cmpbl);
+ for (i = 0; i < blc;) {
+ laststate = state;
+ addr = bl[i].addr;
+
+ do {
+ bs += bl[i].b;
+ ws += bl[i].w;
+ i++;
+ } while (bl[i].addr == addr);
+ if (state == 1 && bs == 0)
+ state = 0;
+ else if (state == 0 && bs > 0)
+ state = 1;
+ if (ws > 0)
+ state = 0;
+ if (laststate == 0 && state == 1) {
+ /* start blacklist */
+ bstart = addr;
+ }
+ if (laststate == 1 && state == 0) {
+ /* end blacklist */
+ cl = range2cidrlist(cl, &cli, &cls, bstart, addr - 1);
+ }
+ laststate = state;
+ }
+ cl[cli].addr = 0;
+ return (cl);
+}
+
+int
+configure_spamd(u_short dport, char *name, char *message,
+ struct cidr *blacklists)
+{
+ int lport = IPPORT_RESERVED - 1, s;
+ struct sockaddr_in sin;
+ FILE* sdc;
+
+ s = rresvport(&lport);
+ if (s == -1)
+ return (-1);
+ memset(&sin, 0, sizeof sin);
+ sin.sin_len = sizeof(sin);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(dport);
+ if (connect(s, (struct sockaddr *)&sin, sizeof sin) == -1)
+ return (-1);
+ sdc = fdopen(s, "w");
+ if (sdc == NULL) {
+ close(s);
+ return (-1);
+ }
+ fprintf(sdc, "%s", name);
+ do_message(sdc, message);
+ while (blacklists->addr != 0) {
+ fprintf(sdc, ";%s/%u", atop(blacklists->addr),
+ blacklists->bits);
+ blacklists++;
+ }
+ fputc('\n', sdc);
+ fclose(sdc);
+ close(s);
+ return (0);
+}
+
+
+int
+configure_pf(struct cidr *blacklists)
+{
+ char *argv[9]= {"pfctl", "-q", "-t", "spamd", "-T", "replace",
+ "-f" "-", NULL};
+ static FILE *pf = NULL;
+ int pdes[2];
+
+ if (pf == NULL) {
+ if (pipe(pdes) != 0)
+ return (-1);
+ switch (fork()) {
+ case -1:
+ close(pdes[0]);
+ close(pdes[1]);
+ return (-1);
+ case 0:
+ /* child */
+ close(pdes[1]);
+ if (pdes[0] != STDIN_FILENO) {
+ dup2(pdes[0], STDIN_FILENO);
+ close(pdes[0]);
+ }
+ execvp(PATH_PFCTL, argv);
+ _exit(1);
+ }
+
+ /* parent */
+ close(pdes[0]);
+ pf = fdopen(pdes[1], "w");
+ if (pf == NULL) {
+ close(pdes[1]);
+ return (-1);
+ }
+ }
+ while (blacklists->addr != 0) {
+ fprintf(pf, "%s/%u\n", atop(blacklists->addr),
+ blacklists->bits);
+ blacklists++;
+ }
+ return (0);
+}
+
+int
+getlist(char ** db_array, char *name, struct blacklist *blist,
+ struct blacklist *blistnew)
+{
+ char *buf, *method, *file, *message;
+ int fd, black = 0, serror;
+ size_t blc, bls;
+ struct bl *bl = NULL;
+ gzFile gzf;
+
+ if (cgetent(&buf, db_array, name) != 0)
+ err(1, "Can't find \"%s\" in spamd config", name);
+ buf = fix_quoted_colons(buf);
+ if (cgetcap(buf, "black", ':') != NULL) {
+ /* use new list */
+ black = 1;
+ blc = blistnew->blc;
+ bls = blistnew->bls;
+ bl = blistnew->bl;
+ } else if (cgetcap(buf, "white", ':') != NULL) {
+ /* apply to most recent blacklist */
+ black = 0;
+ blc = blist->blc;
+ bls = blist->bls;
+ bl = blist->bl;
+ } else
+ errx(1, "Must have \"black\" or \"white\" in %s", name);
+
+ switch (cgetstr(buf, "msg", &message)) {
+ case -1:
+ if (black)
+ errx(1, "No msg for blacklist \"%s\"", name);
+ break;
+ case -2:
+ errx(1, "malloc failed");
+ }
+
+ switch (cgetstr(buf, "method", &method)) {
+ case -1:
+ method = NULL;
+ break;
+ case -2:
+ errx(1, "malloc failed");
+ }
+
+ switch (cgetstr(buf, "file", &file)) {
+ case -1:
+ errx(1, "No file given for %slist %s",
+ black ? "black" : "white", name);
+ case -2:
+ errx(1, "malloc failed");
+ default:
+ fd = open_file(method, file);
+ if (fd == -1)
+ err(1, "Can't open %s by %s method",
+ file, method ? method : "file");
+ free(method);
+ free(file);
+ gzf = gzdopen(fd, "r");
+ if (gzf == NULL)
+ errx(1, "gzdopen");
+ }
+ free(buf);
+ bl = add_blacklist(bl, &blc, &bls, gzf, !black);
+ serror = errno;
+ gzclose(gzf);
+ if (bl == NULL) {
+ errno = serror;
+ warn("Could not add %slist %s", black ? "black" : "white",
+ name);
+ return (0);
+ }
+ if (black) {
+ blistnew->message = message;
+ blistnew->name = name;
+ blistnew->black = black;
+ blistnew->bl = bl;
+ blistnew->blc = blc;
+ blistnew->bls = bls;
+ } else {
+ /* whitelist applied to last active blacklist */
+ blist->bl = bl;
+ blist->blc = blc;
+ blist->bls = bls;
+ }
+ if (debug)
+ fprintf(stderr, "%slist %s %zu entries\n",
+ black ? "black" : "white", name, blc / 2);
+ return (black);
+}
+
+void
+send_blacklist(struct blacklist *blist, in_port_t port)
+{
+ struct cidr *cidrs;
+
+ if (blist->blc > 0) {
+ cidrs = collapse_blacklist(blist->bl, blist->blc);
+ if (cidrs == NULL)
+ errx(1, "malloc failed");
+ if (!dryrun) {
+ if (configure_spamd(port, blist->name,
+ blist->message, cidrs) == -1)
+ err(1, "Can't connect to spamd on port %d",
+ port);
+ if (!greyonly && configure_pf(cidrs) == -1)
+ err(1, "pfctl failed");
+ }
+ free(cidrs);
+ free(blist->bl);
+ }
+}
+
+__dead void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s [-bDdn]\n", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ size_t blc, bls, black, white;
+ char *db_array[2], *buf, *name;
+ struct blacklist *blists;
+ struct servent *ent;
+ int daemonize = 0, ch;
+
+ while ((ch = getopt(argc, argv, "bdDn")) != -1) {
+ switch (ch) {
+ case 'n':
+ dryrun = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ greyonly = 0;
+ break;
+ case 'D':
+ daemonize = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ usage();
+
+ if (daemonize)
+ daemon(0, 0);
+
+ if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL)
+ errx(1, "cannot find service \"spamd-cfg\" in /etc/services");
+ ent->s_port = ntohs(ent->s_port);
+
+ db_array[0] = PATH_SPAMD_CONF;
+ db_array[1] = NULL;
+
+ if (cgetent(&buf, db_array, "all") != 0)
+ err(1, "Can't find \"all\" in spamd config");
+ name = strsep(&buf, ": \t"); /* skip "all" at start */
+ blists = NULL;
+ blc = bls = 0;
+ while ((name = strsep(&buf, ": \t")) != NULL) {
+ if (*name) {
+ /* extract config in order specified in "all" tag */
+ if (blc == bls) {
+ struct blacklist *tmp;
+
+ bls += 32;
+ tmp = realloc(blists,
+ bls * sizeof(struct blacklist));
+ if (tmp == NULL)
+ errx(1, "malloc failed");
+ blists = tmp;
+ }
+ if (blc == 0)
+ black = white = 0;
+ else {
+ white = blc - 1;
+ black = blc;
+ }
+ memset(&blists[black], 0, sizeof(struct blacklist));
+ black = getlist(db_array, name, &blists[white],
+ &blists[black]);
+ if (black && blc > 0) {
+ /* collapse and free previous blacklist */
+ send_blacklist(&blists[blc - 1], ent->s_port);
+ }
+ blc += black;
+ }
+ }
+ /* collapse and free last blacklist */
+ if (blc > 0)
+ send_blacklist(&blists[blc - 1], ent->s_port);
+ return (0);
+}
--- /dev/null
+/Makefile/1.9/Sun Mar 4 03:19:41 2007//
+/grey.h/1.9/Tue Mar 6 23:38:36 2007//
+/sdl.c/1.18/Sat Nov 3 19:16:07 2007//
+/sdl.h/1.6/Sat Nov 3 19:16:07 2007//
+/spamd.8/1.117/Thu Sep 17 06:37:54 2009//
+/sync.c/1.8/Mon Apr 20 17:42:21 2009//
+/sync.h/1.3/Thu May 22 19:54:11 2008//
+/grey.c/1.49/Sat Feb 6 00:59:42 2010//
+/spamd.c/1.108/Sat Feb 6 00:59:42 2010//
+D
--- /dev/null
+src/libexec/spamd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.9 2007/03/04 03:19:41 beck Exp $
+
+PROG= spamd
+SRCS= spamd.c sdl.c grey.c sync.c
+MAN= spamd.8
+
+CFLAGS+= -Wall -Wstrict-prototypes
+
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: grey.c,v 1.49 2010/01/11 10:00:22 beck Exp $ */
+
+/*
+ * Copyright (c) 2004-2006 Bob Beck. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "grey.h"
+#include "sync.h"
+
+extern time_t passtime, greyexp, whiteexp, trapexp;
+extern struct syslog_data sdata;
+extern struct passwd *pw;
+extern u_short cfg_port;
+extern pid_t jail_pid;
+extern FILE *trapcfg;
+extern FILE *grey;
+extern int debug;
+extern int syncsend;
+
+/* From netinet/in.h, but only _KERNEL_ gets them. */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
+ struct sockaddr_in *);
+int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
+ struct sockaddr_in6 *);
+
+size_t whitecount, whitealloc;
+size_t trapcount, trapalloc;
+char **whitelist;
+char **traplist;
+
+char *traplist_name = "spamd-greytrap";
+char *traplist_msg = "\"Your address %A has mailed to spamtraps here\\n\"";
+
+pid_t db_pid = -1;
+int pfdev;
+
+struct db_change {
+ SLIST_ENTRY(db_change) entry;
+ char * key;
+ void * data;
+ size_t dsiz;
+ int act;
+};
+
+#define DBC_ADD 1
+#define DBC_DEL 2
+
+/* db pending changes list */
+SLIST_HEAD(, db_change) db_changes = SLIST_HEAD_INITIALIZER(db_changes);
+
+struct mail_addr {
+ SLIST_ENTRY(mail_addr) entry;
+ char addr[MAX_MAIL];
+};
+
+/* list of suffixes that must match TO: */
+SLIST_HEAD(, mail_addr) match_suffix = SLIST_HEAD_INITIALIZER(match_suffix);
+char *alloweddomains_file = PATH_SPAMD_ALLOWEDDOMAINS;
+
+char *low_prio_mx_ip;
+time_t startup;
+
+static char *pargv[11]= {
+ "pfctl", "-p", "/dev/pf", "-q", "-t",
+ "spamd-white", "-T", "replace", "-f" "-", NULL
+};
+
+/* If the parent gets a signal, kill off the children and exit */
+/* ARGSUSED */
+static void
+sig_term_chld(int sig)
+{
+ if (db_pid != -1)
+ kill(db_pid, SIGTERM);
+ if (jail_pid != -1)
+ kill(jail_pid, SIGTERM);
+ _exit(1);
+}
+
+/*
+ * Greatly simplified version from spamd_setup.c - only
+ * sends one blacklist to an already open stream. Has no need
+ * to collapse cidr ranges since these are only ever single
+ * host hits.
+ */
+void
+configure_spamd(char **addrs, size_t count, FILE *sdc)
+{
+ size_t i;
+
+ fprintf(sdc, "%s;", traplist_name);
+ if (count != 0) {
+ fprintf(sdc, "%s;", traplist_msg);
+ for (i = 0; i < count; i++)
+ fprintf(sdc, "%s/32;", addrs[i]);
+ }
+ fprintf(sdc, "\n");
+ if (fflush(sdc) == EOF)
+ syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)");
+}
+
+
+/* Stolen from ftp-proxy */
+int
+server_lookup(struct sockaddr *client, struct sockaddr *proxy,
+ struct sockaddr *server)
+{
+ if (client->sa_family == AF_INET)
+ return (server_lookup4(satosin(client), satosin(proxy),
+ satosin(server)));
+
+ if (client->sa_family == AF_INET6)
+ return (server_lookup6(satosin6(client), satosin6(proxy),
+ satosin6(server)));
+
+ errno = EPROTONOSUPPORT;
+ return (-1);
+}
+
+int
+server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
+ struct sockaddr_in *server)
+{
+ struct pfioc_natlook pnl;
+
+ memset(&pnl, 0, sizeof pnl);
+ pnl.direction = PF_OUT;
+ pnl.af = AF_INET;
+ pnl.proto = IPPROTO_TCP;
+ memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
+ memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
+ pnl.sport = client->sin_port;
+ pnl.dport = proxy->sin_port;
+
+ if (ioctl(pfdev, DIOCNATLOOK, &pnl) == -1)
+ return (-1);
+
+ memset(server, 0, sizeof(struct sockaddr_in));
+ server->sin_len = sizeof(struct sockaddr_in);
+ server->sin_family = AF_INET;
+ memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
+ sizeof server->sin_addr.s_addr);
+ server->sin_port = pnl.rdport;
+
+ return (0);
+}
+
+int
+server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
+ struct sockaddr_in6 *server)
+{
+ struct pfioc_natlook pnl;
+
+ memset(&pnl, 0, sizeof pnl);
+ pnl.direction = PF_OUT;
+ pnl.af = AF_INET6;
+ pnl.proto = IPPROTO_TCP;
+ memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
+ memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
+ pnl.sport = client->sin6_port;
+ pnl.dport = proxy->sin6_port;
+
+ if (ioctl(pfdev, DIOCNATLOOK, &pnl) == -1)
+ return (-1);
+
+ memset(server, 0, sizeof(struct sockaddr_in6));
+ server->sin6_len = sizeof(struct sockaddr_in6);
+ server->sin6_family = AF_INET6;
+ memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
+ sizeof server->sin6_addr);
+ server->sin6_port = pnl.rdport;
+
+ return (0);
+}
+
+int
+configure_pf(char **addrs, int count)
+{
+ FILE *pf = NULL;
+ int i, pdes[2], status;
+ pid_t pid;
+ char *fdpath;
+ struct sigaction sa;
+
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sig_term_chld;
+
+ if (debug)
+ fprintf(stderr, "configure_pf - device on fd %d\n", pfdev);
+ if (pfdev < 1 || pfdev > 63)
+ return(-1);
+ if (asprintf(&fdpath, "/dev/fd/%d", pfdev) == -1)
+ return(-1);
+ pargv[2] = fdpath;
+ if (pipe(pdes) != 0) {
+ syslog_r(LOG_INFO, &sdata, "pipe failed (%m)");
+ free(fdpath);
+ fdpath = NULL;
+ return(-1);
+ }
+ signal(SIGCHLD, SIG_DFL);
+ switch (pid = fork()) {
+ case -1:
+ syslog_r(LOG_INFO, &sdata, "fork failed (%m)");
+ free(fdpath);
+ fdpath = NULL;
+ close(pdes[0]);
+ close(pdes[1]);
+ sigaction(SIGCHLD, &sa, NULL);
+ return(-1);
+ case 0:
+ /* child */
+ close(pdes[1]);
+ if (pdes[0] != STDIN_FILENO) {
+ dup2(pdes[0], STDIN_FILENO);
+ close(pdes[0]);
+ }
+ execvp(PATH_PFCTL, pargv);
+ syslog_r(LOG_ERR, &sdata, "can't exec %s:%m", PATH_PFCTL);
+ _exit(1);
+ }
+
+ /* parent */
+ free(fdpath);
+ fdpath = NULL;
+ close(pdes[0]);
+ pf = fdopen(pdes[1], "w");
+ if (pf == NULL) {
+ syslog_r(LOG_INFO, &sdata, "fdopen failed (%m)");
+ close(pdes[1]);
+ sigaction(SIGCHLD, &sa, NULL);
+ return(-1);
+ }
+ for (i = 0; i < count; i++)
+ if (addrs[i] != NULL)
+ fprintf(pf, "%s/32\n", addrs[i]);
+ fclose(pf);
+
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ syslog_r(LOG_ERR, &sdata, "%s returned status %d", PATH_PFCTL,
+ WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ syslog_r(LOG_ERR, &sdata, "%s died on signal %d", PATH_PFCTL,
+ WTERMSIG(status));
+
+ sigaction(SIGCHLD, &sa, NULL);
+ return(0);
+}
+
+char *
+dequotetolower(const char *addr)
+{
+ static char buf[MAX_MAIL];
+ char *cp;
+
+ if (*addr == '<')
+ addr++;
+ (void) strlcpy(buf, addr, sizeof(buf));
+ cp = strrchr(buf, '>');
+ if (cp != NULL && cp[1] == '\0')
+ *cp = '\0';
+ cp = buf;
+ while (*cp != '\0') {
+ *cp = tolower(*cp);
+ cp++;
+ }
+ return(buf);
+}
+
+void
+readsuffixlists(void)
+{
+ FILE *fp;
+ char *buf;
+ size_t len;
+ struct mail_addr *m;
+
+ while (!SLIST_EMPTY(&match_suffix)) {
+ m = SLIST_FIRST(&match_suffix);
+ SLIST_REMOVE_HEAD(&match_suffix, entry);
+ free(m);
+ }
+ if ((fp = fopen(alloweddomains_file, "r")) != NULL) {
+ while ((buf = fgetln(fp, &len))) {
+ /* strip white space-characters */
+ while (len > 0 && isspace(buf[len-1]))
+ len--;
+ while (len > 0 && isspace(*buf)) {
+ buf++;
+ len--;
+ }
+ if (len == 0)
+ continue;
+ /* jump over comments and blank lines */
+ if (*buf == '#' || *buf == '\n')
+ continue;
+ if (buf[len-1] == '\n')
+ len--;
+ if ((len + 1) > sizeof(m->addr)) {
+ syslog_r(LOG_ERR, &sdata,
+ "line too long in %s - file ignored",
+ alloweddomains_file);
+ goto bad;
+ }
+ if ((m = malloc(sizeof(struct mail_addr))) == NULL)
+ goto bad;
+ memcpy(m->addr, buf, len);
+ m->addr[len]='\0';
+ syslog_r(LOG_ERR, &sdata, "got suffix %s", m->addr);
+ SLIST_INSERT_HEAD(&match_suffix, m, entry);
+ }
+ }
+ return;
+bad:
+ while (!SLIST_EMPTY(&match_suffix)) {
+ m = SLIST_FIRST(&match_suffix);
+ SLIST_REMOVE_HEAD(&match_suffix, entry);
+ free(m);
+ }
+}
+
+void
+freeaddrlists(void)
+{
+ int i;
+
+ if (whitelist != NULL)
+ for (i = 0; i < whitecount; i++) {
+ free(whitelist[i]);
+ whitelist[i] = NULL;
+ }
+ whitecount = 0;
+ if (traplist != NULL) {
+ for (i = 0; i < trapcount; i++) {
+ free(traplist[i]);
+ traplist[i] = NULL;
+ }
+ }
+ trapcount = 0;
+}
+
+/* validate, then add to list of addrs to whitelist */
+int
+addwhiteaddr(char *addr)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; /*for now*/
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_protocol = IPPROTO_UDP; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(addr, NULL, &hints, &res) == 0) {
+ if (whitecount == whitealloc) {
+ char **tmp;
+
+ tmp = realloc(whitelist,
+ (whitealloc + 1024) * sizeof(char *));
+ if (tmp == NULL) {
+ freeaddrinfo(res);
+ return(-1);
+ }
+ whitelist = tmp;
+ whitealloc += 1024;
+ }
+ whitelist[whitecount] = strdup(addr);
+ if (whitelist[whitecount] == NULL) {
+ freeaddrinfo(res);
+ return(-1);
+ }
+ whitecount++;
+ freeaddrinfo(res);
+ } else
+ return(-1);
+ return(0);
+}
+
+/* validate, then add to list of addrs to traplist */
+int
+addtrapaddr(char *addr)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; /*for now*/
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_protocol = IPPROTO_UDP; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(addr, NULL, &hints, &res) == 0) {
+ if (trapcount == trapalloc) {
+ char **tmp;
+
+ tmp = realloc(traplist,
+ (trapalloc + 1024) * sizeof(char *));
+ if (tmp == NULL) {
+ freeaddrinfo(res);
+ return(-1);
+ }
+ traplist = tmp;
+ trapalloc += 1024;
+ }
+ traplist[trapcount] = strdup(addr);
+ if (traplist[trapcount] == NULL) {
+ freeaddrinfo(res);
+ return(-1);
+ }
+ trapcount++;
+ freeaddrinfo(res);
+ } else
+ return(-1);
+ return(0);
+}
+
+static int
+queue_change(char *key, char *data, size_t dsiz, int act)
+{
+ struct db_change *dbc;
+
+ if ((dbc = malloc(sizeof(*dbc))) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ return(-1);
+ }
+ if ((dbc->key = strdup(key)) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ free(dbc);
+ return(-1);
+ }
+ if ((dbc->data = malloc(dsiz)) == NULL) {
+ syslog_r(LOG_DEBUG, &sdata, "malloc failed (queue change)");
+ free(dbc->key);
+ free(dbc);
+ return(-1);
+ }
+ memcpy(dbc->data, data, dsiz);
+ dbc->dsiz = dsiz;
+ dbc->act = act;
+ syslog_r(LOG_DEBUG, &sdata,
+ "queueing %s of %s", ((act == DBC_ADD) ? "add" : "deletion"),
+ dbc->key);
+ SLIST_INSERT_HEAD(&db_changes, dbc, entry);
+ return(0);
+}
+
+static int
+do_changes(DB *db)
+{
+ DBT dbk, dbd;
+ struct db_change *dbc;
+ int ret = 0;
+
+ while (!SLIST_EMPTY(&db_changes)) {
+ dbc = SLIST_FIRST(&db_changes);
+ switch (dbc->act) {
+ case DBC_ADD:
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(dbc->key);
+ dbk.data = dbc->key;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = dbc->dsiz;
+ dbd.data = dbc->data;
+ if (db->put(db, &dbk, &dbd, 0)) {
+ db->sync(db, 0);
+ syslog_r(LOG_ERR, &sdata,
+ "can't add %s to spamd db (%m)", dbc->key);
+ ret = -1;
+ }
+ db->sync(db, 0);
+ break;
+ case DBC_DEL:
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(dbc->key);
+ dbk.data = dbc->key;
+ if (db->del(db, &dbk, 0)) {
+ syslog_r(LOG_ERR, &sdata,
+ "can't delete %s from spamd db (%m)",
+ dbc->key);
+ ret = -1;
+ }
+ break;
+ default:
+ syslog_r(LOG_ERR, &sdata, "Unrecognized db change");
+ ret = -1;
+ }
+ free(dbc->key);
+ dbc->key = NULL;
+ free(dbc->data);
+ dbc->data = NULL;
+ dbc->act = 0;
+ dbc->dsiz = 0;
+ SLIST_REMOVE_HEAD(&db_changes, entry);
+ free(dbc);
+
+ }
+ return(ret);
+}
+
+int
+db_notin(DB *db, char *key)
+{
+ int i;
+ DBT dbk, dbd;
+
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(key);
+ dbk.data = key;
+ memset(&dbd, 0, sizeof(dbd));
+ i = db->get(db, &dbk, &dbd, 0);
+ if (i == -1)
+ return (-1);
+ if (i)
+ /* not in the database */
+ return (1);
+ else
+ /* it is in the database */
+ return (0);
+}
+
+
+int
+greyscan(char *dbname)
+{
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+ DB *db;
+ struct gdata gd;
+ int r;
+ char *a = NULL;
+ size_t asiz = 0;
+ time_t now = time(NULL);
+
+ /* walk db, expire, and whitelist */
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db == NULL) {
+ syslog_r(LOG_INFO, &sdata, "dbopen failed (%m)");
+ return(-1);
+ }
+ memset(&dbk, 0, sizeof(dbk));
+ memset(&dbd, 0, sizeof(dbd));
+ for (r = db->seq(db, &dbk, &dbd, R_FIRST); !r;
+ r = db->seq(db, &dbk, &dbd, R_NEXT)) {
+ if ((dbk.size < 1) || dbd.size != sizeof(struct gdata)) {
+ syslog_r(LOG_ERR, &sdata, "bogus entry in spamd database");
+ goto bad;
+ }
+ if (asiz < dbk.size + 1) {
+ char *tmp;
+
+ tmp = realloc(a, dbk.size * 2);
+ if (tmp == NULL)
+ goto bad;
+ a = tmp;
+ asiz = dbk.size * 2;
+ }
+ memset(a, 0, asiz);
+ memcpy(a, dbk.data, dbk.size);
+ memcpy(&gd, dbd.data, sizeof(gd));
+ if (gd.expire <= now && gd.pcount != -2) {
+ /* get rid of entry */
+ if (queue_change(a, NULL, 0, DBC_DEL) == -1)
+ goto bad;
+ } else if (gd.pcount == -1) {
+ /* this is a greytrap hit */
+ if ((addtrapaddr(a) == -1) &&
+ (queue_change(a, NULL, 0, DBC_DEL) == -1))
+ goto bad;
+ } else if (gd.pcount >= 0 && gd.pass <= now) {
+ int tuple = 0;
+ char *cp;
+
+ /*
+ * add address to whitelist
+ * add an address-keyed entry to db
+ */
+ cp = strchr(a, '\n');
+ if (cp != NULL) {
+ tuple = 1;
+ *cp = '\0';
+ }
+
+ if (addwhiteaddr(a) == -1) {
+ if (cp != NULL)
+ *cp = '\n';
+ if (queue_change(a, NULL, 0, DBC_DEL) == -1)
+ goto bad;
+ }
+
+ if (tuple && db_notin(db, a)) {
+ if (cp != NULL)
+ *cp = '\0';
+ /* re-add entry, keyed only by ip */
+ gd.expire = now + whiteexp;
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ if (queue_change(a, (void *) &gd, sizeof(gd),
+ DBC_ADD) == -1)
+ goto bad;
+ syslog_r(LOG_DEBUG, &sdata,
+ "whitelisting %s in %s", a, dbname);
+ }
+ if (debug)
+ fprintf(stderr, "whitelisted %s\n", a);
+ }
+ }
+ (void) do_changes(db);
+ db->close(db);
+ db = NULL;
+ configure_pf(whitelist, whitecount);
+ configure_spamd(traplist, trapcount, trapcfg);
+
+ freeaddrlists();
+ free(a);
+ a = NULL;
+ asiz = 0;
+ return(0);
+ bad:
+ (void) do_changes(db);
+ db->close(db);
+ db = NULL;
+ freeaddrlists();
+ free(a);
+ a = NULL;
+ asiz = 0;
+ return(-1);
+}
+
+int
+trapcheck(DB *db, char *to)
+{
+ int i, j, smatch = 0;
+ DBT dbk, dbd;
+ struct mail_addr *m;
+ char * trap;
+ size_t s;
+
+ trap = dequotetolower(to);
+ if (!SLIST_EMPTY(&match_suffix)) {
+ s = strlen(trap);
+ SLIST_FOREACH(m, &match_suffix, entry) {
+ j = s - strlen(m->addr);
+ if ((j >= 0) && (strcasecmp(trap+j, m->addr) == 0))
+ smatch = 1;
+ }
+ if (!smatch)
+ /* no suffixes match, so trap it */
+ return (0);
+ }
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(trap);
+ dbk.data = trap;
+ memset(&dbd, 0, sizeof(dbd));
+ i = db->get(db, &dbk, &dbd, 0);
+ if (i == -1)
+ return (-1);
+ if (i)
+ /* didn't exist - so this doesn't match a known spamtrap */
+ return (1);
+ else
+ /* To: address is a spamtrap, so add as a greytrap entry */
+ return (0);
+}
+
+int
+twupdate(char *dbname, char *what, char *ip, char *source, char *expires)
+{
+ /* we got a TRAP or WHITE update from someone else */
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+ DB *db;
+ struct gdata gd;
+ time_t now, expire;
+ int r, spamtrap;
+
+ now = time(NULL);
+ /* expiry times have to be in the future */
+ expire = strtonum(expires, now, INT_MAX, NULL);
+ if (expire == 0)
+ return(-1);
+
+ if (strcmp(what, "TRAP") == 0)
+ spamtrap = 1;
+ else if (strcmp(what, "WHITE") == 0)
+ spamtrap = 0;
+ else
+ return(-1);
+
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db == NULL)
+ return(-1);
+
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+ r = db->get(db, &dbk, &dbd, 0);
+ if (r == -1)
+ goto bad;
+ if (r) {
+ /* new entry */
+ memset(&gd, 0, sizeof(gd));
+ gd.first = now;
+ gd.pcount = spamtrap ? -1 : 0;
+ gd.expire = expire;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ db->sync(db, 0);
+ if (r)
+ goto bad;
+ if (debug)
+ fprintf(stderr, "added %s %s\n",
+ spamtrap ? "trap entry for" : "", ip);
+ syslog_r(LOG_DEBUG, &sdata,
+ "new %s from %s for %s, expires %s", what, source, ip,
+ expires);
+ } else {
+ /* existing entry */
+ if (dbd.size != sizeof(gd)) {
+ /* whatever this is, it doesn't belong */
+ db->del(db, &dbk, 0);
+ db->sync(db, 0);
+ goto bad;
+ }
+ memcpy(&gd, dbd.data, sizeof(gd));
+ if (spamtrap) {
+ gd.pcount = -1;
+ gd.bcount++;
+ } else
+ gd.pcount++;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ db->sync(db, 0);
+ if (r)
+ goto bad;
+ if (debug)
+ fprintf(stderr, "updated %s\n", ip);
+ }
+ db->close(db);
+ return(0);
+ bad:
+ db->close(db);
+ return(-1);
+
+}
+
+int
+greyupdate(char *dbname, char *helo, char *ip, char *from, char *to, int sync,
+ char *cip)
+{
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+ DB *db;
+ char *key = NULL;
+ char *lookup;
+ struct gdata gd;
+ time_t now, expire;
+ int r, spamtrap;
+
+ now = time(NULL);
+
+ /* open with lock, find record, update, close, unlock */
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db == NULL)
+ return(-1);
+ if (asprintf(&key, "%s\n%s\n%s\n%s", ip, helo, from, to) == -1)
+ goto bad;
+ r = trapcheck(db, to);
+ switch (r) {
+ case 1:
+ /* do not trap */
+ spamtrap = 0;
+ lookup = key;
+ expire = greyexp;
+ break;
+ case 0:
+ /* trap */
+ spamtrap = 1;
+ lookup = ip;
+ expire = trapexp;
+ syslog_r(LOG_DEBUG, &sdata, "Trapping %s for tuple %s", ip,
+ key);
+ break;
+ default:
+ goto bad;
+ break;
+ }
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(lookup);
+ dbk.data = lookup;
+ memset(&dbd, 0, sizeof(dbd));
+ r = db->get(db, &dbk, &dbd, 0);
+ if (r == -1)
+ goto bad;
+ if (r) {
+ /* new entry */
+ if (sync && low_prio_mx_ip &&
+ (strcmp(cip, low_prio_mx_ip) == 0) &&
+ ((startup + 60) < now)) {
+ /* we haven't seen a greylist entry for this tuple,
+ * and yet the connection was to a low priority MX
+ * which we know can't be hit first if the client
+ * is adhering to the RFC's - soo.. kill it!
+ */
+ spamtrap = 1;
+ lookup = ip;
+ expire = trapexp;
+ syslog_r(LOG_DEBUG, &sdata,
+ "Trapping %s for trying %s first for tuple %s",
+ ip, low_prio_mx_ip, key);
+ }
+ memset(&gd, 0, sizeof(gd));
+ gd.first = now;
+ gd.bcount = 1;
+ gd.pcount = spamtrap ? -1 : 0;
+ gd.pass = now + expire;
+ gd.expire = now + expire;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(lookup);
+ dbk.data = lookup;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ db->sync(db, 0);
+ if (r)
+ goto bad;
+ if (debug)
+ fprintf(stderr, "added %s %s\n",
+ spamtrap ? "greytrap entry for" : "", lookup);
+ syslog_r(LOG_DEBUG, &sdata,
+ "new %sentry %s from %s to %s, helo %s",
+ spamtrap ? "greytrap " : "", ip, from, to, helo);
+ } else {
+ /* existing entry */
+ if (dbd.size != sizeof(gd)) {
+ /* whatever this is, it doesn't belong */
+ db->del(db, &dbk, 0);
+ db->sync(db, 0);
+ goto bad;
+ }
+ memcpy(&gd, dbd.data, sizeof(gd));
+ gd.bcount++;
+ gd.pcount = spamtrap ? -1 : 0;
+ if (gd.first + passtime < now)
+ gd.pass = now;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(lookup);
+ dbk.data = lookup;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ db->sync(db, 0);
+ if (r)
+ goto bad;
+ if (debug)
+ fprintf(stderr, "updated %s\n", lookup);
+ }
+ free(key);
+ key = NULL;
+ db->close(db);
+ db = NULL;
+
+ /* Entry successfully update, sent out sync message */
+ if (syncsend && sync) {
+ if (spamtrap) {
+ syslog_r(LOG_DEBUG, &sdata,
+ "sync_trap %s", ip);
+ sync_trapped(now, now + expire, ip);
+ }
+ else
+ sync_update(now, helo, ip, from, to);
+ }
+ return(0);
+ bad:
+ free(key);
+ key = NULL;
+ db->close(db);
+ db = NULL;
+ return(-1);
+}
+
+int
+twread(char *buf)
+{
+ if ((strncmp(buf, "WHITE:", 6) == 0) ||
+ (strncmp(buf, "TRAP:", 5) == 0)) {
+ char **ap, *argv[5];
+ int argc = 0;
+
+ for (ap = argv;
+ ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) {
+ if (**ap != '\0')
+ ap++;
+ argc++;
+ }
+ *ap = NULL;
+ if (argc != 4)
+ return (-1);
+ twupdate(PATH_SPAMD_DB, argv[0], argv[1], argv[2], argv[3]);
+ return (0);
+ } else
+ return (-1);
+}
+
+int
+greyreader(void)
+{
+ char cip[32], ip[32], helo[MAX_MAIL], from[MAX_MAIL], to[MAX_MAIL];
+ char *buf;
+ size_t len;
+ int state, sync;
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET; /*for now*/
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_protocol = IPPROTO_UDP; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+
+ state = 0;
+ sync = 1;
+ if (grey == NULL) {
+ syslog_r(LOG_ERR, &sdata, "No greylist pipe stream!\n");
+ exit(1);
+ }
+
+ /* grab trap suffixes */
+ readsuffixlists();
+
+ while ((buf = fgetln(grey, &len))) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else
+ /* all valid lines end in \n */
+ continue;
+ if (strlen(buf) < 4)
+ continue;
+
+ if (strcmp(buf, "SYNC") == 0) {
+ sync = 0;
+ continue;
+ }
+
+ switch (state) {
+ case 0:
+ if (twread(buf) == 0) {
+ state = 0;
+ break;
+ }
+ if (strncmp(buf, "HE:", 3) != 0) {
+ if (strncmp(buf, "CO:", 3) == 0)
+ strlcpy(cip, buf+3, sizeof(cip));
+ state = 0;
+ break;
+ }
+ strlcpy(helo, buf+3, sizeof(helo));
+ state = 1;
+ break;
+ case 1:
+ if (strncmp(buf, "IP:", 3) != 0)
+ break;
+ strlcpy(ip, buf+3, sizeof(ip));
+ if (getaddrinfo(ip, NULL, &hints, &res) == 0) {
+ freeaddrinfo(res);
+ state = 2;
+ } else
+ state = 0;
+ break;
+ case 2:
+ if (strncmp(buf, "FR:", 3) != 0) {
+ state = 0;
+ break;
+ }
+ strlcpy(from, buf+3, sizeof(from));
+ state = 3;
+ break;
+ case 3:
+ if (strncmp(buf, "TO:", 3) != 0) {
+ state = 0;
+ break;
+ }
+ strlcpy(to, buf+3, sizeof(to));
+ if (debug)
+ fprintf(stderr,
+ "Got Grey HELO %s, IP %s from %s to %s\n",
+ helo, ip, from, to);
+ greyupdate(PATH_SPAMD_DB, helo, ip, from, to, sync, cip);
+ sync = 1;
+ state = 0;
+ break;
+ }
+ }
+ return (0);
+}
+
+void
+greyscanner(void)
+{
+ for (;;) {
+ if (greyscan(PATH_SPAMD_DB) == -1)
+ syslog_r(LOG_NOTICE, &sdata, "scan of %s failed",
+ PATH_SPAMD_DB);
+ sleep(DB_SCAN_INTERVAL);
+ }
+ /* NOTREACHED */
+}
+
+static void
+drop_privs(void)
+{
+ /*
+ * lose root, continue as non-root user
+ */
+ if (pw) {
+ setgroups(1, &pw->pw_gid);
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+ }
+}
+
+static void
+convert_spamd_db(void)
+{
+ char sfn[] = "/var/db/spamd.XXXXXXXXX";
+ int r, fd = -1;
+ DB *db1, *db2;
+ BTREEINFO btreeinfo;
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+
+ /* try to open the db as a BTREE */
+ memset(&btreeinfo, 0, sizeof(btreeinfo));
+ db1 = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_BTREE,
+ &btreeinfo);
+ if (db1 == NULL) {
+ syslog_r(LOG_ERR, &sdata,
+ "corrupt db in %s, remove and restart", PATH_SPAMD_DB);
+ exit(1);
+ }
+
+ if ((fd = mkstemp(sfn)) == -1) {
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s: mkstemp failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db2 = dbopen(sfn, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db2 == NULL) {
+ unlink(sfn);
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s: can't dbopen %s (%m)", PATH_SPAMD_DB,
+ sfn);
+ db1->close(db1);
+ exit(1);
+ }
+
+ memset(&dbk, 0, sizeof(dbk));
+ memset(&dbd, 0, sizeof(dbd));
+ for (r = db1->seq(db1, &dbk, &dbd, R_FIRST); !r;
+ r = db1->seq(db1, &dbk, &dbd, R_NEXT)) {
+ if (db2->put(db2, &dbk, &dbd, 0)) {
+ db2->sync(db2, 0);
+ db2->close(db2);
+ db1->close(db1);
+ unlink(sfn);
+ syslog_r(LOG_ERR, &sdata,
+ "can't convert %s - remove and restart",
+ PATH_SPAMD_DB);
+ exit(1);
+ }
+ }
+ db2->sync(db2, 0);
+ db2->close(db2);
+ db1->sync(db1, 0);
+ db1->close(db1);
+ rename(sfn, PATH_SPAMD_DB);
+ close(fd);
+ /* if we are dropping privs, chown to that user */
+ if (pw && (chown(PATH_SPAMD_DB, pw->pw_uid, pw->pw_gid) == -1)) {
+ syslog_r(LOG_ERR, &sdata,
+ "chown %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+}
+
+static void
+check_spamd_db(void)
+{
+ HASHINFO hashinfo;
+ int i = -1;
+ DB *db;
+
+ /* check to see if /var/db/spamd exists, if not, create it */
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(PATH_SPAMD_DB, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+
+ if (db == NULL) {
+ switch (errno) {
+ case ENOENT:
+ i = open(PATH_SPAMD_DB, O_RDWR|O_CREAT, 0644);
+ if (i == -1) {
+ syslog_r(LOG_ERR, &sdata,
+ "create %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+ /* if we are dropping privs, chown to that user */
+ if (pw && (fchown(i, pw->pw_uid, pw->pw_gid) == -1)) {
+ syslog_r(LOG_ERR, &sdata,
+ "chown %s failed (%m)", PATH_SPAMD_DB);
+ exit(1);
+ }
+ close(i);
+ drop_privs();
+ return;
+ break;
+ case EFTYPE:
+ /*
+ * db may be old BTREE instead of HASH, attempt to
+ * convert.
+ */
+ convert_spamd_db();
+ drop_privs();
+ return;
+ break;
+ default:
+ syslog_r(LOG_ERR, &sdata, "open of %s failed (%m)",
+ PATH_SPAMD_DB);
+ exit(1);
+ }
+ }
+ db->sync(db, 0);
+ db->close(db);
+ drop_privs();
+}
+
+
+int
+greywatcher(void)
+{
+ struct sigaction sa;
+
+ check_spamd_db();
+
+ startup = time(NULL);
+ db_pid = fork();
+ switch (db_pid) {
+ case -1:
+ syslog_r(LOG_ERR, &sdata, "fork failed (%m)");
+ exit(1);
+ case 0:
+ /*
+ * child, talks to jailed spamd over greypipe,
+ * updates db. has no access to pf.
+ */
+ close(pfdev);
+ setproctitle("(%s update)", PATH_SPAMD_DB);
+ greyreader();
+ syslog_r(LOG_ERR, &sdata, "greyreader failed (%m)");
+ /* NOTREACHED */
+ _exit(1);
+ }
+
+
+ fclose(grey);
+ /*
+ * parent, scans db periodically for changes and updates
+ * pf whitelist table accordingly.
+ */
+
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_RESTART;
+ sa.sa_handler = sig_term_chld;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ setproctitle("(pf <spamd-white> update)");
+ greyscanner();
+ /* NOTREACHED */
+ exit(1);
+}
--- /dev/null
+/* $OpenBSD: grey.h,v 1.9 2007/03/06 23:38:36 beck Exp $ */
+
+/*
+ * Copyright (c) 2004 Bob Beck. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define MAX_MAIL 1024 /* how big an email address will we consider */
+#define PASSTIME (60 * 25) /* pass after first retry seen after 25 mins */
+#define GREYEXP (60 * 60 * 4) /* remove grey entries after 4 hours */
+#define WHITEEXP (60 * 60 * 24 * 36) /* remove white entries after 36 days */
+#define TRAPEXP (60 * 60 * 24) /* hitting a spamtrap blacklists for a day */
+#define PATH_PFCTL "/sbin/pfctl"
+#define PATH_SPAMD_ALLOWEDDOMAINS "/etc/mail/spamd.alloweddomains"
+#define DB_SCAN_INTERVAL 60
+#define DB_TRAP_INTERVAL 60 * 10
+#define PATH_SPAMD_DB "/var/db/spamd"
+
+struct gdata {
+ time_t first; /* when did we see it first */
+ time_t pass; /* when was it whitelisted */
+ time_t expire; /* when will we get rid of this entry */
+ int bcount; /* how many times have we blocked it */
+ int pcount; /* how many times passed, or -1 for spamtrap */
+};
+
+extern int greywatcher(void);
+extern int greyupdate(char *, char *, char *, char *, char *, int, char *);
--- /dev/null
+/usr/obj/libexec/spamd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: sdl.c,v 1.18 2007/11/03 19:16:07 beck Exp $ */
+
+/*
+ * Copyright (c) 2003-2007 Bob Beck. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * sdl.c - Implement spamd source lists
+ *
+ * This consists of everything we need to do to determine which lists
+ * someone is on. Spamd gets the connecting address, and looks it up
+ * against all lists to determine what deferral messages to feed back
+ * to the connecting machine. - The redirection to spamd will happen
+ * from pf in the kernel, first macth will rdr to us. Spamd (along with
+ * setup) must keep track of *all* matches, so as to tell someone all the
+ * lists that they are on.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sdl.h"
+
+static void sdl_free(struct sdlist *);
+static void sdl_clear(struct sdlist *);
+int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
+ sa_family_t af);
+
+extern int debug;
+struct sdlist *blacklists = NULL;
+int blc = 0, blu = 0;
+
+int
+sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
+{
+ int i, idx = -1;
+ char astring[40];
+ unsigned int maskbits;
+ struct sdaddr *m, *n;
+
+ /*
+ * if a blacklist of same tag name is already there, replace it,
+ * otherwise append.
+ */
+ for (i = 0; i < blu; i++) {
+ if (strcmp(blacklists[i].tag, sdname) == 0) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx != -1) {
+ if (debug > 0)
+ printf("replacing list %s; %d new entries\n",
+ blacklists[idx].tag, addrc);
+ sdl_free(&blacklists[idx]);
+ } else {
+ if (debug > 0)
+ printf("adding list %s; %d entries\n", sdname, addrc);
+ idx = blu;
+ }
+ if (idx == blu && blu == blc) {
+ struct sdlist *tmp;
+
+ tmp = realloc(blacklists, (blc + 128) *
+ sizeof(struct sdlist));
+ if (tmp == NULL)
+ return (-1);
+ blacklists = tmp;
+ blc += 128;
+ sdl_clear(&blacklists[idx]);
+ }
+
+ if ((blacklists[idx].tag = strdup(sdname)) == NULL)
+ goto misc_error;
+ if ((blacklists[idx].string = strdup(sdstring)) == NULL)
+ goto misc_error;
+
+ blacklists[idx].naddrs = addrc;
+
+ /*
+ * Cycle through addrs, converting. We assume they are correctly
+ * formatted v4 and v6 addrs, if they don't all convert correctly, the
+ * add fails. Each address should be address/maskbits
+ */
+ blacklists[idx].addrs = calloc(addrc, sizeof(struct sdentry));
+ if (blacklists[idx].addrs == NULL)
+ goto misc_error;
+
+ for (i = 0; i < addrc; i++) {
+ int j, k, af;
+
+ n = &blacklists[idx].addrs[i].sda;
+ m = &blacklists[idx].addrs[i].sdm;
+
+ j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
+ if (j != 2)
+ goto parse_error;
+ if (maskbits > 128)
+ goto parse_error;
+ /*
+ * sanity check! we don't allow a 0 mask -
+ * don't blacklist the entire net.
+ */
+ if (maskbits == 0)
+ goto parse_error;
+ if (strchr(astring, ':') != NULL)
+ af = AF_INET6;
+ else
+ af = AF_INET;
+ if (af == AF_INET && maskbits > 32)
+ goto parse_error;
+ j = inet_pton(af, astring, n);
+ if (j != 1)
+ goto parse_error;
+ if (debug > 0)
+ printf("added %s/%u\n", astring, maskbits);
+
+ /* set mask, borrowed from pf */
+ k = 0;
+ for (j = 0; j < 4; j++)
+ m->addr32[j] = 0;
+ while (maskbits >= 32) {
+ m->addr32[k++] = 0xffffffff;
+ maskbits -= 32;
+ }
+ for (j = 31; j > 31 - maskbits; --j)
+ m->addr32[k] |= (1 << j);
+ if (maskbits)
+ m->addr32[k] = htonl(m->addr32[k]);
+
+ /* mask off address bits that won't ever be used */
+ for (j = 0; j < 4; j++)
+ n->addr32[j] = n->addr32[j] & m->addr32[j];
+ }
+ if (idx == blu) {
+ blu++;
+ blacklists[blu].tag = NULL;
+ }
+ return (0);
+ parse_error:
+ if (debug > 0)
+ printf("sdl_add: parse error, \"%s\"\n", addrs[i]);
+ misc_error:
+ sdl_free(&blacklists[idx]);
+ return (-1);
+}
+
+void
+sdl_del(char *sdname)
+{
+ int i, idx = -1;
+
+ for (i = 0; i < blu; i++) {
+ if (strcmp(blacklists[i].tag, sdname) == 0) {
+ idx = i;
+ break;
+ }
+ }
+ if (idx != -1) {
+ if (debug > 0)
+ printf("clearing list %s\n", sdname);
+ free(blacklists[idx].string);
+ free(blacklists[idx].addrs);
+ blacklists[idx].string = NULL;
+ blacklists[idx].addrs = NULL;
+ blacklists[idx].naddrs = 0;
+ }
+}
+
+/*
+ * Return 1 if the addresses a (with mask m) matches address b
+ * otherwise return 0. It is assumed that address a has been
+ * pre-masked out, we only need to mask b.
+ */
+int
+match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
+ sa_family_t af)
+{
+ int match = 0;
+
+ switch (af) {
+ case AF_INET:
+ if ((a->addr32[0]) ==
+ (b->addr32[0] & m->addr32[0]))
+ match++;
+ break;
+ case AF_INET6:
+ if (((a->addr32[0]) ==
+ (b->addr32[0] & m->addr32[0])) &&
+ ((a->addr32[1]) ==
+ (b->addr32[1] & m->addr32[1])) &&
+ ((a->addr32[2]) ==
+ (b->addr32[2] & m->addr32[2])) &&
+ ((a->addr32[3]) ==
+ (b->addr32[3] & m->addr32[3])))
+ match++;
+ break;
+ }
+ return (match);
+}
+
+
+/*
+ * Given an address and address family
+ * return list of pointers to matching nodes. or NULL if none.
+ */
+struct sdlist **
+sdl_lookup(struct sdlist *head, int af, void * src)
+{
+ int i, matches = 0;
+ struct sdlist *sdl;
+ struct sdentry *sda;
+ struct sdaddr *source = (struct sdaddr *) src;
+ int sdnewlen = 0;
+ struct sdlist **sdnew = NULL;
+
+ if (head == NULL)
+ return (NULL);
+ else
+ sdl = head;
+ while (sdl->tag != NULL) {
+ for (i = 0; i < sdl->naddrs; i++) {
+ sda = sdl->addrs + i;
+ if (match_addr(&sda->sda, &sda->sdm, source, af)) {
+ if (matches == sdnewlen) {
+ struct sdlist **tmp;
+
+ tmp = realloc(sdnew,
+ (sdnewlen + 128) *
+ sizeof(struct sdlist *));
+ if (tmp == NULL)
+ /*
+ * XXX out of memory -
+ * return what we have
+ */
+ return (sdnew);
+ sdnew = tmp;
+ sdnewlen += 128;
+ }
+ sdnew[matches]= sdl;
+ matches++;
+ sdnew[matches]=NULL;
+ break;
+ }
+ }
+ sdl++;
+ }
+ return (sdnew);
+}
+
+static void
+sdl_free(struct sdlist *sdl)
+{
+ free(sdl->tag);
+ free(sdl->string);
+ free(sdl->addrs);
+ sdl_clear(sdl);
+}
+
+static void
+sdl_clear(struct sdlist *sdl)
+{
+ sdl->tag = NULL;
+ sdl->string = NULL;
+ sdl->addrs = NULL;
+ sdl->naddrs = 0;
+}
+
--- /dev/null
+/* $OpenBSD: sdl.h,v 1.6 2007/11/03 19:16:07 beck Exp $ */
+
+/*
+ * Copyright (c) 2003-2007 Bob Beck. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDL_H_
+#define _SDL_H_
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/* spamd source list */
+struct sdlist {
+ char *tag; /* sdlist source name */
+ char *string; /* Format (451) string with no smtp code or \r\n */
+ struct sdentry *addrs;
+ size_t naddrs;
+};
+
+/* yeah. Stolen from pf */
+struct sdaddr {
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ u_int8_t addr8[16];
+ u_int16_t addr16[8];
+ u_int32_t addr32[4];
+ } _sda; /* 128-bit address */
+#define v4 _sda.v4
+#define v6 _sda.v6
+#define addr8 _sda.addr8
+#define addr16 _sda.addr16
+#define addr32 _sda.addr32
+};
+
+/* spamd netblock (black) list */
+struct sdentry {
+ struct sdaddr sda;
+ struct sdaddr sdm;
+};
+
+
+extern int sdl_add(char *, char *, char **, int);
+extern void sdl_del(char *);
+extern struct sdlist **sdl_lookup(struct sdlist *head,
+ int af, void * src);
+
+#endif /* _SDL_H_ */
--- /dev/null
+.\" $OpenBSD: spamd.8,v 1.117 2009/09/17 06:37:54 jmc Exp $
+.\"
+.\" Copyright (c) 2002 Theo de Raadt. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd $Mdocdate: September 17 2009 $
+.Dt SPAMD 8
+.Os
+.Sh NAME
+.Nm spamd
+.Nd spam deferral daemon
+.Sh SYNOPSIS
+.Nm spamd
+.Bk -words
+.Op Fl 45bdv
+.Op Fl B Ar maxblack
+.Op Fl c Ar maxcon
+.Oo
+.Fl G
+.Ar passtime : Ns Ar greyexp : Ns Ar whiteexp
+.Oc
+.Op Fl h Ar hostname
+.Op Fl l Ar address
+.Op Fl M Ar address
+.Op Fl n Ar name
+.Op Fl p Ar port
+.Op Fl S Ar secs
+.Op Fl s Ar secs
+.Op Fl w Ar window
+.Op Fl Y Ar synctarget
+.Op Fl y Ar synclisten
+.Ek
+.Sh DESCRIPTION
+.Nm
+is a fake
+.Xr sendmail 8 Ns -like
+daemon which rejects false mail.
+It is designed to be very efficient so that it does not slow down the
+receiving machine.
+.Pp
+.Nm
+considers sending hosts to be of three types:
+.Pp
+.Em blacklisted
+hosts are redirected to
+.Nm
+and
+.Em tarpitted
+i.e. they are communicated with very slowly
+to consume the sender's resources.
+Mail is rejected with either a 450 or 550 error message.
+A blacklisted host will not be allowed to talk to a real mail server.
+.Pp
+.Em whitelisted
+hosts do not talk to
+.Nm .
+Their connections are instead sent to a real mail server,
+such as
+.Xr sendmail 8 .
+.Pp
+.Em greylisted
+hosts are redirected to
+.Nm ,
+but
+.Nm
+has not yet decided if they are likely spammers.
+They are given a temporary failure message by
+.Nm
+when they try to deliver mail.
+.Pp
+When
+.Nm
+is run in default mode,
+it will greylist connections from new hosts.
+Depending on its configuration,
+it may choose to blacklist the host or,
+if the checks described below are met,
+eventually whitelist it.
+When
+.Nm
+is run in blacklist-only mode,
+using the
+.Fl b
+flag,
+it will consult a pre-defined set of blacklist addresses
+to decide whether to tarpit the host or not.
+.Pp
+When a sending host talks to
+.Nm ,
+the reply will be
+.Em stuttered .
+That is,
+the response will be sent back a character at a time, slowly.
+For blacklisted hosts,
+the entire dialogue is stuttered.
+For greylisted hosts,
+the default is to stutter for the first 10 seconds
+of dialogue only.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 4
+For blacklisted entries, return error code 450 to the spammer (default).
+.It Fl 5
+For blacklisted entries, return error code 550 to the spammer.
+.It Fl B Ar maxblack
+The maximum number of concurrent blacklisted connections to stutter at.
+This value may not be greater than maxcon (see below).
+The default is
+.Ar maxcon
+\- 100.
+When this value is exceeded new blacklisted connections will not be stuttered
+at.
+.It Fl b
+Run in blacklist-only mode.
+.It Fl c Ar maxcon
+The maximum number of concurrent connections to allow.
+.Ar maxcon
+may not exceed
+.Va kern.maxfiles
+\- 200, and defaults to 800.
+.It Fl d
+Debug mode.
+.Nm
+does not
+.Xr fork 2
+into the background.
+.It Xo
+.Fl G
+.Ar passtime : Ns Ar greyexp : Ns Ar whiteexp
+.Xc
+Adjust the three time parameters for greylisting.
+.Ar passtime
+defaults to 25 (minutes),
+.Ar greyexp
+to 4 (hours),
+and
+.Ar whiteexp
+to 864 (hours, approximately 36 days).
+.It Fl h Ar hostname
+The hostname that is reported in the SMTP banner.
+.It Fl l Ar address
+Specify the local address to which
+.Nm
+is to
+.Xr bind 2 .
+By default
+.Nm
+listens on all local addresses.
+.It Fl M Ar address
+Specify a local IP address which is listed as a low priority MX record,
+used to identify and trap hosts that connect to MX hosts out of order.
+See
+.Sx GREYTRAPPING
+below for details.
+.It Fl n Ar name
+The SMTP version banner that is reported upon initial connection.
+.It Fl p Ar port
+Specify a different port number from the default port that
+.Nm
+should listen for redirected SMTP connections on.
+The default port is found by looking for the named service
+.Dq spamd
+using
+.Xr getservbyname 3 .
+.It Fl S Ar secs
+Stutter at greylisted connections for the specified amount
+of seconds, after which the connection is not stuttered at.
+The default is 10; maximum is 90.
+.It Fl s Ar secs
+Delay each character sent to the client by the specified
+amount of seconds.
+The default is 1; maximum is 10.
+.It Fl v
+Enable verbose logging.
+By default
+.Nm
+logs connections, disconnections and blacklist matches to
+.Xr syslogd 8
+at
+.Dv LOG_INFO
+level.
+With verbose logging enabled, message detail
+including subject and recipient information is logged at
+.Dv LOG_INFO ,
+along with the message body and SMTP dialogue being logged at
+.Dv LOG_DEBUG
+level.
+.It Fl w Ar window
+Set the socket receive buffer to this many bytes, adjusting the window size.
+.It Fl Y Ar synctarget
+Add target
+.Ar synctarget
+to receive synchronisation messages.
+.Ar synctarget
+can be either an IPv4 address for unicast messages
+or a network interface and optional TTL value for multicast messages
+to the group 224.0.1.240.
+If the multicast TTL is not specified, a default value of 1 is used.
+This option can be specified multiple times.
+See also
+.Sx SYNCHRONISATION
+below.
+.It Fl y Ar synclisten
+Listen on
+.Ar synclisten
+for incoming synchronisation messages.
+The format for
+.Ar synclisten
+is the same as for
+.Ar synctarget ,
+above.
+This option can be specified only once.
+See also
+.Sx SYNCHRONISATION
+below.
+.El
+.Pp
+When run in default mode,
+connections receive the pleasantly innocuous temporary failure of:
+.Bd -literal -offset 4n
+451 Temporary failure, please try again later.
+.Ed
+.Pp
+This happens in the SMTP dialogue
+immediately after the DATA command is received from the client.
+.Nm
+will use the db file in
+.Pa /var/db/spamd
+to track these connections to
+.Nm
+by connecting IP address, HELO/EHLO, envelope-from, and envelope-to, or
+.Em tuple
+for short.
+.Pp
+A previously unseen tuple is added to the
+.Pa /var/db/spamd
+database, recording the time an initial connection attempt was seen.
+After
+.Em passtime
+minutes if
+.Nm
+sees a retried attempt to deliver mail for the same tuple,
+.Nm
+will whitelist the connecting address by adding it as a
+whitelist entry to
+.Pa /var/db/spamd .
+.Pp
+.Nm
+regularly scans the
+.Pa /var/db/spamd
+database and configures all whitelist addresses as the
+.Xr pf 4
+.Aq spamd-white
+table,
+allowing connections to pass to the real MTA.
+Any addresses not found in
+.Aq spamd-white
+are redirected to
+.Nm .
+.Pp
+An example
+.Xr pf.conf 5
+fragment is given below.
+In the example, the file
+.Pa /etc/mail/nospamd
+contains addresses of hosts who should be passed directly
+to the SMTP agent (thus bypassing
+.Nm ) .
+.Bd -literal -offset 4n
+table \*(Ltspamd-white\*(Gt persist
+table \*(Ltnospamd\*(Gt persist file "/etc/mail/nospamd"
+pass in on egress proto tcp from any to any port smtp \e
+ rdr-to 127.0.0.1 port spamd
+pass in on egress proto tcp from \*(Ltnospamd\*(Gt to any port smtp
+pass in log on egress proto tcp from \*(Ltspamd-white\*(Gt to any port smtp
+pass out log on egress proto tcp to any port smtp
+.Ed
+.Pp
+.Nm
+removes tuple entries from the
+.Pa /var/db/spamd
+database if delivery has not been retried within
+.Em greyexp
+hours from the initial time a connection is seen.
+The default is 4 hours as this is the most common setting after which
+MTAs will give up attempting to retry delivery of a message.
+.Pp
+.Nm
+removes whitelist entries from the
+.Pa /var/db/spamd
+database if no mail delivery activity has been seen from the
+whitelisted address by
+.Xr spamlogd 8
+within
+.Em whiteexp
+hours from the initial time an address
+is whitelisted.
+The default is 36 days to allow for the delivery of
+monthly mailing list digests without greylist delays every time.
+.Pp
+.Xr spamd-setup 8
+should be run periodically by
+.Xr cron 8 .
+When run in blacklist-only mode,
+the
+.Fl b
+flag should be specified.
+Use
+.Xr crontab 1
+to uncomment the entry in root's crontab.
+.Pp
+.Xr spamlogd 8
+should be used to update the whitelist entries in
+.Pa /var/db/spamd
+when connections are seen to pass to the real MTA on the
+.Em smtp
+port.
+.Pp
+.Xr spamdb 8
+can be used to examine and alter the contents of
+.Pa /var/db/spamd .
+See
+.Xr spamdb 8
+for further information.
+.Pp
+.Nm
+sends log messages to
+.Xr syslogd 8
+using
+.Em facility
+daemon and, with increasing verbosity,
+.Em level
+err, warn, info, and debug.
+The following
+.Xr syslog.conf 5
+section can be used to log connection details to a dedicated file:
+.Bd -literal -offset indent
+!spamd
+daemon.err;daemon.warn;daemon.info /var/log/spamd
+.Ed
+.Pp
+A typical entry shows the time of the connection and
+the IP address of the connecting host.
+When a host connects,
+the total number of active connections and
+the number of connections from blacklisted hosts is shown
+.Pq connected (xx/xx) .
+When a host disconnects,
+the amount of time spent talking to
+.Nm
+is shown.
+.Sh GREYTRAPPING
+When running
+.Nm
+in default mode,
+it may be useful to define
+.Em spamtrap
+destination addresses to catch spammers as they send mail from greylisted
+hosts.
+Such spamtrap addresses affect only greylisted connections to
+.Nm
+and are used to temporarily blacklist a host that is obviously sending spam.
+Unused email addresses or email addresses on spammers' lists are very
+useful for this.
+When a host that is currently greylisted attempts to send mail to a
+spamtrap address,
+it is blacklisted for 24 hours by adding the host to the
+.Nm
+blacklist
+.Aq spamd-greytrap .
+Spamtrap addresses are added to the
+.Pa /var/db/spamd
+database with the following
+.Xr spamdb 8
+command:
+.Pp
+.Dl # spamdb -T -a 'spamtrap@mydomain.org'
+.Pp
+See
+.Xr spamdb 8
+for further details.
+.Pp
+The file
+.Pa /etc/mail/spamd.alloweddomains
+can be used to specify a list of domainname suffixes, one per line, one of
+which must match each destination email address in the greylist.
+Any destination address which does not match one of the suffixes listed in
+.Pa spamd.alloweddomains
+will be trapped, exactly as if it were sent to a spamtrap address.
+Comment lines beginning with
+.Sq #
+and empty lines are ignored.
+.Pp
+For example, if
+.Pa spamd.alloweddomains
+contains:
+.Bd -literal -offset indent
+@humpingforjesus.com
+obtuse.com
+.Ed
+.Pp
+The following destination addresses
+.Em would not
+cause the sending host to be trapped:
+.Bd -literal -offset indent
+beardedclams@humpingforjesus.com
+beck@obtuse.com
+beck@snouts.obtuse.com
+.Ed
+.Pp
+However the following addresses
+.Em would
+cause the sending host to be trapped:
+.Bd -literal -offset indent
+peter@apostles.humpingforjesus.com
+bigbutts@bofh.ucs.ualberta.ca
+.Ed
+.Pp
+A low priority MX IP address may be specified with the
+.Fl M
+option.
+When
+.Nm
+has such an address specified, no host may enter new greylist
+tuples when connecting to this address; only existing entries
+may be updated.
+Any host attempting to make new deliveries to
+the low priority MX for which a tuple has not previously
+been seen will be trapped.
+.Pp
+Note that it is important to ensure that a host running
+.Nm
+with the low priority MX address active must see all the greylist
+changes for a higher priority MX host for the same domains.
+This is best done by the host itself receiving the connections to
+the higher priority MX on another IP address (which may be an IP alias).
+This will ensure that hosts are not trapped erroneously if the higher
+priority MX is unavailable.
+For example, on a host which is an existing MX record for a domain of
+value 10, a second IP address with MX of value 99 (a higher number, and
+therefore lower priority) would ensure that any RFC conformant client
+would attempt delivery to the IP address with the MX value of 10
+first, and should not attempt to deliver to the address with MX value 99.
+.Sh BLACKLIST-ONLY MODE
+When running in default mode, the
+.Xr pf.conf 5
+rules described above are sufficient.
+However when running in blacklist-only mode,
+a slightly modified
+.Xr pf.conf 5
+ruleset is required,
+redirecting any addresses found in the
+.Aq spamd
+table to
+.Nm .
+Any other addresses
+are passed to the real MTA.
+.Bd -literal -offset 4n
+table \*(Ltspamd\*(Gt persist
+pass on egress proto tcp from \*(Ltspamd\*(Gt to any \e
+ port smtp rdr-to 127.0.0.1 port spamd
+.Ed
+.Pp
+Addresses can be loaded into the
+.Em table ,
+like:
+.Bd -literal -offset 4n
+# pfctl -q -t spamd -T replace -f /usr/local/share/spammers
+.Ed
+.Pp
+.Xr spamd-setup 8
+can also be used to load addresses into the
+.Aq spamd
+table.
+It has the added benefit of being able to remove addresses from
+blacklists, and will connect to
+.Nm
+over a localhost socket, giving
+.Nm
+information about each source of blacklist addresses, as well as custom
+rejection messages for each blacklist source
+that can be used to let any real person whose mail
+is deferred by
+.Nm
+know why their address has been listed
+from sending mail.
+This is important as it allows legitimate mail
+senders to pressure spam sources into behaving properly so that they
+may be removed from the relevant blacklists.
+.Sh CONFIGURATION CONNECTIONS
+.Nm
+listens for configuration connections on the port identified by the
+named service
+.Dq spamd-cfg
+(see
+.Xr services 5 ) .
+The configuration socket listens only on the INADDR_LOOPBACK
+address.
+Configuration of spamd is done by connecting to the configuration
+socket, and sending blacklist information, one blacklist per line.
+Each blacklist consists of a name, a message to reject mail
+with, and addresses in CIDR format, all separated by semicolons (;):
+.Bd -literal -offset indent
+tag;"rejection message";aaa.bbb.ccc.ddd/mm;aaa.bbb.ccc.ddd/mm
+.Ed
+.Pp
+The rejection message must be inside double quotes.
+A \e" will produce a double quote in the output.
+\en will produce a newline.
+%A will expand to the connecting IP address in dotted quad format.
+%% may be used to produce a single % in the output.
+\e\e will produce a single \e.
+.Nm
+will reject mail by displaying all the messages from all blacklists in which
+a connecting address is matched.
+.Xr spamd-setup 8
+is normally used to configure this information.
+.Sh SYNCHRONISATION
+.Nm
+supports realtime synchronisation of spamd databases between
+a number of spamd
+daemons running on multiple machines,
+using the
+.Fl Y
+and
+.Fl y
+options.
+The databases are synchronised for greylisted and trapped entries;
+whitelisted entries and entries made manually using
+.Xr spamdb 8
+are not updated.
+.Pp
+The following example will accept incoming multicast and unicast
+synchronisation messages, and send outgoing multicast messages through
+the network interface
+.Ar em0 :
+.Bd -literal -offset indent
+# /usr/libexec/spamd -y em0 -Y em0
+.Ed
+.Pp
+The second example will increase the multicast TTL to a value of 2,
+add the unicast targets
+.Ar foo.somewhere.org
+and
+.Ar bar.somewhere.org ,
+and accept incoming unicast messages sent to
+.Ar example.somewhere.org
+only.
+.Bd -literal -offset indent
+# /usr/libexec/spamd -y example.somewhere.org -Y em0:2 \e
+ -Y foo.somewhere.org -Y bar.somewhere.org
+.Ed
+.Pp
+If the file
+.Pa /etc/mail/spamd.key
+exists,
+.Nm
+will calculate the message-digest fingerprint (checksum) for the file
+and use it as a shared key to authenticate the synchronisation messages.
+The file itself can contain any data.
+For example, to create a secure random key:
+.Bd -literal -offset indent
+# dd if=/dev/arandom of=/etc/mail/spamd.key bs=2048 count=1
+.Ed
+.Pp
+The file needs to be copied to all hosts
+sending or receiving synchronisation messages.
+.Sh FILES
+.Bl -tag -width "/etc/mail/spamd.alloweddomainsXX" -compact
+.It /etc/mail/spamd.alloweddomains
+Required suffixes for greytrapping.
+.It /etc/mail/spamd.conf
+Default configuration file.
+.It /etc/mail/spamd.key
+Authentication key for synchronisation messages.
+.It /var/db/spamd
+Greylisting database.
+.El
+.Sh SEE ALSO
+.Xr pf.conf 5 ,
+.Xr services 5 ,
+.Xr spamd.conf 5 ,
+.Xr syslog.conf 5 ,
+.Xr pfctl 8 ,
+.Xr spamd-setup 8 ,
+.Xr spamdb 8 ,
+.Xr spamlogd 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Ox 3.3 .
+.Pp
+Previous versions of
+.Nm
+required traps to be entered into the database including the enclosing
+\*(Lt\*(Gt characters;
+current versions expect only the email address without the enclosing
+\*(Lt\*(Gt characters.
+.Pp
+Blacklisted hosts are no longer stored in the
+.Aq spamd
+table when operating in default mode for performance reasons.
+.Sh BUGS
+.Nm
+currently uses the user
+.Dq _spamd
+outside a chroot jail when running in default mode, and requires
+the greylisting database in
+.Pa /var/db/spamd
+to be owned by the
+.Dq _spamd
+user.
+This is wrong and should change to a distinct user from the
+one used by the chrooted
+.Nm
+process.
--- /dev/null
+/* $OpenBSD: spamd.c,v 1.108 2010/01/14 00:44:12 beck Exp $ */
+
+/*
+ * Copyright (c) 2002-2007 Bob Beck. All rights reserved.
+ * Copyright (c) 2002 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <netdb.h>
+
+#include "sdl.h"
+#include "grey.h"
+#include "sync.h"
+
+extern int server_lookup(struct sockaddr *, struct sockaddr *,
+ struct sockaddr *);
+
+struct con {
+ int fd;
+ int state;
+ int laststate;
+ int af;
+ struct sockaddr_storage ss;
+ void *ia;
+ char addr[32];
+ char caddr[32];
+ char helo[MAX_MAIL], mail[MAX_MAIL], rcpt[MAX_MAIL];
+ struct sdlist **blacklists;
+
+ /*
+ * we will do stuttering by changing these to time_t's of
+ * now + n, and only advancing when the time is in the past/now
+ */
+ time_t r;
+ time_t w;
+ time_t s;
+
+ char ibuf[8192];
+ char *ip;
+ int il;
+ char rend[5]; /* any chars in here causes input termination */
+
+ char *obuf;
+ char *lists;
+ size_t osize;
+ char *op;
+ int ol;
+ int data_lines;
+ int data_body;
+ int stutter;
+ int badcmd;
+ int sr;
+} *con;
+
+void usage(void);
+char *grow_obuf(struct con *, int);
+int parse_configline(char *);
+void parse_configs(void);
+void do_config(void);
+int append_error_string (struct con *, size_t, char *, int, void *);
+void build_reply(struct con *);
+void doreply(struct con *);
+void setlog(char *, size_t, char *);
+void initcon(struct con *, int, struct sockaddr *);
+void closecon(struct con *);
+int match(const char *, const char *);
+void nextstate(struct con *);
+void handler(struct con *);
+void handlew(struct con *, int one);
+
+char hostname[MAXHOSTNAMELEN];
+struct syslog_data sdata = SYSLOG_DATA_INIT;
+char *nreply = "450";
+char *spamd = "spamd IP-based SPAM blocker";
+int greypipe[2];
+int trappipe[2];
+FILE *grey;
+FILE *trapcfg;
+time_t passtime = PASSTIME;
+time_t greyexp = GREYEXP;
+time_t whiteexp = WHITEEXP;
+time_t trapexp = TRAPEXP;
+struct passwd *pw;
+pid_t jail_pid = -1;
+u_short cfg_port;
+u_short sync_port;
+
+extern struct sdlist *blacklists;
+extern int pfdev;
+extern char *low_prio_mx_ip;
+
+int conffd = -1;
+int trapfd = -1;
+char *cb;
+size_t cbs, cbu;
+
+time_t t;
+
+#define MAXCON 800
+int maxfiles;
+int maxcon = MAXCON;
+int maxblack = MAXCON;
+int blackcount;
+int clients;
+int debug;
+int greylist = 1;
+int grey_stutter = 10;
+int verbose;
+int stutter = 1;
+int window;
+int syncrecv;
+int syncsend;
+#define MAXTIME 400
+
+void
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr,
+ "usage: %s [-45bdv] [-B maxblack] [-c maxcon] "
+ "[-G passtime:greyexp:whiteexp]\n"
+ "\t[-h hostname] [-l address] [-M address] [-n name] [-p port]\n"
+ "\t[-S secs] [-s secs] "
+ "[-w window] [-Y synctarget] [-y synclisten]\n",
+ __progname);
+
+ exit(1);
+}
+
+char *
+grow_obuf(struct con *cp, int off)
+{
+ char *tmp;
+
+ tmp = realloc(cp->obuf, cp->osize + 8192);
+ if (tmp == NULL) {
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ return (NULL);
+ } else {
+ cp->osize += 8192;
+ cp->obuf = tmp;
+ return (cp->obuf + off);
+ }
+}
+
+int
+parse_configline(char *line)
+{
+ char *cp, prev, *name, *msg;
+ static char **av = NULL;
+ static size_t ac = 0;
+ size_t au = 0;
+ int mdone = 0;
+
+ name = line;
+
+ for (cp = name; *cp && *cp != ';'; cp++)
+ ;
+ if (*cp != ';')
+ goto parse_error;
+ *cp++ = '\0';
+ if (!*cp) {
+ sdl_del(name);
+ return (0);
+ }
+ msg = cp;
+ if (*cp++ != '"')
+ goto parse_error;
+ prev = '\0';
+ for (; !mdone; cp++) {
+ switch (*cp) {
+ case '\\':
+ if (!prev)
+ prev = *cp;
+ else
+ prev = '\0';
+ break;
+ case '"':
+ if (prev != '\\') {
+ cp++;
+ if (*cp == ';') {
+ mdone = 1;
+ *cp = '\0';
+ } else
+ goto parse_error;
+ }
+ break;
+ case '\0':
+ goto parse_error;
+ default:
+ prev = '\0';
+ break;
+ }
+ }
+
+ do {
+ if (ac == au) {
+ char **tmp;
+
+ tmp = realloc(av, (ac + 2048) * sizeof(char *));
+ if (tmp == NULL) {
+ free(av);
+ av = NULL;
+ ac = 0;
+ return (-1);
+ }
+ av = tmp;
+ ac += 2048;
+ }
+ } while ((av[au++] = strsep(&cp, ";")) != NULL);
+
+ /* toss empty last entry to allow for trailing ; */
+ while (au > 0 && (av[au - 1] == NULL || av[au - 1][0] == '\0'))
+ au--;
+
+ if (au < 1)
+ goto parse_error;
+ else
+ sdl_add(name, msg, av, au);
+ return (0);
+
+parse_error:
+ if (debug > 0)
+ printf("bogus config line - need 'tag;message;a/m;a/m;a/m...'\n");
+ return (-1);
+}
+
+void
+parse_configs(void)
+{
+ char *start, *end;
+ int i;
+
+ if (cbu == cbs) {
+ char *tmp;
+
+ tmp = realloc(cb, cbs + 8192);
+ if (tmp == NULL) {
+ if (debug > 0)
+ perror("malloc()");
+ free(cb);
+ cb = NULL;
+ cbs = cbu = 0;
+ return;
+ }
+ cbs += 8192;
+ cb = tmp;
+ }
+ cb[cbu++] = '\0';
+
+ start = cb;
+ end = start;
+ for (i = 0; i < cbu; i++) {
+ if (*end == '\n') {
+ *end = '\0';
+ if (end > start + 1)
+ parse_configline(start);
+ start = ++end;
+ } else
+ ++end;
+ }
+ if (end > start + 1)
+ parse_configline(start);
+}
+
+void
+do_config(void)
+{
+ int n;
+
+ if (debug > 0)
+ printf("got configuration connection\n");
+
+ if (cbu == cbs) {
+ char *tmp;
+
+ tmp = realloc(cb, cbs + 8192);
+ if (tmp == NULL) {
+ if (debug > 0)
+ perror("malloc()");
+ free(cb);
+ cb = NULL;
+ cbs = 0;
+ goto configdone;
+ }
+ cbs += 8192;
+ cb = tmp;
+ }
+
+ n = read(conffd, cb + cbu, cbs - cbu);
+ if (debug > 0)
+ printf("read %d config bytes\n", n);
+ if (n == 0) {
+ parse_configs();
+ goto configdone;
+ } else if (n == -1) {
+ if (debug > 0)
+ perror("read()");
+ goto configdone;
+ } else
+ cbu += n;
+ return;
+
+configdone:
+ cbu = 0;
+ close(conffd);
+ conffd = -1;
+}
+
+int
+read_configline(FILE *config)
+{
+ char *buf;
+ size_t len;
+
+ if ((buf = fgetln(config, &len))) {
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ else
+ return (-1); /* all valid lines end in \n */
+ parse_configline(buf);
+ } else {
+ syslog_r(LOG_DEBUG, &sdata, "read_configline: fgetln (%m)");
+ return (-1);
+ }
+ return (0);
+}
+
+int
+append_error_string(struct con *cp, size_t off, char *fmt, int af, void *ia)
+{
+ char sav = '\0';
+ static int lastcont = 0;
+ char *c = cp->obuf + off;
+ char *s = fmt;
+ size_t len = cp->osize - off;
+ int i = 0;
+
+ if (off == 0)
+ lastcont = 0;
+
+ if (lastcont != 0)
+ cp->obuf[lastcont] = '-';
+ snprintf(c, len, "%s ", nreply);
+ i += strlen(c);
+ lastcont = off + i - 1;
+ if (*s == '"')
+ s++;
+ while (*s) {
+ /*
+ * Make sure we at minimum, have room to add a
+ * format code (4 bytes), and a v6 address(39 bytes)
+ * and a byte saved in sav.
+ */
+ if (i >= len - 46) {
+ c = grow_obuf(cp, off);
+ if (c == NULL)
+ return (-1);
+ len = cp->osize - (off + i);
+ }
+
+ if (c[i-1] == '\n') {
+ if (lastcont != 0)
+ cp->obuf[lastcont] = '-';
+ snprintf(c + i, len, "%s ", nreply);
+ i += strlen(c);
+ lastcont = off + i - 1;
+ }
+
+ switch (*s) {
+ case '\\':
+ case '%':
+ if (!sav)
+ sav = *s;
+ else {
+ c[i++] = sav;
+ sav = '\0';
+ c[i] = '\0';
+ }
+ break;
+ case '"':
+ case 'A':
+ case 'n':
+ if (*(s+1) == '\0') {
+ break;
+ }
+ if (sav == '\\' && *s == 'n') {
+ c[i++] = '\n';
+ sav = '\0';
+ c[i] = '\0';
+ break;
+ } else if (sav == '\\' && *s == '"') {
+ c[i++] = '"';
+ sav = '\0';
+ c[i] = '\0';
+ break;
+ } else if (sav == '%' && *s == 'A') {
+ inet_ntop(af, ia, c + i, (len - i));
+ i += strlen(c + i);
+ sav = '\0';
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (sav)
+ c[i++] = sav;
+ c[i++] = *s;
+ sav = '\0';
+ c[i] = '\0';
+ break;
+ }
+ s++;
+ }
+ return (i);
+}
+
+char *
+loglists(struct con *cp)
+{
+ static char matchlists[80];
+ struct sdlist **matches;
+ int s = sizeof(matchlists) - 4;
+
+ matchlists[0] = '\0';
+ matches = cp->blacklists;
+ if (matches == NULL)
+ return (NULL);
+ for (; *matches; matches++) {
+
+ /* don't report an insane amount of lists in the logs.
+ * just truncate and indicate with ...
+ */
+ if (strlen(matchlists) + strlen(matches[0]->tag) + 1 >= s)
+ strlcat(matchlists, " ...", sizeof(matchlists));
+ else {
+ strlcat(matchlists, " ", s);
+ strlcat(matchlists, matches[0]->tag, s);
+ }
+ }
+ return matchlists;
+}
+
+void
+build_reply(struct con *cp)
+{
+ struct sdlist **matches;
+ int off = 0;
+
+ matches = cp->blacklists;
+ if (matches == NULL)
+ goto nomatch;
+ for (; *matches; matches++) {
+ int used = 0;
+ char *c = cp->obuf + off;
+ int left = cp->osize - off;
+
+ used = append_error_string(cp, off, matches[0]->string,
+ cp->af, cp->ia);
+ if (used == -1)
+ goto bad;
+ off += used;
+ left -= used;
+ if (cp->obuf[off - 1] != '\n') {
+ if (left < 1) {
+ c = grow_obuf(cp, off);
+ if (c == NULL)
+ goto bad;
+ }
+ cp->obuf[off++] = '\n';
+ cp->obuf[off] = '\0';
+ }
+ }
+ return;
+nomatch:
+ /* No match. give generic reply */
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ if (cp->blacklists != NULL)
+ asprintf(&cp->obuf,
+ "%s-Sorry %s\n"
+ "%s-You are trying to send mail from an address "
+ "listed by one\n"
+ "%s or more IP-based registries as being a SPAM source.\n",
+ nreply, cp->addr, nreply, nreply);
+ else
+ asprintf(&cp->obuf,
+ "451 Temporary failure, please try again later.\r\n");
+ if (cp->obuf != NULL)
+ cp->osize = strlen(cp->obuf) + 1;
+ else
+ cp->osize = 0;
+ return;
+bad:
+ if (cp->obuf != NULL) {
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ }
+}
+
+void
+doreply(struct con *cp)
+{
+ build_reply(cp);
+}
+
+void
+setlog(char *p, size_t len, char *f)
+{
+ char *s;
+
+ s = strsep(&f, ":");
+ if (!f)
+ return;
+ while (*f == ' ' || *f == '\t')
+ f++;
+ s = strsep(&f, " \t");
+ if (s == NULL)
+ return;
+ strlcpy(p, s, len);
+ s = strsep(&p, " \t\n\r");
+ if (s == NULL)
+ return;
+ s = strsep(&p, " \t\n\r");
+ if (s)
+ *s = '\0';
+}
+
+/*
+ * Get address client connected to, by doing a DIOCNATLOOK call.
+ * Uses server_lookup code from ftp-proxy.
+ */
+void
+getcaddr(struct con *cp) {
+ struct sockaddr_storage spamd_end;
+ struct sockaddr *sep = (struct sockaddr *) &spamd_end;
+ struct sockaddr_storage original_destination;
+ struct sockaddr *odp = (struct sockaddr *) &original_destination;
+ socklen_t len = sizeof(struct sockaddr_storage);
+ int error;
+
+ cp->caddr[0] = '\0';
+ if (getsockname(cp->fd, sep, &len) == -1)
+ return;
+ if (server_lookup((struct sockaddr *)&cp->ss, sep, odp) != 0)
+ return;
+ error = getnameinfo(odp, odp->sa_len, cp->caddr, sizeof(cp->caddr),
+ NULL, 0, NI_NUMERICHOST);
+ if (error)
+ cp->caddr[0] = '\0';
+}
+
+
+void
+gethelo(char *p, size_t len, char *f)
+{
+ char *s;
+
+ /* skip HELO/EHLO */
+ f+=4;
+ /* skip whitespace */
+ while (*f == ' ' || *f == '\t')
+ f++;
+ s = strsep(&f, " \t");
+ if (s == NULL)
+ return;
+ strlcpy(p, s, len);
+ s = strsep(&p, " \t\n\r");
+ if (s == NULL)
+ return;
+ s = strsep(&p, " \t\n\r");
+ if (s)
+ *s = '\0';
+}
+
+void
+initcon(struct con *cp, int fd, struct sockaddr *sa)
+{
+ socklen_t len = sa->sa_len;
+ time_t tt;
+ char *tmp;
+ int error;
+
+ time(&tt);
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ free(cp->blacklists);
+ cp->blacklists = NULL;
+ free(cp->lists);
+ cp->lists = NULL;
+ bzero(cp, sizeof(struct con));
+ if (grow_obuf(cp, 0) == NULL)
+ err(1, "malloc");
+ cp->fd = fd;
+ if (len > sizeof(cp->ss))
+ errx(1, "sockaddr size");
+ if (sa->sa_family != AF_INET)
+ errx(1, "not supported yet");
+ memcpy(&cp->ss, sa, sa->sa_len);
+ cp->af = sa->sa_family;
+ cp->ia = &((struct sockaddr_in *)&cp->ss)->sin_addr;
+ cp->blacklists = sdl_lookup(blacklists, cp->af, cp->ia);
+ cp->stutter = (greylist && !grey_stutter && cp->blacklists == NULL) ?
+ 0 : stutter;
+ error = getnameinfo(sa, sa->sa_len, cp->addr, sizeof(cp->addr), NULL, 0,
+ NI_NUMERICHOST);
+ if (error)
+ errx(1, "%s", gai_strerror(error));
+ tmp = strdup(ctime(&t));
+ if (tmp == NULL)
+ err(1, "malloc");
+ tmp[strlen(tmp) - 1] = '\0'; /* nuke newline */
+ snprintf(cp->obuf, cp->osize, "220 %s ESMTP %s; %s\r\n",
+ hostname, spamd, tmp);
+ free(tmp);
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = tt + cp->stutter;
+ cp->s = tt;
+ strlcpy(cp->rend, "\n", sizeof cp->rend);
+ clients++;
+ if (cp->blacklists != NULL) {
+ blackcount++;
+ if (greylist && blackcount > maxblack)
+ cp->stutter = 0;
+ cp->lists = strdup(loglists(cp));
+ }
+ else
+ cp->lists = NULL;
+}
+
+void
+closecon(struct con *cp)
+{
+ time_t tt;
+
+ time(&tt);
+ syslog_r(LOG_INFO, &sdata, "%s: disconnected after %ld seconds.%s%s",
+ cp->addr, (long)(tt - cp->s),
+ ((cp->lists == NULL) ? "" : " lists:"),
+ ((cp->lists == NULL) ? "": cp->lists));
+ if (debug > 0)
+ printf("%s connected for %ld seconds.\n", cp->addr,
+ (long)(tt - cp->s));
+ if (cp->lists != NULL) {
+ free(cp->lists);
+ cp->lists = NULL;
+ }
+ if (cp->blacklists != NULL) {
+ blackcount--;
+ free(cp->blacklists);
+ cp->blacklists = NULL;
+ }
+ if (cp->obuf != NULL) {
+ free(cp->obuf);
+ cp->obuf = NULL;
+ cp->osize = 0;
+ }
+ close(cp->fd);
+ clients--;
+ cp->fd = -1;
+}
+
+int
+match(const char *s1, const char *s2)
+{
+ return (strncasecmp(s1, s2, strlen(s2)) == 0);
+}
+
+void
+nextstate(struct con *cp)
+{
+ if (match(cp->ibuf, "QUIT") && cp->state < 99) {
+ snprintf(cp->obuf, cp->osize, "221 %s\r\n", hostname);
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = t + cp->stutter;
+ cp->laststate = cp->state;
+ cp->state = 99;
+ return;
+ }
+
+ if (match(cp->ibuf, "RSET") && cp->state > 2 && cp->state < 50) {
+ snprintf(cp->obuf, cp->osize,
+ "250 Ok to start over.\r\n");
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = t + cp->stutter;
+ cp->laststate = cp->state;
+ cp->state = 2;
+ return;
+ }
+ switch (cp->state) {
+ case 0:
+ /* banner sent; wait for input */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 1;
+ cp->r = t;
+ break;
+ case 1:
+ /* received input: parse, and select next state */
+ if (match(cp->ibuf, "HELO") ||
+ match(cp->ibuf, "EHLO")) {
+ int nextstate = 2;
+ cp->helo[0] = '\0';
+ gethelo(cp->helo, sizeof cp->helo, cp->ibuf);
+ if (cp->helo[0] == '\0') {
+ nextstate = 0;
+ snprintf(cp->obuf, cp->osize,
+ "501 helo requires domain name.\r\n");
+ } else {
+ snprintf(cp->obuf, cp->osize,
+ "250 Hello, spam sender. "
+ "Pleased to be wasting your time.\r\n");
+ }
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->laststate = cp->state;
+ cp->state = nextstate;
+ cp->w = t + cp->stutter;
+ break;
+ }
+ goto mail;
+ case 2:
+ /* sent 250 Hello, wait for input */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 3;
+ cp->r = t;
+ break;
+ case 3:
+ mail:
+ if (match(cp->ibuf, "MAIL")) {
+ setlog(cp->mail, sizeof cp->mail, cp->ibuf);
+ snprintf(cp->obuf, cp->osize,
+ "250 You are about to try to deliver spam. "
+ "Your time will be spent, for nothing.\r\n");
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->laststate = cp->state;
+ cp->state = 4;
+ cp->w = t + cp->stutter;
+ break;
+ }
+ goto rcpt;
+ case 4:
+ /* sent 250 Sender ok */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 5;
+ cp->r = t;
+ break;
+ case 5:
+ rcpt:
+ if (match(cp->ibuf, "RCPT")) {
+ setlog(cp->rcpt, sizeof(cp->rcpt), cp->ibuf);
+ snprintf(cp->obuf, cp->osize,
+ "250 This is hurting you more than it is "
+ "hurting me.\r\n");
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->laststate = cp->state;
+ cp->state = 6;
+ cp->w = t + cp->stutter;
+ if (cp->mail[0] && cp->rcpt[0]) {
+ if (verbose)
+ syslog_r(LOG_INFO, &sdata,
+ "(%s) %s: %s -> %s",
+ cp->blacklists ? "BLACK" : "GREY",
+ cp->addr, cp->mail,
+ cp->rcpt);
+ if (debug)
+ fprintf(stderr, "(%s) %s: %s -> %s\n",
+ cp->blacklists ? "BLACK" : "GREY",
+ cp->addr, cp->mail, cp->rcpt);
+ if (greylist && cp->blacklists == NULL) {
+ /* send this info to the greylister */
+ getcaddr(cp);
+ fprintf(grey,
+ "CO:%s\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
+ cp->caddr, cp->helo, cp->addr,
+ cp->mail, cp->rcpt);
+ fflush(grey);
+ }
+ }
+ break;
+ }
+ goto spam;
+ case 6:
+ /* sent 250 blah */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 5;
+ cp->r = t;
+ break;
+
+ case 50:
+ spam:
+ if (match(cp->ibuf, "DATA")) {
+ snprintf(cp->obuf, cp->osize,
+ "354 Enter spam, end with \".\" on a line by "
+ "itself\r\n");
+ cp->state = 60;
+ if (window && setsockopt(cp->fd, SOL_SOCKET, SO_RCVBUF,
+ &window, sizeof(window)) == -1) {
+ syslog_r(LOG_DEBUG, &sdata,"setsockopt: %m");
+ /* don't fail if this doesn't work. */
+ }
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = t + cp->stutter;
+ if (greylist && cp->blacklists == NULL) {
+ cp->laststate = cp->state;
+ cp->state = 98;
+ goto done;
+ }
+ } else {
+ if (match(cp->ibuf, "NOOP"))
+ snprintf(cp->obuf, cp->osize,
+ "250 2.0.0 OK I did nothing\r\n");
+ else {
+ snprintf(cp->obuf, cp->osize,
+ "500 5.5.1 Command unrecognized\r\n");
+ cp->badcmd++;
+ if (cp->badcmd > 20) {
+ cp->laststate = cp->state;
+ cp->state = 98;
+ goto done;
+ }
+ }
+ cp->state = cp->laststate;
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = t + cp->stutter;
+ }
+ break;
+ case 60:
+ /* sent 354 blah */
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->laststate = cp->state;
+ cp->state = 70;
+ cp->r = t;
+ break;
+ case 70: {
+ char *p, *q;
+
+ for (p = q = cp->ibuf; q <= cp->ip; ++q)
+ if (*q == '\n' || q == cp->ip) {
+ *q = 0;
+ if (q > p && q[-1] == '\r')
+ q[-1] = 0;
+ if (!strcmp(p, ".") ||
+ (cp->data_body && ++cp->data_lines >= 10)) {
+ cp->laststate = cp->state;
+ cp->state = 98;
+ goto done;
+ }
+ if (!cp->data_body && !*p)
+ cp->data_body = 1;
+ if (verbose && cp->data_body && *p)
+ syslog_r(LOG_DEBUG, &sdata, "%s: "
+ "Body: %s", cp->addr, p);
+ else if (verbose && (match(p, "FROM:") ||
+ match(p, "TO:") || match(p, "SUBJECT:")))
+ syslog_r(LOG_INFO, &sdata, "%s: %s",
+ cp->addr, p);
+ p = ++q;
+ }
+ cp->ip = cp->ibuf;
+ cp->il = sizeof(cp->ibuf) - 1;
+ cp->r = t;
+ break;
+ }
+ case 98:
+ done:
+ doreply(cp);
+ cp->op = cp->obuf;
+ cp->ol = strlen(cp->op);
+ cp->w = t + cp->stutter;
+ cp->laststate = cp->state;
+ cp->state = 99;
+ break;
+ case 99:
+ closecon(cp);
+ break;
+ default:
+ errx(1, "illegal state %d", cp->state);
+ break;
+ }
+}
+
+void
+handler(struct con *cp)
+{
+ int end = 0;
+ int n;
+
+ if (cp->r) {
+ n = read(cp->fd, cp->ip, cp->il);
+ if (n == 0)
+ closecon(cp);
+ else if (n == -1) {
+ if (debug > 0)
+ perror("read()");
+ closecon(cp);
+ } else {
+ cp->ip[n] = '\0';
+ if (cp->rend[0])
+ if (strpbrk(cp->ip, cp->rend))
+ end = 1;
+ cp->ip += n;
+ cp->il -= n;
+ }
+ }
+ if (end || cp->il == 0) {
+ while (cp->ip > cp->ibuf &&
+ (cp->ip[-1] == '\r' || cp->ip[-1] == '\n'))
+ cp->ip--;
+ *cp->ip = '\0';
+ cp->r = 0;
+ nextstate(cp);
+ }
+}
+
+void
+handlew(struct con *cp, int one)
+{
+ int n;
+
+ /* kill stutter on greylisted connections after initial delay */
+ if (cp->stutter && greylist && cp->blacklists == NULL &&
+ (t - cp->s) > grey_stutter)
+ cp->stutter=0;
+
+ if (cp->w) {
+ if (*cp->op == '\n' && !cp->sr) {
+ /* insert \r before \n */
+ n = write(cp->fd, "\r", 1);
+ if (n == 0) {
+ closecon(cp);
+ goto handled;
+ } else if (n == -1) {
+ if (debug > 0 && errno != EPIPE)
+ perror("write()");
+ closecon(cp);
+ goto handled;
+ }
+ }
+ if (*cp->op == '\r')
+ cp->sr = 1;
+ else
+ cp->sr = 0;
+ n = write(cp->fd, cp->op, (one && cp->stutter) ? 1 : cp->ol);
+ if (n == 0)
+ closecon(cp);
+ else if (n == -1) {
+ if (debug > 0 && errno != EPIPE)
+ perror("write()");
+ closecon(cp);
+ } else {
+ cp->op += n;
+ cp->ol -= n;
+ }
+ }
+handled:
+ cp->w = t + cp->stutter;
+ if (cp->ol == 0) {
+ cp->w = 0;
+ nextstate(cp);
+ }
+}
+
+static int
+get_maxfiles(void)
+{
+ int mib[2], maxfiles;
+ size_t len;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_MAXFILES;
+ len = sizeof(maxfiles);
+ if (sysctl(mib, 2, &maxfiles, &len, NULL, 0) == -1)
+ return(MAXCON);
+ if ((maxfiles - 200) < 10)
+ errx(1, "kern.maxfiles is only %d, can not continue\n",
+ maxfiles);
+ else
+ return(maxfiles - 200);
+}
+
+int
+main(int argc, char *argv[])
+{
+ fd_set *fdsr = NULL, *fdsw = NULL;
+ struct sockaddr_in sin;
+ struct sockaddr_in lin;
+ int ch, s, s2, conflisten = 0, syncfd = 0, i, omax = 0, one = 1;
+ socklen_t sinlen;
+ u_short port;
+ struct servent *ent;
+ struct rlimit rlp;
+ char *bind_address = NULL;
+ const char *errstr;
+ char *sync_iface = NULL;
+ char *sync_baddr = NULL;
+
+ tzset();
+ openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
+
+ if ((ent = getservbyname("spamd", "tcp")) == NULL)
+ errx(1, "Can't find service \"spamd\" in /etc/services");
+ port = ntohs(ent->s_port);
+ if ((ent = getservbyname("spamd-cfg", "tcp")) == NULL)
+ errx(1, "Can't find service \"spamd-cfg\" in /etc/services");
+ cfg_port = ntohs(ent->s_port);
+ if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
+ errx(1, "Can't find service \"spamd-sync\" in /etc/services");
+ sync_port = ntohs(ent->s_port);
+
+ if (gethostname(hostname, sizeof hostname) == -1)
+ err(1, "gethostname");
+ maxfiles = get_maxfiles();
+ if (maxcon > maxfiles)
+ maxcon = maxfiles;
+ if (maxblack > maxfiles)
+ maxblack = maxfiles;
+ while ((ch =
+ getopt(argc, argv, "45l:c:B:p:bdG:h:s:S:M:n:vw:y:Y:")) != -1) {
+ switch (ch) {
+ case '4':
+ nreply = "450";
+ break;
+ case '5':
+ nreply = "550";
+ break;
+ case 'l':
+ bind_address = optarg;
+ break;
+ case 'B':
+ i = atoi(optarg);
+ maxblack = i;
+ break;
+ case 'c':
+ i = atoi(optarg);
+ if (i > maxfiles) {
+ fprintf(stderr,
+ "%d > system max of %d connections\n",
+ i, maxfiles);
+ usage();
+ }
+ maxcon = i;
+ break;
+ case 'p':
+ i = atoi(optarg);
+ port = i;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'b':
+ greylist = 0;
+ break;
+ case 'G':
+ if (sscanf(optarg, "%d:%d:%d", &passtime, &greyexp,
+ &whiteexp) != 3)
+ usage();
+ /* convert to seconds from minutes */
+ passtime *= 60;
+ /* convert to seconds from hours */
+ whiteexp *= (60 * 60);
+ /* convert to seconds from hours */
+ greyexp *= (60 * 60);
+ break;
+ case 'h':
+ bzero(&hostname, sizeof(hostname));
+ if (strlcpy(hostname, optarg, sizeof(hostname)) >=
+ sizeof(hostname))
+ errx(1, "-h arg too long");
+ break;
+ case 's':
+ i = strtonum(optarg, 0, 10, &errstr);
+ if (errstr)
+ usage();
+ stutter = i;
+ break;
+ case 'S':
+ i = strtonum(optarg, 0, 90, &errstr);
+ if (errstr)
+ usage();
+ grey_stutter = i;
+ break;
+ case 'M':
+ low_prio_mx_ip = optarg;
+ break;
+ case 'n':
+ spamd = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ window = atoi(optarg);
+ if (window <= 0)
+ usage();
+ break;
+ case 'Y':
+ if (sync_addhost(optarg, sync_port) != 0)
+ sync_iface = optarg;
+ syncsend++;
+ break;
+ case 'y':
+ sync_baddr = optarg;
+ syncrecv++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ setproctitle("[priv]%s%s",
+ greylist ? " (greylist)" : "",
+ (syncrecv || syncsend) ? " (sync)" : "");
+
+ if (!greylist)
+ maxblack = maxcon;
+ else if (maxblack > maxcon)
+ usage();
+
+ rlp.rlim_cur = rlp.rlim_max = maxcon + 15;
+ if (setrlimit(RLIMIT_NOFILE, &rlp) == -1)
+ err(1, "setrlimit");
+
+ con = calloc(maxcon, sizeof(*con));
+ if (con == NULL)
+ err(1, "calloc");
+
+ con->obuf = malloc(8192);
+
+ if (con->obuf == NULL)
+ err(1, "malloc");
+ con->osize = 8192;
+
+ for (i = 0; i < maxcon; i++)
+ con[i].fd = -1;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s == -1)
+ err(1, "socket");
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) == -1)
+ return (-1);
+
+ conflisten = socket(AF_INET, SOCK_STREAM, 0);
+ if (conflisten == -1)
+ err(1, "socket");
+
+ if (setsockopt(conflisten, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) == -1)
+ return (-1);
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_len = sizeof(sin);
+ if (bind_address) {
+ if (inet_pton(AF_INET, bind_address, &sin.sin_addr) != 1)
+ err(1, "inet_pton");
+ } else
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+
+ if (bind(s, (struct sockaddr *)&sin, sizeof sin) == -1)
+ err(1, "bind");
+
+ memset(&lin, 0, sizeof sin);
+ lin.sin_len = sizeof(sin);
+ lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ lin.sin_family = AF_INET;
+ lin.sin_port = htons(cfg_port);
+
+ if (bind(conflisten, (struct sockaddr *)&lin, sizeof lin) == -1)
+ err(1, "bind local");
+
+ if (syncsend || syncrecv) {
+ syncfd = sync_init(sync_iface, sync_baddr, sync_port);
+ if (syncfd == -1)
+ err(1, "sync init");
+ }
+
+ if ((pw = getpwnam("_spamd")) == NULL)
+ errx(1, "no such user _spamd");
+
+ if (debug == 0) {
+ if (daemon(1, 1) == -1)
+ err(1, "daemon");
+ }
+
+ if (greylist) {
+ pfdev = open("/dev/pf", O_RDWR);
+ if (pfdev == -1) {
+ syslog_r(LOG_ERR, &sdata, "open /dev/pf: %m");
+ exit(1);
+ }
+
+ maxblack = (maxblack >= maxcon) ? maxcon - 100 : maxblack;
+ if (maxblack < 0)
+ maxblack = 0;
+
+ /* open pipe to talk to greylister */
+ if (pipe(greypipe) == -1) {
+ syslog(LOG_ERR, "pipe (%m)");
+ exit(1);
+ }
+ /* open pipe to recieve spamtrap configs */
+ if (pipe(trappipe) == -1) {
+ syslog(LOG_ERR, "pipe (%m)");
+ exit(1);
+ }
+ jail_pid = fork();
+ switch (jail_pid) {
+ case -1:
+ syslog(LOG_ERR, "fork (%m)");
+ exit(1);
+ case 0:
+ /* child - continue */
+ signal(SIGPIPE, SIG_IGN);
+ grey = fdopen(greypipe[1], "w");
+ if (grey == NULL) {
+ syslog(LOG_ERR, "fdopen (%m)");
+ _exit(1);
+ }
+ close(greypipe[0]);
+ trapfd = trappipe[0];
+ trapcfg = fdopen(trappipe[0], "r");
+ if (trapcfg == NULL) {
+ syslog(LOG_ERR, "fdopen (%m)");
+ _exit(1);
+ }
+ close(trappipe[1]);
+ goto jail;
+ }
+ /* parent - run greylister */
+ grey = fdopen(greypipe[0], "r");
+ if (grey == NULL) {
+ syslog(LOG_ERR, "fdopen (%m)");
+ exit(1);
+ }
+ close(greypipe[1]);
+ trapcfg = fdopen(trappipe[1], "w");
+ if (trapcfg == NULL) {
+ syslog(LOG_ERR, "fdopen (%m)");
+ exit(1);
+ }
+ close(trappipe[0]);
+ return (greywatcher());
+ /* NOTREACHED */
+ }
+
+jail:
+ if (chroot("/var/empty") == -1 || chdir("/") == -1) {
+ syslog(LOG_ERR, "cannot chdir to /var/empty.");
+ exit(1);
+ }
+
+ if (pw)
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ err(1, "failed to drop privs");
+
+ if (listen(s, 10) == -1)
+ err(1, "listen");
+
+ if (listen(conflisten, 10) == -1)
+ err(1, "listen");
+
+ if (debug != 0)
+ printf("listening for incoming connections.\n");
+ syslog_r(LOG_WARNING, &sdata, "listening for incoming connections.");
+
+ while (1) {
+ struct timeval tv, *tvp;
+ int max, n;
+ int writers;
+
+ max = MAX(s, conflisten);
+ if (syncrecv)
+ max = MAX(max, syncfd);
+ max = MAX(max, conffd);
+ max = MAX(max, trapfd);
+
+ time(&t);
+ for (i = 0; i < maxcon; i++)
+ if (con[i].fd != -1)
+ max = MAX(max, con[i].fd);
+
+ if (max > omax) {
+ free(fdsr);
+ fdsr = NULL;
+ free(fdsw);
+ fdsw = NULL;
+ fdsr = (fd_set *)calloc(howmany(max+1, NFDBITS),
+ sizeof(fd_mask));
+ if (fdsr == NULL)
+ err(1, "calloc");
+ fdsw = (fd_set *)calloc(howmany(max+1, NFDBITS),
+ sizeof(fd_mask));
+ if (fdsw == NULL)
+ err(1, "calloc");
+ omax = max;
+ } else {
+ memset(fdsr, 0, howmany(max+1, NFDBITS) *
+ sizeof(fd_mask));
+ memset(fdsw, 0, howmany(max+1, NFDBITS) *
+ sizeof(fd_mask));
+ }
+
+ writers = 0;
+ for (i = 0; i < maxcon; i++) {
+ if (con[i].fd != -1 && con[i].r) {
+ if (con[i].r + MAXTIME <= t) {
+ closecon(&con[i]);
+ continue;
+ }
+ FD_SET(con[i].fd, fdsr);
+ }
+ if (con[i].fd != -1 && con[i].w) {
+ if (con[i].w + MAXTIME <= t) {
+ closecon(&con[i]);
+ continue;
+ }
+ if (con[i].w <= t)
+ FD_SET(con[i].fd, fdsw);
+ writers = 1;
+ }
+ }
+ FD_SET(s, fdsr);
+
+ /* only one active config conn at a time */
+ if (conffd == -1)
+ FD_SET(conflisten, fdsr);
+ else
+ FD_SET(conffd, fdsr);
+ if (trapfd != -1)
+ FD_SET(trapfd, fdsr);
+ if (syncrecv)
+ FD_SET(syncfd, fdsr);
+
+ if (writers == 0) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ n = select(max+1, fdsr, fdsw, NULL, tvp);
+ if (n == -1) {
+ if (errno != EINTR)
+ err(1, "select");
+ continue;
+ }
+ if (n == 0)
+ continue;
+
+ for (i = 0; i < maxcon; i++) {
+ if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsr))
+ handler(&con[i]);
+ if (con[i].fd != -1 && FD_ISSET(con[i].fd, fdsw))
+ handlew(&con[i], clients + 5 < maxcon);
+ }
+ if (FD_ISSET(s, fdsr)) {
+ sinlen = sizeof(sin);
+ s2 = accept(s, (struct sockaddr *)&sin, &sinlen);
+ if (s2 == -1)
+ /* accept failed, they may try again */
+ continue;
+ for (i = 0; i < maxcon; i++)
+ if (con[i].fd == -1)
+ break;
+ if (i == maxcon)
+ close(s2);
+ else {
+ initcon(&con[i], s2, (struct sockaddr *)&sin);
+ syslog_r(LOG_INFO, &sdata,
+ "%s: connected (%d/%d)%s%s",
+ con[i].addr, clients, blackcount,
+ ((con[i].lists == NULL) ? "" :
+ ", lists:"),
+ ((con[i].lists == NULL) ? "":
+ con[i].lists));
+ }
+ }
+ if (FD_ISSET(conflisten, fdsr)) {
+ sinlen = sizeof(lin);
+ conffd = accept(conflisten, (struct sockaddr *)&lin,
+ &sinlen);
+ if (conffd == -1)
+ /* accept failed, they may try again */
+ continue;
+ else if (ntohs(lin.sin_port) >= IPPORT_RESERVED) {
+ close(conffd);
+ conffd = -1;
+ }
+ }
+ if (conffd != -1 && FD_ISSET(conffd, fdsr))
+ do_config();
+ if (trapfd != -1 && FD_ISSET(trapfd, fdsr))
+ read_configline(trapcfg);
+ if (syncrecv && FD_ISSET(syncfd, fdsr))
+ sync_recv();
+ }
+ exit(1);
+}
--- /dev/null
+/* $OpenBSD: sync.c,v 1.8 2009/04/20 17:42:21 beck Exp $ */
+
+/*
+ * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/stdint.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sha1.h>
+#include <syslog.h>
+
+#include <netdb.h>
+
+#include <openssl/hmac.h>
+
+#include "sdl.h"
+#include "grey.h"
+#include "sync.h"
+
+extern struct syslog_data sdata;
+extern int debug;
+extern FILE *grey;
+extern int greylist;
+
+u_int32_t sync_counter;
+int syncfd;
+int sendmcast;
+struct sockaddr_in sync_in;
+struct sockaddr_in sync_out;
+static char *sync_key;
+
+struct sync_host {
+ LIST_ENTRY(sync_host) h_entry;
+
+ char *h_name;
+ struct sockaddr_in sh_addr;
+};
+LIST_HEAD(synchosts, sync_host) sync_hosts = LIST_HEAD_INITIALIZER(sync_hosts);
+
+void sync_send(struct iovec *, int);
+void sync_addr(time_t, time_t, char *, u_int16_t);
+
+int
+sync_addhost(const char *name, u_short port)
+{
+ struct addrinfo hints, *res, *res0;
+ struct sync_host *shost;
+ struct sockaddr_in *addr = NULL;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(name, NULL, &hints, &res0) != 0)
+ return (EINVAL);
+ for (res = res0; res != NULL; res = res->ai_next) {
+ if (addr == NULL && res->ai_family == AF_INET) {
+ addr = (struct sockaddr_in *)res->ai_addr;
+ break;
+ }
+ }
+ if (addr == NULL) {
+ freeaddrinfo(res0);
+ return (EINVAL);
+ }
+ if ((shost = (struct sync_host *)
+ calloc(1, sizeof(struct sync_host))) == NULL) {
+ freeaddrinfo(res0);
+ return (ENOMEM);
+ }
+ if ((shost->h_name = strdup(name)) == NULL) {
+ free(shost);
+ freeaddrinfo(res0);
+ return (ENOMEM);
+ }
+
+ shost->sh_addr.sin_family = AF_INET;
+ shost->sh_addr.sin_port = htons(port);
+ shost->sh_addr.sin_addr.s_addr = addr->sin_addr.s_addr;
+ freeaddrinfo(res0);
+
+ LIST_INSERT_HEAD(&sync_hosts, shost, h_entry);
+
+ if (debug)
+ fprintf(stderr, "added spam sync host %s "
+ "(address %s, port %d)\n", shost->h_name,
+ inet_ntoa(shost->sh_addr.sin_addr), port);
+
+ return (0);
+}
+
+int
+sync_init(const char *iface, const char *baddr, u_short port)
+{
+ int one = 1;
+ u_int8_t ttl;
+ struct ifreq ifr;
+ struct ip_mreq mreq;
+ struct sockaddr_in *addr;
+ char ifnam[IFNAMSIZ], *ttlstr;
+ const char *errstr;
+ struct in_addr ina;
+
+ if (iface != NULL)
+ sendmcast++;
+
+ bzero(&ina, sizeof(ina));
+ if (baddr != NULL) {
+ if (inet_pton(AF_INET, baddr, &ina) != 1) {
+ ina.s_addr = htonl(INADDR_ANY);
+ if (iface == NULL)
+ iface = baddr;
+ else if (iface != NULL && strcmp(baddr, iface) != 0) {
+ fprintf(stderr, "multicast interface does "
+ "not match");
+ return (-1);
+ }
+ }
+ }
+
+ sync_key = SHA1File(SPAM_SYNC_KEY, NULL);
+ if (sync_key == NULL) {
+ if (errno != ENOENT) {
+ fprintf(stderr, "failed to open sync key: %s\n",
+ strerror(errno));
+ return (-1);
+ }
+ /* Use empty key by default */
+ sync_key = "";
+ }
+
+ syncfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (syncfd == -1)
+ return (-1);
+
+ if (setsockopt(syncfd, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(one)) == -1)
+ goto fail;
+
+ bzero(&sync_out, sizeof(sync_out));
+ sync_out.sin_family = AF_INET;
+ sync_out.sin_len = sizeof(sync_out);
+ sync_out.sin_addr.s_addr = ina.s_addr;
+ if (baddr == NULL && iface == NULL)
+ sync_out.sin_port = 0;
+ else
+ sync_out.sin_port = htons(port);
+
+ if (bind(syncfd, (struct sockaddr *)&sync_out, sizeof(sync_out)) == -1)
+ goto fail;
+
+ /* Don't use multicast messages */
+ if (iface == NULL)
+ return (syncfd);
+
+ strlcpy(ifnam, iface, sizeof(ifnam));
+ ttl = SPAM_SYNC_MCASTTTL;
+ if ((ttlstr = strchr(ifnam, ':')) != NULL) {
+ *ttlstr++ = '\0';
+ ttl = (u_int8_t)strtonum(ttlstr, 1, UINT8_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr, "invalid multicast ttl %s: %s",
+ ttlstr, errstr);
+ goto fail;
+ }
+ }
+
+ bzero(&ifr, sizeof(ifr));
+ strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
+ if (ioctl(syncfd, SIOCGIFADDR, &ifr) == -1)
+ goto fail;
+
+ bzero(&sync_in, sizeof(sync_in));
+ addr = (struct sockaddr_in *)&ifr.ifr_addr;
+ sync_in.sin_family = AF_INET;
+ sync_in.sin_len = sizeof(sync_in);
+ sync_in.sin_addr.s_addr = addr->sin_addr.s_addr;
+ sync_in.sin_port = htons(port);
+
+ bzero(&mreq, sizeof(mreq));
+ sync_out.sin_addr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
+ mreq.imr_multiaddr.s_addr = inet_addr(SPAM_SYNC_MCASTADDR);
+ mreq.imr_interface.s_addr = sync_in.sin_addr.s_addr;
+
+ if (setsockopt(syncfd, IPPROTO_IP,
+ IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) {
+ fprintf(stderr, "failed to add multicast membership to %s: %s",
+ SPAM_SYNC_MCASTADDR, strerror(errno));
+ goto fail;
+ }
+ if (setsockopt(syncfd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl)) < 0) {
+ fprintf(stderr, "failed to set multicast ttl to "
+ "%u: %s\n", ttl, strerror(errno));
+ setsockopt(syncfd, IPPROTO_IP,
+ IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+ goto fail;
+ }
+
+ if (debug)
+ printf("using multicast spam sync %smode "
+ "(ttl %u, group %s, port %d)\n",
+ sendmcast ? "" : "receive ",
+ ttl, inet_ntoa(sync_out.sin_addr), port);
+
+ return (syncfd);
+
+ fail:
+ close(syncfd);
+ return (-1);
+}
+
+void
+sync_recv(void)
+{
+ struct spam_synchdr *hdr;
+ struct sockaddr_in addr;
+ struct spam_synctlv_hdr *tlv;
+ struct spam_synctlv_grey *sg;
+ struct spam_synctlv_addr *sd;
+ u_int8_t buf[SPAM_SYNC_MAXSIZE];
+ u_int8_t hmac[2][SPAM_SYNC_HMAC_LEN];
+ struct in_addr ip;
+ char *from, *to, *helo;
+ u_int8_t *p;
+ socklen_t addr_len;
+ ssize_t len;
+ u_int hmac_len;
+ time_t expire;
+
+ bzero(&addr, sizeof(addr));
+ bzero(buf, sizeof(buf));
+
+ addr_len = sizeof(addr);
+ if ((len = recvfrom(syncfd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&addr, &addr_len)) < 1)
+ return;
+ if (addr.sin_addr.s_addr != htonl(INADDR_ANY) &&
+ bcmp(&sync_in.sin_addr, &addr.sin_addr,
+ sizeof(addr.sin_addr)) == 0)
+ return;
+
+ /* Ignore invalid or truncated packets */
+ hdr = (struct spam_synchdr *)buf;
+ if (len < sizeof(struct spam_synchdr) ||
+ hdr->sh_version != SPAM_SYNC_VERSION ||
+ hdr->sh_af != AF_INET ||
+ len < ntohs(hdr->sh_length))
+ goto trunc;
+ len = ntohs(hdr->sh_length);
+
+ /* Compute and validate HMAC */
+ bcopy(hdr->sh_hmac, hmac[0], SPAM_SYNC_HMAC_LEN);
+ bzero(hdr->sh_hmac, SPAM_SYNC_HMAC_LEN);
+ HMAC(EVP_sha1(), sync_key, strlen(sync_key), buf, len,
+ hmac[1], &hmac_len);
+ if (bcmp(hmac[0], hmac[1], SPAM_SYNC_HMAC_LEN) != 0)
+ goto trunc;
+
+ if (debug)
+ fprintf(stderr,
+ "%s(sync): received packet of %d bytes\n",
+ inet_ntoa(addr.sin_addr), (int)len);
+
+ p = (u_int8_t *)(hdr + 1);
+ while (len) {
+ tlv = (struct spam_synctlv_hdr *)p;
+
+ if (len < sizeof(struct spam_synctlv_hdr) ||
+ len < ntohs(tlv->st_length))
+ goto trunc;
+
+ switch (ntohs(tlv->st_type)) {
+ case SPAM_SYNC_GREY:
+ sg = (struct spam_synctlv_grey *)tlv;
+ if ((sizeof(*sg) +
+ ntohs(sg->sg_from_length) +
+ ntohs(sg->sg_to_length) +
+ ntohs(sg->sg_helo_length)) >
+ ntohs(tlv->st_length))
+ goto trunc;
+
+ ip.s_addr = sg->sg_ip;
+ from = (char *)(sg + 1);
+ to = from + ntohs(sg->sg_from_length);
+ helo = to + ntohs(sg->sg_to_length);
+ if (debug) {
+ fprintf(stderr, "%s(sync): "
+ "received grey entry ",
+ inet_ntoa(addr.sin_addr));
+ fprintf(stderr, "helo %s ip %s "
+ "from %s to %s\n",
+ helo, inet_ntoa(ip), from, to);
+ }
+ if (greylist) {
+ /* send this info to the greylister */
+ fprintf(grey,
+ "SYNC\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n",
+ helo, inet_ntoa(ip), from, to);
+ fflush(grey);
+ }
+ break;
+ case SPAM_SYNC_WHITE:
+ sd = (struct spam_synctlv_addr *)tlv;
+ if (sizeof(*sd) != ntohs(tlv->st_length))
+ goto trunc;
+
+ ip.s_addr = sd->sd_ip;
+ expire = ntohl(sd->sd_expire);
+ if (debug) {
+ fprintf(stderr, "%s(sync): "
+ "received white entry ",
+ inet_ntoa(addr.sin_addr));
+ fprintf(stderr, "ip %s ", inet_ntoa(ip));
+ }
+ if (greylist) {
+ /* send this info to the greylister */
+ fprintf(grey, "WHITE:%s:", inet_ntoa(ip));
+ fprintf(grey, "%s:%u\n",
+ inet_ntoa(addr.sin_addr), expire);
+ fflush(grey);
+ }
+ break;
+ case SPAM_SYNC_TRAPPED:
+ sd = (struct spam_synctlv_addr *)tlv;
+ if (sizeof(*sd) != ntohs(tlv->st_length))
+ goto trunc;
+
+ ip.s_addr = sd->sd_ip;
+ expire = ntohl(sd->sd_expire);
+ if (debug) {
+ fprintf(stderr, "%s(sync): "
+ "received trapped entry ",
+ inet_ntoa(addr.sin_addr));
+ fprintf(stderr, "ip %s ", inet_ntoa(ip));
+ }
+ if (greylist) {
+ /* send this info to the greylister */
+ fprintf(grey, "TRAP:%s:", inet_ntoa(ip));
+ fprintf(grey, "%s:%u\n",
+ inet_ntoa(addr.sin_addr), expire);
+ fflush(grey);
+ }
+ break;
+ case SPAM_SYNC_END:
+ goto done;
+ default:
+ printf("invalid type: %d\n", ntohs(tlv->st_type));
+ goto trunc;
+ }
+ len -= ntohs(tlv->st_length);
+ p = ((u_int8_t *)tlv) + ntohs(tlv->st_length);
+ }
+
+ done:
+ return;
+
+ trunc:
+ if (debug)
+ fprintf(stderr, "%s(sync): truncated or invalid packet\n",
+ inet_ntoa(addr.sin_addr));
+}
+
+void
+sync_send(struct iovec *iov, int iovlen)
+{
+ struct sync_host *shost;
+ struct msghdr msg;
+
+ /* setup buffer */
+ bzero(&msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iovlen;
+
+ if (sendmcast) {
+ if (debug)
+ fprintf(stderr, "sending multicast sync message\n");
+ msg.msg_name = &sync_out;
+ msg.msg_namelen = sizeof(sync_out);
+ sendmsg(syncfd, &msg, 0);
+ }
+
+ LIST_FOREACH(shost, &sync_hosts, h_entry) {
+ if (debug)
+ fprintf(stderr, "sending sync message to %s (%s)\n",
+ shost->h_name, inet_ntoa(shost->sh_addr.sin_addr));
+ msg.msg_name = &shost->sh_addr;
+ msg.msg_namelen = sizeof(shost->sh_addr);
+ sendmsg(syncfd, &msg, 0);
+ }
+}
+
+void
+sync_update(time_t now, char *helo, char *ip, char *from, char *to)
+{
+ struct iovec iov[7];
+ struct spam_synchdr hdr;
+ struct spam_synctlv_grey sg;
+ struct spam_synctlv_hdr end;
+ u_int16_t sglen, fromlen, tolen, helolen, padlen;
+ char pad[SPAM_ALIGNBYTES];
+ int i = 0;
+ HMAC_CTX ctx;
+ u_int hmac_len;
+
+ if (debug)
+ fprintf(stderr,
+ "sync grey update helo %s ip %s from %s to %s\n",
+ helo, ip, from, to);
+
+ bzero(&hdr, sizeof(hdr));
+ bzero(&sg, sizeof(sg));
+ bzero(&pad, sizeof(pad));
+
+ fromlen = strlen(from) + 1;
+ tolen = strlen(to) + 1;
+ helolen = strlen(helo) + 1;
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
+
+ sglen = sizeof(sg) + fromlen + tolen + helolen;
+ padlen = SPAM_ALIGN(sglen) - sglen;
+
+ /* Add SPAM sync packet header */
+ hdr.sh_version = SPAM_SYNC_VERSION;
+ hdr.sh_af = AF_INET;
+ hdr.sh_counter = htonl(sync_counter++);
+ hdr.sh_length = htons(sizeof(hdr) + sglen + padlen + sizeof(end));
+ iov[i].iov_base = &hdr;
+ iov[i].iov_len = sizeof(hdr);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add single SPAM sync greylisting entry */
+ sg.sg_type = htons(SPAM_SYNC_GREY);
+ sg.sg_length = htons(sglen + padlen);
+ sg.sg_timestamp = htonl(now);
+ sg.sg_ip = inet_addr(ip);
+ sg.sg_from_length = htons(fromlen);
+ sg.sg_to_length = htons(tolen);
+ sg.sg_helo_length = htons(helolen);
+ iov[i].iov_base = &sg;
+ iov[i].iov_len = sizeof(sg);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ iov[i].iov_base = from;
+ iov[i].iov_len = fromlen;
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ iov[i].iov_base = to;
+ iov[i].iov_len = tolen;
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ iov[i].iov_base = helo;
+ iov[i].iov_len = helolen;
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ iov[i].iov_base = pad;
+ iov[i].iov_len = padlen;
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add end marker */
+ end.st_type = htons(SPAM_SYNC_END);
+ end.st_length = htons(sizeof(end));
+ iov[i].iov_base = &end;
+ iov[i].iov_len = sizeof(end);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
+
+ /* Send message to the target hosts */
+ sync_send(iov, i);
+ HMAC_CTX_cleanup(&ctx);
+}
+
+void
+sync_addr(time_t now, time_t expire, char *ip, u_int16_t type)
+{
+ struct iovec iov[3];
+ struct spam_synchdr hdr;
+ struct spam_synctlv_addr sd;
+ struct spam_synctlv_hdr end;
+ int i = 0;
+ HMAC_CTX ctx;
+ u_int hmac_len;
+
+ if (debug)
+ fprintf(stderr, "sync %s %s\n",
+ type == SPAM_SYNC_WHITE ? "white" : "trapped", ip);
+
+ bzero(&hdr, sizeof(hdr));
+ bzero(&sd, sizeof(sd));
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1());
+
+ /* Add SPAM sync packet header */
+ hdr.sh_version = SPAM_SYNC_VERSION;
+ hdr.sh_af = AF_INET;
+ hdr.sh_counter = htonl(sync_counter++);
+ hdr.sh_length = htons(sizeof(hdr) + sizeof(sd) + sizeof(end));
+ iov[i].iov_base = &hdr;
+ iov[i].iov_len = sizeof(hdr);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add single SPAM sync address entry */
+ sd.sd_type = htons(type);
+ sd.sd_length = htons(sizeof(sd));
+ sd.sd_timestamp = htonl(now);
+ sd.sd_expire = htonl(expire);
+ sd.sd_ip = inet_addr(ip);
+ iov[i].iov_base = &sd;
+ iov[i].iov_len = sizeof(sd);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ /* Add end marker */
+ end.st_type = htons(SPAM_SYNC_END);
+ end.st_length = htons(sizeof(end));
+ iov[i].iov_base = &end;
+ iov[i].iov_len = sizeof(end);
+ HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len);
+ i++;
+
+ HMAC_Final(&ctx, hdr.sh_hmac, &hmac_len);
+
+ /* Send message to the target hosts */
+ sync_send(iov, i);
+ HMAC_CTX_cleanup(&ctx);
+}
+
+void
+sync_white(time_t now, time_t expire, char *ip)
+{
+ sync_addr(now, expire, ip, SPAM_SYNC_WHITE);
+}
+
+void
+sync_trapped(time_t now, time_t expire, char *ip)
+{
+ sync_addr(now, expire, ip, SPAM_SYNC_TRAPPED);
+}
--- /dev/null
+/* $OpenBSD: sync.h,v 1.3 2008/05/22 19:54:11 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SPAMD_SYNC
+#define _SPAMD_SYNC
+
+/*
+ * spamd(8) synchronisation protocol.
+ *
+ * This protocol has been designed for realtime synchronisation between
+ * multiple machines running spamd(8), ie. in front of a MX and a backup MX.
+ * It is a simple Type-Length-Value based protocol, it allows easy
+ * extension with future subtypes and bulk transfers by sending multiple
+ * entries at once. The unencrypted messages will be authenticated using
+ * HMAC-SHA1.
+ *
+ * the spamd(8) synchronisation protocol is not intended to be used as
+ * a public SPAM sender database or distribution between vendors.
+ */
+
+#define SPAM_SYNC_VERSION 2
+#define SPAM_SYNC_MCASTADDR "224.0.1.240" /* XXX choose valid address */
+#define SPAM_SYNC_MCASTTTL IP_DEFAULT_MULTICAST_TTL
+#define SPAM_SYNC_HMAC_LEN 20 /* SHA1 */
+#define SPAM_SYNC_MAXSIZE 1408
+#define SPAM_SYNC_KEY "/etc/mail/spamd.key"
+
+#define SPAM_ALIGNBYTES (15)
+#define SPAM_ALIGN(p) (((u_int)(p) + SPAM_ALIGNBYTES) &~ SPAM_ALIGNBYTES)
+
+struct spam_synchdr {
+ u_int8_t sh_version;
+ u_int8_t sh_af;
+ u_int16_t sh_length;
+ u_int32_t sh_counter;
+ u_int8_t sh_hmac[SPAM_SYNC_HMAC_LEN];
+ u_int8_t sh_pad[4];
+} __packed;
+
+struct spam_synctlv_hdr {
+ u_int16_t st_type;
+ u_int16_t st_length;
+} __packed;
+
+struct spam_synctlv_grey {
+ u_int16_t sg_type;
+ u_int16_t sg_length;
+ u_int32_t sg_timestamp;
+ u_int32_t sg_ip;
+ u_int16_t sg_from_length;
+ u_int16_t sg_to_length;
+ u_int16_t sg_helo_length;
+ /* strings go here, then packet code re-aligns packet */
+} __packed;
+
+struct spam_synctlv_addr {
+ u_int16_t sd_type;
+ u_int16_t sd_length;
+ u_int32_t sd_timestamp;
+ u_int32_t sd_expire;
+ u_int32_t sd_ip;
+} __packed;
+
+#define SPAM_SYNC_END 0x0000
+#define SPAM_SYNC_GREY 0x0001
+#define SPAM_SYNC_WHITE 0x0002
+#define SPAM_SYNC_TRAPPED 0x0003
+
+extern int sync_init(const char *, const char *, u_short);
+extern int sync_addhost(const char *, u_short);
+extern void sync_recv(void);
+extern void sync_update(time_t, char *, char *, char *, char *);
+extern void sync_white(time_t, time_t, char *);
+extern void sync_trapped(time_t, time_t, char *);
+
+#endif /* _SPAMD_SYNC */
--- /dev/null
+/Makefile/1.6/Sun Mar 4 03:19:41 2007//
+/spamlogd.8/1.16/Thu Sep 17 06:37:54 2009//
+/spamlogd.c/1.19/Mon Mar 5 14:55:09 2007//
+D
--- /dev/null
+src/libexec/spamlogd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.6 2007/03/04 03:19:41 beck Exp $
+
+PROG= spamlogd
+SRCS= spamlogd.c sync.c
+MAN= spamlogd.8
+
+CFLAGS+= -Wall -Wstrict-prototypes -I${.CURDIR}/../spamd
+LDADD+= -lpcap -lcrypto
+DPADD+= ${LIBPCAP}
+.PATH: ${.CURDIR}/../spamd
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/spamlogd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: spamlogd.8,v 1.16 2009/09/17 06:37:54 jmc Exp $
+.\"
+.\" Copyright (c) 2004 Bob Beck. All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: September 17 2009 $
+.Dt SPAMLOGD 8
+.Os
+.Sh NAME
+.Nm spamlogd
+.Nd spamd whitelist updating daemon
+.Sh SYNOPSIS
+.Nm spamlogd
+.Op Fl DI
+.Op Fl i Ar interface
+.Op Fl l Ar pflog_interface
+.Op Fl Y Ar synctarget
+.Sh DESCRIPTION
+.Nm
+manipulates the
+.Xr spamd 8
+database in
+.Pa /var/db/spamd
+used for greylisting.
+.Nm
+updates the
+.Pa /var/db/spamd
+whitelist entries whenever a connection
+to port 25 is logged to the
+.Xr pflog 4
+interface.
+The source addresses of inbound connections are whitelisted
+when seen by
+.Nm
+to ensure that their entries in
+.Pa /var/db/spamd
+do not expire if the connecting host continues to send legitimate mail.
+The destination addresses of outbound connections are whitelisted
+when seen by
+.Nm
+so that replies to outbound mail may be received without initial
+greylisting delays.
+Greylisting is explained more fully in
+.Xr spamd 8 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl D
+Debugging mode.
+.Nm
+does not disassociate from the controlling terminal.
+.It Fl I
+Specify that
+.Nm
+is only to whitelist inbound SMTP connections.
+By default
+.Nm
+will whitelist the source of inbound SMTP connections, and the
+target of outbound SMTP connections.
+.It Fl i Ar interface
+Specify a network interface on which packets must arrive.
+The default is to watch for connections logged from all interfaces.
+.It Fl l Ar pflog_interface
+Specify a
+.Xr pflog 4
+interface to listen for connection notifications.
+The default is to watch for connections logged on
+.Dq pflog0 .
+.It Fl Y Ar synctarget
+Add a target to receive synchronisation messages; see
+.Sx SYNCHRONISATION
+below.
+This option can be specified multiple times.
+.El
+.Pp
+It is important to log any connections to and from the real
+MTA in order for
+.Nm
+to update the whitelist entries.
+See
+.Xr spamd 8
+for an example ruleset for logging such connections.
+.Pp
+.Nm
+sends log messages to
+.Xr syslogd 8
+using facility
+.Em daemon .
+.Nm
+will log each connection it sees at level
+.Dv LOG_DEBUG .
+.Sh SYNCHRONISATION
+.Nm
+supports realtime synchronisation of whitelist states by sending
+the information it updates to
+a number of
+.Xr spamd 8
+daemons running on multiple machines.
+To enable synchronisation, use the command line option
+.Fl Y
+to specify the machines to which
+.Nm
+will send messages when it updates the state information.
+For more information, see
+.Xr spamd 8 .
+.Sh FILES
+/var/db/spamd
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr pflog 4 ,
+.Xr spamd.conf 5 ,
+.Xr pflogd 8 ,
+.Xr spamd 8 ,
+.Xr spamd-setup 8 ,
+.Xr spamdb 8 ,
+.Xr syslogd 8 ,
+.Xr tcpdump 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Ox 3.5 .
--- /dev/null
+/* $OpenBSD: spamlogd.c,v 1.19 2007/03/05 14:55:09 beck Exp $ */
+
+/*
+ * Copyright (c) 2006 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2006 Berk D. Demir.
+ * Copyright (c) 2004-2007 Bob Beck.
+ * Copyright (c) 2001 Theo de Raadt.
+ * Copyright (c) 2001 Can Erkin Acar.
+ * All rights reserved
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* watch pf log for mail connections, update whitelist entries. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_pflog.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <net/pfvar.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <pcap.h>
+
+#include "grey.h"
+#include "sync.h"
+
+#define MIN_PFLOG_HDRLEN 45
+#define PCAPSNAP 512
+#define PCAPTIMO 500 /* ms */
+#define PCAPOPTZ 1 /* optimize filter */
+#define PCAPFSIZ 512 /* pcap filter string size */
+
+int debug = 1;
+int greylist = 1;
+FILE *grey = NULL;
+
+u_short sync_port;
+int syncsend;
+u_int8_t flag_debug = 0;
+u_int8_t flag_inbound = 0;
+char *networkif = NULL;
+char *pflogif = "pflog0";
+char errbuf[PCAP_ERRBUF_SIZE];
+pcap_t *hpcap = NULL;
+struct syslog_data sdata = SYSLOG_DATA_INIT;
+extern char *__progname;
+
+void logmsg(int , const char *, ...);
+void sighandler_close(int);
+int init_pcap(void);
+void logpkt_handler(u_char *, const struct pcap_pkthdr *, const u_char *);
+int dbupdate(char *, char *);
+void usage(void);
+
+void
+logmsg(int pri, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+
+ if (flag_debug) {
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ } else
+ vsyslog_r(pri, &sdata, msg, ap);
+
+ va_end(ap);
+}
+
+/* ARGSUSED */
+void
+sighandler_close(int signal)
+{
+ if (hpcap != NULL)
+ pcap_breakloop(hpcap); /* sighdlr safe */
+}
+
+int
+init_pcap(void)
+{
+ struct bpf_program bpfp;
+ char filter[PCAPFSIZ] = "ip and port 25 and action pass "
+ "and tcp[13]&0x12=0x2";
+
+ if ((hpcap = pcap_open_live(pflogif, PCAPSNAP, 1, PCAPTIMO,
+ errbuf)) == NULL) {
+ logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
+ return (-1);
+ }
+
+ if (pcap_datalink(hpcap) != DLT_PFLOG) {
+ logmsg(LOG_ERR, "Invalid datalink type");
+ pcap_close(hpcap);
+ hpcap = NULL;
+ return (-1);
+ }
+
+ if (networkif != NULL) {
+ strlcat(filter, " and on ", PCAPFSIZ);
+ strlcat(filter, networkif, PCAPFSIZ);
+ }
+
+ if (pcap_compile(hpcap, &bpfp, filter, PCAPOPTZ, 0) == -1 ||
+ pcap_setfilter(hpcap, &bpfp) == -1) {
+ logmsg(LOG_ERR, "%s", pcap_geterr(hpcap));
+ return (-1);
+ }
+
+ pcap_freecode(&bpfp);
+
+ if (ioctl(pcap_fileno(hpcap), BIOCLOCK) < 0) {
+ logmsg(LOG_ERR, "BIOCLOCK: %s", strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* ARGSUSED */
+void
+logpkt_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+{
+ sa_family_t af;
+ u_int8_t hdrlen;
+ u_int32_t caplen = h->caplen;
+ const struct ip *ip = NULL;
+ const struct pfloghdr *hdr;
+ char ipstraddr[40] = { '\0' };
+
+ hdr = (const struct pfloghdr *)sp;
+ if (hdr->length < MIN_PFLOG_HDRLEN) {
+ logmsg(LOG_WARNING, "invalid pflog header length (%u/%u). "
+ "packet dropped.", hdr->length, MIN_PFLOG_HDRLEN);
+ return;
+ }
+ hdrlen = BPF_WORDALIGN(hdr->length);
+
+ if (caplen < hdrlen) {
+ logmsg(LOG_WARNING, "pflog header larger than caplen (%u/%u). "
+ "packet dropped.", hdrlen, caplen);
+ return;
+ }
+
+ /* We're interested in passed packets */
+ if (hdr->action != PF_PASS)
+ return;
+
+ af = hdr->af;
+ if (af == AF_INET) {
+ ip = (const struct ip *)(sp + hdrlen);
+ if (hdr->dir == PF_IN)
+ inet_ntop(af, &ip->ip_src, ipstraddr,
+ sizeof(ipstraddr));
+ else if (hdr->dir == PF_OUT && !flag_inbound)
+ inet_ntop(af, &ip->ip_dst, ipstraddr,
+ sizeof(ipstraddr));
+ }
+
+ if (ipstraddr[0] != '\0') {
+ if (hdr->dir == PF_IN)
+ logmsg(LOG_DEBUG,"inbound %s", ipstraddr);
+ else
+ logmsg(LOG_DEBUG,"outbound %s", ipstraddr);
+ dbupdate(PATH_SPAMD_DB, ipstraddr);
+ }
+}
+
+int
+dbupdate(char *dbname, char *ip)
+{
+ HASHINFO hashinfo;
+ DBT dbk, dbd;
+ DB *db;
+ struct gdata gd;
+ time_t now;
+ int r;
+ struct in_addr ia;
+
+ now = time(NULL);
+ memset(&hashinfo, 0, sizeof(hashinfo));
+ db = dbopen(dbname, O_EXLOCK|O_RDWR, 0600, DB_HASH, &hashinfo);
+ if (db == NULL) {
+ logmsg(LOG_ERR, "Can not open db %s: %s", dbname,
+ strerror(errno));
+ return (-1);
+ }
+ if (inet_pton(AF_INET, ip, &ia) != 1) {
+ logmsg(LOG_NOTICE, "Invalid IP address %s", ip);
+ goto bad;
+ }
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+
+ /* add or update whitelist entry */
+ r = db->get(db, &dbk, &dbd, 0);
+ if (r == -1) {
+ logmsg(LOG_NOTICE, "db->get failed (%m)");
+ goto bad;
+ }
+
+ if (r) {
+ /* new entry */
+ memset(&gd, 0, sizeof(gd));
+ gd.first = now;
+ gd.bcount = 1;
+ gd.pass = now;
+ gd.expire = now + WHITEEXP;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ if (r) {
+ logmsg(LOG_NOTICE, "db->put failed (%m)");
+ goto bad;
+ }
+ } else {
+ if (dbd.size != sizeof(gd)) {
+ /* whatever this is, it doesn't belong */
+ db->del(db, &dbk, 0);
+ goto bad;
+ }
+ memcpy(&gd, dbd.data, sizeof(gd));
+ gd.pcount++;
+ gd.expire = now + WHITEEXP;
+ memset(&dbk, 0, sizeof(dbk));
+ dbk.size = strlen(ip);
+ dbk.data = ip;
+ memset(&dbd, 0, sizeof(dbd));
+ dbd.size = sizeof(gd);
+ dbd.data = &gd;
+ r = db->put(db, &dbk, &dbd, 0);
+ if (r) {
+ logmsg(LOG_NOTICE, "db->put failed (%m)");
+ goto bad;
+ }
+ }
+ db->close(db);
+ db = NULL;
+ if (syncsend)
+ sync_white(now, now + WHITEEXP, ip);
+ return (0);
+ bad:
+ db->close(db);
+ db = NULL;
+ return (-1);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-DI] [-i interface] [-l pflog_interface] [-Y synctarget]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ struct passwd *pw;
+ pcap_handler phandler = logpkt_handler;
+ int syncfd = 0;
+ struct servent *ent;
+ char *sync_iface = NULL;
+ char *sync_baddr = NULL;
+
+ if ((ent = getservbyname("spamd-sync", "udp")) == NULL)
+ errx(1, "Can't find service \"spamd-sync\" in /etc/services");
+ sync_port = ntohs(ent->s_port);
+
+ while ((ch = getopt(argc, argv, "DIi:l:Y:")) != -1) {
+ switch (ch) {
+ case 'D':
+ flag_debug = 1;
+ break;
+ case 'I':
+ flag_inbound = 1;
+ break;
+ case 'i':
+ networkif = optarg;
+ break;
+ case 'l':
+ pflogif = optarg;
+ break;
+ case 'Y':
+ if (sync_addhost(optarg, sync_port) != 0)
+ sync_iface = optarg;
+ syncsend++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ signal(SIGINT , sighandler_close);
+ signal(SIGQUIT, sighandler_close);
+ signal(SIGTERM, sighandler_close);
+
+ logmsg(LOG_DEBUG, "Listening on %s for %s %s", pflogif,
+ (networkif == NULL) ? "all interfaces." : networkif,
+ (flag_inbound) ? "Inbound direction only." : "");
+
+ if (init_pcap() == -1)
+ err(1, "couldn't initialize pcap");
+
+ if (syncsend) {
+ syncfd = sync_init(sync_iface, sync_baddr, sync_port);
+ if (syncfd == -1)
+ err(1, "sync init");
+ }
+
+ /* privdrop */
+ pw = getpwnam("_spamd");
+ if (pw == NULL)
+ errx(1, "User '_spamd' not found! ");
+
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+ err(1, "failed to drop privs");
+
+ if (!flag_debug) {
+ if (daemon(0, 0) == -1)
+ err(1, "daemon");
+ tzset();
+ openlog_r("spamlogd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata);
+ }
+
+ pcap_loop(hpcap, -1, phandler, NULL);
+
+ logmsg(LOG_NOTICE, "exiting");
+ if (!flag_debug)
+ closelog_r(&sdata);
+
+ exit(0);
+}
--- /dev/null
+/Makefile/1.3/Tue Mar 28 05:54:39 2000//
+/announce.c/1.21/Tue Oct 27 23:59:31 2009//
+/print.c/1.11/Tue Oct 27 23:59:31 2009//
+/process.c/1.19/Tue Oct 27 23:59:31 2009//
+/table.c/1.14/Tue Oct 27 23:59:31 2009//
+/talkd.8/1.6/Thu May 31 19:19:41 2007//
+/talkd.c/1.22/Tue Oct 27 23:59:31 2009//
+/talkd.h/1.8/Mon Jun 2 19:38:24 2003//
+D
--- /dev/null
+src/libexec/talkd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# from: @(#)Makefile 5.10 (Berkeley) 5/11/90
+# $OpenBSD: Makefile,v 1.3 2000/03/28 05:54:39 deraadt Exp $
+
+PROG= ntalkd
+SRCS= talkd.c announce.c process.c table.c print.c
+MAN= talkd.8
+MLINKS= talkd.8 ntalkd.8
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: announce.c,v 1.21 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vis.h>
+#include <paths.h>
+#include "talkd.h"
+
+static void print_mesg(FILE *,CTL_MSG *,char *);
+
+/*
+ * Announce an invitation to talk. If the user is
+ * accepting messages, announce that a talk is requested.
+ */
+int
+announce(CTL_MSG *request, char *remote_machine)
+{
+ char full_tty[MAXPATHLEN];
+ FILE *tf;
+ struct stat stbuf;
+
+ (void)snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV,
+ request->r_tty);
+ if (access(full_tty, 0) != 0)
+ return (FAILED);
+ if ((tf = fopen(full_tty, "w")) == NULL)
+ return (PERMISSION_DENIED);
+ if (fstat(fileno(tf), &stbuf) < 0) {
+ fclose(tf);
+ return (PERMISSION_DENIED);
+ }
+ if ((stbuf.st_mode & S_IWGRP) == 0) {
+ fclose(tf);
+ return (PERMISSION_DENIED);
+ }
+ print_mesg(tf, request, remote_machine);
+ fclose(tf);
+ return (SUCCESS);
+}
+
+#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
+ * is in vi at the time
+ */
+static void
+print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine)
+{
+ struct timeval clock;
+ time_t clocktime;
+ struct timezone zone;
+ struct tm *localclock;
+ char line_buf[N_LINES][N_CHARS];
+ int sizes[N_LINES];
+ char big_buf[(N_LINES + 1) * N_CHARS];
+ char *bptr, *lptr, vis_user[sizeof(request->l_name) * 4];
+ int i, j, max_size;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&clock, &zone);
+ clocktime = clock.tv_sec;
+ localclock = localtime(&clocktime);
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS,
+ "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++;
+ strvis(vis_user, request->l_name, VIS_CSTYLE);
+ (void)snprintf(line_buf[i], N_CHARS,
+ "talk: connection requested by %s@%s.",
+ vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s",
+ vis_user, remote_machine);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = max(max_size, sizes[i]);
+ i++;
+ bptr = big_buf;
+ *bptr++ = '\007'; /* 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';
+ fprintf(tf, "%s", big_buf);
+ fflush(tf);
+}
--- /dev/null
+/usr/obj/libexec/talkd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: print.c,v 1.11 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <protocols/talkd.h>
+#include <syslog.h>
+#include <stdio.h>
+#include "talkd.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]))
+
+void
+print_request(char *cp, CTL_MSG *mp)
+{
+ char tbuf[80], *tp;
+
+ if (mp->type >= NTYPES) {
+ (void)snprintf(tbuf, sizeof(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);
+}
+
+void
+print_response(char *cp, CTL_RESPONSE *rp)
+{
+ char tbuf[80], *tp, abuf[80], *ap;
+
+ if (rp->type >= NTYPES) {
+ (void)snprintf(tbuf, sizeof(tbuf), "type %d", rp->type);
+ tp = tbuf;
+ } else
+ tp = types[rp->type];
+ if (rp->answer >= NANSWERS) {
+ (void)snprintf(abuf, sizeof(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));
+}
--- /dev/null
+/* $OpenBSD: process.c,v 1.19 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+/*
+ * 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 <arpa/inet.h>
+#include <protocols/talkd.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <paths.h>
+#include "talkd.h"
+
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+void
+process_request(CTL_MSG *mp, CTL_RESPONSE *rp)
+{
+ CTL_MSG *ptr;
+ char *s;
+
+ 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);
+ if (ntohs(mp->addr.sa_family) != AF_INET) {
+ syslog(LOG_WARNING, "Bad address, family %d",
+ ntohs(mp->addr.sa_family));
+ rp->answer = BADADDR;
+ return;
+ }
+ if (ntohs(mp->ctl_addr.sa_family) != AF_INET) {
+ syslog(LOG_WARNING, "Bad control address, family %d",
+ ntohs(mp->ctl_addr.sa_family));
+ rp->answer = BADCTLADDR;
+ return;
+ }
+ for (s = mp->l_name; *s; s++)
+ if (!isprint(*s)) {
+ syslog(LOG_NOTICE, "Illegal user name. Aborting");
+ rp->answer = FAILED;
+ return;
+ }
+ if (memcmp(&satosin(&rp->addr)->sin_addr,
+ &satosin(&mp->ctl_addr)->sin_addr,
+ sizeof(struct in_addr))) {
+ char buf1[32], buf2[32];
+
+ strlcpy(buf1, inet_ntoa(satosin(&rp->addr)->sin_addr),
+ sizeof(buf1));
+ strlcpy(buf2, inet_ntoa(satosin(&mp->ctl_addr)->sin_addr),
+ sizeof(buf2));
+ syslog(LOG_WARNING, "addresses are different, %s != %s",
+ buf1, buf2);
+ }
+ rp->addr.sa_family = 0;
+ 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 = 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);
+}
+
+void
+do_announce(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, sizeof(mp->r_tty));
+ if (result != SUCCESS) {
+ rp->answer = result;
+ return;
+ }
+ 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
+ */
+int
+find_user(char *name, char *tty, size_t ttyl)
+{
+ struct utmp ubuf, ubuf1;
+ int status;
+ FILE *fp;
+ char line[UT_LINESIZE+1];
+ char ftty[MAXPATHLEN];
+ time_t idle, now;
+
+ time(&now);
+ idle = INT_MAX;
+ if ((fp = 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) strlcpy(ftty, _PATH_DEV, sizeof(ftty));
+ while (fread((char *) &ubuf, sizeof(ubuf), 1, fp) == 1)
+ if (SCMPN(ubuf.ut_name, name) == 0) {
+ if (*tty == '\0') {
+ /* no particular tty was requested */
+ struct stat statb;
+
+ memcpy(line, ubuf.ut_line, UT_LINESIZE);
+ line[sizeof(line)-1] = '\0';
+ ftty[sizeof(_PATH_DEV)-1] = '\0';
+ strlcat(ftty, line, sizeof(ftty));
+ if (stat(ftty, &statb) == 0) {
+ if (!(statb.st_mode & S_IWGRP)) {
+ if (status == NOT_HERE)
+ status = PERMISSION_DENIED;
+ } else if (now - statb.st_atime < idle) {
+ idle = now - statb.st_atime;
+ status = SUCCESS;
+ ubuf1 = ubuf;
+ }
+ }
+ } else if (SCMPN(ubuf.ut_line, tty) == 0) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ fclose(fp);
+ if (*tty == '\0' && status == SUCCESS) {
+ memcpy(line, ubuf1.ut_line, UT_LINESIZE);
+ line[sizeof(line)-1] = '\0';
+ strlcpy(tty, line, ttyl);
+ }
+ return (status);
+}
--- /dev/null
+/* $OpenBSD: table.c,v 1.14 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+/*
+ * 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 <sys/queue.h>
+#include <protocols/talkd.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "talkd.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+struct timeval tp;
+struct timezone txp;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ CTL_MSG request;
+ time_t time;
+ TAILQ_ENTRY(table_entry) list;
+};
+TAILQ_HEAD(, table_entry) table;
+
+static void delete(TABLE_ENTRY *);
+
+/*
+ * Init the table
+ */
+void
+init_table(void)
+{
+ TAILQ_INIT(&table);
+}
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+CTL_MSG *
+find_match(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr, *next;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ if (debug)
+ print_request("find_match", request);
+ for (ptr = TAILQ_FIRST(&table); ptr != NULL; ptr = next) {
+ next = TAILQ_NEXT(ptr, list);
+ if ((current_time - ptr->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 (ptr->request.type == LEAVE_INVITE &&
+ strcmp(request->l_name, ptr->request.r_name) == 0 &&
+ strcmp(request->r_name, ptr->request.l_name) == 0)
+ return (&ptr->request);
+ }
+ if (debug)
+ syslog(LOG_DEBUG, "find_match: not found");
+
+ return ((CTL_MSG *)0);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does
+ */
+CTL_MSG *
+find_request(CTL_MSG *request)
+{
+ TABLE_ENTRY *ptr, *next;
+ 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 = TAILQ_FIRST(&table); ptr != NULL; ptr = next) {
+ next = TAILQ_NEXT(ptr, list);
+ if ((current_time - ptr->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 (request->pid == ptr->request.pid &&
+ request->type == ptr->request.type &&
+ strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0) {
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ return (&ptr->request);
+ }
+ }
+ return ((CTL_MSG *)0);
+}
+
+void
+insert_table(CTL_MSG *request, CTL_RESPONSE *response)
+{
+ TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ if (debug)
+ print_request( "insert_table", request );
+ 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 == NULL) {
+ syslog(LOG_ERR, "insert_table: Out of memory");
+ _exit(1);
+ }
+ ptr->time = current_time;
+ ptr->request = *request;
+ TAILQ_INSERT_HEAD(&table, ptr, list);
+}
+
+/*
+ * Generate a unique non-zero sequence number
+ */
+int
+new_id(void)
+{
+ 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'
+ */
+int
+delete_invite(int id_num)
+{
+ TABLE_ENTRY *ptr;
+
+ if (debug)
+ syslog(LOG_DEBUG, "delete_invite(%d)", id_num);
+ TAILQ_FOREACH(ptr, &table, list) {
+ if (ptr->request.id_num == id_num)
+ break;
+ if (debug)
+ print_request("", &ptr->request);
+ }
+ if (ptr != NULL) {
+ delete(ptr);
+ return (SUCCESS);
+ }
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list
+ */
+static void
+delete(TABLE_ENTRY *ptr)
+{
+
+ if (debug)
+ print_request("delete", &ptr->request);
+ TAILQ_REMOVE(&table, ptr, list);
+ free((char *)ptr);
+}
--- /dev/null
+.\" $OpenBSD: talkd.8,v 1.6 2007/05/31 19:19:41 jmc Exp $
+.\"
+.\" Copyright (c) 1983, 1991 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. 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.
+.\"
+.\" from: @(#)talkd.8 6.5 (Berkeley) 3/16/91
+.\" $Id: talkd.8,v 1.6 2007/05/31 19:19:41 jmc Exp $
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt TALKD 8
+.Os
+.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 .
--- /dev/null
+/* $OpenBSD: talkd.c,v 1.22 2009/10/27 23:59:31 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+/*
+ * 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/param.h>
+#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>
+#include "talkd.h"
+
+int debug = 0;
+void timeout(int);
+long lastmsgtime;
+
+char hostname[MAXHOSTNAMELEN];
+
+#define TIMEOUT 30
+#define MAXIDLE 120
+
+int
+main(int argc, char *argv[])
+{
+ if (getuid() != 0) {
+ fprintf(stderr, "%s: getuid: not super-user\n", argv[0]);
+ exit(1);
+ }
+ openlog("talkd", LOG_PID, LOG_DAEMON);
+ if (gethostname(hostname, sizeof(hostname)) < 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;
+ init_table();
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+
+ for (;;) {
+ CTL_RESPONSE response;
+ socklen_t len = sizeof(response.addr);
+ CTL_MSG request;
+ int cc;
+ struct sockaddr ctl_addr;
+
+ memset(&response, 0, sizeof(response));
+ cc = recvfrom(STDIN_FILENO, (char *)&request,
+ sizeof(request), 0, (struct sockaddr *)&response.addr,
+ &len);
+ if (cc != sizeof(request)) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recvfrom: %m");
+ continue;
+ }
+
+ /* Force NUL termination */
+ request.l_name[sizeof(request.l_name) - 1] = '\0';
+ request.r_name[sizeof(request.r_name) - 1] = '\0';
+ request.r_tty[sizeof(request.r_tty) - 1] = '\0';
+
+ memcpy(&ctl_addr, &request.ctl_addr, sizeof(ctl_addr));
+ ctl_addr.sa_family = ntohs(request.ctl_addr.sa_family);
+ ctl_addr.sa_len = sizeof(ctl_addr);
+ if (ctl_addr.sa_family != AF_INET)
+ continue;
+
+ lastmsgtime = time(0);
+ process_request(&request, &response);
+ /* can block here, is this what I want? */
+ cc = sendto(STDOUT_FILENO, (char *)&response,
+ sizeof(response), 0, &ctl_addr, sizeof(ctl_addr));
+ if (cc != sizeof(response))
+ syslog(LOG_WARNING, "sendto: %m");
+ }
+}
+
+void
+timeout(int signo)
+{
+ int save_errno = errno;
+
+ if (time(0) - lastmsgtime >= MAXIDLE)
+ _exit(0);
+ alarm(TIMEOUT);
+ errno = save_errno;
+}
--- /dev/null
+/* $OpenBSD: talkd.h,v 1.8 2003/06/02 19:38:24 millert Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.c */
+extern int debug;
+extern char hostname[];
+
+/* table.c */
+void init_table(void);
+CTL_MSG *find_request(CTL_MSG *);
+CTL_MSG *find_match(CTL_MSG *);
+void insert_table(CTL_MSG *, CTL_RESPONSE *);
+int new_id(void);
+int delete_invite(int);
+
+/* process.c */
+void process_request( CTL_MSG *, CTL_RESPONSE *);
+void do_announce(CTL_MSG *, CTL_RESPONSE *);
+int find_user(char *name, char *tty, size_t ttyl);
+
+/* announce.c */
+int announce(CTL_MSG *,char *);
+
+/* print.c */
+void print_request(char *,CTL_MSG *);
+void print_response(char *,CTL_RESPONSE *);
+
--- /dev/null
+$OpenBSD: BLURB,v 1.2 1997/06/01 05:21:39 downsj Exp $
+@(#) BLURB 1.28 97/03/21 19:27:18
+
+With this package you can monitor and filter incoming requests for the
+SYSTAT, FINGER, FTP, TELNET, RLOGIN, RSH, EXEC, TFTP, TALK, and other
+network services.
+
+The package provides tiny daemon wrapper programs that can be installed
+without any changes to existing software or to existing configuration
+files. The wrappers report the name of the client host and of the
+requested service; the wrappers do not exchange information with the
+client or server applications, and impose no overhead on the actual
+conversation between the client and server applications.
+
+This patch upgrades the tcp wrappers version 7.5 source code to
+version 7.6. The source-routing protection in version 7.5 was not
+as strong as it could be. And all this effort was not needed with
+modern UNIX systems that can already stop source-routed traffic in
+the kernel. Examples are 4.4BSD derivatives, Solaris 2.x, and Linux.
+
+This release does not introduce new features. Do not bother applying
+this patch when you built your version 7.x tcp wrapper without
+enabling the KILL_IP_OPTIONS compiler switch; when you can disable
+IP source routing options in the kernel; when you run a UNIX version
+that pre-dates 4.4BSD, such as SunOS 4. Such systems are unable to
+receive source-routed connections and are therefore not vulnerable
+to IP spoofing attacks with source-routed TCP connections.
+
+A complete change log is given in the CHANGES document. As always,
+problem reports and suggestions for improvement are welcome.
+
+ Wietse Venema (wietse@wzv.win.tue.nl),
+ Department of Mathematics and Computing Science,
+ Eindhoven University of Technology,
+ The Netherlands.
+
+ Currently visiting IBM T.J. Watson Research, Hawthorne NY, USA.
--- /dev/null
+$OpenBSD: CHANGES,v 1.2 1997/06/01 05:21:39 downsj Exp $
+
+Request: after building the programs, please run the `tcpdchk' wrapper
+configuration checker. See the `tcpdchk.8' manual page (`nroff -man'
+format) for instructions. `tcpdchk' automatically identifies the most
+common configuration problems, and will save you and me a lot of time.
+
+Changes per release 7.6 (Mar 1997)
+==================================
+
+- Improved the anti source-routing protection. The code in version
+7.5 was not as strong as it could be, because I tried to be compatible
+with Linux. That was a mistake. Sorry for the inconvenience.
+
+- The program no longer terminates case of a source-routed connection,
+making the IP-spoofing code more usable for long-running daemons.
+
+- When syslogging DNS hostname problems, always stop after a limited
+number of characters.
+
+Changes per release 7.5 (Feb 1997)
+==================================
+
+- Optionally refuse source-routed TCP connections requests altogether.
+Credits to Niels Provos of Universitaet Hamburg. File: fix_options.c.
+
+- Support for IRIX 6 (Lael Tucker).
+
+- Support for Amdahl UTS 2.1.5 (Richard E. Richmond).
+
+- Support for SINIX 5.42 (Klaus Nielsen).
+
+- SCO 5 now has vsyslog() (Bill Golden).
+
+- Hints and tips for dealing with IRIX inetd (Niko Makila, Aaron
+M Lee).
+
+- Support for BSD/OS (Paul Borman).
+
+- Support for Tandem (Emad Qawas).
+
+- Support for ISC (Frederick B. Cohen).
+
+- Workaround for UNICOS - it would choke on a setjmp() expression
+(Bruce Kelly). File: hosts_access.c, tcpdchk.c.
+
+- Increased the level of buffer overflow paranoia when printing
+unwanted IP options. File: fix_options.c.
+
+Changes per release 7.4 (Mar 1996)
+==================================
+
+- IRIX 5.3 (and possibly, earlier releases, too) library routines call
+the non-reentrant strtok() routine. The result is that hosts may slip
+through allow/deny filters. Workaround is to not rely on the vendor's
+strtok() routine (#ifdef LIBC_CALLS_STRTOK). Credits to Th. Eifert
+(Aachen University) for spotting this one. This fix supersedes the
+earlier workaround for a similar problem in FreeBSD 2.0.
+
+Changes per release 7.3 (Feb 1996)
+==================================
+
+- More tests added to tcpdchk and tcpdmatch: make sure that the
+REAL_DAEMON_DIR actually is a directory and not a regular file;
+detect if tcpd recursively calls itself.
+
+- Edwin Kremer found an amusing fencepost error in the xgets()
+routine: lines longer than BUFLEN characters would be garbled.
+
+- The access control routines now refuse to execute "dangerous" actions
+such as `twist' when they are called from within a resident process.
+This prevents you from shooting yourself into the foot with critical
+systems programs such as, e.g., portmap or rpcbind.
+
+- Support for Unicos 8.x (Bruce Kelly). The program now closes the
+syslog client socket before running the real daemon: Cray UNICOS
+refuses to checkpoint processes with open network ports.
+
+- Support for MachTen UNIX (Albert M.C Tam).
+
+- Support for Interactive UNIX R3.2 V4.0 (Bobby D. Wright).
+
+- Support for SCO 3.2v5.0.0 OpenServer 5 (bob@odt.handy.com)
+
+- Support for Unixware 1.x and Unixware 2.x. The old Unixware Makefile
+rule was broken. Sorry about that.
+
+- Some FreeBSD 2.0 libc routines call strtok() and severely mess up the
+allow/deny rule processing. This is very bad. Workaround: call our own
+strtok() clone (#ifdef USE_STRSEP).
+
+- The programs now log a warning when they detect that a non-existent
+banner directory is specified.
+
+- The hosts_access.3 manual page used obsolete names for the RQ_*
+constants.
+
+Changes per release 7.2 (Jan 1995)
+==================================
+
+- Added a note to the README and manpages on using the IDENT service to
+detect sequence number spoofing and other host impersonation attacks.
+
+- Portability: ConvexOS puts RPC version numbers before the daemon path
+name (Jukka Ukkonen).
+
+- Portability: the AIX compiler disliked the strchr() declaration
+in socket.c. I should have removed it when I included <string.h>.
+
+- Backwards compatibility: some people relied on the old leading dot or
+trailing dot magic in daemon process names.
+
+- Backwards compatibility: hostname lookup remains enabled when
+-DPARANOID is turned off. In order to disable hostname lookups you
+must turn off -DALWAYS_HOSTNAME.
+
+- Eliminated false complaints from the tcpdmatch/tcpdchk configuration
+checking programs about process names not in inetd.conf or about KNOWN
+username patterns.
+
+Changes per release 7.1 (Jan 1995)
+==================================
+
+- Portability: HP-UX permits you to break inetd.conf entries with
+backslash-newline.
+
+- Portability: EP/IX has no putenv() and some inetd.conf entries are
+spread out over two lines.
+
+- Portability: SCO with NIS support has no *netgrent() routines.
+
+Changes per release 7.0 (Jan 1995)
+==================================
+
+- Added a last-minute workaround for a Solaris 2.4 gethostbyname()
+foulup with multi-homed hosts in DNS through NIS mode.
+
+- Added a last-minute defense against TLI weirdness: address lookups
+apparently succeed but the result netbuf is empty (ticlts transport).
+
+- Dropped several new solutions that were in need of a problem. Beta
+testers may recognize what new features were kicked out during the last
+weeks before release 7.0 came out. Such is life.
+
+- Got rid of out the environment replacement routines, at least for
+most architectures. One should not have to replace working system
+software when all that is needed is a 4.4BSD setenv() emulator.
+
+- By popular request I have added an option to send banner messages to
+clients. There is a Banners.Makefile that gives some aid for sites that
+are going to use this feature. John C. Wingenbach did some pioneering
+work here. I used to think that banners are frivolous. Now that I had
+a personal need for them I know that banners can be useful.
+
+- At last: an extensible functional interface to the pattern matching
+engine. request_init() and request_set() accept a variable-length
+name-value argument list. The result can be passed to hosts_access().
+
+- When PARANOID mode is disabled (compile time), the wrapper does no
+hostname lookup or hostname double checks unless required by %letter
+expansions, or by access control rules that match host names. This is
+useful for sites that don't care about internet hostnames anyway.
+Inspired by the authors of the firewalls and internet security book.
+
+- When PARANOID mode is disabled (compile time), hosts with a name/name
+or name/address conflict can be matched with the PARANOID host wildcard
+pattern, so that you can take some intelligent action instead of just
+dropping clients. Like showing a banner that explains the problem.
+
+- New percent escapes: %A expands to the server address; %H expands to
+the corresponding hostname (or address if no name is available); %n and
+%N expand to the client and server hostname (or "unknown"); %s expands
+to everything we know about the server endpoint (the opposite of the %c
+sequence for client information).
+
+- Symmetry: server and client host information is now treated on equal
+footing, so that we can reuse a lot of code.
+
+- Lazy evaluation of host names, host addresses, usernames, and so on,
+to avoid doing unnecessary work.
+
+- Dropping #ifdefs for some archaic systems made the code simpler.
+
+- Dropping the FAIL pattern made the pattern matcher much simpler. Run
+the "tcpdchk" program to scan your access control files for any uses of
+this obscure language feature.
+
+- Moving host-specific pattern matching from string_match() to the
+host_match() routine made the code more accurate. Run the "tcpdchk"
+program to scan your access control files for any dependencies on
+undocumented or obscure language features that are gone.
+
+- daemon@host patterns trigger on clients that connect to a specific
+internet address. This can be useful for service providers that offer
+multiple ftp or www archives on different internet addresses, all
+belonging to one and the same host (www.foo.com, ftp.bar.com, you get
+the idea). Inspired by a discussion with Rop Gonggrijp, Cor Bosman,
+and Casper Dik, and earlier discussions with Adrian van Bloois.
+
+- The new "tcpdchk" program critcizes all your access control rules and
+inetd.conf entries. Great for spotting obscure bugs in my own hosts.xxx
+files. This program also detects hosts with name/address conflicts and
+with other DNS-related problems. See the "tcpdchk.8" manual page.
+
+- The "tcpdmatch" program replaces the poor old "try" command. The new
+program looks in your inetd.conf file and therefore produces much more
+accurate predictions. In addition, it detects hosts with name/address
+conflicts and with other DNS-related problems. See the "tcpdmatch.8"
+manual page. The inetd.conf lookup was suggested by Everett F Batey.
+
+- In the access control tables, the `=' between option name and value
+is no longer required.
+
+- Added 60-second timeout to the safe_finger command, to cover another
+potential problem. Suggested by Peter Wemm.
+
+- Andrew Maffei provided code that works with WIN-TCP on NCR System V.4
+UNIX. It reportedly works with versions 02.02.01 and 02.03.00. The code
+pops off all streams modules above the device driver, pushes the timod
+module to get at the peer address, and then restores the streams stack
+to the initial state.
+
+Changes per release 6.3 (Mar 1994)
+==================================
+
+- Keepalives option, to get rid of stuck daemons when people turn off
+their PC while still connected. Files: options.c, hosts_options.5.
+
+- Nice option, to calm down network daemons that take away too much CPU
+time. Files: options.c, hosts_options.5.
+
+- Ultrix perversion: the environ global pointer may be null. The
+environment replacement routines now check for this. File: environ.c.
+
+- Fixed a few places that still assumed the socket is on standard
+input. Fixed some error messages that did not provide access control
+file name and line number. File: options.c.
+
+- Just when I was going to release 6.2 I received code for Dynix/PTX.
+That code is specific to PTX 2.x, so I'll keep around my generic
+PTX code just in case. The difference is in the handling of UDP
+services. Files: tli_sequent.[hc].
+
+Changes per release 6.2 (Feb 1994)
+==================================
+
+- Resurrected my year-old code to reduce DNS load by appending a dot to
+the gethostbyname() argument. This feature is still experimental and it
+may go away if it causes more problems than it solves. File: socket.c.
+
+- Auxiliary code for the Pyramid, BSD universe. Karl Vogel figured out
+what was missing: yp_get_default_domain() and vfprintf(). Files:
+workarounds.c, vfprintf.c.
+
+- Improved support for Dynix/PTX. The wrapper should now be able to
+deal with all TLI over IP services. File: ptx.c.
+
+- The try command now uses the hostname that gethostbyaddr() would
+return, instead of the hostname returned by gethostbyname(). This can
+be significant on systems with NIS that have short host names in the
+hosts map. For example, gethostbyname("wzv.win.tue.nl") returns
+"wzv.win.tue.nl"; gethostbyaddr(131.155.210.17) returns "wzv", and
+that is what we should test with. File: try.c.
+
+Changes per release 6.1 (Dec 1993)
+==================================
+
+- Re-implemented all environment access routines. Most systems have
+putenv() but no setenv(), some systems have setenv() but no putenv(),
+and there are even systems that have neither setenv() nor putenv(). The
+benefit of all this is that more systems can now be treated in the same
+way. File: environ.c.
+
+- Workaround for a weird problem with DG/UX when the wrapper is run as
+nobody (i.e. fingerd). For some reason the ioctl(fd, I_FIND, "sockmod")
+call fails even with socket-based applications. The "fix" is to always
+assume sockets when the ioctl(fd, I_FIND, "timod") call fails. File:
+fromhost.c. Thanks to Paul de Vries (vries@dutentb.et.tudelft.nl) for
+helping me to figure out this one.
+
+- Implemented a workaround for Dynix/PTX and other systems with TLI
+that lack some essential support routines. Thanks to Bugs Brouillard
+(brouill@hsuseq.humboldt.edu) for the hospitality to try things out.
+The trick is to temporarily switch to the socket API to identify the
+client, and to switch back to TLI when done. It still does not work
+right for basic network services such as telnet. File: fromhost.c.
+
+- Easy-to-build procedures for SCO UNIX, ConvexOS with UltraNet, EP/IX,
+Dynix 3.2, Dynix/PTX. File: Makefile.
+
+- Variable rfc931 timeout. Files: rfc931.c, options.c, log_tcp.h, try.c.
+
+- Further simplification of the rfc931 code. File: rfc931.c.
+
+- The fromhost() interface stinks: I cannot change that, but at least
+the from_sock() and from_tli() functions now accept a file descriptor
+argument.
+
+- Fixed a buglet: fromhost() would pass a garbage file descriptor to
+the isastream() call.
+
+- On some systems the finger client program lives in /usr/bsd. File:
+safe_finger.c.
+
+Changes per release 6.0 (Sept 1993)
+===================================
+
+- Easy build procedures for common platforms (sun, ultrix, aix, hpux
+and others).
+
+- TLI support, System V.4 style (Solaris, DG/UX).
+
+- Username lookup integrated with the access control language.
+Selective username lookups are now the default (was: no username
+lookups).
+
+- A safer finger command for booby traps. This one solves a host of
+possible problems with automatic reverse fingers. Thanks, Borja Marcos
+(borjam@we.lc.ehu.es) for some inspiring discussions.
+
+- KNOWN pattern that matches hosts whose name and address are known.
+
+- Cleanup of diagnostics. Errors in access-control files are now shown
+with file name and line number.
+
+- With AIX 3.2, hostnames longer than 32 would be truncated. This
+caused hostname verification failures, so that service would be refused
+when paranoid mode was enabled. Found by: Adrian van Bloois
+(A.vanBloois@info.nic.surfnet.nl).
+
+- With some IRIX versions, remote username lookups failed because the
+fgets() library function does not handle partial read()s from sockets.
+Found by: Daniel O'Callaghan (danny@austin.unimelb.edu.au).
+
+- Added a DISCLAIMER document to help you satisfy legal departments.
+
+The extension language module has undergone major revisions and
+extensions. Thanks, John P. Rouillard (rouilj@ra.cs.umb.edu) for
+discussions, experiments, and for being a good guinea pig. The
+extensions are documented in hosts_options.5, and are enabled by
+editing the Makefile STYLE macro definition.
+
+- (Extension language) The ":" separator may now occur within options
+as long as it is protected with a backslash. A warning is issued when
+a rule ends on ":".
+
+- (Extension language) Better verification mode. When the `try' command
+is run, each option function now explains what it would do.
+
+- (Extension language) New "allow" and "deny" keywords so you can now
+have all rules within a single file. See "nroff -man hosts_options.5"
+for examples.
+
+- (Extension language) "linger" keyword to set the socket linger time
+(SO_LINGER). From: Marc Boucher <marc@cam.org>.
+
+- (Extension language) "severity" keyword to turn the logging noise up
+or down. Many sites wanted a means to shut up the program; other sites
+wanted to emphasize specific events. Adapted from code contributed
+by Dave Mitchell <D.Mitchell@dcs.shef.ac.uk>.
+
+Changes per release 5.1 (Mar 1993)
+==================================
+
+- The additional protection against source-routing attacks from hosts
+that pretend to have someone elses network address has become optional
+because it causes kernel panics with SunOS <= 4.1.3.
+
+Changes per release 5.0 (Mar 1993)
+==================================
+
+- Additional protection against source-routing attacks from hosts that
+pretend to have someone elses network address. For example, the address
+of a trusted host within your own network.
+
+- The access control language has been extended with a simple but
+powerful operator that greatly simplifies the design of rule sets (ALL:
+.foo.edu EXCEPT dialup.foo.edu). Blank lines are permitted, and long
+lines can be continued with backslash-newline.
+
+- All configurable stuff, including path names, has been moved into the
+Makefile so that you no longer have to hack source code to just
+configure the programs.
+
+- Ported to Solaris 2. TLI-based applications not yet supported.
+Several workarounds for System V bugs.
+
+- A small loophole in the netgroup lookup code was closed, and the
+remote username lookup code was made more portable.
+
+- Still more documentation. The README file now provides tutorial
+sections with introductions to client, server, inetd and syslogd.
+
+Changes per release 4.3 (Aug 1992)
+==================================
+
+- Some sites reported that connections would be rejected because
+localhost != localhost.domain. The host name checking code now
+special-cases localhost (problem reported by several sites).
+
+- The programs now report an error if an existing access control file
+cannot be opened (e.g. due to lack of privileges). Until now, the
+programs would just pretend that the access control file does not exist
+(reported by Darren Reed, avalon@coombs.anu.edu.au).
+
+- The timeout period for remote userid lookups was upped to 30 seconds,
+in order to cope with slow hosts or networks. If this is too long for
+you, adjust the TIMEOUT definition in file rfc931.c (problem reported
+by several sites).
+
+- On hosts with more than one IP network interface, remote userid
+lookups could use the IP address of the "wrong" local interface. The
+problem and its solution were discussed on the rfc931-users mailing
+list. Scott Schwartz (schwartz@cs.psu.edu) folded the fix into the
+rfc931.c module.
+
+- The result of % expansion (in shell commands) is now checked for
+stuff that may confuse the shell; it is replaced by underscores
+(problem reported by Icarus Sparry, I.Sparry@gdr.bath.ac.uk).
+
+- A portability problem was fixed that caused compile-time problems
+on a CRAY (problem reported by Michael Barnett, mikeb@rmit.edu.au).
+
+Changes per release 4.0 (Jun 1992)
+==================================
+
+1 - network daemons no longer have to live within a common directory
+2 - the access control code now uses both the host address and name
+3 - an access control pattern that supports netmasks
+4 - additional protection against forged host names
+5 - a pattern that matches hosts whose name or address lookup fails
+6 - an operator that prevents hosts or services from being matched
+7 - optional remote username lookup with the RFC 931 protocol
+8 - an optional umask to prevent the creation of world-writable files
+9 - hooks for access control language extensions
+10 - last but not least, thoroughly revised documentation.
+
+Changes per release 3.0 (Oct 1991)
+==================================
+
+Enhancements over the previous release are: support for datagram (UDP
+and RPC) services, and execution of shell commands when a (remote host,
+requested service) pair matches a pattern in the access control tables.
+
+Changes per release 2.0 (May 1991)
+==================================
+
+Enhancements over the previous release are: protection against rlogin
+and rsh attacks through compromised domain name servers, optional
+netgroup support for systems with NIS (formerly YP), and an extension
+of the wild card patterns supported by the access control files.
+
+Release 1.0 (Jan 1991)
--- /dev/null
+/BLURB/1.2/Sun Jun 1 05:21:39 1997//
+/CHANGES/1.2/Sun Jun 1 05:21:39 1997//
+/DISCLAIMER/1.2/Fri Jun 1 23:28:36 2001//
+/Makefile/1.2/Thu Sep 5 00:08:16 2002//
+/Makefile.inc/1.3/Sat Oct 14 00:56:14 2000//
+/README/1.4/Tue Mar 18 13:05:24 2003//
+D/safe_finger////
+D/tcpd////
+D/tcpdchk////
+D/tcpdmatch////
--- /dev/null
+src/libexec/tcpd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+$OpenBSD: DISCLAIMER,v 1.2 2001/06/01 23:28:36 deraadt Exp $
+
+/************************************************************************
+* Copyright 1995 by Wietse Venema. All rights reserved. Some individual
+* files may be covered by other copyrights.
+*
+* This material was originally written and compiled by Wietse Venema at
+* Eindhoven University of Technology, The Netherlands, in 1990, 1991,
+* 1992, 1993, 1994 and 1995.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that this entire copyright notice
+* is duplicated in all such copies.
+*
+* This software is provided "as is" and without any expressed or implied
+* warranties, including, without limitation, the implied warranties of
+* merchantibility and fitness for any particular purpose.
+************************************************************************/
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2002/09/05 00:08:16 deraadt Exp $
+
+SUBDIR= tcpd tcpdchk tcpdmatch
+
+.include <bsd.subdir.mk>
--- /dev/null
+# $OpenBSD: Makefile.inc,v 1.3 2000/10/14 00:56:14 itojun Exp $
+
+# Configuration options for libwrap. Keep in sync with libwrap/Makefile.
+CFLAGS+=-DPROCESS_OPTIONS -DFACILITY=LOG_AUTH -DSEVERITY=LOG_INFO \
+ -DRFC931_TIMEOUT=10 -DHOSTS_ACCESS -DALWAYS_HOSTNAME \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\" \
+ -DNETGROUP -DSYS_ERRLIST_DEFINED -DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -D_TCPD_PRIVATE
+CFLAGS+=-DINET6
+
+.include "../Makefile.inc"
--- /dev/null
+$OpenBSD: README,v 1.4 2003/03/18 13:05:24 david Exp $
+@(#) README 1.30 97/03/21 19:27:21
+
+This is the 7.6 version of the TCP/IP daemon wrapper package.
+
+Thank you for using this program. If you like it, send me a postcard.
+My postal address is at the bottom of this file.
+
+Read the BLURB file for a brief summary of what is new. The CHANGES
+file gives a complete account of differences with respect to previous
+releases.
+
+Announcements of new releases of this software are posted to Usenet
+(comp.security.unix, comp.unix.admin), to the cert-tools mailing list,
+and to a dedicated mailing list. You can subscribe to the dedicated
+mailing list by sending an email message to majordomo@wzv.win.tue.nl
+with in the body (not subject): subscribe tcp-wrappers-announce.
+
+Table of contents
+-----------------
+
+ 1 - Introduction
+ 2 - Disclaimer
+ 3 - Tutorials
+ 3.1 - How it works
+ 3.2 - Where the logging information goes
+ 4 - Features
+ 4.1 - Access control
+ 4.2 - Host name spoofing
+ 4.3 - Host address spoofing
+ 4.4 - Client username lookups
+ 4.5 - Language extensions
+ 4.6 - Multiple ftp/gopher/www archives on one host
+ 4.7 - Banner messages
+ 4.8 - Sequence number guessing
+ 5 - Other works
+ 5.1 - Related documents
+ 5.2 - Related software
+ 6 - Limitations
+ 6.1 - Known wrapper limitations
+ 6.2 - Known system software bugs
+ 7 - Configuration and installation
+ 7.1 - Easy configuration and installation
+ 7.2 - Advanced configuration and installation
+ 7.3 - Daemons with arbitrary path names
+ 7.4 - Building and testing the access control rules
+ 7.5 - Other applications
+ 8 - Acknowledgements
+
+1 - Introduction
+----------------
+
+With this package you can monitor and filter incoming requests for the
+SYSTAT, FINGER, FTP, TELNET, RLOGIN, RSH, EXEC, TFTP, TALK, and other
+network services.
+
+It supports both 4.3BSD-style sockets and System V.4-style TLI. Praise
+yourself lucky if you don't know what that means.
+
+The package provides tiny daemon wrapper programs that can be installed
+without any changes to existing software or to existing configuration
+files. The wrappers report the name of the client host and of the
+requested service; the wrappers do not exchange information with the
+client or server applications, and impose no overhead on the actual
+conversation between the client and server applications.
+
+Optional features are: access control to restrict what systems can
+connect to what network daemons; client user name lookups with the RFC
+931 etc. protocol; additional protection against hosts that pretend to
+have someone elses host name; additional protection against hosts that
+pretend to have someone elses host address.
+
+The programs are very portable. Build procedures are provided for many
+common (and not so common) environments, and guidelines are provided in
+case your environment is not among them.
+
+Requirements are that network daemons are spawned by a super server
+such as the inetd; a 4.3BSD-style socket programming interface and/or
+System V.4-style TLI programming interface; and the availability of a
+syslog(3) library and of a syslogd(8) daemon. The wrappers should run
+without modification on any system that satisfies these requirements.
+Workarounds have been implemented for several common bugs in systems
+software.
+
+What to do if this is your first encounter with the wrapper programs:
+1) read the tutorial sections for an introduction to the relevant
+concepts and terminology; 2) glance over the security feature sections
+in this document; 3) follow the installation instructions (easy or
+advanced). I recommend that you first use the default security feature
+settings. Run the wrappers for a few days to become familiar with
+their logs, before doing anything drastic such as cutting off access or
+installing booby traps.
+
+2 - Disclaimer
+--------------
+
+The wrapper programs rely on source address information obtained from
+network packets. This information is provided by the client host. It is
+not 100 percent reliable, although the wrappers do their best to expose
+forgeries.
+
+In the absence of cryptographic protection of message contents, and of
+cryptographic authentication of message originators, all data from the
+network should be treated with sound scepticism.
+
+THIS RESTRICTION IS BY NO MEANS SPECIFIC TO THE TCP/IP PROTOCOLS.
+
+3 - Tutorials
+-------------
+
+The tutorial sections give a gentle introduction to the operation of
+the wrapper programs, and introduce some of the terminology that is
+used in the remainder of the document: client, server, the inetd and
+syslogd daemons, and their configuration files.
+
+3.1 - How it works
+------------------
+
+Almost every application of the TCP/IP protocols is based on a client-
+server model. For example, when a user invokes the telnet command to
+connect to one of your systems, a telnet server process is executed on
+the target host. The telnet server process connects the user to a login
+process. A few examples of client and server programs are shown in the
+table below:
+
+ client server application
+ --------------------------------
+ telnet telnetd remote login
+ ftp ftpd file transfer
+ finger fingerd show users
+
+The usual approach is to run one single daemon process that waits for
+all kinds of incoming network connections. Whenever a connection is
+established, this daemon (usually called inetd) runs the appropriate
+server program and goes back to sleep, waiting for other connections.
+
+The wrapper programs rely on a simple, but powerful mechanism. Instead
+of directly running the desired server program, the inetd is tricked
+into running a small wrapper program. The wrapper logs the client host
+name or address and performs some additional checks. When all is well,
+the wrapper executes the desired server program and goes away.
+
+The wrapper programs have no interaction with the client user (or with
+the client process). Nor do the wrappers interact with the server
+application. This has two major advantages: 1) the wrappers are
+application-independent, so that the same program can protect many
+kinds of network services; 2) no interaction also means that the
+wrappers are invisible from outside (at least for authorized users).
+
+Another important property is that the wrapper programs are active only
+when the initial contact between client and server is established. Once
+a wrapper has done its work there is no overhead on the client-server
+conversation.
+
+The simple mechanism has one major drawback: the wrappers go away after
+the initial contact between client and server processes, so the
+wrappers are of little use with network daemons that service more than
+one client. The wrappers would only see the first client attempt to
+contact such a server. The NFS mount daemon is a typical example of a
+daemon that services requests from multiple clients. See the section on
+related software for ways to deal with such server programs.
+
+There are two ways to use the wrapper programs:
+
+1) The easy way: move network daemons to some other directory and fill
+ the resulting holes with copies of the wrapper programs. This
+ approach involves no changes to system configuration files, so there
+ is very little risk of breaking things.
+
+2) The advanced way: leave the network daemons alone and modify the
+ inetd configuration file. For example, an entry such as:
+
+ tftp dgram udp wait root /usr/etc/tcpd in.tftpd -s /tftpboot
+
+ When a tftp request arrives, inetd will run the wrapper program
+ (tcpd) with a process name `in.tftpd'. This is the name that the
+ wrapper will use when logging the request and when scanning the
+ optional access control tables. `in.tftpd' is also the name of the
+ server program that the wrapper will attempt to run when all is
+ well. Any arguments (`-s /tftpboot' in this particular example) are
+ transparently passed on to the server program.
+
+For an account of the history of the wrapper programs, with real-life
+examples, see the section below on related documents.
+
+3.2 - Where the logging information goes
+----------------------------------------
+
+The wrapper programs send their logging information to the syslog
+daemon (syslogd). The disposition of the wrapper logs is determined by
+the syslog configuration file (usually /etc/syslog.conf). Messages are
+written to files, to the console, or are forwarded to a @loghost. Some
+syslogd versions can even forward messages down a |pipeline.
+
+Older syslog implementations (still found on Ultrix systems) only
+support priority levels ranging from 9 (debug-level messages) to 0
+(alerts). All logging information of the specified priority level or
+more urgent is written to the same destination. In the syslog.conf
+file, priority levels are specified in numerical form. For example,
+
+ 8/usr/spool/mqueue/syslog
+
+causes all messages with priority 8 (informational messages), and
+anything that is more urgent, to be appended to the file
+/usr/spool/mqueue/syslog.
+
+Newer syslog implementations support message classes in addition to
+priority levels. Examples of message classes are: mail, daemon, auth
+and news. In the syslog.conf file, priority levels are specified with
+symbolic names: debug, info, notice, ..., emerg. For example,
+
+ mail.debug /var/log/syslog
+
+causes all messages of class mail with priority debug (or more urgent)
+to be appended to the /var/log/syslog file.
+
+By default, the wrapper logs go to the same place as the transaction
+logs of the sendmail daemon. The disposition can be changed by editing
+the Makefile and/or the syslog.conf file. Send a `kill -HUP' to the
+syslogd after changing its configuration file. Remember that syslogd,
+just like sendmail, insists on one or more TABs between the left-hand
+side and the right-hand side expressions in its configuration file.
+
+Solaris 2.x note: the syslog daemon depends on the m4 macro processor.
+The m4 program is installed as part of the software developer packages.
+
+Trouble shooting note: when the syslogging does not work as expected,
+run the program by hand (`syslogd -d') and see what really happens.
+
+4 - Features
+------------
+
+4.1 - Access control
+--------------------
+
+When compiled with -DHOSTS_ACCESS, the wrapper programs support a
+simple form of access control. Access can be controlled per host, per
+service, or combinations thereof. The software provides hooks for the
+execution of shell commands when an access control rule fires; this
+feature may be used to install "booby traps". For details, see the
+hosts_access.5 manual page, which is in `nroff -man' format. A later
+section describes how you can test your access control rules.
+
+Access control can also be used to connect clients to the "right"
+service. What is right may depend on the requested service, the origin
+of the request, and what host address the client connects to. Examples:
+
+(1) A gopher or www database speaks native language when contacted from
+ within the country, otherwise it speaks English.
+
+(2) A service provider offers different ftp, gopher or www services
+ with different internet hostnames from one host (section 4.6).
+
+Access control is enabled by default. It can be turned off by editing
+the Makefile, or by providing no access control tables. The install
+instructions below describe the Makefile editing process.
+
+The hosts_options.5 manual page (`nroff -man' format) documents an
+extended version of the access control language. The extensions are
+disabled by default. See the section below on language extensions.
+
+Later System V implementations provide the Transport Level Interface
+(TLI), a network programming interface that performs functions similar
+to the Berkeley socket programming interface. Like Berkeley sockets,
+TLI was designed to cover multiple protocols, not just Internet.
+
+When the wrapper discovers that the TLI interface sits on top of a
+TCP/IP or UDP/IP conversation it uses this knowledge to provide the
+same functions as with traditional socket-based applications. When
+some other protocol is used underneath TLI, the host address will be
+some universal magic cookie that may not even be usable for access
+control purposes.
+
+4.2 - Host name spoofing
+------------------------
+
+With some network applications, such as RSH or RLOGIN, the client host
+name plays an important role in the authentication process. Host name
+information can be reliable when lookups are done from a _local_ hosts
+table, provided that the client IP address can be trusted.
+
+With _distributed_ name services, authentication schemes that rely on
+host names become more problematic. The security of your system now may
+depend on some far-away DNS (domain name server) outside your own
+control.
+
+The wrapper programs verify the client host name that is returned by
+the address->name DNS server, by asking for a second opinion. To this
+end, the programs look at the name and addresses that are returned by
+the name->address DNS server, which may be an entirely different host.
+
+If any name or address discrepancies are found, or if the second DNS
+opinion is not available, the wrappers assume that one of the two name
+servers is lying, and assume that the client host pretends to have
+someone elses host name.
+
+When compiled with -DPARANOID, the wrappers will always attempt to look
+up and double check the client host name, and will always refuse
+service in case of a host name/address discrepancy. This is a
+reasonable policy for most systems.
+
+When compiled without -DPARANOID, the wrappers by default still perform
+hostname lookup. You can match hosts with a name/address discrepancy
+with the PARANOID wildcard and decide whether or not to grant service.
+
+Automatic hostname verification is enabled by default. Automatic
+hostname lookups and verification can be turned off by editing the
+Makefile. The configuration and installation section below describes
+the Makefile editing process.
+
+4.3 - Host address spoofing
+---------------------------
+
+While host name spoofing can be found out by asking a second opinion,
+it is much harder to find out that a host claims to have someone elses
+network address. And since host names are deduced from network
+addresses, address spoofing is at least as effective as name spoofing.
+
+The wrapper programs can give additional protection against hosts that
+claim to have an address that lies outside their own network. For
+example, some far-away host that claims to be a trusted host within
+your own network. Such things are possible even while the impersonated
+system is up and running.
+
+This additional protection is not an invention of my own; it has been
+present for at least five years in the BSD rsh and rlogin daemons.
+Unfortunately, that feature was added *after* 4.3 BSD came out, so that
+very few, if any, UNIX vendors have adopted it. Our site, and many
+other ones, has been running these enhanced daemons for several years,
+and without any ill effects.
+
+When the wrapper programs are compiled with -DKILL_IP_OPTIONS, the
+programs refuse to service TCP connections with IP source routing
+options. -DKILL_IP_OPTIONS is not needed on modern UNIX systems
+that can stop source-routed traffic in the kernel. Examples are
+4.4BSD derivatives, Solaris 2.x, and Linux. See your system manuals
+for details.
+
+If you are going to use this feature on SunOS 4.1.x you should apply
+patch 100804-03+ or 101790-something depending on your SunOS version.
+Otherwise you may experience "BAD TRAP" and "Data fault" panics when
+the getsockopt() system call is executed after a TCP RESET has been
+received. This is a kernel bug, it is not the fault of the wrappers.
+
+The feature is disabled by default. It can be turned on by editing the
+Makefile. The configuration and installation section below describes
+the Makefile editing process.
+
+UDP services do not benefit from this additional protection. With UDP,
+all you can be certain of is the network packet's destination address.
+
+4.4 - Client username lookups
+-----------------------------
+
+The protocol proposed in RFC 931 provides a means to obtain the client
+user name from the client host. The requirement is that the client
+host runs an RFC 931-compliant daemon. The information provided by such
+a daemon is not intended to be used for authentication purposes, but it
+can provide additional information about the owner of a TCP connection.
+
+The RFC 931 protocol has diverged into different directions (IDENT,
+TAP, RFC 1413). To add to the confusion, they all use the same network
+port. The daemon wrappers implement a common subset of the protocols.
+
+There are some limitations: the number of hosts that run an RFC 931 (or
+compatible) daemon is limited (but growing); client user name lookups
+do not work for datagram (UDP) services. More seriously, client user
+name lookups can cause noticeable delays with connections from non-UNIX
+PCs. Recent PC software seem to have fixed this (for example NCSA
+telnet). The wrappers use a 10-second timeout for RFC931 lookups, to
+accommodate slow networks and slow hosts.
+
+By default, the wrappers will do username lookup only when the access
+control rules require them to do so (via user@host client patterns, see
+the hosts_access.5 manual page) or when the username is needed for
+%<letter> expansions.
+
+You can configure the wrappers to always perform client username
+lookups, by editing the Makefile. The client username lookup timeout
+period (10 seconds default) can be changed by editing the Makefile. The
+installation sections below describe the Makefile editing process.
+
+On System V with TLI-based network services, client username lookups
+will be possible only when the underlying network protocol is TCP/IP.
+
+4.5 - Language extensions
+-------------------------
+
+The wrappers sport only a limited number of features. This is for a
+good reason: programs that run at high privilege levels must be easy to
+verify. And the smaller a program, the easier to verify. There is,
+however, a provision to add features.
+
+The options.c module provides a framework for language extensions.
+Quite a few extensions have already been implemented; they are
+documented in the hosts_options.5 document, which is in `nroff -man'
+format. Examples: changing the severity level at which a request for
+service is logged; "allow" and "deny" keywords; running a customized
+server instead of the standard one; many others.
+
+The language extensions are not enabled by default because they
+introduce an incompatible change to the access control language
+syntax. Instructions to enable the extensions are given in the
+Makefile.
+
+4.6 - Multiple ftp/gopher/www archives on one host
+--------------------------------------------------
+
+Imagine one host with multiple internet addresses. These addresses do
+not need to have the same internet hostname. Thus, it is possible to
+offer services with different internet hostnames from just one host.
+
+Service providers can use this to offer organizations a presence on the
+"net" with their own internet hostname, even when those organizations
+aren't connected to the Internet at all. To the end user it makes no
+difference, because applications use internet hostnames.
+
+There are several ways to assign multiple addresses to one machine.
+The nice way is to take an existing network interface and to assign
+additional internet addresses with the `ifconfig' command. Examples:
+
+ Solaris 2: ifconfig le0:1 <address> netmask <mask> up
+ 4.4 BSD: ifconfig en0 alias <address> netmask <mask>
+
+On other systems one has to increase the number of network interfaces:
+either with hardware interfaces, or with pseudo interfaces like SLIP or
+PPP. The interfaces do not need to be attached to anything. They just
+need to be up and to be assigned a suitable internet address and mask.
+
+With the wrapper software, `daemon@host' access control patterns can be
+used to distinguish requests by the network address that they are aimed
+at. Judicious use of the `twist' option (see the hosts_options.5 file,
+`nroff -man' format) can guide the requests to the right server. These
+can be servers that live in separate chroot areas, or servers modified
+to take additional context from the command line, or a combination.
+
+Another way is to modify gopher or www listeners so that they bind to
+only one specific network address. Multiple gopher or www servers can
+then be run side by side, each taking requests sent to its respective
+network address.
+
+4.7 - Banner messages
+---------------------
+
+Some sites are required to present an informational message to users
+before they attempt to login. Banner messages can also be useful when
+denying service: instead of simply dropping the connection a polite
+explanation is given first. Finally, banners can be used to give your
+system a more personal touch.
+
+The wrapper software provides easy-to-use tools to generate pre-login
+banners for ftp, telnet, rlogin etc. from a single prototype banner
+textfile. Details on banners and on-the-fly %<letter> expansions are
+given in the hosts_options.5 manual page (`nroff -man' format). An
+example is given in the file Banners.Makefile.
+
+In order to support banner messages the wrappers have to be built with
+language extensions enabled. See the section on language extensions.
+
+4.8 - Sequence number guessing
+------------------------------
+
+Recently, systems came under attack from intruders that exploited a
+well-known weakness in TCP/IP sequence number generators. This
+weakness allows intruders to impersonate trusted hosts. Break-ins have
+been reported via the rsh service. In fact, any network service can be
+exploited that trusts the client host name or address.
+
+A long-term solution is to stop using network services that trust the
+client host name or address, and to use data encryption instead.
+
+A short-term solution, as outlined in CERT advisory CA-95:01, is to
+configure network routers so that they discard datagrams from "outside"
+with an "inside" source address. This approach is most fruitful when
+you do not trust any hosts outside your local network.
+
+The IDENT (RFC931 etc.) client username lookup protocol can help to
+detect host impersonation attacks. Before accepting a client request,
+the wrappers can query the client's IDENT server and find out that the
+client never sent that request.
+
+When the client host provides IDENT service, a negative IDENT lookup
+result (the client matches `UNKNOWN@host') is strong evidence of a host
+impersonation attack.
+
+A positive IDENT lookup result (the client matches `KNOWN@host') is
+less trustworthy. It is possible for an attacker to spoof both the
+client request and the IDENT lookup connection, although doing so
+should be much harder than spoofing just a client request. Another
+possibility is that the client's IDENT server is lying.
+
+Client username lookups are described in more detail in a previous
+section. Pointers to IDENT daemon software are described in the section
+on related software.
+
+5 - Other works
+---------------
+
+5.1 - Related documents
+-----------------------
+
+The war story behind the tcp wrapper tools is described in:
+
+ W.Z. Venema, "TCP WRAPPER, network monitoring, access control and
+ booby traps", UNIX Security Symposium III Proceedings (Baltimore),
+ September 1992.
+
+ ftp.win.tue.nl:/pub/security/tcp_wrapper.ps.Z (postscript)
+ ftp.win.tue.nl:/pub/security/tcp_wrapper.txt.Z (flat text)
+
+The same cracker is also described in:
+
+ W.R. Cheswick, "An Evening with Berferd, In Which a Cracker is
+ Lured, Endured, and Studied", Proceedings of the Winter USENIX
+ Conference (San Francisco), January 1992.
+
+ research.att.com:/dist/internet_security/berferd.ps
+
+An updated version of the latter paper appeared in:
+
+ W.R. Cheswick, S.M. Bellovin, "Firewalls and Internet Security",
+ Addison-Wesley, 1994.
+
+Discussions on internet firewalls are archived on ftp.greatcircle.com.
+Subscribe to the mailing list by sending a message to
+
+ majordomo@greatcircle.com
+
+With in the body (not subject): subscribe firewalls.
+
+5.2 - Related software
+----------------------
+
+Network daemons etc. with enhanced logging capabilities can generate
+massive amounts of information: our 150+ workstations generate several
+hundred kbytes each day. egrep-based filters can help to suppress some
+of the noise. A more powerful tool is the Swatch monitoring system by
+Stephen E. Hansen and E. Todd Atkins. Swatch can process log files in
+real time and can associate arbitrary actions with patterns; its
+applications are by no means restricted to security. Swatch is
+available ftp.stanford.edu, directory /general/security-tools/swatch.
+
+Socks, described in the UNIX Security III proceedings, can be used to
+control network traffic from hosts on an internal network, through a
+firewall host, to the outer world. Socks consists of a daemon that is
+run on the firewall host, and of a library with routines that redirect
+application socket calls through the firewall daemon. Socks is
+available from s1.gov in /pub/firewalls/socks.tar.Z.
+
+For a modified Socks version by Ying-Da Lee (ylee@syl.dl.nec.com) try
+ftp.nec.com, directory /pub/security/socks.cstc.
+
+Tcpr is a set of perl scripts by Paul Ziemba that enable you to run ftp
+and telnet commands across a firewall. Unlike socks it can be used with
+unmodified client software. Available from ftp.alantec.com, /pub/tcpr.
+
+The TIS firewall toolkit provides a multitude of tools to build your
+own internet firewall system. ftp.tis.com, directory /pub/firewalls.
+
+Versions of rshd and rlogind, modified to report the client user name
+in addition to the client host name, are available for anonymous ftp
+(ftp.win.tue.nl:/pub/security/logdaemon-XX.tar.Z). These programs are
+drop-in replacements for SunOS 4.x, Ultrix 4.x, SunOS 5.x and HP-UX
+9.x. This archive also contains ftpd/rexecd/login versions that support
+S/Key or SecureNet one-time passwords in addition to traditional UNIX
+reusable passwords.
+
+The securelib shared library by William LeFebvre can be used to control
+access to network daemons that are not run under control of the inetd
+or that serve more than one client, such as the NFS mount daemon that
+runs until the machine goes down. Available from eecs.nwu.edu, file
+/pub/securelib.tar.
+
+xinetd (posted to comp.sources.unix) is an inetd replacement that
+provides, among others, logging, username lookup and access control.
+However, it does not support the System V TLI services, and involves
+much more source code than the daemon wrapper programs. Available
+from ftp.uu.net, directory /usenet/comp.sources.unix.
+
+netlog from Texas A&M relies on the SunOS 4.x /dev/nit interface to
+passively watch all TCP and UDP network traffic on a network. The
+current version is on net.tamu.edu in /pub/security/TAMU.
+
+Where shared libraries or router-based packet filtering are not an
+option, an alternative portmap daemon can help to prevent hackers
+from mounting your NFS file systems using the proxy RPC facility.
+ftp.win.tue.nl:/pub/security/portmap-X.shar.Z was tested with SunOS
+4.1.X Ultrix 3.0 and Ultrix 4.x, HP-UX 8.x and some version of AIX. The
+protection is less effective than that of the securelib library because
+portmap is mostly a dictionary service.
+
+An rpcbind replacement (the Solaris 2.x moral equivalent of portmap)
+can be found on ftp.win.tue.nl in /pub/security. It prevents hackers
+from mounting your NFS file systems by using the proxy RPC facility.
+
+Source for a portable RFC 931 (TAP, IDENT, RFC 1413) daemon by Peter
+Eriksson is available from ftp.lysator.liu.se:/pub/ident/servers.
+
+Some TCP/IP implementations come without syslog library. Some come with
+the library but have no syslog daemon. A replacement can be found in
+ftp.win.tue.nl:/pub/security/surrogate-syslog.tar.Z. The fakesyslog
+library that comes with the nntp sources reportedly works well, too.
+
+6 - Limitations
+---------------
+
+6.1 - Known wrapper limitations
+-------------------------------
+
+Many UDP (and rpc/udp) daemons linger around for a while after they
+have serviced a request, just in case another request comes in. In the
+inetd configuration file these daemons are registered with the `wait'
+option. Only the request that started such a daemon will be seen by the
+wrappers. Such daemons are better protected with the securelib shared
+library (see: Related software).
+
+The wrappers do not work with RPC services over TCP. These services are
+registered as rpc/tcp in the inetd configuration file. The only non-
+trivial service that is affected by this limitation is rexd, which is
+used by the on(1) command. This is no great loss. On most systems,
+rexd is less secure than a wildcard in /etc/hosts.equiv.
+
+Some RPC requests (for example: rwall, rup, rusers) appear to come from
+the server host. What happens is that the client broadcasts its request
+to all portmap daemons on its network; each portmap daemon forwards the
+request to a daemon on its own system. As far as the rwall etc. daemons
+know, the request comes from the local host.
+
+Portmap and RPC (e.g. NIS and NFS) (in)security is a topic in itself.
+See the section in this document on related software.
+
+6.2 - Known system software bugs
+--------------------------------
+
+Workarounds have been implemented for several bugs in system software.
+They are described in the Makefile. Unfortunately, some system software
+bugs cannot be worked around. The result is loss of functionality.
+
+IRIX has so many bugs that it has its own README.IRIX file.
+
+Older ConvexOS versions come with a broken recvfrom(2) implementation.
+This makes it impossible for the daemon wrappers to look up the
+client host address (and hence, the name) in case of UDP requests.
+A patch is available for ConvexOS 10.1; later releases should be OK.
+
+With early Solaris (SunOS 5) versions, the syslog daemon will leave
+behind zombie processes when writing to logged-in users. Workaround:
+increase the syslogd threshold for logging to users, or reduce the
+wrapper's logging severity.
+
+On some systems, the optional RFC 931 etc. client username lookups may
+trigger a kernel bug. When a client host connects to your system, and
+the RFC 931 connection from your system to that client is rejected by a
+router, your kernel may drop all connections with that client. This is
+not a bug in the wrapper programs: complain to your vendor, and don't
+enable client user name lookups until the bug has been fixed.
+
+Reportedly, SunOS 4.1.1, Next 2.0a, ISC 3.0 with TCP 1.3, and AIX 3.2.2
+and later are OK.
+
+Sony News/OS 4.51, HP-UX 8-something and Ultrix 4.3 still have the bug.
+Reportedly, a fix for Ultrix is available (CXO-8919).
+
+The following procedure can be used (from outside the tue.nl domain) to
+find out if your kernel has the bug. From the system under test, do:
+
+ % ftp 131.155.70.19
+
+This command attempts to make an ftp connection to our anonymous ftp
+server (ftp.win.tue.nl). When the connection has been established, run
+the following command from the same system under test, while keeping
+the ftp connection open:
+
+ % telnet 131.155.70.19 111
+
+Do not forget the `111' at the end of the command. This telnet command
+attempts to connect to our portmap process. The telnet command should
+fail with: "host not reachable", or with a timeout error. If your ftp
+connection gets messed up, you have the bug. If the telnet command does
+not fail, please let me know a.s.a.p.!
+
+For those who care, the bug is that the BSD kernel code was not careful
+enough with incoming ICMP UNREACHABLE control messages (it ignored the
+local and remote port numbers, and therefore zapped *all* connections
+with the remote system). The bug is still present in the BSD NET/1
+source release (1989) but apparently has been fixed in BSD NET/2 (1991).
+
+7 - Configuration and installation
+----------------------------------
+
+7.1 - Easy configuration and installation
+-----------------------------------------
+
+The "easy" recipe requires no changes to existing software or
+configuration files. Basically, you move the daemons that you want to
+protect to a different directory and plug the resulting holes with
+copies of the wrapper programs.
+
+If you don't run Ultrix, you won't need the miscd wrapper program. The
+miscd daemon implements among others the SYSTAT service, which produces
+the same output as the WHO command.
+
+Type `make' and follow the instructions. The Makefile comes with
+ready-to-use templates for many common UNIX implementations (sun,
+ultrix, hp-ux, aix, irix,...).
+
+IRIX has so many bugs that it has its own README.IRIX file.
+
+When the `make' succeeds the result is five executables (six in case of
+Ultrix).
+
+You can use the `tcpdchk' program to identify the most common problems
+in your wrapper and inetd configuration files.
+
+With the `tcpdmatch' program you can examine how the wrapper would
+react to specific requests for service.
+
+The `safe_finger' command should be used when you implement booby
+traps: it gives better protection against nasty stuff that remote
+hosts may do in response to your finger probes.
+
+The `try-from' program tests the host and username lookup code. Run it
+from a remote shell command (`rsh host /some/where/try-from') and it
+should be able to figure out from what system it is being called.
+
+The tcpd program can be used to monitor the telnet, finger, ftp, exec,
+rsh, rlogin, tftp, talk, comsat and other tcp or udp services that have
+a one-to-one mapping onto executable files.
+
+The tcpd program can also be used for services that are marked as
+rpc/udp in the inetd configuration file, but not for rpc/tcp services
+such as rexd. You probably do not want to run rexd anyway. On most
+systems it is even less secure than a wildcard in /etc/hosts.equiv.
+
+With System V.4-style systems, the tcpd program can also handle TLI
+services. When TCP/IP or UDP/IP is used underneath TLI, tcpd provides
+the same functions as with socket-based applications. When some other
+protocol is used underneath TLI, functionality will be limited (no
+client username lookups, weird network address formats).
+
+Decide which services you want to monitor. Move the corresponding
+vendor-provided daemon programs to the location specified by the
+REAL_DAEMON_DIR constant in the Makefile, and fill the holes with
+copies of the tcpd program. That is, one copy of (or link to) the tcpd
+program for each service that you want to monitor. For example, to
+monitor the use of your finger service:
+
+ # mkdir REAL_DAEMON_DIR
+ # mv /usr/etc/in.fingerd REAL_DAEMON_DIR
+ # cp tcpd /usr/etc/in.fingerd
+
+The example applies to SunOS 4. With other UNIX implementations the
+network daemons live in /usr/libexec, /usr/sbin or in /etc, or have no
+"in." prefix to their names, but you get the idea.
+
+File protections: the wrapper, all files used by the wrapper, and all
+directories in the path leading to those files, should be accessible
+but not writable for unprivileged users (mode 755 or mode 555). Do not
+install the wrapper set-uid.
+
+Ultrix only: If you want to monitor the SYSTAT service, move the
+vendor-provided miscd daemon to the location specified by the
+REAL_DAEMON_DIR macro in the Makefile, and install the miscd wrapper
+at the original miscd location.
+
+In the absence of any access-control tables, the daemon wrappers
+will just maintain a record of network connections made to your system.
+
+7.2 - Advanced configuration and installation
+---------------------------------------------
+
+The advanced recipe leaves your daemon executables alone, but involves
+simple modifications to the inetd configuration file.
+
+Type `make' and follow the instructions. The Makefile comes with
+ready-to-use templates for many common UNIX implementations (sun,
+ultrix, hp-ux, aix, irix, ...).
+
+IRIX users should read the warnings in the README.IRIX file first.
+
+When the `make' succeeds the result is five executables (six in case of
+Ultrix).
+
+You can use the `tcpdchk' program to identify the most common problems
+in your wrapper and inetd configuration files.
+
+With the `tcpdmatch' program you can examine how the wrapper would
+react to specific requests for service.
+
+The `try-from' program tests the host and username lookup code. Run it
+from a remote shell command (`rsh host /some/where/try-from') and it
+should be able to figure out from what system it is being called.
+
+The `safe_finger' command should be used when you implement a booby
+trap: it gives better protection against nasty stuff that remote hosts
+may do in response to your finger probes.
+
+The tcpd program can be used to monitor the telnet, finger, ftp, exec,
+rsh, rlogin, tftp, talk, comsat and other tcp or udp services that have
+a one-to-one mapping onto executable files.
+
+With System V.4-style systems, the tcpd program can also handle TLI
+services. When TCP/IP or UDP/IP is used underneath TLI, tcpd provides
+the same functions as with socket-based applications. When some other
+protocol is used underneath TLI, functionality will be limited (no
+client username lookups, weird network address formats).
+
+The tcpd program can also be used for services that are marked as
+rpc/udp in the inetd configuration file, but not for rpc/tcp services
+such as rexd. You probably do not want to run rexd anyway. On most
+systems it is even less secure than a wildcard in /etc/hosts.equiv.
+
+Install the tcpd command in a suitable place. Apollo UNIX users will
+want to install it under a different name because the name "tcpd" is
+already taken; a suitable name would be "frontd".
+
+File protections: the wrapper, all files used by the wrapper, and all
+directories in the path leading to those files, should be accessible
+but not writable for unprivileged users (mode 755 or mode 555). Do not
+install the wrapper set-uid.
+
+Then perform the following edits on the inetd configuration file
+(usually /etc/inetd.conf or /etc/inet/inetd.conf):
+
+ finger stream tcp nowait nobody /usr/etc/in.fingerd in.fingerd
+ ^^^^^^^^^^^^^^^^^^^
+becomes:
+
+ finger stream tcp nowait nobody /usr/etc/tcpd in.fingerd
+ ^^^^^^^^^^^^^
+Send a `kill -HUP' to the inetd process to make the change effective.
+Some IRIX inetd implementations require that you first disable the
+finger service (comment out the finger service and `kill -HUP' the
+inetd) before you can turn on the modified version. Sending a HUP
+twice seems to work just as well for IRIX 5.3, 6.0, 6.0.1 and 6.1.
+
+AIX note: you may have to execute the `inetimp' command after changing
+the inetd configuration file.
+
+The example applies to SunOS 4. With other UNIX implementations the
+network daemons live in /usr/libexec, /usr/sbin, or /etc, the network
+daemons have no "in." prefix to their names, or the username field in
+the inetd configuration file may be missing.
+
+When the finger service works as expected you can perform similar
+changes for other network services. Do not forget the `kill -HUP'.
+
+The miscd daemon that comes with Ultrix implements several network
+services. It decides what to do by looking at its process name. One of
+the services is systat, which is a kind of limited finger service. If
+you want to monitor the systat service, install the miscd wrapper in a
+suitable place and update the inetd configuration file:
+
+ systat stream tcp nowait /suitable/place/miscd systatd
+
+Ultrix 4.3 allows you to specify a user id under which the daemon will
+be executed. This feature is not documented in the manual pages. Thus,
+the example would become:
+
+ systat stream tcp nowait nobody /suitable/place/miscd systatd
+
+Older Ultrix systems still run all their network daemons as root.
+
+In the absence of any access-control tables, the daemon wrappers
+will just maintain a record of network connections made to your system.
+
+7.3 - Daemons with arbitrary path names
+---------------------------------------
+
+The above tcpd examples work fine with network daemons that live in a
+common directory, but sometimes that is not practical. Having soft
+links all over your file system is not a clean solution, either.
+
+Instead you can specify, in the inetd configuration file, an absolute
+path name for the daemon process name. For example,
+
+ ntalk dgram udp wait root /usr/etc/tcpd /usr/local/lib/ntalkd
+
+When the daemon process name is an absolute path name, tcpd ignores the
+value of the REAL_DAEMON_DIR constant, and uses the last path component
+of the daemon process name for logging and for access control.
+
+7.4 - Building and testing the access control rules
+---------------------------------------------------
+
+In order to support access control the wrappers must be compiled with
+the -DHOSTS_ACCESS option. The access control policy is given in the
+form of two tables (default: /etc/hosts.allow and /etc/hosts.deny).
+Access control is disabled when there are no access control tables, or
+when the tables are empty.
+
+If you haven't used the wrappers before I recommend that you first run
+them a couple of days without any access control restrictions. The
+logfile records should give you an idea of the process names and of the
+host names that you will have to build into your access control rules.
+
+The syntax of the access control rules is documented in the file
+hosts_access.5, which is in `nroff -man' format. This is a lengthy
+document, and no-one expects you to read it right away from beginning
+to end. Instead, after reading the introductory section, skip to the
+examples at the end so that you get a general idea of the language.
+Then you can appreciate the detailed reference sections near the
+beginning of the document.
+
+The examples in the hosts_access.5 document (`nroff -man' format) show
+two specific types of access control policy: 1) mostly closed (only
+permitting access from a limited number of systems) and 2) mostly open
+(permitting access from everyone except a limited number of trouble
+makers). You will have to choose what model suits your situation best.
+Implementing a mixed policy should not be overly difficult either.
+
+Optional extensions to the access control language are described in the
+hosts_options.5 document (`nroff -man' format).
+
+The `tcpdchk' program examines all rules in your access control files
+and reports any problems it can find. `tcpdchk -v' writes to standard
+output a pretty-printed list of all rules. `tcpdchk -d' examines the
+hosts.access and hosts.allow files in the current directory. This
+program is described in the tcpdchk.8 document (`nroff -man' format).
+
+The `tcpdmatch' command can be used to try out your local access
+control files. The command syntax is:
+
+ tcpdmatch process_name hostname (e.g.: tcpdmatch in.tftpd localhost)
+
+ tcpdmatch process_name address (e.g.: tcpdmatch in.tftpd 127.0.0.1)
+
+This way you can simulate what decisions will be made, and what actions
+will be taken, when hosts connect to your own system. The program is
+described in the tcpdmatch.8 document (`nroff -man' format).
+
+Note 1: `tcpdmatch -d' will look for hosts.{allow,deny} tables in the
+current working directory. This is useful for testing new rules without
+bothering your users.
+
+Note 2: you cannot use the `tcpdmatch' command to simulate what happens
+when the local system connects to other hosts.
+
+In order to find out what process name to use, just use the service and
+watch the process name that shows up in the logfile. Alternatively,
+you can look up the name from the inetd configuration file. Coming back
+to the tftp example in the tutorial section above:
+
+ tftp dgram udp wait root /usr/etc/tcpd in.tftpd -s /tftpboot
+
+This entry causes the inetd to run the wrapper program (tcpd) with a
+process name `in.tftpd'. This is the name that the wrapper will use
+when scanning the access control tables. Therefore, `in.tftpd' is the
+process name that should be given to the `tcpdmatch' command. On your
+system the actual inetd.conf entry may differ (tftpd instead of
+in.tftpd, and no `root' field), but you get the idea.
+
+When you specify a host name, the `tcpdmatch' program will use both the
+host name and address. This way you can simulate the most common case
+where the wrappers know both the host address and the host name. The
+`tcpdmatch' program will iterate over all addresses that it can find
+for the given host name.
+
+When you specify a host address instead of a host name, the `tcpdmatch'
+program will pretend that the host name is unknown, so that you can
+simulate what happens when the wrapper is unable to look up the client
+host name.
+
+7.5 - Other applications
+------------------------
+
+The access control routines can easily be integrated with other
+programs. The hosts_access.3 manual page (`nroff -man' format)
+describes the external interface of the libwrap.a library.
+
+The tcpd program can even be used to control access to the mail
+service. This can be useful when you suspect that someone is trying
+out some obscure sendmail bug, or when a remote site is misconfigured
+and keeps hammering your mail daemon.
+
+In that case, sendmail should not be run as a stand-alone network
+listener, but it should be registered in the inetd configuration file.
+For example:
+
+ smtp stream tcp nowait root /usr/etc/tcpd /usr/lib/sendmail -bs
+
+You will still need to run one sendmail background process to handle
+queued-up outgoing mail. A command like:
+
+ /usr/lib/sendmail -q15m
+
+(no `-bd' flag) should take care of that. You cannot really prevent
+people from posting forged mail this way, because there are many
+unprotected smtp daemons on the network.
+
+8 - Acknowledgements
+--------------------
+
+Many people contributed to the evolution of the programs, by asking
+inspiring questions, by suggesting features or bugfixes, or by
+submitting source code. Nevertheless, all mistakes and bugs in the
+wrappers are my own.
+
+Thanks to Brendan Kehoe (cs.widener.edu), Heimir Sverrisson (hafro.is)
+and Dan Bernstein (kramden.acf.nyu.edu) for feedback on an early
+release of this product. The host name/address check was suggested by
+John Kimball (src.honeywell.com). Apollo's UNIX environment has some
+peculiar quirks: Willem-Jan Withagen (eb.ele.tue.nl), Pieter
+Schoenmakers (es.ele.tue.nl) and Charles S. Fuller (wccs.psc.edu)
+provided assistance. Hal R. Brand (addvax.llnl.gov) told me how to
+get the client IP address in case of datagram-oriented services, and
+suggested the optional shell command feature. Shabbir Safdar
+(mentor.cc.purdue.edu) provided a first version of a much-needed manual
+page. Granville Boman Goza, IV (sei.cmu.edu) suggested to use the
+client IP address even when the host name is available. Casper H.S.
+Dik (fwi.uva.nl) provided additional insight into DNS spoofing
+techniques. The bogus daemon feature was inspired by code from Andrew
+Macpherson (BNR Europe Ltd). Steve Bellovin (research.att.com)
+confirmed some of my suspicions about the darker sides of TCP/IP
+insecurity. Risks of automated fingers were pointed out by Borja Marcos
+(we.lc.ehu.es). Brad Plecs (jhuspo.ca.jhu.edu) was kind enough to try
+my early TLI code and to work out how DG/UX differs from Solaris.
+
+John P. Rouillard (cs.umb.edu) deserves special mention for his
+persistent, but constructive, nagging about wrong or missing things,
+and for trying out and discussing embryonic code or ideas.
+
+Last but not least, Howard Chu (hanauma.jpl.nasa.gov), Darren Reed
+(coombs.anu.edu.au), Icarus Sparry (gdr.bath.ac.uk), Scott Schwartz
+(cs.psu.edu), John A. Kunze (violet.berkeley.edu), Daniel Len Schales
+(engr.latech.edu), Chris Turbeville (cse.uta.edu), Paul Kranenburg
+(cs.few.eur.nl), Marc Boucher (cam.org), Dave Mitchell
+(dcs.shef.ac.uk), Andrew Maffei, Adrian van Bloois, Rop Gonggrijp, John
+C. Wingenbach, Everett F. Batey and many, many others provided fixes,
+code fragments, or ideas for improvements.
+
+ Wietse Venema (wietse@wzv.win.tue.nl)
+ Department of Mathematics and Computing Science
+ Eindhoven University of Technology
+ P.O. Box 513
+ 5600 MB Eindhoven
+ The Netherlands
+
+ Currently visiting IBM T.J. Watson Research, Hawthorne NY, USA.
--- /dev/null
+/Makefile/1.2/Tue Jun 24 02:12:23 1997//
+/safe_finger.8/1.10/Thu May 31 19:19:41 2007//
+/safe_finger.c/1.4/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/tcpd/safe_finger
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 1997/06/24 02:12:23 downsj Exp $
+
+PROG= safe_finger
+MAN= safe_finger.8
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $OpenBSD: safe_finger.8,v 1.10 2007/05/31 19:19:41 jmc Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt SAFE_FINGER 8
+.Os
+.Sh NAME
+.Nm safe_finger
+.Nd TCP wrapper for finger program
+.Sh SYNOPSIS
+.Nm safe_finger
+.Op Ar arguments
+.Sh DESCRIPTION
+.Nm
+is simply a wrapper around the
+.Xr finger 1
+program, meant for use in
+.Xr tcpd 8
+rulesets.
+It accepts exactly the same arguments as
+.Xr finger 1 .
+.Sh SEE ALSO
+.Xr finger 1 ,
+.Xr tcpd 8
+.Sh AUTHORS
+.Bd -unfilled -offset indent
+Wietse Venema (wietse@wzv.win.tue.nl),
+Department of Mathematics and Computing Science,
+Eindhoven University of Technology
+Den Dolech 2, P.O. Box 513,
+5600 MB Eindhoven, The Netherlands
+.Ed
--- /dev/null
+/* $OpenBSD: safe_finger.c,v 1.4 2009/10/27 23:59:31 deraadt Exp $ */
+
+ /*
+ * safe_finger - finger client wrapper that protects against nasty stuff
+ * from finger servers. Use this program for automatic reverse finger
+ * probes, not the raw finger command.
+ *
+ * Build with: cc -o safe_finger safe_finger.c
+ *
+ * The problem: some programs may react to stuff in the first column. Other
+ * programs may get upset by thrash anywhere on a line. File systems may
+ * fill up as the finger server keeps sending data. Text editors may bomb
+ * out on extremely long lines. The finger server may take forever because
+ * it is somehow wedged. The code below takes care of all this badness.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+/* System libraries */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+
+/* Local stuff */
+
+char path[] = "PATH=/bin:/usr/bin:/usr/sbin:/sbin";
+
+#define TIME_LIMIT 60 /* Do not keep listinging forever */
+#define INPUT_LENGTH 100000 /* Do not keep listinging forever */
+#define LINE_LENGTH 128 /* Editors can choke on long lines */
+#define FINGER_PROGRAM "finger" /* Most, if not all, UNIX systems */
+#define UNPRIV_NAME "nobody" /* Preferred privilege level */
+#define UNPRIV_UGID 32767 /* Default uid and gid */
+
+int finger_pid;
+
+int pipe_stdin(char **);
+
+void cleanup(sig)
+int sig;
+{
+ kill(finger_pid, SIGKILL);
+ _exit(0);
+}
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int c;
+ int line_length = 0;
+ int finger_status;
+ int wait_pid;
+ int input_count = 0;
+ struct passwd *pwd;
+
+ /*
+ * First of all, let's don't run with superuser privileges.
+ */
+ if (getuid() == 0 || geteuid() == 0) {
+ if ((pwd = getpwnam(UNPRIV_NAME)) && pwd->pw_uid > 0) {
+ setgid(pwd->pw_gid);
+ setuid(pwd->pw_uid);
+ } else {
+ setgid(UNPRIV_UGID);
+ setuid(UNPRIV_UGID);
+ }
+ }
+
+ /*
+ * Redirect our standard input through the raw finger command.
+ */
+ if (putenv(path)) {
+ fprintf(stderr, "%s: putenv: out of memory", argv[0]);
+ exit(1);
+ }
+ argv[0] = FINGER_PROGRAM;
+ finger_pid = pipe_stdin(argv);
+
+ /*
+ * Don't wait forever (Peter Wemm <peter@gecko.DIALix.oz.au>).
+ */
+ signal(SIGALRM, cleanup);
+ (void) alarm(TIME_LIMIT);
+
+ /*
+ * Main filter loop.
+ */
+ while ((c = getchar()) != EOF) {
+ if (input_count++ >= INPUT_LENGTH) { /* don't listen forever */
+ fclose(stdin);
+ printf("\n\n Input truncated to %d bytes...\n", input_count - 1);
+ break;
+ }
+ if (c == '\n') { /* good: end of line */
+ putchar(c);
+ line_length = 0;
+ } else {
+ if (line_length >= LINE_LENGTH) { /* force end of line */
+ printf("\\\n");
+ line_length = 0;
+ }
+ if (line_length == 0) { /* protect left margin */
+ putchar(' ');
+ line_length++;
+ }
+ if (isascii(c) && (isprint(c) || isspace(c))) { /* text */
+ if (c == '\\') {
+ putchar(c);
+ line_length++;
+ }
+ putchar(c);
+ line_length++;
+ } else { /* quote all other thash */
+ printf("\\%03o", c & 0377);
+ line_length += 4;
+ }
+ }
+ }
+
+ /*
+ * Wait until the finger child process has terminated and account for its
+ * exit status. Which will always be zero on most systems.
+ */
+ while ((wait_pid = wait(&finger_status)) != -1 && wait_pid != finger_pid)
+ /* void */ ;
+ return (wait_pid != finger_pid || finger_status != 0);
+}
+
+/* perror_exit - report system error text and terminate */
+
+void perror_exit(text)
+char *text;
+{
+ perror(text);
+ exit(1);
+}
+
+/* pipe_stdin - pipe stdin through program (from my ANSI to OLD C converter) */
+
+int pipe_stdin(argv)
+char **argv;
+{
+ int pipefds[2];
+ int pid;
+ int i;
+ struct stat st;
+
+ /*
+ * The code that sets up the pipe requires that file descriptors 0,1,2
+ * are already open. All kinds of mysterious things will happen if that
+ * is not the case. The following loops makes sure that descriptors 0,1,2
+ * are set up properly.
+ */
+
+ for (i = 0; i < 3; i++) {
+ if (fstat(i, &st) == -1 && open("/dev/null", O_RDWR) != i)
+ perror_exit("open /dev/null");
+ }
+
+ /*
+ * Set up the pipe that interposes the command into our standard input
+ * stream.
+ */
+
+ if (pipe(pipefds))
+ perror_exit("pipe");
+
+ switch (pid = fork()) {
+ case -1: /* error */
+ perror_exit("fork");
+ /* NOTREACHED */
+ case 0: /* child */
+ (void) close(pipefds[0]); /* close reading end */
+ (void) close(1); /* connect stdout to pipe */
+ if (dup(pipefds[1]) != 1)
+ perror_exit("dup");
+ (void) close(pipefds[1]); /* close redundant fd */
+ (void) execvp(argv[0], argv);
+ perror_exit(argv[0]);
+ /* NOTREACHED */
+ default: /* parent */
+ (void) close(pipefds[1]); /* close writing end */
+ (void) close(0); /* connect stdin to pipe */
+ if (dup(pipefds[0]) != 0)
+ perror_exit("dup");
+ (void) close(pipefds[0]); /* close redundant fd */
+ return (pid);
+ }
+}
--- /dev/null
+/Makefile/1.1/Wed Feb 26 06:17:04 1997//
+/tcpd.8/1.19/Thu May 31 19:19:41 2007//
+/tcpd.c/1.4/Tue Oct 27 23:59:31 2009//
+D
--- /dev/null
+src/libexec/tcpd/tcpd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 1997/02/26 06:17:04 downsj Exp $
+
+PROG= tcpd
+MAN= tcpd.8
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/tcpd/tcpd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: tcpd.8,v 1.19 2007/05/31 19:19:41 jmc Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt TCPD 8
+.Os
+.Sh NAME
+.Nm tcpd
+.Nd tcp wrappers access control facility for internet services
+.Sh DESCRIPTION
+The
+.Nm
+program can be set up to monitor incoming requests for
+.Xr telnet 1 ,
+.Xr finger 1 ,
+.Xr ftp 1 ,
+.Xr rsh 1 ,
+.Xr tftp 1 ,
+.Xr talk 1 ,
+.Xr comsat 8 ,
+and other services that have a one-to-one mapping onto executable files.
+.Pp
+.\" The program supports both
+.\" .Bx 4.3 -style
+.\" sockets and System V.4-style
+.\" TLI. Functionality may be limited when the protocol underneath TLI is
+.\" not an internet protocol.
+.\" .Pp
+Operation is as follows: whenever a request for service arrives, the
+.Xr inetd 8
+daemon is tricked into running the
+.Nm
+program instead of the desired server.
+.Nm
+logs the request and does some additional checks.
+When all is well,
+.Nm
+runs the appropriate server program and goes away.
+.Pp
+Optional features are: pattern-based access control, client username
+lookups with the RFC 931 etc. protocol, protection against hosts that
+pretend to have someone else's host name, and protection against hosts
+that pretend to have someone else's network address.
+.Sh LOGGING
+Connections that are monitored by
+.Nm
+are reported through the
+.Xr syslog 3
+facility.
+Each record contains a time stamp, the client host name and
+the name of the requested service.
+The information can be useful to detect unwanted activities,
+especially when logfile information from several hosts is merged.
+.Pp
+In order to find out where your logs are going, examine the syslog
+configuration file, usually
+.Pa /etc/syslog.conf .
+.Sh ACCESS CONTROL
+Optionally,
+.Nm
+supports a simple form of access control that is based on pattern matching.
+The access-control software provides hooks for the execution
+of shell commands when a pattern fires.
+For details, see the
+.Xr hosts_access 5
+manual page.
+.Sh HOST NAME VERIFICATION
+The authentication scheme of some protocols
+.Pq Xr rsh 1
+relies on host names.
+Some implementations believe the host name that they get from any random
+name server; other implementations are more careful but use a flawed algorithm.
+.Pp
+.Nm
+verifies the client host name that is returned by the address->name DNS
+server by looking at the host name and address that are returned by the
+name->address DNS server.
+If any discrepancy is detected,
+.Nm
+concludes that it is dealing with a host that pretends to have someone
+elses host name.
+.\" .Pp
+.\" If the sources are compiled with -DPARANOID,
+.\" .Nm tcpd
+.\" will drop the connection in case of a host name/address mismatch.
+.\" Otherwise, the hostname can be matched with the
+.\" .Ar PARANOID
+.\" wildcard,
+.\" after which suitable action can be taken.
+.Sh HOST ADDRESS SPOOFING
+Optionally,
+.Nm
+disables source-routing socket options on every connection that it deals with.
+This will take care of most attacks from hosts that pretend
+to have an address that belongs to someone else's network.
+UDP services do not benefit from this protection.
+This feature must be turned on at compile-time.
+.Sh RFC 931
+When RFC 931 etc. lookups are enabled (compile-time option)
+.Nm
+will attempt to establish the name of the client user.
+This will succeed only if the client host runs an RFC 931-compliant daemon.
+Client user name lookups will not work for datagram-oriented
+connections, and may cause noticeable delays in the case of connections
+from PCs.
+.Sh FILES
+The default locations of the host access control tables are:
+.Pp
+.Bl -tag -width /etc/hosts.allow -compact
+.It Pa /etc/hosts.allow
+Access control table (allow list)
+.It Pa /etc/hosts.deny
+Access control table (deny list)
+.El
+.\" .Sh EXAMPLES
+.\" The details of using
+.\" .Nm tcpd
+.\" depend on pathname information that was compiled into the program.
+.\" .Sh EXAMPLE 1
+.\" This example applies when
+.\" .Nm tcpd
+.\" expects that the original network
+.\" daemons will be moved to an "other" place.
+.\" .Pp
+.\" In order to monitor access to the
+.\" .Xr finger 1
+.\" service, move the
+.\" original finger daemon to the "other" place and install tcpd in the
+.\" place of the original finger daemon. No changes are required to
+.\" configuration files.
+.\" .Bd -unfilled -offset indent
+.\" # mkdir /other/place
+.\" # mv /usr/etc/in.fingerd /other/place
+.\" # cp tcpd /usr/etc/in.fingerd
+.\" .Ed
+.\" .Pp
+.\" The example assumes that the network daemons live in /usr/etc. On some
+.\" systems, network daemons live in /usr/sbin or in /usr/libexec, or have
+.\" no `in.\' prefix to their name.
+.\" .Sh EXAMPLE 2
+.Sh EXAMPLES
+This example applies when
+.Nm
+expects that the network daemons
+are left in their original place, as it is configured within
+.Ox .
+.Pp
+In order to monitor access to the
+.Xr finger 1
+service, perform the following edits on the
+.Xr inetd 8
+configuration file,
+.Pa /etc/inetd.conf :
+.Bd -unfilled -offset indent
+finger stream tcp nowait nobody /usr/libexec/fingerd fingerd
+.Ed
+.Pp
+becomes:
+.Bd -unfilled -offset indent
+finger stream tcp nowait nobody /usr/libexec/tcpd fingerd
+.Ed
+.\" .Pp
+.\" The example assumes that the network daemons live in /usr/etc. On some
+.\" systems, network daemons live in /usr/sbin or in /usr/libexec, the
+.\" daemons have no `in.\' prefix to their name, or there is no userid
+.\" field in the inetd configuration file.
+.Pp
+Similar changes will be needed for the other services that are to be
+covered by
+.Nm tcpd .
+Send a `kill -HUP\' to the
+.Xr inetd 8
+process to make the changes effective.
+.\" AIX users may also have to execute the `inetimp\' command.
+.\" .Sh EXAMPLE 3
+.Pp
+In the case of daemons that do not live in a common directory ("secret"
+or otherwise), edit the
+.Xr inetd 8
+configuration file so that it specifies an absolute path name for the process
+name field.
+For example:
+.Bd -unfilled
+ ntalk dgram udp wait root /usr/libexec/tcpd /usr/local/lib/ntalkd
+.Ed
+.Pp
+Only the last component
+.Pq Nm ntalkd
+of the pathname will be used for access control and logging.
+.Sh SEE ALSO
+.Xr hosts_access 5 ,
+.Xr inetd.conf 5 ,
+.Xr syslog.conf 5
+.Sh AUTHORS
+.Bd -unfilled -offset indent
+Wietse Venema (wietse@wzv.win.tue.nl),
+Department of Mathematics and Computing Science,
+Eindhoven University of Technology
+Den Dolech 2, P.O. Box 513,
+5600 MB Eindhoven, The Netherlands
+.Ed
+.\" @(#) tcpd.8 1.5 96/02/21 16:39:16
+.Sh BUGS
+Some UDP (and RPC) daemons linger around for a while after they have
+finished their work, in case another request comes in.
+In the inetd configuration file these services are registered with the
+.Ar wait
+option.
+Only the request that started such a daemon will be logged.
+.Pp
+.\" The program does not work with RPC services over TCP. These services
+.\" are registered as
+.\" .Ar rpc/tcp
+.\" in the inetd configuration file. The
+.\" only non-trivial service that is affected by this limitation is
+.\" .Xr rexd 8 ,
+.\" which is used by the
+.\" .Xr on 1
+.\" command. This is no great
+.\" loss. On most systems,
+.\" .Xr rexd 8
+.\" is less secure than a wildcard in
+.\" .Pa /etc/hosts.equiv .
+.\" .Pp
+RPC broadcast requests (for example:
+.Xr rwall 1 ,
+.Xr rup 1 ,
+.Xr rusers 1 )
+always appear to come from the responding host.
+What happens is that the client broadcasts the request to all
+.Xr portmap 8
+daemons on its network; each
+.Xr portmap 8
+daemon forwards the request to a local daemon.
+As far as the
+.Xr rwalld 8
+etc. daemons know, the request comes from the local host.
--- /dev/null
+/* $OpenBSD: tcpd.c,v 1.4 2009/10/27 23:59:31 deraadt Exp $ */
+
+ /*
+ * General front end for stream and datagram IP services. This program logs
+ * the remote host name and then invokes the real daemon. For example,
+ * install as /usr/etc/{tftpd,fingerd,telnetd,ftpd,rlogind,rshd,rexecd},
+ * after saving the real daemons in the directory specified with the
+ * REAL_DAEMON_DIR macro. This arrangement requires that the network daemons
+ * are started by inetd or something similar. Connections and diagnostics
+ * are logged through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+/* System libraries. */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tcpd.h>
+
+#ifndef MAXPATHNAMELEN
+#define MAXPATHNAMELEN BUFSIZ
+#endif
+
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+/* Local stuff. */
+
+int allow_severity = SEVERITY; /* run-time adjustable */
+int deny_severity = LOG_WARNING; /* ditto */
+
+int main(int argc, char *argv[])
+{
+ struct request_info request;
+ char path[MAXPATHNAMELEN];
+
+ /* Attempt to prevent the creation of world-writable files. */
+
+#ifdef DAEMON_UMASK
+ umask(DAEMON_UMASK);
+#endif
+
+ /*
+ * If argv[0] is an absolute path name, ignore REAL_DAEMON_DIR, and strip
+ * argv[0] to its basename.
+ */
+
+ if (argv[0][0] == '/') {
+ strlcpy(path, argv[0], sizeof path);
+ argv[0] = strrchr(argv[0], '/') + 1;
+ } else {
+ snprintf(path, sizeof path, "%s/%s", REAL_DAEMON_DIR, argv[0]);
+ }
+
+ /*
+ * Open a channel to the syslog daemon. Older versions of openlog()
+ * require only two arguments.
+ */
+
+#ifdef LOG_MAIL
+ (void) openlog(argv[0], LOG_PID, FACILITY);
+#else
+ (void) openlog(argv[0], LOG_PID);
+#endif
+
+ /*
+ * Find out the endpoint addresses of this conversation. Host name
+ * lookups and double checks will be done on demand.
+ */
+
+ request_init(&request, RQ_DAEMON, argv[0], RQ_FILE, STDIN_FILENO, 0);
+ fromhost(&request);
+
+ /*
+ * Optionally look up and double check the remote host name. Sites
+ * concerned with security may choose to refuse connections from hosts
+ * that pretend to have someone elses host name.
+ */
+
+#ifdef PARANOID
+ if (STR_EQ(eval_hostname(request.client), paranoid))
+ refuse(&request);
+#endif
+
+ /*
+ * The BSD rlogin and rsh daemons that came out after 4.3 BSD disallow
+ * socket options at the IP level. They do so for a good reason.
+ * Unfortunately, we cannot use this with SunOS 4.1.x because the
+ * getsockopt() system call can panic the system.
+ */
+
+#ifdef KILL_IP_OPTIONS
+ fix_options(&request);
+#endif
+
+ /*
+ * Check whether this host can access the service in argv[0]. The
+ * access-control code invokes optional shell commands as specified in
+ * the access-control tables.
+ */
+
+#ifdef HOSTS_ACCESS
+ if (!hosts_access(&request))
+ refuse(&request);
+#endif
+
+ /* Report request and invoke the real daemon program. */
+
+ syslog(allow_severity, "connect from %s", eval_client(&request));
+ closelog();
+ (void) execv(path, argv);
+ syslog(LOG_ERR, "error: cannot execute %s: %m", path);
+ clean_exit(&request);
+ /* NOTREACHED */
+}
--- /dev/null
+/Makefile/1.1/Wed Feb 26 06:17:06 1997//
+/inetcf.c/1.5/Tue Oct 27 23:59:32 2009//
+/inetcf.h/1.2/Sat Feb 16 21:27:31 2002//
+/scaffold.c/1.8/Tue Oct 27 23:59:32 2009//
+/scaffold.h/1.3/Fri Jun 7 03:32:04 2002//
+/tcpdchk.8/1.13/Sat May 17 23:31:52 2008//
+/tcpdchk.c/1.11/Tue Oct 27 23:59:32 2009//
+D
--- /dev/null
+src/libexec/tcpd/tcpdchk
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 1997/02/26 06:17:06 downsj Exp $
+
+PROG= tcpdchk
+MAN= tcpdchk.8
+
+SRCS= inetcf.c scaffold.c tcpdchk.c
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+BINDIR= /usr/sbin
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: inetcf.c,v 1.5 2009/10/27 23:59:32 deraadt Exp $ */
+
+ /*
+ * Routines to parse an inetd.conf or tlid.conf file. This would be a great
+ * job for a PERL script.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <tcpd.h>
+
+#include "inetcf.h"
+#include "scaffold.h"
+
+ /*
+ * Network configuration files may live in unusual places. Here are some
+ * guesses. Shorter names follow longer ones.
+ */
+char *inet_files[] = {
+ "/private/etc/inetd.conf", /* NEXT */
+ "/etc/inet/inetd.conf", /* SYSV4 */
+ "/usr/etc/inetd.conf", /* IRIX?? */
+ "/etc/inetd.conf", /* BSD */
+ "/etc/net/tlid.conf", /* SYSV4?? */
+ "/etc/saf/tlid.conf", /* SYSV4?? */
+ "/etc/tlid.conf", /* SYSV4?? */
+ 0,
+};
+
+static void inet_chk(char *, char *, char *, char *);
+static char *base_name(char *);
+
+ /*
+ * Structure with everything we know about a service.
+ */
+struct inet_ent {
+ struct inet_ent *next;
+ int type;
+ char name[1];
+};
+
+static struct inet_ent *inet_list = 0;
+
+static char whitespace[] = " \t\r\n";
+
+/* inet_conf - read in and examine inetd.conf (or tlid.conf) entries */
+
+char *inet_cfg(conf)
+char *conf;
+{
+ char buf[BUFSIZ];
+ FILE *fp = (FILE *)NULL;
+ char *service;
+ char *protocol;
+ char *user;
+ char *path;
+ char *arg0;
+ char *arg1;
+ struct tcpd_context saved_context;
+ int i;
+ struct stat st;
+
+ saved_context = tcpd_context;
+
+ /*
+ * The inetd.conf (or tlid.conf) information is so useful that we insist
+ * on its availability. When no file is given run a series of educated
+ * guesses.
+ */
+ if (conf != 0) {
+ if ((fp = fopen(conf, "r")) == (FILE *)NULL) {
+ fprintf(stderr, percent_m(buf, "open %s: %m\n"), conf);
+ exit(1);
+ }
+ } else {
+ for (i = 0; inet_files[i] && (fp = fopen(inet_files[i], "r")) == 0; i++)
+ /* void */ ;
+ if (fp == (FILE *)NULL) {
+ fprintf(stderr, "Cannot find your inetd.conf or tlid.conf file.\n");
+ fprintf(stderr, "Please specify its location.\n");
+ exit(1);
+ }
+ conf = inet_files[i];
+ check_path(conf, &st);
+ }
+
+ /*
+ * Process the file. After the 7.0 wrapper release it became clear that
+ * there are many more inetd.conf formats than the 8 systems that I had
+ * studied. EP/IX uses a two-line specification for rpc services; HP-UX
+ * permits long lines to be broken with backslash-newline.
+ */
+ tcpd_context.file = conf;
+ tcpd_context.line = 0;
+ while (xgets(buf, sizeof(buf), fp)) {
+ service = strtok(buf, whitespace); /* service */
+ if (service == 0 || *service == '#')
+ continue;
+ if (STR_NE(service, "stream") && STR_NE(service, "dgram"))
+ strtok((char *) 0, whitespace); /* endpoint */
+ protocol = strtok((char *) 0, whitespace);
+ (void) strtok((char *) 0, whitespace); /* wait */
+ if ((user = strtok((char *) 0, whitespace)) == 0)
+ continue;
+ if (user[0] == '/') { /* user */
+ path = user;
+ } else { /* path */
+ if ((path = strtok((char *) 0, whitespace)) == 0)
+ continue;
+ }
+ if (path[0] == '?') /* IRIX optional service */
+ path++;
+ if (STR_EQ(path, "internal"))
+ continue;
+ if (path[strspn(path, "-0123456789")] == 0) {
+
+ /*
+ * ConvexOS puts RPC version numbers before path names. Jukka
+ * Ukkonen <ukkonen@csc.fi>.
+ */
+ if ((path = strtok((char *) 0, whitespace)) == 0)
+ continue;
+ }
+ if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
+ tcpd_warn("incomplete line");
+ continue;
+ }
+ if (arg0[strspn(arg0, "0123456789")] == 0) {
+
+ /*
+ * We're reading a tlid.conf file, the format is:
+ *
+ * ...stuff... path arg_count arguments mod_count modules
+ */
+ if ((arg0 = strtok((char *) 0, whitespace)) == 0) {
+ tcpd_warn("incomplete line");
+ continue;
+ }
+ }
+ if ((arg1 = strtok((char *) 0, whitespace)) == 0)
+ arg1 = "";
+
+ inet_chk(protocol, path, arg0, arg1);
+ }
+ fclose(fp);
+ tcpd_context = saved_context;
+ return (conf);
+}
+
+/* inet_chk - examine one inetd.conf (tlid.conf?) entry */
+
+static void inet_chk(protocol, path, arg0, arg1)
+char *protocol;
+char *path;
+char *arg0;
+char *arg1;
+{
+ char daemon[BUFSIZ];
+ struct stat st;
+ int wrap_status = WR_MAYBE;
+ char *base_name_path = base_name(path);
+ char *tcpd_proc_name = (arg0[0] == '/' ? base_name(arg0) : arg0);
+
+ /*
+ * Always warn when the executable does not exist or when it is not
+ * executable.
+ */
+ if (check_path(path, &st) < 0) {
+ tcpd_warn("%s: not found: %m", path);
+ } else if ((st.st_mode & 0100) == 0) {
+ tcpd_warn("%s: not executable", path);
+ }
+
+ /*
+ * Cheat on the miscd tests, nobody uses it anymore.
+ */
+ if (STR_EQ(base_name_path, "miscd")) {
+ inet_set(arg0, WR_YES);
+ return;
+ }
+
+ /*
+ * While we are here...
+ */
+ if (STR_EQ(tcpd_proc_name, "rexd") || STR_EQ(tcpd_proc_name, "rpc.rexd"))
+ tcpd_warn("%s may be an insecure service", tcpd_proc_name);
+
+ /*
+ * The tcpd program gets most of the attention.
+ */
+ if (STR_EQ(base_name_path, "tcpd")) {
+
+ if (STR_EQ(tcpd_proc_name, "tcpd"))
+ tcpd_warn("%s is recursively calling itself", tcpd_proc_name);
+
+ wrap_status = WR_YES;
+
+ /*
+ * Check: some sites install the wrapper set-uid.
+ */
+ if ((st.st_mode & 06000) != 0)
+ tcpd_warn("%s: file is set-uid or set-gid", path);
+
+ /*
+ * Check: some sites insert tcpd in inetd.conf, instead of replacing
+ * the daemon pathname.
+ */
+ if (arg0[0] == '/' && STR_EQ(tcpd_proc_name, base_name(arg1)))
+ tcpd_warn("%s inserted before %s", path, arg0);
+
+ /*
+ * Check: make sure files exist and are executable. On some systems
+ * the network daemons are set-uid so we cannot complain. Note that
+ * tcpd takes the basename only in case of absolute pathnames.
+ */
+ if (arg0[0] == '/') { /* absolute path */
+ if (check_path(arg0, &st) < 0) {
+ tcpd_warn("%s: not found: %m", arg0);
+ } else if ((st.st_mode & 0100) == 0) {
+ tcpd_warn("%s: not executable", arg0);
+ }
+ } else { /* look in REAL_DAEMON_DIR */
+ snprintf(daemon, sizeof daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
+ if (check_path(daemon, &st) < 0) {
+ tcpd_warn("%s: not found in %s: %m",
+ arg0, REAL_DAEMON_DIR);
+ } else if ((st.st_mode & 0100) == 0) {
+ tcpd_warn("%s: not executable", daemon);
+ }
+ }
+
+ } else {
+
+ /*
+ * No tcpd program found. Perhaps they used the "simple installation"
+ * recipe. Look for a file with the same basename in REAL_DAEMON_DIR.
+ * Draw some conservative conclusions when a distinct file is found.
+ */
+ snprintf(daemon, sizeof daemon, "%s/%s", REAL_DAEMON_DIR, arg0);
+ if (STR_EQ(path, daemon)) {
+ wrap_status = WR_NOT;
+ } else if (check_path(daemon, &st) >= 0) {
+ wrap_status = WR_MAYBE;
+ } else if (errno == ENOENT) {
+ wrap_status = WR_NOT;
+ } else {
+ tcpd_warn("%s: file lookup: %m", daemon);
+ wrap_status = WR_MAYBE;
+ }
+ }
+
+ /*
+ * Alas, we cannot wrap rpc/tcp services.
+ */
+ if (wrap_status == WR_YES && STR_EQ(protocol, "rpc/tcp"))
+ tcpd_warn("%s: cannot wrap rpc/tcp services", tcpd_proc_name);
+
+ inet_set(tcpd_proc_name, wrap_status);
+}
+
+/* inet_set - remember service status */
+
+void inet_set(name, type)
+char *name;
+int type;
+{
+ int len = strlen(name); /* NUL is inside the struct */
+ struct inet_ent *ip =
+ (struct inet_ent *) malloc(sizeof(struct inet_ent) + len);
+
+ if (ip == 0) {
+ fprintf(stderr, "out of memory\n");
+ exit(1);
+ }
+ ip->next = inet_list;
+ strlcpy(ip->name, name, len);
+ ip->type = type;
+ inet_list = ip;
+}
+
+/* inet_get - look up service status */
+
+int inet_get(name)
+char *name;
+{
+ struct inet_ent *ip;
+
+ if (inet_list == 0)
+ return (WR_MAYBE);
+
+ for (ip = inet_list; ip; ip = ip->next)
+ if (STR_EQ(ip->name, name))
+ return (ip->type);
+
+ return (-1);
+}
+
+/* base_name - compute last pathname component */
+
+static char *base_name(path)
+char *path;
+{
+ char *cp;
+
+ if ((cp = strrchr(path, '/')) != 0)
+ path = cp + 1;
+ return (path);
+}
--- /dev/null
+/* $OpenBSD: inetcf.h,v 1.2 2002/02/16 21:27:31 millert Exp $ */
+
+ /*
+ * @(#) inetcf.h 1.1 94/12/28 17:42:30
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#include <sys/cdefs.h>
+
+extern char *inet_cfg(char *);
+extern void inet_set(char *, int);
+extern int inet_get(char *);
+
+#define WR_UNKNOWN (-1) /* service unknown */
+#define WR_NOT 1 /* may not be wrapped */
+#define WR_MAYBE 2 /* may be wrapped */
+#define WR_YES 3 /* service is wrapped */
--- /dev/null
+/usr/obj/libexec/tcpd/tcpdchk
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: scaffold.c,v 1.8 2009/10/27 23:59:32 deraadt Exp $ */
+
+ /*
+ * Routines for testing only. Not really industrial strength.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+/* System libraries. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <tcpd.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
+#endif
+
+/* Application-specific. */
+
+#include "scaffold.h"
+
+ /*
+ * These are referenced by the options module and by rfc931.c.
+ */
+int allow_severity = SEVERITY;
+int deny_severity = LOG_WARNING;
+int rfc931_timeout = RFC931_TIMEOUT;
+
+/* find_inet_addr - find all addresses for this host, result to free() */
+
+struct addrinfo *find_inet_addr(host, flags)
+char *host;
+int flags;
+{
+ struct addrinfo hints, *res;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_CANONNAME | flags;
+ error = getaddrinfo(host, "0", &hints, &res);
+ if (error) {
+ tcpd_warn("%s: %s", host, gai_strerror(error));
+ return (0);
+ }
+
+ if (res->ai_canonname && STR_NE(host, res->ai_canonname)) {
+ tcpd_warn("%s: hostname alias", host);
+ tcpd_warn("(official name: %.*s)", STRING_LENGTH, res->ai_canonname);
+ }
+ return (res);
+}
+
+/* check_dns - give each address thorough workout, return address count */
+
+int check_dns(host)
+char *host;
+{
+ struct request_info request;
+ struct sockaddr_storage ss;
+ struct addrinfo *res0, *res;
+ int count;
+
+ if ((res0 = find_inet_addr(host, 0)) == NULL)
+ return (0);
+ memset(&ss, 0, sizeof(ss));
+ request_init(&request, RQ_CLIENT_SIN, &ss, 0);
+ sock_methods(&request);
+
+ count = 0;
+ for (res = res0; res; res = res->ai_next) {
+ count++;
+ if (res->ai_addrlen > sizeof(ss))
+ continue;
+ memcpy(&ss, res->ai_addr, res->ai_addrlen);
+
+ /*
+ * Force host name and address conversions. Use the request structure
+ * as a cache. Detect hostname lookup problems. Any name/name or
+ * name/address conflicts will be reported while eval_hostname() does
+ * its job.
+ */
+ request_set(&request, RQ_CLIENT_ADDR, "", RQ_CLIENT_NAME, "", 0);
+ if (STR_EQ(eval_hostname(request.client), unknown))
+ tcpd_warn("host address %s->name lookup failed",
+ eval_hostaddr(request.client));
+ tcpd_warn("%s %s", eval_hostname(request.client), unknown);
+ }
+ freeaddrinfo(res0);
+ return (count);
+}
+
+/* dummy function to intercept the real shell_cmd() */
+
+/* ARGSUSED */
+
+void shell_cmd(command)
+char *command;
+{
+ if (hosts_access_verbose)
+ printf("command: %s", command);
+}
+
+/* dummy function to intercept the real clean_exit() */
+
+/* ARGSUSED */
+
+void clean_exit(request)
+struct request_info *request;
+{
+ exit(0);
+}
+
+/* dummy function to intercept the real rfc931() */
+
+/* ARGSUSED */
+void rfc931(a1, a2, d1)
+struct sockaddr *a1, *a2;
+char *d1;
+{
+}
+
+/* check_path - examine accessibility */
+
+int check_path(path, st)
+char *path;
+struct stat *st;
+{
+ struct stat stbuf;
+ char buf[BUFSIZ];
+
+ if (stat(path, st) < 0)
+ return (-1);
+#ifdef notdef
+ if (st->st_uid != 0)
+ tcpd_warn("%s: not owned by root", path);
+ if (st->st_mode & 020)
+ tcpd_warn("%s: group writable", path);
+#endif
+ if (st->st_mode & 002)
+ tcpd_warn("%s: world writable", path);
+ if (path[0] == '/' && path[1] != 0) {
+ strrchr((strlcpy(buf, path, sizeof buf), buf), '/')[0] = 0;
+ (void) check_path(buf[0] ? buf : "/", &stbuf);
+ }
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: scaffold.h,v 1.3 2002/06/07 03:32:04 itojun Exp $ */
+
+ /*
+ * @(#) scaffold.h 1.3 94/12/31 18:19:19
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern struct addrinfo *find_inet_addr(char *, int);
+extern int check_dns(char *);
+extern int check_path(char *, struct stat *);
+__END_DECLS
--- /dev/null
+.\" $OpenBSD: tcpdchk.8,v 1.13 2008/05/17 23:31:52 sobrado Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.Dd $Mdocdate: May 17 2008 $
+.Dt TCPDCHK 8
+.Os
+.Sh NAME
+.Nm tcpdchk
+.Nd tcp wrapper configuration checker
+.Sh SYNOPSIS
+.Nm tcpdchk
+.Op Fl adv
+.Op Fl i Ar inet_conf
+.Sh DESCRIPTION
+.Nm
+examines your tcp wrapper configuration and reports all
+potential and real problems it can find.
+The program examines the
+.Xr tcpd 8
+access control files (by default, these are
+.Pa /etc/hosts.allow
+and
+.Pa /etc/hosts.deny ) ,
+and compares the
+entries in these files against entries in the
+.Xr inetd 8
+network configuration file.
+.Pp
+.Nm
+reports problems such as non-existent pathnames; services
+that appear in
+.Xr tcpd 8
+access control rules, but are not controlled by
+.Xr tcpd 8 ;
+services that should not be wrapped; non-existent host
+names or non-internet address forms; occurrences of host aliases
+instead of official host names; hosts with a name/address conflict;
+inappropriate use of wildcard patterns; inappropriate use of NIS
+netgroups or references to non-existent NIS netgroups; references to
+non-existent options; invalid arguments to options; and so on.
+.Pp
+Where possible,
+.Nm
+provides a helpful suggestion to fix the problem.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Report access control rules that permit access without an explicit
+ALLOW keyword.
+.\" This applies only when the extended access control
+.\" language is enabled (build with -DPROCESS_OPTIONS).
+.It Fl d
+Examine
+.Pa hosts.allow
+and
+.Pa hosts.deny
+files in the current directory instead of the default ones.
+.It Fl i Ar inet_conf
+Specify this option when
+.Nm
+is unable to find your
+.Pa inetd.conf
+network configuration file, or when you wish to test with a non-default one.
+.It Fl v
+Display the contents of each access control rule.
+Daemon lists, client lists, shell commands and options are shown in a
+pretty-printed format; this makes it easier for you to spot any
+discrepancies between what you want and what the program understands.
+.El
+.Sh FILES
+.Bl -tag -width /etc/hosts.allow -compact
+.It Pa /etc/hosts.allow
+access control table (allow list)
+.It Pa /etc/hosts.deny
+access control table (deny list)
+.El
+.Sh SEE ALSO
+.Xr hosts_access 5 ,
+.Xr hosts_options 5 ,
+.Xr inetd.conf 5 ,
+.Xr tcpdmatch 8
+.Sh AUTHORS
+.Bd -unfilled -offset indent
+Wietse Venema (wietse@wzv.win.tue.nl),
+Department of Mathematics and Computing Science,
+Eindhoven University of Technology
+Den Dolech 2, P.O. Box 513,
+5600 MB Eindhoven, The Netherlands
+.Ed
+.\" @(#) tcpdchk.8 1.3 95/01/08 17:00:30
--- /dev/null
+/* $OpenBSD: tcpdchk.c,v 1.11 2009/10/27 23:59:32 deraadt Exp $ */
+
+ /*
+ * tcpdchk - examine all tcpd access control rules and inetd.conf entries
+ *
+ * Usage: tcpdchk [-a] [-d] [-i inet_conf] [-v]
+ *
+ * -a: complain about implicit "allow" at end of rule.
+ *
+ * -d: rules in current directory.
+ *
+ * -i: location of inetd.conf file.
+ *
+ * -v: show all rules.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+/* System libraries. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef NETGROUP
+#include <netgroup.h>
+#endif
+
+#include <tcpd.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* Application-specific. */
+
+#include "inetcf.h"
+#include "scaffold.h"
+
+ /*
+ * Stolen from hosts_access.c...
+ */
+static char sep[] = ", \t\n";
+
+#define BUFLEN 2048
+
+int resident = 0;
+int hosts_access_verbose = 0;
+char *hosts_allow_table = HOSTS_ALLOW;
+char *hosts_deny_table = HOSTS_DENY;
+extern jmp_buf tcpd_buf;
+
+ /*
+ * Local stuff.
+ */
+static void usage(void);
+static void parse_table(char *, struct request_info *);
+static void print_list(char *, char *);
+static void check_daemon_list(char *);
+static void check_client_list(char *);
+static void check_daemon(char *);
+static void check_user(char *);
+#ifdef INET6
+static int check_inet_addr(char *);
+#endif
+static int check_host(char *);
+static int reserved_name(char *);
+
+#define PERMIT 1
+#define DENY 0
+
+#define YES 1
+#define NO 0
+
+static int defl_verdict;
+static char *myname;
+static int allow_check;
+static char *inetcf;
+
+int main(int argc, char *argv[])
+{
+ struct request_info request;
+ struct stat st;
+ int c;
+
+ myname = argv[0];
+
+ /*
+ * Parse the JCL.
+ */
+ while ((c = getopt(argc, argv, "adi:v")) != -1) {
+ switch (c) {
+ case 'a':
+ allow_check = 1;
+ break;
+ case 'd':
+ hosts_allow_table = "hosts.allow";
+ hosts_deny_table = "hosts.deny";
+ break;
+ case 'i':
+ inetcf = optarg;
+ break;
+ case 'v':
+ hosts_access_verbose++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (argc != optind)
+ usage();
+
+ /*
+ * When confusion really strikes...
+ */
+ if (check_path(REAL_DAEMON_DIR, &st) < 0) {
+ tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
+ } else if (!S_ISDIR(st.st_mode)) {
+ tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
+ }
+
+ /*
+ * Process the inet configuration file (or its moral equivalent). This
+ * information is used later to find references in hosts.allow/deny to
+ * unwrapped services, and other possible problems.
+ */
+ inetcf = inet_cfg(inetcf);
+ if (hosts_access_verbose)
+ printf("Using network configuration file: %s\n", inetcf);
+
+ /*
+ * These are not run from inetd but may have built-in access control.
+ */
+ inet_set("portmap", WR_NOT);
+ inet_set("rpcbind", WR_NOT);
+
+ /*
+ * Check accessibility of access control files.
+ */
+ (void) check_path(hosts_allow_table, &st);
+ (void) check_path(hosts_deny_table, &st);
+
+ /*
+ * Fake up an arbitrary service request.
+ */
+ request_init(&request,
+ RQ_DAEMON, "daemon_name",
+ RQ_SERVER_NAME, "server_hostname",
+ RQ_SERVER_ADDR, "server_addr",
+ RQ_USER, "user_name",
+ RQ_CLIENT_NAME, "client_hostname",
+ RQ_CLIENT_ADDR, "client_addr",
+ RQ_FILE, 1,
+ 0);
+
+ /*
+ * Examine all access-control rules.
+ */
+ defl_verdict = PERMIT;
+ parse_table(hosts_allow_table, &request);
+ defl_verdict = DENY;
+ parse_table(hosts_deny_table, &request);
+ return (0);
+}
+
+/* usage - explain */
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-adv] [-i inet_conf]\n", myname);
+ fprintf(stderr, " -a: report rules with implicit \"ALLOW\" at end\n");
+ fprintf(stderr, " -d: use allow/deny files in current directory\n");
+ fprintf(stderr, " -i: location of inetd.conf file\n");
+ fprintf(stderr, " -v: list all rules\n");
+ exit(1);
+}
+
+/* parse_table - like table_match(), but examines _all_ entries */
+
+static void parse_table(table, request)
+char *table;
+struct request_info *request;
+{
+ FILE *fp;
+ int real_verdict;
+ char sv_list[BUFLEN]; /* becomes list of daemons */
+ char *cl_list; /* becomes list of requests */
+ char *sh_cmd; /* becomes optional shell command */
+#ifndef PROCESS_OPTIONS
+ char buf[BUFSIZ];
+#endif
+ int verdict;
+ struct tcpd_context saved_context;
+
+ saved_context = tcpd_context; /* stupid compilers */
+
+ if ((fp = fopen(table, "r")) != (FILE *)NULL) {
+ tcpd_context.file = table;
+ tcpd_context.line = 0;
+ while (xgets(sv_list, sizeof(sv_list), fp)) {
+ if (sv_list[strlen(sv_list) - 1] != '\n') {
+ tcpd_warn("missing newline or line too long");
+ continue;
+ }
+ if (sv_list[0] == '#' || sv_list[strspn(sv_list, " \t\r\n")] == 0)
+ continue;
+ if ((cl_list = split_at(sv_list, ':')) == 0) {
+ tcpd_warn("missing \":\" separator");
+ continue;
+ }
+ sh_cmd = split_at(cl_list, ':');
+
+ if (hosts_access_verbose)
+ printf("\n>>> Rule %s line %d:\n",
+ tcpd_context.file, tcpd_context.line);
+
+ if (hosts_access_verbose)
+ print_list("daemons: ", sv_list);
+ check_daemon_list(sv_list);
+
+ if (hosts_access_verbose)
+ print_list("clients: ", cl_list);
+ check_client_list(cl_list);
+
+#ifdef PROCESS_OPTIONS
+ real_verdict = defl_verdict;
+ if (sh_cmd) {
+ verdict = setjmp(tcpd_buf);
+ if (verdict != 0) {
+ real_verdict = (verdict == AC_PERMIT);
+ } else {
+ dry_run = 1;
+ process_options(sh_cmd, request);
+ if (dry_run == 1 && real_verdict && allow_check)
+ tcpd_warn("implicit \"allow\" at end of rule");
+ }
+ } else if (defl_verdict && allow_check) {
+ tcpd_warn("implicit \"allow\" at end of rule");
+ }
+ if (hosts_access_verbose)
+ printf("access: %s\n", real_verdict ? "granted" : "denied");
+#else
+ if (sh_cmd)
+ shell_cmd(percent_x(buf, sizeof(buf), sh_cmd, request));
+ if (hosts_access_verbose)
+ printf("access: %s\n", defl_verdict ? "granted" : "denied");
+#endif
+ }
+ (void) fclose(fp);
+ } else if (errno != ENOENT) {
+ tcpd_warn("cannot open %s: %m", table);
+ }
+ tcpd_context = saved_context;
+}
+
+/* print_list - pretty-print a list */
+
+static void print_list(title, list)
+char *title;
+char *list;
+{
+ char buf[BUFLEN];
+ char *cp;
+ char *next;
+
+ fputs(title, stdout);
+ strlcpy(buf, list, sizeof buf);
+
+ for (cp = strtok(buf, sep); cp != 0; cp = next) {
+ fputs(cp, stdout);
+ next = strtok((char *) 0, sep);
+ if (next != 0)
+ fputs(" ", stdout);
+ }
+ fputs("\n", stdout);
+}
+
+/* check_daemon_list - criticize daemon list */
+
+static void check_daemon_list(list)
+char *list;
+{
+ char buf[BUFLEN];
+ char *cp;
+ char *host;
+ int daemons = 0;
+
+ strlcpy(buf, list, sizeof buf);
+
+ for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
+ if (STR_EQ(cp, "EXCEPT")) {
+ daemons = 0;
+ } else {
+ daemons++;
+ if ((host = split_at(cp + 1, '@')) != 0 && check_host(host) > 1) {
+ tcpd_warn("host %s has more than one address", host);
+ tcpd_warn("(consider using an address instead)");
+ }
+ check_daemon(cp);
+ }
+ }
+ if (daemons == 0)
+ tcpd_warn("daemon list is empty or ends in EXCEPT");
+}
+
+/* check_client_list - criticize client list */
+
+static void check_client_list(list)
+char *list;
+{
+ char buf[BUFLEN];
+ char *cp;
+ char *host;
+ int clients = 0;
+#ifdef INET6
+ int l;
+#endif
+
+ strlcpy(buf, list, sizeof buf);
+
+ for (cp = strtok(buf, sep); cp != 0; cp = strtok((char *) 0, sep)) {
+#ifdef INET6
+ l = strlen(cp);
+ if (cp[0] == '[' && cp[l - 1] == ']') {
+ cp[l - 1] = '\0';
+ cp++;
+ }
+#endif
+ if (STR_EQ(cp, "EXCEPT")) {
+ clients = 0;
+ } else {
+ clients++;
+ if ((host = split_at(cp + 1, '@')) != NULL) { /* user@host */
+ check_user(cp);
+ check_host(host);
+ } else {
+ check_host(cp);
+ }
+ }
+ }
+ if (clients == 0)
+ tcpd_warn("client list is empty or ends in EXCEPT");
+}
+
+/* check_daemon - criticize daemon pattern */
+
+static void check_daemon(pat)
+char *pat;
+{
+ if (pat[0] == '@') {
+ tcpd_warn("%s: daemon name begins with \"@\"", pat);
+ } else if (pat[0] == '.') {
+ tcpd_warn("%s: daemon name begins with dot", pat);
+ } else if (pat[0] == '\0') {
+ tcpd_warn("%s: daemon name begins with NUL", pat);
+ } else if (pat[strlen(pat) - 1] == '.') {
+ tcpd_warn("%s: daemon name ends in dot", pat);
+ } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)) {
+ /* void */ ;
+ } else if (STR_EQ(pat, "FAIL")) { /* obsolete */
+ tcpd_warn("FAIL is no longer recognized");
+ tcpd_warn("(use EXCEPT or DENY instead)");
+ } else if (reserved_name(pat)) {
+ tcpd_warn("%s: daemon name may be reserved word", pat);
+ } else {
+ switch (inet_get(pat)) {
+ case WR_UNKNOWN:
+ tcpd_warn("%s: no such process name in %s", pat, inetcf);
+ inet_set(pat, WR_YES); /* shut up next time */
+ break;
+ case WR_NOT:
+ tcpd_warn("%s: service possibly not wrapped", pat);
+ inet_set(pat, WR_YES);
+ break;
+ }
+ }
+}
+
+/* check_user - criticize user pattern */
+
+static void check_user(pat)
+char *pat;
+{
+ if (pat[0] == '@') { /* @netgroup */
+ tcpd_warn("%s: user name begins with \"@\"", pat);
+ } else if (pat[0] == '.') {
+ tcpd_warn("%s: user name begins with dot", pat);
+ } else if (pat[0] == '\0') {
+ tcpd_warn("%s: user name begins with NUL", pat);
+ } else if (pat[strlen(pat) - 1] == '.') {
+ tcpd_warn("%s: user name ends in dot", pat);
+ } else if (STR_EQ(pat, "ALL") || STR_EQ(pat, unknown)
+ || STR_EQ(pat, "KNOWN")) {
+ /* void */ ;
+ } else if (STR_EQ(pat, "FAIL")) { /* obsolete */
+ tcpd_warn("FAIL is no longer recognized");
+ tcpd_warn("(use EXCEPT or DENY instead)");
+ } else if (reserved_name(pat)) {
+ tcpd_warn("%s: user name may be reserved word", pat);
+ }
+}
+
+#ifdef INET6
+static int check_inet_addr(pat)
+char *pat;
+{
+ struct addrinfo *res;
+
+ res = find_inet_addr(pat, AI_NUMERICHOST);
+ if (res) {
+ freeaddrinfo(res);
+ return 1;
+ } else
+ return 0;
+}
+#endif
+
+/* check_host - criticize host pattern */
+static int check_host(pat)
+char *pat;
+{
+ char *mask;
+ int addr_count = 1;
+
+ if (pat[0] == '@') { /* @netgroup */
+#ifdef NO_NETGRENT
+ /* SCO has no *netgrent() support */
+#else
+#ifdef NETGROUP
+ const char *machinep;
+ const char *userp;
+ const char *domainp;
+
+ setnetgrent(pat + 1);
+ if (getnetgrent(&machinep, &userp, &domainp) == 0)
+ tcpd_warn("%s: unknown or empty netgroup", pat + 1);
+ endnetgrent();
+#else
+ tcpd_warn("netgroup support disabled");
+#endif
+#endif
+ } else if ((mask = split_at(pat, '/')) != NULL) { /* network/netmask */
+#ifdef INET6
+ char *ep;
+#endif
+ if (dot_quad_addr_new(pat, NULL) && dot_quad_addr_new(mask, NULL))
+ ; /*okay*/
+#ifdef INET6
+ else if (check_inet_addr(pat) && check_inet_addr(mask))
+ ; /*okay*/
+ else if (check_inet_addr(pat) &&
+ (ep = NULL, strtoul(mask, &ep, 10), ep && !*ep))
+ ; /*okay*/
+#endif
+ else
+ tcpd_warn("%s/%s: bad net/mask pattern", pat, mask);
+ } else if (STR_EQ(pat, "FAIL")) { /* obsolete */
+ tcpd_warn("FAIL is no longer recognized");
+ tcpd_warn("(use EXCEPT or DENY instead)");
+ } else if (reserved_name(pat)) { /* other reserved */
+ /* void */ ;
+ } else if (NOT_INADDR(pat)) { /* internet name */
+ if (pat[0] == '\0') {
+ tcpd_warn("%s: domain or host name begins with NUL", pat);
+ } else if (pat[strlen(pat) - 1] == '.') {
+ tcpd_warn("%s: domain or host name ends in dot", pat);
+ } else if (pat[0] != '.') {
+ addr_count = check_dns(pat);
+ }
+ } else { /* numeric form */
+ if (STR_EQ(pat, "0.0.0.0") || STR_EQ(pat, "255.255.255.255")) {
+ /* void */ ;
+ } else if (pat[0] == '.') {
+ tcpd_warn("%s: network number begins with dot", pat);
+ } else if (pat[0] == '\0') {
+ tcpd_warn("%s: network number begins with NUL", pat);
+ } else if (pat[strlen(pat) - 1] != '.') {
+ check_dns(pat);
+ }
+ }
+ return (addr_count);
+}
+
+/* reserved_name - determine if name is reserved */
+
+static int reserved_name(pat)
+char *pat;
+{
+ return (STR_EQ(pat, unknown)
+ || STR_EQ(pat, "KNOWN")
+ || STR_EQ(pat, paranoid)
+ || STR_EQ(pat, "ALL")
+ || STR_EQ(pat, "LOCAL"));
+}
--- /dev/null
+/Makefile/1.1/Wed Feb 26 06:17:10 1997//
+/tcpdmatch.8/1.15/Thu May 31 19:19:41 2007//
+/tcpdmatch.c/1.8/Tue Oct 27 23:59:32 2009//
+D
--- /dev/null
+src/libexec/tcpd/tcpdmatch
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 1997/02/26 06:17:10 downsj Exp $
+
+.PATH: ${.CURDIR}/../tcpdchk
+CFLAGS+=-I${.CURDIR}/../tcpdchk
+
+PROG= tcpdmatch
+MAN= tcpdmatch.8
+
+SRCS= inetcf.c scaffold.c tcpdmatch.c
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+BINDIR= /usr/sbin
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/tcpd/tcpdmatch
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: tcpdmatch.8,v 1.15 2007/05/31 19:19:41 jmc Exp $
+.\"
+.\" Copyright (c) 1997, Jason Downs. 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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 AUTHOR(S) 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.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt TCPDMATCH 8
+.Os
+.Sh NAME
+.Nm tcpdmatch
+.Nd tcp wrapper oracle
+.Sh SYNOPSIS
+.Nm tcpdmatch
+.Op Fl d
+.Op Fl i Ar inet_conf
+.Ar daemon
+.Ar client
+.Pp
+.Nm tcpdmatch
+.Op Fl d
+.Op Fl i Ar inet_conf
+.Ar daemon Op Ar @server
+.Op Ar user@
+.Ar client
+.Sh DESCRIPTION
+.Nm
+predicts how the tcp wrapper would handle a specific request for service.
+Examples are given below.
+.Pp
+The program examines the
+.Xr tcpd 8
+access control tables (default
+.Pa /etc/hosts.allow
+and
+.Pa /etc/hosts.deny )
+and prints its conclusion.
+For maximal accuracy, it extracts additional information from your
+.Xr inetd 8
+network configuration file.
+.Pp
+When
+.Nm
+finds a match in the access control tables, it identifies the matched rule.
+In addition, it displays the optional
+shell commands or options in a pretty-printed format; this makes it
+easier for you to spot any discrepancies between what you want and what
+the program understands.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Examine
+.Pa hosts.allow
+and
+.Pa hosts.deny
+files in the current directory instead of the default ones.
+.It Fl i Ar inet_conf
+Specify this option when
+.Nm
+is unable to find your
+.Pa inetd.conf
+network configuration file, or when you wish to test with a non-default one.
+.El
+.Pp
+The following two arguments are always required:
+.Pp
+.Bl -tag -width XXXXXX -compact
+.It Ar daemon
+A daemon process name.
+Typically, the last component of a daemon executable pathname.
+.It Ar client
+A host name or network address, or one of the
+.Dq unknown
+or
+.Dq paranoid
+wildcard patterns.
+.El
+.Pp
+When a client host name is specified,
+.Nm
+gives a prediction for each address listed for that client.
+.Pp
+When a client address is specified,
+.Nm
+predicts what
+.Xr tcpd 8
+would do when client name lookup fails.
+.Pp
+Optional information specified with the
+.Ar daemon@server
+form:
+.Pp
+.Bl -tag -width XXXXXX -compact
+.It Ar server
+A host name or network address, or one of the
+.Dq unknown
+or
+.Dq paranoid
+wildcard patterns.
+The default server name is
+.Dq unknown .
+.El
+.Pp
+Optional information specified with the
+.Ar user@client
+form:
+.Pp
+.Bl -tag -width XXXXXX -compact
+.It Ar user
+A client user identifier.
+Typically, a login name or a numeric user ID.
+The default user name is
+.Dq unknown .
+.El
+.Sh FILES
+The default locations of the
+.Xr tcpd 8
+access control tables are:
+.Pp
+.Bl -tag -width /etc/hosts.allow -compact
+.It Pa /etc/hosts.allow
+access control table (allow list)
+.It Pa /etc/hosts.deny
+access control table (deny list)
+.El
+.Sh EXAMPLES
+To predict how
+.Xr tcpd 8
+would handle a telnet request from the local system:
+.Pp
+.Dl $ tcpdmatch telnetd localhost
+.Pp
+The same request, pretending that hostname lookup failed:
+.Pp
+.Dl $ tcpdmatch telnetd 127.0.0.1
+.Pp
+To predict what
+.Xr tcpd 8
+would do when the client name does not match the client address:
+.Pp
+.Dl $ tcpdmatch telnetd paranoid
+.\" .Pp
+.\" On some systems, daemon names have no `in.' prefix, or
+.\" .Nm tcpdmatch\
+.\" may need some help to locate the inetd configuration file.
+.Sh SEE ALSO
+.Xr hosts_access 5 ,
+.Xr hosts_options 5 ,
+.Xr inetd.conf 5 ,
+.Xr tcpdchk 8
+.Sh AUTHORS
+.Bd -unfilled -offset indent
+Wietse Venema (wietse@wzv.win.tue.nl),
+Department of Mathematics and Computing Science,
+Eindhoven University of Technology
+Den Dolech 2, P.O. Box 513,
+5600 MB Eindhoven, The Netherlands
+.Ed
+.\" @(#) tcpdmatch.8 1.5 96/02/11 17:01:35
--- /dev/null
+/* $OpenBSD: tcpdmatch.c,v 1.8 2009/10/27 23:59:32 deraadt Exp $ */
+
+ /*
+ * tcpdmatch - explain what tcpd would do in a specific case
+ *
+ * usage: tcpdmatch [-d] [-i inet_conf] daemon[@host] [user@]host
+ *
+ * -d: use the access control tables in the current directory.
+ *
+ * -i: location of inetd.conf file.
+ *
+ * All errors are reported to the standard error stream, including the errors
+ * that would normally be reported via the syslog daemon.
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology, The Netherlands.
+ */
+
+/* System libraries. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <tcpd.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1) /* XXX should be 0xffffffff */
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+/* Application-specific. */
+
+#include "inetcf.h"
+#include "scaffold.h"
+
+static void usage(char *);
+static void expand(char *, char *, struct request_info *);
+static void tcpdmatch(struct request_info *);
+
+/* The main program */
+
+int main(int argc, char *argv[])
+{
+ struct addrinfo *res, *res0;
+ char *myname = argv[0];
+ char *client;
+ char *server;
+ char *user;
+ char *daemon;
+ struct request_info request;
+ int ch;
+ char *inetcf = 0;
+ int count;
+ struct sockaddr_storage server_ss;
+ struct sockaddr_storage client_ss;
+ struct stat st;
+
+ /*
+ * Show what rule actually matched.
+ */
+ hosts_access_verbose = 2;
+
+ /*
+ * Parse the JCL.
+ */
+ while ((ch = getopt(argc, argv, "di:")) != -1) {
+ switch (ch) {
+ case 'd':
+ hosts_allow_table = "hosts.allow";
+ hosts_deny_table = "hosts.deny";
+ break;
+ case 'i':
+ inetcf = optarg;
+ break;
+ default:
+ usage(myname);
+ /* NOTREACHED */
+ }
+ }
+ if (argc != optind + 2)
+ usage(myname);
+
+ /*
+ * When confusion really strikes...
+ */
+ if (check_path(REAL_DAEMON_DIR, &st) < 0) {
+ tcpd_warn("REAL_DAEMON_DIR %s: %m", REAL_DAEMON_DIR);
+ } else if (!S_ISDIR(st.st_mode)) {
+ tcpd_warn("REAL_DAEMON_DIR %s is not a directory", REAL_DAEMON_DIR);
+ }
+
+ /*
+ * Default is to specify a daemon process name. When daemon@host is
+ * specified, separate the two parts.
+ */
+ if ((server = split_at(argv[optind], '@')) == 0)
+ server = unknown;
+ if (argv[optind][0] == '/') {
+ daemon = strrchr(argv[optind], '/') + 1;
+ tcpd_warn("%s: daemon name normalized to: %s", argv[optind], daemon);
+ } else {
+ daemon = argv[optind];
+ }
+
+ /*
+ * Default is to specify a client hostname or address. When user@host is
+ * specified, separate the two parts.
+ */
+ if ((client = split_at(argv[optind + 1], '@')) != 0) {
+ user = argv[optind + 1];
+ } else {
+ client = argv[optind + 1];
+ user = unknown;
+ }
+
+ /*
+ * Analyze the inetd (or tlid) configuration file, so that we can warn
+ * the user about services that may not be wrapped, services that are not
+ * configured, or services that are wrapped in an incorrect manner. Allow
+ * for services that are not run from inetd, or that have tcpd access
+ * control built into them.
+ */
+ inetcf = inet_cfg(inetcf);
+ inet_set("portmap", WR_NOT);
+ inet_set("rpcbind", WR_NOT);
+ switch (inet_get(daemon)) {
+ case WR_UNKNOWN:
+ tcpd_warn("%s: no such process name in %s", daemon, inetcf);
+ break;
+ case WR_NOT:
+ tcpd_warn("%s: service possibly not wrapped", daemon);
+ break;
+ }
+
+ /*
+ * Check accessibility of access control files.
+ */
+ (void) check_path(hosts_allow_table, &st);
+ (void) check_path(hosts_deny_table, &st);
+
+ /*
+ * Fill in what we have figured out sofar. Use socket and DNS routines
+ * for address and name conversions. We attach stdout to the request so
+ * that banner messages will become visible.
+ */
+ request_init(&request, RQ_DAEMON, daemon, RQ_USER, user, RQ_FILE, 1, 0);
+ sock_methods(&request);
+
+ /*
+ * If a server hostname is specified, insist that the name maps to at
+ * most one address. eval_hostname() warns the user about name server
+ * problems, while using the request.server structure as a cache for host
+ * address and name conversion results.
+ */
+ if (NOT_INADDR(server) == 0 || HOSTNAME_KNOWN(server)) {
+ if ((res0 = find_inet_addr(server, 0)) == NULL)
+ exit(1);
+ memset((char *) &server_ss, 0, sizeof(server_ss));
+ request_set(&request, RQ_SERVER_SIN, &server_ss, 0);
+
+ count = 0;
+ for (res = res0; res; res = res->ai_next) {
+ count++;
+ if (res->ai_addrlen > sizeof(server_ss))
+ continue;
+ memcpy(&server_ss, res->ai_addr, res->ai_addrlen);
+
+ /*
+ * Force evaluation of server host name and address. Host name
+ * conflicts will be reported while eval_hostname() does its job.
+ */
+ request_set(&request, RQ_SERVER_NAME, "", RQ_SERVER_ADDR, "", 0);
+ if (STR_EQ(eval_hostname(request.server), unknown))
+ tcpd_warn("host address %s->name lookup failed",
+ eval_hostaddr(request.server));
+ }
+ if (count > 1) {
+ fprintf(stderr, "Error: %s has more than one address\n", server);
+ fprintf(stderr, "Please specify an address instead\n");
+ exit(1);
+ }
+ freeaddrinfo(res0);
+ } else {
+ request_set(&request, RQ_SERVER_NAME, server, 0);
+ }
+
+ /*
+ * If a client address is specified, we simulate the effect of client
+ * hostname lookup failure.
+ */
+ res0 = find_inet_addr(client, AI_NUMERICHOST);
+ if (res0 && !res0->ai_next) {
+ request_set(&request, RQ_CLIENT_SIN, res0->ai_addr);
+ tcpdmatch(&request);
+ freeaddrinfo(res0);
+ exit(0);
+ }
+ if (res0)
+ freeaddrinfo(res0);
+
+ /*
+ * Perhaps they are testing special client hostname patterns that aren't
+ * really host names at all.
+ */
+ if (NOT_INADDR(client) && HOSTNAME_KNOWN(client) == 0) {
+ request_set(&request, RQ_CLIENT_NAME, client, 0);
+ tcpdmatch(&request);
+ exit(0);
+ }
+
+ /*
+ * Otherwise, assume that a client hostname is specified, and insist that
+ * the address can be looked up. The reason for this requirement is that
+ * in real life the client address is available (at least with IP). Let
+ * eval_hostname() figure out if this host is properly registered, while
+ * using the request.client structure as a cache for host name and
+ * address conversion results.
+ */
+ if ((res0 = find_inet_addr(client, 0)) == NULL)
+ exit(1);
+ memset((char *) &client_ss, 0, sizeof(client_ss));
+ request_set(&request, RQ_CLIENT_SIN, &client_ss, 0);
+
+ count = 0;
+ for (res = res0; res; res = res->ai_next) {
+ count++;
+ if (res->ai_addrlen > sizeof(client_ss))
+ continue;
+ memcpy(&client_ss, res->ai_addr, res->ai_addrlen);
+
+ /*
+ * Force evaluation of client host name and address. Host name
+ * conflicts will be reported while eval_hostname() does its job.
+ */
+ request_set(&request, RQ_CLIENT_NAME, "", RQ_CLIENT_ADDR, "", 0);
+ if (STR_EQ(eval_hostname(request.client), unknown))
+ tcpd_warn("host address %s->name lookup failed",
+ eval_hostaddr(request.client));
+ tcpdmatch(&request);
+ if (res->ai_next)
+ printf("\n");
+ }
+ freeaddrinfo(res0);
+ exit(0);
+}
+
+/* Explain how to use this program */
+
+static void usage(myname)
+char *myname;
+{
+ fprintf(stderr, "usage: %s [-d] [-i inet_conf] daemon[@host] [user@]host\n",
+ myname);
+ fprintf(stderr, " -d: use allow/deny files in current directory\n");
+ fprintf(stderr, " -i: location of inetd.conf file\n");
+ exit(1);
+}
+
+/* Print interesting expansions */
+
+static void expand(text, pattern, request)
+char *text;
+char *pattern;
+struct request_info *request;
+{
+ char buf[BUFSIZ];
+
+ if (STR_NE(percent_x(buf, sizeof(buf), pattern, request), unknown))
+ printf("%s %s\n", text, buf);
+}
+
+/* Try out a (server,client) pair */
+
+static void tcpdmatch(request)
+struct request_info *request;
+{
+ int verdict;
+
+ /*
+ * Show what we really know. Suppress uninteresting noise.
+ */
+ expand("client: hostname", "%n", request);
+ expand("client: address ", "%a", request);
+ expand("client: username", "%u", request);
+ expand("server: hostname", "%N", request);
+ expand("server: address ", "%A", request);
+ expand("server: process ", "%d", request);
+
+ /*
+ * Reset stuff that might be changed by options handlers. In dry-run
+ * mode, extension language routines that would not return should inform
+ * us of their plan, by clearing the dry_run flag. This is a bit clumsy
+ * but we must be able to verify hosts with more than one network
+ * address.
+ */
+ rfc931_timeout = RFC931_TIMEOUT;
+ allow_severity = SEVERITY;
+ deny_severity = LOG_WARNING;
+ dry_run = 1;
+
+ /*
+ * When paranoid mode is enabled, access is rejected no matter what the
+ * access control rules say.
+ */
+#ifdef PARANOID
+ if (STR_EQ(eval_hostname(request->client), paranoid)) {
+ printf("access: denied (PARANOID mode)\n\n");
+ return;
+ }
+#endif
+
+ /*
+ * Report the access control verdict.
+ */
+ verdict = hosts_access(request);
+ printf("access: %s\n",
+ dry_run == 0 ? "delegated" :
+ verdict ? "granted" : "denied");
+}
--- /dev/null
+/Makefile/1.1/Wed Dec 28 19:07:07 2005//
+/filter.h/1.2/Thu Jul 30 20:40:27 2009//
+/tftp-proxy.8/1.3/Tue Sep 1 14:15:57 2009//
+/tftp-proxy.c/1.6/Sun Apr 13 00:22:17 2008//
+/filter.c/1.8/Sat Feb 6 00:59:42 2010//
+D
--- /dev/null
+src/libexec/tftp-proxy
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.1 2005/12/28 19:07:07 jcs Exp $
+
+PROG= tftp-proxy
+SRCS= tftp-proxy.c filter.c
+MAN= tftp-proxy.8
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $OpenBSD: filter.c,v 1.8 2010/01/13 01:08:14 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <syslog.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/pfvar.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "filter.h"
+
+/* From netinet/in.h, but only _KERNEL_ gets them. */
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+#define satosin6(sa) ((struct sockaddr_in6 *)(sa))
+
+enum { TRANS_FILTER = 0, TRANS_NAT, TRANS_RDR, TRANS_SIZE };
+
+int prepare_rule(u_int32_t, struct sockaddr *, struct sockaddr *,
+ u_int16_t, u_int8_t);
+int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
+ struct sockaddr_in *, u_int8_t);
+int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
+ struct sockaddr_in6 *, u_int8_t);
+
+static struct pfioc_rule pfr;
+static struct pfioc_trans pft;
+static struct pfioc_trans_e pfte;
+static int dev, rule_log;
+static char *qname;
+
+int
+add_filter(u_int32_t id, u_int8_t dir, struct sockaddr *src,
+ struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
+{
+ if (!src || !dst || !d_port || !proto) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (prepare_rule(id, src, dst, d_port, proto) == -1)
+ return (-1);
+
+ pfr.rule.direction = dir;
+ if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+add_rdr(u_int32_t id, struct sockaddr *src, struct sockaddr *dst,
+ u_int16_t d_port, struct sockaddr *rdr, u_int16_t rdr_port, u_int8_t proto)
+{
+ if (!src || !dst || !d_port || !rdr || !rdr_port || !proto ||
+ (src->sa_family != rdr->sa_family)) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (prepare_rule(id, src, dst, d_port, proto) == -1)
+ return (-1);
+
+ pfr.rule.rdr.addr.type = PF_ADDR_ADDRMASK;
+ if (rdr->sa_family == AF_INET) {
+ memcpy(&pfr.rule.rdr.addr.v.a.addr.v4,
+ &satosin(rdr)->sin_addr.s_addr, 4);
+ memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 4);
+ } else {
+ memcpy(&pfr.rule.rdr.addr.v.a.addr.v6,
+ &satosin6(rdr)->sin6_addr.s6_addr, 16);
+ memset(&pfr.rule.rdr.addr.v.a.mask.addr8, 255, 16);
+ }
+
+ pfr.rule.rdr.proxy_port[0] = rdr_port;
+ if (ioctl(dev, DIOCADDRULE, &pfr) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+do_commit(void)
+{
+ if (ioctl(dev, DIOCXCOMMIT, &pft) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+do_rollback(void)
+{
+ if (ioctl(dev, DIOCXROLLBACK, &pft) == -1)
+ return (-1);
+
+ return (0);
+}
+
+void
+init_filter(char *opt_qname, int opt_verbose)
+{
+ struct pf_status status;
+
+ qname = opt_qname;
+
+ if (opt_verbose == 1)
+ rule_log = PF_LOG;
+ else if (opt_verbose == 2)
+ rule_log = PF_LOG_ALL;
+
+ dev = open("/dev/pf", O_RDWR);
+ if (dev == -1) {
+ syslog(LOG_ERR, "can't open /dev/pf");
+ exit(1);
+ }
+ if (ioctl(dev, DIOCGETSTATUS, &status) == -1) {
+ syslog(LOG_ERR, "DIOCGETSTATUS");
+ exit(1);
+ }
+ if (!status.running) {
+ syslog(LOG_ERR, "pf is disabled");
+ exit(1);
+ }
+}
+
+int
+prepare_commit(u_int32_t id)
+{
+ char an[PF_ANCHOR_NAME_SIZE];
+
+ memset(&pft, 0, sizeof pft);
+ memset(&pfte, 0, sizeof pfte);
+ pft.size = 1;
+ pft.esize = sizeof pfte;
+ pft.array = &pfte;
+
+ snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
+ getpid(), id);
+ strlcpy(pfte.anchor, an, PF_ANCHOR_NAME_SIZE);
+ pfte.type = PF_TRANS_RULESET;
+
+ if (ioctl(dev, DIOCXBEGIN, &pft) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+prepare_rule(u_int32_t id, struct sockaddr *src,
+ struct sockaddr *dst, u_int16_t d_port, u_int8_t proto)
+{
+ char an[PF_ANCHOR_NAME_SIZE];
+
+ if ((src->sa_family != AF_INET && src->sa_family != AF_INET6) ||
+ (src->sa_family != dst->sa_family)) {
+ errno = EPROTONOSUPPORT;
+ return (-1);
+ }
+
+ memset(&pfr, 0, sizeof pfr);
+ snprintf(an, PF_ANCHOR_NAME_SIZE, "%s/%d.%d", FTP_PROXY_ANCHOR,
+ getpid(), id);
+ strlcpy(pfr.anchor, an, PF_ANCHOR_NAME_SIZE);
+
+ pfr.ticket = pfte.ticket;
+
+ /* Generic for all rule types. */
+ pfr.rule.af = src->sa_family;
+ pfr.rule.proto = proto;
+ pfr.rule.src.addr.type = PF_ADDR_ADDRMASK;
+ pfr.rule.dst.addr.type = PF_ADDR_ADDRMASK;
+ pfr.rule.rdr.addr.type = PF_ADDR_NONE;
+ pfr.rule.nat.addr.type = PF_ADDR_NONE;
+
+ if (src->sa_family == AF_INET) {
+ memcpy(&pfr.rule.src.addr.v.a.addr.v4,
+ &satosin(src)->sin_addr.s_addr, 4);
+ memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 4);
+ memcpy(&pfr.rule.dst.addr.v.a.addr.v4,
+ &satosin(dst)->sin_addr.s_addr, 4);
+ memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 4);
+ } else {
+ memcpy(&pfr.rule.src.addr.v.a.addr.v6,
+ &satosin6(src)->sin6_addr.s6_addr, 16);
+ memset(&pfr.rule.src.addr.v.a.mask.addr8, 255, 16);
+ memcpy(&pfr.rule.dst.addr.v.a.addr.v6,
+ &satosin6(dst)->sin6_addr.s6_addr, 16);
+ memset(&pfr.rule.dst.addr.v.a.mask.addr8, 255, 16);
+ }
+ pfr.rule.dst.port_op = PF_OP_EQ;
+ pfr.rule.dst.port[0] = htons(d_port);
+ pfr.rule.action = PF_PASS;
+ pfr.rule.quick = 1;
+ pfr.rule.log = rule_log;
+ pfr.rule.keep_state = 1;
+ pfr.rule.flags = (proto == IPPROTO_TCP ? TH_SYN : 0);
+ pfr.rule.flagset = (proto == IPPROTO_TCP ?
+ (TH_SYN|TH_ACK|TH_FIN|TH_RST) : 0);
+ pfr.rule.max_states = 1;
+ if (qname != NULL)
+ strlcpy(pfr.rule.qname, qname, sizeof pfr.rule.qname);
+
+ return (0);
+}
+
+int
+server_lookup(struct sockaddr *client, struct sockaddr *proxy,
+ struct sockaddr *server, u_int8_t proto)
+{
+ if (client->sa_family == AF_INET)
+ return (server_lookup4(satosin(client), satosin(proxy),
+ satosin(server), proto));
+
+ if (client->sa_family == AF_INET6)
+ return (server_lookup6(satosin6(client), satosin6(proxy),
+ satosin6(server), proto));
+
+ errno = EPROTONOSUPPORT;
+ return (-1);
+}
+
+int
+server_lookup4(struct sockaddr_in *client, struct sockaddr_in *proxy,
+ struct sockaddr_in *server, u_int8_t proto)
+{
+ struct pfioc_natlook pnl;
+
+ memset(&pnl, 0, sizeof pnl);
+ pnl.direction = PF_OUT;
+ pnl.af = AF_INET;
+ pnl.proto = proto;
+ memcpy(&pnl.saddr.v4, &client->sin_addr.s_addr, sizeof pnl.saddr.v4);
+ memcpy(&pnl.daddr.v4, &proxy->sin_addr.s_addr, sizeof pnl.daddr.v4);
+ pnl.sport = client->sin_port;
+ pnl.dport = proxy->sin_port;
+
+ if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
+ return (-1);
+
+ memset(server, 0, sizeof(struct sockaddr_in));
+ server->sin_len = sizeof(struct sockaddr_in);
+ server->sin_family = AF_INET;
+ memcpy(&server->sin_addr.s_addr, &pnl.rdaddr.v4,
+ sizeof server->sin_addr.s_addr);
+ server->sin_port = pnl.rdport;
+
+ return (0);
+}
+
+int
+server_lookup6(struct sockaddr_in6 *client, struct sockaddr_in6 *proxy,
+ struct sockaddr_in6 *server, u_int8_t proto)
+{
+ struct pfioc_natlook pnl;
+
+ memset(&pnl, 0, sizeof pnl);
+ pnl.direction = PF_OUT;
+ pnl.af = AF_INET6;
+ pnl.proto = proto;
+ memcpy(&pnl.saddr.v6, &client->sin6_addr.s6_addr, sizeof pnl.saddr.v6);
+ memcpy(&pnl.daddr.v6, &proxy->sin6_addr.s6_addr, sizeof pnl.daddr.v6);
+ pnl.sport = client->sin6_port;
+ pnl.dport = proxy->sin6_port;
+
+ if (ioctl(dev, DIOCNATLOOK, &pnl) == -1)
+ return (-1);
+
+ memset(server, 0, sizeof(struct sockaddr_in6));
+ server->sin6_len = sizeof(struct sockaddr_in6);
+ server->sin6_family = AF_INET6;
+ memcpy(&server->sin6_addr.s6_addr, &pnl.rdaddr.v6,
+ sizeof server->sin6_addr);
+ server->sin6_port = pnl.rdport;
+
+ return (0);
+}
--- /dev/null
+/* $OpenBSD: filter.h,v 1.2 2009/07/30 20:40:27 sthen Exp $ */
+
+/*
+ * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define FTP_PROXY_ANCHOR "tftp-proxy"
+
+int add_filter(u_int32_t, u_int8_t, struct sockaddr *, struct sockaddr *,
+ u_int16_t, u_int8_t);
+int add_rdr(u_int32_t, struct sockaddr *, struct sockaddr *, u_int16_t,
+ struct sockaddr *, u_int16_t, u_int8_t);
+int do_commit(void);
+int do_rollback(void);
+void init_filter(char *, int);
+int prepare_commit(u_int32_t);
+int server_lookup(struct sockaddr *, struct sockaddr *, struct sockaddr *,
+ u_int8_t);
--- /dev/null
+/usr/obj/libexec/tftp-proxy
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: tftp-proxy.8,v 1.3 2009/09/01 14:15:57 sthen Exp $
+.\"
+.\" Copyright (c) 2005 joshua stein <jcs@openbsd.org>
+.\"
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd $Mdocdate: September 1 2009 $
+.Dt TFTP-PROXY 8
+.Os
+.Sh NAME
+.Nm tftp-proxy
+.Nd Internet Trivial File Transfer Protocol proxy
+.Sh SYNOPSIS
+.Nm tftp-proxy
+.Op Fl v
+.Op Fl w Ar transwait
+.Sh DESCRIPTION
+.Nm
+is a proxy for the Internet Trivial File Transfer Protocol invoked by
+the
+.Xr inetd 8
+internet server.
+TFTP connections should be redirected to the proxy using a
+.Xr pf 4
+rule using the
+.Ar rdr-to
+option, after which the proxy connects to the server on behalf of
+the client.
+.Pp
+The proxy establishes a
+.Xr pf 4
+.Ar rdr-to
+pass rule using the
+.Ar anchor
+facility to rewrite packets between the client and the server.
+Once the rule is established,
+.Nm
+forwards the initial request from the client to the server to begin the
+transfer.
+After
+.Ar transwait
+seconds, the NAT state is assumed to have been established and the
+.Xr pf 4
+rule is deleted and the program exits.
+Once the transfer between the client and the server is completed, the
+NAT state will naturally expire.
+.Pp
+Assuming the TFTP command request is from $client to $server, the
+proxy connected to the server using the $proxy source address, and
+$port is negotiated,
+.Nm
+adds the following rule to the anchor:
+.Bd -literal -offset indent
+rdr proto udp from $server to $proxy port $port -\*(Gt $client
+.Ed
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl v
+Log the connection and request information to
+.Xr syslogd 8 .
+.It Fl w Ar transwait
+Number of seconds to wait for the data transmission to begin before
+removing the
+.Xr pf 4
+rule.
+The default is 2 seconds.
+.El
+.Sh CONFIGURATION
+To make use of the proxy,
+.Xr pf.conf 5
+needs the following rules.
+The anchor is mandatory.
+Adjust the rule as needed for your configuration.
+.Bd -literal -offset indent
+anchor "tftp-proxy/*"
+pass in quick on $int_if proto udp from $lan to any port tftp \e
+ rdr-to 127.0.0.1 port 6969
+.Ed
+.Pp
+.Xr inetd 8
+must be configured to spawn the proxy on the port that packets are
+being forwarded to by
+.Xr pf 4 .
+An example
+.Xr inetd.conf 5
+entry follows:
+.Bd -literal -offset indent
+127.0.0.1:6969 dgram udp wait root \e
+ /usr/libexec/tftp-proxy tftp-proxy
+.Ed
+.Sh SEE ALSO
+.Xr tftp 1 ,
+.Xr pf 4 ,
+.Xr pf.conf 5 ,
+.Xr ftp-proxy 8 ,
+.Xr inetd 8 ,
+.Xr syslogd 8 ,
+.Xr tftpd 8
+.Sh CAVEATS
+.Nm
+chroots to
+.Pa /var/empty
+and changes to user
+.Dq proxy
+to drop privileges.
--- /dev/null
+/* $OpenBSD: tftp-proxy.c,v 1.6 2008/04/13 00:22:17 djm Exp $
+ *
+ * Copyright (c) 2005 DLS Internet Services
+ * Copyright (c) 2004, 2005 Camiel Dobbelaar, <cd@sentia.nl>
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/tftp.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/pfvar.h>
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "filter.h"
+
+#define CHROOT_DIR "/var/empty"
+#define NOPRIV_USER "proxy"
+
+#define PF_NAT_PROXY_PORT_LOW 50001
+#define PF_NAT_PROXY_PORT_HIGH 65535
+
+#define DEFTRANSWAIT 2
+#define NTOP_BUFS 4
+#define PKTSIZE SEGSIZE+4
+
+const char *opcode(int);
+const char *sock_ntop(struct sockaddr *);
+u_int16_t pick_proxy_port(void);
+static void usage(void);
+
+extern char *__progname;
+char ntop_buf[NTOP_BUFS][INET6_ADDRSTRLEN];
+int verbose = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd = 0, on = 1, out_fd = 0, peer, reqsize = 0;
+ int transwait = DEFTRANSWAIT;
+ char *p;
+ struct tftphdr *tp;
+ struct passwd *pw;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
+ } cmsgbuf;
+ char req[PKTSIZE];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+
+ struct sockaddr_storage from, proxy, server, proxy_to_server, s_in;
+ struct sockaddr_in sock_out;
+ socklen_t j;
+ in_port_t bindport;
+
+ openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "vw:")) != -1)
+ switch (c) {
+ case 'v':
+ verbose++;
+ break;
+ case 'w':
+ transwait = strtoll(optarg, &p, 10);
+ if (transwait < 1) {
+ syslog(LOG_ERR, "invalid -w value");
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ break;
+ }
+
+ /* open /dev/pf */
+ init_filter(NULL, verbose);
+
+ tzset();
+
+ pw = getpwnam(NOPRIV_USER);
+ if (!pw) {
+ syslog(LOG_ERR, "no such user %s: %m", NOPRIV_USER);
+ exit(1);
+ }
+ if (chroot(CHROOT_DIR) || chdir("/")) {
+ syslog(LOG_ERR, "chroot %s: %m", CHROOT_DIR);
+ exit(1);
+ }
+ if (setgroups(1, &pw->pw_gid) ||
+ setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+ setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) {
+ syslog(LOG_ERR, "can't revoke privs: %m");
+ exit(1);
+ }
+
+ /* non-blocking io */
+ if (ioctl(fd, FIONBIO, &on) < 0) {
+ syslog(LOG_ERR, "ioctl(FIONBIO): %m");
+ exit(1);
+ }
+
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
+ exit(1);
+ }
+
+ j = sizeof(s_in);
+ if (getsockname(fd, (struct sockaddr *)&s_in, &j) == -1) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+
+ bindport = ((struct sockaddr_in *)&s_in)->sin_port;
+
+ /* req will be pushed back out at the end, unchanged */
+ j = sizeof(from);
+ if ((reqsize = recvfrom(fd, req, sizeof(req), MSG_PEEK,
+ (struct sockaddr *)&from, &j)) < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
+
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = req;
+ iov.iov_len = sizeof(req);
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if (recvmsg(fd, &msg, 0) < 0) {
+ syslog(LOG_ERR, "recvmsg: %m");
+ exit(1);
+ }
+
+ close(fd);
+ close(1);
+
+ peer = socket(from.ss_family, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ memset(&s_in, 0, sizeof(s_in));
+ s_in.ss_family = from.ss_family;
+ s_in.ss_len = from.ss_len;
+
+ /* get local address if possible */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVDSTADDR) {
+ memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
+ CMSG_DATA(cmsg), sizeof(struct in_addr));
+ break;
+ }
+ }
+
+ if (bind(peer, (struct sockaddr *)&s_in, s_in.ss_len) < 0) {
+ syslog(LOG_ERR, "bind: %m");
+ exit(1);
+ }
+ if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
+ syslog(LOG_ERR, "connect: %m");
+ exit(1);
+ }
+
+ tp = (struct tftphdr *)req;
+ if (!(ntohs(tp->th_opcode) == RRQ || ntohs(tp->th_opcode) == WRQ)) {
+ /* not a tftp request, bail */
+ if (verbose) {
+ syslog(LOG_WARNING, "not a valid tftp request");
+ exit(1);
+ } else
+ /* exit 0 so inetd doesn't log anything */
+ exit(0);
+ }
+
+ j = sizeof(struct sockaddr_storage);
+ if (getsockname(fd, (struct sockaddr *)&proxy, &j) == -1) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+
+ ((struct sockaddr_in *)&proxy)->sin_port = bindport;
+
+ /* find the un-rdr'd server and port the client wanted */
+ if (server_lookup((struct sockaddr *)&from,
+ (struct sockaddr *)&proxy, (struct sockaddr *)&server,
+ IPPROTO_UDP) != 0) {
+ syslog(LOG_ERR, "pf connection lookup failed (no rdr?)");
+ exit(1);
+ }
+
+ /* establish a new outbound connection to the remote server */
+ if ((out_fd = socket(((struct sockaddr *)&from)->sa_family,
+ SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ syslog(LOG_ERR, "couldn't create new socket");
+ exit(1);
+ }
+
+ bzero((char *)&sock_out, sizeof(sock_out));
+ sock_out.sin_family = from.ss_family;
+ sock_out.sin_port = htons(pick_proxy_port());
+ if (bind(out_fd, (struct sockaddr *)&sock_out, sizeof(sock_out)) < 0) {
+ syslog(LOG_ERR, "couldn't bind to new socket: %m");
+ exit(1);
+ }
+
+ if (connect(out_fd, (struct sockaddr *)&server,
+ ((struct sockaddr *)&server)->sa_len) < 0 && errno != EINPROGRESS) {
+ syslog(LOG_ERR, "couldn't connect to remote server: %m");
+ exit(1);
+ }
+
+ j = sizeof(struct sockaddr_storage);
+ if ((getsockname(out_fd, (struct sockaddr *)&proxy_to_server,
+ &j)) < 0) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+
+ if (verbose)
+ syslog(LOG_INFO, "%s:%d -> %s:%d/%s:%d -> %s:%d \"%s %s\"",
+ sock_ntop((struct sockaddr *)&from),
+ ntohs(((struct sockaddr_in *)&from)->sin_port),
+ sock_ntop((struct sockaddr *)&proxy),
+ ntohs(((struct sockaddr_in *)&proxy)->sin_port),
+ sock_ntop((struct sockaddr *)&proxy_to_server),
+ ntohs(((struct sockaddr_in *)&proxy_to_server)->sin_port),
+ sock_ntop((struct sockaddr *)&server),
+ ntohs(((struct sockaddr_in *)&server)->sin_port),
+ opcode(ntohs(tp->th_opcode)),
+ tp->th_stuff);
+
+ /* get ready to add rdr and pass rules */
+ if (prepare_commit(1) == -1) {
+ syslog(LOG_ERR, "couldn't prepare pf commit");
+ exit(1);
+ }
+
+ /* rdr from server to us on our random port -> client on its port */
+ if (add_rdr(1, (struct sockaddr *)&server,
+ (struct sockaddr *)&proxy_to_server, ntohs(sock_out.sin_port),
+ (struct sockaddr *)&from,
+ ntohs(((struct sockaddr_in *)&from)->sin_port),
+ IPPROTO_UDP) == -1) {
+ syslog(LOG_ERR, "couldn't add rdr");
+ exit(1);
+ }
+
+ /* explicitly allow the packets to return back to the client (which pf
+ * will see post-rdr) */
+ if (add_filter(1, PF_IN, (struct sockaddr *)&server,
+ (struct sockaddr *)&from,
+ ntohs(((struct sockaddr_in *)&from)->sin_port),
+ IPPROTO_UDP) == -1) {
+ syslog(LOG_ERR, "couldn't add pass in");
+ exit(1);
+ }
+ if (add_filter(1, PF_OUT, (struct sockaddr *)&server,
+ (struct sockaddr *)&from,
+ ntohs(((struct sockaddr_in *)&from)->sin_port),
+ IPPROTO_UDP) == -1) {
+ syslog(LOG_ERR, "couldn't add pass out");
+ exit(1);
+ }
+
+ /* and just in case, to pass out from us to the server */
+ if (add_filter(1, PF_OUT, (struct sockaddr *)&proxy_to_server,
+ (struct sockaddr *)&server,
+ ntohs(((struct sockaddr_in *)&server)->sin_port),
+ IPPROTO_UDP) == -1) {
+ syslog(LOG_ERR, "couldn't add pass out");
+ exit(1);
+ }
+
+ if (do_commit() == -1) {
+ syslog(LOG_ERR, "couldn't commit pf rules");
+ exit(1);
+ }
+
+ /* forward the initial tftp request and start the insanity */
+ if (send(out_fd, tp, reqsize, 0) < 0) {
+ syslog(LOG_ERR, "couldn't forward tftp packet: %m");
+ exit(1);
+ }
+
+ /* allow the transfer to start to establish a state */
+ sleep(transwait);
+
+ /* delete our rdr rule and clean up */
+ prepare_commit(1);
+ do_commit();
+
+ return(0);
+}
+
+const char *
+opcode(int code)
+{
+ static char str[6];
+
+ switch (code) {
+ case 1:
+ (void)snprintf(str, sizeof(str), "RRQ");
+ break;
+ case 2:
+ (void)snprintf(str, sizeof(str), "WRQ");
+ break;
+ default:
+ (void)snprintf(str, sizeof(str), "(%d)", code);
+ break;
+ }
+
+ return (str);
+}
+
+const char *
+sock_ntop(struct sockaddr *sa)
+{
+ static int n = 0;
+
+ /* Cycle to next buffer. */
+ n = (n + 1) % NTOP_BUFS;
+ ntop_buf[n][0] = '\0';
+
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+
+ return (inet_ntop(AF_INET, &sin->sin_addr, ntop_buf[n],
+ sizeof ntop_buf[0]));
+ }
+
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
+
+ return (inet_ntop(AF_INET6, &sin6->sin6_addr, ntop_buf[n],
+ sizeof ntop_buf[0]));
+ }
+
+ return (NULL);
+}
+
+u_int16_t
+pick_proxy_port(void)
+{
+ return (IPPORT_HIFIRSTAUTO +
+ arc4random_uniform(IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO));
+}
+
+static void
+usage(void)
+{
+ syslog(LOG_ERR, "usage: %s [-v] [-w transwait]", __progname);
+ exit(1);
+}
--- /dev/null
+/Makefile/1.2/Sun Jan 28 19:34:35 2001//
+/tftpd.8/1.26/Fri Nov 14 07:34:59 2008//
+/tftpd.c/1.63/Tue Oct 27 23:59:32 2009//
+D
--- /dev/null
+src/libexec/tftpd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:35 niklas Exp $
+
+PROG= tftpd
+SRCS= tftpd.c tftpsubs.c
+MAN= tftpd.8
+.PATH: ${.CURDIR}/../../usr.bin/tftp
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/tftpd
\ No newline at end of file
--- /dev/null
+.\" $OpenBSD: tftpd.8,v 1.26 2008/11/14 07:34:59 stevesk Exp $
+.\"
+.\" Copyright (c) 1983, 1991 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. 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.
+.\"
+.\" from: @(#)tftpd.8 6.7 (Berkeley) 5/13/91
+.\" $OpenBSD: tftpd.8,v 1.26 2008/11/14 07:34:59 stevesk Exp $
+.\"
+.Dd $Mdocdate: November 14 2008 $
+.Dt TFTPD 8
+.Os
+.Sh NAME
+.Nm tftpd
+.Nd
+.Tn DARPA
+Trivial File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm tftpd
+.Op Fl cl
+.Op Ar directory ...
+.Nm tftpd
+.Op Fl cl
+.Fl s Ar directory
+.Sh DESCRIPTION
+.Nm
+is a server which supports the
+.Tn DARPA
+Trivial File Transfer Protocol.
+The 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
+will allow only publicly readable files to be accessed.
+Files may be written only if they already exist and are publicly writable,
+unless the
+.Fl c
+flag is specified
+.Pq see below .
+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.
+.Pp
+The server should have the user ID with the lowest possible privilege,
+unless the
+.Fl s
+flag is specified
+.Pq see below ,
+in which case it must be started with user ID 0.
+.Pp
+Access to files may be restricted by invoking
+.Nm
+with a list of directories by including pathnames
+as server program arguments in
+.Pa /etc/inetd.conf .
+In this case access is restricted to files whose
+names are prefixed by one of the given directories.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Allow new files to be created;
+otherwise uploaded files must already exist.
+Files are created with default permissions
+allowing anyone to read or write to them.
+.It Fl l
+Log the client IP, type of request, and filename using
+.Xr syslog 3
+with a level of
+.Dv LOG_INFO .
+.It Fl s Ar directory
+.Xr chroot 2
+to
+.Ar directory
+on startup;
+the remote host is not expected to pass the directory
+as part of the file name to transfer.
+This option is intended primarily for
+compatibility with SunOS boot ROMs which do not include a directory name.
+.El
+.Sh SEE ALSO
+.Xr tftp 1 ,
+.Xr inetd 8 ,
+.Xr pxeboot 8 ,
+.Xr syslogd 8 ,
+.Xr tftp-proxy 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
+.Pp
+The
+.Fl s
+flag appeared in
+.Nx 0.9a .
+.Pp
+The
+.Fl c
+flag was added in
+.Ox 2.1 .
+.Pp
+The
+.Fl l
+flag was added in
+.Ox 4.3 .
+.Sh BUGS
+Many TFTP clients will not transfer files over 16744448 octets
+.Pq 32767 blocks .
--- /dev/null
+/* $OpenBSD: tftpd.c,v 1.63 2009/10/27 23:59:32 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1983 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. 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.
+ */
+
+/*
+ * Trivial file transfer protocol server.
+ *
+ * This version includes many modifications by Jim Guyton <guyton@rand-unix>
+ */
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/tftp.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <vis.h>
+
+#define TIMEOUT 5 /* packet rexmt timeout */
+#define TIMEOUT_MIN 1 /* minimal packet rexmt timeout */
+#define TIMEOUT_MAX 255 /* maximal packet rexmt timeout */
+
+struct formats;
+
+int readit(FILE *, struct tftphdr **, int, int);
+void read_ahead(FILE *, int, int);
+int writeit(FILE *, struct tftphdr **, int, int);
+int write_behind(FILE *, int);
+int synchnet(int);
+
+__dead void usage(void);
+void tftp(struct tftphdr *, int);
+int validate_access(char *, int);
+int sendfile(struct formats *);
+int recvfile(struct formats *);
+void nak(int);
+void oack(int);
+static char *getip(struct sockaddr *);
+
+FILE *file;
+extern char *__progname;
+struct sockaddr_storage s_in;
+int peer;
+int rexmtval = TIMEOUT;
+int maxtimeout = 5 * TIMEOUT;
+char *buf;
+char *ackbuf;
+struct sockaddr_storage from;
+int ndirs;
+char **dirs;
+int secure;
+int cancreate;
+int logging;
+unsigned int segment_size = SEGSIZE;
+unsigned int packet_size = SEGSIZE + 4;
+int has_options = 0;
+
+struct formats {
+ const char *f_mode;
+ int (*f_validate)(char *, int);
+ int (*f_send)(struct formats *);
+ int (*f_recv)(struct formats *);
+ int f_convert;
+} formats[] = {
+ { "netascii", validate_access, sendfile, recvfile, 1 },
+ { "octet", validate_access, sendfile, recvfile, 0 },
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+struct options {
+ const char *o_type;
+ char *o_request;
+ long long o_reply; /* turn into union if need be */
+} options[] = {
+ { "tsize", NULL, 0 }, /* OPT_TSIZE */
+ { "timeout", NULL, 0 }, /* OPT_TIMEOUT */
+ { "blksize", NULL, 0 }, /* OPT_BLKSIZE */
+ { NULL, NULL, 0 }
+};
+
+enum opt_enum {
+ OPT_TSIZE = 0,
+ OPT_TIMEOUT,
+ OPT_BLKSIZE
+};
+
+struct errmsg {
+ int e_code;
+ const 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" },
+ { EOPTNEG, "Option negotiation failed" },
+ { -1, NULL }
+};
+
+__dead void
+usage(void)
+{
+ syslog(LOG_ERR, "usage: %s [-cl] [directory ...]", __progname);
+ syslog(LOG_ERR, "usage: %s [-cl] -s directory", __progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int n = 0, on = 1, fd = 0, i, c, dobind = 1;
+ struct tftphdr *tp;
+ struct passwd *pw;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
+ } cmsgbuf;
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov;
+ pid_t pid = 0;
+ socklen_t j;
+
+ openlog(__progname, LOG_PID|LOG_NDELAY, LOG_DAEMON);
+
+ while ((c = getopt(argc, argv, "cls")) != -1) {
+ switch (c) {
+ case 'c':
+ cancreate = 1;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 's':
+ secure = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ for (; optind != argc; optind++) {
+ char **d;
+
+ d = realloc(dirs, (ndirs + 2) * sizeof(char *));
+ if (d == NULL) {
+ syslog(LOG_ERR, "realloc: %m");
+ exit(1);
+ }
+ dirs = d;
+ dirs[n++] = argv[optind];
+ dirs[n] = NULL;
+ ndirs++;
+ }
+
+ pw = getpwnam("_tftpd");
+ if (pw == NULL) {
+ syslog(LOG_ERR, "no _tftpd: %m");
+ exit(1);
+ }
+
+ if (secure) {
+ if (ndirs == 0) {
+ syslog(LOG_ERR, "no -s directory");
+ exit(1);
+ }
+ if (ndirs > 1) {
+ syslog(LOG_ERR, "too many -s directories");
+ exit(1);
+ }
+ tzset();
+ if (chroot(dirs[0])) {
+ syslog(LOG_ERR, "chroot %s: %m", dirs[0]);
+ exit(1);
+ }
+ if (chdir("/")) {
+ syslog(LOG_ERR, "chdir: %m");
+ exit(1);
+ }
+ }
+
+ setegid(pw->pw_gid);
+ setgid(pw->pw_gid);
+ seteuid(pw->pw_uid);
+ setuid(pw->pw_uid);
+
+ if (ioctl(fd, FIONBIO, &on) < 0) {
+ syslog(LOG_ERR, "ioctl(FIONBIO): %m");
+ exit(1);
+ }
+
+ j = sizeof(s_in);
+ if (getsockname(fd, (struct sockaddr *)&s_in, &j) == -1) {
+ syslog(LOG_ERR, "getsockname: %m");
+ exit(1);
+ }
+
+ switch (s_in.ss_family) {
+ case AF_INET:
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
+ sizeof(on)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
+ exit (1);
+ }
+ break;
+ case AF_INET6:
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) == -1) {
+ syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %m");
+ exit (1);
+ }
+ break;
+ }
+
+ if ((buf = malloc(SEGSIZE_MAX + 4)) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+ if ((ackbuf = malloc(SEGSIZE_MAX + 4)) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = packet_size;
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ n = recvmsg(fd, &msg, 0);
+ if (n < 0) {
+ syslog(LOG_ERR, "recvmsg: %m");
+ 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
+ * breaks before doing the above "recvfrom", inetd would
+ * spawn endless instances, clogging the system.
+ */
+ for (i = 1; i < 20; i++) {
+ pid = fork();
+ if (pid < 0) {
+ sleep(i);
+ /*
+ * Flush out to most recently sent request.
+ *
+ * This may drop some requests, 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.
+ */
+ bzero(&msg, sizeof(msg));
+ iov.iov_base = buf;
+ iov.iov_len = packet_size;
+ msg.msg_name = &from;
+ msg.msg_namelen = sizeof(from);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ i = recvmsg(fd, &msg, 0);
+ if (i > 0)
+ n = i;
+ } else
+ break;
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ exit(1);
+ } else if (pid != 0)
+ exit(0);
+
+ alarm(0);
+ close(fd);
+ close(1);
+ peer = socket(from.ss_family, SOCK_DGRAM, 0);
+ if (peer < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ memset(&s_in, 0, sizeof(s_in));
+ s_in.ss_family = from.ss_family;
+ s_in.ss_len = from.ss_len;
+
+ /* get local address if possible */
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_RECVDSTADDR) {
+ memcpy(&((struct sockaddr_in *)&s_in)->sin_addr,
+ CMSG_DATA(cmsg), sizeof(struct in_addr));
+ if (((struct sockaddr_in *)&s_in)->sin_addr.s_addr ==
+ INADDR_BROADCAST)
+ dobind = 0;
+ break;
+ }
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *ipi;
+
+ ipi = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ memcpy(&((struct sockaddr_in6 *)&s_in)->sin6_addr,
+ &ipi->ipi6_addr, sizeof(struct in6_addr));
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&ipi->ipi6_addr))
+ ((struct sockaddr_in6 *)&s_in)->sin6_scope_id =
+ ipi->ipi6_ifindex;
+#endif
+ break;
+ }
+ }
+
+ if (dobind) {
+ (void) setsockopt(peer, SOL_SOCKET, SO_REUSEADDR, &on,
+ sizeof(on));
+ (void) setsockopt(peer, SOL_SOCKET, SO_REUSEPORT, &on,
+ sizeof(on));
+
+ if (bind(peer, (struct sockaddr *)&s_in, s_in.ss_len) < 0) {
+ syslog(LOG_ERR, "bind to %s: %m",
+ inet_ntoa(((struct sockaddr_in *)&s_in)->sin_addr));
+ exit(1);
+ }
+ }
+ if (connect(peer, (struct sockaddr *)&from, from.ss_len) < 0) {
+ syslog(LOG_ERR, "connect: %m");
+ 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);
+}
+
+/*
+ * Handle initial connection protocol.
+ */
+void
+tftp(struct tftphdr *tp, int size)
+{
+ char *cp;
+ int i, first = 1, ecode, opcode, to;
+ struct formats *pf;
+ char *filename, *mode = NULL, *option, *ccp;
+ char fnbuf[MAXPATHLEN], nicebuf[MAXPATHLEN];
+ const char *errstr;
+
+ cp = tp->th_stuff;
+again:
+ while (cp < buf + size) {
+ if (*cp == '\0')
+ break;
+ cp++;
+ }
+ if (*cp != '\0') {
+ nak(EBADOP);
+ exit(1);
+ }
+ i = cp - tp->th_stuff;
+ if (i >= sizeof(fnbuf)) {
+ nak(EBADOP);
+ exit(1);
+ }
+ memcpy(fnbuf, tp->th_stuff, i);
+ fnbuf[i] = '\0';
+ filename = fnbuf;
+ 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);
+ }
+ while (++cp < buf + size) {
+ for (i = 2, ccp = cp; i > 0; ccp++) {
+ if (ccp >= buf + size) {
+ /*
+ * Don't reject the request, just stop trying
+ * to parse the option and get on with it.
+ * Some Apple OpenFirmware versions have
+ * trailing garbage on the end of otherwise
+ * valid requests.
+ */
+ goto option_fail;
+ } else if (*ccp == '\0')
+ i--;
+ }
+ for (option = cp; *cp; cp++)
+ if (isupper(*cp))
+ *cp = tolower(*cp);
+ for (i = 0; options[i].o_type != NULL; i++)
+ if (strcmp(option, options[i].o_type) == 0) {
+ options[i].o_request = ++cp;
+ has_options = 1;
+ }
+ cp = ccp - 1;
+ }
+
+option_fail:
+ if (options[OPT_TIMEOUT].o_request) {
+ to = strtonum(options[OPT_TIMEOUT].o_request,
+ TIMEOUT_MIN, TIMEOUT_MAX, &errstr);
+ if (errstr) {
+ nak(EBADOP);
+ exit(1);
+ }
+ options[OPT_TIMEOUT].o_reply = rexmtval = to;
+ }
+
+ if (options[OPT_BLKSIZE].o_request) {
+ segment_size = strtonum(options[OPT_BLKSIZE].o_request,
+ SEGSIZE_MIN, SEGSIZE_MAX, &errstr);
+ if (errstr) {
+ nak(EBADOP);
+ exit(1);
+ }
+ packet_size = segment_size + 4;
+ options[OPT_BLKSIZE].o_reply = segment_size;
+ }
+
+ /* save opcode before it gets overwritten by oack() */
+ opcode = tp->th_opcode;
+
+ (void)strnvis(nicebuf, filename, MAXPATHLEN, VIS_SAFE|VIS_OCTAL);
+ ecode = (*pf->f_validate)(filename, opcode);
+ if (logging)
+ syslog(LOG_INFO, "%s: %s request for '%s'",
+ getip((struct sockaddr *)&from),
+ opcode == WRQ ? "write" : "read",
+ nicebuf);
+ if (has_options)
+ oack(opcode);
+ if (ecode) {
+ syslog(LOG_INFO, "%s: denied %s access to '%s'",
+ getip((struct sockaddr *)&from),
+ opcode == WRQ ? "write" : "read", nicebuf);
+ nak(ecode);
+ exit(1);
+ }
+ if (opcode == WRQ)
+ (*pf->f_recv)(pf);
+ else
+ (*pf->f_send)(pf);
+ exit(0);
+}
+
+/*
+ * 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(char *filename, int mode)
+{
+ struct stat stbuf;
+ char *cp, **dirp;
+ int fd, wmode;
+ const char *errstr;
+
+ if (!secure) {
+ if (*filename != '/')
+ return (EACCESS);
+ /*
+ * Prevent tricksters from getting around the directory
+ * restrictions.
+ */
+ for (cp = filename + 1; *cp; cp++)
+ if (*cp == '.' && strncmp(cp - 1, "/../", 4) == 0)
+ return (EACCESS);
+ for (dirp = dirs; *dirp; dirp++)
+ if (strncmp(filename, *dirp, strlen(*dirp)) == 0)
+ break;
+ if (*dirp == 0 && dirp != dirs)
+ return (EACCESS);
+ }
+
+ /*
+ * We use a different permissions scheme if `cancreate' is
+ * set.
+ */
+ wmode = O_TRUNC;
+ if (stat(filename, &stbuf) < 0) {
+ if (!cancreate)
+ return (errno == ENOENT ? ENOTFOUND : EACCESS);
+ else {
+ if ((errno == ENOENT) && (mode != RRQ))
+ wmode |= O_CREAT;
+ else
+ return (EACCESS);
+ }
+ } else {
+ if (mode == RRQ) {
+ if ((stbuf.st_mode & (S_IREAD >> 6)) == 0)
+ return (EACCESS);
+ } else {
+ if ((stbuf.st_mode & (S_IWRITE >> 6)) == 0)
+ return (EACCESS);
+ }
+ }
+ if (options[OPT_TSIZE].o_request) {
+ if (mode == RRQ)
+ options[OPT_TSIZE].o_reply = stbuf.st_size;
+ else {
+ /* allows writes of 65535 blocks * SEGSIZE_MAX bytes */
+ options[OPT_TSIZE].o_reply =
+ strtonum(options[OPT_TSIZE].o_request,
+ 1, 65535LL * SEGSIZE_MAX, &errstr);
+ if (errstr) {
+ nak(EOPTNEG);
+ exit(1);
+ }
+ }
+ }
+ fd = open(filename, mode == RRQ ? O_RDONLY : (O_WRONLY|wmode), 0666);
+ if (fd < 0)
+ return (errno + 100);
+ /*
+ * If the file was created, set default permissions.
+ */
+ if ((wmode & O_CREAT) && fchmod(fd, 0666) < 0) {
+ int serrno = errno;
+
+ close(fd);
+ unlink(filename);
+
+ return (serrno + 100);
+ }
+ file = fdopen(fd, mode == RRQ ? "r" : "w");
+ if (file == NULL) {
+ close(fd);
+ return (errno + 100);
+ }
+ return (0);
+}
+
+/*
+ * Send the requested file.
+ */
+int
+sendfile(struct formats *pf)
+{
+ struct tftphdr *dp, *r_init(void);
+ struct tftphdr *ap; /* ack packet */
+ struct pollfd pfd[1];
+ volatile unsigned short block = 1;
+ int n, size, nfds, error, timeouts;
+
+ dp = r_init();
+ ap = (struct tftphdr *)ackbuf;
+
+ do {
+ /* read data from file */
+ size = readit(file, &dp, pf->f_convert, segment_size);
+ if (size < 0) {
+ nak(errno + 100);
+ goto abort;
+ }
+ dp->th_opcode = htons((u_short)DATA);
+ dp->th_block = htons((u_short)block);
+
+ /* send data to client and wait for client ACK */
+ for (timeouts = 0, error = 0;;) {
+ if (timeouts >= maxtimeout)
+ exit(1);
+
+ if (!error) {
+ if (send(peer, dp, size + 4, 0) != size + 4) {
+ syslog(LOG_ERR, "send: %m");
+ goto abort;
+ }
+ read_ahead(file, pf->f_convert, segment_size);
+ }
+ error = 0;
+
+ pfd[0].fd = peer;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts += rexmtval;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ goto abort;
+ }
+ n = recv(peer, ackbuf, packet_size, 0);
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "recv: %m");
+ 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))
+ continue;
+ }
+ error = 1; /* FALLTHROUGH */
+ }
+
+ block++;
+ } while (size == segment_size);
+
+abort:
+ fclose(file);
+ return (1);
+}
+
+/*
+ * Receive a file.
+ */
+int
+recvfile(struct formats *pf)
+{
+ struct tftphdr *dp, *w_init(void);
+ struct tftphdr *ap; /* ack buffer */
+ struct pollfd pfd[1];
+ volatile unsigned short block = 0;
+ int n, size, nfds, error, timeouts;
+
+ dp = w_init();
+ ap = (struct tftphdr *)ackbuf;
+
+ /* if we have options, do not send a first ACK */
+ if (has_options) {
+ block++;
+ goto noack;
+ }
+
+ do {
+ /* create new ACK packet */
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons((u_short)block);
+ block++;
+
+ /* send ACK to client and wait for client data */
+ for (timeouts = 0, error = 0;;) {
+ if (timeouts >= maxtimeout)
+ exit(1);
+
+ if (!error) {
+ if (send(peer, ackbuf, 4, 0) != 4) {
+ syslog(LOG_ERR, "send: %m");
+ goto abort;
+ }
+ write_behind(file, pf->f_convert);
+ }
+ error = 0;
+
+ pfd[0].fd = peer;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts += rexmtval;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ goto abort;
+ }
+noack:
+ n = recv(peer, dp, packet_size, 0);
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "recv: %m");
+ 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;
+ /* re-synchronize with the other side */
+ (void)synchnet(peer);
+ if (dp->th_block == (block - 1))
+ continue;
+ }
+ error = 1; /* FALLTHROUGH */
+ }
+
+ /* write data to file */
+ size = writeit(file, &dp, n - 4, pf->f_convert);
+ if (size != (n - 4)) {
+ if (size < 0)
+ nak(errno + 100);
+ else
+ nak(ENOSPACE);
+ goto abort;
+ }
+ } while (size == segment_size);
+
+ /* close data file */
+ write_behind(file, pf->f_convert);
+ (void)fclose(file);
+
+ /* send final ack */
+ ap->th_opcode = htons((u_short)ACK);
+ ap->th_block = htons((u_short)(block));
+ (void)send(peer, ackbuf, 4, 0);
+
+ /* just quit on timeout */
+ pfd[0].fd = peer;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds < 1)
+ exit(1);
+ n = recv(peer, buf, packet_size, 0);
+ /*
+ * If read some data and got a data block then my last ack was lost
+ * resend final ack.
+ */
+ if (n >= 4 && dp->th_opcode == DATA && block == dp->th_block)
+ (void)send(peer, ackbuf, 4, 0);
+
+abort:
+ return (1);
+}
+
+/*
+ * Send a nak packet (error message).
+ * Error code passed in is one of the
+ * standard TFTP codes, or a UNIX errno
+ * offset by 100.
+ */
+void
+nak(int error)
+{
+ struct tftphdr *tp;
+ struct errmsg *pe;
+ int length;
+
+ 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 */
+ }
+ length = strlcpy(tp->th_msg, pe->e_msg, packet_size) + 5;
+ if (length > packet_size)
+ length = packet_size;
+ if (send(peer, buf, length, 0) != length)
+ syslog(LOG_ERR, "nak: %m");
+}
+
+/*
+ * Send an oack packet (option acknowledgement).
+ */
+void
+oack(int opcode)
+{
+ struct tftphdr *tp, *ap;
+ struct pollfd pfd[1];
+ char *bp;
+ int i, n, size, nfds, error, timeouts;
+
+ tp = (struct tftphdr *)buf;
+ bp = buf + 2;
+ size = packet_size - 2;
+ tp->th_opcode = htons((u_short)OACK);
+ for (i = 0; options[i].o_type != NULL; i++) {
+ if (options[i].o_request) {
+ n = snprintf(bp, size, "%s%c%lld", options[i].o_type,
+ 0, options[i].o_reply);
+ if (n == -1 || n >= size) {
+ syslog(LOG_ERR, "oack: no buffer space");
+ exit(1);
+ }
+ bp += n + 1;
+ size -= n + 1;
+ if (size < 0) {
+ syslog(LOG_ERR, "oack: no buffer space");
+ exit(1);
+ }
+ }
+ }
+ size = bp - buf;
+ ap = (struct tftphdr *)ackbuf;
+
+ /* send OACK to client and wait for client ACK */
+ for (timeouts = 0, error = 0;;) {
+ if (timeouts >= maxtimeout)
+ exit(1);
+
+ if (!error) {
+ if (send(peer, buf, size, 0) != size) {
+ syslog(LOG_INFO, "oack: %m");
+ exit(1);
+ }
+ }
+ error = 0;
+
+ pfd[0].fd = peer;
+ pfd[0].events = POLLIN;
+ nfds = poll(pfd, 1, rexmtval * 1000);
+ if (nfds == 0) {
+ timeouts += rexmtval;
+ continue;
+ }
+ if (nfds == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "poll: %m");
+ exit(1);
+ }
+
+ /* no client ACK for write requests with options */
+ if (opcode == WRQ)
+ break;
+
+ n = recv(peer, ackbuf, packet_size, 0);
+ if (n == -1) {
+ error = 1;
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "recv: %m");
+ exit(1);
+ }
+ ap->th_opcode = ntohs((u_short)ap->th_opcode);
+ ap->th_block = ntohs((u_short)ap->th_block);
+
+ if (ap->th_opcode == ERROR)
+ exit(1);
+ if (ap->th_opcode == ACK && ap->th_block == 0)
+ break;
+ error = 1; /* FALLTHROUGH */
+ }
+}
+
+static char *
+getip(struct sockaddr *sa)
+{
+ static char hbuf[NI_MAXHOST];
+
+ if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
+ strlcpy(hbuf, "0.0.0.0", sizeof(hbuf));
+ return(hbuf);
+}
--- /dev/null
+/Makefile/1.5/Sun Jan 28 19:34:35 2001//
+/pathnames.h/1.4/Mon Jun 2 19:38:25 2003//
+/uucpd.8/1.10/Thu May 31 19:19:41 2007//
+/uucpd.c/1.32/Tue Oct 27 23:59:32 2009//
+D
--- /dev/null
+src/libexec/uucpd
--- /dev/null
+:ext:cvs.openbsd.org:/cvs
--- /dev/null
+# $OpenBSD: Makefile,v 1.5 2001/01/28 19:34:35 niklas Exp $
+
+CFLAGS+= -DBSDINETD
+PROG= uucpd
+MAN= uucpd.8
+
+.include <bsd.prog.mk>
--- /dev/null
+/usr/obj/libexec/uucpd
\ No newline at end of file
--- /dev/null
+/* $OpenBSD: pathnames.h,v 1.4 2003/06/02 19:38:25 millert Exp $*/
+
+/*
+ * Copyright (c) 1989 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. 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.
+ *
+ * from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#include <paths.h>
+
+#define _PATH_UUCICO "/usr/local/libexec/uucp/uucico"
--- /dev/null
+.\" $OpenBSD: uucpd.8,v 1.10 2007/05/31 19:19:41 jmc Exp $
+.\"
+.\" Copyright (c) 1998 Matthew R. Green
+.\" 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. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt UUCPD 8
+.Os
+.Sh NAME
+.Nm uucpd
+.Nd unix to unix copy protocol daemon
+.Sh SYNOPSIS
+.Nm uucpd
+.Sh DESCRIPTION
+The
+.Nm
+daemon reads a username and password, and then executes
+.Xr uucico ,
+the UUCP file transfer daemon, if the
+username and password match a valid account and
+the account's shell is
+.Dq /usr/local/libexec/uucp/uucico .
+.Pp
+.Nm
+is started by
+.Xr inetd 8
+and
+.Xr uucico .
+.Pp
+.Sy Note:
+.Xr uucico
+is not part of the base
+.Ox
+system.
+It comes as part of the uucp suite, and can be installed via
+.Xr ports 7
+or
+.Xr packages 7 .
+.Sh SEE ALSO
+.Xr inetd 8
+.Sh HISTORY
+The
+.Nm
+program was first made available in
+.Bx 4.2 .
--- /dev/null
+/* $OpenBSD: uucpd.c,v 1.32 2009/10/27 23:59:32 deraadt Exp $ */
+
+/*
+ * Copyright (c) 1985 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. 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.
+ */
+
+/*
+ * 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 <login_cap.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include "pathnames.h"
+
+void doit(struct sockaddr *);
+int readline(char *, int n);
+void dologout(void);
+void dologin(struct passwd *, struct sockaddr *);
+
+struct sockaddr_storage hisctladdr;
+socklen_t hisaddrlen = sizeof hisctladdr;
+pid_t mypid;
+
+char Username[64], Loginname[64];
+char *nenv[] = {
+ Username,
+ Loginname,
+ NULL,
+};
+
+extern char **environ;
+
+char utline[UT_LINESIZE+1];
+
+int
+main(int argc, char *argv[])
+{
+#ifndef BSDINETD
+ int s, tcp_socket;
+ struct servent *sp;
+#endif /* !BSDINETD */
+ pid_t childpid;
+
+ environ = nenv;
+#ifdef BSDINETD
+ close(1); close(2);
+ dup(0); dup(0);
+ hisaddrlen = sizeof (hisctladdr);
+ if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) {
+ fprintf(stderr, "%s: ", argv[0]);
+ perror("getpeername");
+ _exit(1);
+ }
+ if ((childpid = fork()) == 0)
+ doit((struct sockaddr *)&hisctladdr);
+ snprintf(utline, sizeof(utline), "uucp%.4ld", (long)childpid);
+ dologout();
+ exit(1);
+#else /* !BSDINETD */
+ sp = getservbyname("uucp", "tcp");
+ if (sp == NULL){
+ perror("uucpd: getservbyname");
+ exit(1);
+ }
+ if (fork())
+ exit(0);
+ snprintf(utline, sizeof(utline), "uucp%.4ld", (long)childpid);
+
+ if ((s = open(_PATH_TTY, 2)) >= 0){
+ ioctl(s, TIOCNOTTY, (char *)0);
+ close(s);
+ }
+
+ bzero((char *)&myctladdr, sizeof (myctladdr));
+ myctladdr.sin_len = sizeof(struct sockaddr_in);
+ myctladdr.sin_family = AF_INET;
+ myctladdr.sin_port = sp->s_port;
+ 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 /* !BSDINETD */
+}
+
+void
+doit(struct sockaddr *sa)
+{
+ char user[64], passwd[64];
+ char *xpasswd;
+ struct passwd *pw;
+
+ alarm(60);
+ do {
+ printf("login: ");
+ fflush(stdout);
+ if (readline(user, sizeof user) < 0) {
+ fprintf(stderr, "user read\n");
+ return;
+ }
+ } while (user[0] == '\0');
+ user[MAXLOGNAME] = '\0';
+
+ pw = getpwnam(user);
+ if (pw == NULL) {
+ printf("Password: ");
+ fflush(stdout);
+ if (readline(passwd, sizeof passwd) < 0) {
+ fprintf(stderr, "passwd read\n");
+ return;
+ }
+ 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;
+ }
+ }
+ if (strcmp(pw->pw_shell, _PATH_UUCICO)) {
+ fprintf(stderr, "Login incorrect.\n");
+ return;
+ }
+ alarm(0);
+ (void) snprintf(Username, sizeof(Username), "USER=%s", user);
+ (void) snprintf(Loginname, sizeof(Loginname), "LOGNAME=%s", user);
+ dologin(pw, sa);
+ if (setusercontext(0, pw, pw->pw_uid, LOGIN_SETALL) != 0) {
+ perror("unable to set user context");
+ return;
+ }
+ chdir(pw->pw_dir);
+ execl(_PATH_UUCICO, "uucico", (char *)NULL);
+ perror("uucico server: execl");
+}
+
+int
+readline(char *p, int n)
+{
+ char c;
+
+ while (n-- > 0) {
+ if (read(STDIN_FILENO, &c, 1) <= 0)
+ return(-1);
+ c &= 0177;
+ if (c == '\r') {
+ *p = '\0';
+ return(0);
+ }
+ if (c != '\n')
+ *p++ = c;
+ }
+ return(-1);
+}
+
+#define SCPYN(a, b) strncpy(a, b, sizeof (a))
+
+struct utmp utmp;
+
+void
+dologout(void)
+{
+ int save_errno = errno;
+ int status, wtmp;
+ pid_t pid;
+
+#ifdef BSDINETD
+ while ((pid=wait(&status)) > 0) {
+#else /* !BSDINETD */
+ while ((pid=wait3(&status, WNOHANG, 0)) > 0) {
+#endif /* !BSDINETD */
+ wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND);
+ if (wtmp >= 0) {
+ SCPYN(utmp.ut_line, utline);
+ SCPYN(utmp.ut_name, "");
+ SCPYN(utmp.ut_host, "");
+ (void) time(&utmp.ut_time);
+ (void) write(wtmp, (char *)&utmp, sizeof (utmp));
+ (void) close(wtmp);
+ }
+ }
+ errno = save_errno;
+}
+
+/*
+ * Record login in wtmp file.
+ */
+void
+dologin(struct passwd *pw, struct sockaddr *sa)
+{
+ char line[32];
+ char hbuf[NI_MAXHOST];
+ int wtmp, f;
+
+ if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0))
+ (void)strlcpy(hbuf, "?", sizeof(hbuf));
+ wtmp = open(_PATH_WTMP, O_WRONLY|O_APPEND);
+ if (wtmp >= 0) {
+ /* hack, but must be unique and no tty line */
+ (void) snprintf(line, sizeof line, "uucp%.4ld", (long)getpid());
+ SCPYN(utmp.ut_line, line);
+ SCPYN(utmp.ut_name, pw->pw_name);
+ SCPYN(utmp.ut_host, hbuf);
+ 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, pw->pw_uid * sizeof(struct lastlog), SEEK_SET);
+ SCPYN(ll.ll_line, hbuf);
+ SCPYN(ll.ll_host, hbuf);
+ (void) write(f, (char *) &ll, sizeof ll);
+ (void) close(f);
+ }
+}