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