1 /*
2 * by Manuel Bouyer (bouyer@ensta.fr)
3 *
4 * There is no copyright, you can use it as you want.
5 */
6
7 #include <sys/cdefs.h>
8 __FBSDID("$FreeBSD: stable/12/libexec/rpc.rquotad/rquotad.c 336017 2018-07-05 22:56:13Z sef $");
9
10 #include <sys/param.h>
11 #include <sys/mount.h>
12 #include <sys/file.h>
13 #include <sys/stat.h>
14 #include <sys/socket.h>
15
16 #include <ufs/ufs/quota.h>
17 #include <rpc/rpc.h>
18 #include <rpcsvc/rquota.h>
19 #include <arpa/inet.h>
20 #include <netdb.h>
21
22 #include <ctype.h>
23 #include <errno.h>
24 #include <fstab.h>
25 #include <grp.h>
26 #include <libutil.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <err.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <unistd.h>
35
36 static void rquota_service_1(struct svc_req *request, SVCXPRT *transp);
37 static void rquota_service_2(struct svc_req *request, SVCXPRT *transp);
38 static void sendquota(struct svc_req *request, SVCXPRT *transp);
39 static void sendquota_extended(struct svc_req *request, SVCXPRT *transp);
40 static int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
41
42 static int from_inetd = 1;
43 static int debug = 0;
44
45 static void
cleanup(int sig)46 cleanup(int sig)
47 {
48
49 (void)sig;
50 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
51 exit(0);
52 }
53
54 int
main(int argc,char ** argv)55 main(int argc, char **argv)
56 {
57 SVCXPRT *transp;
58 int ok;
59 struct sockaddr_storage from;
60 socklen_t fromlen;
61 int vers;
62 int ch;
63
64 while ((ch = getopt(argc, argv, "d")) != -1) {
65 switch (ch) {
66 case 'd':
67 debug++;
68 break;
69 default:
70 break;
71 }
72 }
73
74 fromlen = sizeof(from);
75 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
76 from_inetd = 0;
77
78 if (!from_inetd) {
79 if (!debug)
80 daemon(0, 0);
81 (void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
82 (void)signal(SIGINT, cleanup);
83 (void)signal(SIGTERM, cleanup);
84 (void)signal(SIGHUP, cleanup);
85 }
86
87 openlog("rpc.rquotad", LOG_CONS|LOG_PID, LOG_DAEMON);
88
89 /* create and register the service */
90 if (from_inetd) {
91 transp = svc_tli_create(0, NULL, NULL, 0, 0);
92 if (transp == NULL) {
93 syslog(LOG_ERR, "couldn't create udp service.");
94 exit(1);
95 }
96 vers = RQUOTAVERS;
97 ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
98 rquota_service_1, NULL);
99 if (ok) {
100 vers = EXT_RQUOTAVERS;
101 ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
102 rquota_service_2, NULL);
103 }
104 } else {
105 vers = RQUOTAVERS;
106 ok = svc_create(rquota_service_1,
107 RQUOTAPROG, RQUOTAVERS, "udp");
108 if (ok) {
109 vers = EXT_RQUOTAVERS;
110 ok = svc_create(rquota_service_2,
111 RQUOTAPROG, EXT_RQUOTAVERS, "udp");
112
113 }
114 }
115 if (!ok) {
116 syslog(LOG_ERR,
117 "unable to register (RQUOTAPROG, %s, %s)",
118 vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS",
119 from_inetd ? "(inetd)" : "udp");
120 exit(1);
121 }
122
123 svc_run();
124 syslog(LOG_ERR, "svc_run returned");
125 exit(1);
126 }
127
128 static void
rquota_service_2(struct svc_req * request,SVCXPRT * transp)129 rquota_service_2(struct svc_req *request, SVCXPRT *transp)
130 {
131
132 switch (request->rq_proc) {
133 case NULLPROC:
134 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
135 break;
136 case RQUOTAPROC_GETQUOTA:
137 case RQUOTAPROC_GETACTIVEQUOTA:
138 sendquota_extended(request, transp);
139 break;
140 default:
141 svcerr_noproc(transp);
142 break;
143 }
144 if (from_inetd)
145 exit(0);
146 }
147
148 static void
rquota_service_1(struct svc_req * request,SVCXPRT * transp)149 rquota_service_1(struct svc_req *request, SVCXPRT *transp)
150 {
151
152 switch (request->rq_proc) {
153 case NULLPROC:
154 (void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
155 break;
156 case RQUOTAPROC_GETQUOTA:
157 case RQUOTAPROC_GETACTIVEQUOTA:
158 sendquota(request, transp);
159 break;
160 default:
161 svcerr_noproc(transp);
162 break;
163 }
164 if (from_inetd)
165 exit(0);
166 }
167
168 /* read quota for the specified id, and send it */
169 static void
sendquota(struct svc_req * request,SVCXPRT * transp)170 sendquota(struct svc_req *request, SVCXPRT *transp)
171 {
172 struct getquota_args getq_args;
173 struct getquota_rslt getq_rslt;
174 struct dqblk dqblk;
175 struct timeval timev;
176 int scale;
177
178 bzero(&getq_args, sizeof(getq_args));
179 if (!svc_getargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
180 svcerr_decode(transp);
181 return;
182 }
183 if (request->rq_cred.oa_flavor != AUTH_UNIX) {
184 /* bad auth */
185 getq_rslt.status = Q_EPERM;
186 } else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
187 /* failed, return noquota */
188 getq_rslt.status = Q_NOQUOTA;
189 } else {
190 gettimeofday(&timev, NULL);
191 getq_rslt.status = Q_OK;
192 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
193 scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
194 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
195 DEV_BSIZE * scale;
196 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
197 dqblk.dqb_bhardlimit / scale;
198 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
199 dqblk.dqb_bsoftlimit / scale;
200 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
201 dqblk.dqb_curblocks / scale;
202 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
203 dqblk.dqb_ihardlimit;
204 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
205 dqblk.dqb_isoftlimit;
206 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
207 dqblk.dqb_curinodes;
208 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
209 dqblk.dqb_btime - timev.tv_sec;
210 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
211 dqblk.dqb_itime - timev.tv_sec;
212 }
213 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
214 svcerr_systemerr(transp);
215 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
216 syslog(LOG_ERR, "unable to free arguments");
217 exit(1);
218 }
219 }
220
221 static void
sendquota_extended(struct svc_req * request,SVCXPRT * transp)222 sendquota_extended(struct svc_req *request, SVCXPRT *transp)
223 {
224 struct ext_getquota_args getq_args;
225 struct getquota_rslt getq_rslt;
226 struct dqblk dqblk;
227 struct timeval timev;
228 int scale;
229
230 bzero(&getq_args, sizeof(getq_args));
231 if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
232 svcerr_decode(transp);
233 return;
234 }
235 if (request->rq_cred.oa_flavor != AUTH_UNIX) {
236 /* bad auth */
237 getq_rslt.status = Q_EPERM;
238 } else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) {
239 /* failed, return noquota */
240 getq_rslt.status = Q_NOQUOTA;
241 } else {
242 gettimeofday(&timev, NULL);
243 getq_rslt.status = Q_OK;
244 getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
245 scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
246 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
247 DEV_BSIZE * scale;
248 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
249 dqblk.dqb_bhardlimit / scale;
250 getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
251 dqblk.dqb_bsoftlimit / scale;
252 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
253 dqblk.dqb_curblocks / scale;
254 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
255 dqblk.dqb_ihardlimit;
256 getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
257 dqblk.dqb_isoftlimit;
258 getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
259 dqblk.dqb_curinodes;
260 getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
261 dqblk.dqb_btime - timev.tv_sec;
262 getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
263 dqblk.dqb_itime - timev.tv_sec;
264 }
265 if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
266 svcerr_systemerr(transp);
267 if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
268 syslog(LOG_ERR, "unable to free arguments");
269 exit(1);
270 }
271 }
272
273 /*
274 * gets the quotas for id, filesystem path.
275 * Return 0 if fail, 1 otherwise
276 */
277 static int
getfsquota(int type,long id,char * path,struct dqblk * dqblk)278 getfsquota(int type, long id, char *path, struct dqblk *dqblk)
279 {
280 struct quotafile *qf;
281 /*
282 * Remote quota checking is limited to mounted filesystems.
283 * Since UFS and ZFS support the quota system calls, we
284 * only need to make an fstab object that has the path, and
285 * a blank name for the filesystem type.
286 * This allows the quota_open() call to work the way we
287 * expect it to.
288 *
289 * The static char declaration is because compiler warnings
290 * don't allow passing a const char * to a char *.
291 */
292 int rv;
293 static char blank[] = "";
294 struct fstab fst;
295
296 fst.fs_file = path;
297 fst.fs_mntops = blank;
298 fst.fs_vfstype = blank;
299
300 if (type != USRQUOTA && type != GRPQUOTA)
301 return (0);
302
303 qf = quota_open(&fst, type, O_RDONLY);
304 if (debug)
305 warnx("quota_open(<%s, %s>, %d) returned %p",
306 fst.fs_file, fst.fs_mntops, type,
307 qf);
308 if (qf == NULL)
309 return (0);
310
311 rv = quota_read(qf, dqblk, id) == 0;
312 quota_close(qf);
313 if (debug)
314 warnx("getfsquota(%d, %ld, %s, %p) -> %d",
315 type, id, path, dqblk, rv);
316 return (rv);
317 }
318