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