svc_rpcsec_gss.c revision 344275
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 344275 2019-02-19 10:40:35Z trasz $"); 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 refcount_init(&client->cl_refs, 1); 563 sx_init(&client->cl_lock, "GSS-client"); 564 getcredhostid(curthread->td_ucred, &hostid); 565 client->cl_id.ci_hostid = hostid; 566 getboottime(&boottime); 567 client->cl_id.ci_boottime = boottime.tv_sec; 568 client->cl_id.ci_id = svc_rpc_gss_next_clientid++; 569 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 570 sx_xlock(&svc_rpc_gss_lock); 571 TAILQ_INSERT_HEAD(list, client, cl_link); 572 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 573 svc_rpc_gss_client_count++; 574 sx_xunlock(&svc_rpc_gss_lock); 575 576 /* 577 * Start the client off with a short expiration time. We will 578 * try to get a saner value from the client creds later. 579 */ 580 client->cl_state = CLIENT_NEW; 581 client->cl_locked = FALSE; 582 client->cl_expiration = time_uptime + 5*60; 583 584 return (client); 585} 586 587static void 588svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 589{ 590 OM_uint32 min_stat; 591 592 rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); 593 594 if (client->cl_ctx) 595 gss_delete_sec_context(&min_stat, 596 &client->cl_ctx, GSS_C_NO_BUFFER); 597 598 if (client->cl_cname) 599 gss_release_name(&min_stat, &client->cl_cname); 600 601 if (client->cl_rawcred.client_principal) 602 mem_free(client->cl_rawcred.client_principal, 603 sizeof(*client->cl_rawcred.client_principal) 604 + client->cl_rawcred.client_principal->len); 605 606 if (client->cl_cred) 607 crfree(client->cl_cred); 608 609 sx_destroy(&client->cl_lock); 610 mem_free(client, sizeof(*client)); 611} 612 613/* 614 * Drop a reference to a client and free it if that was the last reference. 615 */ 616static void 617svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) 618{ 619 620 if (!refcount_release(&client->cl_refs)) 621 return; 622 svc_rpc_gss_destroy_client(client); 623} 624 625/* 626 * Remove a client from our global lists. 627 * Must be called with svc_rpc_gss_lock held. 628 */ 629static void 630svc_rpc_gss_forget_client_locked(struct svc_rpc_gss_client *client) 631{ 632 struct svc_rpc_gss_client_list *list; 633 634 sx_assert(&svc_rpc_gss_lock, SX_XLOCKED); 635 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 636 TAILQ_REMOVE(list, client, cl_link); 637 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 638 svc_rpc_gss_client_count--; 639} 640 641/* 642 * Remove a client from our global lists and free it if we can. 643 */ 644static void 645svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) 646{ 647 struct svc_rpc_gss_client_list *list; 648 struct svc_rpc_gss_client *tclient; 649 650 list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; 651 sx_xlock(&svc_rpc_gss_lock); 652 TAILQ_FOREACH(tclient, list, cl_link) { 653 /* 654 * Make sure this client has not already been removed 655 * from the lists by svc_rpc_gss_forget_client() or 656 * svc_rpc_gss_forget_client_locked(). 657 */ 658 if (client == tclient) { 659 svc_rpc_gss_forget_client_locked(client); 660 sx_xunlock(&svc_rpc_gss_lock); 661 svc_rpc_gss_release_client(client); 662 return; 663 } 664 } 665 sx_xunlock(&svc_rpc_gss_lock); 666} 667 668static void 669svc_rpc_gss_timeout_clients(void) 670{ 671 struct svc_rpc_gss_client *client; 672 time_t now = time_uptime; 673 674 rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); 675 676 /* 677 * First enforce the max client limit. We keep 678 * svc_rpc_gss_clients in LRU order. 679 */ 680 sx_xlock(&svc_rpc_gss_lock); 681 client = TAILQ_LAST(&svc_rpc_gss_clients, svc_rpc_gss_client_list); 682 while (svc_rpc_gss_client_count > svc_rpc_gss_client_max && client != NULL) { 683 svc_rpc_gss_forget_client_locked(client); 684 sx_xunlock(&svc_rpc_gss_lock); 685 svc_rpc_gss_release_client(client); 686 sx_xlock(&svc_rpc_gss_lock); 687 client = TAILQ_LAST(&svc_rpc_gss_clients, 688 svc_rpc_gss_client_list); 689 } 690again: 691 TAILQ_FOREACH(client, &svc_rpc_gss_clients, cl_alllink) { 692 if (client->cl_state == CLIENT_STALE 693 || now > client->cl_expiration) { 694 svc_rpc_gss_forget_client_locked(client); 695 sx_xunlock(&svc_rpc_gss_lock); 696 rpc_gss_log_debug("expiring client %p", client); 697 svc_rpc_gss_release_client(client); 698 sx_xlock(&svc_rpc_gss_lock); 699 goto again; 700 } 701 } 702 sx_xunlock(&svc_rpc_gss_lock); 703} 704 705#ifdef DEBUG 706/* 707 * OID<->string routines. These are uuuuugly. 708 */ 709static OM_uint32 710gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 711{ 712 char numstr[128]; 713 unsigned long number; 714 int numshift; 715 size_t string_length; 716 size_t i; 717 unsigned char *cp; 718 char *bp; 719 720 /* Decoded according to krb5/gssapi_krb5.c */ 721 722 /* First determine the size of the string */ 723 string_length = 0; 724 number = 0; 725 numshift = 0; 726 cp = (unsigned char *) oid->elements; 727 number = (unsigned long) cp[0]; 728 sprintf(numstr, "%ld ", number/40); 729 string_length += strlen(numstr); 730 sprintf(numstr, "%ld ", number%40); 731 string_length += strlen(numstr); 732 for (i=1; i<oid->length; i++) { 733 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 734 number = (number << 7) | (cp[i] & 0x7f); 735 numshift += 7; 736 } 737 else { 738 *minor_status = 0; 739 return(GSS_S_FAILURE); 740 } 741 if ((cp[i] & 0x80) == 0) { 742 sprintf(numstr, "%ld ", number); 743 string_length += strlen(numstr); 744 number = 0; 745 numshift = 0; 746 } 747 } 748 /* 749 * If we get here, we've calculated the length of "n n n ... n ". Add 4 750 * here for "{ " and "}\0". 751 */ 752 string_length += 4; 753 if ((bp = (char *) mem_alloc(string_length))) { 754 strcpy(bp, "{ "); 755 number = (unsigned long) cp[0]; 756 sprintf(numstr, "%ld ", number/40); 757 strcat(bp, numstr); 758 sprintf(numstr, "%ld ", number%40); 759 strcat(bp, numstr); 760 number = 0; 761 cp = (unsigned char *) oid->elements; 762 for (i=1; i<oid->length; i++) { 763 number = (number << 7) | (cp[i] & 0x7f); 764 if ((cp[i] & 0x80) == 0) { 765 sprintf(numstr, "%ld ", number); 766 strcat(bp, numstr); 767 number = 0; 768 } 769 } 770 strcat(bp, "}"); 771 oid_str->length = strlen(bp)+1; 772 oid_str->value = (void *) bp; 773 *minor_status = 0; 774 return(GSS_S_COMPLETE); 775 } 776 *minor_status = 0; 777 return(GSS_S_FAILURE); 778} 779#endif 780 781static void 782svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 783 const gss_name_t name) 784{ 785 OM_uint32 maj_stat, min_stat; 786 rpc_gss_ucred_t *uc = &client->cl_ucred; 787 int numgroups; 788 789 uc->uid = 65534; 790 uc->gid = 65534; 791 uc->gidlist = client->cl_gid_storage; 792 793 numgroups = NGROUPS; 794 maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, 795 &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); 796 if (GSS_ERROR(maj_stat)) 797 uc->gidlen = 0; 798 else 799 uc->gidlen = numgroups; 800} 801 802static void 803svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) 804{ 805 static gss_OID_desc krb5_mech_oid = 806 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 807 808 /* 809 * Attempt to translate mech type and service into a 810 * 'pseudo flavor'. Hardwire in krb5 support for now. 811 */ 812 if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { 813 switch (client->cl_rawcred.service) { 814 case rpc_gss_svc_default: 815 case rpc_gss_svc_none: 816 client->cl_rpcflavor = RPCSEC_GSS_KRB5; 817 break; 818 case rpc_gss_svc_integrity: 819 client->cl_rpcflavor = RPCSEC_GSS_KRB5I; 820 break; 821 case rpc_gss_svc_privacy: 822 client->cl_rpcflavor = RPCSEC_GSS_KRB5P; 823 break; 824 } 825 } else { 826 client->cl_rpcflavor = RPCSEC_GSS; 827 } 828} 829 830static bool_t 831svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 832 struct svc_req *rqst, 833 struct rpc_gss_init_res *gr, 834 struct rpc_gss_cred *gc) 835{ 836 gss_buffer_desc recv_tok; 837 gss_OID mech; 838 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 839 OM_uint32 cred_lifetime; 840 struct svc_rpc_gss_svc_name *sname; 841 842 rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 843 844 /* Deserialize arguments. */ 845 memset(&recv_tok, 0, sizeof(recv_tok)); 846 847 if (!svc_getargs(rqst, 848 (xdrproc_t) xdr_gss_buffer_desc, 849 (caddr_t) &recv_tok)) { 850 client->cl_state = CLIENT_STALE; 851 return (FALSE); 852 } 853 854 /* 855 * First time round, try all the server names we have until 856 * one matches. Afterwards, stick with that one. 857 */ 858 sx_xlock(&svc_rpc_gss_lock); 859 if (!client->cl_sname) { 860 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 861 if (sname->sn_program == rqst->rq_prog 862 && sname->sn_version == rqst->rq_vers) { 863 retry: 864 gr->gr_major = gss_accept_sec_context( 865 &gr->gr_minor, 866 &client->cl_ctx, 867 sname->sn_cred, 868 &recv_tok, 869 GSS_C_NO_CHANNEL_BINDINGS, 870 &client->cl_cname, 871 &mech, 872 &gr->gr_token, 873 &ret_flags, 874 &cred_lifetime, 875 &client->cl_creds); 876 if (gr->gr_major == 877 GSS_S_CREDENTIALS_EXPIRED) { 878 /* 879 * Either our creds really did 880 * expire or gssd was 881 * restarted. 882 */ 883 if (rpc_gss_acquire_svc_cred(sname)) 884 goto retry; 885 } 886 client->cl_sname = sname; 887 break; 888 } 889 } 890 if (!sname) { 891 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 892 (char *) &recv_tok); 893 sx_xunlock(&svc_rpc_gss_lock); 894 return (FALSE); 895 } 896 } else { 897 gr->gr_major = gss_accept_sec_context( 898 &gr->gr_minor, 899 &client->cl_ctx, 900 client->cl_sname->sn_cred, 901 &recv_tok, 902 GSS_C_NO_CHANNEL_BINDINGS, 903 &client->cl_cname, 904 &mech, 905 &gr->gr_token, 906 &ret_flags, 907 &cred_lifetime, 908 NULL); 909 } 910 sx_xunlock(&svc_rpc_gss_lock); 911 912 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 913 914 /* 915 * If we get an error from gss_accept_sec_context, send the 916 * reply anyway so that the client gets a chance to see what 917 * is wrong. 918 */ 919 if (gr->gr_major != GSS_S_COMPLETE && 920 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 921 rpc_gss_log_status("accept_sec_context", client->cl_mech, 922 gr->gr_major, gr->gr_minor); 923 client->cl_state = CLIENT_STALE; 924 return (TRUE); 925 } 926 927 gr->gr_handle.value = &client->cl_id; 928 gr->gr_handle.length = sizeof(client->cl_id); 929 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 930 931 /* Save client info. */ 932 client->cl_mech = mech; 933 client->cl_qop = GSS_C_QOP_DEFAULT; 934 client->cl_done_callback = FALSE; 935 936 if (gr->gr_major == GSS_S_COMPLETE) { 937 gss_buffer_desc export_name; 938 939 /* 940 * Change client expiration time to be near when the 941 * client creds expire (or 24 hours if we can't figure 942 * that out). 943 */ 944 if (cred_lifetime == GSS_C_INDEFINITE) 945 cred_lifetime = time_uptime + 24*60*60; 946 947 client->cl_expiration = time_uptime + cred_lifetime; 948 949 /* 950 * Fill in cred details in the rawcred structure. 951 */ 952 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 953 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 954 maj_stat = gss_export_name(&min_stat, client->cl_cname, 955 &export_name); 956 if (maj_stat != GSS_S_COMPLETE) { 957 rpc_gss_log_status("gss_export_name", client->cl_mech, 958 maj_stat, min_stat); 959 return (FALSE); 960 } 961 client->cl_rawcred.client_principal = 962 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 963 + export_name.length); 964 client->cl_rawcred.client_principal->len = export_name.length; 965 memcpy(client->cl_rawcred.client_principal->name, 966 export_name.value, export_name.length); 967 gss_release_buffer(&min_stat, &export_name); 968 client->cl_rawcred.svc_principal = 969 client->cl_sname->sn_principal; 970 client->cl_rawcred.service = gc->gc_svc; 971 972 /* 973 * Use gss_pname_to_uid to map to unix creds. For 974 * kerberos5, this uses krb5_aname_to_localname. 975 */ 976 svc_rpc_gss_build_ucred(client, client->cl_cname); 977 svc_rpc_gss_set_flavor(client); 978 gss_release_name(&min_stat, &client->cl_cname); 979 980#ifdef DEBUG 981 { 982 gss_buffer_desc mechname; 983 984 gss_oid_to_str(&min_stat, mech, &mechname); 985 986 rpc_gss_log_debug("accepted context for %s with " 987 "<mech %.*s, qop %d, svc %d>", 988 client->cl_rawcred.client_principal->name, 989 mechname.length, (char *)mechname.value, 990 client->cl_qop, client->cl_rawcred.service); 991 992 gss_release_buffer(&min_stat, &mechname); 993 } 994#endif /* DEBUG */ 995 } 996 return (TRUE); 997} 998 999static bool_t 1000svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 1001 gss_qop_t *qop, rpc_gss_proc_t gcproc) 1002{ 1003 struct opaque_auth *oa; 1004 gss_buffer_desc rpcbuf, checksum; 1005 OM_uint32 maj_stat, min_stat; 1006 gss_qop_t qop_state; 1007 int32_t rpchdr[128 / sizeof(int32_t)]; 1008 int32_t *buf; 1009 1010 rpc_gss_log_debug("in svc_rpc_gss_validate()"); 1011 1012 memset(rpchdr, 0, sizeof(rpchdr)); 1013 1014 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 1015 buf = rpchdr; 1016 IXDR_PUT_LONG(buf, msg->rm_xid); 1017 IXDR_PUT_ENUM(buf, msg->rm_direction); 1018 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 1019 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 1020 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 1021 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 1022 oa = &msg->rm_call.cb_cred; 1023 IXDR_PUT_ENUM(buf, oa->oa_flavor); 1024 IXDR_PUT_LONG(buf, oa->oa_length); 1025 if (oa->oa_length) { 1026 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 1027 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1028 } 1029 rpcbuf.value = rpchdr; 1030 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 1031 1032 checksum.value = msg->rm_call.cb_verf.oa_base; 1033 checksum.length = msg->rm_call.cb_verf.oa_length; 1034 1035 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 1036 &qop_state); 1037 1038 if (maj_stat != GSS_S_COMPLETE) { 1039 rpc_gss_log_status("gss_verify_mic", client->cl_mech, 1040 maj_stat, min_stat); 1041 /* 1042 * A bug in some versions of the Linux client generates a 1043 * Destroy operation with a bogus encrypted checksum. Deleting 1044 * the credential handle for that case causes the mount to fail. 1045 * Since the checksum is bogus (gss_verify_mic() failed), it 1046 * doesn't make sense to destroy the handle and not doing so 1047 * fixes the Linux mount. 1048 */ 1049 if (gcproc != RPCSEC_GSS_DESTROY) 1050 client->cl_state = CLIENT_STALE; 1051 return (FALSE); 1052 } 1053 1054 *qop = qop_state; 1055 return (TRUE); 1056} 1057 1058static bool_t 1059svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 1060 struct svc_req *rqst, u_int seq) 1061{ 1062 gss_buffer_desc signbuf; 1063 gss_buffer_desc mic; 1064 OM_uint32 maj_stat, min_stat; 1065 uint32_t nseq; 1066 1067 rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1068 1069 nseq = htonl(seq); 1070 signbuf.value = &nseq; 1071 signbuf.length = sizeof(nseq); 1072 1073 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1074 &signbuf, &mic); 1075 1076 if (maj_stat != GSS_S_COMPLETE) { 1077 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1078 client->cl_state = CLIENT_STALE; 1079 return (FALSE); 1080 } 1081 1082 KASSERT(mic.length <= MAX_AUTH_BYTES, 1083 ("MIC too large for RPCSEC_GSS")); 1084 1085 rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1086 rqst->rq_verf.oa_length = mic.length; 1087 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1088 1089 gss_release_buffer(&min_stat, &mic); 1090 1091 return (TRUE); 1092} 1093 1094static bool_t 1095svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1096{ 1097 struct svc_rpc_gss_callback *scb; 1098 rpc_gss_lock_t lock; 1099 void *cookie; 1100 bool_t cb_res; 1101 bool_t result; 1102 1103 /* 1104 * See if we have a callback for this guy. 1105 */ 1106 result = TRUE; 1107 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 1108 if (scb->cb_callback.program == rqst->rq_prog 1109 && scb->cb_callback.version == rqst->rq_vers) { 1110 /* 1111 * This one matches. Call the callback and see 1112 * if it wants to veto or something. 1113 */ 1114 lock.locked = FALSE; 1115 lock.raw_cred = &client->cl_rawcred; 1116 cb_res = scb->cb_callback.callback(rqst, 1117 client->cl_creds, 1118 client->cl_ctx, 1119 &lock, 1120 &cookie); 1121 1122 if (!cb_res) { 1123 client->cl_state = CLIENT_STALE; 1124 result = FALSE; 1125 break; 1126 } 1127 1128 /* 1129 * The callback accepted the connection - it 1130 * is responsible for freeing client->cl_creds 1131 * now. 1132 */ 1133 client->cl_creds = GSS_C_NO_CREDENTIAL; 1134 client->cl_locked = lock.locked; 1135 client->cl_cookie = cookie; 1136 return (TRUE); 1137 } 1138 } 1139 1140 /* 1141 * Either no callback exists for this program/version or one 1142 * of the callbacks rejected the connection. We just need to 1143 * clean up the delegated client creds, if any. 1144 */ 1145 if (client->cl_creds) { 1146 OM_uint32 min_ver; 1147 gss_release_cred(&min_ver, &client->cl_creds); 1148 } 1149 return (result); 1150} 1151 1152static bool_t 1153svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1154{ 1155 u_int32_t offset; 1156 int word, bit; 1157 bool_t result; 1158 1159 sx_xlock(&client->cl_lock); 1160 if (seq <= client->cl_seqlast) { 1161 /* 1162 * The request sequence number is less than 1163 * the largest we have seen so far. If it is 1164 * outside the window or if we have seen a 1165 * request with this sequence before, silently 1166 * discard it. 1167 */ 1168 offset = client->cl_seqlast - seq; 1169 if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1170 result = FALSE; 1171 goto out; 1172 } 1173 word = offset / 32; 1174 bit = offset % 32; 1175 if (client->cl_seqmask[word] & (1 << bit)) { 1176 result = FALSE; 1177 goto out; 1178 } 1179 } 1180 1181 result = TRUE; 1182out: 1183 sx_xunlock(&client->cl_lock); 1184 return (result); 1185} 1186 1187static void 1188svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 1189{ 1190 int offset, i, word, bit; 1191 uint32_t carry, newcarry; 1192 1193 sx_xlock(&client->cl_lock); 1194 if (seq > client->cl_seqlast) { 1195 /* 1196 * This request has a sequence number greater 1197 * than any we have seen so far. Advance the 1198 * seq window and set bit zero of the window 1199 * (which corresponds to the new sequence 1200 * number) 1201 */ 1202 offset = seq - client->cl_seqlast; 1203 while (offset > 32) { 1204 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1205 i > 0; i--) { 1206 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 1207 } 1208 client->cl_seqmask[0] = 0; 1209 offset -= 32; 1210 } 1211 carry = 0; 1212 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1213 newcarry = client->cl_seqmask[i] >> (32 - offset); 1214 client->cl_seqmask[i] = 1215 (client->cl_seqmask[i] << offset) | carry; 1216 carry = newcarry; 1217 } 1218 client->cl_seqmask[0] |= 1; 1219 client->cl_seqlast = seq; 1220 } else { 1221 offset = client->cl_seqlast - seq; 1222 word = offset / 32; 1223 bit = offset % 32; 1224 client->cl_seqmask[word] |= (1 << bit); 1225 } 1226 sx_xunlock(&client->cl_lock); 1227} 1228 1229enum auth_stat 1230svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1231 1232{ 1233 OM_uint32 min_stat; 1234 XDR xdrs; 1235 struct svc_rpc_gss_cookedcred *cc; 1236 struct svc_rpc_gss_client *client; 1237 struct rpc_gss_cred gc; 1238 struct rpc_gss_init_res gr; 1239 gss_qop_t qop; 1240 int call_stat; 1241 enum auth_stat result; 1242 1243 rpc_gss_log_debug("in svc_rpc_gss()"); 1244 1245 /* Garbage collect old clients. */ 1246 svc_rpc_gss_timeout_clients(); 1247 1248 /* Initialize reply. */ 1249 rqst->rq_verf = _null_auth; 1250 1251 /* Deserialize client credentials. */ 1252 if (rqst->rq_cred.oa_length <= 0) 1253 return (AUTH_BADCRED); 1254 1255 memset(&gc, 0, sizeof(gc)); 1256 1257 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1258 rqst->rq_cred.oa_length, XDR_DECODE); 1259 1260 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1261 XDR_DESTROY(&xdrs); 1262 return (AUTH_BADCRED); 1263 } 1264 XDR_DESTROY(&xdrs); 1265 1266 client = NULL; 1267 1268 /* Check version. */ 1269 if (gc.gc_version != RPCSEC_GSS_VERSION) { 1270 result = AUTH_BADCRED; 1271 goto out; 1272 } 1273 1274 /* Check the proc and find the client (or create it) */ 1275 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1276 if (gc.gc_handle.length != 0) { 1277 result = AUTH_BADCRED; 1278 goto out; 1279 } 1280 client = svc_rpc_gss_create_client(); 1281 refcount_acquire(&client->cl_refs); 1282 } else { 1283 struct svc_rpc_gss_clientid *p; 1284 if (gc.gc_handle.length != sizeof(*p)) { 1285 result = AUTH_BADCRED; 1286 goto out; 1287 } 1288 p = gc.gc_handle.value; 1289 client = svc_rpc_gss_find_client(p); 1290 if (!client) { 1291 /* 1292 * Can't find the client - we may have 1293 * destroyed it - tell the other side to 1294 * re-authenticate. 1295 */ 1296 result = RPCSEC_GSS_CREDPROBLEM; 1297 goto out; 1298 } 1299 } 1300 cc = rqst->rq_clntcred; 1301 cc->cc_client = client; 1302 cc->cc_service = gc.gc_svc; 1303 cc->cc_seq = gc.gc_seq; 1304 1305 /* 1306 * The service and sequence number must be ignored for 1307 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1308 */ 1309 if (gc.gc_proc != RPCSEC_GSS_INIT 1310 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1311 /* 1312 * Check for sequence number overflow. 1313 */ 1314 if (gc.gc_seq >= MAXSEQ) { 1315 result = RPCSEC_GSS_CTXPROBLEM; 1316 goto out; 1317 } 1318 1319 /* 1320 * Check for valid service. 1321 */ 1322 if (gc.gc_svc != rpc_gss_svc_none && 1323 gc.gc_svc != rpc_gss_svc_integrity && 1324 gc.gc_svc != rpc_gss_svc_privacy) { 1325 result = AUTH_BADCRED; 1326 goto out; 1327 } 1328 } 1329 1330 /* Handle RPCSEC_GSS control procedure. */ 1331 switch (gc.gc_proc) { 1332 1333 case RPCSEC_GSS_INIT: 1334 case RPCSEC_GSS_CONTINUE_INIT: 1335 if (rqst->rq_proc != NULLPROC) { 1336 result = AUTH_REJECTEDCRED; 1337 break; 1338 } 1339 1340 memset(&gr, 0, sizeof(gr)); 1341 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1342 result = AUTH_REJECTEDCRED; 1343 break; 1344 } 1345 1346 if (gr.gr_major == GSS_S_COMPLETE) { 1347 /* 1348 * We borrow the space for the call verf to 1349 * pack our reply verf. 1350 */ 1351 rqst->rq_verf = msg->rm_call.cb_verf; 1352 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1353 result = AUTH_REJECTEDCRED; 1354 break; 1355 } 1356 } else { 1357 rqst->rq_verf = _null_auth; 1358 } 1359 1360 call_stat = svc_sendreply(rqst, 1361 (xdrproc_t) xdr_rpc_gss_init_res, 1362 (caddr_t) &gr); 1363 1364 gss_release_buffer(&min_stat, &gr.gr_token); 1365 1366 if (!call_stat) { 1367 result = AUTH_FAILED; 1368 break; 1369 } 1370 1371 if (gr.gr_major == GSS_S_COMPLETE) 1372 client->cl_state = CLIENT_ESTABLISHED; 1373 1374 result = RPCSEC_GSS_NODISPATCH; 1375 break; 1376 1377 case RPCSEC_GSS_DATA: 1378 case RPCSEC_GSS_DESTROY: 1379 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1380 result = RPCSEC_GSS_NODISPATCH; 1381 break; 1382 } 1383 1384 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 1385 result = RPCSEC_GSS_CREDPROBLEM; 1386 break; 1387 } 1388 1389 /* 1390 * We borrow the space for the call verf to pack our 1391 * reply verf. 1392 */ 1393 rqst->rq_verf = msg->rm_call.cb_verf; 1394 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1395 result = RPCSEC_GSS_CTXPROBLEM; 1396 break; 1397 } 1398 1399 svc_rpc_gss_update_seq(client, gc.gc_seq); 1400 1401 /* 1402 * Change the SVCAUTH ops on the request to point at 1403 * our own code so that we can unwrap the arguments 1404 * and wrap the result. The caller will re-set this on 1405 * every request to point to a set of null wrap/unwrap 1406 * methods. Acquire an extra reference to the client 1407 * which will be released by svc_rpc_gss_release() 1408 * after the request has finished processing. 1409 */ 1410 refcount_acquire(&client->cl_refs); 1411 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1412 rqst->rq_auth.svc_ah_private = cc; 1413 1414 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1415 /* 1416 * We might be ready to do a callback to the server to 1417 * see if it wants to accept/reject the connection. 1418 */ 1419 sx_xlock(&client->cl_lock); 1420 if (!client->cl_done_callback) { 1421 client->cl_done_callback = TRUE; 1422 client->cl_qop = qop; 1423 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1424 client->cl_rawcred.mechanism, qop); 1425 if (!svc_rpc_gss_callback(client, rqst)) { 1426 result = AUTH_REJECTEDCRED; 1427 sx_xunlock(&client->cl_lock); 1428 break; 1429 } 1430 } 1431 sx_xunlock(&client->cl_lock); 1432 1433 /* 1434 * If the server has locked this client to a 1435 * particular service+qop pair, enforce that 1436 * restriction now. 1437 */ 1438 if (client->cl_locked) { 1439 if (client->cl_rawcred.service != gc.gc_svc) { 1440 result = AUTH_FAILED; 1441 break; 1442 } else if (client->cl_qop != qop) { 1443 result = AUTH_BADVERF; 1444 break; 1445 } 1446 } 1447 1448 /* 1449 * If the qop changed, look up the new qop 1450 * name for rawcred. 1451 */ 1452 if (client->cl_qop != qop) { 1453 client->cl_qop = qop; 1454 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1455 client->cl_rawcred.mechanism, qop); 1456 } 1457 1458 /* 1459 * Make sure we use the right service value 1460 * for unwrap/wrap. 1461 */ 1462 if (client->cl_rawcred.service != gc.gc_svc) { 1463 client->cl_rawcred.service = gc.gc_svc; 1464 svc_rpc_gss_set_flavor(client); 1465 } 1466 1467 result = AUTH_OK; 1468 } else { 1469 if (rqst->rq_proc != NULLPROC) { 1470 result = AUTH_REJECTEDCRED; 1471 break; 1472 } 1473 1474 call_stat = svc_sendreply(rqst, 1475 (xdrproc_t) xdr_void, (caddr_t) NULL); 1476 1477 if (!call_stat) { 1478 result = AUTH_FAILED; 1479 break; 1480 } 1481 1482 svc_rpc_gss_forget_client(client); 1483 1484 result = RPCSEC_GSS_NODISPATCH; 1485 break; 1486 } 1487 break; 1488 1489 default: 1490 result = AUTH_BADCRED; 1491 break; 1492 } 1493out: 1494 if (client) 1495 svc_rpc_gss_release_client(client); 1496 1497 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1498 return (result); 1499} 1500 1501static bool_t 1502svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1503{ 1504 struct svc_rpc_gss_cookedcred *cc; 1505 struct svc_rpc_gss_client *client; 1506 1507 rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 1508 1509 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1510 client = cc->cc_client; 1511 if (client->cl_state != CLIENT_ESTABLISHED 1512 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1513 return (TRUE); 1514 } 1515 1516 return (xdr_rpc_gss_wrap_data(mp, 1517 client->cl_ctx, client->cl_qop, 1518 cc->cc_service, cc->cc_seq)); 1519} 1520 1521static bool_t 1522svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 1523{ 1524 struct svc_rpc_gss_cookedcred *cc; 1525 struct svc_rpc_gss_client *client; 1526 1527 rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1528 1529 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1530 client = cc->cc_client; 1531 if (client->cl_state != CLIENT_ESTABLISHED 1532 || cc->cc_service == rpc_gss_svc_none) { 1533 return (TRUE); 1534 } 1535 1536 return (xdr_rpc_gss_unwrap_data(mp, 1537 client->cl_ctx, client->cl_qop, 1538 cc->cc_service, cc->cc_seq)); 1539} 1540 1541static void 1542svc_rpc_gss_release(SVCAUTH *auth) 1543{ 1544 struct svc_rpc_gss_cookedcred *cc; 1545 struct svc_rpc_gss_client *client; 1546 1547 rpc_gss_log_debug("in svc_rpc_gss_release()"); 1548 1549 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1550 client = cc->cc_client; 1551 svc_rpc_gss_release_client(client); 1552} 1553