diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2009-08-09 15:09:43 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2009-08-09 15:09:43 -0400 |
commit | c0c077df009f2f329875051ac5283df235288689 (patch) | |
tree | 50c0f9b6d78ebbfd711d5bf4c436aa116ed85a51 /net/sunrpc/rpcb_clnt.c | |
parent | 7ed0ff983c8ad30bf4e2b9fdbb299a3e3ec08d08 (diff) |
SUNRPC: Introduce new xdr_stream-based decoders to rpcb_clnt.c
Replace the open-coded decode logic for PMAP_GETPORT/RPCB_GETADDR with
an xdr_stream-based implementation, similar to what NFSv4 uses, to
protect against buffer overflows. The new implementation also checks
that the incoming port number is reasonable.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'net/sunrpc/rpcb_clnt.c')
-rw-r--r-- | net/sunrpc/rpcb_clnt.c | 80 |
1 files changed, 77 insertions, 3 deletions
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index fe183af5cc72..88de754e3b02 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c | |||
@@ -454,7 +454,7 @@ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot) | |||
454 | struct rpc_message msg = { | 454 | struct rpc_message msg = { |
455 | .rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT], | 455 | .rpc_proc = &rpcb_procedures2[RPCBPROC_GETPORT], |
456 | .rpc_argp = &map, | 456 | .rpc_argp = &map, |
457 | .rpc_resp = &map.r_port, | 457 | .rpc_resp = &map, |
458 | }; | 458 | }; |
459 | struct rpc_clnt *rpcb_clnt; | 459 | struct rpc_clnt *rpcb_clnt; |
460 | int status; | 460 | int status; |
@@ -484,7 +484,7 @@ static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbi | |||
484 | struct rpc_message msg = { | 484 | struct rpc_message msg = { |
485 | .rpc_proc = proc, | 485 | .rpc_proc = proc, |
486 | .rpc_argp = map, | 486 | .rpc_argp = map, |
487 | .rpc_resp = &map->r_port, | 487 | .rpc_resp = map, |
488 | }; | 488 | }; |
489 | struct rpc_task_setup task_setup_data = { | 489 | struct rpc_task_setup task_setup_data = { |
490 | .rpc_client = rpcb_clnt, | 490 | .rpc_client = rpcb_clnt, |
@@ -727,6 +727,31 @@ static int rpcb_decode_getport(struct rpc_rqst *req, __be32 *p, | |||
727 | return 0; | 727 | return 0; |
728 | } | 728 | } |
729 | 729 | ||
730 | static int rpcb_dec_getport(struct rpc_rqst *req, __be32 *p, | ||
731 | struct rpcbind_args *rpcb) | ||
732 | { | ||
733 | struct rpc_task *task = req->rq_task; | ||
734 | struct xdr_stream xdr; | ||
735 | unsigned long port; | ||
736 | |||
737 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
738 | |||
739 | rpcb->r_port = 0; | ||
740 | |||
741 | p = xdr_inline_decode(&xdr, sizeof(__be32)); | ||
742 | if (unlikely(p == NULL)) | ||
743 | return -EIO; | ||
744 | |||
745 | port = ntohl(*p); | ||
746 | dprintk("RPC: %5u PMAP_%s result: %lu\n", task->tk_pid, | ||
747 | task->tk_msg.rpc_proc->p_name, port); | ||
748 | if (unlikely(port > USHORT_MAX)) | ||
749 | return -EIO; | ||
750 | |||
751 | rpcb->r_port = port; | ||
752 | return 0; | ||
753 | } | ||
754 | |||
730 | static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, | 755 | static int rpcb_decode_set(struct rpc_rqst *req, __be32 *p, |
731 | unsigned int *boolp) | 756 | unsigned int *boolp) |
732 | { | 757 | { |
@@ -871,11 +896,60 @@ out_err: | |||
871 | return -EIO; | 896 | return -EIO; |
872 | } | 897 | } |
873 | 898 | ||
899 | static int rpcb_dec_getaddr(struct rpc_rqst *req, __be32 *p, | ||
900 | struct rpcbind_args *rpcb) | ||
901 | { | ||
902 | struct sockaddr_storage address; | ||
903 | struct sockaddr *sap = (struct sockaddr *)&address; | ||
904 | struct rpc_task *task = req->rq_task; | ||
905 | struct xdr_stream xdr; | ||
906 | u32 len; | ||
907 | |||
908 | rpcb->r_port = 0; | ||
909 | |||
910 | xdr_init_decode(&xdr, &req->rq_rcv_buf, p); | ||
911 | |||
912 | p = xdr_inline_decode(&xdr, sizeof(__be32)); | ||
913 | if (unlikely(p == NULL)) | ||
914 | goto out_fail; | ||
915 | len = ntohl(*p); | ||
916 | |||
917 | /* | ||
918 | * If the returned universal address is a null string, | ||
919 | * the requested RPC service was not registered. | ||
920 | */ | ||
921 | if (len == 0) { | ||
922 | dprintk("RPC: %5u RPCB reply: program not registered\n", | ||
923 | task->tk_pid); | ||
924 | return 0; | ||
925 | } | ||
926 | |||
927 | if (unlikely(len > RPCBIND_MAXUADDRLEN)) | ||
928 | goto out_fail; | ||
929 | |||
930 | p = xdr_inline_decode(&xdr, len); | ||
931 | if (unlikely(p == NULL)) | ||
932 | goto out_fail; | ||
933 | dprintk("RPC: %5u RPCB_%s reply: %s\n", task->tk_pid, | ||
934 | task->tk_msg.rpc_proc->p_name, (char *)p); | ||
935 | |||
936 | if (rpc_uaddr2sockaddr((char *)p, len, sap, sizeof(address)) == 0) | ||
937 | goto out_fail; | ||
938 | rpcb->r_port = rpc_get_port(sap); | ||
939 | |||
940 | return 0; | ||
941 | |||
942 | out_fail: | ||
943 | dprintk("RPC: %5u malformed RPCB_%s reply\n", | ||
944 | task->tk_pid, task->tk_msg.rpc_proc->p_name); | ||
945 | return -EIO; | ||
946 | } | ||
947 | |||
874 | #define PROC(proc, argtype, restype) \ | 948 | #define PROC(proc, argtype, restype) \ |
875 | [RPCBPROC_##proc] = { \ | 949 | [RPCBPROC_##proc] = { \ |
876 | .p_proc = RPCBPROC_##proc, \ | 950 | .p_proc = RPCBPROC_##proc, \ |
877 | .p_encode = (kxdrproc_t) rpcb_enc_##argtype, \ | 951 | .p_encode = (kxdrproc_t) rpcb_enc_##argtype, \ |
878 | .p_decode = (kxdrproc_t) rpcb_decode_##restype, \ | 952 | .p_decode = (kxdrproc_t) rpcb_dec_##restype, \ |
879 | .p_arglen = RPCB_##argtype##args_sz, \ | 953 | .p_arglen = RPCB_##argtype##args_sz, \ |
880 | .p_replen = RPCB_##restype##res_sz, \ | 954 | .p_replen = RPCB_##restype##res_sz, \ |
881 | .p_statidx = RPCBPROC_##proc, \ | 955 | .p_statidx = RPCBPROC_##proc, \ |