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