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