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