diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/sunrpc/auth_gss/Makefile | 3 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_mech.c | 7 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_mech_switch.c | 7 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_upcall.c | 358 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_upcall.h | 48 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_xdr.c | 838 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_xdr.h | 264 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 363 | ||||
| -rw-r--r-- | net/sunrpc/cache.c | 4 | ||||
| -rw-r--r-- | net/sunrpc/clnt.c | 3 | ||||
| -rw-r--r-- | net/sunrpc/netns.h | 6 | ||||
| -rw-r--r-- | net/sunrpc/xprt.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/xprtsock.c | 3 |
14 files changed, 1889 insertions, 19 deletions
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile index 9e4cb59ef9f0..14e9e53e63d5 100644 --- a/net/sunrpc/auth_gss/Makefile +++ b/net/sunrpc/auth_gss/Makefile | |||
| @@ -5,7 +5,8 @@ | |||
| 5 | obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o | 5 | obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o |
| 6 | 6 | ||
| 7 | auth_rpcgss-y := auth_gss.o gss_generic_token.o \ | 7 | auth_rpcgss-y := auth_gss.o gss_generic_token.o \ |
| 8 | gss_mech_switch.o svcauth_gss.o | 8 | gss_mech_switch.o svcauth_gss.o \ |
| 9 | gss_rpc_upcall.o gss_rpc_xdr.o | ||
| 9 | 10 | ||
| 10 | obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o | 11 | obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o |
| 11 | 12 | ||
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 51415b07174e..a764e227fdde 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
| @@ -238,7 +238,7 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct | |||
| 238 | p = ERR_PTR(-EFAULT); | 238 | p = ERR_PTR(-EFAULT); |
| 239 | goto err; | 239 | goto err; |
| 240 | } | 240 | } |
| 241 | ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, GFP_NOFS); | 241 | ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx, NULL, GFP_NOFS); |
| 242 | if (ret < 0) { | 242 | if (ret < 0) { |
| 243 | p = ERR_PTR(ret); | 243 | p = ERR_PTR(ret); |
| 244 | goto err; | 244 | goto err; |
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 33255ff889c0..0d3c158ef8fa 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c | |||
| @@ -679,6 +679,7 @@ out_err: | |||
| 679 | static int | 679 | static int |
| 680 | gss_import_sec_context_kerberos(const void *p, size_t len, | 680 | gss_import_sec_context_kerberos(const void *p, size_t len, |
| 681 | struct gss_ctx *ctx_id, | 681 | struct gss_ctx *ctx_id, |
| 682 | time_t *endtime, | ||
| 682 | gfp_t gfp_mask) | 683 | gfp_t gfp_mask) |
| 683 | { | 684 | { |
| 684 | const void *end = (const void *)((const char *)p + len); | 685 | const void *end = (const void *)((const char *)p + len); |
| @@ -694,9 +695,11 @@ gss_import_sec_context_kerberos(const void *p, size_t len, | |||
| 694 | else | 695 | else |
| 695 | ret = gss_import_v2_context(p, end, ctx, gfp_mask); | 696 | ret = gss_import_v2_context(p, end, ctx, gfp_mask); |
| 696 | 697 | ||
| 697 | if (ret == 0) | 698 | if (ret == 0) { |
| 698 | ctx_id->internal_ctx_id = ctx; | 699 | ctx_id->internal_ctx_id = ctx; |
| 699 | else | 700 | if (endtime) |
| 701 | *endtime = ctx->endtime; | ||
| 702 | } else | ||
| 700 | kfree(ctx); | 703 | kfree(ctx); |
| 701 | 704 | ||
| 702 | dprintk("RPC: %s: returning %d\n", __func__, ret); | 705 | dprintk("RPC: %s: returning %d\n", __func__, ret); |
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index 79881d6e68a1..defa9d33925c 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c | |||
| @@ -175,7 +175,7 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name) | |||
| 175 | return gm; | 175 | return gm; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj) | 178 | struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj) |
| 179 | { | 179 | { |
| 180 | struct gss_api_mech *pos, *gm = NULL; | 180 | struct gss_api_mech *pos, *gm = NULL; |
| 181 | char buf[32]; | 181 | char buf[32]; |
| @@ -386,14 +386,15 @@ int | |||
| 386 | gss_import_sec_context(const void *input_token, size_t bufsize, | 386 | gss_import_sec_context(const void *input_token, size_t bufsize, |
| 387 | struct gss_api_mech *mech, | 387 | struct gss_api_mech *mech, |
| 388 | struct gss_ctx **ctx_id, | 388 | struct gss_ctx **ctx_id, |
| 389 | time_t *endtime, | ||
| 389 | gfp_t gfp_mask) | 390 | gfp_t gfp_mask) |
| 390 | { | 391 | { |
| 391 | if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask))) | 392 | if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask))) |
| 392 | return -ENOMEM; | 393 | return -ENOMEM; |
| 393 | (*ctx_id)->mech_type = gss_mech_get(mech); | 394 | (*ctx_id)->mech_type = gss_mech_get(mech); |
| 394 | 395 | ||
| 395 | return mech->gm_ops | 396 | return mech->gm_ops->gss_import_sec_context(input_token, bufsize, |
| 396 | ->gss_import_sec_context(input_token, bufsize, *ctx_id, gfp_mask); | 397 | *ctx_id, endtime, gfp_mask); |
| 397 | } | 398 | } |
| 398 | 399 | ||
| 399 | /* gss_get_mic: compute a mic over message and return mic_token. */ | 400 | /* gss_get_mic: compute a mic over message and return mic_token. */ |
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c new file mode 100644 index 000000000000..d304f41260f2 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c | |||
| @@ -0,0 +1,358 @@ | |||
| 1 | /* | ||
| 2 | * linux/net/sunrpc/gss_rpc_upcall.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Simo Sorce <simo@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/types.h> | ||
| 22 | #include <linux/un.h> | ||
| 23 | |||
| 24 | #include <linux/sunrpc/svcauth.h> | ||
| 25 | #include "gss_rpc_upcall.h" | ||
| 26 | |||
| 27 | #define GSSPROXY_SOCK_PATHNAME "/var/run/gssproxy.sock" | ||
| 28 | |||
| 29 | #define GSSPROXY_PROGRAM (400112u) | ||
| 30 | #define GSSPROXY_VERS_1 (1u) | ||
| 31 | |||
| 32 | /* | ||
| 33 | * Encoding/Decoding functions | ||
| 34 | */ | ||
| 35 | |||
| 36 | enum { | ||
| 37 | GSSX_NULL = 0, /* Unused */ | ||
| 38 | GSSX_INDICATE_MECHS = 1, | ||
| 39 | GSSX_GET_CALL_CONTEXT = 2, | ||
| 40 | GSSX_IMPORT_AND_CANON_NAME = 3, | ||
| 41 | GSSX_EXPORT_CRED = 4, | ||
| 42 | GSSX_IMPORT_CRED = 5, | ||
| 43 | GSSX_ACQUIRE_CRED = 6, | ||
| 44 | GSSX_STORE_CRED = 7, | ||
| 45 | GSSX_INIT_SEC_CONTEXT = 8, | ||
| 46 | GSSX_ACCEPT_SEC_CONTEXT = 9, | ||
| 47 | GSSX_RELEASE_HANDLE = 10, | ||
| 48 | GSSX_GET_MIC = 11, | ||
| 49 | GSSX_VERIFY = 12, | ||
| 50 | GSSX_WRAP = 13, | ||
| 51 | GSSX_UNWRAP = 14, | ||
| 52 | GSSX_WRAP_SIZE_LIMIT = 15, | ||
| 53 | }; | ||
| 54 | |||
| 55 | #define PROC(proc, name) \ | ||
| 56 | [GSSX_##proc] = { \ | ||
| 57 | .p_proc = GSSX_##proc, \ | ||
| 58 | .p_encode = (kxdreproc_t)gssx_enc_##name, \ | ||
| 59 | .p_decode = (kxdrdproc_t)gssx_dec_##name, \ | ||
| 60 | .p_arglen = GSSX_ARG_##name##_sz, \ | ||
| 61 | .p_replen = GSSX_RES_##name##_sz, \ | ||
| 62 | .p_statidx = GSSX_##proc, \ | ||
| 63 | .p_name = #proc, \ | ||
| 64 | } | ||
| 65 | |||
| 66 | static struct rpc_procinfo gssp_procedures[] = { | ||
| 67 | PROC(INDICATE_MECHS, indicate_mechs), | ||
| 68 | PROC(GET_CALL_CONTEXT, get_call_context), | ||
| 69 | PROC(IMPORT_AND_CANON_NAME, import_and_canon_name), | ||
| 70 | PROC(EXPORT_CRED, export_cred), | ||
| 71 | PROC(IMPORT_CRED, import_cred), | ||
| 72 | PROC(ACQUIRE_CRED, acquire_cred), | ||
| 73 | PROC(STORE_CRED, store_cred), | ||
| 74 | PROC(INIT_SEC_CONTEXT, init_sec_context), | ||
| 75 | PROC(ACCEPT_SEC_CONTEXT, accept_sec_context), | ||
| 76 | PROC(RELEASE_HANDLE, release_handle), | ||
| 77 | PROC(GET_MIC, get_mic), | ||
| 78 | PROC(VERIFY, verify), | ||
| 79 | PROC(WRAP, wrap), | ||
| 80 | PROC(UNWRAP, unwrap), | ||
| 81 | PROC(WRAP_SIZE_LIMIT, wrap_size_limit), | ||
| 82 | }; | ||
| 83 | |||
| 84 | |||
| 85 | |||
| 86 | /* | ||
| 87 | * Common transport functions | ||
| 88 | */ | ||
| 89 | |||
| 90 | static const struct rpc_program gssp_program; | ||
| 91 | |||
| 92 | static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt) | ||
| 93 | { | ||
| 94 | static const struct sockaddr_un gssp_localaddr = { | ||
| 95 | .sun_family = AF_LOCAL, | ||
| 96 | .sun_path = GSSPROXY_SOCK_PATHNAME, | ||
| 97 | }; | ||
| 98 | struct rpc_create_args args = { | ||
| 99 | .net = net, | ||
| 100 | .protocol = XPRT_TRANSPORT_LOCAL, | ||
| 101 | .address = (struct sockaddr *)&gssp_localaddr, | ||
| 102 | .addrsize = sizeof(gssp_localaddr), | ||
| 103 | .servername = "localhost", | ||
| 104 | .program = &gssp_program, | ||
| 105 | .version = GSSPROXY_VERS_1, | ||
| 106 | .authflavor = RPC_AUTH_NULL, | ||
| 107 | /* | ||
| 108 | * Note we want connection to be done in the caller's | ||
| 109 | * filesystem namespace. We therefore turn off the idle | ||
| 110 | * timeout, which would result in reconnections being | ||
| 111 | * done without the correct namespace: | ||
| 112 | */ | ||
| 113 | .flags = RPC_CLNT_CREATE_NOPING | | ||
| 114 | RPC_CLNT_CREATE_NO_IDLE_TIMEOUT | ||
| 115 | }; | ||
| 116 | struct rpc_clnt *clnt; | ||
| 117 | int result = 0; | ||
| 118 | |||
| 119 | clnt = rpc_create(&args); | ||
| 120 | if (IS_ERR(clnt)) { | ||
| 121 | dprintk("RPC: failed to create AF_LOCAL gssproxy " | ||
| 122 | "client (errno %ld).\n", PTR_ERR(clnt)); | ||
| 123 | result = -PTR_ERR(clnt); | ||
| 124 | *_clnt = NULL; | ||
| 125 | goto out; | ||
| 126 | } | ||
| 127 | |||
| 128 | dprintk("RPC: created new gssp local client (gssp_local_clnt: " | ||
| 129 | "%p)\n", clnt); | ||
| 130 | *_clnt = clnt; | ||
| 131 | |||
| 132 | out: | ||
| 133 | return result; | ||
| 134 | } | ||
| 135 | |||
| 136 | void init_gssp_clnt(struct sunrpc_net *sn) | ||
| 137 | { | ||
| 138 | mutex_init(&sn->gssp_lock); | ||
| 139 | sn->gssp_clnt = NULL; | ||
| 140 | init_waitqueue_head(&sn->gssp_wq); | ||
| 141 | } | ||
| 142 | |||
| 143 | int set_gssp_clnt(struct net *net) | ||
| 144 | { | ||
| 145 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 146 | struct rpc_clnt *clnt; | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | mutex_lock(&sn->gssp_lock); | ||
| 150 | ret = gssp_rpc_create(net, &clnt); | ||
| 151 | if (!ret) { | ||
| 152 | if (sn->gssp_clnt) | ||
| 153 | rpc_shutdown_client(sn->gssp_clnt); | ||
| 154 | sn->gssp_clnt = clnt; | ||
| 155 | } | ||
| 156 | mutex_unlock(&sn->gssp_lock); | ||
| 157 | wake_up(&sn->gssp_wq); | ||
| 158 | return ret; | ||
| 159 | } | ||
| 160 | |||
| 161 | void clear_gssp_clnt(struct sunrpc_net *sn) | ||
| 162 | { | ||
| 163 | mutex_lock(&sn->gssp_lock); | ||
| 164 | if (sn->gssp_clnt) { | ||
| 165 | rpc_shutdown_client(sn->gssp_clnt); | ||
| 166 | sn->gssp_clnt = NULL; | ||
| 167 | } | ||
| 168 | mutex_unlock(&sn->gssp_lock); | ||
| 169 | } | ||
| 170 | |||
| 171 | static struct rpc_clnt *get_gssp_clnt(struct sunrpc_net *sn) | ||
| 172 | { | ||
| 173 | struct rpc_clnt *clnt; | ||
| 174 | |||
| 175 | mutex_lock(&sn->gssp_lock); | ||
| 176 | clnt = sn->gssp_clnt; | ||
| 177 | if (clnt) | ||
| 178 | atomic_inc(&clnt->cl_count); | ||
| 179 | mutex_unlock(&sn->gssp_lock); | ||
| 180 | return clnt; | ||
| 181 | } | ||
| 182 | |||
| 183 | static int gssp_call(struct net *net, struct rpc_message *msg) | ||
| 184 | { | ||
| 185 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 186 | struct rpc_clnt *clnt; | ||
| 187 | int status; | ||
| 188 | |||
| 189 | clnt = get_gssp_clnt(sn); | ||
| 190 | if (!clnt) | ||
| 191 | return -EIO; | ||
| 192 | status = rpc_call_sync(clnt, msg, 0); | ||
| 193 | if (status < 0) { | ||
| 194 | dprintk("gssp: rpc_call returned error %d\n", -status); | ||
| 195 | switch (status) { | ||
| 196 | case -EPROTONOSUPPORT: | ||
| 197 | status = -EINVAL; | ||
| 198 | break; | ||
| 199 | case -ECONNREFUSED: | ||
| 200 | case -ETIMEDOUT: | ||
| 201 | case -ENOTCONN: | ||
| 202 | status = -EAGAIN; | ||
| 203 | break; | ||
| 204 | case -ERESTARTSYS: | ||
| 205 | if (signalled ()) | ||
| 206 | status = -EINTR; | ||
| 207 | break; | ||
| 208 | default: | ||
| 209 | break; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | rpc_release_client(clnt); | ||
| 213 | return status; | ||
| 214 | } | ||
| 215 | |||
| 216 | |||
| 217 | /* | ||
| 218 | * Public functions | ||
| 219 | */ | ||
| 220 | |||
| 221 | /* numbers somewhat arbitrary but large enough for current needs */ | ||
| 222 | #define GSSX_MAX_OUT_HANDLE 128 | ||
| 223 | #define GSSX_MAX_SRC_PRINC 256 | ||
| 224 | #define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \ | ||
| 225 | GSSX_max_oid_sz + \ | ||
| 226 | GSSX_max_princ_sz + \ | ||
| 227 | sizeof(struct svc_cred)) | ||
| 228 | |||
| 229 | int gssp_accept_sec_context_upcall(struct net *net, | ||
| 230 | struct gssp_upcall_data *data) | ||
| 231 | { | ||
| 232 | struct gssx_ctx ctxh = { | ||
| 233 | .state = data->in_handle | ||
| 234 | }; | ||
| 235 | struct gssx_arg_accept_sec_context arg = { | ||
| 236 | .input_token = data->in_token, | ||
| 237 | }; | ||
| 238 | struct gssx_ctx rctxh = { | ||
| 239 | /* | ||
| 240 | * pass in the max length we expect for each of these | ||
| 241 | * buffers but let the xdr code kmalloc them: | ||
| 242 | */ | ||
| 243 | .exported_context_token.len = GSSX_max_output_handle_sz, | ||
| 244 | .mech.len = GSS_OID_MAX_LEN, | ||
| 245 | .src_name.display_name.len = GSSX_max_princ_sz | ||
| 246 | }; | ||
| 247 | struct gssx_res_accept_sec_context res = { | ||
| 248 | .context_handle = &rctxh, | ||
| 249 | .output_token = &data->out_token | ||
| 250 | }; | ||
| 251 | struct rpc_message msg = { | ||
| 252 | .rpc_proc = &gssp_procedures[GSSX_ACCEPT_SEC_CONTEXT], | ||
| 253 | .rpc_argp = &arg, | ||
| 254 | .rpc_resp = &res, | ||
| 255 | .rpc_cred = NULL, /* FIXME ? */ | ||
| 256 | }; | ||
| 257 | struct xdr_netobj client_name = { 0 , NULL }; | ||
| 258 | int ret; | ||
| 259 | |||
| 260 | if (data->in_handle.len != 0) | ||
| 261 | arg.context_handle = &ctxh; | ||
| 262 | res.output_token->len = GSSX_max_output_token_sz; | ||
| 263 | |||
| 264 | /* use nfs/ for targ_name ? */ | ||
| 265 | |||
| 266 | ret = gssp_call(net, &msg); | ||
| 267 | |||
| 268 | /* we need to fetch all data even in case of error so | ||
| 269 | * that we can free special strctures is they have been allocated */ | ||
| 270 | data->major_status = res.status.major_status; | ||
| 271 | data->minor_status = res.status.minor_status; | ||
| 272 | if (res.context_handle) { | ||
| 273 | data->out_handle = rctxh.exported_context_token; | ||
| 274 | data->mech_oid.len = rctxh.mech.len; | ||
| 275 | memcpy(data->mech_oid.data, rctxh.mech.data, | ||
| 276 | data->mech_oid.len); | ||
| 277 | client_name = rctxh.src_name.display_name; | ||
| 278 | } | ||
| 279 | |||
| 280 | if (res.options.count == 1) { | ||
| 281 | gssx_buffer *value = &res.options.data[0].value; | ||
| 282 | /* Currently we only decode CREDS_VALUE, if we add | ||
| 283 | * anything else we'll have to loop and match on the | ||
| 284 | * option name */ | ||
| 285 | if (value->len == 1) { | ||
| 286 | /* steal group info from struct svc_cred */ | ||
| 287 | data->creds = *(struct svc_cred *)value->data; | ||
| 288 | data->found_creds = 1; | ||
| 289 | } | ||
| 290 | /* whether we use it or not, free data */ | ||
| 291 | kfree(value->data); | ||
| 292 | } | ||
| 293 | |||
| 294 | if (res.options.count != 0) { | ||
| 295 | kfree(res.options.data); | ||
| 296 | } | ||
| 297 | |||
| 298 | /* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */ | ||
| 299 | if (data->found_creds && client_name.data != NULL) { | ||
| 300 | char *c; | ||
| 301 | |||
| 302 | data->creds.cr_principal = kstrndup(client_name.data, | ||
| 303 | client_name.len, GFP_KERNEL); | ||
| 304 | if (data->creds.cr_principal) { | ||
| 305 | /* terminate and remove realm part */ | ||
| 306 | c = strchr(data->creds.cr_principal, '@'); | ||
| 307 | if (c) { | ||
| 308 | *c = '\0'; | ||
| 309 | |||
| 310 | /* change service-hostname delimiter */ | ||
| 311 | c = strchr(data->creds.cr_principal, '/'); | ||
| 312 | if (c) *c = '@'; | ||
| 313 | } | ||
| 314 | if (!c) { | ||
| 315 | /* not a service principal */ | ||
| 316 | kfree(data->creds.cr_principal); | ||
| 317 | data->creds.cr_principal = NULL; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | } | ||
| 321 | kfree(client_name.data); | ||
| 322 | |||
| 323 | return ret; | ||
| 324 | } | ||
| 325 | |||
| 326 | void gssp_free_upcall_data(struct gssp_upcall_data *data) | ||
| 327 | { | ||
| 328 | kfree(data->in_handle.data); | ||
| 329 | kfree(data->out_handle.data); | ||
| 330 | kfree(data->out_token.data); | ||
| 331 | kfree(data->mech_oid.data); | ||
| 332 | free_svc_cred(&data->creds); | ||
| 333 | } | ||
| 334 | |||
| 335 | /* | ||
| 336 | * Initialization stuff | ||
| 337 | */ | ||
| 338 | |||
| 339 | static const struct rpc_version gssp_version1 = { | ||
| 340 | .number = GSSPROXY_VERS_1, | ||
| 341 | .nrprocs = ARRAY_SIZE(gssp_procedures), | ||
| 342 | .procs = gssp_procedures, | ||
| 343 | }; | ||
| 344 | |||
| 345 | static const struct rpc_version *gssp_version[] = { | ||
| 346 | NULL, | ||
| 347 | &gssp_version1, | ||
| 348 | }; | ||
| 349 | |||
| 350 | static struct rpc_stat gssp_stats; | ||
| 351 | |||
| 352 | static const struct rpc_program gssp_program = { | ||
| 353 | .name = "gssproxy", | ||
| 354 | .number = GSSPROXY_PROGRAM, | ||
| 355 | .nrvers = ARRAY_SIZE(gssp_version), | ||
| 356 | .version = gssp_version, | ||
| 357 | .stats = &gssp_stats, | ||
| 358 | }; | ||
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.h b/net/sunrpc/auth_gss/gss_rpc_upcall.h new file mode 100644 index 000000000000..1e542aded90a --- /dev/null +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* | ||
| 2 | * linux/net/sunrpc/gss_rpc_upcall.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Simo Sorce <simo@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #ifndef _GSS_RPC_UPCALL_H | ||
| 22 | #define _GSS_RPC_UPCALL_H | ||
| 23 | |||
| 24 | #include <linux/sunrpc/gss_api.h> | ||
| 25 | #include <linux/sunrpc/auth_gss.h> | ||
| 26 | #include "gss_rpc_xdr.h" | ||
| 27 | #include "../netns.h" | ||
| 28 | |||
| 29 | struct gssp_upcall_data { | ||
| 30 | struct xdr_netobj in_handle; | ||
| 31 | struct gssp_in_token in_token; | ||
| 32 | struct xdr_netobj out_handle; | ||
| 33 | struct xdr_netobj out_token; | ||
| 34 | struct rpcsec_gss_oid mech_oid; | ||
| 35 | struct svc_cred creds; | ||
| 36 | int found_creds; | ||
| 37 | int major_status; | ||
| 38 | int minor_status; | ||
| 39 | }; | ||
| 40 | |||
| 41 | int gssp_accept_sec_context_upcall(struct net *net, | ||
| 42 | struct gssp_upcall_data *data); | ||
| 43 | void gssp_free_upcall_data(struct gssp_upcall_data *data); | ||
| 44 | |||
| 45 | void init_gssp_clnt(struct sunrpc_net *); | ||
| 46 | int set_gssp_clnt(struct net *); | ||
| 47 | void clear_gssp_clnt(struct sunrpc_net *); | ||
| 48 | #endif /* _GSS_RPC_UPCALL_H */ | ||
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c new file mode 100644 index 000000000000..5c4c61d527e2 --- /dev/null +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c | |||
| @@ -0,0 +1,838 @@ | |||
| 1 | /* | ||
| 2 | * GSS Proxy upcall module | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Simo Sorce <simo@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/sunrpc/svcauth.h> | ||
| 22 | #include "gss_rpc_xdr.h" | ||
| 23 | |||
| 24 | static bool gssx_check_pointer(struct xdr_stream *xdr) | ||
| 25 | { | ||
| 26 | __be32 *p; | ||
| 27 | |||
| 28 | p = xdr_reserve_space(xdr, 4); | ||
| 29 | if (unlikely(p == NULL)) | ||
| 30 | return -ENOSPC; | ||
| 31 | return *p?true:false; | ||
| 32 | } | ||
| 33 | |||
| 34 | static int gssx_enc_bool(struct xdr_stream *xdr, int v) | ||
| 35 | { | ||
| 36 | __be32 *p; | ||
| 37 | |||
| 38 | p = xdr_reserve_space(xdr, 4); | ||
| 39 | if (unlikely(p == NULL)) | ||
| 40 | return -ENOSPC; | ||
| 41 | *p = v ? xdr_one : xdr_zero; | ||
| 42 | return 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v) | ||
| 46 | { | ||
| 47 | __be32 *p; | ||
| 48 | |||
| 49 | p = xdr_inline_decode(xdr, 4); | ||
| 50 | if (unlikely(p == NULL)) | ||
| 51 | return -ENOSPC; | ||
| 52 | *v = be32_to_cpu(*p); | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | static int gssx_enc_buffer(struct xdr_stream *xdr, | ||
| 57 | gssx_buffer *buf) | ||
| 58 | { | ||
| 59 | __be32 *p; | ||
| 60 | |||
| 61 | p = xdr_reserve_space(xdr, sizeof(u32) + buf->len); | ||
| 62 | if (!p) | ||
| 63 | return -ENOSPC; | ||
| 64 | xdr_encode_opaque(p, buf->data, buf->len); | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static int gssx_enc_in_token(struct xdr_stream *xdr, | ||
| 69 | struct gssp_in_token *in) | ||
| 70 | { | ||
| 71 | __be32 *p; | ||
| 72 | |||
| 73 | p = xdr_reserve_space(xdr, 4); | ||
| 74 | if (!p) | ||
| 75 | return -ENOSPC; | ||
| 76 | *p = cpu_to_be32(in->page_len); | ||
| 77 | |||
| 78 | /* all we need to do is to write pages */ | ||
| 79 | xdr_write_pages(xdr, in->pages, in->page_base, in->page_len); | ||
| 80 | |||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | static int gssx_dec_buffer(struct xdr_stream *xdr, | ||
| 86 | gssx_buffer *buf) | ||
| 87 | { | ||
| 88 | u32 length; | ||
| 89 | __be32 *p; | ||
| 90 | |||
| 91 | p = xdr_inline_decode(xdr, 4); | ||
| 92 | if (unlikely(p == NULL)) | ||
| 93 | return -ENOSPC; | ||
| 94 | |||
| 95 | length = be32_to_cpup(p); | ||
| 96 | p = xdr_inline_decode(xdr, length); | ||
| 97 | if (unlikely(p == NULL)) | ||
| 98 | return -ENOSPC; | ||
| 99 | |||
| 100 | if (buf->len == 0) { | ||
| 101 | /* we intentionally are not interested in this buffer */ | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | if (length > buf->len) | ||
| 105 | return -ENOSPC; | ||
| 106 | |||
| 107 | if (!buf->data) { | ||
| 108 | buf->data = kmemdup(p, length, GFP_KERNEL); | ||
| 109 | if (!buf->data) | ||
| 110 | return -ENOMEM; | ||
| 111 | } else { | ||
| 112 | memcpy(buf->data, p, length); | ||
| 113 | } | ||
| 114 | buf->len = length; | ||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | static int gssx_enc_option(struct xdr_stream *xdr, | ||
| 119 | struct gssx_option *opt) | ||
| 120 | { | ||
| 121 | int err; | ||
| 122 | |||
| 123 | err = gssx_enc_buffer(xdr, &opt->option); | ||
| 124 | if (err) | ||
| 125 | return err; | ||
| 126 | err = gssx_enc_buffer(xdr, &opt->value); | ||
| 127 | return err; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int gssx_dec_option(struct xdr_stream *xdr, | ||
| 131 | struct gssx_option *opt) | ||
| 132 | { | ||
| 133 | int err; | ||
| 134 | |||
| 135 | err = gssx_dec_buffer(xdr, &opt->option); | ||
| 136 | if (err) | ||
| 137 | return err; | ||
| 138 | err = gssx_dec_buffer(xdr, &opt->value); | ||
| 139 | return err; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int dummy_enc_opt_array(struct xdr_stream *xdr, | ||
| 143 | struct gssx_option_array *oa) | ||
| 144 | { | ||
| 145 | __be32 *p; | ||
| 146 | |||
| 147 | if (oa->count != 0) | ||
| 148 | return -EINVAL; | ||
| 149 | |||
| 150 | p = xdr_reserve_space(xdr, 4); | ||
| 151 | if (!p) | ||
| 152 | return -ENOSPC; | ||
| 153 | *p = 0; | ||
| 154 | |||
| 155 | return 0; | ||
| 156 | } | ||
| 157 | |||
| 158 | static int dummy_dec_opt_array(struct xdr_stream *xdr, | ||
| 159 | struct gssx_option_array *oa) | ||
| 160 | { | ||
| 161 | struct gssx_option dummy; | ||
| 162 | u32 count, i; | ||
| 163 | __be32 *p; | ||
| 164 | |||
| 165 | p = xdr_inline_decode(xdr, 4); | ||
| 166 | if (unlikely(p == NULL)) | ||
| 167 | return -ENOSPC; | ||
| 168 | count = be32_to_cpup(p++); | ||
| 169 | memset(&dummy, 0, sizeof(dummy)); | ||
| 170 | for (i = 0; i < count; i++) { | ||
| 171 | gssx_dec_option(xdr, &dummy); | ||
| 172 | } | ||
| 173 | |||
| 174 | oa->count = 0; | ||
| 175 | oa->data = NULL; | ||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | static int get_s32(void **p, void *max, s32 *res) | ||
| 180 | { | ||
| 181 | void *base = *p; | ||
| 182 | void *next = (void *)((char *)base + sizeof(s32)); | ||
| 183 | if (unlikely(next > max || next < base)) | ||
| 184 | return -EINVAL; | ||
| 185 | memcpy(res, base, sizeof(s32)); | ||
| 186 | *p = next; | ||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | static int gssx_dec_linux_creds(struct xdr_stream *xdr, | ||
| 191 | struct svc_cred *creds) | ||
| 192 | { | ||
| 193 | u32 length; | ||
| 194 | __be32 *p; | ||
| 195 | void *q, *end; | ||
| 196 | s32 tmp; | ||
| 197 | int N, i, err; | ||
| 198 | |||
| 199 | p = xdr_inline_decode(xdr, 4); | ||
| 200 | if (unlikely(p == NULL)) | ||
| 201 | return -ENOSPC; | ||
| 202 | |||
| 203 | length = be32_to_cpup(p); | ||
| 204 | |||
| 205 | /* FIXME: we do not want to use the scratch buffer for this one | ||
| 206 | * may need to use functions that allows us to access an io vector | ||
| 207 | * directly */ | ||
| 208 | p = xdr_inline_decode(xdr, length); | ||
| 209 | if (unlikely(p == NULL)) | ||
| 210 | return -ENOSPC; | ||
| 211 | |||
| 212 | q = p; | ||
| 213 | end = q + length; | ||
| 214 | |||
| 215 | /* uid */ | ||
| 216 | err = get_s32(&q, end, &tmp); | ||
| 217 | if (err) | ||
| 218 | return err; | ||
| 219 | creds->cr_uid = make_kuid(&init_user_ns, tmp); | ||
| 220 | |||
| 221 | /* gid */ | ||
| 222 | err = get_s32(&q, end, &tmp); | ||
| 223 | if (err) | ||
| 224 | return err; | ||
| 225 | creds->cr_gid = make_kgid(&init_user_ns, tmp); | ||
| 226 | |||
| 227 | /* number of additional gid's */ | ||
| 228 | err = get_s32(&q, end, &tmp); | ||
| 229 | if (err) | ||
| 230 | return err; | ||
| 231 | N = tmp; | ||
| 232 | creds->cr_group_info = groups_alloc(N); | ||
| 233 | if (creds->cr_group_info == NULL) | ||
| 234 | return -ENOMEM; | ||
| 235 | |||
| 236 | /* gid's */ | ||
| 237 | for (i = 0; i < N; i++) { | ||
| 238 | kgid_t kgid; | ||
| 239 | err = get_s32(&q, end, &tmp); | ||
| 240 | if (err) | ||
| 241 | goto out_free_groups; | ||
| 242 | err = -EINVAL; | ||
| 243 | kgid = make_kgid(&init_user_ns, tmp); | ||
| 244 | if (!gid_valid(kgid)) | ||
| 245 | goto out_free_groups; | ||
| 246 | GROUP_AT(creds->cr_group_info, i) = kgid; | ||
| 247 | } | ||
| 248 | |||
| 249 | return 0; | ||
| 250 | out_free_groups: | ||
| 251 | groups_free(creds->cr_group_info); | ||
| 252 | return err; | ||
| 253 | } | ||
| 254 | |||
| 255 | static int gssx_dec_option_array(struct xdr_stream *xdr, | ||
| 256 | struct gssx_option_array *oa) | ||
| 257 | { | ||
| 258 | struct svc_cred *creds; | ||
| 259 | u32 count, i; | ||
| 260 | __be32 *p; | ||
| 261 | int err; | ||
| 262 | |||
| 263 | p = xdr_inline_decode(xdr, 4); | ||
| 264 | if (unlikely(p == NULL)) | ||
| 265 | return -ENOSPC; | ||
| 266 | count = be32_to_cpup(p++); | ||
| 267 | if (count != 0) { | ||
| 268 | /* we recognize only 1 currently: CREDS_VALUE */ | ||
| 269 | oa->count = 1; | ||
| 270 | |||
| 271 | oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL); | ||
| 272 | if (!oa->data) | ||
| 273 | return -ENOMEM; | ||
| 274 | |||
| 275 | creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL); | ||
| 276 | if (!creds) { | ||
| 277 | kfree(oa->data); | ||
| 278 | return -ENOMEM; | ||
| 279 | } | ||
| 280 | |||
| 281 | oa->data[0].option.data = CREDS_VALUE; | ||
| 282 | oa->data[0].option.len = sizeof(CREDS_VALUE); | ||
| 283 | oa->data[0].value.data = (void *)creds; | ||
| 284 | oa->data[0].value.len = 0; | ||
| 285 | } | ||
| 286 | for (i = 0; i < count; i++) { | ||
| 287 | gssx_buffer dummy = { 0, NULL }; | ||
| 288 | u32 length; | ||
| 289 | |||
| 290 | /* option buffer */ | ||
| 291 | p = xdr_inline_decode(xdr, 4); | ||
| 292 | if (unlikely(p == NULL)) | ||
| 293 | return -ENOSPC; | ||
| 294 | |||
| 295 | length = be32_to_cpup(p); | ||
| 296 | p = xdr_inline_decode(xdr, length); | ||
| 297 | if (unlikely(p == NULL)) | ||
| 298 | return -ENOSPC; | ||
| 299 | |||
| 300 | if (length == sizeof(CREDS_VALUE) && | ||
| 301 | memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) { | ||
| 302 | /* We have creds here. parse them */ | ||
| 303 | err = gssx_dec_linux_creds(xdr, creds); | ||
| 304 | if (err) | ||
| 305 | return err; | ||
| 306 | oa->data[0].value.len = 1; /* presence */ | ||
| 307 | } else { | ||
| 308 | /* consume uninteresting buffer */ | ||
| 309 | err = gssx_dec_buffer(xdr, &dummy); | ||
| 310 | if (err) | ||
| 311 | return err; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | return 0; | ||
| 315 | } | ||
| 316 | |||
| 317 | static int gssx_dec_status(struct xdr_stream *xdr, | ||
| 318 | struct gssx_status *status) | ||
| 319 | { | ||
| 320 | __be32 *p; | ||
| 321 | int err; | ||
| 322 | |||
| 323 | /* status->major_status */ | ||
| 324 | p = xdr_inline_decode(xdr, 8); | ||
| 325 | if (unlikely(p == NULL)) | ||
| 326 | return -ENOSPC; | ||
| 327 | p = xdr_decode_hyper(p, &status->major_status); | ||
| 328 | |||
| 329 | /* status->mech */ | ||
| 330 | err = gssx_dec_buffer(xdr, &status->mech); | ||
| 331 | if (err) | ||
| 332 | return err; | ||
| 333 | |||
| 334 | /* status->minor_status */ | ||
| 335 | p = xdr_inline_decode(xdr, 8); | ||
| 336 | if (unlikely(p == NULL)) | ||
| 337 | return -ENOSPC; | ||
| 338 | p = xdr_decode_hyper(p, &status->minor_status); | ||
| 339 | |||
| 340 | /* status->major_status_string */ | ||
| 341 | err = gssx_dec_buffer(xdr, &status->major_status_string); | ||
| 342 | if (err) | ||
| 343 | return err; | ||
| 344 | |||
| 345 | /* status->minor_status_string */ | ||
| 346 | err = gssx_dec_buffer(xdr, &status->minor_status_string); | ||
| 347 | if (err) | ||
| 348 | return err; | ||
| 349 | |||
| 350 | /* status->server_ctx */ | ||
| 351 | err = gssx_dec_buffer(xdr, &status->server_ctx); | ||
| 352 | if (err) | ||
| 353 | return err; | ||
| 354 | |||
| 355 | /* we assume we have no options for now, so simply consume them */ | ||
| 356 | /* status->options */ | ||
| 357 | err = dummy_dec_opt_array(xdr, &status->options); | ||
| 358 | |||
| 359 | return err; | ||
| 360 | } | ||
| 361 | |||
| 362 | static int gssx_enc_call_ctx(struct xdr_stream *xdr, | ||
| 363 | struct gssx_call_ctx *ctx) | ||
| 364 | { | ||
| 365 | struct gssx_option opt; | ||
| 366 | __be32 *p; | ||
| 367 | int err; | ||
| 368 | |||
| 369 | /* ctx->locale */ | ||
| 370 | err = gssx_enc_buffer(xdr, &ctx->locale); | ||
| 371 | if (err) | ||
| 372 | return err; | ||
| 373 | |||
| 374 | /* ctx->server_ctx */ | ||
| 375 | err = gssx_enc_buffer(xdr, &ctx->server_ctx); | ||
| 376 | if (err) | ||
| 377 | return err; | ||
| 378 | |||
| 379 | /* we always want to ask for lucid contexts */ | ||
| 380 | /* ctx->options */ | ||
| 381 | p = xdr_reserve_space(xdr, 4); | ||
| 382 | *p = cpu_to_be32(2); | ||
| 383 | |||
| 384 | /* we want a lucid_v1 context */ | ||
| 385 | opt.option.data = LUCID_OPTION; | ||
| 386 | opt.option.len = sizeof(LUCID_OPTION); | ||
| 387 | opt.value.data = LUCID_VALUE; | ||
| 388 | opt.value.len = sizeof(LUCID_VALUE); | ||
| 389 | err = gssx_enc_option(xdr, &opt); | ||
| 390 | |||
| 391 | /* ..and user creds */ | ||
| 392 | opt.option.data = CREDS_OPTION; | ||
| 393 | opt.option.len = sizeof(CREDS_OPTION); | ||
| 394 | opt.value.data = CREDS_VALUE; | ||
| 395 | opt.value.len = sizeof(CREDS_VALUE); | ||
| 396 | err = gssx_enc_option(xdr, &opt); | ||
| 397 | |||
| 398 | return err; | ||
| 399 | } | ||
| 400 | |||
| 401 | static int gssx_dec_name_attr(struct xdr_stream *xdr, | ||
| 402 | struct gssx_name_attr *attr) | ||
| 403 | { | ||
| 404 | int err; | ||
| 405 | |||
| 406 | /* attr->attr */ | ||
| 407 | err = gssx_dec_buffer(xdr, &attr->attr); | ||
| 408 | if (err) | ||
| 409 | return err; | ||
| 410 | |||
| 411 | /* attr->value */ | ||
| 412 | err = gssx_dec_buffer(xdr, &attr->value); | ||
| 413 | if (err) | ||
| 414 | return err; | ||
| 415 | |||
| 416 | /* attr->extensions */ | ||
| 417 | err = dummy_dec_opt_array(xdr, &attr->extensions); | ||
| 418 | |||
| 419 | return err; | ||
| 420 | } | ||
| 421 | |||
| 422 | static int dummy_enc_nameattr_array(struct xdr_stream *xdr, | ||
| 423 | struct gssx_name_attr_array *naa) | ||
| 424 | { | ||
| 425 | __be32 *p; | ||
| 426 | |||
| 427 | if (naa->count != 0) | ||
| 428 | return -EINVAL; | ||
| 429 | |||
| 430 | p = xdr_reserve_space(xdr, 4); | ||
| 431 | if (!p) | ||
| 432 | return -ENOSPC; | ||
| 433 | *p = 0; | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 438 | static int dummy_dec_nameattr_array(struct xdr_stream *xdr, | ||
| 439 | struct gssx_name_attr_array *naa) | ||
| 440 | { | ||
| 441 | struct gssx_name_attr dummy; | ||
| 442 | u32 count, i; | ||
| 443 | __be32 *p; | ||
| 444 | |||
| 445 | p = xdr_inline_decode(xdr, 4); | ||
| 446 | if (unlikely(p == NULL)) | ||
| 447 | return -ENOSPC; | ||
| 448 | count = be32_to_cpup(p++); | ||
| 449 | for (i = 0; i < count; i++) { | ||
| 450 | gssx_dec_name_attr(xdr, &dummy); | ||
| 451 | } | ||
| 452 | |||
| 453 | naa->count = 0; | ||
| 454 | naa->data = NULL; | ||
| 455 | return 0; | ||
| 456 | } | ||
| 457 | |||
| 458 | static struct xdr_netobj zero_netobj = {}; | ||
| 459 | |||
| 460 | static struct gssx_name_attr_array zero_name_attr_array = {}; | ||
| 461 | |||
| 462 | static struct gssx_option_array zero_option_array = {}; | ||
| 463 | |||
| 464 | static int gssx_enc_name(struct xdr_stream *xdr, | ||
| 465 | struct gssx_name *name) | ||
| 466 | { | ||
| 467 | int err; | ||
| 468 | |||
| 469 | /* name->display_name */ | ||
| 470 | err = gssx_enc_buffer(xdr, &name->display_name); | ||
| 471 | if (err) | ||
| 472 | return err; | ||
| 473 | |||
| 474 | /* name->name_type */ | ||
| 475 | err = gssx_enc_buffer(xdr, &zero_netobj); | ||
| 476 | if (err) | ||
| 477 | return err; | ||
| 478 | |||
| 479 | /* name->exported_name */ | ||
| 480 | err = gssx_enc_buffer(xdr, &zero_netobj); | ||
| 481 | if (err) | ||
| 482 | return err; | ||
| 483 | |||
| 484 | /* name->exported_composite_name */ | ||
| 485 | err = gssx_enc_buffer(xdr, &zero_netobj); | ||
| 486 | if (err) | ||
| 487 | return err; | ||
| 488 | |||
| 489 | /* leave name_attributes empty for now, will add once we have any | ||
| 490 | * to pass up at all */ | ||
| 491 | /* name->name_attributes */ | ||
| 492 | err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array); | ||
| 493 | if (err) | ||
| 494 | return err; | ||
| 495 | |||
| 496 | /* leave options empty for now, will add once we have any options | ||
| 497 | * to pass up at all */ | ||
| 498 | /* name->extensions */ | ||
| 499 | err = dummy_enc_opt_array(xdr, &zero_option_array); | ||
| 500 | |||
| 501 | return err; | ||
| 502 | } | ||
| 503 | |||
| 504 | static int gssx_dec_name(struct xdr_stream *xdr, | ||
| 505 | struct gssx_name *name) | ||
| 506 | { | ||
| 507 | struct xdr_netobj dummy_netobj; | ||
| 508 | struct gssx_name_attr_array dummy_name_attr_array; | ||
| 509 | struct gssx_option_array dummy_option_array; | ||
| 510 | int err; | ||
| 511 | |||
| 512 | /* name->display_name */ | ||
| 513 | err = gssx_dec_buffer(xdr, &name->display_name); | ||
| 514 | if (err) | ||
| 515 | return err; | ||
| 516 | |||
| 517 | /* name->name_type */ | ||
| 518 | err = gssx_dec_buffer(xdr, &dummy_netobj); | ||
| 519 | if (err) | ||
| 520 | return err; | ||
| 521 | |||
| 522 | /* name->exported_name */ | ||
| 523 | err = gssx_dec_buffer(xdr, &dummy_netobj); | ||
| 524 | if (err) | ||
| 525 | return err; | ||
| 526 | |||
| 527 | /* name->exported_composite_name */ | ||
| 528 | err = gssx_dec_buffer(xdr, &dummy_netobj); | ||
| 529 | if (err) | ||
| 530 | return err; | ||
| 531 | |||
| 532 | /* we assume we have no attributes for now, so simply consume them */ | ||
| 533 | /* name->name_attributes */ | ||
| 534 | err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array); | ||
| 535 | if (err) | ||
| 536 | return err; | ||
| 537 | |||
| 538 | /* we assume we have no options for now, so simply consume them */ | ||
| 539 | /* name->extensions */ | ||
| 540 | err = dummy_dec_opt_array(xdr, &dummy_option_array); | ||
| 541 | |||
| 542 | return err; | ||
| 543 | } | ||
| 544 | |||
| 545 | static int dummy_enc_credel_array(struct xdr_stream *xdr, | ||
| 546 | struct gssx_cred_element_array *cea) | ||
| 547 | { | ||
| 548 | __be32 *p; | ||
| 549 | |||
| 550 | if (cea->count != 0) | ||
| 551 | return -EINVAL; | ||
| 552 | |||
| 553 | p = xdr_reserve_space(xdr, 4); | ||
| 554 | if (!p) | ||
| 555 | return -ENOSPC; | ||
| 556 | *p = 0; | ||
| 557 | |||
| 558 | return 0; | ||
| 559 | } | ||
| 560 | |||
| 561 | static int gssx_enc_cred(struct xdr_stream *xdr, | ||
| 562 | struct gssx_cred *cred) | ||
| 563 | { | ||
| 564 | int err; | ||
| 565 | |||
| 566 | /* cred->desired_name */ | ||
| 567 | err = gssx_enc_name(xdr, &cred->desired_name); | ||
| 568 | if (err) | ||
| 569 | return err; | ||
| 570 | |||
| 571 | /* cred->elements */ | ||
| 572 | err = dummy_enc_credel_array(xdr, &cred->elements); | ||
| 573 | |||
| 574 | /* cred->cred_handle_reference */ | ||
| 575 | err = gssx_enc_buffer(xdr, &cred->cred_handle_reference); | ||
| 576 | if (err) | ||
| 577 | return err; | ||
| 578 | |||
| 579 | /* cred->needs_release */ | ||
| 580 | err = gssx_enc_bool(xdr, cred->needs_release); | ||
| 581 | |||
| 582 | return err; | ||
| 583 | } | ||
| 584 | |||
| 585 | static int gssx_enc_ctx(struct xdr_stream *xdr, | ||
| 586 | struct gssx_ctx *ctx) | ||
| 587 | { | ||
| 588 | __be32 *p; | ||
| 589 | int err; | ||
| 590 | |||
| 591 | /* ctx->exported_context_token */ | ||
| 592 | err = gssx_enc_buffer(xdr, &ctx->exported_context_token); | ||
| 593 | if (err) | ||
| 594 | return err; | ||
| 595 | |||
| 596 | /* ctx->state */ | ||
| 597 | err = gssx_enc_buffer(xdr, &ctx->state); | ||
| 598 | if (err) | ||
| 599 | return err; | ||
| 600 | |||
| 601 | /* ctx->need_release */ | ||
| 602 | err = gssx_enc_bool(xdr, ctx->need_release); | ||
| 603 | if (err) | ||
| 604 | return err; | ||
| 605 | |||
| 606 | /* ctx->mech */ | ||
| 607 | err = gssx_enc_buffer(xdr, &ctx->mech); | ||
| 608 | if (err) | ||
| 609 | return err; | ||
| 610 | |||
| 611 | /* ctx->src_name */ | ||
| 612 | err = gssx_enc_name(xdr, &ctx->src_name); | ||
| 613 | if (err) | ||
| 614 | return err; | ||
| 615 | |||
| 616 | /* ctx->targ_name */ | ||
| 617 | err = gssx_enc_name(xdr, &ctx->targ_name); | ||
| 618 | if (err) | ||
| 619 | return err; | ||
| 620 | |||
| 621 | /* ctx->lifetime */ | ||
| 622 | p = xdr_reserve_space(xdr, 8+8); | ||
| 623 | if (!p) | ||
| 624 | return -ENOSPC; | ||
| 625 | p = xdr_encode_hyper(p, ctx->lifetime); | ||
| 626 | |||
| 627 | /* ctx->ctx_flags */ | ||
| 628 | p = xdr_encode_hyper(p, ctx->ctx_flags); | ||
| 629 | |||
| 630 | /* ctx->locally_initiated */ | ||
| 631 | err = gssx_enc_bool(xdr, ctx->locally_initiated); | ||
| 632 | if (err) | ||
| 633 | return err; | ||
| 634 | |||
| 635 | /* ctx->open */ | ||
| 636 | err = gssx_enc_bool(xdr, ctx->open); | ||
| 637 | if (err) | ||
| 638 | return err; | ||
| 639 | |||
| 640 | /* leave options empty for now, will add once we have any options | ||
| 641 | * to pass up at all */ | ||
| 642 | /* ctx->options */ | ||
| 643 | err = dummy_enc_opt_array(xdr, &ctx->options); | ||
| 644 | |||
| 645 | return err; | ||
| 646 | } | ||
| 647 | |||
| 648 | static int gssx_dec_ctx(struct xdr_stream *xdr, | ||
| 649 | struct gssx_ctx *ctx) | ||
| 650 | { | ||
| 651 | __be32 *p; | ||
| 652 | int err; | ||
| 653 | |||
| 654 | /* ctx->exported_context_token */ | ||
| 655 | err = gssx_dec_buffer(xdr, &ctx->exported_context_token); | ||
| 656 | if (err) | ||
| 657 | return err; | ||
| 658 | |||
| 659 | /* ctx->state */ | ||
| 660 | err = gssx_dec_buffer(xdr, &ctx->state); | ||
| 661 | if (err) | ||
| 662 | return err; | ||
| 663 | |||
| 664 | /* ctx->need_release */ | ||
| 665 | err = gssx_dec_bool(xdr, &ctx->need_release); | ||
| 666 | if (err) | ||
| 667 | return err; | ||
| 668 | |||
| 669 | /* ctx->mech */ | ||
| 670 | err = gssx_dec_buffer(xdr, &ctx->mech); | ||
| 671 | if (err) | ||
| 672 | return err; | ||
| 673 | |||
| 674 | /* ctx->src_name */ | ||
| 675 | err = gssx_dec_name(xdr, &ctx->src_name); | ||
| 676 | if (err) | ||
| 677 | return err; | ||
| 678 | |||
| 679 | /* ctx->targ_name */ | ||
| 680 | err = gssx_dec_name(xdr, &ctx->targ_name); | ||
| 681 | if (err) | ||
| 682 | return err; | ||
| 683 | |||
| 684 | /* ctx->lifetime */ | ||
| 685 | p = xdr_inline_decode(xdr, 8+8); | ||
| 686 | if (unlikely(p == NULL)) | ||
| 687 | return -ENOSPC; | ||
| 688 | p = xdr_decode_hyper(p, &ctx->lifetime); | ||
| 689 | |||
| 690 | /* ctx->ctx_flags */ | ||
| 691 | p = xdr_decode_hyper(p, &ctx->ctx_flags); | ||
| 692 | |||
| 693 | /* ctx->locally_initiated */ | ||
| 694 | err = gssx_dec_bool(xdr, &ctx->locally_initiated); | ||
| 695 | if (err) | ||
| 696 | return err; | ||
| 697 | |||
| 698 | /* ctx->open */ | ||
| 699 | err = gssx_dec_bool(xdr, &ctx->open); | ||
| 700 | if (err) | ||
| 701 | return err; | ||
| 702 | |||
| 703 | /* we assume we have no options for now, so simply consume them */ | ||
| 704 | /* ctx->options */ | ||
| 705 | err = dummy_dec_opt_array(xdr, &ctx->options); | ||
| 706 | |||
| 707 | return err; | ||
| 708 | } | ||
| 709 | |||
| 710 | static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb) | ||
| 711 | { | ||
| 712 | __be32 *p; | ||
| 713 | int err; | ||
| 714 | |||
| 715 | /* cb->initiator_addrtype */ | ||
| 716 | p = xdr_reserve_space(xdr, 8); | ||
| 717 | if (!p) | ||
| 718 | return -ENOSPC; | ||
| 719 | p = xdr_encode_hyper(p, cb->initiator_addrtype); | ||
| 720 | |||
| 721 | /* cb->initiator_address */ | ||
| 722 | err = gssx_enc_buffer(xdr, &cb->initiator_address); | ||
| 723 | if (err) | ||
| 724 | return err; | ||
| 725 | |||
| 726 | /* cb->acceptor_addrtype */ | ||
| 727 | p = xdr_reserve_space(xdr, 8); | ||
| 728 | if (!p) | ||
| 729 | return -ENOSPC; | ||
| 730 | p = xdr_encode_hyper(p, cb->acceptor_addrtype); | ||
| 731 | |||
| 732 | /* cb->acceptor_address */ | ||
| 733 | err = gssx_enc_buffer(xdr, &cb->acceptor_address); | ||
| 734 | if (err) | ||
| 735 | return err; | ||
| 736 | |||
| 737 | /* cb->application_data */ | ||
| 738 | err = gssx_enc_buffer(xdr, &cb->application_data); | ||
| 739 | |||
| 740 | return err; | ||
| 741 | } | ||
| 742 | |||
| 743 | void gssx_enc_accept_sec_context(struct rpc_rqst *req, | ||
| 744 | struct xdr_stream *xdr, | ||
| 745 | struct gssx_arg_accept_sec_context *arg) | ||
| 746 | { | ||
| 747 | int err; | ||
| 748 | |||
| 749 | err = gssx_enc_call_ctx(xdr, &arg->call_ctx); | ||
| 750 | if (err) | ||
| 751 | goto done; | ||
| 752 | |||
| 753 | /* arg->context_handle */ | ||
| 754 | if (arg->context_handle) { | ||
| 755 | err = gssx_enc_ctx(xdr, arg->context_handle); | ||
| 756 | if (err) | ||
| 757 | goto done; | ||
| 758 | } else { | ||
| 759 | err = gssx_enc_bool(xdr, 0); | ||
| 760 | } | ||
| 761 | |||
| 762 | /* arg->cred_handle */ | ||
| 763 | if (arg->cred_handle) { | ||
| 764 | err = gssx_enc_cred(xdr, arg->cred_handle); | ||
| 765 | if (err) | ||
| 766 | goto done; | ||
| 767 | } else { | ||
| 768 | err = gssx_enc_bool(xdr, 0); | ||
| 769 | } | ||
| 770 | |||
| 771 | /* arg->input_token */ | ||
| 772 | err = gssx_enc_in_token(xdr, &arg->input_token); | ||
| 773 | if (err) | ||
| 774 | goto done; | ||
| 775 | |||
| 776 | /* arg->input_cb */ | ||
| 777 | if (arg->input_cb) { | ||
| 778 | err = gssx_enc_cb(xdr, arg->input_cb); | ||
| 779 | if (err) | ||
| 780 | goto done; | ||
| 781 | } else { | ||
| 782 | err = gssx_enc_bool(xdr, 0); | ||
| 783 | } | ||
| 784 | |||
| 785 | err = gssx_enc_bool(xdr, arg->ret_deleg_cred); | ||
| 786 | if (err) | ||
| 787 | goto done; | ||
| 788 | |||
| 789 | /* leave options empty for now, will add once we have any options | ||
| 790 | * to pass up at all */ | ||
| 791 | /* arg->options */ | ||
| 792 | err = dummy_enc_opt_array(xdr, &arg->options); | ||
| 793 | |||
| 794 | done: | ||
| 795 | if (err) | ||
| 796 | dprintk("RPC: gssx_enc_accept_sec_context: %d\n", err); | ||
| 797 | } | ||
| 798 | |||
| 799 | int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, | ||
| 800 | struct xdr_stream *xdr, | ||
| 801 | struct gssx_res_accept_sec_context *res) | ||
| 802 | { | ||
| 803 | int err; | ||
| 804 | |||
| 805 | /* res->status */ | ||
| 806 | err = gssx_dec_status(xdr, &res->status); | ||
| 807 | if (err) | ||
| 808 | return err; | ||
| 809 | |||
| 810 | /* res->context_handle */ | ||
| 811 | if (gssx_check_pointer(xdr)) { | ||
| 812 | err = gssx_dec_ctx(xdr, res->context_handle); | ||
| 813 | if (err) | ||
| 814 | return err; | ||
| 815 | } else { | ||
| 816 | res->context_handle = NULL; | ||
| 817 | } | ||
| 818 | |||
| 819 | /* res->output_token */ | ||
| 820 | if (gssx_check_pointer(xdr)) { | ||
| 821 | err = gssx_dec_buffer(xdr, res->output_token); | ||
| 822 | if (err) | ||
| 823 | return err; | ||
| 824 | } else { | ||
| 825 | res->output_token = NULL; | ||
| 826 | } | ||
| 827 | |||
| 828 | /* res->delegated_cred_handle */ | ||
| 829 | if (gssx_check_pointer(xdr)) { | ||
| 830 | /* we do not support upcall servers sending this data. */ | ||
| 831 | return -EINVAL; | ||
| 832 | } | ||
| 833 | |||
| 834 | /* res->options */ | ||
| 835 | err = gssx_dec_option_array(xdr, &res->options); | ||
| 836 | |||
| 837 | return err; | ||
| 838 | } | ||
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h new file mode 100644 index 000000000000..1c98b27d870c --- /dev/null +++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h | |||
| @@ -0,0 +1,264 @@ | |||
| 1 | /* | ||
| 2 | * GSS Proxy upcall module | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Simo Sorce <simo@redhat.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #ifndef _LINUX_GSS_RPC_XDR_H | ||
| 22 | #define _LINUX_GSS_RPC_XDR_H | ||
| 23 | |||
| 24 | #include <linux/sunrpc/xdr.h> | ||
| 25 | #include <linux/sunrpc/clnt.h> | ||
| 26 | #include <linux/sunrpc/xprtsock.h> | ||
| 27 | |||
| 28 | #ifdef RPC_DEBUG | ||
| 29 | # define RPCDBG_FACILITY RPCDBG_AUTH | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #define LUCID_OPTION "exported_context_type" | ||
| 33 | #define LUCID_VALUE "linux_lucid_v1" | ||
| 34 | #define CREDS_OPTION "exported_creds_type" | ||
| 35 | #define CREDS_VALUE "linux_creds_v1" | ||
| 36 | |||
| 37 | typedef struct xdr_netobj gssx_buffer; | ||
| 38 | typedef struct xdr_netobj utf8string; | ||
| 39 | typedef struct xdr_netobj gssx_OID; | ||
| 40 | |||
| 41 | enum gssx_cred_usage { | ||
| 42 | GSSX_C_INITIATE = 1, | ||
| 43 | GSSX_C_ACCEPT = 2, | ||
| 44 | GSSX_C_BOTH = 3, | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct gssx_option { | ||
| 48 | gssx_buffer option; | ||
| 49 | gssx_buffer value; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct gssx_option_array { | ||
| 53 | u32 count; | ||
| 54 | struct gssx_option *data; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct gssx_status { | ||
| 58 | u64 major_status; | ||
| 59 | gssx_OID mech; | ||
| 60 | u64 minor_status; | ||
| 61 | utf8string major_status_string; | ||
| 62 | utf8string minor_status_string; | ||
| 63 | gssx_buffer server_ctx; | ||
| 64 | struct gssx_option_array options; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct gssx_call_ctx { | ||
| 68 | utf8string locale; | ||
| 69 | gssx_buffer server_ctx; | ||
| 70 | struct gssx_option_array options; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct gssx_name_attr { | ||
| 74 | gssx_buffer attr; | ||
| 75 | gssx_buffer value; | ||
| 76 | struct gssx_option_array extensions; | ||
| 77 | }; | ||
| 78 | |||
| 79 | struct gssx_name_attr_array { | ||
| 80 | u32 count; | ||
| 81 | struct gssx_name_attr *data; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct gssx_name { | ||
| 85 | gssx_buffer display_name; | ||
| 86 | }; | ||
| 87 | typedef struct gssx_name gssx_name; | ||
| 88 | |||
| 89 | struct gssx_cred_element { | ||
| 90 | gssx_name MN; | ||
| 91 | gssx_OID mech; | ||
| 92 | u32 cred_usage; | ||
| 93 | u64 initiator_time_rec; | ||
| 94 | u64 acceptor_time_rec; | ||
| 95 | struct gssx_option_array options; | ||
| 96 | }; | ||
| 97 | |||
| 98 | struct gssx_cred_element_array { | ||
| 99 | u32 count; | ||
| 100 | struct gssx_cred_element *data; | ||
| 101 | }; | ||
| 102 | |||
| 103 | struct gssx_cred { | ||
| 104 | gssx_name desired_name; | ||
| 105 | struct gssx_cred_element_array elements; | ||
| 106 | gssx_buffer cred_handle_reference; | ||
| 107 | u32 needs_release; | ||
| 108 | }; | ||
| 109 | |||
| 110 | struct gssx_ctx { | ||
| 111 | gssx_buffer exported_context_token; | ||
| 112 | gssx_buffer state; | ||
| 113 | u32 need_release; | ||
| 114 | gssx_OID mech; | ||
| 115 | gssx_name src_name; | ||
| 116 | gssx_name targ_name; | ||
| 117 | u64 lifetime; | ||
| 118 | u64 ctx_flags; | ||
| 119 | u32 locally_initiated; | ||
| 120 | u32 open; | ||
| 121 | struct gssx_option_array options; | ||
| 122 | }; | ||
| 123 | |||
| 124 | struct gssx_cb { | ||
| 125 | u64 initiator_addrtype; | ||
| 126 | gssx_buffer initiator_address; | ||
| 127 | u64 acceptor_addrtype; | ||
| 128 | gssx_buffer acceptor_address; | ||
| 129 | gssx_buffer application_data; | ||
| 130 | }; | ||
| 131 | |||
| 132 | |||
| 133 | /* This structure is not defined in the protocol. | ||
| 134 | * It is used in the kernel to carry around a big buffer | ||
| 135 | * as a set of pages */ | ||
| 136 | struct gssp_in_token { | ||
| 137 | struct page **pages; /* Array of contiguous pages */ | ||
| 138 | unsigned int page_base; /* Start of page data */ | ||
| 139 | unsigned int page_len; /* Length of page data */ | ||
| 140 | }; | ||
| 141 | |||
| 142 | struct gssx_arg_accept_sec_context { | ||
| 143 | struct gssx_call_ctx call_ctx; | ||
| 144 | struct gssx_ctx *context_handle; | ||
| 145 | struct gssx_cred *cred_handle; | ||
| 146 | struct gssp_in_token input_token; | ||
| 147 | struct gssx_cb *input_cb; | ||
| 148 | u32 ret_deleg_cred; | ||
| 149 | struct gssx_option_array options; | ||
| 150 | }; | ||
| 151 | |||
| 152 | struct gssx_res_accept_sec_context { | ||
| 153 | struct gssx_status status; | ||
| 154 | struct gssx_ctx *context_handle; | ||
| 155 | gssx_buffer *output_token; | ||
| 156 | /* struct gssx_cred *delegated_cred_handle; not used in kernel */ | ||
| 157 | struct gssx_option_array options; | ||
| 158 | }; | ||
| 159 | |||
| 160 | |||
| 161 | |||
| 162 | #define gssx_enc_indicate_mechs NULL | ||
| 163 | #define gssx_dec_indicate_mechs NULL | ||
| 164 | #define gssx_enc_get_call_context NULL | ||
| 165 | #define gssx_dec_get_call_context NULL | ||
| 166 | #define gssx_enc_import_and_canon_name NULL | ||
| 167 | #define gssx_dec_import_and_canon_name NULL | ||
| 168 | #define gssx_enc_export_cred NULL | ||
| 169 | #define gssx_dec_export_cred NULL | ||
| 170 | #define gssx_enc_import_cred NULL | ||
| 171 | #define gssx_dec_import_cred NULL | ||
| 172 | #define gssx_enc_acquire_cred NULL | ||
| 173 | #define gssx_dec_acquire_cred NULL | ||
| 174 | #define gssx_enc_store_cred NULL | ||
| 175 | #define gssx_dec_store_cred NULL | ||
| 176 | #define gssx_enc_init_sec_context NULL | ||
| 177 | #define gssx_dec_init_sec_context NULL | ||
| 178 | void gssx_enc_accept_sec_context(struct rpc_rqst *req, | ||
| 179 | struct xdr_stream *xdr, | ||
| 180 | struct gssx_arg_accept_sec_context *args); | ||
| 181 | int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp, | ||
| 182 | struct xdr_stream *xdr, | ||
| 183 | struct gssx_res_accept_sec_context *res); | ||
| 184 | #define gssx_enc_release_handle NULL | ||
| 185 | #define gssx_dec_release_handle NULL | ||
| 186 | #define gssx_enc_get_mic NULL | ||
| 187 | #define gssx_dec_get_mic NULL | ||
| 188 | #define gssx_enc_verify NULL | ||
| 189 | #define gssx_dec_verify NULL | ||
| 190 | #define gssx_enc_wrap NULL | ||
| 191 | #define gssx_dec_wrap NULL | ||
| 192 | #define gssx_enc_unwrap NULL | ||
| 193 | #define gssx_dec_unwrap NULL | ||
| 194 | #define gssx_enc_wrap_size_limit NULL | ||
| 195 | #define gssx_dec_wrap_size_limit NULL | ||
| 196 | |||
| 197 | /* non implemented calls are set to 0 size */ | ||
| 198 | #define GSSX_ARG_indicate_mechs_sz 0 | ||
| 199 | #define GSSX_RES_indicate_mechs_sz 0 | ||
| 200 | #define GSSX_ARG_get_call_context_sz 0 | ||
| 201 | #define GSSX_RES_get_call_context_sz 0 | ||
| 202 | #define GSSX_ARG_import_and_canon_name_sz 0 | ||
| 203 | #define GSSX_RES_import_and_canon_name_sz 0 | ||
| 204 | #define GSSX_ARG_export_cred_sz 0 | ||
| 205 | #define GSSX_RES_export_cred_sz 0 | ||
| 206 | #define GSSX_ARG_import_cred_sz 0 | ||
| 207 | #define GSSX_RES_import_cred_sz 0 | ||
| 208 | #define GSSX_ARG_acquire_cred_sz 0 | ||
| 209 | #define GSSX_RES_acquire_cred_sz 0 | ||
| 210 | #define GSSX_ARG_store_cred_sz 0 | ||
| 211 | #define GSSX_RES_store_cred_sz 0 | ||
| 212 | #define GSSX_ARG_init_sec_context_sz 0 | ||
| 213 | #define GSSX_RES_init_sec_context_sz 0 | ||
| 214 | |||
| 215 | #define GSSX_default_in_call_ctx_sz (4 + 4 + 4 + \ | ||
| 216 | 8 + sizeof(LUCID_OPTION) + sizeof(LUCID_VALUE) + \ | ||
| 217 | 8 + sizeof(CREDS_OPTION) + sizeof(CREDS_VALUE)) | ||
| 218 | #define GSSX_default_in_ctx_hndl_sz (4 + 4+8 + 4 + 4 + 6*4 + 6*4 + 8 + 8 + \ | ||
| 219 | 4 + 4 + 4) | ||
| 220 | #define GSSX_default_in_cred_sz 4 /* we send in no cred_handle */ | ||
| 221 | #define GSSX_default_in_token_sz 4 /* does *not* include token data */ | ||
| 222 | #define GSSX_default_in_cb_sz 4 /* we do not use channel bindings */ | ||
| 223 | #define GSSX_ARG_accept_sec_context_sz (GSSX_default_in_call_ctx_sz + \ | ||
| 224 | GSSX_default_in_ctx_hndl_sz + \ | ||
| 225 | GSSX_default_in_cred_sz + \ | ||
| 226 | GSSX_default_in_token_sz + \ | ||
| 227 | GSSX_default_in_cb_sz + \ | ||
| 228 | 4 /* no deleg creds boolean */ + \ | ||
| 229 | 4) /* empty options */ | ||
| 230 | |||
| 231 | /* somewhat arbitrary numbers but large enough (we ignore some of the data | ||
| 232 | * sent down, but it is part of the protocol so we need enough space to take | ||
| 233 | * it in) */ | ||
| 234 | #define GSSX_default_status_sz 8 + 24 + 8 + 256 + 256 + 16 + 4 | ||
| 235 | #define GSSX_max_output_handle_sz 128 | ||
| 236 | #define GSSX_max_oid_sz 16 | ||
| 237 | #define GSSX_max_princ_sz 256 | ||
| 238 | #define GSSX_default_ctx_sz (GSSX_max_output_handle_sz + \ | ||
| 239 | 16 + 4 + GSSX_max_oid_sz + \ | ||
| 240 | 2 * GSSX_max_princ_sz + \ | ||
| 241 | 8 + 8 + 4 + 4 + 4) | ||
| 242 | #define GSSX_max_output_token_sz 1024 | ||
| 243 | #define GSSX_max_creds_sz (4 + 4 + 4 + NGROUPS_MAX * 4) | ||
| 244 | #define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \ | ||
| 245 | GSSX_default_ctx_sz + \ | ||
| 246 | GSSX_max_output_token_sz + \ | ||
| 247 | 4 + GSSX_max_creds_sz) | ||
| 248 | |||
| 249 | #define GSSX_ARG_release_handle_sz 0 | ||
| 250 | #define GSSX_RES_release_handle_sz 0 | ||
| 251 | #define GSSX_ARG_get_mic_sz 0 | ||
| 252 | #define GSSX_RES_get_mic_sz 0 | ||
| 253 | #define GSSX_ARG_verify_sz 0 | ||
| 254 | #define GSSX_RES_verify_sz 0 | ||
| 255 | #define GSSX_ARG_wrap_sz 0 | ||
| 256 | #define GSSX_RES_wrap_sz 0 | ||
| 257 | #define GSSX_ARG_unwrap_sz 0 | ||
| 258 | #define GSSX_RES_unwrap_sz 0 | ||
| 259 | #define GSSX_ARG_wrap_size_limit_sz 0 | ||
| 260 | #define GSSX_RES_wrap_size_limit_sz 0 | ||
| 261 | |||
| 262 | |||
| 263 | |||
| 264 | #endif /* _LINUX_GSS_RPC_XDR_H */ | ||
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index c3ba570222dc..871c73c92165 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c | |||
| @@ -48,8 +48,8 @@ | |||
| 48 | #include <linux/sunrpc/svcauth.h> | 48 | #include <linux/sunrpc/svcauth.h> |
| 49 | #include <linux/sunrpc/svcauth_gss.h> | 49 | #include <linux/sunrpc/svcauth_gss.h> |
| 50 | #include <linux/sunrpc/cache.h> | 50 | #include <linux/sunrpc/cache.h> |
| 51 | #include "gss_rpc_upcall.h" | ||
| 51 | 52 | ||
| 52 | #include "../netns.h" | ||
| 53 | 53 | ||
| 54 | #ifdef RPC_DEBUG | 54 | #ifdef RPC_DEBUG |
| 55 | # define RPCDBG_FACILITY RPCDBG_AUTH | 55 | # define RPCDBG_FACILITY RPCDBG_AUTH |
| @@ -497,7 +497,8 @@ static int rsc_parse(struct cache_detail *cd, | |||
| 497 | len = qword_get(&mesg, buf, mlen); | 497 | len = qword_get(&mesg, buf, mlen); |
| 498 | if (len < 0) | 498 | if (len < 0) |
| 499 | goto out; | 499 | goto out; |
| 500 | status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, GFP_KERNEL); | 500 | status = gss_import_sec_context(buf, len, gm, &rsci.mechctx, |
| 501 | NULL, GFP_KERNEL); | ||
| 501 | if (status) | 502 | if (status) |
| 502 | goto out; | 503 | goto out; |
| 503 | 504 | ||
| @@ -505,8 +506,10 @@ static int rsc_parse(struct cache_detail *cd, | |||
| 505 | len = qword_get(&mesg, buf, mlen); | 506 | len = qword_get(&mesg, buf, mlen); |
| 506 | if (len > 0) { | 507 | if (len > 0) { |
| 507 | rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); | 508 | rsci.cred.cr_principal = kstrdup(buf, GFP_KERNEL); |
| 508 | if (!rsci.cred.cr_principal) | 509 | if (!rsci.cred.cr_principal) { |
| 510 | status = -ENOMEM; | ||
| 509 | goto out; | 511 | goto out; |
| 512 | } | ||
| 510 | } | 513 | } |
| 511 | 514 | ||
| 512 | } | 515 | } |
| @@ -987,13 +990,10 @@ gss_write_init_verf(struct cache_detail *cd, struct svc_rqst *rqstp, | |||
| 987 | } | 990 | } |
| 988 | 991 | ||
| 989 | static inline int | 992 | static inline int |
| 990 | gss_read_verf(struct rpc_gss_wire_cred *gc, | 993 | gss_read_common_verf(struct rpc_gss_wire_cred *gc, |
| 991 | struct kvec *argv, __be32 *authp, | 994 | struct kvec *argv, __be32 *authp, |
| 992 | struct xdr_netobj *in_handle, | 995 | struct xdr_netobj *in_handle) |
| 993 | struct xdr_netobj *in_token) | ||
| 994 | { | 996 | { |
| 995 | struct xdr_netobj tmpobj; | ||
| 996 | |||
| 997 | /* Read the verifier; should be NULL: */ | 997 | /* Read the verifier; should be NULL: */ |
| 998 | *authp = rpc_autherr_badverf; | 998 | *authp = rpc_autherr_badverf; |
| 999 | if (argv->iov_len < 2 * 4) | 999 | if (argv->iov_len < 2 * 4) |
| @@ -1009,6 +1009,23 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, | |||
| 1009 | if (dup_netobj(in_handle, &gc->gc_ctx)) | 1009 | if (dup_netobj(in_handle, &gc->gc_ctx)) |
| 1010 | return SVC_CLOSE; | 1010 | return SVC_CLOSE; |
| 1011 | *authp = rpc_autherr_badverf; | 1011 | *authp = rpc_autherr_badverf; |
| 1012 | |||
| 1013 | return 0; | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | static inline int | ||
| 1017 | gss_read_verf(struct rpc_gss_wire_cred *gc, | ||
| 1018 | struct kvec *argv, __be32 *authp, | ||
| 1019 | struct xdr_netobj *in_handle, | ||
| 1020 | struct xdr_netobj *in_token) | ||
| 1021 | { | ||
| 1022 | struct xdr_netobj tmpobj; | ||
| 1023 | int res; | ||
| 1024 | |||
| 1025 | res = gss_read_common_verf(gc, argv, authp, in_handle); | ||
| 1026 | if (res) | ||
| 1027 | return res; | ||
| 1028 | |||
| 1012 | if (svc_safe_getnetobj(argv, &tmpobj)) { | 1029 | if (svc_safe_getnetobj(argv, &tmpobj)) { |
| 1013 | kfree(in_handle->data); | 1030 | kfree(in_handle->data); |
| 1014 | return SVC_DENIED; | 1031 | return SVC_DENIED; |
| @@ -1021,6 +1038,40 @@ gss_read_verf(struct rpc_gss_wire_cred *gc, | |||
| 1021 | return 0; | 1038 | return 0; |
| 1022 | } | 1039 | } |
| 1023 | 1040 | ||
| 1041 | /* Ok this is really heavily depending on a set of semantics in | ||
| 1042 | * how rqstp is set up by svc_recv and pages laid down by the | ||
| 1043 | * server when reading a request. We are basically guaranteed that | ||
| 1044 | * the token lays all down linearly across a set of pages, starting | ||
| 1045 | * at iov_base in rq_arg.head[0] which happens to be the first of a | ||
| 1046 | * set of pages stored in rq_pages[]. | ||
| 1047 | * rq_arg.head[0].iov_base will provide us the page_base to pass | ||
| 1048 | * to the upcall. | ||
| 1049 | */ | ||
| 1050 | static inline int | ||
| 1051 | gss_read_proxy_verf(struct svc_rqst *rqstp, | ||
| 1052 | struct rpc_gss_wire_cred *gc, __be32 *authp, | ||
| 1053 | struct xdr_netobj *in_handle, | ||
| 1054 | struct gssp_in_token *in_token) | ||
| 1055 | { | ||
| 1056 | struct kvec *argv = &rqstp->rq_arg.head[0]; | ||
| 1057 | u32 inlen; | ||
| 1058 | int res; | ||
| 1059 | |||
| 1060 | res = gss_read_common_verf(gc, argv, authp, in_handle); | ||
| 1061 | if (res) | ||
| 1062 | return res; | ||
| 1063 | |||
| 1064 | inlen = svc_getnl(argv); | ||
| 1065 | if (inlen > (argv->iov_len + rqstp->rq_arg.page_len)) | ||
| 1066 | return SVC_DENIED; | ||
| 1067 | |||
| 1068 | in_token->pages = rqstp->rq_pages; | ||
| 1069 | in_token->page_base = (ulong)argv->iov_base & ~PAGE_MASK; | ||
| 1070 | in_token->page_len = inlen; | ||
| 1071 | |||
| 1072 | return 0; | ||
| 1073 | } | ||
| 1074 | |||
| 1024 | static inline int | 1075 | static inline int |
| 1025 | gss_write_resv(struct kvec *resv, size_t size_limit, | 1076 | gss_write_resv(struct kvec *resv, size_t size_limit, |
| 1026 | struct xdr_netobj *out_handle, struct xdr_netobj *out_token, | 1077 | struct xdr_netobj *out_handle, struct xdr_netobj *out_token, |
| @@ -1048,7 +1099,7 @@ gss_write_resv(struct kvec *resv, size_t size_limit, | |||
| 1048 | * the upcall results are available, write the verifier and result. | 1099 | * the upcall results are available, write the verifier and result. |
| 1049 | * Otherwise, drop the request pending an answer to the upcall. | 1100 | * Otherwise, drop the request pending an answer to the upcall. |
| 1050 | */ | 1101 | */ |
| 1051 | static int svcauth_gss_handle_init(struct svc_rqst *rqstp, | 1102 | static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, |
| 1052 | struct rpc_gss_wire_cred *gc, __be32 *authp) | 1103 | struct rpc_gss_wire_cred *gc, __be32 *authp) |
| 1053 | { | 1104 | { |
| 1054 | struct kvec *argv = &rqstp->rq_arg.head[0]; | 1105 | struct kvec *argv = &rqstp->rq_arg.head[0]; |
| @@ -1088,6 +1139,287 @@ out: | |||
| 1088 | return ret; | 1139 | return ret; |
| 1089 | } | 1140 | } |
| 1090 | 1141 | ||
| 1142 | static int gss_proxy_save_rsc(struct cache_detail *cd, | ||
| 1143 | struct gssp_upcall_data *ud, | ||
| 1144 | uint64_t *handle) | ||
| 1145 | { | ||
| 1146 | struct rsc rsci, *rscp = NULL; | ||
| 1147 | static atomic64_t ctxhctr; | ||
| 1148 | long long ctxh; | ||
| 1149 | struct gss_api_mech *gm = NULL; | ||
| 1150 | time_t expiry; | ||
| 1151 | int status = -EINVAL; | ||
| 1152 | |||
| 1153 | memset(&rsci, 0, sizeof(rsci)); | ||
| 1154 | /* context handle */ | ||
| 1155 | status = -ENOMEM; | ||
| 1156 | /* the handle needs to be just a unique id, | ||
| 1157 | * use a static counter */ | ||
| 1158 | ctxh = atomic64_inc_return(&ctxhctr); | ||
| 1159 | |||
| 1160 | /* make a copy for the caller */ | ||
| 1161 | *handle = ctxh; | ||
| 1162 | |||
| 1163 | /* make a copy for the rsc cache */ | ||
| 1164 | if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t))) | ||
| 1165 | goto out; | ||
| 1166 | rscp = rsc_lookup(cd, &rsci); | ||
| 1167 | if (!rscp) | ||
| 1168 | goto out; | ||
| 1169 | |||
| 1170 | /* creds */ | ||
| 1171 | if (!ud->found_creds) { | ||
| 1172 | /* userspace seem buggy, we should always get at least a | ||
| 1173 | * mapping to nobody */ | ||
| 1174 | dprintk("RPC: No creds found, marking Negative!\n"); | ||
| 1175 | set_bit(CACHE_NEGATIVE, &rsci.h.flags); | ||
| 1176 | } else { | ||
| 1177 | |||
| 1178 | /* steal creds */ | ||
| 1179 | rsci.cred = ud->creds; | ||
| 1180 | memset(&ud->creds, 0, sizeof(struct svc_cred)); | ||
| 1181 | |||
| 1182 | status = -EOPNOTSUPP; | ||
| 1183 | /* get mech handle from OID */ | ||
| 1184 | gm = gss_mech_get_by_OID(&ud->mech_oid); | ||
| 1185 | if (!gm) | ||
| 1186 | goto out; | ||
| 1187 | |||
| 1188 | status = -EINVAL; | ||
| 1189 | /* mech-specific data: */ | ||
| 1190 | status = gss_import_sec_context(ud->out_handle.data, | ||
| 1191 | ud->out_handle.len, | ||
| 1192 | gm, &rsci.mechctx, | ||
| 1193 | &expiry, GFP_KERNEL); | ||
| 1194 | if (status) | ||
| 1195 | goto out; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | rsci.h.expiry_time = expiry; | ||
| 1199 | rscp = rsc_update(cd, &rsci, rscp); | ||
| 1200 | status = 0; | ||
| 1201 | out: | ||
| 1202 | gss_mech_put(gm); | ||
| 1203 | rsc_free(&rsci); | ||
| 1204 | if (rscp) | ||
| 1205 | cache_put(&rscp->h, cd); | ||
| 1206 | else | ||
| 1207 | status = -ENOMEM; | ||
| 1208 | return status; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, | ||
| 1212 | struct rpc_gss_wire_cred *gc, __be32 *authp) | ||
| 1213 | { | ||
| 1214 | struct kvec *resv = &rqstp->rq_res.head[0]; | ||
| 1215 | struct xdr_netobj cli_handle; | ||
| 1216 | struct gssp_upcall_data ud; | ||
| 1217 | uint64_t handle; | ||
| 1218 | int status; | ||
| 1219 | int ret; | ||
| 1220 | struct net *net = rqstp->rq_xprt->xpt_net; | ||
| 1221 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1222 | |||
| 1223 | memset(&ud, 0, sizeof(ud)); | ||
| 1224 | ret = gss_read_proxy_verf(rqstp, gc, authp, | ||
| 1225 | &ud.in_handle, &ud.in_token); | ||
| 1226 | if (ret) | ||
| 1227 | return ret; | ||
| 1228 | |||
| 1229 | ret = SVC_CLOSE; | ||
| 1230 | |||
| 1231 | /* Perform synchronous upcall to gss-proxy */ | ||
| 1232 | status = gssp_accept_sec_context_upcall(net, &ud); | ||
| 1233 | if (status) | ||
| 1234 | goto out; | ||
| 1235 | |||
| 1236 | dprintk("RPC: svcauth_gss: gss major status = %d\n", | ||
| 1237 | ud.major_status); | ||
| 1238 | |||
| 1239 | switch (ud.major_status) { | ||
| 1240 | case GSS_S_CONTINUE_NEEDED: | ||
| 1241 | cli_handle = ud.out_handle; | ||
| 1242 | break; | ||
| 1243 | case GSS_S_COMPLETE: | ||
| 1244 | status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle); | ||
| 1245 | if (status) | ||
| 1246 | goto out; | ||
| 1247 | cli_handle.data = (u8 *)&handle; | ||
| 1248 | cli_handle.len = sizeof(handle); | ||
| 1249 | break; | ||
| 1250 | default: | ||
| 1251 | ret = SVC_CLOSE; | ||
| 1252 | goto out; | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | /* Got an answer to the upcall; use it: */ | ||
| 1256 | if (gss_write_init_verf(sn->rsc_cache, rqstp, | ||
| 1257 | &cli_handle, &ud.major_status)) | ||
| 1258 | goto out; | ||
| 1259 | if (gss_write_resv(resv, PAGE_SIZE, | ||
| 1260 | &cli_handle, &ud.out_token, | ||
| 1261 | ud.major_status, ud.minor_status)) | ||
| 1262 | goto out; | ||
| 1263 | |||
| 1264 | ret = SVC_COMPLETE; | ||
| 1265 | out: | ||
| 1266 | gssp_free_upcall_data(&ud); | ||
| 1267 | return ret; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | DEFINE_SPINLOCK(use_gssp_lock); | ||
| 1271 | |||
| 1272 | static bool use_gss_proxy(struct net *net) | ||
| 1273 | { | ||
| 1274 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1275 | |||
| 1276 | if (sn->use_gss_proxy != -1) | ||
| 1277 | return sn->use_gss_proxy; | ||
| 1278 | spin_lock(&use_gssp_lock); | ||
| 1279 | /* | ||
| 1280 | * If you wanted gss-proxy, you should have said so before | ||
| 1281 | * starting to accept requests: | ||
| 1282 | */ | ||
| 1283 | sn->use_gss_proxy = 0; | ||
| 1284 | spin_unlock(&use_gssp_lock); | ||
| 1285 | return 0; | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | #ifdef CONFIG_PROC_FS | ||
| 1289 | |||
| 1290 | static bool set_gss_proxy(struct net *net, int type) | ||
| 1291 | { | ||
| 1292 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1293 | int ret = 0; | ||
| 1294 | |||
| 1295 | WARN_ON_ONCE(type != 0 && type != 1); | ||
| 1296 | spin_lock(&use_gssp_lock); | ||
| 1297 | if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type) | ||
| 1298 | sn->use_gss_proxy = type; | ||
| 1299 | else | ||
| 1300 | ret = -EBUSY; | ||
| 1301 | spin_unlock(&use_gssp_lock); | ||
| 1302 | wake_up(&sn->gssp_wq); | ||
| 1303 | return ret; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | static inline bool gssp_ready(struct sunrpc_net *sn) | ||
| 1307 | { | ||
| 1308 | switch (sn->use_gss_proxy) { | ||
| 1309 | case -1: | ||
| 1310 | return false; | ||
| 1311 | case 0: | ||
| 1312 | return true; | ||
| 1313 | case 1: | ||
| 1314 | return sn->gssp_clnt; | ||
| 1315 | } | ||
| 1316 | WARN_ON_ONCE(1); | ||
| 1317 | return false; | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | static int wait_for_gss_proxy(struct net *net) | ||
| 1321 | { | ||
| 1322 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1323 | |||
| 1324 | return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn)); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | |||
| 1328 | static ssize_t write_gssp(struct file *file, const char __user *buf, | ||
| 1329 | size_t count, loff_t *ppos) | ||
| 1330 | { | ||
| 1331 | struct net *net = PDE_DATA(file->f_path.dentry->d_inode); | ||
| 1332 | char tbuf[20]; | ||
| 1333 | unsigned long i; | ||
| 1334 | int res; | ||
| 1335 | |||
| 1336 | if (*ppos || count > sizeof(tbuf)-1) | ||
| 1337 | return -EINVAL; | ||
| 1338 | if (copy_from_user(tbuf, buf, count)) | ||
| 1339 | return -EFAULT; | ||
| 1340 | |||
| 1341 | tbuf[count] = 0; | ||
| 1342 | res = kstrtoul(tbuf, 0, &i); | ||
| 1343 | if (res) | ||
| 1344 | return res; | ||
| 1345 | if (i != 1) | ||
| 1346 | return -EINVAL; | ||
| 1347 | res = set_gss_proxy(net, 1); | ||
| 1348 | if (res) | ||
| 1349 | return res; | ||
| 1350 | res = set_gssp_clnt(net); | ||
| 1351 | if (res) | ||
| 1352 | return res; | ||
| 1353 | return count; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | static ssize_t read_gssp(struct file *file, char __user *buf, | ||
| 1357 | size_t count, loff_t *ppos) | ||
| 1358 | { | ||
| 1359 | struct net *net = PDE_DATA(file->f_path.dentry->d_inode); | ||
| 1360 | unsigned long p = *ppos; | ||
| 1361 | char tbuf[10]; | ||
| 1362 | size_t len; | ||
| 1363 | int ret; | ||
| 1364 | |||
| 1365 | ret = wait_for_gss_proxy(net); | ||
| 1366 | if (ret) | ||
| 1367 | return ret; | ||
| 1368 | |||
| 1369 | snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net)); | ||
| 1370 | len = strlen(tbuf); | ||
| 1371 | if (p >= len) | ||
| 1372 | return 0; | ||
| 1373 | len -= p; | ||
| 1374 | if (len > count) | ||
| 1375 | len = count; | ||
| 1376 | if (copy_to_user(buf, (void *)(tbuf+p), len)) | ||
| 1377 | return -EFAULT; | ||
| 1378 | *ppos += len; | ||
| 1379 | return len; | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | static const struct file_operations use_gss_proxy_ops = { | ||
| 1383 | .open = nonseekable_open, | ||
| 1384 | .write = write_gssp, | ||
| 1385 | .read = read_gssp, | ||
| 1386 | }; | ||
| 1387 | |||
| 1388 | static int create_use_gss_proxy_proc_entry(struct net *net) | ||
| 1389 | { | ||
| 1390 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1391 | struct proc_dir_entry **p = &sn->use_gssp_proc; | ||
| 1392 | |||
| 1393 | sn->use_gss_proxy = -1; | ||
| 1394 | *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR, | ||
| 1395 | sn->proc_net_rpc, | ||
| 1396 | &use_gss_proxy_ops, net); | ||
| 1397 | if (!*p) | ||
| 1398 | return -ENOMEM; | ||
| 1399 | init_gssp_clnt(sn); | ||
| 1400 | return 0; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | static void destroy_use_gss_proxy_proc_entry(struct net *net) | ||
| 1404 | { | ||
| 1405 | struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); | ||
| 1406 | |||
| 1407 | if (sn->use_gssp_proc) { | ||
| 1408 | remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); | ||
| 1409 | clear_gssp_clnt(sn); | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | #else /* CONFIG_PROC_FS */ | ||
| 1413 | |||
| 1414 | static int create_use_gss_proxy_proc_entry(struct net *net) | ||
| 1415 | { | ||
| 1416 | return 0; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | static void destroy_use_gss_proxy_proc_entry(struct net *net) {} | ||
| 1420 | |||
| 1421 | #endif /* CONFIG_PROC_FS */ | ||
| 1422 | |||
| 1091 | /* | 1423 | /* |
| 1092 | * Accept an rpcsec packet. | 1424 | * Accept an rpcsec packet. |
| 1093 | * If context establishment, punt to user space | 1425 | * If context establishment, punt to user space |
| @@ -1154,7 +1486,10 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) | |||
| 1154 | switch (gc->gc_proc) { | 1486 | switch (gc->gc_proc) { |
| 1155 | case RPC_GSS_PROC_INIT: | 1487 | case RPC_GSS_PROC_INIT: |
| 1156 | case RPC_GSS_PROC_CONTINUE_INIT: | 1488 | case RPC_GSS_PROC_CONTINUE_INIT: |
| 1157 | return svcauth_gss_handle_init(rqstp, gc, authp); | 1489 | if (use_gss_proxy(SVC_NET(rqstp))) |
| 1490 | return svcauth_gss_proxy_init(rqstp, gc, authp); | ||
| 1491 | else | ||
| 1492 | return svcauth_gss_legacy_init(rqstp, gc, authp); | ||
| 1158 | case RPC_GSS_PROC_DATA: | 1493 | case RPC_GSS_PROC_DATA: |
| 1159 | case RPC_GSS_PROC_DESTROY: | 1494 | case RPC_GSS_PROC_DESTROY: |
| 1160 | /* Look up the context, and check the verifier: */ | 1495 | /* Look up the context, and check the verifier: */ |
| @@ -1531,7 +1866,12 @@ gss_svc_init_net(struct net *net) | |||
| 1531 | rv = rsi_cache_create_net(net); | 1866 | rv = rsi_cache_create_net(net); |
| 1532 | if (rv) | 1867 | if (rv) |
| 1533 | goto out1; | 1868 | goto out1; |
| 1869 | rv = create_use_gss_proxy_proc_entry(net); | ||
| 1870 | if (rv) | ||
| 1871 | goto out2; | ||
| 1534 | return 0; | 1872 | return 0; |
| 1873 | out2: | ||
| 1874 | destroy_use_gss_proxy_proc_entry(net); | ||
| 1535 | out1: | 1875 | out1: |
| 1536 | rsc_cache_destroy_net(net); | 1876 | rsc_cache_destroy_net(net); |
| 1537 | return rv; | 1877 | return rv; |
| @@ -1540,6 +1880,7 @@ out1: | |||
| 1540 | void | 1880 | void |
| 1541 | gss_svc_shutdown_net(struct net *net) | 1881 | gss_svc_shutdown_net(struct net *net) |
| 1542 | { | 1882 | { |
| 1883 | destroy_use_gss_proxy_proc_entry(net); | ||
| 1543 | rsi_cache_destroy_net(net); | 1884 | rsi_cache_destroy_net(net); |
| 1544 | rsc_cache_destroy_net(net); | 1885 | rsc_cache_destroy_net(net); |
| 1545 | } | 1886 | } |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index f1889be80912..80fe5c86efd1 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
| @@ -986,8 +986,10 @@ static int cache_open(struct inode *inode, struct file *filp, | |||
| 986 | nonseekable_open(inode, filp); | 986 | nonseekable_open(inode, filp); |
| 987 | if (filp->f_mode & FMODE_READ) { | 987 | if (filp->f_mode & FMODE_READ) { |
| 988 | rp = kmalloc(sizeof(*rp), GFP_KERNEL); | 988 | rp = kmalloc(sizeof(*rp), GFP_KERNEL); |
| 989 | if (!rp) | 989 | if (!rp) { |
| 990 | module_put(cd->owner); | ||
| 990 | return -ENOMEM; | 991 | return -ENOMEM; |
| 992 | } | ||
| 991 | rp->offset = 0; | 993 | rp->offset = 0; |
| 992 | rp->q.reader = 1; | 994 | rp->q.reader = 1; |
| 993 | atomic_inc(&cd->readers); | 995 | atomic_inc(&cd->readers); |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index d259fa966927..3f7930f938cc 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
| @@ -413,6 +413,8 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args) | |||
| 413 | 413 | ||
| 414 | if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS) | 414 | if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS) |
| 415 | xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS; | 415 | xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS; |
| 416 | if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT) | ||
| 417 | xprtargs.flags |= XPRT_CREATE_NO_IDLE_TIMEOUT; | ||
| 416 | /* | 418 | /* |
| 417 | * If the caller chooses not to specify a hostname, whip | 419 | * If the caller chooses not to specify a hostname, whip |
| 418 | * up a string representation of the passed-in address. | 420 | * up a string representation of the passed-in address. |
| @@ -681,6 +683,7 @@ rpc_release_client(struct rpc_clnt *clnt) | |||
| 681 | if (atomic_dec_and_test(&clnt->cl_count)) | 683 | if (atomic_dec_and_test(&clnt->cl_count)) |
| 682 | rpc_free_auth(clnt); | 684 | rpc_free_auth(clnt); |
| 683 | } | 685 | } |
| 686 | EXPORT_SYMBOL_GPL(rpc_release_client); | ||
| 684 | 687 | ||
| 685 | /** | 688 | /** |
| 686 | * rpc_bind_new_program - bind a new RPC program to an existing client | 689 | * rpc_bind_new_program - bind a new RPC program to an existing client |
diff --git a/net/sunrpc/netns.h b/net/sunrpc/netns.h index ce7bd449173d..7111a4c9113b 100644 --- a/net/sunrpc/netns.h +++ b/net/sunrpc/netns.h | |||
| @@ -23,6 +23,12 @@ struct sunrpc_net { | |||
| 23 | struct rpc_clnt *rpcb_local_clnt4; | 23 | struct rpc_clnt *rpcb_local_clnt4; |
| 24 | spinlock_t rpcb_clnt_lock; | 24 | spinlock_t rpcb_clnt_lock; |
| 25 | unsigned int rpcb_users; | 25 | unsigned int rpcb_users; |
| 26 | |||
| 27 | struct mutex gssp_lock; | ||
| 28 | wait_queue_head_t gssp_wq; | ||
| 29 | struct rpc_clnt *gssp_clnt; | ||
| 30 | int use_gss_proxy; | ||
| 31 | struct proc_dir_entry *use_gssp_proc; | ||
| 26 | }; | 32 | }; |
| 27 | 33 | ||
| 28 | extern int sunrpc_net_id; | 34 | extern int sunrpc_net_id; |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 745fca3cfd36..095363eee764 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
| @@ -1300,6 +1300,8 @@ found: | |||
| 1300 | -PTR_ERR(xprt)); | 1300 | -PTR_ERR(xprt)); |
| 1301 | goto out; | 1301 | goto out; |
| 1302 | } | 1302 | } |
| 1303 | if (args->flags & XPRT_CREATE_NO_IDLE_TIMEOUT) | ||
| 1304 | xprt->idle_timeout = 0; | ||
| 1303 | INIT_WORK(&xprt->task_cleanup, xprt_autoclose); | 1305 | INIT_WORK(&xprt->task_cleanup, xprt_autoclose); |
| 1304 | if (xprt_has_timer(xprt)) | 1306 | if (xprt_has_timer(xprt)) |
| 1305 | setup_timer(&xprt->timer, xprt_init_autodisconnect, | 1307 | setup_timer(&xprt->timer, xprt_init_autodisconnect, |
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 9c2825827dec..ffd50348a509 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
| @@ -2655,6 +2655,9 @@ static struct rpc_xprt *xs_setup_local(struct xprt_create *args) | |||
| 2655 | } | 2655 | } |
| 2656 | xprt_set_bound(xprt); | 2656 | xprt_set_bound(xprt); |
| 2657 | xs_format_peer_addresses(xprt, "local", RPCBIND_NETID_LOCAL); | 2657 | xs_format_peer_addresses(xprt, "local", RPCBIND_NETID_LOCAL); |
| 2658 | ret = ERR_PTR(xs_local_setup_socket(transport)); | ||
| 2659 | if (ret) | ||
| 2660 | goto out_err; | ||
| 2658 | break; | 2661 | break; |
| 2659 | default: | 2662 | default: |
| 2660 | ret = ERR_PTR(-EAFNOSUPPORT); | 2663 | ret = ERR_PTR(-EAFNOSUPPORT); |
