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