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