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