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