]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/gssd/gssd.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / usr.sbin / gssd / gssd.c
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 }