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