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