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