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