diff options
Diffstat (limited to 'usr.sbin')
46 files changed, 8282 insertions, 2248 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index c51b87a..7a41e40 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -66,7 +66,6 @@ SUBDIR= IPXrouted \ pim6sd \ pkg_install \ pnpinfo \ - portmap \ ppp \ pppctl \ pppd \ @@ -84,6 +83,7 @@ SUBDIR= IPXrouted \ rip6query \ rmt \ route6d \ + rpcbind \ rpc.lockd \ rpc.statd \ rpc.umntall \ diff --git a/usr.sbin/bootparamd/callbootd/callbootd.c b/usr.sbin/bootparamd/callbootd/callbootd.c index 7a09168..b7827ac 100644 --- a/usr.sbin/bootparamd/callbootd/callbootd.c +++ b/usr.sbin/bootparamd/callbootd/callbootd.c @@ -118,8 +118,9 @@ char **argv; } else { clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS, BOOTPARAMPROC_WHOAMI, - xdr_bp_whoami_arg, &whoami_arg, - xdr_bp_whoami_res, &stat_whoami_res, eachres_whoami); + (xdrproc_t)xdr_bp_whoami_arg, (char *)&whoami_arg, + xdr_bp_whoami_res, (char *)&stat_whoami_res, + (resultproc_t)eachres_whoami); exit(0); } @@ -138,8 +139,9 @@ char **argv; } else { clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS, BOOTPARAMPROC_GETFILE, - xdr_bp_getfile_arg, &getfile_arg, - xdr_bp_getfile_res, &stat_getfile_res,eachres_getfile); + xdr_bp_getfile_arg, (char *)&getfile_arg, + xdr_bp_getfile_res, (char *)&stat_getfile_res, + (resultproc_t)eachres_getfile); exit(0); } diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c index 72a334a..9a156d5 100644 --- a/usr.sbin/keyserv/keyserv.c +++ b/usr.sbin/keyserv/keyserv.c @@ -56,7 +56,6 @@ static const char rcsid[] = #include <sys/stat.h> #include <sys/types.h> #include <rpc/rpc.h> -#include <rpc/pmap_clnt.h> #include <sys/param.h> #include <sys/file.h> #include <rpc/des_crypt.h> @@ -162,46 +161,24 @@ main(argc, argv) setmodulus(HEXMODULUS); getrootkey(&masterkey, nflag); - - /* Create services. */ - - (void) pmap_unset(KEY_PROG, KEY_VERS); - (void) pmap_unset(KEY_PROG, KEY_VERS2); - unlink(KEYSERVSOCK); - - transp = svcudp_create(RPC_ANYSOCK); - if (transp == NULL) - errx(1, "cannot create udp service"); - if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_UDP)) - errx(1, "unable to register (KEY_PROG, KEY_VERS, udp)"); - if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_UDP)) - errx(1, "unable to register (KEY_PROG, KEY_VERS2, udp)"); - - transp = svctcp_create(RPC_ANYSOCK, 0, 0); - if (transp == NULL) - errx(1, "cannot create tcp service"); - if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_TCP)) - errx(1, "unable to register (KEY_PROG, KEY_VERS, tcp)"); - if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_TCP)) - errx(1, "unable to register (KEY_PROG, KEY_VERS2, tcp)"); - - transp = svcunix_create(sock, 0, 0, KEYSERVSOCK); - chmod(KEYSERVSOCK, 0666); - if (transp == NULL) - errx(1, "cannot create AF_UNIX service"); - if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, 0)) - errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)"); - if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, 0)) - errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)"); - if (!svc_register(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, 0)) - errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)"); + if (svc_create(keyprogram, KEY_PROG, KEY_VERS, + "netpath") == 0) { + (void) fprintf(stderr, + "%s: unable to create service\n", argv[0]); + exit(1); + } + + if (svc_create(keyprogram, KEY_PROG, KEY_VERS2, + "netpath") == 0) { + (void) fprintf(stderr, + "%s: unable to create service\n", argv[0]); + exit(1); + } if (!debugging) { daemon(0,0); } - signal(SIGPIPE, SIG_IGN); - svc_run(); abort(); /* NOTREACHED */ diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c index 2f46a9f..b527765 100644 --- a/usr.sbin/mountd/mountd.c +++ b/usr.sbin/mountd/mountd.c @@ -50,12 +50,15 @@ static const char rcsid[] = #include <sys/param.h> #include <sys/mount.h> +#include <sys/fcntl.h> #include <sys/stat.h> #include <sys/syslog.h> #include <sys/sysctl.h> #include <rpc/rpc.h> #include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/mount.h> #include <nfs/rpcv2.h> #include <nfs/nfsproto.h> #include <nfs/nfs.h> @@ -83,6 +86,10 @@ static const char rcsid[] = #include <stdarg.h> #endif +#ifndef MOUNTDLOCK +#define MOUNTDLOCK "/var/run/mountd.lock" +#endif + /* * Structures for keeping the mount list and export list */ @@ -117,13 +124,13 @@ struct exportlist { #define EX_LINKED 0x1 struct netmsk { - u_int32_t nt_net; + struct sockaddr_storage nt_net; u_int32_t nt_mask; char *nt_name; }; union grouptypes { - struct hostent *gt_hostent; + struct addrinfo *gt_addrinfo; struct netmsk gt_net; }; @@ -157,8 +164,8 @@ void add_dlist __P((struct dirlist **, struct dirlist *, void add_mlist __P((char *, char *)); int check_dirpath __P((char *)); int check_options __P((struct dirlist *)); -int chk_host __P((struct dirlist *, u_int32_t, int *, int *)); -void del_mlist __P((char *, char *)); +int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *)); +int del_mlist __P((char *, char *, struct sockaddr *)); struct dirlist *dirp_search __P((struct dirlist *, char *)); int do_mount __P((struct exportlist *, struct grouplist *, int, struct xucred *, char *, int, struct statfs *)); @@ -186,18 +193,25 @@ void nextfield __P((char **, char **)); void out_of_mem __P((void)); void parsecred __P((char *, struct xucred *)); int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); -int scan_tree __P((struct dirlist *, u_int32_t)); +int scan_tree __P((struct dirlist *, struct sockaddr *)); static void usage __P((void)); int xdr_dir __P((XDR *, char *)); int xdr_explist __P((XDR *, caddr_t)); int xdr_fhs __P((XDR *, caddr_t)); int xdr_mlist __P((XDR *, caddr_t)); +void terminate __P((int)); /* C library */ int getnetgrent(); void endnetgrent(); void setnetgrent(); +static int bitcmp __P((void *, void *, int)); +static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int)); +static int sacmp __P((struct sockaddr *, struct sockaddr *)); +static int allones __P((struct sockaddr_storage *, int)); +static int countones __P((struct sockaddr *)); + struct exportlist *exphead; struct mountlist *mlhead; struct grouplist *grphead; @@ -213,7 +227,16 @@ int force_v2 = 0; int resvport_only = 1; int dir_only = 1; int log = 0; + int opt_flags; +static int have_v6 = 1; +#ifdef NI_WITHSCOPEID +static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID; +#else +static const int ninumeric = NI_NUMERICHOST; +#endif + +int mountdlockfd; /* Bits for above */ #define OP_MAPROOT 0x01 #define OP_MAPALL 0x02 @@ -221,6 +244,7 @@ int opt_flags; #define OP_MASK 0x08 #define OP_NET 0x10 #define OP_ALLDIRS 0x40 +#define OP_MASKLEN 0x200 #ifdef DEBUG int debug = 1; @@ -242,10 +266,26 @@ main(argc, argv) int argc; char **argv; { - SVCXPRT *udptransp, *tcptransp; + SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp; + struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; + int udpsock, tcpsock, udp6sock, tcp6sock; + int xcreated = 0, s; + int one = 1; int c, error, mib[3]; struct vfsconf vfc; + /* Check that another mountd isn't already running. */ + + if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) + err(1, "%s", MOUNTDLOCK); + + if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) + errx(1, "another rpc.mountd is already running. Aborting"); + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + have_v6 = 0; + else + close(s); error = getvfsbyname("nfs", &vfc); if (error && vfsisloadable("nfs")) { if(vfsload("nfs")) @@ -301,12 +341,38 @@ main(argc, argv) signal(SIGQUIT, SIG_IGN); } signal(SIGHUP, (void (*) __P((int))) get_exportlist); + signal(SIGTERM, terminate); { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); if (pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } } + rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); + rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); + udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + /* + * We're doing host-based access checks here, so don't allow + * v4-in-v6 to confuse things. The kernel will disable it + * by default on NFS sockets too. + */ + if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &one, sizeof one) < 0){ + syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); + exit(1); + } + if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &one, sizeof one) < 0){ + syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); + exit(1); + } + udpconf = getnetconfigent("udp"); + tcpconf = getnetconfigent("tcp"); + udp6conf = getnetconfigent("udp6"); + tcp6conf = getnetconfigent("tcp6"); if (!resvport_only) { mib[0] = CTL_VFS; mib[1] = vfc.vfc_typenum; @@ -322,17 +388,90 @@ main(argc, argv) syslog(LOG_ERR, "can't create socket"); exit(1); } - pmap_unset(RPCPROG_MNT, 1); - pmap_unset(RPCPROG_MNT, 3); - if (!force_v2) - if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) || - !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) { - syslog(LOG_ERR, "can't register mount"); - exit(1); - } - if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) || - !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) { - syslog(LOG_ERR, "can't register mount"); + if (udpsock != -1 && udpconf != NULL) { + bindresvport(udpsock, NULL); + udptransp = svc_dg_create(udpsock, 0, 0); + if (udptransp != NULL) { + if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, udpconf)) + syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, udpconf)) + syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create UDP services"); + + } + if (tcpsock != -1 && tcpconf != NULL) { + bindresvport(tcpsock, NULL); + listen(tcpsock, SOMAXCONN); + tcptransp = svc_vc_create(tcpsock, 0, 0); + if (tcptransp != NULL) { + if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, tcpconf)) + syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, tcpconf)) + syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create TCP service"); + + } + if (udp6sock != -1 && udp6conf != NULL) { + bindresvport(udp6sock, NULL); + udp6transp = svc_dg_create(udp6sock, 0, 0); + if (udp6transp != NULL) { + if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, udp6conf)) + syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, udp6conf)) + syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create UDP6 service"); + + } + if (tcp6sock != -1 && tcp6conf != NULL) { + bindresvport(tcp6sock, NULL); + listen(tcp6sock, SOMAXCONN); + tcp6transp = svc_vc_create(tcp6sock, 0, 0); + if (tcp6transp != NULL) { + if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, tcp6conf)) + syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, tcp6conf)) + syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create TCP6 service"); + + } + if (xcreated == 0) { + syslog(LOG_ERR, "could not create any services"); exit(1); } svc_run(); @@ -361,20 +500,38 @@ mntsrv(rqstp, transp) struct fhreturn fhr; struct stat stb; struct statfs fsb; - struct hostent *hp; - struct in_addr saddrin; - u_int32_t saddr; + struct addrinfo *ai; + char host[NI_MAXHOST], numerichost[NI_MAXHOST]; + int lookup_failed = 1; + struct sockaddr *saddr; u_short sport; char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; int bad = 0, defset, hostset; sigset_t sighup_mask; + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; sigemptyset(&sighup_mask); sigaddset(&sighup_mask, SIGHUP); - saddr = transp->xp_raddr.sin_addr.s_addr; - saddrin = transp->xp_raddr.sin_addr; - sport = ntohs(transp->xp_raddr.sin_port); - hp = (struct hostent *)NULL; + saddr = svc_getrpccaller(transp)->buf; + switch (saddr->sa_family) { + case AF_INET6: + sin6 = (struct sockaddr_in6 *)saddr; + sport = ntohs(sin6->sin6_port); + break; + case AF_INET: + sin = (struct sockaddr_in *)saddr; + sport = ntohs(sin->sin_port); + break; + default: + syslog(LOG_ERR, "request from unknown address family"); + return; + } + lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, + NULL, 0, 0); + getnameinfo(saddr, saddr->sa_len, numerichost, + sizeof numerichost, NULL, 0, NI_NUMERICHOST); + ai = NULL; switch (rqstp->rq_proc) { case NULLPROC: if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) @@ -384,13 +541,13 @@ mntsrv(rqstp, transp) if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "mount request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_getargs(transp, xdr_dir, rpcpath)) { syslog(LOG_NOTICE, "undecodable mount request from %s", - inet_ntoa(saddrin)); + numerichost); svcerr_decode(transp); return; } @@ -403,12 +560,12 @@ mntsrv(rqstp, transp) if (realpath(rpcpath, dirpath) == NULL || stat(dirpath, &stb) < 0 || (!S_ISDIR(stb.st_mode) && - (dir_only || !S_ISREG(stb.st_mode))) || + (dir_only || !S_ISREG(stb.st_mode))) || statfs(dirpath, &fsb) < 0) { chdir("/"); /* Just in case realpath doesn't */ syslog(LOG_NOTICE, "mount request from %s for non existent path %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); if (debug) warnx("stat failed on %s", dirpath); bad = ENOENT; /* We will send error reply later */ @@ -420,9 +577,9 @@ mntsrv(rqstp, transp) hostset = defset = 0; if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || ((dp = dirp_search(ep->ex_dirl, dirpath)) && - chk_host(dp, saddr, &defset, &hostset)) || - (defset && scan_tree(ep->ex_defdir, saddr) == 0 && - scan_tree(ep->ex_dirl, saddr) == 0))) { + chk_host(dp, saddr, &defset, &hostset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { if (bad) { if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) @@ -448,25 +605,21 @@ mntsrv(rqstp, transp) } if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) syslog(LOG_ERR, "can't send reply"); - if (hp == NULL) - hp = gethostbyaddr((caddr_t)&saddr, - sizeof(saddr), AF_INET); - if (hp) - add_mlist(hp->h_name, dirpath); + if (!lookup_failed) + add_mlist(host, dirpath); else - add_mlist(inet_ntoa(saddrin), - dirpath); + add_mlist(numerichost, dirpath); if (debug) warnx("mount successful"); if (log) syslog(LOG_NOTICE, "mount request succeeded from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } else { bad = EACCES; syslog(LOG_NOTICE, "mount request denied from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad)) @@ -479,56 +632,54 @@ mntsrv(rqstp, transp) else if (log) syslog(LOG_NOTICE, "dump request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; case RPCMNT_UMOUNT: if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "umount request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_getargs(transp, xdr_dir, rpcpath)) { syslog(LOG_NOTICE, "undecodable umount request from %s", - inet_ntoa(saddrin)); + numerichost); svcerr_decode(transp); return; } if (realpath(rpcpath, dirpath) == NULL) { syslog(LOG_NOTICE, "umount request from %s " "for non existent path %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "can't send reply"); - hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); - if (hp) - del_mlist(hp->h_name, dirpath); - del_mlist(inet_ntoa(saddrin), dirpath); + if (!lookup_failed) + del_mlist(host, dirpath, saddr); + del_mlist(numerichost, dirpath, saddr); if (log) syslog(LOG_NOTICE, "umount request succeeded from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); return; case RPCMNT_UMNTALL: if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "umountall request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "can't send reply"); - hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); - if (hp) - del_mlist(hp->h_name, (char *)NULL); - del_mlist(inet_ntoa(saddrin), (char *)NULL); + if (!lookup_failed) + del_mlist(host, NULL, saddr); + del_mlist(numerichost, NULL, saddr); if (log) syslog(LOG_NOTICE, "umountall request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; case RPCMNT_EXPORT: if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) @@ -536,7 +687,7 @@ mntsrv(rqstp, transp) if (log) syslog(LOG_NOTICE, "export request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; default: svcerr_noproc(transp); @@ -690,7 +841,7 @@ put_exlist(dp, xdrsp, adp, putdefp) if (grp->gr_type == GT_HOST) { if (!xdr_bool(xdrsp, &true)) return (1); - strp = grp->gr_ptr.gt_hostent->h_name; + strp = grp->gr_ptr.gt_addrinfo->ai_canonname; if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) return (1); @@ -732,7 +883,7 @@ get_exportlist() struct exportlist **epp; struct dirlist *dirhead; struct statfs fsb, *fsp; - struct hostent *hpe; + struct addrinfo *ai; struct xucred anon; char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; @@ -786,7 +937,7 @@ get_exportlist() fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0) syslog(LOG_ERR, "can't delete exports for %s", - fsp->f_mntonname); + fsp->f_mntonname); } fsp++; } @@ -874,9 +1025,9 @@ get_exportlist() else out_of_mem(); if (debug) - warnx("making new ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); + warnx("making new ep fs=0x%x,0x%x", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); } else if (debug) warnx("found ep fs=0x%x,0x%x", fsb.f_fsid.val[0], @@ -944,14 +1095,17 @@ get_exportlist() if (debug) warnx("adding a default entry"); /* add a default group and make the grp list NULL */ - hpe = (struct hostent *)malloc(sizeof(struct hostent)); - if (hpe == (struct hostent *)NULL) - out_of_mem(); - hpe->h_name = strdup("Default"); - hpe->h_addrtype = AF_INET; - hpe->h_length = sizeof (u_int32_t); - hpe->h_addr_list = (char **)NULL; - grp->gr_ptr.gt_hostent = hpe; + ai = malloc(sizeof(struct addrinfo)); + ai->ai_flags = 0; + ai->ai_family = AF_INET; /* XXXX */ + ai->ai_socktype = SOCK_DGRAM; + /* setting the length to 0 will match anything */ + ai->ai_addrlen = 0; + ai->ai_flags = AI_CANONNAME; + ai->ai_canonname = strdup("Default"); + ai->ai_addr = NULL; + ai->ai_next = NULL; + grp->gr_ptr.gt_addrinfo = ai; /* * Don't allow a network export coincide with a list of @@ -961,13 +1115,13 @@ get_exportlist() getexp_err(ep, tgrp); goto nextline; - /* - * If an export list was specified on this line, make sure + /* + * If an export list was specified on this line, make sure * that we have at least one valid entry, otherwise skip it. */ } else { grp = tgrp; - while (grp && grp->gr_type == GT_IGNORE) + while (grp && grp->gr_type == GT_IGNORE) grp = grp->gr_next; if (! grp) { getexp_err(ep, tgrp); @@ -1219,19 +1373,27 @@ add_dlist(dpp, newdp, grp, flags) /* * Search for a dirpath on the export point. */ +void * +test() +{ +} + +/* + * Search for a dirpath on the export point. + */ struct dirlist * -dirp_search(dp, dirpath) +dirp_search(dp, dirp) struct dirlist *dp; - char *dirpath; + char *dirp; { int cmp; if (dp) { - cmp = strcmp(dp->dp_dirp, dirpath); + cmp = strcmp(dp->dp_dirp, dirp); if (cmp > 0) - return (dirp_search(dp->dp_left, dirpath)); + return (dirp_search(dp->dp_left, dirp)); else if (cmp < 0) - return (dirp_search(dp->dp_right, dirpath)); + return (dirp_search(dp->dp_right, dirp)); else return (dp); } @@ -1239,18 +1401,59 @@ dirp_search(dp, dirpath) } /* + * Some helper functions for netmasks. They all assume masks in network + * order (big endian). + */ +static int +bitcmp(void *dst, void *src, int bitlen) +{ + int i; + u_int8_t *p1 = dst, *p2 = src; + u_int8_t bitmask; + int bytelen, bitsleft; + + bytelen = bitlen / 8; + bitsleft = bitlen % 8; + + if (debug) { + printf("comparing:\n"); + for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) + printf("%02x", p1[i]); + printf("\n"); + for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) + printf("%02x", p2[i]); + printf("\n"); + } + + for (i = 0; i < bytelen; i++) { + if (*p1 != *p2) + return 1; + p1++; + p2++; + } + + for (i = 0; i < bitsleft; i++) { + bitmask = 1 << (7 - i); + if ((*p1 & bitmask) != (*p2 & bitmask)) + return 1; + } + + return 0; +} + +/* * Scan for a host match in a directory tree. */ int chk_host(dp, saddr, defsetp, hostsetp) struct dirlist *dp; - u_int32_t saddr; + struct sockaddr *saddr; int *defsetp; int *hostsetp; { struct hostlist *hp; struct grouplist *grp; - u_int32_t **addrp; + struct addrinfo *ai; if (dp) { if (dp->dp_flag & DP_DEFSET) @@ -1260,22 +1463,22 @@ chk_host(dp, saddr, defsetp, hostsetp) grp = hp->ht_grp; switch (grp->gr_type) { case GT_HOST: - addrp = (u_int32_t **) - grp->gr_ptr.gt_hostent->h_addr_list; - while (*addrp) { - if (**addrp == saddr) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - return (1); + ai = grp->gr_ptr.gt_addrinfo; + for (; ai; ai = ai->ai_next) { + if (!sacmp(ai->ai_addr, saddr)) { + *hostsetp = + (hp->ht_flag | DP_HOSTSET); + return (1); + } } - addrp++; - } break; case GT_NET: - if ((saddr & grp->gr_ptr.gt_net.nt_mask) == - grp->gr_ptr.gt_net.nt_net) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - return (1); - } + if (!netpartcmp(saddr, + (struct sockaddr *) &grp->gr_ptr.gt_net.nt_net, + grp->gr_ptr.gt_net.nt_mask)) { + *hostsetp = (hp->ht_flag | DP_HOSTSET); + return (1); + } break; }; hp = hp->ht_next; @@ -1290,7 +1493,7 @@ chk_host(dp, saddr, defsetp, hostsetp) int scan_tree(dp, saddr) struct dirlist *dp; - u_int32_t saddr; + struct sockaddr *saddr; { int defset, hostset; @@ -1392,6 +1595,11 @@ do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) opt_flags |= OP_MASK; } else if (cpoptarg && (!strcmp(cpopt, "network") || !strcmp(cpopt, "n"))) { + if (strchr(cpoptarg, '/') != NULL) { + if (debug) + fprintf(stderr, "setting OP_MASKLEN\n"); + opt_flags |= OP_MASKLEN; + } if (grp->gr_type != GT_NULL) { syslog(LOG_ERR, "network/host conflict"); return (1); @@ -1442,84 +1650,40 @@ get_host(cp, grp, tgrp) struct grouplist *tgrp; { struct grouplist *checkgrp; - struct hostent *hp, *nhp; - char **addrp, **naddrp; - struct hostent t_host; + struct addrinfo *ai, hints; + int ecode; + char host[NI_MAXHOST]; int i; - u_int32_t saddr; char *aptr[2]; - if (grp->gr_type != GT_NULL) + if (grp->gr_type != GT_NULL) { + syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); return (1); - if ((hp = gethostbyname(cp)) == NULL) { - if (isdigit(*cp)) { - saddr = inet_addr(cp); - if (saddr == -1) { - syslog(LOG_ERR, "inet_addr failed for %s", cp); - return (1); - } - if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr), - AF_INET)) == NULL) { - hp = &t_host; - hp->h_name = cp; - hp->h_addrtype = AF_INET; - hp->h_length = sizeof (u_int32_t); - hp->h_addr_list = aptr; - aptr[0] = (char *)&saddr; - aptr[1] = (char *)NULL; - } - } else { - syslog(LOG_ERR, "gethostbyname failed for %s", cp); - return (1); - } } - /* - * Sanity check: make sure we don't already have an entry - * for this host in the grouplist. - */ - checkgrp = tgrp; - while (checkgrp != NULL) { - if (checkgrp->gr_type == GT_HOST && - checkgrp->gr_ptr.gt_hostent != NULL && - (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name) - || *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr == - *(u_int32_t *)hp->h_addr)) { - grp->gr_type = GT_IGNORE; - return(0); - } - checkgrp = checkgrp->gr_next; - } - + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_CANONNAME; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(cp, NULL, &hints, &ai); + if (ecode != 0) { + syslog(LOG_ERR,"can't get address info for " + "host %s", cp); + return 1; + } grp->gr_type = GT_HOST; - nhp = grp->gr_ptr.gt_hostent = (struct hostent *) - malloc(sizeof(struct hostent)); - if (nhp == (struct hostent *)NULL) - out_of_mem(); - memmove(nhp, hp, sizeof(struct hostent)); - i = strlen(hp->h_name)+1; - nhp->h_name = (char *)malloc(i); - if (nhp->h_name == (char *)NULL) - out_of_mem(); - memmove(nhp->h_name, hp->h_name, i); - addrp = hp->h_addr_list; - i = 1; - while (*addrp++) - i++; - naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *)); - if (naddrp == (char **)NULL) - out_of_mem(); - addrp = hp->h_addr_list; - while (*addrp) { - *naddrp = (char *)malloc(hp->h_length); - if (*naddrp == (char *)NULL) - out_of_mem(); - memmove(*naddrp, *addrp, hp->h_length); - addrp++; - naddrp++; - } - *naddrp = (char *)NULL; - if (debug) - warnx("got host %s", hp->h_name); + grp->gr_ptr.gt_addrinfo = ai; + while (ai != NULL) { + if (ai->ai_canonname == NULL) { + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, + sizeof host, NULL, 0, ninumeric) != 0) + strlcpy(host, "?", sizeof(host)); + ai->ai_canonname = strdup(host); + ai->ai_flags |= AI_CANONNAME; + } else + ai->ai_flags &= ~AI_CANONNAME; + if (debug) + (void)fprintf(stderr, "got host %s\n", ai->ai_canonname); + ai = ai->ai_next; + } return (0); } @@ -1597,68 +1761,64 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) int dirplen; struct statfs *fsb; { - char *cp = (char *)NULL; - u_int32_t **addrp; + struct sockaddr *addrp; + struct sockaddr_storage ss; + struct addrinfo *ai; + int addrlen; + char *cp = NULL; int done; char savedc = '\0'; - struct sockaddr_in sin, imask; union { struct ufs_args ua; struct iso_args ia; struct mfs_args ma; #ifdef __NetBSD__ struct msdosfs_args da; + struct adosfs_args aa; #endif struct ntfs_args na; } args; - u_int32_t net; args.ua.fspec = 0; args.ua.export.ex_flags = exflags; args.ua.export.ex_anon = *anoncrp; args.ua.export.ex_indexfile = ep->ex_indexfile; - memset(&sin, 0, sizeof(sin)); - memset(&imask, 0, sizeof(imask)); - sin.sin_family = AF_INET; - sin.sin_len = sizeof(sin); - imask.sin_family = AF_INET; - imask.sin_len = sizeof(sin); - if (grp->gr_type == GT_HOST) - addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list; - else - addrp = (u_int32_t **)NULL; + if (grp->gr_type == GT_HOST) { + ai = grp->gr_ptr.gt_addrinfo; + addrp = ai->ai_addr; + addrlen = ai->ai_addrlen; + } else + addrp = NULL; done = FALSE; while (!done) { switch (grp->gr_type) { case GT_HOST: - if (addrp) { - sin.sin_addr.s_addr = **addrp; - args.ua.export.ex_addrlen = sizeof(sin); - } else - args.ua.export.ex_addrlen = 0; - args.ua.export.ex_addr = (struct sockaddr *)&sin; + if (addrp != NULL && addrp->sa_family == AF_INET6 && + have_v6 == 0) + goto skip; + args.ua.export.ex_addr = addrp; + args.ua.export.ex_addrlen = addrlen; args.ua.export.ex_masklen = 0; break; case GT_NET: - if (grp->gr_ptr.gt_net.nt_mask) - imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask; - else { - net = ntohl(grp->gr_ptr.gt_net.nt_net); - if (IN_CLASSA(net)) - imask.sin_addr.s_addr = inet_addr("255.0.0.0"); - else if (IN_CLASSB(net)) - imask.sin_addr.s_addr = - inet_addr("255.255.0.0"); - else - imask.sin_addr.s_addr = - inet_addr("255.255.255.0"); - grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr; + args.ua.export.ex_addr = (struct sockaddr *) + &grp->gr_ptr.gt_net.nt_net; + if (args.ua.export.ex_addr->sa_family == AF_INET6 && + have_v6 == 0) + goto skip; + args.ua.export.ex_addrlen = + args.ua.export.ex_addr->sa_len; + memset(&ss, 0, sizeof ss); + ss.ss_family = args.ua.export.ex_addr->sa_family; + ss.ss_len = args.ua.export.ex_addr->sa_len; + if (allones(&ss, grp->gr_ptr.gt_net.nt_mask) != 0) { + syslog(LOG_ERR, "Bad network flag"); + if (cp) + *cp = savedc; + return (1); } - sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net; - args.ua.export.ex_addr = (struct sockaddr *)&sin; - args.ua.export.ex_addrlen = sizeof (sin); - args.ua.export.ex_mask = (struct sockaddr *)&imask; - args.ua.export.ex_masklen = sizeof (imask); + args.ua.export.ex_mask = (struct sockaddr *)&ss; + args.ua.export.ex_masklen = ss.ss_len; break; case GT_IGNORE: return(0); @@ -1678,7 +1838,7 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) * exportable file systems and not just "ufs". */ while (mount(fsb->f_fstypename, dirp, - fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { + fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { if (cp) *cp-- = savedc; else @@ -1707,10 +1867,15 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) savedc = *cp; *cp = '\0'; } +skip: if (addrp) { - ++addrp; - if (*addrp == (u_int32_t *)NULL) + ai = ai->ai_next; + if (ai == NULL) done = TRUE; + else { + addrp = ai->ai_addr; + addrlen = ai->ai_addrlen; + } } else done = TRUE; } @@ -1729,47 +1894,105 @@ get_net(cp, net, maskflg) int maskflg; { struct netent *np; - long netaddr; - struct in_addr inetaddr, inetaddr2; - char *name; + char *name, *p, *prefp; + struct sockaddr_in sin, *sinp; + struct sockaddr *sa; + struct addrinfo hints, *ai = NULL; + char netname[NI_MAXHOST]; + long preflen; + int ecode; + + if ((opt_flags & OP_MASKLEN) && !maskflg) { + p = strchr(cp, '/'); + *p = '\0'; + prefp = p + 1; + } - if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) { - inetaddr = inet_makeaddr(netaddr, 0); - /* - * Due to arbitrary subnet masks, you don't know how many - * bits to shift the address to make it into a network, - * however you do know how to make a network address into - * a host with host == 0 and then compare them. - * (What a pest) - */ - if (!maskflg) { - setnetent(0); - while ((np = getnetent())) { - inetaddr2 = inet_makeaddr(np->n_net, 0); - if (inetaddr2.s_addr == inetaddr.s_addr) - break; - } - endnetent(); - } - } else if ((np = getnetbyname(cp)) != NULL) { - inetaddr = inet_makeaddr(np->n_net, 0); + if ((np = getnetbyname(cp)) != NULL) { + sin.sin_family = AF_INET; + sin.sin_len = sizeof sin; + sin.sin_addr = inet_makeaddr(np->n_net, 0); + sa = (struct sockaddr *)&sin; + } else if (isdigit(*cp)) { + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(cp, NULL, &hints, &ai) != 0) { + /* + * If getaddrinfo() failed, try the inet4 network + * notation with less than 3 dots. + */ + sin.sin_family = AF_INET; + sin.sin_len = sizeof sin; + sin.sin_addr = inet_makeaddr(inet_network(cp),0); + if (debug) + fprintf(stderr, "get_net: v4 addr %x\n", + sin.sin_addr.s_addr); + sa = (struct sockaddr *)&sin; + } else + sa = ai->ai_addr; + } else if (isxdigit(*cp) || *cp == ':') { + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(cp, NULL, &hints, &ai) == 0) + sa = ai->ai_addr; + else + goto fail; } else - return (1); + goto fail; + + ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname, + NULL, 0, ninumeric); + if (ecode != 0) + goto fail; if (maskflg) - net->nt_mask = inetaddr.s_addr; + net->nt_mask = countones(sa); else { + if (opt_flags & OP_MASKLEN) { + preflen = strtol(prefp, NULL, 10); + if (preflen == LONG_MIN && errno == ERANGE) + goto fail; + net->nt_mask = (int)preflen; + *p = '/'; + } + if (np) name = np->n_name; + else { + if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, + NULL, 0, ninumeric) != 0) + strlcpy(netname, "?", sizeof(netname)); + name = netname; + } + net->nt_name = strdup(name); + memcpy(&net->nt_net, sa, sa->sa_len); + } + + if (!maskflg && sa->sa_family == AF_INET && + !(opt_flags & (OP_MASK|OP_MASKLEN))) { + sinp = (struct sockaddr_in *)sa; + if (IN_CLASSA(sinp->sin_addr.s_addr)) + net->nt_mask = 8; + else if (IN_CLASSB(sinp->sin_addr.s_addr)) + net->nt_mask = 16; + else if (IN_CLASSC(sinp->sin_addr.s_addr)) + net->nt_mask = 24; + else if (IN_CLASSD(sinp->sin_addr.s_addr)) + net->nt_mask = 28; else - name = inet_ntoa(inetaddr); - net->nt_name = (char *)malloc(strlen(name) + 1); - if (net->nt_name == (char *)NULL) - out_of_mem(); - strcpy(net->nt_name, name); - net->nt_net = inetaddr.s_addr; + net->nt_mask = 32; /* XXX */ } - return (0); + + if (ai) + freeaddrinfo(ai); + return 0; + +fail: + if (ai) + freeaddrinfo(ai); + return 1; } /* @@ -1958,15 +2181,28 @@ get_mountlist() fclose(mlfile); } -void -del_mlist(hostp, dirp) +int +del_mlist(hostp, dirp, saddr) char *hostp, *dirp; + struct sockaddr *saddr; { struct mountlist *mlp, **mlpp; struct mountlist *mlp2; + u_short sport; FILE *mlfile; int fnd = 0; - + char host[NI_MAXHOST]; + + switch (saddr->sa_family) { + case AF_INET6: + sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); + break; + case AF_INET: + sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); + break; + default: + return -1; + } mlpp = &mlhead; mlp = mlhead; while (mlp) { @@ -2034,17 +2270,11 @@ void free_grp(grp) struct grouplist *grp; { - char **addrp; + struct addrinfo *ai; if (grp->gr_type == GT_HOST) { - if (grp->gr_ptr.gt_hostent->h_name) { - addrp = grp->gr_ptr.gt_hostent->h_addr_list; - while (addrp && *addrp) - free(*addrp++); - free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list); - free(grp->gr_ptr.gt_hostent->h_name); - } - free((caddr_t)grp->gr_ptr.gt_hostent); + if (grp->gr_ptr.gt_addrinfo != NULL) + freeaddrinfo(grp->gr_ptr.gt_addrinfo); } else if (grp->gr_type == GT_NET) { if (grp->gr_ptr.gt_net.nt_name) free(grp->gr_ptr.gt_net.nt_name); @@ -2093,7 +2323,6 @@ check_options(dp) /* * Check an absolute directory path for any symbolic links. Return true - * if no symbolic links are found. */ int check_dirpath(dirp) @@ -2134,3 +2363,146 @@ get_num(cp) } return (res); } + +static int +netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen) +{ + void *src, *dst; + + if (s1->sa_family != s2->sa_family) + return 1; + + switch (s1->sa_family) { + case AF_INET: + src = &((struct sockaddr_in *)s1)->sin_addr; + dst = &((struct sockaddr_in *)s2)->sin_addr; + if (bitlen > sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8) + return 1; + break; + case AF_INET6: + src = &((struct sockaddr_in6 *)s1)->sin6_addr; + dst = &((struct sockaddr_in6 *)s2)->sin6_addr; + if (((struct sockaddr_in6 *)s1)->sin6_scope_id != + ((struct sockaddr_in6 *)s2)->sin6_scope_id) + return 1; + if (bitlen > sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8) + return 1; + break; + default: + return 1; + } + + return bitcmp(src, dst, bitlen); +} + +static int +allones(struct sockaddr_storage *ssp, int bitlen) +{ + u_int8_t *p; + int bytelen, bitsleft, i; + int zerolen; + + switch (ssp->ss_family) { + case AF_INET: + p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr; + zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr); + break; + case AF_INET6: + p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr; + zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr); + break; + default: + return -1; + } + + memset(p, 0, zerolen); + + bytelen = bitlen / 8; + bitsleft = bitlen % 8; + + if (bytelen > zerolen) + return -1; + + for (i = 0; i < bytelen; i++) + *p++ = 0xff; + + for (i = 0; i < bitsleft; i++) + *p |= 1 << (7 - i); + + return 0; +} + +static int +countones(struct sockaddr *sa) +{ + void *mask; + int i, bits = 0, bytelen; + u_int8_t *p; + + switch (sa->sa_family) { + case AF_INET: + mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr; + bytelen = 4; + break; + case AF_INET6: + mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr; + bytelen = 16; + break; + default: + return 0; + } + + p = mask; + + for (i = 0; i < bytelen; i++, p++) { + if (*p != 0xff) { + for (bits = 0; bits < 8; bits++) { + if (!(*p & (1 << (7 - bits)))) + break; + } + break; + } + } + + return (i * 8 + bits); +} + +static int +sacmp(struct sockaddr *sa1, struct sockaddr *sa2) +{ + void *p1, *p2; + int len; + + if (sa1->sa_family != sa2->sa_family) + return 1; + + switch (sa1->sa_family) { + case AF_INET: + p1 = &((struct sockaddr_in *)sa1)->sin_addr; + p2 = &((struct sockaddr_in *)sa2)->sin_addr; + len = 4; + break; + case AF_INET6: + p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; + p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; + len = 16; + if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != + ((struct sockaddr_in6 *)sa2)->sin6_scope_id) + return 1; + break; + default: + return 1; + } + + return memcmp(p1, p2, len); +} + +void terminate(sig) +int sig; +{ + close(mountdlockfd); + unlink(MOUNTDLOCK); + pmap_unset(RPCPROG_MNT, 1); + pmap_unset(RPCPROG_MNT, 3); + exit (0); +} diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8 index fb44149..0031ea1 100644 --- a/usr.sbin/nfsd/nfsd.8 +++ b/usr.sbin/nfsd/nfsd.8 @@ -42,7 +42,7 @@ server .Sh SYNOPSIS .Nm -.Op Fl arut +.Op Fl ardut .Op Fl n Ar num_servers .Op Fl h Ar bindip .Sh DESCRIPTION @@ -64,13 +64,19 @@ The following options are available: Register the .Tn NFS service with -.Xr portmap 8 +.Xr rpcbind 8 without creating any servers. This option can be used along with the .Fl u or .Fl t -options to re-register NFS if the portmap server is restarted. +options to re-register NFS if the rpcbind server is restarted. +.It Fl d +Unregister the +.Tn NFS +service with +.Xr rpcbind 8 +without creating any servers. .It Fl n Specifies how many servers to create. .It Fl h Ar bindip @@ -147,6 +153,16 @@ that the NFS sockets can only be accessed by the inside interface. would then be used to block nfs-related packets that come in on the outside interface. .Pp +.Nm +has to be terminated with SIGUSR1 and cannot be killed with SIGTERM oder SIGQUIT. +.Nm +needs to ignore these signals in order to stay alive as long +as possible during a shutdown, otherwise loopback mounts will +not be able to unmount. If you have to kill +.Nm +just do a +.Dq Li "kill -USR1 <PID of master nfsd>" +.Pp The .Nm utility exits 0 on success, and >0 if an error occurs. @@ -156,7 +172,7 @@ utility exits 0 on success, and >0 if an error occurs. .Xr kldload 8 , .Xr mountd 8 , .Xr nfsiod 8 , -.Xr portmap 8 , +.Xr rpcbind 8 , .Xr ipfw 8 .Sh HISTORY The diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c index 2d923a4..959f0fb 100644 --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -58,9 +58,6 @@ static const char rcsid[] = #include <netdb.h> #include <arpa/inet.h> -#ifdef ISO -#include <netiso/iso.h> -#endif #include <nfs/rpcv2.h> #include <nfs/nfsproto.h> #include <nfs/nfs.h> @@ -76,6 +73,7 @@ static const char rcsid[] = #include <stdlib.h> #include <strings.h> #include <unistd.h> +#include <netdb.h> /* Global defs */ #ifdef DEBUG @@ -103,15 +101,23 @@ struct timeval ktv; NFSKERBKEYSCHED_T kerb_keysched; #endif -void nonfs __P((int)); -void reapchild __P((int)); -void setbindhost __P((struct sockaddr_in *ia, const char *bindhost)); +#define MAXNFSDCNT 20 +#define DEFNFSDCNT 4 +pid_t children[MAXNFSDCNT]; /* PIDs of children */ +int nfsdcnt; /* number of children */ + +void cleanup(int); +void killchildren(void); +void nonfs (int); +void reapchild (int); +int setbindhost (struct addrinfo **ia, const char *bindhost, struct addrinfo hints); #ifdef OLD_SETPROCTITLE #ifdef __FreeBSD__ -void setproctitle __P((char *)); +void setproctitle (char *); #endif #endif -void usage __P((void)); +void unregistration (void); +void usage (void); /* * Nfs server daemon mostly just a user context for nfssvc() @@ -119,7 +125,7 @@ void usage __P((void)); * 1 - do file descriptor and signal cleanup * 2 - fork the nfsd(s) * 3 - create server socket(s) - * 4 - register socket with portmap + * 4 - register socket with rpcbind * * For connectionless protocols, just pass the socket into the kernel via. * nfssvc(). @@ -127,7 +133,8 @@ void usage __P((void)); * socket from accept, pass the msgsock into the kernel via. nfssvc(). * The arguments are: * -c - support iso cltp clients - * -r - reregister with portmapper + * -r - reregister with rpcbind + * -d - unregister with rpcbind * -t - support tcp nfs clients * -u - support udp nfs clients * followed by "n" which is the number of nfsds' to fork off @@ -138,20 +145,20 @@ main(argc, argv, envp) char *argv[], *envp[]; { struct nfsd_args nfsdargs; - struct sockaddr_in inetaddr, inetpeer; -#ifdef ISO - struct sockaddr_iso isoaddr, isopeer; - char *cp; -#endif + struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints; + struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6; + struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6; + struct sockaddr_in inetpeer; + struct sockaddr_in6 inet6peer; fd_set ready, sockbits; + fd_set v4bits, v6bits; int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock; - int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock; - int tp4cnt, tp4flag, tpipcnt, tpipflag, udpflag; - int bindhostc = 0, bindanyflag; + int nfssvc_flag, on = 1, unregister, reregister, sock; + int tcp6sock, ip6flag, tcpflag, tcpsock; + int udpflag, ecode, s; + int bindhostc = 0, bindanyflag, rpcbreg, rpcbregcnt; char **bindhost = NULL; -#ifdef notyet - int tp4sock, tpipsock; -#endif + pid_t pid; #ifdef NFSKERB struct group *grp; struct passwd *pwd; @@ -184,18 +191,11 @@ main(argc, argv, envp) LastArg = envp[-1] + strlen(envp[-1]); #endif -#define MAXNFSDCNT 20 -#define DEFNFSDCNT 4 nfsdcnt = DEFNFSDCNT; - cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0; - bindanyflag = tpipflag = udpflag = 0; -#ifdef ISO -#define GETOPT "ach:n:rtu" -#define USAGE "[-acrtu] [-n num_servers] [-h bindip]" -#else -#define GETOPT "ah:n:rtu" -#define USAGE "[-artu] [-n num_servers] [-h bindip]" -#endif + cltpflag = unregister = reregister = tcpflag = 0; + bindanyflag = udpflag = ip6flag = 0; +#define GETOPT "ah:n:rdtu" +#define USAGE "[-ardtu] [-n num_servers] [-h bindip]" while ((ch = getopt(argc, argv, GETOPT)) != -1) switch (ch) { case 'a': @@ -221,25 +221,15 @@ main(argc, argv, envp) case 'r': reregister = 1; break; + case 'd': + unregister = 1; + break; case 't': tcpflag = 1; break; case 'u': udpflag = 1; break; -#ifdef ISO - case 'c': - cltpflag = 1; - break; -#ifdef notyet - case 'i': - tp4cnt = 1; - break; - case 'p': - tpipcnt = 1; - break; -#endif /* notyet */ -#endif /* ISO */ default: case '?': usage(); @@ -263,6 +253,13 @@ main(argc, argv, envp) nfsdcnt = DEFNFSDCNT; } } + ip6flag = 1; + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0 && (errno == EPROTONOSUPPORT || + errno == EPFNOSUPPORT || errno == EAFNOSUPPORT)) + ip6flag = 0; + else + close(s); if (bindhostc == 0 || bindanyflag) { bindhostc++; @@ -278,33 +275,126 @@ main(argc, argv, envp) daemon(0, 0); (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, SIG_IGN); - (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGSYS, nonfs); + (void)signal(SIGUSR1, cleanup); + /* + * nfsd sits in the kernel most of the time. It needs + * to ignore SIGTERM/SIGQUIT in order to stay alive as long + * as possible during a shutdown, otherwise loopback + * mounts will not be able to unmount. + */ (void)signal(SIGTERM, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); } (void)signal(SIGCHLD, reapchild); - + if (unregister) { + unregistration(); + exit (0); + } if (reregister) { - if (udpflag && - (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT))) - err(1, "can't register with portmap for UDP"); - if (tcpflag && - (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT))) - err(1, "can't register with portmap for TCP"); - exit(0); + if (udpflag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp = getnetconfigent("udp"); + if (nconf_udp == NULL) + err(1, "getnetconfigent udp failed"); + nb_udp.buf = ai_udp->ai_addr; + nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp))) + err(1, "rpcb_set udp failed"); + freeaddrinfo(ai_udp); + } + if (udpflag && ip6flag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp6 = getnetconfigent("udp6"); + if (nconf_udp6 == NULL) + err(1, "getnetconfigent udp6 failed"); + nb_udp6.buf = ai_udp6->ai_addr; + nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6))) + err(1, "rpcb_set udp6 failed"); + freeaddrinfo(ai_udp6); + } + if (tcpflag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp = getnetconfigent("tcp"); + if (nconf_tcp == NULL) + err(1, "getnetconfigent tcp failed"); + nb_tcp.buf = ai_tcp->ai_addr; + nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, &nb_tcp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp, &nb_tcp))) + err(1, "rpcb_set tcp failed"); + freeaddrinfo(ai_tcp); + } + if (tcpflag && ip6flag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp6 = getnetconfigent("tcp6"); + if (nconf_tcp6 == NULL) + err(1, "getnetconfigent tcp6 failed"); + nb_tcp6.buf = ai_tcp6->ai_addr; + nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6))) + err(1, "rpcb_set tcp6 failed"); + freeaddrinfo(ai_tcp6); + } + exit (0); } + openlog("nfsd:", LOG_PID, LOG_DAEMON); for (i = 0; i < nfsdcnt; i++) { - switch (fork()) { + switch ((pid = fork())) { case -1: syslog(LOG_ERR, "fork: %m"); + killchildren(); exit (1); case 0: break; default: + children[i] = pid; continue; } @@ -399,182 +489,295 @@ main(argc, argv, envp) exit(0); } - /* If we are serving udp, set up the socket. */ - for (i = 0; udpflag && i < bindhostc; i++) { - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "can't create udp socket"); - exit(1); - } - setbindhost(&inetaddr, bindhost[i]); - if (bind(sock, - (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind udp addr %s: %m", bindhost[i]); - exit(1); - } - if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)) { - syslog(LOG_ERR, "can't register with udp portmap"); - exit(1); + if (atexit(killchildren) == -1) { + syslog(LOG_ERR, "atexit: %s", strerror(errno)); + exit(1); + } + FD_ZERO(&v4bits); + FD_ZERO(&v6bits); + + rpcbregcnt = 0; + /* Set up the socket for udp and rpcb register it. */ + if (udpflag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + if (setbindhost(&ai_udp, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((sock = socket(ai_udp->ai_family, + ai_udp->ai_socktype, + ai_udp->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create udp socket"); + exit(1); + } + if (bind(sock, ai_udp->ai_addr, + ai_udp->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind udp addr %s: %m", + bindhost[i]); + exit(1); + } + freeaddrinfo(ai_udp); + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, "can't Add UDP socket"); + exit(1); + } + (void)close(sock); + } } - nfsdargs.sock = sock; - nfsdargs.name = NULL; - nfsdargs.namelen = 0; - if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { - syslog(LOG_ERR, "can't Add UDP socket"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp = getnetconfigent("udp"); + if (nconf_udp == NULL) + err(1, "getnetconfigent udp failed"); + nb_udp.buf = ai_udp->ai_addr; + nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp))) + err(1, "rpcb_set udp failed"); + freeaddrinfo(ai_udp); } - (void)close(sock); } -#ifdef ISO - /* If we are serving cltp, set up the socket. */ - if (cltpflag) { - if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "can't create cltp socket"); - exit(1); - } - memset(&isoaddr, 0, sizeof(isoaddr)); - isoaddr.siso_family = AF_ISO; - isoaddr.siso_tlen = 2; - cp = TSEL(&isoaddr); - *cp++ = (NFS_PORT >> 8); - *cp = (NFS_PORT & 0xff); - isoaddr.siso_len = sizeof(isoaddr); - if (bind(sock, - (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) { - syslog(LOG_ERR, "can't bind cltp addr"); - exit(1); - } -#ifdef notyet - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) { - syslog(LOG_ERR, "can't register with udp portmap"); - exit(1); + /* Set up the socket for udp6 and rpcb register it. */ + if (udpflag && ip6flag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + if (setbindhost(&ai_udp6, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((sock = socket(ai_udp6->ai_family, + ai_udp6->ai_socktype, + ai_udp6->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create udp6 socket"); + exit(1); + } + if (setsockopt(sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, + &on, sizeof on) < 0) { + syslog(LOG_ERR, + "can't set v6-only binding for " + "udp6 socket: %m"); + exit(1); + } + if (bind(sock, ai_udp6->ai_addr, + ai_udp6->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind udp6 addr %s: %m", + bindhost[i]); + exit(1); + } + freeaddrinfo(ai_udp6); + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, + "can't add UDP6 socket"); + exit(1); + } + (void)close(sock); + } } -#endif /* notyet */ - nfsdargs.sock = sock; - nfsdargs.name = NULL; - nfsdargs.namelen = 0; - if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { - syslog(LOG_ERR, "can't add UDP socket"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp6 = getnetconfigent("udp6"); + if (nconf_udp6 == NULL) + err(1, "getnetconfigent udp6 failed"); + nb_udp6.buf = ai_udp6->ai_addr; + nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6))) + err(1, "rpcb_set udp6 failed"); + freeaddrinfo(ai_udp6); } - close(sock); } -#endif /* ISO */ - - /* Now set up the master server socket waiting for tcp connections. */ - on = 1; - FD_ZERO(&sockbits); - connect_type_cnt = 0; - for (i = 0; tcpflag && i < bindhostc; i++) { - if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - syslog(LOG_ERR, "can't create tcp socket"); - exit(1); - } - if (setsockopt(tcpsock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - setbindhost(&inetaddr, bindhost[i]); - if (bind(tcpsock, - (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind tcp addr %s: %m", bindhost[i]); - exit(1); - } - if (listen(tcpsock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); + + /* Set up the socket for tcp and rpcb register it. */ + if (tcpflag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (setbindhost(&ai_tcp, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((tcpsock = socket(AF_INET, SOCK_STREAM, + 0)) < 0) { + syslog(LOG_ERR, + "can't create tpc socket"); + exit(1); + } + if (setsockopt(tcpsock, SOL_SOCKET, + SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_REUSEADDR: %m"); + if (bind(tcpsock, ai_tcp->ai_addr, + ai_tcp->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind tcp addr %s: %m", + bindhost[i]); + exit(1); + } + if (listen(tcpsock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + freeaddrinfo(ai_tcp); + FD_SET(tcpsock, &sockbits); + FD_SET(tcpsock, &v4bits); + maxsock = tcpsock; + connect_type_cnt++; + } } - if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, + &ai_tcp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp = getnetconfigent("tcp"); + if (nconf_tcp == NULL) + err(1, "getnetconfigent tcp failed"); + nb_tcp.buf = ai_tcp->ai_addr; + nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, + &nb_tcp)) || (!rpcb_set(RPCPROG_NFS, 3, + nconf_tcp, &nb_tcp))) + err(1, "rpcb_set tcp failed"); + freeaddrinfo(ai_tcp); } - FD_SET(tcpsock, &sockbits); - maxsock = tcpsock; - connect_type_cnt++; } -#ifdef notyet - /* Now set up the master server socket waiting for tp4 connections. */ - if (tp4flag) { - if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) { - syslog(LOG_ERR, "can't create tp4 socket"); - exit(1); - } - if (setsockopt(tp4sock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - memset(&isoaddr, 0, sizeof(isoaddr)); - isoaddr.siso_family = AF_ISO; - isoaddr.siso_tlen = 2; - cp = TSEL(&isoaddr); - *cp++ = (NFS_PORT >> 8); - *cp = (NFS_PORT & 0xff); - isoaddr.siso_len = sizeof(isoaddr); - if (bind(tp4sock, - (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) { - syslog(LOG_ERR, "can't bind tp4 addr"); - exit(1); - } - if (listen(tp4sock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); + /* Set up the socket for tcp6 and rpcb register it. */ + if (tcpflag && ip6flag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (setbindhost(&ai_tcp6, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((tcp6sock = socket(ai_tcp6->ai_family, + ai_tcp6->ai_socktype, + ai_tcp6->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create tcp6 socket"); + exit(1); + } + if (setsockopt(tcp6sock, SOL_SOCKET, + SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_REUSEADDR: %m"); + if (setsockopt(tcp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &on, sizeof on) < 0) { + syslog(LOG_ERR, + "can't set v6-only binding for tcp6 " + "socket: %m"); + exit(1); + } + if (bind(tcp6sock, ai_tcp6->ai_addr, + ai_tcp6->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind tcp6 addr %s: %m", + bindhost[i]); + exit(1); + } + if (listen(tcp6sock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + freeaddrinfo(ai_tcp6); + FD_SET(tcp6sock, &sockbits); + FD_SET(tcp6sock, &v6bits); + if (maxsock < tcp6sock) + maxsock = tcp6sock; + connect_type_cnt++; + } } - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp6 = getnetconfigent("tcp6"); + if (nconf_tcp6 == NULL) + err(1, "getnetconfigent tcp6 failed"); + nb_tcp6.buf = ai_tcp6->ai_addr; + nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6))) + err(1, "rpcb_set tcp6 failed"); + freeaddrinfo(ai_tcp6); } - FD_SET(tp4sock, &sockbits); - maxsock = tp4sock; - connect_type_cnt++; } - /* Now set up the master server socket waiting for tpip connections. */ - for (i = 0; tpipflag && i < bindhostc; i++) { - if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) { - syslog(LOG_ERR, "can't create tpip socket"); - exit(1); - } - if (setsockopt(tpipsock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - setbindhost(&inetaddr, bindhost[i]); - if (bind(tpipsock, - (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind tcp addr %s: %m", bindhost[i]); - exit(1); - } - if (listen(tpipsock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); - } - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); - } - FD_SET(tpipsock, &sockbits); - maxsock = tpipsock; - connect_type_cnt++; + if (rpcbregcnt == 0) { + syslog(LOG_ERR, "rpcb_set() failed, nothing to do: %m"); + exit(1); } -#endif /* notyet */ - if (connect_type_cnt == 0) - exit(0); + if ((tcpflag) && (connect_type_cnt == 0)) { + syslog(LOG_ERR, "tcp connects == 0, nothing to do: %m"); + exit(1); + } setproctitle("master"); @@ -591,82 +794,94 @@ main(argc, argv, envp) exit(1); } } - if (tcpflag && FD_ISSET(tcpsock, &ready)) { - len = sizeof(inetpeer); - if ((msgsock = accept(tcpsock, - (struct sockaddr *)&inetpeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); - } - memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero)); - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, - "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&inetpeer; - nfsdargs.namelen = sizeof(inetpeer); - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); - } -#ifdef notyet - if (tp4flag && FD_ISSET(tp4sock, &ready)) { - len = sizeof(isopeer); - if ((msgsock = accept(tp4sock, - (struct sockaddr *)&isopeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); - } - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, - "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&isopeer; - nfsdargs.namelen = len; - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); - } - if (tpipflag && FD_ISSET(tpipsock, &ready)) { - len = sizeof(inetpeer); - if ((msgsock = accept(tpipsock, - (struct sockaddr *)&inetpeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); + for (tcpsock = 0; tcpsock <= maxsock; tcpsock++) { + if (FD_ISSET(tcpsock, &ready)) { + if (FD_ISSET(tcpsock, &v4bits)) { + len = sizeof(inetpeer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inetpeer, &len)) < 0) { + syslog(LOG_ERR, "accept failed: %m"); + exit(1); + } + memset(inetpeer.sin_zero, 0, + sizeof(inetpeer.sin_zero)); + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inetpeer; + nfsdargs.namelen = sizeof(inetpeer); + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } else if (FD_ISSET(tcpsock, &v6bits)) { + len = sizeof(inet6peer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inet6peer, + &len)) < 0) { + syslog(LOG_ERR, + "accept failed: %m"); + exit(1); + } + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, + sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt " + "SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inet6peer; + nfsdargs.namelen = sizeof(inet6peer); + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } } - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&inetpeer; - nfsdargs.namelen = len; - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); } -#endif /* notyet */ } } -void -setbindhost(struct sockaddr_in *ia, const char *bindhost) +int +setbindhost(struct addrinfo **ai, const char *bindhost, struct addrinfo hints) { - ia->sin_family = AF_INET; - ia->sin_port = htons(NFS_PORT); - ia->sin_len = sizeof(*ia); - if (bindhost == NULL || strcmp(bindhost,"*") == 0) { - ia->sin_addr.s_addr = INADDR_ANY; - } else { - if (inet_aton(bindhost, &ia->sin_addr) == 0) { - struct hostent *he; - - he = gethostbyname2(bindhost, ia->sin_family); - if (he == NULL) { - syslog(LOG_ERR, "gethostbyname of %s failed", bindhost); - exit(1); + int ecode; + u_int32_t host_addr[4]; /* IPv4 or IPv6 */ + const char *hostptr; + + if (bindhost == NULL || strcmp("*", bindhost) == 0) + hostptr = NULL; + else + hostptr = bindhost; + + if (hostptr != NULL) { + switch (hints.ai_family) { + case AF_INET: + if (inet_pton(AF_INET, hostptr, host_addr) == 1) { + hints.ai_flags = AI_NUMERICHOST; + } else { + if (inet_pton(AF_INET6, hostptr, + host_addr) == 1) + return (1); + } + break; + case AF_INET6: + if (inet_pton(AF_INET6, hostptr, host_addr) == 1) { + hints.ai_flags = AI_NUMERICHOST; + } else { + if (inet_pton(AF_INET, hostptr, + host_addr) == 1) + return (1); } - bcopy(he->h_addr, &ia->sin_addr, he->h_length); + break; + default: } } + + ecode = getaddrinfo(hostptr, "nfs", &hints, ai); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo %s: %s", bindhost, + gai_strerror(ecode)); + return (1); + } + return (0); } void @@ -691,6 +906,48 @@ reapchild(signo) while (wait3(NULL, WNOHANG, NULL) > 0); } +void +unregistration() +{ + if ((!rpcb_unset(RPCPROG_NFS, 2, NULL)) || + (!rpcb_unset(RPCPROG_NFS, 3, NULL))) + syslog(LOG_ERR, "rpcb_unset failed"); +} + +void +killchildren() +{ + int i; + sigset_t sigs; + + sigemptyset(&sigs); + /* + * Block SIGCHLD to avoid killing a reaped process (although it is + * unlikely, the pid might have been reused). + */ + sigaddset(&sigs, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &sigs, NULL) == -1) { + syslog(LOG_ERR, "sigprocmask: %s", + strerror(errno)); + return; + } + for (i = 0; i < nfsdcnt; i++) { + if (children[i] > 0) + kill(children[i], SIGKILL); + } + if (sigprocmask(SIG_UNBLOCK, &sigs, NULL) == -1) { + syslog(LOG_ERR, "sigprocmask: %s", strerror(errno)); + } + unregistration(); +} + +void +cleanup(signo) +{ + killchildren(); + exit (0); +} + #ifdef OLD_SETPROCTITLE #ifdef __FreeBSD__ void diff --git a/usr.sbin/portmap/Makefile b/usr.sbin/portmap/Makefile deleted file mode 100644 index b87cf59..0000000 --- a/usr.sbin/portmap/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 -# $FreeBSD$ - -PROG= portmap -MAN8= portmap.8 -SRCS= portmap.c from_local.c pmap_check.c -SUBDIR= pmap_set pmap_dump - -CFLAGS+=-DCHECK_PORT -DHOSTS_ACCESS -DPADD= ${LIBWRAP} -LDADD= -lwrap - -.include <bsd.prog.mk> diff --git a/usr.sbin/portmap/from_local.c b/usr.sbin/portmap/from_local.c deleted file mode 100644 index 9886f12..0000000 --- a/usr.sbin/portmap/from_local.c +++ /dev/null @@ -1,233 +0,0 @@ - /* - * Check if an address belongs to the local system. Adapted from: - * - * @(#)pmap_svc.c 1.32 91/03/11 Copyright 1984,1990 Sun Microsystems, Inc. - * @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC. - */ - -/* - * Sun RPC is a product of Sun Microsystems, Inc. and is provided for - * unrestricted use provided that this legend is included on all tape - * media and as a part of the software program in whole or part. Users - * may copy or modify Sun RPC without charge, but are not authorized - * to license or distribute it to anyone else except as part of a product or - * program developed by the user or with the express written consent of - * Sun Microsystems, Inc. - * - * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE - * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. - * - * Sun RPC is provided with no support and without any obligation on the - * part of Sun Microsystems, Inc. to assist in its use, correction, - * modification or enhancement. - * - * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE - * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC - * OR ANY PART THEREOF. - * - * In no event will Sun Microsystems, Inc. be liable for any lost revenue - * or profits or other special, indirect and consequential damages, even if - * Sun has been advised of the possibility of such damages. - * - * Sun Microsystems, Inc. - * 2550 Garcia Avenue - * Mountain View, California 94043 - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#) from_local.c 1.2 93/11/16 21:50:02"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif - -#ifdef TEST -#undef perror -#endif - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/sysctl.h> -#include <sys/time.h> - -#include <netdb.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <unistd.h> - -#include <net/if.h> -#include <net/if_dl.h> -#include <net/route.h> -#include <netinet/in.h> - -#include "pmap_check.h" - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -#define ROUNDUP(x) ((x) ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long)) - -/* How many interfaces could there be on a computer? */ - -#define ESTIMATED_LOCAL 20 -static int num_local = -1; -static struct in_addr *addrs; - -static void -rtiparse(struct ifa_msghdr *ifam, struct rt_addrinfo *ai) -{ - char *wp; - int rtax; - - wp = (char *)(ifam + 1); - - ai->rti_addrs = ifam->ifam_addrs; - for (rtax = 0; rtax < sizeof ai->rti_info / sizeof *ai->rti_info; rtax++) - if (ifam->ifam_addrs & (1 << rtax)) { - ai->rti_info[rtax] = (struct sockaddr *)wp; - wp += ROUNDUP(ai->rti_info[rtax]->sa_len); - } else - ai->rti_info[rtax] = NULL; -} - -/* find_local - find all IP addresses for this host */ - -static int -find_local() -{ - int mib[6], n, s, alloced; - size_t needed; - char *buf, *end, *ptr; - struct if_msghdr *ifm; - struct ifa_msghdr *ifam; - struct rt_addrinfo ai; - struct ifreq ifr; - struct sockaddr_dl *dl; - - mib[0] = CTL_NET; - mib[1] = PF_ROUTE; - mib[4] = NET_RT_IFLIST; - mib[2] = mib[3] = mib[5] = 0; - - if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return (0); - } - if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { - close(s); - perror("sysctl(NET_RT_IFLIST)"); - return 0; - } - if ((buf = (char *)malloc(needed)) == NULL) { - close(s); - perror("malloc"); - return 0; - } - if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { - close(s); - free(buf); - perror("sysctl(NET_RT_IFLIST)(after malloc)"); - return 0; - } - - if (addrs) { - free(addrs); - addrs = NULL; - } - num_local = 0; - alloced = 0; - end = buf + needed; - - for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) { - ifm = (struct if_msghdr *)ptr; - dl = (struct sockaddr_dl *)(ifm + 1); - - if (ifm->ifm_index != dl->sdl_index || dl->sdl_nlen == 0) - /* Skip over remaining ifa_msghdrs */ - continue; - - n = dl->sdl_nlen > sizeof ifr.ifr_name ? - sizeof ifr.ifr_name : dl->sdl_nlen; - strncpy(ifr.ifr_name, dl->sdl_data, n); - if (n < sizeof ifr.ifr_name) - ifr.ifr_name[n] = '\0'; - - /* we only want the first address from each interface */ - if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) - fprintf(stderr, "%.*s: SIOCGIFFLAGS: %s\n", n, ifr.ifr_name, - strerror(errno)); - else if (ifr.ifr_flags & IFF_UP) { /* active interface */ - ifam = (struct ifa_msghdr *)(ptr + ifm->ifm_msglen); - while ((char *)ifam < end && ifam->ifam_type == RTM_NEWADDR) { - rtiparse(ifam, &ai); - - if (ai.rti_info[RTAX_IFA] != NULL && - ai.rti_info[RTAX_IFA]->sa_family == AF_INET) { - if (alloced < num_local + 1) { - alloced += ESTIMATED_LOCAL; - addrs = (struct in_addr *)realloc(addrs, alloced * sizeof addrs[0]); - if (addrs == NULL) { - perror("malloc/realloc"); - num_local = 0; - break; - } - } - addrs[num_local++] = ((struct sockaddr_in *) - ai.rti_info[RTAX_IFA])->sin_addr; - - } - ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen); - } - } - } - free(buf); - close(s); - - return num_local; -} - -/* from_local - determine whether request comes from the local system */ - -int -from_local(addr) - struct sockaddr_in *addr; -{ - int i; - - if (num_local == -1 && find_local() == 0) - syslog(LOG_ERR, "cannot find any active local network interfaces"); - - for (i = 0; i < num_local; i++) { - if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]), - sizeof(struct in_addr)) == 0) - return (TRUE); - } - return (FALSE); -} - -#ifdef TEST - -int -main(argc, argv) - int argc; - char **argv; -{ - char *inet_ntoa(); - int i; - - find_local(); - for (i = 0; i < num_local; i++) - printf("%s\n", inet_ntoa(addrs[i])); - - return 0; -} - -#endif diff --git a/usr.sbin/portmap/pmap_check.c b/usr.sbin/portmap/pmap_check.c deleted file mode 100644 index 7ad25c9..0000000 --- a/usr.sbin/portmap/pmap_check.c +++ /dev/null @@ -1,263 +0,0 @@ - /* - * pmap_check - additional portmap security. - * - * Always reject non-local requests to update the portmapper tables. - * - * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the - * requests would appear to come from the local system, and nfs export - * restrictions could be bypassed. - * - * Refuse to forward requests to the nfsd process. - * - * Refuse to forward requests to NIS (YP) daemons; The only exception is the - * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial - * contact with the NIS server. - * - * Always allocate an unprivileged port when forwarding a request. - * - * If compiled with -DCHECK_PORT, require that requests to register or - * unregister a privileged port come from a privileged port. This makes it - * more difficult to replace a critical service by a trojan. - * - * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not - * authorized by the /etc/hosts.{allow,deny} files. The local system is - * always treated as an authorized host. The access control tables are never - * consulted for requests from the local system, and are always consulted - * for requests from other hosts. Access control is based on IP addresses - * only; attempts to map an address to a host name might cause the - * portmapper to hang. - * - * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and - * Computing Science, Eindhoven University of Technology, The Netherlands. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#) pmap_check.c 1.6 93/11/21 20:58:59"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif - -#include <stdio.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <rpc/rpc.h> -#include <rpc/pmap_prot.h> -#include <syslog.h> -#include <netdb.h> -#include <sys/signal.h> - -#include "pmap_check.h" - -/* Explicit #defines in case the include files are not available. */ - -#define NFSPROG ((u_long) 100003) -#define MOUNTPROG ((u_long) 100005) -#define YPXPROG ((u_long) 100069) -#define YPPROG ((u_long) 100004) -#define YPPROC_DOMAIN_NONACK ((u_long) 2) -#define MOUNTPROC_MNT ((u_long) 1) - -static void logit __P((int, struct sockaddr_in *, u_long, u_long, const char *)); -static void toggle_verboselog __P((int)); - -int verboselog = 0; -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; - -/* A handful of macros for "readability". */ - -#define good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "") - -#define legal_port(a,p) \ - (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED) - -#define log_bad_port(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from unprivileged port") - -#define log_bad_host(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from unauthorized host") - -#define log_bad_owner(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from non-local host") - -#define log_no_forward(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request not forwarded") - -#define log_client(addr, proc, prog) \ - logit(allow_severity, addr, proc, prog, "") - -/* check_startup - additional startup code */ - -void -check_startup() -{ - - /* - * Give up root privileges so that we can never allocate a privileged - * port when forwarding an rpc request. - */ - if (setuid(1) == -1) { - syslog(LOG_ERR, "setuid(1) failed: %m"); - exit(1); - } - (void) signal(SIGINT, toggle_verboselog); -} - -/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */ - -int -check_default(addr, proc, prog) - struct sockaddr_in *addr; - u_long proc, prog; -{ -#ifdef HOSTS_ACCESS - if (!(from_local(addr) || good_client(addr))) { - log_bad_host(addr, proc, prog); - return (FALSE); - } -#endif - if (verboselog) - log_client(addr, proc, prog); - return (TRUE); -} - -/* check_privileged_port - additional checks for privileged-port updates */ - -int -check_privileged_port(addr, proc, prog, port) - struct sockaddr_in *addr; - u_long proc, prog, port; -{ -#ifdef CHECK_PORT - if (!legal_port(addr, port)) { - log_bad_port(addr, proc, prog); - return (FALSE); - } -#endif - return (TRUE); -} - -/* check_setunset - additional checks for update requests */ - -int -check_setunset(addr, proc, prog, port) - struct sockaddr_in *addr; - u_long proc, prog, port; -{ - if (!from_local(addr)) { -#ifdef HOSTS_ACCESS - (void) good_client(addr); /* because of side effects */ -#endif - log_bad_owner(addr, proc, prog); - return (FALSE); - } - if (port && !check_privileged_port(addr, proc, prog, port)) - return (FALSE); - if (verboselog) - log_client(addr, proc, prog); - return (TRUE); -} - -/* check_callit - additional checks for forwarded requests */ - -int -check_callit(addr, proc, prog, aproc) - struct sockaddr_in *addr; - u_long proc, prog, aproc; -{ -#ifdef HOSTS_ACCESS - if (!(from_local(addr) || good_client(addr))) { - log_bad_host(addr, proc, prog); - return (FALSE); - } -#endif - if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG || - (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) || - (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) { - log_no_forward(addr, proc, prog); - return (FALSE); - } - if (verboselog) - log_client(addr, proc, prog); - return (TRUE); -} - -/* toggle_verboselog - toggle verbose logging flag */ - -static void -toggle_verboselog(sig) - int sig; -{ - (void) signal(sig, toggle_verboselog); - verboselog = !verboselog; -} - -/* logit - report events of interest via the syslog daemon */ - -static void -logit(severity, addr, procnum, prognum, text) - int severity; - struct sockaddr_in *addr; - u_long procnum, prognum; - const char *text; -{ - const char *procname; - char procbuf[4 * sizeof(u_long)]; - const char *progname; - char progbuf[4 * sizeof(u_long)]; - struct rpcent *rpc; - struct proc_map { - u_long code; - const char *proc; - }; - struct proc_map *procp; - static struct proc_map procmap[] = { - {PMAPPROC_CALLIT, "callit"}, - {PMAPPROC_DUMP, "dump"}, - {PMAPPROC_GETPORT, "getport"}, - {PMAPPROC_NULL, "null"}, - {PMAPPROC_SET, "set"}, - {PMAPPROC_UNSET, "unset"}, - {0, 0}, - }; - - /* - * Fork off a process or the portmap daemon might hang while - * getrpcbynumber() or syslog() does its thing. - */ - - if (fork() == 0) { - - /* Try to map program number to name. */ - - if (prognum == 0) { - progname = ""; - } else if ((rpc = getrpcbynumber((int) prognum))) { - progname = rpc->r_name; - } else { - sprintf(progbuf, "%lu", prognum); - progname = progbuf; - } - - /* Try to map procedure number to name. */ - - for (procp = procmap; procp->proc && procp->code != procnum; procp++) - /* void */ ; - if ((procname = procp->proc) == 0) { - sprintf(procbuf, "%lu", (u_long) procnum); - procname = procbuf; - } - - /* Write syslog record. */ - - syslog(severity, "connect from %s to %s(%s)%s", - inet_ntoa(addr->sin_addr), procname, progname, text); - exit(0); - } -} diff --git a/usr.sbin/portmap/pmap_check.h b/usr.sbin/portmap/pmap_check.h deleted file mode 100644 index dc454a5..0000000 --- a/usr.sbin/portmap/pmap_check.h +++ /dev/null @@ -1,41 +0,0 @@ -/*- - * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#) pmap_check.h 1.3 93/11/21 16:18:53 - * - * $FreeBSD$ - */ - -extern int from_local __P((struct sockaddr_in *)); -extern void check_startup __P((void)); -extern int check_default __P((struct sockaddr_in *, u_long, u_long)); -extern int check_setunset __P((struct sockaddr_in *, u_long, u_long, u_long)); -extern int check_privileged_port __P((struct sockaddr_in *, u_long, u_long, - u_long)); -extern int check_callit __P((struct sockaddr_in *, u_long, u_long, u_long)); - -extern int verboselog; -extern int allow_severity; -extern int deny_severity; diff --git a/usr.sbin/portmap/pmap_dump/Makefile b/usr.sbin/portmap/pmap_dump/Makefile deleted file mode 100644 index 0064f28..0000000 --- a/usr.sbin/portmap/pmap_dump/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 - -PROG= pmap_dump -NOMAN= noman - -.include "${.CURDIR}/../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/usr.sbin/portmap/pmap_dump/pmap_dump.c b/usr.sbin/portmap/pmap_dump/pmap_dump.c deleted file mode 100644 index 96fb5c2..0000000 --- a/usr.sbin/portmap/pmap_dump/pmap_dump.c +++ /dev/null @@ -1,69 +0,0 @@ - /* - * pmap_dump - dump portmapper table in format readable by pmap_set - * - * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and - * Computing Science, Eindhoven University of Technology, The Netherlands. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#) pmap_dump.c 1.1 92/06/11 22:53:15"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif - -#include <stdio.h> -#include <sys/types.h> -#ifdef SYSV40 -#include <netinet/in.h> -#include <rpc/rpcent.h> -#else -#include <netdb.h> -#endif -#include <rpc/rpc.h> -#include <rpc/pmap_clnt.h> -#include <rpc/pmap_prot.h> - -static const char *protoname __P((u_long)); - -int -main(argc, argv) - int argc; - char **argv; -{ - struct sockaddr_in addr; - register struct pmaplist *list; - register struct rpcent *rpc; - - get_myaddress(&addr); - - for (list = pmap_getmaps(&addr); list; list = list->pml_next) { - rpc = getrpcbynumber((int) list->pml_map.pm_prog); - printf("%10lu %4lu %5s %6lu %s\n", - list->pml_map.pm_prog, - list->pml_map.pm_vers, - protoname(list->pml_map.pm_prot), - list->pml_map.pm_port, - rpc ? rpc->r_name : ""); - } -#undef perror - return (fclose(stdout) ? (perror(argv[0]), 1) : 0); -} - -static const char * -protoname(proto) - u_long proto; -{ - static char buf[BUFSIZ]; - - switch (proto) { - case IPPROTO_UDP: - return ("udp"); - case IPPROTO_TCP: - return ("tcp"); - default: - sprintf(buf, "%lu", proto); - return (buf); - } -} diff --git a/usr.sbin/portmap/pmap_set/Makefile b/usr.sbin/portmap/pmap_set/Makefile deleted file mode 100644 index 987e320..0000000 --- a/usr.sbin/portmap/pmap_set/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# @(#)Makefile 8.1 (Berkeley) 6/6/93 - -PROG= pmap_set -NOMAN= noman - -.include "${.CURDIR}/../../Makefile.inc" -.include <bsd.prog.mk> diff --git a/usr.sbin/portmap/pmap_set/pmap_set.c b/usr.sbin/portmap/pmap_set/pmap_set.c deleted file mode 100644 index 35a7fbe..0000000 --- a/usr.sbin/portmap/pmap_set/pmap_set.c +++ /dev/null @@ -1,78 +0,0 @@ - /* - * pmap_set - set portmapper table from data produced by pmap_dump - * - * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and - * Computing Science, Eindhoven University of Technology, The Netherlands. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#) pmap_set.c 1.1 92/06/11 22:53:16"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif - -#include <err.h> -#include <stdio.h> -#include <sys/types.h> -#ifdef SYSV40 -#include <netinet/in.h> -#endif -#include <rpc/rpc.h> -#include <rpc/pmap_clnt.h> - -static int parse_line __P((char *, u_long *, u_long *, int *, unsigned *)); - -int -main(argc, argv) - int argc; - char **argv; -{ - struct sockaddr_in addr; - char buf[BUFSIZ]; - u_long prog; - u_long vers; - int prot; - unsigned port; - - get_myaddress(&addr); - - while (fgets(buf, sizeof(buf), stdin)) { - if (parse_line(buf, &prog, &vers, &prot, &port) == 0) { - warnx("malformed line: %s", buf); - return (1); - } - if (pmap_set(prog, vers, prot, (unsigned short) port) == 0) - warnx("not registered: %s", buf); - } - return (0); -} - -/* parse_line - convert line to numbers */ - -static int -parse_line(buf, prog, vers, prot, port) - char *buf; - u_long *prog, *vers; - int *prot; - unsigned *port; -{ - char proto_name[BUFSIZ]; - - if (sscanf(buf, "%lu %lu %s %u", prog, vers, proto_name, port) != 4) { - return (0); - } - if (strcmp(proto_name, "tcp") == 0) { - *prot = IPPROTO_TCP; - return (1); - } - if (strcmp(proto_name, "udp") == 0) { - *prot = IPPROTO_UDP; - return (1); - } - if (sscanf(proto_name, "%d", prot) == 1) { - return (1); - } - return (0); -} diff --git a/usr.sbin/portmap/portmap.8 b/usr.sbin/portmap/portmap.8 deleted file mode 100644 index 455ccbd..0000000 --- a/usr.sbin/portmap/portmap.8 +++ /dev/null @@ -1,122 +0,0 @@ -.\" Copyright (c) 1987 Sun Microsystems -.\" Copyright (c) 1990, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" This product includes software developed by the University of -.\" California, Berkeley and its contributors. -.\" 4. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)portmap.8 8.1 (Berkeley) 6/6/93 -.\" $FreeBSD$ -.\" -.Dd June 6, 1993 -.Dt PORTMAP 8 -.Os BSD 4.3 -.Sh NAME -.Nm portmap -.Nd -.Tn RPC -program,version -to -.Tn DARPA -port mapper -.Sh SYNOPSIS -.Nm -.Op Fl d -.Op Fl v -.Sh DESCRIPTION -.Nm Portmap -is a server that converts -.Tn RPC -program numbers into -.Tn DARPA -protocol port numbers. -It must be running in order to make -.Tn RPC -calls. -.Pp -When an -.Tn RPC -server is started, it will tell -.Nm -what port number it is listening to, and what -.Tn RPC -program numbers it is prepared to serve. -When a client wishes to make an -.Tn RPC -call to a given program number, -it will first contact -.Nm -on the server machine to determine -the port number where -.Tn RPC -packets should be sent. -.Pp -.Nm Portmap -must be started before any -.Tn RPC -servers are invoked. -.Pp -.Nm Portmap -uses -.Xr hosts_access 5 -access control by default. -Access control patterns may only reference IP addresses. -.Pp -Normally -.Nm -forks and dissociates itself from the terminal -like any other daemon. -.Nm Portmap -then logs errors using -.Xr syslog 3 . -.Pp -The following options are available: -.Bl -tag -width indent -.It Fl d -Prevent -.Nm -from running as a daemon, -and causes errors and debugging information -to be printed to the standard error output. -.It Fl v -Enable verbose logging of access control checks. -.El -.Sh SEE ALSO -.Xr hosts_access 5 , -.Xr inetd.conf 5 , -.Xr inetd 8 , -.Xr rpcinfo 8 -.Sh BUGS -If -.Nm -crashes, all servers must be restarted. -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.3 diff --git a/usr.sbin/portmap/portmap.c b/usr.sbin/portmap/portmap.c deleted file mode 100644 index 658b53c..0000000 --- a/usr.sbin/portmap/portmap.c +++ /dev/null @@ -1,612 +0,0 @@ -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1990, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)portmap.c 8.1 (Berkeley) 6/6/93"; -#endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - -/* -@(#)portmap.c 2.3 88/08/11 4.0 RPCSRC -static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro"; -*/ - -/* - * portmap.c, Implements the program,version to port number mapping for - * rpc. - */ - -/* - * Sun RPC is a product of Sun Microsystems, Inc. and is provided for - * unrestricted use provided that this legend is included on all tape - * media and as a part of the software program in whole or part. Users - * may copy or modify Sun RPC without charge, but are not authorized - * to license or distribute it to anyone else except as part of a product or - * program developed by the user. - * - * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE - * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR - * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. - * - * Sun RPC is provided with no support and without any obligation on the - * part of Sun Microsystems, Inc. to assist in its use, correction, - * modification or enhancement. - * - * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE - * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC - * OR ANY PART THEREOF. - * - * In no event will Sun Microsystems, Inc. be liable for any lost revenue - * or profits or other special, indirect and consequential damages, even if - * Sun has been advised of the possibility of such damages. - * - * Sun Microsystems, Inc. - * 2550 Garcia Avenue - * Mountain View, California 94043 - */ - -#include <err.h> -#include <errno.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> -#include <unistd.h> -#include <rpc/rpc.h> -#include <rpc/pmap_prot.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <sys/wait.h> -#include <sys/signal.h> -#include <sys/resource.h> - -#include "pmap_check.h" - -static void reg_service __P((struct svc_req *, SVCXPRT *)); -static void reap __P((int)); -static void callit __P((struct svc_req *, SVCXPRT *)); -static void usage __P((void)); - -struct pmaplist *pmaplist; -int debugging = 0; - -int -main(argc, argv) - int argc; - char **argv; -{ - SVCXPRT *xprt; - int sock, c; - struct sockaddr_in addr; - int len = sizeof(struct sockaddr_in); - register struct pmaplist *pml; - - while ((c = getopt(argc, argv, "dv")) != -1) { - switch (c) { - - case 'd': - debugging = 1; - break; - - case 'v': - verboselog = 1; - break; - - default: - usage(); - } - } - - if (!debugging && daemon(0, 0)) - err(1, "fork"); - - openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID, - LOG_DAEMON); - - if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { - syslog(LOG_ERR, "cannot create udp socket: %m"); - exit(1); - } - - addr.sin_addr.s_addr = 0; - addr.sin_family = AF_INET; - addr.sin_port = htons(PMAPPORT); - if (bind(sock, (struct sockaddr *)&addr, len) != 0) { - syslog(LOG_ERR, "cannot bind udp: %m"); - exit(1); - } - - if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) { - syslog(LOG_ERR, "couldn't do udp_create"); - exit(1); - } - /* make an entry for ourself */ - pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist)); - pml->pml_next = 0; - pml->pml_map.pm_prog = PMAPPROG; - pml->pml_map.pm_vers = PMAPVERS; - pml->pml_map.pm_prot = IPPROTO_UDP; - pml->pml_map.pm_port = PMAPPORT; - pmaplist = pml; - - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - syslog(LOG_ERR, "cannot create tcp socket: %m"); - exit(1); - } - if (bind(sock, (struct sockaddr *)&addr, len) != 0) { - syslog(LOG_ERR, "cannot bind tcp: %m"); - exit(1); - } - if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE)) - == (SVCXPRT *)NULL) { - syslog(LOG_ERR, "couldn't do tcp_create"); - exit(1); - } - /* make an entry for ourself */ - pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist)); - pml->pml_map.pm_prog = PMAPPROG; - pml->pml_map.pm_vers = PMAPVERS; - pml->pml_map.pm_prot = IPPROTO_TCP; - pml->pml_map.pm_port = PMAPPORT; - pml->pml_next = pmaplist; - pmaplist = pml; - - (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE); - - /* additional initializations */ - check_startup(); - (void)signal(SIGCHLD, reap); - svc_run(); - syslog(LOG_ERR, "svc_run returned unexpectedly"); - abort(); -} - -static void -usage() -{ - fprintf(stderr, "usage: portmap [-dv]\n"); - exit(1); -} - -#ifndef lint -/* need to override perror calls in rpc library */ -void -perror(what) - const char *what; -{ - syslog(LOG_ERR, "%s: %m", what); -} -#endif - -static struct pmaplist * -find_service(prog, vers, prot) - u_long prog, vers, prot; -{ - register struct pmaplist *hit = NULL; - register struct pmaplist *pml; - - for (pml = pmaplist; pml != NULL; pml = pml->pml_next) { - if ((pml->pml_map.pm_prog != prog) || - (pml->pml_map.pm_prot != prot)) - continue; - hit = pml; - if (pml->pml_map.pm_vers == vers) - break; - } - return (hit); -} - -/* - * 1 OK, 0 not - */ -static void -reg_service(rqstp, xprt) - struct svc_req *rqstp; - SVCXPRT *xprt; -{ - struct pmap reg; - struct pmaplist *pml, *prevpml, *fnd; - int ans, port; - caddr_t t; - - /* - * Later wrappers change the logging severity on the fly. Reset to - * defaults before handling the next request. - */ - allow_severity = LOG_INFO; - deny_severity = LOG_WARNING; - - if (debugging) - (void) fprintf(stderr, "server: about to do a switch\n"); - switch (rqstp->rq_proc) { - - case PMAPPROC_NULL: - /* - * Null proc call - */ - /* remote host authorization check */ - check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0); - if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) { - abort(); - } - break; - - case PMAPPROC_SET: - /* - * Set a program,version to port mapping - */ - if (!svc_getargs(xprt, xdr_pmap, (caddr_t)®)) - svcerr_decode(xprt); - else { - /* reject non-local requests, protect priv. ports */ - if (!check_setunset(svc_getcaller(xprt), - rqstp->rq_proc, reg.pm_prog, reg.pm_port)) { - ans = 0; - goto done; - } - /* - * check to see if already used - * find_service returns a hit even if - * the versions don't match, so check for it - */ - fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); - if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) { - if (fnd->pml_map.pm_port == reg.pm_port) { - ans = 1; - goto done; - } - else { - ans = 0; - goto done; - } - } else { - /* - * add to END of list - */ - pml = (struct pmaplist *) - malloc((u_int)sizeof(struct pmaplist)); - pml->pml_map = reg; - pml->pml_next = 0; - if (pmaplist == 0) { - pmaplist = pml; - } else { - for (fnd= pmaplist; fnd->pml_next != 0; - fnd = fnd->pml_next); - fnd->pml_next = pml; - } - ans = 1; - } - done: - if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) && - debugging) { - (void) fprintf(stderr, "svc_sendreply\n"); - abort(); - } - } - break; - - case PMAPPROC_UNSET: - /* - * Remove a program,version to port mapping. - */ - if (!svc_getargs(xprt, xdr_pmap, (caddr_t)®)) - svcerr_decode(xprt); - else { - ans = 0; - /* reject non-local requests */ - if (!check_setunset(svc_getcaller(xprt), - rqstp->rq_proc, reg.pm_prog, (u_long) 0)) - goto done; - for (prevpml = NULL, pml = pmaplist; pml != NULL; ) { - if ((pml->pml_map.pm_prog != reg.pm_prog) || - (pml->pml_map.pm_vers != reg.pm_vers)) { - /* both pml & prevpml move forwards */ - prevpml = pml; - pml = pml->pml_next; - continue; - } - /* found it; pml moves forward, prevpml stays */ - /* privileged port check */ - if (!check_privileged_port(svc_getcaller(xprt), - rqstp->rq_proc, - reg.pm_prog, - pml->pml_map.pm_port)) { - ans = 0; - break; - } - ans = 1; - t = (caddr_t)pml; - pml = pml->pml_next; - if (prevpml == NULL) - pmaplist = pml; - else - prevpml->pml_next = pml; - free(t); - } - if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) && - debugging) { - (void) fprintf(stderr, "svc_sendreply\n"); - abort(); - } - } - break; - - case PMAPPROC_GETPORT: - /* - * Lookup the mapping for a program,version and return its port - */ - if (!svc_getargs(xprt, xdr_pmap, (caddr_t)®)) - svcerr_decode(xprt); - else { - /* remote host authorization check */ - if (!check_default(svc_getcaller(xprt), - rqstp->rq_proc, - reg.pm_prog)) { - ans = 0; - goto done; - } - fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot); - if (fnd) - port = fnd->pml_map.pm_port; - else - port = 0; - if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) && - debugging) { - (void) fprintf(stderr, "svc_sendreply\n"); - abort(); - } - } - break; - - case PMAPPROC_DUMP: - /* - * Return the current set of mapped program,version - */ - if (!svc_getargs(xprt, xdr_void, NULL)) - svcerr_decode(xprt); - else { - /* remote host authorization check */ - struct pmaplist *p; - if (!check_default(svc_getcaller(xprt), - rqstp->rq_proc, (u_long) 0)) { - p = 0; /* send empty list */ - } else { - p = pmaplist; - } - if ((!svc_sendreply(xprt, xdr_pmaplist, - (caddr_t)&p)) && debugging) { - (void) fprintf(stderr, "svc_sendreply\n"); - abort(); - } - } - break; - - case PMAPPROC_CALLIT: - /* - * Calls a procedure on the local machine. If the requested - * procedure is not registered this procedure does not return - * error information!! - * This procedure is only supported on rpc/udp and calls via - * rpc/udp. It passes null authentication parameters. - */ - callit(rqstp, xprt); - break; - - default: - /* remote host authorization check */ - check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0); - svcerr_noproc(xprt); - break; - } -} - - -/* - * Stuff for the rmtcall service - */ -#define ARGSIZE 9000 - -struct encap_parms { - u_int arglen; - char *args; -}; - -static bool_t -xdr_encap_parms(xdrs, epp) - XDR *xdrs; - struct encap_parms *epp; -{ - - return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE)); -} - -struct rmtcallargs { - u_long rmt_prog; - u_long rmt_vers; - u_long rmt_port; - u_long rmt_proc; - struct encap_parms rmt_args; -}; - -static bool_t -xdr_rmtcall_args(xdrs, cap) - XDR *xdrs; - struct rmtcallargs *cap; -{ - - /* does not get a port number */ - if (xdr_u_long(xdrs, &(cap->rmt_prog)) && - xdr_u_long(xdrs, &(cap->rmt_vers)) && - xdr_u_long(xdrs, &(cap->rmt_proc))) { - return (xdr_encap_parms(xdrs, &(cap->rmt_args))); - } - return (FALSE); -} - -static bool_t -xdr_rmtcall_result(xdrs, cap) - XDR *xdrs; - struct rmtcallargs *cap; -{ - if (xdr_u_long(xdrs, &(cap->rmt_port))) - return (xdr_encap_parms(xdrs, &(cap->rmt_args))); - return (FALSE); -} - -/* - * only worries about the struct encap_parms part of struct rmtcallargs. - * The arglen must already be set!! - */ -static bool_t -xdr_opaque_parms(xdrs, cap) - XDR *xdrs; - struct rmtcallargs *cap; -{ - return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); -} - -/* - * This routine finds and sets the length of incoming opaque paraters - * and then calls xdr_opaque_parms. - */ -static bool_t -xdr_len_opaque_parms(xdrs, cap) - XDR *xdrs; - struct rmtcallargs *cap; -{ - register u_int beginpos, lowpos, highpos, currpos, pos; - - beginpos = lowpos = pos = xdr_getpos(xdrs); - highpos = lowpos + ARGSIZE; - while ((int)(highpos - lowpos) >= 0) { - currpos = (lowpos + highpos) / 2; - if (xdr_setpos(xdrs, currpos)) { - pos = currpos; - lowpos = currpos + 1; - } else { - highpos = currpos - 1; - } - } - xdr_setpos(xdrs, beginpos); - cap->rmt_args.arglen = pos - beginpos; - return (xdr_opaque_parms(xdrs, cap)); -} - -/* - * Call a remote procedure service - * This procedure is very quiet when things go wrong. - * The proc is written to support broadcast rpc. In the broadcast case, - * a machine should shut-up instead of complain, less the requestor be - * overrun with complaints at the expense of not hearing a valid reply ... - * - * This now forks so that the program & process that it calls can call - * back to the portmapper. - */ -static void -callit(rqstp, xprt) - struct svc_req *rqstp; - SVCXPRT *xprt; -{ - struct rmtcallargs a; - struct pmaplist *pml; - u_short port; - struct sockaddr_in me; - int pid, so = -1; - CLIENT *client; - struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred; - struct timeval timeout; - char buf[ARGSIZE]; - - timeout.tv_sec = 5; - timeout.tv_usec = 0; - a.rmt_args.args = buf; - if (!svc_getargs(xprt, xdr_rmtcall_args, (caddr_t)&a)) - return; - /* host and service access control */ - if (!check_callit(svc_getcaller(xprt), - rqstp->rq_proc, a.rmt_prog, a.rmt_proc)) - return; - if ((pml = find_service(a.rmt_prog, a.rmt_vers, - (u_long)IPPROTO_UDP)) == NULL) - return; - /* - * fork a child to do the work. Parent immediately returns. - * Child exits upon completion. - */ - if ((pid = fork()) != 0) { - if (pid < 0) - syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m", - a.rmt_prog); - return; - } - port = pml->pml_map.pm_port; - get_myaddress(&me); - me.sin_port = htons(port); - client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so); - if (client != (CLIENT *)NULL) { - if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) { - client->cl_auth = authunix_create(au->aup_machname, - au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids); - } - a.rmt_port = (u_long)port; - if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a, - xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) { - svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a); - } - AUTH_DESTROY(client->cl_auth); - clnt_destroy(client); - } - (void)close(so); - exit(0); -} - -static void -reap(sig) - int sig; -{ - int save_errno; - - save_errno = errno; - while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0); - errno = save_errno; -} diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile index 19a6565..ba6b3c7 100644 --- a/usr.sbin/rpc.lockd/Makefile +++ b/usr.sbin/rpc.lockd/Makefile @@ -1,15 +1,17 @@ -# $FreeBSD$ +# $FreeBSD$ +# $NetBSD: Makefile,v 1.12 2000/08/07 16:23:31 thorpej Exp $ -PROG = rpc.lockd -SRCS = nlm_prot_svc.c nlm_prot.h lockd.c procs.c -MAN8 = rpc.lockd.8 +PROG= rpc.lockd +SRCS= nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c +MAN8= rpc.lockd.8 +MLINKS= rpc.lockd.8 lockd.8 -DPADD= ${LIBRPCSVC} -LDADD= -lrpcsvc +CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc -CFLAGS+= -I. +DPADD= ${LIBRPCSVC} ${LIBUTIL} +LDADD= -lrpcsvc -lutil -CLEANFILES= nlm_prot_svc.c nlm_prot.h +CLEANFILES= nlm_prot_svc.c nlm_prot.h test RPCSRC= ${DESTDIR}/usr/include/rpcsvc/nlm_prot.x RPCGEN= rpcgen -L -C @@ -20,7 +22,7 @@ nlm_prot_svc.c: ${RPCSRC} nlm_prot.h: ${RPCSRC} ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} -test: test.c - cc -o test test.c -lrpcsvc +test: ${.CURDIR}/test.c + cc -o test ${.CURDIR}/test.c -lrpcsvc .include <bsd.prog.mk> diff --git a/usr.sbin/rpc.lockd/lock_proc.c b/usr.sbin/rpc.lockd/lock_proc.c new file mode 100644 index 0000000..8b08fd0 --- /dev/null +++ b/usr.sbin/rpc.lockd/lock_proc.c @@ -0,0 +1,1294 @@ +/* $NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $ */ +/* $FreeBSD$ */ +/* + * Copyright (c) 1995 + * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce 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 for the FreeBSD project + * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $"); +#endif + +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <netdb.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <netconfig.h> + +#include <rpc/rpc.h> +#include <rpcsvc/sm_inter.h> + +#include "lockd.h" +#include <rpcsvc/nlm_prot.h> +#include "lockd_lock.h" + + +#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */ +#define CLIENT_CACHE_LIFETIME 120 /* In seconds */ + +static void log_from_addr __P((char *, struct svc_req *)); +static int addrcmp __P((struct sockaddr *, struct sockaddr *)); + +/* log_from_addr ----------------------------------------------------------- */ +/* + * Purpose: Log name of function called and source address + * Returns: Nothing + * Notes: Extracts the source address from the transport handle + * passed in as part of the called procedure specification + */ +static void +log_from_addr(fun_name, req) + char *fun_name; + struct svc_req *req; +{ + struct sockaddr *addr; + char hostname_buf[NI_MAXHOST]; + + addr = svc_getrpccaller(req->rq_xprt)->buf; + if (getnameinfo(addr , addr->sa_len, hostname_buf, sizeof hostname_buf, + NULL, 0, 0) != 0) + return; + + syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf); +} + +/* get_client -------------------------------------------------------------- */ +/* + * Purpose: Get a CLIENT* for making RPC calls to lockd on given host + * Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error + * Notes: Creating a CLIENT* is quite expensive, involving a + * conversation with the remote portmapper to get the + * port number. Since a given client is quite likely + * to make several locking requests in succession, it is + * desirable to cache the created CLIENT*. + * + * Since we are using UDP rather than TCP, there is no cost + * to the remote system in keeping these cached indefinitely. + * Unfortunately there is a snag: if the remote system + * reboots, the cached portmapper results will be invalid, + * and we will never detect this since all of the xxx_msg() + * calls return no result - we just fire off a udp packet + * and hope for the best. + * + * We solve this by discarding cached values after two + * minutes, regardless of whether they have been used + * in the meanwhile (since a bad one might have been used + * plenty of times, as the host keeps retrying the request + * and we keep sending the reply back to the wrong port). + * + * Given that the entries will always expire in the order + * that they were created, there is no point in a LRU + * algorithm for when the cache gets full - entries are + * always re-used in sequence. + */ +static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE]; +static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */ +static struct sockaddr_storage clnt_cache_addr[CLIENT_CACHE_SIZE]; +static int clnt_cache_next_to_use = 0; + +static int +addrcmp(sa1, sa2) + struct sockaddr *sa1; + struct sockaddr *sa2; +{ + int len; + void *p1, *p2; + + if (sa1->sa_family != sa2->sa_family) + return -1; + + switch (sa1->sa_family) { + case AF_INET: + p1 = &((struct sockaddr_in *)sa1)->sin_addr; + p2 = &((struct sockaddr_in *)sa2)->sin_addr; + len = 4; + break; + case AF_INET6: + p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; + p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; + len = 16; + break; + default: + return -1; + } + + return memcmp(p1, p2, len); +} + +CLIENT * +get_client(host_addr, vers) + struct sockaddr *host_addr; + rpcvers_t vers; +{ + CLIENT *client; + struct timeval retry_time, time_now; + int i; + char *netid; + struct netconfig *nconf; + char host[NI_MAXHOST]; + + gettimeofday(&time_now, NULL); + + /* + * Search for the given client in the cache, zapping any expired + * entries that we happen to notice in passing. + */ + for (i = 0; i < CLIENT_CACHE_SIZE; i++) { + client = clnt_cache_ptr[i]; + if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME) + < time_now.tv_sec)) { + /* Cache entry has expired. */ + if (debug_level > 3) + syslog(LOG_DEBUG, "Expired CLIENT* in cache"); + clnt_cache_time[i] = 0L; + clnt_destroy(client); + clnt_cache_ptr[i] = NULL; + client = NULL; + } + if (client && !addrcmp((struct sockaddr *)&clnt_cache_addr[i], + host_addr)) { + /* Found it! */ + if (debug_level > 3) + syslog(LOG_DEBUG, "Found CLIENT* in cache"); + return (client); + } + } + + /* Not found in cache. Free the next entry if it is in use. */ + if (clnt_cache_ptr[clnt_cache_next_to_use]) { + clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]); + clnt_cache_ptr[clnt_cache_next_to_use] = NULL; + } + + /* + * Need a host string for clnt_tp_create. Use NI_NUMERICHOST + * to avoid DNS lookups. + */ + if (getnameinfo(host_addr, host_addr->sa_len, host, sizeof host, + NULL, 0, NI_NUMERICHOST) != 0) { + syslog(LOG_ERR, "unable to get name string for caller"); + return NULL; + } + +#if 1 + if (host_addr->sa_family == AF_INET6) + netid = "udp6"; + else + netid = "udp"; +#else + if (host_addr->sa_family == AF_INET6) + netid = "tcp6"; + else + netid = "tcp"; +#endif + nconf = getnetconfigent(netid); + if (nconf == NULL) { + syslog(LOG_ERR, "could not get netconfig info for '%s': " + "no /etc/netconfig file?", netid); + return NULL; + } + + client = clnt_tp_create(host, NLM_PROG, vers, nconf); + freenetconfigent(nconf); + + if (!client) { + syslog(LOG_ERR, "%s", clnt_spcreateerror("clntudp_create")); + syslog(LOG_ERR, "Unable to return result to %s", host); + return NULL; + } + + /* Success - update the cache entry */ + clnt_cache_ptr[clnt_cache_next_to_use] = client; + memcpy(&clnt_cache_addr[clnt_cache_next_to_use], host_addr, + host_addr->sa_len); + clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec; + if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE) + clnt_cache_next_to_use = 0; + + /* + * Disable the default timeout, so we can specify our own in calls + * to clnt_call(). (Note that the timeout is a different concept + * from the retry period set in clnt_udp_create() above.) + */ + retry_time.tv_sec = -1; + retry_time.tv_usec = -1; + clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time); + + if (debug_level > 3) + syslog(LOG_DEBUG, "Created CLIENT* for %s", host); + return client; +} + + +/* transmit_result --------------------------------------------------------- */ +/* + * Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs + * Returns: Nothing - we have no idea if the datagram got there + * Notes: clnt_call() will always fail (with timeout) as we are + * calling it with timeout 0 as a hack to just issue a datagram + * without expecting a result + */ +void +transmit_result(opcode, result, addr) + int opcode; + nlm_res *result; + struct sockaddr *addr; +{ + static char dummy; + CLIENT *cli; + struct timeval timeo; + int success; + + if ((cli = get_client(addr, NLM_VERS)) != NULL) { + timeo.tv_sec = 0; /* No timeout - not expecting response */ + timeo.tv_usec = 0; + + success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void, + &dummy, timeo); + + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d(%s)", + success, clnt_sperrno(success)); + } +} +/* transmit4_result --------------------------------------------------------- */ +/* + * Purpose: Transmit result for nlm4_xxx_msg pseudo-RPCs + * Returns: Nothing - we have no idea if the datagram got there + * Notes: clnt_call() will always fail (with timeout) as we are + * calling it with timeout 0 as a hack to just issue a datagram + * without expecting a result + */ +void +transmit4_result(opcode, result, addr) + int opcode; + nlm4_res *result; + struct sockaddr *addr; +{ + static char dummy; + CLIENT *cli; + struct timeval timeo; + int success; + + if ((cli = get_client(addr, NLM_VERS4)) != NULL) { + timeo.tv_sec = 0; /* No timeout - not expecting response */ + timeo.tv_usec = 0; + + success = clnt_call(cli, opcode, xdr_nlm4_res, result, xdr_void, + &dummy, timeo); + + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d(%s)", + success, clnt_sperrno(success)); + } +} + +/* + * converts a struct nlm_lock to struct nlm4_lock + */ +static void nlmtonlm4 __P((struct nlm_lock *, struct nlm4_lock *)); +static void +nlmtonlm4(arg, arg4) + struct nlm_lock *arg; + struct nlm4_lock *arg4; +{ + memcpy(arg4, arg, sizeof(nlm_lock)); + arg4->l_offset = arg->l_offset; + arg4->l_len = arg->l_len; +} +/* ------------------------------------------------------------------------- */ +/* + * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd + * involved to ensure reclaim of locks after a crash of the "stateless" + * server. + * + * These all come in two flavours - nlm_xxx() and nlm_xxx_msg(). + * The first are standard RPCs with argument and result. + * The nlm_xxx_msg() calls implement exactly the same functions, but + * use two pseudo-RPCs (one in each direction). These calls are NOT + * standard use of the RPC protocol in that they do not return a result + * at all (NB. this is quite different from returning a void result). + * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged + * datagrams, requiring higher-level code to perform retries. + * + * Despite the disadvantages of the nlm_xxx_msg() approach (some of which + * are documented in the comments to get_client() above), this is the + * interface used by all current commercial NFS implementations + * [Solaris, SCO, AIX etc.]. This is presumed to be because these allow + * implementations to continue using the standard RPC libraries, while + * avoiding the block-until-result nature of the library interface. + * + * No client implementations have been identified so far that make use + * of the true RPC version (early SunOS releases would be a likely candidate + * for testing). + */ + +/* nlm_test ---------------------------------------------------------------- */ +/* + * Purpose: Test whether a specified lock would be granted if requested + * Returns: nlm_granted (or error code) + * Notes: + */ +nlm_testres * +nlm_test_1_svc(arg, rqstp) + nlm_testargs *arg; + struct svc_req *rqstp; +{ + static nlm_testres res; + struct nlm4_lock arg4; + struct nlm4_holder *holder; + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_test", rqstp); + + holder = testlock(&arg4, 0); + /* + * Copy the cookie from the argument into the result. Note that this + * is slightly hazardous, as the structure contains a pointer to a + * malloc()ed buffer that will get freed by the caller. However, the + * main function transmits the result before freeing the argument + * so it is in fact safe. + */ + res.cookie = arg->cookie; + if (holder == NULL) { + res.stat.stat = nlm_granted; + } else { + res.stat.stat = nlm_denied; + memcpy(&res.stat.nlm_testrply_u.holder, holder, + sizeof(struct nlm_holder)); + res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset; + res.stat.nlm_testrply_u.holder.l_len = holder->l_len; + } + return (&res); +} + +void * +nlm_test_msg_1_svc(arg, rqstp) + nlm_testargs *arg; + struct svc_req *rqstp; +{ + nlm_testres res; + static char dummy; + struct sockaddr *addr; + CLIENT *cli; + int success; + struct timeval timeo; + struct nlm4_lock arg4; + struct nlm4_holder *holder; + + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_test_msg", rqstp); + + holder = testlock(&arg4, 0); + + res.cookie = arg->cookie; + if (holder == NULL) { + res.stat.stat = nlm_granted; + } else { + res.stat.stat = nlm_denied; + memcpy(&res.stat.nlm_testrply_u.holder, holder, + sizeof(struct nlm_holder)); + res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset; + res.stat.nlm_testrply_u.holder.l_len = holder->l_len; + } + + /* + * nlm_test has different result type to the other operations, so + * can't use transmit_result() in this case + */ + addr = svc_getrpccaller(rqstp->rq_xprt)->buf; + if ((cli = get_client(addr, NLM_VERS)) != NULL) { + timeo.tv_sec = 0; /* No timeout - not expecting response */ + timeo.tv_usec = 0; + + success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres, + &res, xdr_void, &dummy, timeo); + + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d", success); + } + return (NULL); +} + +/* nlm_lock ---------------------------------------------------------------- */ +/* + * Purposes: Establish a lock + * Returns: granted, denied or blocked + * Notes: *** grace period support missing + */ +nlm_res * +nlm_lock_1_svc(arg, rqstp) + nlm_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lockargs arg4; + nlmtonlm4(&arg->alock, &arg4.alock); + arg4.cookie = arg->cookie; + arg4.block = arg->block; + arg4.exclusive = arg->exclusive; + arg4.reclaim = arg->reclaim; + arg4.state = arg->state; + + if (debug_level) + log_from_addr("nlm_lock", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + + res.stat.stat = getlock(&arg4, rqstp, LOCK_MON); + return (&res); +} + +void * +nlm_lock_msg_1_svc(arg, rqstp) + nlm_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lockargs arg4; + + nlmtonlm4(&arg->alock, &arg4.alock); + arg4.cookie = arg->cookie; + arg4.block = arg->block; + arg4.exclusive = arg->exclusive; + arg4.reclaim = arg->reclaim; + arg4.state = arg->state; + + if (debug_level) + log_from_addr("nlm_lock_msg", rqstp); + + res.cookie = arg->cookie; + res.stat.stat = getlock(&arg4, rqstp, LOCK_ASYNC | LOCK_MON); + transmit_result(NLM_LOCK_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + + return (NULL); +} + +/* nlm_cancel -------------------------------------------------------------- */ +/* + * Purpose: Cancel a blocked lock request + * Returns: granted or denied + * Notes: + */ +nlm_res * +nlm_cancel_1_svc(arg, rqstp) + nlm_cancargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lock arg4; + + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_cancel", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + + /* + * Since at present we never return 'nlm_blocked', there can never be + * a lock to cancel, so this call always fails. + */ + res.stat.stat = unlock(&arg4, LOCK_CANCEL); + return (&res); +} + +void * +nlm_cancel_msg_1_svc(arg, rqstp) + nlm_cancargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lock arg4; + + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_cancel_msg", rqstp); + + res.cookie = arg->cookie; + /* + * Since at present we never return 'nlm_blocked', there can never be + * a lock to cancel, so this call always fails. + */ + res.stat.stat = unlock(&arg4, LOCK_CANCEL); + transmit_result(NLM_CANCEL_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + return (NULL); +} + +/* nlm_unlock -------------------------------------------------------------- */ +/* + * Purpose: Release an existing lock + * Returns: Always granted, unless during grace period + * Notes: "no such lock" error condition is ignored, as the + * protocol uses unreliable UDP datagrams, and may well + * re-try an unlock that has already succeeded. + */ +nlm_res * +nlm_unlock_1_svc(arg, rqstp) + nlm_unlockargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lock arg4; + + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_unlock", rqstp); + + res.stat.stat = unlock(&arg4, 0); + res.cookie = arg->cookie; + + return (&res); +} + +void * +nlm_unlock_msg_1_svc(arg, rqstp) + nlm_unlockargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + struct nlm4_lock arg4; + + nlmtonlm4(&arg->alock, &arg4); + + if (debug_level) + log_from_addr("nlm_unlock_msg", rqstp); + + res.stat.stat = unlock(&arg4, 0); + res.cookie = arg->cookie; + + transmit_result(NLM_UNLOCK_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + return (NULL); +} + +/* ------------------------------------------------------------------------- */ +/* + * Client-side pseudo-RPCs for results. Note that for the client there + * are only nlm_xxx_msg() versions of each call, since the 'real RPC' + * version returns the results in the RPC result, and so the client + * does not normally receive incoming RPCs. + * + * The exception to this is nlm_granted(), which is genuinely an RPC + * call from the server to the client - a 'call-back' in normal procedure + * call terms. + */ + +/* nlm_granted ------------------------------------------------------------- */ +/* + * Purpose: Receive notification that formerly blocked lock now granted + * Returns: always success ('granted') + * Notes: + */ +nlm_res * +nlm_granted_1_svc(arg, rqstp) + nlm_testargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + + if (debug_level) + log_from_addr("nlm_granted", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + + res.stat.stat = nlm_granted; + return (&res); +} + +void * +nlm_granted_msg_1_svc(arg, rqstp) + nlm_testargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + + if (debug_level) + log_from_addr("nlm_granted_msg", rqstp); + + res.cookie = arg->cookie; + res.stat.stat = nlm_granted; + transmit_result(NLM_GRANTED_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + return (NULL); +} + +/* nlm_test_res ------------------------------------------------------------ */ +/* + * Purpose: Accept result from earlier nlm_test_msg() call + * Returns: Nothing + */ +void * +nlm_test_res_1_svc(arg, rqstp) + nlm_testres *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm_test_res", rqstp); + return (NULL); +} + +/* nlm_lock_res ------------------------------------------------------------ */ +/* + * Purpose: Accept result from earlier nlm_lock_msg() call + * Returns: Nothing + */ +void * +nlm_lock_res_1_svc(arg, rqstp) + nlm_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm_lock_res", rqstp); + + return (NULL); +} + +/* nlm_cancel_res ---------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_cancel_msg() call + * Returns: Nothing + */ +void * +nlm_cancel_res_1_svc(arg, rqstp) + nlm_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm_cancel_res", rqstp); + return (NULL); +} + +/* nlm_unlock_res ---------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_unlock_msg() call + * Returns: Nothing + */ +void * +nlm_unlock_res_1_svc(arg, rqstp) + nlm_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm_unlock_res", rqstp); + return (NULL); +} + +/* nlm_granted_res --------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_granted_msg() call + * Returns: Nothing + */ +void * +nlm_granted_res_1_svc(arg, rqstp) + nlm_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm_granted_res", rqstp); + return (NULL); +} + +/* ------------------------------------------------------------------------- */ +/* + * Calls for PCNFS locking (aka non-monitored locking, no involvement + * of rpc.statd). + * + * These are all genuine RPCs - no nlm_xxx_msg() nonsense here. + */ + +/* nlm_share --------------------------------------------------------------- */ +/* + * Purpose: Establish a DOS-style lock + * Returns: success or failure + * Notes: Blocking locks are not supported - client is expected + * to retry if required. + */ +nlm_shareres * +nlm_share_3_svc(arg, rqstp) + nlm_shareargs *arg; + struct svc_req *rqstp; +{ + static nlm_shareres res; + + if (debug_level) + log_from_addr("nlm_share", rqstp); + + res.cookie = arg->cookie; + res.stat = nlm_granted; + res.sequence = 1234356; /* X/Open says this field is ignored? */ + return (&res); +} + +/* nlm_unshare ------------------------------------------------------------ */ +/* + * Purpose: Release a DOS-style lock + * Returns: nlm_granted, unless in grace period + * Notes: + */ +nlm_shareres * +nlm_unshare_3_svc(arg, rqstp) + nlm_shareargs *arg; + struct svc_req *rqstp; +{ + static nlm_shareres res; + + if (debug_level) + log_from_addr("nlm_unshare", rqstp); + + res.cookie = arg->cookie; + res.stat = nlm_granted; + res.sequence = 1234356; /* X/Open says this field is ignored? */ + return (&res); +} + +/* nlm_nm_lock ------------------------------------------------------------ */ +/* + * Purpose: non-monitored version of nlm_lock() + * Returns: as for nlm_lock() + * Notes: These locks are in the same style as the standard nlm_lock, + * but the rpc.statd should not be called to establish a + * monitor for the client machine, since that machine is + * declared not to be running a rpc.statd, and so would not + * respond to the statd protocol. + */ +nlm_res * +nlm_nm_lock_3_svc(arg, rqstp) + nlm_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm_res res; + + if (debug_level) + log_from_addr("nlm_nm_lock", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + res.stat.stat = nlm_granted; + return (&res); +} + +/* nlm_free_all ------------------------------------------------------------ */ +/* + * Purpose: Release all locks held by a named client + * Returns: Nothing + * Notes: Potential denial of service security problem here - the + * locks to be released are specified by a host name, independent + * of the address from which the request has arrived. + * Should probably be rejected if the named host has been + * using monitored locks. + */ +void * +nlm_free_all_3_svc(arg, rqstp) + nlm_notify *arg; + struct svc_req *rqstp; +{ + static char dummy; + + if (debug_level) + log_from_addr("nlm_free_all", rqstp); + return (&dummy); +} + +/* calls for nlm version 4 (NFSv3) */ +/* nlm_test ---------------------------------------------------------------- */ +/* + * Purpose: Test whether a specified lock would be granted if requested + * Returns: nlm_granted (or error code) + * Notes: + */ +nlm4_testres * +nlm4_test_4_svc(arg, rqstp) + nlm4_testargs *arg; + struct svc_req *rqstp; +{ + static nlm4_testres res; + struct nlm4_holder *holder; + + if (debug_level) + log_from_addr("nlm4_test", rqstp); + + holder = testlock(&arg->alock, LOCK_V4); + + /* + * Copy the cookie from the argument into the result. Note that this + * is slightly hazardous, as the structure contains a pointer to a + * malloc()ed buffer that will get freed by the caller. However, the + * main function transmits the result before freeing the argument + * so it is in fact safe. + */ + res.cookie = arg->cookie; + if (holder == NULL) { + res.stat.stat = nlm4_granted; + } else { + res.stat.stat = nlm4_denied; + memcpy(&res.stat.nlm4_testrply_u.holder, holder, + sizeof(struct nlm4_holder)); + } + return (&res); +} + +void * +nlm4_test_msg_4_svc(arg, rqstp) + nlm4_testargs *arg; + struct svc_req *rqstp; +{ + nlm4_testres res; + static char dummy; + struct sockaddr *addr; + CLIENT *cli; + int success; + struct timeval timeo; + struct nlm4_holder *holder; + + if (debug_level) + log_from_addr("nlm4_test_msg", rqstp); + + holder = testlock(&arg->alock, LOCK_V4); + + res.cookie = arg->cookie; + if (holder == NULL) { + res.stat.stat = nlm4_granted; + } else { + res.stat.stat = nlm4_denied; + memcpy(&res.stat.nlm4_testrply_u.holder, holder, + sizeof(struct nlm4_holder)); + } + + /* + * nlm_test has different result type to the other operations, so + * can't use transmit4_result() in this case + */ + addr = svc_getrpccaller(rqstp->rq_xprt)->buf; + if ((cli = get_client(addr, NLM_VERS4)) != NULL) { + timeo.tv_sec = 0; /* No timeout - not expecting response */ + timeo.tv_usec = 0; + + success = clnt_call(cli, NLM4_TEST_RES, xdr_nlm4_testres, + &res, xdr_void, &dummy, timeo); + + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d", success); + } + return (NULL); +} + +/* nlm_lock ---------------------------------------------------------------- */ +/* + * Purposes: Establish a lock + * Returns: granted, denied or blocked + * Notes: *** grace period support missing + */ +nlm4_res * +nlm4_lock_4_svc(arg, rqstp) + nlm4_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_lock", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_4() */ + res.cookie = arg->cookie; + + res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_V4); + return (&res); +} + +void * +nlm4_lock_msg_4_svc(arg, rqstp) + nlm4_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_lock_msg", rqstp); + + res.cookie = arg->cookie; + res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_ASYNC | LOCK_V4); + transmit4_result(NLM4_LOCK_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + + return (NULL); +} + +/* nlm_cancel -------------------------------------------------------------- */ +/* + * Purpose: Cancel a blocked lock request + * Returns: granted or denied + * Notes: + */ +nlm4_res * +nlm4_cancel_4_svc(arg, rqstp) + nlm4_cancargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_cancel", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + + /* + * Since at present we never return 'nlm_blocked', there can never be + * a lock to cancel, so this call always fails. + */ + res.stat.stat = unlock(&arg->alock, LOCK_CANCEL); + return (&res); +} + +void * +nlm4_cancel_msg_4_svc(arg, rqstp) + nlm4_cancargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_cancel_msg", rqstp); + + res.cookie = arg->cookie; + /* + * Since at present we never return 'nlm_blocked', there can never be + * a lock to cancel, so this call always fails. + */ + res.stat.stat = unlock(&arg->alock, LOCK_CANCEL | LOCK_V4); + transmit4_result(NLM4_CANCEL_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + return (NULL); +} + +/* nlm_unlock -------------------------------------------------------------- */ +/* + * Purpose: Release an existing lock + * Returns: Always granted, unless during grace period + * Notes: "no such lock" error condition is ignored, as the + * protocol uses unreliable UDP datagrams, and may well + * re-try an unlock that has already succeeded. + */ +nlm4_res * +nlm4_unlock_4_svc(arg, rqstp) + nlm4_unlockargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_unlock", rqstp); + + res.stat.stat = unlock(&arg->alock, LOCK_V4); + res.cookie = arg->cookie; + + return (&res); +} + +void * +nlm4_unlock_msg_4_svc(arg, rqstp) + nlm4_unlockargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_unlock_msg", rqstp); + + res.stat.stat = unlock(&arg->alock, LOCK_V4); + res.cookie = arg->cookie; + + transmit4_result(NLM4_UNLOCK_RES, &res, + (struct sockaddr *)svc_getcaller(rqstp->rq_xprt)); + return (NULL); +} + +/* ------------------------------------------------------------------------- */ +/* + * Client-side pseudo-RPCs for results. Note that for the client there + * are only nlm_xxx_msg() versions of each call, since the 'real RPC' + * version returns the results in the RPC result, and so the client + * does not normally receive incoming RPCs. + * + * The exception to this is nlm_granted(), which is genuinely an RPC + * call from the server to the client - a 'call-back' in normal procedure + * call terms. + */ + +/* nlm_granted ------------------------------------------------------------- */ +/* + * Purpose: Receive notification that formerly blocked lock now granted + * Returns: always success ('granted') + * Notes: + */ +nlm4_res * +nlm4_granted_4_svc(arg, rqstp) + nlm4_testargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_granted", rqstp); + + /* copy cookie from arg to result. See comment in nlm_test_1() */ + res.cookie = arg->cookie; + + res.stat.stat = nlm4_granted; + return (&res); +} + +void * +nlm4_granted_msg_4_svc(arg, rqstp) + nlm4_testargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_granted_msg", rqstp); + + res.cookie = arg->cookie; + res.stat.stat = nlm4_granted; + transmit4_result(NLM4_GRANTED_RES, &res, + (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf); + return (NULL); +} + +/* nlm_test_res ------------------------------------------------------------ */ +/* + * Purpose: Accept result from earlier nlm_test_msg() call + * Returns: Nothing + */ +void * +nlm4_test_res_4_svc(arg, rqstp) + nlm4_testres *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm4_test_res", rqstp); + return (NULL); +} + +/* nlm_lock_res ------------------------------------------------------------ */ +/* + * Purpose: Accept result from earlier nlm_lock_msg() call + * Returns: Nothing + */ +void * +nlm4_lock_res_4_svc(arg, rqstp) + nlm4_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm4_lock_res", rqstp); + + return (NULL); +} + +/* nlm_cancel_res ---------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_cancel_msg() call + * Returns: Nothing + */ +void * +nlm4_cancel_res_4_svc(arg, rqstp) + nlm4_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm4_cancel_res", rqstp); + return (NULL); +} + +/* nlm_unlock_res ---------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_unlock_msg() call + * Returns: Nothing + */ +void * +nlm4_unlock_res_4_svc(arg, rqstp) + nlm4_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm4_unlock_res", rqstp); + return (NULL); +} + +/* nlm_granted_res --------------------------------------------------------- */ +/* + * Purpose: Accept result from earlier nlm_granted_msg() call + * Returns: Nothing + */ +void * +nlm4_granted_res_4_svc(arg, rqstp) + nlm4_res *arg; + struct svc_req *rqstp; +{ + if (debug_level) + log_from_addr("nlm4_granted_res", rqstp); + return (NULL); +} + +/* ------------------------------------------------------------------------- */ +/* + * Calls for PCNFS locking (aka non-monitored locking, no involvement + * of rpc.statd). + * + * These are all genuine RPCs - no nlm_xxx_msg() nonsense here. + */ + +/* nlm_share --------------------------------------------------------------- */ +/* + * Purpose: Establish a DOS-style lock + * Returns: success or failure + * Notes: Blocking locks are not supported - client is expected + * to retry if required. + */ +nlm4_shareres * +nlm4_share_4_svc(arg, rqstp) + nlm4_shareargs *arg; + struct svc_req *rqstp; +{ + static nlm4_shareres res; + + if (debug_level) + log_from_addr("nlm4_share", rqstp); + + res.cookie = arg->cookie; + res.stat = nlm4_granted; + res.sequence = 1234356; /* X/Open says this field is ignored? */ + return (&res); +} + +/* nlm4_unshare ------------------------------------------------------------ */ +/* + * Purpose: Release a DOS-style lock + * Returns: nlm_granted, unless in grace period + * Notes: + */ +nlm4_shareres * +nlm4_unshare_4_svc(arg, rqstp) + nlm4_shareargs *arg; + struct svc_req *rqstp; +{ + static nlm4_shareres res; + + if (debug_level) + log_from_addr("nlm_unshare", rqstp); + + res.cookie = arg->cookie; + res.stat = nlm4_granted; + res.sequence = 1234356; /* X/Open says this field is ignored? */ + return (&res); +} + +/* nlm4_nm_lock ------------------------------------------------------------ */ +/* + * Purpose: non-monitored version of nlm4_lock() + * Returns: as for nlm4_lock() + * Notes: These locks are in the same style as the standard nlm4_lock, + * but the rpc.statd should not be called to establish a + * monitor for the client machine, since that machine is + * declared not to be running a rpc.statd, and so would not + * respond to the statd protocol. + */ +nlm4_res * +nlm4_nm_lock_4_svc(arg, rqstp) + nlm4_lockargs *arg; + struct svc_req *rqstp; +{ + static nlm4_res res; + + if (debug_level) + log_from_addr("nlm4_nm_lock", rqstp); + + /* copy cookie from arg to result. See comment in nlm4_test_1() */ + res.cookie = arg->cookie; + res.stat.stat = nlm4_granted; + return (&res); +} + +/* nlm4_free_all ------------------------------------------------------------ */ +/* + * Purpose: Release all locks held by a named client + * Returns: Nothing + * Notes: Potential denial of service security problem here - the + * locks to be released are specified by a host name, independent + * of the address from which the request has arrived. + * Should probably be rejected if the named host has been + * using monitored locks. + */ +void * +nlm4_free_all_4_svc(arg, rqstp) + nlm_notify *arg; + struct svc_req *rqstp; +{ + static char dummy; + + if (debug_level) + log_from_addr("nlm4_free_all", rqstp); + return (&dummy); +} + +/* nlm_sm_notify --------------------------------------------------------- */ +/* + * Purpose: called by rpc.statd when a monitored host state changes. + * Returns: Nothing + */ +void * +nlm_sm_notify_0_svc(arg, rqstp) + struct nlm_sm_status *arg; + struct svc_req *rqstp; +{ + static char dummy; + notify(arg->mon_name, arg->state); + return (&dummy); +} diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c index ac32856..e0ac5cf 100644 --- a/usr.sbin/rpc.lockd/lockd.c +++ b/usr.sbin/rpc.lockd/lockd.c @@ -1,3 +1,6 @@ +/* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */ +/* $FreeBSD$ */ + /* * Copyright (c) 1995 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. @@ -31,77 +34,193 @@ * */ +#include <sys/cdefs.h> #ifndef lint -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ +__RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $"); +#endif + +/* + * main() function for NFS lock daemon. Most of the code in this + * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x. + * + * The actual program logic is in the file lock_proc.c + */ -/* main() function for NFS lock daemon. Most of the code in this */ -/* file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x */ -/* The actual program logic is in the file procs.c */ +#include <sys/types.h> +#include <sys/socket.h> #include <err.h> +#include <stdio.h> #include <stdlib.h> +#include <errno.h> +#include <syslog.h> +#include <signal.h> #include <string.h> +#include <unistd.h> +#include <libutil.h> +#include <netconfig.h> + #include <rpc/rpc.h> -#include <rpc/pmap_clnt.h> +#include <rpcsvc/sm_inter.h> + #include "lockd.h" +#include <rpcsvc/nlm_prot.h> + +int debug_level = 0; /* 0 = no debugging syslog() calls */ +int _rpcsvcdirty = 0; + +int grace_expired; -void nlm_prog_1 __P((struct svc_req *, SVCXPRT *)); -void nlm_prog_3 __P((struct svc_req *, SVCXPRT *)); -static void usage __P((void)); +int main __P((int, char **)); +void nlm_prog_0 __P((struct svc_req *, SVCXPRT *)); +void nlm_prog_1 __P((struct svc_req *, SVCXPRT *)); +void nlm_prog_3 __P((struct svc_req *, SVCXPRT *)); +void nlm_prog_4 __P((struct svc_req *, SVCXPRT *)); +void usage __P((void)); -int debug_level = 0; /* Zero means no debugging syslog() calls */ +void sigalarm_handler __P((int)); + +char *transports[] = { "udp", "tcp", "udp6", "tcp6" }; int -main(int argc, char **argv) +main(argc, argv) + int argc; + char **argv; +{ + SVCXPRT *transp; + int ch, i, maxindex, s; + struct sigaction sigchild, sigalarm; + int grace_period = 30; + struct netconfig *nconf; + + while ((ch = getopt(argc, argv, "d:g:")) != (-1)) { + switch (ch) { + case 'd': + debug_level = atoi(optarg); + if (!debug_level) { + usage(); + /* NOTREACHED */ + } + break; + case 'g': + grace_period = atoi(optarg); + if (!grace_period) { + usage(); + /* NOTREACHED */ + } + break; + default: + case '?': + usage(); + /* NOTREACHED */ + } + } + if (geteuid()) { /* This command allowed only to root */ + fprintf(stderr, "Sorry. You are not superuser\n"); + exit(1); + } + + (void)rpcb_unset(NLM_PROG, NLM_SM, NULL); + (void)rpcb_unset(NLM_PROG, NLM_VERS, NULL); + (void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL); + (void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL); + + /* + * Check if IPv6 support is present. + */ + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + maxindex = 2; + else { + close(s); + maxindex = 4; + } + + for (i = 0; i < maxindex; i++) { + nconf = getnetconfigent(transports[i]); + if (nconf == NULL) + errx(1, "cannot get udp netconf."); + + transp = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0); + if (transp == NULL) { + errx(1, "cannot create %s service.", transports[i]); + /* NOTREACHED */ + } + if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0, nconf)) { + errx(1, "unable to register (NLM_PROG, NLM_SM, %s)", + transports[i]); + /* NOTREACHED */ + } + if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1, nconf)) { + errx(1, "unable to register (NLM_PROG, NLM_VERS, %s)", + transports[i]); + /* NOTREACHED */ + } + if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, nconf)) { + errx(1, "unable to register (NLM_PROG, NLM_VERSX, %s)", + transports[i]); + /* NOTREACHED */ + } + if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4, nconf)) { + errx(1, "unable to register (NLM_PROG, NLM_VERS4, %s)", + transports[i]); + /* NOTREACHED */ + } + freenetconfigent(nconf); + } + + /* + * Note that it is NOT sensible to run this program from inetd - the + * protocol assumes that it will run immediately at boot time. + */ + if (daemon(0, 0)) { + err(1, "cannot fork"); + /* NOTREACHED */ + } + + openlog("rpc.lockd", 0, LOG_DAEMON); + if (debug_level) + syslog(LOG_INFO, "Starting, debug level %d", debug_level); + else + syslog(LOG_INFO, "Starting"); + + sigchild.sa_handler = sigchild_handler; + sigemptyset(&sigchild.sa_mask); + sigchild.sa_flags = SA_RESTART; + if (sigaction(SIGCHLD, &sigchild, NULL) != 0) { + syslog(LOG_WARNING, "sigaction(SIGCHLD) failed: %s", + strerror(errno)); + exit(1); + } + sigalarm.sa_handler = sigalarm_handler; + sigemptyset(&sigalarm.sa_mask); + sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */ + sigalarm.sa_flags |= SA_RESTART; + if (sigaction(SIGALRM, &sigalarm, NULL) != 0) { + syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s", + strerror(errno)); + exit(1); + } + grace_expired = 0; + if (alarm(10) < 0) { + syslog(LOG_WARNING, "alarm failed: %s", + strerror(errno)); + exit(1); + } + + svc_run(); /* Should never return */ + exit(1); +} + +void +sigalarm_handler(s) + int s; { - SVCXPRT *transp; - - if (argc > 1) - { - if (strncmp(argv[1], "-d", 2)) - usage(); - if (argc > 2) debug_level = atoi(argv[2]); - else debug_level = atoi(argv[1] + 2); - /* Ensure at least some debug if -d with no specified level */ - if (!debug_level) debug_level = 1; - } - - (void)pmap_unset(NLM_PROG, NLM_VERS); - (void)pmap_unset(NLM_PROG, NLM_VERSX); - - transp = svcudp_create(RPC_ANYSOCK); - if (transp == NULL) - errx(1, "cannot create udp service"); - if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP)) - errx(1, "unable to register (NLM_PROG, NLM_VERS, udp)"); - if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP)) - errx(1, "unable to register (NLM_PROG, NLM_VERSX, udp)"); - - transp = svctcp_create(RPC_ANYSOCK, 0, 0); - if (transp == NULL) - errx(1, "cannot create tcp service"); - if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP)) - errx(1, "unable to register (NLM_PROG, NLM_VERS, tcp)"); - if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP)) - errx(1, "unable to register (NLM_PROG, NLM_VERSX, tcp)"); - - /* Note that it is NOT sensible to run this program from inetd - the */ - /* protocol assumes that it will run immediately at boot time. */ - if (daemon(0,0)) - err(1, "fork"); - openlog("rpc.lockd", 0, LOG_DAEMON); - if (debug_level) syslog(LOG_INFO, "Starting, debug level %d", debug_level); - else syslog(LOG_INFO, "Starting"); - - svc_run(); /* Should never return */ - exit(1); + grace_expired = 1; } -static void +void usage() { - fprintf(stderr, "usage: rpc.lockd [-d [debuglevel]]\n"); - exit(1); + errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]"); } diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h index 39586bf..42f14f1 100644 --- a/usr.sbin/rpc.lockd/lockd.h +++ b/usr.sbin/rpc.lockd/lockd.h @@ -1,3 +1,6 @@ +/* $NetBSD: lockd.h,v 1.2 2000/06/07 14:34:40 bouyer Exp $ */ +/* $FreeBSD$ */ + /* * Copyright (c) 1995 * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved. @@ -31,14 +34,6 @@ * */ - - -#include <stdio.h> -#include <rpc/rpc.h> -#include <syslog.h> -#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */ -#include "nlm_prot.h" /* The protocol we are implementing */ - - -/* global variables ------------------------------------------------------- */ -extern int debug_level; +extern int debug_level; +extern int grace_expired; +void sigchild_handler __P((int)); diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c new file mode 100644 index 0000000..998a200 --- /dev/null +++ b/usr.sbin/rpc.lockd/lockd_lock.c @@ -0,0 +1,759 @@ +/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <syslog.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <rpc/rpc.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/wait.h> +#include <rpcsvc/sm_inter.h> +#include <rpcsvc/nlm_prot.h> +#include "lockd_lock.h" +#include "lockd.h" + +/* A set of utilities for managing file locking */ +LIST_HEAD(lcklst_head, file_lock); +struct lcklst_head lcklst_head = LIST_HEAD_INITIALIZER(lcklst_head); + +/* struct describing a lock */ +struct file_lock { + LIST_ENTRY(file_lock) lcklst; + fhandle_t filehandle; /* NFS filehandle */ + struct sockaddr *addr; + struct nlm4_holder client; /* lock holder */ + netobj client_cookie; /* cookie sent by the client */ + char client_name[128]; + int nsm_status; /* status from the remote lock manager */ + int status; /* lock status, see below */ + int flags; /* lock flags, see lockd_lock.h */ + pid_t locker; /* pid of the child process trying to get the lock */ + int fd; /* file descriptor for this lock */ +}; + +/* lock status */ +#define LKST_LOCKED 1 /* lock is locked */ +#define LKST_WAITING 2 /* file is already locked by another host */ +#define LKST_PROCESSING 3 /* child is trying to aquire the lock */ +#define LKST_DYING 4 /* must dies when we get news from the child */ + +void lfree __P((struct file_lock *)); +enum nlm_stats do_lock __P((struct file_lock *, int)); +enum nlm_stats do_unlock __P((struct file_lock *)); +void send_granted __P((struct file_lock *, int)); +void siglock __P((void)); +void sigunlock __P((void)); + +/* list of hosts we monitor */ +LIST_HEAD(hostlst_head, host); +struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head); + +/* struct describing a lock */ +struct host { + LIST_ENTRY(host) hostlst; + char name[SM_MAXSTRLEN]; + int refcnt; +}; + +void do_mon __P((char *)); + +/* + * testlock(): inform the caller if the requested lock would be granted or not + * returns NULL if lock would granted, or pointer to the current nlm4_holder + * otherwise. + */ + +struct nlm4_holder * +testlock(lock, flags) + struct nlm4_lock *lock; + int flags; +{ + struct file_lock *fl; + fhandle_t filehandle; + + /* convert lock to a local filehandle */ + memcpy(&filehandle, lock->fh.n_bytes, sizeof(filehandle)); + + siglock(); + /* search through the list for lock holder */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (fl->status != LKST_LOCKED) + continue; + if (memcmp(&fl->filehandle, &filehandle, sizeof(filehandle))) + continue; + /* got it ! */ + syslog(LOG_DEBUG, "test for %s: found lock held by %s", + lock->caller_name, fl->client_name); + sigunlock(); + return (&fl->client); + } + /* not found */ + sigunlock(); + syslog(LOG_DEBUG, "test for %s: no lock found", lock->caller_name); + return NULL; +} + +/* + * getlock: try to aquire the lock. + * If file is already locked and we can sleep, put the lock in the list with + * status LKST_WAITING; it'll be processed later. + * Otherwise try to lock. If we're allowed to block, fork a child which + * will do the blocking lock. + */ +enum nlm_stats +getlock(lckarg, rqstp, flags) + nlm4_lockargs * lckarg; + struct svc_req *rqstp; + int flags; +{ + struct file_lock *fl, *newfl; + enum nlm_stats retval; + + if (grace_expired == 0 && lckarg->reclaim == 0) + return (flags & LOCK_V4) ? + nlm4_denied_grace_period : nlm_denied_grace_period; + + /* allocate new file_lock for this request */ + newfl = malloc(sizeof(struct file_lock)); + if (newfl == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + /* failed */ + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) { + syslog(LOG_DEBUG, "recieved fhandle size %d, local size %d", + lckarg->alock.fh.n_len, (int)sizeof(fhandle_t)); + } + memcpy(&newfl->filehandle, lckarg->alock.fh.n_bytes, sizeof(fhandle_t)); + newfl->addr = (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf; + newfl->client.exclusive = lckarg->exclusive; + newfl->client.svid = lckarg->alock.svid; + newfl->client.oh.n_bytes = malloc(lckarg->alock.oh.n_len); + if (newfl->client.oh.n_bytes == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + free(newfl); + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + newfl->client.oh.n_len = lckarg->alock.oh.n_len; + memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes, + lckarg->alock.oh.n_len); + newfl->client.l_offset = lckarg->alock.l_offset; + newfl->client.l_len = lckarg->alock.l_len; + newfl->client_cookie.n_len = lckarg->cookie.n_len; + newfl->client_cookie.n_bytes = malloc(lckarg->cookie.n_len); + if (newfl->client_cookie.n_bytes == NULL) { + syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno)); + free(newfl->client.oh.n_bytes); + free(newfl); + return (flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + } + memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes, + lckarg->cookie.n_len); + strncpy(newfl->client_name, lckarg->alock.caller_name, 128); + newfl->nsm_status = lckarg->state; + newfl->status = 0; + newfl->flags = flags; + siglock(); + /* look for a lock rq from this host for this fh */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (memcmp(&newfl->filehandle, &fl->filehandle, + sizeof(fhandle_t)) == 0) { + if (strcmp(newfl->client_name, fl->client_name) == 0 && + newfl->client.svid == fl->client.svid) { + /* already locked by this host ??? */ + sigunlock(); + syslog(LOG_NOTICE, "duplicate lock from %s", + newfl->client_name); + lfree(newfl); + switch(fl->status) { + case LKST_LOCKED: + return (flags & LOCK_V4) ? + nlm4_granted : nlm_granted; + case LKST_WAITING: + case LKST_PROCESSING: + return (flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + case LKST_DYING: + return (flags & LOCK_V4) ? + nlm4_denied : nlm_denied; + default: + syslog(LOG_NOTICE, "bad status %d", + fl->status); + return (flags & LOCK_V4) ? + nlm4_failed : nlm_denied; + } + } + /* + * We already have a lock for this file. Put this one + * in waiting state if allowed to block + */ + if (lckarg->block) { + syslog(LOG_DEBUG, "lock from %s: already " + "locked, waiting", + lckarg->alock.caller_name); + newfl->status = LKST_WAITING; + LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); + do_mon(lckarg->alock.caller_name); + sigunlock(); + return (flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + } else { + sigunlock(); + syslog(LOG_DEBUG, "lock from %s: already " + "locked, failed", + lckarg->alock.caller_name); + lfree(newfl); + return (flags & LOCK_V4) ? + nlm4_denied : nlm_denied; + } + } + } + /* no entry for this file yet; add to list */ + LIST_INSERT_HEAD(&lcklst_head, newfl, lcklst); + /* do the lock */ + retval = do_lock(newfl, lckarg->block); + switch (retval) { + case nlm4_granted: + /* case nlm_granted: is the same as nlm4_granted */ + case nlm4_blocked: + /* case nlm_blocked: is the same as nlm4_blocked */ + do_mon(lckarg->alock.caller_name); + break; + default: + lfree(newfl); + break; + } + sigunlock(); + return retval; +} + +/* unlock a filehandle */ +enum nlm_stats +unlock(lck, flags) + nlm4_lock *lck; + int flags; +{ + struct file_lock *fl; + fhandle_t filehandle; + int err = (flags & LOCK_V4) ? nlm4_granted : nlm_granted; + + memcpy(&filehandle, lck->fh.n_bytes, sizeof(fhandle_t)); + siglock(); + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (strcmp(fl->client_name, lck->caller_name) || + memcmp(&filehandle, &fl->filehandle, sizeof(fhandle_t)) || + fl->client.oh.n_len != lck->oh.n_len || + memcmp(fl->client.oh.n_bytes, lck->oh.n_bytes, + fl->client.oh.n_len) != 0 || + fl->client.svid != lck->svid) + continue; + /* Got it, unlock and remove from the queue */ + syslog(LOG_DEBUG, "unlock from %s: found struct, status %d", + lck->caller_name, fl->status); + switch (fl->status) { + case LKST_LOCKED: + err = do_unlock(fl); + break; + case LKST_WAITING: + /* remove from the list */ + LIST_REMOVE(fl, lcklst); + lfree(fl); + break; + case LKST_PROCESSING: + /* + * being handled by a child; will clean up + * when the child exits + */ + fl->status = LKST_DYING; + break; + case LKST_DYING: + /* nothing to do */ + break; + default: + syslog(LOG_NOTICE, "unknow status %d for %s", + fl->status, fl->client_name); + } + sigunlock(); + return err; + } + sigunlock(); + /* didn't find a matching entry; log anyway */ + syslog(LOG_NOTICE, "no matching entry for %s", + lck->caller_name); + return (flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +void +lfree(fl) + struct file_lock *fl; +{ + free(fl->client.oh.n_bytes); + free(fl->client_cookie.n_bytes); + free(fl); +} + +void +sigchild_handler(sig) + int sig; +{ + int status; + pid_t pid; + struct file_lock *fl; + + while (1) { + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid == -1) { + if (errno != ECHILD) + syslog(LOG_NOTICE, "wait failed: %s", + strerror(errno)); + else + syslog(LOG_DEBUG, "wait failed: %s", + strerror(errno)); + return; + } + if (pid == 0) { + /* no more child to handle yet */ + return; + } + /* + * if we're here we have a child that exited + * Find the associated file_lock. + */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (pid == fl->locker) + break; + } + if (pid != fl->locker) { + syslog(LOG_NOTICE, "unknow child %d", pid); + } else { + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + syslog(LOG_NOTICE, "child %d failed", pid); + /* + * can't do much here; we can't reply + * anything but OK for blocked locks + * Eventually the client will time out + * and retry. + */ + do_unlock(fl); + return; + } + + /* check lock status */ + syslog(LOG_DEBUG, "processing child %d, status %d", + pid, fl->status); + switch(fl->status) { + case LKST_PROCESSING: + fl->status = LKST_LOCKED; + send_granted(fl, (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted); + break; + case LKST_DYING: + do_unlock(fl); + break; + default: + syslog(LOG_NOTICE, "bad lock status (%d) for" + " child %d", fl->status, pid); + } + } + } +} + +/* + * + * try to aquire the lock described by fl. Eventually fock a child to do a + * blocking lock if allowed and required. + */ + +enum nlm_stats +do_lock(fl, block) + struct file_lock *fl; + int block; +{ + int lflags, error; + struct stat st; + + fl->fd = fhopen(&fl->filehandle, O_RDWR); + if (fl->fd < 0) { + switch (errno) { + case ESTALE: + error = nlm4_stale_fh; + break; + case EROFS: + error = nlm4_rofs; + break; + default: + error = nlm4_failed; + } + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + syslog(LOG_NOTICE, "fhopen failed (from %s): %s", + fl->client_name, strerror(errno)); + LIST_REMOVE(fl, lcklst); + return error;; + } + if (fstat(fl->fd, &st) < 0) { + syslog(LOG_NOTICE, "fstat failed (from %s): %s", + fl->client_name, strerror(errno)); + } + syslog(LOG_DEBUG, "lock from %s for file%s%s: dev %d ino %d (uid %d), " + "flags %d", + fl->client_name, fl->client.exclusive ? " (exclusive)":"", + block ? " (block)":"", + st.st_dev, st.st_ino, st.st_uid, fl->flags); + lflags = LOCK_NB; + if (fl->client.exclusive == 0) + lflags |= LOCK_SH; + else + lflags |= LOCK_EX; + error = flock(fl->fd, lflags); + if (error != 0 && errno == EAGAIN && block) { + switch (fl->locker = fork()) { + case -1: /* fork failed */ + syslog(LOG_NOTICE, "fork failed: %s", strerror(errno)); + LIST_REMOVE(fl, lcklst); + close(fl->fd); + return (fl->flags & LOCK_V4) ? + nlm4_denied_nolock : nlm_denied_nolocks; + case 0: + /* + * Attempt a blocking lock. Will have to call + * NLM_GRANTED later. + */ + setproctitle("%s", fl->client_name); + lflags &= ~LOCK_NB; + if(flock(fl->fd, lflags) != 0) { + syslog(LOG_NOTICE, "flock failed: %s", + strerror(errno)); + exit(-1); + } + /* lock granted */ + exit(0); + default: + syslog(LOG_DEBUG, "lock request from %s: forked %d", + fl->client_name, fl->locker); + fl->status = LKST_PROCESSING; + return (fl->flags & LOCK_V4) ? + nlm4_blocked : nlm_blocked; + } + } + /* non block case */ + if (error != 0) { + switch (errno) { + case EAGAIN: + error = nlm4_denied; + break; + case ESTALE: + error = nlm4_stale_fh; + break; + case EROFS: + error = nlm4_rofs; + break; + default: + error = nlm4_failed; + } + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + if (errno != EAGAIN) + syslog(LOG_NOTICE, "flock for %s failed: %s", + fl->client_name, strerror(errno)); + else syslog(LOG_DEBUG, "flock for %s failed: %s", + fl->client_name, strerror(errno)); + LIST_REMOVE(fl, lcklst); + close(fl->fd); + return error; + } + fl->status = LKST_LOCKED; + return (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted; +} + +void +send_granted(fl, opcode) + struct file_lock *fl; + int opcode; +{ + CLIENT *cli; + static char dummy; + struct timeval timeo; + int success; + static struct nlm_res retval; + static struct nlm4_res retval4; + + cli = get_client(fl->addr, + (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS); + if (cli == NULL) { + syslog(LOG_NOTICE, "failed to get CLIENT for %s", + fl->client_name); + /* + * We fail to notify remote that the lock has been granted. + * The client will timeout and retry, the lock will be + * granted at this time. + */ + return; + } + timeo.tv_sec = 0; + timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */ + + if (fl->flags & LOCK_V4) { + static nlm4_testargs res; + res.cookie = fl->client_cookie; + res.exclusive = fl->client.exclusive; + res.alock.caller_name = fl->client_name; + res.alock.fh.n_len = sizeof(fhandle_t); + res.alock.fh.n_bytes = (char*)&fl->filehandle; + res.alock.oh = fl->client.oh; + res.alock.svid = fl->client.svid; + res.alock.l_offset = fl->client.l_offset; + res.alock.l_len = fl->client.l_len; + syslog(LOG_DEBUG, "sending v4 reply%s", + (fl->flags & LOCK_ASYNC) ? " (async)":""); + if (fl->flags & LOCK_ASYNC) { + success = clnt_call(cli, NLM4_GRANTED_MSG, + xdr_nlm4_testargs, &res, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM4_GRANTED, + xdr_nlm4_testargs, &res, xdr_nlm4_res, + &retval4, timeo); + } + } else { + static nlm_testargs res; + + res.cookie = fl->client_cookie; + res.exclusive = fl->client.exclusive; + res.alock.caller_name = fl->client_name; + res.alock.fh.n_len = sizeof(fhandle_t); + res.alock.fh.n_bytes = (char*)&fl->filehandle; + res.alock.oh = fl->client.oh; + res.alock.svid = fl->client.svid; + res.alock.l_offset = fl->client.l_offset; + res.alock.l_len = fl->client.l_len; + syslog(LOG_DEBUG, "sending v1 reply%s", + (fl->flags & LOCK_ASYNC) ? " (async)":""); + if (fl->flags & LOCK_ASYNC) { + success = clnt_call(cli, NLM_GRANTED_MSG, + xdr_nlm_testargs, &res, xdr_void, &dummy, timeo); + } else { + success = clnt_call(cli, NLM_GRANTED, + xdr_nlm_testargs, &res, xdr_nlm_res, + &retval, timeo); + } + } + if (debug_level > 2) + syslog(LOG_DEBUG, "clnt_call returns %d(%s) for granted", + success, clnt_sperrno(success)); + +} + +enum nlm_stats +do_unlock(rfl) + struct file_lock *rfl; +{ + struct file_lock *fl; + int error; + int lockst; + + /* unlock the file: closing is enouth ! */ + if (close(rfl->fd) < 0) { + if (errno == ESTALE) + error = nlm4_stale_fh; + else + error = nlm4_failed; + if ((fl->flags & LOCK_V4) == 0) + error = nlm_denied; + syslog(LOG_NOTICE, + "close failed (from %s): %s", + rfl->client_name, strerror(errno)); + } else { + error = (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted; + } + LIST_REMOVE(rfl, lcklst); + + /* process the next LKST_WAITING lock request for this fh */ + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; + fl = LIST_NEXT(fl, lcklst)) { + if (fl->status != LKST_WAITING || + memcmp(&rfl->filehandle, &fl->filehandle, + sizeof(fhandle_t)) != 0) + continue; + + lockst = do_lock(fl, 1); /* If it's LKST_WAITING we can block */ + switch (lockst) { + case nlm4_granted: + /* case nlm_granted: same as nlm4_granted */ + send_granted(fl, (fl->flags & LOCK_V4) ? + nlm4_granted : nlm_granted); + break; + case nlm4_blocked: + /* case nlm_blocked: same as nlm4_blocked */ + break; + default: + lfree(fl); + break; + } + break; + } + return error; +} + +void +siglock() +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "siglock failed: %s", strerror(errno)); + } +} + +void +sigunlock() +{ + sigset_t block; + + sigemptyset(&block); + sigaddset(&block, SIGCHLD); + + if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) { + syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno)); + } +} + +/* monitor a host through rpc.statd, and keep a ref count */ +void +do_mon(hostname) + char *hostname; +{ + struct host *hp; + struct mon my_mon; + struct sm_stat_res res; + int retval; + + for (hp = LIST_FIRST(&hostlst_head); hp != NULL; + hp = LIST_NEXT(hp, hostlst)) { + if (strcmp(hostname, hp->name) == 0) { + /* already monitored, just bump refcnt */ + hp->refcnt++; + return; + } + } + /* not found, have to create an entry for it */ + hp = malloc(sizeof(struct host)); + strncpy(hp->name, hostname, SM_MAXSTRLEN); + hp->refcnt = 1; + syslog(LOG_DEBUG, "monitoring host %s", + hostname); + memset(&my_mon, 0, sizeof(my_mon)); + my_mon.mon_id.mon_name = hp->name; + my_mon.mon_id.my_id.my_name = "localhost"; + my_mon.mon_id.my_id.my_prog = NLM_PROG; + my_mon.mon_id.my_id.my_vers = NLM_SM; + my_mon.mon_id.my_id.my_proc = NLM_SM_NOTIFY; + if ((retval = + callrpc("localhost", SM_PROG, SM_VERS, SM_MON, xdr_mon, + (char*)&my_mon, xdr_sm_stat_res, (char*)&res)) != 0) { + syslog(LOG_WARNING, "rpc to statd failed: %s", + clnt_sperrno((enum clnt_stat)retval)); + free(hp); + return; + } + if (res.res_stat == stat_fail) { + syslog(LOG_WARNING, "statd failed"); + free(hp); + return; + } + LIST_INSERT_HEAD(&hostlst_head, hp, hostlst); +} + +void +notify(hostname, state) + char *hostname; + int state; +{ + struct file_lock *fl, *next_fl; + int err; + syslog(LOG_DEBUG, "notify from %s, new state %d", hostname, state); + /* search all lock for this host; if status changed, release the lock */ + siglock(); + for (fl = LIST_FIRST(&lcklst_head); fl != NULL; fl = next_fl) { + next_fl = LIST_NEXT(fl, lcklst); + if (strcmp(hostname, fl->client_name) == 0 && + fl->nsm_status != state) { + syslog(LOG_DEBUG, "state %d, nsm_state %d, unlocking", + fl->status, fl->nsm_status); + switch(fl->status) { + case LKST_LOCKED: + err = do_unlock(fl); + if (err != nlm_granted) + syslog(LOG_DEBUG, + "notify: unlock failed for %s (%d)", + hostname, err); + break; + case LKST_WAITING: + LIST_REMOVE(fl, lcklst); + lfree(fl); + break; + case LKST_PROCESSING: + fl->status = LKST_DYING; + break; + case LKST_DYING: + break; + default: + syslog(LOG_NOTICE, "unknow status %d for %s", + fl->status, fl->client_name); + } + } + } + sigunlock(); +} diff --git a/usr.sbin/rpc.lockd/lockd_lock.h b/usr.sbin/rpc.lockd/lockd_lock.h new file mode 100644 index 0000000..5b0232f --- /dev/null +++ b/usr.sbin/rpc.lockd/lockd_lock.h @@ -0,0 +1,21 @@ +/* $NetBSD: lockd_lock.h,v 1.2 2000/06/09 14:00:54 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* Headers and function declarations for file-locking utilities */ + +struct nlm4_holder * testlock __P((struct nlm4_lock *, int)); + +enum nlm_stats getlock __P((nlm4_lockargs *, struct svc_req *, int)); +enum nlm_stats unlock __P((nlm4_lock *, int)); +void notify __P((char *, int)); + +/* flags for testlock, getlock & unlock */ +#define LOCK_ASYNC 0x01 /* async version (getlock only) */ +#define LOCK_V4 0x02 /* v4 version */ +#define LOCK_MON 0x04 /* monitored lock (getlock only) */ +#define LOCK_CANCEL 0x08 /* cancel, not unlock request (unlock only) */ + +/* callbacks from lock_proc.c */ +void transmit_result __P((int, nlm_res *, struct sockaddr *)); +void transmit4_result __P((int, nlm4_res *, struct sockaddr *)); +CLIENT *get_client __P((struct sockaddr *, rpcvers_t)); diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8 index 2438aacf..e87f5bb 100644 --- a/usr.sbin/rpc.lockd/rpc.lockd.8 +++ b/usr.sbin/rpc.lockd/rpc.lockd.8 @@ -1,4 +1,5 @@ -.\" -*- nroff -*- +.\" $NetBSD: rpc.lockd.8,v 1.5 2000/06/09 18:51:47 cgd Exp $ +.\" $FreeBSD$ .\" .\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk .\" All rights reserved. @@ -31,7 +32,6 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD$ .\" .Dd September 24, 1995 .Dt RPC.LOCKD 8 @@ -41,34 +41,64 @@ .Nd NFS file locking daemon .Sh SYNOPSIS .Nm -.Op Fl d Op Ar debug_level +.Op Fl d Ar debug_level +.Op Fl g Ar grace period .Sh DESCRIPTION -.Nm Rpc.lockd -is a daemon which provides file- and record-locking services in an NFS -environment. +The +.Nm +daemon provides monitored and unmonitored file and record locking services +in an NFS environment. +To monitor the status of hosts requesting locks, +the locking daemon typically operates in conjunction +with +.Xr rpc.statd 8 . .Pp -The following option is available: +Options and operands available for +.Nm : .Bl -tag -width indent .It Fl d -Cause debugging information to be written to syslog, recording -all RPC transactions to the daemon. These messages are logged with level -LOG_DEBUG and facility LOG_DAEMON. If debug_level is not specified, -level 1 is assumed, giving one log line per protocol operation. Higher +The +.Fl d +option causes debugging information to be written to syslog, recording +all RPC transactions to the daemon. +These messages are logged with level +.Dv LOG_DEBUG +and facility +.Dv LOG_DAEMON . +Specifying a +.Ar debug_level +of 1 results +in the generation of one log line per protocol operation. +Higher debug levels can be specified, causing display of operation arguments and internal operations of the daemon. +.It Fl g +The +.Fl g +option allow to specify the +.Ar grace period , +in seconds. +During the grace period +.Nm +only accepts requests from hosts which are reinitialising locks which +existed before the server restart. +Default is 30 seconds. .El .Pp Error conditions are logged to syslog, irrespective of the debug level, -using log level LOG_ERR and facility LOG_DAEMON. +using log level +.Dv LOG_ERR +and facility +.Dv LOG_DAEMON . .Pp The .Nm daemon must NOT be invoked by .Xr inetd 8 because the protocol assumes that the daemon will run from system start time. -Instead, it should be run from -.Xr rc 8 -after the network has been started. +Instead, it should be configured in +.Xr rc.conf 5 +to run at system startup. .Sh FILES .Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact .It Pa /usr/include/rpcsvc/nlm_prot.x @@ -76,21 +106,32 @@ RPC protocol specification for the network lock manager protocol. .El .Sh SEE ALSO .Xr syslog 3 , -.Xr rc 8 , +.Xr rc.conf 5 , .Xr rpc.statd 8 .Sh BUGS The current implementation provides only the server side of the protocol -(ie. clients running other OS types can establish locks on a +(i.e., clients running other OS types can establish locks on a .Fx +/ +.Nx fileserver, but there is currently no means for a .Fx +/ +.Nx client to establish locks). .Pp -Versions 1, 2 and 3 of the protocol are supported. However, only versions -2 (Unix systems) and 3 (PC-NFS clients) seem to be in common use - the version -1 support has not been tested due to the lack of version 1 clients against -which to test. +The current implementation serialises locks requests that could be shared. .Sh STANDARDS -The implementation is based on the specification in X/Open CAE Specification -C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9 +The implementation is based on the specification in +.Rs +.%B "X/Open CAE Specification C218" +.%T "Protocols for X/Open PC Interworking: XNFS, Issue 4" +.%O ISBN 1 872630 66 9 +.Re +.Sh HISTORY +A version of +.Nm +appeared in +.Tn SunOS +4. diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c index 0dd8eae..a751e5c 100644 --- a/usr.sbin/rpc.lockd/test.c +++ b/usr.sbin/rpc.lockd/test.c @@ -1,14 +1,17 @@ +/* $NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $ */ + +#include <sys/cdefs.h> +#include <rpc/rpc.h> +#include <rpcsvc/nlm_prot.h> #ifndef lint #if 0 static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro"; static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC"; +#else +__RCSID("$NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $"); +static const char rcsid[] = "$FreeBSD$"; #endif -static const char rcsid[] = - "$FreeBSD$"; -#endif /* not lint */ - -#include <rpc/rpc.h> -#include <rpcsvc/nlm_prot.h> +#endif /* not lint */ /* Default timeout can be changed using clnt_control() */ static struct timeval TIMEOUT = { 0, 0 }; @@ -304,63 +307,59 @@ nlm_free_all_3(argp, clnt) int main(int argc, char **argv) { - CLIENT *cli; - nlm_res res_block; - nlm_res *out; - nlm_lockargs arg; - struct timeval tim; - - printf("Creating client for host %s\n", argv[1]); - cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp"); - if (!cli) - { - printf("Failed to create client\n"); - exit(1); - } - - - clnt_control(cli, CLGET_TIMEOUT, &tim); - printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec); - tim.tv_usec = -1; - tim.tv_sec = -1; - clnt_control(cli, CLSET_TIMEOUT, &tim); - clnt_control(cli, CLGET_TIMEOUT, &tim); - printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec); - - - arg.cookie.n_len = 4; - arg.cookie.n_bytes = "hello"; - arg.block = 0; - arg.exclusive = 0; - arg.reclaim = 0; - arg.state = 0x1234; - arg.alock.caller_name = "localhost"; - arg.alock.fh.n_len = 32; - arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94"; - arg.alock.oh.n_len = 8; - arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3"; - arg.alock.svid = 0x5678; - arg.alock.l_offset = 0; - arg.alock.l_len = 100; - - res_block.stat.stat = nlm_granted; - res_block.cookie.n_bytes = "hello"; - res_block.cookie.n_len = 5; + CLIENT *cli; + nlm_res res_block; + nlm_res *out; + nlm_lockargs arg; + struct timeval tim; + + printf("Creating client for host %s\n", argv[1]); + cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp"); + if (!cli) { + errx(1, "Failed to create client\n"); + /* NOTREACHED */ + } + clnt_control(cli, CLGET_TIMEOUT, &tim); + printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec); + tim.tv_usec = -1; + tim.tv_sec = -1; + clnt_control(cli, CLSET_TIMEOUT, &tim); + clnt_control(cli, CLGET_TIMEOUT, &tim); + printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec); + + + arg.cookie.n_len = 4; + arg.cookie.n_bytes = "hello"; + arg.block = 0; + arg.exclusive = 0; + arg.reclaim = 0; + arg.state = 0x1234; + arg.alock.caller_name = "localhost"; + arg.alock.fh.n_len = 32; + arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94"; + arg.alock.oh.n_len = 8; + arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3"; + arg.alock.svid = 0x5678; + arg.alock.l_offset = 0; + arg.alock.l_len = 100; + + res_block.stat.stat = nlm_granted; + res_block.cookie.n_bytes = "hello"; + res_block.cookie.n_len = 5; #if 0 - if (nlm_lock_res_1(&res_block, cli)) printf("Success!\n"); - else printf("Fail\n"); + if (nlm_lock_res_1(&res_block, cli)) + printf("Success!\n"); + else + printf("Fail\n"); #else - if (out = nlm_lock_msg_1(&arg, cli)) - { - printf("Success!\n"); - printf("out->stat = %d", out->stat); - } - else - { - printf("Fail\n"); - } + if (out = nlm_lock_msg_1(&arg, cli)) { + printf("Success!\n"); + printf("out->stat = %d", out->stat); + } else { + printf("Fail\n"); + } #endif - return 0; + return 0; } diff --git a/usr.sbin/rpc.statd/statd.h b/usr.sbin/rpc.statd/statd.h index 7b4e659..7bf7b1d 100644 --- a/usr.sbin/rpc.statd/statd.h +++ b/usr.sbin/rpc.statd/statd.h @@ -29,26 +29,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * $FreeBSD$ */ #include "sm_inter.h" -/* These pieces are missing from the distributed sm_inter.x, which */ -/* omits the SM_NOTIFY procedure used between cooperating rpc.statd's */ - -#define SM_NOTIFY ((u_long)6) -extern void *sm_notify_1(); - -struct stat_chge -{ - char *mon_name; - int state; -}; -typedef struct stat_chge stat_chge; -bool_t xdr_stat_chge(); - /* ------------------------------------------------------------------------- */ /* Data structures for recording monitored hosts diff --git a/usr.sbin/rpc.umntall/rpc.umntall.c b/usr.sbin/rpc.umntall/rpc.umntall.c index c59c776..5129ba9 100644 --- a/usr.sbin/rpc.umntall/rpc.umntall.c +++ b/usr.sbin/rpc.umntall/rpc.umntall.c @@ -171,28 +171,23 @@ main(int argc, char **argv) { int do_umntall(char *hostname) { enum clnt_stat clnt_stat; - struct hostent *hp; - struct sockaddr_in saddr; - struct timeval pertry, try; - int so; + struct addrinfo *ai, hints; + struct timeval try; + int so, ecode; CLIENT *clp; - if ((hp = gethostbyname(hostname)) == NULL) { - warnx("gethostbyname(%s) failed", hostname); - return (0); + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_NUMERICHOST; + ecode = getaddrinfo(hostname, NULL, &hints, &ai); + if (ecode != 0) { + warnx("can't get net id for host/nfs: %s", + gai_strerror(ecode)); + return (1); } - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, - sizeof(saddr.sin_addr))); - pertry.tv_sec = 3; - pertry.tv_usec = 0; - so = RPC_ANYSOCK; - if ((clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, - pertry, &so)) == NULL) { - clnt_pcreateerror("Cannot send MNT PRC"); - return (0); + clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp"); + if (clp == NULL) { + clnt_pcreateerror("Cannot MNT PRC"); + return (1); } clp->cl_auth = authunix_create_default(); try.tv_sec = 3; @@ -212,30 +207,25 @@ do_umntall(char *hostname) { int do_umount(char *hostname, char *dirp) { enum clnt_stat clnt_stat; - struct hostent *hp; - struct sockaddr_in saddr; - struct timeval pertry, try; + struct addrinfo *ai, hints; + struct timeval try; CLIENT *clp; - int so; + int so, ecode; - if ((hp = gethostbyname(hostname)) == NULL) { - warnx("gethostbyname(%s) failed", hostname); - return (0); + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_NUMERICHOST; + ecode = getaddrinfo(hostname, NULL, &hints, &ai); + if (ecode != 0) { + warnx("can't get net id for host/nfs: %s", + gai_strerror(ecode)); + return (1); } - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - memmove(&saddr.sin_addr, hp->h_addr, MIN(hp->h_length, - sizeof(saddr.sin_addr))); - pertry.tv_sec = 3; - pertry.tv_usec = 0; - so = RPC_ANYSOCK; - if ((clp = clntudp_create(&saddr, RPCPROG_MNT, RPCMNT_VER1, - pertry, &so)) == NULL) { - clnt_pcreateerror("Cannot send MNT PRC"); - return (0); + clp = clnt_create(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp"); + if (clp == NULL) { + clnt_pcreateerror("Cannot MNT PRC"); + return (1); } - clp->cl_auth = authunix_create_default(); + clp->cl_auth = authsys_create_default(); try.tv_sec = 3; try.tv_usec = 0; clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, dirp, diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c index 03b4825..51f9621 100644 --- a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c +++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c @@ -298,7 +298,18 @@ the %s domain -- aborting", yppasswd_domain); } unlink(sockname); - transp = svcunix_create(sock, 0, 0, sockname); + if (svc_create(yppasswdprog_1, YPPASSWDPROG, YPPASSWDVERS, + "netpath") == 0) { + (void) fprintf(stderr, + "%s: unable to create service\n", argv[0]); + exit(1); + } + if (svc_create(master_yppasswdprog_1, MASTER_YPPASSWDPROG, + MASTER_YPPASSWDVERS, "netpath") == 0) { + (void) fprintf(stderr, + "%s: unable to create service\n", argv[0]); + exit(1); + } if (transp == NULL) { yp_error("cannot create AF_LOCAL service."); exit(1); diff --git a/usr.sbin/rpc.ypupdated/ypupdated_extern.h b/usr.sbin/rpc.ypupdated/ypupdated_extern.h index 28ea7ac..17e9ee9 100644 --- a/usr.sbin/rpc.ypupdated/ypupdated_extern.h +++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h @@ -1,3 +1,4 @@ +/* $FreeBSD$ */ #include <db.h> #define YPOP_CHANGE 1 /* change, do not add */ diff --git a/usr.sbin/rpc.ypupdated/ypupdated_server.c b/usr.sbin/rpc.ypupdated/ypupdated_server.c index f0c5bff..b306268 100644 --- a/usr.sbin/rpc.ypupdated/ypupdated_server.c +++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c @@ -43,7 +43,6 @@ static const char rcsid[] = #include <stdio.h> #include <rpc/rpc.h> -#include <rpc/auth_des.h> #include <rpc/key_prot.h> #include <sys/param.h> #include <sys/cdefs.h> diff --git a/usr.sbin/rpcbind/Makefile b/usr.sbin/rpcbind/Makefile new file mode 100644 index 0000000..fb73b23 --- /dev/null +++ b/usr.sbin/rpcbind/Makefile @@ -0,0 +1,21 @@ +# $NetBSD: Makefile,v 1.3 2000/06/20 13:56:43 fvdl Exp $ +# $FreeBSD$ + +PROG= rpcbind +CFLAGS+= -I${LIBCRPCDIR} -I${LIBCINCLUDE} -DPORTMAP -DINET6 -DLIBWRAP +MAN8= rpcbind.8 +SRCS= check_bound.c rpcb_stat.c rpcb_svc_4.c rpcbind.c pmap_svc.c \ + rpcb_svc.c rpcb_svc_com.c security.c warmstart.c util.c \ + rpc_generic.c + +LIBCDIR= ${.CURDIR}/../../lib/libc +LIBCRPCDIR= ${LIBCDIR}/rpc +LIBCINCLUDE= ${LIBCDIR}/include + + +LDADD+= -lwrap -lutil +DPADD+= ${LIBWRAP} ${LIBUTIL} + +.PATH: ${LIBCRPCDIR} + +.include <bsd.prog.mk> diff --git a/usr.sbin/rpcbind/check_bound.c b/usr.sbin/rpcbind/check_bound.c new file mode 100644 index 0000000..fe503d7 --- /dev/null +++ b/usr.sbin/rpcbind/check_bound.c @@ -0,0 +1,229 @@ +/* $NetBSD: check_bound.c,v 1.2 2000/06/22 08:09:26 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)check_bound.c 1.15 93/07/05 SMI" */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)check_bound.c 1.11 89/04/21 Copyr 1989 Sun Micro"; +#endif +#endif + +/* + * check_bound.c + * Checks to see whether the program is still bound to the + * claimed address and returns the univeral merged address + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <rpc/rpc.h> +#include <stdio.h> +#include <netconfig.h> +#include <syslog.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#include "rpcbind.h" + +struct fdlist { + int fd; + struct netconfig *nconf; + struct fdlist *next; + int check_binding; +}; + +static struct fdlist *fdhead; /* Link list of the check fd's */ +static struct fdlist *fdtail; +static char *nullstring = ""; + +static bool_t check_bound __P((struct fdlist *, char *uaddr)); + +/* + * Returns 1 if the given address is bound for the given addr & transport + * For all error cases, we assume that the address is bound + * Returns 0 for success. + */ +static bool_t +check_bound(struct fdlist *fdl, char *uaddr) +{ + int fd; + struct netbuf *na; + int ans; + + if (fdl->check_binding == FALSE) + return (TRUE); + + na = uaddr2taddr(fdl->nconf, uaddr); + if (!na) + return (TRUE); /* punt, should never happen */ + + fd = __rpc_nconf2fd(fdl->nconf); + if (fd < 0) { + free(na); + return (TRUE); + } + + ans = bind(fd, (struct sockaddr *)na->buf, na->len); + + close(fd); + free(na); + + return (ans == 0 ? FALSE : TRUE); +} + +int +add_bndlist(struct netconfig *nconf, struct netbuf *baddr) +{ + struct fdlist *fdl; + struct netconfig *newnconf; + + newnconf = getnetconfigent(nconf->nc_netid); + if (newnconf == NULL) + return (-1); + fdl = (struct fdlist *)malloc((u_int)sizeof (struct fdlist)); + if (fdl == NULL) { + freenetconfigent(newnconf); + syslog(LOG_ERR, "no memory!"); + return (-1); + } + fdl->nconf = newnconf; + fdl->next = NULL; + if (fdhead == NULL) { + fdhead = fdl; + fdtail = fdl; + } else { + fdtail->next = fdl; + fdtail = fdl; + } + /* XXX no bound checking for now */ + fdl->check_binding = FALSE; + + return 0; +} + +bool_t +is_bound(char *netid, char *uaddr) +{ + struct fdlist *fdl; + + for (fdl = fdhead; fdl; fdl = fdl->next) + if (strcmp(fdl->nconf->nc_netid, netid) == 0) + break; + if (fdl == NULL) + return (TRUE); + return (check_bound(fdl, uaddr)); +} + +/* + * Returns NULL if there was some system error. + * Returns "" if the address was not bound, i.e the server crashed. + * Returns the merged address otherwise. + */ +char * +mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr) +{ + struct fdlist *fdl; + char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL; + + for (fdl = fdhead; fdl; fdl = fdl->next) + if (strcmp(fdl->nconf->nc_netid, netid) == 0) + break; + if (fdl == NULL) + return (NULL); + if (check_bound(fdl, uaddr) == FALSE) + /* that server died */ + return (nullstring); + /* + * If saddr is not NULL, the remote client may have included the + * address by which it contacted us. Use that for the "client" uaddr, + * otherwise use the info from the SVCXPRT. + */ + if (saddr != NULL) { + c_uaddr = saddr; + } else { + c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt)); + if (c_uaddr == NULL) { + syslog(LOG_ERR, "taddr2uaddr failed for %s", + fdl->nconf->nc_netid); + return (NULL); + } + allocated_uaddr = c_uaddr; + } + +#ifdef ND_DEBUG + if (debugging) { + if (saddr == NULL) { + fprintf(stderr, "mergeaddr: client uaddr = %s\n", + c_uaddr); + } else { + fprintf(stderr, "mergeaddr: contact uaddr = %s\n", + c_uaddr); + } + } +#endif + s_uaddr = uaddr; + /* + * This is all we should need for IP 4 and 6 + */ + m_uaddr = addrmerge(svc_getrpccaller(xprt), s_uaddr, c_uaddr, netid); +#ifdef ND_DEBUG + if (debugging) + fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n", + uaddr, m_uaddr); +#endif + if (allocated_uaddr != NULL) + free(allocated_uaddr); + return (m_uaddr); +} + +/* + * Returns a netconf structure from its internal list. This + * structure should not be freed. + */ +struct netconfig * +rpcbind_get_conf(char *netid) +{ + struct fdlist *fdl; + + for (fdl = fdhead; fdl; fdl = fdl->next) + if (strcmp(fdl->nconf->nc_netid, netid) == 0) + break; + if (fdl == NULL) + return (NULL); + return (fdl->nconf); +} diff --git a/usr.sbin/rpcbind/pmap_svc.c b/usr.sbin/rpcbind/pmap_svc.c new file mode 100644 index 0000000..b2cedd9 --- /dev/null +++ b/usr.sbin/rpcbind/pmap_svc.c @@ -0,0 +1,368 @@ +/* $NetBSD: pmap_svc.c,v 1.2 2000/10/20 11:49:40 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)pmap_svc.c 1.14 93/07/05 SMI" */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)pmap_svc.c 1.23 89/04/05 Copyr 1984 Sun Micro"; +#endif +#endif + +/* + * pmap_svc.c + * The server procedure for the version 2 portmaper. + * All the portmapper related interface from the portmap side. + */ + +#ifdef PORTMAP +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +#include <rpc/rpcb_prot.h> +#ifdef RPCBIND_DEBUG +#include <stdlib.h> +#endif +#include "rpcbind.h" + +static struct pmaplist *find_service_pmap __P((rpcprog_t, rpcvers_t, + rpcprot_t)); +static bool_t pmapproc_change __P((struct svc_req *, SVCXPRT *, u_long)); +static bool_t pmapproc_getport __P((struct svc_req *, SVCXPRT *)); +static bool_t pmapproc_dump __P((struct svc_req *, SVCXPRT *)); + +/* + * Called for all the version 2 inquiries. + */ +void +pmap_service(struct svc_req *rqstp, SVCXPRT *xprt) +{ + rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc); + switch (rqstp->rq_proc) { + case PMAPPROC_NULL: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "PMAPPROC_NULL\n"); +#endif + check_access(xprt, rqstp->rq_proc, NULL, PMAPVERS); + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_void, NULL)) && + debugging) { + if (doabort) { + rpcbind_abort(); + } + } + break; + + case PMAPPROC_SET: + /* + * Set a program, version to port mapping + */ + pmapproc_change(rqstp, xprt, rqstp->rq_proc); + break; + + case PMAPPROC_UNSET: + /* + * Remove a program, version to port mapping. + */ + pmapproc_change(rqstp, xprt, rqstp->rq_proc); + break; + + case PMAPPROC_GETPORT: + /* + * Lookup the mapping for a program, version and return its + * port number. + */ + pmapproc_getport(rqstp, xprt); + break; + + case PMAPPROC_DUMP: + /* + * Return the current set of mapped program, version + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "PMAPPROC_DUMP\n"); +#endif + pmapproc_dump(rqstp, xprt); + break; + + case PMAPPROC_CALLIT: + /* + * Calls a procedure on the local machine. If the requested + * procedure is not registered this procedure does not return + * error information!! + * This procedure is only supported on rpc/udp and calls via + * rpc/udp. It passes null authentication parameters. + */ + rpcbproc_callit_com(rqstp, xprt, PMAPPROC_CALLIT, PMAPVERS); + break; + + default: + svcerr_noproc(xprt); + break; + } +} + +/* + * returns the item with the given program, version number. If that version + * number is not found, it returns the item with that program number, so that + * the port number is now returned to the caller. The caller when makes a + * call to this program, version number, the call will fail and it will + * return with PROGVERS_MISMATCH. The user can then determine the highest + * and the lowest version number for this program using clnt_geterr() and + * use those program version numbers. + */ +static struct pmaplist * +find_service_pmap(rpcprog_t prog, rpcvers_t vers, rpcprot_t prot) +{ + register struct pmaplist *hit = NULL; + register struct pmaplist *pml; + + for (pml = list_pml; pml != NULL; pml = pml->pml_next) { + if ((pml->pml_map.pm_prog != prog) || + (pml->pml_map.pm_prot != prot)) + continue; + hit = pml; + if (pml->pml_map.pm_vers == vers) + break; + } + return (hit); +} + +static bool_t +pmapproc_change(struct svc_req *rqstp, SVCXPRT *xprt, unsigned long op) +{ + struct pmap reg; + RPCB rpcbreg; + long ans; + struct sockaddr_in *who; + struct cmsgcred *cmcred; + char uidbuf[32]; + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s request for (%lu, %lu) : ", + op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET", + reg.pm_prog, reg.pm_vers); +#endif + + if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)®)) { + svcerr_decode(xprt); + return (FALSE); + } + + if (!check_access(xprt, op, ®, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + + who = svc_getcaller(xprt); + cmcred = __svc_getcallercreds(xprt); + + /* + * Can't use getpwnam here. We might end up calling ourselves + * and looping. + */ + if (cmcred == NULL) + rpcbreg.r_owner = "unknown"; + else if (cmcred->cmcred_uid == 0) + rpcbreg.r_owner = "superuser"; + else { + /* r_owner will be strdup-ed later */ + snprintf(uidbuf, sizeof uidbuf, "%d", cmcred->cmcred_uid); + rpcbreg.r_owner = uidbuf; + } + + rpcbreg.r_prog = reg.pm_prog; + rpcbreg.r_vers = reg.pm_vers; + + if (op == PMAPPROC_SET) { + char buf[32]; + + sprintf(buf, "0.0.0.0.%d.%d", (int)((reg.pm_port >> 8) & 0xff), + (int)(reg.pm_port & 0xff)); + rpcbreg.r_addr = buf; + if (reg.pm_prot == IPPROTO_UDP) { + rpcbreg.r_netid = udptrans; + } else if (reg.pm_prot == IPPROTO_TCP) { + rpcbreg.r_netid = tcptrans; + } else { + ans = FALSE; + goto done_change; + } + ans = map_set(&rpcbreg, rpcbreg.r_owner); + } else if (op == PMAPPROC_UNSET) { + bool_t ans1, ans2; + + rpcbreg.r_addr = NULL; + rpcbreg.r_netid = tcptrans; + ans1 = map_unset(&rpcbreg, rpcbreg.r_owner); + rpcbreg.r_netid = udptrans; + ans2 = map_unset(&rpcbreg, rpcbreg.r_owner); + ans = ans1 || ans2; + } else { + ans = FALSE; + } +done_change: + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t) &ans)) && + debugging) { + fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + if (op == PMAPPROC_SET) + rpcbs_set(RPCBVERS_2_STAT, ans); + else + rpcbs_unset(RPCBVERS_2_STAT, ans); + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +pmapproc_getport(struct svc_req *rqstp, SVCXPRT *xprt) +{ + struct pmap reg; + long lport; + int port = 0; + struct pmaplist *fnd; +#ifdef RPCBIND_DEBUG + char *uaddr; +#endif + + if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)®)) { + svcerr_decode(xprt); + return (FALSE); + } + + if (!check_access(xprt, PMAPPROC_GETPORT, ®, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + +#ifdef RPCBIND_DEBUG + if (debugging) { + uaddr = taddr2uaddr(rpcbind_get_conf(xprt->xp_netid), + svc_getrpccaller(xprt)); + fprintf(stderr, "PMAP_GETPORT req for (%lu, %lu, %s) from %s :", + reg.pm_prog, reg.pm_vers, + reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr); + free(uaddr); + } +#endif + fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot); + if (fnd) { + char serveuaddr[32], *ua; + int h1, h2, h3, h4, p1, p2; + char *netid; + + if (reg.pm_prot == IPPROTO_UDP) { + ua = udp_uaddr; + netid = udptrans; + } else { + ua = tcp_uaddr; /* To get the len */ + netid = tcptrans; + } + if (ua == NULL) { + goto sendreply; + } + if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3, + &h4, &p1, &p2) == 6) { + p1 = (fnd->pml_map.pm_port >> 8) & 0xff; + p2 = (fnd->pml_map.pm_port) & 0xff; + sprintf(serveuaddr, "%d.%d.%d.%d.%d.%d", + h1, h2, h3, h4, p1, p2); + if (is_bound(netid, serveuaddr)) { + port = fnd->pml_map.pm_port; + } else { /* this service is dead; delete it */ + delete_prog(reg.pm_prog); + } + } + } +sendreply: + lport = port; + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t)&lport)) && + debugging) { + (void) fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "port = %d\n", port); +#endif + rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers, + reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans, + port ? udptrans : ""); + + return (TRUE); +} + +/* ARGSUSED */ +static bool_t +pmapproc_dump(struct svc_req *rqstp, SVCXPRT *xprt) +{ + if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) { + svcerr_decode(xprt); + return (FALSE); + } + + if (!check_access(xprt, PMAPPROC_DUMP, NULL, PMAPVERS)) { + svcerr_weakauth(xprt); + return FALSE; + } + + if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr, + (caddr_t)&list_pml)) && debugging) { + if (debugging) + (void) fprintf(stderr, "portmap: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + return (TRUE); +} + +#endif /* PORTMAP */ diff --git a/usr.sbin/rpcbind/rpcb_stat.c b/usr.sbin/rpcbind/rpcb_stat.c new file mode 100644 index 0000000..0555493 --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_stat.c @@ -0,0 +1,208 @@ +/* + * $NetBSD: rpcb_stat.c,v 1.2 2000/07/04 20:27:40 matt Exp $ + * $FreeBSD$ + */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* #pragma ident "@(#)rpcb_stat.c 1.7 94/04/25 SMI" */ + +/* + * rpcb_stat.c + * Allows for gathering of statistics + * + * Copyright (c) 1990 by Sun Microsystems, Inc. + */ + +#include <stdio.h> +#include <netconfig.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <sys/stat.h> +#ifdef PORTMAP +#include <rpc/pmap_prot.h> +#endif +#include <stdlib.h> +#include <string.h> +#include "rpcbind.h" + +static rpcb_stat_byvers inf; + +void +rpcbs_init() +{ + +} + +void +rpcbs_procinfo(rpcvers_t rtype, rpcproc_t proc) +{ + switch (rtype + 2) { +#ifdef PORTMAP + case PMAPVERS: /* version 2 */ + if (proc > rpcb_highproc_2) + return; + break; +#endif + case RPCBVERS: /* version 3 */ + if (proc > rpcb_highproc_3) + return; + break; + case RPCBVERS4: /* version 4 */ + if (proc > rpcb_highproc_4) + return; + break; + default: return; + } + inf[rtype].info[proc]++; + return; +} + +void +rpcbs_set(rpcvers_t rtype, bool_t success) +{ + if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + return; + inf[rtype].setinfo++; + return; +} + +void +rpcbs_unset(rpcvers_t rtype, bool_t success) +{ + if ((rtype >= RPCBVERS_STAT) || (success == FALSE)) + return; + inf[rtype].unsetinfo++; + return; +} + +void +rpcbs_getaddr(rpcvers_t rtype, rpcprog_t prog, rpcvers_t vers, char *netid, + char *uaddr) +{ + rpcbs_addrlist *al; + struct netconfig *nconf; + + if (rtype >= RPCBVERS_STAT) + return; + for (al = inf[rtype].addrinfo; al; al = al->next) { + + if(al->netid == NULL) + return; + if ((al->prog == prog) && (al->vers == vers) && + (strcmp(al->netid, netid) == 0)) { + if ((uaddr == NULL) || (uaddr[0] == NULL)) + al->failure++; + else + al->success++; + return; + } + } + nconf = rpcbind_get_conf(netid); + if (nconf == NULL) { + return; + } + al = (rpcbs_addrlist *) malloc(sizeof (rpcbs_addrlist)); + if (al == NULL) { + return; + } + al->prog = prog; + al->vers = vers; + al->netid = nconf->nc_netid; + if ((uaddr == NULL) || (uaddr[0] == NULL)) { + al->failure = 1; + al->success = 0; + } else { + al->failure = 0; + al->success = 1; + } + al->next = inf[rtype].addrinfo; + inf[rtype].addrinfo = al; +} + +void +rpcbs_rmtcall(rpcvers_t rtype, rpcproc_t rpcbproc, rpcprog_t prog, + rpcvers_t vers, rpcproc_t proc, char *netid, rpcblist_ptr rbl) +{ + rpcbs_rmtcalllist *rl; + struct netconfig *nconf; + + if (rtype > RPCBVERS_STAT) + return; + for (rl = inf[rtype].rmtinfo; rl; rl = rl->next) { + + if(rl->netid == NULL) + return; + + if ((rl->prog == prog) && (rl->vers == vers) && + (rl->proc == proc) && + (strcmp(rl->netid, netid) == 0)) { + if ((rbl == NULL) || + (rbl->rpcb_map.r_vers != vers)) + rl->failure++; + else + rl->success++; + if (rpcbproc == RPCBPROC_INDIRECT) + rl->indirect++; + return; + } + } + nconf = rpcbind_get_conf(netid); + if (nconf == NULL) { + return; + } + rl = (rpcbs_rmtcalllist *) malloc(sizeof (rpcbs_rmtcalllist)); + if (rl == NULL) { + return; + } + rl->prog = prog; + rl->vers = vers; + rl->proc = proc; + rl->netid = nconf->nc_netid; + if ((rbl == NULL) || + (rbl->rpcb_map.r_vers != vers)) { + rl->failure = 1; + rl->success = 0; + } else { + rl->failure = 0; + rl->success = 1; + } + rl->indirect = 1; + rl->next = inf[rtype].rmtinfo; + inf[rtype].rmtinfo = rl; + return; +} + +/* + */ +void * +rpcbproc_getstat(void *arg, struct svc_req *req, SVCXPRT *xprt, + rpcvers_t versnum) +{ + return (void *)&inf; +} diff --git a/usr.sbin/rpcbind/rpcb_svc.c b/usr.sbin/rpcbind/rpcb_svc.c new file mode 100644 index 0000000..b01e9ac --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc.c @@ -0,0 +1,233 @@ +/* $NetBSD: rpcb_svc.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)rpcb_svc.c 1.16 93/07/05 SMI" */ + +/* + * rpcb_svc.c + * The server procedure for the version 3 rpcbind (TLI). + * + * It maintains a separate list of all the registered services with the + * version 3 of rpcbind. + */ +#include <sys/types.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <netconfig.h> +#include <syslog.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "rpcbind.h" + +static void *rpcbproc_getaddr_3_local __P((void *, struct svc_req *, SVCXPRT *, + rpcvers_t)); +static void *rpcbproc_dump_3_local __P((void *, struct svc_req *, SVCXPRT *, + rpcvers_t)); + +/* + * Called by svc_getreqset. There is a separate server handle for + * every transport that it waits on. + */ +void +rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + RPCB rpcbproc_set_3_arg; + RPCB rpcbproc_unset_3_arg; + RPCB rpcbproc_getaddr_3_local_arg; + struct rpcb_rmtcallargs rpcbproc_callit_3_arg; + char *rpcbproc_uaddr2taddr_3_arg; + struct netbuf rpcbproc_taddr2uaddr_3_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + void *(*local) __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); + + rpcbs_procinfo(RPCBVERS_3_STAT, rqstp->rq_proc); + + switch (rqstp->rq_proc) { + case NULLPROC: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_NULL\n"); +#endif + /* This call just logs, no actual checks */ + check_access(transp, rqstp->rq_proc, NULL, RPCBVERS); + (void) svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL); + return; + + case RPCBPROC_SET: + xdr_argument = (xdrproc_t )xdr_rpcb; + xdr_result = (xdrproc_t )xdr_bool; + local = rpcbproc_set_com; + break; + + case RPCBPROC_UNSET: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_unset_com; + break; + + case RPCBPROC_GETADDR: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getaddr_3_local; + break; + + case RPCBPROC_DUMP: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_DUMP\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcblist_ptr; + local = rpcbproc_dump_3_local; + break; + + case RPCBPROC_CALLIT: + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS); + return; + + case RPCBPROC_GETTIME: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETTIME\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_u_long; + local = rpcbproc_gettime_com; + break; + + case RPCBPROC_UADDR2TADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_wrapstring; + xdr_result = (xdrproc_t)xdr_netbuf; + local = rpcbproc_uaddr2taddr_com; + break; + + case RPCBPROC_TADDR2UADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_netbuf; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_taddr2uaddr_com; + break; + + default: + svcerr_noproc(transp); + return; + } + (void) memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, (xdrproc_t) xdr_argument, + (char *) &argument)) { + svcerr_decode(transp); + if (debugging) + (void) fprintf(stderr, "rpcbind: could not decode\n"); + return; + } + if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS)) { + svcerr_weakauth(transp); + goto done; + } + result = (*local)(&argument, rqstp, transp, RPCBVERS); + if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, + result)) { + svcerr_systemerr(transp); + if (debugging) { + (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +done: + if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *) + &argument)) { + if (debugging) { + (void) fprintf(stderr, "unable to free arguments\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* ARGSUSED */ +static void * +rpcbproc_getaddr_3_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t versnum) +{ + RPCB *regp = (RPCB *)arg; +#ifdef RPCBIND_DEBUG + if (debugging) { + char *uaddr; + + uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), + svc_getrpccaller(transp)); + fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ", + (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, + regp->r_netid, uaddr); + free(uaddr); + } +#endif + return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS, + RPCB_ALLVERS)); +} + +/* ARGSUSED */ +static void * +rpcbproc_dump_3_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t versnum) +{ + return ((void *)&list_rbl); +} diff --git a/usr.sbin/rpcbind/rpcb_svc_4.c b/usr.sbin/rpcbind/rpcb_svc_4.c new file mode 100644 index 0000000..5f14474 --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc_4.c @@ -0,0 +1,454 @@ +/* + * $NetBSD: rpcb_svc_4.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ + * $FreeBSD$ + */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)rpcb_svc_4.c 1.8 93/07/05 SMI" */ + +/* + * rpcb_svc_4.c + * The server procedure for the version 4 rpcbind. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <rpc/rpc.h> +#include <stdio.h> +#include <unistd.h> +#include <netconfig.h> +#include <syslog.h> +#include <string.h> +#include <stdlib.h> +#include "rpcbind.h" + +static void *rpcbproc_getaddr_4_local __P((void *, struct svc_req *, SVCXPRT *, + rpcvers_t)); +static void *rpcbproc_getversaddr_4_local __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); +static void *rpcbproc_getaddrlist_4_local + __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); +static void free_rpcb_entry_list __P((rpcb_entry_list_ptr *)); +static void *rpcbproc_dump_4_local __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); + +/* + * Called by svc_getreqset. There is a separate server handle for + * every transport that it waits on. + */ +void +rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + rpcb rpcbproc_set_4_arg; + rpcb rpcbproc_unset_4_arg; + rpcb rpcbproc_getaddr_4_local_arg; + char *rpcbproc_uaddr2taddr_4_arg; + struct netbuf rpcbproc_taddr2uaddr_4_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + void *(*local) __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); + + rpcbs_procinfo(RPCBVERS_4_STAT, rqstp->rq_proc); + + switch (rqstp->rq_proc) { + case NULLPROC: + /* + * Null proc call + */ +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_NULL\n"); +#endif + check_access(transp, rqstp->rq_proc, NULL, RPCBVERS4); + (void) svc_sendreply(transp, (xdrproc_t) xdr_void, + (char *)NULL); + return; + + case RPCBPROC_SET: + /* + * Check to see whether the message came from + * loopback transports (for security reasons) + */ + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_set_com; + break; + + case RPCBPROC_UNSET: + /* + * Check to see whether the message came from + * loopback transports (for security reasons) + */ + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_bool; + local = rpcbproc_unset_com; + break; + + case RPCBPROC_GETADDR: + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getaddr_4_local; + break; + + case RPCBPROC_GETVERSADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETVERSADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_getversaddr_4_local; + break; + + case RPCBPROC_DUMP: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_DUMP\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcblist_ptr; + local = rpcbproc_dump_4_local; + break; + + case RPCBPROC_INDIRECT: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_INDIRECT\n"); +#endif + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); + return; + +/* case RPCBPROC_CALLIT: */ + case RPCBPROC_BCAST: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_BCAST\n"); +#endif + rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4); + return; + + case RPCBPROC_GETTIME: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETTIME\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_u_long; + local = rpcbproc_gettime_com; + break; + + case RPCBPROC_UADDR2TADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_UADDR2TADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_wrapstring; + xdr_result = (xdrproc_t)xdr_netbuf; + local = rpcbproc_uaddr2taddr_com; + break; + + case RPCBPROC_TADDR2UADDR: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_TADDR2UADDR\n"); +#endif + xdr_argument = (xdrproc_t)xdr_netbuf; + xdr_result = (xdrproc_t)xdr_wrapstring; + local = rpcbproc_taddr2uaddr_com; + break; + + case RPCBPROC_GETADDRLIST: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETADDRLIST\n"); +#endif + xdr_argument = (xdrproc_t)xdr_rpcb; + xdr_result = (xdrproc_t)xdr_rpcb_entry_list_ptr; + local = rpcbproc_getaddrlist_4_local; + break; + + case RPCBPROC_GETSTAT: +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCBPROC_GETSTAT\n"); +#endif + xdr_argument = (xdrproc_t)xdr_void; + xdr_result = (xdrproc_t)xdr_rpcb_stat_byvers; + local = rpcbproc_getstat; + break; + + default: + svcerr_noproc(transp); + return; + } + memset((char *)&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, (xdrproc_t) xdr_argument, + (char *)&argument)) { + svcerr_decode(transp); + if (debugging) + (void) fprintf(stderr, "rpcbind: could not decode\n"); + return; + } + if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS4)) { + svcerr_weakauth(transp); + goto done; + } + result = (*local)(&argument, rqstp, transp, RPCBVERS4); + if (result != NULL && !svc_sendreply(transp, (xdrproc_t) xdr_result, + result)) { + svcerr_systemerr(transp); + if (debugging) { + (void) fprintf(stderr, "rpcbind: svc_sendreply\n"); + if (doabort) { + rpcbind_abort(); + } + } + } +done: + if (!svc_freeargs(transp, (xdrproc_t) xdr_argument, + (char *)&argument)) { + if (debugging) { + (void) fprintf(stderr, "unable to free arguments\n"); + if (doabort) { + rpcbind_abort(); + } + } + } + return; +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * Even if a service with a different version number is available, + * it will return that address. The client should check with an + * clnt_call to verify whether the service is the one that is desired. + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* ARGSUSED */ +static void * +rpcbproc_getaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + RPCB *regp = (RPCB *)arg; +#ifdef RPCBIND_DEBUG + if (debugging) { + char *uaddr; + + uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), + svc_getrpccaller(transp)); + fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ", + (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, + regp->r_netid, uaddr); + free(uaddr); + } +#endif + return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4, + RPCB_ALLVERS)); +} + +/* + * Lookup the mapping for a program, version and return its + * address. Assuming that the caller wants the address of the + * server running on the transport on which the request came. + * + * We also try to resolve the universal address in terms of + * address of the caller. + */ +/* ARGSUSED */ +static void * +rpcbproc_getversaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t versnum) +{ + RPCB *regp = (RPCB *)arg; +#ifdef RPCBIND_DEBUG + if (debugging) { + char *uaddr; + + uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), + svc_getrpccaller(transp)); + fprintf(stderr, "RPCB_GETVERSADDR rqst for (%lu, %lu, %s)" + " from %s : ", + (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, + regp->r_netid, uaddr); + free(uaddr); + } +#endif + return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4, + RPCB_ONEVERS)); +} + +/* + * Lookup the mapping for a program, version and return the + * addresses for all transports in the current transport family. + * We return a merged address. + */ +/* ARGSUSED */ +static void * +rpcbproc_getaddrlist_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t versnum) +{ + RPCB *regp = (RPCB *)arg; + static rpcb_entry_list_ptr rlist; + register rpcblist_ptr rbl; + rpcb_entry_list_ptr rp, tail; + rpcprog_t prog; + rpcvers_t vers; + rpcb_entry *a; + struct netconfig *nconf; + struct netconfig *reg_nconf; + char *saddr, *maddr = NULL; + + free_rpcb_entry_list(&rlist); + prog = regp->r_prog; + vers = regp->r_vers; + reg_nconf = rpcbind_get_conf(transp->xp_netid); + if (reg_nconf == NULL) + return (NULL); + if (*(regp->r_addr) != '\0') { + saddr = regp->r_addr; + } else { + saddr = NULL; + } +#ifdef RPCBIND_DEBUG + if (debugging) { + fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n", + regp->r_addr, regp->r_netid, reg_nconf->nc_protofmly); + } +#endif + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog == prog) && + (rbl->rpcb_map.r_vers == vers)) { + nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid); + if (nconf == NULL) + goto fail; + if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly) + != 0) { + continue; /* not same proto family */ + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "\tmerge with: %s", rbl->rpcb_map.r_addr); +#endif + if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid, + rbl->rpcb_map.r_addr, saddr)) == NULL) { +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, " FAILED\n"); +#endif + continue; + } else if (!maddr[0]) { +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, " SUCCEEDED, but port died - maddr: nullstring\n"); +#endif + /* The server died. Unset this combination */ + delete_prog(regp->r_prog); + continue; + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, " SUCCEEDED maddr: %s\n", maddr); +#endif + /* + * Add it to rlist. + */ + rp = (rpcb_entry_list_ptr) + malloc((u_int)sizeof (rpcb_entry_list)); + if (rp == NULL) + goto fail; + a = &rp->rpcb_entry_map; + a->r_maddr = maddr; + a->r_nc_netid = nconf->nc_netid; + a->r_nc_semantics = nconf->nc_semantics; + a->r_nc_protofmly = nconf->nc_protofmly; + a->r_nc_proto = nconf->nc_proto; + rp->rpcb_entry_next = NULL; + if (rlist == NULL) { + rlist = rp; + tail = rp; + } else { + tail->rpcb_entry_next = rp; + tail = rp; + } + rp = NULL; + } + } +#ifdef RPCBIND_DEBUG + if (debugging) { + for (rp = rlist; rp; rp = rp->rpcb_entry_next) { + fprintf(stderr, "\t%s %s\n", rp->rpcb_entry_map.r_maddr, + rp->rpcb_entry_map.r_nc_proto); + } + } +#endif + /* + * XXX: getaddrlist info is also being stuffed into getaddr. + * Perhaps wrong, but better than it not getting counted at all. + */ + rpcbs_getaddr(RPCBVERS4 - 2, prog, vers, transp->xp_netid, maddr); + return (void *)&rlist; + +fail: free_rpcb_entry_list(&rlist); + return (NULL); +} + +/* + * Free only the allocated structure, rest is all a pointer to some + * other data somewhere else. + */ +static void +free_rpcb_entry_list(rpcb_entry_list_ptr *rlistp) +{ + register rpcb_entry_list_ptr rbl, tmp; + + for (rbl = *rlistp; rbl != NULL; ) { + tmp = rbl; + rbl = rbl->rpcb_entry_next; + free((char *)tmp->rpcb_entry_map.r_maddr); + free((char *)tmp); + } + *rlistp = NULL; +} + +/* ARGSUSED */ +static void * +rpcbproc_dump_4_local(void *arg, struct svc_req *req, SVCXPRT *xprt, + rpcvers_t versnum) +{ + return ((void *)&list_rbl); +} diff --git a/usr.sbin/rpcbind/rpcb_svc_com.c b/usr.sbin/rpcbind/rpcb_svc_com.c new file mode 100644 index 0000000..e0c7487 --- /dev/null +++ b/usr.sbin/rpcbind/rpcb_svc_com.c @@ -0,0 +1,1457 @@ +/* $NetBSD: rpcb_svc_com.c,v 1.6 2000/08/03 00:07:22 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)rpcb_svc_com.c 1.18 94/05/02 SMI" */ + +/* + * rpcb_svc_com.c + * The commom server procedure for the rpcbind. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/svc_dg.h> +#include <netconfig.h> +#include <errno.h> +#include <syslog.h> +#include <unistd.h> +#include <stdio.h> +#ifdef PORTMAP +#include <netinet/in.h> +#include <rpc/pmap_prot.h> +#endif /* PORTMAP */ +#include <string.h> +#include <stdlib.h> + +#include "rpcbind.h" + +#define RPC_BUF_MAX 65536 /* can be raised if required */ + +static char *nullstring = ""; +static int rpcb_rmtcalls; + +struct rmtcallfd_list { + int fd; + SVCXPRT *xprt; + char *netid; + struct rmtcallfd_list *next; +}; + +#define NFORWARD 64 +#define MAXTIME_OFF 300 /* 5 minutes */ + +struct finfo { + int flag; +#define FINFO_ACTIVE 0x1 + u_int32_t caller_xid; + struct netbuf *caller_addr; + u_int32_t forward_xid; + int forward_fd; + char *uaddr; + rpcproc_t reply_type; + rpcvers_t versnum; + time_t time; +}; +static struct finfo FINFO[NFORWARD]; + + +static bool_t xdr_encap_parms __P((XDR *, struct encap_parms *)); +static bool_t xdr_rmtcall_args __P((XDR *, struct r_rmtcall_args *)); +static bool_t xdr_rmtcall_result __P((XDR *, struct r_rmtcall_args *)); +static bool_t xdr_opaque_parms __P((XDR *, struct r_rmtcall_args *)); +static int find_rmtcallfd_by_netid __P((char *)); +static SVCXPRT *find_rmtcallxprt_by_fd __P((int)); +static u_int32_t forward_register __P((u_int32_t, struct netbuf *, int, char *, + rpcproc_t, rpcvers_t)); +static struct finfo *forward_find __P((u_int32_t)); +static int free_slot_by_xid __P((u_int32_t)); +static int free_slot_by_index __P((int)); +static int netbufcmp __P((struct netbuf *, struct netbuf *)); +static struct netbuf *netbufdup __P((struct netbuf *)); +static void netbuffree __P((struct netbuf *)); +static int check_rmtcalls __P((struct pollfd *, int)); +static void xprt_set_caller __P((SVCXPRT *, struct finfo *)); +static void send_svcsyserr __P((SVCXPRT *, struct finfo *)); +static void handle_reply __P((int, SVCXPRT *)); +static void find_versions __P((rpcprog_t, char *, rpcvers_t *, rpcvers_t *)); +static rpcblist_ptr find_service __P((rpcprog_t, rpcvers_t, char *)); +static char *getowner __P((SVCXPRT *, char *, size_t)); +static int add_pmaplist __P((RPCB *)); +static int del_pmaplist __P((RPCB *)); + +/* + * Set a mapping of program, version, netid + */ +/* ARGSUSED */ +void * +rpcbproc_set_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + RPCB *regp = (RPCB *)arg; + static bool_t ans; + char owner[64]; + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCB_SET request for (%lu, %lu, %s, %s) : ", + (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, + regp->r_netid, regp->r_addr); +#endif + ans = map_set(regp, getowner(transp, owner, sizeof owner)); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_set(rpcbversnum - 2, ans); + return (void *)&ans; +} + +bool_t +map_set(RPCB *regp, char *owner) +{ + RPCB reg, *a; + rpcblist_ptr rbl, fnd; + + reg = *regp; + /* + * check to see if already used + * find_service returns a hit even if + * the versions don't match, so check for it + */ + fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid); + if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) { + if (!strcmp(fnd->rpcb_map.r_addr, reg.r_addr)) + /* + * if these match then it is already + * registered so just say "OK". + */ + return (TRUE); + else + return (FALSE); + } + /* + * add to the end of the list + */ + rbl = (rpcblist_ptr) malloc((u_int)sizeof (RPCBLIST)); + if (rbl == (rpcblist_ptr)NULL) { + return (FALSE); + } + a = &(rbl->rpcb_map); + a->r_prog = reg.r_prog; + a->r_vers = reg.r_vers; + a->r_netid = strdup(reg.r_netid); + a->r_addr = strdup(reg.r_addr); + a->r_owner = strdup(owner); + if (!a->r_addr || !a->r_netid || !a->r_owner) { + if (a->r_netid) + free((void *) a->r_netid); + if (a->r_addr) + free((void *) a->r_addr); + if (a->r_owner) + free((void *) a->r_owner); + free((void *)rbl); + return (FALSE); + } + rbl->rpcb_next = (rpcblist_ptr)NULL; + if (list_rbl == NULL) { + list_rbl = rbl; + } else { + for (fnd = list_rbl; fnd->rpcb_next; + fnd = fnd->rpcb_next) + ; + fnd->rpcb_next = rbl; + } +#ifdef PORTMAP + (void) add_pmaplist(regp); +#endif + return (TRUE); +} + +/* + * Unset a mapping of program, version, netid + */ +/* ARGSUSED */ +void * +rpcbproc_unset_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + RPCB *regp = (RPCB *)arg; + static bool_t ans; + char owner[64]; + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "RPCB_UNSET request for (%lu, %lu, %s) : ", + (unsigned long)regp->r_prog, (unsigned long)regp->r_vers, + regp->r_netid); +#endif + ans = map_unset(regp, getowner(transp, owner, sizeof owner)); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed"); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_unset(rpcbversnum - 2, ans); + return (void *)&ans; +} + +bool_t +map_unset(RPCB *regp, char *owner) +{ + int ans = 0; + rpcblist_ptr rbl, prev, tmp; + + if (owner == NULL) + return (0); + + for (prev = NULL, rbl = list_rbl; rbl; /* cstyle */) { + if ((rbl->rpcb_map.r_prog != regp->r_prog) || + (rbl->rpcb_map.r_vers != regp->r_vers) || + (regp->r_netid[0] && strcasecmp(regp->r_netid, + rbl->rpcb_map.r_netid))) { + /* both rbl & prev move forwards */ + prev = rbl; + rbl = rbl->rpcb_next; + continue; + } + /* + * Check whether appropriate uid. Unset only + * if superuser or the owner itself. + */ + if (strcmp(owner, "superuser") && + strcmp(rbl->rpcb_map.r_owner, owner)) + return (0); + /* found it; rbl moves forward, prev stays */ + ans = 1; + tmp = rbl; + rbl = rbl->rpcb_next; + if (prev == NULL) + list_rbl = rbl; + else + prev->rpcb_next = rbl; + free((void *) tmp->rpcb_map.r_addr); + free((void *) tmp->rpcb_map.r_netid); + free((void *) tmp->rpcb_map.r_owner); + free((void *) tmp); + } +#ifdef PORTMAP + if (ans) + (void) del_pmaplist(regp); +#endif + /* + * We return 1 either when the entry was not there or it + * was able to unset it. It can come to this point only if + * atleast one of the conditions is true. + */ + return (1); +} + +void +delete_prog(int prog) +{ + RPCB reg; + register rpcblist_ptr rbl; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog)) + continue; + if (is_bound(rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr)) + continue; + reg.r_prog = rbl->rpcb_map.r_prog; + reg.r_vers = rbl->rpcb_map.r_vers; + reg.r_netid = strdup(rbl->rpcb_map.r_netid); + (void) map_unset(®, "superuser"); + free(reg.r_netid); + } +} + +void * +rpcbproc_getaddr_com(RPCB *regp, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum, rpcvers_t verstype) +{ + static char *uaddr; + char *saddr = NULL; + rpcblist_ptr fnd; + + if (uaddr && uaddr[0]) + free((void *) uaddr); + fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid); + if (fnd && ((verstype == RPCB_ALLVERS) || + (regp->r_vers == fnd->rpcb_map.r_vers))) { + if (*(regp->r_addr) != '\0') { /* may contain a hint about */ + saddr = regp->r_addr; /* the interface that we */ + } /* should use */ + if (!(uaddr = mergeaddr(transp, transp->xp_netid, + fnd->rpcb_map.r_addr, saddr))) { + /* Try whatever we have */ + uaddr = strdup(fnd->rpcb_map.r_addr); + } else if (!uaddr[0]) { + /* + * The server died. Unset all versions of this prog. + */ + delete_prog(regp->r_prog); + uaddr = nullstring; + } + } else { + uaddr = nullstring; + } +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "getaddr: %s\n", uaddr); +#endif + /* XXX: should have used some defined constant here */ + rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers, + transp->xp_netid, uaddr); + return (void *)&uaddr; +} + +/* ARGSUSED */ +void * +rpcbproc_gettime_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + static time_t curtime; + + (void) time(&curtime); + return (void *)&curtime; +} + +/* + * Convert uaddr to taddr. Should be used only by + * local servers/clients. (kernel level stuff only) + */ +/* ARGSUSED */ +void * +rpcbproc_uaddr2taddr_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + char **uaddrp = (char **)arg; + struct netconfig *nconf; + static struct netbuf nbuf; + static struct netbuf *taddr; + + if (taddr) { + free((void *) taddr->buf); + free((void *) taddr); + } + if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || + ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) { + (void) memset((char *)&nbuf, 0, sizeof (struct netbuf)); + return (void *)&nbuf; + } + return (void *)taddr; +} + +/* + * Convert taddr to uaddr. Should be used only by + * local servers/clients. (kernel level stuff only) + */ +/* ARGSUSED */ +void * +rpcbproc_taddr2uaddr_com(void *arg, struct svc_req *rqstp, SVCXPRT *transp, + rpcvers_t rpcbversnum) +{ + struct netbuf *taddr = (struct netbuf *)arg; + static char *uaddr; + struct netconfig *nconf; + +#ifdef CHEW_FDS + int fd; + + if ((fd = open("/dev/null", O_RDONLY)) == -1) { + uaddr = (char *)strerror(errno); + return (&uaddr); + } +#endif /* CHEW_FDS */ + if (uaddr && !uaddr[0]) + free((void *) uaddr); + if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) || + ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) { + uaddr = nullstring; + } + return (void *)&uaddr; +} + + +static bool_t +xdr_encap_parms(XDR *xdrs, struct encap_parms *epp) +{ + return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), ~0)); +} + +/* + * XDR remote call arguments. It ignores the address part. + * written for XDR_DECODE direction only + */ +static bool_t +xdr_rmtcall_args(XDR *xdrs, struct r_rmtcall_args *cap) +{ + /* does not get the address or the arguments */ + if (xdr_u_int32_t(xdrs, &(cap->rmt_prog)) && + xdr_u_int32_t(xdrs, &(cap->rmt_vers)) && + xdr_u_int32_t(xdrs, &(cap->rmt_proc))) { + return (xdr_encap_parms(xdrs, &(cap->rmt_args))); + } + return (FALSE); +} + +/* + * XDR remote call results along with the address. Ignore + * program number, version number and proc number. + * Written for XDR_ENCODE direction only. + */ +static bool_t +xdr_rmtcall_result(XDR *xdrs, struct r_rmtcall_args *cap) +{ + bool_t result; + +#ifdef PORTMAP + if (cap->rmt_localvers == PMAPVERS) { + int h1, h2, h3, h4, p1, p2; + u_long port; + + /* interpret the universal address for TCP/IP */ + if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d", + &h1, &h2, &h3, &h4, &p1, &p2) != 6) + return (FALSE); + port = ((p1 & 0xff) << 8) + (p2 & 0xff); + result = xdr_u_long(xdrs, &port); + } else +#endif + if ((cap->rmt_localvers == RPCBVERS) || + (cap->rmt_localvers == RPCBVERS4)) { + result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr)); + } else { + return (FALSE); + } + if (result == TRUE) + return (xdr_encap_parms(xdrs, &(cap->rmt_args))); + return (FALSE); +} + +/* + * only worries about the struct encap_parms part of struct r_rmtcall_args. + * The arglen must already be set!! + */ +static bool_t +xdr_opaque_parms(XDR *xdrs, struct r_rmtcall_args *cap) +{ + return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen)); +} + +static struct rmtcallfd_list *rmthead; +static struct rmtcallfd_list *rmttail; + +int +create_rmtcall_fd(struct netconfig *nconf) +{ + int fd; + struct rmtcallfd_list *rmt; + SVCXPRT *xprt; + + if ((fd = __rpc_nconf2fd(nconf)) == -1) { + if (debugging) + fprintf(stderr, + "create_rmtcall_fd: couldn't open \"%s\" (errno %d)\n", + nconf->nc_device, errno); + return (-1); + } + xprt = svc_tli_create(fd, 0, (struct t_bind *) 0, 0, 0); + if (xprt == NULL) { + if (debugging) + fprintf(stderr, + "create_rmtcall_fd: svc_tli_create failed\n"); + return (-1); + } + rmt = (struct rmtcallfd_list *)malloc((u_int) + sizeof (struct rmtcallfd_list)); + if (rmt == NULL) { + syslog(LOG_ERR, "create_rmtcall_fd: no memory!"); + return (-1); + } + rmt->xprt = xprt; + rmt->netid = strdup(nconf->nc_netid); + xprt->xp_netid = rmt->netid; + rmt->fd = fd; + rmt->next = NULL; + if (rmthead == NULL) { + rmthead = rmt; + rmttail = rmt; + } else { + rmttail->next = rmt; + rmttail = rmt; + } + /* XXX not threadsafe */ + if (fd > svc_maxfd) + svc_maxfd = fd; + FD_SET(fd, &svc_fdset); + return (fd); +} + +static int +find_rmtcallfd_by_netid(char *netid) +{ + struct rmtcallfd_list *rmt; + + for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { + if (strcmp(netid, rmt->netid) == 0) { + return (rmt->fd); + } + } + return (-1); +} + +static SVCXPRT * +find_rmtcallxprt_by_fd(int fd) +{ + struct rmtcallfd_list *rmt; + + for (rmt = rmthead; rmt != NULL; rmt = rmt->next) { + if (fd == rmt->fd) { + return (rmt->xprt); + } + } + return (NULL); +} + + +/* + * Call a remote procedure service. This procedure is very quiet when things + * go wrong. The proc is written to support broadcast rpc. In the broadcast + * case, a machine should shut-up instead of complain, lest the requestor be + * overrun with complaints at the expense of not hearing a valid reply. + * When receiving a request and verifying that the service exists, we + * + * receive the request + * + * open a new TLI endpoint on the same transport on which we received + * the original request + * + * remember the original request's XID (which requires knowing the format + * of the svc_dg_data structure) + * + * forward the request, with a new XID, to the requested service, + * remembering the XID used to send this request (for later use in + * reassociating the answer with the original request), the requestor's + * address, the file descriptor on which the forwarded request is + * made and the service's address. + * + * mark the file descriptor on which we anticipate receiving a reply from + * the service and one to select for in our private svc_run procedure + * + * At some time in the future, a reply will be received from the service to + * which we forwarded the request. At that time, we detect that the socket + * used was for forwarding (by looking through the finfo structures to see + * whether the fd corresponds to one of those) and call handle_reply() to + * + * receive the reply + * + * bundle the reply, along with the service's universal address + * + * create a SVCXPRT structure and use a version of svc_sendreply + * that allows us to specify the reply XID and destination, send the reply + * to the original requestor. + */ + +void +rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp, + rpcproc_t reply_type, rpcvers_t versnum) +{ + register rpcblist_ptr rbl; + struct netconfig *nconf; + struct netbuf *caller; + struct r_rmtcall_args a; + char *buf_alloc = NULL, *outbufp; + char *outbuf_alloc = NULL; + char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX]; + struct netbuf *na = (struct netbuf *) NULL; + struct rpc_msg call_msg; + int outlen; + u_int sendsz; + XDR outxdr; + AUTH *auth; + int fd = -1; + char *uaddr, *m_uaddr, *local_uaddr = NULL; + u_int32_t *xidp; + struct __rpc_sockinfo si; + struct sockaddr *localsa; + struct netbuf tbuf; + + if (!__rpc_fd2sockinfo(transp->xp_fd, &si)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + if (si.si_socktype != SOCK_DGRAM) + return; /* Only datagram type accepted */ + sendsz = __rpc_get_t_size(si.si_af, si.si_proto, UDPMSGSIZE); + if (sendsz == 0) { /* data transfer not supported */ + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + /* + * Should be multiple of 4 for XDR. + */ + sendsz = ((sendsz + 3) / 4) * 4; + if (sendsz > RPC_BUF_MAX) { +#ifdef notyet + buf_alloc = alloca(sendsz); /* not in IDR2? */ +#else + buf_alloc = malloc(sendsz); +#endif /* notyet */ + if (buf_alloc == NULL) { + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: No Memory!\n"); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + return; + } + a.rmt_args.args = buf_alloc; + } else { + a.rmt_args.args = buf; + } + + call_msg.rm_xid = 0; /* For error checking purposes */ + if (!svc_getargs(transp, (xdrproc_t) xdr_rmtcall_args, (char *) &a)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_decode(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: svc_getargs failed\n"); + goto error; + } + + if (!check_callit(transp, &a, versnum)) { + svcerr_weakauth(transp); + goto error; + } + + caller = svc_getrpccaller(transp); +#ifdef RPCBIND_DEBUG + if (debugging) { + uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller); + fprintf(stderr, "%s %s req for (%lu, %lu, %lu, %s) from %s : ", + versnum == PMAPVERS ? "pmap_rmtcall" : + versnum == RPCBVERS ? "rpcb_rmtcall" : + versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown", + reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit", + (unsigned long)a.rmt_prog, (unsigned long)a.rmt_vers, + (unsigned long)a.rmt_proc, transp->xp_netid, + uaddr ? uaddr : "unknown"); + if (uaddr) + free((void *) uaddr); + } +#endif + + rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid); + + rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers, + a.rmt_proc, transp->xp_netid, rbl); + + if (rbl == (rpcblist_ptr)NULL) { +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "not found\n"); +#endif + if (reply_type == RPCBPROC_INDIRECT) + svcerr_noprog(transp); + goto error; + } + if (rbl->rpcb_map.r_vers != a.rmt_vers) { + if (reply_type == RPCBPROC_INDIRECT) { + rpcvers_t vers_low, vers_high; + + find_versions(a.rmt_prog, transp->xp_netid, + &vers_low, &vers_high); + svcerr_progvers(transp, vers_low, vers_high); + } + goto error; + } + +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr); +#endif + /* + * Check whether this entry is valid and a server is present + * Mergeaddr() returns NULL if no such entry is present, and + * returns "" if the entry was present but the server is not + * present (i.e., it crashed). + */ + if (reply_type == RPCBPROC_INDIRECT) { + uaddr = mergeaddr(transp, transp->xp_netid, + rbl->rpcb_map.r_addr, NULL); + if ((uaddr == (char *) NULL) || uaddr[0] == '\0') { + svcerr_noprog(transp); + if (uaddr != NULL) { + free((void *) uaddr); + } + goto error; + } + if (uaddr != NULL) { + free((void *) uaddr); + } + } + nconf = rpcbind_get_conf(transp->xp_netid); + if (nconf == (struct netconfig *)NULL) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: rpcbind_get_conf failed\n"); + goto error; + } + localsa = local_sa(((struct sockaddr *)caller->buf)->sa_family); + if (localsa == NULL) { + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: no local address\n"); + goto error; + } + tbuf.len = tbuf.maxlen = localsa->sa_len; + tbuf.buf = localsa; + local_uaddr = + addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid); + m_uaddr = addrmerge(caller, rbl->rpcb_map.r_addr, NULL, + nconf->nc_netid); +#ifdef RPCBIND_DEBUG + if (debugging) + fprintf(stderr, "merged uaddr %s\n", m_uaddr); +#endif + if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + free((void *) m_uaddr); + goto error; + } + xidp = __rpcb_get_dg_xidp(transp); + call_msg.rm_xid = forward_register(*xidp, + caller, fd, m_uaddr, reply_type, versnum); + if (call_msg.rm_xid == 0) { + /* + * A duplicate request for the slow server. Let's not + * beat on it any more. + */ + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: duplicate request\n"); + free((void *) m_uaddr); + goto error; + } else if (call_msg.rm_xid == -1) { + /* forward_register failed. Perhaps no memory. */ + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: forward_register failed\n"); + free((void *) m_uaddr); + goto error; + } + +#ifdef DEBUG_RMTCALL + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: original XID %x, new XID %x\n", + *xidp, call_msg.rm_xid); +#endif + call_msg.rm_direction = CALL; + call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; + call_msg.rm_call.cb_prog = a.rmt_prog; + call_msg.rm_call.cb_vers = a.rmt_vers; + if (sendsz > RPC_BUF_MAX) { +#ifdef notyet + outbuf_alloc = alloca(sendsz); /* not in IDR2? */ +#else + outbuf_alloc = malloc(sendsz); +#endif /* notyet */ + if (outbuf_alloc == NULL) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: No memory!\n"); + goto error; + } + xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE); + } else { + xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE); + } + if (!xdr_callhdr(&outxdr, &call_msg)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: xdr_callhdr failed\n"); + goto error; + } + if (!xdr_u_int32_t(&outxdr, &(a.rmt_proc))) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: xdr_u_long failed\n"); + goto error; + } + + if (rqstp->rq_cred.oa_flavor == AUTH_NULL) { + auth = authnone_create(); + } else if (rqstp->rq_cred.oa_flavor == AUTH_SYS) { + struct authunix_parms *au; + + au = (struct authunix_parms *)rqstp->rq_clntcred; + auth = authunix_create(au->aup_machname, + au->aup_uid, au->aup_gid, + au->aup_len, au->aup_gids); + if (auth == NULL) /* fall back */ + auth = authnone_create(); + } else { + /* we do not support any other authentication scheme */ + if (debugging) + fprintf(stderr, +"rpcbproc_callit_com: oa_flavor != AUTH_NONE and oa_flavor != AUTH_SYS\n"); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_weakauth(transp); /* XXX too strong.. */ + goto error; + } + if (auth == NULL) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: authwhatever_create returned NULL\n"); + goto error; + } + if (!AUTH_MARSHALL(auth, &outxdr)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + AUTH_DESTROY(auth); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: AUTH_MARSHALL failed\n"); + goto error; + } + AUTH_DESTROY(auth); + if (!xdr_opaque_parms(&outxdr, &a)) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: xdr_opaque_parms failed\n"); + goto error; + } + outlen = (int) XDR_GETPOS(&outxdr); + if (outbuf_alloc) + outbufp = outbuf_alloc; + else + outbufp = outbuf; + + na = uaddr2taddr(nconf, local_uaddr); + if (!na) { + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + + if (sendto(fd, outbufp, outlen, 0, (struct sockaddr *)na->buf, na->len) + != outlen) { + if (debugging) + fprintf(stderr, + "rpcbproc_callit_com: sendto failed: errno %d\n", errno); + if (reply_type == RPCBPROC_INDIRECT) + svcerr_systemerr(transp); + goto error; + } + goto out; + +error: + if (call_msg.rm_xid != 0) + (void) free_slot_by_xid(call_msg.rm_xid); +out: + if (local_uaddr) + free(local_uaddr); + if (buf_alloc) + free((void *) buf_alloc); + if (outbuf_alloc) + free((void *) outbuf_alloc); + if (na) { + free(na->buf); + free(na); + } +} + +/* + * Makes an entry into the FIFO for the given request. + * If duplicate request, returns a 0, else returns the xid of its call. + */ +static u_int32_t +forward_register(u_int32_t caller_xid, struct netbuf *caller_addr, + int forward_fd, char *uaddr, rpcproc_t reply_type, + rpcvers_t versnum) +{ + int i; + int j = 0; + time_t min_time, time_now; + static u_int32_t lastxid; + int entry = -1; + + min_time = FINFO[0].time; + time_now = time((time_t *)0); + /* initialization */ + if (lastxid == 0) + lastxid = time_now * NFORWARD; + + /* + * Check if it is an duplicate entry. Then, + * try to find an empty slot. If not available, then + * use the slot with the earliest time. + */ + for (i = 0; i < NFORWARD; i++) { + if (FINFO[i].flag & FINFO_ACTIVE) { + if ((FINFO[i].caller_xid == caller_xid) && + (FINFO[i].reply_type == reply_type) && + (FINFO[i].versnum == versnum) && + (!netbufcmp(FINFO[i].caller_addr, + caller_addr))) { + FINFO[i].time = time((time_t *)0); + return (0); /* Duplicate entry */ + } else { + /* Should we wait any longer */ + if ((time_now - FINFO[i].time) > MAXTIME_OFF) + (void) free_slot_by_index(i); + } + } + if (entry == -1) { + if ((FINFO[i].flag & FINFO_ACTIVE) == 0) { + entry = i; + } else if (FINFO[i].time < min_time) { + j = i; + min_time = FINFO[i].time; + } + } + } + if (entry != -1) { + /* use this empty slot */ + j = entry; + } else { + (void) free_slot_by_index(j); + } + if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) { + return (-1); + } + rpcb_rmtcalls++; /* no of pending calls */ + FINFO[j].flag = FINFO_ACTIVE; + FINFO[j].reply_type = reply_type; + FINFO[j].versnum = versnum; + FINFO[j].time = time_now; + FINFO[j].caller_xid = caller_xid; + FINFO[j].forward_fd = forward_fd; + /* + * Though uaddr is not allocated here, it will still be freed + * from free_slot_*(). + */ + FINFO[j].uaddr = uaddr; + lastxid = lastxid + NFORWARD; + FINFO[j].forward_xid = lastxid + j; /* encode slot */ + return (FINFO[j].forward_xid); /* forward on this xid */ +} + +static struct finfo * +forward_find(u_int32_t reply_xid) +{ + int i; + + i = reply_xid % NFORWARD; + if (i < 0) + i += NFORWARD; + if ((FINFO[i].flag & FINFO_ACTIVE) && + (FINFO[i].forward_xid == reply_xid)) { + return (&FINFO[i]); + } + return (NULL); +} + +static int +free_slot_by_xid(u_int32_t xid) +{ + int entry; + + entry = xid % NFORWARD; + if (entry < 0) + entry += NFORWARD; + return (free_slot_by_index(entry)); +} + +static int +free_slot_by_index(int index) +{ + struct finfo *fi; + + fi = &FINFO[index]; + if (fi->flag & FINFO_ACTIVE) { + netbuffree(fi->caller_addr); + /* XXX may be too big, but can't access xprt array here */ + if (fi->forward_fd >= svc_maxfd) + svc_maxfd--; + free((void *) fi->uaddr); + fi->flag &= ~FINFO_ACTIVE; + rpcb_rmtcalls--; + return (1); + } + return (0); +} + +static int +netbufcmp(struct netbuf *n1, struct netbuf *n2) +{ + return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len)); +} + +static struct netbuf * +netbufdup(struct netbuf *ap) +{ + struct netbuf *np; + + np = (struct netbuf *) malloc(sizeof (struct netbuf) + ap->len); + if (np) { + np->maxlen = np->len = ap->len; + np->buf = ((char *) np) + sizeof (struct netbuf); + (void) memcpy(np->buf, ap->buf, ap->len); + } + return (np); +} + +static void +netbuffree(struct netbuf *ap) +{ + free((void *) ap); +} + + +#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND) + +void +my_svc_run() +{ + size_t nfds; + struct pollfd pollfds[FD_SETSIZE]; + int poll_ret, check_ret; + int n; +#ifdef SVC_RUN_DEBUG + int i; +#endif + register struct pollfd *p; + + for (;;) { + p = pollfds; + for (n = 0; n <= svc_maxfd; n++) { + if (FD_ISSET(n, &svc_fdset)) { + p->fd = n; + p->events = MASKVAL; + p++; + } + } + nfds = p - pollfds; + poll_ret = 0; +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "polling for read on fd < "); + for (i = 0, p = pollfds; i < nfds; i++, p++) + if (p->events) + fprintf(stderr, "%d ", p->fd); + fprintf(stderr, ">\n"); + } +#endif + switch (poll_ret = poll(pollfds, nfds, INFTIM)) { + case -1: + /* + * We ignore all errors, continuing with the assumption + * that it was set by the signal handlers (or any + * other outside event) and not caused by poll(). + */ + case 0: + continue; + default: +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "poll returned read fds < "); + for (i = 0, p = pollfds; i < nfds; i++, p++) + if (p->revents) + fprintf(stderr, "%d ", p->fd); + fprintf(stderr, ">\n"); + } +#endif + /* + * If we found as many replies on callback fds + * as the number of descriptors selectable which + * poll() returned, there can be no more so we + * don't call svc_getreq_poll. Otherwise, there + * must be another so we must call svc_getreq_poll. + */ + if ((check_ret = check_rmtcalls(pollfds, nfds)) == + poll_ret) + continue; + svc_getreq_poll(pollfds, poll_ret-check_ret); + } +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "svc_maxfd now %u\n", svc_maxfd); + } +#endif + } +} + +static int +check_rmtcalls(struct pollfd *pfds, int nfds) +{ + int j, ncallbacks_found = 0, rmtcalls_pending; + SVCXPRT *xprt; + + if (rpcb_rmtcalls == 0) + return (0); + + rmtcalls_pending = rpcb_rmtcalls; + for (j = 0; j < nfds; j++) { + if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) { + if (pfds[j].revents) { + ncallbacks_found++; +#ifdef DEBUG_RMTCALL + if (debugging) + fprintf(stderr, +"my_svc_run: polled on forwarding fd %d, netid %s - calling handle_reply\n", + pfds[j].fd, xprt->xp_netid); +#endif + handle_reply(pfds[j].fd, xprt); + pfds[j].revents = 0; + if (ncallbacks_found >= rmtcalls_pending) { + break; + } + } + } + } + return (ncallbacks_found); +} + +static void +xprt_set_caller(SVCXPRT *xprt, struct finfo *fi) +{ + u_int32_t *xidp; + + *(svc_getrpccaller(xprt)) = *(fi->caller_addr); + xidp = __rpcb_get_dg_xidp(xprt); + *xidp = fi->caller_xid; +} + +/* + * Call svcerr_systemerr() only if RPCBVERS4 + */ +static void +send_svcsyserr(SVCXPRT *xprt, struct finfo *fi) +{ + if (fi->reply_type == RPCBPROC_INDIRECT) { + xprt_set_caller(xprt, fi); + svcerr_systemerr(xprt); + } + return; +} + +static void +handle_reply(int fd, SVCXPRT *xprt) +{ + XDR reply_xdrs; + struct rpc_msg reply_msg; + struct rpc_err reply_error; + char *buffer; + struct finfo *fi; + int inlen, pos, len; + struct r_rmtcall_args a; + struct sockaddr_storage ss; + socklen_t fromlen; +#ifdef SVC_RUN_DEBUG + char *uaddr; +#endif + + buffer = malloc(RPC_BUF_MAX); + if (buffer == NULL) + goto done; + + do { + inlen = recvfrom(fd, buffer, RPC_BUF_MAX, 0, + (struct sockaddr *)&ss, &fromlen); + } while (inlen < 0 && errno == EINTR); + if (inlen < 0) { + if (debugging) + fprintf(stderr, + "handle_reply: recvfrom returned %d, errno %d\n", inlen, errno); + goto done; + } + + reply_msg.acpted_rply.ar_verf = _null_auth; + reply_msg.acpted_rply.ar_results.where = 0; + reply_msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void; + + xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE); + if (!xdr_replymsg(&reply_xdrs, &reply_msg)) { + if (debugging) + (void) fprintf(stderr, + "handle_reply: xdr_replymsg failed\n"); + goto done; + } + fi = forward_find(reply_msg.rm_xid); +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "handle_reply: reply xid: %d fi addr: %p\n", + reply_msg.rm_xid, fi); + } +#endif + if (fi == NULL) { + goto done; + } + _seterr_reply(&reply_msg, &reply_error); + if (reply_error.re_status != RPC_SUCCESS) { + if (debugging) + (void) fprintf(stderr, "handle_reply: %s\n", + clnt_sperrno(reply_error.re_status)); + send_svcsyserr(xprt, fi); + goto done; + } + pos = XDR_GETPOS(&reply_xdrs); + len = inlen - pos; + a.rmt_args.args = &buffer[pos]; + a.rmt_args.arglen = len; + a.rmt_uaddr = fi->uaddr; + a.rmt_localvers = fi->versnum; + + xprt_set_caller(xprt, fi); +#ifdef SVC_RUN_DEBUG + uaddr = taddr2uaddr(rpcbind_get_conf("udp"), + svc_getrpccaller(xprt)); + if (debugging) { + fprintf(stderr, "handle_reply: forwarding address %s to %s\n", + a.rmt_uaddr, uaddr ? uaddr : "unknown"); + } + if (uaddr) + free((void *) uaddr); +#endif + svc_sendreply(xprt, (xdrproc_t) xdr_rmtcall_result, (char *) &a); +done: + if (buffer) + free(buffer); + + if (reply_msg.rm_xid == 0) { +#ifdef SVC_RUN_DEBUG + if (debugging) { + fprintf(stderr, "handle_reply: NULL xid on exit!\n"); + } +#endif + } else + (void) free_slot_by_xid(reply_msg.rm_xid); + return; +} + +static void +find_versions(rpcprog_t prog, char *netid, rpcvers_t *lowvp, rpcvers_t *highvp) +{ + register rpcblist_ptr rbl; + int lowv = 0; + int highv = 0; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog) || + ((rbl->rpcb_map.r_netid != NULL) && + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + continue; + if (lowv == 0) { + highv = rbl->rpcb_map.r_vers; + lowv = highv; + } else if (rbl->rpcb_map.r_vers < lowv) { + lowv = rbl->rpcb_map.r_vers; + } else if (rbl->rpcb_map.r_vers > highv) { + highv = rbl->rpcb_map.r_vers; + } + } + *lowvp = lowv; + *highvp = highv; + return; +} + +/* + * returns the item with the given program, version number and netid. + * If that version number is not found, it returns the item with that + * program number, so that address is now returned to the caller. The + * caller when makes a call to this program, version number, the call + * will fail and it will return with PROGVERS_MISMATCH. The user can + * then determine the highest and the lowest version number for this + * program using clnt_geterr() and use those program version numbers. + * + * Returns the RPCBLIST for the given prog, vers and netid + */ +static rpcblist_ptr +find_service(rpcprog_t prog, rpcvers_t vers, char *netid) +{ + register rpcblist_ptr hit = NULL; + register rpcblist_ptr rbl; + + for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) { + if ((rbl->rpcb_map.r_prog != prog) || + ((rbl->rpcb_map.r_netid != NULL) && + (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0))) + continue; + hit = rbl; + if (rbl->rpcb_map.r_vers == vers) + break; + } + return (hit); +} + +/* + * Copies the name associated with the uid of the caller and returns + * a pointer to it. Similar to getwd(). + */ +static char * +getowner(SVCXPRT *transp, char *owner, size_t ownersize) +{ + struct cmsgcred *cmcred; + + cmcred = __svc_getcallercreds(transp); + if (cmcred == NULL) + strlcpy(owner, "unknown", ownersize); + else if (cmcred->cmcred_uid == 0) + strlcpy(owner, "superuser", ownersize); + else + snprintf(owner, ownersize, "%d", cmcred->cmcred_uid); + + return owner; +} + +#ifdef PORTMAP +/* + * Add this to the pmap list only if it is UDP or TCP. + */ +static int +add_pmaplist(RPCB *arg) +{ + struct pmap pmap; + struct pmaplist *pml; + int h1, h2, h3, h4, p1, p2; + + if (strcmp(arg->r_netid, udptrans) == 0) { + /* It is UDP! */ + pmap.pm_prot = IPPROTO_UDP; + } else if (strcmp(arg->r_netid, tcptrans) == 0) { + /* It is TCP */ + pmap.pm_prot = IPPROTO_TCP; + } else + /* Not a IP protocol */ + return (0); + + /* interpret the universal address for TCP/IP */ + if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d", + &h1, &h2, &h3, &h4, &p1, &p2) != 6) + return (0); + pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff); + pmap.pm_prog = arg->r_prog; + pmap.pm_vers = arg->r_vers; + /* + * add to END of list + */ + pml = (struct pmaplist *) malloc((u_int)sizeof (struct pmaplist)); + if (pml == NULL) { + (void) syslog(LOG_ERR, "rpcbind: no memory!\n"); + return (1); + } + pml->pml_map = pmap; + pml->pml_next = NULL; + if (list_pml == NULL) { + list_pml = pml; + } else { + struct pmaplist *fnd; + + /* Attach to the end of the list */ + for (fnd = list_pml; fnd->pml_next; fnd = fnd->pml_next) + ; + fnd->pml_next = pml; + } + return (0); +} + +/* + * Delete this from the pmap list only if it is UDP or TCP. + */ +static int +del_pmaplist(RPCB *arg) +{ + struct pmaplist *pml; + struct pmaplist *prevpml, *fnd; + long prot; + + if (strcmp(arg->r_netid, udptrans) == 0) { + /* It is UDP! */ + prot = IPPROTO_UDP; + } else if (strcmp(arg->r_netid, tcptrans) == 0) { + /* It is TCP */ + prot = IPPROTO_TCP; + } else if (arg->r_netid[0] == NULL) { + prot = 0; /* Remove all occurrences */ + } else { + /* Not a IP protocol */ + return (0); + } + for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) { + if ((pml->pml_map.pm_prog != arg->r_prog) || + (pml->pml_map.pm_vers != arg->r_vers) || + (prot && (pml->pml_map.pm_prot != prot))) { + /* both pml & prevpml move forwards */ + prevpml = pml; + pml = pml->pml_next; + continue; + } + /* found it; pml moves forward, prevpml stays */ + fnd = pml; + pml = pml->pml_next; + if (prevpml == NULL) + list_pml = pml; + else + prevpml->pml_next = pml; + free((void *) fnd); + } + return (0); +} +#endif /* PORTMAP */ diff --git a/usr.sbin/rpcbind/rpcbind.8 b/usr.sbin/rpcbind/rpcbind.8 new file mode 100644 index 0000000..34fea3df --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.8 @@ -0,0 +1,112 @@ +.\" @(#)rpcbind.1m 1.19 92/09/14 SMI; from SVr4 +.\" Copyright 1989 AT&T +.\" Copyright 1991 Sun Microsystems, Inc. +.\" $FreeBSD$ +.Dd September 14, 1992 +.Dt RPCBIND 8 +.Os +.Sh NAME +.Nm rpcbind +.Nd universal addresses to RPC program number mapper +.Sh SYNOPSIS +.Nm +.Op Fl dilLs +.Sh DESCRIPTION +.Nm +is a server that converts +.Tn RPC +program numbers into +universal addresses. +It must be running on the host to be able to make +.Tn RPC +calls +on a server on that machine. +.Pp +When an +.Tn RPC +service is started, +it tells +.Nm +the address at which it is listening, +and the +.Tn RPC +program numbers it is prepared to serve. +When a client wishes to make an +.Tn RPC +call to a given program number, +it first contacts +.Nm +on the server machine to determine +the address where +.Tn RPC +requests should be sent. +.Pp +.Nm +should be started before any other RPC service. +Normally, standard +.Tn RPC +servers are started by port monitors, so +.Nm +must be started before port monitors are invoked. +.Pp +When +.Nm +is started, it checks that certain name-to-address +translation-calls function correctly. +If they fail, the network configuration databases may be corrupt. +Since +.Tn RPC +services cannot function correctly in this situation, +.Nm +reports the condition and terminates. +.Pp +.Nm +can only be started by the super-user. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl d +Run in debug mode. +In this mode, +.Nm +will not fork when it starts, will print additional information +during operation, and will abort on certain errors. +With this option, the name-to-address translation consistency +checks are shown in detail. +.It Fl i +.Dq insecure +mode. +Allows calls to SET and UNSET from any host. +Normally +.Nm +accepts these requests only from the loopback interface for security reasons. +This change is necessary for programs that were compiled with earlier +versions of the rpc library and do not make those requests using the +loopback interface. +.It Fl l +Turns on libwrap connection logging. +.It Fl s +causes +.Nm +to change to the user daemon as soon as possible. +This causes +.Nm +to use non-privileged ports for outgoing connections, preventing non-privileged +clients from using +.Nm +to connect to services from a privileged port. +.It Fl L +Allow old-style local connections over the loopback interface. +Without this flag, local connections are only allowed over a local socket, +.Pa /var/run/rpcbind.sock +.El +.Sh NOTES +All RPC servers must be restarted if +.Nm +is restarted. +.Sh SEE ALSO +.Xr rpcbind 3 , +.Xr rpcinfo 8 +.Sh FILES +.Bl -tag -width /var/run/rpcbind.sock -compact +.It Pa /var/run/rpcbind.sock +.El diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c new file mode 100644 index 0000000..2ddf2c0 --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.c @@ -0,0 +1,568 @@ +/* $NetBSD: rpcbind.c,v 1.1 2000/06/02 23:15:42 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)rpcbind.c 1.19 94/04/25 SMI" */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro"; +#endif +#endif + +/* + * rpcbind.c + * Implements the program, version to address mapping for rpc. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <rpc/rpc.h> +#ifdef PORTMAP +#include <netinet/in.h> +#endif +#include <netdb.h> +#include <stdio.h> +#include <netconfig.h> +#include <stdlib.h> +#include <unistd.h> +#include <syslog.h> +#include <err.h> +#include <libutil.h> +#include <pwd.h> +#include <string.h> +#include <errno.h> +#include "rpcbind.h" + +/* Global variables */ +int debugging = 0; /* Tell me what's going on */ +int doabort = 0; /* When debugging, do an abort on errors */ +rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */ + +/* who to suid to if -s is given */ +#define RUN_AS "daemon" + +int runasdaemon = 0; +int insecure = 0; +int oldstyle_local = 0; +int verboselog = 0; + +#ifdef WARMSTART +/* Local Variable */ +static int warmstart = 0; /* Grab a old copy of registrations */ +#endif + +#ifdef PORTMAP +struct pmaplist *list_pml; /* A list of version 2 rpcbind services */ +char *udptrans; /* Name of UDP transport */ +char *tcptrans; /* Name of TCP transport */ +char *udp_uaddr; /* Universal UDP address */ +char *tcp_uaddr; /* Universal TCP address */ +#endif +static char servname[] = "rpcbind"; +static char superuser[] = "superuser"; + +int main __P((int, char *[])); + +static int init_transport __P((struct netconfig *)); +static void rbllist_add __P((rpcprog_t, rpcvers_t, struct netconfig *, + struct netbuf *)); +static void terminate __P((int)); +static void parseargs __P((int, char *[])); + +int +main(int argc, char *argv[]) +{ + struct netconfig *nconf; + void *nc_handle; /* Net config handle */ + struct rlimit rl; + + parseargs(argc, argv); + + getrlimit(RLIMIT_NOFILE, &rl); + if (rl.rlim_cur < 128) { + if (rl.rlim_max <= 128) + rl.rlim_cur = rl.rlim_max; + else + rl.rlim_cur = 128; + setrlimit(RLIMIT_NOFILE, &rl); + } + openlog("rpcbind", LOG_CONS, LOG_DAEMON); + if (geteuid()) { /* This command allowed only to root */ + fprintf(stderr, "Sorry. You are not superuser\n"); + exit(1); + } + nc_handle = setnetconfig(); /* open netconfig file */ + if (nc_handle == NULL) { + syslog(LOG_ERR, "could not read /etc/netconfig"); + exit(1); + } +#ifdef PORTMAP + udptrans = ""; + tcptrans = ""; +#endif + + nconf = getnetconfigent("unix"); + if (nconf == NULL) { + syslog(LOG_ERR, "%s: can't find local transport\n", argv[0]); + exit(1); + } + init_transport(nconf); + + while ((nconf = getnetconfig(nc_handle))) { + if (nconf->nc_flag & NC_VISIBLE) + init_transport(nconf); + } + endnetconfig(nc_handle); + + /* catch the usual termination signals for graceful exit */ + (void) signal(SIGCHLD, reap); + (void) signal(SIGINT, terminate); + (void) signal(SIGTERM, terminate); + (void) signal(SIGQUIT, terminate); + /* ignore others that could get sent */ + (void) signal(SIGPIPE, SIG_IGN); + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGUSR1, SIG_IGN); + (void) signal(SIGUSR2, SIG_IGN); +#ifdef WARMSTART + if (warmstart) { + read_warmstart(); + } +#endif + if (debugging) { + printf("rpcbind debugging enabled."); + if (doabort) { + printf(" Will abort on errors!\n"); + } else { + printf("\n"); + } + } else { + if (daemon(0, 0)) + err(1, "fork failed"); + } + + if (runasdaemon) { + struct passwd *p; + + if((p = getpwnam(RUN_AS)) == NULL) { + syslog(LOG_ERR, "cannot get uid of daemon: %m"); + exit(1); + } + if (setuid(p->pw_uid) == -1) { + syslog(LOG_ERR, "setuid to daemon failed: %m"); + exit(1); + } + } + + network_init(); + + my_svc_run(); + syslog(LOG_ERR, "svc_run returned unexpectedly"); + rpcbind_abort(); + /* NOTREACHED */ + + return 0; +} + +/* + * Adds the entry into the rpcbind database. + * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also + * Returns 0 if succeeds, else fails + */ +static int +init_transport(struct netconfig *nconf) +{ + int fd; + struct t_bind taddr; + struct addrinfo hints, *res = NULL; + struct __rpc_sockinfo si; + SVCXPRT *my_xprt; + int status; /* bound checking ? */ + int aicode; + int addrlen; + struct sockaddr *sa; + struct sockaddr_un sun; + mode_t oldmask; + + if ((nconf->nc_semantics != NC_TPI_CLTS) && + (nconf->nc_semantics != NC_TPI_COTS) && + (nconf->nc_semantics != NC_TPI_COTS_ORD)) + return (1); /* not my type */ +#ifdef ND_DEBUG + if (debugging) { + int i; + char **s; + + (void) fprintf(stderr, "%s: %ld lookup routines :\n", + nconf->nc_netid, nconf->nc_nlookups); + for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups; + i++, s++) + fprintf(stderr, "[%d] - %s\n", i, *s); + } +#endif + + /* + * XXX - using RPC library internal functions. + */ + if ((fd = __rpc_nconf2fd(nconf)) < 0) { + syslog(LOG_ERR, "cannot create socket for %s", nconf->nc_netid); + return (1); + } + + if (!__rpc_nconf2sockinfo(nconf, &si)) { + syslog(LOG_ERR, "cannot get information for %s", + nconf->nc_netid); + return (1); + } + + if (!strcmp(nconf->nc_netid, "unix")) { + memset(&sun, 0, sizeof sun); + sun.sun_family = AF_LOCAL; + unlink(_PATH_RPCBINDSOCK); + strcpy(sun.sun_path, _PATH_RPCBINDSOCK); + sun.sun_len = SUN_LEN(&sun); + addrlen = sizeof (struct sockaddr_un); + sa = (struct sockaddr *)&sun; + } else { + /* Get rpcbind's address on this transport */ + + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = si.si_af; + hints.ai_socktype = si.si_socktype; + hints.ai_protocol = si.si_proto; + if ((aicode = getaddrinfo(NULL, servname, &hints, &res)) != 0) { + syslog(LOG_ERR, "cannot get local address for %s: %s", + nconf->nc_netid, gai_strerror(aicode)); + return 1; + } + addrlen = res->ai_addrlen; + sa = (struct sockaddr *)res->ai_addr; + } + oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH); + if (bind(fd, sa, addrlen) < 0) { + syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + return 1; + } + (void) umask(oldmask); + + /* Copy the address */ + taddr.addr.len = taddr.addr.maxlen = addrlen; + taddr.addr.buf = malloc(addrlen); + if (taddr.addr.buf == NULL) { + syslog(LOG_ERR, "cannot allocate memory for %s address", + nconf->nc_netid); + if (res != NULL) + freeaddrinfo(res); + return 1; + } + memcpy(taddr.addr.buf, sa, addrlen); +#ifdef ND_DEBUG + if (debugging) { + /* for debugging print out our universal address */ + char *uaddr; + struct netbuf nb; + + nb.buf = sa; + nb.len = nb.maxlen = sa->sa_len; + uaddr = taddr2uaddr(nconf, &nb); + (void) fprintf(stderr, "rpcbind : my address is %s\n", uaddr); + (void) free(uaddr); + } +#endif + + if (res != NULL) + freeaddrinfo(res); + + if (nconf->nc_semantics != NC_TPI_CLTS) + listen(fd, SOMAXCONN); + + my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr, 0, 0); + if (my_xprt == (SVCXPRT *)NULL) { + syslog(LOG_ERR, "%s: could not create service", + nconf->nc_netid); + goto error; + } + +#ifdef PORTMAP + /* + * Register both the versions for tcp/ip, udp/ip and local. + */ + if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 && + (strcmp(nconf->nc_proto, NC_TCP) == 0 || + strcmp(nconf->nc_proto, NC_UDP) == 0)) || + strcmp(nconf->nc_netid, "unix") == 0) { + struct pmaplist *pml; + + if (!svc_register(my_xprt, PMAPPROG, PMAPVERS, + pmap_service, NULL)) { + syslog(LOG_ERR, "could not register on %s", + nconf->nc_netid); + goto error; + } + pml = (struct pmaplist *)malloc((u_int)sizeof (struct pmaplist)); + if (pml == (struct pmaplist *)NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + pml->pml_map.pm_prog = PMAPPROG; + pml->pml_map.pm_vers = PMAPVERS; + pml->pml_map.pm_port = PMAPPORT; + if (strcmp(nconf->nc_proto, NC_TCP) == 0) { + if (tcptrans[0]) { + syslog(LOG_ERR, + "cannot have more than one TCP transport"); + goto error; + } + tcptrans = strdup(nconf->nc_netid); + pml->pml_map.pm_prot = IPPROTO_TCP; + + /* Let's snarf the universal address */ + /* "h1.h2.h3.h4.p1.p2" */ + tcp_uaddr = taddr2uaddr(nconf, &taddr.addr); + } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) { + if (udptrans[0]) { + syslog(LOG_ERR, + "cannot have more than one UDP transport"); + goto error; + } + udptrans = strdup(nconf->nc_netid); + pml->pml_map.pm_prot = IPPROTO_UDP; + + /* Let's snarf the universal address */ + /* "h1.h2.h3.h4.p1.p2" */ + udp_uaddr = taddr2uaddr(nconf, &taddr.addr); + } else if (strcmp(nconf->nc_netid, "unix") == 0) + pml->pml_map.pm_prot = IPPROTO_ST; + pml->pml_next = list_pml; + list_pml = pml; + + /* Add version 3 information */ + pml = (struct pmaplist *)malloc((u_int)sizeof (struct pmaplist)); + if (pml == (struct pmaplist *)NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + pml->pml_map = list_pml->pml_map; + pml->pml_map.pm_vers = RPCBVERS; + pml->pml_next = list_pml; + list_pml = pml; + + /* Add version 4 information */ + pml = (struct pmaplist *)malloc((u_int)sizeof (struct pmaplist)); + if (pml == (struct pmaplist *)NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + pml->pml_map = list_pml->pml_map; + pml->pml_map.pm_vers = RPCBVERS4; + pml->pml_next = list_pml; + list_pml = pml; + + /* Also add version 2 stuff to rpcbind list */ + rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr); + } +#endif + + /* version 3 registration */ + if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) { + syslog(LOG_ERR, "could not register %s version 3", + nconf->nc_netid); + goto error; + } + rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr); + + /* version 4 registration */ + if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) { + syslog(LOG_ERR, "could not register %s version 4", + nconf->nc_netid); + goto error; + } + rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr); + + /* decide if bound checking works for this transport */ + status = add_bndlist(nconf, &taddr.addr); +#ifdef BIND_DEBUG + if (debugging) { + if (status < 0) { + fprintf(stderr, "Error in finding bind status for %s\n", + nconf->nc_netid); + } else if (status == 0) { + fprintf(stderr, "check binding for %s\n", + nconf->nc_netid); + } else if (status > 0) { + fprintf(stderr, "No check binding for %s\n", + nconf->nc_netid); + } + } +#endif + /* + * rmtcall only supported on CLTS transports for now. + */ + if (nconf->nc_semantics == NC_TPI_CLTS) { + status = create_rmtcall_fd(nconf); + +#ifdef BIND_DEBUG + if (debugging) { + if (status < 0) { + fprintf(stderr, + "Could not create rmtcall fd for %s\n", + nconf->nc_netid); + } else { + fprintf(stderr, "rmtcall fd for %s is %d\n", + nconf->nc_netid, status); + } + } +#endif + } + return (0); +error: + close(fd); + return (1); +} + +static void +rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, + struct netbuf *addr) +{ + rpcblist_ptr rbl; + + rbl = (rpcblist_ptr)malloc((u_int)sizeof (rpcblist)); + if (rbl == (rpcblist_ptr)NULL) { + syslog(LOG_ERR, "no memory!"); + exit(1); + } + + rbl->rpcb_map.r_prog = prog; + rbl->rpcb_map.r_vers = vers; + rbl->rpcb_map.r_netid = strdup(nconf->nc_netid); + rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr); + rbl->rpcb_map.r_owner = strdup(superuser); + rbl->rpcb_next = list_rbl; /* Attach to global list */ + list_rbl = rbl; +} + +/* + * Catch the signal and die + */ +static void +terminate(int dummy) +{ +#ifdef WARMSTART + syslog(LOG_ERR, + "rpcbind terminating on signal. Restart with \"rpcbind -w\""); + write_warmstart(); /* Dump yourself */ +#endif + exit(2); +} + +void +rpcbind_abort() +{ +#ifdef WARMSTART + write_warmstart(); /* Dump yourself */ +#endif + abort(); +} + +/* get command line options */ +static void +parseargs(int argc, char *argv[]) +{ + int c; + + while ((c = getopt(argc, argv, "dwailLs")) != -1) { + switch (c) { + case 'a': + doabort = 1; /* when debugging, do an abort on */ + break; /* errors; for rpcbind developers */ + /* only! */ + case 'd': + debugging = 1; + break; + case 'i': + insecure = 1; + break; + case 'L': + oldstyle_local = 1; + break; + case 'l': + verboselog = 1; + break; + case 's': + runasdaemon = 1; + break; +#ifdef WARMSTART + case 'w': + warmstart = 1; + break; +#endif + default: /* error */ + fprintf(stderr, "usage: rpcbind [-Idwils]\n"); + exit (1); + } + } + if (doabort && !debugging) { + fprintf(stderr, + "-a (abort) specified without -d (debugging) -- ignored.\n"); + doabort = 0; + } +} + +void +reap(int dummy) +{ + int save_errno = errno; + + while (wait3(NULL, WNOHANG, NULL) > 0) + ; + errno = save_errno; +} + +void +toggle_verboselog(int dummy) +{ + verboselog = !verboselog; +} diff --git a/usr.sbin/rpcbind/rpcbind.h b/usr.sbin/rpcbind/rpcbind.h new file mode 100644 index 0000000..63cf2d4 --- /dev/null +++ b/usr.sbin/rpcbind/rpcbind.h @@ -0,0 +1,144 @@ +/* $NetBSD: rpcbind.h,v 1.1 2000/06/03 00:47:21 fvdl Exp $ */ +/* $FreeBSD$ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc. + */ + +/* #ident "@(#)rpcbind.h 1.4 90/04/12 SMI" */ + +/* + * rpcbind.h + * The common header declarations + */ + +#ifndef rpcbind_h +#define rpcbind_h + +#ifdef PORTMAP +#include <rpc/pmap_prot.h> +#endif +#include <rpc/rpcb_prot.h> + +/* + * Stuff for the rmtcall service + */ +struct encap_parms { + u_int32_t arglen; + char *args; +}; + +struct r_rmtcall_args { + u_int32_t rmt_prog; + u_int32_t rmt_vers; + u_int32_t rmt_proc; + int rmt_localvers; /* whether to send port # or uaddr */ + char *rmt_uaddr; + struct encap_parms rmt_args; +}; + +extern int debugging; +extern int doabort; +extern int verboselog; +extern int insecure; +extern int oldstyle_local; +extern rpcblist_ptr list_rbl; /* A list of version 3 & 4 rpcbind services */ + +#ifdef PORTMAP +extern struct pmaplist *list_pml; /* A list of version 2 rpcbind services */ +extern char *udptrans; /* Name of UDP transport */ +extern char *tcptrans; /* Name of TCP transport */ +extern char *udp_uaddr; /* Universal UDP address */ +extern char *tcp_uaddr; /* Universal TCP address */ +#endif + +int add_bndlist __P((struct netconfig *, struct netbuf *)); +bool_t is_bound __P((char *, char *)); +char *mergeaddr __P((SVCXPRT *, char *, char *, char *)); +struct netconfig *rpcbind_get_conf __P((char *)); + +void rpcbs_init __P((void)); +void rpcbs_procinfo __P((rpcvers_t, rpcproc_t)); +void rpcbs_set __P((rpcvers_t, bool_t)); +void rpcbs_unset __P((rpcvers_t, bool_t)); +void rpcbs_getaddr __P((rpcvers_t, rpcprog_t, rpcvers_t, char *, char *)); +void rpcbs_rmtcall __P((rpcvers_t, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t, + char *, rpcblist_ptr)); +void *rpcbproc_getstat __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); + +void rpcb_service_3 __P((struct svc_req *, SVCXPRT *)); +void rpcb_service_4 __P((struct svc_req *, SVCXPRT *)); + +/* Common functions shared between versions */ +void *rpcbproc_set_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); +void *rpcbproc_unset_com __P((void *, struct svc_req *, SVCXPRT *, rpcvers_t)); +bool_t map_set __P((RPCB *, char *)); +bool_t map_unset __P((RPCB *, char *)); +void delete_prog __P((int)); +void *rpcbproc_getaddr_com __P((RPCB *, struct svc_req *, SVCXPRT *, rpcvers_t, + rpcvers_t)); +void *rpcbproc_gettime_com __P((void *, struct svc_req *, SVCXPRT *, + rpcvers_t)); +void *rpcbproc_uaddr2taddr_com __P((void *, struct svc_req *, + SVCXPRT *, rpcvers_t)); +void *rpcbproc_taddr2uaddr_com __P((void *, struct svc_req *, SVCXPRT *, + rpcvers_t)); +int create_rmtcall_fd __P((struct netconfig *)); +void rpcbproc_callit_com __P((struct svc_req *, SVCXPRT *, rpcvers_t, + rpcvers_t)); +void my_svc_run __P((void)); + +void rpcbind_abort __P((void)); +void reap __P((int)); +void toggle_verboselog __P((int)); + +int check_access __P((SVCXPRT *, rpcproc_t, void *, int)); +int check_callit __P((SVCXPRT *, struct r_rmtcall_args *, int)); +void logit __P((int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *)); +int is_loopback __P((struct netbuf *)); + +#ifdef PORTMAP +extern void pmap_service __P((struct svc_req *, SVCXPRT *)); +#endif + +void write_warmstart __P((void)); +void read_warmstart __P((void)); + +char *addrmerge __P((struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, + char *netid)); +void network_init __P((void)); +struct sockaddr *local_sa __P((int)); + +/* For different getaddr semantics */ +#define RPCB_ALLVERS 0 +#define RPCB_ONEVERS 1 + +#endif /* rpcbind_h */ diff --git a/usr.sbin/rpcbind/security.c b/usr.sbin/rpcbind/security.c new file mode 100644 index 0000000..8d784d6 --- /dev/null +++ b/usr.sbin/rpcbind/security.c @@ -0,0 +1,283 @@ +/* $NetBSD: security.c,v 1.5 2000/06/08 09:01:05 fvdl Exp $ */ +/* $FreeBSD$ */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/pmap_prot.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libutil.h> +#include <syslog.h> +#include <netdb.h> + +/* + * XXX for special case checks in check_callit. + */ +#include <rpcsvc/mount.h> +#include <rpcsvc/rquota.h> +#include <rpcsvc/nfs_prot.h> +#include <rpcsvc/yp.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yppasswd.h> + +#include "rpcbind.h" + +#ifdef LIBWRAP +# include <tcpd.h> +#ifndef LIBWRAP_ALLOW_FACILITY +# define LIBWRAP_ALLOW_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_ALLOW_SEVERITY +# define LIBWRAP_ALLOW_SEVERITY LOG_INFO +#endif +#ifndef LIBWRAP_DENY_FACILITY +# define LIBWRAP_DENY_FACILITY LOG_AUTH +#endif +#ifndef LIBWRAP_DENY_SEVERITY +# define LIBWRAP_DENY_SEVERITY LOG_WARNING +#endif +int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY; +int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY; +#endif + +#ifndef PORTMAP_LOG_FACILITY +# define PORTMAP_LOG_FACILITY LOG_AUTH +#endif +#ifndef PORTMAP_LOG_SEVERITY +# define PORTMAP_LOG_SEVERITY LOG_INFO +#endif +int log_severity = PORTMAP_LOG_FACILITY|PORTMAP_LOG_SEVERITY; + +extern int verboselog; + +int +check_access(SVCXPRT *xprt, rpcproc_t proc, void *args, int rpcbvers) +{ + struct netbuf *caller = svc_getrpccaller(xprt); + struct sockaddr *addr = (struct sockaddr *)caller->buf; +#ifdef LIBWRAP + struct request_info req; +#endif + rpcprog_t prog = 0; + rpcb *rpcbp; + struct pmap *pmap; + + /* + * The older PMAP_* equivalents have the same numbers, so + * they are accounted for here as well. + */ + switch (proc) { + case RPCBPROC_GETADDR: + case RPCBPROC_SET: + case RPCBPROC_UNSET: + if (rpcbvers > PMAPVERS) { + rpcbp = (rpcb *)args; + prog = rpcbp->r_prog; + } else { + pmap = (struct pmap *)args; + prog = pmap->pm_prog; + } + if (proc == RPCBPROC_GETADDR) + break; + if (!insecure && !is_loopback(caller)) { + if (verboselog) + logit(log_severity, addr, proc, prog, + " declined (non-loopback sender)"); + return 0; + } + break; + case RPCBPROC_CALLIT: + case RPCBPROC_INDIRECT: + case RPCBPROC_DUMP: + case RPCBPROC_GETTIME: + case RPCBPROC_UADDR2TADDR: + case RPCBPROC_TADDR2UADDR: + case RPCBPROC_GETVERSADDR: + case RPCBPROC_GETADDRLIST: + case RPCBPROC_GETSTAT: + default: + } + +#ifdef LIBWRAP + if (addr->sa_family == AF_LOCAL) + return 1; + request_init(&req, RQ_DAEMON, "rpcbind", RQ_CLIENT_SIN, addr, 0); + sock_methods(&req); + if(!hosts_access(&req)) { + logit(deny_severity, addr, proc, prog, ": request from unauthorized host"); + return 0; + } +#endif + if (verboselog) + logit(log_severity, addr, proc, prog, ""); + return 1; +} + +int +is_loopback(struct netbuf *nbuf) +{ + struct sockaddr *addr = (struct sockaddr *)nbuf->buf; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + switch (addr->sa_family) { + case AF_INET: + if (!oldstyle_local) + return 0; + sin = (struct sockaddr_in *)addr; + return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) && + (ntohs(sin->sin_port) < IPPORT_RESERVED)); +#ifdef INET6 + case AF_INET6: + if (!oldstyle_local) + return 0; + sin6 = (struct sockaddr_in6 *)addr; + return (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) && + (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED)); +#endif + case AF_LOCAL: + return 1; + default: + } + + return 0; +} + + +/* logit - report events of interest via the syslog daemon */ +void +logit(int severity, struct sockaddr *addr, rpcproc_t procnum, rpcprog_t prognum, + const char *text) +{ + const char *procname; + char procbuf[32]; + char *progname; + char progbuf[32]; + char fromname[NI_MAXHOST]; + struct rpcent *rpc; + static const char *procmap[] = { + /* RPCBPROC_NULL */ "null", + /* RPCBPROC_SET */ "set", + /* RPCBPROC_UNSET */ "unset", + /* RPCBPROC_GETADDR */ "getport/addr", + /* RPCBPROC_DUMP */ "dump", + /* RPCBPROC_CALLIT */ "callit", + /* RPCBPROC_GETTIME */ "gettime", + /* RPCBPROC_UADDR2TADDR */ "uaddr2taddr", + /* RPCBPROC_TADDR2UADDR */ "taddr2uaddr", + /* RPCBPROC_GETVERSADDR */ "getversaddr", + /* RPCBPROC_INDIRECT */ "indirect", + /* RPCBPROC_GETADDRLIST */ "getaddrlist", + /* RPCBPROC_GETSTAT */ "getstat" + }; + + /* + * Fork off a process or the portmap daemon might hang while + * getrpcbynumber() or syslog() does its thing. + */ + + if (fork() == 0) { + setproctitle("logit"); + + /* Try to map program number to name. */ + + if (prognum == 0) { + progname = ""; + } else if ((rpc = getrpcbynumber((int) prognum))) { + progname = rpc->r_name; + } else { + snprintf(progname = progbuf, sizeof(progbuf), "%u", + (unsigned)prognum); + } + + /* Try to map procedure number to name. */ + + if (procnum > (sizeof procmap / sizeof (char *))) { + snprintf(procbuf, sizeof procbuf, "%u", + (unsigned)procnum); + procname = procbuf; + } else + procname = procmap[procnum]; + + /* Write syslog record. */ + + if (addr->sa_family == AF_LOCAL) + strcpy(fromname, "unix"); + else + getnameinfo(addr, addr->sa_len, fromname, + sizeof fromname, NULL, 0, NI_NUMERICHOST); + + syslog(severity, "connect from %s to %s(%s)%s", + fromname, procname, progname, text); + _exit(0); + } +} + +int +check_callit(SVCXPRT *xprt, struct r_rmtcall_args *args, int versnum) +{ + struct sockaddr *sa = (struct sockaddr *)svc_getrpccaller(xprt)->buf; + + /* + * Always allow calling NULLPROC + */ + if (args->rmt_proc == 0) + return 1; + + /* + * XXX - this special casing sucks. + */ + switch (args->rmt_prog) { + case RPCBPROG: + /* + * Allow indirect calls to ourselves in insecure mode. + * The is_loopback checks aren't useful then anyway. + */ + if (!insecure) + goto deny; + break; + case MOUNTPROG: + if (args->rmt_proc != MOUNTPROC_MNT && + args->rmt_proc != MOUNTPROC_UMNT) + break; + goto deny; + case YPBINDPROG: + if (args->rmt_proc != YPBINDPROC_SETDOM) + break; + /* FALLTHROUGH */ + case YPPASSWDPROG: + case NFS_PROGRAM: + case RQUOTAPROG: + goto deny; + case YPPROG: + switch (args->rmt_proc) { + case YPPROC_ALL: + case YPPROC_MATCH: + case YPPROC_FIRST: + case YPPROC_NEXT: + goto deny; + default: + } + default: + } + + return 1; +deny: +#ifdef LIBWRAP + logit(deny_severity, sa, args->rmt_proc, args->rmt_prog, + ": indirect call not allowed"); +#else + logit(0, sa, args->rmt_proc, args->rmt_prog, + ": indirect call not allowed"); +#endif + return 0; +} diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c new file mode 100644 index 0000000..bf20019 --- /dev/null +++ b/usr.sbin/rpcbind/util.c @@ -0,0 +1,381 @@ +/* + * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $ + * $FreeBSD$ + */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Frank van der Linden. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <net/if.h> +#include <netinet/in.h> +#include <ifaddrs.h> +#include <sys/poll.h> +#include <rpc/rpc.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netdb.h> +#include <netconfig.h> +#include <stdio.h> +#include <arpa/inet.h> + +#include "rpcbind.h" + +static struct sockaddr_in *local_in4; +#ifdef INET6 +static struct sockaddr_in6 *local_in6; +#endif + +static int bitmaskcmp __P((void *, void *, void *, int)); +#ifdef INET6 +static void in6_fillscopeid __P((struct sockaddr_in6 *)); +#endif + +/* + * For all bits set in "mask", compare the corresponding bits in + * "dst" and "src", and see if they match. + */ +static int +bitmaskcmp(void *dst, void *src, void *mask, int bytelen) +{ + int i, j; + u_int8_t *p1 = dst, *p2 = src, *netmask = mask; + u_int8_t bitmask; + + for (i = 0; i < bytelen; i++) { + for (j = 0; j < 8; j++) { + bitmask = 1 << j; + if (!(netmask[i] & bitmask)) + continue; + if ((p1[i] & bitmask) != (p2[i] & bitmask)) + return 1; + } + } + + return 0; +} + +/* + * Taken from ifconfig.c + */ +#ifdef INET6 +static void +in6_fillscopeid(struct sockaddr_in6 *sin6) +{ + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0; + } +} +#endif + +char * +addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr, + char *netid) +{ + struct ifaddrs *ifap, *ifp, *bestif; +#ifdef INET6 + struct sockaddr_in6 *servsin6, *sin6mask, *clntsin6, *ifsin6, *realsin6; + struct sockaddr_in6 *newsin6; +#endif + struct sockaddr_in *servsin, *sinmask, *clntsin, *newsin, *ifsin; + struct netbuf *serv_nbp, *clnt_nbp = NULL, tbuf; + struct sockaddr *serv_sa; + struct sockaddr *clnt_sa; + struct sockaddr_storage ss; + struct netconfig *nconf; + struct sockaddr *clnt = caller->buf; + char *ret = NULL; + +#ifdef ND_DEBUG + if (debugging) + fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr, + clnt_uaddr, netid); +#endif + nconf = getnetconfigent(netid); + if (nconf == NULL) + return NULL; + + /* + * Local merge, just return a duplicate. + */ + if (clnt_uaddr != NULL && strncmp(clnt_uaddr, "0.0.0.0.", 8) == 0) + return strdup(clnt_uaddr); + + serv_nbp = uaddr2taddr(nconf, serv_uaddr); + if (serv_nbp == NULL) + return NULL; + + serv_sa = (struct sockaddr *)serv_nbp->buf; + if (clnt_uaddr != NULL) { + clnt_nbp = uaddr2taddr(nconf, clnt_uaddr); + clnt_sa = (struct sockaddr *)clnt_nbp->buf; + } else { + clnt_sa = (struct sockaddr *) + malloc(sizeof (struct sockaddr_storage)); + memcpy(clnt_sa, clnt, clnt->sa_len); + } + + if (getifaddrs(&ifp) < 0) + return 0; + + /* + * Loop through all interfaces. For each interface, see if the + * network portion of its address is equal to that of the client. + * If so, we have found the interface that we want to use. + */ + for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { + if (ifap->ifa_addr->sa_family != clnt->sa_family || + !(ifap->ifa_flags & IFF_UP)) + continue; + + switch (clnt->sa_family) { + case AF_INET: + /* + * realsin: address that recvfrom gave us. + * ifsin: address of interface being examined. + * clntsin: address that client want us to contact + * it on + * servsin: local address of RPC service. + * sinmask: netmask of this interface + * newsin: initially a copy of clntsin, eventually + * the merged address + */ + servsin = (struct sockaddr_in *)serv_sa; + clntsin = (struct sockaddr_in *)clnt_sa; + sinmask = (struct sockaddr_in *)ifap->ifa_netmask; + newsin = (struct sockaddr_in *)&ss; + ifsin = (struct sockaddr_in *)ifap->ifa_addr; + if (!bitmaskcmp(&ifsin->sin_addr, &clntsin->sin_addr, + &sinmask->sin_addr, sizeof (struct in_addr))) { + /* + * Found it. + */ + memcpy(newsin, ifap->ifa_addr, + clnt_sa->sa_len); + newsin->sin_port = servsin->sin_port; + tbuf.len = clnt_sa->sa_len; + tbuf.maxlen = sizeof (struct sockaddr_storage); + tbuf.buf = newsin; + goto found; + } + break; +#ifdef INET6 + case AF_INET6: + /* + * realsin6: address that recvfrom gave us. + * ifsin6: address of interface being examined. + * clntsin6: address that client want us to contact + * it on + * servsin6: local address of RPC service. + * sin6mask: netmask of this interface + * newsin6: initially a copy of clntsin, eventually + * the merged address + * + * For v6 link local addresses, if the client contacted + * us via a link-local address, and wants us to reply + * to one, use the scope id to see which one. + */ + realsin6 = (struct sockaddr_in6 *)clnt; + ifsin6 = (struct sockaddr_in6 *)ifap->ifa_addr; + in6_fillscopeid(ifsin6); + clntsin6 = (struct sockaddr_in6 *)clnt_sa; + servsin6 = (struct sockaddr_in6 *)serv_sa; + sin6mask = (struct sockaddr_in6 *)ifap->ifa_netmask; + newsin6 = (struct sockaddr_in6 *)&ss; + if (IN6_IS_ADDR_LINKLOCAL(&ifsin6->sin6_addr) && + IN6_IS_ADDR_LINKLOCAL(&realsin6->sin6_addr) && + IN6_IS_ADDR_LINKLOCAL(&clntsin6->sin6_addr)) { + if (ifsin6->sin6_scope_id != + realsin6->sin6_scope_id) + continue; +match: + memcpy(newsin6, ifsin6, clnt_sa->sa_len); + newsin6->sin6_port = servsin6->sin6_port; + tbuf.maxlen = sizeof (struct sockaddr_storage); + tbuf.len = clnt_sa->sa_len; + tbuf.buf = newsin6; + goto found; + } + if (!bitmaskcmp(&ifsin6->sin6_addr, + &clntsin6->sin6_addr, &sin6mask->sin6_addr, + sizeof (struct in6_addr))) + goto match; + break; +#endif + default: + goto freeit; + } + } + /* + * Didn't find anything. Get the first possibly useful interface, + * preferring "normal" interfaces to point-to-point and loopback + * ones. + */ + bestif = NULL; + for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { + if (ifap->ifa_addr->sa_family != clnt->sa_family || + !(ifap->ifa_flags & IFF_UP)) + continue; + if (!(ifap->ifa_flags & IFF_LOOPBACK) && + !(ifap->ifa_flags & IFF_POINTOPOINT)) { + bestif = ifap; + break; + } + if (bestif == NULL) + bestif = ifap; + else if ((bestif->ifa_flags & IFF_LOOPBACK) && + !(ifap->ifa_flags & IFF_LOOPBACK)) + bestif = ifap; + } + ifap = bestif; +found: + if (ifap != NULL) + ret = taddr2uaddr(nconf, &tbuf); +freeit: + freenetconfigent(nconf); + free(serv_sa); + free(serv_nbp); + if (clnt_sa != NULL) + free(clnt_sa); + if (clnt_nbp != NULL) + free(clnt_nbp); + freeifaddrs(ifp); + +#ifdef ND_DEBUG + if (debugging) + fprintf(stderr, "addrmerge: returning %s\n", ret); +#endif + return ret; +} + +void +network_init() +{ +#ifdef INET6 + struct ifaddrs *ifap, *ifp; + struct ipv6_mreq mreq6; + int ifindex, s; +#endif + int ecode; + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { + if (debugging) + fprintf(stderr, "can't get local ip4 address: %s\n", + gai_strerror(ecode)); + } else { + local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4); + if (local_in4 == NULL) { + if (debugging) + fprintf(stderr, "can't alloc local ip4 addr\n"); + } + memcpy(local_in4, res->ai_addr, sizeof *local_in4); + } + +#ifdef INET6 + hints.ai_family = AF_INET6; + if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) { + if (debugging) + fprintf(stderr, "can't get local ip6 address: %s\n", + gai_strerror(ecode)); + } else { + local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6); + if (local_in6 == NULL) { + if (debugging) + fprintf(stderr, "can't alloc local ip6 addr\n"); + } + memcpy(local_in6, res->ai_addr, sizeof *local_in6); + } + + /* + * Now join the RPC ipv6 multicast group on all interfaces. + */ + if (getifaddrs(&ifp) < 0) + return; + + mreq6.ipv6mr_interface = 0; + inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr); + + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + /* + * Loop through all interfaces. For each interface, see if the + * network portion of its address is equal to that of the client. + * If so, we have found the interface that we want to use. + */ + for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) { + if (ifap->ifa_addr->sa_family != AF_INET6 || + !(ifap->ifa_flags & IFF_MULTICAST)) + continue; + ifindex = if_nametoindex(ifap->ifa_name); + if (ifindex == mreq6.ipv6mr_interface) + /* + * Already did this one. + */ + continue; + mreq6.ipv6mr_interface = ifindex; + if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, + sizeof mreq6) < 0) + if (debugging) + perror("setsockopt v6 multicast"); + } +#endif + + /* close(s); */ +} + +struct sockaddr * +local_sa(int af) +{ + switch (af) { + case AF_INET: + return (struct sockaddr *)local_in4; +#ifdef INET6 + case AF_INET6: + return (struct sockaddr *)local_in6; +#endif + default: + return NULL; + } +} diff --git a/usr.sbin/rpcbind/warmstart.c b/usr.sbin/rpcbind/warmstart.c new file mode 100644 index 0000000..fea2789 --- /dev/null +++ b/usr.sbin/rpcbind/warmstart.c @@ -0,0 +1,179 @@ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ +/* + * warmstart.c + * Allows for gathering of registrations from a earlier dumped file. + * + * Copyright (c) 1990 by Sun Microsystems, Inc. + */ + +/* + * #ident "@(#)warmstart.c 1.7 93/07/05 SMI" + * $FreeBSD$/ + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <rpc/rpc.h> +#include <rpc/rpcb_prot.h> +#include <rpc/xdr.h> +#ifdef PORTMAP +#include <netinet/in.h> +#include <rpc/pmap_prot.h> +#endif +#include <syslog.h> +#include <unistd.h> + +#include "rpcbind.h" + +/* + * XXX this code is unsafe and is not used. It should be made safe. + */ + + +/* These files keep the pmap_list and rpcb_list in XDR format */ +#define RPCBFILE "/tmp/rpcbind.file" +#ifdef PORTMAP +#define PMAPFILE "/tmp/portmap.file" +#endif + +static bool_t write_struct __P((char *, xdrproc_t, void *)); +static bool_t read_struct __P((char *, xdrproc_t, void *)); + +static bool_t +write_struct(char *filename, xdrproc_t structproc, void *list) +{ + FILE *fp; + XDR xdrs; + mode_t omask; + + omask = umask(077); + fp = fopen(filename, "w"); + if (fp == NULL) { + int i; + + for (i = 0; i < 10; i++) + close(i); + fp = fopen(filename, "w"); + if (fp == NULL) { + syslog(LOG_ERR, + "cannot open file = %s for writing", filename); + syslog(LOG_ERR, "cannot save any registration"); + return (FALSE); + } + } + (void) umask(omask); + xdrstdio_create(&xdrs, fp, XDR_ENCODE); + + if (structproc(&xdrs, list) == FALSE) { + syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename); + fclose(fp); + return (FALSE); + } + XDR_DESTROY(&xdrs); + fclose(fp); + return (TRUE); +} + +static bool_t +read_struct(char *filename, xdrproc_t structproc, void *list) +{ + FILE *fp; + XDR xdrs; + struct stat sbuf; + + if (stat(filename, &sbuf) != 0) { + fprintf(stderr, + "rpcbind: cannot stat file = %s for reading\n", filename); + goto error; + } + if ((sbuf.st_uid != 0) || (sbuf.st_mode & S_IRWXG) || + (sbuf.st_mode & S_IRWXO)) { + fprintf(stderr, + "rpcbind: invalid permissions on file = %s for reading\n", + filename); + goto error; + } + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, + "rpcbind: cannot open file = %s for reading\n", filename); + goto error; + } + xdrstdio_create(&xdrs, fp, XDR_DECODE); + + if (structproc(&xdrs, list) == FALSE) { + fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename); + fclose(fp); + goto error; + } + XDR_DESTROY(&xdrs); + fclose(fp); + return (TRUE); + +error: fprintf(stderr, "rpcbind: will start from scratch\n"); + return (FALSE); +} + +void +write_warmstart() +{ + (void) write_struct(RPCBFILE, xdr_rpcblist_ptr, &list_rbl); +#ifdef PORTMAP + (void) write_struct(PMAPFILE, xdr_pmaplist_ptr, &list_pml); +#endif + +} + +void +read_warmstart() +{ + rpcblist_ptr tmp_rpcbl = NULL; +#ifdef PORTMAP + struct pmaplist *tmp_pmapl = NULL; +#endif + int ok1, ok2 = TRUE; + + ok1 = read_struct(RPCBFILE, xdr_rpcblist_ptr, &tmp_rpcbl); + if (ok1 == FALSE) + return; +#ifdef PORTMAP + ok2 = read_struct(PMAPFILE, xdr_pmaplist_ptr, &tmp_pmapl); +#endif + if (ok2 == FALSE) { + xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&tmp_rpcbl); + return; + } + xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&list_rbl); + list_rbl = tmp_rpcbl; +#ifdef PORTMAP + xdr_free((xdrproc_t) xdr_pmaplist_ptr, (char *)&list_pml); + list_pml = tmp_pmapl; +#endif +} diff --git a/usr.sbin/ypbind/yp_ping.c b/usr.sbin/ypbind/yp_ping.c index 558843f..744eda0 100644 --- a/usr.sbin/ypbind/yp_ping.c +++ b/usr.sbin/ypbind/yp_ping.c @@ -310,7 +310,7 @@ get_reply: } /* end successful completion */ else { /* maybe our credentials need to be refreshed ... */ - if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) { + if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth, &reply_msg)) { nrefreshes--; goto call_again; } @@ -502,7 +502,7 @@ int __yp_ping(restricted_addrs, cnt, dom, port) clnt->cl_auth = authunix_create_default(); cu = (struct cu_data *)clnt->cl_private; tv.tv_sec = 0; - clnt_control(clnt, CLSET_TIMEOUT, &tv); + clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv); ioctl(sock, FIONBIO, &dontblock); oldfunc = clnt->cl_ops->cl_call; clnt->cl_ops->cl_call = clntudp_a_call; diff --git a/usr.sbin/ypbind/ypbind.c b/usr.sbin/ypbind/ypbind.c index d6e5e2be..5304422 100644 --- a/usr.sbin/ypbind/ypbind.c +++ b/usr.sbin/ypbind/ypbind.c @@ -743,7 +743,7 @@ struct _dom_binding *ypdb; ptr = (char *)&ypdb->dom_domain; stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK, xdr_domainname, (char *)&ptr, xdr_bool, (char *)&out, - broadcast_result); + (resultproc_t)broadcast_result); } if (stat != RPC_SUCCESS) { diff --git a/usr.sbin/yppush/yppush_main.c b/usr.sbin/yppush/yppush_main.c index 39b5bdf..ba25dda 100644 --- a/usr.sbin/yppush/yppush_main.c +++ b/usr.sbin/yppush/yppush_main.c @@ -415,7 +415,7 @@ int yp_push(server, map, tid) job->stat = 0; job->tid = tid; job->port = xprt->xp_port; - job->sock = xprt->xp_sock; /*XXX: Evil!! EEEEEEEVIL!!! */ + job->sock = xprt->xp_fd; /*XXX: Evil!! EEEEEEEVIL!!! */ job->server = strdup(server); job->map = strdup(map); job->prognum = prognum; @@ -437,8 +437,8 @@ int yp_push(server, map, tid) * 2) Even in this day and age, there are still some *NIXes * that don't support async socket I/O. */ - if (fcntl(xprt->xp_sock, F_SETOWN, getpid()) == -1 || - fcntl(xprt->xp_sock, F_SETFL, O_ASYNC) == -1) { + if (fcntl(xprt->xp_fd, F_SETOWN, getpid()) == -1 || + fcntl(xprt->xp_fd, F_SETFL, O_ASYNC) == -1) { yp_error("failed to set async I/O mode: %s", strerror(errno)); yppush_exit(1); diff --git a/usr.sbin/ypserv/yp_dnslookup.c b/usr.sbin/ypserv/yp_dnslookup.c index 5aa03e7..fdbac25 100644 --- a/usr.sbin/ypserv/yp_dnslookup.c +++ b/usr.sbin/ypserv/yp_dnslookup.c @@ -434,7 +434,7 @@ ypstat yp_async_lookup_name(rqstp, name) /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ type = -1; len = sizeof(type); - if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET, + if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1) { yp_error("getsockopt failed: %s", strerror(errno)); return(YP_YPERR); @@ -490,7 +490,7 @@ ypstat yp_async_lookup_addr(rqstp, addr) /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ type = -1; len = sizeof(type); - if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET, + if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1) { yp_error("getsockopt failed: %s", strerror(errno)); return(YP_YPERR); |