]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.sbin/gssd/gssd.c
MFC r306478:
[FreeBSD/stable/8.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 <sys/syslog.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <gssapi/gssapi.h>
46 #include <rpc/rpc.h>
47 #include <rpc/rpc_com.h>
48
49 #include "gssd.h"
50
51 #ifndef _PATH_GSS_MECH
52 #define _PATH_GSS_MECH  "/etc/gss/mech"
53 #endif
54 #ifndef _PATH_GSSDSOCK
55 #define _PATH_GSSDSOCK  "/var/run/gssd.sock"
56 #endif
57
58 struct gss_resource {
59         LIST_ENTRY(gss_resource) gr_link;
60         uint64_t        gr_id;  /* indentifier exported to kernel */
61         void*           gr_res; /* GSS-API resource pointer */
62 };
63 LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
64 int gss_resource_count;
65 uint32_t gss_next_id;
66 uint32_t gss_start_time;
67 int debug_level;
68
69 static void gssd_load_mech(void);
70
71 extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
72 extern int gssd_syscall(char *path);
73
74 int
75 main(int argc, char **argv)
76 {
77         /*
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
80          * directly to us.
81          */
82         struct sockaddr_un sun;
83         int fd, oldmask, ch, debug;
84         SVCXPRT *xprt;
85
86         debug = 0;
87         while ((ch = getopt(argc, argv, "d")) != -1) {
88                 switch (ch) {
89                 case 'd':
90                         debug_level++;
91                         break;
92                 default:
93                         fprintf(stderr, "usage: %s [-d]\n", argv[0]);
94                         exit(1);
95                         break;
96                 }
97         }
98
99         gssd_load_mech();
100
101         if (!debug_level)
102                 daemon(0, 0);
103
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);
110         if (!fd) {
111                 if (debug_level == 0) {
112                         syslog(LOG_ERR, "Can't create local gssd socket");
113                         exit(1);
114                 }
115                 err(1, "Can't create local gssd socket");
116         }
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");
121                         exit(1);
122                 }
123                 err(1, "Can't bind local gssd socket");
124         }
125         umask(oldmask);
126         if (listen(fd, SOMAXCONN) < 0) {
127                 if (debug_level == 0) {
128                         syslog(LOG_ERR, "Can't listen on local gssd socket");
129                         exit(1);
130                 }
131                 err(1, "Can't listen on local gssd socket");
132         }
133         xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
134         if (!xprt) {
135                 if (debug_level == 0) {
136                         syslog(LOG_ERR,
137                             "Can't create transport for local gssd socket");
138                         exit(1);
139                 }
140                 err(1, "Can't create transport for local gssd socket");
141         }
142         if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
143                 if (debug_level == 0) {
144                         syslog(LOG_ERR,
145                             "Can't register service for local gssd socket");
146                         exit(1);
147                 }
148                 err(1, "Can't register service for local gssd socket");
149         }
150
151         LIST_INIT(&gss_resources);
152         gss_next_id = 1;
153         gss_start_time = time(0);
154
155         gssd_syscall(_PATH_GSSDSOCK);
156         svc_run();
157
158         return (0);
159 }
160
161 static void
162 gssd_load_mech(void)
163 {
164         FILE            *fp;
165         char            buf[256];
166         char            *p;
167         char            *name, *oid, *lib, *kobj;
168
169         fp = fopen(_PATH_GSS_MECH, "r");
170         if (!fp)
171                 return;
172
173         while (fgets(buf, sizeof(buf), fp)) {
174                 if (*buf == '#')
175                         continue;
176                 p = buf;
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)
185                         continue;
186
187                 if (strcmp(kobj, "-")) {
188                         /*
189                          * Attempt to load the kernel module if its
190                          * not already present.
191                          */
192                         if (modfind(kobj) < 0) {
193                                 if (kldload(kobj) < 0) {
194                                         fprintf(stderr,
195                         "%s: can't find or load kernel module %s for %s\n",
196                                             getprogname(), kobj, name);
197                                 }
198                         }
199                 }
200         }
201         fclose(fp);
202 }
203
204 static void *
205 gssd_find_resource(uint64_t id)
206 {
207         struct gss_resource *gr;
208
209         if (!id)
210                 return (NULL);
211
212         LIST_FOREACH(gr, &gss_resources, gr_link)
213                 if (gr->gr_id == id)
214                         return (gr->gr_res);
215
216         return (NULL);
217 }
218
219 static uint64_t
220 gssd_make_resource(void *res)
221 {
222         struct gss_resource *gr;
223
224         if (!res)
225                 return (0);
226
227         gr = malloc(sizeof(struct gss_resource));
228         if (!gr)
229                 return (0);
230         gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
231         gr->gr_res = res;
232         LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
233         gss_resource_count++;
234         if (debug_level > 1)
235                 printf("%d resources allocated\n", gss_resource_count);
236
237         return (gr->gr_id);
238 }
239
240 static void
241 gssd_delete_resource(uint64_t id)
242 {
243         struct gss_resource *gr;
244
245         LIST_FOREACH(gr, &gss_resources, gr_link) {
246                 if (gr->gr_id == id) {
247                         LIST_REMOVE(gr, gr_link);
248                         free(gr);
249                         gss_resource_count--;
250                         if (debug_level > 1)
251                                 printf("%d resources allocated\n",
252                                     gss_resource_count);
253                         return;
254                 }
255         }
256 }
257
258 bool_t
259 gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
260 {
261
262         return (TRUE);
263 }
264
265 bool_t
266 gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
267 {
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];
272
273         snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
274             (int) argp->uid);
275         setenv("KRB5CCNAME", ccname, TRUE);
276
277         memset(result, 0, sizeof(*result));
278         if (argp->cred) {
279                 cred = gssd_find_resource(argp->cred);
280                 if (!cred) {
281                         result->major_status = GSS_S_CREDENTIALS_EXPIRED;
282                         return (TRUE);
283                 }
284         }
285         if (argp->ctx) {
286                 ctx = gssd_find_resource(argp->ctx);
287                 if (!ctx) {
288                         result->major_status = GSS_S_CONTEXT_EXPIRED;
289                         return (TRUE);
290                 }
291         }
292         if (argp->name) {
293                 name = gssd_find_resource(argp->name);
294                 if (!name) {
295                         result->major_status = GSS_S_BAD_NAME;
296                         return (TRUE);
297                 }
298         }
299
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);
306
307         if (result->major_status == GSS_S_COMPLETE
308             || result->major_status == GSS_S_CONTINUE_NEEDED) {
309                 if (argp->ctx)
310                         result->ctx = argp->ctx;
311                 else
312                         result->ctx = gssd_make_resource(ctx);
313         }
314
315         return (TRUE);
316 }
317
318 bool_t
319 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
320 {
321         gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
322         gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
323         gss_name_t src_name;
324         gss_cred_id_t delegated_cred_handle;
325
326         memset(result, 0, sizeof(*result));
327         if (argp->ctx) {
328                 ctx = gssd_find_resource(argp->ctx);
329                 if (!ctx) {
330                         result->major_status = GSS_S_CONTEXT_EXPIRED;
331                         return (TRUE);
332                 }
333         }
334         if (argp->cred) {
335                 cred = gssd_find_resource(argp->cred);
336                 if (!cred) {
337                         result->major_status = GSS_S_CREDENTIALS_EXPIRED;
338                         return (TRUE);
339                 }
340         }
341
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);
348
349         if (result->major_status == GSS_S_COMPLETE
350             || result->major_status == GSS_S_CONTINUE_NEEDED) {
351                 if (argp->ctx)
352                         result->ctx = argp->ctx;
353                 else
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);
358         }
359
360         return (TRUE);
361 }
362
363 bool_t
364 gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
365 {
366         gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
367
368         if (ctx) {
369                 result->major_status = gss_delete_sec_context(
370                         &result->minor_status, &ctx, &result->output_token);
371                 gssd_delete_resource(argp->ctx);
372         } else {
373                 result->major_status = GSS_S_COMPLETE;
374                 result->minor_status = 0;
375         }
376
377         return (TRUE);
378 }
379
380 bool_t
381 gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
382 {
383         gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
384
385         if (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);
391         } else {
392                 result->major_status = GSS_S_FAILURE;
393                 result->minor_status = 0;
394                 result->interprocess_token.length = 0;
395                 result->interprocess_token.value = NULL;
396         }
397
398         return (TRUE);
399 }
400
401 bool_t
402 gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
403 {
404         gss_name_t name;
405
406         result->major_status = gss_import_name(&result->minor_status,
407             &argp->input_name_buffer, argp->input_name_type, &name);
408
409         if (result->major_status == GSS_S_COMPLETE)
410                 result->output_name = gssd_make_resource(name);
411         else
412                 result->output_name = 0;
413
414         return (TRUE);
415 }
416
417 bool_t
418 gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
419 {
420         gss_name_t name = gssd_find_resource(argp->input_name);
421         gss_name_t output_name;
422
423         memset(result, 0, sizeof(*result));
424         if (!name) {
425                 result->major_status = GSS_S_BAD_NAME;
426                 return (TRUE);
427         }
428
429         result->major_status = gss_canonicalize_name(&result->minor_status,
430             name, argp->mech_type, &output_name);
431
432         if (result->major_status == GSS_S_COMPLETE)
433                 result->output_name = gssd_make_resource(output_name);
434         else
435                 result->output_name = 0;
436
437         return (TRUE);
438 }
439
440 bool_t
441 gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
442 {
443         gss_name_t name = gssd_find_resource(argp->input_name);
444
445         memset(result, 0, sizeof(*result));
446         if (!name) {
447                 result->major_status = GSS_S_BAD_NAME;
448                 return (TRUE);
449         }
450
451         result->major_status = gss_export_name(&result->minor_status,
452             name, &result->exported_name);
453
454         return (TRUE);
455 }
456
457 bool_t
458 gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
459 {
460         gss_name_t name = gssd_find_resource(argp->input_name);
461
462         if (name) {
463                 result->major_status = gss_release_name(&result->minor_status,
464                     &name);
465                 gssd_delete_resource(argp->input_name);
466         } else {
467                 result->major_status = GSS_S_COMPLETE;
468                 result->minor_status = 0;
469         }
470
471         return (TRUE);
472 }
473
474 bool_t
475 gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
476 {
477         gss_name_t name = gssd_find_resource(argp->pname);
478         uid_t uid;
479         char buf[1024], *bufp;
480         struct passwd pwd, *pw;
481         size_t buflen;
482         int error;
483         static size_t buflen_hint = 1024;
484
485         memset(result, 0, sizeof(*result));
486         if (name) {
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) {
491                         result->uid = uid;
492                         buflen = buflen_hint;
493                         for (;;) {
494                                 pw = NULL;
495                                 bufp = buf;
496                                 if (buflen > sizeof(buf))
497                                         bufp = malloc(buflen);
498                                 if (bufp == NULL)
499                                         break;
500                                 error = getpwuid_r(uid, &pwd, bufp, buflen,
501                                     &pw);
502                                 if (error != ERANGE)
503                                         break;
504                                 if (buflen > sizeof(buf))
505                                         free(bufp);
506                                 buflen += 1024;
507                                 if (buflen > buflen_hint)
508                                         buflen_hint = buflen;
509                         }
510                         if (pw) {
511                                 int len = NGRPS;
512                                 int groups[NGRPS];
513                                 result->gid = pw->pw_gid;
514                                 getgrouplist(pw->pw_name, pw->pw_gid,
515                                     groups, &len);
516                                 result->gidlist.gidlist_len = len;
517                                 result->gidlist.gidlist_val =
518                                         mem_alloc(len * sizeof(int));
519                                 memcpy(result->gidlist.gidlist_val, groups,
520                                     len * sizeof(int));
521                         } else {
522                                 result->gid = 65534;
523                                 result->gidlist.gidlist_len = 0;
524                                 result->gidlist.gidlist_val = NULL;
525                         }
526                         if (bufp != NULL && buflen > sizeof(buf))
527                                 free(bufp);
528                 }
529         } else {
530                 result->major_status = GSS_S_BAD_NAME;
531                 result->minor_status = 0;
532         }
533
534         return (TRUE);
535 }
536
537 bool_t
538 gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
539 {
540         gss_name_t desired_name = GSS_C_NO_NAME;
541         gss_cred_id_t cred;
542         char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
543
544         snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
545             (int) argp->uid);
546         setenv("KRB5CCNAME", ccname, TRUE);
547
548         memset(result, 0, sizeof(*result));
549         if (argp->desired_name) {
550                 desired_name = gssd_find_resource(argp->desired_name);
551                 if (!desired_name) {
552                         result->major_status = GSS_S_BAD_NAME;
553                         return (TRUE);
554                 }
555         }
556
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);
560
561         if (result->major_status == GSS_S_COMPLETE)
562                 result->output_cred = gssd_make_resource(cred);
563         else
564                 result->output_cred = 0;
565
566         return (TRUE);
567 }
568
569 bool_t
570 gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
571 {
572         gss_cred_id_t cred = gssd_find_resource(argp->cred);
573
574         memset(result, 0, sizeof(*result));
575         if (!cred) {
576                 result->major_status = GSS_S_CREDENTIALS_EXPIRED;
577                 return (TRUE);
578         }
579
580         result->major_status = gss_set_cred_option(&result->minor_status,
581             &cred, argp->option_name, &argp->option_value);
582
583         return (TRUE);
584 }
585
586 bool_t
587 gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
588 {
589         gss_cred_id_t cred = gssd_find_resource(argp->cred);
590
591         if (cred) {
592                 result->major_status = gss_release_cred(&result->minor_status,
593                     &cred);
594                 gssd_delete_resource(argp->cred);
595         } else {
596                 result->major_status = GSS_S_COMPLETE;
597                 result->minor_status = 0;
598         }
599
600         return (TRUE);
601 }
602
603 bool_t
604 gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
605 {
606
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);
611
612         return (TRUE);
613 }
614
615 int
616 gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
617 {
618         /*
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
621          * out what to do.
622          */
623         OM_uint32 junk;
624
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);
653         }
654
655         return (TRUE);
656 }