Branch data Line data Source code
1 : : // SPDX-License-Identifier: BSD-3-Clause 2 : : /* 3 : : * linux/net/sunrpc/gss_mech_switch.c 4 : : * 5 : : * Copyright (c) 2001 The Regents of the University of Michigan. 6 : : * All rights reserved. 7 : : * 8 : : * J. Bruce Fields <bfields@umich.edu> 9 : : */ 10 : : 11 : : #include <linux/types.h> 12 : : #include <linux/slab.h> 13 : : #include <linux/module.h> 14 : : #include <linux/oid_registry.h> 15 : : #include <linux/sunrpc/msg_prot.h> 16 : : #include <linux/sunrpc/gss_asn1.h> 17 : : #include <linux/sunrpc/auth_gss.h> 18 : : #include <linux/sunrpc/svcauth_gss.h> 19 : : #include <linux/sunrpc/gss_err.h> 20 : : #include <linux/sunrpc/sched.h> 21 : : #include <linux/sunrpc/gss_api.h> 22 : : #include <linux/sunrpc/clnt.h> 23 : : 24 : : #if IS_ENABLED(CONFIG_SUNRPC_DEBUG) 25 : : # define RPCDBG_FACILITY RPCDBG_AUTH 26 : : #endif 27 : : 28 : : static LIST_HEAD(registered_mechs); 29 : : static DEFINE_SPINLOCK(registered_mechs_lock); 30 : : 31 : : static void 32 : 0 : gss_mech_free(struct gss_api_mech *gm) 33 : : { 34 : : struct pf_desc *pf; 35 : : int i; 36 : : 37 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 38 : 0 : pf = &gm->gm_pfs[i]; 39 : 0 : if (pf->domain) 40 : 0 : auth_domain_put(pf->domain); 41 : 0 : kfree(pf->auth_domain_name); 42 : 0 : pf->auth_domain_name = NULL; 43 : : } 44 : 0 : } 45 : : 46 : : static inline char * 47 : 0 : make_auth_domain_name(char *name) 48 : : { 49 : : static char *prefix = "gss/"; 50 : : char *new; 51 : : 52 : 0 : new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL); 53 : 0 : if (new) { 54 : 0 : strcpy(new, prefix); 55 : 0 : strcat(new, name); 56 : : } 57 : 0 : return new; 58 : : } 59 : : 60 : : static int 61 : 0 : gss_mech_svc_setup(struct gss_api_mech *gm) 62 : : { 63 : : struct auth_domain *dom; 64 : : struct pf_desc *pf; 65 : : int i, status; 66 : : 67 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 68 : 0 : pf = &gm->gm_pfs[i]; 69 : 0 : pf->auth_domain_name = make_auth_domain_name(pf->name); 70 : : status = -ENOMEM; 71 : 0 : if (pf->auth_domain_name == NULL) 72 : : goto out; 73 : 0 : dom = svcauth_gss_register_pseudoflavor( 74 : : pf->pseudoflavor, pf->auth_domain_name); 75 : 0 : if (IS_ERR(dom)) { 76 : : status = PTR_ERR(dom); 77 : 0 : goto out; 78 : : } 79 : 0 : pf->domain = dom; 80 : : } 81 : : return 0; 82 : : out: 83 : 0 : gss_mech_free(gm); 84 : 0 : return status; 85 : : } 86 : : 87 : : /** 88 : : * gss_mech_register - register a GSS mechanism 89 : : * @gm: GSS mechanism handle 90 : : * 91 : : * Returns zero if successful, or a negative errno. 92 : : */ 93 : 0 : int gss_mech_register(struct gss_api_mech *gm) 94 : : { 95 : : int status; 96 : : 97 : 0 : status = gss_mech_svc_setup(gm); 98 : 0 : if (status) 99 : : return status; 100 : : spin_lock(®istered_mechs_lock); 101 : 0 : list_add_rcu(&gm->gm_list, ®istered_mechs); 102 : : spin_unlock(®istered_mechs_lock); 103 : : dprintk("RPC: registered gss mechanism %s\n", gm->gm_name); 104 : 0 : return 0; 105 : : } 106 : : EXPORT_SYMBOL_GPL(gss_mech_register); 107 : : 108 : : /** 109 : : * gss_mech_unregister - release a GSS mechanism 110 : : * @gm: GSS mechanism handle 111 : : * 112 : : */ 113 : 0 : void gss_mech_unregister(struct gss_api_mech *gm) 114 : : { 115 : : spin_lock(®istered_mechs_lock); 116 : : list_del_rcu(&gm->gm_list); 117 : : spin_unlock(®istered_mechs_lock); 118 : : dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name); 119 : 0 : gss_mech_free(gm); 120 : 0 : } 121 : : EXPORT_SYMBOL_GPL(gss_mech_unregister); 122 : : 123 : 0 : struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm) 124 : : { 125 : 0 : __module_get(gm->gm_owner); 126 : 0 : return gm; 127 : : } 128 : : EXPORT_SYMBOL(gss_mech_get); 129 : : 130 : : static struct gss_api_mech * 131 : 0 : _gss_mech_get_by_name(const char *name) 132 : : { 133 : : struct gss_api_mech *pos, *gm = NULL; 134 : : 135 : : rcu_read_lock(); 136 : 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) { 137 : 0 : if (0 == strcmp(name, pos->gm_name)) { 138 : 0 : if (try_module_get(pos->gm_owner)) 139 : 0 : gm = pos; 140 : : break; 141 : : } 142 : : } 143 : : rcu_read_unlock(); 144 : 0 : return gm; 145 : : 146 : : } 147 : : 148 : 0 : struct gss_api_mech * gss_mech_get_by_name(const char *name) 149 : : { 150 : : struct gss_api_mech *gm = NULL; 151 : : 152 : 0 : gm = _gss_mech_get_by_name(name); 153 : 0 : if (!gm) { 154 : 0 : request_module("rpc-auth-gss-%s", name); 155 : 0 : gm = _gss_mech_get_by_name(name); 156 : : } 157 : 0 : return gm; 158 : : } 159 : : 160 : 0 : struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj) 161 : : { 162 : : struct gss_api_mech *pos, *gm = NULL; 163 : : char buf[32]; 164 : : 165 : 0 : if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0) 166 : : return NULL; 167 : : dprintk("RPC: %s(%s)\n", __func__, buf); 168 : 0 : request_module("rpc-auth-gss-%s", buf); 169 : : 170 : : rcu_read_lock(); 171 : 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) { 172 : 0 : if (obj->len == pos->gm_oid.len) { 173 : 0 : if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) { 174 : 0 : if (try_module_get(pos->gm_owner)) 175 : 0 : gm = pos; 176 : : break; 177 : : } 178 : : } 179 : : } 180 : : rcu_read_unlock(); 181 : 0 : return gm; 182 : : } 183 : : 184 : : static inline int 185 : : mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) 186 : : { 187 : : int i; 188 : : 189 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 190 : 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) 191 : : return 1; 192 : : } 193 : : return 0; 194 : : } 195 : : 196 : 0 : static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor) 197 : : { 198 : : struct gss_api_mech *gm = NULL, *pos; 199 : : 200 : : rcu_read_lock(); 201 : 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) { 202 : 0 : if (!mech_supports_pseudoflavor(pos, pseudoflavor)) 203 : 0 : continue; 204 : 0 : if (try_module_get(pos->gm_owner)) 205 : 0 : gm = pos; 206 : : break; 207 : : } 208 : : rcu_read_unlock(); 209 : 0 : return gm; 210 : : } 211 : : 212 : : struct gss_api_mech * 213 : 0 : gss_mech_get_by_pseudoflavor(u32 pseudoflavor) 214 : : { 215 : : struct gss_api_mech *gm; 216 : : 217 : 0 : gm = _gss_mech_get_by_pseudoflavor(pseudoflavor); 218 : : 219 : 0 : if (!gm) { 220 : 0 : request_module("rpc-auth-gss-%u", pseudoflavor); 221 : 0 : gm = _gss_mech_get_by_pseudoflavor(pseudoflavor); 222 : : } 223 : 0 : return gm; 224 : : } 225 : : 226 : : /** 227 : : * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors 228 : : * @array_ptr: array to fill in 229 : : * @size: size of "array" 230 : : * 231 : : * Returns the number of array items filled in, or a negative errno. 232 : : * 233 : : * The returned array is not sorted by any policy. Callers should not 234 : : * rely on the order of the items in the returned array. 235 : : */ 236 : 0 : int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size) 237 : : { 238 : : struct gss_api_mech *pos = NULL; 239 : : int j, i = 0; 240 : : 241 : : rcu_read_lock(); 242 : 0 : list_for_each_entry_rcu(pos, ®istered_mechs, gm_list) { 243 : 0 : for (j = 0; j < pos->gm_pf_num; j++) { 244 : 0 : if (i >= size) { 245 : : spin_unlock(®istered_mechs_lock); 246 : 0 : return -ENOMEM; 247 : : } 248 : 0 : array_ptr[i++] = pos->gm_pfs[j].pseudoflavor; 249 : : } 250 : : } 251 : : rcu_read_unlock(); 252 : 0 : return i; 253 : : } 254 : : 255 : : /** 256 : : * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor 257 : : * @gm: GSS mechanism handle 258 : : * @qop: GSS quality-of-protection value 259 : : * @service: GSS service value 260 : : * 261 : : * Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found. 262 : : */ 263 : 0 : rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop, 264 : : u32 service) 265 : : { 266 : : int i; 267 : : 268 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 269 : 0 : if (gm->gm_pfs[i].qop == qop && 270 : 0 : gm->gm_pfs[i].service == service) { 271 : 0 : return gm->gm_pfs[i].pseudoflavor; 272 : : } 273 : : } 274 : : return RPC_AUTH_MAXFLAVOR; 275 : : } 276 : : 277 : : /** 278 : : * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple 279 : : * @info: a GSS mech OID, quality of protection, and service value 280 : : * 281 : : * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is 282 : : * not supported. 283 : : */ 284 : 0 : rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info) 285 : : { 286 : : rpc_authflavor_t pseudoflavor; 287 : : struct gss_api_mech *gm; 288 : : 289 : 0 : gm = gss_mech_get_by_OID(&info->oid); 290 : 0 : if (gm == NULL) 291 : : return RPC_AUTH_MAXFLAVOR; 292 : : 293 : 0 : pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service); 294 : : 295 : : gss_mech_put(gm); 296 : 0 : return pseudoflavor; 297 : : } 298 : : 299 : : /** 300 : : * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor 301 : : * @pseudoflavor: GSS pseudoflavor to match 302 : : * @info: rpcsec_gss_info structure to fill in 303 : : * 304 : : * Returns zero and fills in "info" if pseudoflavor matches a 305 : : * supported mechanism. Otherwise a negative errno is returned. 306 : : */ 307 : 0 : int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor, 308 : : struct rpcsec_gss_info *info) 309 : : { 310 : : struct gss_api_mech *gm; 311 : : int i; 312 : : 313 : 0 : gm = gss_mech_get_by_pseudoflavor(pseudoflavor); 314 : 0 : if (gm == NULL) 315 : : return -ENOENT; 316 : : 317 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 318 : 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) { 319 : 0 : memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len); 320 : 0 : info->oid.len = gm->gm_oid.len; 321 : 0 : info->qop = gm->gm_pfs[i].qop; 322 : 0 : info->service = gm->gm_pfs[i].service; 323 : : gss_mech_put(gm); 324 : : return 0; 325 : : } 326 : : } 327 : : 328 : : gss_mech_put(gm); 329 : : return -ENOENT; 330 : : } 331 : : 332 : : u32 333 : 0 : gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) 334 : : { 335 : : int i; 336 : : 337 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 338 : 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) 339 : 0 : return gm->gm_pfs[i].service; 340 : : } 341 : : return 0; 342 : : } 343 : : EXPORT_SYMBOL(gss_pseudoflavor_to_service); 344 : : 345 : : bool 346 : 0 : gss_pseudoflavor_to_datatouch(struct gss_api_mech *gm, u32 pseudoflavor) 347 : : { 348 : : int i; 349 : : 350 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 351 : 0 : if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) 352 : 0 : return gm->gm_pfs[i].datatouch; 353 : : } 354 : : return false; 355 : : } 356 : : 357 : : char * 358 : 0 : gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) 359 : : { 360 : : int i; 361 : : 362 : 0 : for (i = 0; i < gm->gm_pf_num; i++) { 363 : 0 : if (gm->gm_pfs[i].service == service) 364 : 0 : return gm->gm_pfs[i].auth_domain_name; 365 : : } 366 : : return NULL; 367 : : } 368 : : 369 : : void 370 : 0 : gss_mech_put(struct gss_api_mech * gm) 371 : : { 372 : 0 : if (gm) 373 : 0 : module_put(gm->gm_owner); 374 : 0 : } 375 : : EXPORT_SYMBOL(gss_mech_put); 376 : : 377 : : /* The mech could probably be determined from the token instead, but it's just 378 : : * as easy for now to pass it in. */ 379 : : int 380 : 0 : gss_import_sec_context(const void *input_token, size_t bufsize, 381 : : struct gss_api_mech *mech, 382 : : struct gss_ctx **ctx_id, 383 : : time_t *endtime, 384 : : gfp_t gfp_mask) 385 : : { 386 : 0 : if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask))) 387 : : return -ENOMEM; 388 : 0 : (*ctx_id)->mech_type = gss_mech_get(mech); 389 : : 390 : 0 : return mech->gm_ops->gss_import_sec_context(input_token, bufsize, 391 : : *ctx_id, endtime, gfp_mask); 392 : : } 393 : : 394 : : /* gss_get_mic: compute a mic over message and return mic_token. */ 395 : : 396 : : u32 397 : 0 : gss_get_mic(struct gss_ctx *context_handle, 398 : : struct xdr_buf *message, 399 : : struct xdr_netobj *mic_token) 400 : : { 401 : 0 : return context_handle->mech_type->gm_ops 402 : 0 : ->gss_get_mic(context_handle, 403 : : message, 404 : : mic_token); 405 : : } 406 : : 407 : : /* gss_verify_mic: check whether the provided mic_token verifies message. */ 408 : : 409 : : u32 410 : 0 : gss_verify_mic(struct gss_ctx *context_handle, 411 : : struct xdr_buf *message, 412 : : struct xdr_netobj *mic_token) 413 : : { 414 : 0 : return context_handle->mech_type->gm_ops 415 : 0 : ->gss_verify_mic(context_handle, 416 : : message, 417 : : mic_token); 418 : : } 419 : : 420 : : /* 421 : : * This function is called from both the client and server code. 422 : : * Each makes guarantees about how much "slack" space is available 423 : : * for the underlying function in "buf"'s head and tail while 424 : : * performing the wrap. 425 : : * 426 : : * The client and server code allocate RPC_MAX_AUTH_SIZE extra 427 : : * space in both the head and tail which is available for use by 428 : : * the wrap function. 429 : : * 430 : : * Underlying functions should verify they do not use more than 431 : : * RPC_MAX_AUTH_SIZE of extra space in either the head or tail 432 : : * when performing the wrap. 433 : : */ 434 : : u32 435 : 0 : gss_wrap(struct gss_ctx *ctx_id, 436 : : int offset, 437 : : struct xdr_buf *buf, 438 : : struct page **inpages) 439 : : { 440 : 0 : return ctx_id->mech_type->gm_ops 441 : 0 : ->gss_wrap(ctx_id, offset, buf, inpages); 442 : : } 443 : : 444 : : u32 445 : 0 : gss_unwrap(struct gss_ctx *ctx_id, 446 : : int offset, 447 : : int len, 448 : : struct xdr_buf *buf) 449 : : { 450 : 0 : return ctx_id->mech_type->gm_ops 451 : 0 : ->gss_unwrap(ctx_id, offset, len, buf); 452 : : } 453 : : 454 : : 455 : : /* gss_delete_sec_context: free all resources associated with context_handle. 456 : : * Note this differs from the RFC 2744-specified prototype in that we don't 457 : : * bother returning an output token, since it would never be used anyway. */ 458 : : 459 : : u32 460 : 0 : gss_delete_sec_context(struct gss_ctx **context_handle) 461 : : { 462 : : dprintk("RPC: gss_delete_sec_context deleting %p\n", 463 : : *context_handle); 464 : : 465 : 0 : if (!*context_handle) 466 : : return GSS_S_NO_CONTEXT; 467 : 0 : if ((*context_handle)->internal_ctx_id) 468 : 0 : (*context_handle)->mech_type->gm_ops 469 : 0 : ->gss_delete_sec_context((*context_handle) 470 : : ->internal_ctx_id); 471 : 0 : gss_mech_put((*context_handle)->mech_type); 472 : 0 : kfree(*context_handle); 473 : 0 : *context_handle=NULL; 474 : 0 : return GSS_S_COMPLETE; 475 : : }