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 static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg) 217 { 218 int i; 219 220 for (i = 0; i < arg->npages && arg->pages[i]; i++) 221 __free_page(arg->pages[i]); 222 } 223 224 static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg) 225 { 226 arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE); 227 arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL); 228 /* 229 * XXX: actual pages are allocated by xdr layer in 230 * xdr_partial_copy_from_skb. 231 */ 232 if (!arg->pages) 233 return -ENOMEM; 234 return 0; 235 } 236 237 /* 238 * Public functions 239 */ 240 241 /* numbers somewhat arbitrary but large enough for current needs */ 242 #define GSSX_MAX_OUT_HANDLE 128 243 #define GSSX_MAX_SRC_PRINC 256 244 #define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \ 245 GSSX_max_oid_sz + \ 246 GSSX_max_princ_sz + \ 247 sizeof(struct svc_cred)) 248 249 int gssp_accept_sec_context_upcall(struct net *net, 250 struct gssp_upcall_data *data) 251 { 252 struct gssx_ctx ctxh = { 253 .state = data->in_handle 254 }; 255 struct gssx_arg_accept_sec_context arg = { 256 .input_token = data->in_token, 257 }; 258 struct gssx_ctx rctxh = { 259 /* 260 * pass in the max length we expect for each of these 261 * buffers but let the xdr code kmalloc them: 262 */ 263 .exported_context_token.len = GSSX_max_output_handle_sz, 264 .mech.len = GSS_OID_MAX_LEN, 265 .src_name.display_name.len = GSSX_max_princ_sz 266 }; 267 struct gssx_res_accept_sec_context res = { 268 .context_handle = &rctxh, 269 .output_token = &data->out_token 270 }; 271 struct rpc_message msg = { 272 .rpc_proc = &gssp_procedures[GSSX_ACCEPT_SEC_CONTEXT], 273 .rpc_argp = &arg, 274 .rpc_resp = &res, 275 .rpc_cred = NULL, /* FIXME ? */ 276 }; 277 struct xdr_netobj client_name = { 0 , NULL }; 278 int ret; 279 280 if (data->in_handle.len != 0) 281 arg.context_handle = &ctxh; 282 res.output_token->len = GSSX_max_output_token_sz; 283 284 ret = gssp_alloc_receive_pages(&arg); 285 if (ret) 286 return ret; 287 288 /* use nfs/ for targ_name ? */ 289 290 ret = gssp_call(net, &msg); 291 292 gssp_free_receive_pages(&arg); 293 294 /* we need to fetch all data even in case of error so 295 * that we can free special strctures is they have been allocated */ 296 data->major_status = res.status.major_status; 297 data->minor_status = res.status.minor_status; 298 if (res.context_handle) { 299 data->out_handle = rctxh.exported_context_token; 300 data->mech_oid.len = rctxh.mech.len; 301 if (rctxh.mech.data) 302 memcpy(data->mech_oid.data, rctxh.mech.data, 303 data->mech_oid.len); 304 client_name = rctxh.src_name.display_name; 305 } 306 307 if (res.options.count == 1) { 308 gssx_buffer *value = &res.options.data[0].value; 309 /* Currently we only decode CREDS_VALUE, if we add 310 * anything else we'll have to loop and match on the 311 * option name */ 312 if (value->len == 1) { 313 /* steal group info from struct svc_cred */ 314 data->creds = *(struct svc_cred *)value->data; 315 data->found_creds = 1; 316 } 317 /* whether we use it or not, free data */ 318 kfree(value->data); 319 } 320 321 if (res.options.count != 0) { 322 kfree(res.options.data); 323 } 324 325 /* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */ 326 if (data->found_creds && client_name.data != NULL) { 327 char *c; 328 329 data->creds.cr_principal = kstrndup(client_name.data, 330 client_name.len, GFP_KERNEL); 331 if (data->creds.cr_principal) { 332 /* terminate and remove realm part */ 333 c = strchr(data->creds.cr_principal, '@'); 334 if (c) { 335 *c = '\0'; 336 337 /* change service-hostname delimiter */ 338 c = strchr(data->creds.cr_principal, '/'); 339 if (c) *c = '@'; 340 } 341 if (!c) { 342 /* not a service principal */ 343 kfree(data->creds.cr_principal); 344 data->creds.cr_principal = NULL; 345 } 346 } 347 } 348 kfree(client_name.data); 349 350 return ret; 351 } 352 353 void gssp_free_upcall_data(struct gssp_upcall_data *data) 354 { 355 kfree(data->in_handle.data); 356 kfree(data->out_handle.data); 357 kfree(data->out_token.data); 358 free_svc_cred(&data->creds); 359 } 360 361 /* 362 * Initialization stuff 363 */ 364 365 static const struct rpc_version gssp_version1 = { 366 .number = GSSPROXY_VERS_1, 367 .nrprocs = ARRAY_SIZE(gssp_procedures), 368 .procs = gssp_procedures, 369 }; 370 371 static const struct rpc_version *gssp_version[] = { 372 NULL, 373 &gssp_version1, 374 }; 375 376 static struct rpc_stat gssp_stats; 377 378 static const struct rpc_program gssp_program = { 379 .name = "gssproxy", 380 .number = GSSPROXY_PROGRAM, 381 .nrvers = ARRAY_SIZE(gssp_version), 382 .version = gssp_version, 383 .stats = &gssp_stats, 384 }; 385