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>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/linker.h>
34 #include <sys/module.h>
35 #include <sys/queue.h>
36 #include <sys/syslog.h>
45 #include <gssapi/gssapi.h>
47 #include <rpc/rpc_com.h>
51 #ifndef _PATH_GSS_MECH
52 #define _PATH_GSS_MECH "/etc/gss/mech"
54 #ifndef _PATH_GSSDSOCK
55 #define _PATH_GSSDSOCK "/var/run/gssd.sock"
59 LIST_ENTRY(gss_resource) gr_link;
60 uint64_t gr_id; /* indentifier exported to kernel */
61 void* gr_res; /* GSS-API resource pointer */
63 LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
64 int gss_resource_count;
66 uint32_t gss_start_time;
69 static void gssd_load_mech(void);
71 extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
72 extern int gssd_syscall(char *path);
75 main(int argc, char **argv)
78 * We provide an RPC service on a local-domain socket. The
79 * kernel's GSS-API code will pass what it can't handle
82 struct sockaddr_un sun;
83 int fd, oldmask, ch, debug;
87 while ((ch = getopt(argc, argv, "d")) != -1) {
93 fprintf(stderr, "usage: %s [-d]\n", argv[0]);
104 memset(&sun, 0, sizeof sun);
105 sun.sun_family = AF_LOCAL;
106 unlink(_PATH_GSSDSOCK);
107 strcpy(sun.sun_path, _PATH_GSSDSOCK);
108 sun.sun_len = SUN_LEN(&sun);
109 fd = socket(AF_LOCAL, SOCK_STREAM, 0);
111 if (debug_level == 0) {
112 syslog(LOG_ERR, "Can't create local gssd socket");
115 err(1, "Can't create local gssd socket");
117 oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
118 if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
119 if (debug_level == 0) {
120 syslog(LOG_ERR, "Can't bind local gssd socket");
123 err(1, "Can't bind local gssd socket");
126 if (listen(fd, SOMAXCONN) < 0) {
127 if (debug_level == 0) {
128 syslog(LOG_ERR, "Can't listen on local gssd socket");
131 err(1, "Can't listen on local gssd socket");
133 xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
135 if (debug_level == 0) {
137 "Can't create transport for local gssd socket");
140 err(1, "Can't create transport for local gssd socket");
142 if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
143 if (debug_level == 0) {
145 "Can't register service for local gssd socket");
148 err(1, "Can't register service for local gssd socket");
151 LIST_INIT(&gss_resources);
153 gss_start_time = time(0);
155 gssd_syscall(_PATH_GSSDSOCK);
167 char *name, *oid, *lib, *kobj;
169 fp = fopen(_PATH_GSS_MECH, "r");
173 while (fgets(buf, sizeof(buf), fp)) {
177 name = strsep(&p, "\t\n ");
178 if (p) while (isspace(*p)) p++;
179 oid = strsep(&p, "\t\n ");
180 if (p) while (isspace(*p)) p++;
181 lib = strsep(&p, "\t\n ");
182 if (p) while (isspace(*p)) p++;
183 kobj = strsep(&p, "\t\n ");
184 if (!name || !oid || !lib || !kobj)
187 if (strcmp(kobj, "-")) {
189 * Attempt to load the kernel module if its
190 * not already present.
192 if (modfind(kobj) < 0) {
193 if (kldload(kobj) < 0) {
195 "%s: can't find or load kernel module %s for %s\n",
196 getprogname(), kobj, name);
205 gssd_find_resource(uint64_t id)
207 struct gss_resource *gr;
212 LIST_FOREACH(gr, &gss_resources, gr_link)
220 gssd_make_resource(void *res)
222 struct gss_resource *gr;
227 gr = malloc(sizeof(struct gss_resource));
230 gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
232 LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
233 gss_resource_count++;
235 printf("%d resources allocated\n", gss_resource_count);
241 gssd_delete_resource(uint64_t id)
243 struct gss_resource *gr;
245 LIST_FOREACH(gr, &gss_resources, gr_link) {
246 if (gr->gr_id == id) {
247 LIST_REMOVE(gr, gr_link);
249 gss_resource_count--;
251 printf("%d resources allocated\n",
259 gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
266 gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
268 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
269 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
270 gss_name_t name = GSS_C_NO_NAME;
271 char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
273 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
275 setenv("KRB5CCNAME", ccname, TRUE);
277 memset(result, 0, sizeof(*result));
279 cred = gssd_find_resource(argp->cred);
281 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
286 ctx = gssd_find_resource(argp->ctx);
288 result->major_status = GSS_S_CONTEXT_EXPIRED;
293 name = gssd_find_resource(argp->name);
295 result->major_status = GSS_S_BAD_NAME;
300 memset(result, 0, sizeof(*result));
301 result->major_status = gss_init_sec_context(&result->minor_status,
302 cred, &ctx, name, argp->mech_type,
303 argp->req_flags, argp->time_req, argp->input_chan_bindings,
304 &argp->input_token, &result->actual_mech_type,
305 &result->output_token, &result->ret_flags, &result->time_rec);
307 if (result->major_status == GSS_S_COMPLETE
308 || result->major_status == GSS_S_CONTINUE_NEEDED) {
310 result->ctx = argp->ctx;
312 result->ctx = gssd_make_resource(ctx);
319 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
321 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
322 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
324 gss_cred_id_t delegated_cred_handle;
326 memset(result, 0, sizeof(*result));
328 ctx = gssd_find_resource(argp->ctx);
330 result->major_status = GSS_S_CONTEXT_EXPIRED;
335 cred = gssd_find_resource(argp->cred);
337 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
342 memset(result, 0, sizeof(*result));
343 result->major_status = gss_accept_sec_context(&result->minor_status,
344 &ctx, cred, &argp->input_token, argp->input_chan_bindings,
345 &src_name, &result->mech_type, &result->output_token,
346 &result->ret_flags, &result->time_rec,
347 &delegated_cred_handle);
349 if (result->major_status == GSS_S_COMPLETE
350 || result->major_status == GSS_S_CONTINUE_NEEDED) {
352 result->ctx = argp->ctx;
354 result->ctx = gssd_make_resource(ctx);
355 result->src_name = gssd_make_resource(src_name);
356 result->delegated_cred_handle =
357 gssd_make_resource(delegated_cred_handle);
364 gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
366 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
369 result->major_status = gss_delete_sec_context(
370 &result->minor_status, &ctx, &result->output_token);
371 gssd_delete_resource(argp->ctx);
373 result->major_status = GSS_S_COMPLETE;
374 result->minor_status = 0;
381 gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
383 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
386 result->major_status = gss_export_sec_context(
387 &result->minor_status, &ctx,
388 &result->interprocess_token);
389 result->format = KGSS_HEIMDAL_1_1;
390 gssd_delete_resource(argp->ctx);
392 result->major_status = GSS_S_FAILURE;
393 result->minor_status = 0;
394 result->interprocess_token.length = 0;
395 result->interprocess_token.value = NULL;
402 gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
406 result->major_status = gss_import_name(&result->minor_status,
407 &argp->input_name_buffer, argp->input_name_type, &name);
409 if (result->major_status == GSS_S_COMPLETE)
410 result->output_name = gssd_make_resource(name);
412 result->output_name = 0;
418 gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
420 gss_name_t name = gssd_find_resource(argp->input_name);
421 gss_name_t output_name;
423 memset(result, 0, sizeof(*result));
425 result->major_status = GSS_S_BAD_NAME;
429 result->major_status = gss_canonicalize_name(&result->minor_status,
430 name, argp->mech_type, &output_name);
432 if (result->major_status == GSS_S_COMPLETE)
433 result->output_name = gssd_make_resource(output_name);
435 result->output_name = 0;
441 gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
443 gss_name_t name = gssd_find_resource(argp->input_name);
445 memset(result, 0, sizeof(*result));
447 result->major_status = GSS_S_BAD_NAME;
451 result->major_status = gss_export_name(&result->minor_status,
452 name, &result->exported_name);
458 gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
460 gss_name_t name = gssd_find_resource(argp->input_name);
463 result->major_status = gss_release_name(&result->minor_status,
465 gssd_delete_resource(argp->input_name);
467 result->major_status = GSS_S_COMPLETE;
468 result->minor_status = 0;
475 gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
477 gss_name_t name = gssd_find_resource(argp->pname);
479 char buf[1024], *bufp;
480 struct passwd pwd, *pw;
483 static size_t buflen_hint = 1024;
485 memset(result, 0, sizeof(*result));
487 result->major_status =
488 gss_pname_to_uid(&result->minor_status,
489 name, argp->mech, &uid);
490 if (result->major_status == GSS_S_COMPLETE) {
492 buflen = buflen_hint;
496 if (buflen > sizeof(buf))
497 bufp = malloc(buflen);
500 error = getpwuid_r(uid, &pwd, bufp, buflen,
504 if (buflen > sizeof(buf))
507 if (buflen > buflen_hint)
508 buflen_hint = buflen;
513 result->gid = pw->pw_gid;
514 getgrouplist(pw->pw_name, pw->pw_gid,
516 result->gidlist.gidlist_len = len;
517 result->gidlist.gidlist_val =
518 mem_alloc(len * sizeof(int));
519 memcpy(result->gidlist.gidlist_val, groups,
523 result->gidlist.gidlist_len = 0;
524 result->gidlist.gidlist_val = NULL;
526 if (bufp != NULL && buflen > sizeof(buf))
530 result->major_status = GSS_S_BAD_NAME;
531 result->minor_status = 0;
538 gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
540 gss_name_t desired_name = GSS_C_NO_NAME;
542 char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
544 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
546 setenv("KRB5CCNAME", ccname, TRUE);
548 memset(result, 0, sizeof(*result));
549 if (argp->desired_name) {
550 desired_name = gssd_find_resource(argp->desired_name);
552 result->major_status = GSS_S_BAD_NAME;
557 result->major_status = gss_acquire_cred(&result->minor_status,
558 desired_name, argp->time_req, argp->desired_mechs,
559 argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec);
561 if (result->major_status == GSS_S_COMPLETE)
562 result->output_cred = gssd_make_resource(cred);
564 result->output_cred = 0;
570 gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
572 gss_cred_id_t cred = gssd_find_resource(argp->cred);
574 memset(result, 0, sizeof(*result));
576 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
580 result->major_status = gss_set_cred_option(&result->minor_status,
581 &cred, argp->option_name, &argp->option_value);
587 gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
589 gss_cred_id_t cred = gssd_find_resource(argp->cred);
592 result->major_status = gss_release_cred(&result->minor_status,
594 gssd_delete_resource(argp->cred);
596 result->major_status = GSS_S_COMPLETE;
597 result->minor_status = 0;
604 gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
607 result->message_context = argp->message_context;
608 result->major_status = gss_display_status(&result->minor_status,
609 argp->status_value, argp->status_type, argp->mech_type,
610 &result->message_context, &result->status_string);
616 gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
619 * We don't use XDR to free the results - anything which was
620 * allocated came from GSS-API. We use xdr_result to figure
625 if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) {
626 init_sec_context_res *p = (init_sec_context_res *) result;
627 gss_release_buffer(&junk, &p->output_token);
628 } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) {
629 accept_sec_context_res *p = (accept_sec_context_res *) result;
630 gss_release_buffer(&junk, &p->output_token);
631 } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) {
632 delete_sec_context_res *p = (delete_sec_context_res *) result;
633 gss_release_buffer(&junk, &p->output_token);
634 } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) {
635 export_sec_context_res *p = (export_sec_context_res *) result;
636 if (p->interprocess_token.length)
637 memset(p->interprocess_token.value, 0,
638 p->interprocess_token.length);
639 gss_release_buffer(&junk, &p->interprocess_token);
640 } else if (xdr_result == (xdrproc_t) xdr_export_name_res) {
641 export_name_res *p = (export_name_res *) result;
642 gss_release_buffer(&junk, &p->exported_name);
643 } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) {
644 acquire_cred_res *p = (acquire_cred_res *) result;
645 gss_release_oid_set(&junk, &p->actual_mechs);
646 } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) {
647 pname_to_uid_res *p = (pname_to_uid_res *) result;
648 if (p->gidlist.gidlist_val)
649 free(p->gidlist.gidlist_val);
650 } else if (xdr_result == (xdrproc_t) xdr_display_status_res) {
651 display_status_res *p = (display_status_res *) result;
652 gss_release_buffer(&junk, &p->status_string);