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