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