1 1.33 gson /* $NetBSD: rquotad.c,v 1.33 2014/03/18 11:00:20 gson 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.33 gson __RCSID("$NetBSD: rquotad.c,v 1.33 2014/03/18 11:00:20 gson 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.16 fvdl (void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL); 66 1.20 bouyer (void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL); 67 1.1 deraadt } 68 1.3 mycroft 69 1.13 mrg openlog("rpc.rquotad", LOG_PID, LOG_DAEMON); 70 1.1 deraadt 71 1.1 deraadt /* create and register the service */ 72 1.15 fvdl if (from_inetd) { 73 1.15 fvdl transp = svc_dg_create(0, 0, 0); 74 1.15 fvdl if (transp == NULL) { 75 1.15 fvdl syslog(LOG_ERR, "couldn't create udp service."); 76 1.15 fvdl exit(1); 77 1.15 fvdl } 78 1.15 fvdl if (!svc_reg(transp, RQUOTAPROG, RQUOTAVERS, rquota_service, 79 1.15 fvdl NULL)) { 80 1.15 fvdl syslog(LOG_ERR, 81 1.15 fvdl "unable to register (RQUOTAPROG, RQUOTAVERS)."); 82 1.15 fvdl exit(1); 83 1.15 fvdl } 84 1.20 bouyer if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS, 85 1.20 bouyer ext_rquota_service, NULL)) { 86 1.20 bouyer syslog(LOG_ERR, 87 1.20 bouyer "unable to register (RQUOTAPROG, EXT_RQUOTAVERS)."); 88 1.20 bouyer exit(1); 89 1.20 bouyer } 90 1.15 fvdl } else { 91 1.15 fvdl if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){ 92 1.15 fvdl syslog(LOG_ERR, 93 1.15 fvdl "unable to create (RQUOTAPROG, RQUOTAVERS)."); 94 1.15 fvdl exit(1); 95 1.15 fvdl } 96 1.20 bouyer if (!svc_create(ext_rquota_service, RQUOTAPROG, 97 1.20 bouyer EXT_RQUOTAVERS, "udp")){ 98 1.20 bouyer syslog(LOG_ERR, 99 1.20 bouyer "unable to create (RQUOTAPROG, EXT_RQUOTAVERS)."); 100 1.20 bouyer exit(1); 101 1.20 bouyer } 102 1.1 deraadt } 103 1.4 mycroft 104 1.33 gson if (!from_inetd) { 105 1.33 gson daemon(0, 0); 106 1.33 gson (void) signal(SIGINT, cleanup); 107 1.33 gson (void) signal(SIGTERM, cleanup); 108 1.33 gson (void) signal(SIGHUP, cleanup); 109 1.33 gson } 110 1.1 deraadt svc_run(); 111 1.4 mycroft syslog(LOG_ERR, "svc_run returned"); 112 1.4 mycroft exit(1); 113 1.1 deraadt } 114 1.1 deraadt 115 1.28 joerg static void 116 1.15 fvdl rquota_service(struct svc_req *request, SVCXPRT *transp) 117 1.1 deraadt { 118 1.1 deraadt switch (request->rq_proc) { 119 1.1 deraadt case NULLPROC: 120 1.30 plunky (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 121 1.1 deraadt break; 122 1.4 mycroft 123 1.1 deraadt case RQUOTAPROC_GETQUOTA: 124 1.1 deraadt case RQUOTAPROC_GETACTIVEQUOTA: 125 1.20 bouyer sendquota(request, RQUOTAVERS, transp); 126 1.20 bouyer break; 127 1.20 bouyer 128 1.20 bouyer default: 129 1.20 bouyer svcerr_noproc(transp); 130 1.20 bouyer break; 131 1.20 bouyer } 132 1.20 bouyer if (from_inetd) 133 1.20 bouyer exit(0); 134 1.20 bouyer } 135 1.20 bouyer 136 1.28 joerg static void 137 1.20 bouyer ext_rquota_service(struct svc_req *request, SVCXPRT *transp) 138 1.20 bouyer { 139 1.20 bouyer switch (request->rq_proc) { 140 1.20 bouyer case NULLPROC: 141 1.30 plunky (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); 142 1.20 bouyer break; 143 1.20 bouyer 144 1.20 bouyer case RQUOTAPROC_GETQUOTA: 145 1.20 bouyer case RQUOTAPROC_GETACTIVEQUOTA: 146 1.20 bouyer sendquota(request, EXT_RQUOTAVERS, transp); 147 1.1 deraadt break; 148 1.4 mycroft 149 1.1 deraadt default: 150 1.4 mycroft svcerr_noproc(transp); 151 1.1 deraadt break; 152 1.1 deraadt } 153 1.4 mycroft if (from_inetd) 154 1.4 mycroft exit(0); 155 1.1 deraadt } 156 1.1 deraadt 157 1.32 dholland /* 158 1.32 dholland * Convert a limit to rquota representation (where 0 == unlimited). 159 1.32 dholland * Clamp the result into a uint32_t. 160 1.32 dholland */ 161 1.32 dholland static uint32_t 162 1.32 dholland limit_to_rquota(uint64_t lim) 163 1.32 dholland { 164 1.32 dholland if (lim == QUOTA_NOLIMIT || lim > 0xfffffffeUL) 165 1.32 dholland return 0; 166 1.32 dholland else 167 1.32 dholland return (lim + 1); 168 1.32 dholland } 169 1.32 dholland 170 1.32 dholland /* 171 1.32 dholland * Convert a time to rquota representation. 172 1.32 dholland */ 173 1.32 dholland static uint32_t 174 1.32 dholland time_to_rquota(time_t when, time_t now) 175 1.32 dholland { 176 1.32 dholland if (when == QUOTA_NOTIME) { 177 1.32 dholland return 0; 178 1.32 dholland } else { 179 1.32 dholland return when - now; 180 1.32 dholland } 181 1.32 dholland } 182 1.32 dholland 183 1.32 dholland /* 184 1.32 dholland * Convert to rquota representation. 185 1.32 dholland */ 186 1.32 dholland static void 187 1.32 dholland quotavals_to_rquota(const struct quotaval *blocks, 188 1.32 dholland const struct quotaval *files, 189 1.32 dholland struct rquota *rq) 190 1.32 dholland { 191 1.32 dholland struct timeval now; 192 1.32 dholland 193 1.32 dholland gettimeofday(&now, NULL); 194 1.32 dholland 195 1.32 dholland rq->rq_active = TRUE; 196 1.32 dholland rq->rq_bsize = DEV_BSIZE; 197 1.32 dholland 198 1.32 dholland rq->rq_bhardlimit = limit_to_rquota(blocks->qv_hardlimit); 199 1.32 dholland rq->rq_bsoftlimit = limit_to_rquota(blocks->qv_softlimit); 200 1.32 dholland rq->rq_curblocks = blocks->qv_usage; 201 1.32 dholland rq->rq_btimeleft = time_to_rquota(blocks->qv_expiretime, now.tv_sec); 202 1.32 dholland 203 1.32 dholland rq->rq_fhardlimit = limit_to_rquota(files->qv_hardlimit); 204 1.32 dholland rq->rq_fsoftlimit = limit_to_rquota(files->qv_softlimit); 205 1.32 dholland rq->rq_curfiles = files->qv_usage; 206 1.32 dholland rq->rq_ftimeleft = time_to_rquota(files->qv_expiretime, now.tv_sec); 207 1.32 dholland } 208 1.32 dholland 209 1.1 deraadt /* read quota for the specified id, and send it */ 210 1.28 joerg static void 211 1.20 bouyer sendquota(struct svc_req *request, int vers, SVCXPRT *transp) 212 1.1 deraadt { 213 1.1 deraadt struct getquota_args getq_args; 214 1.20 bouyer struct ext_getquota_args ext_getq_args; 215 1.1 deraadt struct getquota_rslt getq_rslt; 216 1.32 dholland struct quotahandle *qh; 217 1.32 dholland struct quotakey qk; 218 1.32 dholland struct quotaval blocks, files; 219 1.32 dholland int idtype; 220 1.1 deraadt 221 1.12 perry memset((char *)&getq_args, 0, sizeof(getq_args)); 222 1.21 bouyer memset((char *)&ext_getq_args, 0, sizeof(ext_getq_args)); 223 1.20 bouyer switch (vers) { 224 1.20 bouyer case RQUOTAVERS: 225 1.20 bouyer if (!svc_getargs(transp, xdr_getquota_args, 226 1.20 bouyer (caddr_t)&getq_args)) { 227 1.20 bouyer svcerr_decode(transp); 228 1.20 bouyer return; 229 1.20 bouyer } 230 1.20 bouyer ext_getq_args.gqa_pathp = getq_args.gqa_pathp; 231 1.20 bouyer ext_getq_args.gqa_id = getq_args.gqa_uid; 232 1.20 bouyer ext_getq_args.gqa_type = RQUOTA_USRQUOTA; 233 1.20 bouyer break; 234 1.20 bouyer case EXT_RQUOTAVERS: 235 1.20 bouyer if (!svc_getargs(transp, xdr_ext_getquota_args, 236 1.20 bouyer (caddr_t)&ext_getq_args)) { 237 1.20 bouyer svcerr_decode(transp); 238 1.20 bouyer return; 239 1.20 bouyer } 240 1.20 bouyer break; 241 1.1 deraadt } 242 1.25 bouyer switch (ext_getq_args.gqa_type) { 243 1.25 bouyer case RQUOTA_USRQUOTA: 244 1.32 dholland idtype = QUOTA_IDTYPE_USER; 245 1.25 bouyer break; 246 1.25 bouyer case RQUOTA_GRPQUOTA: 247 1.32 dholland idtype = QUOTA_IDTYPE_GROUP; 248 1.25 bouyer break; 249 1.25 bouyer default: 250 1.25 bouyer getq_rslt.status = Q_NOQUOTA; 251 1.25 bouyer goto out; 252 1.25 bouyer } 253 1.1 deraadt if (request->rq_cred.oa_flavor != AUTH_UNIX) { 254 1.1 deraadt /* bad auth */ 255 1.1 deraadt getq_rslt.status = Q_EPERM; 256 1.32 dholland goto out; 257 1.32 dholland } 258 1.32 dholland 259 1.32 dholland /* 260 1.32 dholland * XXX validate the path... 261 1.32 dholland */ 262 1.32 dholland 263 1.32 dholland qh = quota_open(ext_getq_args.gqa_pathp); 264 1.32 dholland if (qh == NULL) { 265 1.32 dholland /* 266 1.32 dholland * There are only three possible responses: success, 267 1.32 dholland * permission denied, and "no quota", so we return 268 1.32 dholland * the last for essentially all errors. 269 1.32 dholland */ 270 1.32 dholland if (errno == EPERM || errno == EACCES) { 271 1.32 dholland getq_rslt.status = Q_EPERM; 272 1.32 dholland goto out; 273 1.32 dholland } 274 1.32 dholland getq_rslt.status = Q_NOQUOTA; 275 1.32 dholland goto out; 276 1.32 dholland } 277 1.32 dholland 278 1.32 dholland qk.qk_id = ext_getq_args.gqa_id; 279 1.32 dholland qk.qk_idtype = idtype; 280 1.32 dholland qk.qk_objtype = QUOTA_OBJTYPE_BLOCKS; 281 1.32 dholland if (quota_get(qh, &qk, &blocks) < 0) { 282 1.4 mycroft /* failed, return noquota */ 283 1.32 dholland quota_close(qh); 284 1.4 mycroft getq_rslt.status = Q_NOQUOTA; 285 1.32 dholland goto out; 286 1.32 dholland } 287 1.32 dholland 288 1.32 dholland qk.qk_objtype = QUOTA_OBJTYPE_FILES; 289 1.32 dholland if (quota_get(qh, &qk, &files) < 0) { 290 1.32 dholland /* failed, return noquota */ 291 1.32 dholland quota_close(qh); 292 1.32 dholland getq_rslt.status = Q_NOQUOTA; 293 1.32 dholland goto out; 294 1.1 deraadt } 295 1.32 dholland 296 1.32 dholland quota_close(qh); 297 1.32 dholland 298 1.32 dholland quotavals_to_rquota(&blocks, &files, 299 1.32 dholland &getq_rslt.getquota_rslt_u.gqr_rquota); 300 1.32 dholland getq_rslt.status = Q_OK; 301 1.32 dholland 302 1.25 bouyer out: 303 1.29 plunky if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, (char *)&getq_rslt)) 304 1.4 mycroft svcerr_systemerr(transp); 305 1.4 mycroft if (!svc_freeargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) { 306 1.4 mycroft syslog(LOG_ERR, "unable to free arguments"); 307 1.4 mycroft exit(1); 308 1.4 mycroft } 309 1.1 deraadt } 310