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