1 /*- 2 * Copyright (c) 2008 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 /*- 29 SPDX-License-Identifier: BSD-3-Clause 30 31 svc_rpcsec_gss.c 32 33 Copyright (c) 2000 The Regents of the University of Michigan. 34 All rights reserved. 35 36 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 37 All rights reserved, all wrongs reversed. 38 39 Redistribution and use in source and binary forms, with or without 40 modification, are permitted provided that the following conditions 41 are met: 42 43 1. Redistributions of source code must retain the above copyright 44 notice, this list of conditions and the following disclaimer. 45 2. Redistributions in binary form must reproduce the above copyright 46 notice, this list of conditions and the following disclaimer in the 47 documentation and/or other materials provided with the distribution. 48 3. Neither the name of the University nor the names of its 49 contributors may be used to endorse or promote products derived 50 from this software without specific prior written permission. 51 52 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 53 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 54 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 55 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 56 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 57 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 58 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 59 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 60 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 61 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 62 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 64 $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ 65 */ 66 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <string.h> 70 #include <pwd.h> 71 #include <grp.h> 72 #include <errno.h> 73 #include <unistd.h> 74 #include <sys/queue.h> 75 #include <rpc/rpc.h> 76 #include <rpc/rpcsec_gss.h> 77 #include "rpcsec_gss_int.h" 78 79 static bool_t svc_rpc_gss_initialised = FALSE; 80 81 static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 82 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 83 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 84 85 static struct svc_auth_ops svc_auth_gss_ops = { 86 svc_rpc_gss_wrap, 87 svc_rpc_gss_unwrap, 88 }; 89 90 struct svc_rpc_gss_callback { 91 SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 92 rpc_gss_callback_t cb_callback; 93 }; 94 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 95 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 96 97 struct svc_rpc_gss_svc_name { 98 SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; 99 char *sn_principal; 100 gss_OID sn_mech; 101 u_int sn_req_time; 102 gss_cred_id_t sn_cred; 103 u_int sn_program; 104 u_int sn_version; 105 }; 106 static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) 107 svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(svc_rpc_gss_svc_names); 108 109 enum svc_rpc_gss_client_state { 110 CLIENT_NEW, /* still authenticating */ 111 CLIENT_ESTABLISHED, /* context established */ 112 CLIENT_STALE /* garbage to collect */ 113 }; 114 115 #define SVC_RPC_GSS_SEQWINDOW 128 116 117 struct svc_rpc_gss_client { 118 TAILQ_ENTRY(svc_rpc_gss_client) cl_link; 119 TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; 120 uint32_t cl_id; 121 time_t cl_expiration; /* when to gc */ 122 enum svc_rpc_gss_client_state cl_state; /* client state */ 123 bool_t cl_locked; /* fixed service+qop */ 124 gss_ctx_id_t cl_ctx; /* context id */ 125 gss_cred_id_t cl_creds; /* delegated creds */ 126 gss_name_t cl_cname; /* client name */ 127 struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ 128 rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ 129 rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ 130 bool_t cl_done_callback; /* TRUE after call */ 131 void *cl_cookie; /* user cookie from callback */ 132 gid_t cl_gid_storage[NGRPS]; 133 gss_OID cl_mech; /* mechanism */ 134 gss_qop_t cl_qop; /* quality of protection */ 135 u_int cl_seq; /* current sequence number */ 136 u_int cl_win; /* sequence window size */ 137 u_int cl_seqlast; /* sequence window origin */ 138 uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ 139 gss_buffer_desc cl_verf; /* buffer for verf checksum */ 140 }; 141 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 142 143 #define CLIENT_HASH_SIZE 256 144 #define CLIENT_MAX 128 145 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 146 static struct svc_rpc_gss_client_list svc_rpc_gss_clients; 147 static size_t svc_rpc_gss_client_count; 148 static uint32_t svc_rpc_gss_next_clientid = 1; 149 150 #ifdef __GNUC__ 151 static void svc_rpc_gss_init(void) __attribute__ ((constructor)); 152 #endif 153 154 static void 155 svc_rpc_gss_init(void) 156 { 157 int i; 158 159 if (!svc_rpc_gss_initialised) { 160 for (i = 0; i < CLIENT_HASH_SIZE; i++) 161 TAILQ_INIT(&svc_rpc_gss_client_hash[i]); 162 TAILQ_INIT(&svc_rpc_gss_clients); 163 svc_auth_reg(RPCSEC_GSS, svc_rpc_gss); 164 svc_rpc_gss_initialised = TRUE; 165 } 166 } 167 168 bool_t 169 rpc_gss_set_callback(rpc_gss_callback_t *cb) 170 { 171 struct svc_rpc_gss_callback *scb; 172 173 scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); 174 if (!scb) { 175 _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); 176 return (FALSE); 177 } 178 scb->cb_callback = *cb; 179 SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); 180 181 return (TRUE); 182 } 183 184 bool_t 185 rpc_gss_set_svc_name(const char *principal, const char *mechanism, 186 u_int req_time, u_int program, u_int version) 187 { 188 OM_uint32 maj_stat, min_stat; 189 struct svc_rpc_gss_svc_name *sname; 190 gss_buffer_desc namebuf; 191 gss_name_t name; 192 gss_OID mech_oid; 193 gss_OID_set_desc oid_set; 194 gss_cred_id_t cred; 195 196 svc_rpc_gss_init(); 197 198 if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) 199 return (FALSE); 200 oid_set.count = 1; 201 oid_set.elements = mech_oid; 202 203 namebuf.value = (void *)(intptr_t) principal; 204 namebuf.length = strlen(principal); 205 206 maj_stat = gss_import_name(&min_stat, &namebuf, 207 GSS_C_NT_HOSTBASED_SERVICE, &name); 208 if (maj_stat != GSS_S_COMPLETE) 209 return (FALSE); 210 211 maj_stat = gss_acquire_cred(&min_stat, name, 212 req_time, &oid_set, GSS_C_ACCEPT, &cred, NULL, NULL); 213 if (maj_stat != GSS_S_COMPLETE) 214 return (FALSE); 215 216 gss_release_name(&min_stat, &name); 217 218 sname = malloc(sizeof(struct svc_rpc_gss_svc_name)); 219 if (!sname) 220 return (FALSE); 221 sname->sn_principal = strdup(principal); 222 sname->sn_mech = mech_oid; 223 sname->sn_req_time = req_time; 224 sname->sn_cred = cred; 225 sname->sn_program = program; 226 sname->sn_version = version; 227 SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); 228 229 return (TRUE); 230 } 231 232 bool_t 233 rpc_gss_get_principal_name(rpc_gss_principal_t *principal, 234 const char *mech, const char *name, const char *node, const char *domain) 235 { 236 OM_uint32 maj_stat, min_stat; 237 gss_OID mech_oid; 238 size_t namelen; 239 gss_buffer_desc buf; 240 gss_name_t gss_name, gss_mech_name; 241 rpc_gss_principal_t result; 242 243 svc_rpc_gss_init(); 244 245 if (!rpc_gss_mech_to_oid(mech, &mech_oid)) 246 return (FALSE); 247 248 /* 249 * Construct a gss_buffer containing the full name formatted 250 * as "name/node@domain" where node and domain are optional. 251 */ 252 namelen = strlen(name); 253 if (node) { 254 namelen += strlen(node) + 1; 255 } 256 if (domain) { 257 namelen += strlen(domain) + 1; 258 } 259 260 buf.value = mem_alloc(namelen); 261 buf.length = namelen; 262 strcpy((char *) buf.value, name); 263 if (node) { 264 strcat((char *) buf.value, "/"); 265 strcat((char *) buf.value, node); 266 } 267 if (domain) { 268 strcat((char *) buf.value, "@"); 269 strcat((char *) buf.value, domain); 270 } 271 272 /* 273 * Convert that to a gss_name_t and then convert that to a 274 * mechanism name in the selected mechanism. 275 */ 276 maj_stat = gss_import_name(&min_stat, &buf, 277 GSS_C_NT_USER_NAME, &gss_name); 278 mem_free(buf.value, buf.length); 279 if (maj_stat != GSS_S_COMPLETE) { 280 log_status("gss_import_name", mech_oid, maj_stat, min_stat); 281 return (FALSE); 282 } 283 maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, 284 &gss_mech_name); 285 if (maj_stat != GSS_S_COMPLETE) { 286 log_status("gss_canonicalize_name", mech_oid, maj_stat, 287 min_stat); 288 gss_release_name(&min_stat, &gss_name); 289 return (FALSE); 290 } 291 gss_release_name(&min_stat, &gss_name); 292 293 /* 294 * Export the mechanism name and use that to construct the 295 * rpc_gss_principal_t result. 296 */ 297 maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); 298 if (maj_stat != GSS_S_COMPLETE) { 299 log_status("gss_export_name", mech_oid, maj_stat, min_stat); 300 gss_release_name(&min_stat, &gss_mech_name); 301 return (FALSE); 302 } 303 gss_release_name(&min_stat, &gss_mech_name); 304 305 result = mem_alloc(sizeof(int) + buf.length); 306 if (!result) { 307 gss_release_buffer(&min_stat, &buf); 308 return (FALSE); 309 } 310 result->len = buf.length; 311 memcpy(result->name, buf.value, buf.length); 312 gss_release_buffer(&min_stat, &buf); 313 314 *principal = result; 315 return (TRUE); 316 } 317 318 bool_t 319 rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, 320 rpc_gss_ucred_t **ucred, void **cookie) 321 { 322 struct svc_rpc_gss_client *client; 323 324 if (req->rq_cred.oa_flavor != RPCSEC_GSS) 325 return (FALSE); 326 327 client = req->rq_clntcred; 328 if (rcred) 329 *rcred = &client->cl_rawcred; 330 if (ucred) 331 *ucred = &client->cl_ucred; 332 if (cookie) 333 *cookie = client->cl_cookie; 334 return (TRUE); 335 } 336 337 int 338 rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) 339 { 340 struct svc_rpc_gss_client *client = req->rq_clntcred; 341 int want_conf; 342 OM_uint32 max; 343 OM_uint32 maj_stat, min_stat; 344 int result; 345 346 switch (client->cl_rawcred.service) { 347 case rpc_gss_svc_none: 348 return (max_tp_unit_len); 349 break; 350 351 case rpc_gss_svc_default: 352 case rpc_gss_svc_integrity: 353 want_conf = FALSE; 354 break; 355 356 case rpc_gss_svc_privacy: 357 want_conf = TRUE; 358 break; 359 360 default: 361 return (0); 362 } 363 364 maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, 365 client->cl_qop, max_tp_unit_len, &max); 366 367 if (maj_stat == GSS_S_COMPLETE) { 368 result = (int) max; 369 if (result < 0) 370 result = 0; 371 return (result); 372 } else { 373 log_status("gss_wrap_size_limit", client->cl_mech, 374 maj_stat, min_stat); 375 return (0); 376 } 377 } 378 379 static struct svc_rpc_gss_client * 380 svc_rpc_gss_find_client(uint32_t clientid) 381 { 382 struct svc_rpc_gss_client *client; 383 struct svc_rpc_gss_client_list *list; 384 385 386 log_debug("in svc_rpc_gss_find_client(%d)", clientid); 387 388 list = &svc_rpc_gss_client_hash[clientid % CLIENT_HASH_SIZE]; 389 TAILQ_FOREACH(client, list, cl_link) { 390 if (client->cl_id == clientid) { 391 /* 392 * Move this client to the front of the LRU 393 * list. 394 */ 395 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 396 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, 397 cl_alllink); 398 return client; 399 } 400 } 401 402 return (NULL); 403 } 404 405 static struct svc_rpc_gss_client * 406 svc_rpc_gss_create_client(void) 407 { 408 struct svc_rpc_gss_client *client; 409 struct svc_rpc_gss_client_list *list; 410 411 log_debug("in svc_rpc_gss_create_client()"); 412 413 client = mem_alloc(sizeof(struct svc_rpc_gss_client)); 414 memset(client, 0, sizeof(struct svc_rpc_gss_client)); 415 client->cl_id = svc_rpc_gss_next_clientid++; 416 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 417 TAILQ_INSERT_HEAD(list, client, cl_link); 418 TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); 419 420 /* 421 * Start the client off with a short expiration time. We will 422 * try to get a saner value from the client creds later. 423 */ 424 client->cl_state = CLIENT_NEW; 425 client->cl_locked = FALSE; 426 client->cl_expiration = time(0) + 5*60; 427 svc_rpc_gss_client_count++; 428 429 return (client); 430 } 431 432 static void 433 svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) 434 { 435 struct svc_rpc_gss_client_list *list; 436 OM_uint32 min_stat; 437 438 log_debug("in svc_rpc_gss_destroy_client()"); 439 440 if (client->cl_ctx) 441 gss_delete_sec_context(&min_stat, 442 &client->cl_ctx, GSS_C_NO_BUFFER); 443 444 if (client->cl_cname) 445 gss_release_name(&min_stat, &client->cl_cname); 446 447 if (client->cl_rawcred.client_principal) 448 mem_free(client->cl_rawcred.client_principal, 449 sizeof(*client->cl_rawcred.client_principal) 450 + client->cl_rawcred.client_principal->len); 451 452 if (client->cl_verf.value) 453 gss_release_buffer(&min_stat, &client->cl_verf); 454 455 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 456 TAILQ_REMOVE(list, client, cl_link); 457 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 458 svc_rpc_gss_client_count--; 459 mem_free(client, sizeof(*client)); 460 } 461 462 static void 463 svc_rpc_gss_timeout_clients(void) 464 { 465 struct svc_rpc_gss_client *client; 466 struct svc_rpc_gss_client *nclient; 467 time_t now = time(0); 468 469 log_debug("in svc_rpc_gss_timeout_clients()"); 470 /* 471 * First enforce the max client limit. We keep 472 * svc_rpc_gss_clients in LRU order. 473 */ 474 while (svc_rpc_gss_client_count > CLIENT_MAX) 475 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 476 svc_rpc_gss_client_list)); 477 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 478 if (client->cl_state == CLIENT_STALE 479 || now > client->cl_expiration) { 480 log_debug("expiring client %p", client); 481 svc_rpc_gss_destroy_client(client); 482 } 483 } 484 } 485 486 #ifdef DEBUG 487 /* 488 * OID<->string routines. These are uuuuugly. 489 */ 490 static OM_uint32 491 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 492 { 493 char numstr[128]; 494 unsigned long number; 495 int numshift; 496 size_t string_length; 497 size_t i; 498 unsigned char *cp; 499 char *bp; 500 501 /* Decoded according to krb5/gssapi_krb5.c */ 502 503 /* First determine the size of the string */ 504 string_length = 0; 505 number = 0; 506 numshift = 0; 507 cp = (unsigned char *) oid->elements; 508 number = (unsigned long) cp[0]; 509 sprintf(numstr, "%ld ", number/40); 510 string_length += strlen(numstr); 511 sprintf(numstr, "%ld ", number%40); 512 string_length += strlen(numstr); 513 for (i=1; i<oid->length; i++) { 514 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 515 number = (number << 7) | (cp[i] & 0x7f); 516 numshift += 7; 517 } 518 else { 519 *minor_status = 0; 520 return(GSS_S_FAILURE); 521 } 522 if ((cp[i] & 0x80) == 0) { 523 sprintf(numstr, "%ld ", number); 524 string_length += strlen(numstr); 525 number = 0; 526 numshift = 0; 527 } 528 } 529 /* 530 * If we get here, we've calculated the length of "n n n ... n ". Add 4 531 * here for "{ " and "}\0". 532 */ 533 string_length += 4; 534 if ((bp = (char *) mem_alloc(string_length))) { 535 strcpy(bp, "{ "); 536 number = (unsigned long) cp[0]; 537 sprintf(numstr, "%ld ", number/40); 538 strcat(bp, numstr); 539 sprintf(numstr, "%ld ", number%40); 540 strcat(bp, numstr); 541 number = 0; 542 cp = (unsigned char *) oid->elements; 543 for (i=1; i<oid->length; i++) { 544 number = (number << 7) | (cp[i] & 0x7f); 545 if ((cp[i] & 0x80) == 0) { 546 sprintf(numstr, "%ld ", number); 547 strcat(bp, numstr); 548 number = 0; 549 } 550 } 551 strcat(bp, "}"); 552 oid_str->length = strlen(bp)+1; 553 oid_str->value = (void *) bp; 554 *minor_status = 0; 555 return(GSS_S_COMPLETE); 556 } 557 *minor_status = 0; 558 return(GSS_S_FAILURE); 559 } 560 #endif 561 562 static void 563 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 564 const gss_name_t name) 565 { 566 OM_uint32 maj_stat, min_stat; 567 char buf[128]; 568 uid_t uid; 569 struct passwd pwd, *pw; 570 rpc_gss_ucred_t *uc = &client->cl_ucred; 571 572 uc->uid = 65534; 573 uc->gid = 65534; 574 uc->gidlen = 0; 575 uc->gidlist = client->cl_gid_storage; 576 577 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 578 if (maj_stat != GSS_S_COMPLETE) 579 return; 580 581 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 582 if (pw) { 583 int len = NGRPS; 584 uc->uid = pw->pw_uid; 585 uc->gid = pw->pw_gid; 586 uc->gidlist = client->cl_gid_storage; 587 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 588 uc->gidlen = len; 589 } 590 } 591 592 static bool_t 593 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 594 struct svc_req *rqst, 595 struct rpc_gss_init_res *gr, 596 struct rpc_gss_cred *gc) 597 { 598 gss_buffer_desc recv_tok; 599 gss_OID mech; 600 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 601 OM_uint32 cred_lifetime; 602 struct svc_rpc_gss_svc_name *sname; 603 604 log_debug("in svc_rpc_gss_accept_context()"); 605 606 /* Deserialize arguments. */ 607 memset(&recv_tok, 0, sizeof(recv_tok)); 608 609 if (!svc_getargs(rqst->rq_xprt, 610 (xdrproc_t) xdr_gss_buffer_desc, 611 (caddr_t) &recv_tok)) { 612 client->cl_state = CLIENT_STALE; 613 return (FALSE); 614 } 615 616 /* 617 * First time round, try all the server names we have until 618 * one matches. Afterwards, stick with that one. 619 */ 620 if (!client->cl_sname) { 621 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 622 if (sname->sn_program == rqst->rq_prog 623 && sname->sn_version == rqst->rq_vers) { 624 gr->gr_major = gss_accept_sec_context( 625 &gr->gr_minor, 626 &client->cl_ctx, 627 sname->sn_cred, 628 &recv_tok, 629 GSS_C_NO_CHANNEL_BINDINGS, 630 &client->cl_cname, 631 &mech, 632 &gr->gr_token, 633 &ret_flags, 634 &cred_lifetime, 635 &client->cl_creds); 636 client->cl_sname = sname; 637 break; 638 } 639 } 640 if (!sname) { 641 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 642 (char *) &recv_tok); 643 return (FALSE); 644 } 645 } else { 646 gr->gr_major = gss_accept_sec_context( 647 &gr->gr_minor, 648 &client->cl_ctx, 649 client->cl_sname->sn_cred, 650 &recv_tok, 651 GSS_C_NO_CHANNEL_BINDINGS, 652 &client->cl_cname, 653 &mech, 654 &gr->gr_token, 655 &ret_flags, 656 &cred_lifetime, 657 NULL); 658 } 659 660 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 661 662 /* 663 * If we get an error from gss_accept_sec_context, send the 664 * reply anyway so that the client gets a chance to see what 665 * is wrong. 666 */ 667 if (gr->gr_major != GSS_S_COMPLETE && 668 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 669 log_status("accept_sec_context", client->cl_mech, 670 gr->gr_major, gr->gr_minor); 671 client->cl_state = CLIENT_STALE; 672 return (TRUE); 673 } 674 675 gr->gr_handle.value = &client->cl_id; 676 gr->gr_handle.length = sizeof(client->cl_id); 677 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 678 679 /* Save client info. */ 680 client->cl_mech = mech; 681 client->cl_qop = GSS_C_QOP_DEFAULT; 682 client->cl_seq = gc->gc_seq; 683 client->cl_win = gr->gr_win; 684 client->cl_done_callback = FALSE; 685 686 if (gr->gr_major == GSS_S_COMPLETE) { 687 gss_buffer_desc export_name; 688 689 /* 690 * Change client expiration time to be near when the 691 * client creds expire (or 24 hours if we can't figure 692 * that out). 693 */ 694 if (cred_lifetime == GSS_C_INDEFINITE) 695 cred_lifetime = time(0) + 24*60*60; 696 697 client->cl_expiration = time(0) + cred_lifetime; 698 699 /* 700 * Fill in cred details in the rawcred structure. 701 */ 702 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 703 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 704 maj_stat = gss_export_name(&min_stat, client->cl_cname, 705 &export_name); 706 if (maj_stat != GSS_S_COMPLETE) { 707 log_status("gss_export_name", client->cl_mech, 708 maj_stat, min_stat); 709 return (FALSE); 710 } 711 client->cl_rawcred.client_principal = 712 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 713 + export_name.length); 714 client->cl_rawcred.client_principal->len = export_name.length; 715 memcpy(client->cl_rawcred.client_principal->name, 716 export_name.value, export_name.length); 717 gss_release_buffer(&min_stat, &export_name); 718 client->cl_rawcred.svc_principal = 719 client->cl_sname->sn_principal; 720 client->cl_rawcred.service = gc->gc_svc; 721 722 /* 723 * Use gss_pname_to_uid to map to unix creds. For 724 * kerberos5, this uses krb5_aname_to_localname. 725 */ 726 svc_rpc_gss_build_ucred(client, client->cl_cname); 727 gss_release_name(&min_stat, &client->cl_cname); 728 729 #ifdef DEBUG 730 { 731 gss_buffer_desc mechname; 732 733 gss_oid_to_str(&min_stat, mech, &mechname); 734 735 log_debug("accepted context for %s with " 736 "<mech %.*s, qop %d, svc %d>", 737 client->cl_rawcred.client_principal->name, 738 mechname.length, (char *)mechname.value, 739 client->cl_qop, client->rawcred.service); 740 741 gss_release_buffer(&min_stat, &mechname); 742 } 743 #endif /* DEBUG */ 744 } 745 return (TRUE); 746 } 747 748 static bool_t 749 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 750 gss_qop_t *qop) 751 { 752 struct opaque_auth *oa; 753 gss_buffer_desc rpcbuf, checksum; 754 OM_uint32 maj_stat, min_stat; 755 gss_qop_t qop_state; 756 int32_t rpchdr[128 / sizeof(int32_t)]; 757 int32_t *buf; 758 759 log_debug("in svc_rpc_gss_validate()"); 760 761 memset(rpchdr, 0, sizeof(rpchdr)); 762 763 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 764 buf = rpchdr; 765 IXDR_PUT_LONG(buf, msg->rm_xid); 766 IXDR_PUT_ENUM(buf, msg->rm_direction); 767 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 768 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 769 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 770 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 771 oa = &msg->rm_call.cb_cred; 772 IXDR_PUT_ENUM(buf, oa->oa_flavor); 773 IXDR_PUT_LONG(buf, oa->oa_length); 774 if (oa->oa_length) { 775 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 776 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 777 } 778 rpcbuf.value = rpchdr; 779 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 780 781 checksum.value = msg->rm_call.cb_verf.oa_base; 782 checksum.length = msg->rm_call.cb_verf.oa_length; 783 784 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 785 &qop_state); 786 787 if (maj_stat != GSS_S_COMPLETE) { 788 log_status("gss_verify_mic", client->cl_mech, 789 maj_stat, min_stat); 790 client->cl_state = CLIENT_STALE; 791 return (FALSE); 792 } 793 *qop = qop_state; 794 return (TRUE); 795 } 796 797 static bool_t 798 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 799 struct svc_req *rqst, u_int seq) 800 { 801 gss_buffer_desc signbuf; 802 OM_uint32 maj_stat, min_stat; 803 uint32_t nseq; 804 805 log_debug("in svc_rpc_gss_nextverf()"); 806 807 nseq = htonl(seq); 808 signbuf.value = &nseq; 809 signbuf.length = sizeof(nseq); 810 811 if (client->cl_verf.value) 812 gss_release_buffer(&min_stat, &client->cl_verf); 813 814 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 815 &signbuf, &client->cl_verf); 816 817 if (maj_stat != GSS_S_COMPLETE) { 818 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 819 client->cl_state = CLIENT_STALE; 820 return (FALSE); 821 } 822 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 823 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 824 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 825 826 return (TRUE); 827 } 828 829 static bool_t 830 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 831 { 832 struct svc_rpc_gss_callback *scb; 833 rpc_gss_lock_t lock; 834 void *cookie; 835 bool_t cb_res; 836 bool_t result; 837 838 /* 839 * See if we have a callback for this guy. 840 */ 841 result = TRUE; 842 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 843 if (scb->cb_callback.program == rqst->rq_prog 844 && scb->cb_callback.version == rqst->rq_vers) { 845 /* 846 * This one matches. Call the callback and see 847 * if it wants to veto or something. 848 */ 849 lock.locked = FALSE; 850 lock.raw_cred = &client->cl_rawcred; 851 cb_res = scb->cb_callback.callback(rqst, 852 client->cl_creds, 853 client->cl_ctx, 854 &lock, 855 &cookie); 856 857 if (!cb_res) { 858 client->cl_state = CLIENT_STALE; 859 result = FALSE; 860 break; 861 } 862 863 /* 864 * The callback accepted the connection - it 865 * is responsible for freeing client->cl_creds 866 * now. 867 */ 868 client->cl_creds = GSS_C_NO_CREDENTIAL; 869 client->cl_locked = lock.locked; 870 client->cl_cookie = cookie; 871 return (TRUE); 872 } 873 } 874 875 /* 876 * Either no callback exists for this program/version or one 877 * of the callbacks rejected the connection. We just need to 878 * clean up the delegated client creds, if any. 879 */ 880 if (client->cl_creds) { 881 OM_uint32 min_ver; 882 gss_release_cred(&min_ver, &client->cl_creds); 883 } 884 return (result); 885 } 886 887 static bool_t 888 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 889 { 890 u_int32_t offset; 891 int word, bit; 892 893 if (seq <= client->cl_seqlast) { 894 /* 895 * The request sequence number is less than 896 * the largest we have seen so far. If it is 897 * outside the window or if we have seen a 898 * request with this sequence before, silently 899 * discard it. 900 */ 901 offset = client->cl_seqlast - seq; 902 if (offset >= SVC_RPC_GSS_SEQWINDOW) 903 return (FALSE); 904 word = offset / 32; 905 bit = offset % 32; 906 if (client->cl_seqmask[word] & (1 << bit)) 907 return (FALSE); 908 } 909 910 return (TRUE); 911 } 912 913 static void 914 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 915 { 916 int offset, i, word, bit; 917 uint32_t carry, newcarry; 918 uint32_t* maskp; 919 920 maskp = client->cl_seqmask; 921 if (seq > client->cl_seqlast) { 922 /* 923 * This request has a sequence number greater 924 * than any we have seen so far. Advance the 925 * seq window and set bit zero of the window 926 * (which corresponds to the new sequence 927 * number) 928 */ 929 offset = seq - client->cl_seqlast; 930 while (offset >= 32) { 931 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 932 i > 0; i--) { 933 maskp[i] = maskp[i-1]; 934 } 935 maskp[0] = 0; 936 offset -= 32; 937 } 938 if (offset > 0) { 939 carry = 0; 940 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 941 newcarry = maskp[i] >> (32 - offset); 942 maskp[i] = (maskp[i] << offset) | carry; 943 carry = newcarry; 944 } 945 } 946 maskp[0] |= 1; 947 client->cl_seqlast = seq; 948 } else { 949 offset = client->cl_seqlast - seq; 950 word = offset / 32; 951 bit = offset % 32; 952 maskp[word] |= (1 << bit); 953 } 954 955 } 956 957 enum auth_stat 958 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 959 960 { 961 OM_uint32 min_stat; 962 XDR xdrs; 963 struct svc_rpc_gss_client *client; 964 struct rpc_gss_cred gc; 965 struct rpc_gss_init_res gr; 966 gss_qop_t qop; 967 int call_stat; 968 enum auth_stat result; 969 970 log_debug("in svc_rpc_gss()"); 971 972 /* Garbage collect old clients. */ 973 svc_rpc_gss_timeout_clients(); 974 975 /* Initialize reply. */ 976 rqst->rq_xprt->xp_verf = _null_auth; 977 978 /* Deserialize client credentials. */ 979 if (rqst->rq_cred.oa_length <= 0) 980 return (AUTH_BADCRED); 981 982 memset(&gc, 0, sizeof(gc)); 983 984 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 985 rqst->rq_cred.oa_length, XDR_DECODE); 986 987 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 988 XDR_DESTROY(&xdrs); 989 return (AUTH_BADCRED); 990 } 991 XDR_DESTROY(&xdrs); 992 993 /* Check version. */ 994 if (gc.gc_version != RPCSEC_GSS_VERSION) { 995 result = AUTH_BADCRED; 996 goto out; 997 } 998 999 /* Check the proc and find the client (or create it) */ 1000 if (gc.gc_proc == RPCSEC_GSS_INIT) { 1001 if (gc.gc_handle.length != 0) { 1002 result = AUTH_BADCRED; 1003 goto out; 1004 } 1005 client = svc_rpc_gss_create_client(); 1006 } else { 1007 if (gc.gc_handle.length != sizeof(uint32_t)) { 1008 result = AUTH_BADCRED; 1009 goto out; 1010 } 1011 uint32_t *p = gc.gc_handle.value; 1012 client = svc_rpc_gss_find_client(*p); 1013 if (!client) { 1014 /* 1015 * Can't find the client - we may have 1016 * destroyed it - tell the other side to 1017 * re-authenticate. 1018 */ 1019 result = RPCSEC_GSS_CREDPROBLEM; 1020 goto out; 1021 } 1022 } 1023 rqst->rq_clntcred = client; 1024 1025 /* 1026 * The service and sequence number must be ignored for 1027 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1028 */ 1029 if (gc.gc_proc != RPCSEC_GSS_INIT 1030 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1031 /* 1032 * Check for sequence number overflow. 1033 */ 1034 if (gc.gc_seq >= MAXSEQ) { 1035 result = RPCSEC_GSS_CTXPROBLEM; 1036 goto out; 1037 } 1038 client->cl_seq = gc.gc_seq; 1039 1040 /* 1041 * Check for valid service. 1042 */ 1043 if (gc.gc_svc != rpc_gss_svc_none && 1044 gc.gc_svc != rpc_gss_svc_integrity && 1045 gc.gc_svc != rpc_gss_svc_privacy) { 1046 result = AUTH_BADCRED; 1047 goto out; 1048 } 1049 } 1050 1051 /* Handle RPCSEC_GSS control procedure. */ 1052 switch (gc.gc_proc) { 1053 1054 case RPCSEC_GSS_INIT: 1055 case RPCSEC_GSS_CONTINUE_INIT: 1056 if (rqst->rq_proc != NULLPROC) { 1057 result = AUTH_REJECTEDCRED; 1058 break; 1059 } 1060 1061 memset(&gr, 0, sizeof(gr)); 1062 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1063 result = AUTH_REJECTEDCRED; 1064 break; 1065 } 1066 1067 if (gr.gr_major == GSS_S_COMPLETE) { 1068 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1069 result = AUTH_REJECTEDCRED; 1070 break; 1071 } 1072 } else { 1073 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1074 rqst->rq_xprt->xp_verf.oa_length = 0; 1075 } 1076 1077 call_stat = svc_sendreply(rqst->rq_xprt, 1078 (xdrproc_t) xdr_rpc_gss_init_res, 1079 (caddr_t) &gr); 1080 1081 gss_release_buffer(&min_stat, &gr.gr_token); 1082 1083 if (!call_stat) { 1084 result = AUTH_FAILED; 1085 break; 1086 } 1087 1088 if (gr.gr_major == GSS_S_COMPLETE) 1089 client->cl_state = CLIENT_ESTABLISHED; 1090 1091 result = RPCSEC_GSS_NODISPATCH; 1092 break; 1093 1094 case RPCSEC_GSS_DATA: 1095 case RPCSEC_GSS_DESTROY: 1096 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1097 result = RPCSEC_GSS_NODISPATCH; 1098 break; 1099 } 1100 1101 if (!svc_rpc_gss_validate(client, msg, &qop)) { 1102 result = RPCSEC_GSS_CREDPROBLEM; 1103 break; 1104 } 1105 1106 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1107 result = RPCSEC_GSS_CTXPROBLEM; 1108 break; 1109 } 1110 1111 svc_rpc_gss_update_seq(client, gc.gc_seq); 1112 1113 /* 1114 * Change the SVCAUTH ops on the transport to point at 1115 * our own code so that we can unwrap the arguments 1116 * and wrap the result. The caller will re-set this on 1117 * every request to point to a set of null wrap/unwrap 1118 * methods. 1119 */ 1120 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1121 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1122 1123 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1124 /* 1125 * We might be ready to do a callback to the server to 1126 * see if it wants to accept/reject the connection. 1127 */ 1128 if (!client->cl_done_callback) { 1129 client->cl_done_callback = TRUE; 1130 client->cl_qop = qop; 1131 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1132 client->cl_rawcred.mechanism, qop); 1133 if (!svc_rpc_gss_callback(client, rqst)) { 1134 result = AUTH_REJECTEDCRED; 1135 break; 1136 } 1137 } 1138 1139 /* 1140 * If the server has locked this client to a 1141 * particular service+qop pair, enforce that 1142 * restriction now. 1143 */ 1144 if (client->cl_locked) { 1145 if (client->cl_rawcred.service != gc.gc_svc) { 1146 result = AUTH_FAILED; 1147 break; 1148 } else if (client->cl_qop != qop) { 1149 result = AUTH_BADVERF; 1150 break; 1151 } 1152 } 1153 1154 /* 1155 * If the qop changed, look up the new qop 1156 * name for rawcred. 1157 */ 1158 if (client->cl_qop != qop) { 1159 client->cl_qop = qop; 1160 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1161 client->cl_rawcred.mechanism, qop); 1162 } 1163 1164 /* 1165 * Make sure we use the right service value 1166 * for unwrap/wrap. 1167 */ 1168 client->cl_rawcred.service = gc.gc_svc; 1169 1170 result = AUTH_OK; 1171 } else { 1172 if (rqst->rq_proc != NULLPROC) { 1173 result = AUTH_REJECTEDCRED; 1174 break; 1175 } 1176 1177 call_stat = svc_sendreply(rqst->rq_xprt, 1178 (xdrproc_t) xdr_void, (caddr_t) NULL); 1179 1180 if (!call_stat) { 1181 result = AUTH_FAILED; 1182 break; 1183 } 1184 1185 svc_rpc_gss_destroy_client(client); 1186 1187 result = RPCSEC_GSS_NODISPATCH; 1188 break; 1189 } 1190 break; 1191 1192 default: 1193 result = AUTH_BADCRED; 1194 break; 1195 } 1196 out: 1197 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1198 return (result); 1199 } 1200 1201 bool_t 1202 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1203 { 1204 struct svc_rpc_gss_client *client; 1205 1206 log_debug("in svc_rpc_gss_wrap()"); 1207 1208 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1209 if (client->cl_state != CLIENT_ESTABLISHED 1210 || client->cl_rawcred.service == rpc_gss_svc_none) { 1211 return xdr_func(xdrs, xdr_ptr); 1212 } 1213 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1214 client->cl_ctx, client->cl_qop, 1215 client->cl_rawcred.service, client->cl_seq)); 1216 } 1217 1218 bool_t 1219 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1220 { 1221 struct svc_rpc_gss_client *client; 1222 1223 log_debug("in svc_rpc_gss_unwrap()"); 1224 1225 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1226 if (client->cl_state != CLIENT_ESTABLISHED 1227 || client->cl_rawcred.service == rpc_gss_svc_none) { 1228 return xdr_func(xdrs, xdr_ptr); 1229 } 1230 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1231 client->cl_ctx, client->cl_qop, 1232 client->cl_rawcred.service, client->cl_seq)); 1233 } 1234