blob: 338b70f3b2c8f7511706173969f31285f1a1ab15 [file] [log] [blame]
/*
* QUOTA An implementation of the diskquota system for the LINUX operating
* system. QUOTA is implemented using the BSD systemcall interface
* as the means of communication with the user level. Should work for
* all filesystems because of integration into the VFS layer of the
* operating system. This is based on the Melbourne quota system wich
* uses both user and group quota files.
*
* Rquota service handlers.
*
* Author: Marco van Wieringen <mvw@planets.elm.net>
* changes for new utilities by Jan Kara <jack@suse.cz>
* patches by Jani Jaakkola <jjaakkol@cs.helsinki.fi>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
#include "config.h"
#include <rpc/rpc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <rpc/pmap_clnt.h> /* for pmap_unset */
#include <stdio.h>
#include <stdlib.h> /* getenv, exit */
#include <string.h> /* strcmp */
#include <memory.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <errno.h>
#ifdef HOSTS_ACCESS
#include <tcpd.h>
#include <netdb.h>
int deny_severity, allow_severity; /* Needed by some versions of libwrap */
#endif
#ifdef __STDC__
#define SIG_PF void(*)(int)
#endif
extern int svctcp_socket (u_long __number, int __port, int __reuse);
extern int svcudp_socket (u_long __number, int __port, int __reuse);
#include "pot.h"
#include "common.h"
#include "rquota.h"
#include "quotasys.h"
char *progname;
/*
* Global authentication credentials.
*/
struct authunix_parms *unix_cred;
#define FL_SETQUOTA 1 /* Enable setquota rpc */
#define FL_NODAEMON 2 /* Disable daemon() call */
#define FL_AUTOFS 4 /* Don't ignore autofs mountpoints */
int flags; /* Options specified on command line */
static int port; /* Port to use (0 for default one) */
static char xtab_path[PATH_MAX]; /* Path to NFSD export table */
char nfs_pseudoroot[PATH_MAX]; /* Root of the virtual NFS filesystem ('/' for NFSv3) */
static struct option options[]= {
{ "version", 0, NULL, 'V' },
{ "help", 0, NULL, 'h' },
{ "foreground", 0 , NULL, 'F' },
#ifdef RPC_SETQUOTA
{ "no-setquota", 0 , NULL, 's' },
{ "setquota", 0, NULL, 'S' },
#endif
{ "autofs", 0, NULL, 'I'},
{ "port", 1, NULL, 'p' },
{ "xtab", 1, NULL, 'x' },
{ NULL, 0, NULL , 0 }
};
static void show_help(void)
{
#ifdef RPC_SETQUOTA
errstr(_("Usage: %s [options]\nOptions are:\n\
-h --help shows this text\n\
-V --version shows version information\n\
-F --foreground starts the quota service in foreground\n\
-I --autofs do not ignore mountpoints mounted by automounter\n\
-p --port <port> listen on given port\n\
-s --no-setquota disables remote calls to setquota (default)\n\
-S --setquota enables remote calls to setquota\n\
-x --xtab <path> set an alternative file with NFSD export table\n"), progname);
#else
errstr(_("Usage: %s [options]\nOptions are:\n\
-h --help shows this text\n\
-V --version shows version information\n\
-F --foreground starts the quota service in foreground\n\
-I --autofs do not ignore mountpoints mounted by automounter\n\
-p --port <port> listen on given port\n\
-x --xtab <path> set an alternative file with NFSD export table\n"), progname);
#endif
}
static void parse_options(int argc, char **argv)
{
char ostr[128]="", *endptr;
int i,opt;
int j=0;
sstrncpy(xtab_path, NFSD_XTAB_PATH, PATH_MAX);
for(i=0; options[i].name; i++) {
ostr[j++] = options[i].val;
if (options[i].has_arg)
ostr[j++] = ':';
}
while ((opt=getopt_long(argc, argv, ostr, options, NULL))>=0) {
switch(opt) {
case 'V':
version();
exit(0);
case 'h':
show_help();
exit(0);
case 'F':
flags |= FL_NODAEMON;
break;
#ifdef RPC_SETQUOTA
case 's':
flags &= ~FL_SETQUOTA;
break;
case 'S':
flags |= FL_SETQUOTA;
break;
#endif
case 'I':
flags |= FL_AUTOFS;
break;
case 'p':
port = strtol(optarg, &endptr, 0);
if (*endptr || port <= 0) {
errstr(_("Illegal port number: %s\n"), optarg);
show_help();
exit(1);
}
break;
case 'x':
if (access(optarg, R_OK) < 0) {
errstr(_("Cannot access the specified xtab file %s: %s\n"), optarg, strerror(errno));
show_help();
exit(1);
}
sstrncpy(xtab_path, optarg, PATH_MAX);
break;
default:
errstr(_("Unknown option '%c'.\n"), opt);
show_help();
exit(1);
}
}
}
/*
* good_client checks if an quota client should be allowed to
* execute the requested rpc call.
*/
static int good_client(struct sockaddr_in *addr, ulong rq_proc)
{
#ifdef HOSTS_ACCESS
struct request_info req;
#endif
char *remote = inet_ntoa(addr->sin_addr);
if (rq_proc==RQUOTAPROC_SETQUOTA ||
rq_proc==RQUOTAPROC_SETACTIVEQUOTA) {
/* If setquota is disabled, fail always */
if (!(flags & FL_SETQUOTA)) {
errstr(_("host %s attempted to call setquota when disabled\n"),
remote);
return 0;
}
/* Require that SETQUOTA calls originate from port < 1024 */
if (ntohs(addr->sin_port)>=1024) {
errstr(_("host %s attempted to call setquota from port >= 1024\n"),
remote);
return 0;
}
/* Setquota OK */
}
#ifdef HOSTS_ACCESS
/* NOTE: we could use different servicename for setquota calls to
* allow only some hosts to call setquota. */
request_init(&req, RQ_DAEMON, "rquotad", RQ_CLIENT_SIN, addr, 0);
sock_methods(&req);
if (hosts_access(&req))
return 1;
errstr(_("Denied access to host %s\n"), remote);
return 0;
#else
/* If no access checking is available, OK always */
return 1;
#endif
}
static void rquotaprog_1(struct svc_req *rqstp, register SVCXPRT * transp)
{
union {
getquota_args rquotaproc_getquota_1_arg;
setquota_args rquotaproc_setquota_1_arg;
getquota_args rquotaproc_getactivequota_1_arg;
setquota_args rquotaproc_setactivequota_1_arg;
} argument;
char *result;
xdrproc_t xdr_argument, xdr_result;
char *(*local) (char *, struct svc_req *);
/*
* Authenticate host
*/
if (!good_client(svc_getcaller(rqstp->rq_xprt),rqstp->rq_proc)) {
svcerr_auth (transp, AUTH_FAILED);
return;
}
/*
* Don't bother authentication for NULLPROC.
*/
if (rqstp->rq_proc == NULLPROC) {
(void)svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
return;
}
/*
* Get authentication.
*/
switch (rqstp->rq_cred.oa_flavor) {
case AUTH_UNIX:
unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
break;
case AUTH_NULL:
default:
svcerr_weakauth(transp);
return;
}
switch (rqstp->rq_proc) {
case RQUOTAPROC_GETQUOTA:
xdr_argument = (xdrproc_t) xdr_getquota_args;
xdr_result = (xdrproc_t) xdr_getquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_getquota_1_svc;
break;
case RQUOTAPROC_SETQUOTA:
xdr_argument = (xdrproc_t) xdr_setquota_args;
xdr_result = (xdrproc_t) xdr_setquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_setquota_1_svc;
break;
case RQUOTAPROC_GETACTIVEQUOTA:
xdr_argument = (xdrproc_t) xdr_getquota_args;
xdr_result = (xdrproc_t) xdr_getquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_getactivequota_1_svc;
break;
case RQUOTAPROC_SETACTIVEQUOTA:
xdr_argument = (xdrproc_t) xdr_setquota_args;
xdr_result = (xdrproc_t) xdr_setquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_setactivequota_1_svc;
break;
default:
svcerr_noproc(transp);
return;
}
memset(&argument, 0, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t) & argument)) {
svcerr_decode(transp);
return;
}
result = (*local) ((char *)&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t) & argument)) {
errstr(_("unable to free arguments\n"));
exit(1);
}
return;
}
static void rquotaprog_2(struct svc_req *rqstp, register SVCXPRT * transp)
{
union {
ext_getquota_args rquotaproc_getquota_2_arg;
ext_setquota_args rquotaproc_setquota_2_arg;
ext_getquota_args rquotaproc_getactivequota_2_arg;
ext_setquota_args rquotaproc_setactivequota_2_arg;
} argument;
char *result;
xdrproc_t xdr_argument, xdr_result;
char *(*local) (char *, struct svc_req *);
/*
* Authenticate host
*/
if (!good_client(svc_getcaller(rqstp->rq_xprt),rqstp->rq_proc)) {
svcerr_auth (transp, AUTH_FAILED);
return;
}
/*
* Don't bother authentication for NULLPROC.
*/
if (rqstp->rq_proc == NULLPROC) {
(void)svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
return;
}
/*
* Get authentication.
*/
switch (rqstp->rq_cred.oa_flavor) {
case AUTH_UNIX:
unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
break;
case AUTH_NULL:
default:
svcerr_weakauth(transp);
return;
}
switch (rqstp->rq_proc) {
case RQUOTAPROC_GETQUOTA:
xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
xdr_result = (xdrproc_t) xdr_getquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_getquota_2_svc;
break;
case RQUOTAPROC_SETQUOTA:
xdr_argument = (xdrproc_t) xdr_ext_setquota_args;
xdr_result = (xdrproc_t) xdr_setquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_setquota_2_svc;
break;
case RQUOTAPROC_GETACTIVEQUOTA:
xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
xdr_result = (xdrproc_t) xdr_getquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_getactivequota_2_svc;
break;
case RQUOTAPROC_SETACTIVEQUOTA:
xdr_argument = (xdrproc_t) xdr_ext_setquota_args;
xdr_result = (xdrproc_t) xdr_setquota_rslt;
local = (char *(*)(char *, struct svc_req *))rquotaproc_setactivequota_2_svc;
break;
default:
svcerr_noproc(transp);
return;
}
memset(&argument, 0, sizeof(argument));
if (!svc_getargs(transp, xdr_argument, (caddr_t) & argument)) {
svcerr_decode(transp);
return;
}
result = (*local) ((char *)&argument, rqstp);
if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
svcerr_systemerr(transp);
}
if (!svc_freeargs(transp, xdr_argument, (caddr_t) & argument)) {
errstr(_("unable to free arguments\n"));
exit(1);
}
return;
}
static void
unregister (int sig)
{
pmap_unset(RQUOTAPROG, RQUOTAVERS);
pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
exit(0);
}
/* Parse NFSD export table and find a filesystem pseudoroot if it is there */
static void get_pseudoroot(void)
{
FILE *f;
char exp_line[1024];
char *c;
strcpy(nfs_pseudoroot, "/");
if (!(f = fopen(xtab_path, "r"))) {
errstr(_("Warning: Cannot open export table %s: %s\nUsing '/' as a pseudofilesystem root.\n"), xtab_path, strerror(errno));
return;
}
while (fgets(exp_line, sizeof(exp_line), f)) {
if (exp_line[0] == '#' || exp_line[0] == '\n') /* Comment, empty line? */
continue;
c = strchr(exp_line, '\t');
if (!c) /* Huh, line we don't understand... */
continue;
*c = 0;
/* Find the beginning of export options */
c = strchr(c+1, '(');
if (!c)
continue;
c = strstr(c, "fsid=0");
if (c) {
sstrncpy(nfs_pseudoroot, exp_line, PATH_MAX);
sstrncat(nfs_pseudoroot, "/", PATH_MAX);
break;
}
}
fclose(f);
}
int main(int argc, char **argv)
{
register SVCXPRT *transp;
struct sigaction sa;
int sock;
gettexton();
progname = basename(argv[0]);
parse_options(argc, argv);
init_kernel_interface();
get_pseudoroot();
pmap_unset(RQUOTAPROG, RQUOTAVERS);
pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
sa.sa_handler = unregister;
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sock = svcudp_socket(RQUOTAPROG, port, 1);
transp = svcudp_create(sock == -1 ? RPC_ANYSOCK : sock);
if (transp == NULL) {
errstr(_("cannot create udp service.\n"));
exit(1);
}
if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_UDP)) {
errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, UDP).\n"));
exit(1);
}
if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_UDP)) {
errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP).\n"));
exit(1);
}
sock = svctcp_socket(RQUOTAPROG, port, 1);
transp = svctcp_create(sock == -1 ? RPC_ANYSOCK : sock, 0, 0);
if (transp == NULL) {
errstr(_("cannot create TCP service.\n"));
exit(1);
}
if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_TCP)) {
errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, TCP).\n"));
exit(1);
}
if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_TCP)) {
errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP).\n"));
exit(1);
}
if (!(flags & FL_NODAEMON)) {
use_syslog();
daemon(0, 0);
}
svc_run();
errstr(_("svc_run returned\n"));
exit(1);
/* NOTREACHED */
}