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(cr, uc->gidlen, uc->gidlist); 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 929 rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); 930 931 /* Deserialize arguments. */ 932 memset(&recv_tok, 0, sizeof(recv_tok)); 933 934 if (!svc_getargs(rqst, 935 (xdrproc_t) xdr_gss_buffer_desc, 936 (caddr_t) &recv_tok)) { 937 client->cl_state = CLIENT_STALE; 938 return (FALSE); 939 } 940 941 /* 942 * First time round, try all the server names we have until 943 * one matches. Afterwards, stick with that one. 944 */ 945 sx_xlock(&svc_rpc_gss_lock); 946 if (!client->cl_sname) { 947 SLIST_FOREACH(sname, &KGSS_VNET(svc_rpc_gss_svc_names), 948 sn_link) { 949 if (sname->sn_program == rqst->rq_prog 950 && sname->sn_version == rqst->rq_vers) { 951 retry: 952 gr->gr_major = gss_accept_sec_context( 953 &gr->gr_minor, 954 &client->cl_ctx, 955 sname->sn_cred, 956 &recv_tok, 957 GSS_C_NO_CHANNEL_BINDINGS, 958 &client->cl_cname, 959 &mech, 960 &gr->gr_token, 961 &ret_flags, 962 &cred_lifetime, 963 &client->cl_creds); 964 if (gr->gr_major == 965 GSS_S_CREDENTIALS_EXPIRED) { 966 /* 967 * Either our creds really did 968 * expire or gssd was 969 * restarted. 970 */ 971 if (rpc_gss_acquire_svc_cred(sname)) 972 goto retry; 973 } 974 client->cl_sname = sname; 975 break; 976 } 977 } 978 if (!sname) { 979 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 980 (char *) &recv_tok); 981 sx_xunlock(&svc_rpc_gss_lock); 982 return (FALSE); 983 } 984 } else { 985 gr->gr_major = gss_accept_sec_context( 986 &gr->gr_minor, 987 &client->cl_ctx, 988 client->cl_sname->sn_cred, 989 &recv_tok, 990 GSS_C_NO_CHANNEL_BINDINGS, 991 &client->cl_cname, 992 &mech, 993 &gr->gr_token, 994 &ret_flags, 995 &cred_lifetime, 996 NULL); 997 } 998 sx_xunlock(&svc_rpc_gss_lock); 999 1000 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 1001 1002 /* 1003 * If we get an error from gss_accept_sec_context, send the 1004 * reply anyway so that the client gets a chance to see what 1005 * is wrong. 1006 */ 1007 if (gr->gr_major != GSS_S_COMPLETE && 1008 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 1009 rpc_gss_log_status("accept_sec_context", client->cl_mech, 1010 gr->gr_major, gr->gr_minor); 1011 client->cl_state = CLIENT_STALE; 1012 return (TRUE); 1013 } 1014 1015 gr->gr_handle.value = &client->cl_id; 1016 gr->gr_handle.length = sizeof(client->cl_id); 1017 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 1018 1019 /* Save client info. */ 1020 client->cl_mech = mech; 1021 client->cl_qop = GSS_C_QOP_DEFAULT; 1022 client->cl_done_callback = FALSE; 1023 1024 if (gr->gr_major == GSS_S_COMPLETE) { 1025 gss_buffer_desc export_name; 1026 1027 /* 1028 * Change client expiration time to be near when the 1029 * client creds expire (or 24 hours if we can't figure 1030 * that out). 1031 */ 1032 if (cred_lifetime == GSS_C_INDEFINITE) 1033 cred_lifetime = 24*60*60; 1034 1035 /* 1036 * Cap cred_lifetime if sysctl kern.rpc.gss.lifetime_max is set. 1037 */ 1038 if (svc_rpc_gss_lifetime_max > 0 && cred_lifetime > 1039 svc_rpc_gss_lifetime_max) 1040 cred_lifetime = svc_rpc_gss_lifetime_max; 1041 1042 client->cl_expiration = time_uptime + cred_lifetime; 1043 1044 /* 1045 * Fill in cred details in the rawcred structure. 1046 */ 1047 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 1048 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 1049 maj_stat = gss_export_name(&min_stat, client->cl_cname, 1050 &export_name); 1051 if (maj_stat != GSS_S_COMPLETE) { 1052 rpc_gss_log_status("gss_export_name", client->cl_mech, 1053 maj_stat, min_stat); 1054 return (FALSE); 1055 } 1056 client->cl_rawcred.client_principal = 1057 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 1058 + export_name.length); 1059 client->cl_rawcred.client_principal->len = export_name.length; 1060 memcpy(client->cl_rawcred.client_principal->name, 1061 export_name.value, export_name.length); 1062 gss_release_buffer(&min_stat, &export_name); 1063 client->cl_rawcred.svc_principal = 1064 client->cl_sname->sn_principal; 1065 client->cl_rawcred.service = gc->gc_svc; 1066 1067 /* 1068 * Use gss_pname_to_uid to map to unix creds. For 1069 * kerberos5, this uses krb5_aname_to_localname. 1070 */ 1071 svc_rpc_gss_build_ucred(client, client->cl_cname); 1072 svc_rpc_gss_set_flavor(client); 1073 gss_release_name(&min_stat, &client->cl_cname); 1074 1075 #ifdef DEBUG 1076 { 1077 gss_buffer_desc mechname; 1078 1079 gss_oid_to_str(&min_stat, mech, &mechname); 1080 1081 rpc_gss_log_debug("accepted context for %s with " 1082 "<mech %.*s, qop %d, svc %d>", 1083 client->cl_rawcred.client_principal->name, 1084 mechname.length, (char *)mechname.value, 1085 client->cl_qop, client->cl_rawcred.service); 1086 1087 gss_release_buffer(&min_stat, &mechname); 1088 } 1089 #endif /* DEBUG */ 1090 } 1091 return (TRUE); 1092 } 1093 1094 static bool_t 1095 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 1096 gss_qop_t *qop, rpc_gss_proc_t gcproc) 1097 { 1098 struct opaque_auth *oa; 1099 gss_buffer_desc rpcbuf, checksum; 1100 OM_uint32 maj_stat, min_stat; 1101 gss_qop_t qop_state; 1102 int32_t rpchdr[128 / sizeof(int32_t)]; 1103 int32_t *buf; 1104 1105 rpc_gss_log_debug("in svc_rpc_gss_validate()"); 1106 1107 memset(rpchdr, 0, sizeof(rpchdr)); 1108 1109 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 1110 buf = rpchdr; 1111 IXDR_PUT_LONG(buf, msg->rm_xid); 1112 IXDR_PUT_ENUM(buf, msg->rm_direction); 1113 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 1114 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 1115 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 1116 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 1117 oa = &msg->rm_call.cb_cred; 1118 IXDR_PUT_ENUM(buf, oa->oa_flavor); 1119 IXDR_PUT_LONG(buf, oa->oa_length); 1120 if (oa->oa_length) { 1121 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 1122 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 1123 } 1124 rpcbuf.value = rpchdr; 1125 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 1126 1127 checksum.value = msg->rm_call.cb_verf.oa_base; 1128 checksum.length = msg->rm_call.cb_verf.oa_length; 1129 1130 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 1131 &qop_state); 1132 1133 if (maj_stat != GSS_S_COMPLETE) { 1134 rpc_gss_log_status("gss_verify_mic", client->cl_mech, 1135 maj_stat, min_stat); 1136 /* 1137 * A bug in some versions of the Linux client generates a 1138 * Destroy operation with a bogus encrypted checksum. Deleting 1139 * the credential handle for that case causes the mount to fail. 1140 * Since the checksum is bogus (gss_verify_mic() failed), it 1141 * doesn't make sense to destroy the handle and not doing so 1142 * fixes the Linux mount. 1143 */ 1144 if (gcproc != RPCSEC_GSS_DESTROY) 1145 client->cl_state = CLIENT_STALE; 1146 return (FALSE); 1147 } 1148 1149 *qop = qop_state; 1150 return (TRUE); 1151 } 1152 1153 static bool_t 1154 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 1155 struct svc_req *rqst, u_int seq) 1156 { 1157 gss_buffer_desc signbuf; 1158 gss_buffer_desc mic; 1159 OM_uint32 maj_stat, min_stat; 1160 uint32_t nseq; 1161 1162 rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); 1163 1164 nseq = htonl(seq); 1165 signbuf.value = &nseq; 1166 signbuf.length = sizeof(nseq); 1167 1168 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 1169 &signbuf, &mic); 1170 1171 if (maj_stat != GSS_S_COMPLETE) { 1172 rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 1173 client->cl_state = CLIENT_STALE; 1174 return (FALSE); 1175 } 1176 1177 KASSERT(mic.length <= MAX_AUTH_BYTES, 1178 ("MIC too large for RPCSEC_GSS")); 1179 1180 rqst->rq_verf.oa_flavor = RPCSEC_GSS; 1181 rqst->rq_verf.oa_length = mic.length; 1182 bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); 1183 1184 gss_release_buffer(&min_stat, &mic); 1185 1186 return (TRUE); 1187 } 1188 1189 static bool_t 1190 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 1191 { 1192 struct svc_rpc_gss_callback *scb; 1193 rpc_gss_lock_t lock; 1194 void *cookie; 1195 bool_t cb_res; 1196 bool_t result; 1197 1198 /* 1199 * See if we have a callback for this guy. 1200 */ 1201 result = TRUE; 1202 SLIST_FOREACH(scb, &KGSS_VNET(svc_rpc_gss_callbacks), cb_link) { 1203 if (scb->cb_callback.program == rqst->rq_prog 1204 && scb->cb_callback.version == rqst->rq_vers) { 1205 /* 1206 * This one matches. Call the callback and see 1207 * if it wants to veto or something. 1208 */ 1209 lock.locked = FALSE; 1210 lock.raw_cred = &client->cl_rawcred; 1211 cb_res = scb->cb_callback.callback(rqst, 1212 client->cl_creds, 1213 client->cl_ctx, 1214 &lock, 1215 &cookie); 1216 1217 if (!cb_res) { 1218 client->cl_state = CLIENT_STALE; 1219 result = FALSE; 1220 break; 1221 } 1222 1223 /* 1224 * The callback accepted the connection - it 1225 * is responsible for freeing client->cl_creds 1226 * now. 1227 */ 1228 client->cl_creds = GSS_C_NO_CREDENTIAL; 1229 client->cl_locked = lock.locked; 1230 client->cl_cookie = cookie; 1231 return (TRUE); 1232 } 1233 } 1234 1235 /* 1236 * Either no callback exists for this program/version or one 1237 * of the callbacks rejected the connection. We just need to 1238 * clean up the delegated client creds, if any. 1239 */ 1240 if (client->cl_creds) { 1241 OM_uint32 min_ver; 1242 gss_release_cred(&min_ver, &client->cl_creds); 1243 } 1244 return (result); 1245 } 1246 1247 static bool_t 1248 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 1249 { 1250 uint32_t offset; 1251 int word, bit; 1252 bool_t result; 1253 1254 sx_xlock(&client->cl_lock); 1255 if (seq <= client->cl_seqlast) { 1256 /* 1257 * The request sequence number is less than 1258 * the largest we have seen so far. If it is 1259 * outside the window or if we have seen a 1260 * request with this sequence before, silently 1261 * discard it. 1262 */ 1263 offset = client->cl_seqlast - seq; 1264 if (offset >= SVC_RPC_GSS_SEQWINDOW) { 1265 result = FALSE; 1266 goto out; 1267 } 1268 word = offset / 32; 1269 bit = offset % 32; 1270 if (client->cl_seqmask[word] & (1 << bit)) { 1271 result = FALSE; 1272 goto out; 1273 } 1274 } 1275 1276 result = TRUE; 1277 out: 1278 sx_xunlock(&client->cl_lock); 1279 return (result); 1280 } 1281 1282 static void 1283 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 1284 { 1285 int offset, i, word, bit; 1286 uint32_t carry, newcarry; 1287 1288 sx_xlock(&client->cl_lock); 1289 if (seq > client->cl_seqlast) { 1290 /* 1291 * This request has a sequence number greater 1292 * than any we have seen so far. Advance the 1293 * seq window and set bit zero of the window 1294 * (which corresponds to the new sequence 1295 * number) 1296 */ 1297 offset = seq - client->cl_seqlast; 1298 while (offset > 32) { 1299 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 1300 i > 0; i--) { 1301 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 1302 } 1303 client->cl_seqmask[0] = 0; 1304 offset -= 32; 1305 } 1306 carry = 0; 1307 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 1308 newcarry = client->cl_seqmask[i] >> (32 - offset); 1309 client->cl_seqmask[i] = 1310 (client->cl_seqmask[i] << offset) | carry; 1311 carry = newcarry; 1312 } 1313 client->cl_seqmask[0] |= 1; 1314 client->cl_seqlast = seq; 1315 } else { 1316 offset = client->cl_seqlast - seq; 1317 word = offset / 32; 1318 bit = offset % 32; 1319 client->cl_seqmask[word] |= (1 << bit); 1320 } 1321 sx_xunlock(&client->cl_lock); 1322 } 1323 1324 enum auth_stat 1325 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 1326 1327 { 1328 OM_uint32 min_stat; 1329 XDR xdrs; 1330 struct svc_rpc_gss_cookedcred *cc; 1331 struct svc_rpc_gss_client *client; 1332 struct rpc_gss_cred gc; 1333 struct rpc_gss_init_res gr; 1334 gss_qop_t qop; 1335 int call_stat; 1336 enum auth_stat result; 1337 1338 KGSS_CURVNET_SET_QUIET(KGSS_TD_TO_VNET(curthread)); 1339 rpc_gss_log_debug("in svc_rpc_gss()"); 1340 1341 /* Garbage collect old clients. */ 1342 svc_rpc_gss_timeout_clients(); 1343 1344 /* Initialize reply. */ 1345 rqst->rq_verf = _null_auth; 1346 1347 /* Deserialize client credentials. */ 1348 if (rqst->rq_cred.oa_length <= 0) { 1349 KGSS_CURVNET_RESTORE(); 1350 return (AUTH_BADCRED); 1351 } 1352 1353 memset(&gc, 0, sizeof(gc)); 1354 1355 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 1356 rqst->rq_cred.oa_length, XDR_DECODE); 1357 1358 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 1359 XDR_DESTROY(&xdrs); 1360 KGSS_CURVNET_RESTORE(); 1361 return (AUTH_BADCRED); 1362 } 1363 XDR_DESTROY(&xdrs); 1364 1365 client = NULL; 1366 1367 /* Check version. */ 1368 if (gc.gc_version != RPCSEC_GSS_VERSION) { 1369 result = AUTH_BADCRED; 1370 goto out; 1371 } 1372 1373 /* Check the proc and find the client (or create it) */ 1374 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1375 if (gc.gc_handle.length != 0) { 1376 result = AUTH_BADCRED; 1377 goto out; 1378 } 1379 client = svc_rpc_gss_create_client(); 1380 } else { 1381 struct svc_rpc_gss_clientid *p; 1382 if (gc.gc_handle.length != sizeof(*p)) { 1383 result = AUTH_BADCRED; 1384 goto out; 1385 } 1386 p = gc.gc_handle.value; 1387 client = svc_rpc_gss_find_client(p); 1388 if (!client) { 1389 /* 1390 * Can't find the client - we may have 1391 * destroyed it - tell the other side to 1392 * re-authenticate. 1393 */ 1394 result = RPCSEC_GSS_CREDPROBLEM; 1395 goto out; 1396 } 1397 } 1398 cc = rqst->rq_clntcred; 1399 cc->cc_client = client; 1400 cc->cc_service = gc.gc_svc; 1401 cc->cc_seq = gc.gc_seq; 1402 1403 /* 1404 * The service and sequence number must be ignored for 1405 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1406 */ 1407 if (gc.gc_proc != RPCSEC_GSS_INIT 1408 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1409 /* 1410 * Check for sequence number overflow. 1411 */ 1412 if (gc.gc_seq >= MAXSEQ) { 1413 result = RPCSEC_GSS_CTXPROBLEM; 1414 goto out; 1415 } 1416 1417 /* 1418 * Check for valid service. 1419 */ 1420 if (gc.gc_svc != rpc_gss_svc_none && 1421 gc.gc_svc != rpc_gss_svc_integrity && 1422 gc.gc_svc != rpc_gss_svc_privacy) { 1423 result = AUTH_BADCRED; 1424 goto out; 1425 } 1426 } 1427 1428 /* Handle RPCSEC_GSS control procedure. */ 1429 switch (gc.gc_proc) { 1430 1431 case RPCSEC_GSS_INIT: 1432 case RPCSEC_GSS_CONTINUE_INIT: 1433 if (rqst->rq_proc != NULLPROC) { 1434 result = AUTH_REJECTEDCRED; 1435 break; 1436 } 1437 1438 memset(&gr, 0, sizeof(gr)); 1439 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1440 result = AUTH_REJECTEDCRED; 1441 break; 1442 } 1443 1444 if (gr.gr_major == GSS_S_COMPLETE) { 1445 /* 1446 * We borrow the space for the call verf to 1447 * pack our reply verf. 1448 */ 1449 rqst->rq_verf = msg->rm_call.cb_verf; 1450 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1451 result = AUTH_REJECTEDCRED; 1452 break; 1453 } 1454 } else { 1455 rqst->rq_verf = _null_auth; 1456 } 1457 1458 call_stat = svc_sendreply(rqst, 1459 (xdrproc_t) xdr_rpc_gss_init_res, 1460 (caddr_t) &gr); 1461 1462 gss_release_buffer(&min_stat, &gr.gr_token); 1463 1464 if (!call_stat) { 1465 result = AUTH_FAILED; 1466 break; 1467 } 1468 1469 if (gr.gr_major == GSS_S_COMPLETE) 1470 client->cl_state = CLIENT_ESTABLISHED; 1471 1472 result = RPCSEC_GSS_NODISPATCH; 1473 break; 1474 1475 case RPCSEC_GSS_DATA: 1476 case RPCSEC_GSS_DESTROY: 1477 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1478 result = RPCSEC_GSS_NODISPATCH; 1479 break; 1480 } 1481 1482 if (!svc_rpc_gss_validate(client, msg, &qop, gc.gc_proc)) { 1483 result = RPCSEC_GSS_CREDPROBLEM; 1484 break; 1485 } 1486 1487 /* 1488 * We borrow the space for the call verf to pack our 1489 * reply verf. 1490 */ 1491 rqst->rq_verf = msg->rm_call.cb_verf; 1492 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1493 result = RPCSEC_GSS_CTXPROBLEM; 1494 break; 1495 } 1496 1497 svc_rpc_gss_update_seq(client, gc.gc_seq); 1498 1499 /* 1500 * Change the SVCAUTH ops on the request to point at 1501 * our own code so that we can unwrap the arguments 1502 * and wrap the result. The caller will re-set this on 1503 * every request to point to a set of null wrap/unwrap 1504 * methods. Acquire an extra reference to the client 1505 * which will be released by svc_rpc_gss_release() 1506 * after the request has finished processing. 1507 */ 1508 refcount_acquire(&client->cl_refs); 1509 rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; 1510 rqst->rq_auth.svc_ah_private = cc; 1511 1512 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1513 /* 1514 * We might be ready to do a callback to the server to 1515 * see if it wants to accept/reject the connection. 1516 */ 1517 sx_xlock(&client->cl_lock); 1518 if (!client->cl_done_callback) { 1519 client->cl_done_callback = TRUE; 1520 client->cl_qop = qop; 1521 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1522 client->cl_rawcred.mechanism, qop); 1523 if (!svc_rpc_gss_callback(client, rqst)) { 1524 result = AUTH_REJECTEDCRED; 1525 sx_xunlock(&client->cl_lock); 1526 break; 1527 } 1528 } 1529 sx_xunlock(&client->cl_lock); 1530 1531 /* 1532 * If the server has locked this client to a 1533 * particular service+qop pair, enforce that 1534 * restriction now. 1535 */ 1536 if (client->cl_locked) { 1537 if (client->cl_rawcred.service != gc.gc_svc) { 1538 result = AUTH_FAILED; 1539 break; 1540 } else if (client->cl_qop != qop) { 1541 result = AUTH_BADVERF; 1542 break; 1543 } 1544 } 1545 1546 /* 1547 * If the qop changed, look up the new qop 1548 * name for rawcred. 1549 */ 1550 if (client->cl_qop != qop) { 1551 client->cl_qop = qop; 1552 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1553 client->cl_rawcred.mechanism, qop); 1554 } 1555 1556 /* 1557 * Make sure we use the right service value 1558 * for unwrap/wrap. 1559 */ 1560 if (client->cl_rawcred.service != gc.gc_svc) { 1561 client->cl_rawcred.service = gc.gc_svc; 1562 svc_rpc_gss_set_flavor(client); 1563 } 1564 1565 result = AUTH_OK; 1566 } else { 1567 if (rqst->rq_proc != NULLPROC) { 1568 result = AUTH_REJECTEDCRED; 1569 break; 1570 } 1571 1572 call_stat = svc_sendreply(rqst, 1573 (xdrproc_t) xdr_void, (caddr_t) NULL); 1574 1575 if (!call_stat) { 1576 result = AUTH_FAILED; 1577 break; 1578 } 1579 1580 svc_rpc_gss_forget_client(client); 1581 1582 result = RPCSEC_GSS_NODISPATCH; 1583 break; 1584 } 1585 break; 1586 1587 default: 1588 result = AUTH_BADCRED; 1589 break; 1590 } 1591 out: 1592 if (client) 1593 svc_rpc_gss_release_client(client); 1594 1595 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1596 KGSS_CURVNET_RESTORE(); 1597 return (result); 1598 } 1599 1600 static bool_t 1601 svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) 1602 { 1603 struct svc_rpc_gss_cookedcred *cc; 1604 struct svc_rpc_gss_client *client; 1605 1606 rpc_gss_log_debug("in svc_rpc_gss_wrap()"); 1607 1608 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1609 client = cc->cc_client; 1610 if (client->cl_state != CLIENT_ESTABLISHED 1611 || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { 1612 return (TRUE); 1613 } 1614 1615 return (xdr_rpc_gss_wrap_data(mp, 1616 client->cl_ctx, client->cl_qop, 1617 cc->cc_service, cc->cc_seq)); 1618 } 1619 1620 static bool_t 1621 svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) 1622 { 1623 struct svc_rpc_gss_cookedcred *cc; 1624 struct svc_rpc_gss_client *client; 1625 1626 rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); 1627 1628 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1629 client = cc->cc_client; 1630 if (client->cl_state != CLIENT_ESTABLISHED 1631 || cc->cc_service == rpc_gss_svc_none) { 1632 return (TRUE); 1633 } 1634 1635 return (xdr_rpc_gss_unwrap_data(mp, 1636 client->cl_ctx, client->cl_qop, 1637 cc->cc_service, cc->cc_seq)); 1638 } 1639 1640 static void 1641 svc_rpc_gss_release(SVCAUTH *auth) 1642 { 1643 struct svc_rpc_gss_cookedcred *cc; 1644 struct svc_rpc_gss_client *client; 1645 1646 rpc_gss_log_debug("in svc_rpc_gss_release()"); 1647 1648 cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; 1649 client = cc->cc_client; 1650 svc_rpc_gss_release_client(client); 1651 } 1652