1 /*- 2 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3 * Authors: Doug Rabson <dfr@rabson.org> 4 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 #include <sys/linker.h> 34 #include <sys/module.h> 35 #include <sys/queue.h> 36 #include <ctype.h> 37 #include <err.h> 38 #include <pwd.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <gssapi/gssapi.h> 44 #include <rpc/rpc.h> 45 #include <rpc/rpc_com.h> 46 47 #include "gssd.h" 48 49 #ifndef _PATH_GSS_MECH 50 #define _PATH_GSS_MECH "/etc/gss/mech" 51 #endif 52 #ifndef _PATH_GSSDSOCK 53 #define _PATH_GSSDSOCK "/var/run/gssd.sock" 54 #endif 55 56 struct gss_resource { 57 LIST_ENTRY(gss_resource) gr_link; 58 uint64_t gr_id; /* indentifier exported to kernel */ 59 void* gr_res; /* GSS-API resource pointer */ 60 }; 61 LIST_HEAD(gss_resource_list, gss_resource) gss_resources; 62 int gss_resource_count; 63 uint32_t gss_next_id; 64 uint32_t gss_start_time; 65 int debug_level; 66 67 static void gssd_load_mech(void); 68 69 extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); 70 extern int gssd_syscall(char *path); 71 72 int 73 main(int argc, char **argv) 74 { 75 /* 76 * We provide an RPC service on a local-domain socket. The 77 * kernel's GSS-API code will pass what it can't handle 78 * directly to us. 79 */ 80 struct sockaddr_un sun; 81 int fd, oldmask, ch, debug; 82 SVCXPRT *xprt; 83 84 debug = 0; 85 while ((ch = getopt(argc, argv, "d")) != -1) { 86 switch (ch) { 87 case 'd': 88 debug_level++; 89 break; 90 default: 91 fprintf(stderr, "usage: %s [-d]\n", argv[0]); 92 exit(1); 93 break; 94 } 95 } 96 97 gssd_load_mech(); 98 99 if (!debug_level) 100 daemon(0, 0); 101 102 memset(&sun, 0, sizeof sun); 103 sun.sun_family = AF_LOCAL; 104 unlink(_PATH_GSSDSOCK); 105 strcpy(sun.sun_path, _PATH_GSSDSOCK); 106 sun.sun_len = SUN_LEN(&sun); 107 fd = socket(AF_LOCAL, SOCK_STREAM, 0); 108 if (!fd) { 109 err(1, "Can't create local gssd socket"); 110 } 111 oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 112 if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) { 113 err(1, "Can't bind local gssd socket"); 114 } 115 umask(oldmask); 116 if (listen(fd, SOMAXCONN) < 0) { 117 err(1, "Can't listen on local gssd socket"); 118 } 119 xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 120 if (!xprt) { 121 err(1, "Can't create transport for local gssd socket"); 122 } 123 if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 124 err(1, "Can't register service for local gssd socket"); 125 } 126 127 LIST_INIT(&gss_resources); 128 gss_next_id = 1; 129 gss_start_time = time(0); 130 131 gssd_syscall(_PATH_GSSDSOCK); 132 svc_run(); 133 134 return (0); 135 } 136 137 static void 138 gssd_load_mech(void) 139 { 140 FILE *fp; 141 char buf[256]; 142 char *p; 143 char *name, *oid, *lib, *kobj; 144 145 fp = fopen(_PATH_GSS_MECH, "r"); 146 if (!fp) 147 return; 148 149 while (fgets(buf, sizeof(buf), fp)) { 150 if (*buf == '#') 151 continue; 152 p = buf; 153 name = strsep(&p, "\t\n "); 154 if (p) while (isspace(*p)) p++; 155 oid = strsep(&p, "\t\n "); 156 if (p) while (isspace(*p)) p++; 157 lib = strsep(&p, "\t\n "); 158 if (p) while (isspace(*p)) p++; 159 kobj = strsep(&p, "\t\n "); 160 if (!name || !oid || !lib || !kobj) 161 continue; 162 163 if (strcmp(kobj, "-")) { 164 /* 165 * Attempt to load the kernel module if its 166 * not already present. 167 */ 168 if (modfind(kobj) < 0) { 169 if (kldload(kobj) < 0) { 170 fprintf(stderr, 171 "%s: can't find or load kernel module %s for %s\n", 172 getprogname(), kobj, name); 173 } 174 } 175 } 176 } 177 fclose(fp); 178 } 179 180 static void * 181 gssd_find_resource(uint64_t id) 182 { 183 struct gss_resource *gr; 184 185 if (!id) 186 return (NULL); 187 188 LIST_FOREACH(gr, &gss_resources, gr_link) 189 if (gr->gr_id == id) 190 return (gr->gr_res); 191 192 return (NULL); 193 } 194 195 static uint64_t 196 gssd_make_resource(void *res) 197 { 198 struct gss_resource *gr; 199 200 if (!res) 201 return (0); 202 203 gr = malloc(sizeof(struct gss_resource)); 204 if (!gr) 205 return (0); 206 gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 207 gr->gr_res = res; 208 LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 209 gss_resource_count++; 210 if (debug_level > 1) 211 printf("%d resources allocated\n", gss_resource_count); 212 213 return (gr->gr_id); 214 } 215 216 static void 217 gssd_delete_resource(uint64_t id) 218 { 219 struct gss_resource *gr; 220 221 LIST_FOREACH(gr, &gss_resources, gr_link) { 222 if (gr->gr_id == id) { 223 LIST_REMOVE(gr, gr_link); 224 free(gr); 225 gss_resource_count--; 226 if (debug_level > 1) 227 printf("%d resources allocated\n", 228 gss_resource_count); 229 return; 230 } 231 } 232 } 233 234 bool_t 235 gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 236 { 237 238 return (TRUE); 239 } 240 241 bool_t 242 gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 243 { 244 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 245 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 246 gss_name_t name = GSS_C_NO_NAME; 247 char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1]; 248 249 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 250 (int) argp->uid); 251 setenv("KRB5CCNAME", ccname, TRUE); 252 253 memset(result, 0, sizeof(*result)); 254 if (argp->cred) { 255 cred = gssd_find_resource(argp->cred); 256 if (!cred) { 257 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 258 return (TRUE); 259 } 260 } 261 if (argp->ctx) { 262 ctx = gssd_find_resource(argp->ctx); 263 if (!ctx) { 264 result->major_status = GSS_S_CONTEXT_EXPIRED; 265 return (TRUE); 266 } 267 } 268 if (argp->name) { 269 name = gssd_find_resource(argp->name); 270 if (!name) { 271 result->major_status = GSS_S_BAD_NAME; 272 return (TRUE); 273 } 274 } 275 276 memset(result, 0, sizeof(*result)); 277 result->major_status = gss_init_sec_context(&result->minor_status, 278 cred, &ctx, name, argp->mech_type, 279 argp->req_flags, argp->time_req, argp->input_chan_bindings, 280 &argp->input_token, &result->actual_mech_type, 281 &result->output_token, &result->ret_flags, &result->time_rec); 282 283 if (result->major_status == GSS_S_COMPLETE 284 || result->major_status == GSS_S_CONTINUE_NEEDED) { 285 if (argp->ctx) 286 result->ctx = argp->ctx; 287 else 288 result->ctx = gssd_make_resource(ctx); 289 } 290 291 return (TRUE); 292 } 293 294 bool_t 295 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 296 { 297 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 298 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 299 gss_name_t src_name; 300 gss_cred_id_t delegated_cred_handle; 301 302 memset(result, 0, sizeof(*result)); 303 if (argp->ctx) { 304 ctx = gssd_find_resource(argp->ctx); 305 if (!ctx) { 306 result->major_status = GSS_S_CONTEXT_EXPIRED; 307 return (TRUE); 308 } 309 } 310 if (argp->cred) { 311 cred = gssd_find_resource(argp->cred); 312 if (!cred) { 313 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 314 return (TRUE); 315 } 316 } 317 318 memset(result, 0, sizeof(*result)); 319 result->major_status = gss_accept_sec_context(&result->minor_status, 320 &ctx, cred, &argp->input_token, argp->input_chan_bindings, 321 &src_name, &result->mech_type, &result->output_token, 322 &result->ret_flags, &result->time_rec, 323 &delegated_cred_handle); 324 325 if (result->major_status == GSS_S_COMPLETE 326 || result->major_status == GSS_S_CONTINUE_NEEDED) { 327 if (argp->ctx) 328 result->ctx = argp->ctx; 329 else 330 result->ctx = gssd_make_resource(ctx); 331 result->src_name = gssd_make_resource(src_name); 332 result->delegated_cred_handle = 333 gssd_make_resource(delegated_cred_handle); 334 } 335 336 return (TRUE); 337 } 338 339 bool_t 340 gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 341 { 342 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 343 344 if (ctx) { 345 result->major_status = gss_delete_sec_context( 346 &result->minor_status, &ctx, &result->output_token); 347 gssd_delete_resource(argp->ctx); 348 } else { 349 result->major_status = GSS_S_COMPLETE; 350 result->minor_status = 0; 351 } 352 353 return (TRUE); 354 } 355 356 bool_t 357 gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 358 { 359 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 360 361 if (ctx) { 362 result->major_status = gss_export_sec_context( 363 &result->minor_status, &ctx, 364 &result->interprocess_token); 365 result->format = KGSS_HEIMDAL_1_1; 366 gssd_delete_resource(argp->ctx); 367 } else { 368 result->major_status = GSS_S_FAILURE; 369 result->minor_status = 0; 370 result->interprocess_token.length = 0; 371 result->interprocess_token.value = NULL; 372 } 373 374 return (TRUE); 375 } 376 377 bool_t 378 gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 379 { 380 gss_name_t name; 381 382 result->major_status = gss_import_name(&result->minor_status, 383 &argp->input_name_buffer, argp->input_name_type, &name); 384 385 if (result->major_status == GSS_S_COMPLETE) 386 result->output_name = gssd_make_resource(name); 387 else 388 result->output_name = 0; 389 390 return (TRUE); 391 } 392 393 bool_t 394 gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 395 { 396 gss_name_t name = gssd_find_resource(argp->input_name); 397 gss_name_t output_name; 398 399 memset(result, 0, sizeof(*result)); 400 if (!name) { 401 result->major_status = GSS_S_BAD_NAME; 402 return (TRUE); 403 } 404 405 result->major_status = gss_canonicalize_name(&result->minor_status, 406 name, argp->mech_type, &output_name); 407 408 if (result->major_status == GSS_S_COMPLETE) 409 result->output_name = gssd_make_resource(output_name); 410 else 411 result->output_name = 0; 412 413 return (TRUE); 414 } 415 416 bool_t 417 gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 418 { 419 gss_name_t name = gssd_find_resource(argp->input_name); 420 421 memset(result, 0, sizeof(*result)); 422 if (!name) { 423 result->major_status = GSS_S_BAD_NAME; 424 return (TRUE); 425 } 426 427 result->major_status = gss_export_name(&result->minor_status, 428 name, &result->exported_name); 429 430 return (TRUE); 431 } 432 433 bool_t 434 gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 435 { 436 gss_name_t name = gssd_find_resource(argp->input_name); 437 438 if (name) { 439 result->major_status = gss_release_name(&result->minor_status, 440 &name); 441 gssd_delete_resource(argp->input_name); 442 } else { 443 result->major_status = GSS_S_COMPLETE; 444 result->minor_status = 0; 445 } 446 447 return (TRUE); 448 } 449 450 bool_t 451 gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 452 { 453 gss_name_t name = gssd_find_resource(argp->pname); 454 uid_t uid; 455 char buf[128]; 456 struct passwd pwd, *pw; 457 458 memset(result, 0, sizeof(*result)); 459 if (name) { 460 result->major_status = 461 gss_pname_to_uid(&result->minor_status, 462 name, argp->mech, &uid); 463 if (result->major_status == GSS_S_COMPLETE) { 464 result->uid = uid; 465 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 466 if (pw) { 467 int len = NGRPS; 468 int groups[NGRPS]; 469 result->gid = pw->pw_gid; 470 getgrouplist(pw->pw_name, pw->pw_gid, 471 groups, &len); 472 result->gidlist.gidlist_len = len; 473 result->gidlist.gidlist_val = 474 mem_alloc(len * sizeof(int)); 475 memcpy(result->gidlist.gidlist_val, groups, 476 len * sizeof(int)); 477 } else { 478 result->gid = 65534; 479 result->gidlist.gidlist_len = 0; 480 result->gidlist.gidlist_val = NULL; 481 } 482 } 483 } else { 484 result->major_status = GSS_S_BAD_NAME; 485 result->minor_status = 0; 486 } 487 488 return (TRUE); 489 } 490 491 bool_t 492 gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 493 { 494 gss_name_t desired_name = GSS_C_NO_NAME; 495 gss_cred_id_t cred; 496 char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1]; 497 498 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 499 (int) argp->uid); 500 setenv("KRB5CCNAME", ccname, TRUE); 501 502 memset(result, 0, sizeof(*result)); 503 if (argp->desired_name) { 504 desired_name = gssd_find_resource(argp->desired_name); 505 if (!desired_name) { 506 result->major_status = GSS_S_BAD_NAME; 507 return (TRUE); 508 } 509 } 510 511 result->major_status = gss_acquire_cred(&result->minor_status, 512 desired_name, argp->time_req, argp->desired_mechs, 513 argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 514 515 if (result->major_status == GSS_S_COMPLETE) 516 result->output_cred = gssd_make_resource(cred); 517 else 518 result->output_cred = 0; 519 520 return (TRUE); 521 } 522 523 bool_t 524 gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 525 { 526 gss_cred_id_t cred = gssd_find_resource(argp->cred); 527 528 memset(result, 0, sizeof(*result)); 529 if (!cred) { 530 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 531 return (TRUE); 532 } 533 534 result->major_status = gss_set_cred_option(&result->minor_status, 535 &cred, argp->option_name, &argp->option_value); 536 537 return (TRUE); 538 } 539 540 bool_t 541 gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 542 { 543 gss_cred_id_t cred = gssd_find_resource(argp->cred); 544 545 if (cred) { 546 result->major_status = gss_release_cred(&result->minor_status, 547 &cred); 548 gssd_delete_resource(argp->cred); 549 } else { 550 result->major_status = GSS_S_COMPLETE; 551 result->minor_status = 0; 552 } 553 554 return (TRUE); 555 } 556 557 bool_t 558 gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 559 { 560 561 result->message_context = argp->message_context; 562 result->major_status = gss_display_status(&result->minor_status, 563 argp->status_value, argp->status_type, argp->mech_type, 564 &result->message_context, &result->status_string); 565 566 return (TRUE); 567 } 568 569 int 570 gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 571 { 572 /* 573 * We don't use XDR to free the results - anything which was 574 * allocated came from GSS-API. We use xdr_result to figure 575 * out what to do. 576 */ 577 OM_uint32 junk; 578 579 if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 580 init_sec_context_res *p = (init_sec_context_res *) result; 581 gss_release_buffer(&junk, &p->output_token); 582 } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 583 accept_sec_context_res *p = (accept_sec_context_res *) result; 584 gss_release_buffer(&junk, &p->output_token); 585 } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 586 delete_sec_context_res *p = (delete_sec_context_res *) result; 587 gss_release_buffer(&junk, &p->output_token); 588 } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 589 export_sec_context_res *p = (export_sec_context_res *) result; 590 if (p->interprocess_token.length) 591 memset(p->interprocess_token.value, 0, 592 p->interprocess_token.length); 593 gss_release_buffer(&junk, &p->interprocess_token); 594 } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 595 export_name_res *p = (export_name_res *) result; 596 gss_release_buffer(&junk, &p->exported_name); 597 } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 598 acquire_cred_res *p = (acquire_cred_res *) result; 599 gss_release_oid_set(&junk, &p->actual_mechs); 600 } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 601 pname_to_uid_res *p = (pname_to_uid_res *) result; 602 if (p->gidlist.gidlist_val) 603 free(p->gidlist.gidlist_val); 604 } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 605 display_status_res *p = (display_status_res *) result; 606 gss_release_buffer(&junk, &p->status_string); 607 } 608 609 return (TRUE); 610 } 611