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