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