/* * 3. Validate */ static bool authdes_validate(AUTH *auth, struct opaque_auth *rverf) { /* LINTED pointer alignment */ struct ad_private *ad = AUTH_PRIVATE(auth); struct authdes_verf verf; int status; uint32_t *ixdr; des_block buf; if (rverf->oa_length != (2 + 1) * BYTES_PER_XDR_UNIT) return (false); /* LINTED pointer alignment */ ixdr = (uint32_t *) rverf->oa_base; buf.key.high = (uint32_t) *ixdr++; buf.key.low = (uint32_t) *ixdr++; verf.adv_int_u = (uint32_t) *ixdr++; /* * Decrypt the timestamp */ status = ecb_crypt((char *)&auth->ah_key, (char *)&buf, (u_int) sizeof(des_block), DES_DECRYPT | DES_HW); if (DES_FAILED(status)) { __warnx(TIRPC_DEBUG_FLAG_AUTH, "authdes_validate: DES decryption failure"); return (false); } /* * xdr the decrypted timestamp */ /* LINTED pointer alignment */ ixdr = (uint32_t *) buf.c; verf.adv_timestamp.tv_sec = IXDR_GET_INT32(ixdr) + 1; verf.adv_timestamp.tv_usec = IXDR_GET_INT32(ixdr); /* * validate */ if (bcmp ((char *)&ad->ad_timestamp, (char *)&verf.adv_timestamp, sizeof(struct timeval)) != 0) { __warnx(TIRPC_DEBUG_FLAG_AUTH, "authdes_validate: verifier mismatch"); return (false); } /* * We have a nickname now, let's use it */ ad->ad_nickname = verf.adv_nickname; ad->ad_cred.adc_namekind = ADN_NICKNAME; return (true); }
/* * System (Unix) longhand authenticator */ enum auth_stat __svcauth_sys(struct svc_req *rqst, struct rpc_msg *msg) { struct authsys_parms *aup; int32_t *buf; struct authsys_area *area; uint_t auth_len; uint_t str_len, gid_len; int i; /* LINTED pointer cast */ area = (struct authsys_area *)rqst->rq_clntcred; aup = &area->area_aup; aup->aup_machname = area->area_machname; aup->aup_gids = area->area_gids; auth_len = msg->rm_call.cb_cred.oa_length; if (auth_len == 0) return (AUTH_BADCRED); /* LINTED pointer cast */ buf = (int32_t *)msg->rm_call.cb_cred.oa_base; aup->aup_time = IXDR_GET_INT32(buf); str_len = IXDR_GET_U_INT32(buf); if (str_len > MAX_MACHINE_NAME) return (AUTH_BADCRED); (void) memcpy(aup->aup_machname, buf, str_len); aup->aup_machname[str_len] = 0; str_len = RNDUP(str_len); buf += str_len / (int)sizeof (int32_t); aup->aup_uid = IXDR_GET_INT32(buf); aup->aup_gid = IXDR_GET_INT32(buf); gid_len = IXDR_GET_U_INT32(buf); if (gid_len > NGRPS) return (AUTH_BADCRED); aup->aup_len = gid_len; for (i = 0; i < gid_len; i++) { aup->aup_gids[i] = (gid_t)IXDR_GET_INT32(buf); } /* * five is the smallest unix credentials structure - * timestamp, hostname len (0), uid, gid, and gids len (0). */ if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) return (AUTH_BADCRED); rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; rqst->rq_xprt->xp_verf.oa_length = 0; return (AUTH_OK); }
bool_t xdr_Create_LinkParms (XDR *xdrs, Create_LinkParms *objp) { #if defined(SOLARIS) && !defined(_LP64) register long *buf; #else register int32_t *buf; #endif if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_long (xdrs, &objp->clientId)) return FALSE; if (!xdr_bool (xdrs, &objp->lockDevice)) return FALSE; if (!xdr_u_long (xdrs, &objp->lock_timeout)) return FALSE; } else { IXDR_PUT_INT32(buf, objp->clientId); IXDR_PUT_BOOL(buf, objp->lockDevice); IXDR_PUT_U_INT32(buf, objp->lock_timeout); } if (!xdr_string (xdrs, &objp->device, ~0)) return FALSE; return TRUE; } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_long (xdrs, &objp->clientId)) return FALSE; if (!xdr_bool (xdrs, &objp->lockDevice)) return FALSE; if (!xdr_u_long (xdrs, &objp->lock_timeout)) return FALSE; } else { objp->clientId = IXDR_GET_INT32(buf); objp->lockDevice = IXDR_GET_BOOL(buf); objp->lock_timeout = IXDR_GET_U_INT32(buf); } if (!xdr_string (xdrs, &objp->device, ~0)) return FALSE; return TRUE; } if (!xdr_long (xdrs, &objp->clientId)) return FALSE; if (!xdr_bool (xdrs, &objp->lockDevice)) return FALSE; if (!xdr_u_long (xdrs, &objp->lock_timeout)) return FALSE; if (!xdr_string (xdrs, &objp->device, ~0)) return FALSE; return TRUE; }
/* * XDR a call message */ bool_t xdr_callmsg(XDR *xdrs, struct rpc_msg *cmsg) { rpc_inline_t *buf; struct opaque_auth *oa; if (xdrs->x_op == XDR_ENCODE) { if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) return (FALSE); if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) return (FALSE); buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT + RNDUP(cmsg->rm_call.cb_cred.oa_length) + 2 * BYTES_PER_XDR_UNIT + RNDUP(cmsg->rm_call.cb_verf.oa_length)); if (buf != NULL) { IXDR_PUT_INT32(buf, cmsg->rm_xid); IXDR_PUT_ENUM(buf, cmsg->rm_direction); if (cmsg->rm_direction != CALL) return (FALSE); IXDR_PUT_INT32(buf, cmsg->rm_call.cb_rpcvers); if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) return (FALSE); IXDR_PUT_INT32(buf, cmsg->rm_call.cb_prog); IXDR_PUT_INT32(buf, cmsg->rm_call.cb_vers); IXDR_PUT_INT32(buf, cmsg->rm_call.cb_proc); oa = &cmsg->rm_call.cb_cred; IXDR_PUT_ENUM(buf, oa->oa_flavor); IXDR_PUT_INT32(buf, oa->oa_length); if (oa->oa_length) { (void) memcpy(buf, oa->oa_base, oa->oa_length); buf += RNDUP(oa->oa_length) / sizeof (int32_t); } oa = &cmsg->rm_call.cb_verf; IXDR_PUT_ENUM(buf, oa->oa_flavor); IXDR_PUT_INT32(buf, oa->oa_length); if (oa->oa_length) { (void) memcpy(buf, oa->oa_base, oa->oa_length); /* * no real need.... * buf += RNDUP(oa->oa_length) / sizeof * (int32_t); */ } return (TRUE); } } if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE(xdrs, 8 * BYTES_PER_XDR_UNIT); if (buf != NULL) { cmsg->rm_xid = IXDR_GET_INT32(buf); cmsg->rm_direction = IXDR_GET_ENUM(buf, enum msg_type); if (cmsg->rm_direction != CALL) return (FALSE); cmsg->rm_call.cb_rpcvers = IXDR_GET_INT32(buf); if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) return (FALSE); cmsg->rm_call.cb_prog = IXDR_GET_INT32(buf); cmsg->rm_call.cb_vers = IXDR_GET_INT32(buf); cmsg->rm_call.cb_proc = IXDR_GET_INT32(buf); oa = &cmsg->rm_call.cb_cred; oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); oa->oa_length = IXDR_GET_INT32(buf); if (oa->oa_length) { if (oa->oa_length > MAX_AUTH_BYTES) return (FALSE); if (oa->oa_base == NULL) { oa->oa_base = malloc(oa->oa_length); if (oa->oa_base == NULL) { syslog(LOG_ERR, "xdr_callmsg : " "out of memory."); return (FALSE); } } buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length)); if (buf == NULL) { if (xdr_opaque(xdrs, oa->oa_base, oa->oa_length) == FALSE) return (FALSE); } else { (void) memcpy(oa->oa_base, buf, (size_t)oa->oa_length); /* * no real need.... * buf += RNDUP(oa->oa_length) / * (int)sizeof (int32_t); */ } } oa = &cmsg->rm_call.cb_verf; buf = XDR_INLINE(xdrs, 2 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (xdr_enum(xdrs, &oa->oa_flavor) == FALSE || xdr_u_int(xdrs, &oa->oa_length) == FALSE) return (FALSE); } else { oa->oa_flavor = IXDR_GET_ENUM(buf, enum_t); oa->oa_length = IXDR_GET_INT32(buf); } if (oa->oa_length) { if (oa->oa_length > MAX_AUTH_BYTES) return (FALSE); if (oa->oa_base == NULL) { oa->oa_base = malloc(oa->oa_length); if (oa->oa_base == NULL) { syslog(LOG_ERR, "xdr_callmsg : " "out of memory."); return (FALSE); } } buf = XDR_INLINE(xdrs, RNDUP(oa->oa_length)); if (buf == NULL) { if (xdr_opaque(xdrs, oa->oa_base, oa->oa_length) == FALSE) return (FALSE); } else { (void) memcpy(oa->oa_base, buf, (size_t)oa->oa_length); /* * no real need... * buf += RNDUP(oa->oa_length) / * (int)sizeof (int32_t); */ } } return (TRUE); }
/* * Unix longhand authenticator */ enum auth_stat _svcauth_unix(struct svc_req *rqst, struct rpc_msg *msg) { enum auth_stat stat; XDR xdrs; int32_t *buf; uint32_t time; struct xucred *xcr; u_int auth_len; size_t str_len, gid_len; u_int i; xcr = rqst->rq_clntcred; auth_len = (u_int)msg->rm_call.cb_cred.oa_length; xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len, XDR_DECODE); buf = XDR_INLINE(&xdrs, auth_len); if (buf != NULL) { time = IXDR_GET_UINT32(buf); str_len = (size_t)IXDR_GET_UINT32(buf); if (str_len > MAX_MACHINE_NAME) { stat = AUTH_BADCRED; goto done; } str_len = RNDUP(str_len); buf += str_len / sizeof (int32_t); xcr->cr_uid = IXDR_GET_UINT32(buf); xcr->cr_groups[0] = IXDR_GET_UINT32(buf); gid_len = (size_t)IXDR_GET_UINT32(buf); if (gid_len > NGRPS) { stat = AUTH_BADCRED; goto done; } for (i = 0; i < gid_len; i++) { if (i + 1 < XU_NGROUPS) xcr->cr_groups[i + 1] = IXDR_GET_INT32(buf); else buf++; } if (gid_len + 1 > XU_NGROUPS) xcr->cr_ngroups = XU_NGROUPS; else xcr->cr_ngroups = gid_len + 1; /* * five is the smallest unix credentials structure - * timestamp, hostname len (0), uid, gid, and gids len (0). */ if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) { (void) printf("bad auth_len gid %ld str %ld auth %u\n", (long)gid_len, (long)str_len, auth_len); stat = AUTH_BADCRED; goto done; } } else if (! xdr_authunix_parms(&xdrs, &time, xcr)) { stat = AUTH_BADCRED; goto done; } rqst->rq_verf = _null_auth; stat = AUTH_OK; done: XDR_DESTROY(&xdrs); return (stat); }
static int rpc_helper_cb(struct pkt_buff *pkt, uint32_t protoff, struct myct *myct, uint32_t ctinfo) { int dir = CTINFO2DIR(ctinfo); unsigned int offset = protoff, datalen; uint32_t *data, *port_ptr = NULL, xid; uint16_t port; uint8_t proto = nfct_get_attr_u8(myct->ct, ATTR_L4PROTO); enum msg_type rm_dir; struct rpc_info *rpc_info = myct->priv_data; union nfct_attr_grp_addr addr, daddr; struct nf_expect *exp = NULL; int ret = NF_ACCEPT; /* Until there's been traffic both ways, don't look into TCP packets. */ if (proto == IPPROTO_TCP && ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) { pr_debug("TCP RPC: Conntrackinfo = %u\n", ctinfo); return ret; } if (proto == IPPROTO_TCP) { struct tcphdr *th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); offset += th->doff * 4; } else { offset += sizeof(struct udphdr); } /* Skip broken headers */ if (offset % 4) { pr_debug("RPC: broken header: offset %u%%4 != 0\n", offset); return ret; } /* Take into Record Fragment header */ if (proto == IPPROTO_TCP) offset += 4; datalen = pktb_len(pkt); data = (uint32_t *)(pktb_network_header(pkt) + offset); /* rpc_msg { * xid * direction * xdr_union { * call_body * reply_body * } * } */ /* Check minimal msg size: xid + direction */ if (datalen < OFFSET(offset, 2*4)) { pr_debug("RPC: too short packet: %u < %u\n", datalen, offset); return ret; } xid = IXDR_GET_INT32(data); rm_dir = IXDR_GET_INT32(data); /* Check direction */ if (!((rm_dir == CALL && dir == MYCT_DIR_ORIG) || (rm_dir == REPLY && dir == MYCT_DIR_REPL))) { pr_debug("RPC: rm_dir != dir %u != %u\n", rm_dir, dir); goto out; } if (rm_dir == CALL) { if (rpc_call(data, offset, datalen, rpc_info) < 0) goto out; rpc_info->xid = xid; return ret; } else { /* Check XID */ if (xid != rpc_info->xid) { pr_debug("RPC REPL: XID does not match: %u != %u\n", xid, rpc_info->xid); goto out; } if (rpc_reply(data, offset, datalen, rpc_info, &port_ptr) < 0) goto out; port = IXDR_GET_INT32(port_ptr); port = htons(port); /* We refer to the reverse direction ("!dir") tuples here, * because we're expecting something in the other direction. * Doesn't matter unless NAT is happening. */ cthelper_get_addr_dst(myct->ct, !dir, &daddr); cthelper_get_addr_src(myct->ct, !dir, &addr); exp = nfexp_new(); if (exp == NULL) goto out; if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, rpc_info->pm_prot, NULL, &port, NF_CT_EXPECT_PERMANENT)) { pr_debug("RPC: failed to init expectation\n"); goto out_exp; } /* Now, NAT might want to mangle the packet, and register the * (possibly changed) expectation itself. */ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { ret = nf_nat_rpc(pkt, dir, exp, rpc_info->pm_prot, port_ptr); goto out_exp; } /* Can't expect this? Best to drop packet now. */ if (cthelper_add_expect(exp) < 0) { pr_debug("RPC: cannot add expectation: %s\n", strerror(errno)); ret = NF_DROP; } } out_exp: nfexp_destroy(exp); out: rpc_info->xid = 0; return ret; }
static int rpc_reply(uint32_t *data, uint32_t offset, uint32_t datalen, struct rpc_info *rpc_info, uint32_t **port_ptr) { uint16_t port; uint32_t p, r; /* RPC REPLY message body */ /* reply_body { * reply_stat * xdr_union { * accepted_reply * rejected_reply * } * } * accepted_reply { * opaque_auth verf * accept_stat * xdr_union { * port * struct mismatch_info * } * } */ /* Check size: reply status */ if (datalen < OFFSET(offset, 4)) { pr_debug("RPC REPL: too short, missing rp_stat: %u < %u\n", datalen, offset); return -1; } p = IXDR_GET_INT32(data); /* Check accepted request */ if (p != MSG_ACCEPTED) { pr_debug("RPC REPL: not accepted %u != %u\n", p, MSG_ACCEPTED); return -1; } /* Check and skip verifier */ if (datalen < OFFSET(offset, 2*4)) { pr_debug("RPC REPL: too short, missing verf: %u < %u\n", datalen, offset); return -1; } r = IXDR_GET_INT32(data); p = IXDR_GET_INT32(data); pr_debug("RPC REPL: verf: %u %u (%u, %u)\n", r, p, datalen, offset); if (p > MAX_AUTH_BYTES) { pr_debug("RPC REPL: invalid sized verf %u > %u\n", p, MAX_AUTH_BYTES); return -1; } r = ROUNDUP(p); /* verifier + ac_stat + port */ if (datalen != OFFSET(offset, r) + 2*4) { pr_debug("RPC REPL: invalid size to carry verf and " "success: %u != %u\n", datalen, offset + 2*4); return -1; } data += r/4; /* Check success */ p = IXDR_GET_INT32(data); if (p != SUCCESS) { pr_debug("RPC REPL: not success %u != %u\n", p, SUCCESS); return -1; } /* Get port */ *port_ptr = data; port = IXDR_GET_INT32(data); /* -Wunused-but-set-parameter */ if (port == 0) { pr_debug("RPC REPL: port is zero\n"); return -1; } pr_debug("RPC REPL: processed: xid %u, prog %u, vers %u, " "prot %u, port %u\n", rpc_info->xid, rpc_info->pm_prog, rpc_info->pm_vers, rpc_info->pm_prot, port); return 0; }
static int rpc_call(const uint32_t *data, uint32_t offset, uint32_t datalen, struct rpc_info *rpc_info) { uint32_t p, r; /* RPC CALL message body */ /* call_body { * rpcvers * prog * vers * proc * opaque_auth cred * opaque_auth verf * pmap * } * * opaque_auth { * flavour * opaque[len] <= MAX_AUTH_BYTES * } */ if (datalen < OFFSET(offset, 4*4 + 2*2*4)) { pr_debug("RPC CALL: too short packet: %u < %u\n", datalen, offset); return -1; } /* Check rpcversion */ p = IXDR_GET_INT32(data); if (p != SUPPORTED_RPC_VERSION) { pr_debug("RPC CALL: wrong rpcvers %u != %u\n", p, SUPPORTED_RPC_VERSION); return -1; } /* Skip non-portmap requests */ p = IXDR_GET_INT32(data); if (p != PMAPPROG) { pr_debug("RPC CALL: not portmap %u != %lu\n", p, PMAPPROG); return -1; } /* Check portmap version */ p = IXDR_GET_INT32(data); if (p != PMAPVERS) { pr_debug("RPC CALL: wrong portmap version %u != %lu\n", p, PMAPVERS); return -1; } /* Skip non PMAPPROC_GETPORT requests */ p = IXDR_GET_INT32(data); if (p != PMAPPROC_GETPORT) { pr_debug("RPC CALL: not PMAPPROC_GETPORT %u != %lu\n", p, PMAPPROC_GETPORT); return -1; } /* Check and skip credentials */ r = IXDR_GET_INT32(data); p = IXDR_GET_INT32(data); pr_debug("RPC CALL: cred: %u %u (%u, %u)\n", r, p, datalen, offset); if (p > MAX_AUTH_BYTES) { pr_debug("RPC CALL: invalid sized cred %u > %u\n", p, MAX_AUTH_BYTES); return -1; } r = ROUNDUP(p); if (datalen < OFFSET(offset, r)) { pr_debug("RPC CALL: too short to carry cred: %u < %u, %u\n", datalen, offset, r); return -1; } data += r/4; /* Check and skip verifier */ r = IXDR_GET_INT32(data); p = IXDR_GET_INT32(data); pr_debug("RPC CALL: verf: %u %u (%u, %u)\n", r, p, datalen, offset); if (p > MAX_AUTH_BYTES) { pr_debug("RPC CALL: invalid sized verf %u > %u\n", p, MAX_AUTH_BYTES); return -1; } r = ROUNDUP(p); if (datalen < OFFSET(offset, r)) { pr_debug("RPC CALL: too short to carry verf: %u < %u, %u\n", datalen, offset, r); return -1; } data += r/4; /* pmap { * prog * vers * prot * port * } */ /* Check CALL size */ if (datalen != offset + 4*4) { pr_debug("RPC CALL: invalid size to carry pmap: %u != %u\n", datalen, offset + 4*4); return -1; } rpc_info->pm_prog = IXDR_GET_INT32(data); rpc_info->pm_vers = IXDR_GET_INT32(data); rpc_info->pm_prot = IXDR_GET_INT32(data); /* Check supported protocols */ if (!(rpc_info->pm_prot == IPPROTO_TCP || rpc_info->pm_prot == IPPROTO_UDP)) { pr_debug("RPC CALL: unsupported protocol %u", rpc_info->pm_prot); return -1; } p = IXDR_GET_INT32(data); /* Check port: must be zero */ if (p != 0) { pr_debug("RPC CALL: port is nonzero %u\n", ntohl(p)); return -1; } pr_debug("RPC CALL: processed: xid %u, prog %u, vers %u, prot %u\n", rpc_info->xid, rpc_info->pm_prog, rpc_info->pm_vers, rpc_info->pm_prot); return 0; }
/* * Unix longhand authenticator */ enum auth_stat _svcauth_unix(struct svc_req *rqst, struct rpc_msg *msg) { enum auth_stat stat; XDR xdrs; struct authunix_parms *aup; int32_t *buf; struct area { struct authunix_parms area_aup; char area_machname[MAX_MACHINE_NAME+1]; int area_gids[NGRPS]; } *area; u_int auth_len; size_t str_len, gid_len; u_int i; assert(rqst != NULL); assert(msg != NULL); area = (struct area *) rqst->rq_clntcred; aup = &area->area_aup; aup->aup_machname = area->area_machname; aup->aup_gids = area->area_gids; auth_len = (u_int)msg->rm_call.cb_cred.oa_length; xdrmem_create(&xdrs, msg->rm_call.cb_cred.oa_base, auth_len,XDR_DECODE); buf = XDR_INLINE(&xdrs, auth_len); if (buf != NULL) { aup->aup_time = IXDR_GET_INT32(buf); str_len = (size_t)IXDR_GET_U_INT32(buf); if (str_len > MAX_MACHINE_NAME) { stat = AUTH_BADCRED; goto done; } memmove(aup->aup_machname, buf, str_len); aup->aup_machname[str_len] = 0; str_len = RNDUP(str_len); buf += str_len / sizeof (int32_t); aup->aup_uid = (int)IXDR_GET_INT32(buf); aup->aup_gid = (int)IXDR_GET_INT32(buf); gid_len = (size_t)IXDR_GET_U_INT32(buf); if (gid_len > NGRPS) { stat = AUTH_BADCRED; goto done; } aup->aup_len = gid_len; for (i = 0; i < gid_len; i++) { aup->aup_gids[i] = (int)IXDR_GET_INT32(buf); } /* * five is the smallest unix credentials structure - * timestamp, hostname len (0), uid, gid, and gids len (0). */ if ((5 + gid_len) * BYTES_PER_XDR_UNIT + str_len > auth_len) { printf("bad auth_len gid %ld str %ld auth %u\n", (long)gid_len, (long)str_len, auth_len); stat = AUTH_BADCRED; goto done; } } else if (! xdr_authunix_parms(&xdrs, aup)) { xdrs.x_op = XDR_FREE; xdr_authunix_parms(&xdrs, aup); stat = AUTH_BADCRED; goto done; } /* get the verifier */ if ((u_int)msg->rm_call.cb_verf.oa_length) { rqst->rq_xprt->xp_verf.oa_flavor = msg->rm_call.cb_verf.oa_flavor; rqst->rq_xprt->xp_verf.oa_base = msg->rm_call.cb_verf.oa_base; rqst->rq_xprt->xp_verf.oa_length = msg->rm_call.cb_verf.oa_length; } else { rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; rqst->rq_xprt->xp_verf.oa_length = 0; } stat = AUTH_OK; done: XDR_DESTROY(&xdrs); return (stat); }
bool_t xdr_rpcbs_rmtcalllist(XDR *xdrs, rpcbs_rmtcalllist *objp) { int32_t *buf; struct rpcbs_rmtcalllist **pnext; if (xdrs->x_op == XDR_ENCODE) { buf = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int32_t(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } } else { IXDR_PUT_U_INT32(buf, objp->prog); IXDR_PUT_U_INT32(buf, objp->vers); IXDR_PUT_U_INT32(buf, objp->proc); IXDR_PUT_INT32(buf, objp->success); IXDR_PUT_INT32(buf, objp->failure); IXDR_PUT_INT32(buf, objp->indirect); } if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { return (FALSE); } pnext = &objp->next; if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } else if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE(xdrs, 6 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (!xdr_u_int32_t(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } } else { objp->prog = (rpcprog_t)IXDR_GET_U_INT32(buf); objp->vers = (rpcvers_t)IXDR_GET_U_INT32(buf); objp->proc = (rpcproc_t)IXDR_GET_U_INT32(buf); objp->success = (int)IXDR_GET_INT32(buf); objp->failure = (int)IXDR_GET_INT32(buf); objp->indirect = (int)IXDR_GET_INT32(buf); } if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { return (FALSE); } if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); } if (!xdr_u_int32_t(xdrs, &objp->prog)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->vers)) { return (FALSE); } if (!xdr_u_int32_t(xdrs, &objp->proc)) { return (FALSE); } if (!xdr_int(xdrs, &objp->success)) { return (FALSE); } if (!xdr_int(xdrs, &objp->failure)) { return (FALSE); } if (!xdr_int(xdrs, &objp->indirect)) { return (FALSE); } if (!xdr_string(xdrs, &objp->netid, (u_int)~0)) { return (FALSE); } if (!xdr_pointer(xdrs, (char **) pnext, sizeof (rpcbs_rmtcalllist), (xdrproc_t)xdr_rpcbs_rmtcalllist)) { return (FALSE); } return (TRUE); }
static int check_rpc_packet(const u_int32_t *data, const void *matchinfo, int *hotdrop, int dir, struct ip_conntrack *ct, int offset, struct list_head request_p_list) { const struct ipt_rpc_info *rpcinfo = matchinfo; struct request_p *req_p; u_int32_t xid; /* Get XID */ xid = *data; /* This does sanity checking on RPC payloads, * and permits only the RPC "get port" (3) * in authorised procedures in client * communications with the portmapper. */ data += 5; /* Get RPC requestor */ if (IXDR_GET_INT32(data) != 3) { DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); if(rpcinfo->strict == 1) *hotdrop = 1; return 0; } DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); data++; /* Jump Credentials and Verfifier */ data = data + IXDR_GET_INT32(data) + 2; data = data + IXDR_GET_INT32(data) + 2; /* Get RPC procedure */ if (match_rpcs((char *)&rpcinfo->c_procs, rpcinfo->i_procs, IXDR_GET_INT32(data)) == 0) { DEBUGP("RPC packet contains illegal procedure request [%u]. [drop]\n", (unsigned int)IXDR_GET_INT32(data)); /* If the RPC conntrack half entry already exists .. */ switch (ct->tuplehash[0].tuple.dst.protonum) { case IPPROTO_UDP: WRITE_LOCK(&ipct_rpc_udp_lock); case IPPROTO_TCP: WRITE_LOCK(&ipct_rpc_tcp_lock); } req_p = LIST_FIND(&request_p_list, request_p_cmp, struct request_p *, xid, ct->tuplehash[dir].tuple.src.ip, ct->tuplehash[dir].tuple.src.u.all); if (req_p) { DEBUGP("found req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", xid, ct->tuplehash[dir].tuple.dst.protonum, NIPQUAD(ct->tuplehash[dir].tuple.src.ip), ntohs(ct->tuplehash[dir].tuple.src.u.all)); /* .. remove it */ if (del_timer(&req_p->timeout)) req_p->timeout.expires = 0; LIST_DELETE(&request_p_list, req_p); DEBUGP("RPC req_p removed. [done]\n"); } else { DEBUGP("no req_p found for xid=%u proto=%u %u.%u.%u.%u:%u\n", xid, ct->tuplehash[dir].tuple.dst.protonum, NIPQUAD(ct->tuplehash[dir].tuple.src.ip), ntohs(ct->tuplehash[dir].tuple.src.u.all)); } switch (ct->tuplehash[0].tuple.dst.protonum) { case IPPROTO_UDP: WRITE_UNLOCK(&ipct_rpc_udp_lock); case IPPROTO_TCP: WRITE_UNLOCK(&ipct_rpc_tcp_lock); } if(rpcinfo->strict == 1) *hotdrop = 1; return 0; }
} switch (ct->tuplehash[0].tuple.dst.protonum) { case IPPROTO_UDP: WRITE_UNLOCK(&ipct_rpc_udp_lock); case IPPROTO_TCP: WRITE_UNLOCK(&ipct_rpc_tcp_lock); } if(rpcinfo->strict == 1) *hotdrop = 1; return 0; } DEBUGP("RPC packet contains authorised procedure request [%u]. [match]\n", (unsigned int)IXDR_GET_INT32(data)); return (1 && (!offset)); } static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, const void *hdr, u_int16_t datalen, int *hotdrop) { struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; const u_int32_t *data; enum ip_conntrack_dir dir; const struct tcphdr *tcp; const struct ipt_rpc_info *rpcinfo = matchinfo; int port, portsok;
bool xdr_rquota(XDR * xdrs, rquota * objp) { register int32_t *buf; if (xdrs->x_op == XDR_ENCODE) { buf = xdr_inline_encode(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ IXDR_PUT_INT32(buf, objp->rq_bsize); IXDR_PUT_BOOL(buf, objp->rq_active); IXDR_PUT_U_INT32(buf, objp->rq_bhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_bsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curblocks); IXDR_PUT_U_INT32(buf, objp->rq_fhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_fsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curfiles); IXDR_PUT_U_INT32(buf, objp->rq_btimeleft); IXDR_PUT_U_INT32(buf, objp->rq_ftimeleft); } else { if (!XDR_PUTINT32(xdrs, objp->rq_bsize)) return false; if (!XDR_PUTBOOL(xdrs, objp->rq_active)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_bhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_bsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curblocks)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curfiles)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_btimeleft)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_ftimeleft)) return false; } return true; } if (xdrs->x_op == XDR_DECODE) { buf = xdr_inline_decode(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ objp->rq_bsize = IXDR_GET_INT32(buf); objp->rq_active = IXDR_GET_BOOL(buf); objp->rq_bhardlimit = IXDR_GET_U_INT32(buf); objp->rq_bsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curblocks = IXDR_GET_U_INT32(buf); objp->rq_fhardlimit = IXDR_GET_U_INT32(buf); objp->rq_fsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curfiles = IXDR_GET_U_INT32(buf); objp->rq_btimeleft = IXDR_GET_U_INT32(buf); objp->rq_ftimeleft = IXDR_GET_U_INT32(buf); } else { if (!XDR_GETINT32(xdrs, &objp->rq_bsize)) return false; if (!XDR_GETBOOL(xdrs, &objp->rq_active)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_bhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_bsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curblocks)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curfiles)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_btimeleft)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_ftimeleft)) return false; } return true; } if (!xdr_int(xdrs, &objp->rq_bsize)) return false; if (!xdr_bool(xdrs, &objp->rq_active)) return false; if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curblocks)) return false; if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curfiles)) return false; if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) return false; if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) return false; return true; }
/* * XDR a call message */ bool_t xdr_callmsg (XDR *xdrs, struct rpc_msg *cmsg) { int32_t *buf; struct opaque_auth *oa; if (xdrs->x_op == XDR_ENCODE) { if (cmsg->rm_call.cb_cred.oa_length > MAX_AUTH_BYTES) { return (FALSE); } if (cmsg->rm_call.cb_verf.oa_length > MAX_AUTH_BYTES) { return (FALSE); } buf = XDR_INLINE (xdrs, 8 * BYTES_PER_XDR_UNIT + RNDUP (cmsg->rm_call.cb_cred.oa_length) + 2 * BYTES_PER_XDR_UNIT + RNDUP (cmsg->rm_call.cb_verf.oa_length)); if (buf != NULL) { (void) IXDR_PUT_LONG (buf, cmsg->rm_xid); (void) IXDR_PUT_ENUM (buf, cmsg->rm_direction); if (cmsg->rm_direction != CALL) return FALSE; (void) IXDR_PUT_LONG (buf, cmsg->rm_call.cb_rpcvers); if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) return FALSE; (void) IXDR_PUT_LONG (buf, cmsg->rm_call.cb_prog); (void) IXDR_PUT_LONG (buf, cmsg->rm_call.cb_vers); (void) IXDR_PUT_LONG (buf, cmsg->rm_call.cb_proc); oa = &cmsg->rm_call.cb_cred; (void) IXDR_PUT_ENUM (buf, oa->oa_flavor); (void) IXDR_PUT_INT32 (buf, oa->oa_length); if (oa->oa_length) { memcpy ((caddr_t) buf, oa->oa_base, oa->oa_length); buf = (int32_t *) ((char *) buf + RNDUP (oa->oa_length)); } oa = &cmsg->rm_call.cb_verf; (void) IXDR_PUT_ENUM (buf, oa->oa_flavor); (void) IXDR_PUT_INT32 (buf, oa->oa_length); if (oa->oa_length) { memcpy ((caddr_t) buf, oa->oa_base, oa->oa_length); /* no real need.... buf = (long *) ((char *) buf + RNDUP(oa->oa_length)); */ } return TRUE; } } if (xdrs->x_op == XDR_DECODE) { buf = XDR_INLINE (xdrs, 8 * BYTES_PER_XDR_UNIT); if (buf != NULL) { cmsg->rm_xid = IXDR_GET_LONG (buf); cmsg->rm_direction = IXDR_GET_ENUM (buf, enum msg_type); if (cmsg->rm_direction != CALL) { return FALSE; } cmsg->rm_call.cb_rpcvers = IXDR_GET_LONG (buf); if (cmsg->rm_call.cb_rpcvers != RPC_MSG_VERSION) { return FALSE; } cmsg->rm_call.cb_prog = IXDR_GET_LONG (buf); cmsg->rm_call.cb_vers = IXDR_GET_LONG (buf); cmsg->rm_call.cb_proc = IXDR_GET_LONG (buf); oa = &cmsg->rm_call.cb_cred; oa->oa_flavor = IXDR_GET_ENUM (buf, enum_t); oa->oa_length = IXDR_GET_INT32 (buf); if (oa->oa_length) { if (oa->oa_length > MAX_AUTH_BYTES) return FALSE; if (oa->oa_base == NULL) { oa->oa_base = (caddr_t) mem_alloc (oa->oa_length); } buf = XDR_INLINE (xdrs, RNDUP (oa->oa_length)); if (buf == NULL) { if (INTUSE(xdr_opaque) (xdrs, oa->oa_base, oa->oa_length) == FALSE) return FALSE; } else { memcpy (oa->oa_base, (caddr_t) buf, oa->oa_length); /* no real need.... buf = (long *) ((char *) buf + RNDUP(oa->oa_length)); */ } } oa = &cmsg->rm_call.cb_verf; buf = XDR_INLINE (xdrs, 2 * BYTES_PER_XDR_UNIT); if (buf == NULL) { if (INTUSE(xdr_enum) (xdrs, &oa->oa_flavor) == FALSE || INTUSE(xdr_u_int) (xdrs, &oa->oa_length) == FALSE) { return FALSE; } } else { oa->oa_flavor = IXDR_GET_ENUM (buf, enum_t); oa->oa_length = IXDR_GET_INT32 (buf); } if (oa->oa_length) { if (oa->oa_length > MAX_AUTH_BYTES) return FALSE; if (oa->oa_base == NULL) { oa->oa_base = (caddr_t) mem_alloc (oa->oa_length); } buf = XDR_INLINE (xdrs, RNDUP (oa->oa_length)); if (buf == NULL) { if (INTUSE(xdr_opaque) (xdrs, oa->oa_base, oa->oa_length) == FALSE) return FALSE; } else { memcpy (oa->oa_base, (caddr_t) buf, oa->oa_length); /* no real need... buf = (long *) ((char *) buf + RNDUP(oa->oa_length)); */ } } return TRUE; }
static int check_rpc_packet(const u_int32_t *data, int dir, struct ip_conntrack *ct, struct list_head request_p_list) { int ret = NF_ACCEPT; u_int32_t xid; struct request_p *req_p; struct ip_conntrack_expect *exp; /* Translstion's buffer for XDR */ u_int16_t port_buf; /* Get XID */ xid = *data; /* This does sanity checking on RPC payloads, * and permits only the RPC "get port" (3) * in authorised procedures in client * communications with the portmapper. */ /* perform direction dependant RPC work */ if (dir == IP_CT_DIR_ORIGINAL) { data += 5; /* Get RPC requestor */ if (IXDR_GET_INT32(data) != 3) { DEBUGP("RPC packet contains an invalid (non \"get\") requestor. [skip]\n"); return NF_ACCEPT; } DEBUGP("RPC packet contains a \"get\" requestor. [cont]\n"); data++; /* Jump Credentials and Verfifier */ data = data + IXDR_GET_INT32(data) + 2; data = data + IXDR_GET_INT32(data) + 2; /* Get RPC procedure */ DEBUGP("RPC packet contains procedure request [%u]. [cont]\n", (unsigned int)IXDR_GET_INT32(data)); /* Get RPC protocol and store against client parameters */ data = data + 2; alloc_request_p(xid, IXDR_GET_INT32(data), ct->tuplehash[dir].tuple.src.ip, ct->tuplehash[dir].tuple.src.u.all); DEBUGP("allocated RPC req_p for xid=%u proto=%u %u.%u.%u.%u:%u\n", xid, IXDR_GET_INT32(data), NIPQUAD(ct->tuplehash[dir].tuple.src.ip), ntohs(ct->tuplehash[dir].tuple.src.u.all)); DEBUGP("allocated RPC request for protocol %u. [done]\n", (unsigned int)IXDR_GET_INT32(data)); } else { /* Check for returning packet's stored counterpart */ req_p = LIST_FIND(&request_p_list_udp, request_p_cmp, struct request_p *, xid, ct->tuplehash[!dir].tuple.src.ip, ct->tuplehash[!dir].tuple.src.u.all); /* Drop unexpected packets */ if (!req_p) { DEBUGP("packet is not expected. [skip]\n"); return NF_ACCEPT; } /* Verifies if packet is really an RPC reply packet */ data++; if (IXDR_GET_INT32(data) != 1) { DEBUGP("packet is not a valid RPC reply. [skip]\n"); return NF_ACCEPT; } /* Is status accept? */ data++; if (IXDR_GET_INT32(data)) { DEBUGP("packet is not an RPC accept. [skip]\n"); return NF_ACCEPT; } /* Get Verifier length. Jump verifier */ data++; data = data + IXDR_GET_INT32(data) + 2; /* Is accpet status "success"? */ if (IXDR_GET_INT32(data)) { DEBUGP("packet is not an RPC accept status of success. [skip]\n"); return NF_ACCEPT; } /* Get server port number */ data++; port_buf = (u_int16_t) IXDR_GET_INT32(data); /* If a packet has made it this far then it deserves an * expectation ... if port == 0, then this service is * not going to be registered. */ if (port_buf) { DEBUGP("port found: %u\n", port_buf); exp = ip_conntrack_expect_alloc(); if (!exp) { ret = NF_DROP; goto out; } /* Watch out, Radioactive-Man! */ exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; exp->mask.src.ip = 0xffffffff; exp->mask.dst.ip = 0xffffffff; switch (req_p->proto) { case IPPROTO_UDP: exp->tuple.src.u.udp.port = 0; exp->tuple.dst.u.udp.port = htons(port_buf); exp->tuple.dst.protonum = IPPROTO_UDP; exp->mask.src.u.udp.port = 0; exp->mask.dst.u.udp.port = htons(0xffff); exp->mask.dst.protonum = 0xff; break; case IPPROTO_TCP: exp->tuple.src.u.tcp.port = 0; exp->tuple.dst.u.tcp.port = htons(port_buf); exp->tuple.dst.protonum = IPPROTO_TCP; exp->mask.src.u.tcp.port = 0; exp->mask.dst.u.tcp.port = htons(0xffff); exp->mask.dst.protonum = 0xff; break; } exp->expectfn = NULL; exp->master = ct; DEBUGP("expect related ip %u.%u.%u.%u:0-%u.%u.%u.%u:%u proto=%u\n", NIPQUAD(exp->tuple.src.ip), NIPQUAD(exp->tuple.dst.ip), port_buf, req_p->proto); DEBUGP("expect related mask %u.%u.%u.%u:0-%u.%u.%u.%u:65535 proto=%u\n", NIPQUAD(exp->mask.src.ip), NIPQUAD(exp->mask.dst.ip), exp->mask.dst.protonum); if (ip_conntrack_expect_related(exp) != 0) { ip_conntrack_expect_free(exp); ret = NF_DROP; } } out: req_cl(req_p); DEBUGP("packet evaluated. [expect]\n"); } return ret; }