1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * Copyright (c) 1990 The Regents of the University of California. 4 * 5 * Copyright (c) 2008 Doug Rabson 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 /* 30 svc_rpcsec_gss.c 31 32 Copyright (c) 2000 The Regents of the University of Michigan. 33 All rights reserved. 34 35 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 36 All rights reserved, all wrongs reversed. 37 38 Redistribution and use in source and binary forms, with or without 39 modification, are permitted provided that the following conditions 40 are met: 41 42 1. Redistributions of source code must retain the above copyright 43 notice, this list of conditions and the following disclaimer. 44 2. Redistributions in binary form must reproduce the above copyright 45 notice, this list of conditions and the following disclaimer in the 46 documentation and/or other materials provided with the distribution. 47 3. Neither the name of the University nor the names of its 48 contributors may be used to endorse or promote products derived 49 from this software without specific prior written permission. 50 51 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 52 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 53 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 54 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 56 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 57 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 58 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 59 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 60 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 61 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 63 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 64 */ 65 66 #include <sys/cdefs.h> 67 __FBSDID("$FreeBSD$"); 68 69 #include <sys/param.h> 70 #include <sys/systm.h> 71 #include <sys/jail.h> 72 #include <sys/kernel.h> 73 #include <sys/kobj.h> 74 #include <sys/lock.h> 75 #include <sys/malloc.h> 76 #include <sys/mbuf.h> 77 #include <sys/mutex.h> 78 #include <sys/proc.h> 79 #include <sys/sx.h> 80 #include <sys/ucred.h> 81 82 #include <rpc/rpc.h> 83 #include <rpc/rpcsec_gss.h> 84 85 #include "rpcsec_gss_int.h" 86 87 static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); 88 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); 89 static void svc_rpc_gss_release(SVCAUTH *); 90 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 91 static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *); 92 93 static const struct svc_auth_ops svc_auth_gss_ops = { 94 .svc_ah_wrap = svc_rpc_gss_wrap, 95 .svc_ah_unwrap = svc_rpc_gss_unwrap, 96 .svc_ah_release = svc_rpc_gss_release, 97 }; 98 99 struct sx svc_rpc_gss_lock; 100 101 struct svc_rpc_gss_callback { 102 SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 103 rpc_gss_callback_t cb_callback; 104 }; 105 SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback); 106 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_callback_list, 107 svc_rpc_gss_callbacks) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 108 109 struct svc_rpc_gss_svc_name { 110 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 111 char *sn_principal; 112 gss_OID sn_mech; 113 u_int sn_req_time; 114 gss_cred_id_t sn_cred; 115 u_int sn_program; 116 u_int sn_version; 117 }; 118 SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name); 119 KGSS_VNET_DEFINE_STATIC(struct svc_rpc_gss_svc_name_list, 120 svc_rpc_gss_svc_names) = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 121 122 enum svc_rpc_gss_client_state { 123 CLIENT_NEW, /* still authenticating */ 124 CLIENT_ESTABLISHED, /* context established */ 125 CLIENT_STALE /* garbage to collect */ 126 }; 127 128 #define SVC_RPC_GSS_SEQWINDOW 128 129 130 struct svc_rpc_gss_clientid { 131 unsigned long ci_hostid; 132 uint32_t ci_boottime; 133 uint32_t ci_id; 134 }; 135 136 struct svc_rpc_gss_client { 137 TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 138 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 139 volatile u_int cl_refs; 140 struct sx cl_lock; 141 struct svc_rpc_gss_clientid cl_id; 142 time_t cl_expiration; /* when to gc */ 143 enum svc_rpc_gss_client_state cl_state; /* client state */ 144 bool_t cl_locked; /* fixed service+qop */ 145 gss_ctx_id_t cl_ctx; /* context id */ 146 gss_cred_id_t cl_creds; /* delegated creds */ 147 gss_name_t cl_cname; /* client name */ 148 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 149 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 150 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 151 struct ucred *cl_cred; /* kernel-style credentials */ 152 int cl_rpcflavor; /* RPC pseudo sec flavor */ 153 bool_t cl_done_callback; /* TRUE after call */ 154 void *cl_cookie; /* user cookie from callback */ 155 gid_t cl_gid_storage[NGROUPS]; 156 gss_OID cl_mech; /* mechanism */ 157 gss_qop_t cl_qop; /* quality of protection */ 158 uint32_t cl_seqlast; /* sequence window origin */ 159 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 160 }; 161 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 162 163 /* 164 * This structure holds enough information to unwrap arguments or wrap 165 * results for a given request. We use the rq_clntcred area for this 166 * (which is a per-request buffer). 167 */ 168 struct svc_rpc_gss_cookedcred { 169 struct svc_rpc_gss_client *cc_client; 170 rpc_gss_service_t cc_service; 171 uint32_t cc_seq; 172 }; 173 174 #define CLIENT_HASH_SIZE 256 175 #define CLIENT_MAX 1024 176 u_int svc_rpc_gss_client_max = CLIENT_MAX; 177 u_int svc_rpc_gss_client_hash_size = CLIENT_HASH_SIZE; 178 179 SYSCTL_NODE(_kern, OID_AUTO, rpc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 180 "RPC"); 181 SYSCTL_NODE(_kern_rpc, OID_AUTO, gss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 182 "GSS"); 183 184 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_max, CTLFLAG_RW, 185 &svc_rpc_gss_client_max, 0, 186 "Max number of rpc-gss clients"); 187 188 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_hash, CTLFLAG_RDTUN, 189 &svc_rpc_gss_client_hash_size, 0, 190 "Size of rpc-gss client hash table"); 191 192 static u_int svc_rpc_gss_lifetime_max = 0; 193 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, lifetime_max, CTLFLAG_RW, 194 &svc_rpc_gss_lifetime_max, 0, 195 "Maximum lifetime (seconds) of rpc-gss clients"); 196 197 static u_int svc_rpc_gss_client_count; 198 SYSCTL_UINT(_kern_rpc_gss, OID_AUTO, client_count, CTLFLAG_RD, 199 &svc_rpc_gss_client_count, 0, 200 "Number of rpc-gss clients"); 201 202 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list *, svc_rpc_gss_client_hash); 203 KGSS_VNET_DEFINE(struct svc_rpc_gss_client_list, svc_rpc_gss_clients); 204 KGSS_VNET_DEFINE_STATIC(uint32_t, svc_rpc_gss_next_clientid) = 1; 205 206 static void 207 svc_rpc_gss_init(void *unused __unused) 208 { 209 210 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); 211 sx_init(&svc_rpc_gss_lock, "gsslock"); 212 } 213 SYSINIT(svc_rpc_gss_init, SI_SUB_VFS, SI_ORDER_ANY, 214 svc_rpc_gss_init, NULL); 215 216 static void 217 svc_rpc_gss_cleanup(void *unused __unused) 218 { 219 220 sx_destroy(&svc_rpc_gss_lock); 221 } 222 SYSUNINIT(svc_rpc_gss_cleanup, SI_SUB_VFS, SI_ORDER_ANY, 223 svc_rpc_gss_cleanup, NULL); 224 225 static void 226 svc_rpc_gss_vnetinit(void *unused __unused) 227 { 228 int i; 229 230 KGSS_VNET(svc_rpc_gss_client_hash) = mem_alloc( 231 sizeof(struct svc_rpc_gss_client_list) * 232 svc_rpc_gss_client_hash_size); 233 for (i = 0; i < svc_rpc_gss_client_hash_size; i++) 234 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_client_hash)[i]); 235 TAILQ_INIT(&KGSS_VNET(svc_rpc_gss_clients)); 236 } 237 VNET_SYSINIT(svc_rpc_gss_vnetinit, SI_SUB_VNET_DONE, SI_ORDER_ANY, 238 svc_rpc_gss_vnetinit, NULL); 239 240 static void 241 svc_rpc_gss_vnet_cleanup(void *unused __unused) 242 { 243 244 mem_free(KGSS_VNET(svc_rpc_gss_client_hash), 245 sizeof(struct svc_rpc_gss_client_list) * 246 svc_rpc_gss_client_hash_size); 247 } 248 VNET_SYSUNINIT(svc_rpc_gss_vnet_cleanup, SI_SUB_VNET_DONE, SI_ORDER_ANY, 249 svc_rpc_gss_vnet_cleanup, NULL); 250 251 bool_t 252 rpc_gss_set_callback(rpc_gss_callback_t *cb) 253 { 254 struct svc_rpc_gss_callback *scb; 255 256 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 257 if (!scb) { 258 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 259 return (FALSE); 260 } 261 scb->cb_callback = *cb; 262 sx_xlock(&svc_rpc_gss_lock); 263 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_callbacks), scb, cb_link); 264 sx_xunlock(&svc_rpc_gss_lock); 265 266 return (TRUE); 267 } 268 269 void 270 rpc_gss_clear_callback(rpc_gss_callback_t *cb) 271 { 272 struct svc_rpc_gss_callback *scb; 273 274 sx_xlock(&svc_rpc_gss_lock); 275 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) { 276 if (scb->cb_callback.program == cb->program 277 && scb->cb_callback.version == cb->version 278 && scb->cb_callback.callback == cb->callback) { 279 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_callbacks), scb, 280 svc_rpc_gss_callback, cb_link); 281 sx_xunlock(&svc_rpc_gss_lock); 282 mem_free(scb, sizeof(*scb)); 283 return; 284 } 285 } 286 sx_xunlock(&svc_rpc_gss_lock); 287 } 288 289 static bool_t 290 rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) 291 { 292 OM_uint32 maj_stat, min_stat; 293 gss_buffer_desc namebuf; 294 gss_name_t name; 295 gss_OID_set_desc oid_set; 296 297 oid_set.count = 1; 298 oid_set.elements = sname->sn_mech; 299 300 namebuf.value = (void *) sname->sn_principal; 301 namebuf.length = strlen(sname->sn_principal); 302 303 maj_stat = gss_import_name(&min_stat, &namebuf, 304 GSS_C_NT_HOSTBASED_SERVICE, &name); 305 if (maj_stat != GSS_S_COMPLETE) 306 return (FALSE); 307 308 if (sname->sn_cred != GSS_C_NO_CREDENTIAL) 309 gss_release_cred(&min_stat, &sname->sn_cred); 310 311 maj_stat = gss_acquire_cred(&min_stat, name, 312 sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, 313 NULL, NULL); 314 if (maj_stat != GSS_S_COMPLETE) { 315 gss_release_name(&min_stat, &name); 316 return (FALSE); 317 } 318 gss_release_name(&min_stat, &name); 319 320 return (TRUE); 321 } 322 323 bool_t 324 rpc_gss_set_svc_name(const char *principal, const char *mechanism, 325 u_int req_time, u_int program, u_int version) 326 { 327 struct svc_rpc_gss_svc_name *sname; 328 gss_OID mech_oid; 329 330 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 331 return (FALSE); 332 333 sname = mem_alloc(sizeof(*sname)); 334 if (!sname) 335 return (FALSE); 336 sname->sn_principal = strdup(principal, M_RPC); 337 sname->sn_mech = mech_oid; 338 sname->sn_req_time = req_time; 339 sname->sn_cred = GSS_C_NO_CREDENTIAL; 340 sname->sn_program = program; 341 sname->sn_version = version; 342 343 if (!rpc_gss_acquire_svc_cred(sname)) { 344 free(sname->sn_principal, M_RPC); 345 mem_free(sname, sizeof(*sname)); 346 return (FALSE); 347 } 348 349 sx_xlock(&svc_rpc_gss_lock); 350 SLIST_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_svc_names), sname, sn_link); 351 sx_xunlock(&svc_rpc_gss_lock); 352 353 return (TRUE); 354 } 355 356 void 357 rpc_gss_clear_svc_name(u_int program, u_int version) 358 { 359 OM_uint32 min_stat; 360 struct svc_rpc_gss_svc_name *sname; 361 362 sx_xlock(&svc_rpc_gss_lock); 363 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), sn_link) { 364 if (sname->sn_program == program 365 && sname->sn_version == version) { 366 SLIST_REMOVE(&KGSS_VNET(svc_rpc_gss_svc_names), sname, 367 svc_rpc_gss_svc_name, sn_link); 368 sx_xunlock(&svc_rpc_gss_lock); 369 gss_release_cred(&min_stat, &sname->sn_cred); 370 free(sname->sn_principal, M_RPC); 371 mem_free(sname, sizeof(*sname)); 372 return; 373 } 374 } 375 sx_xunlock(&svc_rpc_gss_lock); 376 } 377 378 bool_t 379 rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 380 const char *mech, const char *name, const char *node, const char *domain) 381 { 382 OM_uint32 maj_stat, min_stat; 383 gss_OID mech_oid; 384 size_t namelen; 385 gss_buffer_desc buf; 386 gss_name_t gss_name, gss_mech_name; 387 rpc_gss_principal_t result; 388 389 if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 390 return (FALSE); 391 392 /* 393 * Construct a gss_buffer containing the full name formatted 394 * as "name/node@domain" where node and domain are optional. 395 */ 396 namelen = strlen(name) + 1; 397 if (node) { 398 namelen += strlen(node) + 1; 399 } 400 if (domain) { 401 namelen += strlen(domain) + 1; 402 } 403 404 buf.value = mem_alloc(namelen); 405 buf.length = namelen; 406 strcpy((char *) buf.value, name); 407 if (node) { 408 strcat((char *) buf.value, "/"); 409 strcat((char *) buf.value, node); 410 } 411 if (domain) { 412 strcat((char *) buf.value, "@"); 413 strcat((char *) buf.value, domain); 414 } 415 416 /* 417 * Convert that to a gss_name_t and then convert that to a 418 * mechanism name in the selected mechanism. 419 */ 420 maj_stat = gss_import_name(&min_stat, &buf, 421 GSS_C_NT_USER_NAME, &gss_name); 422 mem_free(buf.value, buf.length); 423 if (maj_stat != GSS_S_COMPLETE) { 424 rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); 425 return (FALSE); 426 } 427 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 428 &gss_mech_name); 429 if (maj_stat != GSS_S_COMPLETE) { 430 rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, 431 min_stat); 432 gss_release_name(&min_stat, &gss_name); 433 return (FALSE); 434 } 435 gss_release_name(&min_stat, &gss_name); 436 437 /* 438 * Export the mechanism name and use that to construct the 439 * rpc_gss_principal_t result. 440 */ 441 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 442 if (maj_stat != GSS_S_COMPLETE) { 443 rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); 444 gss_release_name(&min_stat, &gss_mech_name); 445 return (FALSE); 446 } 447 gss_release_name(&min_stat, &gss_mech_name); 448 449 result = mem_alloc(sizeof(int) + buf.length); 450 if (!result) { 451 gss_release_buffer(&min_stat, &buf); 452 return (FALSE); 453 } 454 result->len = buf.length; 455 memcpy(result->name, buf.value, buf.length); 456 gss_release_buffer(&min_stat, &buf); 457 458 *principal = result; 459 return (TRUE); 460 } 461 462 bool_t 463 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 464 rpc_gss_ucred_t **ucred, void **cookie) 465 { 466 struct svc_rpc_gss_cookedcred *cc; 467 struct svc_rpc_gss_client *client; 468 469 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 470 return (FALSE); 471 472 cc = req->rq_clntcred; 473 client = cc->cc_client; 474 if (rcred) 475 *rcred = &client->cl_rawcred; 476 if (ucred) 477 *ucred = &client->cl_ucred; 478 if (cookie) 479 *cookie = client->cl_cookie; 480 return (TRUE); 481 } 482 483 /* 484 * This simpler interface is used by svc_getcred to copy the cred data 485 * into a kernel cred structure. 486 */ 487 static int 488 rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp) 489 { 490 struct ucred *cr; 491 struct svc_rpc_gss_cookedcred *cc; 492 struct svc_rpc_gss_client *client; 493 rpc_gss_ucred_t *uc; 494 495 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 496 return (FALSE); 497 498 cc = req->rq_clntcred; 499 client = cc->cc_client; 500 501 if (flavorp) 502 *flavorp = client->cl_rpcflavor; 503 504 if (client->cl_cred) { 505 *crp = crhold(client->cl_cred); 506 return (TRUE); 507 } 508 509 uc = &client->cl_ucred; 510 cr = client->cl_cred = crget(); 511 cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; 512 cr->cr_rgid = cr->cr_svgid = uc->gid; 513 crsetgroups(cr, uc->gidlen, uc->gidlist); 514 cr->cr_prison = curthread->td_ucred->cr_prison; 515 prison_hold(cr->cr_prison); 516 *crp = crhold(cr); 517 518 return (TRUE); 519 } 520 521 int 522 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 523 { 524 struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; 525 struct svc_rpc_gss_client *client = cc->cc_client; 526 int want_conf; 527 OM_uint32 max; 528 OM_uint32 maj_stat, min_stat; 529 int result; 530 531 switch (client->cl_rawcred.service) { 532 case rpc_gss_svc_none: 533 return (max_tp_unit_len); 534 break; 535 536 case rpc_gss_svc_default: 537 case rpc_gss_svc_integrity: 538 want_conf = FALSE; 539 break; 540 541 case rpc_gss_svc_privacy: 542 want_conf = TRUE; 543 break; 544 545 default: 546 return (0); 547 } 548 549 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 550 client->cl_qop, max_tp_unit_len, &max); 551 552 if (maj_stat == GSS_S_COMPLETE) { 553 result = (int) max; 554 if (result < 0) 555 result = 0; 556 return (result); 557 } else { 558 rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, 559 maj_stat, min_stat); 560 return (0); 561 } 562 } 563 564 static struct svc_rpc_gss_client * 565 svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) 566 { 567 struct svc_rpc_gss_client *client; 568 struct svc_rpc_gss_client_list *list; 569 struct timeval boottime; 570 unsigned long hostid; 571 572 rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); 573 574 getcredhostid(curthread->td_ucred, &hostid); 575 getboottime(&boottime); 576 if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) 577 return (NULL); 578 579 list = &KGSS_VNET(svc_rpc_gss_client_hash) 580 [id->ci_id % svc_rpc_gss_client_hash_size]; 581 sx_xlock(&svc_rpc_gss_lock); 582 TAILQ_FOREACH(client, list, cl_link) { 583 if (client->cl_id.ci_id == id->ci_id) { 584 /* 585 * Move this client to the front of the LRU 586 * list. 587 */ 588 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, 589 cl_alllink); 590 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), 591 client, cl_alllink); 592 refcount_acquire(&client->cl_refs); 593 break; 594 } 595 } 596 sx_xunlock(&svc_rpc_gss_lock); 597 598 return (client); 599 } 600 601 static struct svc_rpc_gss_client * 602 svc_rpc_gss_create_client(void) 603 { 604 struct svc_rpc_gss_client *client; 605 struct svc_rpc_gss_client_list *list; 606 struct timeval boottime; 607 unsigned long hostid; 608 609 rpc_gss_log_debug("in svc_rpc_gss_create_client()"); 610 611 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 612 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 613 614 /* 615 * Set the initial value of cl_refs to two. One for the caller 616 * and the other to hold onto the client structure until it expires. 617 */ 618 refcount_init(&client->cl_refs, 2); 619 sx_init(&client->cl_lock, "GSS-client"); 620 getcredhostid(curthread->td_ucred, &hostid); 621 client->cl_id.ci_hostid = hostid; 622 getboottime(&boottime); 623 client->cl_id.ci_boottime = boottime.tv_sec; 624 client->cl_id.ci_id = KGSS_VNET(svc_rpc_gss_next_clientid)++; 625 626 /* 627 * Start the client off with a short expiration time. We will 628 * try to get a saner value from the client creds later. 629 */ 630 client->cl_state = CLIENT_NEW; 631 client->cl_locked = FALSE; 632 client->cl_expiration = time_uptime + 5*60; 633 634 list = &KGSS_VNET(svc_rpc_gss_client_hash) 635 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 636 sx_xlock(&svc_rpc_gss_lock); 637 TAILQ_INSERT_HEAD(list, client, cl_link); 638 TAILQ_INSERT_HEAD(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink); 639 svc_rpc_gss_client_count++; 640 sx_xunlock(&svc_rpc_gss_lock); 641 return (client); 642 } 643 644 static void 645 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 646 { 647 OM_uint32 min_stat; 648 649 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 650 651 if (client->cl_ctx) 652 gss_delete_sec_context(&min_stat, 653 &client->cl_ctx, GSS_C_NO_BUFFER); 654 655 if (client->cl_cname) 656 gss_release_name(&min_stat, &client->cl_cname); 657 658 if (client->cl_rawcred.client_principal) 659 mem_free(client->cl_rawcred.client_principal, 660 sizeof(*client->cl_rawcred.client_principal) 661 + client->cl_rawcred.client_principal->len); 662 663 if (client->cl_cred) 664 crfree(client->cl_cred); 665 666 sx_destroy(&client->cl_lock); 667 mem_free(client, sizeof(*client)); 668 } 669 670 /* 671 * Drop a reference to a client and free it if that was the last reference. 672 */ 673 static void 674 svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) 675 { 676 677 if (!refcount_release(&client->cl_refs)) 678 return; 679 svc_rpc_gss_destroy_client(client); 680 } 681 682 /* 683 * Remove a client from our global lists. 684 * Must be called with svc_rpc_gss_lock held. 685 */ 686 static void 687 svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client) 688 { 689 struct svc_rpc_gss_client_list *list; 690 691 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED); 692 list = &KGSS_VNET(svc_rpc_gss_client_hash) 693 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 694 TAILQ_REMOVE(list, client, cl_link); 695 TAILQ_REMOVE(&KGSS_VNET(svc_rpc_gss_clients), client, cl_alllink); 696 svc_rpc_gss_client_count--; 697 } 698 699 /* 700 * Remove a client from our global lists and free it if we can. 701 */ 702 static void 703 svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 704 { 705 struct svc_rpc_gss_client_list *list; 706 struct svc_rpc_gss_client *tclient; 707 708 list = &KGSS_VNET(svc_rpc_gss_client_hash) 709 [client->cl_id.ci_id % svc_rpc_gss_client_hash_size]; 710 sx_xlock(&svc_rpc_gss_lock); 711 TAILQ_FOREACH(tclient, list, cl_link) { 712 /* 713 * Make sure this client has not already been removed 714 * from the lists by svc_rpc_gss_forget_client() or 715 * svc_rpc_gss_forget_client_locked(). 716 */ 717 if (client == tclient) { 718 svc_rpc_gss_forget_client_locked(client); 719 sx_xunlock(&svc_rpc_gss_lock); 720 svc_rpc_gss_release_client(client); 721 return; 722 } 723 } 724 sx_xunlock(&svc_rpc_gss_lock); 725 } 726 727 static void 728 svc_rpc_gss_timeout_clients(void) 729 { 730 struct svc_rpc_gss_client *client; 731 time_t now = time_uptime; 732 733 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 734 735 /* 736 * First enforce the max client limit. We keep 737 * svc_rpc_gss_clients in LRU order. 738 */ 739 sx_xlock(&svc_rpc_gss_lock); 740 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients), 741 svc_rpc_gss_client_list); 742 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) { 743 svc_rpc_gss_forget_client_locked(client); 744 sx_xunlock(&svc_rpc_gss_lock); 745 svc_rpc_gss_release_client(client); 746 sx_xlock(&svc_rpc_gss_lock); 747 client = TAILQ_LAST(&KGSS_VNET(svc_rpc_gss_clients), 748 svc_rpc_gss_client_list); 749 } 750 again: 751 TAILQ_FOREACH(client, &KGSS_VNET(svc_rpc_gss_clients), cl_alllink) { 752 if (client->cl_state == CLIENT_STALE 753 || now > client->cl_expiration) { 754 svc_rpc_gss_forget_client_locked(client); 755 sx_xunlock(&svc_rpc_gss_lock); 756 rpc_gss_log_debug("expiring client %p", client); 757 svc_rpc_gss_release_client(client); 758 sx_xlock(&svc_rpc_gss_lock); 759 goto again; 760 } 761 } 762 sx_xunlock(&svc_rpc_gss_lock); 763 } 764 765 #ifdef DEBUG 766 /* 767 * OID<->string routines. These are uuuuugly. 768 */ 769 static OM_uint32 770 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 771 { 772 char numstr[128]; 773 unsigned long number; 774 int numshift; 775 size_t string_length; 776 size_t i; 777 unsigned char *cp; 778 char *bp; 779 780 /* Decoded according to krb5/gssapi_krb5.c */ 781 782 /* First determine the size of the string */ 783 string_length = 0; 784 number = 0; 785 numshift = 0; 786 cp = (unsigned char *) oid->elements; 787 number = (unsigned long) cp[0]; 788 sprintf(numstr, "%ld ", number/40); 789 string_length += strlen(numstr); 790 sprintf(numstr, "%ld ", number%40); 791 string_length += strlen(numstr); 792 for (i=1; i<oid->length; i++) { 793 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 794 number = (number << 7) | (cp[i] & 0x7f); 795 numshift += 7; 796 } 797 else { 798 *minor_status = 0; 799 return(GSS_S_FAILURE); 800 } 801 if ((cp[i] & 0x80) == 0) { 802 sprintf(numstr, "%ld ", number); 803 string_length += strlen(numstr); 804 number = 0; 805 numshift = 0; 806 } 807 } 808 /* 809 * If we get here, we've calculated the length of "n n n ... n ". Add 4 810 * here for "{ " and "}\0". 811 */ 812 string_length += 4; 813 if ((bp = malloc(string_length, M_GSSAPI, M_WAITOK | M_ZERO))) { 814 strcpy(bp, "{ "); 815 number = (unsigned long) cp[0]; 816 sprintf(numstr, "%ld ", number/40); 817 strcat(bp, numstr); 818 sprintf(numstr, "%ld ", number%40); 819 strcat(bp, numstr); 820 number = 0; 821 cp = (unsigned char *) oid->elements; 822 for (i=1; i<oid->length; i++) { 823 number = (number << 7) | (cp[i] & 0x7f); 824 if ((cp[i] & 0x80) == 0) { 825 sprintf(numstr, "%ld ", number); 826 strcat(bp, numstr); 827 number = 0; 828 } 829 } 830 strcat(bp, "}"); 831 oid_str->length = strlen(bp)+1; 832 oid_str->value = (void *) bp; 833 *minor_status = 0; 834 return(GSS_S_COMPLETE); 835 } 836 *minor_status = 0; 837 return(GSS_S_FAILURE); 838 } 839 #endif 840 841 static void 842 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 843 const gss_name_t name) 844 { 845 OM_uint32 maj_stat, min_stat; 846 rpc_gss_ucred_t *uc = &client->cl_ucred; 847 int numgroups; 848 849 uc->uid = 65534; 850 uc->gid = 65534; 851 uc->gidlist = client->cl_gid_storage; 852 853 numgroups = NGROUPS; 854 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 855 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 856 if (GSS_ERROR(maj_stat)) 857 uc->gidlen = 0; 858 else 859 uc->gidlen = numgroups; 860 } 861 862 static void 863 svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 864 { 865 static gss_OID_desc krb5_mech_oid = 866 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 867 868 /* 869 * Attempt to translate mech type and service into a 870 * 'pseudo flavor'. Hardwire in krb5 support for now. 871 */ 872 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 873 switch (client->cl_rawcred.service) { 874 case rpc_gss_svc_default: 875 case rpc_gss_svc_none: 876 client->cl_rpcflavor = RPCSEC_GSS_KRB5; 877 break; 878 case rpc_gss_svc_integrity: 879 client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 880 break; 881 case rpc_gss_svc_privacy: 882 client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 883 break; 884 } 885 } else { 886 client->cl_rpcflavor = RPCSEC_GSS; 887 } 888 } 889 890 static bool_t 891 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 892 struct svc_req *rqst, 893 struct rpc_gss_init_res *gr, 894 struct rpc_gss_cred *gc) 895 { 896 gss_buffer_desc recv_tok; 897 gss_OID mech; 898 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 899 OM_uint32 cred_lifetime; 900 struct svc_rpc_gss_svc_name *sname; 901 902 rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 903 904 /* Deserialize arguments. */ 905 memset(&recv_tok, 0, sizeof(recv_tok)); 906 907 if (!svc_getargs(rqst, 908 (xdrproc_t) xdr_gss_buffer_desc, 909 (caddr_t) &recv_tok)) { 910 client->cl_state = CLIENT_STALE; 911 return (FALSE); 912 } 913 914 /* 915 * First time round, try all the server names we have until 916 * one matches. Afterwards, stick with that one. 917 */ 918 sx_xlock(&svc_rpc_gss_lock); 919 if (!client->cl_sname) { 920 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), 921 sn_link) { 922 if (sname->sn_program == rqst->rq_prog 923 && sname->sn_version == rqst->rq_vers) { 924 retry: 925 gr->gr_major = gss_accept_sec_context( 926 &gr->gr_minor, 927 &client->cl_ctx, 928 sname->sn_cred, 929 &recv_tok, 930 GSS_C_NO_CHANNEL_BINDINGS, 931 &client->cl_cname, 932 &mech, 933 &gr->gr_token, 934 &ret_flags, 935 &cred_lifetime, 936 &client->cl_creds); 937 if (gr->gr_major == 938 GSS_S_CREDENTIALS_EXPIRED) { 939 /* 940 * Either our creds really did 941 * expire or gssd was 942 * restarted. 943 */ 944 if (rpc_gss_acquire_svc_cred(sname)) 945 goto retry; 946 } 947 client->cl_sname = sname; 948 break; 949 } 950 } 951 if (!sname) { 952 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 953 (char *) &recv_tok); 954 sx_xunlock(&svc_rpc_gss_lock); 955 return (FALSE); 956 } 957 } else { 958 gr->gr_major = gss_accept_sec_context( 959 &gr->gr_minor, 960 &client->cl_ctx, 961 client->cl_sname->sn_cred, 962 &recv_tok, 963 GSS_C_NO_CHANNEL_BINDINGS, 964 &client->cl_cname, 965 &mech, 966 &gr->gr_token, 967 &ret_flags, 968 &cred_lifetime, 969 NULL); 970 } 971 sx_xunlock(&svc_rpc_gss_lock); 972 973 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 974 975 /* 976 * If we get an error from gss_accept_sec_context, send the 977 * reply anyway so that the client gets a chance to see what 978 * is wrong. 979 */ 980 if (gr->gr_major != GSS_S_COMPLETE && 981 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 982 rpc_gss_log_status("accept_sec_context", client->cl_mech, 983 gr->gr_major, gr->gr_minor); 984 client->cl_state = CLIENT_STALE; 985 return (TRUE); 986 } 987 988 gr->gr_handle.value = &client->cl_id; 989 gr->gr_handle.length = sizeof(client->cl_id); 990 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 991 992 /* Save client info. */ 993 client->cl_mech = mech; 994 client->cl_qop = GSS_C_QOP_DEFAULT; 995 client->cl_done_callback = FALSE; 996 997 if (gr->gr_major == GSS_S_COMPLETE) { 998 gss_buffer_desc export_name; 999 1000 /* 1001 * Change client expiration time to be near when the 1002 * client creds expire (or 24 hours if we can't figure 1003 * that out). 1004 */ 1005 if (cred_lifetime == GSS_C_INDEFINITE) 1006 cred_lifetime = 24*60*60; 1007 1008 /* 1009 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set. 1010 */ 1011 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime > 1012 svc_rpc_gss_lifetime_max) 1013 cred_lifetime = svc_rpc_gss_lifetime_max; 1014 1015 client->cl_expiration = time_uptime + cred_lifetime; 1016 1017 /* 1018 * Fill in cred details in the rawcred structure. 1019 */ 1020 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 1021 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 1022 maj_stat = gss_export_name(&min_stat, client->cl_cname, 1023 &export_name); 1024 if (maj_stat != GSS_S_COMPLETE) { 1025 rpc_gss_log_status("gss_export_name", client->cl_mech, 1026 maj_stat, min_stat); 1027 return (FALSE); 1028 } 1029 client->cl_rawcred.client_principal = 1030 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 1031 + export_name.length); 1032 client->cl_rawcred.client_principal->len = export_name.length; 1033 memcpy(client->cl_rawcred.client_principal->name, 1034 export_name.value, export_name.length); 1035 gss_release_buffer(&min_stat, &export_name); 1036 client->cl_rawcred.svc_principal = 1037 client->cl_sname->sn_principal; 1038 client->cl_rawcred.service = gc->gc_svc; 1039 1040 /* 1041 * Use gss_pname_to_uid to map to unix creds. For 1042 * kerberos5, this uses krb5_aname_to_localname. 1043 */ 1044 svc_rpc_gss_build_ucred(client, client->cl_cname); 1045 svc_rpc_gss_set_flavor(client); 1046 gss_release_name(&min_stat, &client->cl_cname); 1047 1048 #ifdef DEBUG 1049 { 1050 gss_buffer_desc mechname; 1051 1052 gss_oid_to_str(&min_stat, mech, &mechname); 1053 1054 rpc_gss_log_debug("accepted context for %s with " 1055 "<mech %.*s, qop %d, svc %d>", 1056 client->cl_rawcred.client_principal->name, 1057 mechname.length, (char *)mechname.value, 1058 client->cl_qop, client->cl_rawcred.service); 1059 1060 gss_release_buffer(&min_stat, &mechname); 1061 } 1062 #endif /* DEBUG */ 1063 } 1064 return (TRUE); 1065 } 1066 1067 static bool_t 1068 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 1069 gss_qop_t *qop, rpc_gss_proc_t gcproc) 1070 { 1071 struct opaque_auth *oa; 1072 gss_buffer_desc rpcbuf, checksum; 1073 OM_uint32 maj_stat, min_stat; 1074 gss_qop_t qop_state; 1075 int32_t rpchdr[128 / sizeof(int32_t)]; 1076 int32_t *buf; 1077 1078 rpc_gss_log_debug("in svc_rpc_gss_validate()"); 1079 1080 memset(rpchdr, 0, sizeof(rpchdr)); 1081 1082 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 1083 buf = rpchdr; 1084 IXDR_PUT_LONG(buf, msg->rm_xid); 1085 IXDR_PUT_ENUM(buf, msg->rm_direction); 1086 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 1087 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 1088 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 1089 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 1090 oa = &msg->rm_call.cb_cred; 1091 IXDR_PUT_ENUM(buf, oa->oa_flavor); 1092 IXDR_PUT_LONG(buf, oa->oa_length); 1093 if (oa->oa_length) { 1094 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 1095 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1096 } 1097 rpcbuf.value = rpchdr; 1098 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 1099 1100 checksum.value = msg->rm_call.cb_verf.oa_base; 1101 checksum.length = msg->rm_call.cb_verf.oa_length; 1102 1103 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 1104 &qop_state); 1105 1106 if (maj_stat != GSS_S_COMPLETE) { 1107 rpc_gss_log_status("gss_verify_mic", client->cl_mech, 1108 maj_stat, min_stat); 1109 /* 1110 * A bug in some versions of the Linux client generates a 1111 * Destroy operation with a bogus encrypted checksum. Deleting 1112 * the credential handle for that case causes the mount to fail. 1113 * Since the checksum is bogus (gss_verify_mic() failed), it 1114 * doesn't make sense to destroy the handle and not doing so 1115 * fixes the Linux mount. 1116 */ 1117 if (gcproc != RPCSEC_GSS_DESTROY) 1118 client->cl_state = CLIENT_STALE; 1119 return (FALSE); 1120 } 1121 1122 *qop = qop_state; 1123 return (TRUE); 1124 } 1125 1126 static bool_t 1127 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 1128 struct svc_req *rqst, u_int seq) 1129 { 1130 gss_buffer_desc signbuf; 1131 gss_buffer_desc mic; 1132 OM_uint32 maj_stat, min_stat; 1133 uint32_t nseq; 1134 1135 rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1136 1137 nseq = htonl(seq); 1138 signbuf.value = &nseq; 1139 signbuf.length = sizeof(nseq); 1140 1141 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1142 &signbuf, &mic); 1143 1144 if (maj_stat != GSS_S_COMPLETE) { 1145 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1146 client->cl_state = CLIENT_STALE; 1147 return (FALSE); 1148 } 1149 1150 KASSERT(mic.length <= MAX_AUTH_BYTES, 1151 ("MIC too large for RPCSEC_GSS")); 1152 1153 rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1154 rqst->rq_verf.oa_length = mic.length; 1155 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1156 1157 gss_release_buffer(&min_stat, &mic); 1158 1159 return (TRUE); 1160 } 1161 1162 static bool_t 1163 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1164 { 1165 struct svc_rpc_gss_callback *scb; 1166 rpc_gss_lock_t lock; 1167 void *cookie; 1168 bool_t cb_res; 1169 bool_t result; 1170 1171 /* 1172 * See if we have a callback for this guy. 1173 */ 1174 result = TRUE; 1175 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) { 1176 if (scb->cb_callback.program == rqst->rq_prog 1177 && scb->cb_callback.version == rqst->rq_vers) { 1178 /* 1179 * This one matches. Call the callback and see 1180 * if it wants to veto or something. 1181 */ 1182 lock.locked = FALSE; 1183 lock.raw_cred = &client->cl_rawcred; 1184 cb_res = scb->cb_callback.callback(rqst, 1185 client->cl_creds, 1186 client->cl_ctx, 1187 &lock, 1188 &cookie); 1189 1190 if (!cb_res) { 1191 client->cl_state = CLIENT_STALE; 1192 result = FALSE; 1193 break; 1194 } 1195 1196 /* 1197 * The callback accepted the connection - it 1198 * is responsible for freeing client->cl_creds 1199 * now. 1200 */ 1201 client->cl_creds = GSS_C_NO_CREDENTIAL; 1202 client->cl_locked = lock.locked; 1203 client->cl_cookie = cookie; 1204 return (TRUE); 1205 } 1206 } 1207 1208 /* 1209 * Either no callback exists for this program/version or one 1210 * of the callbacks rejected the connection. We just need to 1211 * clean up the delegated client creds, if any. 1212 */ 1213 if (client->cl_creds) { 1214 OM_uint32 min_ver; 1215 gss_release_cred(&min_ver, &client->cl_creds); 1216 } 1217 return (result); 1218 } 1219 1220 static bool_t 1221 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1222 { 1223 uint32_t offset; 1224 int word, bit; 1225 bool_t result; 1226 1227 sx_xlock(&client->cl_lock); 1228 if (seq <= client->cl_seqlast) { 1229 /* 1230 * The request sequence number is less than 1231 * the largest we have seen so far. If it is 1232 * outside the window or if we have seen a 1233 * request with this sequence before, silently 1234 * discard it. 1235 */ 1236 offset = client->cl_seqlast - seq; 1237 if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1238 result = FALSE; 1239 goto out; 1240 } 1241 word = offset / 32; 1242 bit = offset % 32; 1243 if (client->cl_seqmask[word] & (1 << bit)) { 1244 result = FALSE; 1245 goto out; 1246 } 1247 } 1248 1249 result = TRUE; 1250 out: 1251 sx_xunlock(&client->cl_lock); 1252 return (result); 1253 } 1254 1255 static void 1256 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 1257 { 1258 int offset, i, word, bit; 1259 uint32_t carry, newcarry; 1260 1261 sx_xlock(&client->cl_lock); 1262 if (seq > client->cl_seqlast) { 1263 /* 1264 * This request has a sequence number greater 1265 * than any we have seen so far. Advance the 1266 * seq window and set bit zero of the window 1267 * (which corresponds to the new sequence 1268 * number) 1269 */ 1270 offset = seq - client->cl_seqlast; 1271 while (offset > 32) { 1272 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1273 i > 0; i--) { 1274 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 1275 } 1276 client->cl_seqmask[0] = 0; 1277 offset -= 32; 1278 } 1279 carry = 0; 1280 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1281 newcarry = client->cl_seqmask[i] >> (32 - offset); 1282 client->cl_seqmask[i] = 1283 (client->cl_seqmask[i] << offset) | carry; 1284 carry = newcarry; 1285 } 1286 client->cl_seqmask[0] |= 1; 1287 client->cl_seqlast = seq; 1288 } else { 1289 offset = client->cl_seqlast - seq; 1290 word = offset / 32; 1291 bit = offset % 32; 1292 client->cl_seqmask[word] |= (1 << bit); 1293 } 1294 sx_xunlock(&client->cl_lock); 1295 } 1296 1297 enum auth_stat 1298 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1299 1300 { 1301 OM_uint32 min_stat; 1302 XDR xdrs; 1303 struct svc_rpc_gss_cookedcred *cc; 1304 struct svc_rpc_gss_client *client; 1305 struct rpc_gss_cred gc; 1306 struct rpc_gss_init_res gr; 1307 gss_qop_t qop; 1308 int call_stat; 1309 enum auth_stat result; 1310 1311 KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread)); 1312 rpc_gss_log_debug("in svc_rpc_gss()"); 1313 1314 /* Garbage collect old clients. */ 1315 svc_rpc_gss_timeout_clients(); 1316 1317 /* Initialize reply. */ 1318 rqst->rq_verf = _null_auth; 1319 1320 /* Deserialize client credentials. */ 1321 if (rqst->rq_cred.oa_length <= 0) { 1322 KGSS_CURVNET_RESTORE(); 1323 return (AUTH_BADCRED); 1324 } 1325 1326 memset(&gc, 0, sizeof(gc)); 1327 1328 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1329 rqst->rq_cred.oa_length, XDR_DECODE); 1330 1331 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1332 XDR_DESTROY(&xdrs); 1333 KGSS_CURVNET_RESTORE(); 1334 return (AUTH_BADCRED); 1335 } 1336 XDR_DESTROY(&xdrs); 1337 1338 client = NULL; 1339 1340 /* Check version. */ 1341 if (gc.gc_version != RPCSEC_GSS_VERSION) { 1342 result = AUTH_BADCRED; 1343 goto out; 1344 } 1345 1346 /* Check the proc and find the client (or create it) */ 1347 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1348 if (gc.gc_handle.length != 0) { 1349 result = AUTH_BADCRED; 1350 goto out; 1351 } 1352 client = svc_rpc_gss_create_client(); 1353 } else { 1354 struct svc_rpc_gss_clientid *p; 1355 if (gc.gc_handle.length != sizeof(*p)) { 1356 result = AUTH_BADCRED; 1357 goto out; 1358 } 1359 p = gc.gc_handle.value; 1360 client = svc_rpc_gss_find_client(p); 1361 if (!client) { 1362 /* 1363 * Can't find the client - we may have 1364 * destroyed it - tell the other side to 1365 * re-authenticate. 1366 */ 1367 result = RPCSEC_GSS_CREDPROBLEM; 1368 goto out; 1369 } 1370 } 1371 cc = rqst->rq_clntcred; 1372 cc->cc_client = client; 1373 cc->cc_service = gc.gc_svc; 1374 cc->cc_seq = gc.gc_seq; 1375 1376 /* 1377 * The service and sequence number must be ignored for 1378 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1379 */ 1380 if (gc.gc_proc != RPCSEC_GSS_INIT 1381 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1382 /* 1383 * Check for sequence number overflow. 1384 */ 1385 if (gc.gc_seq >= MAXSEQ) { 1386 result = RPCSEC_GSS_CTXPROBLEM; 1387 goto out; 1388 } 1389 1390 /* 1391 * Check for valid service. 1392 */ 1393 if (gc.gc_svc != rpc_gss_svc_none && 1394 gc.gc_svc != rpc_gss_svc_integrity && 1395 gc.gc_svc != rpc_gss_svc_privacy) { 1396 result = AUTH_BADCRED; 1397 goto out; 1398 } 1399 } 1400 1401 /* Handle RPCSEC_GSS control procedure. */ 1402 switch (gc.gc_proc) { 1403 1404 case RPCSEC_GSS_INIT: 1405 case RPCSEC_GSS_CONTINUE_INIT: 1406 if (rqst->rq_proc != NULLPROC) { 1407 result = AUTH_REJECTEDCRED; 1408 break; 1409 } 1410 1411 memset(&gr, 0, sizeof(gr)); 1412 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1413 result = AUTH_REJECTEDCRED; 1414 break; 1415 } 1416 1417 if (gr.gr_major == GSS_S_COMPLETE) { 1418 /* 1419 * We borrow the space for the call verf to 1420 * pack our reply verf. 1421 */ 1422 rqst->rq_verf = msg->rm_call.cb_verf; 1423 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1424 result = AUTH_REJECTEDCRED; 1425 break; 1426 } 1427 } else { 1428 rqst->rq_verf = _null_auth; 1429 } 1430 1431 call_stat = svc_sendreply(rqst, 1432 (xdrproc_t) xdr_rpc_gss_init_res, 1433 (caddr_t) &gr); 1434 1435 gss_release_buffer(&min_stat, &gr.gr_token); 1436 1437 if (!call_stat) { 1438 result = AUTH_FAILED; 1439 break; 1440 } 1441 1442 if (gr.gr_major == GSS_S_COMPLETE) 1443 client->cl_state = CLIENT_ESTABLISHED; 1444 1445 result = RPCSEC_GSS_NODISPATCH; 1446 break; 1447 1448 case RPCSEC_GSS_DATA: 1449 case RPCSEC_GSS_DESTROY: 1450 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1451 result = RPCSEC_GSS_NODISPATCH; 1452 break; 1453 } 1454 1455 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 1456 result = RPCSEC_GSS_CREDPROBLEM; 1457 break; 1458 } 1459 1460 /* 1461 * We borrow the space for the call verf to pack our 1462 * reply verf. 1463 */ 1464 rqst->rq_verf = msg->rm_call.cb_verf; 1465 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1466 result = RPCSEC_GSS_CTXPROBLEM; 1467 break; 1468 } 1469 1470 svc_rpc_gss_update_seq(client, gc.gc_seq); 1471 1472 /* 1473 * Change the SVCAUTH ops on the request to point at 1474 * our own code so that we can unwrap the arguments 1475 * and wrap the result. The caller will re-set this on 1476 * every request to point to a set of null wrap/unwrap 1477 * methods. Acquire an extra reference to the client 1478 * which will be released by svc_rpc_gss_release() 1479 * after the request has finished processing. 1480 */ 1481 refcount_acquire(&client->cl_refs); 1482 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1483 rqst->rq_auth.svc_ah_private = cc; 1484 1485 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1486 /* 1487 * We might be ready to do a callback to the server to 1488 * see if it wants to accept/reject the connection. 1489 */ 1490 sx_xlock(&client->cl_lock); 1491 if (!client->cl_done_callback) { 1492 client->cl_done_callback = TRUE; 1493 client->cl_qop = qop; 1494 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1495 client->cl_rawcred.mechanism, qop); 1496 if (!svc_rpc_gss_callback(client, rqst)) { 1497 result = AUTH_REJECTEDCRED; 1498 sx_xunlock(&client->cl_lock); 1499 break; 1500 } 1501 } 1502 sx_xunlock(&client->cl_lock); 1503 1504 /* 1505 * If the server has locked this client to a 1506 * particular service+qop pair, enforce that 1507 * restriction now. 1508 */ 1509 if (client->cl_locked) { 1510 if (client->cl_rawcred.service != gc.gc_svc) { 1511 result = AUTH_FAILED; 1512 break; 1513 } else if (client->cl_qop != qop) { 1514 result = AUTH_BADVERF; 1515 break; 1516 } 1517 } 1518 1519 /* 1520 * If the qop changed, look up the new qop 1521 * name for rawcred. 1522 */ 1523 if (client->cl_qop != qop) { 1524 client->cl_qop = qop; 1525 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1526 client->cl_rawcred.mechanism, qop); 1527 } 1528 1529 /* 1530 * Make sure we use the right service value 1531 * for unwrap/wrap. 1532 */ 1533 if (client->cl_rawcred.service != gc.gc_svc) { 1534 client->cl_rawcred.service = gc.gc_svc; 1535 svc_rpc_gss_set_flavor(client); 1536 } 1537 1538 result = AUTH_OK; 1539 } else { 1540 if (rqst->rq_proc != NULLPROC) { 1541 result = AUTH_REJECTEDCRED; 1542 break; 1543 } 1544 1545 call_stat = svc_sendreply(rqst, 1546 (xdrproc_t) xdr_void, (caddr_t) NULL); 1547 1548 if (!call_stat) { 1549 result = AUTH_FAILED; 1550 break; 1551 } 1552 1553 svc_rpc_gss_forget_client(client); 1554 1555 result = RPCSEC_GSS_NODISPATCH; 1556 break; 1557 } 1558 break; 1559 1560 default: 1561 result = AUTH_BADCRED; 1562 break; 1563 } 1564 out: 1565 if (client) 1566 svc_rpc_gss_release_client(client); 1567 1568 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1569 KGSS_CURVNET_RESTORE(); 1570 return (result); 1571 } 1572 1573 static bool_t 1574 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1575 { 1576 struct svc_rpc_gss_cookedcred *cc; 1577 struct svc_rpc_gss_client *client; 1578 1579 rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 1580 1581 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1582 client = cc->cc_client; 1583 if (client->cl_state != CLIENT_ESTABLISHED 1584 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1585 return (TRUE); 1586 } 1587 1588 return (xdr_rpc_gss_wrap_data(mp, 1589 client->cl_ctx, client->cl_qop, 1590 cc->cc_service, cc->cc_seq)); 1591 } 1592 1593 static bool_t 1594 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 1595 { 1596 struct svc_rpc_gss_cookedcred *cc; 1597 struct svc_rpc_gss_client *client; 1598 1599 rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1600 1601 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1602 client = cc->cc_client; 1603 if (client->cl_state != CLIENT_ESTABLISHED 1604 || cc->cc_service == rpc_gss_svc_none) { 1605 return (TRUE); 1606 } 1607 1608 return (xdr_rpc_gss_unwrap_data(mp, 1609 client->cl_ctx, client->cl_qop, 1610 cc->cc_service, cc->cc_seq)); 1611 } 1612 1613 static void 1614 svc_rpc_gss_release(SVCAUTH *auth) 1615 { 1616 struct svc_rpc_gss_cookedcred *cc; 1617 struct svc_rpc_gss_client *client; 1618 1619 rpc_gss_log_debug("in svc_rpc_gss_release()"); 1620 1621 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1622 client = cc->cc_client; 1623 svc_rpc_gss_release_client(client); 1624 } 1625