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