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 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 77 static bool_t svc_rpc_gss_initialised = FALSE; 78 79 static bool_t svc_rpc_gss_wrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 80 static bool_t svc_rpc_gss_unwrap(SVCAUTH *, XDR *, xdrproc_t, caddr_t); 81 static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); 82 83 static struct svc_auth_ops svc_auth_gss_ops = { 84 svc_rpc_gss_wrap, 85 svc_rpc_gss_unwrap, 86 }; 87 88 struct svc_rpc_gss_callback { 89 SLIST_ENTRY(svc_rpc_gss_callback) cb_link; 90 rpc_gss_callback_t cb_callback; 91 }; 92 static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) 93 svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(svc_rpc_gss_callbacks); 94 95 struct 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 }; 104 static 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 107 enum 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 115 struct 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 }; 139 TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); 140 141 #define CLIENT_HASH_SIZE 256 142 #define CLIENT_MAX 128 143 static struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; 144 static struct svc_rpc_gss_client_list svc_rpc_gss_clients; 145 static size_t svc_rpc_gss_client_count; 146 static uint32_t svc_rpc_gss_next_clientid = 1; 147 148 #ifdef __GNUC__ 149 static void svc_rpc_gss_init(void) __attribute__ ((constructor)); 150 #endif 151 152 static void 153 svc_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 166 bool_t 167 rpc_gss_set_callback(rpc_gss_callback_t *cb) 168 { 169 struct svc_rpc_gss_callback *scb; 170 171 scb = mem_alloc(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 182 bool_t 183 rpc_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 230 bool_t 231 rpc_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 = mem_alloc(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 mem_free(buf.value, buf.length); 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 = mem_alloc(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 316 bool_t 317 rpc_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 335 int 336 rpc_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 377 static struct svc_rpc_gss_client * 378 svc_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 403 static struct svc_rpc_gss_client * 404 svc_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 430 static void 431 svc_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 mem_free(client->cl_rawcred.client_principal, 447 sizeof(*client->cl_rawcred.client_principal) 448 + client->cl_rawcred.client_principal->len); 449 450 if (client->cl_verf.value) 451 gss_release_buffer(&min_stat, &client->cl_verf); 452 453 list = &svc_rpc_gss_client_hash[client->cl_id % CLIENT_HASH_SIZE]; 454 TAILQ_REMOVE(list, client, cl_link); 455 TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); 456 svc_rpc_gss_client_count--; 457 mem_free(client, sizeof(*client)); 458 } 459 460 static void 461 svc_rpc_gss_timeout_clients(void) 462 { 463 struct svc_rpc_gss_client *client; 464 struct svc_rpc_gss_client *nclient; 465 time_t now = time(0); 466 467 log_debug("in svc_rpc_gss_timeout_clients()"); 468 /* 469 * First enforce the max client limit. We keep 470 * svc_rpc_gss_clients in LRU order. 471 */ 472 while (svc_rpc_gss_client_count > CLIENT_MAX) 473 svc_rpc_gss_destroy_client(TAILQ_LAST(&svc_rpc_gss_clients, 474 svc_rpc_gss_client_list)); 475 TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { 476 if (client->cl_state == CLIENT_STALE 477 || now > client->cl_expiration) { 478 log_debug("expiring client %p", client); 479 svc_rpc_gss_destroy_client(client); 480 } 481 } 482 } 483 484 #ifdef DEBUG 485 /* 486 * OID<->string routines. These are uuuuugly. 487 */ 488 static OM_uint32 489 gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) 490 { 491 char numstr[128]; 492 unsigned long number; 493 int numshift; 494 size_t string_length; 495 size_t i; 496 unsigned char *cp; 497 char *bp; 498 499 /* Decoded according to krb5/gssapi_krb5.c */ 500 501 /* First determine the size of the string */ 502 string_length = 0; 503 number = 0; 504 numshift = 0; 505 cp = (unsigned char *) oid->elements; 506 number = (unsigned long) cp[0]; 507 sprintf(numstr, "%ld ", number/40); 508 string_length += strlen(numstr); 509 sprintf(numstr, "%ld ", number%40); 510 string_length += strlen(numstr); 511 for (i=1; i<oid->length; i++) { 512 if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { 513 number = (number << 7) | (cp[i] & 0x7f); 514 numshift += 7; 515 } 516 else { 517 *minor_status = 0; 518 return(GSS_S_FAILURE); 519 } 520 if ((cp[i] & 0x80) == 0) { 521 sprintf(numstr, "%ld ", number); 522 string_length += strlen(numstr); 523 number = 0; 524 numshift = 0; 525 } 526 } 527 /* 528 * If we get here, we've calculated the length of "n n n ... n ". Add 4 529 * here for "{ " and "}\0". 530 */ 531 string_length += 4; 532 if ((bp = (char *) mem_alloc(string_length))) { 533 strcpy(bp, "{ "); 534 number = (unsigned long) cp[0]; 535 sprintf(numstr, "%ld ", number/40); 536 strcat(bp, numstr); 537 sprintf(numstr, "%ld ", number%40); 538 strcat(bp, numstr); 539 number = 0; 540 cp = (unsigned char *) oid->elements; 541 for (i=1; i<oid->length; i++) { 542 number = (number << 7) | (cp[i] & 0x7f); 543 if ((cp[i] & 0x80) == 0) { 544 sprintf(numstr, "%ld ", number); 545 strcat(bp, numstr); 546 number = 0; 547 } 548 } 549 strcat(bp, "}"); 550 oid_str->length = strlen(bp)+1; 551 oid_str->value = (void *) bp; 552 *minor_status = 0; 553 return(GSS_S_COMPLETE); 554 } 555 *minor_status = 0; 556 return(GSS_S_FAILURE); 557 } 558 #endif 559 560 static void 561 svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, 562 const gss_name_t name) 563 { 564 OM_uint32 maj_stat, min_stat; 565 char buf[128]; 566 uid_t uid; 567 struct passwd pwd, *pw; 568 rpc_gss_ucred_t *uc = &client->cl_ucred; 569 570 uc->uid = 65534; 571 uc->gid = 65534; 572 uc->gidlen = 0; 573 uc->gidlist = client->cl_gid_storage; 574 575 maj_stat = gss_pname_to_uid(&min_stat, name, client->cl_mech, &uid); 576 if (maj_stat != GSS_S_COMPLETE) 577 return; 578 579 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 580 if (pw) { 581 int len = NGRPS; 582 uc->uid = pw->pw_uid; 583 uc->gid = pw->pw_gid; 584 uc->gidlist = client->cl_gid_storage; 585 getgrouplist(pw->pw_name, pw->pw_gid, uc->gidlist, &len); 586 uc->gidlen = len; 587 } 588 } 589 590 static bool_t 591 svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, 592 struct svc_req *rqst, 593 struct rpc_gss_init_res *gr, 594 struct rpc_gss_cred *gc) 595 { 596 gss_buffer_desc recv_tok; 597 gss_OID mech; 598 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; 599 OM_uint32 cred_lifetime; 600 struct svc_rpc_gss_svc_name *sname; 601 602 log_debug("in svc_rpc_gss_accept_context()"); 603 604 /* Deserialize arguments. */ 605 memset(&recv_tok, 0, sizeof(recv_tok)); 606 607 if (!svc_getargs(rqst->rq_xprt, 608 (xdrproc_t) xdr_gss_buffer_desc, 609 (caddr_t) &recv_tok)) { 610 client->cl_state = CLIENT_STALE; 611 return (FALSE); 612 } 613 614 /* 615 * First time round, try all the server names we have until 616 * one matches. Afterwards, stick with that one. 617 */ 618 if (!client->cl_sname) { 619 SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { 620 if (sname->sn_program == rqst->rq_prog 621 && sname->sn_version == rqst->rq_vers) { 622 gr->gr_major = gss_accept_sec_context( 623 &gr->gr_minor, 624 &client->cl_ctx, 625 sname->sn_cred, 626 &recv_tok, 627 GSS_C_NO_CHANNEL_BINDINGS, 628 &client->cl_cname, 629 &mech, 630 &gr->gr_token, 631 &ret_flags, 632 &cred_lifetime, 633 &client->cl_creds); 634 client->cl_sname = sname; 635 break; 636 } 637 } 638 if (!sname) { 639 xdr_free((xdrproc_t) xdr_gss_buffer_desc, 640 (char *) &recv_tok); 641 return (FALSE); 642 } 643 } else { 644 gr->gr_major = gss_accept_sec_context( 645 &gr->gr_minor, 646 &client->cl_ctx, 647 client->cl_sname->sn_cred, 648 &recv_tok, 649 GSS_C_NO_CHANNEL_BINDINGS, 650 &client->cl_cname, 651 &mech, 652 &gr->gr_token, 653 &ret_flags, 654 &cred_lifetime, 655 NULL); 656 } 657 658 xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); 659 660 /* 661 * If we get an error from gss_accept_sec_context, send the 662 * reply anyway so that the client gets a chance to see what 663 * is wrong. 664 */ 665 if (gr->gr_major != GSS_S_COMPLETE && 666 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 667 log_status("accept_sec_context", client->cl_mech, 668 gr->gr_major, gr->gr_minor); 669 client->cl_state = CLIENT_STALE; 670 return (TRUE); 671 } 672 673 gr->gr_handle.value = &client->cl_id; 674 gr->gr_handle.length = sizeof(client->cl_id); 675 gr->gr_win = SVC_RPC_GSS_SEQWINDOW; 676 677 /* Save client info. */ 678 client->cl_mech = mech; 679 client->cl_qop = GSS_C_QOP_DEFAULT; 680 client->cl_seq = gc->gc_seq; 681 client->cl_win = gr->gr_win; 682 client->cl_done_callback = FALSE; 683 684 if (gr->gr_major == GSS_S_COMPLETE) { 685 gss_buffer_desc export_name; 686 687 /* 688 * Change client expiration time to be near when the 689 * client creds expire (or 24 hours if we can't figure 690 * that out). 691 */ 692 if (cred_lifetime == GSS_C_INDEFINITE) 693 cred_lifetime = time(0) + 24*60*60; 694 695 client->cl_expiration = time(0) + cred_lifetime; 696 697 /* 698 * Fill in cred details in the rawcred structure. 699 */ 700 client->cl_rawcred.version = RPCSEC_GSS_VERSION; 701 rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); 702 maj_stat = gss_export_name(&min_stat, client->cl_cname, 703 &export_name); 704 if (maj_stat != GSS_S_COMPLETE) { 705 log_status("gss_export_name", client->cl_mech, 706 maj_stat, min_stat); 707 return (FALSE); 708 } 709 client->cl_rawcred.client_principal = 710 mem_alloc(sizeof(*client->cl_rawcred.client_principal) 711 + export_name.length); 712 client->cl_rawcred.client_principal->len = export_name.length; 713 memcpy(client->cl_rawcred.client_principal->name, 714 export_name.value, export_name.length); 715 gss_release_buffer(&min_stat, &export_name); 716 client->cl_rawcred.svc_principal = 717 client->cl_sname->sn_principal; 718 client->cl_rawcred.service = gc->gc_svc; 719 720 /* 721 * Use gss_pname_to_uid to map to unix creds. For 722 * kerberos5, this uses krb5_aname_to_localname. 723 */ 724 svc_rpc_gss_build_ucred(client, client->cl_cname); 725 gss_release_name(&min_stat, &client->cl_cname); 726 727 #ifdef DEBUG 728 { 729 gss_buffer_desc mechname; 730 731 gss_oid_to_str(&min_stat, mech, &mechname); 732 733 log_debug("accepted context for %s with " 734 "<mech %.*s, qop %d, svc %d>", 735 client->cl_rawcred.client_principal->name, 736 mechname.length, (char *)mechname.value, 737 client->cl_qop, client->rawcred.service); 738 739 gss_release_buffer(&min_stat, &mechname); 740 } 741 #endif /* DEBUG */ 742 } 743 return (TRUE); 744 } 745 746 static bool_t 747 svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, 748 gss_qop_t *qop) 749 { 750 struct opaque_auth *oa; 751 gss_buffer_desc rpcbuf, checksum; 752 OM_uint32 maj_stat, min_stat; 753 gss_qop_t qop_state; 754 int32_t rpchdr[128 / sizeof(int32_t)]; 755 int32_t *buf; 756 757 log_debug("in svc_rpc_gss_validate()"); 758 759 memset(rpchdr, 0, sizeof(rpchdr)); 760 761 /* Reconstruct RPC header for signing (from xdr_callmsg). */ 762 buf = rpchdr; 763 IXDR_PUT_LONG(buf, msg->rm_xid); 764 IXDR_PUT_ENUM(buf, msg->rm_direction); 765 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 766 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 767 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 768 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 769 oa = &msg->rm_call.cb_cred; 770 IXDR_PUT_ENUM(buf, oa->oa_flavor); 771 IXDR_PUT_LONG(buf, oa->oa_length); 772 if (oa->oa_length) { 773 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 774 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 775 } 776 rpcbuf.value = rpchdr; 777 rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; 778 779 checksum.value = msg->rm_call.cb_verf.oa_base; 780 checksum.length = msg->rm_call.cb_verf.oa_length; 781 782 maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, 783 &qop_state); 784 785 if (maj_stat != GSS_S_COMPLETE) { 786 log_status("gss_verify_mic", client->cl_mech, 787 maj_stat, min_stat); 788 client->cl_state = CLIENT_STALE; 789 return (FALSE); 790 } 791 *qop = qop_state; 792 return (TRUE); 793 } 794 795 static bool_t 796 svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, 797 struct svc_req *rqst, u_int seq) 798 { 799 gss_buffer_desc signbuf; 800 OM_uint32 maj_stat, min_stat; 801 uint32_t nseq; 802 803 log_debug("in svc_rpc_gss_nextverf()"); 804 805 nseq = htonl(seq); 806 signbuf.value = &nseq; 807 signbuf.length = sizeof(nseq); 808 809 if (client->cl_verf.value) 810 gss_release_buffer(&min_stat, &client->cl_verf); 811 812 maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, 813 &signbuf, &client->cl_verf); 814 815 if (maj_stat != GSS_S_COMPLETE) { 816 log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); 817 client->cl_state = CLIENT_STALE; 818 return (FALSE); 819 } 820 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 821 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)client->cl_verf.value; 822 rqst->rq_xprt->xp_verf.oa_length = (u_int)client->cl_verf.length; 823 824 return (TRUE); 825 } 826 827 static bool_t 828 svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) 829 { 830 struct svc_rpc_gss_callback *scb; 831 rpc_gss_lock_t lock; 832 void *cookie; 833 bool_t cb_res; 834 bool_t result; 835 836 /* 837 * See if we have a callback for this guy. 838 */ 839 result = TRUE; 840 SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { 841 if (scb->cb_callback.program == rqst->rq_prog 842 && scb->cb_callback.version == rqst->rq_vers) { 843 /* 844 * This one matches. Call the callback and see 845 * if it wants to veto or something. 846 */ 847 lock.locked = FALSE; 848 lock.raw_cred = &client->cl_rawcred; 849 cb_res = scb->cb_callback.callback(rqst, 850 client->cl_creds, 851 client->cl_ctx, 852 &lock, 853 &cookie); 854 855 if (!cb_res) { 856 client->cl_state = CLIENT_STALE; 857 result = FALSE; 858 break; 859 } 860 861 /* 862 * The callback accepted the connection - it 863 * is responsible for freeing client->cl_creds 864 * now. 865 */ 866 client->cl_creds = GSS_C_NO_CREDENTIAL; 867 client->cl_locked = lock.locked; 868 client->cl_cookie = cookie; 869 return (TRUE); 870 } 871 } 872 873 /* 874 * Either no callback exists for this program/version or one 875 * of the callbacks rejected the connection. We just need to 876 * clean up the delegated client creds, if any. 877 */ 878 if (client->cl_creds) { 879 OM_uint32 min_ver; 880 gss_release_cred(&min_ver, &client->cl_creds); 881 } 882 return (result); 883 } 884 885 static bool_t 886 svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) 887 { 888 u_int32_t offset; 889 int word, bit; 890 891 if (seq <= client->cl_seqlast) { 892 /* 893 * The request sequence number is less than 894 * the largest we have seen so far. If it is 895 * outside the window or if we have seen a 896 * request with this sequence before, silently 897 * discard it. 898 */ 899 offset = client->cl_seqlast - seq; 900 if (offset >= SVC_RPC_GSS_SEQWINDOW) 901 return (FALSE); 902 word = offset / 32; 903 bit = offset % 32; 904 if (client->cl_seqmask[word] & (1 << bit)) 905 return (FALSE); 906 } 907 908 return (TRUE); 909 } 910 911 static void 912 svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) 913 { 914 int offset, i, word, bit; 915 uint32_t carry, newcarry; 916 917 if (seq > client->cl_seqlast) { 918 /* 919 * This request has a sequence number greater 920 * than any we have seen so far. Advance the 921 * seq window and set bit zero of the window 922 * (which corresponds to the new sequence 923 * number) 924 */ 925 offset = seq - client->cl_seqlast; 926 while (offset > 32) { 927 for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; 928 i > 0; i--) { 929 client->cl_seqmask[i] = client->cl_seqmask[i-1]; 930 } 931 client->cl_seqmask[0] = 0; 932 offset -= 32; 933 } 934 carry = 0; 935 for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { 936 newcarry = client->cl_seqmask[i] >> (32 - offset); 937 client->cl_seqmask[i] = 938 (client->cl_seqmask[i] << offset) | carry; 939 carry = newcarry; 940 } 941 client->cl_seqmask[0] |= 1; 942 client->cl_seqlast = seq; 943 } else { 944 offset = client->cl_seqlast - seq; 945 word = offset / 32; 946 bit = offset % 32; 947 client->cl_seqmask[word] |= (1 << bit); 948 } 949 950 } 951 952 enum auth_stat 953 svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) 954 955 { 956 OM_uint32 min_stat; 957 XDR xdrs; 958 struct svc_rpc_gss_client *client; 959 struct rpc_gss_cred gc; 960 struct rpc_gss_init_res gr; 961 gss_qop_t qop; 962 int call_stat; 963 enum auth_stat result; 964 965 log_debug("in svc_rpc_gss()"); 966 967 /* Garbage collect old clients. */ 968 svc_rpc_gss_timeout_clients(); 969 970 /* Initialize reply. */ 971 rqst->rq_xprt->xp_verf = _null_auth; 972 973 /* Deserialize client credentials. */ 974 if (rqst->rq_cred.oa_length <= 0) 975 return (AUTH_BADCRED); 976 977 memset(&gc, 0, sizeof(gc)); 978 979 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 980 rqst->rq_cred.oa_length, XDR_DECODE); 981 982 if (!xdr_rpc_gss_cred(&xdrs, &gc)) { 983 XDR_DESTROY(&xdrs); 984 return (AUTH_BADCRED); 985 } 986 XDR_DESTROY(&xdrs); 987 988 /* Check version. */ 989 if (gc.gc_version != RPCSEC_GSS_VERSION) { 990 result = AUTH_BADCRED; 991 goto out; 992 } 993 994 /* Check the proc and find the client (or create it) */ 995 if (gc.gc_proc == RPCSEC_GSS_INIT) { 996 if (gc.gc_handle.length != 0) { 997 result = AUTH_BADCRED; 998 goto out; 999 } 1000 client = svc_rpc_gss_create_client(); 1001 } else { 1002 if (gc.gc_handle.length != sizeof(uint32_t)) { 1003 result = AUTH_BADCRED; 1004 goto out; 1005 } 1006 uint32_t *p = gc.gc_handle.value; 1007 client = svc_rpc_gss_find_client(*p); 1008 if (!client) { 1009 /* 1010 * Can't find the client - we may have 1011 * destroyed it - tell the other side to 1012 * re-authenticate. 1013 */ 1014 result = RPCSEC_GSS_CREDPROBLEM; 1015 goto out; 1016 } 1017 } 1018 rqst->rq_clntcred = client; 1019 1020 /* 1021 * The service and sequence number must be ignored for 1022 * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. 1023 */ 1024 if (gc.gc_proc != RPCSEC_GSS_INIT 1025 && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { 1026 /* 1027 * Check for sequence number overflow. 1028 */ 1029 if (gc.gc_seq >= MAXSEQ) { 1030 result = RPCSEC_GSS_CTXPROBLEM; 1031 goto out; 1032 } 1033 client->cl_seq = gc.gc_seq; 1034 1035 /* 1036 * Check for valid service. 1037 */ 1038 if (gc.gc_svc != rpc_gss_svc_none && 1039 gc.gc_svc != rpc_gss_svc_integrity && 1040 gc.gc_svc != rpc_gss_svc_privacy) { 1041 result = AUTH_BADCRED; 1042 goto out; 1043 } 1044 } 1045 1046 /* Handle RPCSEC_GSS control procedure. */ 1047 switch (gc.gc_proc) { 1048 1049 case RPCSEC_GSS_INIT: 1050 case RPCSEC_GSS_CONTINUE_INIT: 1051 if (rqst->rq_proc != NULLPROC) { 1052 result = AUTH_REJECTEDCRED; 1053 break; 1054 } 1055 1056 memset(&gr, 0, sizeof(gr)); 1057 if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { 1058 result = AUTH_REJECTEDCRED; 1059 break; 1060 } 1061 1062 if (gr.gr_major == GSS_S_COMPLETE) { 1063 if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { 1064 result = AUTH_REJECTEDCRED; 1065 break; 1066 } 1067 } else { 1068 rqst->rq_xprt->xp_verf.oa_flavor = AUTH_NULL; 1069 rqst->rq_xprt->xp_verf.oa_length = 0; 1070 } 1071 1072 call_stat = svc_sendreply(rqst->rq_xprt, 1073 (xdrproc_t) xdr_rpc_gss_init_res, 1074 (caddr_t) &gr); 1075 1076 gss_release_buffer(&min_stat, &gr.gr_token); 1077 1078 if (!call_stat) { 1079 result = AUTH_FAILED; 1080 break; 1081 } 1082 1083 if (gr.gr_major == GSS_S_COMPLETE) 1084 client->cl_state = CLIENT_ESTABLISHED; 1085 1086 result = RPCSEC_GSS_NODISPATCH; 1087 break; 1088 1089 case RPCSEC_GSS_DATA: 1090 case RPCSEC_GSS_DESTROY: 1091 if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { 1092 result = RPCSEC_GSS_NODISPATCH; 1093 break; 1094 } 1095 1096 if (!svc_rpc_gss_validate(client, msg, &qop)) { 1097 result = RPCSEC_GSS_CREDPROBLEM; 1098 break; 1099 } 1100 1101 if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { 1102 result = RPCSEC_GSS_CTXPROBLEM; 1103 break; 1104 } 1105 1106 svc_rpc_gss_update_seq(client, gc.gc_seq); 1107 1108 /* 1109 * Change the SVCAUTH ops on the transport to point at 1110 * our own code so that we can unwrap the arguments 1111 * and wrap the result. The caller will re-set this on 1112 * every request to point to a set of null wrap/unwrap 1113 * methods. 1114 */ 1115 SVC_AUTH(rqst->rq_xprt).svc_ah_ops = &svc_auth_gss_ops; 1116 SVC_AUTH(rqst->rq_xprt).svc_ah_private = client; 1117 1118 if (gc.gc_proc == RPCSEC_GSS_DATA) { 1119 /* 1120 * We might be ready to do a callback to the server to 1121 * see if it wants to accept/reject the connection. 1122 */ 1123 if (!client->cl_done_callback) { 1124 client->cl_done_callback = TRUE; 1125 client->cl_qop = qop; 1126 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1127 client->cl_rawcred.mechanism, qop); 1128 if (!svc_rpc_gss_callback(client, rqst)) { 1129 result = AUTH_REJECTEDCRED; 1130 break; 1131 } 1132 } 1133 1134 /* 1135 * If the server has locked this client to a 1136 * particular service+qop pair, enforce that 1137 * restriction now. 1138 */ 1139 if (client->cl_locked) { 1140 if (client->cl_rawcred.service != gc.gc_svc) { 1141 result = AUTH_FAILED; 1142 break; 1143 } else if (client->cl_qop != qop) { 1144 result = AUTH_BADVERF; 1145 break; 1146 } 1147 } 1148 1149 /* 1150 * If the qop changed, look up the new qop 1151 * name for rawcred. 1152 */ 1153 if (client->cl_qop != qop) { 1154 client->cl_qop = qop; 1155 client->cl_rawcred.qop = _rpc_gss_num_to_qop( 1156 client->cl_rawcred.mechanism, qop); 1157 } 1158 1159 /* 1160 * Make sure we use the right service value 1161 * for unwrap/wrap. 1162 */ 1163 client->cl_rawcred.service = gc.gc_svc; 1164 1165 result = AUTH_OK; 1166 } else { 1167 if (rqst->rq_proc != NULLPROC) { 1168 result = AUTH_REJECTEDCRED; 1169 break; 1170 } 1171 1172 call_stat = svc_sendreply(rqst->rq_xprt, 1173 (xdrproc_t) xdr_void, (caddr_t) NULL); 1174 1175 if (!call_stat) { 1176 result = AUTH_FAILED; 1177 break; 1178 } 1179 1180 svc_rpc_gss_destroy_client(client); 1181 1182 result = RPCSEC_GSS_NODISPATCH; 1183 break; 1184 } 1185 break; 1186 1187 default: 1188 result = AUTH_BADCRED; 1189 break; 1190 } 1191 out: 1192 xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); 1193 return (result); 1194 } 1195 1196 bool_t 1197 svc_rpc_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1198 { 1199 struct svc_rpc_gss_client *client; 1200 1201 log_debug("in svc_rpc_gss_wrap()"); 1202 1203 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1204 if (client->cl_state != CLIENT_ESTABLISHED 1205 || client->cl_rawcred.service == rpc_gss_svc_none) { 1206 return xdr_func(xdrs, xdr_ptr); 1207 } 1208 return (xdr_rpc_gss_wrap_data(xdrs, xdr_func, xdr_ptr, 1209 client->cl_ctx, client->cl_qop, 1210 client->cl_rawcred.service, client->cl_seq)); 1211 } 1212 1213 bool_t 1214 svc_rpc_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 1215 { 1216 struct svc_rpc_gss_client *client; 1217 1218 log_debug("in svc_rpc_gss_unwrap()"); 1219 1220 client = (struct svc_rpc_gss_client *) auth->svc_ah_private; 1221 if (client->cl_state != CLIENT_ESTABLISHED 1222 || client->cl_rawcred.service == rpc_gss_svc_none) { 1223 return xdr_func(xdrs, xdr_ptr); 1224 } 1225 return (xdr_rpc_gss_unwrap_data(xdrs, xdr_func, xdr_ptr, 1226 client->cl_ctx, client->cl_qop, 1227 client->cl_rawcred.service, client->cl_seq)); 1228 } 1229