diff options
Diffstat (limited to 'usr.sbin/ypserv')
| -rw-r--r-- | usr.sbin/ypserv/Makefile | 51 | ||||
| -rw-r--r-- | usr.sbin/ypserv/Makefile.depend | 18 | ||||
| -rw-r--r-- | usr.sbin/ypserv/Makefile.depend.options | 5 | ||||
| -rw-r--r-- | usr.sbin/ypserv/Makefile.yp | 694 | ||||
| -rw-r--r-- | usr.sbin/ypserv/common/yplib_host.c | 353 | ||||
| -rw-r--r-- | usr.sbin/ypserv/common/yplib_host.h | 51 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_access.c | 332 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_dblookup.c | 727 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_dnslookup.c | 545 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_error.c | 71 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_extern.h | 115 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_main.c | 578 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_server.c | 985 | ||||
| -rw-r--r-- | usr.sbin/ypserv/yp_svc_udp.c | 71 | ||||
| -rw-r--r-- | usr.sbin/ypserv/ypinit.8 | 198 | ||||
| -rw-r--r-- | usr.sbin/ypserv/ypinit.sh | 386 | ||||
| -rw-r--r-- | usr.sbin/ypserv/ypserv.8 | 461 |
17 files changed, 5641 insertions, 0 deletions
diff --git a/usr.sbin/ypserv/Makefile b/usr.sbin/ypserv/Makefile new file mode 100644 index 000000000000..b4e59b719692 --- /dev/null +++ b/usr.sbin/ypserv/Makefile @@ -0,0 +1,51 @@ +.include <src.opts.mk> + +RPCDIR= ${SRCTOP}/include/rpcsvc +.PATH: ${RPCDIR} ${.CURDIR}/common + +PACKAGE= yp +PROG= ypserv +MAN= ypserv.8 ypinit.8 +SRCS= yp_svc.c yp_server.c yp_dblookup.c yp_dnslookup.c \ + ypxfr_clnt.c yp.h yp_main.c yp_error.c yp_access.c yp_svc_udp.c \ + yplib_host.c + +CFLAGS+= -DDB_CACHE -I. + +WARNS?= 0 + +.if ${MK_TCP_WRAPPERS} != "no" +CFLAGS+= -DTCP_WRAPPER +LIBADD+= wrap +.endif + +CLEANFILES= yp_svc.c ypxfr_clnt.c yp.h + +RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -I -C + +# We need to remove the 'static' keyword from _rpcsvcstate so that +# yp_main.c can see it. +yp_svc.c: yp.x + rm -f ${.TARGET} + ${RPCGEN} -DYPSERV_ONLY -m ${RPCDIR}/yp.x | \ + sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET} + +ypxfr_clnt.c: yp.x + ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x + +yp.h: yp.x + ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x + +FILES= Makefile.yp +FILESNAME= Makefile.dist +FILESDIR= /var/yp +SCRIPTS= ypinit.sh + +# If NO_ROOT is defined, we are doing a stage install and always need to +# install the symlink, otherwise, don't overwrite the user's existing +# symlink. +.if defined(NO_ROOT) || !exists(${DESTDIR}${FILESDIR}/Makefile) +SYMLINKS= ${FILESNAME} ${FILESDIR}/Makefile +.endif + +.include <bsd.prog.mk> diff --git a/usr.sbin/ypserv/Makefile.depend b/usr.sbin/ypserv/Makefile.depend new file mode 100644 index 000000000000..732a025c9552 --- /dev/null +++ b/usr.sbin/ypserv/Makefile.depend @@ -0,0 +1,18 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + include \ + include/arpa \ + include/rpc \ + include/rpcsvc \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/ypserv/Makefile.depend.options b/usr.sbin/ypserv/Makefile.depend.options new file mode 100644 index 000000000000..fbc21670804e --- /dev/null +++ b/usr.sbin/ypserv/Makefile.depend.options @@ -0,0 +1,5 @@ +# This file is not autogenerated - take care! + +DIRDEPS_OPTIONS= TCP_WRAPPERS + +.include <dirdeps-options.mk> diff --git a/usr.sbin/ypserv/Makefile.yp b/usr.sbin/ypserv/Makefile.yp new file mode 100644 index 000000000000..18d6509a9fa7 --- /dev/null +++ b/usr.sbin/ypserv/Makefile.yp @@ -0,0 +1,694 @@ +# +# Makefile for the NIS databases +# +# +# This Makefile should only be run on the NIS master server of a domain. +# All updated maps will be pushed to all NIS slave servers listed in the +# /var/yp/ypservers file. Please make sure that the hostnames of all +# NIS servers in your domain are listed in /var/yp/ypservers. +# +# This Makefile can be modified to support more NIS maps if desired. +# + +# If this machine is an NIS master, reset this variable (NOPUSH=) +# in Makefile.local so that changes to the NIS maps can be propagated to +# the slave servers. (By default we assume that we are only serving a +# small domain with only one server.) +# +NOPUSH = "True" + +# If this machine does not wish to generate a linux-style shadow map +# from the master.passwd file, reset this variable (SHADOW=) in +# Makefile.local. +SHADOW = "True" + +# If you want to use a FreeBSD NIS server to serve non-FreeBSD clients +# (i.e. clients who expect the password field in the passwd maps to be +# valid) then set this variable (UNSECURE="True") in Makefile.local. +# This will cause $YPDIR/passwd to be generated with valid password +# fields. This is insecure: FreeBSD normally only serves the +# master.passwd and shadow maps (which have real encrypted passwords +# in them) to the superuser on other FreeBSD machines, but non-FreeBSD +# clients (e.g. SunOS, Solaris (without NIS+), IRIX, HP-UX, etc...) +# will only work properly in 'unsecure' mode. +# +#UNSECURE = "True" + +# The following line encodes the YP_INTERDOMAIN key into the hosts.byname +# and hosts.byaddr maps so that ypserv(8) will do DNS lookups to resolve +# hosts not in the current domain. Resetting this variable in +# Makefile.local (B=) will disable the DNS lookups. +B=-b + +# Normally, the master.passwd.* and shadow.* maps are guarded against access +# from non-privileged users. By resetting S in Makefile.local (S=), the +# YP_SECURE key will be removed from these maps, allowing anyone to access +# them. +S=-s + +# These are commands which this Makefile needs to properly rebuild the +# NIS databases. Don't change these unless you have a good reason. Also +# be sure not to place an @ in front of /usr/bin/awk: it isn't necessary +# and it'll break everything in sight. +# +AWK = /usr/bin/awk +RM = @/bin/rm -f +MV = @/bin/mv -f +RMV = /bin/mv -f + +MKDB = /usr/sbin/yp_mkdb +DBLOAD = $(MKDB) -m `hostname` +MKNETID = /usr/libexec/mknetid +NEWALIASES = /usr/bin/newaliases +YPPUSH = /usr/sbin/yppush +.if !defined(UPDATE_DOMAIN) +DOMAIN = `/bin/domainname` +.else +DOMAIN = $(UPDATE_DOMAIN) +.endif +REVNETGROUP = /usr/libexec/revnetgroup +TMP = `echo $@.$$$$` + +# It is advisable to create a separate directory to contain the +# source files used to generate your NIS maps. If you intend to +# support multiple domains, something like /src/dir/$DOMAIN +# would work well. +YPSRCDIR = /etc +.if !defined(YP_DIR) +YPDIR = /var/yp +.else +YPDIR = $(YP_DIR) +.endif +YPMAPDIR = $(YPDIR)/$(DOMAIN) + +# These are the files from which the NIS databases are built. You may edit +# these to taste in the event that you wish to keep your NIS source files +# separate from your NIS server's actual configuration files. Note that the +# NIS passwd and master.passwd files are stored in /var/yp: the server's +# real password database is not used by default. However, you may use +# the real /etc/passwd and /etc/master.passwd files by: +# +# +# - invoking yppasswdd with `-t /etc/master.passwd' (yppasswdd will do a +# 'pwd_mkdb' as needed if /etc/master.passwd is thus specified). +# - Specifying the location of the master.passwd file using the +# MASTER_PASSWD variable, i.e.: +# +# # make MASTER_PASSWD=/path/to/some/other/master.passwd +# +# - (optionally): editing this Makefile to change the default location. +# +# To add a user, edit $(YPDIR)/master.passwd and type 'make'. The raw +# passwd file will be generated from the master.passwd file automagically. +# +ETHERS = $(YPSRCDIR)/ethers # ethernet addresses (for rarpd) +EUI64 = $(YPSRCDIR)/eui64 # eui64 addresses (for firewire) +BOOTPARAMS= $(YPSRCDIR)/bootparams # for booting Sun boxes (bootparamd) +HOSTS = $(YPSRCDIR)/hosts +IPNODES = $(YPDIR)/ipnodes +NETWORKS = $(YPSRCDIR)/networks +PROTOCOLS = $(YPSRCDIR)/protocols +RPC = $(YPSRCDIR)/rpc +SERVICES = $(YPSRCDIR)/services +SHELLS = $(YPSRCDIR)/shells +GROUP = $(YPSRCDIR)/group +ALIASES = $(YPSRCDIR)/mail/aliases +NETGROUP = $(YPDIR)/netgroup +PASSWD = $(YPDIR)/passwd +.if !defined(MASTER_PASSWD) +MASTER = $(YPDIR)/master.passwd +.else +MASTER = $(MASTER_PASSWD) +.endif +YPSERVERS = $(YPDIR)/ypservers # List of all NIS servers for a domain +PUBLICKEY = $(YPSRCDIR)/publickey +NETID = $(YPSRCDIR)/netid +AMDHOST = $(YPSRCDIR)/amd.map + +target: + @if [ ! -d $(DOMAIN) ]; then mkdir $(DOMAIN); fi; \ + cd $(DOMAIN) ; echo "NIS Map update started on `date` for domain $(DOMAIN)" ; \ + make -f ../Makefile all; echo "NIS Map update completed." + +# Read overrides. Note, the current directory will be /var/yp/<domain> +# when 'all' is built. +.if exists(${YPDIR}/Makefile.local) +.include "${YPDIR}/Makefile.local" +.endif + +# List of maps that are always built. +# If you want to omit some of them, feel free to comment +# them out from this list. +TARGETS= servers hosts networks protocols rpc services shells group +TARGETS+= ipnodes +#TARGETS+= aliases + +# Sanity checks: filter out targets we can't build +# Note that we don't build the ethers, eui64, or boorparams maps by default +# since /etc/ethers, /etc/eui64 and /etc/bootparams are not likely to be present +# on all systems. +.if exists($(ETHERS)) +TARGETS+= ethers +.else +ETHERS= /dev/null +.endif + +.if exists($(EUI64)) +TARGETS+= eui64 +.else +EUI64= /dev/null +.endif + +.if exists($(BOOTPARAMS)) +TARGETS+= bootparams +.else +BOOTPARAMS= /dev/null +.endif + +.if exists($(NETGROUP)) +TARGETS+= netgrp +.else +NETGROUP= /dev/null +.endif + +.if exists($(MASTER)) +TARGETS+= passwd master.passwd netid +.if ${SHADOW} == "\"True\"" +TARGETS+= shadow +.endif +.else +MASTER= /dev/null +TARGETS+= nopass +.endif + +.if exists($(PUBLICKEY)) +TARGETS+= publickey +.else +PUBLICKEY= /dev/null +.endif + +.if exists($(AMDHOST)) +TARGETS+= amd.map +.else +AMDHOST= /dev/null +.endif + +.if !exists($(IPNODES)) +IPNODES= $(HOSTS) +.endif + +all: $(TARGETS) + +ethers: ethers.byname ethers.byaddr +eui64: eui64.byname eui64.byid +bootparam: bootparams +hosts: hosts.byname hosts.byaddr +ipnodes: ipnodes.byname ipnodes.byaddr +networks: networks.byaddr networks.byname +protocols: protocols.bynumber protocols.byname +rpc: rpc.byname rpc.bynumber +services: services.byname +passwd: passwd.byname passwd.byuid +shadow: shadow.byname shadow.byuid +group: group.byname group.bygid +netgrp: netgroup +netid: netid.byname +servers: ypservers +publickey: publickey.byname +aliases: mail.aliases + +master.passwd: master.passwd.byname master.passwd.byuid + +# +# This is a special target used only when doing in-place updates with +# rpc.yppasswdd. In this case, the maps will be updated by the rpc.yppasswdd +# server and won't need to be remade. They will have to be pushed to the +# slaves however. Calling this target implicitly insures that this will +# happen. +# +pushpw: + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) master.passwd.byname ; fi + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) master.passwd.byuid ; fi +.if ${SHADOW} == "\"True\"" + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) shadow.byname ; fi + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) shadow.byuid ; fi +.endif + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) passwd.byname ; fi + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) passwd.byuid ; fi + +pushmap: + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $(PUSHMAP) ; fi + +nopass: + @echo "" + @echo " ********WARNING********" + @echo " Couldn't find the master.passwd source file. This file" + @echo " is needed to generate the master.passwd and passwd maps." + @echo " The default location is /var/yp/master.passwd. You should" + @echo " edit /var/yp/Makefile and set the MASTER variable to point" + @echo " to the source file you wish to use for building the passwd" + @echo " maps, or else invoke make(1) in the following manner:" + @echo "" + @echo " make MASTER_PASSWD=/path/to/master.passwd" + @echo "" + +mail.aliases: $(ALIASES) + @echo "Updating $@..." + @$(NEWALIASES) -oA$(ALIASES) + @$(MKDB) -u $(ALIASES).db \ + | $(DBLOAD) -i $(ALIASES) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +ypservers: $(YPSERVERS) + @echo "Updating $@..." + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*") print $$0"\t"$$0 }' \ + $(YPSERVERS) \ + | $(DBLOAD) -i $(YPSERVERS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + +ethers.byname: $(ETHERS) + @echo "Updating $@..." +.if ${ETHERS} == "/dev/null" + @echo "Ethers source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$2"\t"$$0 }' $(ETHERS) | $(DBLOAD) -i $(ETHERS) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + +ethers.byaddr: $(ETHERS) + @echo "Updating $@..." +.if ${ETHERS} == "/dev/null" + @echo "Ethers source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$0 }' $(ETHERS) | $(DBLOAD) -i $(ETHERS) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + +eui64.byname: $(EUI64) + @echo "Updating $@..." +.if ${EUI64} == "/dev/null" + @echo "EUI64 source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$2"\t"$$0 }' $(EUI64) | $(DBLOAD) -i $(EUI64) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + +eui64.byid: $(EUI64) + @echo "Updating $@..." +.if ${EUI64} == "/dev/null" + @echo "EUI64 source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$0 }' $(EUI64) | $(DBLOAD) -i $(EUI64) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +bootparams: $(BOOTPARAMS) + @echo "Updating $@..." +.if ${BOOTPARAMS} == "/dev/null" + @echo "Bootparams source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$0 }' $(BOOTPARAMS) | $(DBLOAD) -i $(BOOTPARAMS) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +netgroup: $(NETGROUP) netgroup.byhost netgroup.byuser + @echo "Updating $@..." +.if ${NETGROUP} == "/dev/null" + @echo "Netgroup source file not found -- skipping" +.else + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$0 }' $(NETGROUP) | $(DBLOAD) -i $(NETGROUP) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +netgroup.byhost: $(NETGROUP) + @echo "Updating $@..." +.if ${NETGROUP} == "/dev/null" + @echo "Netgroup source file not found -- skipping" +.else + @$(REVNETGROUP) -h -f $(NETGROUP) | \ + $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$0 }' | $(DBLOAD) -i $(NETGROUP) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +netgroup.byuser: $(NETGROUP) + @echo "Updating $@..." +.if ${NETGROUP} == "/dev/null" + @echo "Netgroup source file not found -- skipping" +.else + @$(REVNETGROUP) -u -f $(NETGROUP) | \ + $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$0 }' | $(DBLOAD) -i $(NETGROUP) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +# Solaris 8 does the following: +# - /etc/hosts and hosts.{byname,byaddr} are IPv4 only. +# - /etc/inet/ipnodes and ipnodes.{byname,byaddr} are used for protocol +# independent name-to-address mapping. +# +# For local name resolution, we made /etc/hosts protocol independent. +# For NIS name resolution, we obey Solaris 8 practice. +# - We keep hosts.{byname,byaddr} IPv4 only, to be friendly with Solaris 8 +# clients. +# - ipnodes.{byname,byaddr} is used for protocol independent mapping. +# We generate all the mappings from /etc/hosts unless /var/yp/ipnodes +# exists, for compatibility with FreeBSD local name resolution. +# +hosts.byname: $(HOSTS) + @echo "Updating $@..." + @$(AWK) '/^[0-9.]+[\t ]/ { for (n=2; n<=NF && $$n !~ "^#.*"; n++) \ + print $$n"\t"$$0 }' $(HOSTS) | $(DBLOAD) ${B} -i $(HOSTS) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +hosts.byaddr: $(HOSTS) + @echo "Updating $@..." + @$(AWK) '/^[0-9.]+[\t ]/ { print $$1"\t"$$0 }' $(HOSTS) \ + | $(DBLOAD) ${B} -i $(HOSTS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +ipnodes.byname: $(IPNODES) + @echo "Updating $@..." + @$(AWK) '/^[0-9a-fA-F:]/ { for (n=2; n<=NF && $$n !~ "^#.*"; n++) \ + print $$n"\t"$$0 }' $(IPNODES) | $(DBLOAD) ${B} -i $(IPNODES) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +ipnodes.byaddr: $(IPNODES) + @echo "Updating $@..." + @$(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$0 }' $(IPNODES) \ + | $(DBLOAD) ${B} -i $(IPNODES) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +networks.byname: $(NETWORKS) + @echo "Updating $@..." + @$(AWK) \ + '$$1 !~ "^#.*" { print $$1"\t"$$0; \ + for (n=3; n<=NF && $$n !~ "^#.*"; n++) \ + print $$n"\t"$$0 \ + }' $(NETWORKS) \ + | $(DBLOAD) -i $(NETWORKS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +networks.byaddr: $(NETWORKS) + @echo "Updating $@..." + @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(NETWORKS) \ + | $(DBLOAD) -i $(NETWORKS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +protocols.byname: $(PROTOCOLS) + @echo "Updating $@..." + @$(AWK) \ + '$$1 !~ "^#.*" { print $$1"\t"$$0; \ + for (n=3; n<=NF && $$n !~ "^#.*"; n++) \ + print $$n"\t"$$0 \ + }' $(PROTOCOLS) | $(DBLOAD) -i $(PROTOCOLS) \ + -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +protocols.bynumber: $(PROTOCOLS) + @echo "Updating $@..." + @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(PROTOCOLS) \ + | $(DBLOAD) -i $(PROTOCOLS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +rpc.byname: $(RPC) + @echo "Updating $@..." + @$(AWK) \ + '$$1 !~ "^#.*" { print $$1"\t"$$0; \ + for (n=3; n<=NF && $$n !~ "^#.*"; n++) \ + print $$n"\t"$$0 \ + }' $(RPC) | $(DBLOAD) -i $(RPC) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +rpc.bynumber: $(RPC) + @echo "Updating $@..." + @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(RPC) \ + | $(DBLOAD) -i $(RPC) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +services.byname: $(SERVICES) + @echo "Updating $@..." + @$(AWK) \ + '$$1 !~ "^#.*" { for (n=1; n<=NF && $$n !~ "^#.*"; n++) { \ + if (split($$2, t, "/")) { \ + printf("%s/%s", $$n, t[2]) }; \ + print "\t"$$0; \ + if (n == 1) n = 2; \ + } ; print $$2"\t"$$0 ; \ + }' $(SERVICES) \ + | $(DBLOAD) -i $(SERVICES) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + +shells: $(SHELLS) + @echo "Updating $@..." + @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*") print $$0"\t"$$0 }' \ + $(SHELLS) \ + | $(DBLOAD) -i $(SHELLS) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + +publickey.byname: $(PUBLICKEY) + @echo "Updating $@..." +.if ${PUBLICKEY} == "/dev/null" + @echo "Publickey source file not found -- skipping" +.else + @$(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$2 }' $(PUBLICKEY) \ + | $(DBLOAD) -i $(PUBLICKEY) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +$(PASSWD): $(MASTER) + @echo "Creating new $@ file from $(MASTER)..." + @if [ ! $(UNSECURE) ]; then \ + $(AWK) -F: '{if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1":*:"$$3":"$$4":"$$8":"$$9":"$$10}' $(MASTER) \ + > $(PASSWD) ; \ + else \ + $(AWK) -F: '{if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1":"$$2":"$$3":"$$4":"$$8":"$$9":"$$10}' $(MASTER) \ + > $(PASSWD) ; fi + + +passwd.byname: $(PASSWD) + @echo "Updating $@..." + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$0 }' $(PASSWD) \ + | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +passwd.byuid: $(PASSWD) + @echo "Updating $@..." + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$3"\t"$$0 }' $(PASSWD) \ + | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +group.byname: $(GROUP) + @echo "Updating $@..." + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$0 }' $(GROUP) \ + | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +group.bygid: $(GROUP) + @echo "Updating $@..." + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$3"\t"$$0 }' $(GROUP) \ + | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +netid.byname: $(GROUP) $(PASSWD) $(HOSTS) + @echo "Updating $@..." + @$(MKNETID) -q -p $(PASSWD) -g $(GROUP) -h $(HOSTS) -n $(NETID) \ + -d $(DOMAIN) | $(DBLOAD) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi + + +master.passwd.byname: $(MASTER) + @echo "Updating $@..." +.if ${MASTER} == "/dev/null" + @echo "Master.passwd source file not found -- skipping" +.else + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$0 }' $(MASTER) \ + | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +master.passwd.byuid: $(MASTER) + @echo "Updating $@..." +.if ${MASTER} == "/dev/null" + @echo "Master.passwd source file not found -- skipping" +.else + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$3"\t"$$0 }' $(MASTER) \ + | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + + +shadow.byname: $(MASTER) + @echo "Updating $@..." +.if ${MASTER} == "/dev/null" + @echo "Master.passwd source file not found -- skipping" +.else + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$1"\t"$$1":"$$2":12000:0:99999:7:::" }' $(MASTER) \ + | sed 's/\( [^:]*:\)\*:/\1!:/' \ + | $(DBLOAD) ${S} -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + +shadow.byuid: $(MASTER) + @echo "Updating $@..." +.if ${MASTER} == "/dev/null" + @echo "Master.passwd source file not found -- skipping" +.else + @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \ + print $$3"\t"$$1":"$$2":12000:0:99999:7:::" }' $(MASTER) \ + | sed 's/\( [^:]*:\)\*:/\1!:/' \ + | $(DBLOAD) ${S} -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi +.endif + +amd.map: $(AMDHOST) + @echo "Updating $@..." + @$(AWK) '$$1 !~ "^#.*" { \ + for (i = 1; i <= NF; i++) \ + if (i == NF) { \ + if (substr($$i, length($$i), 1) == "\\") \ + printf("%s", substr($$i, 1, length($$i) - 1)); \ + else \ + printf("%s\n", $$i); \ + } \ + else \ + printf("%s ", $$i); \ + }' $(AMDHOST) | \ + $(DBLOAD) -i $(AMDHOST) -o $(YPMAPDIR)/$@ - $(TMP); \ + $(RMV) $(TMP) $@ + @$(DBLOAD) -c + @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi + @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi diff --git a/usr.sbin/ypserv/common/yplib_host.c b/usr.sbin/ypserv/common/yplib_host.c new file mode 100644 index 000000000000..2751c5950d87 --- /dev/null +++ b/usr.sbin/ypserv/common/yplib_host.c @@ -0,0 +1,353 @@ +/* $OpenBSD: yplib_host.c,v 1.18 2015/01/16 06:40:22 deraadt Exp $ */ + +/* + * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@theos.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/file.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <rpc/rpc.h> +#include <rpc/xdr.h> +#include <rpcsvc/yp.h> +#include <rpcsvc/ypclnt.h> + +#include "yplib_host.h" + +extern bool_t xdr_ypresp_all_seq(XDR *, unsigned long *); + +extern int (*ypresp_allfn)(u_long, char *, int, char *, int, void *); +extern void *ypresp_data; + +static int _yplib_host_timeout = 10; + +CLIENT * +yp_bind_host(char *server, u_long program, u_long version, u_short port, + int usetcp) +{ + struct sockaddr_in rsrv_sin; + static CLIENT *client; + struct hostent *h; + struct timeval tv; + int rsrv_sock; + + memset(&rsrv_sin, 0, sizeof rsrv_sin); + rsrv_sin.sin_len = sizeof rsrv_sin; + rsrv_sin.sin_family = AF_INET; + rsrv_sock = RPC_ANYSOCK; + if (port != 0) + rsrv_sin.sin_port = htons(port); + + if (*server >= '0' && *server <= '9') { + if (inet_aton(server, &rsrv_sin.sin_addr) == 0) { + errx(1, "inet_aton: invalid address %s.", + server); + } + } else { + h = gethostbyname(server); + if (h == NULL) { + errx(1, "gethostbyname: unknown host %s.", + server); + } + rsrv_sin.sin_addr.s_addr = *(u_int32_t *)h->h_addr; + } + + tv.tv_sec = 10; + tv.tv_usec = 0; + + if (usetcp) + client = clnttcp_create(&rsrv_sin, program, version, + &rsrv_sock, 0, 0); + else + client = clntudp_create(&rsrv_sin, program, version, tv, + &rsrv_sock); + + if (client == NULL) { + errx(1, "clntudp_create: no contact with host %s.", + server); + } + + return (client); +} + +CLIENT * +yp_bind_local(u_long program, u_long version) +{ + struct sockaddr_in rsrv_sin; + static CLIENT *client; + struct timeval tv; + int rsrv_sock; + + memset(&rsrv_sin, 0, sizeof rsrv_sin); + rsrv_sin.sin_len = sizeof rsrv_sin; + rsrv_sin.sin_family = AF_INET; + rsrv_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + rsrv_sock = RPC_ANYSOCK; + + tv.tv_sec = 10; + tv.tv_usec = 0; + + client = clntudp_create(&rsrv_sin, program, version, tv, &rsrv_sock); + if (client == NULL) { + errx(1, "clntudp_create: no contact with localhost."); + } + + return (client); +} + +int +yp_match_host(CLIENT *client, char *indomain, char *inmap, const char *inkey, + int inkeylen, char **outval, int *outvallen) +{ + struct ypresp_val yprv; + struct ypreq_key yprk; + struct timeval tv; + int r; + + *outval = NULL; + *outvallen = 0; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + yprk.domain = indomain; + yprk.map = inmap; + yprk.key.keydat_val = (char *)inkey; + yprk.key.keydat_len = inkeylen; + + memset(&yprv, 0, sizeof yprv); + + r = clnt_call(client, YPPROC_MATCH, + (xdrproc_t)xdr_ypreq_key, &yprk, + (xdrproc_t)xdr_ypresp_val, &yprv, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_match_host: clnt_call"); + if ( !(r = ypprot_err(yprv.stat)) ) { + *outvallen = yprv.val.valdat_len; + *outval = malloc(*outvallen + 1); + memcpy(*outval, yprv.val.valdat_val, *outvallen); + (*outval)[*outvallen] = '\0'; + } + xdr_free((xdrproc_t)xdr_ypresp_val, (char *)&yprv); + + return (r); +} + +int +yp_first_host(CLIENT *client, char *indomain, char *inmap, char **outkey, + int *outkeylen, char **outval, int *outvallen) +{ + struct ypresp_key_val yprkv; + struct ypreq_nokey yprnk; + struct timeval tv; + int r; + + *outkey = *outval = NULL; + *outkeylen = *outvallen = 0; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + yprnk.domain = indomain; + yprnk.map = inmap; + memset(&yprkv, 0, sizeof yprkv); + + r = clnt_call(client, YPPROC_FIRST, + (xdrproc_t)xdr_ypreq_nokey, &yprnk, + (xdrproc_t)xdr_ypresp_key_val, &yprkv, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_first_host: clnt_call"); + if ( !(r = ypprot_err(yprkv.stat)) ) { + *outkeylen = yprkv.key.keydat_len; + *outkey = malloc(*outkeylen+1); + memcpy(*outkey, yprkv.key.keydat_val, *outkeylen); + (*outkey)[*outkeylen] = '\0'; + *outvallen = yprkv.val.valdat_len; + *outval = malloc(*outvallen+1); + memcpy(*outval, yprkv.val.valdat_val, *outvallen); + (*outval)[*outvallen] = '\0'; + } + xdr_free((xdrproc_t)xdr_ypresp_key_val, (char *)&yprkv); + + return (r); +} + +int +yp_next_host(CLIENT *client, char *indomain, char *inmap, char *inkey, + int inkeylen, char **outkey, int *outkeylen, char **outval, int *outvallen) +{ + struct ypresp_key_val yprkv; + struct ypreq_key yprk; + struct timeval tv; + int r; + + *outkey = *outval = NULL; + *outkeylen = *outvallen = 0; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + yprk.domain = indomain; + yprk.map = inmap; + yprk.key.keydat_val = inkey; + yprk.key.keydat_len = inkeylen; + memset(&yprkv, 0, sizeof yprkv); + + r = clnt_call(client, YPPROC_NEXT, + (xdrproc_t)xdr_ypreq_key, &yprk, + (xdrproc_t)xdr_ypresp_key_val, &yprkv, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_next_host: clnt_call"); + if ( !(r = ypprot_err(yprkv.stat)) ) { + *outkeylen = yprkv.key.keydat_len; + *outkey = malloc(*outkeylen+1); + memcpy(*outkey, yprkv.key.keydat_val, *outkeylen); + (*outkey)[*outkeylen] = '\0'; + *outvallen = yprkv.val.valdat_len; + *outval = malloc(*outvallen+1); + memcpy(*outval, yprkv.val.valdat_val, *outvallen); + (*outval)[*outvallen] = '\0'; + } + xdr_free((xdrproc_t)xdr_ypresp_key_val, (char *)&yprkv); + + return (r); +} + +int +yp_all_host(CLIENT *client, char *indomain, char *inmap, + struct ypall_callback *incallback) +{ + struct ypreq_nokey yprnk; + struct timeval tv; + u_long status; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + yprnk.domain = indomain; + yprnk.map = inmap; + ypresp_allfn = incallback->foreach; + ypresp_data = (void *)incallback->data; + + (void) clnt_call(client, YPPROC_ALL, + (xdrproc_t)xdr_ypreq_nokey, &yprnk, + (xdrproc_t)xdr_ypresp_all_seq, &status, tv); + if (status != YP_FALSE) + return ypprot_err(status); + + return (0); +} + +int +yp_order_host(CLIENT *client, char *indomain, char *inmap, u_int32_t *outorder) +{ + struct ypresp_order ypro; + struct ypreq_nokey yprnk; + struct timeval tv; + int r; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + yprnk.domain = indomain; + yprnk.map = inmap; + + memset(&ypro, 0, sizeof ypro); + + r = clnt_call(client, YPPROC_ORDER, + (xdrproc_t)xdr_ypreq_nokey, &yprnk, + (xdrproc_t)xdr_ypresp_order, &ypro, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_order_host: clnt_call"); + *outorder = ypro.ordernum; + xdr_free((xdrproc_t)xdr_ypresp_order, (char *)&ypro); + + return ypprot_err(ypro.stat); +} + +int +yp_master_host(CLIENT *client, char *indomain, char *inmap, char **outname) +{ + struct ypresp_master yprm; + struct ypreq_nokey yprnk; + struct timeval tv; + int r; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + yprnk.domain = indomain; + yprnk.map = inmap; + + memset(&yprm, 0, sizeof yprm); + + r = clnt_call(client, YPPROC_MASTER, + (xdrproc_t)xdr_ypreq_nokey, &yprnk, + (xdrproc_t)xdr_ypresp_master, &yprm, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_master: clnt_call"); + if (!(r = ypprot_err(yprm.stat))) + *outname = strdup(yprm.peer); + xdr_free((xdrproc_t)xdr_ypresp_master, (char *)&yprm); + + return (r); +} + +int +yp_maplist_host(CLIENT *client, char *indomain, struct ypmaplist **outmaplist) +{ + struct ypresp_maplist ypml; + struct timeval tv; + int r; + + tv.tv_sec = _yplib_host_timeout; + tv.tv_usec = 0; + + memset(&ypml, 0, sizeof ypml); + + r = clnt_call(client, YPPROC_MAPLIST, + (xdrproc_t)xdr_domainname, &indomain, + (xdrproc_t)xdr_ypresp_maplist, &ypml, tv); + if (r != RPC_SUCCESS) + clnt_perror(client, "yp_maplist: clnt_call"); + *outmaplist = ypml.maps; + /* NO: xdr_free(xdr_ypresp_maplist, &ypml);*/ + + return ypprot_err(ypml.stat); +} diff --git a/usr.sbin/ypserv/common/yplib_host.h b/usr.sbin/ypserv/common/yplib_host.h new file mode 100644 index 000000000000..eabafae9c1b9 --- /dev/null +++ b/usr.sbin/ypserv/common/yplib_host.h @@ -0,0 +1,51 @@ +/* $OpenBSD: yplib_host.h,v 1.8 2003/06/02 04:12:38 deraadt Exp $ */ + +/* + * Copyright (c) 1992, 1993 Theo de Raadt <deraadt@theos.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _YPLIB_HOST_H_ +#define _YPLIB_HOST_H_ + +int yp_match_host(CLIENT *client, char *indomain, char *inmap, + const char *inkey, int inkeylen, char **outval, int *outvallen); +int yp_first_host(CLIENT *client, char *indomain, char *inmap, + char **outkey, int *outkeylen, char **outval, int *outvallen); +int yp_next_host(CLIENT *client, char *indomain, char *inmap, char *inkey, + int inkeylen, char **outkey, int *outkeylen, char **outval, + int *outvallen); +int yp_master_host(CLIENT *client, char *indomain, char *inmap, + char **outname); +int yp_order_host(CLIENT *client, char *indomain, char *inmap, + u_int32_t *outorder); +int yp_all_host(CLIENT *client, char *indomain, char *inmap, + struct ypall_callback *incallback); +int yp_maplist_host(CLIENT *client, char *indomain, + struct ypmaplist **outmaplist); +CLIENT *yp_bind_local(u_long program, u_long version); +CLIENT *yp_bind_host(char *server, u_long program, u_long version, + u_short port, int usetcp); + +#endif /* _YPLIB_HOST_H_ */ diff --git a/usr.sbin/ypserv/yp_access.c b/usr.sbin/ypserv/yp_access.c new file mode 100644 index 000000000000..fefe96076092 --- /dev/null +++ b/usr.sbin/ypserv/yp_access.c @@ -0,0 +1,332 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +#include <stdlib.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp.h> +#include <rpcsvc/yppasswd.h> +#include <rpcsvc/ypxfrd.h> +#include <sys/types.h> +#include <limits.h> +#include <db.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/stat.h> +#include <sys/fcntl.h> +#include <paths.h> +#include <errno.h> +#include <sys/param.h> +#include "yp_extern.h" +#ifdef TCP_WRAPPER +#include "tcpd.h" +#endif + +static const char *yp_procs[] = { + /* NIS v1 */ + "ypoldproc_null", + "ypoldproc_domain", + "ypoldproc_domain_nonack", + "ypoldproc_match", + "ypoldproc_first", + "ypoldproc_next", + "ypoldproc_poll", + "ypoldproc_push", + "ypoldproc_get", + "badproc1", /* placeholder */ + "badproc2", /* placeholder */ + "badproc3", /* placeholder */ + + /* NIS v2 */ + "ypproc_null", + "ypproc_domain", + "ypproc_domain_nonack", + "ypproc_match", + "ypproc_first", + "ypproc_next", + "ypproc_xfr", + "ypproc_clear", + "ypproc_all", + "ypproc_master", + "ypproc_order", + "ypproc_maplist" +}; + +struct securenet { + struct in_addr net; + struct in_addr mask; + struct securenet *next; +}; + +static struct securenet *securenets; + +#define LINEBUFSZ 1024 + +#ifdef TCP_WRAPPER +int hosts_ctl(char *, char *, char *, char *); +#endif + +/* + * Read /var/yp/securenets file and initialize the securenets + * list. If the file doesn't exist, we set up a dummy entry that + * allows all hosts to connect. + */ +void +load_securenets(void) +{ + FILE *fp; + char path[MAXPATHLEN + 2]; + char linebuf[1024 + 2]; + struct securenet *tmp; + + /* + * If securenets is not NULL, we are being called to reload + * the list; free the existing list before re-reading the + * securenets file. + */ + while (securenets) { + tmp = securenets->next; + free(securenets); + securenets = tmp; + } + + snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir); + + if ((fp = fopen(path, "r")) == NULL) { + if (errno == ENOENT) { + securenets = malloc(sizeof(struct securenet)); + securenets->net.s_addr = INADDR_ANY; + securenets->mask.s_addr = INADDR_ANY; + securenets->next = NULL; + return; + } else { + yp_error("fopen(%s) failed: %s", path, strerror(errno)); + exit(1); + } + } + + securenets = NULL; + + while (fgets(linebuf, LINEBUFSZ, fp)) { + char addr1[20], addr2[20]; + + if ((linebuf[0] == '#') + || (strspn(linebuf, " \t\r\n") == strlen(linebuf))) + continue; + if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) { + yp_error("badly formatted securenets entry: %s", + linebuf); + continue; + } + + tmp = malloc(sizeof(struct securenet)); + + if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) { + yp_error("badly formatted securenets entry: %s", addr1); + free(tmp); + continue; + } + + if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) { + yp_error("badly formatted securenets entry: %s", addr2); + free(tmp); + continue; + } + + tmp->next = securenets; + securenets = tmp; + } + + fclose(fp); + +} + +/* + * Access control functions. + * + * yp_access() checks the mapname and client host address and watches for + * the following things: + * + * - If the client is referencing one of the master.passwd.* or shadow.* maps, + * it must be using a privileged port to make its RPC to us. If it is, then + * we can assume that the caller is root and allow the RPC to succeed. If it + * isn't access is denied. + * + * - The client's IP address is checked against the securenets rules. + * There are two kinds of securenets support: the built-in support, + * which is very simple and depends on the presence of a + * /var/yp/securenets file, and tcp-wrapper support, which requires + * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper + * package does not ship with FreeBSD, we use the built-in support + * by default. Users can recompile the server with the tcp-wrapper library + * if they already have it installed and want to use hosts.allow and + * hosts.deny to control access instead of having a separate securenets + * file.) + * + * If no /var/yp/securenets file is present, the host access checks + * are bypassed and all hosts are allowed to connect. + * + * The yp_validdomain() function checks the domain specified by the caller + * to make sure it's actually served by this server. This is more a sanity + * check than an a security check, but this seems to be the best place for + * it. + */ + +#ifdef DB_CACHE +int +yp_access(const char *map, const char *domain, const struct svc_req *rqstp) +#else +int +yp_access(const char *map, const struct svc_req *rqstp) +#endif +{ + struct sockaddr_in *rqhost; + int status_securenets = 0; +#ifdef TCP_WRAPPER + int status_tcpwrap; +#endif + static unsigned long oldaddr = 0; + struct securenet *tmp; + const char *yp_procedure = NULL; + char procbuf[50]; + + if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) { + snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", + (unsigned long)rqstp->rq_prog, + (unsigned long)rqstp->rq_proc); + yp_procedure = (char *)&procbuf; + } else { + yp_procedure = rqstp->rq_prog == YPPASSWDPROG ? + "yppasswdprog_update" : + yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))]; + } + + rqhost = svc_getcaller(rqstp->rq_xprt); + + if (debug) { + yp_error("procedure %s called from %s:%d", yp_procedure, + inet_ntoa(rqhost->sin_addr), + ntohs(rqhost->sin_port)); + if (map != NULL) + yp_error("client is referencing map \"%s\".", map); + } + + /* Check the map name if one was supplied. */ + if (map != NULL) { + if (strchr(map, '/')) { + yp_error("embedded slash in map name \"%s\" -- \ +possible spoof attempt from %s:%d", + map, inet_ntoa(rqhost->sin_addr), + ntohs(rqhost->sin_port)); + return(1); + } +#ifdef DB_CACHE + if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) || +#else + if ((strstr(map, "master.passwd.") || strstr(map, "shadow.") || +#endif + (rqstp->rq_prog == YPPROG && + rqstp->rq_proc == YPPROC_XFR) || + (rqstp->rq_prog == YPXFRD_FREEBSD_PROG && + rqstp->rq_proc == YPXFRD_GETMAP)) && + ntohs(rqhost->sin_port) >= IPPORT_RESERVED) { + yp_error("access to %s denied -- client %s:%d \ +not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); + return(1); + } + } + +#ifdef TCP_WRAPPER + status_tcpwrap = hosts_ctl("ypserv", STRING_UNKNOWN, + inet_ntoa(rqhost->sin_addr), ""); +#endif + tmp = securenets; + while (tmp) { + if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr) + | tmp->net.s_addr) == rqhost->sin_addr.s_addr) { + status_securenets = 1; + break; + } + tmp = tmp->next; + } + +#ifdef TCP_WRAPPER + if (status_securenets == 0 || status_tcpwrap == 0) { +#else + if (status_securenets == 0) { +#endif + /* + * One of the following two events occurred: + * + * (1) The /var/yp/securenets exists and the remote host does not + * match any of the networks specified in it. + * (2) The hosts.allow file has denied access and TCP_WRAPPER is + * defined. + * + * In either case deny access. + */ + if (rqhost->sin_addr.s_addr != oldaddr) { + yp_error("connect from %s:%d to procedure %s refused", + inet_ntoa(rqhost->sin_addr), + ntohs(rqhost->sin_port), + yp_procedure); + oldaddr = rqhost->sin_addr.s_addr; + } + return(1); + } + return(0); + +} + +int +yp_validdomain(const char *domain) +{ + struct stat statbuf; + char dompath[MAXPATHLEN + 2]; + + if (domain == NULL || strstr(domain, "binding") || + !strcmp(domain, ".") || !strcmp(domain, "..") || + strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN) + return(1); + + snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain); + + if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) + return(1); + + + return(0); +} diff --git a/usr.sbin/ypserv/yp_dblookup.c b/usr.sbin/ypserv/yp_dblookup.c new file mode 100644 index 000000000000..c0aefb048cb9 --- /dev/null +++ b/usr.sbin/ypserv/yp_dblookup.c @@ -0,0 +1,727 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +#include <db.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <rpcsvc/yp.h> +#include "yp_extern.h" + +int ypdb_debug = 0; +enum ypstat yp_errno = YP_TRUE; + +#define PERM_SECURE (S_IRUSR|S_IWUSR) +HASHINFO openinfo = { + 4096, /* bsize */ + 32, /* ffactor */ + 256, /* nelem */ + 2048 * 512, /* cachesize */ + NULL, /* hash */ + 0, /* lorder */ +}; + +#ifdef DB_CACHE +#include <sys/queue.h> + +#ifndef MAXDBS +#define MAXDBS 20 +#endif + +static int numdbs = 0; + +struct dbent { + DB *dbp; + char *name; + char *key; + int size; + int flags; +}; + +static TAILQ_HEAD(circlehead, circleq_entry) qhead; + +struct circleq_entry { + struct dbent *dbptr; + TAILQ_ENTRY(circleq_entry) links; +}; + +/* + * Initialize the circular queue. + */ +void +yp_init_dbs(void) +{ + TAILQ_INIT(&qhead); +} + +/* + * Dynamically allocate an entry for the circular queue. + * Return a NULL pointer on failure. + */ +static struct circleq_entry * +yp_malloc_qent(void) +{ + register struct circleq_entry *q; + + q = malloc(sizeof(struct circleq_entry)); + if (q == NULL) { + yp_error("failed to malloc() circleq entry"); + return(NULL); + } + bzero((char *)q, sizeof(struct circleq_entry)); + q->dbptr = malloc(sizeof(struct dbent)); + if (q->dbptr == NULL) { + yp_error("failed to malloc() circleq entry"); + free(q); + return(NULL); + } + bzero((char *)q->dbptr, sizeof(struct dbent)); + + return(q); +} + +/* + * Free a previously allocated circular queue + * entry. + */ +static void +yp_free_qent(struct circleq_entry *q) +{ + /* + * First, close the database. In theory, this is also + * supposed to free the resources allocated by the DB + * package, including the memory pointed to by q->dbptr->key. + * This means we don't have to free q->dbptr->key here. + */ + if (q->dbptr->dbp) { + (void)(q->dbptr->dbp->close)(q->dbptr->dbp); + q->dbptr->dbp = NULL; + } + /* + * Then free the database name, which was strdup()'ed. + */ + free(q->dbptr->name); + + /* + * Free the rest of the dbent struct. + */ + free(q->dbptr); + q->dbptr = NULL; + + /* + * Free the circleq struct. + */ + free(q); + q = NULL; +} + +/* + * Zorch a single entry in the dbent queue and release + * all its resources. (This always removes the last entry + * in the queue.) + */ +static void +yp_flush(void) +{ + register struct circleq_entry *qptr; + + qptr = TAILQ_LAST(&qhead, circlehead); + TAILQ_REMOVE(&qhead, qptr, links); + yp_free_qent(qptr); + numdbs--; +} + +/* + * Close all databases, erase all database names and empty the queue. + */ +void +yp_flush_all(void) +{ + register struct circleq_entry *qptr; + + while (!TAILQ_EMPTY(&qhead)) { + qptr = TAILQ_FIRST(&qhead); /* save this */ + TAILQ_REMOVE(&qhead, qptr, links); + yp_free_qent(qptr); + } + numdbs = 0; + +} + +static char *inter_string = "YP_INTERDOMAIN"; +static char *secure_string = "YP_SECURE"; +static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; +static int secure_sz = sizeof("YP_SECURE") - 1; + +static int +yp_setflags(DB *dbp) +{ + DBT key = { NULL, 0 }, data = { NULL, 0 }; + int flags = 0; + + key.data = inter_string; + key.size = inter_sz; + + if (!(dbp->get)(dbp, &key, &data, 0)) + flags |= YP_INTERDOMAIN; + + key.data = secure_string; + key.size = secure_sz; + + if (!(dbp->get)(dbp, &key, &data, 0)) + flags |= YP_SECURE; + + return(flags); +} + +int +yp_testflag(char *map, char *domain, int flag) +{ + char buf[MAXPATHLEN + 2]; + register struct circleq_entry *qptr; + + if (map == NULL || domain == NULL) + return(0); + + strcpy(buf, domain); + strcat(buf, "/"); + strcat(buf, map); + + TAILQ_FOREACH(qptr, &qhead, links) { + if (!strcmp(qptr->dbptr->name, buf)) { + if (qptr->dbptr->flags & flag) + return(1); + else + return(0); + } + } + + if (yp_open_db_cache(domain, map, NULL, 0) == NULL) + return(0); + + if (TAILQ_FIRST(&qhead)->dbptr->flags & flag) + return(1); + + return(0); +} + +/* + * Add a DB handle and database name to the cache. We only maintain + * fixed number of entries in the cache, so if we're asked to store + * a new entry when all our slots are already filled, we have to kick + * out the entry in the last slot to make room. + */ +static int +yp_cache_db(DB *dbp, char *name, int size) +{ + register struct circleq_entry *qptr; + + if (numdbs == MAXDBS) { + if (ypdb_debug) + yp_error("queue overflow -- releasing last slot"); + yp_flush(); + } + + /* + * Allocate a new queue entry. + */ + + if ((qptr = yp_malloc_qent()) == NULL) { + yp_error("failed to allocate a new cache entry"); + return(1); + } + + qptr->dbptr->dbp = dbp; + qptr->dbptr->name = strdup(name); + qptr->dbptr->size = size; + qptr->dbptr->key = NULL; + + qptr->dbptr->flags = yp_setflags(dbp); + + TAILQ_INSERT_HEAD(&qhead, qptr, links); + numdbs++; + + return(0); +} + +/* + * Search the list for a database matching 'name.' If we find it, + * move it to the head of the list and return its DB handle. If + * not, just fail: yp_open_db_cache() will subsequently try to open + * the database itself and call yp_cache_db() to add it to the + * list. + * + * The search works like this: + * + * - The caller specifies the name of a database to locate. We try to + * find an entry in our queue with a matching name. + * + * - If the caller doesn't specify a key or size, we assume that the + * first entry that we encounter with a matching name is returned. + * This will result in matches regardless of the key/size values + * stored in the queue entry. + * + * - If the caller also specifies a key and length, we check to see + * if the key and length saved in the queue entry also matches. + * This lets us return a DB handle that's already positioned at the + * correct location within a database. + * + * - Once we have a match, it gets migrated to the top of the queue + * so that it will be easier to find if another request for + * the same database comes in later. + */ +static DB * +yp_find_db(const char *name, const char *key, int size) +{ + register struct circleq_entry *qptr; + + TAILQ_FOREACH(qptr, &qhead, links) { + if (!strcmp(qptr->dbptr->name, name)) { + if (size) { + if (size != qptr->dbptr->size || + strncmp(qptr->dbptr->key, key, size)) + continue; + } else { + if (qptr->dbptr->size) + continue; + } + if (qptr != TAILQ_FIRST(&qhead)) { + TAILQ_REMOVE(&qhead, qptr, links); + TAILQ_INSERT_HEAD(&qhead, qptr, links); + } + return(qptr->dbptr->dbp); + } + } + + return(NULL); +} + +/* + * Open a DB database and cache the handle for later use. We first + * check the cache to see if the required database is already open. + * If so, we fetch the handle from the cache. If not, we try to open + * the database and save the handle in the cache for later use. + */ +DB * +yp_open_db_cache(const char *domain, const char *map, const char *key, + const int size) +{ + DB *dbp = NULL; + char buf[MAXPATHLEN + 2]; +/* + snprintf(buf, sizeof(buf), "%s/%s", domain, map); +*/ + yp_errno = YP_TRUE; + + strcpy(buf, domain); + strcat(buf, "/"); + strcat(buf, map); + + if ((dbp = yp_find_db(buf, key, size)) != NULL) { + return(dbp); + } else { + if ((dbp = yp_open_db(domain, map)) != NULL) { + if (yp_cache_db(dbp, buf, size)) { + (void)(dbp->close)(dbp); + yp_errno = YP_YPERR; + return(NULL); + } + } + } + + return (dbp); +} +#endif + +/* + * Open a DB database. + */ +DB * +yp_open_db(const char *domain, const char *map) +{ + DB *dbp = NULL; + char buf[MAXPATHLEN + 2]; + + yp_errno = YP_TRUE; + + if (map[0] == '.' || strchr(map, '/')) { + yp_errno = YP_BADARGS; + return (NULL); + } + +#ifdef DB_CACHE + if (yp_validdomain(domain)) { + yp_errno = YP_NODOM; + return(NULL); + } +#endif + snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); + +#ifdef DB_CACHE +again: +#endif + dbp = dbopen(buf, O_RDONLY, PERM_SECURE, DB_HASH, NULL); + + if (dbp == NULL) { + switch (errno) { +#ifdef DB_CACHE + case ENFILE: + /* + * We ran out of file descriptors. Nuke an + * open one and try again. + */ + yp_error("ran out of file descriptors"); + yp_flush(); + goto again; + break; +#endif + case ENOENT: + yp_errno = YP_NOMAP; + break; + case EFTYPE: + yp_errno = YP_BADDB; + break; + default: + yp_errno = YP_YPERR; + break; + } + } + + return (dbp); +} + +/* + * Database access routines. + * + * - yp_get_record(): retrieve an arbitrary key/data pair given one key + * to match against. + * + * - yp_first_record(): retrieve first key/data base in a database. + * + * - yp_next_record(): retrieve key/data pair that sequentially follows + * the supplied key value in the database. + */ + +#ifdef DB_CACHE +int +yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow) +#else +int +yp_get_record(const char *domain, const char *map, + const DBT *key, DBT *data, int allow) +#endif +{ +#ifndef DB_CACHE + DB *dbp; +#endif + int rval = 0; +#ifndef DB_CACHE + static unsigned char buf[YPMAXRECORD]; +#endif + + if (ypdb_debug) + yp_error("looking up key [%.*s]", + (int)key->size, (char *)key->data); + + /* + * Avoid passing back magic "YP_*" entries unless + * the caller specifically requested them by setting + * the 'allow' flag. + */ + if (!allow && !strncmp(key->data, "YP_", 3)) + return(YP_NOKEY); + +#ifndef DB_CACHE + if ((dbp = yp_open_db(domain, map)) == NULL) { + return(yp_errno); + } +#endif + + if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#else + (void)(dbp->close)(dbp); +#endif + if (rval == 1) + return(YP_NOKEY); + else + return(YP_BADDB); + } + + if (ypdb_debug) + yp_error("result of lookup: key: [%.*s] data: [%.*s]", + (int)key->size, (char *)key->data, + (int)data->size, (char *)data->data); + +#ifdef DB_CACHE + if (TAILQ_FIRST(&qhead)->dbptr->size) { + TAILQ_FIRST(&qhead)->dbptr->key = ""; + TAILQ_FIRST(&qhead)->dbptr->size = 0; + } +#else + bcopy(data->data, &buf, data->size); + data->data = &buf; + (void)(dbp->close)(dbp); +#endif + + return(YP_TRUE); +} + +int +yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow) +{ + int rval; +#ifndef DB_CACHE + static unsigned char buf[YPMAXRECORD]; +#endif + + if (ypdb_debug) + yp_error("retrieving first key in map"); + + if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#endif + if (rval == 1) + return(YP_NOKEY); + else + return(YP_BADDB); + } + + /* Avoid passing back magic "YP_*" records. */ + while (!strncmp(key->data, "YP_", 3) && !allow) { + if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#endif + if (rval == 1) + return(YP_NOKEY); + else + return(YP_BADDB); + } + } + + if (ypdb_debug) + yp_error("result of lookup: key: [%.*s] data: [%.*s]", + (int)key->size, (char *)key->data, + (int)data->size, (char *)data->data); + +#ifdef DB_CACHE + if (TAILQ_FIRST(&qhead)->dbptr->size) { + TAILQ_FIRST(&qhead)->dbptr->key = key->data; + TAILQ_FIRST(&qhead)->dbptr->size = key->size; + } +#else + bcopy(data->data, &buf, data->size); + data->data = &buf; +#endif + + return(YP_TRUE); +} + +int +yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow) +{ + static DBT lkey = { NULL, 0 }; + static DBT ldata = { NULL, 0 }; + int rval; +#ifndef DB_CACHE + static unsigned char keybuf[YPMAXRECORD]; + static unsigned char datbuf[YPMAXRECORD]; +#endif + + if (key == NULL || !key->size || key->data == NULL) { + rval = yp_first_record(dbp,key,data,allow); + if (rval == YP_NOKEY) + return(YP_NOMORE); + else { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->key = key->data; + TAILQ_FIRST(&qhead)->dbptr->size = key->size; +#endif + return(rval); + } + } + + if (ypdb_debug) + yp_error("retrieving next key, previous was: [%.*s]", + (int)key->size, (char *)key->data); + + if (!all) { +#ifdef DB_CACHE + if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) { +#endif + (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); + while (key->size != lkey.size || + strncmp(key->data, lkey.data, + (int)key->size)) + if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#endif + return(YP_NOKEY); + } + +#ifdef DB_CACHE + } +#endif + } + + if ((dbp->seq)(dbp,key,data,R_NEXT)) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#endif + return(YP_NOMORE); + } + + /* Avoid passing back magic "YP_*" records. */ + while (!strncmp(key->data, "YP_", 3) && !allow) + if ((dbp->seq)(dbp,key,data,R_NEXT)) { +#ifdef DB_CACHE + TAILQ_FIRST(&qhead)->dbptr->size = 0; +#endif + return(YP_NOMORE); + } + + if (ypdb_debug) + yp_error("result of lookup: key: [%.*s] data: [%.*s]", + (int)key->size, (char *)key->data, + (int)data->size, (char *)data->data); + +#ifdef DB_CACHE + if (TAILQ_FIRST(&qhead)->dbptr->size) { + TAILQ_FIRST(&qhead)->dbptr->key = key->data; + TAILQ_FIRST(&qhead)->dbptr->size = key->size; + } +#else + bcopy(key->data, &keybuf, key->size); + lkey.data = &keybuf; + lkey.size = key->size; + bcopy(data->data, &datbuf, data->size); + data->data = &datbuf; +#endif + + return(YP_TRUE); +} + +#ifdef DB_CACHE +/* + * Database glue functions. + */ + +static DB *yp_currmap_db = NULL; +static int yp_allow_db = 0; + +ypstat +yp_select_map(char *map, char *domain, keydat *key, int allow) +{ + if (key == NULL) + yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0); + else + yp_currmap_db = yp_open_db_cache(domain, map, + key->keydat_val, + key->keydat_len); + + yp_allow_db = allow; + return(yp_errno); +} + +ypstat +yp_getbykey(keydat *key, valdat *val) +{ + DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; + ypstat rval; + + db_key.data = key->keydat_val; + db_key.size = key->keydat_len; + + rval = yp_get_record(yp_currmap_db, + &db_key, &db_val, yp_allow_db); + + if (rval == YP_TRUE) { + val->valdat_val = db_val.data; + val->valdat_len = db_val.size; + } + + return(rval); +} + +ypstat +yp_firstbykey(keydat *key, valdat *val) +{ + DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; + ypstat rval; + + rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); + + if (rval == YP_TRUE) { + key->keydat_val = db_key.data; + key->keydat_len = db_key.size; + val->valdat_val = db_val.data; + val->valdat_len = db_val.size; + } + + return(rval); +} + +ypstat +yp_nextbykey(keydat *key, valdat *val) +{ + DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; + ypstat rval; + + db_key.data = key->keydat_val; + db_key.size = key->keydat_len; + + rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); + + if (rval == YP_TRUE) { + key->keydat_val = db_key.data; + key->keydat_len = db_key.size; + val->valdat_val = db_val.data; + val->valdat_len = db_val.size; + } + + return(rval); +} +#endif diff --git a/usr.sbin/ypserv/yp_dnslookup.c b/usr.sbin/ypserv/yp_dnslookup.c new file mode 100644 index 000000000000..699c4d7e8018 --- /dev/null +++ b/usr.sbin/ypserv/yp_dnslookup.c @@ -0,0 +1,545 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995, 1996 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +/* + * Do standard and reverse DNS lookups using the resolver library. + * Take care of all the dirty work here so the main program only has to + * pass us a pointer to an array of characters. + * + * We have to use direct resolver calls here otherwise the YP server + * could end up looping by calling itself over and over again until + * it disappeared up its own belly button. + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/fcntl.h> +#include <sys/queue.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <resolv.h> +#include <unistd.h> + +#include <rpcsvc/yp.h> +#include "yp_extern.h" + +static char * +parse(struct hostent *hp) +{ + static char result[MAXHOSTNAMELEN * 2]; + int i; + size_t len; + char addr[46]; + + if (hp == NULL) + return(NULL); + + if (inet_ntop(hp->h_addrtype, hp->h_addr, addr, sizeof(addr)) == NULL) + return(NULL); + + len = strlen(addr) + 1 + strlen(hp->h_name); + for (i = 0; hp->h_aliases[i]; i++) + len += strlen(hp->h_aliases[i]) + 1; + len++; + + if (len > sizeof(result)) + return(NULL); + + bzero(result, sizeof(result)); + snprintf(result, sizeof(result), "%s %s", addr, hp->h_name); + for (i = 0; hp->h_aliases[i]; i++) { + strcat(result, " "); + strcat(result, hp->h_aliases[i]); + } + + return ((char *)&result); +} + +#define MAXPACKET (64*1024) +#define DEF_TTL 50 + +#define BY_DNS_ID 1 +#define BY_RPC_XID 2 + +extern struct hostent *__dns_getanswer(char *, int, char *, int); + +static TAILQ_HEAD(dns_qhead, circleq_dnsentry) qhead; + +struct circleq_dnsentry { + SVCXPRT *xprt; + unsigned long xid; + struct sockaddr_in client_addr; + unsigned long ypvers; + unsigned long id; + unsigned long ttl; + unsigned long type; + unsigned short prot_type; + char **domain; + char *name; + int addrtype; + int addrlen; + uint32_t addr[4]; /* IPv4 or IPv6 */ + TAILQ_ENTRY(circleq_dnsentry) links; +}; + +static int pending = 0; + +int +yp_init_resolver(void) +{ + TAILQ_INIT(&qhead); + if (!(_res.options & RES_INIT) && res_init() == -1) { + yp_error("res_init failed"); + return(1); + } + if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + yp_error("couldn't create socket"); + return(1); + } + if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) { + yp_error("couldn't make resolver socket non-blocking"); + return(1); + } + return(0); +} + +static struct +circleq_dnsentry *yp_malloc_dnsent(void) +{ + register struct circleq_dnsentry *q; + + q = malloc(sizeof(struct circleq_dnsentry)); + + if (q == NULL) { + yp_error("failed to malloc() circleq dns entry"); + return(NULL); + } + + return(q); +} + +/* + * Transmit a query. + */ +static unsigned long +yp_send_dns_query(char *name, int type) +{ + char buf[MAXPACKET]; + int n; + HEADER *hptr; + int ns; + int rval; + unsigned long id; + + bzero(buf, sizeof(buf)); + + n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf)); + + if (n <= 0) { + yp_error("res_mkquery failed for %s type %d", name, type); + return(0); + } + + hptr = (HEADER *)&buf; + id = ntohs(hptr->id); + + for (ns = 0; ns < _res.nscount; ns++) { + rval = sendto(resfd, buf, n, 0, + (struct sockaddr *)&_res.nsaddr_list[ns], + sizeof(struct sockaddr)); + if (rval == -1) { + yp_error("sendto failed"); + return(0); + } + } + + return(id); +} + +static struct circleq_dnsentry * +yp_find_dnsqent(unsigned long id, int type) +{ + register struct circleq_dnsentry *q; + + TAILQ_FOREACH(q, &qhead, links) { + switch (type) { + case BY_RPC_XID: + if (id == q->xid) + return(q); + break; + case BY_DNS_ID: + default: + if (id == q->id) + return(q); + break; + } + } + return (NULL); +} + +static void +yp_send_dns_reply(struct circleq_dnsentry *q, char *buf) +{ + ypresponse result_v1; + ypresp_val result_v2; + unsigned long xid; + struct sockaddr_in client_addr; + xdrproc_t xdrfunc; + char *result; + + /* + * Set up correct reply struct and + * XDR filter depending on ypvers. + */ + switch (q->ypvers) { + case YPVERS: + bzero((char *)&result_v2, sizeof(result_v2)); + + if (buf == NULL) + result_v2.stat = YP_NOKEY; + else { + result_v2.val.valdat_len = strlen(buf); + result_v2.val.valdat_val = buf; + result_v2.stat = YP_TRUE; + } + result = (char *)&result_v2; + xdrfunc = (xdrproc_t)xdr_ypresp_val; + break; + case YPOLDVERS: + /* + * The odds are we will _never_ execute this + * particular code, but we include it anyway + * for the sake of completeness. + */ + bzero((char *)&result_v1, sizeof(result_v1)); + result_v1.yp_resptype = YPRESP_VAL; + +#define YPVAL ypresponse_u.yp_resp_valtype + if (buf == NULL) + result_v1.YPVAL.stat = YP_NOKEY; + else { + result_v1.YPVAL.val.valdat_len = strlen(buf); + result_v1.YPVAL.val.valdat_val = buf; + result_v1.YPVAL.stat = YP_TRUE; + } + result = (char *)&result_v1; + xdrfunc = (xdrproc_t)xdr_ypresponse; + break; + default: + yp_error("bad YP program version (%lu)!", q->ypvers); + return; + break; + } + + if (debug) + yp_error("sending dns reply to %s (%lu)", + inet_ntoa(q->client_addr.sin_addr), q->id); + /* + * XXX This is disgusting. There's basically one transport + * handle for UDP, but we're holding off on replying to a + * client until we're ready, by which time we may have received + * several other queries from other clients with different + * transaction IDs. So to make the delayed response thing work, + * we have to save the transaction ID and client address of + * each request, then jam them into the transport handle when + * we're ready to send a reply. Then after we've send the reply, + * we put the old transaction ID and remote address back the + * way we found 'em. This is _INCREDIBLY_ non-portable; it's + * not even supported by the RPC library. + */ + /* + * XXX Don't frob the transaction ID for TCP handles. + */ + if (q->prot_type == SOCK_DGRAM) + xid = svcudp_set_xid(q->xprt, q->xid); + client_addr = q->xprt->xp_raddr; + q->xprt->xp_raddr = q->client_addr; + + if (!svc_sendreply(q->xprt, xdrfunc, result)) + yp_error("svc_sendreply failed"); + + /* + * Now that we sent the reply, + * put the handle back the way it was. + */ + if (q->prot_type == SOCK_DGRAM) + svcudp_set_xid(q->xprt, xid); + q->xprt->xp_raddr = client_addr; +} + +/* + * Decrement TTL on all queue entries, possibly nuking + * any that have been around too long without being serviced. + */ +void +yp_prune_dnsq(void) +{ + register struct circleq_dnsentry *q, *n; + + q = TAILQ_FIRST(&qhead); + while (q != NULL) { + q->ttl--; + n = TAILQ_NEXT(q, links); + if (!q->ttl) { + TAILQ_REMOVE(&qhead, q, links); + free(q->name); + free(q); + pending--; + } + q = n; + } + + if (pending < 0) + pending = 0; +} + +/* + * Data is pending on the DNS socket; check for valid replies + * to our queries and dispatch them to waiting clients. + */ +void +yp_run_dnsq(void) +{ + register struct circleq_dnsentry *q; + char buf[sizeof(HEADER) + MAXPACKET]; + struct sockaddr_in sin; + socklen_t len; + int rval; + HEADER *hptr; + struct hostent *hent; + + if (debug) + yp_error("running dns queue"); + + bzero(buf, sizeof(buf)); + + len = sizeof(struct sockaddr_in); + rval = recvfrom(resfd, buf, sizeof(buf), 0, + (struct sockaddr *)&sin, &len); + + if (rval == -1) { + yp_error("recvfrom failed: %s", strerror(errno)); + return; + } + + /* + * We may have data left in the socket that represents + * replies to earlier queries that we don't care about + * anymore. If there are no lookups pending or the packet + * ID doesn't match any of the queue IDs, just drop it + * on the floor. + */ + hptr = (HEADER *)&buf; + if (!pending || + (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) { + /* ignore */ + return; + } + + if (debug) + yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr)); + + hent = __dns_getanswer(buf, rval, q->name, q->type); + + if (hent != NULL) { + if (q->type == T_PTR) { + hent->h_addr = (char *)q->addr; + hent->h_addrtype = q->addrtype; + hent->h_length = q->addrlen; + } + } + + /* Got an answer ready for a client -- send it off. */ + yp_send_dns_reply(q, parse(hent)); + pending--; + TAILQ_REMOVE(&qhead, q, links); + free(q->name); + free(q); + + /* Decrement TTLs on other entries while we're here. */ + yp_prune_dnsq(); +} + +/* + * Queue and transmit an asynchronous DNS hostname lookup. + */ +ypstat +yp_async_lookup_name(struct svc_req *rqstp, char *name, int af) +{ + register struct circleq_dnsentry *q; + socklen_t len; + int type; + + /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ + type = -1; + len = sizeof(type); + if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, + SO_TYPE, &type, &len) == -1) { + yp_error("getsockopt failed: %s", strerror(errno)); + return(YP_YPERR); + } + + /* Avoid transmitting dupe requests. */ + if (type == SOCK_DGRAM && + yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) + return(YP_TRUE); + + if ((q = yp_malloc_dnsent()) == NULL) + return(YP_YPERR); + + q->type = (af == AF_INET) ? T_A : T_AAAA; + q->ttl = DEF_TTL; + q->xprt = rqstp->rq_xprt; + q->ypvers = rqstp->rq_vers; + q->prot_type = type; + if (q->prot_type == SOCK_DGRAM) + q->xid = svcudp_get_xid(q->xprt); + q->client_addr = q->xprt->xp_raddr; + q->domain = _res.dnsrch; + q->id = yp_send_dns_query(name, q->type); + + if (q->id == 0) { + yp_error("DNS query failed"); + free(q); + return(YP_YPERR); + } + + q->name = strdup(name); + TAILQ_INSERT_HEAD(&qhead, q, links); + pending++; + + if (debug) + yp_error("queueing async DNS name lookup (%lu)", q->id); + + yp_prune_dnsq(); + return(YP_TRUE); +} + +/* + * Queue and transmit an asynchronous DNS IP address lookup. + */ +ypstat +yp_async_lookup_addr(struct svc_req *rqstp, char *addr, int af) +{ + register struct circleq_dnsentry *q; + char buf[MAXHOSTNAMELEN], *qp; + uint32_t abuf[4]; /* IPv4 or IPv6 */ + u_char *uaddr = (u_char *)abuf; + socklen_t len; + int type, n; + + /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ + type = -1; + len = sizeof(type); + if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, + SO_TYPE, &type, &len) == -1) { + yp_error("getsockopt failed: %s", strerror(errno)); + return(YP_YPERR); + } + + /* Avoid transmitting dupe requests. */ + if (type == SOCK_DGRAM && + yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) + return(YP_TRUE); + + switch (af) { + case AF_INET: + if (inet_aton(addr, (struct in_addr *)uaddr) != 1) + return(YP_NOKEY); + snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa", + (uaddr[3] & 0xff), (uaddr[2] & 0xff), + (uaddr[1] & 0xff), (uaddr[0] & 0xff)); + len = INADDRSZ; + break; + case AF_INET6: + if (inet_pton(af, addr, uaddr) != 1) + return(YP_NOKEY); + qp = buf; + for (n = IN6ADDRSZ - 1; n >= 0; n--) { + qp += (size_t)sprintf(qp, "%x.%x.", uaddr[n] & 0xf, + (uaddr[n] >> 4) & 0xf); + } + strlcat(buf, "ip6.arpa", sizeof(buf)); + len = IN6ADDRSZ; + break; + default: + return(YP_YPERR); + } + + if ((q = yp_malloc_dnsent()) == NULL) + return(YP_YPERR); + + if (debug) + yp_error("DNS address is: %s", buf); + + q->type = T_PTR; + q->ttl = DEF_TTL; + q->xprt = rqstp->rq_xprt; + q->ypvers = rqstp->rq_vers; + q->domain = NULL; + q->prot_type = type; + if (q->prot_type == SOCK_DGRAM) + q->xid = svcudp_get_xid(q->xprt); + q->client_addr = q->xprt->xp_raddr; + q->id = yp_send_dns_query(buf, q->type); + + if (q->id == 0) { + yp_error("DNS query failed"); + free(q); + return(YP_YPERR); + } + + memcpy(q->addr, uaddr, len); + q->addrlen = len; + q->addrtype = af; + q->name = strdup(buf); + TAILQ_INSERT_HEAD(&qhead, q, links); + pending++; + + if (debug) + yp_error("queueing async DNS address lookup (%lu)", q->id); + + yp_prune_dnsq(); + return(YP_TRUE); +} diff --git a/usr.sbin/ypserv/yp_error.c b/usr.sbin/ypserv/yp_error.c new file mode 100644 index 000000000000..314c11d2514d --- /dev/null +++ b/usr.sbin/ypserv/yp_error.c @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +/* + * error logging/reporting facilities + * stolen from /usr/libexec/mail.local via ypserv + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include "yp_extern.h" + +extern int _rpcpmstart; +extern char *progname; +static void __verr(const char *fmt, va_list ap) __printflike(1, 0); + +static void +__verr(const char *fmt, va_list ap) +{ + if (debug && !_rpcpmstart) { + fprintf(stderr,"%s: ",progname); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vsyslog(LOG_NOTICE, fmt, ap); + } +} + +void +yp_error(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + __verr(fmt,ap); + va_end(ap); +} diff --git a/usr.sbin/ypserv/yp_extern.h b/usr.sbin/ypserv/yp_extern.h new file mode 100644 index 000000000000..3fe03df71b88 --- /dev/null +++ b/usr.sbin/ypserv/yp_extern.h @@ -0,0 +1,115 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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 <db.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <sys/types.h> +#include <rpc/rpc.h> +#include <rpcsvc/yp.h> + +#ifndef _PATH_YP +#define _PATH_YP "/var/yp/" +#endif + +#ifndef _PATH_LIBEXEC +#define _PATH_LIBEXEC "/usr/libexec/" +#endif + +#ifndef MAX_CHILDREN +#define MAX_CHILDREN 20 +#endif + +#define YP_SECURE 0x1 +#define YP_INTERDOMAIN 0x2 + +/* + * External functions and variables. + */ + +extern int debug; +extern int ypdb_debug; +extern int do_dns; +extern int children; +extern int resfd; +extern char *progname; +extern char *yp_dir; +extern pid_t yp_pid; + +extern enum ypstat yp_errno; +extern void yp_error(const char *, ...) __printflike(1, 2); +#ifdef DB_CACHE +extern int yp_get_record(DB *, const DBT *, DBT *, int); +#else +extern int yp_get_record(const char *, const char *, const DBT *, DBT *, int); +#endif +extern int yp_first_record(const DB *, DBT *, DBT *, int); +extern int yp_next_record(const DB *, DBT *, DBT *, int, int); +extern char *yp_dnsname(char *); +extern char *yp_dnsaddr(const char *); +#ifdef DB_CACHE +extern int yp_access(const char *, const char *, const struct svc_req *); +#else +extern int yp_access(const char *, const struct svc_req *); +#endif +extern int yp_validdomain(const char *); +extern DB *yp_open_db(const char *, const char *); +extern DB *yp_open_db_cache(const char *, const char *, const char *, int); +extern void yp_flush_all(void); +extern void yp_init_dbs(void); +extern int yp_testflag(char *, char *, int); +extern void load_securenets(void); + +#ifdef DB_CACHE +extern ypstat yp_select_map(char *, char *, keydat *, int); +extern ypstat yp_getbykey(keydat *, valdat *); +extern ypstat yp_firstbykey(keydat *, valdat *); +extern ypstat yp_nextbykey(keydat *, valdat *); +#endif + +extern unsigned long svcudp_set_xid(SVCXPRT *, unsigned long); +extern unsigned long svcudp_get_xid(SVCXPRT *); + +#ifndef RESOLVER_TIMEOUT +#define RESOLVER_TIMEOUT 3600 +#endif + +extern int yp_init_resolver(void); +extern void yp_run_dnsq(void); +extern void yp_prune_dnsq(void); +extern ypstat yp_async_lookup_name(struct svc_req *, char *, int); +extern ypstat yp_async_lookup_addr(struct svc_req *, char *, int); diff --git a/usr.sbin/ypserv/yp_main.c b/usr.sbin/ypserv/yp_main.c new file mode 100644 index 000000000000..d326603a9229 --- /dev/null +++ b/usr.sbin/ypserv/yp_main.c @@ -0,0 +1,578 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +/* + * ypserv startup function. + * We need out own main() since we have to do some additional work + * that rpcgen won't do for us. Most of this file was generated using + * rpcgen.new, and later modified. + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include "yp.h" +#include <err.h> +#include <errno.h> +#include <memory.h> +#include <stdio.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> /* getenv, exit */ +#include <string.h> /* strcmp */ +#include <syslog.h> +#include <unistd.h> +#ifdef __cplusplus +#include <sysent.h> /* getdtablesize, open */ +#endif /* __cplusplus */ +#include <netinet/in.h> +#include <netdb.h> +#include "yp_extern.h" +#include <netconfig.h> +#include <rpc/rpc.h> +#include <rpc/rpc_com.h> + +#ifndef SIG_PF +#define SIG_PF void(*)(int) +#endif + +#define _RPCSVC_CLOSEDOWN 120 +int _rpcpmstart; /* Started by a port monitor ? */ +static int _rpcfdtype; /* Whether Stream or Datagram? */ +static int _rpcaf; +static int _rpcfd; + +/* States a server can be in wrt request */ +#define _IDLE 0 +#define _SERVED 1 +#define _SERVING 2 + +extern void ypprog_1(struct svc_req *, SVCXPRT *); +extern void ypprog_2(struct svc_req *, SVCXPRT *); +extern int _rpc_dtablesize(void); +extern int _rpcsvcstate; /* Set when a request is serviced */ +char *progname = "ypserv"; +char *yp_dir = _PATH_YP; +int debug; +int do_dns = 0; +int resfd; + +struct socklistent { + int sle_sock; + struct sockaddr_storage sle_ss; + SLIST_ENTRY(socklistent) sle_next; +}; +static SLIST_HEAD(, socklistent) sle_head = + SLIST_HEAD_INITIALIZER(sle_head); + +struct bindaddrlistent { + const char *ble_hostname; + SLIST_ENTRY(bindaddrlistent) ble_next; +}; +static SLIST_HEAD(, bindaddrlistent) ble_head = + SLIST_HEAD_INITIALIZER(ble_head); + +static char *servname = "0"; + +static +void _msgout(char* msg, ...) +{ + va_list ap; + + va_start(ap, msg); + if (debug) { + if (_rpcpmstart) + vsyslog(LOG_ERR, msg, ap); + else + vwarnx(msg, ap); + } else + vsyslog(LOG_ERR, msg, ap); + va_end(ap); +} + +pid_t yp_pid; + +static void +yp_svc_run(void) +{ +#ifdef FD_SETSIZE + fd_set readfds; +#else + int readfds; +#endif /* def FD_SETSIZE */ + int fd_setsize = _rpc_dtablesize(); + struct timeval timeout; + + /* Establish the identity of the parent ypserv process. */ + yp_pid = getpid(); + + for (;;) { +#ifdef FD_SETSIZE + readfds = svc_fdset; +#else + readfds = svc_fds; +#endif /* def FD_SETSIZE */ + + FD_SET(resfd, &readfds); + + timeout.tv_sec = RESOLVER_TIMEOUT; + timeout.tv_usec = 0; + switch (select(fd_setsize, &readfds, NULL, NULL, + &timeout)) { + case -1: + if (errno == EINTR) { + continue; + } + warn("svc_run: - select failed"); + return; + case 0: + if (getpid() == yp_pid) + yp_prune_dnsq(); + break; + default: + if (getpid() == yp_pid) { + if (FD_ISSET(resfd, &readfds)) { + yp_run_dnsq(); + FD_CLR(resfd, &readfds); + } + svc_getreqset(&readfds); + } + } + if (yp_pid != getpid()) + _exit(0); + } +} + +static void +unregister(void) +{ + (void)svc_unreg(YPPROG, YPVERS); + (void)svc_unreg(YPPROG, YPOLDVERS); +} + +static void +reaper(int sig) +{ + int status; + int saved_errno; + + saved_errno = errno; + + if (sig == SIGHUP) { + load_securenets(); +#ifdef DB_CACHE + yp_flush_all(); +#endif + errno = saved_errno; + return; + } + + if (sig == SIGCHLD) { + while (wait3(&status, WNOHANG, NULL) > 0) + children--; + } else { + unregister(); + exit(0); + } + errno = saved_errno; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: ypserv [-h addr] [-d] [-n] [-p path] [-P port]\n"); + exit(1); +} + +static void +closedown(int sig) +{ + if (_rpcsvcstate == _IDLE) { + extern fd_set svc_fdset; + static int size; + int i, openfd; + + if (_rpcfdtype == SOCK_DGRAM) { + unregister(); + exit(0); + } + if (size == 0) { + size = getdtablesize(); + } + for (i = 0, openfd = 0; i < size && openfd < 2; i++) + if (FD_ISSET(i, &svc_fdset)) + openfd++; + if (openfd <= 1) { + unregister(); + exit(0); + } + } + if (_rpcsvcstate == _SERVED) + _rpcsvcstate = _IDLE; + + (void) signal(SIGALRM, (SIG_PF) closedown); + (void) alarm(_RPCSVC_CLOSEDOWN/2); +} + +static int +create_service(const int sock, const struct netconfig *nconf, + const struct __rpc_sockinfo *si) +{ + int error; + + SVCXPRT *transp; + struct addrinfo hints, *res, *res0; + struct socklistent *slep; + struct bindaddrlistent *blep; + struct netbuf svcaddr; + + SLIST_INIT(&sle_head); + memset(&hints, 0, sizeof(hints)); + memset(&svcaddr, 0, sizeof(svcaddr)); + + hints.ai_family = si->si_af; + hints.ai_socktype = si->si_socktype; + hints.ai_protocol = si->si_proto; + + /* + * Build socketlist from bindaddrlist. + */ + if (sock == RPC_ANYFD) { + SLIST_FOREACH(blep, &ble_head, ble_next) { + if (blep->ble_hostname == NULL) + hints.ai_flags = AI_PASSIVE; + else + hints.ai_flags = 0; + error = getaddrinfo(blep->ble_hostname, servname, + &hints, &res0); + if (error) { + _msgout("getaddrinfo(): %s", + gai_strerror(error)); + return -1; + } + for (res = res0; res; res = res->ai_next) { + int s; + + s = __rpc_nconf2fd(nconf); + if (s < 0) { + if (errno == EAFNOSUPPORT) + _msgout("unsupported" + " transport: %s", + nconf->nc_netid); + else + _msgout("cannot create" + " %s socket: %s", + nconf->nc_netid, + strerror(errno)); + freeaddrinfo(res0); + return -1; + } + if (bindresvport_sa(s, res->ai_addr) == -1) { + if ((errno != EPERM) || + (bind(s, res->ai_addr, + res->ai_addrlen) == -1)) { + _msgout("cannot bind " + "%s socket: %s", + nconf->nc_netid, + strerror(errno)); + freeaddrinfo(res0); + close(sock); + return -1; + } + } + if (nconf->nc_semantics != NC_TPI_CLTS) + listen(s, SOMAXCONN); + + slep = malloc(sizeof(*slep)); + if (slep == NULL) { + _msgout("malloc failed: %s", + strerror(errno)); + freeaddrinfo(res0); + close(s); + return -1; + } + memset(slep, 0, sizeof(*slep)); + memcpy(&slep->sle_ss, res->ai_addr, + res->ai_addrlen); + slep->sle_sock = s; + SLIST_INSERT_HEAD(&sle_head, slep, sle_next); + + /* + * If servname == "0", redefine it by using + * the bound socket. + */ + if (strncmp("0", servname, 1) == 0) { + struct sockaddr *sap; + socklen_t slen; + char *sname; + + sname = malloc(NI_MAXSERV); + if (sname == NULL) { + _msgout("malloc(): %s", + strerror(errno)); + freeaddrinfo(res0); + close(s); + return -1; + } + memset(sname, 0, NI_MAXSERV); + + sap = (struct sockaddr *)&slep->sle_ss; + slen = sizeof(*sap); + error = getsockname(s, sap, &slen); + if (error) { + _msgout("getsockname(): %s", + strerror(errno)); + freeaddrinfo(res0); + close(s); + free(sname); + return -1; + } + error = getnameinfo(sap, slen, + NULL, 0, + sname, NI_MAXSERV, + NI_NUMERICHOST | NI_NUMERICSERV); + if (error) { + _msgout("getnameinfo(): %s", + strerror(errno)); + freeaddrinfo(res0); + close(s); + free(sname); + return -1; + } + servname = sname; + } + } + freeaddrinfo(res0); + } + } else { + slep = malloc(sizeof(*slep)); + if (slep == NULL) { + _msgout("malloc failed: %s", strerror(errno)); + return -1; + } + memset(slep, 0, sizeof(*slep)); + slep->sle_sock = sock; + SLIST_INSERT_HEAD(&sle_head, slep, sle_next); + } + + /* + * Traverse socketlist and create rpc service handles for each socket. + */ + SLIST_FOREACH(slep, &sle_head, sle_next) { + if (nconf->nc_semantics == NC_TPI_CLTS) + transp = svc_dg_create(slep->sle_sock, 0, 0); + else + transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE, + RPC_MAXDATASIZE); + if (transp == NULL) { + _msgout("unable to create service: %s", + nconf->nc_netid); + continue; + } + if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) { + svc_destroy(transp); + close(slep->sle_sock); + _msgout("unable to register (YPPROG, YPOLDVERS, %s):" + " %s", nconf->nc_netid, strerror(errno)); + continue; + } + if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) { + svc_destroy(transp); + close(slep->sle_sock); + _msgout("unable to register (YPPROG, YPVERS, %s): %s", + nconf->nc_netid, strerror(errno)); + continue; + } + } + while(!(SLIST_EMPTY(&sle_head))) + SLIST_REMOVE_HEAD(&sle_head, sle_next); + + /* + * Register RPC service to rpcbind by using AI_PASSIVE address. + */ + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(NULL, servname, &hints, &res0); + if (error) { + _msgout("getaddrinfo(): %s", gai_strerror(error)); + return -1; + } + svcaddr.buf = res0->ai_addr; + svcaddr.len = res0->ai_addrlen; + + if (si->si_af == AF_INET) { + /* XXX: ignore error intentionally */ + rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr); + } + /* XXX: ignore error intentionally */ + rpcb_set(YPPROG, YPVERS, nconf, &svcaddr); + freeaddrinfo(res0); + return 0; +} + +int +main(int argc, char *argv[]) +{ + int ch; + int error; + int ntrans; + + void *nc_handle; + struct netconfig *nconf; + struct __rpc_sockinfo si; + struct bindaddrlistent *blep; + + memset(&si, 0, sizeof(si)); + SLIST_INIT(&ble_head); + + while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) { + switch (ch) { + case 'd': + debug = ypdb_debug = 1; + break; + case 'h': + blep = malloc(sizeof(*blep)); + if (blep == NULL) + err(1, "malloc() failed: -h %s", optarg); + blep->ble_hostname = optarg; + SLIST_INSERT_HEAD(&ble_head, blep, ble_next); + break; + case 'n': + do_dns = 1; + break; + case 'p': + yp_dir = optarg; + break; + case 'P': + servname = optarg; + break; + default: + usage(); + } + } + /* + * Add "anyaddr" entry if no -h is specified. + */ + if (SLIST_EMPTY(&ble_head)) { + blep = malloc(sizeof(*blep)); + if (blep == NULL) + err(1, "malloc() failed"); + memset(blep, 0, sizeof(*blep)); + SLIST_INSERT_HEAD(&ble_head, blep, ble_next); + } + + load_securenets(); + yp_init_resolver(); +#ifdef DB_CACHE + yp_init_dbs(); +#endif + nc_handle = setnetconfig(); + if (nc_handle == NULL) + err(1, "cannot read %s", NETCONFIG); + if (__rpc_fd2sockinfo(0, &si) != 0) { + /* invoked from inetd */ + _rpcpmstart = 1; + _rpcfdtype = si.si_socktype; + _rpcaf = si.si_af; + _rpcfd = 0; + openlog("ypserv", LOG_PID, LOG_DAEMON); + } else { + /* standalone mode */ + if (!debug) { + if (daemon(0,0)) { + err(1,"cannot fork"); + } + openlog("ypserv", LOG_PID, LOG_DAEMON); + } + _rpcpmstart = 0; + _rpcaf = AF_INET; + _rpcfd = RPC_ANYFD; + unregister(); + } + + if (madvise(NULL, 0, MADV_PROTECT) != 0) + _msgout("madvise(): %s", strerror(errno)); + + /* + * Create RPC service for each transport. + */ + ntrans = 0; + while((nconf = getnetconfig(nc_handle))) { + if ((nconf->nc_flag & NC_VISIBLE)) { + if (__rpc_nconf2sockinfo(nconf, &si) == 0) { + _msgout("cannot get information for %s. " + "Ignored.", nconf->nc_netid); + continue; + } + if (_rpcpmstart) { + if (si.si_socktype != _rpcfdtype || + si.si_af != _rpcaf) + continue; + } else if (si.si_af != _rpcaf) + continue; + error = create_service(_rpcfd, nconf, &si); + if (error) { + endnetconfig(nc_handle); + exit(1); + } + ntrans++; + } + } + endnetconfig(nc_handle); + while(!(SLIST_EMPTY(&ble_head))) + SLIST_REMOVE_HEAD(&ble_head, ble_next); + if (ntrans == 0) { + _msgout("no transport is available. Aborted."); + exit(1); + } + if (_rpcpmstart) { + (void) signal(SIGALRM, (SIG_PF) closedown); + (void) alarm(_RPCSVC_CLOSEDOWN/2); + } +/* + * Make sure SIGPIPE doesn't blow us away while servicing TCP + * connections. + */ + (void) signal(SIGPIPE, SIG_IGN); + (void) signal(SIGCHLD, (SIG_PF) reaper); + (void) signal(SIGTERM, (SIG_PF) reaper); + (void) signal(SIGINT, (SIG_PF) reaper); + (void) signal(SIGHUP, (SIG_PF) reaper); + yp_svc_run(); + _msgout("svc_run returned"); + exit(1); + /* NOTREACHED */ +} diff --git a/usr.sbin/ypserv/yp_server.c b/usr.sbin/ypserv/yp_server.c new file mode 100644 index 000000000000..62e45e35cbe1 --- /dev/null +++ b/usr.sbin/ypserv/yp_server.c @@ -0,0 +1,985 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1995 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +#include "yp.h" +#include "yp_extern.h" +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> + +int children = 0; + +#define MASTER_STRING "YP_MASTER_NAME" +#define MASTER_SZ sizeof(MASTER_STRING) - 1 +#define ORDER_STRING "YP_LAST_MODIFIED" +#define ORDER_SZ sizeof(ORDER_STRING) - 1 + +static pid_t +yp_fork(void) +{ + if (yp_pid != getpid()) { + yp_error("child %d trying to fork!", getpid()); + errno = EEXIST; + return(-1); + } + + return(fork()); +} + +/* + * NIS v2 support. This is where most of the action happens. + */ + +void * +ypproc_null_2_svc(void *argp, struct svc_req *rqstp) +{ + static char * result; + static char rval = 0; + +#ifdef DB_CACHE + if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) +#else + if (yp_access(NULL, (struct svc_req *)rqstp)) +#endif + return(NULL); + + result = &rval; + + return((void *) &result); +} + +bool_t * +ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp) +{ + static bool_t result; + +#ifdef DB_CACHE + if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) { +#else + if (yp_access(NULL, (struct svc_req *)rqstp)) { +#endif + result = FALSE; + return (&result); + } + + if (argp == NULL || yp_validdomain(*argp)) + result = FALSE; + else + result = TRUE; + + return (&result); +} + +bool_t * +ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp) +{ + static bool_t result; + +#ifdef DB_CACHE + if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) +#else + if (yp_access(NULL, (struct svc_req *)rqstp)) +#endif + return (NULL); + + if (argp == NULL || yp_validdomain(*argp)) + return (NULL); + else + result = TRUE; + + return (&result); +} + +ypresp_val * +ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp) +{ + static ypresp_val result; + + result.val.valdat_val = ""; + result.val.valdat_len = 0; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return (&result); + } + + if (argp->domain == NULL || argp->map == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + if (yp_select_map(argp->map, argp->domain, NULL, 1) != YP_TRUE) { + result.stat = yp_errno; + return(&result); + } + + result.stat = yp_getbykey(&argp->key, &result.val); + + /* + * Do DNS lookups for hosts maps if database lookup failed. + */ + +#ifdef DB_CACHE + if (do_dns && result.stat != YP_TRUE && + (yp_testflag(argp->map, argp->domain, YP_INTERDOMAIN) || + (strstr(argp->map, "hosts") || strstr(argp->map, "ipnodes")))) { +#else + if (do_dns && result.stat != YP_TRUE && + (strstr(argp->map, "hosts") || strstr(argp->map, "ipnodes"))) { +#endif + char *nbuf; + + nbuf = alloca(argp->key.keydat_len + 1); + /* NUL terminate! NUL terminate!! NUL TERMINATE!!! */ + bcopy(argp->key.keydat_val, nbuf, argp->key.keydat_len); + nbuf[argp->key.keydat_len] = '\0'; + + if (debug) + yp_error("doing DNS lookup of %s", nbuf); + + if (!strcmp(argp->map, "hosts.byname")) + result.stat = yp_async_lookup_name(rqstp, nbuf, + AF_INET); + else if (!strcmp(argp->map, "hosts.byaddr")) + result.stat = yp_async_lookup_addr(rqstp, nbuf, + AF_INET); + else if (!strcmp(argp->map, "ipnodes.byname")) + result.stat = yp_async_lookup_name(rqstp, nbuf, + AF_INET6); + else if (!strcmp(argp->map, "ipnodes.byaddr")) + result.stat = yp_async_lookup_addr(rqstp, nbuf, + AF_INET6); + + if (result.stat == YP_TRUE) + return(NULL); + } + + return (&result); +} + +ypresp_key_val * +ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) +{ + static ypresp_key_val result; + + result.val.valdat_val = result.key.keydat_val = ""; + result.val.valdat_len = result.key.keydat_len = 0; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return (&result); + } + + if (argp->domain == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + if (yp_select_map(argp->map, argp->domain, NULL, 0) != YP_TRUE) { + result.stat = yp_errno; + return(&result); + } + + result.stat = yp_firstbykey(&result.key, &result.val); + + return (&result); +} + +ypresp_key_val * +ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp) +{ + static ypresp_key_val result; + + result.val.valdat_val = result.key.keydat_val = ""; + result.val.valdat_len = result.key.keydat_len = 0; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return (&result); + } + + if (argp->domain == NULL || argp->map == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + if (yp_select_map(argp->map, argp->domain, &argp->key, 0) != YP_TRUE) { + result.stat = yp_errno; + return(&result); + } + + result.key.keydat_len = argp->key.keydat_len; + result.key.keydat_val = argp->key.keydat_val; + + result.stat = yp_nextbykey(&result.key, &result.val); + + return (&result); +} + +static void +ypxfr_callback(ypxfrstat rval, struct sockaddr_in *addr, unsigned int transid, + unsigned int prognum, unsigned long port) +{ + CLIENT *clnt; + int sock = RPC_ANYSOCK; + struct timeval timeout; + yppushresp_xfr ypxfr_resp; + struct rpc_err err; + + timeout.tv_sec = 5; + timeout.tv_usec = 0; + addr->sin_port = htons(port); + + if ((clnt = clntudp_create(addr,prognum,1,timeout,&sock)) == NULL) { + yp_error("%s: %s", inet_ntoa(addr->sin_addr), + clnt_spcreateerror("failed to establish callback handle")); + return; + } + + ypxfr_resp.status = rval; + ypxfr_resp.transid = transid; + + /* Turn the timeout off -- we don't want to block. */ + timeout.tv_sec = 0; + if (clnt_control(clnt, CLSET_TIMEOUT, &timeout) == FALSE) + yp_error("failed to set timeout on ypproc_xfr callback"); + + if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) { + clnt_geterr(clnt, &err); + if (err.re_status != RPC_SUCCESS && + err.re_status != RPC_TIMEDOUT) + yp_error("%s", clnt_sperror(clnt, + "ypxfr callback failed")); + } + + clnt_destroy(clnt); +} + +#define YPXFR_RETURN(CODE) \ + /* Order is important: send regular RPC reply, then callback */ \ + result.xfrstat = CODE; \ + svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_ypresp_xfr, &result); \ + ypxfr_callback(CODE,rqhost,argp->transid, \ + argp->prog,argp->port); \ + return(NULL); + +ypresp_xfr * +ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp) +{ + static ypresp_xfr result; + struct sockaddr_in *rqhost; + ypresp_master *mres; + ypreq_nokey mreq; + + result.transid = argp->transid; + rqhost = svc_getcaller(rqstp->rq_xprt); + +#ifdef DB_CACHE + if (yp_access(argp->map_parms.map, + argp->map_parms.domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) { +#endif + YPXFR_RETURN(YPXFR_REFUSED) + } + + + if (argp->map_parms.domain == NULL) { + YPXFR_RETURN(YPXFR_BADARGS) + } + + if (yp_validdomain(argp->map_parms.domain)) { + YPXFR_RETURN(YPXFR_NODOM) + } + + /* + * Determine the master host ourselves. The caller may + * be up to no good. This has the side effect of verifying + * that the requested map and domain actually exist. + */ + + mreq.domain = argp->map_parms.domain; + mreq.map = argp->map_parms.map; + + mres = ypproc_master_2_svc(&mreq, rqstp); + + if (mres->stat != YP_TRUE) { + yp_error("couldn't find master for map %s@%s", + argp->map_parms.map, + argp->map_parms.domain); + yp_error("host at %s (%s) may be pulling my leg", + argp->map_parms.peer, + inet_ntoa(rqhost->sin_addr)); + YPXFR_RETURN(YPXFR_REFUSED) + } + + switch (yp_fork()) { + case 0: + { + char g[11], t[11], p[11]; + char ypxfr_command[MAXPATHLEN + 2]; + + snprintf (ypxfr_command, sizeof(ypxfr_command), "%sypxfr", _PATH_LIBEXEC); + snprintf (t, sizeof(t), "%u", argp->transid); + snprintf (g, sizeof(g), "%u", argp->prog); + snprintf (p, sizeof(p), "%u", argp->port); + if (debug) { + close(0); close(1); close(2); + } + if (strcmp(yp_dir, _PATH_YP)) { + execl(ypxfr_command, "ypxfr", + "-d", argp->map_parms.domain, + "-h", mres->peer, + "-p", yp_dir, "-C", t, + g, inet_ntoa(rqhost->sin_addr), + p, argp->map_parms.map, + NULL); + } else { + execl(ypxfr_command, "ypxfr", + "-d", argp->map_parms.domain, + "-h", mres->peer, + "-C", t, + g, inet_ntoa(rqhost->sin_addr), + p, argp->map_parms.map, + NULL); + } + yp_error("ypxfr execl(%s): %s", ypxfr_command, strerror(errno)); + YPXFR_RETURN(YPXFR_XFRERR) + /* + * Just to safe, prevent PR #10970 from biting us in + * the unlikely case that execing ypxfr fails. We don't + * want to have any child processes spawned from this + * child process. + */ + _exit(0); + break; + } + case -1: + yp_error("ypxfr fork(): %s", strerror(errno)); + YPXFR_RETURN(YPXFR_XFRERR) + break; + default: + result.xfrstat = YPXFR_SUCC; + children++; + break; + } + + return (&result); +} +#undef YPXFR_RETURN + +void * +ypproc_clear_2_svc(void *argp, struct svc_req *rqstp) +{ + static char * result; + static char rval = 0; + +#ifdef DB_CACHE + if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) +#else + if (yp_access(NULL, (struct svc_req *)rqstp)) +#endif + return (NULL); +#ifdef DB_CACHE + /* clear out the database cache */ + yp_flush_all(); +#endif + /* Re-read the securenets database for the hell of it. */ + load_securenets(); + + result = &rval; + return((void *) &result); +} + +/* + * For ypproc_all, we have to send a stream of ypresp_all structures + * via TCP, but the XDR filter generated from the yp.x protocol + * definition file only serializes one such structure. This means that + * to send the whole stream, you need a wrapper which feeds all the + * records into the underlying XDR routine until it hits an 'EOF.' + * But to use the wrapper, you have to violate the boundaries between + * RPC layers by calling svc_sendreply() directly from the ypproc_all + * service routine instead of letting the RPC dispatcher do it. + * + * Bleah. + */ + +/* + * Custom XDR routine for serialzing results of ypproc_all: keep + * reading from the database and spew until we run out of records + * or encounter an error. + */ +static bool_t +xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp) +{ + while (1) { + /* Get a record. */ + if ((objp->ypresp_all_u.val.stat = + yp_nextbykey(&objp->ypresp_all_u.val.key, + &objp->ypresp_all_u.val.val)) == YP_TRUE) { + objp->more = TRUE; + } else { + objp->more = FALSE; + } + + /* Serialize. */ + if (!xdr_ypresp_all(xdrs, objp)) + return(FALSE); + if (objp->more == FALSE) + return(TRUE); + } +} + +ypresp_all * +ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) +{ + static ypresp_all result; + + /* + * Set this here so that the client will be forced to make + * at least one attempt to read from us even if all we're + * doing is returning an error. + */ + result.more = TRUE; + result.ypresp_all_u.val.key.keydat_len = 0; + result.ypresp_all_u.val.key.keydat_val = ""; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.ypresp_all_u.val.stat = YP_YPERR; + return (&result); + } + + if (argp->domain == NULL || argp->map == NULL) { + result.ypresp_all_u.val.stat = YP_BADARGS; + return (&result); + } + + /* + * XXX If we hit the child limit, fail the request. + * If we don't, and the map is large, we could block for + * a long time in the parent. + */ + if (children >= MAX_CHILDREN) { + result.ypresp_all_u.val.stat = YP_YPERR; + return(&result); + } + + /* + * The ypproc_all procedure can take a while to complete. + * Best to handle it in a subprocess so the parent doesn't + * block. (Is there a better way to do this? Maybe with + * async socket I/O?) + */ + if (!debug) { + switch (yp_fork()) { + case 0: + break; + case -1: + yp_error("ypall fork(): %s", strerror(errno)); + result.ypresp_all_u.val.stat = YP_YPERR; + return(&result); + break; + default: + children++; + return (NULL); + break; + } + } + + /* + * Fix for PR #10971: don't let the child ypserv share + * DB handles with the parent process. + */ +#ifdef DB_CACHE + yp_flush_all(); +#endif + + if (yp_select_map(argp->map, argp->domain, + &result.ypresp_all_u.val.key, 0) != YP_TRUE) { + result.ypresp_all_u.val.stat = yp_errno; + return(&result); + } + + /* Kick off the actual data transfer. */ + svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_my_ypresp_all, &result); + + /* + * Proper fix for PR #10970: exit here so that we don't risk + * having a child spawned from this sub-process. + */ + if (!debug) + _exit(0); + + return &result; +} + +ypresp_master * +ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) +{ + static ypresp_master result; + static char ypvalbuf[YPMAXRECORD]; + keydat key = { MASTER_SZ, MASTER_STRING }; + valdat val; + + result.peer = ""; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return(&result); + } + + if (argp->domain == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) { + result.stat = yp_errno; + return(&result); + } + + /* + * Note that we copy the data retrieved from the database to + * a private buffer and NUL terminate the buffer rather than + * terminating the data in place. We do this because by stuffing + * a '\0' into data.data, we will actually be corrupting memory + * allocated by the DB package. This is a bad thing now that we + * cache DB handles rather than closing the database immediately. + */ + result.stat = yp_getbykey(&key, &val); + if (result.stat == YP_TRUE) { + bcopy(val.valdat_val, &ypvalbuf, val.valdat_len); + ypvalbuf[val.valdat_len] = '\0'; + result.peer = ypvalbuf; + } else + result.peer = ""; + + return (&result); +} + +ypresp_order * +ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp) +{ + static ypresp_order result; + keydat key = { ORDER_SZ, ORDER_STRING }; + valdat val; + + result.ordernum = 0; + +#ifdef DB_CACHE + if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) { +#else + if (yp_access(argp->map, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return(&result); + } + + if (argp->domain == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + /* + * We could just check the timestamp on the map file, + * but that's a hack: we'll only know the last time the file + * was touched, not the last time the database contents were + * updated. + */ + + if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) { + result.stat = yp_errno; + return(&result); + } + + result.stat = yp_getbykey(&key, &val); + + if (result.stat == YP_TRUE) + result.ordernum = atoi(val.valdat_val); + else + result.ordernum = 0; + + return (&result); +} + +static void yp_maplist_free(struct ypmaplist *yp_maplist) +{ + register struct ypmaplist *next; + + while (yp_maplist) { + next = yp_maplist->next; + free(yp_maplist->map); + free(yp_maplist); + yp_maplist = next; + } +} + +static struct ypmaplist * +yp_maplist_create(const char *domain) +{ + char yp_mapdir[MAXPATHLEN + 2]; + char yp_mapname[MAXPATHLEN + 2]; + struct ypmaplist *cur = NULL; + struct ypmaplist *yp_maplist = NULL; + DIR *dird; + struct dirent *dirp; + struct stat statbuf; + + snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain); + + if ((dird = opendir(yp_mapdir)) == NULL) { + yp_error("opendir(%s) failed: %s", yp_mapdir, strerror(errno)); + return(NULL); + } + + while ((dirp = readdir(dird)) != NULL) { + if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) { + snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s", + yp_mapdir,dirp->d_name); + if (stat(yp_mapname, &statbuf) < 0 || + !S_ISREG(statbuf.st_mode)) + continue; + if ((cur = (struct ypmaplist *) + malloc(sizeof(struct ypmaplist))) == NULL) { + yp_error("malloc() failed"); + closedir(dird); + yp_maplist_free(yp_maplist); + return(NULL); + } + if ((cur->map = strdup(dirp->d_name)) == NULL) { + yp_error("strdup() failed: %s",strerror(errno)); + closedir(dird); + yp_maplist_free(yp_maplist); + free(cur); + return(NULL); + } + cur->next = yp_maplist; + yp_maplist = cur; + if (debug) + yp_error("map: %s", yp_maplist->map); + } + + } + closedir(dird); + return(yp_maplist); +} + +ypresp_maplist * +ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp) +{ + static ypresp_maplist result = { 0, NULL }; + +#ifdef DB_CACHE + if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) { +#else + if (yp_access(NULL, (struct svc_req *)rqstp)) { +#endif + result.stat = YP_YPERR; + return(&result); + } + + if (argp == NULL) { + result.stat = YP_BADARGS; + return (&result); + } + + if (yp_validdomain(*argp)) { + result.stat = YP_NODOM; + return (&result); + } + + /* + * We have to construct a linked list for the ypproc_maplist + * procedure using dynamically allocated memory. Since the XDR + * layer won't free this list for us, we have to deal with it + * ourselves. We call yp_maplist_free() first to free any + * previously allocated data we may have accumulated to insure + * that we have only one linked list in memory at any given + * time. + */ + + yp_maplist_free(result.maps); + + if ((result.maps = yp_maplist_create(*argp)) == NULL) { + yp_error("yp_maplist_create failed"); + result.stat = YP_YPERR; + return(&result); + } else + result.stat = YP_TRUE; + + return (&result); +} + +/* + * NIS v1 support. The nullproc, domain and domain_nonack + * functions from v1 are identical to those in v2, so all + * we have to do is hand off to them. + * + * The other functions are mostly just wrappers around their v2 + * counterparts. For example, for the v1 'match' procedure, we + * crack open the argument structure, make a request to the v2 + * 'match' function, repackage the data into a v1 response and + * then send it on its way. + * + * Note that we don't support the pull, push and get procedures. + * There's little documentation available to show what they + * do, and I suspect they're meant largely for map transfers + * between master and slave servers. + */ + +void * +ypoldproc_null_1_svc(void *argp, struct svc_req *rqstp) +{ + return(ypproc_null_2_svc(argp, rqstp)); +} + +bool_t * +ypoldproc_domain_1_svc(domainname *argp, struct svc_req *rqstp) +{ + return(ypproc_domain_2_svc(argp, rqstp)); +} + +bool_t * +ypoldproc_domain_nonack_1_svc(domainname *argp, struct svc_req *rqstp) +{ + return (ypproc_domain_nonack_2_svc(argp, rqstp)); +} + +/* + * the 'match' procedure sends a response of type YPRESP_VAL + */ +ypresponse * +ypoldproc_match_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + ypresp_val *v2_result; + + result.yp_resptype = YPRESP_VAL; + result.ypresponse_u.yp_resp_valtype.val.valdat_val = ""; + result.ypresponse_u.yp_resp_valtype.val.valdat_len = 0; + + if (argp->yp_reqtype != YPREQ_KEY) { + result.ypresponse_u.yp_resp_valtype.stat = YP_BADARGS; + return(&result); + } + + v2_result = ypproc_match_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp); + if (v2_result == NULL) + return(NULL); + + bcopy(v2_result, &result.ypresponse_u.yp_resp_valtype, + sizeof(ypresp_val)); + + return (&result); +} + +/* + * the 'first' procedure sends a response of type YPRESP_KEY_VAL + */ +ypresponse * +ypoldproc_first_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + ypresp_key_val *v2_result; + + result.yp_resptype = YPRESP_KEY_VAL; + result.ypresponse_u.yp_resp_key_valtype.val.valdat_val = + result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = ""; + result.ypresponse_u.yp_resp_key_valtype.val.valdat_len = + result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0; + + if (argp->yp_reqtype != YPREQ_NOKEY) { + result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS; + return(&result); + } + + v2_result = ypproc_first_2_svc(&argp->yprequest_u.yp_req_nokeytype, + rqstp); + if (v2_result == NULL) + return(NULL); + + bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype, + sizeof(ypresp_key_val)); + + return (&result); +} + +/* + * the 'next' procedure sends a response of type YPRESP_KEY_VAL + */ +ypresponse * +ypoldproc_next_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + ypresp_key_val *v2_result; + + result.yp_resptype = YPRESP_KEY_VAL; + result.ypresponse_u.yp_resp_key_valtype.val.valdat_val = + result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = ""; + result.ypresponse_u.yp_resp_key_valtype.val.valdat_len = + result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0; + + if (argp->yp_reqtype != YPREQ_KEY) { + result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS; + return(&result); + } + + v2_result = ypproc_next_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp); + if (v2_result == NULL) + return(NULL); + + bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype, + sizeof(ypresp_key_val)); + + return (&result); +} + +/* + * the 'poll' procedure sends a response of type YPRESP_MAP_PARMS + */ +ypresponse * +ypoldproc_poll_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + ypresp_master *v2_result1; + ypresp_order *v2_result2; + + result.yp_resptype = YPRESP_MAP_PARMS; + result.ypresponse_u.yp_resp_map_parmstype.domain = + argp->yprequest_u.yp_req_nokeytype.domain; + result.ypresponse_u.yp_resp_map_parmstype.map = + argp->yprequest_u.yp_req_nokeytype.map; + /* + * Hmm... there is no 'status' value in the + * yp_resp_map_parmstype structure, so I have to + * guess at what to do to indicate a failure. + * I hope this is right. + */ + result.ypresponse_u.yp_resp_map_parmstype.ordernum = 0; + result.ypresponse_u.yp_resp_map_parmstype.peer = ""; + + if (argp->yp_reqtype != YPREQ_MAP_PARMS) { + return(&result); + } + + v2_result1 = ypproc_master_2_svc(&argp->yprequest_u.yp_req_nokeytype, + rqstp); + if (v2_result1 == NULL) + return(NULL); + + if (v2_result1->stat != YP_TRUE) { + return(&result); + } + + v2_result2 = ypproc_order_2_svc(&argp->yprequest_u.yp_req_nokeytype, + rqstp); + if (v2_result2 == NULL) + return(NULL); + + if (v2_result2->stat != YP_TRUE) { + return(&result); + } + + result.ypresponse_u.yp_resp_map_parmstype.peer = + v2_result1->peer; + result.ypresponse_u.yp_resp_map_parmstype.ordernum = + v2_result2->ordernum; + + return (&result); +} + +ypresponse * +ypoldproc_push_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + + /* + * Not implemented. + */ + + return (&result); +} + +ypresponse * +ypoldproc_pull_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + + /* + * Not implemented. + */ + + return (&result); +} + +ypresponse * +ypoldproc_get_1_svc(yprequest *argp, struct svc_req *rqstp) +{ + static ypresponse result; + + /* + * Not implemented. + */ + + return (&result); +} diff --git a/usr.sbin/ypserv/yp_svc_udp.c b/usr.sbin/ypserv/yp_svc_udp.c new file mode 100644 index 000000000000..9cb904db01b1 --- /dev/null +++ b/usr.sbin/ypserv/yp_svc_udp.c @@ -0,0 +1,71 @@ +/*- + * SPDX-License-Identifier: BSD-4-Clause + * + * Copyright (c) 1996 + * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Bill Paul. + * 4. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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/cdefs.h> +#include <rpc/rpc.h> +#include <rpc/svc_dg.h> +#include "yp_extern.h" + +#define su_data(xprt) ((struct svc_dg_data *)((xprt)->xp_p2)) + +/* + * We need to be able to manually set the transaction ID in the + * UDP transport handle, but the standard library offers us no way + * to do that. Hence we need this garbage. + */ + +unsigned long +svcudp_get_xid(SVCXPRT *xprt) +{ + struct svc_dg_data *su; + + if (xprt == NULL) + return(0); + su = su_data(xprt); + return(su->su_xid); +} + +unsigned long +svcudp_set_xid(SVCXPRT *xprt, unsigned long xid) +{ + struct svc_dg_data *su; + unsigned long old_xid; + + if (xprt == NULL) + return(0); + su = su_data(xprt); + old_xid = su->su_xid; + su->su_xid = xid; + return(old_xid); +} diff --git a/usr.sbin/ypserv/ypinit.8 b/usr.sbin/ypserv/ypinit.8 new file mode 100644 index 000000000000..6e09a64aa86e --- /dev/null +++ b/usr.sbin/ypserv/ypinit.8 @@ -0,0 +1,198 @@ +.\" Copyright (c) 1997 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul OR THE VOICES IN HIS HEAD +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (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 August 18, 2015 +.Dt YPINIT 8 +.Os +.Sh NAME +.Nm ypinit +.Nd build and install NIS databases +.Sh SYNOPSIS +.Nm +.Fl m +.Op Ar domainname +.Nm +.Fl s +.Ar master_server +.Op Ar domainname +.Nm +.Fl u +.Op Ar domainname +.Sh DESCRIPTION +The +.Nm +utility is a script which sets up databases on a Network Information Service +(NIS) +master or slave server. +.Pp +On a master server, +.Nm +creates the +.Pa /var/yp/$DOMAINNAME +directory, the +.Pa /var/yp/ypservers +file, and calls +.Pa /var/yp/Makefile +to create and populate an initial set of NIS maps. +The maps are +created from local source files using the +.Xr yp_mkdb 8 +utility. +The utility will prompt the user for a list of servers +that support the specified domain; this list is used to populate +the ypservers map. +.Pp +On a slave server, +.Nm +creates the +.Pa /var/yp/$DOMAINNAME , +populates it with copies of the NIS maps from the master. +The maps +are obtained from the master using the +.Xr ypxfr 8 +utility. +The +.Nm +utility obtains the list of maps to transfer in one of two ways: if +the system is configured as an NIS client and is bound to the master +server, +.Nm +is able to use the +.Xr ypwhich 1 +utility to obtain a list of maps exported by the master server. +If the system is not configured as a client of the NIS master, +.Nm +uses a hardcoded list of maps, some of which may or may not actually +exist on the master. +The system administrator can edit the +.Nm +script and +modify the map list if necessary. +Otherwise, individual maps can +be transferred manually from the master using +.Xr ypxfr 8 . +.Sh OPTIONS +The +.Nm +utility supports the following options: +.Bl -tag -width indent +.It Fl m Op Ar domainname +Set up a master server. +By default, +.Nm +sets up a server for +the system default domain. +The user can override this default by specifying +.Ar domainname +explicitly. +Maps are constructed from scratch using local files as templates using +the +.Xr yp_mkdb 8 +utility. +.It Fl s Ar master_server Op Ar domainname +Set up a slave server using +.Ar master_name +as the master. +Maps are copied from +.Ar master_server +to the slave using +.Xr ypxfr 8 . +By default, +.Nm +sets up a server for +the system default domain. +The user can override this default by specifying +.Ar domainname +explicitly. +.It Fl u Op Ar domainname +Update the ypservers map on the master server. +When a new slave +server is added to a domain, its hostname must be added to the +ypservers map so that +.Xr yppush 8 +can propagate updates on the master to all of the slaves. +.El +.Sh FILES +.Bl -tag -width /var/yp/master.passwd -compact +.It Pa /etc/bootparams +Bootparams source file +.It Pa /etc/ethers +Ethers data source file +.It Pa /etc/eui64 +EUI64 data source file +.It Pa /etc/group +Group source file +.It Pa /etc/hosts +Hostname/IP address source file +.It Pa /etc/netid +RPC netid source file +.It Pa /etc/networks +Networks source file +.It Pa /etc/protocols +Protocols source file +.It Pa /etc/publickey +RPC public key/secret key source file +.It Pa /etc/services +Services data source file +.It Pa /etc/shells +Shells source file +.It Pa /var/yp/master.passwd +Passwd database source file +.It Pa /var/yp/netgroup +Netgroup data source file +.It Pa /var/yp/ypservers +Ypservers source file (generated by +.Nm ) +.El +.Sh SEE ALSO +.Xr mknetid 8 , +.Xr revnetgroup 8 , +.Xr yp 8 , +.Xr yp_mkdb 8 , +.Xr yppush 8 , +.Xr ypserv 8 , +.Xr ypxfr 8 +.Sh HISTORY +This version of +.Nm +is based on the +.Nm +script in +.Ox . +It first appeared in +.Fx 3.0 . +.Sh AUTHORS +.An -nosplit +The original script was written by +.An Mats O Jansson Aq Mt moj@stacken.kth.se . +It was modified for +.Fx +by +.An Bill Paul Aq Mt wpaul@ctr.columbia.edu . diff --git a/usr.sbin/ypserv/ypinit.sh b/usr.sbin/ypserv/ypinit.sh new file mode 100644 index 000000000000..63f4e0706b92 --- /dev/null +++ b/usr.sbin/ypserv/ypinit.sh @@ -0,0 +1,386 @@ +#!/bin/sh +# +# ypinit.sh - setup a master or slave server. +# (Taken from OpenBSD and modified for FreeBSD.) +# +DOMAINNAME=/bin/domainname +HOSTNAME=/bin/hostname +YPWHICH=/usr/bin/ypwhich +YPXFR=/usr/libexec/ypxfr +YP_DIR=/var/yp +MAKEDBM=/usr/sbin/yp_mkdb +MAPLIST="master.passwd.byname master.passwd.byuid passwd.byname passwd.byuid \ + group.byname group.bygid hosts.byname hosts.byaddr services.byname \ + rpc.byname rpc.bynumber networks.byname networks.byaddr netgroup \ + netgroup.byuser netgroup.byhost netid.byname publickey.byname \ + bootparams ethers.byname ethers.byaddr eui64.byname eui64.byid \ + amd.host mail.aliases ypservers protocols.byname protocols.bynumber \ + netmasks.byaddr" + +ERROR_EXISTS="NO" +umask 077 + +#set -xv + +ERROR=USAGE # assume usage error + +if [ $# -eq 1 ] +then + if [ $1 = "-m" ] # ypinit -m + then + DOMAIN=`${DOMAINNAME}` + SERVERTYPE=MASTER + ERROR= + fi + + if [ $1 = "-u" ] # ypinit -u + then + DOMAIN=`${DOMAINNAME}` + SERVERTYPE=UPDATE + ERROR= + fi +fi + +if [ $# -eq 2 ] +then + if [ $1 = "-m" ] # ypinit -m domainname + then + DOMAIN=${2} + SERVERTYPE=MASTER + ERROR= + fi + + if [ $1 = "-s" ] # ypinit -s master_server + then + DOMAIN=`${DOMAINNAME}` + SERVERTYPE=SLAVE + MASTER=${2} + ERROR= + fi + + if [ $1 = "-u" ] # ypinit -u domainname + then + DOMAIN=${2} + SERVERTYPE=UPDATE + ERROR= + fi +fi + +if [ $# -eq 3 ] +then + if [ $1 = "-s" ] # ypinit -s master_server domainname + then + DOMAIN=${3} + SERVERTYPE=SLAVE + MASTER=${2} + ERROR= + fi +fi + +if [ "${ERROR}" = "USAGE" ]; then + cat << \__usage 1>&2 +usage: ypinit -m [domainname] + ypinit -s master_server [domainname] + ypinit -u [domainname] + +The `-m' flag builds a master YP server, and the `-s' flag builds +a slave YP server. When building a slave YP server, `master_server' +must be an existing, reachable YP server. +The `-u' is for updating the ypservers map on a master server. +__usage + + exit 1 +fi + +# Check if domainname is set, don't accept an empty domainname +if [ -z "${DOMAIN}" ]; then + cat << \__no_domain 1>&2 +The local host's YP domain name has not been set. Please set it with +the domainname(1) command or pass the domain as an argument to ypinit(8). +__no_domain + + exit 1 +fi + +# Check if hostname is set, don't accept an empty hostname +HOST=`${HOSTNAME}` +if [ -z "${HOST}" ]; then + cat << \__no_hostname 1>&2 +The local host's hostname has not been set. Please set it with the +hostname(1) command. +__no_hostname + + exit 1 +fi + +# Check if we have contact with master. +# If we can't list the maps on the master, then we fake it with a +# hard-coded list of maps. The FreeBSD ypxfr command will work even +# if ypbind isn't running or if we are bound to ourselves instead of +# the master (the slave should be bound to itself, but since it has +# no maps yet, we can't get a maplist from it). +if [ "${SERVERTYPE}" = "SLAVE" ]; +then + COUNT=`${YPWHICH} -d ${DOMAIN} -m 2>/dev/null | grep -i ${MASTER} | wc -l | tr -d " "` + if [ "$COUNT" = "0" ] + then + echo "Can't enumerate maps from ${MASTER}. Please check that it is running." 1>&2 + echo "Note: using hardcoded maplist for map transfers." 1>&2 + YPMAPLIST=${MAPLIST} + else + YPMAPLIST=`${YPWHICH} -d ${DOMAIN} -m | cut -d\ -f1` + fi + echo "" 1>&2 +fi + +# Check if user is root +ID=`id -u` +if [ "${ID}" != "0" ]; then + echo "You have to be the superuser to run this. Please login as root." 1>&2 + exit 1 +fi + +# Check if the YP directory exists. + +if [ ! -d ${YP_DIR} -o -f ${YP_DIR} ] +then + echo "The directory ${YP_DIR} doesn't exist. Restore it from the distribution." 1>&2 + exit 1 + +fi + +echo -n "Server Type: ${SERVERTYPE} Domain: ${DOMAIN}" +if [ "${SERVERTYPE}" = "SLAVE" ]; then + echo -n " Master: ${MASTER}" +fi +echo "" + +if [ "${SERVERTYPE}" != "UPDATE" ]; +then + cat << \__notice1 + +Creating an YP server will require that you answer a few questions. +Questions will all be asked at the beginning of the procedure. + +__notice1 + + echo -n "Do you want this procedure to quit on non-fatal errors? [y/n: n] " + read DOEXIT + + case ${DOEXIT} in + y*|Y*) + ERROR_EXIT="YES" + ;; + + *) ERROR_EXIT="NO" + echo "" + echo "Ok, please remember to go back and redo manually whatever fails." + echo "If you don't, something might not work. " + ;; + esac + + if [ -d "${YP_DIR}/${DOMAIN}" ]; then + echo "" + echo -n "Can we destroy the existing ${YP_DIR}/${DOMAIN} and its contents? [y/n: n] " + read KILL + + ERROR= + case ${KILL} in + y*|Y*) + ERROR="DELETE" + ;; + + *) ERROR= + ;; + esac + + if [ "${ERROR}" = "DELETE" ]; then + if ! rm -rf ${YP_DIR}/${DOMAIN}; then + echo "Can't clean up old directory ${YP_DIR}/${DOMAIN}." 1>&2 + exit 1 + fi + else + echo "OK, please clean it up by hand and start again. Bye" + exit 0 + fi + fi + + if ! mkdir "${YP_DIR}/${DOMAIN}"; then + echo "Can't make new directory ${YP_DIR}/${DOMAIN}." 1>&2 + exit 1 + fi +fi + +if [ "${SERVERTYPE}" = "MASTER" ]; +then + + if [ ! -f ${YP_DIR}/Makefile ] + then + if [ ! -f ${YP_DIR}/Makefile.dist ] + then + echo "Can't find ${YP_DIR}/Makefile.dist. " 1>&2 + exit 1 + fi + cp ${YP_DIR}/Makefile.dist ${YP_DIR}/Makefile + fi + +fi + +if [ "${SERVERTYPE}" = "SLAVE" ]; +then + + echo "There will be no further questions. The remainder of the procedure" + echo "should take a few minutes, to copy the databases from ${MASTER}." + + for MAP in ${YPMAPLIST} + do + echo "Transferring ${MAP}..." + if ! ${YPXFR} -p ${YP_DIR} -h ${MASTER} -c -d ${DOMAIN} ${MAP}; then + echo "Can't transfer map ${MAP}." 1>&2 + ERROR_EXISTS="YES" + if [ "${ERROR_EXIT}" = "YES" ]; then + exit 1 + fi + fi + done + + echo "" + if [ "${ERROR_EXISTS}" = "YES" ]; then + echo "${HOST} has been setup as an YP slave server with errors. " 1>&2 + echo "Please remember fix any problem that occurred." 1>&2 + else + echo "${HOST} has been setup as an YP slave server without any errors. " + fi + + echo "Don't forget to update map ypservers on ${MASTER}." + exit 0 +fi + +LIST_OK="NO" + +while [ "${LIST_OK}" = "NO" ]; +do + if [ "${SERVERTYPE}" = "MASTER" ]; + then + HOST_LIST="${HOST}" + echo "" + echo "At this point, we have to construct a list of this domains YP servers." + echo "${HOST} is already known as master server." + echo "Please continue to add any slave servers, one per line. When you are" + echo "done with the list, type a <control D>." + echo " master server : ${HOST}" + fi + + if [ "${SERVERTYPE}" = "UPDATE" ]; + then + HOST_LIST="${HOST}" + NEW_LIST="" + MASTER_NAME="" + SHORT_HOST=`echo ${HOST} | cut -d. -f1` + if [ -f ${YP_DIR}/${DOMAIN}/ypservers ]; + then + for srv in `${MAKEDBM} -u ${YP_DIR}/${DOMAIN}/ypservers | grep -v "^YP" | tr "\t" " " | cut -d\ -f1`; + do + short_srv=`echo ${srv} | cut -d. -f1` + if [ "${SHORT_HOST}" != "${short_srv}" ] + then + if [ "${NEW_LIST}" = "" ]; + then + NEW_LIST="${srv}" + else + NEW_LIST="${NEW_LIST} ${srv}" + fi + fi + done; + MASTER_NAME=`${MAKEDBM} -u ${YP_DIR}/${DOMAIN}/ypservers | grep "^YP_MASTER_NAME" | tr "\t" " " | cut -d\ -f2` + fi + echo "" + echo "Update the list of hosts running YP servers in domain ${DOMAIN}." + echo "Master for this domain is ${MASTER_NAME}." + echo "" + echo "First verify old servers, type \\\\ to remove a server." + echo "Then add new servers, one per line. When done type a <control D>." + echo "" + echo " master server : ${HOST}" + if [ "${NEW_LIST}" != "" ]; then + for node in $NEW_LIST; do + echo -n " verify host : [${node}] " + read verify + if [ "${verify}" != "\\" ]; then + HOST_LIST="${HOST_LIST} ${node}" + fi + done; + fi + fi + + echo -n " next host to add: " + + while read h + do + echo -n " next host to add: " + HOST_LIST="${HOST_LIST} ${h}" + done + + echo "" + echo "The current list of NIS servers looks like this:" + echo "" + + for h in `echo ${HOST_LIST}`; + do + echo ${h} + done + + echo "" + echo -n "Is this correct? [y/n: y] " + read hlist_ok + + case $hlist_ok in + n*) echo "Let's try the whole thing again...";; + N*) echo "Let's try the whole thing again...";; + *) LIST_OK="YES";; + esac + +done + +echo "Building ${YP_DIR}/${DOMAIN}/ypservers..." +rm -f ${YP_DIR}/ypservers +touch -f ${YP_DIR}/ypservers +rm -f ${YP_DIR}/${DOMAIN}/ypservers +for host in ${HOST_LIST}; +do + echo "${host} ${host}" >> ${YP_DIR}/ypservers + echo "${host} ${host}" +done | ${MAKEDBM} - ${YP_DIR}/${DOMAIN}/ypservers + +if [ $? -ne 0 ]; then + echo "" 1>&2 + echo "Couldn't build yp data base ${YP_DIR}/${DOMAIN}/ypservers." 1>&2 + ERROR_EXISTS="YES" + if [ "${ERROR_EXIT}" = "YES" ]; then + exit 1 + fi +fi + +if [ "${SERVERTYPE}" = "MASTER" ]; then + CUR_PWD=`pwd` + cd ${YP_DIR} + echo "Running ${YP_DIR}/Makefile..." + if ! make NOPUSH=True UPDATE_DOMAIN=${DOMAIN} YP_DIR=${YP_DIR}; then + echo "" 1>&2 + echo "Error running Makefile." 1>&2 + ERROR_EXISTS="YES" + if [ "${ERROR_EXIT}" = "YES" ]; then + exit 1 + fi + fi + + cd ${CUR_PWD} + + echo "" + if [ "${ERROR_EXISTS}" = "YES" ]; then + echo "${HOST} has been setup as an YP master server with errors. " 1>&2 + echo "Please remember fix any problem that occurred." 1>&2 + else + echo "${HOST} has been setup as an YP master server without any errors. " + fi +fi diff --git a/usr.sbin/ypserv/ypserv.8 b/usr.sbin/ypserv/ypserv.8 new file mode 100644 index 000000000000..9c33b4b95ddc --- /dev/null +++ b/usr.sbin/ypserv/ypserv.8 @@ -0,0 +1,461 @@ +.\" Copyright (c) 1995 +.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Bill Paul. +.\" 4. Neither the name of the author nor the names of any co-contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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 December 13, 2009 +.Dt YPSERV 8 +.Os +.Sh NAME +.Nm ypserv +.Nd NIS database server +.Sh SYNOPSIS +.Nm +.Op Fl n +.Op Fl d +.Op Fl P Ar port +.Op Fl p Ar path +.Sh DESCRIPTION +.Tn NIS +is an RPC-based service designed to allow a number of UNIX-based +machines to share a common set of configuration files. +Rather than +requiring a system administrator to update several copies of files +such as +.Pa /etc/hosts , +.Pa /etc/passwd +and +.Pa /etc/group , +which tend to require frequent changes in most environments, +.Tn NIS +allows groups of computers to share one set of data which can be +updated from a single location. +.Pp +The +.Nm +utility is the server that distributes +.Tn NIS +databases to client systems within an +.Tn NIS +.Em domain . +Each client in an +.Tn NIS +domain must have its domainname set to +one of the domains served by +.Nm +using the +.Xr domainname 1 +command. +The clients must also run +.Xr ypbind 8 +in order to attach to a particular server, since it is possible to +have several servers within a single +.Tn NIS +domain. +.Pp +The databases distributed by +.Nm +are stored in +.Pa /var/yp/[domainname] +where +.Pa domainname +is the name of the domain being served. +There can be several +such directories with different domainnames, and you need only one +.Nm +daemon to handle them all. +.Pp +The databases, or +.Pa maps +as they are often called, +are created by +.Pa /var/yp/Makefile +using several system files as source. +The database files are in +.Xr db 3 +format to help speed retrieval when there are many records involved. +In +.Fx , +the maps are always readable and writable only by root for security +reasons. +Technically this is only necessary for the password +maps, but since the data in the other maps can be found in +other world-readable files anyway, it does not hurt and it is considered +good general practice. +.Pp +The +.Nm +utility is started by +.Pa /etc/rc.d/ypserv +if it has been enabled in +.Pa /etc/rc.conf . +.Sh SPECIAL FEATURES +There are some problems associated with distributing a +.Fx +password +database via +.Tn NIS : +.Fx +normally only stores encrypted passwords +in +.Pa /etc/master.passwd , +which is readable and writable only by root. +By turning this file +into an +.Tn NIS +map, this security feature would be completely defeated. +.Pp +To make up for this, the +.Fx +version of +.Nm +handles the +.Pa master.passwd.byname +and +.Pa master.passwd.byuid +maps in a special way. +When the server receives a request to access +either of these two maps (or in fact either of the +.Pa shadow.byname +or +.Pa shadow.byuid +maps), it will check the TCP port from which the +request originated and return an error if the port number is greater +than 1023. +Since only the superuser is allowed to bind to TCP ports +with values less than 1024, the server can use this test to determine +whether or not the access request came from a privileged user. +Any requests made by non-privileged users are therefore rejected. +.Pp +Furthermore, the +.Xr getpwent 3 +routines in the +.Fx +standard C library will only attempt to retrieve +data from the +.Pa master.passwd.byname +and +.Pa master.passwd.byuid +maps for the superuser: if a normal user calls any of these functions, +the standard +.Pa passwd.byname +and +.Pa passwd.byuid +maps will be accessed instead. +The latter two maps are constructed by +.Pa /var/yp/Makefile +by parsing the +.Pa master.passwd +file and stripping out the password fields, and are therefore +safe to pass on to unprivileged users. +In this way, the shadow password +aspect of the protected +.Pa master.passwd +database is maintained through +.Tn NIS . +.Sh NOTES +.Ss Setting Up Master and Slave Servers +.Xr ypinit 8 +is a convenient script that will help setup master and slave +.Tn NIS +servers. +.Ss Limitations +There are two problems inherent with password shadowing in +.Tn NIS +that users should +be aware of: +.Bl -enum -offset indent +.It +The +.Sq TCP port less than 1024 +test is trivial to defeat for users with +unrestricted access to machines on your network (even those machines +which do not run UNIX-based operating systems). +.It +If you plan to use a +.Fx +system to serve +.No non- Ns Fx +clients that +have no support for password shadowing (which is most of them), you +will have to disable the password shadowing entirely by uncommenting the +.Em UNSECURE=True +entry in +.Pa /var/yp/Makefile . +This will cause the standard +.Pa passwd.byname +and +.Pa passwd.byuid +maps to be generated with valid encrypted password fields, which is +necessary in order for +.No non- Ns Fx +clients to perform user +authentication through +.Tn NIS . +.El +.Ss Security +In general, any remote user can issue an RPC to +.Nm +and retrieve the contents of your +.Tn NIS +maps, provided the remote user +knows your domain name. +To prevent such unauthorized transactions, +.Nm +supports a feature called +.Pa securenets +which can be used to restrict access to a given set of hosts. +At startup, +.Nm +will attempt to load the securenets information from a file +called +.Pa /var/yp/securenets . +(Note that this path varies depending on the path specified with +the +.Fl p +option, which is explained below.) +This file contains entries +that consist of a network specification and a network mask separated +by white space. +Lines starting with +.Dq \&# +are considered to be comments. +A +sample securenets file might look like this: +.Bd -unfilled -offset indent +# allow connections from local host -- mandatory +127.0.0.1 255.255.255.255 +# allow connections from any host +# on the 192.168.128.0 network +192.168.128.0 255.255.255.0 +# allow connections from any host +# between 10.0.0.0 to 10.0.15.255 +10.0.0.0 255.255.240.0 +.Ed +.Pp +If +.Nm +receives a request from an address that matches one of these rules, +it will process the request normally. +If the address fails to match +a rule, the request will be ignored and a warning message will be +logged. +If the +.Pa /var/yp/securenets +file does not exist, +.Nm +will allow connections from any host. +.Pp +The +.Nm +utility also has support for Wietse Venema's +.Em tcpwrapper +package. +This allows the administrator to use the tcpwrapper +configuration files +.Pa ( /etc/hosts.allow +and +.Pa /etc/hosts.deny ) +for access control instead of +.Pa /var/yp/securenets . +.Pp +Note: while both of these access control mechanisms provide some +security, they, like the privileged port test, are both vulnerable +to +.Dq IP spoofing +attacks. +.Ss NIS v1 compatibility +This version of +.Nm +has some support for serving +.Tn NIS +v1 clients. +The +.Fx +.Tn NIS +implementation only uses the +.Tn NIS +v2 protocol, however other implementations +include support for the v1 protocol for backwards compatibility +with older systems. +The +.Xr ypbind 8 +daemons supplied with these systems will try to establish a binding +to an +.Tn NIS +v1 server even though they may never actually need it (and they may +persist in broadcasting in search of one even after they receive a +response from a v2 server). +Note that while +support for normal client calls is provided, this version of +.Nm +does not handle v1 map transfer requests; consequently, it cannot +be used as a master or slave in conjunction with older +.Tn NIS +servers that +only support the v1 protocol. +Fortunately, there probably are not any +such servers still in use today. +.Ss NIS servers that are also NIS clients +Care must be taken when running +.Nm +in a multi-server domain where the server machines are also +.Tn NIS +clients. +It is generally a good idea to force the servers to +bind to themselves rather than allowing them to broadcast bind +requests and possibly become bound to each other: strange failure +modes can result if one server goes down and +others are dependent upon on it. +(Eventually all the clients will +time out and attempt to bind to other servers, but the delay +involved can be considerable and the failure mode is still present +since the servers might bind to each other all over again). +.Pp +Refer to the +.Xr ypbind 8 +man page for details on how to force it to bind to a particular +server. +.Sh OPTIONS +The following options are supported by +.Nm : +.Bl -tag -width flag +.It Fl n +This option affects the way +.Nm +handles yp_match requests for the +.Pa hosts.byname +and +.Pa hosts.byaddress +maps. +By default, if +.Nm +cannot find an entry for a given host in its hosts maps, it will +return an error and perform no further processing. +With the +.Fl n +flag, +.Nm +will go one step further: rather than giving up immediately, it +will try to resolve the hostname or address using a DNS nameserver +query. +If the query is successful, +.Nm +will construct a fake database record and return it to the client, +thereby making it seem as though the client's yp_match request +succeeded. +.Pp +This feature is provided for compatibility with SunOS 4.1.x, +which has brain-damaged resolver functions in its standard C +library that depend on +.Tn NIS +for hostname and address resolution. +The +.Fx +resolver can be configured to do DNS +queries directly, therefore it is not necessary to enable this +option when serving only +.Fx +.Tn NIS +clients. +.It Fl d +Cause the server to run in debugging mode. +Normally, +.Nm +reports only unusual errors (access violations, file access failures) +using the +.Xr syslog 3 +facility. +In debug mode, the server does not background +itself and prints extra status messages to stderr for each +request that it receives. +Also, while running in debug mode, +.Nm +will not spawn any additional subprocesses as it normally does +when handling yp_all requests or doing DNS lookups. +(These actions +often take a fair amount of time to complete and are therefore handled +in subprocesses, allowing the parent server process to go on handling +other requests.) +This makes it easier to trace the server with +a debugging tool. +.It Fl h Ar addr +Specify a specific address to bind to for requests. This option may be +specified multiple times. If no +.Fl h +option is specified, +.Nm +will bind to default passive address +.Pq e.g. INADDR_ANY for IPv4 +for each transport. +.It Fl P Ar port +Force ypserv to bind to a specific TCP/UDP port, rather than selecting +its own. +.It Fl p Ar path +Normally, +.Nm +assumes that all +.Tn NIS +maps are stored under +.Pa /var/yp . +The +.Fl p +flag may be used to specify an alternate +.Tn NIS +root path, allowing +the system administrator to move the map files to a different place +within the file system. +.El +.Sh FILES +.Bl -tag -width Pa -compact +.It Pa /var/yp/[domainname]/[maps] +the +.Tn NIS +maps +.It Pa /etc/nsswitch.conf +name switch configuration file +.It Pa /var/yp/securenets +host access control file +.El +.Sh SEE ALSO +.Xr ypcat 1 , +.Xr db 3 , +.Xr hosts_access 5 , +.Xr rpc.yppasswdd 8 , +.Xr yp 8 , +.Xr ypbind 8 , +.Xr ypinit 8 , +.Xr yppush 8 , +.Xr ypxfr 8 +.Sh HISTORY +This version of +.Nm +first appeared in +.Fx 2.2 . +.Sh AUTHORS +.An Bill Paul Aq Mt wpaul@ctr.columbia.edu |
