diff options
Diffstat (limited to 'libexec')
73 files changed, 1045 insertions, 7108 deletions
diff --git a/libexec/Makefile b/libexec/Makefile index 55e586434087..e87b48b153a8 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -10,7 +10,6 @@ SUBDIR= ${_atf} \ flua \ getty \ ${_hyperv} \ - kgdb \ ${_mail.local} \ ${_makewhatis.local} \ ${_mknetid} \ @@ -54,10 +53,6 @@ SUBDIR+= fingerd _phttpget= phttpget .endif -.if ${MK_FTP} != "no" -SUBDIR+= ftpd -.endif - .if ${MK_MAIL} != "no" _comsat= comsat .endif diff --git a/libexec/atf/Makefile.inc b/libexec/atf/Makefile.inc index 42c507a6f772..e40827fa0b69 100644 --- a/libexec/atf/Makefile.inc +++ b/libexec/atf/Makefile.inc @@ -24,6 +24,8 @@ # SUCH DAMAGE. # +PACKAGE= atf +LIB_PACKAGE= CFLAGS+= -DHAVE_CONFIG_H WARNS?= 3 diff --git a/libexec/atf/atf-check/Makefile b/libexec/atf/atf-check/Makefile index 87d7a7cfdada..cf598e384c86 100644 --- a/libexec/atf/atf-check/Makefile +++ b/libexec/atf/atf-check/Makefile @@ -30,7 +30,6 @@ ATF= ${SRCTOP}/contrib/atf .PATH: ${ATF}/atf-sh -PACKAGE= tests PROG_CXX= atf-check SRCS= atf-check.cpp MAN= atf-check.1 diff --git a/libexec/atf/atf-pytest-wrapper/Makefile b/libexec/atf/atf-pytest-wrapper/Makefile index 80b5e411ec4e..75b1bc3e1004 100644 --- a/libexec/atf/atf-pytest-wrapper/Makefile +++ b/libexec/atf/atf-pytest-wrapper/Makefile @@ -1,7 +1,6 @@ .include <src.opts.mk> .include <bsd.init.mk> -PACKAGE= tests PROG_CXX= atf_pytest_wrapper SRCS= atf_pytest_wrapper.cpp MAN= diff --git a/libexec/atf/atf-sh/Makefile b/libexec/atf/atf-sh/Makefile index 2e821684d8a8..afd848581f36 100644 --- a/libexec/atf/atf-sh/Makefile +++ b/libexec/atf/atf-sh/Makefile @@ -30,7 +30,6 @@ ATF= ${SRCTOP}/contrib/atf .PATH: ${ATF}/atf-sh -PACKAGE= tests PROG_CXX= atf-sh SRCS= atf-sh.cpp MAN= atf-sh.1 atf-sh.3 diff --git a/libexec/blacklistd-helper/Makefile b/libexec/blacklistd-helper/Makefile index 50fd3b5c982b..d32b69c278a8 100644 --- a/libexec/blacklistd-helper/Makefile +++ b/libexec/blacklistd-helper/Makefile @@ -1,5 +1,7 @@ BLOCKLIST_DIR=${SRCTOP}/contrib/blocklist +PACKAGE= blocklist + SCRIPTS= ${BLOCKLIST_DIR}/libexec/blacklistd-helper .include <bsd.prog.mk> diff --git a/libexec/flua/Makefile b/libexec/flua/Makefile index 86d27c0653d4..23de404710d0 100644 --- a/libexec/flua/Makefile +++ b/libexec/flua/Makefile @@ -1,10 +1,42 @@ .include <src.lua.mk> -SUBDIR+= libfreebsd -SUBDIR+= libhash -SUBDIR+= libjail -SUBDIR+= libucl -SUBDIR+= liblyaml +# New flua modules should be added here rather than to SUBDIR so that we can do +# the right thing for both bootstrap flua and target flua. The former does not +# do any shared libs, so we just build them all straight into flua itself rather +# than mucking about with the infrastructure to make them linkable -- thus, why +# these are all structured to have a Makefile that describes what we want +# *installed*, and a Makefile.inc that describes what we need to *build*. +FLUA_MODULES+= lfbsd +FLUA_MODULES+= lfs +FLUA_MODULES+= libhash +.ifndef BOOTSTRAPPING +# Bootstrap flua can't usefully do anything with libjail anyways, because it +# can't assume it's being run on a system that even supports jails. +FLUA_MODULES+= libjail +.endif +FLUA_MODULES+= libucl +FLUA_MODULES+= liblyaml + +.ifdef BOOTSTRAPPING +# libfreebsd is generally omitted from the bootstrap flua because its +# functionality largely assumes a FreeBSD kernel/system headers, so it doesn't +# really offer functionality that we can use in bootstrap. +CFLAGS+= -I${.CURDIR} -DBOOTSTRAPPING + +SHAREDIR= ${WORLDTMP}/legacy/usr/share/flua +FLUA_PATH= ${SHAREDIR}/?.lua;${SHAREDIR}/?/init.lua +CFLAGS+= -DBOOTSTRAP_FLUA_PATH=\"${FLUA_PATH:Q}\" + +.for mod in ${FLUA_MODULES} +.include "${mod}/Makefile.inc" +.endfor + +.else + +FLUA_MODULES+= libfreebsd +SUBDIR+= ${FLUA_MODULES} + +.endif LUASRC?= ${SRCTOP}/contrib/lua/src .PATH: ${LUASRC} @@ -14,7 +46,7 @@ WARNS?= 3 CWARNFLAGS.gcc+= -Wno-format-nonliteral -LIBADD= lua +LIBADD+= lua # Entry point SRCS+= lua.c @@ -22,7 +54,7 @@ SRCS+= lua.c # FreeBSD Extensions .PATH: ${.CURDIR}/modules SRCS+= linit_flua.c -SRCS+= lfs.c lposix.c lfbsd.c +SRCS+= lposix.c CFLAGS+= -I${SRCTOP}/lib/liblua -I${.CURDIR}/modules -I${LUASRC} CFLAGS+= -DLUA_PROGNAME="\"${PROG}\"" diff --git a/libexec/flua/Makefile.inc b/libexec/flua/Makefile.inc index 34505d54d7df..37a49e258ecb 100644 --- a/libexec/flua/Makefile.inc +++ b/libexec/flua/Makefile.inc @@ -2,4 +2,9 @@ SHLIBDIR?= ${LIBDIR}/flua CFLAGS+= \ -I${SRCTOP}/contrib/lua/src \ - -I${SRCTOP}/lib/liblua + -I${SRCTOP}/lib/liblua \ + -I${SRCTOP}/libexec/flua + +.ifdef BOOTSTRAPPING +CFLAGS+= -DBOOTSTRAPPING +.endif diff --git a/libexec/flua/bootstrap.h b/libexec/flua/bootstrap.h new file mode 100644 index 000000000000..caf00518c1e0 --- /dev/null +++ b/libexec/flua/bootstrap.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef FLUA_BOOTSTRAP_H +#define FLUA_BOOTSTRAP_H + +#ifdef BOOTSTRAPPING +#include <sys/linker_set.h> + +#include <lauxlib.h> + +#define FLUA_MODULE_SETNAME flua_modules + +SET_DECLARE(FLUA_MODULE_SETNAME, const luaL_Reg); +#define FLUA_MODULE_DEF(ident, modname, openfn) \ + static const luaL_Reg ident = { modname, openfn }; \ + DATA_SET(FLUA_MODULE_SETNAME, ident) + +#define FLUA_MODULE_NAMED(mod, name) \ + FLUA_MODULE_DEF(module_ ## mod, name, luaopen_ ## mod) +#define FLUA_MODULE(mod) \ + FLUA_MODULE_DEF(module_ ## mod, #mod, luaopen_ ## mod) +#else /* !BOOTSTRAPPING */ +#define FLUA_MODULE_DEF(ident, modname, openfn) +#define FLUA_MODULE_NAMED(mod, name) +#define FLUA_MODULE(modname) +#endif /* BOOTSTRAPPING */ + +#endif /* FLUA_BOOTSTRAP_H */ diff --git a/libexec/flua/lfbsd/Makefile b/libexec/flua/lfbsd/Makefile new file mode 100644 index 000000000000..e2a4aae14bcd --- /dev/null +++ b/libexec/flua/lfbsd/Makefile @@ -0,0 +1,5 @@ +SHLIB_NAME= fbsd.so +WARNS?= 3 + +.include "Makefile.inc" +.include <bsd.lib.mk> diff --git a/libexec/flua/lfbsd/Makefile.inc b/libexec/flua/lfbsd/Makefile.inc new file mode 100644 index 000000000000..7a78ef82e0fc --- /dev/null +++ b/libexec/flua/lfbsd/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= lfbsd.c diff --git a/libexec/flua/modules/lfbsd.c b/libexec/flua/lfbsd/lfbsd.c index ef660ba9fd77..541b6c9611df 100644 --- a/libexec/flua/modules/lfbsd.c +++ b/libexec/flua/lfbsd/lfbsd.c @@ -40,6 +40,8 @@ #include "lauxlib.h" #include "lfbsd.h" +#include "bootstrap.h" + #define FBSD_PROCESSHANDLE "fbsd_process_t*" struct fbsd_process { @@ -283,3 +285,5 @@ luaopen_fbsd(lua_State *L) return (1); } + +FLUA_MODULE(fbsd); diff --git a/libexec/flua/modules/lfbsd.h b/libexec/flua/lfbsd/lfbsd.h index 01034a3ad7cd..01034a3ad7cd 100644 --- a/libexec/flua/modules/lfbsd.h +++ b/libexec/flua/lfbsd/lfbsd.h diff --git a/libexec/flua/lfs/Makefile b/libexec/flua/lfs/Makefile new file mode 100644 index 000000000000..3df83d6d2fc1 --- /dev/null +++ b/libexec/flua/lfs/Makefile @@ -0,0 +1,5 @@ +SHLIB_NAME= lfs.so +WARNS?= 3 + +.include "Makefile.inc" +.include <bsd.lib.mk> diff --git a/libexec/flua/lfs/Makefile.inc b/libexec/flua/lfs/Makefile.inc new file mode 100644 index 000000000000..9d40c42dc0e6 --- /dev/null +++ b/libexec/flua/lfs/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= lfs.c diff --git a/libexec/flua/modules/lfs.c b/libexec/flua/lfs/lfs.c index 8cb8d6fc9fed..517e16ae65c8 100644 --- a/libexec/flua/modules/lfs.c +++ b/libexec/flua/lfs/lfs.c @@ -66,9 +66,10 @@ #ifdef _STANDALONE #include "lstd.h" #include "lutils.h" -#include "bootstrap.h" #endif +#include "bootstrap.h" + #ifndef nitems #define nitems(x) (sizeof((x)) / sizeof((x)[0])) #endif @@ -446,3 +447,7 @@ luaopen_lfs(lua_State *L) #endif return 1; } + +#ifndef _STANDALONE +FLUA_MODULE(lfs); +#endif diff --git a/libexec/flua/modules/lfs.h b/libexec/flua/lfs/lfs.h index a99e66d7f601..a99e66d7f601 100644 --- a/libexec/flua/modules/lfs.h +++ b/libexec/flua/lfs/lfs.h diff --git a/libexec/flua/libfreebsd/kenv/Makefile b/libexec/flua/libfreebsd/kenv/Makefile index 1726c892c515..a1b388bb3612 100644 --- a/libexec/flua/libfreebsd/kenv/Makefile +++ b/libexec/flua/libfreebsd/kenv/Makefile @@ -1,5 +1,5 @@ SHLIB_NAME= kenv.so -SRCS+= kenv.c MAN= freebsd.kenv.3lua +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/libfreebsd/kenv/Makefile.inc b/libexec/flua/libfreebsd/kenv/Makefile.inc new file mode 100644 index 000000000000..05819c5280d9 --- /dev/null +++ b/libexec/flua/libfreebsd/kenv/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= kenv.c diff --git a/libexec/flua/libfreebsd/kenv/kenv.c b/libexec/flua/libfreebsd/kenv/kenv.c index 954baa00facb..56b24c72904a 100644 --- a/libexec/flua/libfreebsd/kenv/kenv.c +++ b/libexec/flua/libfreebsd/kenv/kenv.c @@ -14,6 +14,8 @@ #include <lualib.h> #include <lauxlib.h> +#include "bootstrap.h" + int luaopen_freebsd_kenv(lua_State *L); static int @@ -94,3 +96,5 @@ luaopen_freebsd_kenv(lua_State *L) return (1); } + +FLUA_MODULE_NAMED(freebsd_kenv, "freebsd.kenv"); diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile b/libexec/flua/libfreebsd/sys/linker/Makefile index 1adf547b503c..f1f65ad5f6c1 100644 --- a/libexec/flua/libfreebsd/sys/linker/Makefile +++ b/libexec/flua/libfreebsd/sys/linker/Makefile @@ -1,7 +1,6 @@ SHLIB_NAME= linker.so -SRCS+= linker.c - MAN= freebsd.sys.linker.3lua +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/libfreebsd/sys/linker/Makefile.inc b/libexec/flua/libfreebsd/sys/linker/Makefile.inc new file mode 100644 index 000000000000..da65c0070170 --- /dev/null +++ b/libexec/flua/libfreebsd/sys/linker/Makefile.inc @@ -0,0 +1,2 @@ +.PATH: ${.PARSEDIR} +SRCS+= linker.c diff --git a/libexec/flua/libfreebsd/sys/linker/linker.c b/libexec/flua/libfreebsd/sys/linker/linker.c index 87eccfb651f0..c78fbb2b39d2 100644 --- a/libexec/flua/libfreebsd/sys/linker/linker.c +++ b/libexec/flua/libfreebsd/sys/linker/linker.c @@ -15,6 +15,8 @@ #include <lualib.h> #include <lauxlib.h> +#include "bootstrap.h" + int luaopen_freebsd_sys_linker(lua_State *L); static int @@ -80,3 +82,5 @@ luaopen_freebsd_sys_linker(lua_State *L) return (1); } + +FLUA_MODULE_NAMED(freebsd_sys_linker, "freebsd.sys.linker"); diff --git a/libexec/flua/libhash/Makefile b/libexec/flua/libhash/Makefile index b7c8d7ee9948..9cbd6f15acae 100644 --- a/libexec/flua/libhash/Makefile +++ b/libexec/flua/libhash/Makefile @@ -1,9 +1,6 @@ SHLIB_NAME= hash.so -SRCS+= lhash.c - -LIBADD+= md - MAN= hash.3lua +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/libhash/Makefile.inc b/libexec/flua/libhash/Makefile.inc new file mode 100644 index 000000000000..d112dfe7df33 --- /dev/null +++ b/libexec/flua/libhash/Makefile.inc @@ -0,0 +1,3 @@ +.PATH: ${.PARSEDIR} +SRCS+= lhash.c +LIBADD+= md diff --git a/libexec/flua/libhash/lhash.c b/libexec/flua/libhash/lhash.c index 4587961fe8a0..f455f006bf27 100644 --- a/libexec/flua/libhash/lhash.c +++ b/libexec/flua/libhash/lhash.c @@ -11,6 +11,8 @@ #include <sha256.h> #include <string.h> +#include "bootstrap.h" + #define SHA256_META "SHA256 meta table" #define SHA256_DIGEST_LEN 32 @@ -175,3 +177,7 @@ luaopen_hash(lua_State *L) return 1; } + +#ifndef _STANDALONE +FLUA_MODULE(hash); +#endif diff --git a/libexec/flua/libjail/Makefile b/libexec/flua/libjail/Makefile index 20cd9f5f1429..b9c8bdc39095 100644 --- a/libexec/flua/libjail/Makefile +++ b/libexec/flua/libjail/Makefile @@ -1,9 +1,6 @@ SHLIB_NAME= jail.so -SRCS+= lua_jail.c - -LIBADD+= jail - MAN= jail.3lua +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/libjail/Makefile.inc b/libexec/flua/libjail/Makefile.inc new file mode 100644 index 000000000000..a896bf38c65b --- /dev/null +++ b/libexec/flua/libjail/Makefile.inc @@ -0,0 +1,3 @@ +.PATH: ${.PARSEDIR} +SRCS+= lua_jail.c +LIBADD+= jail diff --git a/libexec/flua/libjail/lua_jail.c b/libexec/flua/libjail/lua_jail.c index 9632db795775..8c3ec6c1d500 100644 --- a/libexec/flua/libjail/lua_jail.c +++ b/libexec/flua/libjail/lua_jail.c @@ -38,6 +38,8 @@ #include <lauxlib.h> #include <lualib.h> +#include "bootstrap.h" + #define JAIL_METATABLE "jail iterator metatable" /* @@ -716,3 +718,5 @@ luaopen_jail(lua_State *L) return (1); } + +FLUA_MODULE(jail); diff --git a/libexec/flua/liblyaml/Makefile b/libexec/flua/liblyaml/Makefile index e7a89d09bb9e..8d1432acd325 100644 --- a/libexec/flua/liblyaml/Makefile +++ b/libexec/flua/liblyaml/Makefile @@ -1,22 +1,4 @@ SHLIB_NAME= yaml.so -WARNS= 1 -LYAMLSRC?= ${SRCTOP}/contrib/lyaml -.PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml -SRCS= emitter.c \ - parser.c \ - scanner.c \ - yaml.c -CFLAGS+= \ - -I${LYAMLSRC}/ext/yaml \ - -I${SRCTOP}/contrib/libyaml/include \ - -DVERSION=\"6.2.8\" -LIBADD+= yaml - -FILES= explicit.lua \ - functional.lua \ - implicit.lua \ - init.lua -FILESDIR= ${SHAREDIR}/flua/lyaml - +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/liblyaml/Makefile.inc b/libexec/flua/liblyaml/Makefile.inc new file mode 100644 index 000000000000..caa1f37b57eb --- /dev/null +++ b/libexec/flua/liblyaml/Makefile.inc @@ -0,0 +1,20 @@ +WARNS= 1 + +LYAMLSRC?= ${SRCTOP}/contrib/lyaml +.PATH: ${LYAMLSRC}/ext/yaml ${LYAMLSRC}/lib/lyaml +SRCS+= emitter.c \ + parser.c \ + scanner.c \ + yaml.c +CFLAGS+= \ + -I${LYAMLSRC}/ext/yaml \ + -I${SRCTOP}/contrib/libyaml/include \ + -DVERSION=\"6.2.8\" +LIBADD+= yaml + +FILESGROUPS+= YAML +YAML= explicit.lua \ + functional.lua \ + implicit.lua \ + init.lua +YAMLDIR= ${SHAREDIR}/flua/lyaml diff --git a/libexec/flua/libucl/Makefile b/libexec/flua/libucl/Makefile index a88c8bda6bfc..32d76d1ea1ad 100644 --- a/libexec/flua/libucl/Makefile +++ b/libexec/flua/libucl/Makefile @@ -1,14 +1,4 @@ SHLIB_NAME= ucl.so -WARNS= 2 - -UCLSRC?= ${SRCTOP}/contrib/libucl -.PATH: ${UCLSRC}/lua -SRCS+= lua_ucl.c -CFLAGS+= \ - -I${UCLSRC}/include \ - -I${UCLSRC}/src \ - -I${UCLSRC}/uthash -LIBADD+= ucl - +.include "Makefile.inc" .include <bsd.lib.mk> diff --git a/libexec/flua/libucl/Makefile.inc b/libexec/flua/libucl/Makefile.inc new file mode 100644 index 000000000000..70fb0f265635 --- /dev/null +++ b/libexec/flua/libucl/Makefile.inc @@ -0,0 +1,12 @@ +.if ${WARNS:U6} > 2 +WARNS= 2 +.endif + +UCLSRC?= ${SRCTOP}/contrib/libucl +.PATH: ${UCLSRC}/lua +SRCS+= lua_ucl.c +CFLAGS+= \ + -I${UCLSRC}/include \ + -I${UCLSRC}/src \ + -I${UCLSRC}/uthash +LIBADD+= ucl diff --git a/libexec/flua/linit_flua.c b/libexec/flua/linit_flua.c index b466b7872158..65356c938671 100644 --- a/libexec/flua/linit_flua.c +++ b/libexec/flua/linit_flua.c @@ -26,16 +26,16 @@ #include "lprefix.h" - #include <stddef.h> +#include <stdlib.h> #include "lua.h" #include "lualib.h" #include "lauxlib.h" -#include "lfs.h" #include "lposix.h" -#include "lfbsd.h" + +#include "bootstrap.h" /* ** these libs are loaded by lua.c and are readily available to any Lua @@ -56,12 +56,32 @@ static const luaL_Reg loadedlibs[] = { {LUA_BITLIBNAME, luaopen_bit32}, #endif /* FreeBSD Extensions */ - {"lfs", luaopen_lfs}, {"posix", luaopen_posix}, - {"fbsd", luaopen_fbsd}, {NULL, NULL} }; +#ifdef BOOTSTRAPPING +static void __attribute__((constructor)) flua_init_env(void) { + /* + * This happens in the middle of luaopen_package(). We could move it into + * flua_setup_mods(), but it seems better to avoid its timing being so + * important that it would break some of our bootstrap modules if someone + * were to reorder things. + */ + if (getenv("LUA_PATH") == NULL) + setenv("LUA_PATH", BOOTSTRAP_FLUA_PATH, 1); +} + +static void flua_setup_mods (lua_State *L) { + const luaL_Reg **flib; + + SET_FOREACH(flib, FLUA_MODULE_SETNAME) { + luaL_requiref(L, (*flib)->name, (*flib)->func, 1); + lua_pop(L, 1); /* remove lib */ + } +}; +#endif + LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; /* "require" functions from 'loadedlibs' and set results to global table */ @@ -69,4 +89,7 @@ LUALIB_API void luaL_openlibs (lua_State *L) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } +#ifdef BOOTSTRAPPING + flua_setup_mods(L); +#endif } diff --git a/libexec/ftpd/Makefile b/libexec/ftpd/Makefile deleted file mode 100644 index a040fa57f7d7..000000000000 --- a/libexec/ftpd/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -.include <src.opts.mk> - -PACKAGE= ftpd - -CONFS= ftpusers -PROG= ftpd -MAN= ftpd.8 ftpchroot.5 -SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c - -CFLAGS+=-DSETPROCTITLE -DLOGIN_CAP -DVIRTUAL_HOSTING -CFLAGS+=-I${.CURDIR} -YFLAGS= -WARNS?= 2 -WFORMAT=0 - -LIBADD= crypt md util - -.PATH: ${SRCTOP}/bin/ls -SRCS+= ls.c cmp.c print.c util.c -CFLAGS+=-Dmain=ls_main -I${SRCTOP}/bin/ls -LIBADD+= m - -.if ${MK_BLACKLIST_SUPPORT} != "no" -CFLAGS+= -DUSE_BLACKLIST -I${SRCTOP}/contrib/blocklist/include -SRCS+= blacklist.c -LIBADD+= blacklist -LDFLAGS+=-L${LIBBLACKLISTDIR} -.endif - -.if ${MK_INET6_SUPPORT} != "no" -CFLAGS+=-DINET6 -.endif - -.if ${MK_PAM_SUPPORT} != "no" -CFLAGS+=-DUSE_PAM -LIBADD+= pam -.endif - -.include <bsd.prog.mk> diff --git a/libexec/ftpd/Makefile.depend b/libexec/ftpd/Makefile.depend deleted file mode 100644 index b6e94a3cf93c..000000000000 --- a/libexec/ftpd/Makefile.depend +++ /dev/null @@ -1,21 +0,0 @@ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/libc \ - lib/libcompiler_rt \ - lib/libcrypt \ - lib/libmd \ - lib/libutil \ - lib/msun \ - usr.bin/yacc.host \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/libexec/ftpd/Makefile.depend.options b/libexec/ftpd/Makefile.depend.options deleted file mode 100644 index 5f186bb031f2..000000000000 --- a/libexec/ftpd/Makefile.depend.options +++ /dev/null @@ -1,5 +0,0 @@ -# This file is not autogenerated - take care! - -DIRDEPS_OPTIONS= BLACKLIST_SUPPORT PAM_SUPPORT - -.include <dirdeps-options.mk> diff --git a/libexec/ftpd/blacklist.c b/libexec/ftpd/blacklist.c deleted file mode 100644 index 0a45f9369074..000000000000 --- a/libexec/ftpd/blacklist.c +++ /dev/null @@ -1,55 +0,0 @@ -/*- - * Copyright (c) 2016 The FreeBSD Foundation - * - * This software was developed by Kurt Lidl under sponsorship from the - * FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must 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 <ctype.h> -#include <stdarg.h> -#include <stdlib.h> -#include <unistd.h> - -#include <blacklist.h> -#include "blacklist_client.h" - -static struct blacklist *blstate; -extern int use_blacklist; - -void -blacklist_init(void) -{ - - if (use_blacklist) - blstate = blacklist_open(); -} - -void -blacklist_notify(int action, int fd, const char *msg) -{ - - if (blstate == NULL) - return; - (void)blacklist_r(blstate, action, fd, msg); -} diff --git a/libexec/ftpd/blacklist_client.h b/libexec/ftpd/blacklist_client.h deleted file mode 100644 index 0b6805dc218e..000000000000 --- a/libexec/ftpd/blacklist_client.h +++ /dev/null @@ -1,53 +0,0 @@ -/*- - * Copyright (c) 2016 The FreeBSD Foundation - * - * This software was developed by Kurt Lidl under sponsorship from the - * FreeBSD Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must 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 BLACKLIST_CLIENT_H -#define BLACKLIST_CLIENT_H - -#ifndef BLACKLIST_API_ENUM -enum { - BLACKLIST_AUTH_OK = 0, - BLACKLIST_AUTH_FAIL -}; -#endif - -#ifdef USE_BLACKLIST -void blacklist_init(void); -void blacklist_notify(int, int, const char *); - -#define BLACKLIST_INIT() blacklist_init() -#define BLACKLIST_NOTIFY(x, y, z) blacklist_notify(x, y, z) - -#else - -#define BLACKLIST_INIT() -#define BLACKLIST_NOTIFY(x, y, z) - -#endif - -#endif /* BLACKLIST_CLIENT_H */ diff --git a/libexec/ftpd/config.h b/libexec/ftpd/config.h deleted file mode 100644 index c5ca1f01e10e..000000000000 --- a/libexec/ftpd/config.h +++ /dev/null @@ -1,280 +0,0 @@ - - -/* config.h. Generated automatically by configure. */ -/* config.h.in. Generated automatically from configure.in by autoheader. */ -/* $Id: config.h.in,v 1.15 2001/04/28 07:11:46 lukem Exp $ */ - - -/* Define if the closedir function returns void instead of int. */ -/* #undef CLOSEDIR_VOID */ - -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Define if your Fortran 77 compiler doesn't accept -c and -o together. */ -/* #undef F77_NO_MINUS_C_MINUS_O */ - -/* Define to `long' if <sys/types.h> doesn't define. */ -/* #undef off_t */ - -/* Define to the type of arg1 for select(). */ -/* #undef SELECT_TYPE_ARG1 */ - -/* Define to the type of args 2, 3 and 4 for select(). */ -/* #undef SELECT_TYPE_ARG234 */ - -/* Define to the type of arg5 for select(). */ -/* #undef SELECT_TYPE_ARG5 */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if you can safely include both <sys/time.h> and <time.h>. */ -#define TIME_WITH_SYS_TIME 1 - -/* Define if the closedir function returns void instead of int. */ -/* #undef VOID_CLOSEDIR */ - -/* The number of bytes in a off_t. */ -#define SIZEOF_OFF_T 0 - -/* Define if you have the err function. */ -#define HAVE_ERR 1 - -/* Define if you have the fgetln function. */ -#define HAVE_FGETLN 1 - -/* Define if you have the flock function. */ -#define HAVE_FLOCK 1 - -/* Define if you have the fparseln function. */ -#define HAVE_FPARSELN 1 - -/* Define if you have the fts_open function. */ -#define HAVE_FTS_OPEN 1 - -/* Define if you have the getaddrinfo function. */ -#define HAVE_GETADDRINFO 1 - -/* Define if you have the getgrouplist function. */ -#define HAVE_GETGROUPLIST 1 - -/* Define if you have the getnameinfo function. */ -#define HAVE_GETNAMEINFO 1 - -/* Define if you have the getspnam function. */ -/* #undef HAVE_GETSPNAM */ - -/* Define if you have the getusershell function. */ -#define HAVE_GETUSERSHELL 1 - -/* Define if you have the inet_net_pton function. */ -#define HAVE_INET_NET_PTON 1 - -/* Define if you have the inet_ntop function. */ -#define HAVE_INET_NTOP 1 - -/* Define if you have the inet_pton function. */ -#define HAVE_INET_PTON 1 - -/* Define if you have the lockf function. */ -#define HAVE_LOCKF 1 - -/* Define if you have the mkstemp function. */ -#define HAVE_MKSTEMP 1 - -/* Define if you have the setlogin function. */ -#define HAVE_SETLOGIN 1 - -/* Define if you have the setproctitle function. */ -#define HAVE_SETPROCTITLE 1 - -/* Define if you have the sl_init function. */ -#define HAVE_SL_INIT 1 - -/* Define if you have the snprintf function. */ -#define HAVE_SNPRINTF 1 - -/* Define if you have the strdup function. */ -#define HAVE_STRDUP 1 - -/* Define if you have the strerror function. */ -#define HAVE_STRERROR 1 - -/* Define if you have the strlcat function. */ -#define HAVE_STRLCAT 1 - -/* Define if you have the strlcpy function. */ -#define HAVE_STRLCPY 1 - -/* Define if you have the strmode function. */ -#define HAVE_STRMODE 1 - -/* Define if you have the strsep function. */ -#define HAVE_STRSEP 1 - -/* Define if you have the strtoll function. */ -#define HAVE_STRTOLL 1 - -/* Define if you have the user_from_uid function. */ -#define HAVE_USER_FROM_UID 1 - -/* Define if you have the usleep function. */ -#define HAVE_USLEEP 1 - -/* Define if you have the vfork function. */ -#define HAVE_VFORK 1 - -/* Define if you have the vsyslog function. */ -#define HAVE_VSYSLOG 1 - -/* Define if you have the <arpa/nameser.h> header file. */ -#define HAVE_ARPA_NAMESER_H 1 - -/* Define if you have the <dirent.h> header file. */ -#define HAVE_DIRENT_H 1 - -/* Define if you have the <err.h> header file. */ -#define HAVE_ERR_H 1 - -/* Define if you have the <fts.h> header file. */ -#define HAVE_FTS_H 1 - -/* Define if you have the <libutil.h> header file. */ -#define HAVE_LIBUTIL_H 1 - -/* Define if you have the <ndir.h> header file. */ -/* #undef HAVE_NDIR_H */ - -/* Define if you have the <paths.h> header file. */ -#define HAVE_PATHS_H 1 - -/* Define if you have the <sys/dir.h> header file. */ -#define HAVE_SYS_DIR_H 1 - -/* Define if you have the <sys/ndir.h> header file. */ -/* #undef HAVE_SYS_NDIR_H */ - -/* Define if you have the <sys/sysmacros.h> header file. */ -/* #undef HAVE_SYS_SYSMACROS_H */ - -/* Define if you have the <util.h> header file. */ -/* #undef HAVE_UTIL_H */ - -/* Define if you have the crypt library (-lcrypt). */ -#define HAVE_LIBCRYPT 1 - -/* Define if you have the nsl library (-lnsl). */ -/* #undef HAVE_LIBNSL */ - -/* Define if you have the skey library (-lskey). */ -/* #undef HAVE_LIBSKEY */ - -/* Define if you have the socket library (-lsocket). */ -/* #undef HAVE_LIBSOCKET */ - -/* Define if you have the util library (-lutil). */ -#define HAVE_LIBUTIL 1 - -/* Define if your compiler supports `long long' */ -#define HAVE_LONG_LONG 1 - -/* Define if *printf() uses %qd to print `long long' (otherwise uses %lld) */ -#define HAVE_PRINTF_QD 1 - -/* Define if in_port_t exists */ -#define HAVE_IN_PORT_T 1 - -/* Define if struct sockaddr.sa_len exists (implies sockaddr_in.sin_len, etc) */ -#define HAVE_SOCKADDR_SA_LEN 1 - -/* Define if socklen_t exists */ -#define HAVE_SOCKLEN_T 1 - -/* Define if AF_INET6 exists in <sys/socket.h> */ -#define HAVE_AF_INET6 1 - -/* Define if `struct sockaddr_in6' exists in <netinet/in.h> */ -#define HAVE_SOCKADDR_IN6 1 - -/* Define if `struct addrinfo' exists in <netdb.h> */ -#define HAVE_ADDRINFO 1 - -/* - * Define if <netdb.h> contains AI_NUMERICHOST et al. - * Systems which only implement RFC2133 will need this. - */ -#define HAVE_RFC2553_NETDB 1 - -/* Define if `struct direct' has a d_namlen element */ -#define HAVE_D_NAMLEN 1 - -/* Define if struct passwd.pw_expire exists. */ -#define HAVE_PW_EXPIRE 1 - -/* Define if GLOB_BRACE, gl_path and gl_match exist in <glob.h> */ -#define HAVE_WORKING_GLOB 1 - -/* Define if crypt() is declared in <unistd.h> */ -#define HAVE_CRYPT_D 1 - -/* Define if fclose() is declared in <stdio.h> */ -#define HAVE_FCLOSE_D 1 - -/* Define if optarg is declared in <stdlib.h> or <unistd.h> */ -#define HAVE_OPTARG_D 1 - -/* Define if optind is declared in <stdlib.h> or <unistd.h> */ -#define HAVE_OPTIND_D 1 - -/* Define if optreset exists */ -#define HAVE_OPTRESET 1 - -/* Define if pclose() is declared in <stdio.h> */ -#define HAVE_PCLOSE_D 1 - -/* Define if getusershell() is declared in <unistd.h> */ -#define HAVE_GETUSERSHELL_D 1 - -/* Define if `long long' is supported and sizeof(off_t) >= 8 */ -#define HAVE_QUAD_SUPPORT 1 - -/* Define if not using in-built /bin/ls code */ -/* #undef NO_INTERNAL_LS */ - -/* Define if using S/Key */ -/* #undef SKEY */ - -/* - * Define this if compiling with SOCKS (the firewall traversal library). - * Also, you must define connect, getsockname, bind, accept, listen, and - * select to their R-versions. - */ -/* #undef SOCKS */ -/* #undef SOCKS4 */ -/* #undef SOCKS5 */ -/* #undef connect */ -/* #undef getsockname */ -/* #undef bind */ -/* #undef accept */ -/* #undef listen */ -/* #undef select */ -/* #undef dup */ -/* #undef dup2 */ -/* #undef fclose */ -/* #undef gethostbyname */ -/* #undef getpeername */ -/* #undef read */ -/* #undef recv */ -/* #undef recvfrom */ -/* #undef rresvport */ -/* #undef send */ -/* #undef sendto */ -/* #undef shutdown */ -/* #undef write */ - -/* Define if you have the <arpa/ftp.h> header file. */ -#define HAVE_FTP_NAMES 1 diff --git a/libexec/ftpd/extern.h b/libexec/ftpd/extern.h deleted file mode 100644 index 047e8573dd09..000000000000 --- a/libexec/ftpd/extern.h +++ /dev/null @@ -1,110 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -#include <sys/types.h> -#include <sys/socket.h> - -void blkfree(char **); -char **copyblk(char **); -void cwd(char *); -void delete(char *); -void dologout(int); -void fatalerror(char *); -void ftpd_logwtmp(char *, char *, struct sockaddr *addr); -int ftpd_pclose(FILE *); -FILE *ftpd_popen(char *, char *); -int get_line(char *, int, FILE *); -void lreply(int, const char *, ...) __printflike(2, 3); -void makedir(char *); -void nack(char *); -void pass(char *); -void passive(void); -void long_passive(char *, int); -void perror_reply(int, char *); -void pwd(void); -void removedir(char *); -void renamecmd(char *, char *); -char *renamefrom(char *); -void reply(int, const char *, ...) __printflike(2, 3); -void retrieve(char *, char *); -void send_file_list(char *); -void statcmd(void); -void statfilecmd(char *); -void store(char *, char *, int); -void upper(char *); -void user(char *); -void yyerror(char *); -int yyparse(void); -int ls_main(int, char **); - -extern int assumeutf8; -extern char cbuf[]; -extern union sockunion data_dest; -extern int epsvall; -extern int form; -extern int ftpdebug; -extern int guest; -extern union sockunion his_addr; -extern char *homedir; -extern int hostinfo; -extern char *hostname; -extern int maxtimeout; -extern int logged_in; -extern int logging; -extern int noepsv; -extern int noguestretr; -extern int noretr; -extern int paranoid; -extern struct passwd *pw; -extern int pdata; -extern char proctitle[]; -extern int readonly; -extern off_t restart_point; -extern int timeout; -extern char tmpline[]; -extern int type; -extern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ -extern int usedefault; - -struct sockaddr_in; -struct sockaddr_in6; -union sockunion { - struct sockinet { - u_char si_len; - u_char si_family; - u_short 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/libexec/ftpd/ftpchroot.5 b/libexec/ftpd/ftpchroot.5 deleted file mode 100644 index cb2f15f719ad..000000000000 --- a/libexec/ftpd/ftpchroot.5 +++ /dev/null @@ -1,118 +0,0 @@ -.\" Copyright (c) 2003 FreeBSD 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. -.\" -.\" 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. -.\" -.Dd January 26, 2003 -.Dt FTPCHROOT 5 -.Os -.Sh NAME -.Nm ftpchroot -.Nd "list users and groups subject to FTP access restrictions" -.Sh DESCRIPTION -The file -.Nm -is read by -.Xr ftpd 8 -at the beginning of an FTP session, after having authenticated the user. -Each line in -.Nm -corresponds to a user or group. -If a line in -.Nm -matches the current user or a group he is a member of, -access restrictions will be applied to this -session by changing its root directory with -.Xr chroot 2 -to that specified on the line or to the user's login directory. -.Pp -The order of records in -.Nm -is important because the first match will be used. -Fields on each line are separated by tabs or spaces. -.Pp -The first field specifies a user or group name. -If it is prefixed by an -.Dq at -sign, -.Ql @ , -it specifies a group name; -the line will match each user who is a member of this group. -As a special case, a single -.Ql @ -in this field will match any user. -A username is specified otherwise. -.Pp -The optional second field describes the directory for the user -or each member of the group to be locked up in using -.Xr chroot 2 . -Be it omitted, the user's login directory will be used. -If it is not an absolute pathname, then it will be relative -to the user's login directory. -If it contains the -.Pa /./ -separator, -.Xr ftpd 8 -will treat its left-hand side as the name of the directory to do -.Xr chroot 2 -to, and its right-hand side to change the current directory to afterwards. -.Sh FILES -.Bl -tag -width ".Pa /etc/ftpchroot" -compact -.It Pa /etc/ftpchroot -.El -.Sh EXAMPLES -These lines in -.Nm -will lock up the user -.Dq Li webuser -and each member of the group -.Dq Li hostee -in their respective login directories: -.Bd -literal -offset indent -webuser -@hostee -.Ed -.Pp -And this line will tell -.Xr ftpd 8 -to lock up the user -.Dq Li joe -in -.Pa /var/spool/ftp -and then to change the current directory to -.Pa /joe , -which is relative to the session's new root: -.Pp -.Dl "joe /var/spool/ftp/./joe" -.Pp -And finally the following line will lock up every user connecting -through FTP in his respective -.Pa ~/public_html , -thus lowering possible impact on the system -from intrinsic insecurity of FTP: -.Pp -.Dl "@ public_html" -.Sh SEE ALSO -.Xr chroot 2 , -.Xr group 5 , -.Xr passwd 5 , -.Xr ftpd 8 diff --git a/libexec/ftpd/ftpcmd.y b/libexec/ftpd/ftpcmd.y deleted file mode 100644 index c090130d8137..000000000000 --- a/libexec/ftpd/ftpcmd.y +++ /dev/null @@ -1,1806 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -/* - * 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 <libutil.h> -#include <limits.h> -#include <md5.h> -#include <netdb.h> -#include <pwd.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#include "extern.h" -#include "pathnames.h" - -#define yylex ftpcmd_yylex - -off_t restart_point; - -static int cmd_type; -static int cmd_form; -static int cmd_bytesz; -static int state; -char cbuf[512]; -char *fromname = NULL; - -%} - -%union { - struct { - off_t o; - int i; - } u; - char *s; -} - -%token - A B C E F I - L N P R S T - ALL - - SP CRLF COMMA - - USER PASS ACCT REIN QUIT PORT - PASV TYPE STRU MODE RETR STOR - APPE MLFL MAIL MSND MSOM MSAM - MRSQ MRCP ALLO REST RNFR RNTO - ABOR DELE CWD LIST NLST SITE - STAT HELP NOOP MKD RMD PWD - CDUP STOU SMNT SYST SIZE MDTM - LPRT LPSV EPRT EPSV FEAT - - UMASK IDLE CHMOD MDFIVE - - LEXERR NOTIMPL - -%token <s> STRING -%token <u> NUMBER - -%type <u.i> check_login octal_number byte_size -%type <u.i> check_login_ro check_login_epsv -%type <u.i> struct_code mode_code type_code form_code -%type <s> pathstring pathname password username -%type <s> ALL NOTIMPL - -%start cmd_list - -%% - -cmd_list - : /* empty */ - | cmd_list cmd - { - if (fromname) - free(fromname); - fromname = NULL; - restart_point = 0; - } - | cmd_list rcmd - ; - -cmd - : USER SP username CRLF - { - user($3); - free($3); - } - | PASS SP password CRLF - { - pass($3); - free($3); - } - | PASS CRLF - { - pass(""); - } - | PORT check_login SP host_port CRLF - { - if (epsvall) { - reply(501, "No PORT allowed after EPSV ALL."); - goto port_done; - } - if (!$2) - goto port_done; - if (port_check("PORT") == 1) - goto port_done; -#ifdef INET6 - if ((his_addr.su_family != AF_INET6 || - !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { - /* shoud never happen */ - usedefault = 1; - reply(500, "Invalid address rejected."); - goto port_done; - } - port_check_v6("pcmd"); -#endif - port_done: - ; - } - | LPRT check_login SP host_long_port CRLF - { - if (epsvall) { - reply(501, "No LPRT allowed after EPSV ALL."); - goto lprt_done; - } - if (!$2) - goto lprt_done; - if (port_check("LPRT") == 1) - goto lprt_done; -#ifdef INET6 - if (his_addr.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - goto lprt_done; - } - if (port_check_v6("LPRT") == 1) - goto lprt_done; -#endif - lprt_done: - ; - } - | EPRT check_login SP STRING CRLF - { - char delim; - char *tmp = NULL; - char *p, *q; - char *result[3]; - struct addrinfo hints; - struct addrinfo *res; - int i; - - if (epsvall) { - reply(501, "No EPRT allowed after EPSV ALL."); - goto eprt_done; - } - if (!$2) - goto eprt_done; - - memset(&data_dest, 0, sizeof(data_dest)); - tmp = strdup($4); - if (ftpdebug) - syslog(LOG_DEBUG, "%s", tmp); - if (!tmp) { - fatalerror("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) { - parsefail: - reply(500, - "Invalid argument, rejected."); - if (tmp) - free(tmp); - usedefault = 1; - goto eprt_done; - } - *q++ = '\0'; - result[i] = p; - if (ftpdebug) - syslog(LOG_DEBUG, "%d: %s", i, p); - p = q; - } - - /* some more sanity check */ - p = result[0]; - while (*p) { - if (!isdigit(*p)) - goto parsefail; - p++; - } - p = result[2]; - while (*p) { - if (!isdigit(*p)) - goto parsefail; - p++; - } - - /* grab address */ - memset(&hints, 0, sizeof(hints)); - if (atoi(result[0]) == 1) - hints.ai_family = PF_INET; -#ifdef INET6 - else if (atoi(result[0]) == 2) - hints.ai_family = PF_INET6; -#endif - else - hints.ai_family = PF_UNSPEC; /*XXX*/ - hints.ai_socktype = SOCK_STREAM; - i = getaddrinfo(result[1], result[2], &hints, &res); - if (i) - goto parsefail; - memcpy(&data_dest, res->ai_addr, res->ai_addrlen); -#ifdef INET6 - 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; - } -#endif - free(tmp); - tmp = NULL; - - if (port_check("EPRT") == 1) - goto eprt_done; -#ifdef INET6 - if (his_addr.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - goto eprt_done; - } - if (port_check_v6("EPRT") == 1) - goto eprt_done; -#endif - eprt_done: - free($4); - } - | PASV check_login CRLF - { - if (epsvall) - reply(501, "No PASV allowed after EPSV ALL."); - else if ($2) - passive(); - } - | LPSV check_login CRLF - { - if (epsvall) - reply(501, "No LPSV allowed after EPSV ALL."); - else if ($2) - long_passive("LPSV", PF_UNSPEC); - } - | EPSV check_login_epsv SP NUMBER CRLF - { - if ($2) { - int pf; - switch ($4.i) { - case 1: - pf = PF_INET; - break; -#ifdef INET6 - case 2: - pf = PF_INET6; - break; -#endif - default: - pf = -1; /*junk value*/ - break; - } - long_passive("EPSV", pf); - } - } - | EPSV check_login_epsv SP ALL CRLF - { - if ($2) { - reply(200, "EPSV ALL command successful."); - epsvall++; - } - } - | EPSV check_login_epsv 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 CHAR_BIT == 8 - if (cmd_bytesz == 8) { - reply(200, - "Type set to L (byte size 8)."); - type = cmd_type; - } else - reply(504, "Byte size must be 8."); -#else /* CHAR_BIT == 8 */ - UNIMPLEMENTED for CHAR_BIT != 8 -#endif /* CHAR_BIT == 8 */ - } - } - } - | STRU check_login SP struct_code CRLF - { - if ($2) { - switch ($4) { - - case STRU_F: - reply(200, "STRU F accepted."); - 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 accepted."); - 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 (noretr || (guest && noguestretr)) - reply(500, "RETR command disabled."); - else if ($2 && $4 != NULL) - retrieve(NULL, $4); - - if ($4 != NULL) - free($4); - } - | STOR check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - store($4, "w", 0); - if ($4 != NULL) - free($4); - } - | APPE check_login_ro 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 pathstring CRLF - { - if ($2) - send_file_list($4); - free($4); - } - | LIST check_login CRLF - { - if ($2) - retrieve(_PATH_LS " -lA", ""); - } - | LIST check_login SP pathstring CRLF - { - if ($2) - retrieve(_PATH_LS " -lA %s", $4); - 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_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - delete($4); - if ($4 != NULL) - free($4); - } - | RNTO check_login_ro 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(homedir); - } - } - | 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_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - makedir($4); - if ($4 != NULL) - free($4); - } - | RMD check_login_ro 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 MDFIVE check_login SP pathname CRLF - { - char p[64], *q; - - if ($4 && $6) { - q = MD5File($6, p); - if (q != NULL) - reply(200, "MD5(%s) = %s", $6, p); - else - perror_reply(550, $6); - } - if ($6) - free($6); - } - | SITE SP UMASK check_login CRLF - { - int oldmask; - - if ($4) { - oldmask = umask(0); - (void) umask(oldmask); - reply(200, "Current UMASK is %03o.", oldmask); - } - } - | SITE SP UMASK check_login SP octal_number CRLF - { - int oldmask; - - if ($4) { - if (($6 == -1) || ($6 > 0777)) { - reply(501, "Bad UMASK value."); - } else { - oldmask = umask($6); - reply(200, - "UMASK set to %03o (was %03o).", - $6, oldmask); - } - } - } - | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF - { - if ($4 && ($8 != NULL)) { - if (($6 == -1 ) || ($6 > 0777)) - reply(501, "Bad mode value."); - 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.i < 30 || $6.i > maxtimeout) { - reply(501, - "Maximum IDLE time must be between 30 and %d seconds.", - maxtimeout); - } else { - timeout = $6.i; - (void) alarm(timeout); - reply(200, - "Maximum IDLE time set to %d seconds.", - timeout); - } - } - } - | STOU check_login_ro SP pathname CRLF - { - if ($2 && $4 != NULL) - store($4, "w", 1); - if ($4 != NULL) - free($4); - } - | FEAT CRLF - { - lreply(211, "Extensions supported:"); -#if 0 - /* XXX these two keywords are non-standard */ - printf(" EPRT\r\n"); - if (!noepsv) - printf(" EPSV\r\n"); -#endif - printf(" MDTM\r\n"); - printf(" REST STREAM\r\n"); - printf(" SIZE\r\n"); - if (assumeutf8) { - /* TVFS requires UTF8, see RFC 3659 */ - printf(" TVFS\r\n"); - printf(" UTF8\r\n"); - } - reply(211, "End."); - } - | SYST check_login CRLF - { - if ($2) { - if (hostinfo) -#ifdef BSD - reply(215, "UNIX Type: L%d Version: BSD-%d", - CHAR_BIT, BSD); -#else /* BSD */ - reply(215, "UNIX Type: L%d", CHAR_BIT); -#endif /* BSD */ - else - reply(215, "UNKNOWN Type: L%d", CHAR_BIT); - } - } - - /* - * 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) - perror_reply(550, $4); - 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", - 1900 + 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); - } - | NOTIMPL - { - nack($1); - } - | error - { - yyclearin; /* discard lookahead data */ - yyerrok; /* clear error condition */ - state = CMD; /* reset lexer state */ - } - ; -rcmd - : RNFR check_login_ro SP pathname CRLF - { - restart_point = 0; - if ($2 && $4) { - if (fromname) - free(fromname); - fromname = NULL; - if (renamefrom($4)) - fromname = $4; - else - free($4); - } else if ($4) { - free($4); - } - } - | REST check_login SP NUMBER CRLF - { - if ($2) { - if (fromname) - free(fromname); - fromname = NULL; - restart_point = $4.o; - reply(350, "Restarting at %jd. %s", - (intmax_t)restart_point, - "Send STORE or RETRIEVE to initiate transfer."); - } - } - ; - -username - : STRING - ; - -password - : /* empty */ - { - $$ = (char *)calloc(1, sizeof(char)); - } - | STRING - ; - -byte_size - : NUMBER - { - $$ = $1.i; - } - ; - -host_port - : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA - NUMBER COMMA NUMBER - { - char *a, *p; - - data_dest.su_len = sizeof(struct sockaddr_in); - data_dest.su_family = AF_INET; - p = (char *)&data_dest.su_sin.sin_port; - p[0] = $9.i; p[1] = $11.i; - a = (char *)&data_dest.su_sin.sin_addr; - a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; - } - ; - -host_long_port - : 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; - - memset(&data_dest, 0, sizeof(data_dest)); - data_dest.su_len = sizeof(struct sockaddr_in6); - data_dest.su_family = AF_INET6; - p = (char *)&data_dest.su_port; - p[0] = $39.i; p[1] = $41.i; - a = (char *)&data_dest.su_sin6.sin6_addr; - a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; - a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; - a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; - a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; - 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; - } - if ($1.i != 6 || $3.i != 16 || $37.i != 2) - memset(&data_dest, 0, sizeof(data_dest)); - } - | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA - NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA - NUMBER - { - char *a, *p; - - memset(&data_dest, 0, sizeof(data_dest)); - 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.i; p[1] = $17.i; - a = (char *)&data_dest.su_sin.sin_addr; - a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; - if ($1.i != 4 || $3.i != 4 || $13.i != 2) - memset(&data_dest, 0, sizeof(data_dest)); - } - ; - -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 = CHAR_BIT; - } - | 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 - { - if (logged_in && $1) { - char *p; - - /* - * Expand ~user manually since glob(3) - * will return the unexpanded pathname - * if the corresponding file/directory - * doesn't exist yet. Using sole glob(3) - * would break natural commands like - * MKD ~user/newdir - * or - * RNTO ~/newfile - */ - if ((p = exptilde($1)) != NULL) { - $$ = expglob(p); - free(p); - } else - $$ = NULL; - 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.i; - 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 */ - { - $$ = check_login1(); - } - ; - -check_login_epsv - : /* empty */ - { - if (noepsv) { - reply(500, "EPSV command disabled."); - $$ = 0; - } - else - $$ = check_login1(); - } - ; - -check_login_ro - : /* empty */ - { - if (readonly) { - reply(550, "Permission denied."); - $$ = 0; - } - else - $$ = check_login1(); - } - ; - -%% - -#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 /* optional SP then optional STRING */ -#define ZSTR2 6 /* optional STRING after SP */ -#define SITECMD 7 /* SITE command */ -#define NSTR 8 /* Number followed by a string */ - -#define MAXGLOBARGS 1000 - -#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ - -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, b5" }, - { "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)" }, - { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, - { "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[] = { - { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, - { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, - { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, - { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, - { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, - { NULL, 0, 0, 0, 0 } -}; - -static char *copy(char *); -static char *expglob(char *); -static char *exptilde(char *); -static void help(struct tab *, char *); -static struct tab * - lookup(struct tab *, char *); -static int port_check(const char *); -#ifdef INET6 -static int port_check_v6(const char *); -#endif -static void sizecmd(char *); -static void toolong(int); -#ifdef INET6 -static void v4map_data_dest(void); -#endif -static int yylex(void); - -static struct tab * -lookup(struct tab *p, char *cmd) -{ - - for (; p->name != NULL; p++) - if (strcmp(cmd, p->name) == 0) - return (p); - return (0); -} - -#include <arpa/telnet.h> - -/* - * get_line - a hacked up version of fgets to ignore TELNET escape codes. - */ -int -get_line(char *s, int n, FILE *iop) -{ - int c; - register char *cs; - sigset_t sset, osset; - - 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 (ftpdebug) - syslog(LOG_DEBUG, "command: %s", s); - tmpline[0] = '\0'; - return(0); - } - if (c == 0) - tmpline[0] = '\0'; - } - /* SIGURG would interrupt stdio if not blocked during the read loop */ - sigemptyset(&sset); - sigaddset(&sset, SIGURG); - sigprocmask(SIG_BLOCK, &sset, &osset); - while ((c = getc(iop)) != EOF) { - c &= 0377; - if (c == IAC) { - if ((c = getc(iop)) == EOF) - goto got_eof; - c &= 0377; - switch (c) { - case WILL: - case WONT: - if ((c = getc(iop)) == EOF) - goto got_eof; - printf("%c%c%c", IAC, DONT, 0377&c); - (void) fflush(stdout); - continue; - case DO: - case DONT: - if ((c = getc(iop)) == EOF) - goto got_eof; - 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; - } -got_eof: - sigprocmask(SIG_SETMASK, &osset, NULL); - if (c == EOF && cs == s) - return (-1); - *cs++ = '\0'; - if (ftpdebug) { - if (!guest && strncasecmp("pass ", s, 5) == 0) { - /* Don't syslog passwords */ - syslog(LOG_DEBUG, "command: %.5s ???", s); - } else { - register char *cp; - register int len; - - /* Don't syslog trailing CR-LF */ - len = strlen(s); - cp = s + len - 1; - while (cp >= s && (*cp == '\n' || *cp == '\r')) { - --cp; - --len; - } - syslog(LOG_DEBUG, "command: %.*s", len, s); - } - } - return (0); -} - -static void -toolong(int signo) -{ - - reply(421, - "Timeout (%d seconds): closing control connection.", timeout); - if (logging) - syslog(LOG_INFO, "User %s timed out after %d seconds", - (pw ? pw -> pw_name : "unknown"), timeout); - dologout(1); -} - -static int -yylex(void) -{ - static int cpos; - char *cp, *cp2; - struct tab *p; - int n; - char c; - - for (;;) { - switch (state) { - - case CMD: - (void) signal(SIGALRM, toolong); - (void) alarm(timeout); - n = get_line(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."); - (void) alarm(0); - continue; - } - (void) alarm(0); -#ifdef SETPROCTITLE - if (strncasecmp(cbuf, "PASS", 4) != 0) - setproctitle("%s: %s", proctitle, cbuf); -#endif /* SETPROCTITLE */ - if ((cp = strchr(cbuf, '\r'))) { - *cp++ = '\n'; - *cp = '\0'; - } - if ((cp = strpbrk(cbuf, " \n"))) - cpos = cp - cbuf; - if (cpos == 0) - cpos = 4; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - upper(cbuf); - p = lookup(cmdtab, cbuf); - cbuf[cpos] = c; - if (p != 0) { - yylval.s = p->name; - if (!p->implemented) - return (NOTIMPL); /* state remains CMD */ - state = p->state; - 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 (guest == 0 && p != 0) { - yylval.s = p->name; - if (!p->implemented) { - state = CMD; - return (NOTIMPL); - } - state = p->state; - return (p->token); - } - state = CMD; - break; - - case ZSTR1: - case OSTR: - if (cbuf[cpos] == '\n') { - state = CMD; - return (CRLF); - } - /* FALLTHROUGH */ - - case STR1: - 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 = copy(cp); - cbuf[cpos] = '\n'; - state = ARGS; - return (STRING); - } - break; - - case NSTR: - if (cbuf[cpos] == ' ') { - cpos++; - return (SP); - } - if (isdigit(cbuf[cpos])) { - cp = &cbuf[cpos]; - while (isdigit(cbuf[++cpos])) - ; - c = cbuf[cpos]; - cbuf[cpos] = '\0'; - yylval.u.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.u.i = atoi(cp); - yylval.u.o = strtoull(cp, NULL, 10); - 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: - fatalerror("Unknown state in scanner."); - } - state = CMD; - return (LEXERR); - } -} - -void -upper(char *s) -{ - while (*s != '\0') { - if (islower(*s)) - *s = toupper(*s); - s++; - } -} - -static char * -copy(char *s) -{ - char *p; - - p = malloc(strlen(s) + 1); - if (p == NULL) - fatalerror("Ran out of memory."); - (void) strcpy(p, s); - return (p); -} - -static void -help(struct tab *ctab, char *s) -{ - struct tab *c; - int width, NCMDS; - char *type; - - if (ctab == sitetab) - type = "SITE "; - else - type = ""; - width = 0, NCMDS = 0; - for (c = ctab; c->name != NULL; c++) { - int len = strlen(c->name); - - if (len > width) - width = len; - NCMDS++; - } - width = (width + 8) &~ 7; - if (s == 0) { - int i, j, w; - int columns, lines; - - lreply(214, "The following %scommands are recognized %s.", - type, "(* =>'s unimplemented)"); - columns = 76 / width; - if (columns == 0) - columns = 1; - lines = (NCMDS + columns - 1) / columns; - for (i = 0; i < lines; i++) { - printf(" "); - for (j = 0; j < columns; j++) { - c = ctab + j * lines + i; - printf("%s%c", c->name, - c->implemented ? ' ' : '*'); - if (c + lines >= &ctab[NCMDS]) - break; - w = strlen(c->name) + 1; - while (w < width) { - putchar(' '); - w++; - } - } - printf("\r\n"); - } - (void) fflush(stdout); - if (hostinfo) - reply(214, "Direct comments to ftp-bugs@%s.", hostname); - else - reply(214, "End."); - 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(char *filename) -{ - switch (type) { - case TYPE_L: - case TYPE_I: { - struct stat stbuf; - if (stat(filename, &stbuf) < 0) - perror_reply(550, filename); - else if (!S_ISREG(stbuf.st_mode)) - reply(550, "%s: not a plain file.", filename); - else - reply(213, "%jd", (intmax_t)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) { - perror_reply(550, filename); - (void) fclose(fin); - return; - } else if (!S_ISREG(stbuf.st_mode)) { - reply(550, "%s: not a plain file.", filename); - (void) fclose(fin); - return; - } else if (stbuf.st_size > MAXASIZE) { - reply(550, "%s: too large for type A 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, "%jd", (intmax_t)count); - break; } - default: - reply(504, "SIZE not implemented for type %s.", - typenames[type]); - } -} - -/* Return 1, if port check is done. Return 0, if not yet. */ -static int -port_check(const char *pcmd) -{ - if (his_addr.su_family == AF_INET) { - if (data_dest.su_family != AF_INET) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return 1; - } - if (paranoid && - ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || - 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 range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "%s command successful.", pcmd); - } - return 1; - } - return 0; -} - -static int -check_login1(void) -{ - if (logged_in) - return 1; - else { - reply(530, "Please login with USER and PASS."); - return 0; - } -} - -/* - * Replace leading "~user" in a pathname by the user's login directory. - * Returned string will be in a freshly malloced buffer unless it's NULL. - */ -static char * -exptilde(char *s) -{ - char *p, *q; - char *path, *user; - struct passwd *ppw; - - if ((p = strdup(s)) == NULL) - return (NULL); - if (*p != '~') - return (p); - - user = p + 1; /* skip tilde */ - if ((path = strchr(p, '/')) != NULL) - *(path++) = '\0'; /* separate ~user from the rest of path */ - if (*user == '\0') /* no user specified, use the current user */ - user = pw->pw_name; - /* read passwd even for the current user since we may be chrooted */ - if ((ppw = getpwnam(user)) != NULL) { - /* user found, substitute login directory for ~user */ - if (path) - asprintf(&q, "%s/%s", ppw->pw_dir, path); - else - q = strdup(ppw->pw_dir); - free(p); - p = q; - } else { - /* user not found, undo the damage */ - if (path) - path[-1] = '/'; - } - return (p); -} - -/* - * Expand glob(3) patterns possibly present in a pathname. - * Avoid expanding to a pathname including '\r' or '\n' in order to - * not disrupt the FTP protocol. - * The expansion found must be unique. - * Return the result as a malloced string, or NULL if an error occurred. - * - * 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. - */ -static char * -expglob(char *s) -{ - char *p, **pp, *rval; - int flags = GLOB_BRACE | GLOB_NOCHECK; - int n; - glob_t gl; - - memset(&gl, 0, sizeof(gl)); - flags |= GLOB_LIMIT; - gl.gl_matchc = MAXGLOBARGS; - if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { - for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) - if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { - p = *pp; - n++; - } - if (n == 0) - rval = strdup(s); - else if (n == 1) - rval = strdup(p); - else { - reply(550, "Wildcard is ambiguous."); - rval = NULL; - } - } else { - reply(550, "Wildcard expansion error."); - rval = NULL; - } - globfree(&gl); - return (rval); -} - -#ifdef INET6 -/* Return 1, if port check is done. Return 0, if not yet. */ -static int -port_check_v6(const char *pcmd) -{ - if (his_addr.su_family == AF_INET6) { - if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) - /* Convert data_dest into v4 mapped sockaddr.*/ - v4map_data_dest(); - if (data_dest.su_family != AF_INET6) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return 1; - } - if (paranoid && - ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || - memcmp(&data_dest.su_sin6.sin6_addr, - &his_addr.su_sin6.sin6_addr, - sizeof(data_dest.su_sin6.sin6_addr)))) { - usedefault = 1; - reply(500, "Illegal PORT range rejected."); - } else { - usedefault = 0; - if (pdata >= 0) { - (void) close(pdata); - pdata = -1; - } - reply(200, "%s command successful.", pcmd); - } - return 1; - } - return 0; -} - -static void -v4map_data_dest(void) -{ - struct in_addr savedaddr; - int savedport; - - if (data_dest.su_family != AF_INET) { - usedefault = 1; - reply(500, "Invalid address rejected."); - return; - } - - savedaddr = data_dest.su_sin.sin_addr; - savedport = data_dest.su_port; - - memset(&data_dest, 0, sizeof(data_dest)); - data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); - data_dest.su_sin6.sin6_family = AF_INET6; - data_dest.su_sin6.sin6_port = savedport; - memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); - memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], - (caddr_t)&savedaddr, sizeof(savedaddr)); -} -#endif diff --git a/libexec/ftpd/ftpd.8 b/libexec/ftpd/ftpd.8 deleted file mode 100644 index 96db4753209e..000000000000 --- a/libexec/ftpd/ftpd.8 +++ /dev/null @@ -1,589 +0,0 @@ -.\" 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. -.\" -.Dd June 26, 2025 -.Dt FTPD 8 -.Os -.Sh NAME -.Nm ftpd -.Nd Internet File Transfer Protocol server -.Sh SYNOPSIS -.Nm -.Op Fl 468BDdEhMmOoRrSUvW -.Bq Fl A | Fl n -.Op Fl l Op Fl l -.Op Fl a Ar address -.Op Fl P Ar port -.Op Fl p Ar file -.Op Fl T Ar maxtimeout -.Op Fl t Ar timeout -.Op Fl u Ar umask -.Sh DEPRECATION NOTICE -The -.Fx -base system -.Nm -is deprecated, and will be removed in -.Fx 15.0 . -Users are advised to install the -.Pa ftp/freebsd-ftpd -port or package instead. -.Sh DESCRIPTION -The -.Nm -utility is the -Internet File Transfer Protocol -server process. -The server uses the TCP protocol -and listens at the port specified with the -.Fl P -option or in the -.Dq ftp -service specification; see -.Xr services 5 . -.Pp -Available options: -.Bl -tag -width indent -.It Fl 4 -When -.Fl D -is specified, accept connections via -.Dv AF_INET -socket. -.It Fl 6 -When -.Fl D -is specified, accept connections via -.Dv AF_INET6 -socket. -.It Fl 8 -Enable transparent UTF-8 mode. -RFC\ 2640 compliant clients will be told that the character encoding -used by the server is UTF-8, which is the only effect of the option. -.Pp -This option does not enable any encoding conversion for server file names; -it implies instead that the names of files on the server are encoded -in UTF-8. -As for files uploaded via FTP, it is the duty of the RFC\ 2640 compliant -client to convert their names from the client's local encoding to UTF-8. -FTP command names and own -.Nm -messages are always encoded in ASCII, which is a subset of UTF-8. -Hence no need for server-side conversion at all. -.It Fl A -Allow only anonymous ftp access. -.It Fl a -When -.Fl D -is specified, accept connections only on the specified -.Ar address . -.It Fl B -With this option set, -.Nm -sends authentication success and failure messages to the -.Xr blacklistd 8 -daemon. -If this option is not specified, no communcation with the -.Xr blacklistd 8 -daemon is attempted. -.It Fl D -With this option set, -.Nm -will detach and become a daemon, accepting connections on the FTP port and -forking children processes to handle them. -This is 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 E -Disable the EPSV command. -This is useful for servers behind older firewalls. -.It Fl h -Disable printing host-specific information, such as the -server software version or hostname, in server messages. -.It Fl l -Each successful and failed -.Xr ftp 1 -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. -By default, -.Xr syslogd 8 -logs these to -.Pa /var/log/xferlog . -.It Fl M -Prevent anonymous users from creating directories. -.It Fl m -Permit anonymous users to overwrite or modify -existing files if allowed by file system permissions. -By default, anonymous users cannot modify existing files; -in particular, files to upload will be created under a unique name. -.It Fl n -Disable anonymous FTP access. -The -.Fl n -option is mutually exclusive with the -.Fl A -option. -.It Fl O -Put server in write-only mode for anonymous users only. -RETR is disabled for anonymous users, preventing anonymous downloads. -This has no effect if -.Fl o -is also specified. -.It Fl o -Put server in write-only mode. -RETR is disabled, preventing downloads. -.It Fl P -When -.Fl D -is specified, accept connections at -.Ar port , -specified as a numeric value or service name, instead of at the default -.Dq ftp -port. -.It Fl p -When -.Fl D -is specified, write the daemon's process ID to -.Ar file -instead of the default pid file, -.Pa /var/run/ftpd.pid . -.It Fl R -With this option set, -.Nm -will revert to historical behavior with regard to security checks on -user operations and restrictions on PORT requests. -Currently, -.Nm -will only honor PORT commands directed to unprivileged ports on the -remote user's host (which violates the FTP protocol specification but -closes some security holes). -.It Fl r -Put server in read-only mode. -All commands which may modify the local file system are disabled. -.It Fl S -With this option set, -.Nm -logs all anonymous file downloads to the file -.Pa /var/log/ftpd -when this file exists. -.It Fl T -A client may also request a different timeout period; -the maximum period allowed may be set to -.Ar timeout -seconds with the -.Fl T -option. -The default limit is 2 hours. -.It Fl t -The inactivity timeout period is set to -.Ar timeout -seconds (the default is 15 minutes). -.It Fl U -This option instructs ftpd to use data ports in the range of -.Dv IP_PORTRANGE_DEFAULT -instead of in the range of -.Dv IP_PORTRANGE_HIGH . -Such a change may be useful for some specific firewall configurations; -see -.Xr ip 4 -for more information. -.Pp -Note that option is a virtual no-op in -.Fx 5.0 -and above; both port -ranges are identical by default. -.It Fl u -The default file creation mode mask is set to -.Ar umask , -which is expected to be an octal numeric value. -Refer to -.Xr umask 2 -for details. -This option may be overridden by -.Xr login.conf 5 . -.It Fl v -A synonym for -.Fl d . -.It Fl W -Do not log FTP sessions to the user accounting database. -.El -.Pp -The file -.Pa /var/run/nologin -can be used to disable ftp access. -If the file exists, -.Nm -displays it and exits. -If the file -.Pa /etc/ftpwelcome -exists, -.Nm -prints it before issuing the -.Dq ready -message. -If the file -.Pa /etc/ftpmotd -exists, -.Nm -prints it after a successful login. -Note the motd file used is the one -relative to the login environment. -This means the one in -.Pa ~ftp/etc -in the anonymous user's case. -.Pp -The ftp server currently supports the following ftp requests. -The case of the requests is ignored. -Requests marked [RW] are -disabled if -.Fl r -is specified. -.Bl -column "Request" -offset indent -.It Sy Request Ta Sy "Description" -.It ABOR Ta "abort previous command" -.It ACCT Ta "specify account (ignored)" -.It ALLO Ta "allocate storage (vacuously)" -.It APPE Ta "append to a file [RW]" -.It CDUP Ta "change to parent of current working directory" -.It CWD Ta "change working directory" -.It DELE Ta "delete a file [RW]" -.It EPRT Ta "specify data connection port, multiprotocol" -.It EPSV Ta "prepare for server-to-server transfer, multiprotocol" -.It FEAT Ta "give information on extended features of server" -.It HELP Ta "give help information" -.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lA" -.It LPRT Ta "specify data connection port, multiprotocol" -.It LPSV Ta "prepare for server-to-server transfer, multiprotocol" -.It MDTM Ta "show last modification time of file" -.It MKD Ta "make a directory [RW]" -.It MODE Ta "specify data transfer" Em mode -.It NLST Ta "give name list of files in directory" -.It NOOP Ta "do nothing" -.It PASS Ta "specify password" -.It PASV Ta "prepare for server-to-server transfer" -.It PORT Ta "specify data connection port" -.It PWD Ta "print the current working directory" -.It QUIT Ta "terminate session" -.It REST Ta "restart incomplete transfer" -.It RETR Ta "retrieve a file" -.It RMD Ta "remove a directory [RW]" -.It RNFR Ta "specify rename-from file name [RW]" -.It RNTO Ta "specify rename-to file name [RW]" -.It SITE Ta "non-standard commands (see next section)" -.It SIZE Ta "return size of file" -.It STAT Ta "return status of server" -.It STOR Ta "store a file [RW]" -.It STOU Ta "store a file with a unique name [RW]" -.It STRU Ta "specify data transfer" Em structure -.It SYST Ta "show operating system type of server system" -.It TYPE Ta "specify data transfer" Em type -.It USER Ta "specify user name" -.It XCUP Ta "change to parent of current working directory (deprecated)" -.It XCWD Ta "change working directory (deprecated)" -.It XMKD Ta "make a directory (deprecated) [RW]" -.It XPWD Ta "print the current working directory (deprecated)" -.It XRMD Ta "remove a directory (deprecated) [RW]" -.El -.Pp -The following non-standard or -.Ux -specific commands are supported -by the -SITE request. -.Bl -column Request -offset indent -.It Sy Request Ta Sy Description -.It UMASK Ta change umask, e.g. ``SITE UMASK 002'' -.It IDLE Ta set idle-timer, e.g. ``SITE IDLE 60'' -.It CHMOD Ta "change mode of a file [RW], e.g. ``SITE CHMOD 755 filename''" -.It MD5 Ta "report the files MD5 checksum, e.g. ``SITE MD5 filename''" -.It HELP Ta give help information -.El -.Pp -Note: SITE requests are disabled in case of anonymous logins. -.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. -To avoid possible denial-of-service attacks, SIZE requests against -files larger than 10240 bytes will be denied if the current transfer -type is ASCII. -.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 -The -.Nm -utility interprets file names according to the -.Dq globbing -conventions used by -.Xr csh 1 . -This allows users to utilize the metacharacters -.Dq Li \&*?[]{}~ . -.Pp -The -.Nm -utility authenticates users according to six rules. -.Bl -enum -offset indent -.It -The login name must be in the password data base -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 login name must not be a member of a group specified in the file -.Pa /etc/ftpusers . -Entries in this file interpreted as group names are prefixed by an "at" -.Ql \&@ -sign. -.It -The user must have a standard shell returned by -.Xr getusershell 3 . -.It -If the user name appears in the file -.Pa /etc/ftpchroot , -or the user is a member of a group with a group entry in this file, -i.e., one prefixed with -.Ql \&@ , -the session's root will be changed to the directory specified -in this file or to the user's login directory by -.Xr chroot 2 -as for an -.Dq anonymous -or -.Dq ftp -account (see next item). -See -.Xr ftpchroot 5 -for a detailed description of the format of this file. -This facility may also be triggered by enabling the boolean "ftp-chroot" -capability in -.Xr login.conf 5 . -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). -When the -.Fl S -option is set, all transfers are logged as well. -.El -.Pp -In the last case, -.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. -As a special case if the -.Dq ftp -user's home directory pathname contains the -.Pa /./ -separator, -.Nm -uses its left-hand side as the name of the directory to do -.Xr chroot 2 -to, and its right-hand side to change the current directory to afterwards. -A typical example for this case would be -.Pa /var/spool/ftp/./pub . -In order that system security is not breached, it is recommended -that the -.Dq ftp -subtree be constructed with care, following these rules: -.Bl -tag -width "~ftp/pub" -offset indent -.It Pa ~ftp -Make the home directory owned by -.Dq root -and unwritable by anyone. -.It Pa ~ftp/etc -Make this directory owned by -.Dq root -and unwritable by anyone (mode 555). -The files pwd.db (see -.Xr passwd 5 ) -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 -.Xr passwd 5 -is not used, and should not contain real passwords. -The file -.Pa ftpmotd , -if present, will be printed after a successful login. -These files should be mode 444. -.It Pa ~ftp/pub -This directory and the subdirectories beneath it should be owned -by the users and groups responsible for placing files in them, -and be writable only by them (mode 755 or 775). -They should -.Em not -be owned or writable by -.Dq ftp -or its group, otherwise guest users -can fill the drive with unwanted files. -.El -.Pp -If the system has multiple IP addresses, -.Nm -supports the idea of virtual hosts, which provides the ability to -define multiple anonymous ftp areas, each one allocated to a different -internet address. -The file -.Pa /etc/ftphosts -contains information pertaining to each of the virtual hosts. -Each host is defined on its own line which contains a number of -fields separated by whitespace: -.Bl -tag -offset indent -width hostname -.It hostname -Contains the hostname or IP address of the virtual host. -.It user -Contains a user record in the system password file. -As with normal anonymous ftp, this user's access uid, gid and group -memberships determine file access to the anonymous ftp area. -The anonymous ftp area (to which any user is chrooted on login) -is determined by the home directory defined for the account. -User id and group for any ftp account may be the same as for the -standard ftp user. -.It statfile -File to which all file transfers are logged, which -defaults to -.Pa /var/log/ftpd . -.It welcome -This file is the welcome message displayed before the server ready -prompt. -It defaults to -.Pa /etc/ftpwelcome . -.It motd -This file is displayed after the user logs in. -It defaults to -.Pa /etc/ftpmotd . -.El -.Pp -Lines beginning with a '#' are ignored and can be used to include -comments. -.Pp -Defining a virtual host for the primary IP address or hostname -changes the default for ftp logins to that address. -The 'user', 'statfile', 'welcome' and 'motd' fields may be left -blank, or a single hyphen '-' used to indicate that the default -value is to be used. -.Pp -As with any anonymous login configuration, due care must be given -to setup and maintenance to guard against security related problems. -.Pp -The -.Nm -utility has internal support for handling remote requests to list -files, and will not execute -.Pa /bin/ls -in either a chrooted or non-chrooted environment. -The -.Pa ~/bin/ls -executable need not be placed into the chrooted tree, nor need the -.Pa ~/bin -directory exist. -.Sh FILES -.Bl -tag -width ".Pa /var/run/ftpd.pid" -compact -.It Pa /etc/ftpusers -List of unwelcome/restricted users. -.It Pa /etc/ftpchroot -List of normal users who should be chroot'd. -.It Pa /etc/ftphosts -Virtual hosting configuration file. -.It Pa /etc/ftpwelcome -Welcome notice. -.It Pa /etc/ftpmotd -Welcome notice after login. -.It Pa /var/run/ftpd.pid -Default pid file for daemon mode. -.It Pa /var/run/nologin -Displayed and access refused. -.It Pa /var/log/ftpd -Log file for anonymous transfers. -.It Pa /var/log/xferlog -Default place for session logs. -.It Pa /var/spool/ftp -Recommended directory for the FTP root directory -(the home directory of the ftp user). -.El -.Sh SEE ALSO -.Xr ftp 1 , -.Xr umask 2 , -.Xr getusershell 3 , -.Xr ftpchroot 5 , -.Xr login.conf 5 , -.Xr inetd 8 , -.Xr syslogd 8 -.Sh HISTORY -The -.Nm -utility appeared in -.Bx 4.2 . -IPv6 support was added in WIDE Hydrangea IPv6 stack kit. -.Sh BUGS -The server must run as the super-user -to create sockets with privileged port numbers. -It maintains -an effective user id of the logged in user, reverting to -the super-user only when binding addresses to sockets. -The -possible security holes have been extensively -scrutinized, but are possibly incomplete. diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c deleted file mode 100644 index 751d77b218b7..000000000000 --- a/libexec/ftpd/ftpd.c +++ /dev/null @@ -1,3446 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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/ioctl.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.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 <ctype.h> -#include <dirent.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <glob.h> -#include <limits.h> -#include <netdb.h> -#include <pwd.h> -#include <grp.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> -#include <libutil.h> -#ifdef LOGIN_CAP -#include <login_cap.h> -#endif - -#ifdef USE_PAM -#include <security/pam_appl.h> -#endif - -#include "blacklist_client.h" -#include "pathnames.h" -#include "extern.h" - -#include <stdarg.h> - -static char version[] = "Version 6.00LS"; -#undef main - -union sockunion ctrl_addr; -union sockunion data_source; -union sockunion data_dest; -union sockunion his_addr; -union sockunion pasv_addr; - -int daemon_mode; -int data; -int dataport; -int hostinfo = 1; /* print host-specific info in messages */ -int logged_in; -struct passwd *pw; -char *homedir; -int ftpdebug; -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 restricted_data_ports = 1; -int paranoid = 1; /* be extra careful about security */ -int anon_only = 0; /* Only anonymous ftp allowed */ -int noanon = 0; /* disable anonymous ftp */ -int assumeutf8 = 0; /* Assume that server file names are in UTF-8 */ -int guest; -int dochroot; -char *chrootdir; -int dowtmp = 1; -int stats; -int statfd = -1; -int type; -int form; -int stru; /* avoid C keyword */ -int mode; -int usedefault = 1; /* for data transfers */ -int pdata = -1; /* for passive mode */ -int readonly = 0; /* Server is in readonly mode. */ -int noepsv = 0; /* EPSV command is disabled. */ -int noretr = 0; /* RETR command is disabled. */ -int noguestretr = 0; /* RETR command is disabled for anon users. */ -int noguestmkd = 0; /* MKD command is disabled for anon users. */ -int noguestmod = 1; /* anon users may not modify existing files. */ -int use_blacklist = 0; - -off_t file_size; -off_t byte_count; -#if !defined(CMASK) || CMASK == 0 -#undef CMASK -#define CMASK 027 -#endif -int defumask = CMASK; /* default umask value */ -char tmpline[7]; -char *hostname; -int epsvall = 0; - -#ifdef VIRTUAL_HOSTING -char *ftpuser; - -static struct ftphost { - struct ftphost *next; - struct addrinfo *hostinfo; - char *hostname; - char *anonuser; - char *statfile; - char *welcome; - char *loginmsg; -} *thishost, *firsthost; - -#endif -char remotehost[NI_MAXHOST]; -char *ident = NULL; - -static char wtmpid[20]; - -#ifdef USE_PAM -static int auth_pam(struct passwd**, const char*); -pam_handle_t *pamh = NULL; -#endif - -char *pid_file = NULL; /* means default location to pidfile(3) */ - -/* - * Limit number of pathnames that glob can return. - * A limit of 0 indicates the number of pathnames is unlimited. - */ -#define MAXGLOBARGS 16384 -# - -/* - * Timeout intervals for retrying connections - * to hosts that don't accept PORT cmds. This - * is a kludge, but given the problems with TCP... - */ -#define SWAITMAX 90 /* wait at most 90 seconds */ -#define SWAITINT 5 /* interval between retries */ - -int swaitmax = SWAITMAX; -int swaitint = SWAITINT; - -#ifdef SETPROCTITLE -char proctitle[LINE_MAX]; /* initial part of title */ -#endif /* SETPROCTITLE */ - -#define LOGCMD(cmd, file) logcmd((cmd), (file), NULL, -1) -#define LOGCMD2(cmd, file1, file2) logcmd((cmd), (file1), (file2), -1) -#define LOGBYTES(cmd, file, cnt) logcmd((cmd), (file), NULL, (cnt)) - -static volatile sig_atomic_t recvurg; -static int transflag; /* NB: for debugging only */ - -#define STARTXFER flagxfer(1) -#define ENDXFER flagxfer(0) - -#define START_UNSAFE maskurg(1) -#define END_UNSAFE maskurg(0) - -/* It's OK to put an `else' clause after this macro. */ -#define CHECKOOB(action) \ - if (recvurg) { \ - recvurg = 0; \ - if (myoob()) { \ - ENDXFER; \ - action; \ - } \ - } - -#ifdef VIRTUAL_HOSTING -static void inithosts(int); -static void selecthost(union sockunion *); -#endif -static void ack(char *); -static void sigurg(int); -static void maskurg(int); -static void flagxfer(int); -static int myoob(void); -static int checkuser(char *, char *, int, char **, int *); -static FILE *dataconn(char *, off_t, char *); -static void dolog(struct sockaddr *); -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 int send_data(FILE *, FILE *, size_t, off_t, int); -static struct passwd * - sgetpwnam(char *); -static char *sgetsave(char *); -static void reapchild(int); -static void appendf(char **, char *, ...) __printflike(2, 3); -static void logcmd(char *, char *, char *, off_t); -static void logxfer(char *, off_t, time_t); -static char *doublequote(char *); -static int *socksetup(int, char *, const char *); - -int -main(int argc, char *argv[], char **envp) -{ - socklen_t addrlen; - int ch, on = 1, tos, s = STDIN_FILENO; - char *cp, line[LINE_MAX]; - FILE *fd; - char *bindname = NULL; - const char *bindport = "ftp"; - int family = AF_UNSPEC; - struct sigaction sa; - - tzset(); /* in case no timezone database in ~ftp */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - - /* - * Prevent diagnostic messages from appearing on stderr. - * We run as a daemon or from inetd; in both cases, there's - * more reason in logging to syslog. - */ - (void) freopen(_PATH_DEVNULL, "w", stderr); - opterr = 0; - - /* - * 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); - - while ((ch = getopt(argc, argv, - "468a:ABdDEhlmMnoOp:P:rRSt:T:u:UvW")) != -1) { - switch (ch) { - case '4': - family = (family == AF_INET6) ? AF_UNSPEC : AF_INET; - break; - - case '6': - family = (family == AF_INET) ? AF_UNSPEC : AF_INET6; - break; - - case '8': - assumeutf8 = 1; - break; - - case 'a': - bindname = optarg; - break; - - case 'A': - anon_only = 1; - break; - - case 'B': -#ifdef USE_BLACKLIST - use_blacklist = 1; -#else - syslog(LOG_WARNING, "not compiled with USE_BLACKLIST support"); -#endif - break; - - case 'd': - ftpdebug++; - break; - - case 'D': - daemon_mode++; - break; - - case 'E': - noepsv = 1; - break; - - case 'h': - hostinfo = 0; - break; - - case 'l': - logging++; /* > 1 == extra logging */ - break; - - case 'm': - noguestmod = 0; - break; - - case 'M': - noguestmkd = 1; - break; - - case 'n': - noanon = 1; - break; - - case 'o': - noretr = 1; - break; - - case 'O': - noguestretr = 1; - break; - - case 'p': - pid_file = optarg; - break; - - case 'P': - bindport = optarg; - break; - - case 'r': - readonly = 1; - break; - - case 'R': - paranoid = 0; - break; - - case 'S': - stats++; - break; - - case 't': - timeout = atoi(optarg); - if (maxtimeout < timeout) - maxtimeout = timeout; - break; - - case 'T': - maxtimeout = atoi(optarg); - if (timeout > maxtimeout) - timeout = maxtimeout; - break; - - case 'u': - { - long val = 0; - - val = strtol(optarg, &optarg, 8); - if (*optarg != '\0' || val < 0) - syslog(LOG_WARNING, "bad value for -u"); - else - defumask = val; - break; - } - case 'U': - restricted_data_ports = 0; - break; - - case 'v': - ftpdebug++; - break; - - case 'W': - dowtmp = 0; - break; - - default: - syslog(LOG_WARNING, "unknown flag -%c ignored", optopt); - break; - } - } - - if (noanon && anon_only) { - syslog(LOG_ERR, "-n and -A are mutually exclusive"); - exit(1); - } - - /* handle filesize limit gracefully */ - sa.sa_handler = SIG_IGN; - (void)sigaction(SIGXFSZ, &sa, NULL); - - if (daemon_mode) { - int *ctl_sock, fd, maxfd = -1, nfds, i; - fd_set defreadfds, readfds; - pid_t pid; - struct pidfh *pfh; - - if ((pfh = pidfile_open(pid_file, 0600, &pid)) == NULL) { - if (errno == EEXIST) { - syslog(LOG_ERR, "%s already running, pid %d", - getprogname(), (int)pid); - exit(1); - } - syslog(LOG_WARNING, "pidfile_open: %m"); - } - - /* - * Detach from parent. - */ - if (daemon(1, 1) < 0) { - syslog(LOG_ERR, "failed to become a daemon"); - exit(1); - } - - if (pfh != NULL && pidfile_write(pfh) == -1) - syslog(LOG_WARNING, "pidfile_write: %m"); - - sa.sa_handler = reapchild; - (void)sigaction(SIGCHLD, &sa, NULL); - -#ifdef VIRTUAL_HOSTING - inithosts(family); -#endif - - /* - * Open a socket, bind it to the FTP port, and start - * listening. - */ - ctl_sock = socksetup(family, bindname, bindport); - if (ctl_sock == NULL) - exit(1); - - FD_ZERO(&defreadfds); - for (i = 1; i <= *ctl_sock; i++) { - FD_SET(ctl_sock[i], &defreadfds); - if (listen(ctl_sock[i], 32) < 0) { - syslog(LOG_ERR, "control listen: %m"); - exit(1); - } - if (maxfd < ctl_sock[i]) - maxfd = ctl_sock[i]; - } - - /* - * Loop forever accepting connection requests and forking off - * children to handle them. - */ - while (1) { - FD_COPY(&defreadfds, &readfds); - nfds = select(maxfd + 1, &readfds, NULL, NULL, 0); - if (nfds <= 0) { - if (nfds < 0 && errno != EINTR) - syslog(LOG_WARNING, "select: %m"); - continue; - } - - pid = -1; - for (i = 1; i <= *ctl_sock; i++) - if (FD_ISSET(ctl_sock[i], &readfds)) { - addrlen = sizeof(his_addr); - fd = accept(ctl_sock[i], - (struct sockaddr *)&his_addr, - &addrlen); - if (fd == -1) { - syslog(LOG_WARNING, - "accept: %m"); - continue; - } - switch (pid = fork()) { - case 0: - /* child */ - (void) dup2(fd, s); - (void) dup2(fd, STDOUT_FILENO); - (void) close(fd); - for (i = 1; i <= *ctl_sock; i++) - close(ctl_sock[i]); - if (pfh != NULL) - pidfile_close(pfh); - goto gotchild; - case -1: - syslog(LOG_WARNING, "fork: %m"); - /* FALLTHROUGH */ - default: - close(fd); - } - } - } - } else { - addrlen = sizeof(his_addr); - if (getpeername(s, (struct sockaddr *)&his_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); - exit(1); - } - -#ifdef VIRTUAL_HOSTING - if (his_addr.su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) - family = AF_INET; - else - family = his_addr.su_family; - inithosts(family); -#endif - } - -gotchild: - 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); - - addrlen = sizeof(ctrl_addr); - if (getsockname(s, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { - syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); - exit(1); - } - dataport = ntohs(ctrl_addr.su_port) - 1; /* as per RFC 959 */ -#ifdef VIRTUAL_HOSTING - /* select our identity from virtual host table */ - selecthost(&ctrl_addr); -#endif -#ifdef IP_TOS - if (ctrl_addr.su_family == AF_INET) - { - tos = IPTOS_LOWDELAY; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "control setsockopt (IP_TOS): %m"); - } -#endif - /* - * Disable Nagle on the control channel so that we don't have to wait - * for peer's ACK before issuing our next reply. - */ - if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "control setsockopt (TCP_NODELAY): %m"); - - data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1); - - (void)snprintf(wtmpid, sizeof(wtmpid), "%xftpd", getpid()); - - /* Try to handle urgent data inline */ -#ifdef SO_OOBINLINE - if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "control setsockopt (SO_OOBINLINE): %m"); -#endif - -#ifdef F_SETOWN - if (fcntl(s, F_SETOWN, getpid()) == -1) - syslog(LOG_ERR, "fcntl F_SETOWN: %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 ((fd = fopen(_PATH_NOLOGIN,"r")) != NULL) { - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(530, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - reply(530, "System not available."); - exit(0); - } -#ifdef VIRTUAL_HOSTING - fd = fopen(thishost->welcome, "r"); -#else - fd = fopen(_PATH_FTPWELCOME, "r"); -#endif - if (fd != NULL) { - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(220, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - /* reply(220,) must follow */ - } -#ifndef VIRTUAL_HOSTING - if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) - fatalerror("Ran out of memory."); - if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) - hostname[0] = '\0'; - hostname[MAXHOSTNAMELEN - 1] = '\0'; -#endif - if (hostinfo) - reply(220, "%s FTP server (%s) ready.", hostname, version); - else - reply(220, "FTP server ready."); - BLACKLIST_INIT(); - for (;;) - (void) yyparse(); - /* NOTREACHED */ -} - -static void -lostconn(int signo) -{ - - if (ftpdebug) - syslog(LOG_DEBUG, "lost connection"); - dologout(1); -} - -static void -sigquit(int signo) -{ - - syslog(LOG_ERR, "got signal %d", signo); - dologout(1); -} - -#ifdef VIRTUAL_HOSTING -/* - * read in virtual host tables (if they exist) - */ - -static void -inithosts(int family) -{ - int insert; - size_t len; - FILE *fp; - char *cp, *mp, *line; - char *hostname; - char *vhost, *anonuser, *statfile, *welcome, *loginmsg; - struct ftphost *hrp, *lhrp; - struct addrinfo hints, *res, *ai; - - /* - * Fill in the default host information - */ - if ((hostname = malloc(MAXHOSTNAMELEN)) == NULL) - fatalerror("Ran out of memory."); - if (gethostname(hostname, MAXHOSTNAMELEN - 1) < 0) - hostname[0] = '\0'; - hostname[MAXHOSTNAMELEN - 1] = '\0'; - if ((hrp = malloc(sizeof(struct ftphost))) == NULL) - fatalerror("Ran out of memory."); - hrp->hostname = hostname; - hrp->hostinfo = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(hrp->hostname, NULL, &hints, &res) == 0) - hrp->hostinfo = res; - hrp->statfile = _PATH_FTPDSTATFILE; - hrp->welcome = _PATH_FTPWELCOME; - hrp->loginmsg = _PATH_FTPLOGINMESG; - hrp->anonuser = "ftp"; - hrp->next = NULL; - thishost = firsthost = lhrp = hrp; - if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) { - int addrsize, gothost; - void *addr; - struct hostent *hp; - - while ((line = fgetln(fp, &len)) != NULL) { - int i, hp_error; - - /* skip comments */ - if (line[0] == '#') - continue; - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - mp = NULL; - } else { - if ((mp = malloc(len + 1)) == NULL) - fatalerror("Ran out of memory."); - memcpy(mp, line, len); - mp[len] = '\0'; - line = mp; - } - cp = strtok(line, " \t"); - /* skip empty lines */ - if (cp == NULL) - goto nextline; - vhost = cp; - - /* set defaults */ - anonuser = "ftp"; - statfile = _PATH_FTPDSTATFILE; - welcome = _PATH_FTPWELCOME; - loginmsg = _PATH_FTPLOGINMESG; - - /* - * Preparse the line so we can use its info - * for all the addresses associated with - * the virtual host name. - * Field 0, the virtual host name, is special: - * it's already parsed off and will be strdup'ed - * later, after we know its canonical form. - */ - for (i = 1; i < 5 && (cp = strtok(NULL, " \t")); i++) - if (*cp != '-' && (cp = strdup(cp))) - switch (i) { - case 1: /* anon user permissions */ - anonuser = cp; - break; - case 2: /* statistics file */ - statfile = cp; - break; - case 3: /* welcome message */ - welcome = cp; - break; - case 4: /* login message */ - loginmsg = cp; - break; - default: /* programming error */ - abort(); - /* NOTREACHED */ - } - - hints.ai_flags = AI_PASSIVE; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo(vhost, NULL, &hints, &res) != 0) - goto nextline; - for (ai = res; ai != NULL && ai->ai_addr != NULL; - ai = ai->ai_next) { - - gothost = 0; - for (hrp = firsthost; hrp != NULL; hrp = hrp->next) { - struct addrinfo *hi; - - for (hi = hrp->hostinfo; hi != NULL; - hi = hi->ai_next) - if (hi->ai_addrlen == ai->ai_addrlen && - memcmp(hi->ai_addr, - ai->ai_addr, - ai->ai_addr->sa_len) == 0) { - gothost++; - break; - } - if (gothost) - break; - } - if (hrp == NULL) { - if ((hrp = malloc(sizeof(struct ftphost))) == NULL) - goto nextline; - hrp->hostname = NULL; - insert = 1; - } else { - if (hrp->hostinfo && hrp->hostinfo != res) - freeaddrinfo(hrp->hostinfo); - insert = 0; /* host already in the chain */ - } - hrp->hostinfo = res; - - /* - * determine hostname to use. - * force defined name if there is a valid alias - * otherwise fallback to primary hostname - */ - /* XXX: getaddrinfo() can't do alias check */ - switch(hrp->hostinfo->ai_family) { - case AF_INET: - addr = &((struct sockaddr_in *)hrp->hostinfo->ai_addr)->sin_addr; - addrsize = sizeof(struct in_addr); - break; - case AF_INET6: - addr = &((struct sockaddr_in6 *)hrp->hostinfo->ai_addr)->sin6_addr; - addrsize = sizeof(struct in6_addr); - break; - default: - /* should not reach here */ - freeaddrinfo(hrp->hostinfo); - if (insert) - free(hrp); /*not in chain, can free*/ - else - hrp->hostinfo = NULL; /*mark as blank*/ - goto nextline; - /* NOTREACHED */ - } - if ((hp = getipnodebyaddr(addr, addrsize, - hrp->hostinfo->ai_family, - &hp_error)) != NULL) { - if (strcmp(vhost, hp->h_name) != 0) { - if (hp->h_aliases == NULL) - vhost = hp->h_name; - else { - i = 0; - while (hp->h_aliases[i] && - strcmp(vhost, hp->h_aliases[i]) != 0) - ++i; - if (hp->h_aliases[i] == NULL) - vhost = hp->h_name; - } - } - } - if (hrp->hostname && - strcmp(hrp->hostname, vhost) != 0) { - free(hrp->hostname); - hrp->hostname = NULL; - } - if (hrp->hostname == NULL && - (hrp->hostname = strdup(vhost)) == NULL) { - freeaddrinfo(hrp->hostinfo); - hrp->hostinfo = NULL; /* mark as blank */ - if (hp) - freehostent(hp); - goto nextline; - } - hrp->anonuser = anonuser; - hrp->statfile = statfile; - hrp->welcome = welcome; - hrp->loginmsg = loginmsg; - if (insert) { - hrp->next = NULL; - lhrp->next = hrp; - lhrp = hrp; - } - if (hp) - freehostent(hp); - } -nextline: - if (mp) - free(mp); - } - (void) fclose(fp); - } -} - -static void -selecthost(union sockunion *su) -{ - struct ftphost *hrp; - u_int16_t port; -#ifdef INET6 - struct in6_addr *mapped_in6 = NULL; -#endif - struct addrinfo *hi; - -#ifdef INET6 - /* - * XXX IPv4 mapped IPv6 addr consideraton, - * specified in rfc2373. - */ - if (su->su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr)) - mapped_in6 = &su->su_sin6.sin6_addr; -#endif - - hrp = thishost = firsthost; /* default */ - port = su->su_port; - su->su_port = 0; - while (hrp != NULL) { - for (hi = hrp->hostinfo; hi != NULL; hi = hi->ai_next) { - if (memcmp(su, hi->ai_addr, hi->ai_addrlen) == 0) { - thishost = hrp; - goto found; - } -#ifdef INET6 - /* XXX IPv4 mapped IPv6 addr consideraton */ - if (hi->ai_addr->sa_family == AF_INET && mapped_in6 != NULL && - (memcmp(&mapped_in6->s6_addr[12], - &((struct sockaddr_in *)hi->ai_addr)->sin_addr, - sizeof(struct in_addr)) == 0)) { - thishost = hrp; - goto found; - } -#endif - } - hrp = hrp->next; - } -found: - su->su_port = port; - /* setup static variables as appropriate */ - hostname = thishost->hostname; - ftpuser = thishost->anonuser; -} -#endif - -/* - * Helper function for sgetpwnam(). - */ -static char * -sgetsave(char *s) -{ - char *new = malloc(strlen(s) + 1); - - if (new == NULL) { - reply(421, "Ran out of memory."); - dologout(1); - /* NOTREACHED */ - } - (void) strcpy(new, s); - return (new); -} - -/* - * Save the result of a getpwnam. Used for USER command, since - * the data returned must not be clobbered by any other command - * (e.g., globbing). - * NB: The data returned by sgetpwnam() will remain valid until - * the next call to this function. Its difference from getpwnam() - * is that sgetpwnam() is known to be called from ftpd code only. - */ -static struct passwd * -sgetpwnam(char *name) -{ - static struct passwd save; - struct passwd *p; - - if ((p = getpwnam(name)) == NULL) - return (p); - if (save.pw_name) { - free(save.pw_name); - free(save.pw_passwd); - free(save.pw_class); - free(save.pw_gecos); - free(save.pw_dir); - free(save.pw_shell); - } - save = *p; - save.pw_name = sgetsave(p->pw_name); - save.pw_passwd = sgetsave(p->pw_passwd); - save.pw_class = sgetsave(p->pw_class); - save.pw_gecos = sgetsave(p->pw_gecos); - save.pw_dir = sgetsave(p->pw_dir); - save.pw_shell = sgetsave(p->pw_shell); - return (&save); -} - -static int login_attempts; /* number of failed login attempts */ -static int askpasswd; /* had user command, ask for passwd */ -static char curname[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) -{ - int ecode; - char *cp, *shell; - - if (logged_in) { - if (guest) { - reply(530, "Can't change user from guest login."); - return; - } else if (dochroot) { - reply(530, "Can't change user from chroot user."); - return; - } - end_login(); - } - - guest = 0; -#ifdef VIRTUAL_HOSTING - pw = sgetpwnam(thishost->anonuser); -#else - pw = sgetpwnam("ftp"); -#endif - if (!noanon && - (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0)) { - if (checkuser(_PATH_FTPUSERS, "ftp", 0, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT)) - reply(530, "User %s access denied.", name); - else if (checkuser(_PATH_FTPUSERS, "anonymous", 0, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT)) - reply(530, "User %s access denied.", name); - else if (pw != NULL) { - guest = 1; - askpasswd = 1; - 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; - } - if (anon_only != 0) { - reply(530, "Sorry, only anonymous ftp allowed."); - return; - } - - if ((pw = sgetpwnam(name))) { - if ((shell = pw->pw_shell) == NULL || *shell == 0) - shell = _PATH_BSHELL; - setusershell(); - while ((cp = getusershell()) != NULL) - if (strcmp(cp, shell) == 0) - break; - endusershell(); - - if (cp == NULL || - (checkuser(_PATH_FTPUSERS, name, 1, NULL, &ecode) || - (ecode != 0 && ecode != ENOENT))) { - reply(530, "User %s access denied.", name); - if (logging) - syslog(LOG_NOTICE, - "FTP LOGIN REFUSED FROM %s, %s", - remotehost, name); - pw = NULL; - return; - } - } - if (logging) - strlcpy(curname, name, sizeof(curname)); - - 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(login_attempts); -} - -/* - * Check if a user is in the file "fname", - * return a pointer to a malloc'd string with the rest - * of the matching line in "residue" if not NULL. - */ -static int -checkuser(char *fname, char *name, int pwset, char **residue, int *ecode) -{ - FILE *fd; - int found = 0; - size_t len; - char *line, *mp, *p; - - if (ecode != NULL) - *ecode = 0; - if ((fd = fopen(fname, "r")) != NULL) { - while (!found && (line = fgetln(fd, &len)) != NULL) { - /* skip comments */ - if (line[0] == '#') - continue; - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - mp = NULL; - } else { - if ((mp = malloc(len + 1)) == NULL) - fatalerror("Ran out of memory."); - memcpy(mp, line, len); - mp[len] = '\0'; - line = mp; - } - /* avoid possible leading and trailing whitespace */ - p = strtok(line, " \t"); - /* skip empty lines */ - if (p == NULL) - goto nextline; - /* - * if first chr is '@', check group membership - */ - if (p[0] == '@') { - int i = 0; - struct group *grp; - - if (p[1] == '\0') /* single @ matches anyone */ - found = 1; - else { - if ((grp = getgrnam(p+1)) == NULL) - goto nextline; - /* - * Check user's default group - */ - if (pwset && grp->gr_gid == pw->pw_gid) - found = 1; - /* - * Check supplementary groups - */ - while (!found && grp->gr_mem[i]) - found = strcmp(name, - grp->gr_mem[i++]) - == 0; - } - } - /* - * Otherwise, just check for username match - */ - else - found = strcmp(p, name) == 0; - /* - * Save the rest of line to "residue" if matched - */ - if (found && residue) { - if ((p = strtok(NULL, "")) != NULL) - p += strspn(p, " \t"); - if (p && *p) { - if ((*residue = strdup(p)) == NULL) - fatalerror("Ran out of memory."); - } else - *residue = NULL; - } -nextline: - if (mp) - free(mp); - } - (void) fclose(fd); - } else if (ecode != NULL) - *ecode = errno; - 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) -{ -#ifdef USE_PAM - int e; -#endif - - (void) seteuid(0); -#ifdef LOGIN_CAP - setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | - LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | - LOGIN_SETENV)); -#endif - if (logged_in && dowtmp) - ftpd_logwtmp(wtmpid, NULL, NULL); - pw = NULL; -#ifdef USE_PAM - if (pamh) { - if ((e = pam_setcred(pamh, PAM_DELETE_CRED)) != PAM_SUCCESS) - syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); - if ((e = pam_close_session(pamh,0)) != PAM_SUCCESS) - syslog(LOG_ERR, "pam_close_session: %s", pam_strerror(pamh, e)); - if ((e = pam_end(pamh, e)) != PAM_SUCCESS) - syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); - pamh = NULL; - } -#endif - logged_in = 0; - guest = 0; - dochroot = 0; -} - -#ifdef USE_PAM - -/* - * the following code is stolen from imap-uw PAM authentication module and - * login.c - */ -#define COPY_STRING(s) (s ? strdup(s) : NULL) - -struct cred_t { - const char *uname; /* user name */ - const char *pass; /* password */ -}; -typedef struct cred_t cred_t; - -static int -auth_conv(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata) -{ - int i; - cred_t *cred = (cred_t *) appdata; - struct pam_response *reply; - - reply = calloc(num_msg, sizeof *reply); - if (reply == NULL) - return PAM_BUF_ERR; - - for (i = 0; i < num_msg; i++) { - switch (msg[i]->msg_style) { - case PAM_PROMPT_ECHO_ON: /* assume want user name */ - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = COPY_STRING(cred->uname); - /* PAM frees resp. */ - break; - case PAM_PROMPT_ECHO_OFF: /* assume want password */ - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = COPY_STRING(cred->pass); - /* PAM frees resp. */ - break; - case PAM_TEXT_INFO: - case PAM_ERROR_MSG: - reply[i].resp_retcode = PAM_SUCCESS; - reply[i].resp = NULL; - break; - default: /* unknown message style */ - free(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - return PAM_SUCCESS; -} - -/* - * Attempt to authenticate the user using PAM. Returns 0 if the user is - * authenticated, or 1 if not authenticated. If some sort of PAM system - * error occurs (e.g., the "/etc/pam.conf" file is missing) then this - * function returns -1. This can be used as an indication that we should - * fall back to a different authentication mechanism. - */ -static int -auth_pam(struct passwd **ppw, const char *pass) -{ - const char *tmpl_user; - const void *item; - int rval; - int e; - cred_t auth_cred = { (*ppw)->pw_name, pass }; - struct pam_conv conv = { &auth_conv, &auth_cred }; - - e = pam_start("ftpd", (*ppw)->pw_name, &conv, &pamh); - if (e != PAM_SUCCESS) { - /* - * In OpenPAM, it's OK to pass NULL to pam_strerror() - * if context creation has failed in the first place. - */ - syslog(LOG_ERR, "pam_start: %s", pam_strerror(NULL, e)); - return -1; - } - - e = pam_set_item(pamh, PAM_RHOST, remotehost); - if (e != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_set_item(PAM_RHOST): %s", - pam_strerror(pamh, e)); - if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); - } - pamh = NULL; - return -1; - } - - e = pam_authenticate(pamh, 0); - switch (e) { - case PAM_SUCCESS: - /* - * With PAM we support the concept of a "template" - * user. The user enters a login name which is - * authenticated by PAM, usually via a remote service - * such as RADIUS or TACACS+. If authentication - * succeeds, a different but related "template" name - * is used for setting the credentials, shell, and - * home directory. The name the user enters need only - * exist on the remote authentication server, but the - * template name must be present in the local password - * database. - * - * This is supported by two various mechanisms in the - * individual modules. However, from the application's - * point of view, the template user is always passed - * back as a changed value of the PAM_USER item. - */ - if ((e = pam_get_item(pamh, PAM_USER, &item)) == - PAM_SUCCESS) { - tmpl_user = (const char *) item; - if (strcmp((*ppw)->pw_name, tmpl_user) != 0) - *ppw = getpwnam(tmpl_user); - } else - syslog(LOG_ERR, "Couldn't get PAM_USER: %s", - pam_strerror(pamh, e)); - rval = 0; - break; - - case PAM_AUTH_ERR: - case PAM_USER_UNKNOWN: - case PAM_MAXTRIES: - rval = 1; - break; - - default: - syslog(LOG_ERR, "pam_authenticate: %s", pam_strerror(pamh, e)); - rval = -1; - break; - } - - if (rval == 0) { - e = pam_acct_mgmt(pamh, 0); - if (e != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_acct_mgmt: %s", - pam_strerror(pamh, e)); - rval = 1; - } - } - - if (rval != 0) { - if ((e = pam_end(pamh, e)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_end: %s", pam_strerror(pamh, e)); - } - pamh = NULL; - } - return rval; -} - -#endif /* USE_PAM */ - -void -pass(char *passwd) -{ - int rval, ecode; - FILE *fd; -#ifdef LOGIN_CAP - login_cap_t *lc = NULL; -#endif -#ifdef USE_PAM - int e; -#endif - char *residue = NULL; - char *xpasswd; - - if (logged_in || askpasswd == 0) { - reply(503, "Login with USER first."); - return; - } - askpasswd = 0; - if (!guest) { /* "ftp" is only account allowed no password */ - if (pw == NULL) { - rval = 1; /* failure below */ - goto skip; - } -#ifdef USE_PAM - rval = auth_pam(&pw, passwd); - if (rval >= 0) { - goto skip; - } -#endif - xpasswd = crypt(passwd, pw->pw_passwd); - if (passwd[0] == '\0' && pw->pw_passwd[0] != '\0') - xpasswd = ":"; - rval = strcmp(pw->pw_passwd, xpasswd); - if (pw->pw_expire && time(NULL) >= pw->pw_expire) - rval = 1; /* failure */ -skip: - /* - * If rval == 1, the user failed the authentication check - * above. If rval == 0, either PAM or local authentication - * succeeded. - */ - if (rval) { - reply(530, "Login incorrect."); - BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, STDIN_FILENO, "Login incorrect"); - if (logging) { - syslog(LOG_NOTICE, - "FTP LOGIN FAILED FROM %s", - remotehost); - syslog(LOG_AUTHPRIV | LOG_NOTICE, - "FTP LOGIN FAILED FROM %s, %s", - remotehost, curname); - } - pw = NULL; - if (login_attempts++ >= 5) { - syslog(LOG_NOTICE, - "repeated login failures from %s", - remotehost); - exit(0); - } - return; - } else { - BLACKLIST_NOTIFY(BLACKLIST_AUTH_OK, STDIN_FILENO, "Login successful"); - } - } - login_attempts = 0; /* this time successful */ - if (setegid(pw->pw_gid) < 0) { - reply(550, "Can't set gid."); - return; - } - /* May be overridden by login.conf */ - (void) umask(defumask); -#ifdef LOGIN_CAP - if ((lc = login_getpwclass(pw)) != NULL) { - char remote_ip[NI_MAXHOST]; - - if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, - remote_ip, sizeof(remote_ip) - 1, NULL, 0, - NI_NUMERICHOST)) - *remote_ip = 0; - remote_ip[sizeof(remote_ip) - 1] = 0; - if (!auth_hostok(lc, remotehost, remote_ip)) { - syslog(LOG_INFO|LOG_AUTH, - "FTP LOGIN FAILED (HOST) as %s: permission denied.", - pw->pw_name); - reply(530, "Permission denied."); - pw = NULL; - return; - } - if (!auth_timeok(lc, time(NULL))) { - reply(530, "Login not available right now."); - pw = NULL; - return; - } - } - setusercontext(lc, pw, 0, LOGIN_SETALL & - ~(LOGIN_SETRESOURCES | LOGIN_SETUSER | LOGIN_SETPATH | LOGIN_SETENV)); -#else - setlogin(pw->pw_name); - (void) initgroups(pw->pw_name, pw->pw_gid); -#endif - -#ifdef USE_PAM - if (pamh) { - if ((e = pam_open_session(pamh, 0)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, e)); - } else if ((e = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { - syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, e)); - } - } -#endif - - dochroot = - checkuser(_PATH_FTPCHROOT, pw->pw_name, 1, &residue, &ecode) -#ifdef LOGIN_CAP /* Allow login.conf configuration as well */ - || login_getcapbool(lc, "ftp-chroot", 0) -#endif - ; - /* - * It is possible that checkuser() failed to open the chroot file. - * If this is the case, report that logins are un-available, since we - * have no way of checking whether or not the user should be chrooted. - * We ignore ENOENT since it is not required that this file be present. - */ - if (ecode != 0 && ecode != ENOENT) { - reply(530, "Login not available right now."); - return; - } - chrootdir = NULL; - - /* Disable wtmp logging when chrooting. */ - if (dochroot || guest) - dowtmp = 0; - if (dowtmp) - ftpd_logwtmp(wtmpid, pw->pw_name, - (struct sockaddr *)&his_addr); - logged_in = 1; - -#ifdef LOGIN_CAP - setusercontext(lc, pw, 0, LOGIN_SETRESOURCES); -#endif - - if (guest && stats && statfd < 0) { -#ifdef VIRTUAL_HOSTING - statfd = open(thishost->statfile, O_WRONLY|O_APPEND); -#else - statfd = open(_PATH_FTPDSTATFILE, O_WRONLY|O_APPEND); -#endif - if (statfd < 0) - stats = 0; - } - - /* - * For a chrooted local user, - * a) see whether ftpchroot(5) specifies a chroot directory, - * b) extract the directory pathname from the line, - * c) expand it to the absolute pathname if necessary. - */ - if (dochroot && residue && - (chrootdir = strtok(residue, " \t")) != NULL) { - if (chrootdir[0] != '/') - asprintf(&chrootdir, "%s/%s", pw->pw_dir, chrootdir); - else - chrootdir = strdup(chrootdir); /* make it permanent */ - if (chrootdir == NULL) - fatalerror("Ran out of memory."); - } - if (guest || dochroot) { - /* - * If no chroot directory set yet, use the login directory. - * Copy it so it can be modified while pw->pw_dir stays intact. - */ - if (chrootdir == NULL && - (chrootdir = strdup(pw->pw_dir)) == NULL) - fatalerror("Ran out of memory."); - /* - * Check for the "/chroot/./home" syntax, - * separate the chroot and home directory pathnames. - */ - if ((homedir = strstr(chrootdir, "/./")) != NULL) { - *(homedir++) = '\0'; /* wipe '/' */ - homedir++; /* skip '.' */ - } else { - /* - * We MUST do a chdir() after the chroot. Otherwise - * the old current directory will be accessible as "." - * outside the new root! - */ - homedir = "/"; - } - /* - * Finally, do chroot() - */ - if (chroot(chrootdir) < 0) { - reply(550, "Can't change root."); - goto bad; - } - __FreeBSD_libc_enter_restricted_mode(); - } else /* real user w/o chroot */ - homedir = pw->pw_dir; - /* - * Set euid *before* doing chdir() so - * a) the user won't be carried to a directory that he couldn't reach - * on his own due to no permission to upper path components, - * b) NFS mounted homedirs w/restrictive permissions will be accessible - * (uid 0 has no root power over NFS if not mapped explicitly.) - */ - if (seteuid(pw->pw_uid) < 0) { - if (guest || dochroot) { - fatalerror("Can't set uid."); - } else { - reply(550, "Can't set uid."); - goto bad; - } - } - /* - * Do not allow the session to live if we're chroot()'ed and chdir() - * fails. Otherwise the chroot jail can be escaped. - */ - if (chdir(homedir) < 0) { - if (guest || dochroot) { - fatalerror("Can't change to base directory."); - } else { - if (chdir("/") < 0) { - reply(550, "Root is inaccessible."); - goto bad; - } - lreply(230, "No directory! Logging in with home=/."); - } - } - - /* - * Display a login message, if it exists. - * N.B. reply(230,) must follow the message. - */ -#ifdef VIRTUAL_HOSTING - fd = fopen(thishost->loginmsg, "r"); -#else - fd = fopen(_PATH_FTPLOGINMESG, "r"); -#endif - if (fd != NULL) { - char *cp, line[LINE_MAX]; - - while (fgets(line, sizeof(line), fd) != NULL) { - if ((cp = strchr(line, '\n')) != NULL) - *cp = '\0'; - lreply(230, "%s", line); - } - (void) fflush(stdout); - (void) fclose(fd); - } - if (guest) { - if (ident != NULL) - free(ident); - ident = strdup(passwd); - if (ident == NULL) - fatalerror("Ran out of memory."); - - reply(230, "Guest login ok, access restrictions apply."); -#ifdef SETPROCTITLE -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous(%s)/%s", remotehost, hostname, - passwd); - else -#endif - snprintf(proctitle, sizeof(proctitle), - "%s: anonymous/%s", remotehost, passwd); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s", - remotehost, passwd); - } else { - if (dochroot) - reply(230, "User %s logged in, " - "access restrictions apply.", pw->pw_name); - else - reply(230, "User %s logged in.", pw->pw_name); - -#ifdef SETPROCTITLE - snprintf(proctitle, sizeof(proctitle), - "%s: user/%s", remotehost, pw->pw_name); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - if (logging) - syslog(LOG_INFO, "FTP LOGIN FROM %s as %s", - remotehost, pw->pw_name); - } - if (logging && (guest || dochroot)) - syslog(LOG_INFO, "session root changed to %s", chrootdir); -#ifdef LOGIN_CAP - login_close(lc); -#endif - if (residue) - free(residue); - return; -bad: - /* Forget all about it... */ -#ifdef LOGIN_CAP - login_close(lc); -#endif - if (residue) - free(residue); - end_login(); -} - -void -retrieve(char *cmd, char *name) -{ - FILE *fin, *dout; - struct stat st; - int (*closefunc)(FILE *); - time_t start; - char line[BUFSIZ]; - - if (cmd == 0) { - fin = fopen(name, "r"), closefunc = fclose; - st.st_size = 0; - } else { - (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) { - if (fstat(fileno(fin), &st) < 0) { - perror_reply(550, name); - goto done; - } - if (!S_ISREG(st.st_mode)) { - /* - * Never sending a raw directory is a workaround - * for buggy clients that will attempt to RETR - * a directory before listing it, e.g., Mozilla. - * Preventing a guest from getting irregular files - * is a simple security measure. - */ - if (S_ISDIR(st.st_mode) || guest) { - reply(550, "%s: not a plain file.", name); - goto done; - } - st.st_size = -1; - /* st.st_blksize is set for all descriptor types */ - } - } - if (restart_point) { - if (type == TYPE_A) { - off_t i, n; - int c; - - n = restart_point; - i = 0; - while (i++ < n) { - if ((c=getc(fin)) == EOF) { - perror_reply(550, name); - goto done; - } - if (c == '\n') - i++; - } - } else if (lseek(fileno(fin), restart_point, L_SET) < 0) { - perror_reply(550, name); - goto done; - } - } - dout = dataconn(name, st.st_size, "w"); - if (dout == NULL) - goto done; - time(&start); - send_data(fin, dout, st.st_blksize, st.st_size, - restart_point == 0 && cmd == 0 && S_ISREG(st.st_mode)); - if (cmd == 0 && guest && stats && byte_count > 0) - logxfer(name, byte_count, start); - (void) fclose(dout); - data = -1; - pdata = -1; -done: - if (cmd == 0) - LOGBYTES("get", name, byte_count); - (*closefunc)(fin); -} - -void -store(char *name, char *mode, int unique) -{ - int fd; - FILE *fout, *din; - int (*closefunc)(FILE *); - - if (*mode == 'a') { /* APPE */ - if (unique) { - /* Programming error */ - syslog(LOG_ERR, "Internal: unique flag to APPE"); - unique = 0; - } - if (guest && noguestmod) { - reply(550, "Appending to existing file denied."); - goto err; - } - restart_point = 0; /* not affected by preceding REST */ - } - if (unique) /* STOU overrides REST */ - restart_point = 0; - if (guest && noguestmod) { - if (restart_point) { /* guest STOR w/REST */ - reply(550, "Modifying existing file denied."); - goto err; - } else /* treat guest STOR as STOU */ - unique = 1; - } - - if (restart_point) - mode = "r+"; /* so ASCII manual seek can work */ - if (unique) { - if ((fd = guniquefd(name, &name)) < 0) - goto err; - fout = fdopen(fd, mode); - } else - fout = fopen(name, mode); - closefunc = fclose; - if (fout == NULL) { - perror_reply(553, name); - goto err; - } - byte_count = -1; - if (restart_point) { - if (type == TYPE_A) { - off_t i, n; - int c; - - n = restart_point; - i = 0; - while (i++ < n) { - if ((c=getc(fout)) == EOF) { - perror_reply(550, name); - goto done; - } - if (c == '\n') - i++; - } - /* - * We must do this seek to "current" position - * because we are changing from reading to - * writing. - */ - if (fseeko(fout, 0, SEEK_CUR) < 0) { - perror_reply(550, name); - goto done; - } - } else if (lseek(fileno(fout), restart_point, L_SET) < 0) { - perror_reply(550, name); - goto done; - } - } - din = dataconn(name, -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 == 'a' ? "append" : "put", name, byte_count); - (*closefunc)(fout); - return; -err: - LOGCMD(*mode == 'a' ? "append" : "put" , name); - return; -} - -static FILE * -getdatasock(char *mode) -{ - int on = 1, s, t, tries; - - if (data >= 0) - return (fdopen(data, mode)); - - s = socket(data_dest.su_family, SOCK_STREAM, 0); - if (s < 0) - goto bad; - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "data setsockopt (SO_REUSEADDR): %m"); - /* anchor socket to avoid multi-homing problems */ - data_source = ctrl_addr; - data_source.su_port = htons(dataport); - (void) seteuid(0); - for (tries = 1; ; tries++) { - /* - * We should loop here since it's possible that - * another ftpd instance has passed this point and is - * trying to open a data connection in active mode now. - * Until the other connection is opened, we'll be getting - * EADDRINUSE because no SOCK_STREAM sockets in the system - * can share both local and remote addresses, localIP:20 - * and *:* in this case. - */ - if (bind(s, (struct sockaddr *)&data_source, - data_source.su_len) >= 0) - break; - if (errno != EADDRINUSE || tries > 10) - goto bad; - sleep(tries); - } - (void) seteuid(pw->pw_uid); -#ifdef IP_TOS - if (data_source.su_family == AF_INET) - { - on = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof(int)) < 0) - syslog(LOG_WARNING, "data 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(). - */ - on = 1; - if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, &on, sizeof on) < 0) - syslog(LOG_WARNING, "data setsockopt (TCP_NOPUSH): %m"); -#endif - return (fdopen(s, mode)); -bad: - /* Return the real value of errno (close may change it) */ - t = errno; - (void) seteuid(pw->pw_uid); - (void) close(s); - errno = t; - return (NULL); -} - -static FILE * -dataconn(char *name, off_t size, char *mode) -{ - char sizebuf[32]; - FILE *file; - int retry = 0, tos, conerrno; - - file_size = size; - byte_count = 0; - if (size != -1) - (void) snprintf(sizebuf, sizeof(sizebuf), - " (%jd bytes)", (intmax_t)size); - else - *sizebuf = '\0'; - if (pdata >= 0) { - union sockunion from; - socklen_t fromlen = ctrl_addr.su_len; - int flags, s; - struct timeval timeout; - fd_set set; - - FD_ZERO(&set); - FD_SET(pdata, &set); - - timeout.tv_usec = 0; - timeout.tv_sec = 120; - - /* - * Granted a socket is in the blocking I/O mode, - * accept() will block after a successful select() - * if the selected connection dies in between. - * Therefore set the non-blocking I/O flag here. - */ - if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || - fcntl(pdata, F_SETFL, flags | O_NONBLOCK) == -1) - goto pdata_err; - if (select(pdata+1, &set, NULL, NULL, &timeout) <= 0 || - (s = accept(pdata, (struct sockaddr *) &from, &fromlen)) < 0) - goto pdata_err; - (void) close(pdata); - pdata = s; - /* - * Unset the inherited non-blocking I/O flag - * on the child socket so stdio can work on it. - */ - if ((flags = fcntl(pdata, F_GETFL, 0)) == -1 || - fcntl(pdata, F_SETFL, flags & ~O_NONBLOCK) == -1) - goto pdata_err; -#ifdef IP_TOS - if (from.su_family == AF_INET) - { - tos = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(int)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (IP_TOS): %m"); - } -#endif - reply(150, "Opening %s mode data connection for '%s'%s.", - type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); - return (fdopen(pdata, mode)); -pdata_err: - reply(425, "Can't open data connection."); - (void) close(pdata); - pdata = -1; - return (NULL); - } - 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 { - file = getdatasock(mode); - if (file == NULL) { - char hostbuf[NI_MAXHOST], portbuf[NI_MAXSERV]; - - if (getnameinfo((struct sockaddr *)&data_source, - data_source.su_len, - hostbuf, sizeof(hostbuf) - 1, - portbuf, sizeof(portbuf) - 1, - NI_NUMERICHOST|NI_NUMERICSERV)) - *hostbuf = *portbuf = 0; - hostbuf[sizeof(hostbuf) - 1] = 0; - portbuf[sizeof(portbuf) - 1] = 0; - reply(425, "Can't create data socket (%s,%s): %s.", - hostbuf, portbuf, strerror(errno)); - return (NULL); - } - data = fileno(file); - conerrno = 0; - if (connect(data, (struct sockaddr *)&data_dest, - data_dest.su_len) == 0) - break; - conerrno = errno; - (void) fclose(file); - data = -1; - if (conerrno == EADDRINUSE) { - sleep(swaitint); - retry += swaitint; - } else { - break; - } - } while (retry <= swaitmax); - if (conerrno != 0) { - reply(425, "Can't build data connection: %s.", - strerror(conerrno)); - return (NULL); - } - reply(150, "Opening %s mode data connection for '%s'%s.", - type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf); - return (file); -} - -/* - * A helper macro to avoid code duplication - * in send_data() and receive_data(). - * - * XXX We have to block SIGURG during putc() because BSD stdio - * is unable to restart interrupted write operations and hence - * the entire buffer contents will be lost as soon as a write() - * call indicates EINTR to stdio. - */ -#define FTPD_PUTC(ch, file, label) \ - do { \ - int ret; \ - \ - do { \ - START_UNSAFE; \ - ret = putc((ch), (file)); \ - END_UNSAFE; \ - CHECKOOB(return (-1)) \ - else if (ferror(file)) \ - goto label; \ - clearerr(file); \ - } while (ret == EOF); \ - } while (0) - -/* - * 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, size_t blksize, off_t filesize, int isreg) -{ - int c, cp, filefd, netfd; - char *buf; - - STARTXFER; - - switch (type) { - - case TYPE_A: - cp = EOF; - for (;;) { - c = getc(instr); - CHECKOOB(return (-1)) - else if (c == EOF && ferror(instr)) - goto file_err; - if (c == EOF) { - if (ferror(instr)) { /* resume after OOB */ - clearerr(instr); - continue; - } - if (feof(instr)) /* EOF */ - break; - syslog(LOG_ERR, "Internal: impossible condition" - " on file after getc()"); - goto file_err; - } - if (c == '\n' && cp != '\r') { - FTPD_PUTC('\r', outstr, data_err); - byte_count++; - } - FTPD_PUTC(c, outstr, data_err); - byte_count++; - cp = c; - } -#ifdef notyet /* BSD stdio isn't ready for that */ - while (fflush(outstr) == EOF) { - CHECKOOB(return (-1)) - else - goto data_err; - clearerr(outstr); - } - ENDXFER; -#else - ENDXFER; - if (fflush(outstr) == EOF) - goto data_err; -#endif - 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) { - char *msg = "Transfer complete."; - off_t cnt, offset; - int err; - - cnt = offset = 0; - - while (filesize > 0) { - err = sendfile(filefd, netfd, offset, 0, - NULL, &cnt, 0); - /* - * Calculate byte_count before OOB processing. - * It can be used in myoob() later. - */ - byte_count += cnt; - offset += cnt; - filesize -= cnt; - CHECKOOB(return (-1)) - else if (err == -1) { - if (errno != EINTR && - cnt == 0 && offset == 0) - goto oldway; - goto data_err; - } - if (err == -1) /* resume after OOB */ - continue; - /* - * We hit the EOF prematurely. - * Perhaps the file was externally truncated. - */ - if (cnt == 0) { - msg = "Transfer finished due to " - "premature end of file."; - break; - } - } - ENDXFER; - reply(226, "%s", msg); - return (0); - } - -oldway: - if ((buf = malloc(blksize)) == NULL) { - ENDXFER; - reply(451, "Ran out of memory."); - return (-1); - } - - for (;;) { - int cnt, len; - char *bp; - - cnt = read(filefd, buf, blksize); - CHECKOOB(free(buf); return (-1)) - else if (cnt < 0) { - free(buf); - goto file_err; - } - if (cnt < 0) /* resume after OOB */ - continue; - if (cnt == 0) /* EOF */ - break; - for (len = cnt, bp = buf; len > 0;) { - cnt = write(netfd, bp, len); - CHECKOOB(free(buf); return (-1)) - else if (cnt < 0) { - free(buf); - goto data_err; - } - if (cnt <= 0) - continue; - len -= cnt; - bp += cnt; - byte_count += cnt; - } - } - ENDXFER; - free(buf); - reply(226, "Transfer complete."); - return (0); - default: - ENDXFER; - reply(550, "Unimplemented TYPE %d in send_data.", type); - return (-1); - } - -data_err: - ENDXFER; - perror_reply(426, "Data connection"); - return (-1); - -file_err: - ENDXFER; - perror_reply(551, "Error on input file"); - 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, cp; - int bare_lfs = 0; - - STARTXFER; - - switch (type) { - - case TYPE_I: - case TYPE_L: - for (;;) { - int cnt, len; - char *bp; - char buf[BUFSIZ]; - - cnt = read(fileno(instr), buf, sizeof(buf)); - CHECKOOB(return (-1)) - else if (cnt < 0) - goto data_err; - if (cnt < 0) /* resume after OOB */ - continue; - if (cnt == 0) /* EOF */ - break; - for (len = cnt, bp = buf; len > 0;) { - cnt = write(fileno(outstr), bp, len); - CHECKOOB(return (-1)) - else if (cnt < 0) - goto file_err; - if (cnt <= 0) - continue; - len -= cnt; - bp += cnt; - byte_count += cnt; - } - } - ENDXFER; - return (0); - - case TYPE_E: - ENDXFER; - reply(553, "TYPE E not implemented."); - return (-1); - - case TYPE_A: - cp = EOF; - for (;;) { - c = getc(instr); - CHECKOOB(return (-1)) - else if (c == EOF && ferror(instr)) - goto data_err; - if (c == EOF && ferror(instr)) { /* resume after OOB */ - clearerr(instr); - continue; - } - - if (cp == '\r') { - if (c != '\n') - FTPD_PUTC('\r', outstr, file_err); - } else - if (c == '\n') - bare_lfs++; - if (c == '\r') { - byte_count++; - cp = c; - continue; - } - - /* Check for EOF here in order not to lose last \r. */ - if (c == EOF) { - if (feof(instr)) /* EOF */ - break; - syslog(LOG_ERR, "Internal: impossible condition" - " on data stream after getc()"); - goto data_err; - } - - byte_count++; - FTPD_PUTC(c, outstr, file_err); - cp = c; - } -#ifdef notyet /* BSD stdio isn't ready for that */ - while (fflush(outstr) == EOF) { - CHECKOOB(return (-1)) - else - goto file_err; - clearerr(outstr); - } - ENDXFER; -#else - ENDXFER; - if (fflush(outstr) == EOF) - goto file_err; -#endif - if (bare_lfs) { - lreply(226, - "WARNING! %d bare linefeeds received in ASCII mode.", - bare_lfs); - (void)printf(" File may not have transferred correctly.\r\n"); - } - return (0); - default: - ENDXFER; - reply(550, "Unimplemented TYPE %d in receive_data.", type); - return (-1); - } - -data_err: - ENDXFER; - perror_reply(426, "Data connection"); - return (-1); - -file_err: - ENDXFER; - perror_reply(452, "Error writing to file"); - return (-1); -} - -void -statfilecmd(char *filename) -{ - FILE *fin; - int atstart; - int c, code; - char line[LINE_MAX]; - struct stat st; - - code = lstat(filename, &st) == 0 && S_ISDIR(st.st_mode) ? 212 : 213; - (void)snprintf(line, sizeof(line), _PATH_LS " -lA %s", filename); - fin = ftpd_popen(line, "r"); - if (fin == NULL) { - perror_reply(551, filename); - return; - } - lreply(code, "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); - } - /* - * RFC 959 says neutral text should be prepended before - * a leading 3-digit number followed by whitespace, but - * many ftp clients can be confused by any leading digits, - * as a matter of fact. - */ - if (atstart && isdigit(c)) - (void) putc(' ', stdout); - (void) putc(c, stdout); - atstart = (c == '\n'); - } - (void) ftpd_pclose(fin); - reply(code, "End of status."); -} - -void -statcmd(void) -{ - union sockunion *su; - u_char *a, *p; - char hname[NI_MAXHOST]; - int ispassive; - - if (hostinfo) { - lreply(211, "%s FTP server status:", hostname); - printf(" %s\r\n", version); - } else - lreply(211, "FTP server status:"); - printf(" Connected to %s", remotehost); - if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, - hname, sizeof(hname) - 1, NULL, 0, NI_NUMERICHOST)) { - hname[sizeof(hname) - 1] = 0; - if (strcmp(hname, remotehost) != 0) - printf(" (%s)", hname); - } - 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 CHAR_BIT == 8 - printf(" %d", CHAR_BIT); -#else - printf(" %d", bytesize); /* need definition! */ -#endif - printf("; STRUcture: %s; transfer MODE: %s\r\n", - strunames[stru], modenames[mode]); - if (data != -1) - printf(" Data connection open\r\n"); - else if (pdata != -1) { - ispassive = 1; - su = &pasv_addr; - goto printaddr; - } else if (usedefault == 0) { - ispassive = 0; - su = &data_dest; -printaddr: -#define UC(b) (((int) b) & 0xff) - if (epsvall) { - printf(" EPSV only mode (EPSV ALL)\r\n"); - goto epsvonly; - } - - /* PORT/PASV */ - if (su->su_family == AF_INET) { - a = (u_char *) &su->su_sin.sin_addr; - p = (u_char *) &su->su_sin.sin_port; - printf(" %s (%d,%d,%d,%d,%d,%d)\r\n", - ispassive ? "PASV" : "PORT", - UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(p[0]), UC(p[1])); - } - - /* LPRT/LPSV */ - { - int alen, af, i; - - 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) { - printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT", - af, alen); - for (i = 0; i < alen; i++) - printf("%d,", UC(a[i])); - printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1])); - } - } - -epsvonly:; - /* EPRT/EPSV */ - { - int af; - - switch (su->su_family) { - case AF_INET: - af = 1; - break; - case AF_INET6: - af = 2; - break; - default: - af = 0; - break; - } - if (af) { - union sockunion tmp; - - tmp = *su; - if (tmp.su_family == AF_INET6) - tmp.su_sin6.sin6_scope_id = 0; - if (!getnameinfo((struct sockaddr *)&tmp, tmp.su_len, - hname, sizeof(hname) - 1, NULL, 0, - NI_NUMERICHOST)) { - hname[sizeof(hname) - 1] = 0; - printf(" %s |%d|%s|%d|\r\n", - ispassive ? "EPSV" : "EPRT", - af, hname, htons(tmp.su_port)); - } - } - } -#undef UC - } else - printf(" No data connection\r\n"); - reply(211, "End of status."); -} - -void -fatalerror(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, ...) -{ - va_list ap; - - (void)printf("%d ", n); - va_start(ap, fmt); - (void)vprintf(fmt, ap); - va_end(ap); - (void)printf("\r\n"); - (void)fflush(stdout); - if (ftpdebug) { - syslog(LOG_DEBUG, "<--- %d ", n); - va_start(ap, fmt); - vsyslog(LOG_DEBUG, fmt, ap); - va_end(ap); - } -} - -void -lreply(int n, const char *fmt, ...) -{ - va_list ap; - - (void)printf("%d- ", n); - va_start(ap, fmt); - (void)vprintf(fmt, ap); - va_end(ap); - (void)printf("\r\n"); - (void)fflush(stdout); - if (ftpdebug) { - syslog(LOG_DEBUG, "<--- %d- ", n); - va_start(ap, fmt); - 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) -{ - char *cp; - - if ((cp = strchr(cbuf,'\n'))) - *cp = '\0'; - reply(500, "%s: command not understood.", cbuf); -} - -void -delete(char *name) -{ - struct stat st; - - LOGCMD("delete", name); - if (lstat(name, &st) < 0) { - perror_reply(550, name); - return; - } - if (S_ISDIR(st.st_mode)) { - if (rmdir(name) < 0) { - perror_reply(550, name); - return; - } - goto done; - } - if (guest && noguestmod) { - reply(550, "Operation not permitted."); - return; - } - if (unlink(name) < 0) { - perror_reply(550, name); - return; - } -done: - ack("DELE"); -} - -void -cwd(char *path) -{ - - if (chdir(path) < 0) - perror_reply(550, path); - else - ack("CWD"); -} - -void -makedir(char *name) -{ - char *s; - - LOGCMD("mkdir", name); - if (guest && noguestmkd) - reply(550, "Operation not permitted."); - else if (mkdir(name, 0777) < 0) - perror_reply(550, name); - else { - if ((s = doublequote(name)) == NULL) - fatalerror("Ran out of memory."); - reply(257, "\"%s\" directory created.", s); - free(s); - } -} - -void -removedir(char *name) -{ - - LOGCMD("rmdir", name); - if (rmdir(name) < 0) - perror_reply(550, name); - else - ack("RMD"); -} - -void -pwd(void) -{ - char *s, path[MAXPATHLEN + 1]; - - if (getcwd(path, sizeof(path)) == NULL) - perror_reply(550, "Get current directory"); - else { - if ((s = doublequote(path)) == NULL) - fatalerror("Ran out of memory."); - reply(257, "\"%s\" is current directory.", s); - free(s); - } -} - -char * -renamefrom(char *name) -{ - struct stat st; - - if (guest && noguestmod) { - reply(550, "Operation not permitted."); - return (NULL); - } - if (lstat(name, &st) < 0) { - perror_reply(550, name); - return (NULL); - } - reply(350, "File exists, ready for destination name."); - return (name); -} - -void -renamecmd(char *from, char *to) -{ - struct stat st; - - LOGCMD2("rename", from, to); - - if (guest && (stat(to, &st) == 0)) { - reply(550, "%s: permission denied.", to); - return; - } - - if (rename(from, to) < 0) - perror_reply(550, "rename"); - else - ack("RNTO"); -} - -static void -dolog(struct sockaddr *who) -{ - char who_name[NI_MAXHOST]; - - realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len); - remotehost[sizeof(remotehost) - 1] = 0; - if (getnameinfo(who, who->sa_len, - who_name, sizeof(who_name) - 1, NULL, 0, NI_NUMERICHOST)) - *who_name = 0; - who_name[sizeof(who_name) - 1] = 0; - -#ifdef SETPROCTITLE -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - snprintf(proctitle, sizeof(proctitle), "%s: connected (to %s)", - remotehost, hostname); - else -#endif - snprintf(proctitle, sizeof(proctitle), "%s: connected", - remotehost); - setproctitle("%s", proctitle); -#endif /* SETPROCTITLE */ - - if (logging) { -#ifdef VIRTUAL_HOSTING - if (thishost != firsthost) - syslog(LOG_INFO, "connection from %s (%s) to %s", - remotehost, who_name, hostname); - else -#endif - syslog(LOG_INFO, "connection from %s (%s)", - remotehost, who_name); - } -} - -/* - * Record logout in wtmp file - * and exit with supplied status. - */ -void -dologout(int status) -{ - - if (logged_in && dowtmp) { - (void) seteuid(0); -#ifdef LOGIN_CAP - setusercontext(NULL, getpwuid(0), 0, LOGIN_SETALL & ~(LOGIN_SETLOGIN | - LOGIN_SETUSER | LOGIN_SETGROUP | LOGIN_SETPATH | - LOGIN_SETENV)); -#endif - ftpd_logwtmp(wtmpid, NULL, NULL); - } - /* beware of flushing buffers after a SIGPIPE */ - _exit(status); -} - -static void -sigurg(int signo) -{ - - recvurg = 1; -} - -static void -maskurg(int flag) -{ - int oerrno; - sigset_t sset; - - if (!transflag) { - syslog(LOG_ERR, "Internal: maskurg() while no transfer"); - return; - } - oerrno = errno; - sigemptyset(&sset); - sigaddset(&sset, SIGURG); - sigprocmask(flag ? SIG_BLOCK : SIG_UNBLOCK, &sset, NULL); - errno = oerrno; -} - -static void -flagxfer(int flag) -{ - - if (flag) { - if (transflag) - syslog(LOG_ERR, "Internal: flagxfer(1): " - "transfer already under way"); - transflag = 1; - maskurg(0); - recvurg = 0; - } else { - if (!transflag) - syslog(LOG_ERR, "Internal: flagxfer(0): " - "no active transfer"); - maskurg(1); - transflag = 0; - } -} - -/* - * Returns 0 if OK to resume or -1 if abort requested. - */ -static int -myoob(void) -{ - char *cp; - int ret; - - if (!transflag) { - syslog(LOG_ERR, "Internal: myoob() while no transfer"); - return (0); - } - cp = tmpline; - ret = get_line(cp, 7, stdin); - if (ret == -1) { - reply(221, "You could at least say goodbye."); - dologout(0); - } else if (ret == -2) { - /* Ignore truncated command. */ - return (0); - } - upper(cp); - if (strcmp(cp, "ABOR\r\n") == 0) { - tmpline[0] = '\0'; - reply(426, "Transfer aborted. Data connection closed."); - reply(226, "Abort successful."); - return (-1); - } - if (strcmp(cp, "STAT\r\n") == 0) { - tmpline[0] = '\0'; - if (file_size != -1) - reply(213, "Status: %jd of %jd bytes transferred.", - (intmax_t)byte_count, (intmax_t)file_size); - else - reply(213, "Status: %jd bytes transferred.", - (intmax_t)byte_count); - } - return (0); -} - -/* - * 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; - char *p, *a; - - if (pdata >= 0) /* close old port if one set */ - close(pdata); - - pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); - if (pdata < 0) { - perror_reply(425, "Can't open passive connection"); - return; - } - on = 1; - if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); - - (void) seteuid(0); - -#ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - on = restricted_data_ports ? IP_PORTRANGE_HIGH - : IP_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif -#ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - on = restricted_data_ports ? IPV6_PORTRANGE_HIGH - : IPV6_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif - - pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0) - goto pasv_error; - - (void) seteuid(pw->pw_uid); - - len = sizeof(pasv_addr); - if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) - goto pasv_error; - if (listen(pdata, 1) < 0) - goto pasv_error; - if (pasv_addr.su_family == AF_INET) - a = (char *) &pasv_addr.su_sin.sin_addr; - else if (pasv_addr.su_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) - a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; - else - goto pasv_error; - - p = (char *) &pasv_addr.su_port; - -#define UC(b) (((int) b) & 0xff) - - reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), - UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); - return; - -pasv_error: - (void) seteuid(pw->pw_uid); - (void) close(pdata); - pdata = -1; - perror_reply(425, "Can't open passive connection"); - return; -} - -/* - * Long Passive defined in RFC 1639. - * 228 Entering Long Passive Mode - * (af, hal, h1, h2, h3,..., pal, p1, p2...) - */ - -void -long_passive(char *cmd, int pf) -{ - socklen_t len; - int on; - char *p, *a; - - if (pdata >= 0) /* close old port if one set */ - close(pdata); - - if (pf != PF_UNSPEC) { - if (ctrl_addr.su_family != pf) { - switch (ctrl_addr.su_family) { - case AF_INET: - pf = 1; - break; - case AF_INET6: - pf = 2; - break; - default: - pf = 0; - break; - } - /* - * XXX - * only EPRT/EPSV ready clients will understand this - */ - if (strcmp(cmd, "EPSV") == 0 && pf) { - reply(522, "Network protocol mismatch, " - "use (%d)", pf); - } else - reply(501, "Network protocol mismatch."); /*XXX*/ - - return; - } - } - - pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0); - if (pdata < 0) { - perror_reply(425, "Can't open passive connection"); - return; - } - on = 1; - if (setsockopt(pdata, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) - syslog(LOG_WARNING, "pdata setsockopt (SO_REUSEADDR): %m"); - - (void) seteuid(0); - - pasv_addr = ctrl_addr; - pasv_addr.su_port = 0; - len = pasv_addr.su_len; - -#ifdef IP_PORTRANGE - if (ctrl_addr.su_family == AF_INET) { - on = restricted_data_ports ? IP_PORTRANGE_HIGH - : IP_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IP, IP_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif -#ifdef IPV6_PORTRANGE - if (ctrl_addr.su_family == AF_INET6) { - on = restricted_data_ports ? IPV6_PORTRANGE_HIGH - : IPV6_PORTRANGE_DEFAULT; - - if (setsockopt(pdata, IPPROTO_IPV6, IPV6_PORTRANGE, - &on, sizeof(on)) < 0) - goto pasv_error; - } -#endif - - if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0) - goto pasv_error; - - (void) seteuid(pw->pw_uid); - - if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) - goto pasv_error; - if (listen(pdata, 1) < 0) - goto pasv_error; - -#define UC(b) (((int) b) & 0xff) - - if (strcmp(cmd, "LPSV") == 0) { - p = (char *)&pasv_addr.su_port; - switch (pasv_addr.su_family) { - case AF_INET: - a = (char *) &pasv_addr.su_sin.sin_addr; - v4_reply: - reply(228, -"Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)", - 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - 2, UC(p[0]), UC(p[1])); - return; - case AF_INET6: - if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) { - a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12]; - goto v4_reply; - } - a = (char *) &pasv_addr.su_sin6.sin6_addr; - reply(228, -"Entering Long Passive Mode " -"(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", - 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), - UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]), - UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]), - UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]), - 2, UC(p[0]), UC(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 (|||%d|)", - ntohs(pasv_addr.su_port)); - return; - } - } else { - /* more proper error code? */ - } - -pasv_error: - (void) seteuid(pw->pw_uid); - (void) close(pdata); - pdata = -1; - perror_reply(425, "Can't open passive connection"); - return; -} - -/* - * Generate unique name for file with basename "local" - * and open the file in order to avoid possible races. - * Try "local" first, then "local.1", "local.2" etc, up to "local.99". - * Return descriptor to the file, set "name" to its name. - * - * Generates failure reply on error. - */ -static int -guniquefd(char *local, char **name) -{ - static char new[MAXPATHLEN]; - struct stat st; - char *cp; - int count; - int fd; - - cp = strrchr(local, '/'); - if (cp) - *cp = '\0'; - if (stat(cp ? local : ".", &st) < 0) { - perror_reply(553, cp ? local : "."); - return (-1); - } - if (cp) { - /* - * Let not overwrite dirname with counter suffix. - * -4 is for /nn\0 - * In this extreme case dot won't be put in front of suffix. - */ - if (strlen(local) > sizeof(new) - 4) { - reply(553, "Pathname too long."); - return (-1); - } - *cp = '/'; - } - /* -4 is for the .nn<null> we put on the end below */ - (void) snprintf(new, sizeof(new) - 4, "%s", local); - cp = new + strlen(new); - /* - * Don't generate dotfile unless requested explicitly. - * This covers the case when basename gets truncated off - * by buffer size. - */ - if (cp > new && cp[-1] != '/') - *cp++ = '.'; - for (count = 0; count < 100; count++) { - /* At count 0 try unmodified name */ - if (count) - (void)sprintf(cp, "%d", count); - if ((fd = open(count ? new : local, - O_RDWR | O_CREAT | O_EXCL, 0666)) >= 0) { - *name = count ? new : local; - return (fd); - } - if (errno != EEXIST) { - perror_reply(553, count ? new : local); - return (-1); - } - } - 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, *dirname; - int simple = 0; - int freeglob = 0; - glob_t gl; - - if (strpbrk(whichf, "~{[*?") != NULL) { - int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; - - memset(&gl, 0, sizeof(gl)); - gl.gl_matchc = MAXGLOBARGS; - flags |= GLOB_LIMIT; - freeglob = 1; - if (glob(whichf, flags, 0, &gl)) { - reply(550, "No matching files 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 && - dout == NULL) - retrieve(_PATH_LS " %s", dirname); - else - perror_reply(550, whichf); - goto out; - } - - if (S_ISREG(st.st_mode)) { - if (dout == NULL) { - dout = dataconn("file list", -1, "w"); - if (dout == NULL) - goto out; - STARTXFER; - } - START_UNSAFE; - fprintf(dout, "%s%s\n", dirname, - type == TYPE_A ? "\r" : ""); - END_UNSAFE; - if (ferror(dout)) - goto data_err; - byte_count += strlen(dirname) + - (type == TYPE_A ? 2 : 1); - CHECKOOB(goto abrt); - continue; - } else if (!S_ISDIR(st.st_mode)) - continue; - - if ((dirp = opendir(dirname)) == NULL) - continue; - - while ((dir = readdir(dirp)) != NULL) { - char nbuf[MAXPATHLEN]; - - CHECKOOB(goto abrt); - - 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", -1, "w"); - if (dout == NULL) - goto out; - STARTXFER; - } - START_UNSAFE; - 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" : ""); - END_UNSAFE; - if (ferror(dout)) - goto data_err; - byte_count += strlen(nbuf) + - (type == TYPE_A ? 2 : 1); - CHECKOOB(goto abrt); - } - } - (void) closedir(dirp); - dirp = NULL; - } - - if (dout == NULL) - reply(550, "No files found."); - else if (ferror(dout)) -data_err: perror_reply(550, "Data connection"); - else - reply(226, "Transfer complete."); -out: - if (dout) { - ENDXFER; -abrt: - (void) fclose(dout); - data = -1; - pdata = -1; - } - if (dirp) - (void) closedir(dirp); - if (freeglob) { - freeglob = 0; - globfree(&gl); - } -} - -void -reapchild(int signo) -{ - while (waitpid(-1, NULL, WNOHANG) > 0); -} - -static void -appendf(char **strp, char *fmt, ...) -{ - va_list ap; - char *ostr, *p; - - va_start(ap, fmt); - vasprintf(&p, fmt, ap); - va_end(ap); - if (p == NULL) - fatalerror("Ran out of memory."); - if (*strp == NULL) - *strp = p; - else { - ostr = *strp; - asprintf(strp, "%s%s", ostr, p); - if (*strp == NULL) - fatalerror("Ran out of memory."); - free(ostr); - } -} - -static void -logcmd(char *cmd, char *file1, char *file2, off_t cnt) -{ - char *msg = NULL; - char wd[MAXPATHLEN + 1]; - - if (logging <= 1) - return; - - if (getcwd(wd, sizeof(wd) - 1) == NULL) - strcpy(wd, strerror(errno)); - - appendf(&msg, "%s", cmd); - if (file1) - appendf(&msg, " %s", file1); - if (file2) - appendf(&msg, " %s", file2); - if (cnt >= 0) - appendf(&msg, " = %jd bytes", (intmax_t)cnt); - appendf(&msg, " (wd: %s", wd); - if (guest || dochroot) - appendf(&msg, "; chrooted"); - appendf(&msg, ")"); - syslog(LOG_INFO, "%s", msg); - free(msg); -} - -static void -logxfer(char *name, off_t size, time_t start) -{ - char buf[MAXPATHLEN + 1024]; - char path[MAXPATHLEN + 1]; - time_t now; - - if (statfd >= 0) { - time(&now); - if (realpath(name, path) == NULL) { - syslog(LOG_NOTICE, "realpath failed on %s: %m", path); - return; - } - snprintf(buf, sizeof(buf), "%.20s!%s!%s!%s!%jd!%ld\n", - ctime(&now)+4, ident, remotehost, - path, (intmax_t)size, - (long)(now - start + (now == start))); - write(statfd, buf, strlen(buf)); - } -} - -static char * -doublequote(char *s) -{ - int n; - char *p, *s2; - - for (p = s, n = 0; *p; p++) - if (*p == '"') - n++; - - if ((s2 = malloc(p - s + n + 1)) == NULL) - return (NULL); - - for (p = s2; *s; s++, p++) { - if ((*p = *s) == '"') - *(++p) = '"'; - } - *p = '\0'; - - return (s2); -} - -/* setup server socket for specified address family */ -/* if af is PF_UNSPEC more than one socket may be returned */ -/* the returned list is dynamically allocated, so caller needs to free it */ -static int * -socksetup(int af, char *bindname, const char *bindport) -{ - struct addrinfo hints, *res, *r; - int error, maxs, *s, *socks; - const int on = 1; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = af; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(bindname, bindport, &hints, &res); - if (error) { - syslog(LOG_ERR, "%s", gai_strerror(error)); - if (error == EAI_SYSTEM) - syslog(LOG_ERR, "%s", strerror(errno)); - return NULL; - } - - /* Count max number of sockets we may open */ - for (maxs = 0, r = res; r; r = r->ai_next, maxs++) - ; - socks = malloc((maxs + 1) * sizeof(int)); - if (!socks) { - freeaddrinfo(res); - syslog(LOG_ERR, "couldn't allocate memory for sockets"); - return NULL; - } - - *socks = 0; /* num of sockets counter at start of array */ - s = socks + 1; - for (r = res; r; r = r->ai_next) { - *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); - if (*s < 0) { - syslog(LOG_DEBUG, "control socket: %m"); - continue; - } - if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) < 0) - syslog(LOG_WARNING, - "control setsockopt (SO_REUSEADDR): %m"); - if (r->ai_family == AF_INET6) { - if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof(on)) < 0) - syslog(LOG_WARNING, - "control setsockopt (IPV6_V6ONLY): %m"); - } - if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { - syslog(LOG_DEBUG, "control bind: %m"); - close(*s); - continue; - } - (*socks)++; - s++; - } - - if (res) - freeaddrinfo(res); - - if (*socks == 0) { - syslog(LOG_ERR, "control socket: Couldn't bind to any socket"); - free(socks); - return NULL; - } - return(socks); -} diff --git a/libexec/ftpd/ftpusers b/libexec/ftpd/ftpusers deleted file mode 100644 index 2e6f80a5968c..000000000000 --- a/libexec/ftpd/ftpusers +++ /dev/null @@ -1,28 +0,0 @@ -# -# list of users disallowed any ftp access. -# read by ftpd(8). -root -toor -daemon -operator -bin -tty -kmem -games -news -ntpd -man -sshd -smmsp -mailnull -bind -unbound -proxy -_pflogd -_dhcp -uucp -pop -auditdistd -www -hast -nobody diff --git a/libexec/ftpd/logwtmp.c b/libexec/ftpd/logwtmp.c deleted file mode 100644 index 85db1f13419d..000000000000 --- a/libexec/ftpd/logwtmp.c +++ /dev/null @@ -1,70 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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/stat.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/socket.h> - -#include <libutil.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <utmpx.h> -#include "extern.h" - -void -ftpd_logwtmp(char *id, char *user, struct sockaddr *addr) -{ - struct utmpx ut; - - memset(&ut, 0, sizeof(ut)); - - if (user != NULL) { - /* Log in. */ - ut.ut_type = USER_PROCESS; - (void)strncpy(ut.ut_user, user, sizeof(ut.ut_user)); - if (addr != NULL) - realhostname_sa(ut.ut_host, sizeof(ut.ut_host), - addr, addr->sa_len); - } else { - /* Log out. */ - ut.ut_type = DEAD_PROCESS; - } - - ut.ut_pid = getpid(); - gettimeofday(&ut.ut_tv, NULL); - (void)strncpy(ut.ut_id, id, sizeof(ut.ut_id)); - (void)strncpy(ut.ut_line, "ftpd", sizeof(ut.ut_line)); - - pututxline(&ut); -} diff --git a/libexec/ftpd/pathnames.h b/libexec/ftpd/pathnames.h deleted file mode 100644 index 1ff753123b1c..000000000000 --- a/libexec/ftpd/pathnames.h +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -#include <paths.h> - -#define _PATH_FTPCHROOT "/etc/ftpchroot" -#define _PATH_FTPWELCOME "/etc/ftpwelcome" -#define _PATH_FTPLOGINMESG "/etc/ftpmotd" -#define _PATH_FTPHOSTS "/etc/ftphosts" -#define _PATH_FTPDSTATFILE "/var/log/ftpd" -#define _PATH_LS "/bin/ls" diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c deleted file mode 100644 index 5f48356a376f..000000000000 --- a/libexec/ftpd/popen.c +++ /dev/null @@ -1,193 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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 <netinet/in.h> - -#include <errno.h> -#include <glob.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "extern.h" -#include "pathnames.h" -#include <syslog.h> -#include <time.h> - -#define MAXUSRARGS 100 -#define MAXGLOBARGS 1000 - -/* - * Special version of popen which avoids call to shell. This ensures no one - * may create a pipe to a hidden program as a side effect of a list or dir - * command. - */ -static int *pids; -static int fds; - -FILE * -ftpd_popen(char *program, char *type) -{ - char *cp; - FILE *iop; - int argc, gargc, pdes[2], pid; - char **pop, *argv[MAXUSRARGS], *gargv[MAXGLOBARGS]; - - if (((*type != 'r') && (*type != 'w')) || type[1]) - return (NULL); - - if (!pids) { - if ((fds = getdtablesize()) <= 0) - return (NULL); - if ((pids = calloc(fds, sizeof(int))) == NULL) - return (NULL); - } - if (pipe(pdes) < 0) - return (NULL); - - /* break up string into pieces */ - for (argc = 0, cp = program; argc < MAXUSRARGS; cp = NULL) { - if (!(argv[argc++] = strtok(cp, " \t\n"))) - break; - } - argv[argc - 1] = NULL; - - /* glob each piece */ - gargv[0] = argv[0]; - for (gargc = argc = 1; argv[argc] && gargc < (MAXGLOBARGS-1); argc++) { - glob_t gl; - int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; - - memset(&gl, 0, sizeof(gl)); - gl.gl_matchc = MAXGLOBARGS; - flags |= GLOB_LIMIT; - if (glob(argv[argc], flags, NULL, &gl)) - gargv[gargc++] = strdup(argv[argc]); - else if (gl.gl_pathc > 0) { - for (pop = gl.gl_pathv; *pop && gargc < (MAXGLOBARGS-1); - pop++) - gargv[gargc++] = strdup(*pop); - } - globfree(&gl); - } - gargv[gargc] = NULL; - - iop = NULL; - fflush(NULL); - pid = (strcmp(gargv[0], _PATH_LS) == 0) ? fork() : vfork(); - switch(pid) { - 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]); - } - /* Drop privileges before proceeding */ - if (getuid() != geteuid() && setuid(geteuid()) < 0) - _exit(1); - if (strcmp(gargv[0], _PATH_LS) == 0) { - /* Reset getopt for ls_main() */ - optreset = optind = optopt = 1; - /* Close syslogging to remove pwd.db missing msgs */ - closelog(); - /* Trigger to sense new /etc/localtime after chroot */ - if (getenv("TZ") == NULL) { - setenv("TZ", "", 0); - tzset(); - unsetenv("TZ"); - tzset(); - } - 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, omask, status; - pid_t pid; - - /* - * pclose returns -1 if stream is not associated with a - * `popened' command, or, if already `pclosed'. - */ - if (pids == NULL || pids[fdes = fileno(iop)] == 0) - return (-1); - (void)fclose(iop); - omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); - while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) - continue; - (void)sigsetmask(omask); - pids[fdes] = 0; - if (pid < 0) - return (pid); - if (WIFEXITED(status)) - return (WEXITSTATUS(status)); - return (1); -} diff --git a/libexec/kgdb/Makefile b/libexec/kgdb/Makefile deleted file mode 100644 index f6b255ab4f60..000000000000 --- a/libexec/kgdb/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -FILESDIR?= /usr/libexec/kgdb - -FILES= acttrace.py - -.include <bsd.prog.mk> diff --git a/libexec/kgdb/acttrace.py b/libexec/kgdb/acttrace.py deleted file mode 100644 index 3229ff708de1..000000000000 --- a/libexec/kgdb/acttrace.py +++ /dev/null @@ -1,63 +0,0 @@ -#- -# Copyright (c) 2022 The FreeBSD Foundation -# -# This software was developed by Mark Johnston under sponsorship from the -# FreeBSD Foundation. -# - -import gdb - - -def symval(name): - return gdb.lookup_global_symbol(name).value() - - -def tid_to_gdb_thread(tid): - for thread in gdb.inferiors()[0].threads(): - if thread.ptid[2] == tid: - return thread - else: - return None - - -def all_pcpus(): - mp_maxid = symval("mp_maxid") - cpuid_to_pcpu = symval("cpuid_to_pcpu") - - cpu = 0 - while cpu <= mp_maxid: - pcpu = cpuid_to_pcpu[cpu] - if pcpu: - yield pcpu - cpu = cpu + 1 - - -class acttrace(gdb.Command): - def __init__(self): - super(acttrace, self).__init__("acttrace", gdb.COMMAND_USER) - - def invoke(self, arg, from_tty): - # Save the current thread so that we can switch back after. - curthread = gdb.selected_thread() - - for pcpu in all_pcpus(): - td = pcpu['pc_curthread'] - tid = td['td_tid'] - - gdb_thread = tid_to_gdb_thread(tid) - if gdb_thread is None: - print("failed to find GDB thread with TID {}".format(tid)) - else: - gdb_thread.switch() - - p = td['td_proc'] - print("Tracing command {} pid {} tid {} (CPU {})".format( - p['p_comm'], p['p_pid'], td['td_tid'], pcpu['pc_cpuid'])) - gdb.execute("bt") - print() - - curthread.switch() - - -# Registers the command with gdb, doesn't do anything. -acttrace() diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua index 493ae11d6ca7..3eeb2ea0b44c 100644 --- a/libexec/nuageinit/nuage.lua +++ b/libexec/nuageinit/nuage.lua @@ -2,11 +2,23 @@ -- SPDX-License-Identifier: BSD-2-Clause -- -- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> +-- Copyright(c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> local unistd = require("posix.unistd") local sys_stat = require("posix.sys.stat") local lfs = require("lfs") +local function getlocalbase() + local f = io.popen("sysctl -in user.localbase 2> /dev/null") + local localbase = f:read("*l") + f:close() + if localbase == nil or localbase:len() == 0 then + -- fallback + localbase = "/usr/local" + end + return localbase +end + local function decode_base64(input) local b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' input = string.gsub(input, '[^'..b..'=]', '') @@ -127,6 +139,58 @@ local function splitlist(list) return ret end +local function splitlines(s) + local ret = {} + + for line in string.gmatch(s, "[^\n]+") do + ret[#ret + 1] = line + end + + return ret +end + +local function getgroups() + local ret = {} + + local root = os.getenv("NUAGE_FAKE_ROOTDIR") + local cmd = "pw " + if root then + cmd = cmd .. "-R " .. root .. " " + end + + local f = io.popen(cmd .. "groupshow -a 2> /dev/null | cut -d: -f1") + local groups = f:read("*a") + f:close() + + return splitlines(groups) +end + +local function checkgroup(group) + local groups = getgroups() + + for _, group2chk in ipairs(groups) do + if group == group2chk then + return true + end + end + + return false +end + +local function purge_group(groups) + local ret = {} + + for _, group in ipairs(groups) do + if checkgroup(group) then + ret[#ret + 1] = group + else + warnmsg("ignoring non-existent group '" .. group .. "'") + end + end + + return ret +end + local function adduser(pwd) if (type(pwd) ~= "table") then warnmsg("Argument should be a table") @@ -152,7 +216,14 @@ local function adduser(pwd) local extraargs = "" if pwd.groups then local list = splitlist(pwd.groups) - extraargs = " -G " .. table.concat(list, ",") + -- pw complains if the group does not exist, so if the user + -- specifies one that cannot be found, nuageinit will generate + -- an exception and exit, unlike cloud-init, which only issues + -- a warning but creates the user anyway. + list = purge_group(list) + if #list > 0 then + extraargs = " -G " .. table.concat(list, ",") + end end -- pw will automatically create a group named after the username -- do not add a -g option in this case @@ -276,11 +347,59 @@ local function addsshkey(homedir, key) end end +local function adddoas(pwd) + local chmodetcdir = false + local chmoddoasconf = false + local root = os.getenv("NUAGE_FAKE_ROOTDIR") + local localbase = getlocalbase() + local etcdir = localbase .. "/etc" + if root then + etcdir= root .. etcdir + end + local doasconf = etcdir .. "/doas.conf" + local doasconf_attr = lfs.attributes(doasconf) + if doasconf_attr == nil then + chmoddoasconf = true + local dirattrs = lfs.attributes(etcdir) + if dirattrs == nil then + local r, err = mkdir_p(etcdir) + if not r then + return nil, err .. " (creating " .. etcdir .. ")" + end + chmodetcdir = true + end + end + local f = io.open(doasconf, "a") + if not f then + warnmsg("impossible to open " .. doasconf) + return + end + if type(pwd.doas) == "string" then + local rule = pwd.doas + rule = rule:gsub("%%u", pwd.name) + f:write(rule .. "\n") + elseif type(pwd.doas) == "table" then + for _, str in ipairs(pwd.doas) do + local rule = str + rule = rule:gsub("%%u", pwd.name) + f:write(rule .. "\n") + end + end + f:close() + if chmoddoasconf then + chmod(doasconf, "0640") + end + if chmodetcdir then + chmod(etcdir, "0755") + end +end + local function addsudo(pwd) local chmodsudoersd = false local chmodsudoers = false local root = os.getenv("NUAGE_FAKE_ROOTDIR") - local sudoers_dir = "/usr/local/etc/sudoers.d" + local localbase = getlocalbase() + local sudoers_dir = localbase .. "/etc/sudoers.d" if root then sudoers_dir= root .. sudoers_dir end @@ -311,10 +430,10 @@ local function addsudo(pwd) end f:close() if chmodsudoers then - chmod(sudoers, "0640") + chmod(sudoers, "0440") end if chmodsudoersd then - chmod(sudoers, "0740") + chmod(sudoers_dir, "0750") end end @@ -451,6 +570,23 @@ local function chpasswd(obj) end end +local function settimezone(timezone) + if timezone == nil then + return + end + local root = os.getenv("NUAGE_FAKE_ROOTDIR") + if not root then + root = "/" + end + + f, _, rc = os.execute("tzsetup -s -C " .. root .. " " .. timezone) + + if not f then + warnmsg("Impossible to configure time zone ( rc = " .. rc .. " )") + return + end +end + local function pkg_bootstrap() if os.getenv("NUAGE_RUN_TESTS") then return true @@ -480,7 +616,7 @@ local function install_package(package) end local function run_pkg_cmd(subcmd) - local cmd = "pkg " .. subcmd .. " -y" + local cmd = "env ASSUME_ALWAYS_YES=yes pkg " .. subcmd if os.getenv("NUAGE_RUN_TESTS") then print(cmd) return true @@ -556,6 +692,7 @@ local n = { dirname = dirname, mkdir_p = mkdir_p, sethostname = sethostname, + settimezone = settimezone, adduser = adduser, addgroup = addgroup, addsshkey = addsshkey, @@ -566,6 +703,7 @@ local n = { update_packages = update_packages, upgrade_packages = upgrade_packages, addsudo = addsudo, + adddoas = adddoas, addfile = addfile } diff --git a/libexec/nuageinit/nuageinit b/libexec/nuageinit/nuageinit index 0fcdc7274db3..f29fa8ba1bac 100755 --- a/libexec/nuageinit/nuageinit +++ b/libexec/nuageinit/nuageinit @@ -3,8 +3,10 @@ -- SPDX-License-Identifier: BSD-2-Clause-FreeBSD -- -- Copyright(c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> +-- Copyright(c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> local nuage = require("nuage") +local lfs = require("lfs") local ucl = require("ucl") local yaml = require("lyaml") @@ -46,7 +48,15 @@ local function open_config(name) return openat("/etc/rc.conf.d", name) end -local function get_ifaces() +local function open_resolv_conf() + return openat("/etc", "resolv.conf") +end + +local function open_resolvconf_conf() + return openat("/etc", "resolvconf.conf") +end + +local function get_ifaces_by_mac() local parser = ucl.parser() -- grab ifaces local ns = io.popen("netstat -i --libxo json") @@ -77,6 +87,10 @@ local function sethostname(obj) end end +local function settimezone(obj) + nuage.settimezone(obj.timezone) +end + local function groups(obj) if obj.groups == nil then return end @@ -127,6 +141,9 @@ local function users(obj) if u.sudo then nuage.addsudo(u) end + if u.doas then + nuage.adddoas(u) + end else nuage.warn("invalid type : " .. type(u) .. " for users entry number " .. n) end @@ -171,6 +188,59 @@ local function ssh_authorized_keys(obj) end end +local function nameservers(interface, obj) + local resolvconf_conf_handler = open_resolvconf_conf() + + if obj.search then + local with_space = false + + resolvconf_conf_handler:write('search_domains="') + + for _, d in ipairs(obj.search) do + if with_space then + resolvconf_conf_handler:write(" " .. d) + else + resolvconf_conf_handler:write(d) + with_space = true + end + end + + resolvconf_conf_handler:write('"\n') + end + + if obj.addresses then + local with_space = false + + resolvconf_conf_handler:write('name_servers="') + + for _, a in ipairs(obj.addresses) do + if with_space then + resolvconf_conf_handler:write(" " .. a) + else + resolvconf_conf_handler:write(a) + with_space = true + end + end + + resolvconf_conf_handler:write('"\n') + end + + resolvconf_conf_handler:close() + + local resolv_conf = root .. "/etc/resolv.conf" + + resolv_conf_attr = lfs.attributes(resolv_conf) + + if resolv_conf_attr == nil then + resolv_conf_handler = open_resolv_conf() + resolv_conf_handler:close() + end + + if not os.execute("resolvconf -a " .. interface .. " < " .. resolv_conf) then + nuage.warn("Failed to execute resolvconf(8)") + end +end + local function install_packages(packages) if not nuage.pkg_bootstrap() then nuage.warn("Failed to bootstrap pkg, skip installing packages") @@ -187,6 +257,85 @@ local function install_packages(packages) end end +local function list_ifaces() + local proc = io.popen("ifconfig -l") + local raw_ifaces = proc:read("*a") + proc:close() + local ifaces = {} + for i in raw_ifaces:gmatch("[^%s]+") do + table.insert(ifaces, i) + end + return ifaces +end + +local function get_ifaces_by_driver() + local proc = io.popen("ifconfig -D") + local drivers = {} + local last_interface = nil + for line in proc:lines() do + local interface = line:match("^([%S]+): ") + + if interface then + last_interface = interface + end + + local driver = line:match("^[%s]+drivername: ([%S]+)$") + + if driver then + drivers[driver] = last_interface + end + end + proc:close() + + return drivers +end + +local function match_rules(rules) + -- To comply with the cloud-init specification, all rules must match and a table + -- with the matching interfaces must be returned. This changes the way we initially + -- thought about our implementation, since at first we only needed one interface, + -- but cloud-init performs actions on a group of matching interfaces. + local interfaces = {} + if rules.macaddress then + local ifaces = get_ifaces_by_mac() + local interface = ifaces[rules.macaddress] + if not interface then + nuage.warn("not interface matching by MAC address: " .. rules.macaddress) + return + end + interfaces[interface] = 1 + end + if rules.name then + local match = false + for _, i in pairs(list_ifaces()) do + if i:match(rules.name) then + match = true + interfaces[i] = 1 + end + end + if not match then + nuage.warn("not interface matching by name: " .. rules.name) + return + end + end + if rules.driver then + local match = false + local drivers = get_ifaces_by_driver() + for d in pairs(drivers) do + if d:match(rules.driver) then + match = true + interface = drivers[d] + interfaces[interface] = 1 + end + end + if not match then + nuage.warn("not interface matching by driver: " .. rules.driver) + return + end + end + return interfaces +end + local function write_files(files, defer) if not files then return @@ -210,41 +359,76 @@ end local function network_config(obj) if obj.network == nil then return end - local ifaces = get_ifaces() local network = open_config("network") local routing = open_config("routing") local ipv6 = {} - for _, v in pairs(obj.network.ethernets) do - if not v.match then - goto next + local set_defaultrouter = true + local set_defaultrouter6 = true + local set_nameservers = true + for i, v in pairs(obj.network.ethernets) do + local interfaces = {} + if v.match then + interfaces = match_rules(v.match) + + if next(interfaces) == nil then + goto next + end + else + interfaces[i] = 1 end - if not v.match.macaddress then - goto next + local extra_opts = "" + if v.wakeonlan then + extra_opts = extra_opts .. " wol" end - if not ifaces[v.match.macaddress] then - nuage.warn("not interface matching: " .. v.match.macaddress) - goto next + if v.mtu then + if type(v.mtu) == "number" then + mtu = tostring(v.mtu) + else + mtu = v.mtu + end + if mtu:match("%d") then + extra_opts = extra_opts .. " mtu " .. mtu + else + nuage.warn("MTU is not set because the specified value is invalid: " .. mtu) + end end - local interface = ifaces[v.match.macaddress] - if v.dhcp4 then - network:write("ifconfig_" .. interface .. '="DHCP"\n') - elseif v.addresses then - for _, a in pairs(v.addresses) do - if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then - network:write("ifconfig_" .. interface .. '="inet ' .. a .. '"\n') - else - network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. a .. '"\n') - ipv6[#ipv6 + 1] = interface + for interface in pairs(interfaces) do + if v.match and v.match.macaddress and v["set-name"] then + local ifaces = get_ifaces_by_mac() + local matched = ifaces[v.match.macaddress] + if matched and matched == interface then + network:write("ifconfig_" .. interface .. '_name=' .. v["set-name"] .. '\n') + interface = v["set-name"] + end + end + if v.dhcp4 then + network:write("ifconfig_" .. interface .. '="DHCP"' .. extra_opts .. '\n') + elseif v.addresses then + for _, a in pairs(v.addresses) do + if a:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)") then + network:write("ifconfig_" .. interface .. '="inet ' .. a .. extra_opts .. '"\n') + else + network:write("ifconfig_" .. interface .. '_ipv6="inet6 ' .. a .. extra_opts .. '"\n') + ipv6[#ipv6 + 1] = interface + end + end + if set_nameservers and v.nameservers then + set_nameservers = false + nameservers(interface, v.nameservers) + end + if set_defaultrouter and v.gateway4 then + set_defaultrouter = false + routing:write('defaultrouter="' .. v.gateway4 .. '"\n') + end + if v.gateway6 then + if set_defaultrouter6 then + set_defaultrouter6 = false + routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n') + end + routing:write("ipv6_route_" .. interface .. '="' .. v.gateway6) + routing:write(" -prefixlen 128 -interface " .. interface .. '"\n') end end - end - if v.gateway4 then - routing:write('defaultrouter="' .. v.gateway4 .. '"\n') - end - if v.gateway6 then - routing:write('ipv6_defaultrouter="' .. v.gateway6 .. '"\n') - routing:write("ipv6_route_" .. interface .. '="' .. v.gateway6) - routing:write(" -prefixlen 128 -interface " .. interface .. '"\n') end ::next:: end @@ -316,7 +500,7 @@ local function config2_network(p) end local obj = parser:get_object() - local ifaces = get_ifaces() + local ifaces = get_ifaces_by_mac() if not ifaces then nuage.warn("no network interfaces found") return @@ -403,6 +587,26 @@ local function config2_network(p) routing:close() end +local function parse_network_config() + local nc_file = ni_path .. "/network-config" + local nc_file_attr = lfs.attributes(nc_file) + if nc_file_attr == nil then + return + end + local f, err = io.open(nc_file) + if err then + nuage.err("error parsing nocloud network-config: " .. err) + end + local obj = yaml.load(f:read("*a")) + f:close() + if not obj then + nuage.err("error parsing nocloud network-config") + end + local netobj = {} + netobj["network"] = obj + return netobj +end + if citype == "config-2" then local parser = ucl.parser() local res, err = parser:parse_file(ni_path .. "/meta_data.json") @@ -468,6 +672,7 @@ f:close() if line == "#cloud-config" then local pre_network_calls = { sethostname, + settimezone, groups, create_default_user, ssh_keys, @@ -498,7 +703,16 @@ if line == "#cloud-config" then end for i = 1, #calls_table do - calls_table[i](obj) + if citype == "nocloud" and calls_table[i] == network_config then + netobj = parse_network_config() + if netobj == nil then + network_config(obj) + else + network_config(netobj) + end + else + calls_table[i](obj) + end end elseif line:sub(1, 2) == "#!" then -- delay for execution at rc.local time -- diff --git a/libexec/nuageinit/nuageinit.7 b/libexec/nuageinit/nuageinit.7 index 327ce160e151..b527c984970c 100644 --- a/libexec/nuageinit/nuageinit.7 +++ b/libexec/nuageinit/nuageinit.7 @@ -1,6 +1,7 @@ .\" SPDX-License-Identifier: BSD-2-Clause .\" .\" Copyright (c) 2025 Baptiste Daroussin <bapt@FreeBSD.org> +.\" Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> .\" .Dd June 26, 2025 .Dt NUAGEINIT 7 @@ -143,6 +144,11 @@ Specify a fully qualified domain name for the instance. Specify the hostname of the instance if .Qq Ic fqdn is not set. +.It Ic timezone +Sets the system timezone based on the value provided. +.Pp +See also +.Xr tzfile 3 Ns . .It Ic groups An array of strings or objects to be created: .Bl -bullet @@ -176,6 +182,96 @@ boolean which determines the value of the configuration in .Pa /etc/ssh/sshd_config .It Ic network +Network configuration parameters. +.Pp +Specifying the following parameters from a file named +.Pa network-config +takes precedence over their specification from the +.Ic network +parameter of +.Pa user-data Ns . +.Bl -tag -width "ethernets" +.It Ic ethernets +Mapping representing a generic configuration for existing network interfaces. +.Pp +Each key is an interface name that is only used when no +.Sy match +rule is specified. +If +.Sy match +rules are specified, an arbitrary name can be used +.Po e.g.: id0 Pc Ns . +.Bl -tag -width "nameservers" +.It Ic match +This selects a subset of available physical devices by various hardware properties. +The following configuration will then apply to all matching devices, as soon as +they appear. +All specified properties must match. +The following properties for +creating matches are supported: +.Bl -tag -width "macaddress" +.It Ic macaddress +.No Device's MAC address in the form Sy xx:xx:xx:xx:xx:xx Ns . +Letters should be lowercase. +.It Ic name +Current interface name. +Lua pattern-matching expressions are supported. +.It Ic driver +Interface driver name and unit number of the interface. +Lua pattern-natching expressions +are supported. +.El +.It Ic set-name +When matching on unique properties such as MAC, match rules can be written so that they +match only one device. +Then this property can be used to give that device a more +specific/desirable/nicer name than the default. +.Pp +While multiple properties can be used in a match, +.Sy macaddress +is required for nuageinit to perform the rename. +.It Ic mtu +The MTU key represents a device's Maximum Transmission Unit, the largest size packet +or frame. +.It Ic wakeonlan +Enable wake on LAN. +Off by default. +.It Ic dhcp4 +Configure the interface to use DHCP. +.Pp +This takes precedence over +.Sy addresses +when both are specified. +.It Ic addresses +List of strings representing IPv4 or IPv6 addresses. +.It Ic gateway4 +Set default gateway for IPv4, for manual address configuration. +This requires setting +.Sy addresses +too. +.Pp +Since only one default router can be configured at a time, this parameter is applied +when processing the first entry, and any others are silently ignored. +.It Ic gateway6 +Set default gateway for IPv6, for manual address configuration. +This requires setting +.Sy addresses +too. +.Pp +Since only one default router can be configured at a time, this parameter is applied +when processing the first entry, and any others are silently ignored. +.It Ic nameservers +Set DNS servers and search domains, for manual address configuration. +.Pp +There are two supported fields: +.Bl -tag -width "addresses" +.It Ic search +Search list for host-name lookup. +.It Ic addresses +List of IPv4 or IPv6 name server addresses that the resolver should query. +.El +.El +.El .It Ic runcmd An array of commands to be run at the end of the boot process .It Ic packages @@ -186,7 +282,7 @@ Update the remote package metadata. Upgrade the packages installed to their latest version. .It Ic users Specify a list of users to be created: -.Bl -tag -width "plain_text_passwd" +.Bl -tag -width "ssh_authorized_keys" .It Ic name Name of the user. .It Ic gecos @@ -201,18 +297,25 @@ The list of other groups the user should belong to. A boolean which determines if the home directory should be created or not. .It Ic shell The shell that should be used for the user. +.It Ic ssh_authorized_keys +List of SSH keys for the user. .It Ic passwd The encrypted password for the user. .It Ic plain_text_passwd The password in plain text for the user. Ignored if an encrypted password is already provided. -.It Ic groups -The list of other groups the user should belong to. .It Ic locked Boolean to determine if the user account should be locked. .It Ic sudo -A string or an array of strings which which should be appended to -.Pa /usr/local/etc/sudoers.d/90-nuageinit-users +A string or an array of strings which should be appended to +.Pa ${LOCALBASE}/etc/sudoers.d/90-nuageinit-users +.It Ic doas +A string or an array of strings which should be appended to +.Pa ${LOCALBASE}/etc/doas.conf +.Pp +Instead of hardcoding the username, you can use +.Sy %u Ns , +which will be replaced by the current username. .El .Pp A special case exist: if the entry is a simple string with the value @@ -251,7 +354,7 @@ It accepts the following keys for each objects: The content to be written to the file. If this key is not existing then an empty file will be created. .It Ic encoding -Specifiy the encoding used for content. +Specify the encoding used for content. If not specified, then plain text is considered. Only .Ar b64 @@ -287,7 +390,7 @@ users: - name: user gecos: Foo B. Bar sudo: ALL=(ALL) NOPASSWD:ALL - ssh-authorized-keys: + ssh_authorized_keys: - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAr... packages: - neovim @@ -303,6 +406,12 @@ ssh_keys: ... -----END OPENSSH PRIVATE KEY----- ed25519_public: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+MH4E8KO32N5CXRvXVqvyZVl0+6ue4DobdhU0FqFd+ +network: + ethernets: + vtnet0: + addresses: + - 192.168.8.2/24 + gateway4: 192.168.8.1 .Ed .Sh SEE ALSO .Xr kenv 2 , diff --git a/libexec/nuageinit/tests/Makefile b/libexec/nuageinit/tests/Makefile index c69bc28a4c86..dc8997717b59 100644 --- a/libexec/nuageinit/tests/Makefile +++ b/libexec/nuageinit/tests/Makefile @@ -15,6 +15,7 @@ ${PACKAGE}FILES+= adduser_passwd.lua ${PACKAGE}FILES+= dirname.lua ${PACKAGE}FILES+= err.lua ${PACKAGE}FILES+= sethostname.lua +${PACKAGE}FILES+= settimezone.lua ${PACKAGE}FILES+= warn.lua ${PACKAGE}FILES+= addfile.lua diff --git a/libexec/nuageinit/tests/nuage.sh b/libexec/nuageinit/tests/nuage.sh index 56651c8c5bb7..57d83b62928a 100644 --- a/libexec/nuageinit/tests/nuage.sh +++ b/libexec/nuageinit/tests/nuage.sh @@ -1,5 +1,6 @@ #- # Copyright (c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> +# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> # # SPDX-License-Identifier: BSD-2-Clause # @@ -7,12 +8,21 @@ export NUAGE_FAKE_ROOTDIR="$PWD" atf_test_case sethostname +atf_test_case settimezone atf_test_case addsshkey atf_test_case adduser atf_test_case adduser_passwd atf_test_case addgroup atf_test_case addfile +settimezone_body() +{ + atf_check /usr/libexec/flua $(atf_get_srcdir)/settimezone.lua + if [ ! -f etc/localtime ]; then + atf_fail "localtime not written" + fi +} + sethostname_body() { atf_check /usr/libexec/flua $(atf_get_srcdir)/sethostname.lua diff --git a/libexec/nuageinit/tests/nuageinit.sh b/libexec/nuageinit/tests/nuageinit.sh index 849f1c258b62..2b7c5226c97a 100644 --- a/libexec/nuageinit/tests/nuageinit.sh +++ b/libexec/nuageinit/tests/nuageinit.sh @@ -1,5 +1,6 @@ #- # Copyright (c) 2022-2025 Baptiste Daroussin <bapt@FreeBSD.org> +# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> # # SPDX-License-Identifier: BSD-2-Clause # @@ -119,12 +120,16 @@ users: gecos: Foo B. Bar primary_group: foobar sudo: ALL=(ALL) NOPASSWD:ALL + doas: permit persist %u as root groups: users passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ - name: bla sudo: - "ALL=(ALL) NOPASSWD:/usr/sbin/pw" - "ALL=(ALL) ALL" + doas: + - "deny %u as foobar" + - "permit persist %u as root cmd whoami" EOF atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit nocloud atf_check /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet @@ -147,7 +152,13 @@ EOF sed -i "" "s/freebsd:.*:1001/freebsd:freebsd:1001/" "${PWD}"/etc/master.passwd atf_check -o file:expectedpasswd cat "${PWD}"/etc/master.passwd atf_check -o file:expectedgroup cat "${PWD}"/etc/group - atf_check -o inline:"foobar ALL=(ALL) NOPASSWD:ALL\nbla ALL=(ALL) NOPASSWD:/usr/sbin/pw\nbla ALL=(ALL) ALL\n" cat ${PWD}/usr/local/etc/sudoers.d/90-nuageinit-users + localbase=`sysctl -ni user.localbase 2> /dev/null` + if [ -z "${localbase}" ]; then + # fallback + localbase="/usr/local" + fi + atf_check -o inline:"foobar ALL=(ALL) NOPASSWD:ALL\nbla ALL=(ALL) NOPASSWD:/usr/sbin/pw\nbla ALL=(ALL) ALL\n" cat "${PWD}/${localbase}/etc/sudoers.d/90-nuageinit-users" + atf_check -o inline:"permit persist foobar as root\ndeny bla as foobar\npermit persist bla as root cmd whoami\n" cat "${PWD}/${localbase}/etc/doas.conf" } nocloud_network_head() @@ -815,7 +826,7 @@ config2_userdata_update_packages_body() package_update: true EOF chmod 755 "${PWD}"/media/nuageinit/user_data - atf_check -o inline:"pkg update -y\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet + atf_check -o inline:"env ASSUME_ALWAYS_YES=yes pkg update\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet } config2_userdata_upgrade_packages_body() @@ -829,7 +840,7 @@ config2_userdata_upgrade_packages_body() package_upgrade: true EOF chmod 755 "${PWD}"/media/nuageinit/user_data - atf_check -o inline:"pkg upgrade -y\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet + atf_check -o inline:"env ASSUME_ALWAYS_YES=yes pkg upgrade\n" /usr/libexec/nuageinit "${PWD}"/media/nuageinit postnet } config2_userdata_shebang_body() diff --git a/libexec/nuageinit/tests/settimezone.lua b/libexec/nuageinit/tests/settimezone.lua new file mode 100644 index 000000000000..a8cacf09f4e7 --- /dev/null +++ b/libexec/nuageinit/tests/settimezone.lua @@ -0,0 +1,5 @@ +#!/usr/libexec/flua + +local n = require("nuage") + +n.settimezone("UTC") diff --git a/libexec/nuageinit/tests/utils.sh b/libexec/nuageinit/tests/utils.sh index 26f117d81d60..76cd7e045473 100644 --- a/libexec/nuageinit/tests/utils.sh +++ b/libexec/nuageinit/tests/utils.sh @@ -1,5 +1,6 @@ #- # Copyright (c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> +# Copyright (c) 2025 Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org> # # SPDX-License-Identifier: BSD-2-Clause # diff --git a/libexec/pppoed/Makefile b/libexec/pppoed/Makefile index b1b88c4c1b6c..26339249c3b5 100644 --- a/libexec/pppoed/Makefile +++ b/libexec/pppoed/Makefile @@ -1,3 +1,4 @@ +PACKAGE=ppp PROG= pppoed LIBADD= netgraph MAN= pppoed.8 diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf index bfa46bd343a6..c776a815003c 100644 --- a/libexec/rc/rc.conf +++ b/libexec/rc/rc.conf @@ -369,9 +369,6 @@ sshd_enable="NO" # Enable sshd sshd_oomprotect="YES" # Don't kill sshd when swap space is exhausted. sshd_program="/usr/sbin/sshd" # path to sshd, if you want a different one. sshd_flags="" # Additional flags for sshd. -ftpd_enable="NO" # Enable stand-alone ftpd. -ftpd_program="/usr/libexec/ftpd" # Path to ftpd, if you want a different one. -ftpd_flags="" # Additional flags to stand-alone ftpd. ### Network daemon (NFS): All need rpcbind_enable="YES" ### autofs_enable="NO" # Run autofs daemons. @@ -503,6 +500,9 @@ netwait_enable="NO" # Enable rc.d/netwait (or NO) netwait_timeout="60" # Total number of seconds to perform pings. #netwait_if="" # Wait for active link on each intf in this list. netwait_if_timeout="30" # Total number of seconds to monitor link state. +netwait_dad="NO" # Wait for DAD to complete +netwait_dad_timeout="" # Total number of seconds to wait for DAD, zero + # or unset to autodetect ### Miscellaneous network options: ### icmp_bmcastecho="NO" # respond to broadcast ping packets @@ -589,12 +589,16 @@ saver="NO" # screen saver: Uses /boot/kernel/${saver}_saver.ko moused_nondefault_enable="YES" # Treat non-default mice as enabled unless # specifically overridden in rc.conf(5). moused_enable="NO" # Run the mouse daemon. -moused_type="auto" # See man page for rc.conf(5) for available settings. +moused_type="evdev" # See man page for rc.conf(5) for available settings. moused_port="/dev/psm0" # Set to your mouse port. moused_flags="" # Any additional flags to moused. mousechar_start="NO" # if 0xd0-0xd3 default range is occupied in your # language code table, specify alternative range # start like mousechar_start=3, see vidcontrol(1) +msconvd_enable="NO" # Run the mouse protocol conversion daemon. +msconvd_type="auto" # See rc.conf(5) man page for available moused_type-s. +msconvd_ports="" # List of msconvd ports. +msconvd_flags="" # Any additional flags to msconvd. allscreens_flags="" # Set this vidcontrol mode for all virtual screens allscreens_kbdflags="" # Set this kbdcontrol mode for all virtual screens diff --git a/libexec/rc/rc.d/Makefile b/libexec/rc/rc.d/Makefile index d0c6fc1dee46..03f0933533ca 100644 --- a/libexec/rc/rc.d/Makefile +++ b/libexec/rc/rc.d/Makefile @@ -50,8 +50,6 @@ CONFS= DAEMON \ ${_nscd} \ ${_opensm} \ os-release \ - powerd \ - pppoed \ pwcheck \ quota \ random \ @@ -127,6 +125,14 @@ CONFGROUPS+= NEWSYSLOG NEWSYSLOG= newsyslog NEWSYSLOGPACKAGE= newsyslog +CONFGROUPS+= POWERD +POWERD= powerd +POWERDPACKAGE= powerd + +CONFGROUPS+= PPPOED +PPPOED= pppoed +PPPOEDPACKAGE= ppp + CONFGROUPS+= SYSLOGD SYSLOGD= syslogd SYSLOGDPACKAGE= syslogd @@ -209,13 +215,13 @@ CCD= ccd CCDPACKAGE= ccdconfig .endif -.if ${MK_FTP} != "no" -CONFGROUPS+= FTPD -FTPD= ftpd -FTPDPACKAGE= ftpd +.if ${MK_CUSE} != "no" +CONFGROUPS+= VOSS +VOSS= virtual_oss +VOSSPACKAGE= sound .endif -.if ${MK_GSSAPI} != "no" && ${MK_KERBEROS_SUPPORT} != "no" +.if ${MK_KERBEROS_SUPPORT} != "no" CONFGROUPS+= GSSD GSSD= gssd GSSDPACKAGE= gssd @@ -273,6 +279,7 @@ JAILPACKAGE= jail .if ${MK_LEGACY_CONSOLE} != "no" CONFGROUPS+= CONSOLE CONSOLE+= moused +CONSOLE+= msconvd CONSOLE+= syscons CONSOLEPACKAGE= console-tools .endif diff --git a/libexec/rc/rc.d/bluetooth b/libexec/rc/rc.d/bluetooth index 22bd5078034d..193fd969967f 100755 --- a/libexec/rc/rc.d/bluetooth +++ b/libexec/rc/rc.d/bluetooth @@ -127,8 +127,17 @@ bluetooth_setup_stack() > /dev/null 2>&1 || return 1 # Initilalize HCI node - ${hccontrol} -n ${dev}hci reset \ - > /dev/null 2>&1 || return 1 + for loop in 1 2 3 + do + ${hccontrol} -n ${dev}hci reset \ + > /dev/null 2>&1 && break + if [ ${loop} -eq 3 ] + then + warn Reset failed three times, giving up. + return 1 + fi + warn Reset failed, retrying. + done ${hccontrol} -n ${dev}hci read_bd_addr \ > /dev/null 2>&1 || return 1 diff --git a/libexec/rc/rc.d/ftpd b/libexec/rc/rc.d/ftpd deleted file mode 100755 index e25a561a520a..000000000000 --- a/libexec/rc/rc.d/ftpd +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# -# - -# PROVIDE: ftpd -# REQUIRE: LOGIN FILESYSTEMS -# KEYWORD: shutdown - -. /etc/rc.subr - -name="ftpd" -desc="Internet File Transfer Protocol daemon" -rcvar="ftpd_enable" -command="/usr/libexec/${name}" -pidfile="/var/run/${name}.pid" - -: ${ftpd_svcj_options:="net_basic"} - -load_rc_config $name - -flags="-D ${flags} ${rc_flags}" - -run_rc_command "$1" diff --git a/libexec/rc/rc.d/moused b/libexec/rc/rc.d/moused index 6f1b95af0f0a..e267ae5b3cd8 100755 --- a/libexec/rc/rc.d/moused +++ b/libexec/rc/rc.d/moused @@ -16,6 +16,7 @@ start_cmd="moused_start" pidprefix="/var/run/moused" pidfile="${pidprefix}.pid" pidarg= +typearg= load_rc_config $name # doesn't make sense to run in a svcj: nojail keyword @@ -27,9 +28,10 @@ moused_svcj="NO" # expected to be the mouse device. # if [ -n "$2" ]; then - eval moused_$2_enable=\${moused_$2_enable-${moused_nondefault_enable}} - rcvar="moused_${2}_enable" - pidfile="${pidprefix}.$2.pid" + ms=`basename $2` + eval moused_${ms}_enable=\${moused_${ms}_enable-${moused_nondefault_enable}} + rcvar="moused_${ms}_enable" + pidfile="${pidprefix}.${ms}.pid" pidarg="-I $pidfile" fi @@ -44,20 +46,22 @@ moused_start() # the moused_port variable, which if not defined sets it to the # passed in device name. # - ms=$1 - if [ -n "$ms" ]; then + if [ -n "$1" ]; then + ms=`basename $1` eval myflags=\${moused_${ms}_flags-$moused_flags} - eval myport=\${moused_${ms}_port-/dev/$ms} + eval myport=\${moused_${ms}_port-/dev/$1} eval mytype=\${moused_${ms}_type-$moused_type} + if [ -n "$mytype" ] && check_kern_features evdev_support; then + typearg="-t ${mytype}" + fi else ms="default" myflags="$moused_flags" myport="$moused_port" - mytype="$moused_type" fi startmsg -n "Starting ${ms} moused" - /usr/sbin/moused ${myflags} -p ${myport} -t ${mytype} ${pidarg} + /usr/sbin/moused ${myflags} -p ${myport} ${typearg} ${pidarg} startmsg '.' mousechar_arg= @@ -70,6 +74,7 @@ moused_start() esac for ttyv in /dev/ttyv* ; do + [ "$ttyv" = '/dev/ttyv*' ] && break vidcontrol < ${ttyv} ${mousechar_arg} -m on done } diff --git a/libexec/rc/rc.d/msconvd b/libexec/rc/rc.d/msconvd new file mode 100755 index 000000000000..c2a96bf2eb68 --- /dev/null +++ b/libexec/rc/rc.d/msconvd @@ -0,0 +1,61 @@ +#!/bin/sh +# +# + +# PROVIDE: msconvd +# REQUIRE: DAEMON FILESYSTEMS +# KEYWORD: nojail shutdown + +. /etc/rc.subr + +name="msconvd" +desc="Mouse protocol conversion daemon" +command="/usr/sbin/${name}" +start_cmd="msconvd_start" +pidprefix="/var/run/msconvd" +load_rc_config $name + +: ${msconvd_enable="NO"} +: ${msconvd_type="auto"} + +# doesn't make sense to run in a svcj: nojail keyword +# XXX: How does msconvd communiacte with the kernel? +# XXX: Does the kernel prevent this communcation in jails? +msconvd_svcj="NO" + +# Set the pid file and variable name. The second argument, if it exists, is +# expected to be the mouse device. +# +if [ -n "$2" ]; then + eval msconvd_$2_enable=\${msconvd_$2_enable-${msconvd_enable}} + rcvar="msconvd_$2_enable" + pidfile="${pidprefix}.$2.pid" +else + for ms in ${msconvd_ports}; do + /etc/rc.d/msconvd $1 ${ms} + done + exit 0 +fi + +msconvd_start() +{ + local ms myflags myport mytype + + # Set the mouse device and get any related variables. If + # a msconvd device has been specified on the commandline, then + # rc.conf(5) variables defined for that device take precedence + # over the generic msconvd_* variables. The only exception is + # the msconvd_port variable, which if not defined sets it to + # the passed in device name. + # + ms=$1 + eval myflags=\${msconvd_${ms}_flags-$msconvd_flags} + eval myport=\${msconvd_${ms}_port-/dev/${ms}} + eval mytype=\${msconvd_${ms}_type-$msconvd_type} + + startmsg -n "Starting ${ms} ${name}" + ${command} ${myflags} -p ${myport} -t ${mytype} -I ${pidfile} + startmsg '.' +} + +run_rc_command $* diff --git a/libexec/rc/rc.d/netwait b/libexec/rc/rc.d/netwait index 3f374806d97c..05874552cf1c 100755 --- a/libexec/rc/rc.d/netwait +++ b/libexec/rc/rc.d/netwait @@ -2,12 +2,14 @@ # # PROVIDE: netwait # REQUIRE: devd ipfw pf routing -# KEYWORD: nojail # -# The netwait script helps handle two situations: +# The netwait script helps handle three situations: # - Systems with USB or other late-attaching network hardware which # is initialized by devd events. The script waits for all the # interfaces named in the netwait_if list to appear. +# - Systems with IPv6 addresses, especially jails, where we need to +# wait for DAD to complete before starting daemons, as they will +# otherwise fail to bind to IN6ADDR_ANY. # - Systems with statically-configured IP addresses in rc.conf(5). # The IP addresses in the netwait_ip list are pinged. The script # waits for any single IP in the list to respond to the ping. If your @@ -29,28 +31,38 @@ netwait_start() { local ip rc count output link wait_if got_if any_error - if [ -z "${netwait_if}" ] && [ -z "${netwait_ip}" ]; then - err 1 "No interface or IP addresses listed, nothing to wait for" + if [ -z "${netwait_if}" ] && [ -z "${netwait_ip}" ] && + ! checkyesno netwait_dad ; then + err 1 "Nothing to wait for" fi - if [ ${netwait_timeout} -lt 1 ]; then + if ! [ "${netwait_if_timeout:=0}" -ge 1 ]; then + err 1 "netwait_if_timeout must be >= 1" + fi + if ! check_kern_features inet6; then + netwait_dad="NO" + elif ! [ "${netwait_dad_timeout:=0}" -ge 1 ]; then + netwait_dad_timeout=$(($(sysctl -n net.inet6.ip6.dad_count)+1)) + fi + if ! [ "${netwait_timeout:=0}" -ge 1 ]; then err 1 "netwait_timeout must be >= 1" fi + any_error=false + if [ -n "${netwait_if}" ]; then - any_error=0 for wait_if in ${netwait_if}; do echo -n "Waiting for ${wait_if}" link="" - got_if=0 + got_if=false count=1 - # Handle SIGINT (Ctrl-C); force abort of while() loop + # Handle SIGINT (Ctrl-C); force abort of while loop trap break SIGINT while [ ${count} -le ${netwait_if_timeout} ]; do if output=`/sbin/ifconfig ${wait_if} 2>/dev/null`; then - if [ ${got_if} -eq 0 ]; then + if ! ${got_if}; then echo -n ", interface present" - got_if=1 + got_if=true fi link=`expr "${output}" : '.*[[:blank:]]status: \(no carrier\)'` if [ -z "${link}" ]; then @@ -63,22 +75,45 @@ netwait_start() done # Restore default SIGINT handler trap - SIGINT - if [ ${got_if} -eq 0 ]; then + if ! ${got_if}; then echo ", wait failed: interface never appeared." - any_error=1 + any_error=true elif [ -n "${link}" ]; then echo ", wait failed: interface still has no link." - any_error=1 + any_error=true fi done - if [ ${any_error} -eq 1 ]; then - warn "Continuing with startup, but be aware you may not have " - warn "a fully functional networking layer at this point." - fi fi + if checkyesno netwait_dad; then + got_dad=false + # Handle SIGINT (Ctrl-C); force abort of while loop + trap break SIGINT + + echo -n "Waiting for DAD to complete" + count=1 + while [ ${count} -le ${netwait_dad_timeout} ]; do + if ! ifconfig | grep -q 'inet6.*tentative'; then + echo ', done.' + got_dad=true + break + fi + sleep 1 + count=$((count+1)) + done + + # Restore default SIGINT handler + trap - SIGINT + + if ! ${got_dad}; then + echo ', timed out.' + any_error=true + fi + fi + if [ -n "${netwait_ip}" ]; then - # Handle SIGINT (Ctrl-C); force abort of for() loop + got_ip=false + # Handle SIGINT (Ctrl-C); force abort of for loop trap break SIGINT for ip in ${netwait_ip}; do @@ -90,11 +125,9 @@ netwait_start() rc=$? if [ $rc -eq 0 ]; then - # Restore default SIGINT handler - trap - SIGINT - echo ', got response.' - return + got_ip=false + break 2 fi count=$((count+1)) done @@ -104,10 +137,15 @@ netwait_start() # Restore default SIGINT handler trap - SIGINT - warn "Exhausted IP list. Continuing with startup, but be aware you may" - warn "not have a fully functional networking layer at this point." + if ! ${got_ip}; then + any_error=true + fi fi + if ${any_error}; then + warn "Continuing with startup, but be aware you may not have " + warn "a fully functional networking layer at this point." + fi } load_rc_config $name diff --git a/libexec/rc/rc.d/serial b/libexec/rc/rc.d/serial index ea60c8aa77da..f8ddc7ff30d4 100755 --- a/libexec/rc/rc.d/serial +++ b/libexec/rc/rc.d/serial @@ -45,7 +45,7 @@ default() { # Reset everything changed by the other functions to initial defaults. dc=$1; shift # device name character - drainwait=`sysctl -n kern.drainwait` + drainwait=`sysctl -n kern.tty_drainwait` for i in $* do @@ -84,7 +84,7 @@ modem() { for i in $* do # may depend on modem - comcontrol /dev/tty${dc}${i} dtrwait 100 drainwait 180 + comcontrol /dev/tty${dc}${i} drainwait 180 # Lock crtscts on. # Speed reasonable for V42bis. stty < /dev/tty${dc}${i}.init crtscts 115200 @@ -156,12 +156,3 @@ terminal() { # modem u 1 # terminal u 0 # 3wire u 0 - -# Initialize all ports on a Cyclades-8yo. -# modem c 00 01 02 03 04 05 06 07 - -# Initialize all ports on a Cyclades-16ye. -# modem c 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f - -# Initialize all ports on a Digiboard 8. -# modem D 00 01 02 03 04 05 06 07 diff --git a/libexec/rc/rc.d/syscons b/libexec/rc/rc.d/syscons index 325628a83d8c..b01b648ace6e 100755 --- a/libexec/rc/rc.d/syscons +++ b/libexec/rc/rc.d/syscons @@ -238,6 +238,7 @@ syscons_configure_keyboard() sc_init echo -n ' allscreens_kbd' for ttyv in /dev/ttyv*; do + [ "$ttyv" = '/dev/ttyv*' ] && break kbdcontrol ${allscreens_kbdflags} < ${ttyv} > ${ttyv} 2>&1 done fi @@ -382,6 +383,7 @@ syscons_start() sc_init echo -n ' allscreens' for ttyv in /dev/ttyv*; do + [ "$ttyv" = '/dev/ttyv*' ] && break vidcontrol ${allscreens_flags} < ${ttyv} > ${ttyv} 2>&1 done fi diff --git a/libexec/rc/rc.d/virtual_oss b/libexec/rc/rc.d/virtual_oss new file mode 100644 index 000000000000..4f5c34ce03f3 --- /dev/null +++ b/libexec/rc/rc.d/virtual_oss @@ -0,0 +1,119 @@ +#!/bin/sh + +# PROVIDE: virtual_oss +# REQUIRE: kld ldconfig +# BEFORE: LOGIN sndiod +# KEYWORD: shutdown + +. /etc/rc.subr + +name="virtual_oss" +desc="virtual_oss device manager" +rcvar="${name}_enable" + +command="/usr/sbin/${name}" +command_args="-B" + +load_rc_config "$name" +start_precmd="${name}_precmd" +start_cmd="${name}_start" +stop_cmd="${name}_stop" +status_cmd="${name}_status" + +configs= +pidpath="/var/run/${name}" +virtual_oss_default_args="\ + -S \ + -C 2 \ + -c 2 \ + -r 48000 \ + -b 24 \ + -s 8ms \ + -i 8 \ + -f /dev/dsp \ + -d dsp \ + -t vdsp.ctl" + +# Set to NO by default. Set it to "YES" to enable virtual_oss. +: "${virtual_oss_enable:="NO"}" + +# List of configurations to use. Default is "dsp". +: "${virtual_oss_configs:="dsp"}" + +# Default (dsp) virtual_oss config. +: "${virtual_oss_dsp:="${virtual_oss_default_args}"}" + +virtual_oss_pids() +{ + pids=$(pgrep -d ' ' ${name}) + pids=${pids% } + printf '%s\n' "${pids}" +} + +virtual_oss_precmd() +{ + /usr/bin/install -d -m 0755 -o root "${pidpath}" + load_kld cuse +} + +start_instance() +{ + config="$1" + instance_args=$(eval "echo \$virtual_oss_${config}") + if [ -z "${instance_args}" ]; then + warn "no such config: ${config}" + else + startmsg -n "Starting virtual_oss config: ${config}: " + ${command} \ + ${command_args} \ + -D "${pidpath}/${config}.pid" \ + ${instance_args} + startmsg "done" + fi +} + +stop_instance() +{ + config="$1" + instance_args=$(eval "echo \$virtual_oss_${config}") + if [ -z "${instance_args}" ]; then + warn "no such config: ${config}" + else + startmsg -n "Stopping virtual_oss config: ${config}: " + kill "$(cat "${pidpath}/${config}.pid")" + rm -f "${pidpath}/${config}.pid" + startmsg "done" + fi +} + +virtual_oss_start() +{ + configs="$1" + [ -z "${configs}" ] && configs="${virtual_oss_configs}" + for config in ${configs}; do + start_instance "${config}" + done +} + +virtual_oss_stop() +{ + configs="$1" + [ -z "${configs}" ] && configs="${virtual_oss_configs}" + for config in ${configs}; do + stop_instance "${config}" + done +} + +virtual_oss_status() +{ + pids=$(virtual_oss_pids) + + if [ "${pids}" ]; then + echo "${name} is running as pid ${pids}." + else + echo "${name} is not running." + return 1 + fi +} + +run_rc_command "$@" diff --git a/libexec/rc/rc.subr b/libexec/rc/rc.subr index 06b1bd51384c..6be226021949 100644 --- a/libexec/rc/rc.subr +++ b/libexec/rc/rc.subr @@ -804,7 +804,6 @@ wait_for_pids() for _j in $_list; do if kill -0 $_j 2>/dev/null; then _nlist="${_nlist}${_nlist:+ }$_j" - [ -n "$_prefix" ] && sleep 1 fi done if [ -z "$_nlist" ]; then @@ -814,6 +813,10 @@ wait_for_pids() echo -n ${_prefix:-"Waiting for PIDS: "}$_list _prefix=", " pwait -o $_list 2>/dev/null + # At least one of the processes we were waiting for + # has terminated. Give init a chance to collect it + # before looping around and checking again. + sleep 1 done if [ -n "$_prefix" ]; then echo "." diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 8977d5947010..d27af520c21d 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -859,6 +859,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) linkmap_add(obj_main); linkmap_add(&obj_rtld); + LD_UTRACE(UTRACE_LOAD_OBJECT, obj_main, obj_main->mapbase, + obj_main->mapsize, 0, obj_main->path); + LD_UTRACE(UTRACE_LOAD_OBJECT, &obj_rtld, obj_rtld.mapbase, + obj_rtld.mapsize, 0, obj_rtld.path); /* Link the main program into the list of objects. */ TAILQ_INSERT_HEAD(&obj_list, obj_main, next); @@ -2437,11 +2441,21 @@ parse_rtld_phdr(Obj_Entry *obj) { const Elf_Phdr *ph; Elf_Addr note_start, note_end; + bool first_seg; + first_seg = true; obj->stack_flags = PF_X | PF_R | PF_W; for (ph = obj->phdr; (const char *)ph < (const char *)obj->phdr + obj->phsize; ph++) { switch (ph->p_type) { + case PT_LOAD: + if (first_seg) { + obj->vaddrbase = rtld_trunc_page(ph->p_vaddr); + first_seg = false; + } + obj->mapsize = rtld_round_page(ph->p_vaddr + + ph->p_memsz) - obj->vaddrbase; + break; case PT_GNU_STACK: obj->stack_flags = ph->p_flags; break; diff --git a/libexec/rtld-elf/tests/Makefile.inc b/libexec/rtld-elf/tests/Makefile.inc index 01cf83634239..3bd0b8590cdc 100644 --- a/libexec/rtld-elf/tests/Makefile.inc +++ b/libexec/rtld-elf/tests/Makefile.inc @@ -1,2 +1,3 @@ PACKAGE?= tests +NO_DEV_PACKAGE= TESTSDIR?= ${TESTSBASE}/libexec/rtld-elf |