1 /*        $NetBSD: rquotad.c,v 1.33 2014/03/18 11:00:20 gson Exp $    */
2 
3 /*
4  * by Manuel Bouyer (bouyer@ensta.fr). Public domain.
5  */
6 
7 #include <sys/cdefs.h>
8 #ifndef lint
9 __RCSID("$NetBSD: rquotad.c,v 1.33 2014/03/18 11:00:20 gson Exp $");
10 #endif
11 
12 #include <sys/param.h>
13 #include <sys/types.h>
14 #include <sys/mount.h>
15 #include <sys/file.h>
16 #include <sys/stat.h>
17 #include <sys/socket.h>
18 #include <signal.h>
19 
20 #include <stdio.h>
21 #include <fstab.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <syslog.h>
30 
31 #include <rpc/rpc.h>
32 #include <rpcsvc/rquota.h>
33 #include <arpa/inet.h>
34 
35 #include <quota.h>
36 
37 static void rquota_service(struct svc_req *request, SVCXPRT *transp);
38 static void ext_rquota_service(struct svc_req *request, SVCXPRT *transp);
39 static void sendquota(struct svc_req *request, int vers, SVCXPRT *transp);
40 __dead static void cleanup(int);
41 
42 static int from_inetd = 1;
43 
44 static void
cleanup(int dummy)45 cleanup(int dummy)
46 {
47 
48           (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
49           (void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
50           exit(0);
51 }
52 
53 int
main(int argc,char * argv[])54 main(int argc, char *argv[])
55 {
56           SVCXPRT *transp;
57           struct sockaddr_storage from;
58           socklen_t fromlen;
59 
60           fromlen = sizeof(from);
61           if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
62                     from_inetd = 0;
63 
64           if (!from_inetd) {
65                     (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
66                     (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
67           }
68 
69           openlog("rpc.rquotad", LOG_PID, LOG_DAEMON);
70 
71           /* create and register the service */
72           if (from_inetd) {
73                     transp = svc_dg_create(0, 0, 0);
74                     if (transp == NULL) {
75                               syslog(LOG_ERR, "couldn't create udp service.");
76                               exit(1);
77                     }
78                     if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service,
79                         NULL)) {
80                               syslog(LOG_ERR,
81                                   "unable to register (RQUOTAPROG, RQUOTAVERS).");
82                               exit(1);
83                     }
84                     if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
85                         ext_rquota_service, NULL)) {
86                               syslog(LOG_ERR,
87                                   "unable to register (RQUOTAPROG, EXT_RQUOTAVERS).");
88                               exit(1);
89                     }
90           } else {
91                     if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){
92                               syslog(LOG_ERR,
93                                   "unable to create (RQUOTAPROG, RQUOTAVERS).");
94                               exit(1);
95                     }
96                     if (!svc_create(ext_rquota_service, RQUOTAPROG,
97                         EXT_RQUOTAVERS, "udp")){
98                               syslog(LOG_ERR,
99                                   "unable to create (RQUOTAPROG, EXT_RQUOTAVERS).");
100                               exit(1);
101                     }
102           }
103 
104           if (!from_inetd) {
105                     daemon(0, 0);
106                     (void) signal(SIGINT, cleanup);
107                     (void) signal(SIGTERM, cleanup);
108                     (void) signal(SIGHUP, cleanup);
109           }
110           svc_run();
111           syslog(LOG_ERR, "svc_run returned");
112           exit(1);
113 }
114 
115 static void
rquota_service(struct svc_req * request,SVCXPRT * transp)116 rquota_service(struct svc_req *request, SVCXPRT *transp)
117 {
118           switch (request->rq_proc) {
119           case NULLPROC:
120                     (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
121                     break;
122 
123           case RQUOTAPROC_GETQUOTA:
124           case RQUOTAPROC_GETACTIVEQUOTA:
125                     sendquota(request, RQUOTAVERS, transp);
126                     break;
127 
128           default:
129                     svcerr_noproc(transp);
130                     break;
131           }
132           if (from_inetd)
133                     exit(0);
134 }
135 
136 static void
ext_rquota_service(struct svc_req * request,SVCXPRT * transp)137 ext_rquota_service(struct svc_req *request, SVCXPRT *transp)
138 {
139           switch (request->rq_proc) {
140           case NULLPROC:
141                     (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
142                     break;
143 
144           case RQUOTAPROC_GETQUOTA:
145           case RQUOTAPROC_GETACTIVEQUOTA:
146                     sendquota(request, EXT_RQUOTAVERS, transp);
147                     break;
148 
149           default:
150                     svcerr_noproc(transp);
151                     break;
152           }
153           if (from_inetd)
154                     exit(0);
155 }
156 
157 /*
158  * Convert a limit to rquota representation (where 0 == unlimited).
159  * Clamp the result into a uint32_t.
160  */
161 static uint32_t
limit_to_rquota(uint64_t lim)162 limit_to_rquota(uint64_t lim)
163 {
164           if (lim == QUOTA_NOLIMIT || lim > 0xfffffffeUL)
165                     return 0;
166           else
167                     return (lim + 1);
168 }
169 
170 /*
171  * Convert a time to rquota representation.
172  */
173 static uint32_t
time_to_rquota(time_t when,time_t now)174 time_to_rquota(time_t when, time_t now)
175 {
176           if (when == QUOTA_NOTIME) {
177                     return 0;
178           } else {
179                     return when - now;
180           }
181 }
182 
183 /*
184  * Convert to rquota representation.
185  */
186 static void
quotavals_to_rquota(const struct quotaval * blocks,const struct quotaval * files,struct rquota * rq)187 quotavals_to_rquota(const struct quotaval *blocks,
188                         const struct quotaval *files,
189                         struct rquota *rq)
190 {
191           struct timeval now;
192 
193           gettimeofday(&now, NULL);
194 
195           rq->rq_active = TRUE;
196           rq->rq_bsize = DEV_BSIZE;
197 
198           rq->rq_bhardlimit = limit_to_rquota(blocks->qv_hardlimit);
199           rq->rq_bsoftlimit = limit_to_rquota(blocks->qv_softlimit);
200           rq->rq_curblocks = blocks->qv_usage;
201           rq->rq_btimeleft = time_to_rquota(blocks->qv_expiretime, now.tv_sec);
202 
203           rq->rq_fhardlimit = limit_to_rquota(files->qv_hardlimit);
204           rq->rq_fsoftlimit = limit_to_rquota(files->qv_softlimit);
205           rq->rq_curfiles = files->qv_usage;
206           rq->rq_ftimeleft = time_to_rquota(files->qv_expiretime, now.tv_sec);
207 }
208 
209 /* read quota for the specified id, and send it */
210 static void
sendquota(struct svc_req * request,int vers,SVCXPRT * transp)211 sendquota(struct svc_req *request, int vers, SVCXPRT *transp)
212 {
213           struct getquota_args getq_args;
214           struct ext_getquota_args ext_getq_args;
215           struct getquota_rslt getq_rslt;
216           struct quotahandle *qh;
217           struct quotakey qk;
218           struct quotaval blocks, files;
219           int idtype;
220 
221           memset((char *)&getq_args, 0, sizeof(getq_args));
222           memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args));
223           switch (vers) {
224           case RQUOTAVERS:
225                     if (!svc_getargs(transp, xdr_getquota_args,
226                         (caddr_t)&getq_args)) {
227                               svcerr_decode(transp);
228                               return;
229                     }
230                     ext_getq_args.gqa_pathp = getq_args.gqa_pathp;
231                     ext_getq_args.gqa_id = getq_args.gqa_uid;
232                     ext_getq_args.gqa_type = RQUOTA_USRQUOTA;
233                     break;
234           case EXT_RQUOTAVERS:
235                     if (!svc_getargs(transp, xdr_ext_getquota_args,
236                         (caddr_t)&ext_getq_args)) {
237                               svcerr_decode(transp);
238                               return;
239                     }
240                     break;
241           }
242           switch (ext_getq_args.gqa_type) {
243           case RQUOTA_USRQUOTA:
244                     idtype = QUOTA_IDTYPE_USER;
245                     break;
246           case RQUOTA_GRPQUOTA:
247                     idtype = QUOTA_IDTYPE_GROUP;
248                     break;
249           default:
250                     getq_rslt.status = Q_NOQUOTA;
251                     goto out;
252           }
253           if (request->rq_cred.oa_flavor != AUTH_UNIX) {
254                     /* bad auth */
255                     getq_rslt.status = Q_EPERM;
256                     goto out;
257           }
258 
259           /*
260            * XXX validate the path...
261            */
262 
263           qh = quota_open(ext_getq_args.gqa_pathp);
264           if (qh == NULL) {
265                     /*
266                      * There are only three possible responses: success,
267                      * permission denied, and "no quota", so we return
268                      * the last for essentially all errors.
269                      */
270                     if (errno == EPERM || errno == EACCES) {
271                               getq_rslt.status = Q_EPERM;
272                               goto out;
273                     }
274                     getq_rslt.status = Q_NOQUOTA;
275                     goto out;
276           }
277 
278           qk.qk_id = ext_getq_args.gqa_id;
279           qk.qk_idtype = idtype;
280           qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS;
281           if (quota_get(qh, &qk, &blocks) < 0) {
282                     /* failed, return noquota */
283                     quota_close(qh);
284                     getq_rslt.status = Q_NOQUOTA;
285                     goto out;
286           }
287 
288           qk.qk_objtype = QUOTA_OBJTYPE_FILES;
289           if (quota_get(qh, &qk, &files) < 0) {
290                     /* failed, return noquota */
291                     quota_close(qh);
292                     getq_rslt.status = Q_NOQUOTA;
293                     goto out;
294           }
295 
296           quota_close(qh);
297 
298           quotavals_to_rquota(&blocks, &files,
299                                   &getq_rslt.getquota_rslt_u.gqr_rquota);
300           getq_rslt.status = Q_OK;
301 
302 out:
303           if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, (char *)&getq_rslt))
304                     svcerr_systemerr(transp);
305           if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
306                     syslog(LOG_ERR, "unable to free arguments");
307                     exit(1);
308           }
309 }
310