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