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 29 static LIST_HEAD(registered_mechs); 30 static DEFINE_SPINLOCK(registered_mechs_lock); 31 32 static void 33 gss_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 47 static inline char * 48 make_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 61 static int 62 gss_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; 83 out: 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 */ 94 int 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 } 107 EXPORT_SYMBOL_GPL(gss_mech_register); 108 109 /** 110 * gss_mech_unregister - release a GSS mechanism 111 * @gm: GSS mechanism handle 112 * 113 */ 114 void 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 } 122 EXPORT_SYMBOL_GPL(gss_mech_unregister); 123 124 struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm) 125 { 126 __module_get(gm->gm_owner); 127 return gm; 128 } 129 EXPORT_SYMBOL(gss_mech_get); 130 131 static 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 149 struct 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 161 struct 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 186 static inline int 187 mech_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 198 static 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 214 struct gss_api_mech * 215 gss_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 */ 236 rpc_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 */ 257 rpc_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 */ 280 int 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 305 u32 306 gss_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 } 316 EXPORT_SYMBOL(gss_pseudoflavor_to_service); 317 318 bool 319 gss_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 330 char * 331 gss_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 342 void 343 gss_mech_put(struct gss_api_mech * gm) 344 { 345 if (gm) 346 module_put(gm->gm_owner); 347 } 348 EXPORT_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. */ 352 int 353 gss_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 369 u32 370 gss_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 382 u32 383 gss_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 */ 407 u32 408 gss_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 417 u32 418 gss_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 432 u32 433 gss_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 } 449
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.