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