| 
 | 1 | +/*  | 
 | 2 | + *  linux/net/sunrpc/gss_rpc_upcall.c  | 
 | 3 | + *  | 
 | 4 | + *  Copyright (C) 2012 Simo Sorce <[email protected]>  | 
 | 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 | +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 | +}  | 
 | 141 | + | 
 | 142 | +int set_gssp_clnt(struct net *net)  | 
 | 143 | +{  | 
 | 144 | +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  | 
 | 145 | +	struct rpc_clnt *clnt;  | 
 | 146 | +	int ret;  | 
 | 147 | + | 
 | 148 | +	mutex_lock(&sn->gssp_lock);  | 
 | 149 | +	ret = gssp_rpc_create(net, &clnt);  | 
 | 150 | +	if (!ret) {  | 
 | 151 | +		if (sn->gssp_clnt)  | 
 | 152 | +			rpc_shutdown_client(sn->gssp_clnt);  | 
 | 153 | +		sn->gssp_clnt = clnt;  | 
 | 154 | +	}  | 
 | 155 | +	mutex_unlock(&sn->gssp_lock);  | 
 | 156 | +	return ret;  | 
 | 157 | +}  | 
 | 158 | + | 
 | 159 | +void clear_gssp_clnt(struct sunrpc_net *sn)  | 
 | 160 | +{  | 
 | 161 | +	mutex_lock(&sn->gssp_lock);  | 
 | 162 | +	if (sn->gssp_clnt) {  | 
 | 163 | +		rpc_shutdown_client(sn->gssp_clnt);  | 
 | 164 | +		sn->gssp_clnt = NULL;  | 
 | 165 | +	}  | 
 | 166 | +	mutex_unlock(&sn->gssp_lock);  | 
 | 167 | +}  | 
 | 168 | + | 
 | 169 | +static struct rpc_clnt *get_gssp_clnt(struct sunrpc_net *sn)  | 
 | 170 | +{  | 
 | 171 | +	struct rpc_clnt *clnt;  | 
 | 172 | + | 
 | 173 | +	mutex_lock(&sn->gssp_lock);  | 
 | 174 | +	clnt = sn->gssp_clnt;  | 
 | 175 | +	if (clnt)  | 
 | 176 | +		atomic_inc(&clnt->cl_count);  | 
 | 177 | +	mutex_unlock(&sn->gssp_lock);  | 
 | 178 | +	return clnt;  | 
 | 179 | +}  | 
 | 180 | + | 
 | 181 | +static int gssp_call(struct net *net, struct rpc_message *msg)  | 
 | 182 | +{  | 
 | 183 | +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  | 
 | 184 | +	struct rpc_clnt *clnt;  | 
 | 185 | +	int status;  | 
 | 186 | + | 
 | 187 | +	clnt = get_gssp_clnt(sn);  | 
 | 188 | +	if (!clnt)  | 
 | 189 | +		return -EIO;  | 
 | 190 | +	status = rpc_call_sync(clnt, msg, 0);  | 
 | 191 | +	if (status < 0) {  | 
 | 192 | +		dprintk("gssp: rpc_call returned error %d\n", -status);  | 
 | 193 | +		switch (status) {  | 
 | 194 | +		case -EPROTONOSUPPORT:  | 
 | 195 | +			status = -EINVAL;  | 
 | 196 | +			break;  | 
 | 197 | +		case -ECONNREFUSED:  | 
 | 198 | +		case -ETIMEDOUT:  | 
 | 199 | +		case -ENOTCONN:  | 
 | 200 | +			status = -EAGAIN;  | 
 | 201 | +			break;  | 
 | 202 | +		case -ERESTARTSYS:  | 
 | 203 | +			if (signalled ())  | 
 | 204 | +				status = -EINTR;  | 
 | 205 | +			break;  | 
 | 206 | +		default:  | 
 | 207 | +			break;  | 
 | 208 | +		}  | 
 | 209 | +	}  | 
 | 210 | +	rpc_release_client(clnt);  | 
 | 211 | +	return status;  | 
 | 212 | +}  | 
 | 213 | + | 
 | 214 | + | 
 | 215 | +/*  | 
 | 216 | + * Public functions  | 
 | 217 | + */  | 
 | 218 | + | 
 | 219 | +/* numbers somewhat arbitrary but large enough for current needs */  | 
 | 220 | +#define GSSX_MAX_OUT_HANDLE	128  | 
 | 221 | +#define GSSX_MAX_MECH_OID	16  | 
 | 222 | +#define GSSX_MAX_SRC_PRINC	256  | 
 | 223 | +#define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \  | 
 | 224 | +			GSSX_max_oid_sz + \  | 
 | 225 | +			GSSX_max_princ_sz + \  | 
 | 226 | +			sizeof(struct svc_cred))  | 
 | 227 | + | 
 | 228 | +int gssp_accept_sec_context_upcall(struct net *net,  | 
 | 229 | +				struct gssp_upcall_data *data)  | 
 | 230 | +{  | 
 | 231 | +	struct gssx_ctx ctxh = {  | 
 | 232 | +		.state = data->in_handle  | 
 | 233 | +	};  | 
 | 234 | +	struct gssx_arg_accept_sec_context arg = {  | 
 | 235 | +		.input_token = data->in_token,  | 
 | 236 | +	};  | 
 | 237 | +	struct gssx_ctx rctxh = {  | 
 | 238 | +		/*  | 
 | 239 | +		 * pass in the max length we expect for each of these  | 
 | 240 | +		 * buffers but let the xdr code kmalloc them:  | 
 | 241 | +		 */  | 
 | 242 | +		.exported_context_token.len = GSSX_max_output_handle_sz,  | 
 | 243 | +		.mech.len = GSSX_max_oid_sz,  | 
 | 244 | +		.src_name.display_name.len = GSSX_max_princ_sz  | 
 | 245 | +	};  | 
 | 246 | +	struct gssx_res_accept_sec_context res = {  | 
 | 247 | +		.context_handle = &rctxh,  | 
 | 248 | +		.output_token = &data->out_token  | 
 | 249 | +	};  | 
 | 250 | +	struct rpc_message msg = {  | 
 | 251 | +		.rpc_proc = &gssp_procedures[GSSX_ACCEPT_SEC_CONTEXT],  | 
 | 252 | +		.rpc_argp = &arg,  | 
 | 253 | +		.rpc_resp = &res,  | 
 | 254 | +		.rpc_cred = NULL, /* FIXME ? */  | 
 | 255 | +	};  | 
 | 256 | +	struct xdr_netobj client_name = { 0 , NULL };  | 
 | 257 | +	int ret;  | 
 | 258 | + | 
 | 259 | +	if (data->in_handle.len != 0)  | 
 | 260 | +		arg.context_handle = &ctxh;  | 
 | 261 | +	res.output_token->len = GSSX_max_output_token_sz;  | 
 | 262 | + | 
 | 263 | +	/* use nfs/ for targ_name ? */  | 
 | 264 | + | 
 | 265 | +	ret = gssp_call(net, &msg);  | 
 | 266 | + | 
 | 267 | +	/* we need to fetch all data even in case of error so  | 
 | 268 | +	 * that we can free special strctures is they have been allocated */  | 
 | 269 | +	data->major_status = res.status.major_status;  | 
 | 270 | +	data->minor_status = res.status.minor_status;  | 
 | 271 | +	if (res.context_handle) {  | 
 | 272 | +		data->out_handle = rctxh.exported_context_token;  | 
 | 273 | +		data->mech_oid = rctxh.mech;  | 
 | 274 | +		client_name = rctxh.src_name.display_name;  | 
 | 275 | +	}  | 
 | 276 | + | 
 | 277 | +	if (res.options.count == 1) {  | 
 | 278 | +		gssx_buffer *value = &res.options.data[0].value;  | 
 | 279 | +		/* Currently we only decode CREDS_VALUE, if we add  | 
 | 280 | +		 * anything else we'll have to loop and match on the  | 
 | 281 | +		 * option name */  | 
 | 282 | +		if (value->len == 1) {  | 
 | 283 | +			/* steal group info from struct svc_cred */  | 
 | 284 | +			data->creds = *(struct svc_cred *)value->data;  | 
 | 285 | +			data->found_creds = 1;  | 
 | 286 | +		}  | 
 | 287 | +		/* whether we use it or not, free data */  | 
 | 288 | +		kfree(value->data);  | 
 | 289 | +	}  | 
 | 290 | + | 
 | 291 | +	if (res.options.count != 0) {  | 
 | 292 | +		kfree(res.options.data);  | 
 | 293 | +	}  | 
 | 294 | + | 
 | 295 | +	/* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */  | 
 | 296 | +	if (data->found_creds && client_name.data != NULL) {  | 
 | 297 | +		char *c;  | 
 | 298 | + | 
 | 299 | +		data->creds.cr_principal = kstrndup(client_name.data,  | 
 | 300 | +						client_name.len, GFP_KERNEL);  | 
 | 301 | +		if (data->creds.cr_principal) {  | 
 | 302 | +			/* terminate and remove realm part */  | 
 | 303 | +			c = strchr(data->creds.cr_principal, '@');  | 
 | 304 | +			if (c) {  | 
 | 305 | +				*c = '\0';  | 
 | 306 | + | 
 | 307 | +				/* change service-hostname delimiter */  | 
 | 308 | +				c = strchr(data->creds.cr_principal, '/');  | 
 | 309 | +				if (c) *c = '@';  | 
 | 310 | +			}  | 
 | 311 | +			if (!c) {  | 
 | 312 | +				/* not a service principal */  | 
 | 313 | +				kfree(data->creds.cr_principal);  | 
 | 314 | +				data->creds.cr_principal = NULL;  | 
 | 315 | +			}  | 
 | 316 | +		}  | 
 | 317 | +	}  | 
 | 318 | +	kfree(client_name.data);  | 
 | 319 | + | 
 | 320 | +	return ret;  | 
 | 321 | +}  | 
 | 322 | + | 
 | 323 | +void gssp_free_upcall_data(struct gssp_upcall_data *data)  | 
 | 324 | +{  | 
 | 325 | +	kfree(data->in_handle.data);  | 
 | 326 | +	kfree(data->out_handle.data);  | 
 | 327 | +	kfree(data->out_token.data);  | 
 | 328 | +	kfree(data->mech_oid.data);  | 
 | 329 | +	free_svc_cred(&data->creds);  | 
 | 330 | +}  | 
 | 331 | + | 
 | 332 | +/*  | 
 | 333 | + * Initialization stuff  | 
 | 334 | + */  | 
 | 335 | + | 
 | 336 | +static const struct rpc_version gssp_version1 = {  | 
 | 337 | +	.number		= GSSPROXY_VERS_1,  | 
 | 338 | +	.nrprocs	= ARRAY_SIZE(gssp_procedures),  | 
 | 339 | +	.procs		= gssp_procedures,  | 
 | 340 | +};  | 
 | 341 | + | 
 | 342 | +static const struct rpc_version *gssp_version[] = {  | 
 | 343 | +	NULL,  | 
 | 344 | +	&gssp_version1,  | 
 | 345 | +};  | 
 | 346 | + | 
 | 347 | +static struct rpc_stat gssp_stats;  | 
 | 348 | + | 
 | 349 | +static const struct rpc_program gssp_program = {  | 
 | 350 | +	.name		= "gssproxy",  | 
 | 351 | +	.number		= GSSPROXY_PROGRAM,  | 
 | 352 | +	.nrvers		= ARRAY_SIZE(gssp_version),  | 
 | 353 | +	.version	= gssp_version,  | 
 | 354 | +	.stats		= &gssp_stats,  | 
 | 355 | +};  | 
0 commit comments