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