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>
43 #include <gssapi/gssapi.h>
45 #include <rpc/rpc_com.h>
49 #ifndef _PATH_GSS_MECH
50 #define _PATH_GSS_MECH "/etc/gss/mech"
52 #ifndef _PATH_GSSDSOCK
53 #define _PATH_GSSDSOCK "/var/run/gssd.sock"
57 LIST_ENTRY(gss_resource) gr_link;
58 uint64_t gr_id; /* indentifier exported to kernel */
59 void* gr_res; /* GSS-API resource pointer */
61 LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
62 int gss_resource_count;
64 uint32_t gss_start_time;
67 static void gssd_load_mech(void);
69 extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
70 extern int gssd_syscall(char *path);
73 main(int argc, char **argv)
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
80 struct sockaddr_un sun;
81 int fd, oldmask, ch, debug;
85 while ((ch = getopt(argc, argv, "d")) != -1) {
91 fprintf(stderr, "usage: %s [-d]\n", argv[0]);
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);
109 err(1, "Can't create local gssd socket");
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");
116 if (listen(fd, SOMAXCONN) < 0) {
117 err(1, "Can't listen on local gssd socket");
119 xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
121 err(1, "Can't create transport for local gssd socket");
123 if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
124 err(1, "Can't register service for local gssd socket");
127 LIST_INIT(&gss_resources);
129 gss_start_time = time(0);
131 gssd_syscall(_PATH_GSSDSOCK);
143 char *name, *oid, *lib, *kobj;
145 fp = fopen(_PATH_GSS_MECH, "r");
149 while (fgets(buf, sizeof(buf), fp)) {
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)
163 if (strcmp(kobj, "-")) {
165 * Attempt to load the kernel module if its
166 * not already present.
168 if (modfind(kobj) < 0) {
169 if (kldload(kobj) < 0) {
171 "%s: can't find or load kernel module %s for %s\n",
172 getprogname(), kobj, name);
181 gssd_find_resource(uint64_t id)
183 struct gss_resource *gr;
188 LIST_FOREACH(gr, &gss_resources, gr_link)
196 gssd_make_resource(void *res)
198 struct gss_resource *gr;
203 gr = malloc(sizeof(struct gss_resource));
206 gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
208 LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
209 gss_resource_count++;
211 printf("%d resources allocated\n", gss_resource_count);
217 gssd_delete_resource(uint64_t id)
219 struct gss_resource *gr;
221 LIST_FOREACH(gr, &gss_resources, gr_link) {
222 if (gr->gr_id == id) {
223 LIST_REMOVE(gr, gr_link);
225 gss_resource_count--;
227 printf("%d resources allocated\n",
235 gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
242 gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
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];
249 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
251 setenv("KRB5CCNAME", ccname, TRUE);
253 memset(result, 0, sizeof(*result));
255 cred = gssd_find_resource(argp->cred);
257 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
262 ctx = gssd_find_resource(argp->ctx);
264 result->major_status = GSS_S_CONTEXT_EXPIRED;
269 name = gssd_find_resource(argp->name);
271 result->major_status = GSS_S_BAD_NAME;
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);
283 if (result->major_status == GSS_S_COMPLETE
284 || result->major_status == GSS_S_CONTINUE_NEEDED) {
286 result->ctx = argp->ctx;
288 result->ctx = gssd_make_resource(ctx);
295 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
297 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
298 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
300 gss_cred_id_t delegated_cred_handle;
302 memset(result, 0, sizeof(*result));
304 ctx = gssd_find_resource(argp->ctx);
306 result->major_status = GSS_S_CONTEXT_EXPIRED;
311 cred = gssd_find_resource(argp->cred);
313 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
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);
325 if (result->major_status == GSS_S_COMPLETE
326 || result->major_status == GSS_S_CONTINUE_NEEDED) {
328 result->ctx = argp->ctx;
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);
340 gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
342 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
345 result->major_status = gss_delete_sec_context(
346 &result->minor_status, &ctx, &result->output_token);
347 gssd_delete_resource(argp->ctx);
349 result->major_status = GSS_S_COMPLETE;
350 result->minor_status = 0;
357 gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
359 gss_ctx_id_t ctx = gssd_find_resource(argp->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);
368 result->major_status = GSS_S_FAILURE;
369 result->minor_status = 0;
370 result->interprocess_token.length = 0;
371 result->interprocess_token.value = NULL;
378 gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
382 result->major_status = gss_import_name(&result->minor_status,
383 &argp->input_name_buffer, argp->input_name_type, &name);
385 if (result->major_status == GSS_S_COMPLETE)
386 result->output_name = gssd_make_resource(name);
388 result->output_name = 0;
394 gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
396 gss_name_t name = gssd_find_resource(argp->input_name);
397 gss_name_t output_name;
399 memset(result, 0, sizeof(*result));
401 result->major_status = GSS_S_BAD_NAME;
405 result->major_status = gss_canonicalize_name(&result->minor_status,
406 name, argp->mech_type, &output_name);
408 if (result->major_status == GSS_S_COMPLETE)
409 result->output_name = gssd_make_resource(output_name);
411 result->output_name = 0;
417 gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
419 gss_name_t name = gssd_find_resource(argp->input_name);
421 memset(result, 0, sizeof(*result));
423 result->major_status = GSS_S_BAD_NAME;
427 result->major_status = gss_export_name(&result->minor_status,
428 name, &result->exported_name);
434 gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
436 gss_name_t name = gssd_find_resource(argp->input_name);
439 result->major_status = gss_release_name(&result->minor_status,
441 gssd_delete_resource(argp->input_name);
443 result->major_status = GSS_S_COMPLETE;
444 result->minor_status = 0;
451 gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
453 gss_name_t name = gssd_find_resource(argp->pname);
456 struct passwd pwd, *pw;
458 memset(result, 0, sizeof(*result));
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) {
465 getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
469 result->gid = pw->pw_gid;
470 getgrouplist(pw->pw_name, pw->pw_gid,
472 result->gidlist.gidlist_len = len;
473 result->gidlist.gidlist_val =
474 mem_alloc(len * sizeof(int));
475 memcpy(result->gidlist.gidlist_val, groups,
479 result->gidlist.gidlist_len = 0;
480 result->gidlist.gidlist_val = NULL;
484 result->major_status = GSS_S_BAD_NAME;
485 result->minor_status = 0;
492 gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
494 gss_name_t desired_name = GSS_C_NO_NAME;
496 char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
498 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
500 setenv("KRB5CCNAME", ccname, TRUE);
502 memset(result, 0, sizeof(*result));
503 if (argp->desired_name) {
504 desired_name = gssd_find_resource(argp->desired_name);
506 result->major_status = GSS_S_BAD_NAME;
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);
515 if (result->major_status == GSS_S_COMPLETE)
516 result->output_cred = gssd_make_resource(cred);
518 result->output_cred = 0;
524 gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
526 gss_cred_id_t cred = gssd_find_resource(argp->cred);
528 memset(result, 0, sizeof(*result));
530 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
534 result->major_status = gss_set_cred_option(&result->minor_status,
535 &cred, argp->option_name, &argp->option_value);
541 gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
543 gss_cred_id_t cred = gssd_find_resource(argp->cred);
546 result->major_status = gss_release_cred(&result->minor_status,
548 gssd_delete_resource(argp->cred);
550 result->major_status = GSS_S_COMPLETE;
551 result->minor_status = 0;
558 gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
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);
570 gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
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
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);