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