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