OSDN Git Service

initial commit
authorTakuya ASADA <syuu@dokukino.com>
Mon, 16 Aug 2010 07:01:07 +0000 (16:01 +0900)
committerTakuya ASADA <syuu@dokukino.com>
Mon, 16 Aug 2010 07:01:07 +0000 (16:01 +0900)
469 files changed:
src/libexec/CVS/Entries [new file with mode: 0644]
src/libexec/CVS/Repository [new file with mode: 0644]
src/libexec/CVS/Root [new file with mode: 0644]
src/libexec/Makefile [new file with mode: 0644]
src/libexec/Makefile.inc [new file with mode: 0644]
src/libexec/comsat/CVS/Entries [new file with mode: 0644]
src/libexec/comsat/CVS/Repository [new file with mode: 0644]
src/libexec/comsat/CVS/Root [new file with mode: 0644]
src/libexec/comsat/Makefile [new file with mode: 0644]
src/libexec/comsat/comsat.8 [new file with mode: 0644]
src/libexec/comsat/comsat.c [new file with mode: 0644]
src/libexec/comsat/obj [new symlink]
src/libexec/fingerd/CVS/Entries [new file with mode: 0644]
src/libexec/fingerd/CVS/Repository [new file with mode: 0644]
src/libexec/fingerd/CVS/Root [new file with mode: 0644]
src/libexec/fingerd/Makefile [new file with mode: 0644]
src/libexec/fingerd/fingerd.8 [new file with mode: 0644]
src/libexec/fingerd/fingerd.c [new file with mode: 0644]
src/libexec/fingerd/obj [new symlink]
src/libexec/fingerd/pathnames.h [new file with mode: 0644]
src/libexec/ftpd/CVS/Entries [new file with mode: 0644]
src/libexec/ftpd/CVS/Repository [new file with mode: 0644]
src/libexec/ftpd/CVS/Root [new file with mode: 0644]
src/libexec/ftpd/Makefile [new file with mode: 0644]
src/libexec/ftpd/extern.h [new file with mode: 0644]
src/libexec/ftpd/ftpcmd.y [new file with mode: 0644]
src/libexec/ftpd/ftpd.8 [new file with mode: 0644]
src/libexec/ftpd/ftpd.c [new file with mode: 0644]
src/libexec/ftpd/logutmp.c [new file with mode: 0644]
src/libexec/ftpd/logwtmp.c [new file with mode: 0644]
src/libexec/ftpd/monitor.c [new file with mode: 0644]
src/libexec/ftpd/monitor.h [new file with mode: 0644]
src/libexec/ftpd/monitor_fdpass.c [new file with mode: 0644]
src/libexec/ftpd/obj [new symlink]
src/libexec/ftpd/pathnames.h [new file with mode: 0644]
src/libexec/ftpd/popen.c [new file with mode: 0644]
src/libexec/getNAME/CVS/Entries [new file with mode: 0644]
src/libexec/getNAME/CVS/Repository [new file with mode: 0644]
src/libexec/getNAME/CVS/Root [new file with mode: 0644]
src/libexec/getNAME/Makefile [new file with mode: 0644]
src/libexec/getNAME/getNAME.8 [new file with mode: 0644]
src/libexec/getNAME/getNAME.c [new file with mode: 0644]
src/libexec/getNAME/obj [new symlink]
src/libexec/getty/CVS/Entries [new file with mode: 0644]
src/libexec/getty/CVS/Repository [new file with mode: 0644]
src/libexec/getty/CVS/Root [new file with mode: 0644]
src/libexec/getty/Makefile [new file with mode: 0644]
src/libexec/getty/extern.h [new file with mode: 0644]
src/libexec/getty/getty.8 [new file with mode: 0644]
src/libexec/getty/gettytab.5 [new file with mode: 0644]
src/libexec/getty/gettytab.h [new file with mode: 0644]
src/libexec/getty/init.c [new file with mode: 0644]
src/libexec/getty/main.c [new file with mode: 0644]
src/libexec/getty/obj [new symlink]
src/libexec/getty/pathnames.h [new file with mode: 0644]
src/libexec/getty/subr.c [new file with mode: 0644]
src/libexec/getty/ttys.5 [new file with mode: 0644]
src/libexec/identd/CREDITS [new file with mode: 0644]
src/libexec/identd/CVS/Entries [new file with mode: 0644]
src/libexec/identd/CVS/Repository [new file with mode: 0644]
src/libexec/identd/CVS/Root [new file with mode: 0644]
src/libexec/identd/Makefile [new file with mode: 0644]
src/libexec/identd/identd.8 [new file with mode: 0644]
src/libexec/identd/identd.c [new file with mode: 0644]
src/libexec/identd/identd.h [new file with mode: 0644]
src/libexec/identd/obj [new symlink]
src/libexec/identd/openbsd.c [new file with mode: 0644]
src/libexec/identd/parse.c [new file with mode: 0644]
src/libexec/ld.so/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/Makefile [new file with mode: 0644]
src/libexec/ld.so/alpha/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/alpha/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/alpha/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/alpha/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/alpha/archdep.h [new file with mode: 0644]
src/libexec/ld.so/alpha/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/alpha/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/alpha/syscall.h [new file with mode: 0644]
src/libexec/ld.so/amd64/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/amd64/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/amd64/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/amd64/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/amd64/archdep.h [new file with mode: 0644]
src/libexec/ld.so/amd64/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/amd64/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/amd64/syscall.h [new file with mode: 0644]
src/libexec/ld.so/arm/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/arm/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/arm/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/arm/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/arm/archdep.h [new file with mode: 0644]
src/libexec/ld.so/arm/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/arm/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/arm/syscall.h [new file with mode: 0644]
src/libexec/ld.so/dir.c [new file with mode: 0644]
src/libexec/ld.so/dir.h [new file with mode: 0644]
src/libexec/ld.so/dl_prebind.c [new file with mode: 0644]
src/libexec/ld.so/dl_prebind.h [new file with mode: 0644]
src/libexec/ld.so/dl_printf.c [new file with mode: 0644]
src/libexec/ld.so/dlfcn.c [new file with mode: 0644]
src/libexec/ld.so/hppa/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/hppa/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/hppa/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/hppa/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/hppa/archdep.h [new file with mode: 0644]
src/libexec/ld.so/hppa/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/hppa/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/hppa/syscall.h [new file with mode: 0644]
src/libexec/ld.so/i386/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/i386/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/i386/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/i386/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/i386/archdep.h [new file with mode: 0644]
src/libexec/ld.so/i386/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/i386/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/i386/syscall.h [new file with mode: 0644]
src/libexec/ld.so/ld.so.1 [new file with mode: 0644]
src/libexec/ld.so/ldconfig/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/ldconfig/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/ldconfig/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/ldconfig/Makefile [new file with mode: 0644]
src/libexec/ld.so/ldconfig/debug.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/dl_prebind.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/etc.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/ld.h [new file with mode: 0644]
src/libexec/ld.so/ldconfig/ldconfig.8 [new file with mode: 0644]
src/libexec/ld.so/ldconfig/ldconfig.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/library.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/obj [new symlink]
src/libexec/ld.so/ldconfig/prebind.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/prebind.h [new file with mode: 0644]
src/libexec/ld.so/ldconfig/prebind_delete.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/prebind_struct.h [new file with mode: 0644]
src/libexec/ld.so/ldconfig/shlib.c [new file with mode: 0644]
src/libexec/ld.so/ldconfig/sod.c [new file with mode: 0644]
src/libexec/ld.so/ldd/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/ldd/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/ldd/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/ldd/Makefile [new file with mode: 0644]
src/libexec/ld.so/ldd/ldd.1 [new file with mode: 0644]
src/libexec/ld.so/ldd/ldd.c [new file with mode: 0644]
src/libexec/ld.so/ldd/obj [new symlink]
src/libexec/ld.so/library.c [new file with mode: 0644]
src/libexec/ld.so/library_mquery.c [new file with mode: 0644]
src/libexec/ld.so/library_subr.c [new file with mode: 0644]
src/libexec/ld.so/loader.c [new file with mode: 0644]
src/libexec/ld.so/mips64/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/mips64/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/mips64/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/mips64/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/mips64/archdep.h [new file with mode: 0644]
src/libexec/ld.so/mips64/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/mips64/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/mips64/syscall.h [new file with mode: 0644]
src/libexec/ld.so/obj [new symlink]
src/libexec/ld.so/powerpc/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/powerpc/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/powerpc/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/powerpc/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/powerpc/archdep.h [new file with mode: 0644]
src/libexec/ld.so/powerpc/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/powerpc/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/powerpc/syscall.h [new file with mode: 0644]
src/libexec/ld.so/prebind.h [new file with mode: 0644]
src/libexec/ld.so/resolve.c [new file with mode: 0644]
src/libexec/ld.so/resolve.h [new file with mode: 0644]
src/libexec/ld.so/sh/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/sh/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/sh/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/sh/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/sh/archdep.h [new file with mode: 0644]
src/libexec/ld.so/sh/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/sh/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/sh/syscall.h [new file with mode: 0644]
src/libexec/ld.so/sod.c [new file with mode: 0644]
src/libexec/ld.so/sod.h [new file with mode: 0644]
src/libexec/ld.so/sparc/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/sparc/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/sparc/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/sparc/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/sparc/archdep.h [new file with mode: 0644]
src/libexec/ld.so/sparc/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/sparc/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/sparc/syscall.h [new file with mode: 0644]
src/libexec/ld.so/sparc64/CVS/Entries [new file with mode: 0644]
src/libexec/ld.so/sparc64/CVS/Repository [new file with mode: 0644]
src/libexec/ld.so/sparc64/CVS/Root [new file with mode: 0644]
src/libexec/ld.so/sparc64/Makefile.inc [new file with mode: 0644]
src/libexec/ld.so/sparc64/archdep.h [new file with mode: 0644]
src/libexec/ld.so/sparc64/ldasm.S [new file with mode: 0644]
src/libexec/ld.so/sparc64/rtld_machine.c [new file with mode: 0644]
src/libexec/ld.so/sparc64/syscall.h [new file with mode: 0644]
src/libexec/ld.so/strtol.c [new file with mode: 0644]
src/libexec/ld.so/util.c [new file with mode: 0644]
src/libexec/ld.so/util.h [new file with mode: 0644]
src/libexec/lockspool/CVS/Entries [new file with mode: 0644]
src/libexec/lockspool/CVS/Repository [new file with mode: 0644]
src/libexec/lockspool/CVS/Root [new file with mode: 0644]
src/libexec/lockspool/Makefile [new file with mode: 0644]
src/libexec/lockspool/lockspool.1 [new file with mode: 0644]
src/libexec/lockspool/lockspool.c [new file with mode: 0644]
src/libexec/lockspool/obj [new symlink]
src/libexec/login_chpass/CVS/Entries [new file with mode: 0644]
src/libexec/login_chpass/CVS/Repository [new file with mode: 0644]
src/libexec/login_chpass/CVS/Root [new file with mode: 0644]
src/libexec/login_chpass/Makefile [new file with mode: 0644]
src/libexec/login_chpass/login_chpass.8 [new file with mode: 0644]
src/libexec/login_chpass/login_chpass.c [new file with mode: 0644]
src/libexec/login_chpass/obj [new symlink]
src/libexec/login_krb5-or-pwd/CVS/Entries [new file with mode: 0644]
src/libexec/login_krb5-or-pwd/CVS/Repository [new file with mode: 0644]
src/libexec/login_krb5-or-pwd/CVS/Root [new file with mode: 0644]
src/libexec/login_krb5-or-pwd/Makefile [new file with mode: 0644]
src/libexec/login_krb5-or-pwd/login_krb5-or-pwd.8 [new file with mode: 0644]
src/libexec/login_krb5-or-pwd/obj [new symlink]
src/libexec/login_krb5/CVS/Entries [new file with mode: 0644]
src/libexec/login_krb5/CVS/Repository [new file with mode: 0644]
src/libexec/login_krb5/CVS/Root [new file with mode: 0644]
src/libexec/login_krb5/Makefile [new file with mode: 0644]
src/libexec/login_krb5/login_krb5.8 [new file with mode: 0644]
src/libexec/login_krb5/login_krb5.c [new file with mode: 0644]
src/libexec/login_krb5/obj [new symlink]
src/libexec/login_lchpass/CVS/Entries [new file with mode: 0644]
src/libexec/login_lchpass/CVS/Repository [new file with mode: 0644]
src/libexec/login_lchpass/CVS/Root [new file with mode: 0644]
src/libexec/login_lchpass/Makefile [new file with mode: 0644]
src/libexec/login_lchpass/login_lchpass.8 [new file with mode: 0644]
src/libexec/login_lchpass/login_lchpass.c [new file with mode: 0644]
src/libexec/login_lchpass/obj [new symlink]
src/libexec/login_passwd/CVS/Entries [new file with mode: 0644]
src/libexec/login_passwd/CVS/Repository [new file with mode: 0644]
src/libexec/login_passwd/CVS/Root [new file with mode: 0644]
src/libexec/login_passwd/Makefile [new file with mode: 0644]
src/libexec/login_passwd/common.h [new file with mode: 0644]
src/libexec/login_passwd/login.c [new file with mode: 0644]
src/libexec/login_passwd/login_passwd.8 [new file with mode: 0644]
src/libexec/login_passwd/login_passwd.c [new file with mode: 0644]
src/libexec/login_passwd/obj [new symlink]
src/libexec/login_radius/CVS/Entries [new file with mode: 0644]
src/libexec/login_radius/CVS/Repository [new file with mode: 0644]
src/libexec/login_radius/CVS/Root [new file with mode: 0644]
src/libexec/login_radius/Makefile [new file with mode: 0644]
src/libexec/login_radius/login_radius.8 [new file with mode: 0644]
src/libexec/login_radius/login_radius.c [new file with mode: 0644]
src/libexec/login_radius/login_radius.h [new file with mode: 0644]
src/libexec/login_radius/obj [new symlink]
src/libexec/login_radius/raddauth.c [new file with mode: 0644]
src/libexec/login_reject/CVS/Entries [new file with mode: 0644]
src/libexec/login_reject/CVS/Repository [new file with mode: 0644]
src/libexec/login_reject/CVS/Root [new file with mode: 0644]
src/libexec/login_reject/Makefile [new file with mode: 0644]
src/libexec/login_reject/login_reject.8 [new file with mode: 0644]
src/libexec/login_reject/login_reject.c [new file with mode: 0644]
src/libexec/login_reject/obj [new symlink]
src/libexec/login_skey/CVS/Entries [new file with mode: 0644]
src/libexec/login_skey/CVS/Repository [new file with mode: 0644]
src/libexec/login_skey/CVS/Root [new file with mode: 0644]
src/libexec/login_skey/Makefile [new file with mode: 0644]
src/libexec/login_skey/login_skey.8 [new file with mode: 0644]
src/libexec/login_skey/login_skey.c [new file with mode: 0644]
src/libexec/login_skey/obj [new symlink]
src/libexec/login_tis/CVS/Entries [new file with mode: 0644]
src/libexec/login_tis/CVS/Repository [new file with mode: 0644]
src/libexec/login_tis/CVS/Root [new file with mode: 0644]
src/libexec/login_tis/Makefile [new file with mode: 0644]
src/libexec/login_tis/login_tis.8 [new file with mode: 0644]
src/libexec/login_tis/login_tis.c [new file with mode: 0644]
src/libexec/login_tis/login_tis.h [new file with mode: 0644]
src/libexec/login_tis/obj [new symlink]
src/libexec/login_token/CVS/Entries [new file with mode: 0644]
src/libexec/login_token/CVS/Repository [new file with mode: 0644]
src/libexec/login_token/CVS/Root [new file with mode: 0644]
src/libexec/login_token/Makefile [new file with mode: 0644]
src/libexec/login_token/init.c [new file with mode: 0644]
src/libexec/login_token/login_token.8 [new file with mode: 0644]
src/libexec/login_token/login_token.c [new file with mode: 0644]
src/libexec/login_token/obj [new symlink]
src/libexec/login_token/token.c [new file with mode: 0644]
src/libexec/login_token/token.h [new file with mode: 0644]
src/libexec/login_token/tokendb.c [new file with mode: 0644]
src/libexec/login_token/tokendb.h [new file with mode: 0644]
src/libexec/mail.local/CVS/Entries [new file with mode: 0644]
src/libexec/mail.local/CVS/Repository [new file with mode: 0644]
src/libexec/mail.local/CVS/Root [new file with mode: 0644]
src/libexec/mail.local/Makefile [new file with mode: 0644]
src/libexec/mail.local/locking.c [new file with mode: 0644]
src/libexec/mail.local/mail.local.8 [new file with mode: 0644]
src/libexec/mail.local/mail.local.c [new file with mode: 0644]
src/libexec/mail.local/mail.local.h [new file with mode: 0644]
src/libexec/mail.local/obj [new symlink]
src/libexec/mail.local/pathnames.h [new file with mode: 0644]
src/libexec/makewhatis/CVS/Entries [new file with mode: 0644]
src/libexec/makewhatis/CVS/Repository [new file with mode: 0644]
src/libexec/makewhatis/CVS/Root [new file with mode: 0644]
src/libexec/makewhatis/Makefile [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/CVS/Entries [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/CVS/Repository [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/CVS/Root [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis.pm [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Entries [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Repository [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Root [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/Check.pm [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/Find.pm [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/Formated.pm [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/Unformated.pm [new file with mode: 0644]
src/libexec/makewhatis/OpenBSD/Makewhatis/Whatis.pm [new file with mode: 0644]
src/libexec/makewhatis/makewhatis [new file with mode: 0644]
src/libexec/makewhatis/makewhatis.8 [new file with mode: 0644]
src/libexec/makewhatis/obj [new symlink]
src/libexec/rpc.rquotad/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.rquotad/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.rquotad/CVS/Root [new file with mode: 0644]
src/libexec/rpc.rquotad/Makefile [new file with mode: 0644]
src/libexec/rpc.rquotad/obj [new symlink]
src/libexec/rpc.rquotad/rpc.rquotad.8 [new file with mode: 0644]
src/libexec/rpc.rquotad/rquotad.c [new file with mode: 0644]
src/libexec/rpc.rstatd/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.rstatd/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.rstatd/CVS/Root [new file with mode: 0644]
src/libexec/rpc.rstatd/Makefile [new file with mode: 0644]
src/libexec/rpc.rstatd/obj [new symlink]
src/libexec/rpc.rstatd/rpc.rstatd.8 [new file with mode: 0644]
src/libexec/rpc.rstatd/rstat_proc.c [new file with mode: 0644]
src/libexec/rpc.rstatd/rstatd.c [new file with mode: 0644]
src/libexec/rpc.rusersd/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.rusersd/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.rusersd/CVS/Root [new file with mode: 0644]
src/libexec/rpc.rusersd/Makefile [new file with mode: 0644]
src/libexec/rpc.rusersd/obj [new symlink]
src/libexec/rpc.rusersd/rpc.rusersd.8 [new file with mode: 0644]
src/libexec/rpc.rusersd/rusers_proc.c [new file with mode: 0644]
src/libexec/rpc.rusersd/rusersd.c [new file with mode: 0644]
src/libexec/rpc.rwalld/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.rwalld/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.rwalld/CVS/Root [new file with mode: 0644]
src/libexec/rpc.rwalld/Makefile [new file with mode: 0644]
src/libexec/rpc.rwalld/obj [new symlink]
src/libexec/rpc.rwalld/rpc.rwalld.8 [new file with mode: 0644]
src/libexec/rpc.rwalld/rwalld.c [new file with mode: 0644]
src/libexec/rpc.sprayd/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.sprayd/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.sprayd/CVS/Root [new file with mode: 0644]
src/libexec/rpc.sprayd/Makefile [new file with mode: 0644]
src/libexec/rpc.sprayd/obj [new symlink]
src/libexec/rpc.sprayd/rpc.sprayd.8 [new file with mode: 0644]
src/libexec/rpc.sprayd/sprayd.c [new file with mode: 0644]
src/libexec/rpc.yppasswdd/CVS/Entries [new file with mode: 0644]
src/libexec/rpc.yppasswdd/CVS/Repository [new file with mode: 0644]
src/libexec/rpc.yppasswdd/CVS/Root [new file with mode: 0644]
src/libexec/rpc.yppasswdd/Makefile [new file with mode: 0644]
src/libexec/rpc.yppasswdd/obj [new symlink]
src/libexec/rpc.yppasswdd/rpc.yppasswdd.8 [new file with mode: 0644]
src/libexec/rpc.yppasswdd/rpc.yppasswdd.c [new file with mode: 0644]
src/libexec/rpc.yppasswdd/yppasswd.h [new file with mode: 0644]
src/libexec/rpc.yppasswdd/yppasswdd_mkpw.c [new file with mode: 0644]
src/libexec/rpc.yppasswdd/yppasswdd_proc.c [new file with mode: 0644]
src/libexec/rshd/CVS/Entries [new file with mode: 0644]
src/libexec/rshd/CVS/Repository [new file with mode: 0644]
src/libexec/rshd/CVS/Root [new file with mode: 0644]
src/libexec/rshd/Makefile [new file with mode: 0644]
src/libexec/rshd/obj [new symlink]
src/libexec/rshd/rshd.8 [new file with mode: 0644]
src/libexec/rshd/rshd.c [new file with mode: 0644]
src/libexec/spamd-setup/CVS/Entries [new file with mode: 0644]
src/libexec/spamd-setup/CVS/Repository [new file with mode: 0644]
src/libexec/spamd-setup/CVS/Root [new file with mode: 0644]
src/libexec/spamd-setup/Makefile [new file with mode: 0644]
src/libexec/spamd-setup/obj [new symlink]
src/libexec/spamd-setup/spamd-setup.8 [new file with mode: 0644]
src/libexec/spamd-setup/spamd-setup.c [new file with mode: 0644]
src/libexec/spamd/CVS/Entries [new file with mode: 0644]
src/libexec/spamd/CVS/Repository [new file with mode: 0644]
src/libexec/spamd/CVS/Root [new file with mode: 0644]
src/libexec/spamd/Makefile [new file with mode: 0644]
src/libexec/spamd/grey.c [new file with mode: 0644]
src/libexec/spamd/grey.h [new file with mode: 0644]
src/libexec/spamd/obj [new symlink]
src/libexec/spamd/sdl.c [new file with mode: 0644]
src/libexec/spamd/sdl.h [new file with mode: 0644]
src/libexec/spamd/spamd.8 [new file with mode: 0644]
src/libexec/spamd/spamd.c [new file with mode: 0644]
src/libexec/spamd/sync.c [new file with mode: 0644]
src/libexec/spamd/sync.h [new file with mode: 0644]
src/libexec/spamlogd/CVS/Entries [new file with mode: 0644]
src/libexec/spamlogd/CVS/Repository [new file with mode: 0644]
src/libexec/spamlogd/CVS/Root [new file with mode: 0644]
src/libexec/spamlogd/Makefile [new file with mode: 0644]
src/libexec/spamlogd/obj [new symlink]
src/libexec/spamlogd/spamlogd.8 [new file with mode: 0644]
src/libexec/spamlogd/spamlogd.c [new file with mode: 0644]
src/libexec/talkd/CVS/Entries [new file with mode: 0644]
src/libexec/talkd/CVS/Repository [new file with mode: 0644]
src/libexec/talkd/CVS/Root [new file with mode: 0644]
src/libexec/talkd/Makefile [new file with mode: 0644]
src/libexec/talkd/announce.c [new file with mode: 0644]
src/libexec/talkd/obj [new symlink]
src/libexec/talkd/print.c [new file with mode: 0644]
src/libexec/talkd/process.c [new file with mode: 0644]
src/libexec/talkd/table.c [new file with mode: 0644]
src/libexec/talkd/talkd.8 [new file with mode: 0644]
src/libexec/talkd/talkd.c [new file with mode: 0644]
src/libexec/talkd/talkd.h [new file with mode: 0644]
src/libexec/tcpd/BLURB [new file with mode: 0644]
src/libexec/tcpd/CHANGES [new file with mode: 0644]
src/libexec/tcpd/CVS/Entries [new file with mode: 0644]
src/libexec/tcpd/CVS/Repository [new file with mode: 0644]
src/libexec/tcpd/CVS/Root [new file with mode: 0644]
src/libexec/tcpd/DISCLAIMER [new file with mode: 0644]
src/libexec/tcpd/Makefile [new file with mode: 0644]
src/libexec/tcpd/Makefile.inc [new file with mode: 0644]
src/libexec/tcpd/README [new file with mode: 0644]
src/libexec/tcpd/safe_finger/CVS/Entries [new file with mode: 0644]
src/libexec/tcpd/safe_finger/CVS/Repository [new file with mode: 0644]
src/libexec/tcpd/safe_finger/CVS/Root [new file with mode: 0644]
src/libexec/tcpd/safe_finger/Makefile [new file with mode: 0644]
src/libexec/tcpd/safe_finger/safe_finger.8 [new file with mode: 0644]
src/libexec/tcpd/safe_finger/safe_finger.c [new file with mode: 0644]
src/libexec/tcpd/tcpd/CVS/Entries [new file with mode: 0644]
src/libexec/tcpd/tcpd/CVS/Repository [new file with mode: 0644]
src/libexec/tcpd/tcpd/CVS/Root [new file with mode: 0644]
src/libexec/tcpd/tcpd/Makefile [new file with mode: 0644]
src/libexec/tcpd/tcpd/obj [new symlink]
src/libexec/tcpd/tcpd/tcpd.8 [new file with mode: 0644]
src/libexec/tcpd/tcpd/tcpd.c [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/CVS/Entries [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/CVS/Repository [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/CVS/Root [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/Makefile [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/inetcf.c [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/inetcf.h [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/obj [new symlink]
src/libexec/tcpd/tcpdchk/scaffold.c [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/scaffold.h [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/tcpdchk.8 [new file with mode: 0644]
src/libexec/tcpd/tcpdchk/tcpdchk.c [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/CVS/Entries [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/CVS/Repository [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/CVS/Root [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/Makefile [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/obj [new symlink]
src/libexec/tcpd/tcpdmatch/tcpdmatch.8 [new file with mode: 0644]
src/libexec/tcpd/tcpdmatch/tcpdmatch.c [new file with mode: 0644]
src/libexec/tftp-proxy/CVS/Entries [new file with mode: 0644]
src/libexec/tftp-proxy/CVS/Repository [new file with mode: 0644]
src/libexec/tftp-proxy/CVS/Root [new file with mode: 0644]
src/libexec/tftp-proxy/Makefile [new file with mode: 0644]
src/libexec/tftp-proxy/filter.c [new file with mode: 0644]
src/libexec/tftp-proxy/filter.h [new file with mode: 0644]
src/libexec/tftp-proxy/obj [new symlink]
src/libexec/tftp-proxy/tftp-proxy.8 [new file with mode: 0644]
src/libexec/tftp-proxy/tftp-proxy.c [new file with mode: 0644]
src/libexec/tftpd/CVS/Entries [new file with mode: 0644]
src/libexec/tftpd/CVS/Repository [new file with mode: 0644]
src/libexec/tftpd/CVS/Root [new file with mode: 0644]
src/libexec/tftpd/Makefile [new file with mode: 0644]
src/libexec/tftpd/obj [new symlink]
src/libexec/tftpd/tftpd.8 [new file with mode: 0644]
src/libexec/tftpd/tftpd.c [new file with mode: 0644]
src/libexec/uucpd/CVS/Entries [new file with mode: 0644]
src/libexec/uucpd/CVS/Repository [new file with mode: 0644]
src/libexec/uucpd/CVS/Root [new file with mode: 0644]
src/libexec/uucpd/Makefile [new file with mode: 0644]
src/libexec/uucpd/obj [new symlink]
src/libexec/uucpd/pathnames.h [new file with mode: 0644]
src/libexec/uucpd/uucpd.8 [new file with mode: 0644]
src/libexec/uucpd/uucpd.c [new file with mode: 0644]

diff --git a/src/libexec/CVS/Entries b/src/libexec/CVS/Entries
new file mode 100644 (file)
index 0000000..65638fb
--- /dev/null
@@ -0,0 +1,37 @@
+/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////
diff --git a/src/libexec/CVS/Repository b/src/libexec/CVS/Repository
new file mode 100644 (file)
index 0000000..31ac426
--- /dev/null
@@ -0,0 +1 @@
+src/libexec
diff --git a/src/libexec/CVS/Root b/src/libexec/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/Makefile b/src/libexec/Makefile
new file mode 100644 (file)
index 0000000..aa6b2f3
--- /dev/null
@@ -0,0 +1,26 @@
+#      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>
diff --git a/src/libexec/Makefile.inc b/src/libexec/Makefile.inc
new file mode 100644 (file)
index 0000000..84b5d42
--- /dev/null
@@ -0,0 +1,3 @@
+#      $OpenBSD: Makefile.inc,v 1.2 2001/01/28 19:34:26 niklas Exp $
+
+BINDIR?=       /usr/libexec
diff --git a/src/libexec/comsat/CVS/Entries b/src/libexec/comsat/CVS/Entries
new file mode 100644 (file)
index 0000000..d6770d5
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/comsat/CVS/Repository b/src/libexec/comsat/CVS/Repository
new file mode 100644 (file)
index 0000000..80e5300
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/comsat
diff --git a/src/libexec/comsat/CVS/Root b/src/libexec/comsat/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/comsat/Makefile b/src/libexec/comsat/Makefile
new file mode 100644 (file)
index 0000000..ad3d5a7
--- /dev/null
@@ -0,0 +1,6 @@
+#      $OpenBSD: Makefile,v 1.2 2001/01/28 19:34:27 niklas Exp $
+
+PROG=  comsat
+MAN=   comsat.8
+
+.include <bsd.prog.mk>
diff --git a/src/libexec/comsat/comsat.8 b/src/libexec/comsat/comsat.8
new file mode 100644 (file)
index 0000000..1465107
--- /dev/null
@@ -0,0 +1,93 @@
+.\"    $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.
diff --git a/src/libexec/comsat/comsat.c b/src/libexec/comsat/comsat.c
new file mode 100644 (file)
index 0000000..eb2e1b4
--- /dev/null
@@ -0,0 +1,318 @@
+/*     $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);
+}
diff --git a/src/libexec/comsat/obj b/src/libexec/comsat/obj
new file mode 120000 (symlink)
index 0000000..7cdc301
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/comsat
\ No newline at end of file
diff --git a/src/libexec/fingerd/CVS/Entries b/src/libexec/fingerd/CVS/Entries
new file mode 100644 (file)
index 0000000..7896bea
--- /dev/null
@@ -0,0 +1,5 @@
+/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
diff --git a/src/libexec/fingerd/CVS/Repository b/src/libexec/fingerd/CVS/Repository
new file mode 100644 (file)
index 0000000..bfe7d3b
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/fingerd
diff --git a/src/libexec/fingerd/CVS/Root b/src/libexec/fingerd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/fingerd/Makefile b/src/libexec/fingerd/Makefile
new file mode 100644 (file)
index 0000000..5faf097
--- /dev/null
@@ -0,0 +1,6 @@
+#      $OpenBSD: Makefile,v 1.3 2001/01/28 19:34:27 niklas Exp $
+
+PROG=  fingerd
+MAN=   fingerd.8
+
+.include <bsd.prog.mk>
diff --git a/src/libexec/fingerd/fingerd.8 b/src/libexec/fingerd/fingerd.8
new file mode 100644 (file)
index 0000000..6b59d7a
--- /dev/null
@@ -0,0 +1,176 @@
+.\"    $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.
diff --git a/src/libexec/fingerd/fingerd.c b/src/libexec/fingerd/fingerd.c
new file mode 100644 (file)
index 0000000..d4f9ce6
--- /dev/null
@@ -0,0 +1,223 @@
+/*     $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);
+}
diff --git a/src/libexec/fingerd/obj b/src/libexec/fingerd/obj
new file mode 120000 (symlink)
index 0000000..ce2b1a1
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/fingerd
\ No newline at end of file
diff --git a/src/libexec/fingerd/pathnames.h b/src/libexec/fingerd/pathnames.h
new file mode 100644 (file)
index 0000000..c95a2cb
--- /dev/null
@@ -0,0 +1,34 @@
+/*     $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"
diff --git a/src/libexec/ftpd/CVS/Entries b/src/libexec/ftpd/CVS/Entries
new file mode 100644 (file)
index 0000000..8225625
--- /dev/null
@@ -0,0 +1,13 @@
+/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
diff --git a/src/libexec/ftpd/CVS/Repository b/src/libexec/ftpd/CVS/Repository
new file mode 100644 (file)
index 0000000..3d8c4a2
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ftpd
diff --git a/src/libexec/ftpd/CVS/Root b/src/libexec/ftpd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ftpd/Makefile b/src/libexec/ftpd/Makefile
new file mode 100644 (file)
index 0000000..1213468
--- /dev/null
@@ -0,0 +1,33 @@
+#      $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>
diff --git a/src/libexec/ftpd/extern.h b/src/libexec/ftpd/extern.h
new file mode 100644 (file)
index 0000000..7268cda
--- /dev/null
@@ -0,0 +1,120 @@
+/*     $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
diff --git a/src/libexec/ftpd/ftpcmd.y b/src/libexec/ftpd/ftpcmd.y
new file mode 100644 (file)
index 0000000..ae38072
--- /dev/null
@@ -0,0 +1,1540 @@
+/*     $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]);
+       }
+}
diff --git a/src/libexec/ftpd/ftpd.8 b/src/libexec/ftpd/ftpd.8
new file mode 100644 (file)
index 0000000..0308414
--- /dev/null
@@ -0,0 +1,544 @@
+.\"    $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 .
diff --git a/src/libexec/ftpd/ftpd.c b/src/libexec/ftpd/ftpd.c
new file mode 100644 (file)
index 0000000..1f65a15
--- /dev/null
@@ -0,0 +1,2923 @@
+/*     $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);
+}
diff --git a/src/libexec/ftpd/logutmp.c b/src/libexec/ftpd/logutmp.c
new file mode 100644 (file)
index 0000000..fd3cfd3
--- /dev/null
@@ -0,0 +1,127 @@
+/*     $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);
+}
diff --git a/src/libexec/ftpd/logwtmp.c b/src/libexec/ftpd/logwtmp.c
new file mode 100644 (file)
index 0000000..4e62ac6
--- /dev/null
@@ -0,0 +1,75 @@
+/*     $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);
+       }
+}
diff --git a/src/libexec/ftpd/monitor.c b/src/libexec/ftpd/monitor.c
new file mode 100644 (file)
index 0000000..9983dc0
--- /dev/null
@@ -0,0 +1,529 @@
+/*     $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);
+}
diff --git a/src/libexec/ftpd/monitor.h b/src/libexec/ftpd/monitor.h
new file mode 100644 (file)
index 0000000..737156e
--- /dev/null
@@ -0,0 +1,42 @@
+/*     $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 */
diff --git a/src/libexec/ftpd/monitor_fdpass.c b/src/libexec/ftpd/monitor_fdpass.c
new file mode 100644 (file)
index 0000000..f4923a1
--- /dev/null
@@ -0,0 +1,110 @@
+/*     $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);
+       }
+}
diff --git a/src/libexec/ftpd/obj b/src/libexec/ftpd/obj
new file mode 120000 (symlink)
index 0000000..322f26d
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/ftpd
\ No newline at end of file
diff --git a/src/libexec/ftpd/pathnames.h b/src/libexec/ftpd/pathnames.h
new file mode 100644 (file)
index 0000000..d2395a2
--- /dev/null
@@ -0,0 +1,42 @@
+/*     $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"
diff --git a/src/libexec/ftpd/popen.c b/src/libexec/ftpd/popen.c
new file mode 100644 (file)
index 0000000..d19243b
--- /dev/null
@@ -0,0 +1,199 @@
+/*     $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);
+}
diff --git a/src/libexec/getNAME/CVS/Entries b/src/libexec/getNAME/CVS/Entries
new file mode 100644 (file)
index 0000000..7279c62
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/getNAME/CVS/Repository b/src/libexec/getNAME/CVS/Repository
new file mode 100644 (file)
index 0000000..4c5a3bf
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/getNAME
diff --git a/src/libexec/getNAME/CVS/Root b/src/libexec/getNAME/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/getNAME/Makefile b/src/libexec/getNAME/Makefile
new file mode 100644 (file)
index 0000000..a85e02a
--- /dev/null
@@ -0,0 +1,7 @@
+#      $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>
diff --git a/src/libexec/getNAME/getNAME.8 b/src/libexec/getNAME/getNAME.8
new file mode 100644 (file)
index 0000000..6b1f5d6
--- /dev/null
@@ -0,0 +1,90 @@
+.\"    $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.
diff --git a/src/libexec/getNAME/getNAME.c b/src/libexec/getNAME/getNAME.c
new file mode 100644 (file)
index 0000000..5238ffc
--- /dev/null
@@ -0,0 +1,342 @@
+/*     $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);
+}
diff --git a/src/libexec/getNAME/obj b/src/libexec/getNAME/obj
new file mode 120000 (symlink)
index 0000000..030f34e
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/getNAME
\ No newline at end of file
diff --git a/src/libexec/getty/CVS/Entries b/src/libexec/getty/CVS/Entries
new file mode 100644 (file)
index 0000000..397a88a
--- /dev/null
@@ -0,0 +1,11 @@
+/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
diff --git a/src/libexec/getty/CVS/Repository b/src/libexec/getty/CVS/Repository
new file mode 100644 (file)
index 0000000..db37409
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/getty
diff --git a/src/libexec/getty/CVS/Root b/src/libexec/getty/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/getty/Makefile b/src/libexec/getty/Makefile
new file mode 100644 (file)
index 0000000..7d5dd67
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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>
diff --git a/src/libexec/getty/extern.h b/src/libexec/getty/extern.h
new file mode 100644 (file)
index 0000000..f460146
--- /dev/null
@@ -0,0 +1,52 @@
+/*     $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);
diff --git a/src/libexec/getty/getty.8 b/src/libexec/getty/getty.8
new file mode 100644 (file)
index 0000000..9507c8a
--- /dev/null
@@ -0,0 +1,133 @@
+.\"    $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 .
diff --git a/src/libexec/getty/gettytab.5 b/src/libexec/getty/gettytab.5
new file mode 100644 (file)
index 0000000..e3d0f61
--- /dev/null
@@ -0,0 +1,417 @@
+.\"    $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.
diff --git a/src/libexec/getty/gettytab.h b/src/libexec/getty/gettytab.h
new file mode 100644 (file)
index 0000000..fa77a9b
--- /dev/null
@@ -0,0 +1,169 @@
+/*     $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;
diff --git a/src/libexec/getty/init.c b/src/libexec/getty/init.c
new file mode 100644 (file)
index 0000000..822218f
--- /dev/null
@@ -0,0 +1,127 @@
+/*     $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 }
+};
diff --git a/src/libexec/getty/main.c b/src/libexec/getty/main.c
new file mode 100644 (file)
index 0000000..9ad71b0
--- /dev/null
@@ -0,0 +1,586 @@
+/*     $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++;
+       }
+}
diff --git a/src/libexec/getty/obj b/src/libexec/getty/obj
new file mode 120000 (symlink)
index 0000000..ca6c589
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/getty
\ No newline at end of file
diff --git a/src/libexec/getty/pathnames.h b/src/libexec/getty/pathnames.h
new file mode 100644 (file)
index 0000000..2237c13
--- /dev/null
@@ -0,0 +1,37 @@
+/*     $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"
diff --git a/src/libexec/getty/subr.c b/src/libexec/getty/subr.c
new file mode 100644 (file)
index 0000000..b97c007
--- /dev/null
@@ -0,0 +1,724 @@
+/*     $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);
+}
diff --git a/src/libexec/getty/ttys.5 b/src/libexec/getty/ttys.5
new file mode 100644 (file)
index 0000000..5c2cae2
--- /dev/null
@@ -0,0 +1,165 @@
+.\"    $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 .
diff --git a/src/libexec/identd/CREDITS b/src/libexec/identd/CREDITS
new file mode 100644 (file)
index 0000000..c117a03
--- /dev/null
@@ -0,0 +1,52 @@
+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.*)
+
diff --git a/src/libexec/identd/CVS/Entries b/src/libexec/identd/CVS/Entries
new file mode 100644 (file)
index 0000000..369bd4f
--- /dev/null
@@ -0,0 +1,8 @@
+/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
diff --git a/src/libexec/identd/CVS/Repository b/src/libexec/identd/CVS/Repository
new file mode 100644 (file)
index 0000000..b59851b
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/identd
diff --git a/src/libexec/identd/CVS/Root b/src/libexec/identd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/identd/Makefile b/src/libexec/identd/Makefile
new file mode 100644 (file)
index 0000000..7ee1e63
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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>
diff --git a/src/libexec/identd/identd.8 b/src/libexec/identd/identd.8
new file mode 100644 (file)
index 0000000..bd392be
--- /dev/null
@@ -0,0 +1,233 @@
+.\"    $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.
diff --git a/src/libexec/identd/identd.c b/src/libexec/identd/identd.c
new file mode 100644 (file)
index 0000000..37e8351
--- /dev/null
@@ -0,0 +1,548 @@
+/*     $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));
+}
diff --git a/src/libexec/identd/identd.h b/src/libexec/identd/identd.h
new file mode 100644 (file)
index 0000000..941ac4a
--- /dev/null
@@ -0,0 +1,48 @@
+/*     $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
diff --git a/src/libexec/identd/obj b/src/libexec/identd/obj
new file mode 120000 (symlink)
index 0000000..f80469b
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/identd
\ No newline at end of file
diff --git a/src/libexec/identd/openbsd.c b/src/libexec/identd/openbsd.c
new file mode 100644 (file)
index 0000000..3782440
--- /dev/null
@@ -0,0 +1,107 @@
+/* $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);
+}
diff --git a/src/libexec/identd/parse.c b/src/libexec/identd/parse.c
new file mode 100644 (file)
index 0000000..768b2ec
--- /dev/null
@@ -0,0 +1,573 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/CVS/Entries b/src/libexec/ld.so/CVS/Entries
new file mode 100644 (file)
index 0000000..4871195
--- /dev/null
@@ -0,0 +1,32 @@
+/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//
diff --git a/src/libexec/ld.so/CVS/Repository b/src/libexec/ld.so/CVS/Repository
new file mode 100644 (file)
index 0000000..a212006
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so
diff --git a/src/libexec/ld.so/CVS/Root b/src/libexec/ld.so/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/Makefile b/src/libexec/ld.so/Makefile
new file mode 100644 (file)
index 0000000..1850615
--- /dev/null
@@ -0,0 +1,38 @@
+#      $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>
diff --git a/src/libexec/ld.so/alpha/CVS/Entries b/src/libexec/ld.so/alpha/CVS/Entries
new file mode 100644 (file)
index 0000000..65170cd
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/alpha/CVS/Repository b/src/libexec/ld.so/alpha/CVS/Repository
new file mode 100644 (file)
index 0000000..a31e897
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/alpha
diff --git a/src/libexec/ld.so/alpha/CVS/Root b/src/libexec/ld.so/alpha/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/alpha/Makefile.inc b/src/libexec/ld.so/alpha/Makefile.inc
new file mode 100644 (file)
index 0000000..375ba49
--- /dev/null
@@ -0,0 +1,5 @@
+#      $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"
diff --git a/src/libexec/ld.so/alpha/archdep.h b/src/libexec/ld.so/alpha/archdep.h
new file mode 100644 (file)
index 0000000..3c77ae4
--- /dev/null
@@ -0,0 +1,76 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/alpha/ldasm.S b/src/libexec/ld.so/alpha/ldasm.S
new file mode 100644 (file)
index 0000000..7de78da
--- /dev/null
@@ -0,0 +1,315 @@
+/*     $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)
+
diff --git a/src/libexec/ld.so/alpha/rtld_machine.c b/src/libexec/ld.so/alpha/rtld_machine.c
new file mode 100644 (file)
index 0000000..ac97fd3
--- /dev/null
@@ -0,0 +1,341 @@
+/*     $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;
+       }
+}
diff --git a/src/libexec/ld.so/alpha/syscall.h b/src/libexec/ld.so/alpha/syscall.h
new file mode 100644 (file)
index 0000000..ab63c4b
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/amd64/CVS/Entries b/src/libexec/ld.so/amd64/CVS/Entries
new file mode 100644 (file)
index 0000000..8eb4bcc
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/amd64/CVS/Repository b/src/libexec/ld.so/amd64/CVS/Repository
new file mode 100644 (file)
index 0000000..d994d52
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/amd64
diff --git a/src/libexec/ld.so/amd64/CVS/Root b/src/libexec/ld.so/amd64/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/amd64/Makefile.inc b/src/libexec/ld.so/amd64/Makefile.inc
new file mode 100644 (file)
index 0000000..7d4c29f
--- /dev/null
@@ -0,0 +1,4 @@
+#      $OpenBSD: Makefile.inc,v 1.3 2005/09/17 01:06:53 deraadt Exp $
+
+CFLAGS += -fPIC
+AFLAGS += -fpic
diff --git a/src/libexec/ld.so/amd64/archdep.h b/src/libexec/ld.so/amd64/archdep.h
new file mode 100644 (file)
index 0000000..364123f
--- /dev/null
@@ -0,0 +1,86 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/amd64/ldasm.S b/src/libexec/ld.so/amd64/ldasm.S
new file mode 100644 (file)
index 0000000..4e9ed24
--- /dev/null
@@ -0,0 +1,153 @@
+/*     $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
+
diff --git a/src/libexec/ld.so/amd64/rtld_machine.c b/src/libexec/ld.so/amd64/rtld_machine.c
new file mode 100644 (file)
index 0000000..6dad018
--- /dev/null
@@ -0,0 +1,443 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/amd64/syscall.h b/src/libexec/ld.so/amd64/syscall.h
new file mode 100644 (file)
index 0000000..406b4bf
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/arm/CVS/Entries b/src/libexec/ld.so/arm/CVS/Entries
new file mode 100644 (file)
index 0000000..e721986
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/arm/CVS/Repository b/src/libexec/ld.so/arm/CVS/Repository
new file mode 100644 (file)
index 0000000..35761d2
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/arm
diff --git a/src/libexec/ld.so/arm/CVS/Root b/src/libexec/ld.so/arm/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/arm/Makefile.inc b/src/libexec/ld.so/arm/Makefile.inc
new file mode 100644 (file)
index 0000000..edc5bc5
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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/
diff --git a/src/libexec/ld.so/arm/archdep.h b/src/libexec/ld.so/arm/archdep.h
new file mode 100644 (file)
index 0000000..d9ffdbf
--- /dev/null
@@ -0,0 +1,86 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/arm/ldasm.S b/src/libexec/ld.so/arm/ldasm.S
new file mode 100644 (file)
index 0000000..6ac1807
--- /dev/null
@@ -0,0 +1,137 @@
+/*     $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
diff --git a/src/libexec/ld.so/arm/rtld_machine.c b/src/libexec/ld.so/arm/rtld_machine.c
new file mode 100644 (file)
index 0000000..0ee1882
--- /dev/null
@@ -0,0 +1,430 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/arm/syscall.h b/src/libexec/ld.so/arm/syscall.h
new file mode 100644 (file)
index 0000000..465e030
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/dir.c b/src/libexec/ld.so/dir.c
new file mode 100644 (file)
index 0000000..17de54d
--- /dev/null
@@ -0,0 +1,135 @@
+/*     $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);
+       }
+}
diff --git a/src/libexec/ld.so/dir.h b/src/libexec/ld.so/dir.h
new file mode 100644 (file)
index 0000000..ab476fd
--- /dev/null
@@ -0,0 +1,34 @@
+/*     $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);
diff --git a/src/libexec/ld.so/dl_prebind.c b/src/libexec/ld.so/dl_prebind.c
new file mode 100644 (file)
index 0000000..8a83e28
--- /dev/null
@@ -0,0 +1,618 @@
+/* $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 */
diff --git a/src/libexec/ld.so/dl_prebind.h b/src/libexec/ld.so/dl_prebind.h
new file mode 100644 (file)
index 0000000..f229491
--- /dev/null
@@ -0,0 +1,36 @@
+/* $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;
diff --git a/src/libexec/ld.so/dl_printf.c b/src/libexec/ld.so/dl_printf.c
new file mode 100644 (file)
index 0000000..18f2c34
--- /dev/null
@@ -0,0 +1,241 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/dlfcn.c b/src/libexec/ld.so/dlfcn.c
new file mode 100644 (file)
index 0000000..f09b8b5
--- /dev/null
@@ -0,0 +1,653 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/hppa/CVS/Entries b/src/libexec/ld.so/hppa/CVS/Entries
new file mode 100644 (file)
index 0000000..b5a0a91
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/hppa/CVS/Repository b/src/libexec/ld.so/hppa/CVS/Repository
new file mode 100644 (file)
index 0000000..c759a78
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/hppa
diff --git a/src/libexec/ld.so/hppa/CVS/Root b/src/libexec/ld.so/hppa/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/hppa/Makefile.inc b/src/libexec/ld.so/hppa/Makefile.inc
new file mode 100644 (file)
index 0000000..c345370
--- /dev/null
@@ -0,0 +1,6 @@
+#      $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`
diff --git a/src/libexec/ld.so/hppa/archdep.h b/src/libexec/ld.so/hppa/archdep.h
new file mode 100644 (file)
index 0000000..250553b
--- /dev/null
@@ -0,0 +1,108 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/hppa/ldasm.S b/src/libexec/ld.so/hppa/ldasm.S
new file mode 100644 (file)
index 0000000..87eb024
--- /dev/null
@@ -0,0 +1,295 @@
+/*     $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
diff --git a/src/libexec/ld.so/hppa/rtld_machine.c b/src/libexec/ld.so/hppa/rtld_machine.c
new file mode 100644 (file)
index 0000000..73af16e
--- /dev/null
@@ -0,0 +1,478 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/hppa/syscall.h b/src/libexec/ld.so/hppa/syscall.h
new file mode 100644 (file)
index 0000000..406b4bf
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/i386/CVS/Entries b/src/libexec/ld.so/i386/CVS/Entries
new file mode 100644 (file)
index 0000000..7e8e784
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/i386/CVS/Repository b/src/libexec/ld.so/i386/CVS/Repository
new file mode 100644 (file)
index 0000000..41b42d4
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/i386
diff --git a/src/libexec/ld.so/i386/CVS/Root b/src/libexec/ld.so/i386/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/i386/Makefile.inc b/src/libexec/ld.so/i386/Makefile.inc
new file mode 100644 (file)
index 0000000..28573b3
--- /dev/null
@@ -0,0 +1,4 @@
+#      $OpenBSD: Makefile.inc,v 1.1 2002/08/23 23:02:48 drahn Exp $
+
+CFLAGS += -fPIC
+AFLAGS += -fpic
diff --git a/src/libexec/ld.so/i386/archdep.h b/src/libexec/ld.so/i386/archdep.h
new file mode 100644 (file)
index 0000000..57badd1
--- /dev/null
@@ -0,0 +1,96 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/i386/ldasm.S b/src/libexec/ld.so/i386/ldasm.S
new file mode 100644 (file)
index 0000000..2052a03
--- /dev/null
@@ -0,0 +1,189 @@
+/*     $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
+
diff --git a/src/libexec/ld.so/i386/rtld_machine.c b/src/libexec/ld.so/i386/rtld_machine.c
new file mode 100644 (file)
index 0000000..620a658
--- /dev/null
@@ -0,0 +1,456 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/i386/syscall.h b/src/libexec/ld.so/i386/syscall.h
new file mode 100644 (file)
index 0000000..a409e17
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/ld.so.1 b/src/libexec/ld.so/ld.so.1
new file mode 100644 (file)
index 0000000..5643f53
--- /dev/null
@@ -0,0 +1,214 @@
+.\"    $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.
diff --git a/src/libexec/ld.so/ldconfig/CVS/Entries b/src/libexec/ld.so/ldconfig/CVS/Entries
new file mode 100644 (file)
index 0000000..b9ed913
--- /dev/null
@@ -0,0 +1,15 @@
+/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
diff --git a/src/libexec/ld.so/ldconfig/CVS/Repository b/src/libexec/ld.so/ldconfig/CVS/Repository
new file mode 100644 (file)
index 0000000..7af49a6
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/ldconfig
diff --git a/src/libexec/ld.so/ldconfig/CVS/Root b/src/libexec/ld.so/ldconfig/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/ldconfig/Makefile b/src/libexec/ld.so/ldconfig/Makefile
new file mode 100644 (file)
index 0000000..c65c6d1
--- /dev/null
@@ -0,0 +1,15 @@
+#      $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>
diff --git a/src/libexec/ld.so/ldconfig/debug.c b/src/libexec/ld.so/ldconfig/debug.c
new file mode 100644 (file)
index 0000000..9f16f95
--- /dev/null
@@ -0,0 +1,160 @@
+/* $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);
+       }
+}
+
diff --git a/src/libexec/ld.so/ldconfig/dl_prebind.c b/src/libexec/ld.so/ldconfig/dl_prebind.c
new file mode 100644 (file)
index 0000000..9e50a68
--- /dev/null
@@ -0,0 +1,619 @@
+/* $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 */
diff --git a/src/libexec/ld.so/ldconfig/etc.c b/src/libexec/ld.so/ldconfig/etc.c
new file mode 100644 (file)
index 0000000..b8f3eb0
--- /dev/null
@@ -0,0 +1,69 @@
+/* $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);
+}
diff --git a/src/libexec/ld.so/ldconfig/ld.h b/src/libexec/ld.so/ldconfig/ld.h
new file mode 100644 (file)
index 0000000..868d86e
--- /dev/null
@@ -0,0 +1,24 @@
+/* $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
diff --git a/src/libexec/ld.so/ldconfig/ldconfig.8 b/src/libexec/ld.so/ldconfig/ldconfig.8
new file mode 100644 (file)
index 0000000..c4d0057
--- /dev/null
@@ -0,0 +1,197 @@
+.\"    $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 .
diff --git a/src/libexec/ld.so/ldconfig/ldconfig.c b/src/libexec/ld.so/ldconfig/ldconfig.c
new file mode 100644 (file)
index 0000000..bcd9ce0
--- /dev/null
@@ -0,0 +1,552 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/ldconfig/library.c b/src/libexec/ld.so/ldconfig/library.c
new file mode 100644 (file)
index 0000000..07b5a70
--- /dev/null
@@ -0,0 +1,340 @@
+/* $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;
+}
+
diff --git a/src/libexec/ld.so/ldconfig/obj b/src/libexec/ld.so/ldconfig/obj
new file mode 120000 (symlink)
index 0000000..46d2d5b
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/ld.so/ldconfig
\ No newline at end of file
diff --git a/src/libexec/ld.so/ldconfig/prebind.c b/src/libexec/ld.so/ldconfig/prebind.c
new file mode 100644 (file)
index 0000000..f9c974f
--- /dev/null
@@ -0,0 +1,2278 @@
+/* $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);*/
+}
diff --git a/src/libexec/ld.so/ldconfig/prebind.h b/src/libexec/ld.so/ldconfig/prebind.h
new file mode 100644 (file)
index 0000000..c9e3c34
--- /dev/null
@@ -0,0 +1,68 @@
+/* $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);
diff --git a/src/libexec/ld.so/ldconfig/prebind_delete.c b/src/libexec/ld.so/ldconfig/prebind_delete.c
new file mode 100644 (file)
index 0000000..c0296f4
--- /dev/null
@@ -0,0 +1,292 @@
+/* $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);
+}
diff --git a/src/libexec/ld.so/ldconfig/prebind_struct.h b/src/libexec/ld.so/ldconfig/prebind_struct.h
new file mode 100644 (file)
index 0000000..0b5dfe7
--- /dev/null
@@ -0,0 +1,86 @@
+/* $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);
diff --git a/src/libexec/ld.so/ldconfig/shlib.c b/src/libexec/ld.so/ldconfig/shlib.c
new file mode 100644 (file)
index 0000000..def2ac0
--- /dev/null
@@ -0,0 +1,197 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/ldconfig/sod.c b/src/libexec/ld.so/ldconfig/sod.c
new file mode 100644 (file)
index 0000000..d3383ee
--- /dev/null
@@ -0,0 +1,265 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/ldd/CVS/Entries b/src/libexec/ld.so/ldd/CVS/Entries
new file mode 100644 (file)
index 0000000..7a7d860
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/ld.so/ldd/CVS/Repository b/src/libexec/ld.so/ldd/CVS/Repository
new file mode 100644 (file)
index 0000000..3c95a2c
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/ldd
diff --git a/src/libexec/ld.so/ldd/CVS/Root b/src/libexec/ld.so/ldd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/ldd/Makefile b/src/libexec/ld.so/ldd/Makefile
new file mode 100644 (file)
index 0000000..bd5740f
--- /dev/null
@@ -0,0 +1,10 @@
+#      $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>
diff --git a/src/libexec/ld.so/ldd/ldd.1 b/src/libexec/ld.so/ldd/ldd.1
new file mode 100644 (file)
index 0000000..de3cd1f
--- /dev/null
@@ -0,0 +1,70 @@
+.\"    $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
diff --git a/src/libexec/ld.so/ldd/ldd.c b/src/libexec/ld.so/ldd/ldd.c
new file mode 100644 (file)
index 0000000..2e36089
--- /dev/null
@@ -0,0 +1,187 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/ldd/obj b/src/libexec/ld.so/ldd/obj
new file mode 120000 (symlink)
index 0000000..9c830c7
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/ld.so/ldd
\ No newline at end of file
diff --git a/src/libexec/ld.so/library.c b/src/libexec/ld.so/library.c
new file mode 100644 (file)
index 0000000..0ddbb48
--- /dev/null
@@ -0,0 +1,263 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/library_mquery.c b/src/libexec/ld.so/library_mquery.c
new file mode 100644 (file)
index 0000000..cd0ea37
--- /dev/null
@@ -0,0 +1,308 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/library_subr.c b/src/libexec/ld.so/library_subr.c
new file mode 100644 (file)
index 0000000..a1eb5fa
--- /dev/null
@@ -0,0 +1,482 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/loader.c b/src/libexec/ld.so/loader.c
new file mode 100644 (file)
index 0000000..1b577ef
--- /dev/null
@@ -0,0 +1,933 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/mips64/CVS/Entries b/src/libexec/ld.so/mips64/CVS/Entries
new file mode 100644 (file)
index 0000000..d8b389a
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/mips64/CVS/Repository b/src/libexec/ld.so/mips64/CVS/Repository
new file mode 100644 (file)
index 0000000..7adfc27
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/mips64
diff --git a/src/libexec/ld.so/mips64/CVS/Root b/src/libexec/ld.so/mips64/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/mips64/Makefile.inc b/src/libexec/ld.so/mips64/Makefile.inc
new file mode 100644 (file)
index 0000000..c708fc1
--- /dev/null
@@ -0,0 +1,5 @@
+#      $OpenBSD: Makefile.inc,v 1.1 2004/08/11 17:11:45 pefo Exp $
+
+# CFLAGS += -fpic -msoft-float
+# ADDR=-Tdata 8000
+# ELF_LDFLAGS+=${ADDR}
diff --git a/src/libexec/ld.so/mips64/archdep.h b/src/libexec/ld.so/mips64/archdep.h
new file mode 100644 (file)
index 0000000..300af59
--- /dev/null
@@ -0,0 +1,102 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/mips64/ldasm.S b/src/libexec/ld.so/mips64/ldasm.S
new file mode 100644 (file)
index 0000000..366fa09
--- /dev/null
@@ -0,0 +1,154 @@
+/*     $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
diff --git a/src/libexec/ld.so/mips64/rtld_machine.c b/src/libexec/ld.so/mips64/rtld_machine.c
new file mode 100644 (file)
index 0000000..811f200
--- /dev/null
@@ -0,0 +1,318 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/mips64/syscall.h b/src/libexec/ld.so/mips64/syscall.h
new file mode 100644 (file)
index 0000000..b45fd1e
--- /dev/null
@@ -0,0 +1,360 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/obj b/src/libexec/ld.so/obj
new file mode 120000 (symlink)
index 0000000..c3dcc95
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/ld.so
\ No newline at end of file
diff --git a/src/libexec/ld.so/powerpc/CVS/Entries b/src/libexec/ld.so/powerpc/CVS/Entries
new file mode 100644 (file)
index 0000000..8ac4e6c
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/powerpc/CVS/Repository b/src/libexec/ld.so/powerpc/CVS/Repository
new file mode 100644 (file)
index 0000000..fd30274
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/powerpc
diff --git a/src/libexec/ld.so/powerpc/CVS/Root b/src/libexec/ld.so/powerpc/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/powerpc/Makefile.inc b/src/libexec/ld.so/powerpc/Makefile.inc
new file mode 100644 (file)
index 0000000..f59eaab
--- /dev/null
@@ -0,0 +1,3 @@
+#      $OpenBSD: Makefile.inc,v 1.2 2002/12/09 20:56:34 drahn Exp $
+
+CFLAGS += -fpic -msoft-float
diff --git a/src/libexec/ld.so/powerpc/archdep.h b/src/libexec/ld.so/powerpc/archdep.h
new file mode 100644 (file)
index 0000000..49fdbd0
--- /dev/null
@@ -0,0 +1,106 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/powerpc/ldasm.S b/src/libexec/ld.so/powerpc/ldasm.S
new file mode 100644 (file)
index 0000000..77df737
--- /dev/null
@@ -0,0 +1,161 @@
+/*     $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
diff --git a/src/libexec/ld.so/powerpc/rtld_machine.c b/src/libexec/ld.so/powerpc/rtld_machine.c
new file mode 100644 (file)
index 0000000..6fd57bc
--- /dev/null
@@ -0,0 +1,657 @@
+/*     $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");
diff --git a/src/libexec/ld.so/powerpc/syscall.h b/src/libexec/ld.so/powerpc/syscall.h
new file mode 100644 (file)
index 0000000..b1212f6
--- /dev/null
@@ -0,0 +1,378 @@
+/*     $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__*/
+
diff --git a/src/libexec/ld.so/prebind.h b/src/libexec/ld.so/prebind.h
new file mode 100644 (file)
index 0000000..369308a
--- /dev/null
@@ -0,0 +1,64 @@
+/* $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;
+};
diff --git a/src/libexec/ld.so/resolve.c b/src/libexec/ld.so/resolve.c
new file mode 100644 (file)
index 0000000..765a5b6
--- /dev/null
@@ -0,0 +1,484 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/resolve.h b/src/libexec/ld.so/resolve.h
new file mode 100644 (file)
index 0000000..feb11d5
--- /dev/null
@@ -0,0 +1,278 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/sh/CVS/Entries b/src/libexec/ld.so/sh/CVS/Entries
new file mode 100644 (file)
index 0000000..f99b515
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/sh/CVS/Repository b/src/libexec/ld.so/sh/CVS/Repository
new file mode 100644 (file)
index 0000000..24df387
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/sh
diff --git a/src/libexec/ld.so/sh/CVS/Root b/src/libexec/ld.so/sh/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/sh/Makefile.inc b/src/libexec/ld.so/sh/Makefile.inc
new file mode 100644 (file)
index 0000000..4dd3fec
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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`
diff --git a/src/libexec/ld.so/sh/archdep.h b/src/libexec/ld.so/sh/archdep.h
new file mode 100644 (file)
index 0000000..7c1bbc5
--- /dev/null
@@ -0,0 +1,94 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/sh/ldasm.S b/src/libexec/ld.so/sh/ldasm.S
new file mode 100644 (file)
index 0000000..61833cc
--- /dev/null
@@ -0,0 +1,230 @@
+/*     $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
+
+
diff --git a/src/libexec/ld.so/sh/rtld_machine.c b/src/libexec/ld.so/sh/rtld_machine.c
new file mode 100644 (file)
index 0000000..28b16c0
--- /dev/null
@@ -0,0 +1,877 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/sh/syscall.h b/src/libexec/ld.so/sh/syscall.h
new file mode 100644 (file)
index 0000000..85c1857
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/sod.c b/src/libexec/ld.so/sod.c
new file mode 100644 (file)
index 0000000..7b421b2
--- /dev/null
@@ -0,0 +1,262 @@
+/*     $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;
+}
diff --git a/src/libexec/ld.so/sod.h b/src/libexec/ld.so/sod.h
new file mode 100644 (file)
index 0000000..c4bf491
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $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;
diff --git a/src/libexec/ld.so/sparc/CVS/Entries b/src/libexec/ld.so/sparc/CVS/Entries
new file mode 100644 (file)
index 0000000..b5840ef
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/sparc/CVS/Repository b/src/libexec/ld.so/sparc/CVS/Repository
new file mode 100644 (file)
index 0000000..57a2984
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/sparc
diff --git a/src/libexec/ld.so/sparc/CVS/Root b/src/libexec/ld.so/sparc/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/sparc/Makefile.inc b/src/libexec/ld.so/sparc/Makefile.inc
new file mode 100644 (file)
index 0000000..04e7d29
--- /dev/null
@@ -0,0 +1,8 @@
+#      $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
diff --git a/src/libexec/ld.so/sparc/archdep.h b/src/libexec/ld.so/sparc/archdep.h
new file mode 100644 (file)
index 0000000..509b17c
--- /dev/null
@@ -0,0 +1,86 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/sparc/ldasm.S b/src/libexec/ld.so/sparc/ldasm.S
new file mode 100644 (file)
index 0000000..1b21037
--- /dev/null
@@ -0,0 +1,397 @@
+/*     $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):
diff --git a/src/libexec/ld.so/sparc/rtld_machine.c b/src/libexec/ld.so/sparc/rtld_machine.c
new file mode 100644 (file)
index 0000000..904965b
--- /dev/null
@@ -0,0 +1,535 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/sparc/syscall.h b/src/libexec/ld.so/sparc/syscall.h
new file mode 100644 (file)
index 0000000..093221e
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/sparc64/CVS/Entries b/src/libexec/ld.so/sparc64/CVS/Entries
new file mode 100644 (file)
index 0000000..947271d
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/ld.so/sparc64/CVS/Repository b/src/libexec/ld.so/sparc64/CVS/Repository
new file mode 100644 (file)
index 0000000..82110d0
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/ld.so/sparc64
diff --git a/src/libexec/ld.so/sparc64/CVS/Root b/src/libexec/ld.so/sparc64/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/ld.so/sparc64/Makefile.inc b/src/libexec/ld.so/sparc64/Makefile.inc
new file mode 100644 (file)
index 0000000..d2418bc
--- /dev/null
@@ -0,0 +1,4 @@
+#      $OpenBSD: Makefile.inc,v 1.1 2002/08/21 15:40:30 art Exp $
+
+CFLAGS += -fpic -msoft-float
+AFLAGS += -fpic
diff --git a/src/libexec/ld.so/sparc64/archdep.h b/src/libexec/ld.so/sparc64/archdep.h
new file mode 100644 (file)
index 0000000..61e32e7
--- /dev/null
@@ -0,0 +1,80 @@
+/*     $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_ */
diff --git a/src/libexec/ld.so/sparc64/ldasm.S b/src/libexec/ld.so/sparc64/ldasm.S
new file mode 100644 (file)
index 0000000..64d8018
--- /dev/null
@@ -0,0 +1,329 @@
+/*     $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
+
diff --git a/src/libexec/ld.so/sparc64/rtld_machine.c b/src/libexec/ld.so/sparc64/rtld_machine.c
new file mode 100644 (file)
index 0000000..feb9983
--- /dev/null
@@ -0,0 +1,770 @@
+/*     $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);
+}
diff --git a/src/libexec/ld.so/sparc64/syscall.h b/src/libexec/ld.so/sparc64/syscall.h
new file mode 100644 (file)
index 0000000..d018613
--- /dev/null
@@ -0,0 +1,64 @@
+/*     $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__*/
diff --git a/src/libexec/ld.so/strtol.c b/src/libexec/ld.so/strtol.c
new file mode 100644 (file)
index 0000000..fbaf805
--- /dev/null
@@ -0,0 +1,138 @@
+/* $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);
+}
diff --git a/src/libexec/ld.so/util.c b/src/libexec/ld.so/util.c
new file mode 100644 (file)
index 0000000..0098281
--- /dev/null
@@ -0,0 +1,144 @@
+/*     $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);
+}
+
+
diff --git a/src/libexec/ld.so/util.h b/src/libexec/ld.so/util.h
new file mode 100644 (file)
index 0000000..e5dee83
--- /dev/null
@@ -0,0 +1,191 @@
+/*     $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__*/
diff --git a/src/libexec/lockspool/CVS/Entries b/src/libexec/lockspool/CVS/Entries
new file mode 100644 (file)
index 0000000..5df3fee
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/lockspool/CVS/Repository b/src/libexec/lockspool/CVS/Repository
new file mode 100644 (file)
index 0000000..cdd24e6
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/lockspool
diff --git a/src/libexec/lockspool/CVS/Root b/src/libexec/lockspool/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/lockspool/Makefile b/src/libexec/lockspool/Makefile
new file mode 100644 (file)
index 0000000..6392884
--- /dev/null
@@ -0,0 +1,10 @@
+#      $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>
diff --git a/src/libexec/lockspool/lockspool.1 b/src/libexec/lockspool/lockspool.1
new file mode 100644 (file)
index 0000000..7c6b18c
--- /dev/null
@@ -0,0 +1,77 @@
+.\"    $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 .
diff --git a/src/libexec/lockspool/lockspool.c b/src/libexec/lockspool/lockspool.c
new file mode 100644 (file)
index 0000000..67cc7eb
--- /dev/null
@@ -0,0 +1,110 @@
+/*     $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);
+}
diff --git a/src/libexec/lockspool/obj b/src/libexec/lockspool/obj
new file mode 120000 (symlink)
index 0000000..3304791
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/lockspool
\ No newline at end of file
diff --git a/src/libexec/login_chpass/CVS/Entries b/src/libexec/login_chpass/CVS/Entries
new file mode 100644 (file)
index 0000000..0833d60
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/login_chpass/CVS/Repository b/src/libexec/login_chpass/CVS/Repository
new file mode 100644 (file)
index 0000000..9afa001
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_chpass
diff --git a/src/libexec/login_chpass/CVS/Root b/src/libexec/login_chpass/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_chpass/Makefile b/src/libexec/login_chpass/Makefile
new file mode 100644 (file)
index 0000000..a323bbf
--- /dev/null
@@ -0,0 +1,33 @@
+#      $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>
diff --git a/src/libexec/login_chpass/login_chpass.8 b/src/libexec/login_chpass/login_chpass.8
new file mode 100644 (file)
index 0000000..12de9da
--- /dev/null
@@ -0,0 +1,69 @@
+.\" $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
diff --git a/src/libexec/login_chpass/login_chpass.c b/src/libexec/login_chpass/login_chpass.c
new file mode 100644 (file)
index 0000000..2d665d5
--- /dev/null
@@ -0,0 +1,273 @@
+/*     $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
diff --git a/src/libexec/login_chpass/obj b/src/libexec/login_chpass/obj
new file mode 120000 (symlink)
index 0000000..3b21bed
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_chpass
\ No newline at end of file
diff --git a/src/libexec/login_krb5-or-pwd/CVS/Entries b/src/libexec/login_krb5-or-pwd/CVS/Entries
new file mode 100644 (file)
index 0000000..dbe52e5
--- /dev/null
@@ -0,0 +1,3 @@
+/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
diff --git a/src/libexec/login_krb5-or-pwd/CVS/Repository b/src/libexec/login_krb5-or-pwd/CVS/Repository
new file mode 100644 (file)
index 0000000..2fda07e
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_krb5-or-pwd
diff --git a/src/libexec/login_krb5-or-pwd/CVS/Root b/src/libexec/login_krb5-or-pwd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_krb5-or-pwd/Makefile b/src/libexec/login_krb5-or-pwd/Makefile
new file mode 100644 (file)
index 0000000..e8a0b41
--- /dev/null
@@ -0,0 +1,27 @@
+#      $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>
diff --git a/src/libexec/login_krb5-or-pwd/login_krb5-or-pwd.8 b/src/libexec/login_krb5-or-pwd/login_krb5-or-pwd.8
new file mode 100644 (file)
index 0000000..5a7af7a
--- /dev/null
@@ -0,0 +1,82 @@
+.\" $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
diff --git a/src/libexec/login_krb5-or-pwd/obj b/src/libexec/login_krb5-or-pwd/obj
new file mode 120000 (symlink)
index 0000000..cc31acf
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_krb5-or-pwd
\ No newline at end of file
diff --git a/src/libexec/login_krb5/CVS/Entries b/src/libexec/login_krb5/CVS/Entries
new file mode 100644 (file)
index 0000000..228f004
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/login_krb5/CVS/Repository b/src/libexec/login_krb5/CVS/Repository
new file mode 100644 (file)
index 0000000..87a7240
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_krb5
diff --git a/src/libexec/login_krb5/CVS/Root b/src/libexec/login_krb5/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_krb5/Makefile b/src/libexec/login_krb5/Makefile
new file mode 100644 (file)
index 0000000..bdf5ebb
--- /dev/null
@@ -0,0 +1,26 @@
+#      $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>
diff --git a/src/libexec/login_krb5/login_krb5.8 b/src/libexec/login_krb5/login_krb5.8
new file mode 100644 (file)
index 0000000..e1e1815
--- /dev/null
@@ -0,0 +1,95 @@
+.\" $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
diff --git a/src/libexec/login_krb5/login_krb5.c b/src/libexec/login_krb5/login_krb5.c
new file mode 100644 (file)
index 0000000..3fb44d2
--- /dev/null
@@ -0,0 +1,222 @@
+/*     $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);
+}
diff --git a/src/libexec/login_krb5/obj b/src/libexec/login_krb5/obj
new file mode 120000 (symlink)
index 0000000..cd143f5
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_krb5
\ No newline at end of file
diff --git a/src/libexec/login_lchpass/CVS/Entries b/src/libexec/login_lchpass/CVS/Entries
new file mode 100644 (file)
index 0000000..1c3b4e6
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/login_lchpass/CVS/Repository b/src/libexec/login_lchpass/CVS/Repository
new file mode 100644 (file)
index 0000000..27f0e07
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_lchpass
diff --git a/src/libexec/login_lchpass/CVS/Root b/src/libexec/login_lchpass/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_lchpass/Makefile b/src/libexec/login_lchpass/Makefile
new file mode 100644 (file)
index 0000000..9151f0c
--- /dev/null
@@ -0,0 +1,19 @@
+#      $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>
diff --git a/src/libexec/login_lchpass/login_lchpass.8 b/src/libexec/login_lchpass/login_lchpass.8
new file mode 100644 (file)
index 0000000..c2a9f8a
--- /dev/null
@@ -0,0 +1,65 @@
+.\" $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
diff --git a/src/libexec/login_lchpass/login_lchpass.c b/src/libexec/login_lchpass/login_lchpass.c
new file mode 100644 (file)
index 0000000..7d0a429
--- /dev/null
@@ -0,0 +1,151 @@
+/*     $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);
+}
diff --git a/src/libexec/login_lchpass/obj b/src/libexec/login_lchpass/obj
new file mode 120000 (symlink)
index 0000000..beb09e1
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_lchpass
\ No newline at end of file
diff --git a/src/libexec/login_passwd/CVS/Entries b/src/libexec/login_passwd/CVS/Entries
new file mode 100644 (file)
index 0000000..45303ef
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/login_passwd/CVS/Repository b/src/libexec/login_passwd/CVS/Repository
new file mode 100644 (file)
index 0000000..80185de
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_passwd
diff --git a/src/libexec/login_passwd/CVS/Root b/src/libexec/login_passwd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_passwd/Makefile b/src/libexec/login_passwd/Makefile
new file mode 100644 (file)
index 0000000..44e4b45
--- /dev/null
@@ -0,0 +1,16 @@
+#      $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>
diff --git a/src/libexec/login_passwd/common.h b/src/libexec/login_passwd/common.h
new file mode 100644 (file)
index 0000000..a430c4d
--- /dev/null
@@ -0,0 +1,65 @@
+/* $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_ */
diff --git a/src/libexec/login_passwd/login.c b/src/libexec/login_passwd/login.c
new file mode 100644 (file)
index 0000000..186c00c
--- /dev/null
@@ -0,0 +1,168 @@
+/*     $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);
+}
diff --git a/src/libexec/login_passwd/login_passwd.8 b/src/libexec/login_passwd/login_passwd.8
new file mode 100644 (file)
index 0000000..7632afb
--- /dev/null
@@ -0,0 +1,88 @@
+.\" $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
diff --git a/src/libexec/login_passwd/login_passwd.c b/src/libexec/login_passwd/login_passwd.c
new file mode 100644 (file)
index 0000000..5a54013
--- /dev/null
@@ -0,0 +1,83 @@
+/*     $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);
+}
diff --git a/src/libexec/login_passwd/obj b/src/libexec/login_passwd/obj
new file mode 120000 (symlink)
index 0000000..a559846
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_passwd
\ No newline at end of file
diff --git a/src/libexec/login_radius/CVS/Entries b/src/libexec/login_radius/CVS/Entries
new file mode 100644 (file)
index 0000000..6fb6cd5
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/login_radius/CVS/Repository b/src/libexec/login_radius/CVS/Repository
new file mode 100644 (file)
index 0000000..43c95d0
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_radius
diff --git a/src/libexec/login_radius/CVS/Root b/src/libexec/login_radius/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_radius/Makefile b/src/libexec/login_radius/Makefile
new file mode 100644 (file)
index 0000000..fb9f330
--- /dev/null
@@ -0,0 +1,15 @@
+#      $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>
diff --git a/src/libexec/login_radius/login_radius.8 b/src/libexec/login_radius/login_radius.8
new file mode 100644 (file)
index 0000000..8811131
--- /dev/null
@@ -0,0 +1,171 @@
+.\" $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 .
diff --git a/src/libexec/login_radius/login_radius.c b/src/libexec/login_radius/login_radius.c
new file mode 100644 (file)
index 0000000..9fffef8
--- /dev/null
@@ -0,0 +1,210 @@
+/*     $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);
+}
diff --git a/src/libexec/login_radius/login_radius.h b/src/libexec/login_radius/login_radius.h
new file mode 100644 (file)
index 0000000..8c117c3
--- /dev/null
@@ -0,0 +1,21 @@
+/*     $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);
diff --git a/src/libexec/login_radius/obj b/src/libexec/login_radius/obj
new file mode 120000 (symlink)
index 0000000..4f13e87
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_radius
\ No newline at end of file
diff --git a/src/libexec/login_radius/raddauth.c b/src/libexec/login_radius/raddauth.c
new file mode 100644 (file)
index 0000000..6597523
--- /dev/null
@@ -0,0 +1,617 @@
+/*     $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;
+       }
+}
diff --git a/src/libexec/login_reject/CVS/Entries b/src/libexec/login_reject/CVS/Entries
new file mode 100644 (file)
index 0000000..3856c58
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/login_reject/CVS/Repository b/src/libexec/login_reject/CVS/Repository
new file mode 100644 (file)
index 0000000..f0851c7
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_reject
diff --git a/src/libexec/login_reject/CVS/Root b/src/libexec/login_reject/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_reject/Makefile b/src/libexec/login_reject/Makefile
new file mode 100644 (file)
index 0000000..211059d
--- /dev/null
@@ -0,0 +1,14 @@
+#      $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>
diff --git a/src/libexec/login_reject/login_reject.8 b/src/libexec/login_reject/login_reject.8
new file mode 100644 (file)
index 0000000..c44d4c3
--- /dev/null
@@ -0,0 +1,74 @@
+.\" $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
diff --git a/src/libexec/login_reject/login_reject.c b/src/libexec/login_reject/login_reject.c
new file mode 100644 (file)
index 0000000..5f264b0
--- /dev/null
@@ -0,0 +1,134 @@
+/*     $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);
+}
diff --git a/src/libexec/login_reject/obj b/src/libexec/login_reject/obj
new file mode 120000 (symlink)
index 0000000..d08b4cf
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_reject
\ No newline at end of file
diff --git a/src/libexec/login_skey/CVS/Entries b/src/libexec/login_skey/CVS/Entries
new file mode 100644 (file)
index 0000000..4501c69
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/login_skey/CVS/Repository b/src/libexec/login_skey/CVS/Repository
new file mode 100644 (file)
index 0000000..30a899a
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_skey
diff --git a/src/libexec/login_skey/CVS/Root b/src/libexec/login_skey/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_skey/Makefile b/src/libexec/login_skey/Makefile
new file mode 100644 (file)
index 0000000..ee95e92
--- /dev/null
@@ -0,0 +1,14 @@
+#      $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>
diff --git a/src/libexec/login_skey/login_skey.8 b/src/libexec/login_skey/login_skey.8
new file mode 100644 (file)
index 0000000..742d1a7
--- /dev/null
@@ -0,0 +1,107 @@
+.\" $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
diff --git a/src/libexec/login_skey/login_skey.c b/src/libexec/login_skey/login_skey.c
new file mode 100644 (file)
index 0000000..b67e78a
--- /dev/null
@@ -0,0 +1,284 @@
+/*     $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");
+}
diff --git a/src/libexec/login_skey/obj b/src/libexec/login_skey/obj
new file mode 120000 (symlink)
index 0000000..b8b2418
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_skey
\ No newline at end of file
diff --git a/src/libexec/login_tis/CVS/Entries b/src/libexec/login_tis/CVS/Entries
new file mode 100644 (file)
index 0000000..08c664c
--- /dev/null
@@ -0,0 +1,5 @@
+/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
diff --git a/src/libexec/login_tis/CVS/Repository b/src/libexec/login_tis/CVS/Repository
new file mode 100644 (file)
index 0000000..e382522
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_tis
diff --git a/src/libexec/login_tis/CVS/Root b/src/libexec/login_tis/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_tis/Makefile b/src/libexec/login_tis/Makefile
new file mode 100644 (file)
index 0000000..5cd5ba1
--- /dev/null
@@ -0,0 +1,14 @@
+#      $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>
diff --git a/src/libexec/login_tis/login_tis.8 b/src/libexec/login_tis/login_tis.8
new file mode 100644 (file)
index 0000000..f4817ce
--- /dev/null
@@ -0,0 +1,153 @@
+.\" $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
diff --git a/src/libexec/login_tis/login_tis.c b/src/libexec/login_tis/login_tis.c
new file mode 100644 (file)
index 0000000..ce8bc9c
--- /dev/null
@@ -0,0 +1,694 @@
+/*     $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);
+}
diff --git a/src/libexec/login_tis/login_tis.h b/src/libexec/login_tis/login_tis.h
new file mode 100644 (file)
index 0000000..e5c7542
--- /dev/null
@@ -0,0 +1,37 @@
+/*     $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;
+};
diff --git a/src/libexec/login_tis/obj b/src/libexec/login_tis/obj
new file mode 120000 (symlink)
index 0000000..a6a3263
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_tis
\ No newline at end of file
diff --git a/src/libexec/login_token/CVS/Entries b/src/libexec/login_token/CVS/Entries
new file mode 100644 (file)
index 0000000..c4c8f61
--- /dev/null
@@ -0,0 +1,9 @@
+/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
diff --git a/src/libexec/login_token/CVS/Repository b/src/libexec/login_token/CVS/Repository
new file mode 100644 (file)
index 0000000..5eb185c
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/login_token
diff --git a/src/libexec/login_token/CVS/Root b/src/libexec/login_token/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/login_token/Makefile b/src/libexec/login_token/Makefile
new file mode 100644 (file)
index 0000000..81644bf
--- /dev/null
@@ -0,0 +1,27 @@
+#      $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>
diff --git a/src/libexec/login_token/init.c b/src/libexec/login_token/init.c
new file mode 100644 (file)
index 0000000..631a582
--- /dev/null
@@ -0,0 +1,126 @@
+/*     $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);
+}
diff --git a/src/libexec/login_token/login_token.8 b/src/libexec/login_token/login_token.8
new file mode 100644 (file)
index 0000000..cb3ad8a
--- /dev/null
@@ -0,0 +1,103 @@
+.\"    $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
diff --git a/src/libexec/login_token/login_token.c b/src/libexec/login_token/login_token.c
new file mode 100644 (file)
index 0000000..668a3b0
--- /dev/null
@@ -0,0 +1,187 @@
+/*     $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);
+}
diff --git a/src/libexec/login_token/obj b/src/libexec/login_token/obj
new file mode 120000 (symlink)
index 0000000..bd5735f
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/login_token
\ No newline at end of file
diff --git a/src/libexec/login_token/token.c b/src/libexec/login_token/token.c
new file mode 100644 (file)
index 0000000..36a6125
--- /dev/null
@@ -0,0 +1,391 @@
+/*     $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++;
+       }
+}
diff --git a/src/libexec/login_token/token.h b/src/libexec/login_token/token.h
new file mode 100644 (file)
index 0000000..ae17e00
--- /dev/null
@@ -0,0 +1,84 @@
+/*     $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);
diff --git a/src/libexec/login_token/tokendb.c b/src/libexec/login_token/tokendb.c
new file mode 100644 (file)
index 0000000..def4135
--- /dev/null
@@ -0,0 +1,363 @@
+/*     $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);
+}
+
diff --git a/src/libexec/login_token/tokendb.h b/src/libexec/login_token/tokendb.h
new file mode 100644 (file)
index 0000000..e8d43af
--- /dev/null
@@ -0,0 +1,82 @@
+/*     $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);
diff --git a/src/libexec/mail.local/CVS/Entries b/src/libexec/mail.local/CVS/Entries
new file mode 100644 (file)
index 0000000..a05a955
--- /dev/null
@@ -0,0 +1,7 @@
+/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
diff --git a/src/libexec/mail.local/CVS/Repository b/src/libexec/mail.local/CVS/Repository
new file mode 100644 (file)
index 0000000..ca372a1
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/mail.local
diff --git a/src/libexec/mail.local/CVS/Root b/src/libexec/mail.local/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/mail.local/Makefile b/src/libexec/mail.local/Makefile
new file mode 100644 (file)
index 0000000..8a21c1a
--- /dev/null
@@ -0,0 +1,8 @@
+#      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>
diff --git a/src/libexec/mail.local/locking.c b/src/libexec/mail.local/locking.c
new file mode 100644 (file)
index 0000000..441edd5
--- /dev/null
@@ -0,0 +1,168 @@
+/*     $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);
+}
diff --git a/src/libexec/mail.local/mail.local.8 b/src/libexec/mail.local/mail.local.8
new file mode 100644 (file)
index 0000000..da0e420
--- /dev/null
@@ -0,0 +1,183 @@
+.\"    $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 ) .
diff --git a/src/libexec/mail.local/mail.local.c b/src/libexec/mail.local/mail.local.c
new file mode 100644 (file)
index 0000000..5c77632
--- /dev/null
@@ -0,0 +1,324 @@
+/*     $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 ...");
+}
diff --git a/src/libexec/mail.local/mail.local.h b/src/libexec/mail.local/mail.local.h
new file mode 100644 (file)
index 0000000..0377aa2
--- /dev/null
@@ -0,0 +1,42 @@
+/*     $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);
diff --git a/src/libexec/mail.local/obj b/src/libexec/mail.local/obj
new file mode 120000 (symlink)
index 0000000..56640f0
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/mail.local
\ No newline at end of file
diff --git a/src/libexec/mail.local/pathnames.h b/src/libexec/mail.local/pathnames.h
new file mode 100644 (file)
index 0000000..b405a04
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $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"
diff --git a/src/libexec/makewhatis/CVS/Entries b/src/libexec/makewhatis/CVS/Entries
new file mode 100644 (file)
index 0000000..5c1650c
--- /dev/null
@@ -0,0 +1,4 @@
+/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////
diff --git a/src/libexec/makewhatis/CVS/Repository b/src/libexec/makewhatis/CVS/Repository
new file mode 100644 (file)
index 0000000..7a29d27
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/makewhatis
diff --git a/src/libexec/makewhatis/CVS/Root b/src/libexec/makewhatis/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/makewhatis/Makefile b/src/libexec/makewhatis/Makefile
new file mode 100644 (file)
index 0000000..de1f063
--- /dev/null
@@ -0,0 +1,30 @@
+#      $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>
diff --git a/src/libexec/makewhatis/OpenBSD/CVS/Entries b/src/libexec/makewhatis/OpenBSD/CVS/Entries
new file mode 100644 (file)
index 0000000..c23d537
--- /dev/null
@@ -0,0 +1,2 @@
+/Makewhatis.pm/1.6/Wed Aug 22 15:50:05 2007//
+D/Makewhatis////
diff --git a/src/libexec/makewhatis/OpenBSD/CVS/Repository b/src/libexec/makewhatis/OpenBSD/CVS/Repository
new file mode 100644 (file)
index 0000000..5885734
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/makewhatis/OpenBSD
diff --git a/src/libexec/makewhatis/OpenBSD/CVS/Root b/src/libexec/makewhatis/OpenBSD/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis.pm
new file mode 100644 (file)
index 0000000..2f98021
--- /dev/null
@@ -0,0 +1,204 @@
+# 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;
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Entries b/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Entries
new file mode 100644 (file)
index 0000000..f006355
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Repository b/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Repository
new file mode 100644 (file)
index 0000000..d76f492
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/makewhatis/OpenBSD/Makewhatis
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Root b/src/libexec/makewhatis/OpenBSD/Makewhatis/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/Check.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis/Check.pm
new file mode 100644 (file)
index 0000000..96e7a57
--- /dev/null
@@ -0,0 +1,84 @@
+# 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;
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/Find.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis/Find.pm
new file mode 100644 (file)
index 0000000..8d38b86
--- /dev/null
@@ -0,0 +1,45 @@
+# 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;
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/Formated.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis/Formated.pm
new file mode 100644 (file)
index 0000000..1b6c850
--- /dev/null
@@ -0,0 +1,139 @@
+# 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;
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/Unformated.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis/Unformated.pm
new file mode 100644 (file)
index 0000000..bfd2fc4
--- /dev/null
@@ -0,0 +1,223 @@
+# 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;
diff --git a/src/libexec/makewhatis/OpenBSD/Makewhatis/Whatis.pm b/src/libexec/makewhatis/OpenBSD/Makewhatis/Whatis.pm
new file mode 100644 (file)
index 0000000..308e7c7
--- /dev/null
@@ -0,0 +1,66 @@
+# 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;
diff --git a/src/libexec/makewhatis/makewhatis b/src/libexec/makewhatis/makewhatis
new file mode 100644 (file)
index 0000000..dfea4c8
--- /dev/null
@@ -0,0 +1,37 @@
+#!/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";
+#}
diff --git a/src/libexec/makewhatis/makewhatis.8 b/src/libexec/makewhatis/makewhatis.8
new file mode 100644 (file)
index 0000000..774780e
--- /dev/null
@@ -0,0 +1,136 @@
+.\"    $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.
diff --git a/src/libexec/makewhatis/obj b/src/libexec/makewhatis/obj
new file mode 120000 (symlink)
index 0000000..f87e233
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/makewhatis
\ No newline at end of file
diff --git a/src/libexec/rpc.rquotad/CVS/Entries b/src/libexec/rpc.rquotad/CVS/Entries
new file mode 100644 (file)
index 0000000..7d99548
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/rpc.rquotad/CVS/Repository b/src/libexec/rpc.rquotad/CVS/Repository
new file mode 100644 (file)
index 0000000..a7d91df
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.rquotad
diff --git a/src/libexec/rpc.rquotad/CVS/Root b/src/libexec/rpc.rquotad/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.rquotad/Makefile b/src/libexec/rpc.rquotad/Makefile
new file mode 100644 (file)
index 0000000..beb4703
--- /dev/null
@@ -0,0 +1,11 @@
+#      $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>
diff --git a/src/libexec/rpc.rquotad/obj b/src/libexec/rpc.rquotad/obj
new file mode 120000 (symlink)
index 0000000..6cc9bc8
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.rquotad
\ No newline at end of file
diff --git a/src/libexec/rpc.rquotad/rpc.rquotad.8 b/src/libexec/rpc.rquotad/rpc.rquotad.8
new file mode 100644 (file)
index 0000000..9081d34
--- /dev/null
@@ -0,0 +1,58 @@
+.\"   $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.
diff --git a/src/libexec/rpc.rquotad/rquotad.c b/src/libexec/rpc.rquotad/rquotad.c
new file mode 100644 (file)
index 0000000..ddc39ce
--- /dev/null
@@ -0,0 +1,314 @@
+/*     $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);
+}
diff --git a/src/libexec/rpc.rstatd/CVS/Entries b/src/libexec/rpc.rstatd/CVS/Entries
new file mode 100644 (file)
index 0000000..b7a1283
--- /dev/null
@@ -0,0 +1,5 @@
+/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
diff --git a/src/libexec/rpc.rstatd/CVS/Repository b/src/libexec/rpc.rstatd/CVS/Repository
new file mode 100644 (file)
index 0000000..5aee56d
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.rstatd
diff --git a/src/libexec/rpc.rstatd/CVS/Root b/src/libexec/rpc.rstatd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.rstatd/Makefile b/src/libexec/rpc.rstatd/Makefile
new file mode 100644 (file)
index 0000000..d2bc91f
--- /dev/null
@@ -0,0 +1,14 @@
+#      $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>
+
diff --git a/src/libexec/rpc.rstatd/obj b/src/libexec/rpc.rstatd/obj
new file mode 120000 (symlink)
index 0000000..a041224
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.rstatd
\ No newline at end of file
diff --git a/src/libexec/rpc.rstatd/rpc.rstatd.8 b/src/libexec/rpc.rstatd/rpc.rstatd.8
new file mode 100644 (file)
index 0000000..d8fc980
--- /dev/null
@@ -0,0 +1,64 @@
+.\"    $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
diff --git a/src/libexec/rpc.rstatd/rstat_proc.c b/src/libexec/rpc.rstatd/rstat_proc.c
new file mode 100644 (file)
index 0000000..c8eb0bd
--- /dev/null
@@ -0,0 +1,353 @@
+/*     $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);
+       }
+}
diff --git a/src/libexec/rpc.rstatd/rstatd.c b/src/libexec/rpc.rstatd/rstatd.c
new file mode 100644 (file)
index 0000000..3df7483
--- /dev/null
@@ -0,0 +1,189 @@
+/*     $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);
+               }
+       }
+}
diff --git a/src/libexec/rpc.rusersd/CVS/Entries b/src/libexec/rpc.rusersd/CVS/Entries
new file mode 100644 (file)
index 0000000..f29529d
--- /dev/null
@@ -0,0 +1,5 @@
+/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
diff --git a/src/libexec/rpc.rusersd/CVS/Repository b/src/libexec/rpc.rusersd/CVS/Repository
new file mode 100644 (file)
index 0000000..9a0bc7d
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.rusersd
diff --git a/src/libexec/rpc.rusersd/CVS/Root b/src/libexec/rpc.rusersd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.rusersd/Makefile b/src/libexec/rpc.rusersd/Makefile
new file mode 100644 (file)
index 0000000..a26364c
--- /dev/null
@@ -0,0 +1,11 @@
+#      $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>
+
diff --git a/src/libexec/rpc.rusersd/obj b/src/libexec/rpc.rusersd/obj
new file mode 120000 (symlink)
index 0000000..0bcb565
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.rusersd
\ No newline at end of file
diff --git a/src/libexec/rpc.rusersd/rpc.rusersd.8 b/src/libexec/rpc.rusersd/rpc.rusersd.8
new file mode 100644 (file)
index 0000000..c6b452a
--- /dev/null
@@ -0,0 +1,70 @@
+.\"    $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
diff --git a/src/libexec/rpc.rusersd/rusers_proc.c b/src/libexec/rpc.rusersd/rusers_proc.c
new file mode 100644 (file)
index 0000000..2cecb9f
--- /dev/null
@@ -0,0 +1,437 @@
+/*     $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);
+}
diff --git a/src/libexec/rpc.rusersd/rusersd.c b/src/libexec/rpc.rusersd/rusersd.c
new file mode 100644 (file)
index 0000000..36cbbbe
--- /dev/null
@@ -0,0 +1,143 @@
+/*     $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);
+}
diff --git a/src/libexec/rpc.rwalld/CVS/Entries b/src/libexec/rpc.rwalld/CVS/Entries
new file mode 100644 (file)
index 0000000..b1a26b2
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/rpc.rwalld/CVS/Repository b/src/libexec/rpc.rwalld/CVS/Repository
new file mode 100644 (file)
index 0000000..5334da4
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.rwalld
diff --git a/src/libexec/rpc.rwalld/CVS/Root b/src/libexec/rpc.rwalld/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.rwalld/Makefile b/src/libexec/rpc.rwalld/Makefile
new file mode 100644 (file)
index 0000000..a092b80
--- /dev/null
@@ -0,0 +1,11 @@
+#      $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>
diff --git a/src/libexec/rpc.rwalld/obj b/src/libexec/rpc.rwalld/obj
new file mode 120000 (symlink)
index 0000000..a2b27c7
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.rwalld
\ No newline at end of file
diff --git a/src/libexec/rpc.rwalld/rpc.rwalld.8 b/src/libexec/rpc.rwalld/rpc.rwalld.8
new file mode 100644 (file)
index 0000000..24b6366
--- /dev/null
@@ -0,0 +1,63 @@
+.\"    $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
diff --git a/src/libexec/rpc.rwalld/rwalld.c b/src/libexec/rpc.rwalld/rwalld.c
new file mode 100644 (file)
index 0000000..0c79973
--- /dev/null
@@ -0,0 +1,178 @@
+/*     $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);
+}
diff --git a/src/libexec/rpc.sprayd/CVS/Entries b/src/libexec/rpc.sprayd/CVS/Entries
new file mode 100644 (file)
index 0000000..56493a7
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/rpc.sprayd/CVS/Repository b/src/libexec/rpc.sprayd/CVS/Repository
new file mode 100644 (file)
index 0000000..10fa372
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.sprayd
diff --git a/src/libexec/rpc.sprayd/CVS/Root b/src/libexec/rpc.sprayd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.sprayd/Makefile b/src/libexec/rpc.sprayd/Makefile
new file mode 100644 (file)
index 0000000..4246ee0
--- /dev/null
@@ -0,0 +1,12 @@
+#      $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>
+
diff --git a/src/libexec/rpc.sprayd/obj b/src/libexec/rpc.sprayd/obj
new file mode 120000 (symlink)
index 0000000..2bdc38d
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.sprayd
\ No newline at end of file
diff --git a/src/libexec/rpc.sprayd/rpc.sprayd.8 b/src/libexec/rpc.sprayd/rpc.sprayd.8
new file mode 100644 (file)
index 0000000..54e870a
--- /dev/null
@@ -0,0 +1,56 @@
+.\"    $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
diff --git a/src/libexec/rpc.sprayd/sprayd.c b/src/libexec/rpc.sprayd/sprayd.c
new file mode 100644 (file)
index 0000000..c7f0da7
--- /dev/null
@@ -0,0 +1,152 @@
+/*     $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");
+       }
+}
diff --git a/src/libexec/rpc.yppasswdd/CVS/Entries b/src/libexec/rpc.yppasswdd/CVS/Entries
new file mode 100644 (file)
index 0000000..c6d4b69
--- /dev/null
@@ -0,0 +1,7 @@
+/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
diff --git a/src/libexec/rpc.yppasswdd/CVS/Repository b/src/libexec/rpc.yppasswdd/CVS/Repository
new file mode 100644 (file)
index 0000000..ebeea9b
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rpc.yppasswdd
diff --git a/src/libexec/rpc.yppasswdd/CVS/Root b/src/libexec/rpc.yppasswdd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rpc.yppasswdd/Makefile b/src/libexec/rpc.yppasswdd/Makefile
new file mode 100644 (file)
index 0000000..b51161c
--- /dev/null
@@ -0,0 +1,12 @@
+#      $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>
diff --git a/src/libexec/rpc.yppasswdd/obj b/src/libexec/rpc.yppasswdd/obj
new file mode 120000 (symlink)
index 0000000..e7f2383
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rpc.yppasswdd
\ No newline at end of file
diff --git a/src/libexec/rpc.yppasswdd/rpc.yppasswdd.8 b/src/libexec/rpc.yppasswdd/rpc.yppasswdd.8
new file mode 100644 (file)
index 0000000..621bf64
--- /dev/null
@@ -0,0 +1,94 @@
+.\"    $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
diff --git a/src/libexec/rpc.yppasswdd/rpc.yppasswdd.c b/src/libexec/rpc.yppasswdd/rpc.yppasswdd.c
new file mode 100644 (file)
index 0000000..1478d7a
--- /dev/null
@@ -0,0 +1,174 @@
+/*     $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;
+}
diff --git a/src/libexec/rpc.yppasswdd/yppasswd.h b/src/libexec/rpc.yppasswdd/yppasswd.h
new file mode 100644 (file)
index 0000000..8b2b091
--- /dev/null
@@ -0,0 +1,61 @@
+/*     $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 */
diff --git a/src/libexec/rpc.yppasswdd/yppasswdd_mkpw.c b/src/libexec/rpc.yppasswdd/yppasswdd_mkpw.c
new file mode 100644 (file)
index 0000000..e98cab8
--- /dev/null
@@ -0,0 +1,239 @@
+/*     $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);
+}
diff --git a/src/libexec/rpc.yppasswdd/yppasswdd_proc.c b/src/libexec/rpc.yppasswdd/yppasswdd_proc.c
new file mode 100644 (file)
index 0000000..b4983c5
--- /dev/null
@@ -0,0 +1,57 @@
+/*     $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);
+}
diff --git a/src/libexec/rshd/CVS/Entries b/src/libexec/rshd/CVS/Entries
new file mode 100644 (file)
index 0000000..bb22003
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/rshd/CVS/Repository b/src/libexec/rshd/CVS/Repository
new file mode 100644 (file)
index 0000000..f70e7bd
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/rshd
diff --git a/src/libexec/rshd/CVS/Root b/src/libexec/rshd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/rshd/Makefile b/src/libexec/rshd/Makefile
new file mode 100644 (file)
index 0000000..0df4c7d
--- /dev/null
@@ -0,0 +1,18 @@
+#      $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>
diff --git a/src/libexec/rshd/obj b/src/libexec/rshd/obj
new file mode 120000 (symlink)
index 0000000..0e22489
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/rshd
\ No newline at end of file
diff --git a/src/libexec/rshd/rshd.8 b/src/libexec/rshd/rshd.8
new file mode 100644 (file)
index 0000000..63e89cd
--- /dev/null
@@ -0,0 +1,220 @@
+.\"    $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.
diff --git a/src/libexec/rshd/rshd.c b/src/libexec/rshd/rshd.c
new file mode 100644 (file)
index 0000000..ddd005a
--- /dev/null
@@ -0,0 +1,827 @@
+/*     $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);
+}
diff --git a/src/libexec/spamd-setup/CVS/Entries b/src/libexec/spamd-setup/CVS/Entries
new file mode 100644 (file)
index 0000000..8944ca9
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/spamd-setup/CVS/Repository b/src/libexec/spamd-setup/CVS/Repository
new file mode 100644 (file)
index 0000000..47c8129
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/spamd-setup
diff --git a/src/libexec/spamd-setup/CVS/Root b/src/libexec/spamd-setup/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/spamd-setup/Makefile b/src/libexec/spamd-setup/Makefile
new file mode 100644 (file)
index 0000000..1d95ad8
--- /dev/null
@@ -0,0 +1,12 @@
+#      $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>
diff --git a/src/libexec/spamd-setup/obj b/src/libexec/spamd-setup/obj
new file mode 120000 (symlink)
index 0000000..4d4dffc
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/spamd-setup
\ No newline at end of file
diff --git a/src/libexec/spamd-setup/spamd-setup.8 b/src/libexec/spamd-setup/spamd-setup.8
new file mode 100644 (file)
index 0000000..985d4e9
--- /dev/null
@@ -0,0 +1,125 @@
+.\"    $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.
diff --git a/src/libexec/spamd-setup/spamd-setup.c b/src/libexec/spamd-setup/spamd-setup.c
new file mode 100644 (file)
index 0000000..ee794e7
--- /dev/null
@@ -0,0 +1,867 @@
+/*     $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);
+}
diff --git a/src/libexec/spamd/CVS/Entries b/src/libexec/spamd/CVS/Entries
new file mode 100644 (file)
index 0000000..80b4507
--- /dev/null
@@ -0,0 +1,10 @@
+/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
diff --git a/src/libexec/spamd/CVS/Repository b/src/libexec/spamd/CVS/Repository
new file mode 100644 (file)
index 0000000..1cc6dc1
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/spamd
diff --git a/src/libexec/spamd/CVS/Root b/src/libexec/spamd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/spamd/Makefile b/src/libexec/spamd/Makefile
new file mode 100644 (file)
index 0000000..0ac6f19
--- /dev/null
@@ -0,0 +1,12 @@
+#      $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>
diff --git a/src/libexec/spamd/grey.c b/src/libexec/spamd/grey.c
new file mode 100644 (file)
index 0000000..108b9e1
--- /dev/null
@@ -0,0 +1,1238 @@
+/*     $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);
+}
diff --git a/src/libexec/spamd/grey.h b/src/libexec/spamd/grey.h
new file mode 100644 (file)
index 0000000..c76e4e9
--- /dev/null
@@ -0,0 +1,39 @@
+/*     $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 *);
diff --git a/src/libexec/spamd/obj b/src/libexec/spamd/obj
new file mode 120000 (symlink)
index 0000000..92c05b2
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/spamd
\ No newline at end of file
diff --git a/src/libexec/spamd/sdl.c b/src/libexec/spamd/sdl.c
new file mode 100644 (file)
index 0000000..39edaad
--- /dev/null
@@ -0,0 +1,285 @@
+/*     $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;
+}
+
diff --git a/src/libexec/spamd/sdl.h b/src/libexec/spamd/sdl.h
new file mode 100644 (file)
index 0000000..c50370b
--- /dev/null
@@ -0,0 +1,61 @@
+/*     $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_ */
diff --git a/src/libexec/spamd/spamd.8 b/src/libexec/spamd/spamd.8
new file mode 100644 (file)
index 0000000..75b9178
--- /dev/null
@@ -0,0 +1,638 @@
+.\"    $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.
diff --git a/src/libexec/spamd/spamd.c b/src/libexec/spamd/spamd.c
new file mode 100644 (file)
index 0000000..b860f6d
--- /dev/null
@@ -0,0 +1,1463 @@
+/*     $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);
+}
diff --git a/src/libexec/spamd/sync.c b/src/libexec/spamd/sync.c
new file mode 100644 (file)
index 0000000..98b8736
--- /dev/null
@@ -0,0 +1,581 @@
+/*     $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);
+}
diff --git a/src/libexec/spamd/sync.h b/src/libexec/spamd/sync.h
new file mode 100644 (file)
index 0000000..81be864
--- /dev/null
@@ -0,0 +1,91 @@
+/*     $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 */
diff --git a/src/libexec/spamlogd/CVS/Entries b/src/libexec/spamlogd/CVS/Entries
new file mode 100644 (file)
index 0000000..5bfd10a
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/spamlogd/CVS/Repository b/src/libexec/spamlogd/CVS/Repository
new file mode 100644 (file)
index 0000000..c8f61b4
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/spamlogd
diff --git a/src/libexec/spamlogd/CVS/Root b/src/libexec/spamlogd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/spamlogd/Makefile b/src/libexec/spamlogd/Makefile
new file mode 100644 (file)
index 0000000..fca36de
--- /dev/null
@@ -0,0 +1,12 @@
+#      $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>
diff --git a/src/libexec/spamlogd/obj b/src/libexec/spamlogd/obj
new file mode 120000 (symlink)
index 0000000..bbad760
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/spamlogd
\ No newline at end of file
diff --git a/src/libexec/spamlogd/spamlogd.8 b/src/libexec/spamlogd/spamlogd.8
new file mode 100644 (file)
index 0000000..b48c59e
--- /dev/null
@@ -0,0 +1,133 @@
+.\"    $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 .
diff --git a/src/libexec/spamlogd/spamlogd.c b/src/libexec/spamlogd/spamlogd.c
new file mode 100644 (file)
index 0000000..922074f
--- /dev/null
@@ -0,0 +1,373 @@
+/*     $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);
+}
diff --git a/src/libexec/talkd/CVS/Entries b/src/libexec/talkd/CVS/Entries
new file mode 100644 (file)
index 0000000..e66ece5
--- /dev/null
@@ -0,0 +1,9 @@
+/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
diff --git a/src/libexec/talkd/CVS/Repository b/src/libexec/talkd/CVS/Repository
new file mode 100644 (file)
index 0000000..125502f
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/talkd
diff --git a/src/libexec/talkd/CVS/Root b/src/libexec/talkd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/talkd/Makefile b/src/libexec/talkd/Makefile
new file mode 100644 (file)
index 0000000..e673fca
--- /dev/null
@@ -0,0 +1,9 @@
+#      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>
diff --git a/src/libexec/talkd/announce.c b/src/libexec/talkd/announce.c
new file mode 100644 (file)
index 0000000..f29a196
--- /dev/null
@@ -0,0 +1,153 @@
+/*     $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);
+}
diff --git a/src/libexec/talkd/obj b/src/libexec/talkd/obj
new file mode 120000 (symlink)
index 0000000..8940f74
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/talkd
\ No newline at end of file
diff --git a/src/libexec/talkd/print.c b/src/libexec/talkd/print.c
new file mode 100644 (file)
index 0000000..9a2dadc
--- /dev/null
@@ -0,0 +1,82 @@
+/*     $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));
+}
diff --git a/src/libexec/talkd/process.c b/src/libexec/talkd/process.c
new file mode 100644 (file)
index 0000000..6d2df62
--- /dev/null
@@ -0,0 +1,239 @@
+/*     $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);
+}
diff --git a/src/libexec/talkd/table.c b/src/libexec/talkd/table.c
new file mode 100644 (file)
index 0000000..5bf065a
--- /dev/null
@@ -0,0 +1,228 @@
+/*     $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);
+}
diff --git a/src/libexec/talkd/talkd.8 b/src/libexec/talkd/talkd.8
new file mode 100644 (file)
index 0000000..5694809
--- /dev/null
@@ -0,0 +1,74 @@
+.\"    $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 .
diff --git a/src/libexec/talkd/talkd.c b/src/libexec/talkd/talkd.c
new file mode 100644 (file)
index 0000000..ee2e7be
--- /dev/null
@@ -0,0 +1,131 @@
+/*     $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;
+}
diff --git a/src/libexec/talkd/talkd.h b/src/libexec/talkd/talkd.h
new file mode 100644 (file)
index 0000000..b84fa82
--- /dev/null
@@ -0,0 +1,55 @@
+/*     $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 *);
+
diff --git a/src/libexec/tcpd/BLURB b/src/libexec/tcpd/BLURB
new file mode 100644 (file)
index 0000000..8d82fa7
--- /dev/null
@@ -0,0 +1,37 @@
+$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.
diff --git a/src/libexec/tcpd/CHANGES b/src/libexec/tcpd/CHANGES
new file mode 100644 (file)
index 0000000..aa23b24
--- /dev/null
@@ -0,0 +1,453 @@
+$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)
diff --git a/src/libexec/tcpd/CVS/Entries b/src/libexec/tcpd/CVS/Entries
new file mode 100644 (file)
index 0000000..669cab5
--- /dev/null
@@ -0,0 +1,10 @@
+/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////
diff --git a/src/libexec/tcpd/CVS/Repository b/src/libexec/tcpd/CVS/Repository
new file mode 100644 (file)
index 0000000..8a53981
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tcpd
diff --git a/src/libexec/tcpd/CVS/Root b/src/libexec/tcpd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tcpd/DISCLAIMER b/src/libexec/tcpd/DISCLAIMER
new file mode 100644 (file)
index 0000000..6b438fa
--- /dev/null
@@ -0,0 +1,18 @@
+$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.
+************************************************************************/
diff --git a/src/libexec/tcpd/Makefile b/src/libexec/tcpd/Makefile
new file mode 100644 (file)
index 0000000..74d92ba
--- /dev/null
@@ -0,0 +1,5 @@
+#      $OpenBSD: Makefile,v 1.2 2002/09/05 00:08:16 deraadt Exp $
+
+SUBDIR=        tcpd tcpdchk tcpdmatch
+
+.include <bsd.subdir.mk>
diff --git a/src/libexec/tcpd/Makefile.inc b/src/libexec/tcpd/Makefile.inc
new file mode 100644 (file)
index 0000000..8a62a4b
--- /dev/null
@@ -0,0 +1,11 @@
+#      $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"
diff --git a/src/libexec/tcpd/README b/src/libexec/tcpd/README
new file mode 100644 (file)
index 0000000..6e89e09
--- /dev/null
@@ -0,0 +1,1039 @@
+$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.
diff --git a/src/libexec/tcpd/safe_finger/CVS/Entries b/src/libexec/tcpd/safe_finger/CVS/Entries
new file mode 100644 (file)
index 0000000..43c4f56
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/tcpd/safe_finger/CVS/Repository b/src/libexec/tcpd/safe_finger/CVS/Repository
new file mode 100644 (file)
index 0000000..e51bdab
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tcpd/safe_finger
diff --git a/src/libexec/tcpd/safe_finger/CVS/Root b/src/libexec/tcpd/safe_finger/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tcpd/safe_finger/Makefile b/src/libexec/tcpd/safe_finger/Makefile
new file mode 100644 (file)
index 0000000..86fcffc
--- /dev/null
@@ -0,0 +1,6 @@
+#      $OpenBSD: Makefile,v 1.2 1997/06/24 02:12:23 downsj Exp $
+
+PROG=  safe_finger
+MAN=   safe_finger.8
+
+.include <bsd.prog.mk>
diff --git a/src/libexec/tcpd/safe_finger/safe_finger.8 b/src/libexec/tcpd/safe_finger/safe_finger.8
new file mode 100644 (file)
index 0000000..7c70989
--- /dev/null
@@ -0,0 +1,54 @@
+.\"    $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
diff --git a/src/libexec/tcpd/safe_finger/safe_finger.c b/src/libexec/tcpd/safe_finger/safe_finger.c
new file mode 100644 (file)
index 0000000..6b4c4a8
--- /dev/null
@@ -0,0 +1,198 @@
+/*     $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);
+    }
+}
diff --git a/src/libexec/tcpd/tcpd/CVS/Entries b/src/libexec/tcpd/tcpd/CVS/Entries
new file mode 100644 (file)
index 0000000..a2d9598
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/tcpd/tcpd/CVS/Repository b/src/libexec/tcpd/tcpd/CVS/Repository
new file mode 100644 (file)
index 0000000..5a34f01
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tcpd/tcpd
diff --git a/src/libexec/tcpd/tcpd/CVS/Root b/src/libexec/tcpd/tcpd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tcpd/tcpd/Makefile b/src/libexec/tcpd/tcpd/Makefile
new file mode 100644 (file)
index 0000000..e802c7e
--- /dev/null
@@ -0,0 +1,9 @@
+#      $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>
diff --git a/src/libexec/tcpd/tcpd/obj b/src/libexec/tcpd/tcpd/obj
new file mode 120000 (symlink)
index 0000000..aca0594
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/tcpd/tcpd
\ No newline at end of file
diff --git a/src/libexec/tcpd/tcpd/tcpd.8 b/src/libexec/tcpd/tcpd/tcpd.8
new file mode 100644 (file)
index 0000000..c1f321f
--- /dev/null
@@ -0,0 +1,259 @@
+.\"    $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.
diff --git a/src/libexec/tcpd/tcpd/tcpd.c b/src/libexec/tcpd/tcpd/tcpd.c
new file mode 100644 (file)
index 0000000..18cc19b
--- /dev/null
@@ -0,0 +1,125 @@
+/*     $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 */
+}
diff --git a/src/libexec/tcpd/tcpdchk/CVS/Entries b/src/libexec/tcpd/tcpdchk/CVS/Entries
new file mode 100644 (file)
index 0000000..40ebe66
--- /dev/null
@@ -0,0 +1,8 @@
+/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
diff --git a/src/libexec/tcpd/tcpdchk/CVS/Repository b/src/libexec/tcpd/tcpdchk/CVS/Repository
new file mode 100644 (file)
index 0000000..11bd98f
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tcpd/tcpdchk
diff --git a/src/libexec/tcpd/tcpdchk/CVS/Root b/src/libexec/tcpd/tcpdchk/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tcpd/tcpdchk/Makefile b/src/libexec/tcpd/tcpdchk/Makefile
new file mode 100644 (file)
index 0000000..4a24a0f
--- /dev/null
@@ -0,0 +1,13 @@
+#      $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>
diff --git a/src/libexec/tcpd/tcpdchk/inetcf.c b/src/libexec/tcpd/tcpdchk/inetcf.c
new file mode 100644 (file)
index 0000000..96f1b46
--- /dev/null
@@ -0,0 +1,315 @@
+/*     $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);
+}
diff --git a/src/libexec/tcpd/tcpdchk/inetcf.h b/src/libexec/tcpd/tcpdchk/inetcf.h
new file mode 100644 (file)
index 0000000..05716c3
--- /dev/null
@@ -0,0 +1,18 @@
+/*     $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 */
diff --git a/src/libexec/tcpd/tcpdchk/obj b/src/libexec/tcpd/tcpdchk/obj
new file mode 120000 (symlink)
index 0000000..ec4b4c2
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/tcpd/tcpdchk
\ No newline at end of file
diff --git a/src/libexec/tcpd/tcpdchk/scaffold.c b/src/libexec/tcpd/tcpdchk/scaffold.c
new file mode 100644 (file)
index 0000000..ac8b05b
--- /dev/null
@@ -0,0 +1,158 @@
+/*     $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);
+}
diff --git a/src/libexec/tcpd/tcpdchk/scaffold.h b/src/libexec/tcpd/tcpdchk/scaffold.h
new file mode 100644 (file)
index 0000000..7bb69bc
--- /dev/null
@@ -0,0 +1,15 @@
+/*     $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
diff --git a/src/libexec/tcpd/tcpdchk/tcpdchk.8 b/src/libexec/tcpd/tcpdchk/tcpdchk.8
new file mode 100644 (file)
index 0000000..dedf5cd
--- /dev/null
@@ -0,0 +1,113 @@
+.\"    $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
diff --git a/src/libexec/tcpd/tcpdchk/tcpdchk.c b/src/libexec/tcpd/tcpdchk/tcpdchk.c
new file mode 100644 (file)
index 0000000..20213a8
--- /dev/null
@@ -0,0 +1,507 @@
+/*     $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"));
+}
diff --git a/src/libexec/tcpd/tcpdmatch/CVS/Entries b/src/libexec/tcpd/tcpdmatch/CVS/Entries
new file mode 100644 (file)
index 0000000..e3e22d0
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/tcpd/tcpdmatch/CVS/Repository b/src/libexec/tcpd/tcpdmatch/CVS/Repository
new file mode 100644 (file)
index 0000000..44f7813
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tcpd/tcpdmatch
diff --git a/src/libexec/tcpd/tcpdmatch/CVS/Root b/src/libexec/tcpd/tcpdmatch/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tcpd/tcpdmatch/Makefile b/src/libexec/tcpd/tcpdmatch/Makefile
new file mode 100644 (file)
index 0000000..f601a98
--- /dev/null
@@ -0,0 +1,16 @@
+#      $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>
diff --git a/src/libexec/tcpd/tcpdmatch/obj b/src/libexec/tcpd/tcpdmatch/obj
new file mode 120000 (symlink)
index 0000000..090a7df
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/tcpd/tcpdmatch
\ No newline at end of file
diff --git a/src/libexec/tcpd/tcpdmatch/tcpdmatch.8 b/src/libexec/tcpd/tcpdmatch/tcpdmatch.8
new file mode 100644 (file)
index 0000000..a88df85
--- /dev/null
@@ -0,0 +1,179 @@
+.\"    $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
diff --git a/src/libexec/tcpd/tcpdmatch/tcpdmatch.c b/src/libexec/tcpd/tcpdmatch/tcpdmatch.c
new file mode 100644 (file)
index 0000000..ebb94f7
--- /dev/null
@@ -0,0 +1,331 @@
+/*     $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");
+}
diff --git a/src/libexec/tftp-proxy/CVS/Entries b/src/libexec/tftp-proxy/CVS/Entries
new file mode 100644 (file)
index 0000000..796c75d
--- /dev/null
@@ -0,0 +1,6 @@
+/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
diff --git a/src/libexec/tftp-proxy/CVS/Repository b/src/libexec/tftp-proxy/CVS/Repository
new file mode 100644 (file)
index 0000000..e0997ca
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tftp-proxy
diff --git a/src/libexec/tftp-proxy/CVS/Root b/src/libexec/tftp-proxy/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tftp-proxy/Makefile b/src/libexec/tftp-proxy/Makefile
new file mode 100644 (file)
index 0000000..b5f4eef
--- /dev/null
@@ -0,0 +1,7 @@
+#      $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>
diff --git a/src/libexec/tftp-proxy/filter.c b/src/libexec/tftp-proxy/filter.c
new file mode 100644 (file)
index 0000000..e9ca970
--- /dev/null
@@ -0,0 +1,305 @@
+/*     $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);
+}
diff --git a/src/libexec/tftp-proxy/filter.h b/src/libexec/tftp-proxy/filter.h
new file mode 100644 (file)
index 0000000..bc0e608
--- /dev/null
@@ -0,0 +1,30 @@
+/*     $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);
diff --git a/src/libexec/tftp-proxy/obj b/src/libexec/tftp-proxy/obj
new file mode 120000 (symlink)
index 0000000..3c2d4a6
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/tftp-proxy
\ No newline at end of file
diff --git a/src/libexec/tftp-proxy/tftp-proxy.8 b/src/libexec/tftp-proxy/tftp-proxy.8
new file mode 100644 (file)
index 0000000..3e508f5
--- /dev/null
@@ -0,0 +1,127 @@
+.\"    $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.
diff --git a/src/libexec/tftp-proxy/tftp-proxy.c b/src/libexec/tftp-proxy/tftp-proxy.c
new file mode 100644 (file)
index 0000000..d2d2875
--- /dev/null
@@ -0,0 +1,395 @@
+/* $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);
+}
diff --git a/src/libexec/tftpd/CVS/Entries b/src/libexec/tftpd/CVS/Entries
new file mode 100644 (file)
index 0000000..efa37d3
--- /dev/null
@@ -0,0 +1,4 @@
+/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
diff --git a/src/libexec/tftpd/CVS/Repository b/src/libexec/tftpd/CVS/Repository
new file mode 100644 (file)
index 0000000..58c21b2
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/tftpd
diff --git a/src/libexec/tftpd/CVS/Root b/src/libexec/tftpd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/tftpd/Makefile b/src/libexec/tftpd/Makefile
new file mode 100644 (file)
index 0000000..4a7330e
--- /dev/null
@@ -0,0 +1,8 @@
+#      $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>
diff --git a/src/libexec/tftpd/obj b/src/libexec/tftpd/obj
new file mode 120000 (symlink)
index 0000000..56f39bb
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/tftpd
\ No newline at end of file
diff --git a/src/libexec/tftpd/tftpd.8 b/src/libexec/tftpd/tftpd.8
new file mode 100644 (file)
index 0000000..ca35a91
--- /dev/null
@@ -0,0 +1,143 @@
+.\"   $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 .
diff --git a/src/libexec/tftpd/tftpd.c b/src/libexec/tftpd/tftpd.c
new file mode 100644 (file)
index 0000000..01e9f15
--- /dev/null
@@ -0,0 +1,940 @@
+/*     $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);
+}
diff --git a/src/libexec/uucpd/CVS/Entries b/src/libexec/uucpd/CVS/Entries
new file mode 100644 (file)
index 0000000..06b8857
--- /dev/null
@@ -0,0 +1,5 @@
+/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
diff --git a/src/libexec/uucpd/CVS/Repository b/src/libexec/uucpd/CVS/Repository
new file mode 100644 (file)
index 0000000..bb2ac62
--- /dev/null
@@ -0,0 +1 @@
+src/libexec/uucpd
diff --git a/src/libexec/uucpd/CVS/Root b/src/libexec/uucpd/CVS/Root
new file mode 100644 (file)
index 0000000..5bdd6b1
--- /dev/null
@@ -0,0 +1 @@
+:ext:cvs.openbsd.org:/cvs
diff --git a/src/libexec/uucpd/Makefile b/src/libexec/uucpd/Makefile
new file mode 100644 (file)
index 0000000..223c381
--- /dev/null
@@ -0,0 +1,7 @@
+#      $OpenBSD: Makefile,v 1.5 2001/01/28 19:34:35 niklas Exp $
+
+CFLAGS+= -DBSDINETD
+PROG=  uucpd
+MAN=   uucpd.8
+
+.include <bsd.prog.mk>
diff --git a/src/libexec/uucpd/obj b/src/libexec/uucpd/obj
new file mode 120000 (symlink)
index 0000000..6c7a562
--- /dev/null
@@ -0,0 +1 @@
+/usr/obj/libexec/uucpd
\ No newline at end of file
diff --git a/src/libexec/uucpd/pathnames.h b/src/libexec/uucpd/pathnames.h
new file mode 100644 (file)
index 0000000..6e3bc44
--- /dev/null
@@ -0,0 +1,36 @@
+/*     $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"
diff --git a/src/libexec/uucpd/uucpd.8 b/src/libexec/uucpd/uucpd.8
new file mode 100644 (file)
index 0000000..5dbf889
--- /dev/null
@@ -0,0 +1,68 @@
+.\"    $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 .
diff --git a/src/libexec/uucpd/uucpd.c b/src/libexec/uucpd/uucpd.c
new file mode 100644 (file)
index 0000000..2b38ea6
--- /dev/null
@@ -0,0 +1,296 @@
+/*     $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);
+       }
+}