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