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