Initial commit to Gerrit
[profile/ivi/quota.git] / rquota_svc.c
1 /*
2  * QUOTA    An implementation of the diskquota system for the LINUX operating
3  *          system. QUOTA is implemented using the BSD systemcall interface
4  *          as the means of communication with the user level. Should work for
5  *          all filesystems because of integration into the VFS layer of the
6  *          operating system. This is based on the Melbourne quota system wich
7  *          uses both user and group quota files.
8  *
9  *          Rquota service handlers.
10  *
11  * Author:  Marco van Wieringen <mvw@planets.elm.net>
12  *          changes for new utilities by Jan Kara <jack@suse.cz>
13  *          patches by Jani Jaakkola <jjaakkol@cs.helsinki.fi>
14  *
15  * Version: $Id: rquota_svc.c,v 1.22 2010/01/05 16:04:57 jkar8572 Exp $
16  *
17  *          This program is free software; you can redistribute it and/or
18  *          modify it under the terms of the GNU General Public License as
19  *          published by the Free Software Foundation; either version 2 of
20  *          the License, or (at your option) any later version.
21  */
22                                                                                                           
23 #include "config.h"
24
25 #include <rpc/rpc.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <rpc/pmap_clnt.h>      /* for pmap_unset */
30 #include <stdio.h>
31 #include <stdlib.h>             /* getenv, exit */
32 #include <string.h>             /* strcmp */
33 #include <memory.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #include <signal.h>
37 #include <errno.h>
38 #ifdef HOSTS_ACCESS
39 #include <tcpd.h>
40 #include <netdb.h>
41
42 int deny_severity, allow_severity;      /* Needed by some versions of libwrap */
43 #endif
44
45 #ifdef __STDC__
46 #define SIG_PF void(*)(int)
47 #endif
48
49 extern int svctcp_socket (u_long __number, int __port, int __reuse);
50 extern int svcudp_socket (u_long __number, int __port, int __reuse);
51
52 #include "pot.h"
53 #include "common.h"
54 #include "rquota.h"
55 #include "quotasys.h"
56
57 char *progname;
58
59 /*
60  * Global authentication credentials.
61  */
62 struct authunix_parms *unix_cred;
63
64 #define FL_SETQUOTA 1   /* Enable setquota rpc */
65 #define FL_NODAEMON 2   /* Disable daemon() call */
66 #define FL_AUTOFS   4   /* Don't ignore autofs mountpoints */
67
68 int flags;                              /* Options specified on command line */ 
69 static int port;                        /* Port to use (0 for default one) */
70 static char xtab_path[PATH_MAX];        /* Path to NFSD export table */
71 char nfs_pseudoroot[PATH_MAX];          /* Root of the virtual NFS filesystem ('/' for NFSv3) */
72
73 static struct option options[]= {
74         { "version", 0, NULL, 'V' },
75         { "help", 0, NULL, 'h' },
76         { "foreground", 0 , NULL, 'F' },
77 #ifdef RPC_SETQUOTA
78         { "no-setquota", 0 , NULL, 's' },
79         { "setquota", 0, NULL, 'S' },
80 #endif
81         { "autofs", 0, NULL, 'I'},
82         { "port", 1, NULL, 'p' },
83         { "xtab", 1, NULL, 'x' },
84         { NULL, 0, NULL , 0 }
85 };
86
87 static void show_help(void)
88 {
89 #ifdef RPC_SETQUOTA
90         errstr(_("Usage: %s [options]\nOptions are:\n\
91  -h --help             shows this text\n\
92  -V --version          shows version information\n\
93  -F --foreground       starts the quota service in foreground\n\
94  -I --autofs           do not ignore mountpoints mounted by automounter\n\
95  -p --port <port>      listen on given port\n\
96  -s --no-setquota      disables remote calls to setquota (default)\n\
97  -S --setquota         enables remote calls to setquota\n\
98  -x --xtab <path>      set an alternative file with NFSD export table\n"), progname);
99
100 #else
101         errstr(_("Usage: %s [options]\nOptions are:\n\
102  -h --help             shows this text\n\
103  -V --version          shows version information\n\
104  -F --foreground       starts the quota service in foreground\n\
105  -I --autofs           do not ignore mountpoints mounted by automounter\n\
106  -p --port <port>      listen on given port\n\
107  -x --xtab <path>      set an alternative file with NFSD export table\n"), progname);
108 #endif
109 }
110
111 static void parse_options(int argc, char **argv)
112 {
113         char ostr[128]="", *endptr;
114         int i,opt;
115         int j=0;
116
117         sstrncpy(xtab_path, NFSD_XTAB_PATH, PATH_MAX);
118         for(i=0; options[i].name; i++) {
119                 ostr[j++] = options[i].val;
120                 if (options[i].has_arg)
121                         ostr[j++] = ':';
122         }
123         while ((opt=getopt_long(argc, argv, ostr, options, NULL))>=0) {
124                 switch(opt) {
125                         case 'V': 
126                                 version();
127                                 exit(0);
128                         case 'h':
129                                 show_help();
130                                 exit(0);
131                         case 'F':
132                                 flags |= FL_NODAEMON;
133                                 break;
134 #ifdef RPC_SETQUOTA
135                         case 's':
136                                 flags &= ~FL_SETQUOTA;
137                                 break;
138                         case 'S':       
139                                 flags |= FL_SETQUOTA;
140                                 break;
141 #endif
142                         case 'I':
143                                 flags |= FL_AUTOFS;
144                                 break;
145                         case 'p': 
146                                 port = strtol(optarg, &endptr, 0);
147                                 if (*endptr || port <= 0) {
148                                         errstr(_("Illegal port number: %s\n"), optarg);
149                                         show_help();
150                                         exit(1);
151                                 }
152                                 break;
153                         case 'x':
154                                 if (access(optarg, R_OK) < 0) {
155                                         errstr(_("Cannot access the specified xtab file %s: %s\n"), optarg, strerror(errno));
156                                         show_help();
157                                         exit(1);
158                                 }
159                                 sstrncpy(xtab_path, optarg, PATH_MAX);
160                                 break;
161                         default:
162                                 errstr(_("Unknown option '%c'.\n"), opt);
163                                 show_help();
164                                 exit(1);
165                 }
166         }
167 }
168
169
170 /*
171  * good_client checks if an quota client should be allowed to
172  * execute the requested rpc call.
173  */
174 int good_client(struct sockaddr_in *addr, ulong rq_proc)
175 {
176 #ifdef HOSTS_ACCESS
177         struct request_info req;
178 #endif
179         char *remote = inet_ntoa(addr->sin_addr);
180
181         if (rq_proc==RQUOTAPROC_SETQUOTA ||
182              rq_proc==RQUOTAPROC_SETACTIVEQUOTA) {
183                 /* If setquota is disabled, fail always */
184                 if (!(flags & FL_SETQUOTA)) {
185                         errstr(_("host %s attempted to call setquota when disabled\n"),
186                                remote);
187
188                         return 0;
189                 }
190                 /* Require that SETQUOTA calls originate from port < 1024 */
191                 if (ntohs(addr->sin_port)>=1024) {
192                         errstr(_("host %s attempted to call setquota from port >= 1024\n"),
193                                remote);
194                         return 0;
195                 }
196                 /* Setquota OK */
197         }
198
199 #ifdef HOSTS_ACCESS
200         /* NOTE: we could use different servicename for setquota calls to
201          * allow only some hosts to call setquota. */
202
203         request_init(&req, RQ_DAEMON, "rquotad", RQ_CLIENT_SIN, addr, 0);
204         sock_methods(&req);
205         if (hosts_access(&req))
206                 return 1;
207         errstr(_("Denied access to host %s\n"), remote);
208         return 0;
209 #else
210         /* If no access checking is available, OK always */
211         return 1;
212 #endif
213 }
214
215 static void rquotaprog_1(struct svc_req *rqstp, register SVCXPRT * transp)
216 {
217         union {
218                 getquota_args rquotaproc_getquota_1_arg;
219                 setquota_args rquotaproc_setquota_1_arg;
220                 getquota_args rquotaproc_getactivequota_1_arg;
221                 setquota_args rquotaproc_setactivequota_1_arg;
222         } argument;
223         char *result;
224         xdrproc_t xdr_argument, xdr_result;
225         char *(*local) (char *, struct svc_req *);
226
227         /*
228          *  Authenticate host
229          */
230         if (!good_client(svc_getcaller(rqstp->rq_xprt),rqstp->rq_proc)) {
231                 svcerr_auth (transp, AUTH_FAILED);
232                 return;
233         }
234
235         /*
236          * Don't bother authentication for NULLPROC.
237          */
238         if (rqstp->rq_proc == NULLPROC) {
239                 (void)svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
240                 return;
241         }
242
243         /*
244          * Get authentication.
245          */
246         switch (rqstp->rq_cred.oa_flavor) {
247           case AUTH_UNIX:
248                   unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
249                   break;
250           case AUTH_NULL:
251           default:
252                   svcerr_weakauth(transp);
253                   return;
254         }
255
256         switch (rqstp->rq_proc) {
257           case RQUOTAPROC_GETQUOTA:
258                   xdr_argument = (xdrproc_t) xdr_getquota_args;
259                   xdr_result = (xdrproc_t) xdr_getquota_rslt;
260                   local = (char *(*)(char *, struct svc_req *))rquotaproc_getquota_1_svc;
261                   break;
262
263           case RQUOTAPROC_SETQUOTA:
264                   xdr_argument = (xdrproc_t) xdr_setquota_args;
265                   xdr_result = (xdrproc_t) xdr_setquota_rslt;
266                   local = (char *(*)(char *, struct svc_req *))rquotaproc_setquota_1_svc;
267                   break;
268
269           case RQUOTAPROC_GETACTIVEQUOTA:
270                   xdr_argument = (xdrproc_t) xdr_getquota_args;
271                   xdr_result = (xdrproc_t) xdr_getquota_rslt;
272                   local = (char *(*)(char *, struct svc_req *))rquotaproc_getactivequota_1_svc;
273                   break;
274
275           case RQUOTAPROC_SETACTIVEQUOTA:
276                   xdr_argument = (xdrproc_t) xdr_setquota_args;
277                   xdr_result = (xdrproc_t) xdr_setquota_rslt;
278                   local = (char *(*)(char *, struct svc_req *))rquotaproc_setactivequota_1_svc;
279                   break;
280
281           default:
282                   svcerr_noproc(transp);
283                   return;
284         }
285         memset(&argument, 0, sizeof(argument));
286         if (!svc_getargs(transp, xdr_argument, (caddr_t) & argument)) {
287                 svcerr_decode(transp);
288                 return;
289         }
290         result = (*local) ((char *)&argument, rqstp);
291         if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
292                 svcerr_systemerr(transp);
293         }
294         if (!svc_freeargs(transp, xdr_argument, (caddr_t) & argument)) {
295                 errstr(_("unable to free arguments\n"));
296                 exit(1);
297         }
298         return;
299 }
300
301 static void rquotaprog_2(struct svc_req *rqstp, register SVCXPRT * transp)
302 {
303         union {
304                 ext_getquota_args rquotaproc_getquota_2_arg;
305                 ext_setquota_args rquotaproc_setquota_2_arg;
306                 ext_getquota_args rquotaproc_getactivequota_2_arg;
307                 ext_setquota_args rquotaproc_setactivequota_2_arg;
308         } argument;
309         char *result;
310         xdrproc_t xdr_argument, xdr_result;
311         char *(*local) (char *, struct svc_req *);
312
313         /*
314          *  Authenticate host
315          */
316         if (!good_client(svc_getcaller(rqstp->rq_xprt),rqstp->rq_proc)) {
317                 svcerr_auth (transp, AUTH_FAILED);
318                 return;
319         }
320
321         /*
322          * Don't bother authentication for NULLPROC.
323          */
324         if (rqstp->rq_proc == NULLPROC) {
325                 (void)svc_sendreply(transp, (xdrproc_t) xdr_void, (char *)NULL);
326                 return;
327         }
328
329         /*
330          * Get authentication.
331          */
332         switch (rqstp->rq_cred.oa_flavor) {
333           case AUTH_UNIX:
334                   unix_cred = (struct authunix_parms *)rqstp->rq_clntcred;
335                   break;
336           case AUTH_NULL:
337           default:
338                   svcerr_weakauth(transp);
339                   return;
340         }
341
342         switch (rqstp->rq_proc) {
343           case RQUOTAPROC_GETQUOTA:
344                   xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
345                   xdr_result = (xdrproc_t) xdr_getquota_rslt;
346                   local = (char *(*)(char *, struct svc_req *))rquotaproc_getquota_2_svc;
347                   break;
348
349           case RQUOTAPROC_SETQUOTA:
350                   xdr_argument = (xdrproc_t) xdr_ext_setquota_args;
351                   xdr_result = (xdrproc_t) xdr_setquota_rslt;
352                   local = (char *(*)(char *, struct svc_req *))rquotaproc_setquota_2_svc;
353                   break;
354
355           case RQUOTAPROC_GETACTIVEQUOTA:
356                   xdr_argument = (xdrproc_t) xdr_ext_getquota_args;
357                   xdr_result = (xdrproc_t) xdr_getquota_rslt;
358                   local = (char *(*)(char *, struct svc_req *))rquotaproc_getactivequota_2_svc;
359                   break;
360
361           case RQUOTAPROC_SETACTIVEQUOTA:
362                   xdr_argument = (xdrproc_t) xdr_ext_setquota_args;
363                   xdr_result = (xdrproc_t) xdr_setquota_rslt;
364                   local = (char *(*)(char *, struct svc_req *))rquotaproc_setactivequota_2_svc;
365                   break;
366
367           default:
368                   svcerr_noproc(transp);
369                   return;
370         }
371         memset(&argument, 0, sizeof(argument));
372         if (!svc_getargs(transp, xdr_argument, (caddr_t) & argument)) {
373                 svcerr_decode(transp);
374                 return;
375         }
376         result = (*local) ((char *)&argument, rqstp);
377         if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
378                 svcerr_systemerr(transp);
379         }
380         if (!svc_freeargs(transp, xdr_argument, (caddr_t) & argument)) {
381                 errstr(_("unable to free arguments\n"));
382                 exit(1);
383         }
384         return;
385 }
386
387 static void
388 unregister (int sig)
389 {
390         pmap_unset(RQUOTAPROG, RQUOTAVERS);
391         pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
392         exit(0);
393 }
394
395 /* Parse NFSD export table and find a filesystem pseudoroot if it is there */
396 static void get_pseudoroot(void)
397 {
398         FILE *f;
399         char exp_line[1024];
400         char *c;
401
402         strcpy(nfs_pseudoroot, "/");
403         if (!(f = fopen(xtab_path, "r"))) {
404                 errstr(_("Warning: Cannot open export table %s: %s\nUsing '/' as a pseudofilesystem root.\n"), xtab_path, strerror(errno));
405                 return;
406         }
407         while (fgets(exp_line, sizeof(exp_line), f)) {
408                 if (exp_line[0] == '#' || exp_line[0] == '\n')  /* Comment, empty line? */
409                         continue;
410                 c = strchr(exp_line, '\t');
411                 if (!c) /* Huh, line we don't understand... */
412                         continue;
413                 *c = 0;
414                 /* Find the beginning of export options */
415                 c = strchr(c+1, '(');
416                 if (!c)
417                         continue;
418                 c = strstr(c, "fsid=0");
419                 if (c) {
420                         sstrncpy(nfs_pseudoroot, exp_line, PATH_MAX);
421                         sstrncat(nfs_pseudoroot, "/", PATH_MAX);
422                         break;
423                 }
424         }
425         fclose(f);
426 }
427
428 int main(int argc, char **argv)
429 {
430         register SVCXPRT *transp;
431         struct sigaction sa;
432         int sock;
433
434         gettexton();
435         progname = basename(argv[0]);
436         parse_options(argc, argv);
437
438         init_kernel_interface();
439         get_pseudoroot();
440         pmap_unset(RQUOTAPROG, RQUOTAVERS);
441         pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
442
443         sa.sa_handler = SIG_IGN;
444         sa.sa_flags = 0;
445         sigemptyset(&sa.sa_mask);
446         sigaction(SIGCHLD, &sa, NULL);
447
448         sa.sa_handler = unregister;
449         sigaction(SIGHUP, &sa, NULL);
450         sigaction(SIGINT, &sa, NULL);
451         sigaction(SIGTERM, &sa, NULL);
452
453         sock = svcudp_socket(RQUOTAPROG, port, 1);
454         transp = svcudp_create(sock == -1 ? RPC_ANYSOCK : sock);
455         if (transp == NULL) {
456                 errstr(_("cannot create udp service.\n"));
457                 exit(1);
458         }
459         if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_UDP)) {
460                 errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, udp).\n"));
461                 exit(1);
462         }
463         if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_UDP)) {
464                 errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, udp).\n"));
465                 exit(1);
466         }
467
468         sock = svctcp_socket(RQUOTAPROG, port, 1);
469         transp = svctcp_create(sock == -1 ? RPC_ANYSOCK : sock, 0, 0);
470         if (transp == NULL) {
471                 errstr(_("cannot create tcp service.\n"));
472                 exit(1);
473         }
474         if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_TCP)) {
475                 errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, tcp).\n"));
476                 exit(1);
477         }
478         if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_TCP)) {
479                 errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, tcp).\n"));
480                 exit(1);
481         }
482
483         if (!(flags & FL_NODAEMON)) {
484                 use_syslog();
485                 daemon(0, 0);
486         }
487         svc_run();
488         errstr(_("svc_run returned\n"));
489         exit(1);
490         /* NOTREACHED */
491 }