| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (c) 2021, 2022 Oracle. All rights reserved. | |
| * | |
| * The AUTH_TLS credential is used only to probe a remote peer | |
| * for RPC-over-TLS support. | |
| */ | |
| #include <linux/types.h> | |
| #include <linux/module.h> | |
| #include <linux/sunrpc/clnt.h> | |
| static const char *starttls_token = "STARTTLS"; | |
| static const size_t starttls_len = 8; | |
| static struct rpc_auth tls_auth; | |
| static struct rpc_cred tls_cred; | |
| static void tls_encode_probe(struct rpc_rqst *rqstp, struct xdr_stream *xdr, | |
| const void *obj) | |
| { | |
| } | |
| static int tls_decode_probe(struct rpc_rqst *rqstp, struct xdr_stream *xdr, | |
| void *obj) | |
| { | |
| return 0; | |
| } | |
| static const struct rpc_procinfo rpcproc_tls_probe = { | |
| .p_encode = tls_encode_probe, | |
| .p_decode = tls_decode_probe, | |
| }; | |
| static void rpc_tls_probe_call_prepare(struct rpc_task *task, void *data) | |
| { | |
| task->tk_flags &= ~RPC_TASK_NO_RETRANS_TIMEOUT; | |
| rpc_call_start(task); | |
| } | |
| static void rpc_tls_probe_call_done(struct rpc_task *task, void *data) | |
| { | |
| } | |
| static const struct rpc_call_ops rpc_tls_probe_ops = { | |
| .rpc_call_prepare = rpc_tls_probe_call_prepare, | |
| .rpc_call_done = rpc_tls_probe_call_done, | |
| }; | |
| static int tls_probe(struct rpc_clnt *clnt) | |
| { | |
| struct rpc_message msg = { | |
| .rpc_proc = &rpcproc_tls_probe, | |
| }; | |
| struct rpc_task_setup task_setup_data = { | |
| .rpc_client = clnt, | |
| .rpc_message = &msg, | |
| .rpc_op_cred = &tls_cred, | |
| .callback_ops = &rpc_tls_probe_ops, | |
| .flags = RPC_TASK_SOFT | RPC_TASK_SOFTCONN, | |
| }; | |
| struct rpc_task *task; | |
| int status; | |
| task = rpc_run_task(&task_setup_data); | |
| if (IS_ERR(task)) | |
| return PTR_ERR(task); | |
| status = task->tk_status; | |
| rpc_put_task(task); | |
| return status; | |
| } | |
| static struct rpc_auth *tls_create(const struct rpc_auth_create_args *args, | |
| struct rpc_clnt *clnt) | |
| { | |
| refcount_inc(&tls_auth.au_count); | |
| return &tls_auth; | |
| } | |
| static void tls_destroy(struct rpc_auth *auth) | |
| { | |
| } | |
| static struct rpc_cred *tls_lookup_cred(struct rpc_auth *auth, | |
| struct auth_cred *acred, int flags) | |
| { | |
| return get_rpccred(&tls_cred); | |
| } | |
| static void tls_destroy_cred(struct rpc_cred *cred) | |
| { | |
| } | |
| static int tls_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags) | |
| { | |
| return 1; | |
| } | |
| static int tls_marshal(struct rpc_task *task, struct xdr_stream *xdr) | |
| { | |
| __be32 *p; | |
| p = xdr_reserve_space(xdr, 4 * XDR_UNIT); | |
| if (!p) | |
| return -EMSGSIZE; | |
| /* Credential */ | |
| *p++ = rpc_auth_tls; | |
| *p++ = xdr_zero; | |
| /* Verifier */ | |
| *p++ = rpc_auth_null; | |
| *p = xdr_zero; | |
| return 0; | |
| } | |
| static int tls_refresh(struct rpc_task *task) | |
| { | |
| set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_rqstp->rq_cred->cr_flags); | |
| return 0; | |
| } | |
| static int tls_validate(struct rpc_task *task, struct xdr_stream *xdr) | |
| { | |
| __be32 *p; | |
| void *str; | |
| p = xdr_inline_decode(xdr, XDR_UNIT); | |
| if (!p) | |
| return -EIO; | |
| if (*p != rpc_auth_null) | |
| return -EIO; | |
| if (xdr_stream_decode_opaque_inline(xdr, &str, starttls_len) != starttls_len) | |
| return -EPROTONOSUPPORT; | |
| if (memcmp(str, starttls_token, starttls_len)) | |
| return -EPROTONOSUPPORT; | |
| return 0; | |
| } | |
| const struct rpc_authops authtls_ops = { | |
| .owner = THIS_MODULE, | |
| .au_flavor = RPC_AUTH_TLS, | |
| .au_name = "NULL", | |
| .create = tls_create, | |
| .destroy = tls_destroy, | |
| .lookup_cred = tls_lookup_cred, | |
| .ping = tls_probe, | |
| }; | |
| static struct rpc_auth tls_auth = { | |
| .au_cslack = NUL_CALLSLACK, | |
| .au_rslack = NUL_REPLYSLACK, | |
| .au_verfsize = NUL_REPLYSLACK, | |
| .au_ralign = NUL_REPLYSLACK, | |
| .au_ops = &authtls_ops, | |
| .au_flavor = RPC_AUTH_TLS, | |
| .au_count = REFCOUNT_INIT(1), | |
| }; | |
| static const struct rpc_credops tls_credops = { | |
| .cr_name = "AUTH_TLS", | |
| .crdestroy = tls_destroy_cred, | |
| .crmatch = tls_match, | |
| .crmarshal = tls_marshal, | |
| .crwrap_req = rpcauth_wrap_req_encode, | |
| .crrefresh = tls_refresh, | |
| .crvalidate = tls_validate, | |
| .crunwrap_resp = rpcauth_unwrap_resp_decode, | |
| }; | |
| static struct rpc_cred tls_cred = { | |
| .cr_lru = LIST_HEAD_INIT(tls_cred.cr_lru), | |
| .cr_auth = &tls_auth, | |
| .cr_ops = &tls_credops, | |
| .cr_count = REFCOUNT_INIT(2), | |
| .cr_flags = 1UL << RPCAUTH_CRED_UPTODATE, | |
| }; |