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