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 }; 359