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