1 /* 2 svc_auth_gss.c 3 4 Copyright (c) 2000 The Regents of the University of Michigan. 5 All rights reserved. 6 7 Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. 8 All rights reserved, all wrongs reversed. 9 10 Redistribution and use in source and binary forms, with or without 11 modification, are permitted provided that the following conditions 12 are met: 13 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in the 18 documentation and/or other materials provided with the distribution. 19 3. Neither the name of the University nor the names of its 20 contributors may be used to endorse or promote products derived 21 from this software without specific prior written permission. 22 23 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 24 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 25 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 30 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 35 */ 36 #include <wintirpc.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <rpc/rpc.h> 41 #include <gssapi/gssapi.h> 42 43 extern SVCAUTH svc_auth_none; 44 45 /* 46 * from mit-krb5-1.2.1 mechglue/mglueP.h: 47 * Array of context IDs typed by mechanism OID 48 */ 49 typedef struct gss_union_ctx_id_t { 50 gss_OID mech_type; 51 gss_ctx_id_t internal_ctx_id; 52 } gss_union_ctx_id_desc, *gss_union_ctx_id_t; 53 54 55 56 static bool_t svcauth_gss_destroy(); 57 static bool_t svcauth_gss_wrap(); 58 static bool_t svcauth_gss_unwrap(); 59 60 struct svc_auth_ops svc_auth_gss_ops = { 61 svcauth_gss_wrap, 62 svcauth_gss_unwrap, 63 svcauth_gss_destroy 64 }; 65 66 struct svc_rpc_gss_data { 67 bool_t established; /* context established */ 68 gss_ctx_id_t ctx; /* context id */ 69 struct rpc_gss_sec sec; /* security triple */ 70 gss_buffer_desc cname; /* GSS client name */ 71 u_int seq; /* sequence number */ 72 u_int win; /* sequence window */ 73 u_int seqlast; /* last sequence number */ 74 u_int32_t seqmask; /* bitmask of seqnums */ 75 gss_name_t client_name; /* unparsed name string */ 76 }; 77 78 #define SVCAUTH_PRIVATE(auth) \ 79 ((struct svc_rpc_gss_data *)(auth)->svc_ah_private) 80 81 /* Global server credentials. */ 82 gss_cred_id_t _svcauth_gss_creds; 83 static gss_name_t _svcauth_gss_name = NULL; 84 85 bool_t 86 svcauth_gss_set_svc_name(gss_name_t name) 87 { 88 OM_uint32 maj_stat, min_stat; 89 90 log_debug("in svcauth_gss_set_svc_name()"); 91 92 if (_svcauth_gss_name != NULL) { 93 maj_stat = gss_release_name(&min_stat, &_svcauth_gss_name); 94 95 if (maj_stat != GSS_S_COMPLETE) { 96 log_status("gss_release_name", maj_stat, min_stat); 97 return (FALSE); 98 } 99 _svcauth_gss_name = NULL; 100 } 101 maj_stat = gss_duplicate_name(&min_stat, name, &_svcauth_gss_name); 102 103 if (maj_stat != GSS_S_COMPLETE) { 104 log_status("gss_duplicate_name", maj_stat, min_stat); 105 return (FALSE); 106 } 107 108 return (TRUE); 109 } 110 111 static bool_t 112 svcauth_gss_import_name(char *service) 113 { 114 gss_name_t name; 115 gss_buffer_desc namebuf; 116 OM_uint32 maj_stat, min_stat; 117 118 log_debug("in svcauth_gss_import_name()"); 119 120 namebuf.value = service; 121 namebuf.length = strlen(service); 122 123 maj_stat = gss_import_name(&min_stat, &namebuf, 124 (gss_OID)GSS_C_NT_HOSTBASED_SERVICE, &name); 125 126 if (maj_stat != GSS_S_COMPLETE) { 127 log_status("gss_import_name", maj_stat, min_stat); 128 return (FALSE); 129 } 130 if (svcauth_gss_set_svc_name(name) != TRUE) { 131 gss_release_name(&min_stat, &name); 132 return (FALSE); 133 } 134 return (TRUE); 135 } 136 137 static bool_t 138 svcauth_gss_acquire_cred(void) 139 { 140 OM_uint32 maj_stat, min_stat; 141 142 log_debug("in svcauth_gss_acquire_cred()"); 143 144 maj_stat = gss_acquire_cred(&min_stat, _svcauth_gss_name, 0, 145 GSS_C_NULL_OID_SET, GSS_C_ACCEPT, 146 &_svcauth_gss_creds, NULL, NULL); 147 148 if (maj_stat != GSS_S_COMPLETE) { 149 log_status("gss_acquire_cred", maj_stat, min_stat); 150 return (FALSE); 151 } 152 return (TRUE); 153 } 154 155 static bool_t 156 svcauth_gss_release_cred(void) 157 { 158 OM_uint32 maj_stat, min_stat; 159 160 log_debug("in svcauth_gss_release_cred()"); 161 162 maj_stat = gss_release_cred(&min_stat, &_svcauth_gss_creds); 163 164 if (maj_stat != GSS_S_COMPLETE) { 165 log_status("gss_release_cred", maj_stat, min_stat); 166 return (FALSE); 167 } 168 169 _svcauth_gss_creds = NULL; 170 171 return (TRUE); 172 } 173 174 static bool_t 175 svcauth_gss_accept_sec_context(struct svc_req *rqst, 176 struct rpc_gss_init_res *gr) 177 { 178 struct svc_rpc_gss_data *gd; 179 struct rpc_gss_cred *gc; 180 gss_buffer_desc recv_tok, seqbuf, checksum; 181 gss_OID mech; 182 OM_uint32 maj_stat = 0, min_stat = 0, ret_flags, seq; 183 184 log_debug("in svcauth_gss_accept_context()"); 185 186 gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); 187 gc = (struct rpc_gss_cred *)rqst->rq_clntcred; 188 memset(gr, 0, sizeof(*gr)); 189 190 /* Deserialize arguments. */ 191 memset(&recv_tok, 0, sizeof(recv_tok)); 192 193 if (!svc_getargs(rqst->rq_xprt, (xdrproc_t)xdr_rpc_gss_init_args, 194 (caddr_t)&recv_tok)) 195 return (FALSE); 196 197 gr->gr_major = gss_accept_sec_context(&gr->gr_minor, 198 &gd->ctx, 199 _svcauth_gss_creds, 200 &recv_tok, 201 GSS_C_NO_CHANNEL_BINDINGS, 202 &gd->client_name, 203 &mech, 204 &gr->gr_token, 205 &ret_flags, 206 NULL, 207 NULL); 208 209 if (gr->gr_major != GSS_S_COMPLETE && 210 gr->gr_major != GSS_S_CONTINUE_NEEDED) { 211 log_status("accept_sec_context", gr->gr_major, gr->gr_minor); 212 gd->ctx = GSS_C_NO_CONTEXT; 213 gss_release_buffer(&min_stat, &gr->gr_token); 214 return (FALSE); 215 } 216 /* ANDROS: krb5 mechglue returns ctx of size 8 - two pointers, 217 * one to the mechanism oid, one to the internal_ctx_id */ 218 if ((gr->gr_ctx.value = mem_alloc(sizeof(gss_union_ctx_id_desc))) == NULL) { 219 fprintf(stderr, "svcauth_gss_accept_context: out of memory\n"); 220 return (FALSE); 221 } 222 memcpy(gr->gr_ctx.value, gd->ctx, sizeof(gss_union_ctx_id_desc)); 223 gr->gr_ctx.length = sizeof(gss_union_ctx_id_desc); 224 225 /* ANDROS: change for debugging linux kernel version... 226 gr->gr_win = sizeof(gd->seqmask) * 8; 227 */ 228 gr->gr_win = 0x00000005; 229 230 /* Save client info. */ 231 gd->sec.mech = mech; 232 gd->sec.qop = GSS_C_QOP_DEFAULT; 233 gd->sec.svc = gc->gc_svc; 234 gd->seq = gc->gc_seq; 235 gd->win = gr->gr_win; 236 237 if (gr->gr_major == GSS_S_COMPLETE) { 238 maj_stat = gss_display_name(&min_stat, gd->client_name, 239 &gd->cname, &gd->sec.mech); 240 if (maj_stat != GSS_S_COMPLETE) { 241 log_status("display_name", maj_stat, min_stat); 242 return (FALSE); 243 } 244 #ifdef DEBUG 245 #ifdef HAVE_KRB5 246 { 247 gss_buffer_desc mechname; 248 249 gss_oid_to_str(&min_stat, mech, &mechname); 250 251 log_debug("accepted context for %.*s with " 252 "<mech %.*s, qop %d, svc %d>", 253 gd->cname.length, (char *)gd->cname.value, 254 mechname.length, (char *)mechname.value, 255 gd->sec.qop, gd->sec.svc); 256 257 gss_release_buffer(&min_stat, &mechname); 258 } 259 #elif HAVE_HEIMDAL 260 log_debug("accepted context for %.*s with " 261 "<mech {}, qop %d, svc %d>", 262 gd->cname.length, (char *)gd->cname.value, 263 gd->sec.qop, gd->sec.svc); 264 #endif 265 #endif /* DEBUG */ 266 seq = htonl(gr->gr_win); 267 seqbuf.value = &seq; 268 seqbuf.length = sizeof(seq); 269 270 maj_stat = gss_sign(&min_stat, gd->ctx, GSS_C_QOP_DEFAULT, 271 &seqbuf, &checksum); 272 273 if (maj_stat != GSS_S_COMPLETE) 274 return (FALSE); 275 276 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 277 rqst->rq_xprt->xp_verf.oa_base = checksum.value; 278 rqst->rq_xprt->xp_verf.oa_length = checksum.length; 279 } 280 return (TRUE); 281 } 282 283 static bool_t 284 svcauth_gss_validate(struct svc_rpc_gss_data *gd, struct rpc_msg *msg) 285 { 286 struct opaque_auth *oa; 287 gss_buffer_desc rpcbuf, checksum; 288 OM_uint32 maj_stat, min_stat, qop_state; 289 u_char rpchdr[128]; 290 int32_t *buf; 291 292 log_debug("in svcauth_gss_validate()"); 293 294 memset(rpchdr, 0, sizeof(rpchdr)); 295 296 /* XXX - Reconstruct RPC header for signing (from xdr_callmsg). */ 297 oa = &msg->rm_call.cb_cred; 298 if (oa->oa_length > MAX_AUTH_BYTES) 299 return (FALSE); 300 301 /* 8 XDR units from the IXDR macro calls. */ 302 if (sizeof(rpchdr) < (8 * BYTES_PER_XDR_UNIT + 303 RNDUP(oa->oa_length))) 304 return (FALSE); 305 306 buf = (int32_t *)rpchdr; 307 IXDR_PUT_LONG(buf, msg->rm_xid); 308 IXDR_PUT_ENUM(buf, msg->rm_direction); 309 IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); 310 IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); 311 IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); 312 IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); 313 IXDR_PUT_ENUM(buf, oa->oa_flavor); 314 IXDR_PUT_LONG(buf, oa->oa_length); 315 if (oa->oa_length) { 316 memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); 317 buf += RNDUP(oa->oa_length) / sizeof(int32_t); 318 } 319 rpcbuf.value = rpchdr; 320 rpcbuf.length = (u_char *)buf - rpchdr; 321 322 checksum.value = msg->rm_call.cb_verf.oa_base; 323 checksum.length = msg->rm_call.cb_verf.oa_length; 324 325 maj_stat = gss_verify_mic(&min_stat, gd->ctx, &rpcbuf, &checksum, 326 &qop_state); 327 328 if (maj_stat != GSS_S_COMPLETE) { 329 log_status("gss_verify_mic", maj_stat, min_stat); 330 return (FALSE); 331 } 332 return (TRUE); 333 } 334 335 bool_t 336 svcauth_gss_nextverf(struct svc_req *rqst, u_int num) 337 { 338 struct svc_rpc_gss_data *gd; 339 gss_buffer_desc signbuf, checksum; 340 OM_uint32 maj_stat, min_stat; 341 342 log_debug("in svcauth_gss_nextverf()"); 343 344 if (rqst->rq_xprt->xp_auth == NULL) 345 return (FALSE); 346 347 gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); 348 349 signbuf.value = # 350 signbuf.length = sizeof(num); 351 352 maj_stat = gss_get_mic(&min_stat, gd->ctx, gd->sec.qop, 353 &signbuf, &checksum); 354 355 if (maj_stat != GSS_S_COMPLETE) { 356 log_status("gss_get_mic", maj_stat, min_stat); 357 return (FALSE); 358 } 359 rqst->rq_xprt->xp_verf.oa_flavor = RPCSEC_GSS; 360 rqst->rq_xprt->xp_verf.oa_base = (caddr_t)checksum.value; 361 rqst->rq_xprt->xp_verf.oa_length = (u_int)checksum.length; 362 363 return (TRUE); 364 } 365 366 enum auth_stat 367 _svcauth_gss(struct svc_req *rqst, struct rpc_msg *msg, bool_t *no_dispatch) 368 { 369 XDR xdrs; 370 SVCAUTH *auth; 371 struct svc_rpc_gss_data *gd; 372 struct rpc_gss_cred *gc; 373 struct rpc_gss_init_res gr; 374 int call_stat, offset; 375 376 log_debug("in svcauth_gss()"); 377 378 /* Initialize reply. */ 379 rqst->rq_xprt->xp_verf = _null_auth; 380 381 /* Allocate and set up server auth handle. */ 382 if (rqst->rq_xprt->xp_auth == NULL || 383 rqst->rq_xprt->xp_auth == &svc_auth_none) { 384 if ((auth = calloc(sizeof(*auth), 1)) == NULL) { 385 fprintf(stderr, "svcauth_gss: out_of_memory\n"); 386 return (AUTH_FAILED); 387 } 388 if ((gd = calloc(sizeof(*gd), 1)) == NULL) { 389 fprintf(stderr, "svcauth_gss: out_of_memory\n"); 390 return (AUTH_FAILED); 391 } 392 auth->svc_ah_ops = &svc_auth_gss_ops; 393 auth->svc_ah_private = (caddr_t) gd; 394 rqst->rq_xprt->xp_auth = auth; 395 } 396 else gd = SVCAUTH_PRIVATE(rqst->rq_xprt->xp_auth); 397 398 /* Deserialize client credentials. */ 399 if (rqst->rq_cred.oa_length <= 0) 400 return (AUTH_BADCRED); 401 402 gc = (struct rpc_gss_cred *)rqst->rq_clntcred; 403 memset(gc, 0, sizeof(*gc)); 404 405 xdrmem_create(&xdrs, rqst->rq_cred.oa_base, 406 rqst->rq_cred.oa_length, XDR_DECODE); 407 408 if (!xdr_rpc_gss_cred(&xdrs, gc)) { 409 XDR_DESTROY(&xdrs); 410 return (AUTH_BADCRED); 411 } 412 XDR_DESTROY(&xdrs); 413 414 /* Check version. */ 415 if (gc->gc_v != RPCSEC_GSS_VERSION) 416 return (AUTH_BADCRED); 417 418 /* Check RPCSEC_GSS service. */ 419 if (gc->gc_svc != RPCSEC_GSS_SVC_NONE && 420 gc->gc_svc != RPCSEC_GSS_SVC_INTEGRITY && 421 gc->gc_svc != RPCSEC_GSS_SVC_PRIVACY) 422 return (AUTH_BADCRED); 423 424 /* Check sequence number. */ 425 if (gd->established) { 426 if (gc->gc_seq > MAXSEQ) 427 return (RPCSEC_GSS_CTXPROBLEM); 428 429 if ((offset = gd->seqlast - gc->gc_seq) < 0) { 430 gd->seqlast = gc->gc_seq; 431 offset = 0 - offset; 432 gd->seqmask <<= offset; 433 offset = 0; 434 } 435 else if (offset >= gd->win || (gd->seqmask & (1 << offset))) { 436 *no_dispatch = 1; 437 return (RPCSEC_GSS_CTXPROBLEM); 438 } 439 gd->seq = gc->gc_seq; 440 gd->seqmask |= (1 << offset); 441 } 442 443 if (gd->established) { 444 rqst->rq_clntname = (char *)gd->client_name; 445 rqst->rq_svcname = (char *)gd->ctx; 446 } 447 448 /* Handle RPCSEC_GSS control procedure. */ 449 switch (gc->gc_proc) { 450 451 case RPCSEC_GSS_INIT: 452 case RPCSEC_GSS_CONTINUE_INIT: 453 if (rqst->rq_proc != NULLPROC) 454 return (AUTH_FAILED); /* XXX ? */ 455 456 if (_svcauth_gss_name == NULL) { 457 if (!svcauth_gss_import_name("nfs")) 458 return (AUTH_FAILED); 459 } 460 461 if (!svcauth_gss_acquire_cred()) 462 return (AUTH_FAILED); 463 464 if (!svcauth_gss_accept_sec_context(rqst, &gr)) 465 return (AUTH_REJECTEDCRED); 466 467 if (!svcauth_gss_nextverf(rqst, htonl(gr.gr_win))) 468 return (AUTH_FAILED); 469 470 *no_dispatch = TRUE; 471 472 call_stat = svc_sendreply(rqst->rq_xprt, 473 (xdrproc_t)xdr_rpc_gss_init_res, (caddr_t)&gr); 474 475 if (!call_stat) 476 return (AUTH_FAILED); 477 478 if (gr.gr_major == GSS_S_COMPLETE) 479 gd->established = TRUE; 480 481 break; 482 483 case RPCSEC_GSS_DATA: 484 if (!svcauth_gss_validate(gd, msg)) 485 return (RPCSEC_GSS_CREDPROBLEM); 486 487 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) 488 return (AUTH_FAILED); 489 break; 490 491 case RPCSEC_GSS_DESTROY: 492 if (rqst->rq_proc != NULLPROC) 493 return (AUTH_FAILED); /* XXX ? */ 494 495 if (!svcauth_gss_validate(gd, msg)) 496 return (RPCSEC_GSS_CREDPROBLEM); 497 498 if (!svcauth_gss_nextverf(rqst, htonl(gc->gc_seq))) 499 return (AUTH_FAILED); 500 501 if (!svcauth_gss_release_cred()) 502 return (AUTH_FAILED); 503 504 SVCAUTH_DESTROY(rqst->rq_xprt->xp_auth); 505 rqst->rq_xprt->xp_auth = &svc_auth_none; 506 507 break; 508 509 default: 510 return (AUTH_REJECTEDCRED); 511 break; 512 } 513 return (AUTH_OK); 514 } 515 516 bool_t 517 svcauth_gss_destroy(SVCAUTH *auth) 518 { 519 struct svc_rpc_gss_data *gd; 520 OM_uint32 min_stat; 521 522 log_debug("in svcauth_gss_destroy()"); 523 524 gd = SVCAUTH_PRIVATE(auth); 525 526 gss_delete_sec_context(&min_stat, &gd->ctx, GSS_C_NO_BUFFER); 527 gss_release_buffer(&min_stat, &gd->cname); 528 529 if (gd->client_name) 530 gss_release_name(&min_stat, &gd->client_name); 531 532 mem_free(gd, sizeof(*gd)); 533 mem_free(auth, sizeof(*auth)); 534 535 return (TRUE); 536 } 537 538 bool_t 539 svcauth_gss_wrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 540 { 541 struct svc_rpc_gss_data *gd; 542 543 log_debug("in svcauth_gss_wrap()"); 544 545 gd = SVCAUTH_PRIVATE(auth); 546 547 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { 548 return ((*xdr_func)(xdrs, xdr_ptr)); 549 } 550 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, 551 gd->ctx, gd->sec.qop, 552 gd->sec.svc, gd->seq)); 553 } 554 555 bool_t 556 svcauth_gss_unwrap(SVCAUTH *auth, XDR *xdrs, xdrproc_t xdr_func, caddr_t xdr_ptr) 557 { 558 struct svc_rpc_gss_data *gd; 559 560 log_debug("in svcauth_gss_unwrap()"); 561 562 gd = SVCAUTH_PRIVATE(auth); 563 564 if (!gd->established || gd->sec.svc == RPCSEC_GSS_SVC_NONE) { 565 return ((*xdr_func)(xdrs, xdr_ptr)); 566 } 567 return (xdr_rpc_gss_data(xdrs, xdr_func, xdr_ptr, 568 gd->ctx, gd->sec.qop, 569 gd->sec.svc, gd->seq)); 570 } 571 572 char * 573 svcauth_gss_get_principal(SVCAUTH *auth) 574 { 575 struct svc_rpc_gss_data *gd; 576 char *pname; 577 578 gd = SVCAUTH_PRIVATE(auth); 579 580 if (gd->cname.length == 0) 581 return (NULL); 582 583 if ((pname = malloc(gd->cname.length + 1)) == NULL) 584 return (NULL); 585 586 memcpy(pname, gd->cname.value, gd->cname.length); 587 pname[gd->cname.length] = '\0'; 588 589 return (pname); 590 } 591